+ *bptr = '\0';
+ if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
+ return -1;
+ return Target->BufUsed;
+}
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief Append a string, escaping characters which have meaning in HTML + json.
+ *
+ * @param Target target buffer
+ * @param Source source buffer; set to NULL if you just have a C-String
+ * @param PlainIn Plain-C string to append; set to NULL if unused
+ * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
+ * @param nolinebreaks if set to 1, linebreaks are removed from the string.
+ * if set to 2, linebreaks are replaced by <br/>
+ */
+long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
+{
+ const char *aptr, *eiptr;
+ char *bptr, *eptr;
+ long len;
+ int IsUtf8Sequence = 0;
+
+ if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
+ return -1;
+
+ if (PlainIn != NULL) {
+ aptr = PlainIn;
+ len = strlen(PlainIn);
+ eiptr = aptr + len;
+ }
+ else {
+ aptr = Source->buf;
+ eiptr = aptr + Source->BufUsed;
+ len = Source->BufUsed;
+ }
+
+ if (len == 0)
+ return -1;
+
+ bptr = Target->buf + Target->BufUsed;
+ eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
+
+ while (aptr < eiptr){
+ if(bptr >= eptr) {
+ IncreaseBuf(Target, 1, -1);
+ eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
+ bptr = Target->buf + Target->BufUsed;
+ }
+ switch (*aptr) {
+ case '<':
+ memcpy(bptr, HKEY("<"));
+ bptr += 4;
+ Target->BufUsed += 4;
+ break;
+ case '>':
+ memcpy(bptr, HKEY(">"));
+ bptr += 4;
+ Target->BufUsed += 4;
+ break;
+ case '&':
+ memcpy(bptr, HKEY("&"));
+ bptr += 5;
+ Target->BufUsed += 5;
+ break;
+ case LB:
+ *bptr = '<';
+ bptr ++;
+ Target->BufUsed ++;
+ break;
+ case RB:
+ *bptr = '>';
+ bptr ++;
+ Target->BufUsed ++;
+ break;
+ case '\n':
+ switch (nolinebreaks) {
+ case 1:
+ *bptr='\0'; /* nothing */
+ break;
+ case 2:
+ memcpy(bptr, HKEY("<br/>"));
+ bptr += 11;
+ Target->BufUsed += 11;
+ break;
+ default:
+ memcpy(bptr, HKEY("\\n"));
+ bptr += 2;
+ Target->BufUsed += 2;
+ }
+ break;
+ case '\r':
+ switch (nolinebreaks) {
+ case 1:
+ case 2:
+ *bptr='\0'; /* nothing */
+ break;
+ default:
+ memcpy(bptr, HKEY("\\r"));
+ bptr += 2;
+ Target->BufUsed += 2;
+ break;
+ }
+ break;
+ case '"':
+ case QU:
+ *bptr = '\\';
+ bptr ++;
+ *bptr = '"';
+ bptr ++;
+ Target->BufUsed += 2;
+ break;
+ case '\\':
+ if ((*(aptr + 1) == 'u') &&
+ isxdigit(*(aptr + 2)) &&
+ isxdigit(*(aptr + 3)) &&
+ isxdigit(*(aptr + 4)) &&
+ isxdigit(*(aptr + 5)))
+ { /* oh, a unicode escaper. let it pass through. */
+ memcpy(bptr, aptr, 6);
+ aptr += 5;
+ bptr +=6;
+ Target->BufUsed += 6;
+ }
+ else
+ {
+ *bptr = '\\';
+ bptr ++;
+ *bptr = '\\';
+ bptr ++;
+ Target->BufUsed += 2;
+ }
+ break;
+ case '\b':
+ *bptr = '\\';
+ bptr ++;
+ *bptr = 'b';
+ bptr ++;
+ Target->BufUsed += 2;
+ break;
+ case '\f':
+ *bptr = '\\';
+ bptr ++;
+ *bptr = 'f';
+ bptr ++;
+ Target->BufUsed += 2;
+ break;
+ case '\t':
+ *bptr = '\\';
+ bptr ++;
+ *bptr = 't';
+ bptr ++;
+ Target->BufUsed += 2;
+ break;
+ case 32:
+ if (nbsp == 1) {
+ memcpy(bptr, HKEY(" "));
+ bptr += 6;
+ Target->BufUsed += 6;
+ break;
+ }
+ default:
+ IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
+ while (IsUtf8Sequence > 0){
+ *bptr = *aptr;
+ Target->BufUsed ++;
+ if (--IsUtf8Sequence)
+ aptr++;
+ bptr++;
+ }
+ }
+ aptr ++;
+ }
+ *bptr = '\0';
+ if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
+ return -1;
+ return Target->BufUsed;
+}
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief unhide special chars hidden to the HTML escaper
+ * @param target buffer to put the unescaped string in
+ * @param source buffer to unescape
+ */
+void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
+{
+ int a, b, len;
+ char hex[3];
+
+ if (target != NULL)
+ FlushStrBuf(target);
+
+ if (source == NULL ||target == NULL)
+ {
+ return;
+ }
+
+ len = source->BufUsed;
+ for (a = 0; a < len; ++a) {
+ if (target->BufUsed >= target->BufSize)
+ IncreaseBuf(target, 1, -1);
+
+ if (source->buf[a] == '=') {
+ hex[0] = source->buf[a + 1];
+ hex[1] = source->buf[a + 2];
+ hex[2] = 0;
+ b = 0;
+ sscanf(hex, "%02x", &b);
+ target->buf[target->BufUsed] = b;
+ target->buf[++target->BufUsed] = 0;
+ a += 2;
+ }
+ else {
+ target->buf[target->BufUsed] = source->buf[a];
+ target->buf[++target->BufUsed] = 0;
+ }
+ }
+}
+
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief hide special chars from the HTML escapers and friends
+ * @param target buffer to put the escaped string in
+ * @param source buffer to escape
+ */
+void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
+{
+ int i, len;
+
+ if (target != NULL)
+ FlushStrBuf(target);
+
+ if (source == NULL ||target == NULL)
+ {
+ return;
+ }
+
+ len = source->BufUsed;
+ for (i=0; i<len; ++i) {
+ if (target->BufUsed + 4 >= target->BufSize)
+ IncreaseBuf(target, 1, -1);
+ if ( (isalnum(source->buf[i])) ||
+ (source->buf[i]=='-') ||
+ (source->buf[i]=='_') ) {
+ target->buf[target->BufUsed++] = source->buf[i];
+ }
+ else {
+ sprintf(&target->buf[target->BufUsed],
+ "=%02X",
+ (0xFF &source->buf[i]));
+ target->BufUsed += 3;
+ }
+ }
+ target->buf[target->BufUsed + 1] = '\0';
+}
+
+
+/*******************************************************************************
+ * Quoted Printable de/encoding *
+ *******************************************************************************/
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief decode a buffer from base 64 encoding; destroys original
+ * @param Buf Buffor to transform
+ */
+int StrBufDecodeBase64(StrBuf *Buf)
+{
+ char *xferbuf;
+ size_t siz;
+ if (Buf == NULL) return -1;
+
+ xferbuf = (char*) malloc(Buf->BufSize);
+ *xferbuf = '\0';
+ siz = CtdlDecodeBase64(xferbuf,
+ Buf->buf,
+ Buf->BufUsed);
+ free(Buf->buf);
+ Buf->buf = xferbuf;
+ Buf->BufUsed = siz;
+ return siz;
+}
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief decode a buffer from base 64 encoding; destroys original
+ * @param Buf Buffor to transform
+ */
+int StrBufDecodeHex(StrBuf *Buf)
+{
+ unsigned int ch;
+ char *pch, *pche, *pchi;
+
+ if (Buf == NULL) return -1;
+
+ pch = pchi = Buf->buf;
+ pche = pch + Buf->BufUsed;
+
+ while (pchi < pche){
+ ch = decode_hex(pchi);
+ *pch = ch;
+ pch ++;
+ pchi += 2;
+ }
+
+ *pch = '\0';
+ Buf->BufUsed = pch - Buf->buf;
+ return Buf->BufUsed;
+}
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief replace all chars >0x20 && < 0x7F with Mute
+ * @param Mute char to put over invalid chars
+ * @param Buf Buffor to transform
+ */
+int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
+{
+ unsigned char *pch;
+
+ if (Buf == NULL) return -1;
+ pch = (unsigned char *)Buf->buf;
+ while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
+ if ((*pch < 0x20) || (*pch > 0x7F))
+ *pch = Mute;
+ pch ++;
+ }
+ return Buf->BufUsed;
+}
+
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
+ * @param Buf Buffer to translate
+ * @param StripBlanks Reduce several blanks to one?
+ */
+long StrBufUnescape(StrBuf *Buf, int StripBlanks)
+{
+ int a, b;
+ char hex[3];
+ long len;
+
+ if (Buf == NULL)
+ return -1;
+
+ while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
+ Buf->buf[Buf->BufUsed - 1] = '\0';
+ Buf->BufUsed --;
+ }
+
+ a = 0;
+ while (a < Buf->BufUsed) {
+ if (Buf->buf[a] == '+')
+ Buf->buf[a] = ' ';
+ else if (Buf->buf[a] == '%') {
+ /* don't let % chars through, rather truncate the input. */
+ if (a + 2 > Buf->BufUsed) {
+ Buf->buf[a] = '\0';
+ Buf->BufUsed = a;
+ }
+ else {
+ hex[0] = Buf->buf[a + 1];
+ hex[1] = Buf->buf[a + 2];
+ hex[2] = 0;
+ b = 0;
+ sscanf(hex, "%02x", &b);
+ Buf->buf[a] = (char) b;
+ len = Buf->BufUsed - a - 2;
+ if (len > 0)
+ memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
+
+ Buf->BufUsed -=2;
+ }
+ }
+ a++;
+ }
+ return a;
+}
+
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief RFC2047-encode a header field if necessary.
+ * If no non-ASCII characters are found, the string
+ * will be copied verbatim without encoding.
+ *
+ * @param target Target buffer.
+ * @param source Source string to be encoded.
+ * @returns encoded length; -1 if non success.
+ */
+int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
+{
+ const char headerStr[] = "=?UTF-8?Q?";
+ int need_to_encode = 0;
+ int i = 0;
+ unsigned char ch;
+
+ if ((source == NULL) ||
+ (target == NULL))
+ return -1;
+
+ while ((i < source->BufUsed) &&
+ (!IsEmptyStr (&source->buf[i])) &&
+ (need_to_encode == 0)) {
+ if (((unsigned char) source->buf[i] < 32) ||
+ ((unsigned char) source->buf[i] > 126)) {
+ need_to_encode = 1;
+ }
+ i++;
+ }
+
+ if (!need_to_encode) {
+ if (*target == NULL) {
+ *target = NewStrBufPlain(source->buf, source->BufUsed);
+ }
+ else {
+ FlushStrBuf(*target);
+ StrBufAppendBuf(*target, source, 0);
+ }
+ return (*target)->BufUsed;
+ }
+ if (*target == NULL)
+ *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
+ else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
+ IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
+ memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
+ (*target)->BufUsed = sizeof(headerStr) - 1;
+ for (i=0; (i < source->BufUsed); ++i) {
+ if ((*target)->BufUsed + 4 >= (*target)->BufSize)
+ IncreaseBuf(*target, 1, 0);
+ ch = (unsigned char) source->buf[i];
+ if ((ch < 32) ||
+ (ch > 126) ||
+ (ch == 61) ||
+ (ch == '=') ||
+ (ch == '?') ||
+ (ch == '_') ||
+ (ch == '[') ||
+ (ch == ']') )
+ {
+ sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
+ (*target)->BufUsed += 3;
+ }
+ else {
+ if (ch == ' ')
+ (*target)->buf[(*target)->BufUsed] = '_';
+ else
+ (*target)->buf[(*target)->BufUsed] = ch;
+ (*target)->BufUsed++;
+ }
+ }
+
+ if ((*target)->BufUsed + 4 >= (*target)->BufSize)
+ IncreaseBuf(*target, 1, 0);
+
+ (*target)->buf[(*target)->BufUsed++] = '?';
+ (*target)->buf[(*target)->BufUsed++] = '=';
+ (*target)->buf[(*target)->BufUsed] = '\0';
+ return (*target)->BufUsed;;
+}
+
+
+
+static void AddRecipient(StrBuf *Target,
+ StrBuf *UserName,
+ StrBuf *EmailAddress,
+ StrBuf *EncBuf)
+{
+ int QuoteMe = 0;
+
+ if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
+ if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
+
+ if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
+ StrBufRFC2047encode(&EncBuf, UserName);
+ StrBufAppendBuf(Target, EncBuf, 0);
+ if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
+ else StrBufAppendBufPlain(Target, HKEY(" "), 0);
+
+ if (StrLength(EmailAddress) > 0){
+ StrBufAppendBufPlain(Target, HKEY("<"), 0);
+ StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
+ StrBufAppendBufPlain(Target, HKEY(">"), 0);
+ }
+}
+
+
+/**
+ * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
+ * \param Recp Source list of email recipients
+ * \param UserName Temporary buffer for internal use; Please provide valid buffer.
+ * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
+ * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
+ * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
+ */
+StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
+ StrBuf *UserName,
+ StrBuf *EmailAddress,
+ StrBuf *EncBuf)
+{
+ StrBuf *Target;
+ const char *pch, *pche;
+ const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
+
+ if ((Recp == NULL) || (StrLength(Recp) == 0))
+ return NULL;
+
+ pch = ChrPtr(Recp);
+ pche = pch + StrLength(Recp);
+
+ if (!CheckEncode(pch, -1, pche))
+ return NewStrBufDup(Recp);
+
+ Target = NewStrBufPlain(NULL, StrLength(Recp));
+
+ while ((pch != NULL) && (pch < pche))
+ {
+ while (isspace(*pch)) pch++;
+ UserStart = UserEnd = EmailStart = EmailEnd = NULL;
+
+ if ((*pch == '"') || (*pch == '\'')) {
+ UserStart = pch + 1;
+
+ UserEnd = strchr(UserStart, *pch);
+ if (UserEnd == NULL)
+ break; ///TODO: Userfeedback??
+ EmailStart = UserEnd + 1;
+ while (isspace(*EmailStart))
+ EmailStart++;
+ if (UserEnd == UserStart) {
+ UserStart = UserEnd = NULL;
+ }
+
+ if (*EmailStart == '<') {
+ EmailStart++;
+ EmailEnd = strchr(EmailStart, '>');
+ if (EmailEnd == NULL)
+ EmailEnd = strchr(EmailStart, ',');
+
+ }
+ else {
+ EmailEnd = strchr(EmailStart, ',');
+ }
+ if (EmailEnd == NULL)
+ EmailEnd = pche;
+ pch = EmailEnd + 1;
+ }
+ else {
+ int gt = 0;
+ UserStart = pch;
+ EmailEnd = strchr(UserStart, ',');
+ if (EmailEnd == NULL) {
+ EmailEnd = strchr(pch, '>');
+ pch = NULL;
+ if (EmailEnd != NULL) {
+ gt = 1;
+ }
+ else {
+ EmailEnd = pche;
+ }
+ }
+ else {
+
+ pch = EmailEnd + 1;
+ while ((EmailEnd > UserStart) && !gt &&
+ ((*EmailEnd == ',') ||
+ (*EmailEnd == '>') ||
+ (isspace(*EmailEnd))))
+ {
+ if (*EmailEnd == '>')
+ gt = 1;
+ else
+ EmailEnd--;
+ }
+ if (EmailEnd == UserStart)
+ break;
+ }
+ if (gt) {
+ EmailStart = strchr(UserStart, '<');
+ if ((EmailStart == NULL) || (EmailStart > EmailEnd))
+ break;
+ UserEnd = EmailStart;
+
+ while ((UserEnd > UserStart) &&
+ isspace (*(UserEnd - 1)))
+ UserEnd --;
+ EmailStart ++;
+ if (UserStart >= UserEnd)
+ UserStart = UserEnd = NULL;
+ At = strchr(EmailStart, '@');
+ }
+ else { /* this is a local recipient... no domain, just a realname */
+ EmailStart = UserStart;
+ At = strchr(EmailStart, '@');
+ if (At == NULL) {
+ UserEnd = EmailEnd;
+ EmailEnd = NULL;
+ }
+ else {
+ EmailStart = UserStart;
+ UserStart = NULL;
+ }
+ }
+ }
+
+ if ((UserStart != NULL) && (UserEnd != NULL))
+ StrBufPlain(UserName, UserStart, UserEnd - UserStart);
+ else if ((UserStart != NULL) && (UserEnd == NULL))
+ StrBufPlain(UserName, UserStart, UserEnd - UserStart);
+ else
+ FlushStrBuf(UserName);
+
+ if ((EmailStart != NULL) && (EmailEnd != NULL))
+ StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
+ else if ((EmailStart != NULL) && (EmailEnd == NULL))
+ StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
+ else
+ FlushStrBuf(EmailAddress);
+
+ AddRecipient(Target, UserName, EmailAddress, EncBuf);
+
+ if (pch == NULL)
+ break;
+
+ if ((pch != NULL) && (*pch == ','))
+ pch ++;
+ if (pch != NULL) while (isspace(*pch))
+ pch ++;
+ }
+ return Target;
+}
+
+
+/**
+ * @ingroup StrBuf
+ * @brief replaces all occurances of 'search' by 'replace'
+ * @param buf Buffer to modify
+ * @param search character to search
+ * @param replace character to replace search by
+ */
+void StrBufReplaceChars(StrBuf *buf, char search, char replace)
+{
+ long i;
+ if (buf == NULL)
+ return;
+ for (i=0; i<buf->BufUsed; i++)
+ if (buf->buf[i] == search)
+ buf->buf[i] = replace;
+
+}
+
+/**
+ * @ingroup StrBuf
+ * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
+ * @param buf Buffer to modify
+ */
+void StrBufToUnixLF(StrBuf *buf)
+{
+ char *pche, *pchS, *pchT;
+ if (buf == NULL)
+ return;
+
+ pche = buf->buf + buf->BufUsed;
+ pchS = pchT = buf->buf;
+ while (pchS < pche)
+ {
+ if (*pchS == '\r')
+ {
+ pchS ++;
+ if (*pchS != '\n') {
+ *pchT = '\n';
+ pchT++;
+ }
+ }
+ *pchT = *pchS;
+ pchT++; pchS++;
+ }
+ *pchT = '\0';
+ buf->BufUsed = pchT - buf->buf;
+}
+
+
+/*******************************************************************************
+ * Iconv Wrapper; RFC822 de/encoding *
+ *******************************************************************************/
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief Wrapper around iconv_open()
+ * Our version adds aliases for non-standard Microsoft charsets
+ * such as 'MS950', aliasing them to names like 'CP950'
+ *
+ * @param tocode Target encoding
+ * @param fromcode Source encoding
+ * @param pic anonimized pointer to iconv struct
+ */
+void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
+{
+#ifdef HAVE_ICONV
+ iconv_t ic = (iconv_t)(-1) ;
+ ic = iconv_open(tocode, fromcode);
+ if (ic == (iconv_t)(-1) ) {
+ char alias_fromcode[64];
+ if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
+ safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
+ alias_fromcode[0] = 'C';
+ alias_fromcode[1] = 'P';
+ ic = iconv_open(tocode, alias_fromcode);
+ }
+ }
+ *(iconv_t *)pic = ic;
+#endif
+}
+
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief find one chunk of a RFC822 encoded string
+ * @param Buffer where to search
+ * @param bptr where to start searching
+ * @returns found position, NULL if none.
+ */
+static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
+{
+ const char * end;
+ /* Find the next ?Q? */
+ if (Buf->BufUsed - (bptr - Buf->buf) < 6)
+ return NULL;
+
+ end = strchr(bptr + 2, '?');
+
+ if (end == NULL)
+ return NULL;
+
+ if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
+ (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
+ ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
+ (*(end + 2) == '?')) {
+ /* skip on to the end of the cluster, the next ?= */
+ end = strstr(end + 3, "?=");
+ }
+ else
+ /* sort of half valid encoding, try to find an end. */
+ end = strstr(bptr, "?=");
+ return end;
+}
+
+
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief convert one buffer according to the preselected iconv pointer PIC
+ * @param ConvertBuf buffer we need to translate
+ * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
+ * @param pic Pointer to the iconv-session Object
+ */
+void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
+{
+#ifdef HAVE_ICONV
+ long trycount = 0;
+ size_t siz;
+ iconv_t ic;
+ char *ibuf; /**< Buffer of characters to be converted */
+ char *obuf; /**< Buffer for converted characters */
+ size_t ibuflen; /**< Length of input buffer */
+ size_t obuflen; /**< Length of output buffer */
+
+
+ /* since we're converting to utf-8, one glyph may take up to 6 bytes */
+ if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
+ IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
+TRYAGAIN:
+ ic = *(iconv_t*)pic;
+ ibuf = ConvertBuf->buf;
+ ibuflen = ConvertBuf->BufUsed;
+ obuf = TmpBuf->buf;
+ obuflen = TmpBuf->BufSize;
+
+ siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
+
+ if (siz < 0) {
+ if (errno == E2BIG) {
+ trycount ++;
+ IncreaseBuf(TmpBuf, 0, 0);
+ if (trycount < 5)
+ goto TRYAGAIN;
+
+ }
+ else if (errno == EILSEQ){
+ /* hm, invalid utf8 sequence... what to do now? */
+ /* An invalid multibyte sequence has been encountered in the input */
+ }
+ else if (errno == EINVAL) {
+ /* An incomplete multibyte sequence has been encountered in the input. */
+ }
+
+ FlushStrBuf(TmpBuf);
+ }
+ else {
+ TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
+ TmpBuf->buf[TmpBuf->BufUsed] = '\0';
+
+ /* little card game: wheres the red lady? */
+ SwapBuffers(ConvertBuf, TmpBuf);
+ FlushStrBuf(TmpBuf);
+ }
+#endif
+}
+
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief catches one RFC822 encoded segment, and decodes it.
+ * @param Target buffer to fill with result
+ * @param DecodeMe buffer with stuff to process
+ * @param SegmentStart points to our current segment in DecodeMe
+ * @param SegmentEnd Points to the end of our current segment in DecodeMe
+ * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
+ * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
+ * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
+ */
+inline static void DecodeSegment(StrBuf *Target,
+ const StrBuf *DecodeMe,
+ const char *SegmentStart,
+ const char *SegmentEnd,
+ StrBuf *ConvertBuf,
+ StrBuf *ConvertBuf2,
+ StrBuf *FoundCharset)
+{
+ StrBuf StaticBuf;
+ char charset[128];
+ char encoding[16];
+#ifdef HAVE_ICONV
+ iconv_t ic = (iconv_t)(-1);
+#else
+ void *ic = NULL;
+#endif
+ /* Now we handle foreign character sets properly encoded
+ * in RFC2047 format.
+ */
+ StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
+ StaticBuf.BufUsed = SegmentEnd - SegmentStart;
+ StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
+ extract_token(charset, SegmentStart, 1, '?', sizeof charset);
+ if (FoundCharset != NULL) {
+ FlushStrBuf(FoundCharset);
+ StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
+ }
+ extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
+ StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
+
+ *encoding = toupper(*encoding);
+ if (*encoding == 'B') { /**< base64 */
+ if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
+ IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
+ ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
+ ConvertBuf->buf,
+ ConvertBuf->BufUsed);
+ }
+ else if (*encoding == 'Q') { /**< quoted-printable */
+ long pos;
+
+ pos = 0;
+ while (pos < ConvertBuf->BufUsed)
+ {
+ if (ConvertBuf->buf[pos] == '_')
+ ConvertBuf->buf[pos] = ' ';
+ pos++;
+ }
+
+ if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
+ IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
+
+ ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
+ ConvertBuf2->buf,
+ ConvertBuf->buf,
+ ConvertBuf->BufUsed);
+ }
+ else {
+ StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
+ }
+#ifdef HAVE_ICONV
+ ctdl_iconv_open("UTF-8", charset, &ic);
+ if (ic != (iconv_t)(-1) ) {
+#endif
+ StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
+ StrBufAppendBuf(Target, ConvertBuf2, 0);
+#ifdef HAVE_ICONV
+ iconv_close(ic);