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 
40 class WXDLLIMPEXP_NETUTILS wxMapiData
41 {
42 public:
wxMapiData()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 
wxMapiSession()69 wxMapiSession::wxMapiSession()
70 {
71     m_data = new wxMapiData;
72 
73     Initialise();
74 }
75 
~wxMapiSession()76 wxMapiSession::~wxMapiSession()
77 {
78     //Logoff if logged on
79     Logoff();
80 
81     //Unload the MAPI dll
82     Deinitialise();
83 
84     delete m_data;
85 }
86 
Initialise()87 void 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 
Deinitialise()122 void 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 
Logon(const wxString & sProfileName,const wxString & sPassword,wxWindow * pParentWnd)137 bool 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 
LoggedOn() const216 bool wxMapiSession::LoggedOn() const
217 {
218     return (m_data->m_hSession != 0);
219 }
220 
MapiInstalled() const221 bool wxMapiSession::MapiInstalled() const
222 {
223     return (m_data->m_hMapi != NULL);
224 }
225 
Logoff()226 bool 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 
Resolve(const wxString & sName,void * lppRecip1)255 bool 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 
Send(wxMailMessage & message)283 bool 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 
GetLastError() const492 long wxMapiSession::GetLastError() const
493 {
494     return m_data->m_nLastError;
495 }
496 
497 #endif // __WXMSW__
498