Python-Zimbra examples

Have a great idea for extending Zimbra? Share ideas, ask questions, contribute, and get feedback.
Post Reply
rikacain
Posts: 2
Joined: Wed Jun 15, 2022 4:23 am

Python-Zimbra examples

Post by rikacain »

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:

Code: Select all

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: Select all

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: Select all

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: Select all

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: Select all

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!
Jhon
Posts: 2
Joined: Fri Oct 28, 2022 10:24 am

Re: Python-Zimbra examples

Post by Jhon »

Hi rikacain,
When you do an exemple sending a message with the General Structure, how did you create the variable upload_id ? Because I have a problem sending the attachments because an invalid Id.
rikacain
Posts: 2
Joined: Wed Jun 15, 2022 4:23 am

Re: Python-Zimbra examples

Post by rikacain »

Late reply, so I don't think it will be helpful anymore, but if you still need it or want to compare codes:

function variables: entryID is an overarching tracker. file_list is a list of files (fullpath) that i want to upload.

Code: Select all

def upload_file(entryID, file_list):
   #upload to UploadFileServlet
   url_post  = 'https://mail.examplehost.com/service/upload?ftm=raw,extended'

   fn_no = 0
   uploadID_list = []
   for filename in file_list:
      fn_no += 1
      fn = {'file': ( os.path.basename(filename).replace(f"{entryID}_","") , open(filename, 'rb'))}
      client_token = datetime.today().strftime('%Y%m%d%H%M%S') + '_' + entryID + '_' + str(fn_no)
      data = { 'requestId':client_token }

      print(f'Uploading %s to the UploadFileServlet...' % os.path.basename(filename) )
      cookies = { 'ZM_AUTH_TOKEN': usr_token }
      r = requests.post(url_post,data=data,files=fn,cookies=cookies)
      if '200' in r.text:
         print('Upload successful.')
      else:
         print('Upload failed.')

      #extract server_token
      print('Extracting upload ID...')
      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)
      print('Upload ID extracted.')

      uploadID_list.append(server_token)

   return uploadID_list
Hope this helps whoever comes across this!
Post Reply