diff --git a/headers/private/mail/mail_util.h b/headers/private/mail/mail_util.h index 205b2c9af1..021d069636 100644 --- a/headers/private/mail/mail_util.h +++ b/headers/private/mail/mail_util.h @@ -1,4 +1,5 @@ /* + * Copyright 2011, Haiku, Inc. All rights reserved. * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. */ #ifndef ZOIDBERG_GARGOYLE_MAIL_UTIL_H @@ -10,9 +11,10 @@ #include -#include - +#include #include +#include +#include // TODO: this should only be preserved for gcc2 compatibility @@ -60,7 +62,7 @@ ssize_t utf8_to_rfc2047(char **bufp, ssize_t length,uint32 charset, char encodin // Unidentified charsets and conversion errors cause // the offending text to be skipped. -void FoldLineAtWhiteSpaceAndAddCRLF (BString &string); +void FoldLineAtWhiteSpaceAndAddCRLF(BString &string); // Insert CRLF at various spots in the given string (before white space) so // that the line length is mostly under 78 bytes. Also makes sure there is a // CRLF at the very end. diff --git a/src/kits/mail/HaikuMailFormatFilter.cpp b/src/kits/mail/HaikuMailFormatFilter.cpp index 3462bc00be..7b711effe4 100644 --- a/src/kits/mail/HaikuMailFormatFilter.cpp +++ b/src/kits/mail/HaikuMailFormatFilter.cpp @@ -1,6 +1,7 @@ /* * Copyright 2011, Haiku, Inc. All rights reserved. * Copyright 2011, Clemens Zeidler + * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. * Distributed under the terms of the MIT License. */ @@ -17,10 +18,9 @@ struct mail_header_field { - const char *rfc_name; - - const char *attr_name; - type_code attr_type; + const char* rfc_name; + const char* attr_name; + type_code attr_type; // currently either B_STRING_TYPE and B_TIME_TYPE }; @@ -65,7 +65,7 @@ HaikuMailFormatFilter::HeaderFetched(const entry_ref& ref, BFile* file) file->Seek(0, SEEK_SET); BMessage attributes; - // TODO attributes.AddInt32(B_MAIL_ATTR_CONTENT, length); + // TODO: attributes.AddInt32(B_MAIL_ATTR_CONTENT, length); attributes.AddInt32(B_MAIL_ATTR_ACCOUNT_ID, fAccountID); attributes.AddString(B_MAIL_ATTR_ACCOUNT, fAccountName); @@ -83,6 +83,7 @@ HaikuMailFormatFilter::HeaderFetched(const entry_ref& ref, BFile* file) gDefaultFields[i].rfc_name, target); if (status != B_OK) continue; + switch (gDefaultFields[i].attr_type){ case B_STRING_TYPE: attributes.AddString(gDefaultFields[i].attr_name, target); @@ -111,8 +112,9 @@ HaikuMailFormatFilter::HeaderFetched(const entry_ref& ref, BFile* file) if (name.Length() <= 0) name = "No Subject"; attributes.AddString(B_MAIL_ATTR_THREAD, name); + // Avoid hidden files, starting with a dot. if (name[0] == '.') - name.Prepend ("_"); // Avoid hidden files, starting with a dot. + name.Prepend ("_"); // Convert the date into a year-month-day fixed digit width format, so that // sorting by file name will give all the messages with the same subject in @@ -120,23 +122,20 @@ HaikuMailFormatFilter::HeaderFetched(const entry_ref& ref, BFile* file) time_t dateAsTime = 0; const time_t* datePntr; ssize_t dateSize; - char numericDateString [40]; + char numericDateString[40]; struct tm timeFields; if (attributes.FindData(B_MAIL_ATTR_WHEN, B_TIME_TYPE, - (const void**)&datePntr, &dateSize) == B_OK) + (const void**)&datePntr, &dateSize) == B_OK) dateAsTime = *datePntr; localtime_r(&dateAsTime, &timeFields); - sprintf(numericDateString, "%04d%02d%02d%02d%02d%02d", - timeFields.tm_year + 1900, - timeFields.tm_mon + 1, - timeFields.tm_mday, - timeFields.tm_hour, - timeFields.tm_min, - timeFields.tm_sec); + snprintf(numericDateString, sizeof(numericDateString), + "%04d%02d%02d%02d%02d%02d", + timeFields.tm_year + 1900, timeFields.tm_mon + 1, timeFields.tm_mday, + timeFields.tm_hour, timeFields.tm_min, timeFields.tm_sec); name << " " << numericDateString; - BString worker = attributes.FindString("MAIL:from"); + BString worker = attributes.FindString(B_MAIL_ATTR_FROM); extract_address_name(worker); name << " " << worker; @@ -149,8 +148,9 @@ HaikuMailFormatFilter::HeaderFetched(const entry_ref& ref, BFile* file) name.ReplaceAll('!', '_'); name.ReplaceAll('<', '_'); name.ReplaceAll('>', '_'); - while (name.FindFirst(" ") >= 0) // Remove multiple spaces. - name.Replace(" " /* Old */, " " /* New */, 1024 /* Count */); + // Remove multiple spaces. + while (name.FindFirst(" ") >= 0) + name.Replace(" ", " ", 1024); worker = name; int32 identicalNumber = 1; @@ -162,10 +162,10 @@ HaikuMailFormatFilter::HeaderFetched(const entry_ref& ref, BFile* file) worker << "_" << identicalNumber; status = _SetFileName(ref, worker); } - if (status < B_OK) + if (status < B_OK) { printf("FolderFilter::ProcessMailMessage: could not rename mail (%s)! " "(should be: %s)\n",strerror(status), worker.String()); - else { + } else { entry_ref to(ref.device, ref.directory, worker); fMailProtocol.FileRenamed(ref, to); } diff --git a/src/kits/mail/MailProtocol.cpp b/src/kits/mail/MailProtocol.cpp index e494c711d5..54623d5e12 100644 --- a/src/kits/mail/MailProtocol.cpp +++ b/src/kits/mail/MailProtocol.cpp @@ -1,7 +1,7 @@ -/* BMailProtocol - the base class for protocol filters -** -** Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. -*/ +/* + * Copyright 2011, Haiku, Inc. All rights reserved. + * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. + */ #include @@ -35,56 +35,65 @@ using std::map; +const uint32 kMsgSyncMessages = '&SyM'; +const uint32 kMsgDeleteMessage = '&DeM'; +const uint32 kMsgAppendMessage = '&ApM'; + +const uint32 kMsgMoveFile = '&MoF'; +const uint32 kMsgDeleteFile = '&DeF'; +const uint32 kMsgFileRenamed = '&FiR'; +const uint32 kMsgFileDeleted = '&FDe'; +const uint32 kMsgInit = '&Ini'; + +const uint32 kMsgSendMessage = '&SeM'; + + MailFilter::MailFilter(MailProtocol& protocol, AddonSettings* settings) : fMailProtocol(protocol), fAddonSettings(settings) { - } MailFilter::~MailFilter() { - } void MailFilter::HeaderFetched(const entry_ref& ref, BFile* file) { - } void MailFilter::BodyFetched(const entry_ref& ref, BFile* file) { - } void MailFilter::MailboxSynced(status_t status) { - } void MailFilter::MessageReadyToSend(const entry_ref& ref, BFile* file) { - } void MailFilter::MessageSent(const entry_ref& ref, BFile* file) { - } +// #pragma mark - + + MailProtocol::MailProtocol(BMailAccountSettings* settings) : fMailNotifier(NULL), @@ -379,6 +388,9 @@ MailProtocol::_LoadFilter(AddonSettings* filterSettings) } +// #pragma mark - + + InboundProtocol::InboundProtocol(BMailAccountSettings* settings) : MailProtocol(settings) @@ -389,7 +401,7 @@ InboundProtocol::InboundProtocol(BMailAccountSettings* settings) InboundProtocol::~InboundProtocol() { - + } @@ -408,6 +420,9 @@ InboundProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flag) } +// #pragma mark - + + OutboundProtocol::OutboundProtocol(BMailAccountSettings* settings) : MailProtocol(settings) @@ -418,15 +433,11 @@ OutboundProtocol::OutboundProtocol(BMailAccountSettings* settings) OutboundProtocol::~OutboundProtocol() { - + } -const uint32 kMsgMoveFile = '&MoF'; -const uint32 kMsgDeleteFile = '&DeF'; -const uint32 kMsgFileRenamed = '&FiR'; -const uint32 kMsgFileDeleted = '&FDe'; -const uint32 kMsgInit = '&Ini'; +// #pragma mark - MailProtocolThread::MailProtocolThread(MailProtocol* protocol) @@ -448,48 +459,50 @@ void MailProtocolThread::MessageReceived(BMessage* message) { switch (message->what) { - case kMsgInit: - fMailProtocol->SetProtocolThread(this); - break; + case kMsgInit: + fMailProtocol->SetProtocolThread(this); + break; - case kMsgMoveFile: - { - entry_ref file; - message->FindRef("file", &file); - entry_ref dir; - message->FindRef("directory", &dir); - BDirectory directory(&dir); - fMailProtocol->MoveMessage(file, directory); - break; - } + case kMsgMoveFile: + { + entry_ref file; + message->FindRef("file", &file); + entry_ref dir; + message->FindRef("directory", &dir); + BDirectory directory(&dir); + fMailProtocol->MoveMessage(file, directory); + break; + } - case kMsgDeleteFile: - { - entry_ref file; - message->FindRef("file", &file); - fMailProtocol->DeleteMessage(file); - break; - } + case kMsgDeleteFile: + { + entry_ref file; + message->FindRef("file", &file); + fMailProtocol->DeleteMessage(file); + break; + } - case kMsgFileRenamed: - { - entry_ref from; - message->FindRef("from", &from); - entry_ref to; - message->FindRef("to", &to); - fMailProtocol->FileRenamed(from, to); - } + case kMsgFileRenamed: + { + entry_ref from; + message->FindRef("from", &from); + entry_ref to; + message->FindRef("to", &to); + fMailProtocol->FileRenamed(from, to); + break; + } - case kMsgFileDeleted: - { - node_ref node; - message->FindInt32("device",&node.device); - message->FindInt64("node", &node.node); - fMailProtocol->FileDeleted(node); - } + case kMsgFileDeleted: + { + node_ref node; + message->FindInt32("device",&node.device); + message->FindInt64("node", &node.node); + fMailProtocol->FileDeleted(node); + break; + } - default: - BLooper::MessageReceived(message); + default: + BLooper::MessageReceived(message); } } @@ -538,9 +551,7 @@ MailProtocolThread::TriggerFileDeleted(const node_ref& node) } -const uint32 kMsgSyncMessages = '&SyM'; -const uint32 kMsgDeleteMessage = '&DeM'; -const uint32 kMsgAppendMessage = '&ApM'; +// #pragma mark - InboundProtocolThread::InboundProtocolThread(InboundProtocol* protocol) @@ -562,57 +573,58 @@ void InboundProtocolThread::MessageReceived(BMessage* message) { switch (message->what) { - case kMsgSyncMessages: - { - status_t status = fProtocol->SyncMessages(); - _NotiyMailboxSynced(status); - break; - } - - case kMsgFetchBody: - { - entry_ref ref; - message->FindRef("ref", &ref); - status_t status = fProtocol->FetchBody(ref); - - BMessenger target; - if (message->FindMessenger("target", &target) != B_OK) + case kMsgSyncMessages: + { + status_t status = fProtocol->SyncMessages(); + _NotiyMailboxSynced(status); break; + } - BMessage message(kMsgBodyFetched); - message.AddInt32("status", status); - message.AddRef("ref", &ref); - target.SendMessage(&message); - break; - } + case kMsgFetchBody: + { + entry_ref ref; + message->FindRef("ref", &ref); + status_t status = fProtocol->FetchBody(ref); - case kMsgMarkMessageAsRead: - { - entry_ref ref; - message->FindRef("ref", &ref); - read_flags read = (read_flags)message->FindInt32("read"); - fProtocol->MarkMessageAsRead(ref, read); - break; - } + BMessenger target; + if (message->FindMessenger("target", &target) != B_OK) + break; - case kMsgDeleteMessage: - { - entry_ref ref; - message->FindRef("ref", &ref); - fProtocol->DeleteMessage(ref); - break; - } + BMessage message(kMsgBodyFetched); + message.AddInt32("status", status); + message.AddRef("ref", &ref); + target.SendMessage(&message); + break; + } - case kMsgAppendMessage: - { - entry_ref ref; - message->FindRef("ref", &ref); - fProtocol->AppendMessage(ref); - break; - } + case kMsgMarkMessageAsRead: + { + entry_ref ref; + message->FindRef("ref", &ref); + read_flags read = (read_flags)message->FindInt32("read"); + fProtocol->MarkMessageAsRead(ref, read); + break; + } - default: - MailProtocolThread::MessageReceived(message); + case kMsgDeleteMessage: + { + entry_ref ref; + message->FindRef("ref", &ref); + fProtocol->DeleteMessage(ref); + break; + } + + case kMsgAppendMessage: + { + entry_ref ref; + message->FindRef("ref", &ref); + fProtocol->AppendMessage(ref); + break; + } + + default: + MailProtocolThread::MessageReceived(message); + break; } } @@ -671,7 +683,7 @@ InboundProtocolThread::_NotiyMailboxSynced(status_t status) } -const uint32 kMsgSendMessage = '&SeM'; +// #pragma mark - OutboundProtocolThread::OutboundProtocolThread(OutboundProtocol* protocol) @@ -693,22 +705,22 @@ void OutboundProtocolThread::MessageReceived(BMessage* message) { switch (message->what) { - case kMsgSendMessage: - { - std::vector mails; - for (int32 i = 0; ;i++) { - entry_ref ref; - if (message->FindRef("ref", i, &ref) != B_OK) - break; - mails.push_back(ref); + case kMsgSendMessage: + { + std::vector mails; + for (int32 i = 0; ;i++) { + entry_ref ref; + if (message->FindRef("ref", i, &ref) != B_OK) + break; + mails.push_back(ref); + } + size_t size = message->FindInt32("size"); + fProtocol->SendMessages(mails, size); + break; } - size_t size = message->FindInt32("size"); - fProtocol->SendMessages(mails, size); - break; - } - default: - MailProtocolThread::MessageReceived(message); + default: + MailProtocolThread::MessageReceived(message); } } diff --git a/src/kits/mail/mail_util.cpp b/src/kits/mail/mail_util.cpp index 1bf5423184..751690aa0a 100644 --- a/src/kits/mail/mail_util.cpp +++ b/src/kits/mail/mail_util.cpp @@ -1,15 +1,10 @@ -/* mail util - header parsing -** -** Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. -*/ +/* + * Copyright 2011, Haiku, Inc. All rights reserved. + * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. + */ -#include -#include -#include -#include -#include -#include +#include #include #include @@ -18,27 +13,30 @@ #include #include #include + +#include +#include #include +#include +#include #include -#include - #include #include + using namespace BPrivate; + #define CRLF "\r\n" -struct CharsetConversionEntry -{ +struct CharsetConversionEntry { const char *charset; uint32 flavor; }; -extern const CharsetConversionEntry mail_charsets [] = -{ +extern const CharsetConversionEntry mail_charsets[] = { // In order of authority, so when searching for the name for a particular // numbered conversion, start at the beginning of the array. {"iso-8859-1", B_ISO1_CONVERSION}, // MIME STANDARD @@ -86,239 +84,16 @@ extern const CharsetConversionEntry mail_charsets [] = }; -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; - -#if R5_COMPATIBLE - // manage the status string only if it currently has a "read" status - BString currentStatus; - if (node.ReadAttrString(B_MAIL_ATTR_STATUS, ¤tStatus) == B_OK) { - if (currentStatus.ICompare("New") != 0 - && currentStatus.ICompare("Read") != 0 - && currentStatus.ICompare("Seen") != 0) - return B_OK; - } - - const char* statusString = (flag == B_READ) ? "Read" - : (flag == B_SEEN) ? "Seen" : "New"; - if (node.WriteAttr(B_MAIL_ATTR_STATUS, B_STRING_TYPE, 0, statusString, - strlen(statusString)) < 0) - return B_ERROR; -#endif - return B_OK; -} +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]; -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; - -#if R5_COMPATIBLE - BString statusString; - if (node.ReadAttrString(B_MAIL_ATTR_STATUS, &statusString) == B_OK) { - if (statusString.ICompare("New")) - flag = B_UNREAD; - else - flag = B_READ; - - return B_OK; - } -#endif - return B_ERROR; -} - - -// The next couple of functions are our wrapper around convert_to_utf8 and -// convert_from_utf8 so that they can also convert from UTF-8 to UTF-8 by -// specifying the B_MAIL_UTF8_CONVERSION constant as the conversion operation. It -// also lets us add new conversions, like B_MAIL_US_ASCII_CONVERSION. - -_EXPORT 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) - // Invalid, could also use substitute, but better to strip high bit. - *dst++ = letter - 0x80U; - else if (letter == 0x80U) - // Can't convert to 0x00 since that's NUL, which would cause problems. - *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) { - // Replace spurious NUL bytes, which should normally not be in the - // output of the decoding (not normal UTF-8 characters, and no NULs are - // in our usual input strings). They happen for some odd ISO-2022-JP - // byte pair combinations which are improperly handled by the BeOS - // routines. Like "\e$ByD\e(B" where \e is the ESC character $1B, the - // first ESC $ B switches to a Japanese character set, then the next - // two bytes "yD" specify a character, then ESC ( B switches back to - // the ASCII character set. The UTF-8 conversion yields a NUL byte. - int32 i; - for (i = 0; i < *dstLen; i++) - if (originalDst[i] == 0) - originalDst[i] = substitute; - } - return returnCode; -} - - -_EXPORT 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; - - // state contains the number of source bytes to skip, left over from a - // partial UTF-8 character split over the end of the buffer from last - // time. - 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; // Regular ASCII equivalent code. - else if (letter < 0xC0) - characterLength = 1; // Invalid in-between data byte 10xxxxxx. - 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; // 0xFE and 0xFF are invalid in UTF-8. - if (letter < 0x80) - *dst++ = *src; - else - *dst++ = substitute; - dstRemaining--; - if (srcRemaining < characterLength) { - // Character split past the end of the buffer. - *state = characterLength - srcRemaining; - srcRemaining = 0; - } else { - src += characterLength; - srcRemaining -= characterLength; - } - } - // Update with the amounts used. - *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; - - // B_JIS_CONVERSION (ISO-2022-JP) works by shifting between different - // character subsets. For E-mail headers (and other uses), it needs to be - // switched back to ASCII at the end (otherwise the last character gets - // lost or other weird things happen in the headers). Note that we can't - // just append the escape code since the convert_from_utf8 "state" will be - // wrong. So we append an ASCII letter and throw it away, leaving just the - // escape code. Well, it actually switches to the Roman character set, not - // ASCII, but that should be OK. - - tempDstLen = originalDstLen - *dstLen; - if (tempDstLen < 3) // Not enough space remaining in the output. - return B_OK; // Sort of an error, but we did convert the rest OK. - tempSrcLen = 1; - errorCode = convert_from_utf8 (dstEncoding, "a", &tempSrcLen, - dst + *dstLen, &tempDstLen, state, substitute); - if (errorCode != B_OK) - return errorCode; - *dstLen += tempDstLen - 1 /* don't include the ASCII letter */; - return B_OK; -} - - - -static int handle_non_rfc2047_encoding(char **buffer,size_t *bufferLength,size_t *sourceLength) +static int +handle_non_rfc2047_encoding(char **buffer, size_t *bufferLength, + size_t *sourceLength) { char *string = *buffer; int32 length = *sourceLength; @@ -374,7 +149,230 @@ static int handle_non_rfc2047_encoding(char **buffer,size_t *bufferLength,size_t } -_EXPORT ssize_t rfc2047_to_utf8(char **bufp, size_t *bufLen, size_t strLen) +// #pragma mark - + + +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; + + // manage the status string only if it currently has a "read" status + BString currentStatus; + if (node.ReadAttrString(B_MAIL_ATTR_STATUS, ¤tStatus) == B_OK) { + if (currentStatus.ICompare("New") != 0 + && currentStatus.ICompare("Read") != 0 + && currentStatus.ICompare("Seen") != 0) + return B_OK; + } + + const char* statusString = flag == B_READ ? "Read" + : flag == B_SEEN ? "Seen" : "New"; + if (node.WriteAttr(B_MAIL_ATTR_STATUS, B_STRING_TYPE, 0, statusString, + strlen(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")) + flag = B_UNREAD; + else + flag = B_READ; + + return B_OK; + } + + return B_ERROR; +} + + +// The next couple of functions are our wrapper around convert_to_utf8 and +// convert_from_utf8 so that they can also convert from UTF-8 to UTF-8 by +// specifying the B_MAIL_UTF8_CONVERSION constant as the conversion operation. +// It also lets us add new conversions, like B_MAIL_US_ASCII_CONVERSION. + + +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) + // Invalid, could also use substitute, but better to strip high bit. + *dst++ = letter - 0x80U; + else if (letter == 0x80U) + // Can't convert to 0x00 since that's NUL, which would cause problems. + *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) { + // Replace spurious NUL bytes, which should normally not be in the + // output of the decoding (not normal UTF-8 characters, and no NULs are + // in our usual input strings). They happen for some odd ISO-2022-JP + // byte pair combinations which are improperly handled by the BeOS + // routines. Like "\e$ByD\e(B" where \e is the ESC character $1B, the + // first ESC $ B switches to a Japanese character set, then the next + // two bytes "yD" specify a character, then ESC ( B switches back to + // the ASCII character set. The UTF-8 conversion yields a NUL byte. + 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; + + // state contains the number of source bytes to skip, left over from a + // partial UTF-8 character split over the end of the buffer from last + // time. + 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; // Regular ASCII equivalent code. + else if (letter < 0xC0) + characterLength = 1; // Invalid in-between data byte 10xxxxxx. + 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; // 0xFE and 0xFF are invalid in UTF-8. + if (letter < 0x80) + *dst++ = *src; + else + *dst++ = substitute; + dstRemaining--; + if (srcRemaining < characterLength) { + // Character split past the end of the buffer. + *state = characterLength - srcRemaining; + srcRemaining = 0; + } else { + src += characterLength; + srcRemaining -= characterLength; + } + } + // Update with the amounts used. + *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; + + // B_JIS_CONVERSION (ISO-2022-JP) works by shifting between different + // character subsets. For E-mail headers (and other uses), it needs to be + // switched back to ASCII at the end (otherwise the last character gets + // lost or other weird things happen in the headers). Note that we can't + // just append the escape code since the convert_from_utf8 "state" will be + // wrong. So we append an ASCII letter and throw it away, leaving just the + // escape code. Well, it actually switches to the Roman character set, not + // ASCII, but that should be OK. + + tempDstLen = originalDstLen - *dstLen; + if (tempDstLen < 3) // Not enough space remaining in the output. + return B_OK; // Sort of an error, but we did convert the rest OK. + tempSrcLen = 1; + errorCode = convert_from_utf8(dstEncoding, "a", &tempSrcLen, + dst + *dstLen, &tempDstLen, state, substitute); + if (errorCode != B_OK) + return errorCode; + *dstLen += tempDstLen - 1 /* don't include the ASCII letter */; + return B_OK; +} + + +ssize_t +rfc2047_to_utf8(char **bufp, size_t *bufLen, size_t strLen) { char *head, *tail; char *charset, *encoding, *end; @@ -384,7 +382,7 @@ _EXPORT ssize_t rfc2047_to_utf8(char **bufp, size_t *bufLen, size_t strLen) return -1; char *string = *bufp; - + //---------Handle *&&^%*&^ non-RFC compliant, 8bit mail if (handle_non_rfc2047_encoding(bufp,bufLen,&strLen)) return strLen; @@ -434,25 +432,25 @@ _EXPORT ssize_t rfc2047_to_utf8(char **bufp, size_t *bufLen, size_t strLen) end += 2; // find the charset this text is in now - size_t cLen = encoding - 1 - charset; - bool base64encoded = toupper(*encoding) == 'B'; + size_t cLen = encoding - 1 - charset; + bool base64encoded = toupper(*encoding) == 'B'; - uint32 convert_id = B_MAIL_NULL_CONVERSION; - char charset_string[cLen+1]; - memcpy(charset_string, charset, cLen); - charset_string[cLen] = '\0'; - if (strcasecmp(charset_string, "us-ascii") == 0) { - convert_id = B_MAIL_US_ASCII_CONVERSION; - } else if (strcasecmp(charset_string, "utf-8") == 0) { - convert_id = B_MAIL_UTF8_CONVERSION; + 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 * cs = BCharacterSetRoster::FindCharacterSetByName(charset_string); - if (cs != NULL) { - convert_id = cs->GetConversionID(); + const BCharacterSet* charSet + = BCharacterSetRoster::FindCharacterSetByName(charsetName); + if (charSet != NULL) { + convertID = charSet->GetConversionID(); } } - if (convert_id == B_MAIL_NULL_CONVERSION) - { + if (convertID == B_MAIL_NULL_CONVERSION) { // unidentified charset // what to do? doing nothing skips the encoded text; // but we should keep it: we copy it to the output. @@ -469,7 +467,7 @@ _EXPORT ssize_t rfc2047_to_utf8(char **bufp, size_t *bufLen, size_t strLen) // decode text, get decoded length (reducing xforms) srcLen = !base64encoded ? decode_qp(src, src, srcLen, 1) - : decode_base64(src, src, srcLen); + : decode_base64(src, src, srcLen); // allocate space for the converted text int32 dstLen = end-string + *bufLen-strLen; @@ -480,9 +478,9 @@ _EXPORT ssize_t rfc2047_to_utf8(char **bufp, size_t *bufLen, size_t strLen) // // do the conversion // - ret = mail_convert_to_utf8(convert_id, src, &cvLen, dst, &dstLen, &convState); - if (ret != B_OK) - { + ret = mail_convert_to_utf8(convertID, src, &cvLen, dst, &dstLen, + &convState); + if (ret != B_OK) { // what to do? doing nothing skips the encoded text // but we should keep it: we copy it to the output. @@ -524,10 +522,8 @@ _EXPORT ssize_t rfc2047_to_utf8(char **bufp, size_t *bufLen, size_t strLen) continue; } */ - else - { - if (dstLen > end-string) - { + else { + if (dstLen > end-string) { // copy the string forward... memmove(string+dstLen, end, strLen - (end-head) + 1); strLen += string+dstLen - end; @@ -553,7 +549,9 @@ _EXPORT ssize_t rfc2047_to_utf8(char **bufp, size_t *bufLen, size_t strLen) } -_EXPORT ssize_t utf8_to_rfc2047 (char **bufp, ssize_t length, uint32 charset, char encoding) { +ssize_t +utf8_to_rfc2047 (char **bufp, ssize_t length, uint32 charset, char encoding) +{ struct word { BString originalWord; BString convertedWord; @@ -748,16 +746,15 @@ _EXPORT ssize_t utf8_to_rfc2047 (char **bufp, ssize_t length, uint32 charset, ch } -//==================================================================== - -void FoldLineAtWhiteSpaceAndAddCRLF (BString &string) +void +FoldLineAtWhiteSpaceAndAddCRLF(BString &string) { - int inputLength = string.Length(); - int lineStartIndex; - const int maxLineLength = 78; // Doesn't include CRLF. - BString output; - int splitIndex; - int tempIndex; + int inputLength = string.Length(); + int lineStartIndex; + const int maxLineLength = 78; // Doesn't include CRLF. + BString output; + int splitIndex; + int tempIndex; lineStartIndex = 0; while (true) { @@ -827,21 +824,18 @@ void FoldLineAtWhiteSpaceAndAddCRLF (BString &string) } -//==================================================================== - -_EXPORT ssize_t readfoldedline(FILE *file, char **buffer, size_t *buflen) +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; // Number of characters currently in the buffer. int c; - while (true) - { + while (true) { // Make sure there is space in the buffer for two more characters (one // for the next character, and one for the end of string NUL byte). - if (buf == NULL || cnt + 2 >= len) - { + if (buf == NULL || cnt + 2 >= len) { char *temp = (char *)realloc(buf, len + 64); if (temp == NULL) { // Out of memory, however existing buffer remains allocated. @@ -898,7 +892,6 @@ _EXPORT ssize_t readfoldedline(FILE *file, char **buffer, size_t *buflen) } } - if (buf != NULL && cnt >= 0) buf[cnt] = '\0'; @@ -914,9 +907,8 @@ _EXPORT ssize_t readfoldedline(FILE *file, char **buffer, size_t *buflen) } -//==================================================================== - -_EXPORT ssize_t readfoldedline(BPositionIO &in, char **buffer, size_t *buflen) +ssize_t +readfoldedline(BPositionIO &in, char **buffer, size_t *buflen) { ssize_t len = buflen && *buflen ? *buflen : 0; char * buf = buffer && *buffer ? *buffer : NULL; @@ -924,12 +916,10 @@ _EXPORT ssize_t readfoldedline(BPositionIO &in, char **buffer, size_t *buflen) char c; status_t errorCode; - while (true) - { + while (true) { // Make sure there is space in the buffer for two more characters (one // for the next character, and one for the end of string NUL byte). - if (buf == NULL || cnt + 2 >= len) - { + if (buf == NULL || cnt + 2 >= len) { char *temp = (char *)realloc(buf, len + 64); if (temp == NULL) { // Out of memory, however existing buffer remains allocated. @@ -1005,7 +995,7 @@ _EXPORT ssize_t readfoldedline(BPositionIO &in, char **buffer, size_t *buflen) } -_EXPORT ssize_t +ssize_t nextfoldedline(const char** header, char **buffer, size_t *buflen) { ssize_t len = buflen && *buflen ? *buflen : 0; @@ -1085,7 +1075,7 @@ nextfoldedline(const char** header, char **buffer, size_t *buflen) } -_EXPORT void +void trim_white_space(BString &string) { int32 i; @@ -1105,12 +1095,11 @@ trim_white_space(BString &string) } -/** Tries to return a human-readable name from the specified - * header parameter (should be from "To:" or "From:"). - * Tries to return the name rather than the eMail address. - */ - -_EXPORT void +/*! Tries to return a human-readable name from the specified + 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; @@ -1198,19 +1187,13 @@ extract_address_name(BString &header) } - -// Given a subject in a BString, remove the extraneous RE: re: and other stuff -// 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. - -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]; - -_EXPORT void SubjectToThread (BString &string) +/*! Given a subject in a BString, remove the extraneous RE: re: and other stuff + 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) { // a regex that matches a non-ASCII UTF8 character: #define U8C \ @@ -1230,8 +1213,7 @@ _EXPORT void SubjectToThread (BString &string) "|^( +| *(\\<(\\w|" U8C "){2,3} *(\\[[^\\]]*\\])? *:)+ *)" \ "| *\\(fwd\\) *$" - if (gRebuf == NULL && atomic_add(&gLocker,1) == 0) - { + if (gRebuf == NULL && atomic_add(&gLocker, 1) == 0) { // the idea is to compile the regexp once to speed up testing for (int i=0; i<256; ++i) gTranslation[i]=i; @@ -1256,16 +1238,13 @@ _EXPORT void SubjectToThread (BString &string) gRebuf = &gRe; else fprintf(stderr, "Failed to compile the regex: %s\n", err); - } - else - { + } else { int32 tries = 200; while (gRebuf == NULL && tries-- > 0) snooze(10000); } - if (gRebuf) - { + if (gRebuf) { struct re_registers regs; // can't be static if this function is to be thread-safe @@ -1273,11 +1252,8 @@ _EXPORT void SubjectToThread (BString &string) 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; - ) - { + for (int start = 0; (start = re_search(gRebuf, string.String(), + string.Length(), 0, string.Length(), ®s)) >= 0;) { // // we found something // @@ -1287,7 +1263,8 @@ _EXPORT void SubjectToThread (BString &string) start = regs.start[2]; string.Remove(start,regs.end[0]-start); - if (start) string.Insert(' ',1,start); + if (start) + string.Insert(' ',1,start); // TODO: for some subjects this results in an endless loop, check // why this happen. @@ -1306,19 +1283,19 @@ _EXPORT void SubjectToThread (BString &string) } - -// Converts a date to a time. Handles numeric time zones too, unlike -// parsedate. Returns -1 if it fails. - -_EXPORT time_t ParseDateWithTimeZone (const char *DateString) +/*! Converts a date to a time. Handles numeric time zones too, unlike + 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; + time_t currentTime; + time_t dateAsTime; + char tempDateString[80]; + char tempZoneString[6]; + time_t zoneDeltaTime; + int zoneIndex; + char *zonePntr; // See if we can remove the time zone portion. parsedate understands time // zone 3 letter names, but doesn't understand the numeric +9999 time zone @@ -1349,7 +1326,7 @@ _EXPORT time_t ParseDateWithTimeZone (const char *DateString) return -1; // Empty string. } } - + // Look for a numeric time zone like Tue, 30 Dec 2003 05:01:40 +0000 for (zoneIndex = strlen (tempDateString); zoneIndex >= 0; zoneIndex--) { @@ -1390,10 +1367,9 @@ _EXPORT time_t ParseDateWithTimeZone (const char *DateString) } -/** Parses a mail header and fills the headers BMessage - */ - -_EXPORT status_t +/*! Parses a mail header and fills the headers BMessage +*/ +status_t parse_header(BMessage &headers, BPositionIO &input) { char *buffer = NULL; @@ -1417,10 +1393,12 @@ parse_header(BMessage &headers, BPositionIO &input) // unified case for later fetch delimiter++; // Skip the colon. - while (isspace (*delimiter)) - delimiter++; // Skip over leading white space and tabs. To do: (comments in brackets). + // Skip over leading white space and tabs. + // TODO: (comments in brackets). + while (isspace(*delimiter)) + delimiter++; - // ToDo: implement joining of multiple header tags (i.e. multiple "Cc:"s) + // TODO: implement joining of multiple header tags (i.e. multiple "Cc:"s) headers.AddString(header.String(), delimiter); } free(buffer); @@ -1429,7 +1407,7 @@ parse_header(BMessage &headers, BPositionIO &input) } -_EXPORT status_t +status_t extract_from_header(const BString& header, const BString& field, BString& target) { @@ -1440,7 +1418,7 @@ extract_from_header(const BString& header, const BString& field, if (pos < 0) return B_BAD_VALUE; fieldEndPos = pos + field.Length(); - + if (pos != 0 && header.ByteAt(pos - 1) != '\n') continue; if (header.ByteAt(fieldEndPos) == ':') @@ -1486,7 +1464,7 @@ extract_address(BString &address) int32 first; // first, remove all quoted text - + if ((first = address.FindFirst('"')) >= 0) { int32 last = first + 1; while (string[last] && string[last] != '"')