Hi there. This is my first time on the forums, and my first time working with python, zimbra, and SOAP APIs. I'm very grateful for the forum, because without it I could not accomplish what I've managed to do today. But I have noticed that there were a lot of people who didn't quite understand how the thing is supposed to look like, so I want to give back to the forum and leave examples of my code so people like me in the future can have a starting point.
I think there was also a higher-level python library for connecting with zimbra, but I wasn't able to make heads or tails of that so all examples here are related to python-zimbra.
CONNECTING TO ZIMBRA:
NOTE: You can generate the key using this method.
GENERAL REQUEST STRUCTURE
I'm using a send message request here as an example.
NOTE: The way to translate the SOAP API Reference given here is to translate the structure to dictionaries and nested dictionaries. Some code allows you to put in a list (see the e for emails). The good thing about python is that it allows you to put in variables as you can see from the formatted strings! Very convenient.
Similarly, to call the specific parts of the info_response.get_response(), sometimes you have to put in numbers even though the SOAP API Reference says nothing about numbers. An example of my get_response() from a GetMsg Request:
UPLOADING AND SENDING ATTACHMENTS:
This got me stumped for a couple of days. But eventually I figured it out. You will need the python requests module for this.
NOTE:
1. In theory I should be able to upload multiple files? But I think I'll stick to the safe one file per upload ID.
2. client_token can be anything, but I put it as DateTime (plus other identifiers I removed) to make sure they don't repeat.
3. The upload ID will come in a string of characters separated by a colon if I remember right. You only need the latter half for the upload ID used in the SendMsgRequest example above. Note that saving the upload to the Briefcase will remove the content attached to the Upload ID.
In the example above, I appended the server_token to a upload_id list. If you're only uploading one file, you likely won't need the list.
DOWNLOADING ATTACHMENTS:
If you know which email MsgID and part to download from, then downloading is easy.
NOTE: You can probably get the filename from r.headers['content-disposition']. Also note that part 1 will always be the main email.
I hope this helps people out there! The only thing that has truly stumped me is the whole CreateWaitSet and WaitSet requests that I can't seem to get to work at all. But I think what I've did so far isn't terrible! Thank you very much once again to this forum and have a lovely day!
I think there was also a higher-level python library for connecting with zimbra, but I wasn't able to make heads or tails of that so all examples here are related to python-zimbra.
CONNECTING TO ZIMBRA:
Code:
url = "https://mail.yourserver.com/service/soap"
comm = Communication(url)
acct = "account@yourserver.com"
key = "#####"
usr_token = auth.authenticate(url, acct, key)
NOTE: You can generate the key using this method.
GENERAL REQUEST STRUCTURE
I'm using a send message request here as an example.
Code:
info_request = comm.gen_request(token=usr_token)
info_request.add_request(
'SendMsgRequest',
{
'm':{
'su': f'Mail Subject Here', #subject
'e':[{'a':f'{mail}','t':'t'}, {'a':f'{bccmail}','t':'b'}], #email(s)
'mp':{
'content':style + message, #mail content. can be styled with html and css.
'ct':'text/html'
},
'attach':{'aid': f'{upload_id[0]},{upload_id[1]}' } #attachments
}
},
'urn:zimbraMail')
info_response = comm.send_request(info_request)
if info_response.is_fault():
print(info_response.get_fault_message())
else:
info_response.get_response()
NOTE: The way to translate the SOAP API Reference given here is to translate the structure to dictionaries and nested dictionaries. Some code allows you to put in a list (see the e for emails). The good thing about python is that it allows you to put in variables as you can see from the formatted strings! Very convenient.
Similarly, to call the specific parts of the info_response.get_response(), sometimes you have to put in numbers even though the SOAP API Reference says nothing about numbers. An example of my get_response() from a GetMsg Request:
Code:
mailcontent = info_response.get_response()['GetMsgResponse']['m']['mp'][0]['content']
UPLOADING AND SENDING ATTACHMENTS:
This got me stumped for a couple of days. But eventually I figured it out. You will need the python requests module for this.
Code:
url_post = 'https://mail.yourserver.com/service/upload'
cookies = { 'ZM_AUTH_TOKEN': usr_token }
fn = {'file': ( filename , open(file, 'rb'))} #you can rename the file.
client_token = datetime.today().strftime('%Y%m%d%H%M%S')
data = { 'requestId':client_token }
r = requests.post(url_post,data=data,files=fn,cookies=cookies)
server_token_regex = re.compile(rf"'{client_token}','[^']*'")
server_token_search = server_token_regex.search(r.text)
server_token = re.sub(rf"'{client_token}',",'', server_token_search.group()).strip()
server_token = re.sub("'",'',server_token)
NOTE:
1. In theory I should be able to upload multiple files? But I think I'll stick to the safe one file per upload ID.
2. client_token can be anything, but I put it as DateTime (plus other identifiers I removed) to make sure they don't repeat.
3. The upload ID will come in a string of characters separated by a colon if I remember right. You only need the latter half for the upload ID used in the SendMsgRequest example above. Note that saving the upload to the Briefcase will remove the content attached to the Upload ID.
In the example above, I appended the server_token to a upload_id list. If you're only uploading one file, you likely won't need the list.
DOWNLOADING ATTACHMENTS:
If you know which email MsgID and part to download from, then downloading is easy.
Code:
cookies = { 'ZM_AUTH_TOKEN': usr_token }
url = f"https://mail.yourserver.com/service/user/~/?id={msgID}&part={part}"
r = requests.get(url,stream=True,cookies=cookies)
file = os.path.join(outdir, filename)
with open(file, 'wb') as doc:
doc.write(r.content)
NOTE: You can probably get the filename from r.headers['content-disposition']. Also note that part 1 will always be the main email.
I hope this helps people out there! The only thing that has truly stumped me is the whole CreateWaitSet and WaitSet requests that I can't seem to get to work at all. But I think what I've did so far isn't terrible! Thank you very much once again to this forum and have a lovely day!
Statistics: Posted by rikacain — Wed Jun 15, 2022 5:14 am