2
Copyright (c) 2007 Ian Cook and John Popplewell
 
 
4
Permission is hereby granted, free of charge, to any person obtaining
 
 
5
a copy of this software and associated documentation files (the
 
 
6
"Software"), to deal in the Software without restriction, including
 
 
7
without limitation the rights to use, copy, modify, merge, publish,
 
 
8
distribute, sublicense, and/or sell copies of the Software, and to
 
 
9
permit persons to whom the Software is furnished to do so, subject to
 
 
10
the following conditions:
 
 
12
The above copyright notice and this permission notice shall be included
 
 
13
in all copies or substantial portions of the Software.
 
 
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
 
16
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
 
17
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 
 
18
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 
 
19
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 
 
20
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 
 
21
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 
25
Contact : John Popplewell
 
 
26
Email   : john@johnnypops.demon.co.uk
 
 
27
Web     : http://www.johnnypops.demon.co.uk/python/
 
 
28
Origin  : Based on the original script by Ian Cook
 
 
29
          http://www.kirbyfooty.com/simplemapi.py
 
 
30
Comments: Works (and tested) with:
 
 
31
          Outlook Express, Outlook 97 and 2000, 
 
 
32
          Eudora, Incredimail and Mozilla Thunderbird (1.5.0.2)
 
 
33
Thanks  : Werner F. Bruhin and Michele Petrazzo on the ctypes list.
 
 
35
If you have any bug-fixes, enhancements or suggestions regarding this 
 
 
36
software, please contact me at the above email address.
 
 
44
LPLHANDLE = POINTER(LHANDLE)
 
 
49
MAPI_E_USER_ABORT               = MAPI_USER_ABORT
 
 
51
MAPI_E_LOGON_FAILURE            = 3
 
 
52
MAPI_E_LOGIN_FAILURE            = MAPI_E_LOGON_FAILURE
 
 
54
MAPI_E_INSUFFICIENT_MEMORY      = 5
 
 
55
MAPI_E_ACCESS_DENIED            = 6
 
 
56
MAPI_E_TOO_MANY_SESSIONS        = 8
 
 
57
MAPI_E_TOO_MANY_FILES           = 9
 
 
58
MAPI_E_TOO_MANY_RECIPIENTS      = 10
 
 
59
MAPI_E_ATTACHMENT_NOT_FOUND     = 11
 
 
60
MAPI_E_ATTACHMENT_OPEN_FAILURE  = 12
 
 
61
MAPI_E_ATTACHMENT_WRITE_FAILURE = 13
 
 
62
MAPI_E_UNKNOWN_RECIPIENT        = 14
 
 
63
MAPI_E_BAD_RECIPTYPE            = 15
 
 
64
MAPI_E_NO_MESSAGES              = 16
 
 
65
MAPI_E_INVALID_MESSAGE          = 17
 
 
66
MAPI_E_TEXT_TOO_LARGE           = 18
 
 
67
MAPI_E_INVALID_SESSION          = 19
 
 
68
MAPI_E_TYPE_NOT_SUPPORTED       = 20
 
 
69
MAPI_E_AMBIGUOUS_RECIPIENT      = 21
 
 
70
MAPI_E_AMBIG_RECIP              = MAPI_E_AMBIGUOUS_RECIPIENT
 
 
71
MAPI_E_MESSAGE_IN_USE           = 22
 
 
72
MAPI_E_NETWORK_FAILURE          = 23
 
 
73
MAPI_E_INVALID_EDITFIELDS       = 24
 
 
74
MAPI_E_INVALID_RECIPS           = 25
 
 
75
MAPI_E_NOT_SUPPORTED            = 26
 
 
83
class MapiRecipDesc(Structure):
 
 
85
        ('ulReserved',      c_ulong),
 
 
86
        ('ulRecipClass',    c_ulong),
 
 
87
        ('lpszName',        c_char_p),
 
 
88
        ('lpszAddress',     c_char_p),
 
 
89
        ('ulEIDSize',       c_ulong),
 
 
90
        ('lpEntryID',       c_void_p),
 
 
92
lpMapiRecipDesc  = POINTER(MapiRecipDesc)
 
 
93
lppMapiRecipDesc = POINTER(lpMapiRecipDesc)
 
 
95
class MapiFileDesc(Structure):
 
 
97
        ('ulReserved',      c_ulong),
 
 
99
        ('nPosition',       c_ulong),
 
 
100
        ('lpszPathName',    c_char_p),
 
 
101
        ('lpszFileName',    c_char_p),
 
 
102
        ('lpFileType',      c_void_p),
 
 
104
lpMapiFileDesc = POINTER(MapiFileDesc)
 
 
106
class MapiMessage(Structure):
 
 
108
        ('ulReserved',          c_ulong),
 
 
109
        ('lpszSubject',         c_char_p),
 
 
110
        ('lpszNoteText',        c_char_p),
 
 
111
        ('lpszMessageType',     c_char_p),
 
 
112
        ('lpszDateReceived',    c_char_p),
 
 
113
        ('lpszConversationID',  c_char_p),
 
 
115
        ('lpOriginator',        lpMapiRecipDesc),
 
 
116
        ('nRecipCount',         c_ulong),
 
 
117
        ('lpRecips',            lpMapiRecipDesc),
 
 
118
        ('nFileCount',          c_ulong),
 
 
119
        ('lpFiles',             lpMapiFileDesc),
 
 
121
lpMapiMessage = POINTER(MapiMessage)
 
 
124
MAPISendMail            = MAPI.MAPISendMail
 
 
125
MAPISendMail.restype    = c_ulong
 
 
126
MAPISendMail.argtypes   = (LHANDLE, c_ulong, lpMapiMessage, FLAGS, c_ulong)
 
 
128
MAPIResolveName         = MAPI.MAPIResolveName
 
 
129
MAPIResolveName.restype = c_ulong
 
 
130
MAPIResolveName.argtypes= (LHANDLE, c_ulong, c_char_p, FLAGS, c_ulong, lppMapiRecipDesc)
 
 
132
MAPIFreeBuffer          = MAPI.MAPIFreeBuffer
 
 
133
MAPIFreeBuffer.restype  = c_ulong
 
 
134
MAPIFreeBuffer.argtypes = (c_void_p, )
 
 
136
MAPILogon               = MAPI.MAPILogon
 
 
137
MAPILogon.restype       = c_ulong
 
 
138
MAPILogon.argtypes      = (LHANDLE, c_char_p, c_char_p, FLAGS, c_ulong, LPLHANDLE)
 
 
140
MAPILogoff              = MAPI.MAPILogoff
 
 
141
MAPILogoff.restype      = c_ulong
 
 
142
MAPILogoff.argtypes     = (LHANDLE, c_ulong, FLAGS, c_ulong)
 
 
145
class MAPIError(WindowsError):
 
 
147
    def __init__(self, code):
 
 
148
        WindowsError.__init__(self)
 
 
152
        return 'MAPI error %d' % (self.code,)
 
 
155
def _logon(profileName=None, password=None):
 
 
157
    rc = MAPILogon(0, profileName, password, MAPI_LOGON_UI, 0, byref(pSession))
 
 
158
    if rc != SUCCESS_SUCCESS:
 
 
163
def _logoff(session):
 
 
164
    rc = MAPILogoff(session, 0, 0, 0)
 
 
165
    if rc != SUCCESS_SUCCESS:
 
 
169
def _resolveName(session, name):
 
 
170
    pRecipDesc = lpMapiRecipDesc()
 
 
171
    rc = MAPIResolveName(session, 0, name, 0, 0, byref(pRecipDesc))
 
 
172
    if rc != SUCCESS_SUCCESS:
 
 
174
    rd = pRecipDesc.contents
 
 
175
    name, address = rd.lpszName, rd.lpszAddress
 
 
176
    rc = MAPIFreeBuffer(pRecipDesc)
 
 
177
    if rc != SUCCESS_SUCCESS:
 
 
182
def _sendMail(session, recipient, subject, body, attach):
 
 
183
    nFileCount = len(attach)
 
 
185
        MapiFileDesc_A = MapiFileDesc * len(attach) 
 
 
186
        fda = MapiFileDesc_A() 
 
 
187
        for fd, fa in zip(fda, attach): 
 
 
192
            fd.lpszFileName = None 
 
 
196
        lpFiles = lpMapiFileDesc()
 
 
198
    RecipWork = recipient.split(';')
 
 
199
    RecipCnt = len(RecipWork)
 
 
200
    MapiRecipDesc_A = MapiRecipDesc * len(RecipWork) 
 
 
201
    rda = MapiRecipDesc_A() 
 
 
202
    for rd, ra in zip(rda, RecipWork):
 
 
204
        rd.ulRecipClass = MAPI_TO
 
 
206
            rd.lpszName, rd.lpszAddress = _resolveName(session, ra)
 
 
208
            # work-round for Mozilla Thunderbird
 
 
209
            rd.lpszName, rd.lpszAddress = None, ra
 
 
214
    msg = MapiMessage(0, subject, body, None, None, None, 0, lpMapiRecipDesc(),
 
 
218
    rc = MAPISendMail(session, 0, byref(msg), MAPI_DIALOG, 0)
 
 
219
    if rc != SUCCESS_SUCCESS:
 
 
223
def SendMail(recipient, subject="", body="", attachfiles=""):
 
 
224
    """Post an e-mail message using Simple MAPI
 
 
226
    recipient - string: address to send to (multiple addresses separated with a semicolon)
 
 
227
    subject   - string: subject header
 
 
228
    body      - string: message text
 
 
229
    attach    - string: files to attach (multiple attachments separated with a semicolon)
 
 
233
    AttachWork = attachfiles.split(';')
 
 
234
    for file in AttachWork:
 
 
235
        if os.path.exists(file):
 
 
237
    attach = map(os.path.abspath, attach)
 
 
239
    restore = os.getcwd()
 
 
243
            _sendMail(session, recipient, subject, body, attach)
 
 
250
if __name__ == '__main__':
 
 
252
    recipient = "test@johnnypops.demon.co.uk"
 
 
253
    subject = "Test Message Subject"
 
 
254
    body = "Hi,\r\n\r\nthis is a quick test message,\r\n\r\ncheers,\r\nJohn."
 
 
255
    attachment = sys.argv[0]
 
 
256
    SendMail(recipient, subject, body, attachment)