new release
[gnupg.git] / util / iobuf.c
index 1c35db6..772dfa1 100644 (file)
@@ -1,14 +1,14 @@
 /* iobuf.c  -  file handling
- *     Copyright (c) 1997 by Werner Koch (dd9jn)
+ *     Copyright (C) 1998 Free Software Foundation, Inc.
  *
- * This file is part of G10.
+ * This file is part of GNUPG.
  *
- * G10 is free software; you can redistribute it and/or modify
+ * GNUPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- * G10 is distributed in the hope that it will be useful,
+ * GNUPG is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
@@ -24,6 +24,7 @@
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -40,6 +41,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;
 
@@ -49,7 +51,7 @@ static int underflow(IOBUF a);
  * Read data from a file into buf which has an allocated length of *LEN.
  * return the number of read bytes in *LEN. OPAQUE is the FILE * of
  * the stream. A is not used.
- * control maybe:
+ * control may be:
  * IOBUFCTRL_INIT: called just before the function is linked into the
  *                list of function. This can be used to prepare internal
  *                data structures of the function.
@@ -143,20 +145,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 +222,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 +252,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 +265,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 +314,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));
@@ -319,7 +372,7 @@ iobuf_open( const char *fname )
     file_filter_ctx_t *fcx;
     size_t len;
 
-    if( !fname ) {
+    if( !fname || (*fname=='-' && !fname[1])  ) {
        fp = stdin; /* fixme: set binary mode for msdoze */
        fname = "[stdin]";
     }
@@ -340,7 +393,7 @@ iobuf_open( const char *fname )
 }
 
 /****************
- * create a iobuf for writing to a file; the file will be created.
+ * create an iobuf for writing to a file; the file will be created.
  */
 IOBUF
 iobuf_create( const char *fname )
@@ -350,7 +403,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]";
     }
@@ -371,8 +424,8 @@ iobuf_create( const char *fname )
 }
 
 /****************
- * append to a iobuf if the file does not exits; create it.
- * cannont be used for stdout.
+ * append to an iobuf; if the file does not exist, create it.
+ * cannot be used for stdout.
  */
 IOBUF
 iobuf_append( const char *fname )
@@ -400,6 +453,32 @@ iobuf_append( const char *fname )
     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.
  */
@@ -424,6 +503,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;
@@ -500,7 +580,7 @@ iobuf_pop_filter( IOBUF a, int (*f)(void *opaque, int control,
        return rc;
     }
 
-    /* and look how to remove it */
+    /* and see how to remove it */
     if( a == b && !b->chain )
        log_bug("can't remove the last filter from the chain\n");
     else if( a == b ) { /* remove the first iobuf from the chain */
@@ -513,7 +593,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");
@@ -553,12 +633,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;
        }
 
@@ -619,6 +701,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 */
 
@@ -634,6 +725,67 @@ iobuf_readbyte(IOBUF a)
 
 
 int
+iobuf_read(IOBUF a, byte *buf, unsigned buflen )
+{
+    int c, n;
+
+    if( a->unget.buf || a->nlimit ) {
+       /* handle special cases */
+       for(n=0 ; n < buflen; n++, buf++ ) {
+           if( (c = iobuf_readbyte(a)) == -1 ) {
+               if( !n )
+                   return -1; /* eof */
+               break;
+           }
+           else
+               *buf = c;
+       }
+       return n;
+    }
+
+    n = 0;
+    do {
+       for( ; n < buflen && a->d.start < a->d.len; n++ )
+           *buf++ = a->d.buf[a->d.start++];
+       if( n < buflen ) {
+           if( (c=underflow(a)) == -1 ) {
+               a->nbytes += n;
+               return n? n : -1/*EOF*/;
+           }
+           *buf++ = c; n++;
+       }
+    } while( n < buflen );
+    a->nbytes += n;
+    return n;
+}
+
+
+/****************
+ * Have a look at the iobuf.
+ * NOTE: This only works in special cases.
+ */
+int
+iobuf_peek(IOBUF a, byte *buf, unsigned buflen )
+{
+    int n=0;
+
+    if( !(a->d.start < a->d.len) ) {
+       if( underflow(a) == -1 )
+           return -1;
+       /* and unget this character */
+       assert(a->d.start == 1);
+       a->d.start = 0;
+    }
+
+    for(n=0 ; n < buflen && (a->d.start+n) < a->d.len ; n++, buf++ )
+       *buf = a->d.buf[n];
+    return n;
+}
+
+
+
+
+int
 iobuf_writebyte(IOBUF a, unsigned c)
 {
     if( a->d.len == a->d.size )
@@ -649,12 +801,18 @@ iobuf_writebyte(IOBUF a, unsigned c)
 int
 iobuf_write(IOBUF a, byte *buf, unsigned buflen )
 {
-    for( ; buflen; buflen--, buf++ )
-       if( iobuf_writebyte(a, *buf) )
-           return -1;
+    do {
+       for( ; buflen && a->d.len < a->d.size; buflen--, buf++ )
+           a->d.buf[a->d.len++] = *buf;
+       if( buflen ) {
+           if( iobuf_flush(a) )
+               return -1;
+       }
+    } while( buflen );
     return 0;
 }
 
+
 int
 iobuf_writestr(IOBUF a, const char *buf )
 {
@@ -689,9 +847,30 @@ 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.
+ * Set a limit on how many bytes may be read from the input stream A.
  * Setting the limit to 0 disables this feature.
  */
 void
@@ -736,6 +915,11 @@ iobuf_tell( IOBUF a )
 }
 
 
+
+/****************
+ * This is a very limited implementation. It simply discards all internal
+ * buffering and removes all filters but the first one.
+ */
 int
 iobuf_seek( IOBUF a, ulong newpos )
 {
@@ -754,8 +938,15 @@ iobuf_seek( IOBUF a, ulong newpos )
        log_error("can't seek to %lu: %s\n", newpos, strerror(errno) );
        return -1;
     }
+    a->d.len = 0;   /* discard buffer */
+    a->d.start = 0;
+    a->nbytes = 0;
+    a->nlimit = 0;
+    a->ntotal = newpos;
+    /* remove filters, but the last */
+    while( a->chain )
+       iobuf_pop_filter( a, a->filter, NULL );
 
-    /* FIXME: flush all buffers (and remove filters?)*/
 
     return 0;
 }
@@ -771,8 +962,6 @@ iobuf_seek( IOBUF a, ulong newpos )
 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;
@@ -803,16 +992,37 @@ iobuf_set_block_mode( IOBUF a, size_t n )
     }
 }
 
+/****************
+ * enable partial block mode as described 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 whether 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 */
 }