1/////////////////////////////////////////////////////////////////////////////
2// Name:        smapi.cpp
3// Purpose:     Simple MAPI classes
4// Author:      PJ Naughter <pjna@naughter.com>
5// Modified by: Julian Smart
6// Created:     2001-08-21
7// RCS-ID:      $Id: smapi.cpp 35650 2005-09-23 12:56:45Z MR $
8// Copyright:   (c) PJ Naughter
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// For compilers that support precompilation, includes "wx/wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16#pragma hdrstop
17#endif
18
19#ifdef __WXMSW__
20
21#ifndef WX_PRECOMP
22#include "wx/wx.h"
23#endif
24
25#include "wx/string.h"
26#include "wx/msw/private.h"
27
28// mapi.h in Cygwin's include directory isn't a full implementation and is
29// not sufficient for this lib. However recent versions of Cygwin also
30// have another mapi.h in include/w32api which can be used.
31//
32#ifdef __CYGWIN__
33#include <w32api/mapi.h>
34#else
35#include <mapi.h>
36#endif
37
38#include "wx/net/smapi.h"
39
40class WXDLLIMPEXP_NETUTILS wxMapiData
41{
42public:
43    wxMapiData()
44    {
45        m_hSession = 0;
46        m_nLastError = 0;
47        m_hMapi = NULL;
48        m_lpfnMAPILogon = NULL;
49        m_lpfnMAPILogoff = NULL;
50        m_lpfnMAPISendMail = NULL;
51        m_lpfnMAPIResolveName = NULL;
52        m_lpfnMAPIFreeBuffer = NULL;
53    }
54
55    //Data
56    LHANDLE             m_hSession; //Mapi Session handle
57    long                m_nLastError; //Last Mapi error value
58    HINSTANCE           m_hMapi; //Instance handle of the MAPI dll
59    LPMAPILOGON         m_lpfnMAPILogon; //MAPILogon function pointer
60    LPMAPILOGOFF        m_lpfnMAPILogoff; //MAPILogoff function pointer
61    LPMAPISENDMAIL      m_lpfnMAPISendMail; //MAPISendMail function pointer
62    LPMAPIRESOLVENAME   m_lpfnMAPIResolveName; //MAPIResolveName function pointer
63    LPMAPIFREEBUFFER    m_lpfnMAPIFreeBuffer; //MAPIFreeBuffer function pointer
64};
65
66
67////////////////////////////////// Implementation /////////////////////////////
68
69wxMapiSession::wxMapiSession()
70{
71    m_data = new wxMapiData;
72
73    Initialise();
74}
75
76wxMapiSession::~wxMapiSession()
77{
78    //Logoff if logged on
79    Logoff();
80
81    //Unload the MAPI dll
82    Deinitialise();
83
84    delete m_data;
85}
86
87void wxMapiSession::Initialise()
88{
89    //First make sure the "WIN.INI" entry for MAPI is present aswell
90    //as the MAPI32 dll being present on the system
91    bool bMapiInstalled = (GetProfileInt(_T("MAIL"), _T("MAPI"), 0) != 0) &&
92        (SearchPath(NULL, _T("MAPI32.DLL"), NULL, 0, NULL, NULL) != 0);
93
94    if (bMapiInstalled)
95    {
96        //Load up the MAPI dll and get the function pointers we are interested in
97        m_data->m_hMapi = ::LoadLibrary(_T("MAPI32.DLL"));
98        if (m_data->m_hMapi)
99        {
100            m_data->m_lpfnMAPILogon = (LPMAPILOGON) GetProcAddress(m_data->m_hMapi, "MAPILogon");
101            m_data->m_lpfnMAPILogoff = (LPMAPILOGOFF) GetProcAddress(m_data->m_hMapi, "MAPILogoff");
102            m_data->m_lpfnMAPISendMail = (LPMAPISENDMAIL) GetProcAddress(m_data->m_hMapi, "MAPISendMail");
103            m_data->m_lpfnMAPIResolveName = (LPMAPIRESOLVENAME) GetProcAddress(m_data->m_hMapi, "MAPIResolveName");
104            m_data->m_lpfnMAPIFreeBuffer = (LPMAPIFREEBUFFER) GetProcAddress(m_data->m_hMapi, "MAPIFreeBuffer");
105
106            //If any of the functions are not installed then fail the load
107            if (m_data->m_lpfnMAPILogon == NULL ||
108                m_data->m_lpfnMAPILogoff == NULL ||
109                m_data->m_lpfnMAPISendMail == NULL ||
110                m_data->m_lpfnMAPIResolveName == NULL ||
111                m_data->m_lpfnMAPIFreeBuffer == NULL)
112            {
113                wxLogDebug(_T("Failed to get one of the functions pointer in MAPI32.DLL\n"));
114                Deinitialise();
115            }
116        }
117    }
118    else
119        wxLogDebug(_T("Mapi is not installed on this computer\n"));
120}
121
122void wxMapiSession::Deinitialise()
123{
124    if (m_data->m_hMapi)
125    {
126        //Unload the MAPI dll and reset the function pointers to NULL
127        FreeLibrary(m_data->m_hMapi);
128        m_data->m_hMapi = NULL;
129        m_data->m_lpfnMAPILogon = NULL;
130        m_data->m_lpfnMAPILogoff = NULL;
131        m_data->m_lpfnMAPISendMail = NULL;
132        m_data->m_lpfnMAPIResolveName = NULL;
133        m_data->m_lpfnMAPIFreeBuffer = NULL;
134    }
135}
136
137bool wxMapiSession::Logon(const wxString& sProfileName, const wxString& sPassword, wxWindow* pParentWnd)
138{
139    wxASSERT(MapiInstalled()); //MAPI must be installed
140    wxASSERT(m_data->m_lpfnMAPILogon); //Function pointer must be valid
141
142    //Initialise the function return value
143    bool bSuccess = FALSE;
144
145    //Just in case we are already logged in
146    Logoff();
147
148    //Setup the ascii versions of the profile name and password
149    int nProfileLength = sProfileName.Length();
150
151    LPSTR pszProfileName = NULL;
152    LPSTR pszPassword = NULL;
153    wxCharBuffer cbProfile(1),cbPassword(1);
154    if (nProfileLength)
155    {
156#ifndef UNICODE
157        pszProfileName = (LPSTR) sProfileName.c_str();
158        pszPassword = (LPSTR) sPassword.c_str();
159#else
160        cbProfile = sProfileName.mb_str();
161        cbPassword = sPassword.mb_str();
162        pszProfileName = cbProfile.data();
163        pszPassword = cbPassword.data();
164#endif
165    }
166
167    //Setup the flags & UIParam parameters used in the MapiLogon call
168    FLAGS flags = 0;
169    ULONG nUIParam = 0;
170    if (nProfileLength == 0)
171    {
172        //No profile name given, then we must interactively request a profile name
173        if (pParentWnd)
174        {
175            nUIParam = (ULONG) (HWND) pParentWnd->GetHWND();
176            flags |= MAPI_LOGON_UI;
177        }
178        else
179        {
180            //No window given, just use the main window of the app as the parent window
181            if (wxTheApp->GetTopWindow())
182            {
183                nUIParam = (ULONG) (HWND) wxTheApp->GetTopWindow()->GetHWND();
184                flags |= MAPI_LOGON_UI;
185            }
186        }
187    }
188
189    //First try to acquire a new MAPI session using the supplied settings using the MAPILogon functio
190    ULONG nError = m_data->m_lpfnMAPILogon(nUIParam, pszProfileName, pszPassword, flags | MAPI_NEW_SESSION, 0, &m_data->m_hSession);
191    if (nError != SUCCESS_SUCCESS && nError != MAPI_E_USER_ABORT)
192    {
193        //Failed to create a create mapi session, try to acquire a shared mapi session
194        wxLogDebug(_T("Failed to logon to MAPI using a new session, trying to acquire a shared one\n"));
195        nError = m_data->m_lpfnMAPILogon(nUIParam, NULL, NULL, 0, 0, &m_data->m_hSession);
196        if (nError == SUCCESS_SUCCESS)
197        {
198            m_data->m_nLastError = SUCCESS_SUCCESS;
199            bSuccess = TRUE;
200        }
201        else
202        {
203            wxLogDebug(_T("Failed to logon to MAPI using a shared session, Error:%ld\n"), nError);
204            m_data->m_nLastError = nError;
205        }
206    }
207    else if (nError == SUCCESS_SUCCESS)
208    {
209        m_data->m_nLastError = SUCCESS_SUCCESS;
210        bSuccess = TRUE;
211    }
212
213    return bSuccess;
214}
215
216bool wxMapiSession::LoggedOn() const
217{
218    return (m_data->m_hSession != 0);
219}
220
221bool wxMapiSession::MapiInstalled() const
222{
223    return (m_data->m_hMapi != NULL);
224}
225
226bool wxMapiSession::Logoff()
227{
228    wxASSERT(MapiInstalled()); //MAPI must be installed
229    wxASSERT(m_data->m_lpfnMAPILogoff); //Function pointer must be valid
230
231    //Initialise the function return value
232    bool bSuccess = FALSE;
233
234    if (m_data->m_hSession)
235    {
236        //Call the MAPILogoff function
237        ULONG nError = m_data->m_lpfnMAPILogoff(m_data->m_hSession, 0, 0, 0);
238        if (nError != SUCCESS_SUCCESS)
239        {
240            wxLogDebug(_T("Failed in call to MapiLogoff, Error:%ld"), nError);
241            m_data->m_nLastError = nError;
242            bSuccess = TRUE;
243        }
244        else
245        {
246            m_data->m_nLastError = SUCCESS_SUCCESS;
247            bSuccess = TRUE;
248        }
249        m_data->m_hSession = 0;
250    }
251
252    return bSuccess;
253}
254
255bool wxMapiSession::Resolve(const wxString& sName, void* lppRecip1)
256{
257    lpMapiRecipDesc* lppRecip = (lpMapiRecipDesc*) lppRecip1;
258
259    wxASSERT(MapiInstalled()); //MAPI must be installed
260    wxASSERT(m_data->m_lpfnMAPIResolveName); //Function pointer must be valid
261    wxASSERT(LoggedOn()); //Must be logged on to MAPI
262    wxASSERT(m_data->m_hSession); //MAPI session handle must be valid
263
264    //Call the MAPIResolveName function
265#ifndef UNICODE
266    LPSTR lpszAsciiName = (LPSTR) sName.c_str();
267#else
268    wxCharBuffer cbName(1);
269    cbName = sName.mb_str();
270    LPSTR lpszAsciiName = cbName.data();
271#endif
272    ULONG nError = m_data->m_lpfnMAPIResolveName(m_data->m_hSession, 0, lpszAsciiName, 0, 0, lppRecip);
273    if (nError != SUCCESS_SUCCESS)
274    {
275        wxLogDebug(_T("Failed to resolve the name: %s, Error:%ld\n"),
276                   sName.c_str(), nError);
277        m_data->m_nLastError = nError;
278    }
279
280    return (nError == SUCCESS_SUCCESS);
281}
282
283bool wxMapiSession::Send(wxMailMessage& message)
284{
285    wxASSERT(MapiInstalled()); //MAPI must be installed
286    wxASSERT(m_data->m_lpfnMAPISendMail); //Function pointer must be valid
287    wxASSERT(m_data->m_lpfnMAPIFreeBuffer); //Function pointer must be valid
288    wxASSERT(LoggedOn()); //Must be logged on to MAPI
289    wxASSERT(m_data->m_hSession); //MAPI session handle must be valid
290
291    //Initialise the function return value
292    bool bSuccess = FALSE;
293
294    //Create the MapiMessage structure to match the message parameter send into us
295    MapiMessage mapiMessage;
296    ZeroMemory(&mapiMessage, sizeof(mapiMessage));
297#ifndef UNICODE
298    mapiMessage.lpszSubject = (LPSTR) message.m_subject.c_str();
299    mapiMessage.lpszNoteText = (LPSTR) message.m_body.c_str();
300#else
301    wxCharBuffer cbSubject(1),cbBody(1),cbOriginator(1);
302    cbSubject = message.m_subject.mb_str();
303    cbBody = message.m_body.mb_str();
304    mapiMessage.lpszSubject = cbSubject.data();
305    mapiMessage.lpszNoteText = cbBody.data();
306#endif
307    mapiMessage.nRecipCount = message.m_to.GetCount() + message.m_cc.GetCount() + message.m_bcc.GetCount();
308    wxASSERT(mapiMessage.nRecipCount); //Must have at least 1 recipient!
309
310    //Allocate the recipients array
311    mapiMessage.lpRecips = new MapiRecipDesc[mapiMessage.nRecipCount];
312
313    // If we have a 'From' field, use it
314    if (!message.m_from.IsEmpty())
315    {
316        mapiMessage.lpOriginator = new MapiRecipDesc;
317        ZeroMemory(mapiMessage.lpOriginator, sizeof(MapiRecipDesc));
318
319        mapiMessage.lpOriginator->ulRecipClass = MAPI_ORIG;
320        // TODO Do we have to call Resolve?
321#ifndef UNICODE
322        mapiMessage.lpOriginator->lpszName = (LPSTR) message.m_from.c_str();
323#else
324        cbOriginator = message.m_from.mb_str();
325        mapiMessage.lpOriginator->lpszName = cbOriginator.data();
326#endif
327    }
328
329    //Setup the "To" recipients
330    int nRecipIndex = 0;
331    int nToSize = message.m_to.GetCount();
332    int i;
333    for (i=0; i<nToSize; i++)
334    {
335        MapiRecipDesc& recip = mapiMessage.lpRecips[nRecipIndex];
336        ZeroMemory(&recip, sizeof(MapiRecipDesc));
337        recip.ulRecipClass = MAPI_TO;
338        wxString& sName = message.m_to[i];
339
340        //Try to resolve the name
341        lpMapiRecipDesc lpTempRecip;
342        if (Resolve(sName, (void*) &lpTempRecip))
343        {
344            //Resolve worked, put the resolved name back into the sName
345            sName = wxString(lpTempRecip->lpszName,*wxConvCurrent);
346
347            //Don't forget to free up the memory MAPI allocated for us
348            m_data->m_lpfnMAPIFreeBuffer(lpTempRecip);
349        }
350#ifndef UNICODE
351        recip.lpszName = (LPSTR) sName.c_str();
352#else
353        recip.lpszName = sName.mb_str().release();
354#endif
355
356        ++nRecipIndex;
357    }
358
359    //Setup the "CC" recipients
360    int nCCSize = message.m_cc.GetCount();
361    for (i=0; i<nCCSize; i++)
362    {
363        MapiRecipDesc& recip = mapiMessage.lpRecips[nRecipIndex];
364        ZeroMemory(&recip, sizeof(MapiRecipDesc));
365        recip.ulRecipClass = MAPI_CC;
366        wxString& sName = message.m_cc[i];
367
368        //Try to resolve the name
369        lpMapiRecipDesc lpTempRecip;
370        if (Resolve(sName, (void*) &lpTempRecip))
371        {
372            //Resolve worked, put the resolved name back into the sName
373            sName = wxString(lpTempRecip->lpszName,*wxConvCurrent);
374
375            //Don't forget to free up the memory MAPI allocated for us
376            m_data->m_lpfnMAPIFreeBuffer(lpTempRecip);
377        }
378#ifndef UNICODE
379        recip.lpszName = (LPSTR) sName.c_str();
380#else
381        recip.lpszName = sName.mb_str().release();
382#endif
383
384        ++nRecipIndex;
385    }
386
387    //Setup the "BCC" recipients
388    int nBCCSize = message.m_bcc.GetCount();
389    for (i=0; i<nBCCSize; i++)
390    {
391        MapiRecipDesc& recip = mapiMessage.lpRecips[nRecipIndex];
392        ZeroMemory(&recip, sizeof(MapiRecipDesc));
393        recip.ulRecipClass = MAPI_BCC;
394        wxString& sName = message.m_bcc[i];
395
396        //Try to resolve the name
397        lpMapiRecipDesc lpTempRecip;
398        if (Resolve(sName, (void*) &lpTempRecip))
399        {
400            //Resolve worked, put the resolved name back into the sName
401            sName = wxString(lpTempRecip->lpszName,wxConvCurrent);
402
403            //Don't forget to free up the memory MAPI allocated for us
404            m_data->m_lpfnMAPIFreeBuffer(lpTempRecip);
405        }
406#ifndef UNICODE
407        recip.lpszName = (LPSTR) sName.c_str();
408#else
409        recip.lpszName = sName.mb_str().release();
410#endif
411
412        ++nRecipIndex;
413    }
414
415    //Setup the attachments
416    int nAttachmentSize = message.m_attachments.GetCount();
417    int nTitleSize = message.m_attachmentTitles.GetCount();
418    if (nTitleSize)
419    {
420        wxASSERT(nTitleSize == nAttachmentSize); //If you are going to set the attachment titles then you must set
421        //the attachment title for each attachment
422    }
423    if (nAttachmentSize)
424    {
425        mapiMessage.nFileCount = nAttachmentSize;
426        mapiMessage.lpFiles = new MapiFileDesc[nAttachmentSize];
427        for (i=0; i<nAttachmentSize; i++)
428        {
429            MapiFileDesc& file = mapiMessage.lpFiles[i];
430            ZeroMemory(&file, sizeof(MapiFileDesc));
431            file.nPosition = 0xFFFFFFFF;
432            wxString& sFilename = message.m_attachments[i];
433
434#ifndef UNICODE
435            file.lpszPathName = (LPSTR) sFilename.c_str();
436#else
437            file.lpszPathName = sFilename.mb_str().release();
438#endif
439            //file.lpszFileName = file.lpszPathName;
440            file.lpszFileName = NULL;
441
442            if (nTitleSize && !message.m_attachmentTitles[i].IsEmpty())
443            {
444                wxString& sTitle = message.m_attachmentTitles[i];
445#ifndef UNICODE
446                file.lpszFileName = (LPSTR) sTitle.c_str();
447#else
448                file.lpszFileName = sTitle.mb_str().release();
449#endif
450            }
451        }
452    }
453
454    //Do the actual send using MAPISendMail
455    ULONG nError = m_data->m_lpfnMAPISendMail(m_data->m_hSession, 0, &mapiMessage, MAPI_DIALOG, 0);
456    if (nError == SUCCESS_SUCCESS)
457    {
458        bSuccess = TRUE;
459        m_data->m_nLastError = SUCCESS_SUCCESS;
460    }
461    else
462    {
463        wxLogDebug(_T("Failed to send mail message, Error:%ld\n"), nError);
464        m_data->m_nLastError = nError;
465    }
466
467    //Tidy up the Attachements
468    if (nAttachmentSize)
469    {
470#ifdef UNICODE
471        for (i = 0;i < nAttachmentSize;i++)
472        {
473            free(mapiMessage.lpFiles[i].lpszPathName);
474            free(mapiMessage.lpFiles[i].lpszFileName);
475        }
476#endif
477        delete [] mapiMessage.lpFiles;
478    }
479
480    //Free up the Recipients and Originator memory
481#ifdef UNICODE
482    for (i = 0;i < nRecipIndex;i++)
483        free(mapiMessage.lpRecips[i].lpszName);
484#endif
485    delete [] mapiMessage.lpRecips;
486
487    delete mapiMessage.lpOriginator;
488
489    return bSuccess;
490}
491
492long wxMapiSession::GetLastError() const
493{
494    return m_data->m_nLastError;
495}
496
497#endif // __WXMSW__
498