* Copyright 2011-2016, Haiku, Inc. All rights reserved.
* Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
*/
#include <mail_util.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#define __USE_GNU
#include <regex.h>
#include <ctype.h>
#include <errno.h>
#include <FindDirectory.h>
#include <List.h>
#include <Locker.h>
#include <parsedate.h>
#include <Path.h>
#include <String.h>
#include <UTF8.h>
#include <mail_encoding.h>
#include <AttributeUtilities.h>
#include <CharacterSet.h>
#include <CharacterSetRoster.h>
using namespace BPrivate;
#define CRLF "\r\n"
struct CharsetConversionEntry {
const char *charset;
uint32 flavor;
};
extern const CharsetConversionEntry mail_charsets[] = {
{"iso-8859-1", B_ISO1_CONVERSION},
{"iso-8859-2", B_ISO2_CONVERSION},
{"iso-8859-3", B_ISO3_CONVERSION},
{"iso-8859-4", B_ISO4_CONVERSION},
{"iso-8859-5", B_ISO5_CONVERSION},
{"iso-8859-6", B_ISO6_CONVERSION},
{"iso-8859-7", B_ISO7_CONVERSION},
{"iso-8859-8", B_ISO8_CONVERSION},
{"iso-8859-9", B_ISO9_CONVERSION},
{"iso-8859-10", B_ISO10_CONVERSION},
{"iso-8859-13", B_ISO13_CONVERSION},
{"iso-8859-14", B_ISO14_CONVERSION},
{"iso-8859-15", B_ISO15_CONVERSION},
{"shift_jis", B_SJIS_CONVERSION},
{"shift-jis", B_SJIS_CONVERSION},
{"iso-2022-jp", B_JIS_CONVERSION},
{"euc-jp", B_EUC_CONVERSION},
{"euc-kr", B_EUC_KR_CONVERSION},
{"ksc5601", B_EUC_KR_CONVERSION},
{"ks_c_5601-1987", B_EUC_KR_CONVERSION},
{"koi8-r", B_KOI8R_CONVERSION},
{"windows-1251",B_MS_WINDOWS_1251_CONVERSION},
{"windows-1252",B_MS_WINDOWS_CONVERSION},
{"dos-437", B_MS_DOS_CONVERSION},
{"dos-866", B_MS_DOS_866_CONVERSION},
{"x-mac-roman", B_MAC_ROMAN_CONVERSION},
{"big5", 24},
{"gb18030", 25},
{"gb2312", 25},
{"gbk", 25},
{"us-ascii", B_MAIL_US_ASCII_CONVERSION},
{"utf-8", B_MAIL_UTF8_CONVERSION },
{NULL, (uint32) -1}
};
static int32 gLocker = 0;
static size_t gNsub = 1;
static re_pattern_buffer gRe;
static re_pattern_buffer *gRebuf = NULL;
static unsigned char gTranslation[256];
static int
handle_non_rfc2047_encoding(char **buffer, size_t *bufferLength,
size_t *sourceLength)
{
char *string = *buffer;
int32 length = *sourceLength;
int32 i;
for (i = 0;i < length;i++)
if (string[i] & 0x80)
break;
if (i == length)
return false;
int32 singletons = 0,doubles = 0;
for (i = 0;i < length;i++)
{
if (string[i] & 0x80)
{
if ((string[i + 1] & 0x80) == 0)
singletons++;
else doubles++;
i++;
}
}
if (singletons != 0)
{
int32 state = 0;
int32 destLength = length * 4 + 1;
int32 destBufferLength = destLength;
char *dest = (char*)malloc(destLength);
if (dest == NULL)
return 0;
if (convert_to_utf8(B_ISO1_CONVERSION, string, &length,dest,
&destLength, &state) == B_OK) {
*buffer = dest;
*bufferLength = destBufferLength;
*sourceLength = destLength;
return true;
}
free(dest);
return false;
}
return true;
}
status_t
write_read_attr(BNode& node, read_flags flag)
{
if (node.WriteAttr(B_MAIL_ATTR_READ, B_INT32_TYPE, 0, &flag, sizeof(int32))
< 0)
return B_ERROR;
BString currentStatus;
if (node.ReadAttrString(B_MAIL_ATTR_STATUS, ¤tStatus) == B_OK
&& currentStatus.ICompare("New") != 0
&& currentStatus.ICompare("Read") != 0
&& currentStatus.ICompare("Seen") != 0) {
return B_OK;
}
BString statusString = flag == B_READ ? "Read"
: flag == B_SEEN ? "Seen" : "New";
if (node.WriteAttrString(B_MAIL_ATTR_STATUS, &statusString) < 0)
return B_ERROR;
return B_OK;
}
status_t
read_read_attr(BNode& node, read_flags& flag)
{
if (node.ReadAttr(B_MAIL_ATTR_READ, B_INT32_TYPE, 0, &flag, sizeof(int32))
== sizeof(int32))
return B_OK;
BString statusString;
if (node.ReadAttrString(B_MAIL_ATTR_STATUS, &statusString) == B_OK) {
if (statusString.ICompare("New") == 0)
flag = B_UNREAD;
else
flag = B_READ;
return B_OK;
}
return B_ERROR;
}
status_t
mail_convert_to_utf8(uint32 srcEncoding, const char *src, int32 *srcLen,
char *dst, int32 *dstLen, int32 *state, char substitute)
{
int32 copyAmount;
char *originalDst = dst;
status_t returnCode = -1;
if (srcEncoding == B_MAIL_UTF8_CONVERSION) {
copyAmount = *srcLen;
if (*dstLen < copyAmount)
copyAmount = *dstLen;
memcpy (dst, src, copyAmount);
*srcLen = copyAmount;
*dstLen = copyAmount;
returnCode = B_OK;
} else if (srcEncoding == B_MAIL_US_ASCII_CONVERSION) {
int32 i;
unsigned char letter;
copyAmount = *srcLen;
if (*dstLen < copyAmount)
copyAmount = *dstLen;
for (i = 0; i < copyAmount; i++) {
letter = *src++;
if (letter > 0x80U)
*dst++ = letter - 0x80U;
else if (letter == 0x80U)
*dst++ = substitute;
else
*dst++ = letter;
}
*srcLen = copyAmount;
*dstLen = copyAmount;
returnCode = B_OK;
} else
returnCode = convert_to_utf8 (srcEncoding, src, srcLen,
dst, dstLen, state, substitute);
if (returnCode == B_OK) {
int32 i;
for (i = 0; i < *dstLen; i++)
if (originalDst[i] == 0)
originalDst[i] = substitute;
}
return returnCode;
}
status_t
mail_convert_from_utf8(uint32 dstEncoding, const char *src, int32 *srcLen,
char *dst, int32 *dstLen, int32 *state, char substitute)
{
int32 copyAmount;
status_t errorCode;
int32 originalDstLen = *dstLen;
int32 tempDstLen;
int32 tempSrcLen;
if (dstEncoding == B_MAIL_UTF8_CONVERSION) {
copyAmount = *srcLen;
if (*dstLen < copyAmount)
copyAmount = *dstLen;
memcpy (dst, src, copyAmount);
*srcLen = copyAmount;
*dstLen = copyAmount;
return B_OK;
}
if (dstEncoding == B_MAIL_US_ASCII_CONVERSION) {
int32 characterLength;
int32 dstRemaining = *dstLen;
unsigned char letter;
int32 srcRemaining = *srcLen;
if (srcRemaining <= *state) {
*state -= srcRemaining;
*dstLen = 0;
return B_OK;
}
srcRemaining -= *state;
src += *state;
*state = 0;
while (true) {
if (srcRemaining <= 0 || dstRemaining <= 0)
break;
letter = *src;
if (letter < 0x80)
characterLength = 1;
else if (letter < 0xC0)
characterLength = 1;
else if (letter < 0xE0)
characterLength = 2;
else if (letter < 0xF0)
characterLength = 3;
else if (letter < 0xF8)
characterLength = 4;
else if (letter < 0xFC)
characterLength = 5;
else if (letter < 0xFE)
characterLength = 6;
else
characterLength = 1;
if (letter < 0x80)
*dst++ = *src;
else
*dst++ = substitute;
dstRemaining--;
if (srcRemaining < characterLength) {
*state = characterLength - srcRemaining;
srcRemaining = 0;
} else {
src += characterLength;
srcRemaining -= characterLength;
}
}
*srcLen = *srcLen - srcRemaining;
*dstLen = *dstLen - dstRemaining;
return B_OK;
}
errorCode = convert_from_utf8(dstEncoding, src, srcLen, dst, dstLen, state,
substitute);
if (errorCode != B_OK)
return errorCode;
if (dstEncoding != B_JIS_CONVERSION)
return B_OK;
tempDstLen = originalDstLen - *dstLen;
if (tempDstLen < 3)
return B_OK;
tempSrcLen = 1;
errorCode = convert_from_utf8(dstEncoding, "a", &tempSrcLen,
dst + *dstLen, &tempDstLen, state, substitute);
if (errorCode != B_OK)
return errorCode;
*dstLen += tempDstLen - 1 ;
return B_OK;
}
ssize_t
rfc2047_to_utf8(char **bufp, size_t *bufLen, size_t strLen)
{
char *head, *tail;
char *charset, *encoding, *end;
ssize_t ret = B_OK;
if (bufp == NULL || *bufp == NULL)
return -1;
char *string = *bufp;
if (handle_non_rfc2047_encoding(bufp,bufLen,&strLen))
return strLen;
if (strLen == 0)
strLen = strlen(*bufp);
char lastChar = (*bufp)[strLen];
(*bufp)[strLen] = '\0';
bool encodedWordFoundPreviously = false;
for (head = tail = string;
((charset = strstr(tail, "=?")) != NULL)
&& (((encoding = strchr(charset + 2, '?')) != NULL)
&& encoding[1] && (encoding[2] == '?') && encoding[3])
&& (end = strstr(encoding + 3, "?=")) != NULL;
tail = end)
{
bool nonSpaceFound = false;
for (int i = 0; i < charset-tail; i++) {
if (!isspace (tail[i])) {
nonSpaceFound = true;
break;
}
}
if (!encodedWordFoundPreviously || nonSpaceFound) {
if (string != tail && tail != charset)
memmove(string, tail, charset-tail);
string += charset-tail;
}
tail = charset;
encodedWordFoundPreviously = true;
charset += 2;
encoding += 1;
end += 2;
size_t cLen = encoding - 1 - charset;
bool base64encoded = toupper(*encoding) == 'B';
uint32 convertID = B_MAIL_NULL_CONVERSION;
char charsetName[cLen + 1];
memcpy(charsetName, charset, cLen);
charsetName[cLen] = '\0';
if (strcasecmp(charsetName, "us-ascii") == 0) {
convertID = B_MAIL_US_ASCII_CONVERSION;
} else if (strcasecmp(charsetName, "utf-8") == 0) {
convertID = B_MAIL_UTF8_CONVERSION;
} else {
const BCharacterSet* charSet
= BCharacterSetRoster::FindCharacterSetByName(charsetName);
if (charSet != NULL) {
convertID = charSet->GetConversionID();
}
}
if (convertID == B_MAIL_NULL_CONVERSION) {
if (string != tail && tail != end)
memmove(string, tail, end-tail);
string += end-tail;
continue;
}
char *src = encoding+2;
int32 srcLen = end - 2 - src;
srcLen = !base64encoded ? decode_qp(src, src, srcLen, 1)
: decode_base64(src, src, srcLen);
int32 dstLen = end-string + *bufLen-strLen;
char *dst = (char*)malloc(dstLen);
int32 cvLen = srcLen;
int32 convState = 0;
ret = mail_convert_to_utf8(convertID, src, &cvLen, dst, &dstLen,
&convState);
if (ret != B_OK) {
free(dst);
if (string != tail && tail != end)
memmove(string, tail, end-tail);
string += end-tail;
continue;
}
test data is screwed up. Whatever it is, Not Enough
Space is not the only cause of the below, so we just
assume it succeeds if it converts anything at all.
else if (cvLen < srcLen)
{
// not enough room to convert the data;
// grow *buf and retry
free(dst);
char *temp = (char*)realloc(*bufp, 2*(*bufLen + 1));
if (temp == NULL)
{
ret = B_NO_MEMORY;
break;
}
*bufp = temp;
*bufLen = 2*(*bufLen + 1);
string = *bufp + (string-head);
tail = *bufp + (tail-head);
charset = *bufp + (charset-head);
encoding = *bufp + (encoding-head);
end = *bufp + (end-head);
src = *bufp + (src-head);
head = *bufp;
continue;
}
*/
else {
if (dstLen > end-string) {
memmove(string+dstLen, end, strLen - (end-head) + 1);
strLen += string+dstLen - end;
end = string + dstLen;
}
memcpy(string, dst, dstLen);
string += dstLen;
free(dst);
continue;
}
}
size_t tailLen = strLen - (tail - head);
memmove(string, tail, tailLen+1);
string += tailLen;
(*bufp)[strLen] = lastChar;
return ret < B_OK ? ret : string-head;
}
ssize_t
utf8_to_rfc2047 (char **bufp, ssize_t length, uint32 charset, char encoding)
{
struct word {
BString originalWord;
BString convertedWord;
bool needsEncoding;
void ConvertWordToCharset (uint32 charset) {
int32 state = 0;
int32 originalLength = originalWord.Length();
int32 convertedLength = originalLength * 5 + 1;
char *convertedBuffer = convertedWord.LockBuffer (convertedLength);
mail_convert_from_utf8 (charset, originalWord.String(),
&originalLength, convertedBuffer, &convertedLength, &state);
for (int i = 0; i < convertedLength; i++) {
if ((convertedBuffer[i] & (1 << 7)) ||
(convertedBuffer[i] >= 0 && convertedBuffer[i] < 32)) {
needsEncoding = true;
break;
}
}
convertedWord.UnlockBuffer (convertedLength);
};
};
struct word *currentWord;
BList words;
const char *source = *bufp;
const char *bufEnd = *bufp + length;
const char *specialChars = "\"()<>@,";
while (source < bufEnd) {
currentWord = new struct word;
currentWord->needsEncoding = false;
int wordEnd = 0;
while (source + wordEnd < bufEnd && isspace (source[wordEnd]))
wordEnd++;
if (source + wordEnd < bufEnd &&
strchr (specialChars, source[wordEnd]) != NULL) {
wordEnd++;
} else {
while (source + wordEnd < bufEnd) {
if (isspace(source[wordEnd]) ||
strchr (specialChars, source[wordEnd]) != NULL)
break;
if (wordEnd > 51 &&
0xC0 == (0xC0 & (unsigned int) source[wordEnd])) {
currentWord->needsEncoding = true;
break;
}
wordEnd++;
}
}
currentWord->originalWord.SetTo (source, wordEnd);
currentWord->ConvertWordToCharset (charset);
words.AddItem(currentWord);
source += wordEnd;
}
struct word *run;
for (int32 i = 0; (currentWord = (struct word *) words.ItemAt (i)) != NULL; i++) {
if (!currentWord->needsEncoding)
continue;
for (int32 g = i+1; (run = (struct word *) words.ItemAt (g)) != NULL; g++) {
if (!run->needsEncoding)
break;
if ((currentWord->convertedWord.Length() + run->convertedWord.Length() <= 53)) {
currentWord->originalWord.Append (run->originalWord);
currentWord->ConvertWordToCharset (charset);
words.RemoveItem(g);
delete run;
g--;
} else
break;
}
}
BString rfc2047;
bool previousWordNeededEncoding = false;
const char *charset_dec = "none-bug";
for (int32 i = 0; mail_charsets[i].charset != NULL; i++) {
if (mail_charsets[i].flavor == charset) {
charset_dec = mail_charsets[i].charset;
break;
}
}
while ((currentWord = (struct word *)words.RemoveItem((int32)0)) != NULL) {
if ((encoding != quoted_printable && encoding != base64) ||
!currentWord->needsEncoding) {
rfc2047.Append (currentWord->convertedWord);
} else {
if (previousWordNeededEncoding)
rfc2047 << ' ';
else {
if (currentWord->originalWord.Length() > 1 &&
isspace (currentWord->originalWord[0])) {
rfc2047 << currentWord->originalWord[0];
currentWord->originalWord.Remove (0 , 1 );
currentWord->ConvertWordToCharset (charset);
}
}
char *encoded = NULL;
ssize_t encoded_len = 0;
int32 convertedLength = currentWord->convertedWord.Length ();
const char *convertedBuffer = currentWord->convertedWord.String ();
switch (encoding) {
case quoted_printable:
encoded = (char *) malloc (convertedLength * 3);
encoded_len = encode_qp (encoded, convertedBuffer, convertedLength, true );
break;
case base64:
encoded = (char *) malloc (convertedLength * 2);
encoded_len = encode_base64 (encoded, convertedBuffer, convertedLength, true );
break;
default:
encoded = (char *) convertedBuffer;
encoded_len = convertedLength;
break;
}
rfc2047 << "=?" << charset_dec << '?' << encoding << '?';
rfc2047.Append (encoded, encoded_len);
rfc2047 << "?=";
if (encoding == quoted_printable || encoding == base64)
free(encoded);
}
previousWordNeededEncoding = currentWord->needsEncoding;
delete currentWord;
}
free(*bufp);
ssize_t finalLength = rfc2047.Length ();
*bufp = (char *) (malloc (finalLength + 1));
memcpy (*bufp, rfc2047.String(), finalLength);
(*bufp)[finalLength] = 0;
return finalLength;
}
void
FoldLineAtWhiteSpaceAndAddCRLF(BString &string)
{
int inputLength = string.Length();
int lineStartIndex;
const int maxLineLength = 78;
BString output;
int splitIndex;
int tempIndex;
lineStartIndex = 0;
while (true) {
if (lineStartIndex + maxLineLength >= inputLength) {
if (lineStartIndex < inputLength) {
output.Insert (string, lineStartIndex ,
inputLength - lineStartIndex ,
output.Length() );
output.Append (CRLF);
}
break;
}
tempIndex = lineStartIndex + maxLineLength;
if (tempIndex > inputLength)
tempIndex = inputLength;
splitIndex = string.FindLast (", ", tempIndex);
if (splitIndex >= lineStartIndex)
splitIndex++;
if (splitIndex <= lineStartIndex)
splitIndex = string.FindLast (" ", tempIndex);
if (splitIndex <= lineStartIndex)
splitIndex = string.FindLast ("\t", tempIndex);
if (splitIndex <= lineStartIndex)
splitIndex = string.FindFirst (" ", lineStartIndex + 1);
if (splitIndex <= lineStartIndex)
splitIndex = string.FindFirst ("\t", lineStartIndex + 1);
if (splitIndex <= lineStartIndex) {
if (lineStartIndex < inputLength) {
output.Insert (string, lineStartIndex ,
inputLength - lineStartIndex ,
output.Length() );
output.Append (CRLF);
}
break;
}
output.Insert (string, lineStartIndex ,
splitIndex - lineStartIndex ,
output.Length() );
output.Append (CRLF);
lineStartIndex = splitIndex;
}
string.SetTo (output);
}
ssize_t
readfoldedline(FILE *file, char **buffer, size_t *buflen)
{
ssize_t len = buflen && *buflen ? *buflen : 0;
char * buf = buffer && *buffer ? *buffer : NULL;
ssize_t cnt = 0;
int c;
while (true) {
if (buf == NULL || cnt + 2 >= len) {
char *temp = (char *)realloc(buf, len + 64);
if (temp == NULL) {
cnt = ENOMEM;
break;
}
len += 64;
buf = temp;
}
if ((c = fgetc(file)) == EOF) {
if (ferror (file)) {
cnt = errno;
if (cnt >= 0)
cnt = -1;
} else {
if (cnt > 0) {
buf[cnt++] = '\n';
if (buf[cnt-2] == '\r') {
buf[cnt-2] = '\n';
--cnt;
}
}
}
break;
}
buf[cnt++] = c;
if (c == '\n') {
if (cnt >= 2 && buf[cnt-2] == '\r') {
buf[cnt-2] = '\n';
--cnt;
}
if (cnt <= 1)
break;
c = fgetc(file);
if (c == ' ' || c == '\t')
buf[cnt-1] = c;
else {
ungetc(c,file);
break;
}
}
}
if (buf != NULL && cnt >= 0)
buf[cnt] = '\0';
if (buffer)
*buffer = buf;
else if (buf)
free(buf);
if (buflen)
*buflen = len;
return cnt;
}
ssize_t
readfoldedline(BPositionIO &in, char **buffer, size_t *buflen)
{
ssize_t len = buflen && *buflen ? *buflen : 0;
char * buf = buffer && *buffer ? *buffer : NULL;
ssize_t cnt = 0;
char c;
status_t errorCode;
while (true) {
if (buf == NULL || cnt + 2 >= len) {
char *temp = (char *)realloc(buf, len + 64);
if (temp == NULL) {
cnt = ENOMEM;
break;
}
len += 64;
buf = temp;
}
errorCode = in.Read (&c,1);
if (errorCode != 1) {
if (errorCode < 0) {
cnt = errorCode;
} else {
if (cnt > 0) {
buf[cnt++] = '\n';
if (buf[cnt-2] == '\r') {
buf[cnt-2] = '\n';
--cnt;
}
}
}
break;
}
buf[cnt++] = c;
if (c == '\n') {
if (cnt >= 2 && buf[cnt-2] == '\r') {
buf[cnt-2] = '\n';
--cnt;
}
if (cnt <= 1)
break;
errorCode = in.Read(&c,1);
if (errorCode == 1) {
if (c == ' ' || c == '\t')
buf[cnt-1] = c;
else {
in.Seek(-1,SEEK_CUR);
break;
}
} else if (errorCode < 0) {
cnt = errorCode;
break;
} else
break;
}
}
if (buf != NULL && cnt >= 0)
buf[cnt] = '\0';
if (buffer)
*buffer = buf;
else if (buf)
free(buf);
if (buflen)
*buflen = len;
return cnt;
}
ssize_t
nextfoldedline(const char** header, char **buffer, size_t *buflen)
{
ssize_t len = buflen && *buflen ? *buflen : 0;
char * buf = buffer && *buffer ? *buffer : NULL;
ssize_t cnt = 0;
char c;
while (true)
{
if (buf == NULL || cnt + 2 >= len)
{
char *temp = (char *)realloc(buf, len + 64);
if (temp == NULL) {
cnt = ENOMEM;
break;
}
len += 64;
buf = temp;
}
if ((c = *(*header)++) == 0) {
if (cnt > 0) {
buf[cnt++] = '\n';
if (buf[cnt-2] == '\r') {
buf[cnt-2] = '\n';
--cnt;
}
}
break;
}
buf[cnt++] = c;
if (c == '\n') {
if (cnt >= 2 && buf[cnt-2] == '\r') {
buf[cnt-2] = '\n';
--cnt;
}
if (cnt <= 1)
break;
c = *(*header)++;
if (c == ' ' || c == '\t')
buf[cnt-1] = c;
else {
(*header)--;
break;
}
}
}
if (buf != NULL && cnt >= 0)
buf[cnt] = '\0';
if (buffer)
*buffer = buf;
else if (buf)
free(buf);
if (buflen)
*buflen = len;
return cnt;
}
void
trim_white_space(BString &string)
{
int32 i;
int32 length = string.Length();
char *buffer = string.LockBuffer(length + 1);
while (length > 0 && isspace(buffer[length - 1]))
length--;
buffer[length] = '\0';
for (i = 0; buffer[i] && isspace(buffer[i]); i++) {}
if (i != 0) {
length -= i;
memmove(buffer,buffer + i,length + 1);
}
string.UnlockBuffer(length);
}
header parameter (should be from "To:" or "From:").
Tries to return the name rather than the eMail address.
*/
void
extract_address_name(BString &header)
{
BString name;
const char *start = header.String();
const char *stop = start + strlen (start);
for (int i = 0; i <= 3; i++) {
const char *p1 = NULL, *p2 = NULL;
switch (i) {
case 0:
if ((p1 = strchr(start,'(')) != NULL) {
p1++;
size_t nest = 1;
for (p2 = p1; p2 < stop; ++p2)
{
if (*p2 == ')')
--nest;
else if (*p2 == '(')
++nest;
if (nest <= 0)
break;
}
if (nest != 0)
p2 = NULL;
}
break;
case 1:
if ((p1 = strchr(start, '\"')) != NULL)
p2 = strchr(++p1, '\"');
break;
case 2:
p1 = start;
if (name.Length() == 0)
p2 = strchr(start, '<');
break;
case 3:
p1 = start;
if (name.Length() == 0)
p2 = stop;
break;
}
if (p2 != NULL) {
while (p1 < p2 && (isspace (*p1)))
++p1;
while (p1 < p2 && (isspace (p2[-1])))
--p2;
int newLength = p2 - p1;
if (name.Length() < newLength)
name.SetTo(p1, newLength);
}
}
int32 lessIndex = name.FindFirst('<');
int32 greaterIndex = name.FindLast('>');
if (lessIndex == 0) {
if (greaterIndex > 0)
name.Remove(greaterIndex, 1);
name.Remove(lessIndex, 1);
} else if (lessIndex > 0 && lessIndex < greaterIndex) {
name.Remove(lessIndex, greaterIndex - lessIndex + 1);
}
trim_white_space(name);
header = name;
}
to get down to the core subject string, which should be identical for all
messages posted about a topic. The input string is modified in place to
become the output core subject string.
*/
void
SubjectToThread (BString &string)
{
#define U8C \
"[\302-\337][\200-\277]" \
"|\340[\302-\337][\200-\277]" \
"|[\341-\357][\200-\277][\200-\277]" \
"|\360[\220-\277][\200-\277][\200-\277]" \
"|[\361-\367][\200-\277][\200-\277][\200-\277]" \
"|\370[\210-\277][\200-\277][\200-\277][\200-\277]" \
"|[\371-\373][\200-\277][\200-\277][\200-\277][\200-\277]" \
"|\374[\204-\277][\200-\277][\200-\277][\200-\277][\200-\277]" \
"|\375[\200-\277][\200-\277][\200-\277][\200-\277][\200-\277]"
#define PATTERN \
"^ +" \
"|^(\\[[^]]*\\])(\\<| +| *(\\<(\\w|" U8C "){2,3} *(\\[[^\\]]*\\])? *:)+ *)" \
"|^( +| *(\\<(\\w|" U8C "){2,3} *(\\[[^\\]]*\\])? *:)+ *)" \
"| *\\(fwd\\) *$"
if (gRebuf == NULL && atomic_add(&gLocker, 1) == 0) {
for (int i=0; i<256; ++i) gTranslation[i]=i;
for (int i='a'; i<='z'; ++i) gTranslation[i]=toupper(i);
gRe.translate = gTranslation;
gRe.regs_allocated = REGS_FIXED;
re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
const char *pattern = PATTERN;
for (unsigned int i=0; pattern[i] != 0; ++i)
{
if (pattern[i] == '\\')
++i;
else if (pattern[i] == '(')
++gNsub;
}
const char *err = re_compile_pattern(pattern,strlen(pattern),&gRe);
if (err == NULL)
gRebuf = &gRe;
else
fprintf(stderr, "Failed to compile the regex: %s\n", err);
} else {
int32 tries = 200;
while (gRebuf == NULL && tries-- > 0)
snooze(10000);
}
if (gRebuf) {
struct re_registers regs;
regs.num_regs = gNsub;
regs.start = (regoff_t*)malloc(gNsub*sizeof(regoff_t));
regs.end = (regoff_t*)malloc(gNsub*sizeof(regoff_t));
for (int start = 0; (start = re_search(gRebuf, string.String(),
string.Length(), 0, string.Length(), ®s)) >= 0;) {
if (start == regs.start[1])
start = regs.start[2];
string.Remove(start,regs.end[0]-start);
if (start)
string.Insert(' ',1,start);
if (regs.end[0] - start <= 1)
break;
}
free(regs.start);
free(regs.end);
}
trim_white_space(string);
}
parsedate(). Returns -1 if it fails.
*/
time_t
ParseDateWithTimeZone(const char *DateString)
{
time_t currentTime;
time_t dateAsTime;
char tempDateString[80];
char tempZoneString[6];
time_t zoneDeltaTime;
int zoneIndex;
char *zonePntr;
strncpy (tempDateString, DateString, sizeof (tempDateString));
tempDateString[sizeof (tempDateString) - 1] = 0;
zonePntr = tempDateString + strlen (tempDateString) - 1;
while (zonePntr >= tempDateString && isspace (*zonePntr))
*zonePntr-- = 0;
if (zonePntr < tempDateString)
return -1;
if (tempDateString[strlen(tempDateString)-1] == ')')
{
zonePntr = strrchr (tempDateString, '(');
if (zonePntr != NULL)
{
*zonePntr-- = 0;
while (zonePntr >= tempDateString && isspace (*zonePntr))
*zonePntr-- = 0;
if (zonePntr < tempDateString)
return -1;
}
}
for (zoneIndex = strlen (tempDateString); zoneIndex >= 0; zoneIndex--)
{
zonePntr = tempDateString + zoneIndex;
if (zonePntr[0] == '+' || zonePntr[0] == '-')
{
if (zonePntr[1] >= '0' && zonePntr[1] <= '9' &&
zonePntr[2] >= '0' && zonePntr[2] <= '9' &&
zonePntr[3] >= '0' && zonePntr[3] <= '9' &&
zonePntr[4] >= '0' && zonePntr[4] <= '9')
break;
}
}
if (zoneIndex >= 0)
{
memcpy (tempZoneString, zonePntr, 5);
tempZoneString [5] = 0;
strcpy (zonePntr, "GMT");
}
else
strcpy (tempZoneString, "+0000");
time (¤tTime);
dateAsTime = parsedate (tempDateString, currentTime);
if (dateAsTime == (time_t) -1)
return -1;
zoneDeltaTime = 60 * atol (tempZoneString + 3);
tempZoneString[3] = 0;
zoneDeltaTime += atol (tempZoneString + 1) * 60 * 60;
if (tempZoneString[0] == '+')
zoneDeltaTime = 0 - zoneDeltaTime;
dateAsTime += zoneDeltaTime;
return dateAsTime;
}
*/
status_t
parse_header(BMessage &headers, BPositionIO &input)
{
char *buffer = NULL;
size_t bufferSize = 0;
int32 length;
while ((length = readfoldedline(input, &buffer, &bufferSize)) >= 2) {
--length;
length = rfc2047_to_utf8(&buffer, &bufferSize, length);
buffer[length] = '\0';
const char *delimiter = strstr(buffer, ":");
if (delimiter == NULL)
continue;
BString header(buffer, delimiter - buffer);
header.CapitalizeEachWord();
delimiter++;
while (isspace(*delimiter))
delimiter++;
headers.AddString(header.String(), delimiter);
}
free(buffer);
return B_OK;
}
status_t
extract_from_header(const BString& header, const BString& field,
BString& target)
{
int32 headerLength = header.Length();
int32 fieldEndPos = 0;
while (true) {
int32 pos = header.IFindFirst(field, fieldEndPos);
if (pos < 0)
return B_BAD_VALUE;
fieldEndPos = pos + field.Length();
if (pos != 0 && header.ByteAt(pos - 1) != '\n')
continue;
if (header.ByteAt(fieldEndPos) == ':')
break;
}
fieldEndPos++;
int32 crPos = fieldEndPos;
while (true) {
fieldEndPos = crPos;
crPos = header.FindFirst('\n', crPos);
if (crPos < 0)
crPos = headerLength;
BString temp;
header.CopyInto(temp, fieldEndPos, crPos - fieldEndPos);
if (header.ByteAt(crPos - 1) == '\r') {
temp.Truncate(temp.Length() - 1);
temp += " ";
}
target += temp;
crPos++;
if (crPos >= headerLength)
break;
char nextByte = header.ByteAt(crPos);
if (nextByte != ' ' && nextByte != '\t')
break;
crPos++;
}
size_t bufferSize = target.Length();
char* buffer = target.LockBuffer(bufferSize);
size_t length = rfc2047_to_utf8(&buffer, &bufferSize, bufferSize);
target.UnlockBuffer(length);
trim_white_space(target);
return B_OK;
}
void
extract_address(BString &address)
{
const char *string = address.String();
int32 first;
if ((first = address.FindFirst('"')) >= 0) {
int32 last = first + 1;
while (string[last] && string[last] != '"')
last++;
if (string[last] == '"')
address.Remove(first, last + 1 - first);
}
if ((first = address.FindFirst('<')) >= 0) {
int32 last = address.FindFirst('>');
if (last >= 0) {
address.Truncate(last);
address.Remove(0, first + 1);
return;
}
}
if ((first = address.FindFirst('(')) >= 0) {
int32 last = first + 1;
while (string[last] && string[last] != ')')
last++;
if (string[last] == ')')
address.Remove(first, last + 1 - first);
}
trim_white_space(address);
}
void
get_address_list(BList &list, const char *string,
void (*cleanupFunc)(BString &))
{
if (string == NULL || !string[0])
return;
const char *start = string;
while (true) {
if (string[0] == '"') {
const char *quoteEnd = ++string;
while (quoteEnd[0] && quoteEnd[0] != '"')
quoteEnd++;
if (!quoteEnd[0])
quoteEnd = string;
string = quoteEnd + 1;
}
if (string[0] == ',' || string[0] == '\0') {
BString address(start, string - start);
trim_white_space(address);
if (cleanupFunc)
cleanupFunc(address);
list.AddItem(strdup(address.String()));
start = string + 1;
}
if (!string[0])
break;
string++;
}
}
status_t
CopyMailFolderAttributes(const char* targetPath)
{
BPath path;
status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
if (status != B_OK)
return status;
path.Append("Tracker");
path.Append("DefaultQueryTemplates");
path.Append("text_x-email");
BNode source(path.Path());
BNode target(targetPath);
return BPrivate::CopyAttributes(source, target);
}