|
|
// ----------------------------------------------------------------------------------------------------------
// M A I L U T I L . C P P
// ----------------------------------------------------------------------------------------------------------
#include "pch.hxx"
#include "demand.h"
#include "resource.h"
#include "mimeole.h"
#include "mimeutil.h"
#include "strconst.h"
#include "url.h"
#include "mailutil.h"
#include <spoolapi.h>
#include <fonts.h>
#include "instance.h"
#include "pop3task.h"
#include <ntverp.h>
#include "msgfldr.h"
#include "storutil.h"
#include "note.h"
#include "shlwapip.h"
#include <iert.h>
#include "storecb.h"
#include "conman.h"
#include "multiusr.h"
#include "ipab.h"
#include "secutil.h"
// ----------------------------------------------------------------------------------------------------------
// Folder Dialog Info
// ----------------------------------------------------------------------------------------------------------
typedef struct FLDRDLG_tag { FOLDERID idFolder; BOOL fPending; CStoreDlgCB *pCallback; } FLDRDLG, *PFLDRDLG;
// ----------------------------------------------------------------------------------------------------------
// Prototypes
// ----------------------------------------------------------------------------------------------------------
INT_PTR CALLBACK MailUtil_FldrDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK WebPageDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); HRESULT HrDlgCreateWebPage(HWND hwndDlg);
// ----------------------------------------------------------------------------------------------------------
// DoFolderDialog
// ----------------------------------------------------------------------------------------------------------
void MailUtil_DoFolderDialog(HWND hwndParent, FOLDERID idFolder) { FLDRDLG fdlg;
fdlg.idFolder = idFolder; fdlg.fPending = FALSE; fdlg.pCallback = new CStoreDlgCB; if (fdlg.pCallback == NULL) // TODO: an error message might be nice
return;
DialogBoxParam(g_hLocRes, MAKEINTRESOURCE(iddNewFolder), hwndParent, MailUtil_FldrDlgProc, (LPARAM)&fdlg);
fdlg.pCallback->Release(); }
// ----------------------------------------------------------------------------------------------------------
// FldrDlgProc
// ----------------------------------------------------------------------------------------------------------
INT_PTR CALLBACK MailUtil_FldrDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { PFLDRDLG pfdlg; TCHAR sz[CCHMAX_STRINGRES]; HWND hwndT; HRESULT hr; WORD id; FOLDERINFO Folder;
Assert(CCHMAX_STRINGRES > CCHMAX_FOLDER_NAME);
switch(msg) { case WM_INITDIALOG: pfdlg = (PFLDRDLG)lParam; Assert(pfdlg != NULL); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pfdlg);
hwndT = GetDlgItem(hwnd, idtxtFolderName); Assert(hwndT != NULL); SetIntlFont(hwndT); SendMessage(hwndT, EM_LIMITTEXT, CCHMAX_FOLDER_NAME, 0);
LoadString(g_hLocRes, idsRenameFolderTitle, sz, ARRAYSIZE(sz)); SetWindowText(hwnd, sz);
hr = g_pStore->GetFolderInfo(pfdlg->idFolder, &Folder); if (!FAILED(hr)) { SetWindowText(hwndT, Folder.pszName); g_pStore->FreeRecord(&Folder); }
pfdlg->pCallback->Initialize(hwnd);
SendMessage(hwndT, EM_SETSEL, 0, -1);
CenterDialog(hwnd); return(TRUE);
case WM_STORE_COMPLETE: pfdlg = (PFLDRDLG)GetDlgThisPtr(hwnd);
Assert(pfdlg->fPending); pfdlg->fPending = FALSE;
hr = pfdlg->pCallback->GetResult(); if (hr == S_FALSE) { EndDialog(hwnd, IDCANCEL); } else if (FAILED(hr)) { // No need to put up error dialog, CStoreDlgCB already did this on failed OnComplete
/*
AthErrorMessageW(hwnd, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsErrRenameFld), hr); */ hwndT = GetDlgItem(hwnd, idtxtFolderName); SendMessage(hwndT, EM_SETSEL, 0, -1); SetFocus(hwndT); } else { EndDialog(hwnd, IDOK); } break;
case WM_COMMAND: pfdlg = (PFLDRDLG)GetDlgThisPtr(hwnd);
id=GET_WM_COMMAND_ID(wParam, lParam);
if (id == IDOK) { if (pfdlg->fPending) break;
pfdlg->pCallback->Reset();
hwndT = GetDlgItem(hwnd, idtxtFolderName); GetWindowText(hwndT, sz, ARRAYSIZE(sz));
hr = g_pStore->RenameFolder(pfdlg->idFolder, sz, NOFLAGS, (IStoreCallback *)pfdlg->pCallback); if (hr == E_PENDING) { pfdlg->fPending = TRUE; } else if (FAILED(hr)) { AthErrorMessageW(hwnd, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsErrRenameFld), hr); SendMessage(hwndT, EM_SETSEL, 0, -1); SetFocus(hwndT); } else { EndDialog(hwnd, IDOK); } } else if (id==IDCANCEL) { if (pfdlg->fPending) pfdlg->pCallback->Cancel(); else EndDialog(hwnd, IDCANCEL); } break; } return FALSE; }
//
// FUNCTION: MailUtil_OnImportAddressBook()
//
// PURPOSE: Calls the WAB migration code to handle import/export.
//
// PARAMETERS:
// <in> fImport - TRUE if we should import, FALSE to export.
//
HRESULT MailUtil_OnImportExportAddressBook(HWND hwnd, BOOL fImport) { OFSTRUCT of; HFILE hfile; TCHAR szParam[255]; LPTSTR lpParam = fImport ? _T("/import") : _T("/export");
StrCpyN(szParam, lpParam, ARRAYSIZE(szParam)); //MU_GetCurrentUserInfo(szParam+13, ARRAYSIZE(szParam) - 13, NULL, 0);
hfile = OpenFile((TCHAR *)c_szWabMigExe, &of, OF_EXIST); if (hfile == HFILE_ERROR) return(E_FAIL);
ShellExecute(hwnd, _T("Open"), of.szPathName, szParam, NULL, SW_SHOWNORMAL);
return(S_OK); }
HRESULT HrSendWebPageDirect(LPWSTR pwszUrl, HWND hwnd, BOOL fModal, BOOL fMail, FOLDERID folderID, BOOL fIncludeSig, IUnknown *pUnkPump, IMimeMessage *pMsg) { HRESULT hr; LPSTREAM pstm=NULL; HCURSOR hcur=0; INIT_MSGSITE_STRUCT initStruct; DWORD dwCreateFlags = OENCF_SENDIMMEDIATE|OENCF_USESTATIONERYFONT; HCHARSET hCharset; ENCODINGTYPE ietEncoding = IET_DECODED; BOOL fLittleEndian; LPSTR pszCharset = NULL;
hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
if (!pMsg) { //We were not passed pMessage, so we need to create one.
IF_FAILEXIT(hr = HrCreateMessage(&pMsg)); } else { pMsg->AddRef(); }
IF_FAILEXIT(hr = HrCreateBasedWebPage(pwszUrl, &pstm));
if (S_OK == HrIsStreamUnicode(pstm, &fLittleEndian)) { if (SUCCEEDED(MimeOleFindCharset("utf-8", &hCharset))) { pMsg->SetCharset(hCharset, CSET_APPLY_ALL); }
ietEncoding = IET_UNICODE; } else if((S_OK == GetHtmlCharset(pstm, &pszCharset)) && pszCharset) { if (SUCCEEDED(MimeOleFindCharset(pszCharset, &hCharset))) { pMsg->SetCharset(hCharset, CSET_APPLY_ALL); }
ietEncoding = IET_INETCSET; }
IF_FAILEXIT(hr = pMsg->SetTextBody(TXT_HTML, ietEncoding, NULL, pstm, NULL));
initStruct.dwInitType = OEMSIT_MSG; initStruct.pMsg = pMsg; initStruct.folderID = folderID;
if (!fIncludeSig) dwCreateFlags |= OENCF_NOSIGNATURE;
if (fModal) dwCreateFlags |= OENCF_MODAL;
if (!fMail) dwCreateFlags |= OENCF_NEWSFIRST;
IF_FAILEXIT(hr = CreateAndShowNote(OENA_STATIONERY, dwCreateFlags, &initStruct, hwnd, pUnkPump));
exit: if (hcur) SetCursor(hcur);
ReleaseObj(pMsg); ReleaseObj(pstm); return hr; }
HRESULT HrSendWebPage(HWND hwnd, BOOL fModal, BOOL fMail, FOLDERID folderID, IUnknown *pUnkPump) { HRESULT hr; LPMIMEMESSAGE pMsg=0; LPSTREAM pstm=0; INIT_MSGSITE_STRUCT initStruct; DWORD dwCreateFlags = OENCF_SENDIMMEDIATE; LPSTR pszCharset; HCHARSET hCharset; BOOL fLittleEndian; ENCODINGTYPE ietEncoding=IET_INETCSET;
if(DialogBoxParam(g_hLocRes, MAKEINTRESOURCE(iddWebPage), hwnd, WebPageDlgProc, (LPARAM)&pstm)==IDCANCEL) return NOERROR;
hr=HrCreateMessage(&pMsg); if (FAILED(hr)) goto error;
// [SBAILEY]: Raid 23209: OE: File/Send Web Page sends all pages as Latin-1 even if they aren't
if (S_OK == HrIsStreamUnicode(pstm, &fLittleEndian)) { if (SUCCEEDED(MimeOleFindCharset("utf-8", &hCharset))) { pMsg->SetCharset(hCharset, CSET_APPLY_ALL); }
ietEncoding = IET_UNICODE; }
else if (SUCCEEDED(GetHtmlCharset(pstm, &pszCharset))) { if (SUCCEEDED(MimeOleFindCharset(pszCharset, &hCharset))) { pMsg->SetCharset(hCharset, CSET_APPLY_ALL); }
MemFree(pszCharset); }
hr=pMsg->SetTextBody(TXT_HTML, ietEncoding, NULL, pstm, NULL); if (FAILED(hr)) goto error;
if (fModal) dwCreateFlags |= OENCF_MODAL;
if (!fMail) dwCreateFlags |= OENCF_NEWSFIRST;
initStruct.dwInitType = OEMSIT_MSG; initStruct.pMsg = pMsg; initStruct.folderID = folderID;
hr = CreateAndShowNote(OENA_WEBPAGE, dwCreateFlags, &initStruct, hwnd, pUnkPump);
error: ReleaseObj(pMsg); ReleaseObj(pstm); return hr; }
static const HELPMAP g_rgCtxMapWebPage[] = { {idTxtWebPage, 50210}, {0, 0} };
INT_PTR CALLBACK WebPageDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_INITDIALOG: { int iTxtLength; HWND hwndEdit = GetDlgItem(hwnd, idTxtWebPage);
Assert(hwndEdit); Assert(lParam!= NULL);
SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM)lParam); SendDlgItemMessage(hwnd, idTxtWebPage, EM_LIMITTEXT, MAX_PATH-1, NULL);
SetFocus(hwndEdit); SHAutoComplete(hwndEdit, SHACF_URLALL); CenterDialog(hwnd); return FALSE; }
case WM_HELP: case WM_CONTEXTMENU: return OnContextHelp(hwnd, msg, wParam, lParam, g_rgCtxMapWebPage);
case WM_COMMAND: { int id = GET_WM_COMMAND_ID(wParam, lParam); HWND hwndEdit = GetDlgItem(hwnd, idTxtWebPage);
switch(id) { case IDOK: if (FAILED(HrDlgCreateWebPage(hwnd))) { AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsErrSendWebPageUrl), NULL, MB_OK); SendMessage(hwndEdit, EM_SETSEL, 0, -1); SetFocus(hwndEdit); return 0; }
// fall thro'
case IDCANCEL: EndDialog(hwnd, id); break; } } } return FALSE; }
static const CHAR c_wszHTTP[] = "http://"; HRESULT HrDlgCreateWebPage(HWND hwndDlg) { WCHAR wszUrl[MAX_PATH+1], wszUrlCanon[MAX_PATH + 10 + 1]; DWORD cCanon = ARRAYSIZE(wszUrlCanon); LPSTREAM *ppstm = NULL; HRESULT hr = E_FAIL; HCURSOR hcur=0;
*wszUrlCanon = 0;
ppstm = (LPSTREAM *)GetWindowLongPtr(hwndDlg, DWLP_USER);
if(!GetWindowTextWrapW(GetDlgItem(hwndDlg, idTxtWebPage), wszUrl, ARRAYSIZE(wszUrl))) goto exit;
hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
IF_FAILEXIT(hr = UrlApplySchemeW(wszUrl, wszUrlCanon, &cCanon, URL_APPLY_DEFAULT|URL_APPLY_GUESSSCHEME|URL_APPLY_GUESSFILE));
// If UrlApplyScheme returns S_FALSE, then it thought that the original works just fine, so use original
IF_FAILEXIT(hr = HrCreateBasedWebPage((S_FALSE == hr) ? wszUrl : wszUrlCanon, ppstm));
exit: if (hcur) SetCursor(hcur); return hr; }
HRESULT HrSaveMessageInFolder(HWND hwnd, IMessageFolder *pfldr, LPMIMEMESSAGE pMsg, MESSAGEFLAGS dwFlags, MESSAGEID *pNewMsgid, BOOL fSaveChanges) { CStoreCB *pCB; HRESULT hr;
Assert(pfldr != NULL);
pCB = new CStoreCB; if (pCB == NULL) return(E_OUTOFMEMORY);
hr = pCB->Initialize(hwnd, MAKEINTRESOURCE(idsSavingToFolder), TRUE); if (SUCCEEDED(hr)) { hr = SaveMessageInFolder((IStoreCallback *)pCB, pfldr, pMsg, dwFlags, pNewMsgid, fSaveChanges); if (hr == E_PENDING) hr = pCB->Block();
pCB->Close(); }
pCB->Release();
return(hr); }
HRESULT SaveMessageInFolder(IStoreCallback *pStoreCB, IMessageFolder *pfldr, LPMIMEMESSAGE pMsg, MESSAGEFLAGS dwFlags, MESSAGEID *pNewMsgid, BOOL fSaveChanges) { // Locals
HRESULT hr=S_OK; HRESULT hrWarnings=S_OK; MESSAGEID msgid;
// Trace
TraceCall("HrSaveMessageInFolder");
// Invalid Args
if (pMsg == NULL || pfldr == NULL) return TraceResult(E_INVALIDARG);
// Don't save changes, then clear the dirty bit
if (FALSE == fSaveChanges) MimeOleClearDirtyTree(pMsg);
// Save the message
IF_FAILEXIT(hr = pMsg->Commit(0));
// Insert the Message
hr = pfldr->SaveMessage(pNewMsgid, SAVE_MESSAGE_GENID, dwFlags, 0, pMsg, pStoreCB);
exit: // Done
return (hr == S_OK ? hrWarnings : hr); }
HRESULT SaveMessageInFolder(IStoreCallback *pStoreCB, FOLDERID idFolder, LPMIMEMESSAGE pMsg, MESSAGEFLAGS dwFlags, MESSAGEID *pNewMsgid) { // Locals
HRESULT hr;
IMessageFolder *pfldr=NULL;
// Open the Folder..
hr = g_pStore->OpenFolder(idFolder, NULL, NOFLAGS, &pfldr); if (SUCCEEDED(hr)) { hr = SaveMessageInFolder(pStoreCB, pfldr, pMsg, dwFlags, pNewMsgid, TRUE); pfldr->Release(); }
return hr; }
HRESULT HrSaveMessageInFolder(HWND hwnd, FOLDERID idFolder, LPMIMEMESSAGE pMsg, MESSAGEFLAGS dwFlags, MESSAGEID *pNewMsgid) { // Locals
HRESULT hr;
IMessageFolder *pfldr=NULL;
// Open the Folder..
hr = g_pStore->OpenFolder(idFolder, NULL, NOFLAGS, &pfldr); if (SUCCEEDED(hr)) { hr = HrSaveMessageInFolder(hwnd, pfldr, pMsg, dwFlags, pNewMsgid, TRUE); pfldr->Release(); }
return hr; }
HRESULT HrSendMailToOutBox(HWND hwndOwner, LPMIMEMESSAGE pMsg, BOOL fSendImmediate, BOOL fNoUI, BOOL fMail) { CStoreCB *pCB; HRESULT hr;
pCB = new CStoreCB; if (pCB == NULL) return(E_OUTOFMEMORY);
hr = pCB->Initialize(hwndOwner, MAKEINTRESOURCE(idsSendingToOutbox), TRUE); if (SUCCEEDED(hr)) { hr = SendMailToOutBox((IStoreCallback *)pCB, pMsg, fSendImmediate, fNoUI, fMail); if (hr == E_PENDING) hr = pCB->Block();
pCB->Close(); }
pCB->Release();
return(hr); }
HRESULT SendMailToOutBox(IStoreCallback *pStoreCB, LPMIMEMESSAGE pMsg, BOOL fSendImmediate, BOOL fNoUI, BOOL fMail) { HRESULT hr; FOLDERINFO Outbox; const TCHAR c_szXMailerAndNewsReader[] = "Microsoft Outlook Express " VER_PRODUCTVERSION_STR; DWORD dwSendFlags = fMail ? ARF_SUBMITTED|ARF_UNSENT : ARF_SUBMITTED|ARF_UNSENT|ARF_NEWSMSG; HWND hwnd = 0; BOOL fSecure = IsSecure(pMsg);
Assert(pStoreCB); pStoreCB->GetParentWindow(0, &hwnd);
hr = g_pStore->GetSpecialFolderInfo(FOLDERID_LOCAL_STORE, FOLDER_OUTBOX, &Outbox); if (FAILED(hr)) return hr;
// make sure we never send mail with the X-Unsent header on it.
pMsg->DeleteBodyProp(HBODY_ROOT, PIDTOSTR(PID_HDR_XUNSENT));
if (fMail) { // pound the X-Mailer prop always for anyone going' thro' our spooler.
MimeOleSetBodyPropA(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_XMAILER), NOFLAGS, c_szXMailerAndNewsReader); } else { DWORD dwLines; TCHAR rgch[12]; HrComputeLineCount(pMsg, &dwLines); wnsprintf(rgch, ARRAYSIZE(rgch), "%d", dwLines); MimeOleSetBodyPropA(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_LINES), NOFLAGS, rgch); MimeOleSetBodyPropA(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_XNEWSRDR), NOFLAGS, c_szXMailerAndNewsReader); }
hr = SaveMessageInFolder(pStoreCB, Outbox.idFolder, pMsg, dwSendFlags, NULL); if (FAILED(hr)) goto error;
// if immediate send is required, tell the spooler to pick up next cycle
// or start a cycle...
if (fSendImmediate) { Assert(g_pSpooler); if (fMail) g_pSpooler->StartDelivery(hwnd, NULL, FOLDERID_INVALID, DELIVER_BACKGROUND | DELIVER_QUEUE | DELIVER_MAIL_SEND | DELIVER_NOSKIP); else { PROPVARIANT var;
var.vt = VT_LPSTR; hr = pMsg->GetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &var); if (FAILED(hr)) var.pszVal = NULL;
if (S_OK == g_pConMan->CanConnect(var.pszVal)) g_pSpooler->StartDelivery(hwnd, var.pszVal, FOLDERID_INVALID, DELIVER_BACKGROUND | DELIVER_NOSKIP | DELIVER_NEWS_SEND); else { // Warn the user that this message is going to live in their
// outbox for all of eternity
DoDontShowMeAgainDlg(hwnd, c_szDSPostInOutbox, MAKEINTRESOURCE(idsPostNewsMsg), MAKEINTRESOURCE(idsPostInOutbox), MB_OK); hr = S_FALSE; } SafeMemFree(var.pszVal); } } else if (!fNoUI) { HWND hwnd = 0; pStoreCB->GetParentWindow(0, &hwnd);
AssertSz(hwnd, "How did we not get an hwnd???");
// warn the user, before we close if it will be stacked in the outbox.
DoDontShowMeAgainDlg(hwnd, fMail?c_szDSSendMail:c_szDSSendNews, MAKEINTRESOURCE(fMail?idsSendMail:idsPostNews), MAKEINTRESOURCE(fMail?idsMailInOutbox:idsPostInOutbox), MB_OK); }
error: // Cleanup
g_pStore->FreeRecord(&Outbox); return hr; }
HRESULT HrSetSenderInfoUtil(IMimeMessage *pMsg, IImnAccount *pAccount, LPWABAL lpWabal, BOOL fMail, CODEPAGEID cpID, BOOL fCheckConflictOnly) { HRESULT hr = S_OK;
// Don't set any info if no account.
if (pAccount) { char szEMail[CCHMAX_EMAIL_ADDRESS] = ""; char szName[CCHMAX_DISPLAY_NAME] = ""; char szOrg[CCHMAX_ORG_NAME] = ""; BOOL fUseEmailAsName = FALSE; DWORD propID;
if (fCheckConflictOnly) { hr = S_OK; propID = fMail ? AP_SMTP_DISPLAY_NAME : AP_NNTP_DISPLAY_NAME; if (SUCCEEDED(pAccount->GetPropSz(propID, szName, ARRAYSIZE(szName))) && *szName) { IF_FAILEXIT(hr = HrSafeToEncodeToCPA(szName, CP_ACP, cpID)); if (MIME_S_CHARSET_CONFLICT == hr) goto exit; }
propID = fMail ? AP_SMTP_ORG_NAME : AP_NNTP_ORG_NAME; if (SUCCEEDED(pAccount->GetPropSz(propID, szOrg, ARRAYSIZE(szOrg))) && *szOrg) { IF_FAILEXIT(hr = HrSafeToEncodeToCPA(szOrg, CP_ACP, cpID)); if (MIME_S_CHARSET_CONFLICT == hr) goto exit; } } else { hr = hrNoSender; lpWabal->DeleteRecipType(MAPI_REPLYTO);
propID = fMail ? AP_SMTP_EMAIL_ADDRESS : AP_NNTP_EMAIL_ADDRESS; if (SUCCEEDED(pAccount->GetPropSz(propID, szEMail, ARRAYSIZE(szEMail))) && *szEMail) { propID = fMail ? AP_SMTP_DISPLAY_NAME : AP_NNTP_DISPLAY_NAME; // we've got enough to post
if (FAILED(pAccount->GetPropSz(propID, szName, ARRAYSIZE(szName))) && *szName) fUseEmailAsName = TRUE;
IF_FAILEXIT(hr = lpWabal->HrAddEntryA(fUseEmailAsName?szEMail:szName, szEMail, MAPI_ORIG)); }
propID = fMail ? AP_SMTP_REPLY_EMAIL_ADDRESS : AP_NNTP_REPLY_EMAIL_ADDRESS; if (SUCCEEDED(pAccount->GetPropSz(propID, szEMail, ARRAYSIZE(szEMail))) && *szEMail) IF_FAILEXIT(hr = lpWabal->HrAddEntryA((*szName)?szName:szEMail, szEMail, MAPI_REPLYTO));
propID = fMail ? AP_SMTP_ORG_NAME : AP_NNTP_ORG_NAME; if (SUCCEEDED(pAccount->GetPropSz(propID, szOrg, ARRAYSIZE(szOrg))) && *szOrg) { MimeOleSetBodyPropA(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_ORG), NOFLAGS, szOrg); } } }
exit: return hr; }
// This function takes an existing refererences line and appends a new
// references to the end removing any references necessary.
// NOTE these SHOULD be char and not TCHAR.
HRESULT HrCreateReferences(LPWSTR pszOrigRefs, LPWSTR pszNewRef, LPWSTR *ppszRefs) { UINT cch, cchOrig, cchNew;
// Validate the arguements
if (!pszNewRef || !*pszNewRef) { AssertSz(FALSE, TEXT("pszNewRef cannot be empty.")); return (E_INVALIDARG); }
// Figure out how long the new references line must be
cchNew = lstrlenW(pszNewRef);
// It's possible not to have original references if this is the first
// reply to an article.
if (pszOrigRefs && *pszOrigRefs) cchOrig = lstrlenW(pszOrigRefs); else cchOrig = 0;
cch = cchNew + cchOrig + 1; // extra is for the separator space
if (!MemAlloc((LPVOID*) ppszRefs, (cch + 1)*sizeof(WCHAR))) return (E_OUTOFMEMORY);
// The line length should be < 1000 chars. If it is, this is simple
if (cch <= 1000) { if (pszOrigRefs) wnsprintfW(*ppszRefs, cch+1, L"%s %s", pszOrigRefs, pszNewRef); else StrCpyNW(*ppszRefs, pszNewRef, cch+1); }
// Since cch > 1000, we have some extra work to do.
// We need to remove some references. The Son-of-1036 recommends to leave
// the first and last three. Unless the IDs are greater than 255, we will
// be able to do at least this. Otherwise, we will dump as many ids as
// needed to get below the 1000 char limit.
// For each ID removed, the Son-of-1036 says that we must add 3 spaces in
// place of the removed ID.
else { UINT cchMaxWithoutNewRef, // Max length that the orig size can be
cchNewOrigSize = cchOrig; // Size of orig after deletion. Always shows final size
LPWSTR pszNew = *ppszRefs, pszOld = pszOrigRefs; BOOL fCopiedFirstValidID = FALSE;
*pszNew = 0;
// Make sure the new ID is not too long. If it is, discard it.
if (cchNew > 255) { cchNew = 0; cchMaxWithoutNewRef = 1000; } else cchMaxWithoutNewRef = 1000 - cchNew - 1; // the space between
// parse the old string looking for ids
// Son-of-1036 says that we must try to keep the first and the most recent
// three IDs. So we will copy in the first valid ID and then follow a FIFO
// algorithm until we can fit the rest of the IDs into the 1000 char limit
while (pszOld && *pszOld) { // Is what is left from the original string too big for the left over buffer?
if (cchNewOrigSize >= cchMaxWithoutNewRef) { UINT cchEntryLen = 0; // Size of particular entry ID.
// If this is the first ID, make sure we copy into the buffer as we
// get length, as well, add the additional spaces required when any
// deletion will be happening. Since we only delete directly after
// the first valid ID, add the required 3 spaces now
if (!fCopiedFirstValidID) { while (*pszOld && *pszOld != L' ') { *pszNew++ = *pszOld++; cchEntryLen++; } *pszNew++ = L' '; *pszNew++ = L' '; *pszNew++ = L' '; cchNewOrigSize += 3; cchEntryLen += 3; } // If this in not the first ID, then just skip over it.
else { while (*pszOld && *pszOld != L' ') { pszOld++; cchEntryLen++; } }
// Skip over whitespace in old references between IDs that
// we are deleting anyway.
while (*pszOld == L' ') { pszOld++; cchNewOrigSize--; }
// If we already did the first, or the current one is invalid
// we need to do some fix up with sizes. And in the case that
// we copied one that is not valid, we need to reset the pointer
// as well as reset the size.
if (fCopiedFirstValidID || (cchEntryLen > 255)) { cchNewOrigSize -= cchEntryLen;
// Did we copy an invalid ID?
if (!fCopiedFirstValidID) pszNew -= cchEntryLen; }
// If we haven't copied the first one in yet and this
// ID is valid, then remember that we have at this
// point copied the first valid ID.
if (!fCopiedFirstValidID && (cchEntryLen <= 255)) fCopiedFirstValidID = TRUE; } else { // Since we now have a orig string that will fit in the max allowed,
// just rip through the rest of the orig string and copy.
while (*pszOld) *pszNew++ = *pszOld++; } }
// At this point, pszNew should be pointing the the char directly after
// the last digit. If we add a new reference, then we need to add a space.
// If we don't add a new reference, then we need to null terminate the string.
if (cchNew) { // With this assignment, we can end up with 4 spaces in a row if we
// deleted all references after the first valid one was copied. The
// son-of-1036 only specifies minimum of 3 spaces when deleting, so
// we will be OK with that, especially since the only way to get into
// this situation is by forcing the references line into a strange state.
*pszNew++ = L' '; pszOld = pszNewRef; while (*pszOld) *pszNew++ = *pszOld++; }
// NULL terminate the string of references.
*pszNew = 0; } return S_OK; }
|