a couple of changes; but some parts are now broken
[gnupg.git] / util / iobuf.c
index 61e1c59..65efc0e 100644 (file)
@@ -40,6 +40,7 @@ typedef struct {
     int usage;
     size_t size;
     size_t count;
+    int partial;  /* 1 = partial header, 2 in last partial packet */
     int eof;
 } block_filter_ctx_t;
 
@@ -143,20 +144,64 @@ block_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
            rc = -1;
        while( !rc && size ) {
            if( !a->size ) { /* get the length bytes */
-               c = iobuf_get(chain);
-               a->size = c << 8;
-               c = iobuf_get(chain);
-               a->size |= c;
-               if( c == -1 ) {
-                   log_error("block_filter: error reading length info\n");
-                   rc = G10ERR_READ_FILE;
-               }
-               if( !a->size ) {
+               if( a->partial == 2 ) {
                    a->eof = 1;
                    if( !n )
                        rc = -1;
                    break;
                }
+               else if( a->partial ) {
+                   if( (c = iobuf_get(chain)) == -1 ) {
+                       log_error("block_filter: 1st length byte missing\n");
+                       rc = G10ERR_READ_FILE;
+                       break;
+                   }
+                   if( c < 192 ) {
+                       a->size = c;
+                       a->partial = 2;
+                       if( !a->size ) {
+                           a->eof = 1;
+                           if( !n )
+                               rc = -1;
+                           break;
+                       }
+                   }
+                   else if( c < 224 ) {
+                       a->size = (c - 192) * 256;
+                       if( (c = iobuf_get(chain)) == -1 ) {
+                           log_error("block_filter: 2nd length byte missing\n");
+                           rc = G10ERR_READ_FILE;
+                           break;
+                       }
+                       a->size += c + 192;
+                       a->partial = 2;
+                       if( !a->size ) {
+                           a->eof = 1;
+                           if( !n )
+                               rc = -1;
+                           break;
+                       }
+                   }
+                   else { /* next partial body length */
+                       a->size = 1 << (c & 0x1f);
+                   }
+               }
+               else {
+                   c = iobuf_get(chain);
+                   a->size = c << 8;
+                   c = iobuf_get(chain);
+                   a->size |= c;
+                   if( c == -1 ) {
+                       log_error("block_filter: error reading length info\n");
+                       rc = G10ERR_READ_FILE;
+                   }
+                   if( !a->size ) {
+                       a->eof = 1;
+                       if( !n )
+                           rc = -1;
+                       break;
+                   }
+               }
            }
 
            for(; !rc && size && a->size; size--, a->size-- ) {
@@ -176,6 +221,7 @@ block_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
     else if( control == IOBUFCTRL_FLUSH ) {
        size_t avail, n;
 
+       assert( !a->partial );
        for(p=buf; !rc && size; ) {
            n = size;
            avail = a->size - a->count;
@@ -205,7 +251,9 @@ block_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
     else if( control == IOBUFCTRL_INIT ) {
        if( DBG_IOBUF )
            log_debug("init block_filter %p\n", a );
-       if( a->usage == 1 )
+       if( a->partial )
+           a->count = 0;
+       else if( a->usage == 1 )
            a->count = a->size = 0;
        else
            a->count = a->size; /* force first length bytes */
@@ -216,8 +264,12 @@ block_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
     }
     else if( control == IOBUFCTRL_FREE ) {
        if( a->usage == 2 ) { /* write the end markers */
-           iobuf_writebyte(chain, 0);
-           iobuf_writebyte(chain, 0);
+           if( a->partial ) {
+           }
+           else {
+               iobuf_writebyte(chain, 0);
+               iobuf_writebyte(chain, 0);
+           }
        }
        else if( a->size ) {
            log_error("block_filter: pending bytes!\n");
@@ -261,7 +313,7 @@ iobuf_close( IOBUF a )
     size_t dummy_len;
     int rc=0;
 
-    for( ; a; a = a2 ) {
+    for( ; a && !rc ; a = a2 ) {
        a2 = a->chain;
        if( a->usage == 2 && (rc=iobuf_flush(a)) )
            log_error("iobuf_flush failed on close: %s\n", g10_errstr(rc));
@@ -271,7 +323,6 @@ iobuf_close( IOBUF a )
        if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE,
                                         a->chain, NULL, &dummy_len)) )
            log_error("IOBUFCTRL_FREE failed on close: %s\n", g10_errstr(rc) );
-       m_free(a->recorder.buf);
        m_free(a->d.buf);
        m_free(a);
     }
@@ -351,7 +402,7 @@ iobuf_create( const char *fname )
     file_filter_ctx_t *fcx;
     size_t len;
 
-    if( !fname ) {
+    if( !fname || (*fname=='-' && !fname[1]) ) {
        fp = stdout;
        fname = "[stdout]";
     }
@@ -372,6 +423,62 @@ iobuf_create( const char *fname )
 }
 
 /****************
+ * append to a iobuf if the file does not exits; create it.
+ * cannont be used for stdout.
+ */
+IOBUF
+iobuf_append( const char *fname )
+{
+    IOBUF a;
+    FILE *fp;
+    file_filter_ctx_t *fcx;
+    size_t len;
+
+    if( !fname )
+       return NULL;
+    else if( !(fp = fopen(fname, "ab")) )
+       return NULL;
+    a = iobuf_alloc(2, 8192 );
+    fcx = m_alloc( sizeof *fcx + strlen(fname) );
+    fcx->fp = fp;
+    strcpy(fcx->fname, fname );
+    a->filter = file_filter;
+    a->filter_ov = fcx;
+    file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
+    file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
+    if( DBG_IOBUF )
+       log_debug("iobuf-%d.%d: append '%s'\n", a->no, a->subno, a->desc );
+
+    return a;
+}
+
+IOBUF
+iobuf_openrw( const char *fname )
+{
+    IOBUF a;
+    FILE *fp;
+    file_filter_ctx_t *fcx;
+    size_t len;
+
+    if( !fname )
+       return NULL;
+    else if( !(fp = fopen(fname, "r+b")) )
+       return NULL;
+    a = iobuf_alloc(2, 8192 );
+    fcx = m_alloc( sizeof *fcx + strlen(fname) );
+    fcx->fp = fp;
+    strcpy(fcx->fname, fname );
+    a->filter = file_filter;
+    a->filter_ov = fcx;
+    file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
+    file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
+    if( DBG_IOBUF )
+       log_debug("iobuf-%d.%d: openrw '%s'\n", a->no, a->subno, a->desc );
+
+    return a;
+}
+
+/****************
  * Register an i/o filter.
  */
 int
@@ -395,6 +502,7 @@ iobuf_push_filter( IOBUF a,
     /* remove the filter stuff from the new stream */
     a->filter = NULL;
     a->filter_ov = NULL;
+    a->filter_eof = 0;
     if( a->usage == 2 ) { /* allocate a fresh buffer for the original stream */
        b->d.buf = m_alloc( a->d.size );
        b->d.len = 0;
@@ -406,9 +514,8 @@ iobuf_push_filter( IOBUF a,
        a->d.start = 0;
     }
     /* disable nlimit for the new stream */
+    a->ntotal = b->ntotal + b->nbytes;
     a->nlimit = a->nbytes = 0;
-    /* disable recorder for the original stream */
-    b->recorder.buf = NULL;
     /* make a link from the new stream to the original stream */
     a->chain = b;
     a->opaque = b->opaque;
@@ -485,7 +592,7 @@ iobuf_pop_filter( IOBUF a, int (*f)(void *opaque, int control,
        m_free(b);
     }
     else if( !b->chain ) { /* remove the last iobuf from the chain */
-       log_bug("Ohh jeee, trying to a head filter\n");
+       log_bug("Ohh jeee, trying to remove a head filter\n");
     }
     else {  /* remove an intermediate iobuf from the chain */
        log_bug("Ohh jeee, trying to remove an intermediate filter\n");
@@ -525,12 +632,14 @@ underflow(IOBUF a)
            size_t dummy_len;
 
            /* and tell the filter to free it self */
-           if( (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE, a->chain,
-                              NULL, &dummy_len)) )
-               log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) );
-           a->filter = NULL;
-           a->desc = NULL;
-           a->filter_ov = NULL;
+           if( a->filter != file_filter ) {
+               if( (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE, a->chain,
+                                  NULL, &dummy_len)) )
+                   log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) );
+               a->filter = NULL;
+               a->desc = NULL;
+               a->filter_ov = NULL;
+           }
            a->filter_eof = 1;
        }
 
@@ -591,6 +700,15 @@ iobuf_readbyte(IOBUF a)
 {
     int c;
 
+    /* nlimit does not work together with unget */
+    /* nbytes is also not valid! */
+    if( a->unget.buf ) {
+       if( a->unget.start < a->unget.len )
+           return a->unget.buf[a->unget.start++];
+       m_free(a->unget.buf);
+       a->unget.buf = NULL;
+    }
+
     if( a->nlimit && a->nbytes >= a->nlimit )
        return -1; /* forced EOF */
 
@@ -601,18 +719,30 @@ iobuf_readbyte(IOBUF a)
        return -1; /* EOF */
 
     a->nbytes++;
+    return c;
+}
+
 
-    if( a->recorder.buf ) {
-       if( a->recorder.len >= a->recorder.size ) {
-           a->recorder.size += 500;
-           a->recorder.buf = m_realloc( a->recorder.buf, a->recorder.size );
+int
+iobuf_read(IOBUF a, byte *buf, unsigned buflen )
+{
+    int c, n;
+
+    for(n=0 ; n < buflen; n++, buf++ ) {
+       if( (c = iobuf_readbyte(a)) == -1 ) {
+           if( !n )
+               return -1; /* eof */
+           break;
        }
-       ((byte*)a->recorder.buf)[a->recorder.len++] = c;
+       else
+           *buf = c;
     }
-    return c;
+    return n;
 }
 
 
+
+
 int
 iobuf_writebyte(IOBUF a, unsigned c)
 {
@@ -669,6 +799,27 @@ iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen )
     return n;
 }
 
+/****************
+ * unget the contents of the temp io stream to A and close temp
+ * Could be optimized!!
+ */
+void
+iobuf_unget_and_close_temp( IOBUF a, IOBUF temp )
+{
+    if( a->unget.buf ) {
+       if( a->unget.start < a->unget.len )
+           log_fatal("cannot do any more ungets on this buffer\n");
+       /* not yet cleaned up; do it now */
+       m_free(a->unget.buf);
+       a->unget.buf = NULL;
+    }
+    a->unget.size = temp->d.len;
+    a->unget.buf = m_alloc( a->unget.size );
+    a->unget.len = temp->d.len;
+    memcpy( a->unget.buf, temp->d.buf, a->unget.len );
+    iobuf_close(temp);
+}
+
 
 /****************
  * Set a limit, how much bytes may be read from the input stream A.
@@ -678,47 +829,12 @@ void
 iobuf_set_limit( IOBUF a, unsigned long nlimit )
 {
     a->nlimit = nlimit;
+    a->ntotal += a->nbytes;
     a->nbytes = 0;
 }
 
 
 
-void
-iobuf_start_recorder( IOBUF a )
-{
-    m_free(a->recorder.buf);
-    a->recorder.size = 500;
-    a->recorder.buf = m_alloc(a->recorder.size);
-    a->recorder.len = 0;
-}
-
-void
-iobuf_push_recorder( IOBUF a, int c )
-{
-    if( a->recorder.buf ) {
-       if( a->recorder.len >= a->recorder.size ) {
-           a->recorder.size += 500;
-           a->recorder.buf = m_realloc( a->recorder.buf, a->recorder.size );
-       }
-       ((byte*)a->recorder.buf)[a->recorder.len++] = c;
-    }
-}
-
-
-char *
-iobuf_stop_recorder( IOBUF a, size_t *n )
-{
-    char *p;
-    if( !a->recorder.buf )
-       log_bug("iobuf_recorder not started\n");
-    p = a->recorder.buf;
-    if( n )
-       *n = a->recorder.len;
-    a->recorder.buf = NULL;
-    return p;
-}
-
-
 /****************
  * Return the length of an open file
  */
@@ -742,13 +858,53 @@ iobuf_get_filelength( IOBUF a )
 }
 
 /****************
+ * Tell the file position, where the next read will take place
+ */
+ulong
+iobuf_tell( IOBUF a )
+{
+    return a->ntotal + a->nbytes;
+}
+
+
+int
+iobuf_seek( IOBUF a, ulong newpos )
+{
+    file_filter_ctx_t *b = NULL;
+
+    for( ; a; a = a->chain ) {
+       if( !a->chain && a->filter == file_filter ) {
+           b = a->filter_ov;
+           break;
+       }
+    }
+    if( !a )
+       return -1;
+
+    if( fseek( b->fp, newpos, SEEK_SET ) ) {
+       log_error("can't seek to %lu: %s\n", newpos, strerror(errno) );
+       return -1;
+    }
+    a->ntotal = newpos;
+    /* remove filters, but the last */
+    while( a->chain )
+       iobuf_pop_filter( a, a->filter, NULL );
+
+
+    return 0;
+}
+
+
+
+
+
+
+/****************
  * Retrieve the filename
  */
 const char *
 iobuf_get_fname( IOBUF a )
 {
-    struct stat st;
-
     for( ; a; a = a->chain )
        if( !a->chain && a->filter == file_filter ) {
            file_filter_ctx_t *b = a->filter_ov;
@@ -779,16 +935,37 @@ iobuf_set_block_mode( IOBUF a, size_t n )
     }
 }
 
+/****************
+ * enable patial block mode as descriped in the OpenPGP draft.
+ * LEN is the first length
+ */
+void
+iobuf_set_partial_block_mode( IOBUF a, size_t len )
+{
+    block_filter_ctx_t *ctx = m_alloc_clear( sizeof *ctx );
+
+    assert( a->usage == 1 || a->usage == 2 );
+    ctx->usage = a->usage;
+    if( !len ) {
+       iobuf_pop_filter(a, block_filter, NULL );
+    }
+    else {
+       ctx->partial = 1;
+       ctx->size = len;
+       iobuf_push_filter(a, block_filter, ctx );
+    }
+}
+
 
 /****************
- * checks wether the stream is in block mode
+ * Checks wether the stream is in block mode
+ * Note: This does not work if other filters are pushed on the stream.
  */
 int
 iobuf_in_block_mode( IOBUF a )
 {
-    for(; a; a = a->chain )
-       if( a->filter == block_filter )
-           return 1; /* yes */
+    if( a && a->filter == block_filter )
+       return 1; /* yes */
     return 0; /* no */
 }