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; }
void MessageAttachmentStripper::_WriteToDisk(shared_ptr<Message> pMessage, MimeBody &oMainMessage, shared_ptr<MimeBody> pBody) { if (!pBody) return; AnsiString sHeader; vector<MimeField> oFieldList = oMainMessage.Fields(); vector<MimeField>::iterator iterField = oFieldList.begin(); while (iterField != oFieldList.end()) { MimeField oField = (*iterField); AnsiString sName = oField.GetName(); if (sName.CompareNoCase("subject") == 0) { String sTextVirusFound = Configuration::Instance()->GetServerMessages()->GetMessage("VIRUS_FOUND"); sHeader += sName; sHeader += ": " + sTextVirusFound +": "; sHeader += oField.GetValue(); sHeader += "\r\n"; } else if (sName.CompareNoCase("content-type") != 0) { sHeader += sName; sHeader += ": "; sHeader += oField.GetValue(); sHeader += "\r\n"; } iterField++; } String sBody = pBody->GetRawText(); String sNotification = Configuration::Instance()->GetServerMessages()->GetMessage("VIRUS_ATTACHMENT_REMOVED"); sBody = sNotification + sBody; String sData = sHeader; sData += "\r\n"; sData += sBody + "\r\n"; pMessage->SetSize(sData.GetLength()); String fileName = PersistentMessage::GetFileName(pMessage); FileUtilities::WriteToFile(fileName, sData, false); }
// set 'boundary' parameter (for multipart) of Content-Type field void MimeHeader::SetBoundary(const char* pszBoundary/*=NULL*/) { static int s_nPartNumber = 0; char buf[80]; if (!pszBoundary) // generate a new boundary delimeter { ::srand(((unsigned)::time(NULL)) ^ (unsigned)this); ::sprintf(buf, "__=_Part_Boundary_%03d_%06d.%06d", ++s_nPartNumber, rand(), rand()); if (s_nPartNumber >= 9) s_nPartNumber = 0; pszBoundary = buf; } MimeField *pfd = GetField(CMimeConst::ContentType()); if (!pfd) { MimeField fd; fd.SetName(CMimeConst::ContentType()); fd.SetValue("multipart/mixed"); fd.SetParameter(CMimeConst::Boundary(), pszBoundary); m_listFields.push_back(fd); } else { if (::_memicmp(pfd->GetValue(), "multipart", 9) != 0) pfd->SetValue("multipart/mixed"); pfd->SetParameter(CMimeConst::Boundary(), pszBoundary); } }
// get the subtype string MimeHeader::GetSubType() const { string strSubType; MimeField *pfd = GetField(CMimeConst::ContentType()); if (pfd != NULL) { string strType; pfd->GetValue(strType); string::size_type nSlash = strType.find('/'); if (nSlash > 0) strSubType = strType.substr(nSlash+1); } else strSubType = "plain"; return strSubType; }
// load a header from string buffer int MimeHeader::Load(const char* pszData, int nDataSize, bool unfold) { ASSERT(pszData != NULL); int nInput = 0; while (pszData[nInput] != 0 && pszData[nInput] != '\r') { MimeField fd; int nSize = fd.Load(pszData+nInput, nDataSize-nInput, unfold); if (nSize <= 0) return nSize; nInput += nSize; m_listFields.push_back(fd); // don't use SetField in case of same name fields } return nInput + 2; // skip the ending CRLF }
void MimeHeader::SetUnicodeFieldValue(const AnsiString &sFieldName, const String & sFieldValue, const AnsiString & sCharset) { // Retrieve the current charset for this field. MimeField* pfd = GetField(sFieldName); if (sFieldValue.GetLength() == 0) { SetRawFieldValue(sFieldName, "", ""); return; } AnsiString sCharsetToUse = sCharset; // If client hasn't specified character set, assume it's the existing one. if (pfd && pfd->GetCharset() && sCharsetToUse.IsEmpty()) sCharsetToUse = pfd->GetCharset(); // If there's no existing charset, assume it's the same as for the email. if (sCharsetToUse.IsEmpty()) sCharsetToUse = GetCharset(); if (sCharsetToUse.IsEmpty()) sCharsetToUse = "utf-8"; AnsiString sMBText = Charset::ToMultiByte(sFieldValue, sCharsetToUse); // Encode the value FieldCodeBase* pCoder = MimeEnvironment::CreateFieldCoder(sFieldName); pCoder->SetInput(sMBText, sMBText.GetLength(), true); pCoder->SetCharset(sCharsetToUse.c_str()); AnsiString sEncodedValue; pCoder->GetOutput(sEncodedValue); delete pCoder; SetRawFieldValue(sFieldName, sEncodedValue, sCharsetToUse); }
// set the 'charset' parameter (for text) of Content-Type field void MimeHeader::SetCharset(const char* pszCharset) { MimeField *pfd = GetField(CMimeConst::ContentType()); if (!pfd) { MimeField fd; fd.SetName(CMimeConst::ContentType()); fd.SetValue("text/plain"); fd.SetParameter(CMimeConst::Charset(), pszCharset); m_listFields.push_back(fd); } else pfd->SetParameter(CMimeConst::Charset(), pszCharset); }
// set the 'name' parameter (for attachment) of Content-Type field void MimeHeader::SetName(const char* pszName) { MimeField *pfd = GetField(CMimeConst::ContentType()); if (!pfd) { // get the appropriate media-type/subtype according to file extension ASSERT(pszName != NULL); string strType; const char* pszType = "application/octet-stream"; const char* pszFileExt = ::strrchr(pszName, '.'); if (pszFileExt != NULL) { pszFileExt++; int nIndex = 0; while (m_TypeCvtTable[nIndex].nMediaType != MEDIA_UNKNOWN) { if (!::_stricmp(pszFileExt, m_TypeCvtTable[nIndex].pszFileExt)) { strType = m_TypeTable[m_TypeCvtTable[nIndex].nMediaType]; strType += '/'; strType += m_TypeCvtTable[nIndex].pszSubType; pszType = strType.c_str(); break; } nIndex++; } } MimeField fd; fd.SetName(CMimeConst::ContentType()); fd.SetValue(pszType); fd.SetParameter(CMimeConst::Name(), pszName); m_listFields.push_back(fd); } else pfd->SetParameter(CMimeConst::Name(), pszName); }
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; }