String MessageUtilities::GetSendersIP(boost::shared_ptr<Message> pMessage) { const String fileName = PersistentMessage::GetFileName(pMessage); AnsiString sHeader = PersistentMessage::LoadHeader(fileName); MimeHeader oHeader; oHeader.Load(sHeader, sHeader.GetLength(), true); // Locate the first Received header MimeField *pReceivedHeader = oHeader.GetField("Received"); if (pReceivedHeader == 0) return "127.0.0.1"; // Now we should try to find the IP in the received header. String sReceivedValue = pReceivedHeader->GetValue(); int iAddressStart = sReceivedValue.Find(_T("[")) +1; int iAddressEnd = sReceivedValue.Find(_T("]"), iAddressStart); int iAddressLen = iAddressEnd - iAddressStart; if (iAddressLen <= 0) return "127.0.0.1"; String sIPAddress = sReceivedValue.Mid(iAddressStart, iAddressLen); if (!StringParser::IsValidIPAddress(sIPAddress)) return "127.0.0.1"; return sIPAddress; }
/* Returns one of the following Neutral - Undecided Pass - Signature verified properly. TempFail - Failed to verify signature, potentially a local problem. PermFail - Failed to verify signature. */ DKIM::Result DKIM::Verify(const String &fileName) { if (FileUtilities::FileSize(fileName) > MaxFileSize) return Neutral; AnsiString messageHeader = PersistentMessage::LoadHeader(fileName); MimeHeader mimeHeader; mimeHeader.Load(messageHeader.GetBuffer(), messageHeader.GetLength(), false); vector<pair<AnsiString, AnsiString> > signatureFields = GetSignatureFields(mimeHeader); if (signatureFields.size() == 0) { // No signatures in message. return Neutral; } Result result = Neutral; typedef pair<AnsiString, AnsiString> HeaderField; boost_foreach (HeaderField signatureField, signatureFields) { result = _VerifySignature(fileName, messageHeader, signatureField); if (result == Pass) return Pass; };
void IMAPSort::CacheHeaderFields_(std::shared_ptr<IMAPConnection> pConnection, const std::vector<std::pair<int, std::shared_ptr<Message> > > &vecMessages, const std::map<__int64, String > &databaseMetaData, SortField &sortField, std::map<__int64, String> &mapHeaderFields) { mapHeaderFields.clear(); String sHeaderField; switch (sortField) { case From: sHeaderField = "From"; break; case Subject: sHeaderField = "Subject"; break; case To: sHeaderField = "To"; break; case CC: sHeaderField = "CC"; break; case Date: sHeaderField = "Date"; break; } // Cache the header field for all messages. bool bTrimLeadingSpecialCharacters = (sortField == From || sortField == To || sortField == CC); std::vector<std::pair<int, std::shared_ptr<Message> > >::const_iterator iter = vecMessages.begin(); std::vector<std::pair<int, std::shared_ptr<Message> > >::const_iterator iterEnd = vecMessages.end(); for (; iter != iterEnd; iter++) { // Fetch message. const std::shared_ptr<Message> p1 = (*iter).second; String sFieldValue = ""; // Read message and parse out the value from the header. std::map<__int64, String >::const_iterator dbMetaIter = databaseMetaData.find(p1->GetID()); if (dbMetaIter == databaseMetaData.end()) { String fileName = PersistentMessage::GetFileName(pConnection->GetAccount(), p1); AnsiString sHeader = PersistentMessage::LoadHeader(fileName); MimeHeader oHeader;oHeader.Load(sHeader, sHeader.GetLength()); sFieldValue = oHeader.GetUnicodeFieldValue(sHeaderField); if (sortField == Date) { DateTime dt = Time::GetDateTimeFromMimeHeader(sFieldValue); sFieldValue = Time::GetTimeStampFromDateTime(dt); } } else { sFieldValue = (*dbMetaIter).second; } if (sortField == Date && sFieldValue.IsEmpty()) { /* * RFC 5256 "2.2. Sent Date" chapter. If the sent date cannot be determined (a Date: header is missing or cannot be parsed), * the INTERNALDATE for that message is used as the sent date. */ sFieldValue = p1->GetCreateTime(); } // Convert the string to upper case. The sorting should be // case insensitive. Otherwise b will come before A. sFieldValue.ToUpper(); // Remove leading ". If we sort on From, "Test" should come in // same position as Test. (quotes / no quotes) if (bTrimLeadingSpecialCharacters) { sFieldValue.TrimLeft(_T("\"")); sFieldValue.TrimLeft(_T("<")); } // Cache the header mapHeaderFields[p1->GetID()] = sFieldValue; } }
void SMTPVacationMessageCreator::CreateVacationMessage(shared_ptr<const Account> recipientAccount, const String &sToAddress, const String &sVacationSubject, const String &sVacationMessage, const shared_ptr<Message> pOriginalMessage) { if (!_CanSendVacationMessage(recipientAccount->GetAddress(), sToAddress)) return; // We have already notified this user. LOG_DEBUG("Creating out-of-office message."); String sModifiedSubject = sVacationSubject; String sModifiedBody = sVacationMessage; const String originalFileName = PersistentMessage::GetFileName(recipientAccount, pOriginalMessage); if (sModifiedSubject.Find(_T("%")) >= 0 || sModifiedBody.Find(_T("%")) >= 0) { // Replace macros in the string. MimeHeader header; AnsiString sHeader = PersistentMessage::LoadHeader(originalFileName); header.Load(sHeader, sHeader.GetLength(), true); String sOldSubject; if (header.GetField("Subject")) sOldSubject = header.GetField("Subject")->GetValue(); sModifiedSubject.ReplaceNoCase(_T("%SUBJECT%"), sOldSubject); sModifiedBody.ReplaceNoCase(_T("%SUBJECT%"), sOldSubject); } if (sModifiedSubject.GetLength() == 0) { // Parse out the subject in the original // message, so that we can Re: that.. MimeHeader header; AnsiString sHeader = PersistentMessage::LoadHeader(originalFileName); header.Load(sHeader, sHeader.GetLength(), true); String sOldSubject; if (header.GetField("Subject")) sOldSubject = header.GetField("Subject")->GetValue(); sModifiedSubject = "Re: " + sOldSubject; } // Send a copy of this email. shared_ptr<Message> pMsg = shared_ptr<Message>(new Message()); pMsg->SetState(Message::Delivering); pMsg->SetFromAddress(recipientAccount->GetAddress()); const String newFileName = PersistentMessage::GetFileName(pMsg); shared_ptr<MessageData> pNewMsgData = shared_ptr<MessageData>(new MessageData()); pNewMsgData->LoadFromMessage(newFileName, pMsg); // Required headers pNewMsgData->SetReturnPath(recipientAccount->GetAddress()); pNewMsgData->GenerateMessageID(); pNewMsgData->SetSentTime(Time::GetCurrentMimeDate()); pNewMsgData->SetFieldValue("Content-Type", "text/plain; charset=\"utf-8\""); // Optional headers pNewMsgData->SetFrom(recipientAccount->GetAddress()); pNewMsgData->SetTo(sToAddress); pNewMsgData->SetSubject(sModifiedSubject); pNewMsgData->SetBody(sModifiedBody); pNewMsgData->SetAutoReplied(); pNewMsgData->IncreaseRuleLoopCount(); // Write message data pNewMsgData->Write(newFileName); // Add recipients. bool recipientOK = false; RecipientParser recipientParser; recipientParser.CreateMessageRecipientList(sToAddress, pMsg->GetRecipients(), recipientOK); // Save message if (pMsg->GetRecipients()->GetCount() > 0) PersistentMessage::SaveObject(pMsg); // Tell app to submit mail Application::Instance()->SubmitPendingEmail(); }
void SMTPVirusNotifier::CreateMessageDeletedNotification(const boost::shared_ptr<Message> pOriginalMessage, const String &sRecipient) { LOG_DEBUG("SMTPVirusNotifier::CreateMessageDeletedNotification"); if (pOriginalMessage->GetFromAddress().IsEmpty()) { // No sender address specified, hence no place to // deliver the error log. return; } // Parse out the subject in the original // message, so that we can Re: that.. MimeHeader header; AnsiString sHeader = PersistentMessage::LoadHeader(PersistentMessage::GetFileName(pOriginalMessage)); header.Load(sHeader, sHeader.GetLength(), true); String sMailerDaemonAddress = MailerDaemonAddressDeterminer::GetMailerDaemonAddress(pOriginalMessage); String sTextVirusFound = Configuration::Instance()->GetServerMessages()->GetMessage("VIRUS_FOUND"); String sErrMsg = Configuration::Instance()->GetServerMessages()->GetMessage("VIRUS_NOTIFICATION"); String sOriginalSubject = header.GetRawFieldValue("Subject"); // Replace macros. sErrMsg.Replace(_T("%MACRO_FROM%"), String(header.GetRawFieldValue("From"))); sErrMsg.Replace(_T("%MACRO_TO%"), String(header.GetRawFieldValue("To"))); sErrMsg.Replace(_T("%MACRO_SENT%"), String(header.GetRawFieldValue("Date"))); sErrMsg.Replace(_T("%MACRO_SUBJECT%"), sOriginalSubject); // Send a copy of this email. boost::shared_ptr<Message> pMsg = boost::shared_ptr<Message>(new Message()); pMsg->SetState(Message::Delivering); pMsg->SetFromAddress(""); boost::shared_ptr<Account> account; boost::shared_ptr<MessageData> pNewMsgData = boost::shared_ptr<MessageData>(new MessageData()); pNewMsgData->LoadFromMessage(account, pMsg); // Required headers pNewMsgData->SetReturnPath(""); pNewMsgData->GenerateMessageID(); pNewMsgData->SetSentTime(Time::GetCurrentMimeDate()); // Optional headers pNewMsgData->SetFrom(sMailerDaemonAddress); pNewMsgData->SetTo(sRecipient); pNewMsgData->SetSubject(sTextVirusFound + ": " + sOriginalSubject); pNewMsgData->SetBody(sErrMsg); pNewMsgData->IncreaseRuleLoopCount(); // Write message data pNewMsgData->Write(PersistentMessage::GetFileName(pMsg)); // Add recipients. bool recipientOK = false; RecipientParser recipientParser; recipientParser.CreateMessageRecipientList(sRecipient, pMsg->GetRecipients(), recipientOK); // Save message if (pMsg->GetRecipients()->GetCount() > 0) PersistentMessage::SaveObject(pMsg); // Tell app to submit mail Application::Instance()->SubmitPendingEmail(); LOG_DEBUG("SMTPVirusNotifier::~CreateMessageDeletedNotification"); }
AnsiString RelaxedCanonicalization::CanonicalizeHeader(AnsiString header, const std::pair<AnsiString, AnsiString> &signatureField, const std::vector<AnsiString> &fieldsToInclude, AnsiString &fieldList) { MimeHeader mimeHeader; mimeHeader.Load(header, header.GetLength(), true); std::vector<MimeField> fields = mimeHeader.Fields(); String result; for(AnsiString headerField : fieldsToInclude) { headerField.Trim(); // locate this field, starting from the end. String value; for (size_t i = fields.size(); i > 0; i--) { size_t fieldIndex = i - 1; MimeField field = fields[fieldIndex]; if (headerField.CompareNoCase(field.GetName()) == 0) { // found fields.erase(fields.begin() + fieldIndex); value = field.GetValue(); break; } } if (value.GetLength() > 0) { /* Convert all header field names (not the header field values) to lowercase. For example, convert "SUBJect: AbC" to "subject: AbC". Delete any WSP characters remaining before and after the colon separating the header field name from the header field value. The colon separator MUST be retained. */ String relaxedFieldValue; String relaxedHeaderName = CanonicalizeHeaderName(headerField); relaxedFieldValue = CanonicalizeHeaderValue(value); result += relaxedHeaderName + ":" + relaxedFieldValue + "\r\n"; if (!fieldList.IsEmpty()) fieldList += ":"; fieldList += headerField; } } if (!signatureField.first.IsEmpty()) { // Don't pick the value from the actual header, use the header we're verifying instead // If there are more than one DKIM-signature fields in the header, this will be important. AnsiString relaxedHeaderName = CanonicalizeHeaderName(signatureField.first); AnsiString relaxedFieldValue = CanonicalizeHeaderValue(signatureField.second); //and without a trailing CRLF. result += relaxedHeaderName + ":" + GetDKIMWithoutSignature_(relaxedFieldValue); } return result; }