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;
   }
Beispiel #2
0
   /*
      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;
   }