status_t ParseFilter::ProcessMailMessage(BPositionIO **data, BEntry */*entry*/, BMessage *headers, BPath */*folder*/, const char */*uid*/) { char byte; (*data)->ReadAt(0,&byte, 1); (*data)->Seek(SEEK_SET, 0); status_t status = parse_header(*headers, **data); if (status < B_OK) return status; // // add pseudo-header THREAD, that contains the subject // minus stuff in []s (added by mailing lists) and // Re: prefixes, added by mailers when you reply. // This will generally be the "thread subject". // BString string; string.SetTo(headers->FindString("Subject")); SubjectToThread(string); headers->AddString("THREAD", string.String()); // name if (headers->FindString(fNameField.String(), 0, &string) == B_OK) { extract_address_name(string); headers->AddString("NAME", string); } // header length headers->AddInt32(B_MAIL_ATTR_HEADER, (int32)((*data)->Position())); // What about content length? If we do that, we have to D/L the // whole message... //--NathanW says let the disk consumer do that (*data)->Seek(0, SEEK_SET); return B_OK; }
BMailFilterAction HaikuMailFormatFilter::HeaderFetched(entry_ref& ref, BFile& file, BMessage& attributes) { file.Seek(0, SEEK_SET); // TODO: attributes.AddInt32(B_MAIL_ATTR_CONTENT, length); attributes.AddInt32(B_MAIL_ATTR_ACCOUNT_ID, fAccountID); attributes.AddString(B_MAIL_ATTR_ACCOUNT, fAccountName); BString header; off_t size; if (file.GetSize(&size) == B_OK) { char* buffer = header.LockBuffer(size); if (buffer == NULL) return B_NO_MEMORY; ssize_t bytesRead = file.Read(buffer, size); if (bytesRead < 0) return bytesRead; if (bytesRead != size) return B_IO_ERROR; header.UnlockBuffer(size); } for (int i = 0; gDefaultFields[i].rfc_name; ++i) { BString target; status_t status = extract_from_header(header, gDefaultFields[i].rfc_name, target); if (status != B_OK) continue; switch (gDefaultFields[i].attr_type){ case B_STRING_TYPE: sanitize_white_space(target); attributes.AddString(gDefaultFields[i].attr_name, target); break; case B_TIME_TYPE: { time_t when; when = ParseDateWithTimeZone(target); if (when == -1) when = time(NULL); // Use current time if it's undecodable. attributes.AddData(B_MAIL_ATTR_WHEN, B_TIME_TYPE, &when, sizeof(when)); break; } } } BString senderName = _ExtractName(attributes.FindString(B_MAIL_ATTR_FROM)); attributes.AddString(B_MAIL_ATTR_NAME, senderName); // Generate a file name for the incoming message. See also // Message::RenderTo which does a similar thing for outgoing messages. BString name = attributes.FindString(B_MAIL_ATTR_SUBJECT); SubjectToThread(name); // Extract the core subject words. if (name.Length() <= 0) name = "No Subject"; attributes.AddString(B_MAIL_ATTR_THREAD, name); // 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 // order of date. time_t dateAsTime = 0; const time_t* datePntr; ssize_t dateSize; char numericDateString[40]; struct tm timeFields; if (attributes.FindData(B_MAIL_ATTR_WHEN, B_TIME_TYPE, (const void**)&datePntr, &dateSize) == B_OK) dateAsTime = *datePntr; localtime_r(&dateAsTime, &timeFields); 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 workerName = attributes.FindString(B_MAIL_ATTR_FROM); extract_address_name(workerName); name << " " << workerName; name.Truncate(222); // reserve space for the unique number // Get rid of annoying characters which are hard to use in the shell. name.ReplaceAll('/', '_'); name.ReplaceAll('\'', '_'); name.ReplaceAll('"', '_'); name.ReplaceAll('!', '_'); name.ReplaceAll('<', '_'); name.ReplaceAll('>', '_'); _RemoveExtraWhitespace(name); _RemoveLeadingDots(name); // Avoid files starting with a dot. if (!attributes.HasString(B_MAIL_ATTR_STATUS)) attributes.AddString(B_MAIL_ATTR_STATUS, "New"); _SetType(attributes, B_PARTIAL_MAIL_TYPE); ref.set_name(name.String()); return B_MOVE_MAIL_ACTION; }
status_t BEmailMessage::RenderTo(BDirectory *dir, BEntry *msg) { time_t currentTime; char numericDateString [40]; struct tm timeFields; BString worker; // Generate a file name for the outgoing message. See also // FolderFilter::ProcessMailMessage which does something similar for // incoming messages. BString name = Subject(); SubjectToThread (name); // Extract the core subject words. if (name.Length() <= 0) name = "No Subject"; if (name[0] == '.') name.Prepend ("_"); // Avoid hidden files, starting with a dot. // 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 // order of date. time (¤tTime); localtime_r (¤tTime, &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); name << " " << numericDateString; worker = From(); extract_address_name(worker); name << " " << worker; name.Truncate(222); // reserve space for the uniquer // Get rid of annoying characters which are hard to use in the shell. name.ReplaceAll('/','_'); name.ReplaceAll('\'','_'); name.ReplaceAll('"','_'); name.ReplaceAll('!','_'); name.ReplaceAll('<','_'); name.ReplaceAll('>','_'); while (name.FindFirst(" ") >= 0) // Remove multiple spaces. name.Replace(" " /* Old */, " " /* New */, 1024 /* Count */); int32 uniquer = time(NULL); worker = name; int32 tries = 30; bool exists; while ((exists = dir->Contains(worker.String())) && --tries > 0) { srand(rand()); uniquer += (rand() >> 16) - 16384; worker = name; worker << ' ' << uniquer; } if (exists) printf("could not create mail! (should be: %s)\n", worker.String()); BFile file; status_t status = dir->CreateFile(worker.String(), &file); if (status < B_OK) return status; if (msg != NULL) msg->SetTo(dir,worker.String()); return RenderToRFC822(&file); }