Fix off by one when cutting utf8-strings
[citadel.git] / libcitadel / lib / stringbuf.c
index 1171e9939840e308c29b52a7985d1f58da6bc738..12fd259e4582ef93d9da48158582d4a0ca0bca40 100644 (file)
@@ -256,7 +256,7 @@ void dbg_Init(StrBuf *Buf)
  * @param A First one
  * @param B second one
  */
-static inline void SwapBuffers(StrBuf *A, StrBuf *B)
+static inline void iSwapBuffers(StrBuf *A, StrBuf *B)
 {
        StrBuf C;
 
@@ -266,6 +266,12 @@ static inline void SwapBuffers(StrBuf *A, StrBuf *B)
 
 }
 
+void SwapBuffers(StrBuf *A, StrBuf *B)
+{
+       iSwapBuffers(A, B);
+}
+
+
 /** 
  * @ingroup StrBuf_Cast
  * @brief Cast operator to Plain String 
@@ -510,7 +516,7 @@ void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, cons
                }
                else 
                        NewBuf = *CreateRelpaceMe;
-               SwapBuffers (NewBuf, CopyFlushMe);
+               iSwapBuffers (NewBuf, CopyFlushMe);
        }
        if (!KeepOriginal)
                FlushStrBuf(CopyFlushMe);
@@ -2006,32 +2012,13 @@ void StrBufXMLEscAppend(StrBuf *OutBuf,
                        *pt = *pch;
                        pt++; pch++;
                }
-               else if (*pch < 0x20) {
-                       /* we probably shouldn't be doing this */
-                       if (OverrideLowChars)
-                       {
-                               *pt = '_';
-                               pt ++;
-                               pch ++;
-                       }
-                       else
-                       {
-                               *pt = '&';
-                               pt++;
-                               *pt = HexList[*(unsigned char*)pch][0];
-                               pt ++;
-                               *pt = HexList[*(unsigned char*)pch][1];
-                               pt ++; pch ++;
-                               *pt = '&';
-                               pt++;
-                               pch ++;
-                       }
-               }
                else {
                        IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(pch, pche);
                        if (IsUtf8Sequence)
                        {
-                               while (IsUtf8Sequence > 0){
+                               while ((IsUtf8Sequence > 0) && 
+                                      (pch < pche))
+                               {
                                        *pt = *pch;
                                        pt ++;
                                        pch ++;
@@ -2846,7 +2833,7 @@ int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
                return -1;
 
        if (BufOut->BufSize < BufIn->BufUsed)
-               IncreaseBuf(BufOut, BufIn->BufUsed, 0);
+               IncreaseBuf(BufOut, 0, BufIn->BufUsed);
 
        BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
                                           BufIn->buf,
@@ -2859,9 +2846,10 @@ typedef struct __z_enc_stream {
        z_stream zstream;
 } z_enc_stream;
 
-void *StrBufNewStreamContext(eStreamType type)
+vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err)
 {
        base64_decodestate *state;;
+       *Err = NULL;
 
        switch (type)
        {
@@ -2869,26 +2857,28 @@ void *StrBufNewStreamContext(eStreamType type)
        case eBase64Encode:
                state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
                base64_init_decodestate(state);
-               return state;
+               return (vStreamT*) state;
                break;
        case eZLibDecode:
        {
-/*
+
                z_enc_stream *stream;
                int err;
 
                stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
                memset(stream, 0, sizeof(z_enc_stream));
-               stream->OutBuf.BufSize = 64*SIZ;
+               stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
                stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
 
-               err = deflateInit(&stream->zstream,
-                                 ZLibCompressionRatio);
+               err = inflateInit(&stream->zstream);
+
+               if (err != Z_OK) {
+                       StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
+                       *Err = zError(err);
+                       return NULL;
+               }
+               return (vStreamT*) stream;
 
-               if (err != Z_OK)
-                       return NULL;/// tODO cleanup
-               return stream;
-*/
        }
        case eZLibEncode:
        {
@@ -2899,28 +2889,27 @@ void *StrBufNewStreamContext(eStreamType type)
                memset(stream, 0, sizeof(z_enc_stream));
                stream->OutBuf.BufSize = 4*SIZ; /// todo 64
                stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
-               /* write gzip header * /
+               /* write gzip header */
                stream->OutBuf.BufUsed = snprintf
                        (stream->OutBuf.buf,
                         stream->OutBuf.BufSize, 
                         "%c%c%c%c%c%c%c%c%c%c",
                         gz_magic[0], gz_magic[1], Z_DEFLATED,
-                        0 /*flags * / , 0, 0, 0, 0 /*time * / , 0 /* xflags * / ,
+                        0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
                         OS_CODE);
-/*
+
                err = deflateInit2(&stream->zstream,
                                   ZLibCompressionRatio,
                                   Z_DEFLATED,
                                   -MAX_WBITS,
                                   DEF_MEM_LEVEL,
                                   Z_DEFAULT_STRATEGY);
-*/
-               err = deflateInit(&stream->zstream,
-                                 ZLibCompressionRatio);
-
-               if (err != Z_OK)
-                       return NULL;/// tODO cleanup
-               return stream;
+               if (err != Z_OK) {
+                       StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
+                       *Err = zError(err);
+                       return NULL;
+               }
+               return (vStreamT*) stream;
        }
        case eEmtyCodec:
                /// TODO
@@ -2930,8 +2919,16 @@ void *StrBufNewStreamContext(eStreamType type)
        return NULL;
 }
 
-void StrBufDestroyStreamContext(eStreamType type, void **vStream)
+int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err)
 {
+       int err;
+       int rc = 0;
+       *Err = NULL;
+       
+       if ((vStream == NULL) || (*vStream==NULL)) {
+               *Err = strerror(EINVAL);
+               return EINVAL;
+       }
        switch (type)
        {
        case eBase64Encode:
@@ -2940,23 +2937,34 @@ void StrBufDestroyStreamContext(eStreamType type, void **vStream)
                *vStream = NULL;
                break;
        case eZLibDecode:
+       {
+               z_enc_stream *stream = (z_enc_stream *)*vStream;
+               (void)inflateEnd(&stream->zstream);
+               free(stream->OutBuf.buf);
+               free(stream);
+       }
        case eZLibEncode:
        {
                z_enc_stream *stream = (z_enc_stream *)*vStream;
+               err = deflateEnd(&stream->zstream);
+               if (err != Z_OK) {
+                       *Err = zError(err);
+                       rc = -1;
+               }
                free(stream->OutBuf.buf);
                free(stream);
-// todo more?
                *vStream = NULL;
                break;
        }
        case eEmtyCodec: 
                break; /// TODO
        }
+       return rc;
 }
 
-void StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, void *vStream, int LastChunk)
+int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err)
 {
-
+       int rc = 0;
        switch (type)
        {
        case eBase64Encode:
@@ -3017,7 +3025,7 @@ void StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, con
        case eZLibEncode:
        {
                z_enc_stream *stream = (z_enc_stream *)vStream;
-               int org_outbuf_len = stream->zstream.total_out;
+               int org_outbuf_len = stream->OutBuf.BufUsed;
                int err;
                unsigned int chunkavail;
 
@@ -3032,16 +3040,21 @@ void StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, con
                        stream->zstream.next_in = (Bytef *) In->Buf->buf;
                        stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
                }
-
+               
                stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
                stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
 
                err = deflate(&stream->zstream,  (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
 
                stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
-               /// todo + org_outbuf_len;
 
-               if (Target) SwapBuffers(Target->Buf, &stream->OutBuf);
+               if (Target && 
+                   (LastChunk ||
+                    (stream->OutBuf.BufUsed != org_outbuf_len)
+                           ))
+               {
+                       iSwapBuffers(Target->Buf, &stream->OutBuf);
+               }
 
                if (stream->zstream.avail_in == 0)
                {
@@ -3066,7 +3079,11 @@ void StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, con
                                        (In->Buf->BufUsed - stream->zstream.avail_in);
                        }
                }
-
+               rc = (LastChunk && (err != Z_FINISH));
+               if (!rc && (err != Z_OK)) {
+                       *Err = zError(err);
+               }
+               
        }
        break;
        case eZLibDecode: {
@@ -3074,26 +3091,40 @@ void StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, con
                int org_outbuf_len = stream->zstream.total_out;
                int err;
 
-               if (In->ReadWritePointer != NULL)
-               {
-                       stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
-                       stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
-                               (In->ReadWritePointer - In->Buf->buf);
-               }
-               else
-               {
-                       stream->zstream.next_in = (Bytef *) In->Buf->buf;
-                       stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
+               if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
+                       if (In->ReadWritePointer != NULL)
+                       {
+                               stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
+                               stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
+                                       (In->ReadWritePointer - In->Buf->buf);
+                       }
+                       else
+                       {
+                               stream->zstream.next_in = (Bytef *) In->Buf->buf;
+                               stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
+                       }
                }
 
                stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
                stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
 
-               err = deflate(&stream->zstream,  (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
+               err = inflate(&stream->zstream, Z_NO_FLUSH);
+
+               ///assert(ret != Z_STREAM_ERROR);  /* state not clobbered * /
+               switch (err) {
+               case Z_NEED_DICT:
+                       err = Z_DATA_ERROR;     /* and fall through */
+
+               case Z_DATA_ERROR:
+                       *Err = zError(err);
+               case Z_MEM_ERROR:
+                       (void)inflateEnd(&stream->zstream);
+                       return err;
+               }
 
                stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
 
-               if (Target) SwapBuffers(Target->Buf, &stream->OutBuf);
+               if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
 
                if (stream->zstream.avail_in == 0)
                {
@@ -3125,6 +3156,7 @@ void StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, con
        }
                break; /// TODO
        }
+       return rc;
 }
 
 /**
@@ -3741,7 +3773,7 @@ TRYAGAIN:
                TmpBuf->buf[TmpBuf->BufUsed] = '\0';
                
                /* little card game: wheres the red lady? */
-               SwapBuffers(ConvertBuf, TmpBuf);
+               iSwapBuffers(ConvertBuf, TmpBuf);
                FlushStrBuf(TmpBuf);
        }
 #endif
@@ -4110,11 +4142,11 @@ long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
                        n++;
                        aptr++;
                }
-               if (n > maxlen) {
+               if (n >= maxlen) {
                        *aptr = '\0';
                        Buf->BufUsed = aptr - Buf->buf;
                        return Buf->BufUsed;
-               }                       
+               }
        }
        return Buf->BufUsed;