bool CMapiMessage::CopyBinAttachToFile(LPATTACH lpAttach, nsIFile **tmp_file) { nsCOMPtr<nsIFile> _tmp_file; nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR, "mapiattach.tmp", getter_AddRefs(_tmp_file)); NS_ENSURE_SUCCESS(rv, false); rv = _tmp_file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600); NS_ENSURE_SUCCESS(rv, false); nsCString tmpPath; _tmp_file->GetNativePath(tmpPath); LPSTREAM lpStreamFile; HRESULT hr = CMapiApi::OpenStreamOnFile(gpMapiAllocateBuffer, gpMapiFreeBuffer, STGM_READWRITE | STGM_CREATE, const_cast<char*>(tmpPath.get()), NULL, &lpStreamFile); if (HR_FAILED(hr)) { MAPI_TRACE1("~~ERROR~~ OpenStreamOnFile failed - temp path: %s\r\n", tmpPath.get()); return false; } bool bResult = true; LPSTREAM lpAttachStream; hr = lpAttach->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, 0, 0, (LPUNKNOWN *)&lpAttachStream); if (HR_FAILED(hr)) { MAPI_TRACE0("~~ERROR~~ OpenProperty failed for PR_ATTACH_DATA_BIN.\r\n"); lpAttachStream = NULL; bResult = false; } else { STATSTG st; hr = lpAttachStream->Stat(&st, STATFLAG_NONAME); if (HR_FAILED(hr)) { MAPI_TRACE0("~~ERROR~~ Stat failed for attachment stream\r\n"); bResult = false; } else { hr = lpAttachStream->CopyTo(lpStreamFile, st.cbSize, NULL, NULL); if (HR_FAILED(hr)) { MAPI_TRACE0("~~ERROR~~ Attach Stream CopyTo temp file failed.\r\n"); bResult = false; } } } if (lpAttachStream) lpAttachStream->Release(); lpStreamFile->Release(); if (!bResult) _tmp_file->Remove(false); else CallQueryInterface(_tmp_file, tmp_file); return bResult; }
bool CMapiMessage::CopyMsgAttachToFile(LPATTACH lpAttach, /*out*/ nsIFile **tmp_file) { bool bResult = true; LPMESSAGE lpMsg; HRESULT hr = lpAttach->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, 0, reinterpret_cast<LPUNKNOWN *>(&lpMsg)); NS_ENSURE_SUCCESS(hr, false); if (!GetTmpFile(tmp_file)) return false; nsCOMPtr<nsIOutputStream> destOutputStream; nsresult rv = MsgNewBufferedFileOutputStream(getter_AddRefs(destOutputStream), *tmp_file, -1, 0600); if (NS_SUCCEEDED(rv)) rv = nsOutlookMail::ImportMessage(lpMsg, destOutputStream, nsIMsgSend::nsMsgSaveAsDraft); if (NS_FAILED(rv)) { (*tmp_file)->Remove(false); (*tmp_file)->Release(); *tmp_file = 0; } return NS_SUCCEEDED(rv); }
BOOL CMapiMessage::GetAttachmentInfo( int idx) { ClearTempAttachFile(); BOOL bResult = TRUE; if ((idx < 0) || (idx >= (int)m_attachNums.Length())) { return( FALSE); } DWORD aNum = m_attachNums[idx]; LPATTACH lpAttach = NULL; HRESULT hr = m_lpMsg->OpenAttach( aNum, NULL, 0, &lpAttach); if (HR_FAILED( hr)) { MAPI_TRACE2( "\t\t****Attachment error, unable to open attachment: %d, 0x%lx\r\n", idx, hr); return( FALSE); } LPSPropValue pVal; pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_MIME_TAG); if (pVal) CMapiApi::GetStringFromProp( pVal, m_attachMimeType); else m_attachMimeType.Truncate(); pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_METHOD); if (pVal) { LONG aMethod = CMapiApi::GetLongFromProp( pVal); if ((aMethod == ATTACH_BY_REF_ONLY) || (aMethod == ATTACH_BY_REFERENCE) || (aMethod == ATTACH_BY_REF_RESOLVE)) { m_attachPath.Truncate(); pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_PATHNAME); if (pVal) CMapiApi::GetStringFromProp( pVal, m_attachPath); MAPI_TRACE2("\t\t** Attachment #%d by ref: %s\r\n", idx, m_attachPath.get()); m_ownsAttachFile = FALSE; } else if (aMethod == ATTACH_BY_VALUE) { MAPI_TRACE1( "\t\t** Attachment #%d by value.\r\n", idx); bResult = CopyBinAttachToFile( lpAttach); } else if (aMethod == ATTACH_OLE) { MAPI_TRACE1( "\t\t** Attachment #%d by OLE - yuck!!!\r\n", idx); } else if (aMethod == ATTACH_EMBEDDED_MSG) { MAPI_TRACE1( "\t\t** Attachment #%d by Embedded Message??\r\n", idx); } else { MAPI_TRACE2( "\t\t** Attachment #%d unknown attachment method - 0x%lx\r\n", idx, aMethod); bResult = FALSE; } } else bResult = FALSE; nsCString fName, fExt; pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_LONG_FILENAME); if (pVal) CMapiApi::GetStringFromProp( pVal, fName); pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_EXTENSION); if (pVal) CMapiApi::GetStringFromProp( pVal, fExt); pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_SIZE); long sz = 0; if (pVal) sz = CMapiApi::GetLongFromProp( pVal); /* // I have no idea how this tag is used, how to interpret it's value, etc. // Fortunately, the Microsoft documentation is ABSOLUTELY NO HELP AT ALL. In fact, // if one goes by the docs and sample code, this tag is completely 100% useless. I'm // sure it has some important meaning which will one day be obvious, but for now, // it is ignored. pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_TAG); if (pVal) { ::MAPIFreeBuffer( pVal); } */ MAPI_TRACE1("\t\t\t--- Mime type: %s\r\n", m_attachMimeType.get()); MAPI_TRACE2("\t\t\t--- File name: %s, extension: %s\r\n", fName.get(), fExt.get()); MAPI_TRACE1("\t\t\t--- Size: %ld\r\n", sz); if (fExt.IsEmpty()) { int idx = fName.RFindChar( '.'); if (idx != -1) fName.Right( fExt, fName.Length() - idx); } if ((fName.RFindChar( '.') == -1) && !fExt.IsEmpty()) { fName += "."; fName += fExt; } m_attachFileName = fName; if (m_attachMimeType.IsEmpty()) { PRUint8 *pType = NULL; if (!fExt.IsEmpty()) { pType = CMimeTypes::GetMimeType( fExt); } if (pType) m_attachMimeType = (PC_S8)pType; else m_attachMimeType = "application/octet-stream"; } pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_TRANSPORT_NAME); if (pVal) { CMapiApi::GetStringFromProp( pVal, fName); MAPI_TRACE1("\t\t\t--- Transport name: %s\r\n", fName.get()); } lpAttach->Release(); return( bResult); }
BOOL CMapiMessage::CopyBinAttachToFile( LPATTACH lpAttach) { LPSTREAM lpStreamFile; m_ownsAttachFile = FALSE; m_attachPath.Truncate(); nsCOMPtr<nsIFile> tmpFile; nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR, "mapiattach.tmp", getter_AddRefs(tmpFile)); NS_ENSURE_SUCCESS(rv, rv); rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600); NS_ENSURE_SUCCESS(rv, rv); nsCString tmpPath; tmpFile->GetNativePath(tmpPath); HRESULT hr = CMapiApi::OpenStreamOnFile( gpMapiAllocateBuffer, gpMapiFreeBuffer, STGM_READWRITE | STGM_CREATE, (char *) tmpPath.get(), NULL, &lpStreamFile); if (HR_FAILED(hr)) { MAPI_TRACE1("~~ERROR~~ OpenStreamOnFile failed - temp path: %s\r\n", tmpPath.get()); return( FALSE); } MAPI_TRACE1("\t\t** Attachment extracted to temp file: %s\r\n", m_attachPath.get()); BOOL bResult = TRUE; LPSTREAM lpAttachStream; hr = lpAttach->OpenProperty( PR_ATTACH_DATA_BIN, &IID_IStream, 0, 0, (LPUNKNOWN *)&lpAttachStream); if (HR_FAILED( hr)) { MAPI_TRACE0( "~~ERROR~~ OpenProperty failed for PR_ATTACH_DATA_BIN.\r\n"); lpAttachStream = NULL; bResult = FALSE; } else { STATSTG st; hr = lpAttachStream->Stat( &st, STATFLAG_NONAME); if (HR_FAILED( hr)) { MAPI_TRACE0( "~~ERROR~~ Stat failed for attachment stream\r\n"); bResult = FALSE; } else { hr = lpAttachStream->CopyTo( lpStreamFile, st.cbSize, NULL, NULL); if (HR_FAILED( hr)) { MAPI_TRACE0( "~~ERROR~~ Attach Stream CopyTo temp file failed.\r\n"); bResult = FALSE; } } } m_attachPath = tmpPath; if (lpAttachStream) lpAttachStream->Release(); lpStreamFile->Release(); if (!bResult) tmpFile->Remove(PR_FALSE); else m_ownsAttachFile = TRUE; return( bResult); }
/* This seems to be called if one clicks on Save in the context menu. And also sometimes before an Open click. */ STDMETHODIMP GpgolAttachedFileEvents::OnWritePattToSzFile (LPATTACH att, LPTSTR file, ULONG flags) { HRESULT hr; ULONG tag; char *iv; size_t ivlen; symenc_t symenc; LPSTREAM stream; char tmpbuf[16]; ULONG nread; int rc; log_debug ("%s:%s: att=%p file=`%s' flags=%lx\n", SRCNAME, __func__, att, file, flags); if (!att) return E_FAIL; if (get_gpgolprotectiv_tag ((LPMESSAGE)att, &tag) ) return E_ABORT; iv = mapi_get_binary_prop ((LPMESSAGE)att, tag, &ivlen); if (!iv) return S_FALSE; /* Not encrypted by us - Let OL continue as usual. */ symenc = symenc_open (get_128bit_session_key (), 16, iv, ivlen); xfree (iv); if (!symenc) { log_error ("%s:%s: can't open encryption context", SRCNAME, __func__); return E_ABORT; } hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 0, 0, (LPUNKNOWN*) &stream); if (FAILED (hr)) { log_error ("%s:%s: can't open data stream of attachment: hr=%#lx", SRCNAME, __func__, hr); symenc_close (symenc); return E_ABORT; } hr = stream->Read (tmpbuf, 16, &nread); if (hr) { log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr); stream->Release (); symenc_close (symenc); return E_ABORT; } symenc_cfb_decrypt (symenc, tmpbuf, tmpbuf, 16); if (memcmp (tmpbuf, "GpgOL attachment", 16)) { MessageBox (NULL, _("Sorry, we are not able to decrypt this attachment.\n\n" "Please use the decrypt/verify button to decrypt the\n" "entire message again. Then open this attachment."), "GpgOL", MB_ICONERROR|MB_OK); stream->Release (); symenc_close (symenc); return E_ABORT; } rc = decrypt_and_write_file (stream, file, symenc); stream->Release (); symenc_close (symenc); return rc; }
bool CMapiMessage::AddAttachment(DWORD aNum) { LPATTACH lpAttach = NULL; HRESULT hr = m_lpMsg->OpenAttach(aNum, NULL, 0, &lpAttach); if (HR_FAILED(hr)) { MAPI_TRACE2("\t\t****Attachment error, unable to open attachment: %d, 0x%lx\r\n", idx, hr); return false; } bool bResult = false; attach_data *data = new attach_data; ULONG aMethod; if (data) { bResult = true; // 1. Get the file that contains the attachment data LPSPropValue pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_METHOD); if (pVal) { aMethod = CMapiApi::GetLongFromProp(pVal); switch (aMethod) { case ATTACH_BY_VALUE: MAPI_TRACE1("\t\t** Attachment #%d by value.\r\n", aNum); bResult = CopyBinAttachToFile(lpAttach, getter_AddRefs(data->tmp_file)); data->delete_file = true; break; case ATTACH_BY_REFERENCE: case ATTACH_BY_REF_RESOLVE: case ATTACH_BY_REF_ONLY: pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_PATHNAME_W); if (pVal) { nsCString path; CMapiApi::GetStringFromProp(pVal, path); nsresult rv; data->tmp_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); if (NS_FAILED(rv) || !data->tmp_file) { MAPI_TRACE0("*** Error creating file spec for attachment\n"); bResult = false; } else data->tmp_file->InitWithNativePath(path); } MAPI_TRACE2("\t\t** Attachment #%d by ref: %s\r\n", aNum, m_attachPath.get()); break; case ATTACH_EMBEDDED_MSG: MAPI_TRACE1("\t\t** Attachment #%d by Embedded Message??\r\n", aNum); // Convert the embedded IMessage from PR_ATTACH_DATA_OBJ to rfc822 attachment // (see http://msdn.microsoft.com/en-us/library/cc842329.aspx) // This is a recursive call. bResult = CopyMsgAttachToFile(lpAttach, getter_AddRefs(data->tmp_file)); data->delete_file = true; break; case ATTACH_OLE: MAPI_TRACE1("\t\t** Attachment #%d by OLE - yuck!!!\r\n", aNum); break; default: MAPI_TRACE2("\t\t** Attachment #%d unknown attachment method - 0x%lx\r\n", aNum, aMethod); bResult = false; } } else bResult = false; if (bResult) bResult = data->tmp_file; if (bResult) { bool isFile = false; bool exists = false; data->tmp_file->Exists(&exists); data->tmp_file->IsFile(&isFile); if (!exists || !isFile) { bResult = false; MAPI_TRACE0("Attachment file does not exist\n"); } } if (bResult) bResult = GetURL(data->tmp_file, getter_AddRefs(data->orig_url)); if (bResult) { // Now we have the file; proceed to the other properties data->encoding = NS_strdup(ENCODING_BINARY); nsString fname, fext; pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_LONG_FILENAME_W); if (!pVal) pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_FILENAME_W); CMapiApi::GetStringFromProp(pVal, fname); pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_EXTENSION_W); CMapiApi::GetStringFromProp(pVal, fext); MAPI_TRACE2("\t\t\t--- File name: %s, extension: %s\r\n", fname.get(), fext.get()); if (fext.IsEmpty()) { int idx = fname.RFindChar(L'.'); if (idx != -1) fext = Substring(fname, idx); } else if (fname.RFindChar(L'.') == -1) { fname += L"."; fname += fext; } if (fname.IsEmpty()) { // If no description use "Attachment i" format. fname = L"Attachment "; fname.AppendInt(static_cast<uint32_t>(aNum)); } data->real_name = ToNewUTF8String(fname); nsCString tmp; // We have converted it to the rfc822 document if (aMethod == ATTACH_EMBEDDED_MSG) { data->type = NS_strdup(MESSAGE_RFC822); } else { pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_MIME_TAG_A); CMapiApi::GetStringFromProp(pVal, tmp); MAPI_TRACE1("\t\t\t--- Mime type: %s\r\n", tmp.get()); if (tmp.IsEmpty()) { uint8_t *pType = NULL; if (!fext.IsEmpty()) { pType = CMimeTypes::GetMimeType(fext); } if (pType) data->type = NS_strdup((PC_S8)pType); else data->type = NS_strdup(APPLICATION_OCTET_STREAM); } else data->type = ToNewCString(tmp); } pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_CONTENT_ID_A); CMapiApi::GetStringFromProp(pVal, tmp); if (!tmp.IsEmpty()) data->cid = ToNewCString(tmp); } if (bResult) { // Now we need to decide if this attachment is embedded or not. // At first, I tried to simply check for the presence of the Content-Id. // But it turned out that this method is unreliable, since there exist cases // when an attachment has a Content-Id while isn't embedded (even in a message // with a plain-text body!). So next I tried to look for <img> tags that contain // the found Content-Id. But this is unreliable, too, because there exist cases // where other places of HTML reference the embedded messages (e.g. it may be // a background of a table cell, or some CSS; further, it is possible that the // reference to an embedded object is not in the main body, but in another // embedded object - like body references a CSS attachment that in turn references // a picture as a background of its element). From the other hand, it's unreliable // to relax the search criteria to any occurence of the Content-Id string in the body - // partly because the string may be simply in a text or other non-referencing part, // partly because of the abovementioned possibility that the reference is outside // the body at all. // There exist the PR_ATTACH_FLAGS property of the attachment. The MS documentation // tells about two possible flags in it: ATT_INVISIBLE_IN_HTML and ATT_INVISIBLE_IN_RTF. // There is at least one more undocumented flag: ATT_MHTML_REF. Some sources in Internet // suggest simply check for the latter flag to distinguish between the embedded // and ordinary attachments. But my observations indicate that even if the flags // don't include ATT_MHTML_REF, the attachment is still may be embedded. // However, my observations always show that the message is embedded if the flags // is not 0. // So now I will simply test for the non-zero flags to decide whether the attachment // is embedded or not. Possible advantage is reliability (I hope). // Another advantage is that it's much faster than search the body for Content-Id. DWORD flags = 0; pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_FLAGS); if (pVal) flags = CMapiApi::GetLongFromProp(pVal); if (m_bodyIsHtml && data->cid && (flags != 0)) // this is the embedded attachment m_embattachments.push_back(data); else // this is ordinary attachment m_stdattachments.push_back(data); } else { delete data; } } lpAttach->Release(); return bResult; }