Skip to content

Instantly share code, notes, and snippets.

@philippkeller
Last active December 15, 2015 00:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save philippkeller/5175842 to your computer and use it in GitHub Desktop.
Save philippkeller/5175842 to your computer and use it in GitHub Desktop.
from ewstype import EWSType
from suds.sax.element import Element
from suds.transport.https import HttpAuthenticated
from suds.client import Client
from suds import WebFault
from datetime import datetime
import urllib2
import os
def exchange(username, password):
transport = HttpAuthenticated(username=username, password=password)
t = HttpAuthenticated(username=username, password=password)
t.handler = urllib2.HTTPBasicAuthHandler(t.pm)
t.urlopener = urllib2.build_opener(t.handler)
cwd = os.path.dirname(os.path.realpath(__file__))
wsdl = "file://%s/wsdl/Services.wsdl" % cwd
client = Client(wsdl, transport=t)
w = Wrapper(client)
return w
class Wrapper:
'''EWSWrapper functions'''
hasMoreItems = False
synchState = None
#Time Zone settings
BaseOffset = "-P0DT1H0M0.0S"
Offset = "-P0DT1H0M0.0S"
DaylightTime = "02:00:00.0000000"
StandardOffset = "P0DT0H0M0.0S"
StandardTime = "03:00:00.0000000"
TimeZoneName = "(GMT+01:00) Warsaw"
def __init__(self, client, version='Exchange2007_SP1'):
"""
version: see in your types.xsd: the root element has an argument 'version'. That's the version. In my case it was Exchange2007_SP1
"""
# Enums
self.types = EWSType
self.client = client
self.version = version
def wrap(self, xml):
'''Generate the necessary boilerplate XML for a raw SOAP request.
The XML is specific to the server version.'''
header = Element('t:RequestServerVersion')
header.set('Version', self.version)
return '''<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<s:Header>%s</s:Header>
<s:Body>%s</s:Body>
</s:Envelope>''' % (header, xml)
def _restriction(self, operator, field, value, extended = False):
'''Helper function to build retriction call'''
if not extended:
uri = Element('t:FieldURI')
uri.set('FieldURI', field)
else:
uri = field
val = Element('t:Constant')
val.set('Value', value)
if operator == '==':
op = Element('t:IsEqualTo')
elif operator == '!=':
op = Element('t:IsNotEqualTo')
elif operator == '>':
op = Element('t:IsGreaterThan')
elif operator == '<':
op = Element('t:IsLessThan')
elif operator == '<=':
op = Element('t:IsLessThanOrEqualTo')
elif operator == '>=':
op = Element('t:IsGreaterThanOrEqualTo')
elif operator == 'contains':
op = Element('t:Contains')
op.set('ContainmentMode', 'Substring')
op.set('ContainmentComparison', 'Exact')
op.append(uri)
op.append(val)
return op
else:
raise Exception('Operator not supported')
op.append(uri)
const = Element('t:FieldURIOrConstant')
const.append(val)
op.append(const)
return op
def _mapi_reference(self, property_id, property_type, set_id):
'''construct a mapi field reference'''
path = Element('t:ExtendedFieldURI')
path.set('DistinguishedPropertySetId', set_id)
path.set('PropertyId', property_id)
path.set('PropertyType', property_type)
return path
def listCalendarEvent(self, id=None, start=None, end=None, on_behalf=None, shape='DEFAULT_PROPERTIES', categories=None):
'''======================================
// List Calendar Events
//======================================
/* @param string id - item id. takes precendense over timeframe
* @param string $onbehalf - "on behalf" seneder's email
* @param int $start - event start timestamp
* @param int $end - event end time
*
* @return object response
*/
'''
type = 'CALENDAR'
return self.listItems(type, id, start, end, on_behalf, shape, categories)
def synchCalendarEvent(self, on_behalf=None, num_to_synch=10, synch_state=None, shape='DEFAULT_PROPERTIES'):
'''======================================
// Synchronize Calendar Events
//======================================
* @param string $onbehalf - "on behalf": item owner email
* @param int num_to_synch - how many items to synch in one go (max 500)
* @param string synch_state - current synch state, blank on initial synch
* @param string $shape - detail level (enumarted in DefaultShapeNamesType)
*
* @return object response
*/
'''
type = 'CALENDAR'
return self.synchFolder(type, on_behalf, num_to_synch, synch_state, shape)
def addCalendarEvent(self, subject, body, start, end, attendees, on_behalf=None, \
location=None, allday = False, bodyType="TEXT", category="default"):
'''=====================================
// Add Calendar Event
//======================================
/* @param string $subject - event subject
* @param int $start - event start timestamp
* @param int $end - event end time
* @param array $anttendees - array of email addresses of invited poeople
* @param string $body - event body
* @param string $onbehalf - "on behalf" seneder's email
* @param string $location - event loaction
* @param bool $allday - is it an all-day event?
* @param string $bodyType - body format (Text/HTML)
* @param string $category - event actegory
*
* @return object response
*
'''
createitem = Element('m:CreateItem')
createitem.set('SendMeetingInvitations', self.types.EWSType_CalendarItemCreateOrDeleteOperationType.SEND_TO_NONE)
saveditemfolderid = Element('m:SavedItemFolderId')
distinguishedfolderid = Element('t:DistinguishedFolderId')
distinguishedfolderid.set('Id', self.types.EWSType_DistinguishedFolderIdNameType.CALENDAR)
if on_behalf is not None:
mailbox = Element('t:Mailbox')
emailaddress = Element('t:EmailAddress').setText(on_behalf)
mailbox.append(emailaddress)
distinguishedfolderid.append(mailbox)
saveditemfolderid.append(distinguishedfolderid)
createitem.append(saveditemfolderid)
calitems = Element('m:Items')
# Prepare objects for CalendarItem creation
calitem = Element('t:CalendarItem')
timeZone = Element('t:MeetingTimeZone')
timeZone.set('TimeZoneName', TimeZoneName)
timeZone.append(Element('t:BaseOffset').setText(BaseOffset))
standard = Element('t:Standard')
standard.append(Element('t:Offset').setText(StandardOffset))
standard.append(Element('t:Time').setText(StandardTime))
timeZone.append(standard)
daylight = Element('t:Daylight')
daylight.append(Element('t:Offset').setText(Offset))
daylight.append(Element('t:Time').setText(DaylightTime))
timeZone.append(daylight)
Subject = Element('t:Subject')
Body = Element('t:Body')
Body.set('BodyType', getattr(self.types.EWSType_BodyTypeResponseType, bodyType))
cats = None
if category:
cats = Element('t:Categories')
if not isinstance(category, list):
cats.append(Element('t:String').setText(category))
else:
for cat in category:
cats.append(Element('t:String').setText(cat))
Start = Element('t:Start')
End = Element('t:End')
Location = Element('t:Location')
alldayevent = Element('t:IsAllDayEvent')
calitem.append(Subject.setText(subject))
calitem.append(Body.setText(body))
calitem.append(cats)
calitem.append(Start.setText(start.ewsformat()))
calitem.append(End.setText(end.ewsformat()))
calitem.append(alldayevent.setText(int(allday)))
if location:
calitem.append(Location.setText(location))
atts = Element('t:RequiredAttendees')
for attendee in attendees:
att = Element('t:Attendee')
mailbox = Element('t:Mailbox')
emailaddress = Element('t:EmailAddress').setText(attendee)
mailbox.append(emailaddress)
att.append(mailbox)
atts.append(att)
calitem.append(atts)
calitem.append(timeZone)
#add item to items
calitems.append(calitem)
#add item to CreateItems
createitem.append(calitems)
#make the call
xml = self.wrap(createitem)
try:
result = self.client.service.CreateItem(__inject={'msg':xml})
except WebFault as e:
raise e
# Result may come as a single object or a list of objects
if type(result.CreateItemResponseMessage).__name__ == 'list':
msgs = result.CreateItemResponseMessage
else:
msgs = [result.CreateItemResponseMessage]
idlist = []
for msg in msgs:
rspclass = msg._ResponseClass
rspcode = msg.ResponseCode
if rspclass == 'Error':
raise Exception('Error code: %s message: %s' % \
(rspcode, msg.MessageText))
if rspclass != 'Success':
raise Exception('Unknown response class: %s code: %s' % \
(rspclass, rspcode))
if rspcode != 'NoError':
raise Exception('Unknown response code: %s' % rspcode)
item = msg.Items.CalendarItem.ItemId
idlist.extend([item._Id, item._ChangeKey])
return idlist
def editCalendarEvent(self, id, ckey, subject=None, body=None, bodytype="TEXT", \
start=None, end=None, location=None, attendees=[], \
allday=None, category=None):
'''======================================
// Edit Calendar Event
//======================================
/* @param string $id - event id
* @param string $ckey - event change key
* @param string $subject - event subject
* @param string $body - event body
* @param int $start - event start timestamp
* @param int $end - event end time
* @param string $location - event location
* @param array $anttendees - array of email addresses of invited poeople
* @param bool $allday - is it an all-day event?
* @param string $category - event actegory
*
* @return object response
*/
'''
Updates = {
'calendar:Start' : start.ewsformat() if start else None,
'calendar:End' : end.ewsformat() if end else None,
'calendar:Location' : location,
'calendar:IsAllDayEvent' : allday,
'item:Subject' : subject,
}
updateitem = Element('m:UpdateItem')
updateitem.set('SendMeetingInvitationsOrCancellations', self.types.EWSType_CalendarItemCreateOrDeleteOperationType.SEND_TO_NONE)
updateitem.set('MessageDisposition', self.types.EWSType_MessageDispositionType.SAVEONLY)
updateitem.set('ConflictResolution', self.types.EWSType_ConflictResolutionType.ALWAYSOVERWRITE)
itemchanges = Element('m:ItemChanges')
itemchange = Element('t:ItemChange')
itemid = Element('t:ItemId')
itemid.set('Id', id)
itemid.set('ChangeKey', ckey)
itemchange.append(itemid)
updates = Element('t:Updates')
#popoulate update array
for key,value in Updates.items():
if value:
prop = key.split(':').pop()
itemfield = Element('t:SetItemField')
itemfield.append(Element('t:FieldURI'))
itemfield.children[0].set('FieldURI', key)
itemfield.append(Element('t:CalendarItem'))
itemfield.children[1].append(Element('t:'+prop).setText(value))
updates.append(itemfield)
if attendees:
itemfield = Element('t:SetItemField')
itemfield.append(Element('t:FieldURI'))
itemfield.children[0].set('FieldURI', 'calendar:RequiredAttendees')
itemfield.append(Element('t:CalendarItem'))
reqatts = Element('t:RequiredAttendees')
for attendee in attendees:
att = Element('t:Attendee')
mailbox = Element('t:Mailbox')
emailaddress = Element('t:EmailAddress').setText(attendee)
mailbox.append(emailaddress)
att.append(mailbox)
reqatts.append(att)
itemfield.children[1].append(reqatts)
updates.append(itemfield)
if category:
itemfield = Element('t:SetItemField')
itemfield.append(Element('t:FieldURI'))
itemfield.children[0].set('FieldURI', 'item:Categories')
itemfield.append(Element('t:CalendarItem'))
categories = Element('t:Categories')
if not isinstance(category, list):
categories.append(Element('t:String').setText(category))
else:
for cat in category:
categories.append(Element('t:String').setText(cat))
itemfield.children[1].append(categories)
updates.append(itemfield)
if body:
itemfield = Element('t:SetItemField')
itemfield.append(Element('t:FieldURI'))
itemfield.children[0].set('FieldURI', 'item:Body')
itemfield.append(Element('t:CalendarItem'))
Body = Element('t:Body')
Body.set('BodyType', getattr(self.types.EWSType_BodyTypeResponseType, bodytype))
Body.setText(body)
itemfield.children[1].append(Body)
updates.append(itemfield)
#timezone
itemfield = Element('t:SetItemField')
itemfield.append(Element('t:FieldURI'))
itemfield.children[0].set('FieldURI', 'calendar:MeetingTimeZone')
itemfield.append(Element('t:CalendarItem'))
timeZone = Element('t:MeetingTimeZone')
timeZone.set('TimeZoneName', TimeZoneName)
timeZone.append(Element('t:BaseOffset').setText(BaseOffset))
standard = Element('t:Standard')
standard.append(Element('t:Offset').setText(StandardOffset))
#ryr = Element(t:'RelativeYearlyRecurrence')
#ryr.append(t:'DaysOfWeek').setText('DaysOfWeek')
#ryr.append(t:'DayOfWeekIndex').setText('First')
#ryr.append(t:'Month').setText('November')
#standard.append(ryr)
standard.append(Element('t:Time').setText(StandardTime))
timeZone.append(standard)
daylight = Element('t:Daylight')
daylight.append(Element('t:Offset').setText(Offset))
daylight.append(Element('t:Time').setText(DaylightTime))
timeZone.append(daylight)
itemfield.children[1].append(timeZone)
updates.append(itemfield)
itemchange.append(updates)
itemchanges.append(itemchange)
updateitem.append(itemchanges)
#make the call
xml = self.wrap(updateitem)
try:
result = self.client.service.UpdateItem(__inject={'msg':xml})
except WebFault as e:
raise e
# Result may come as a single object or a list of objects
if type(result.UpdateItemResponseMessage).__name__ == 'list':
msgs = result.UpdateItemResponseMessage
else:
msgs = [result.UpdateItemResponseMessage]
idlist = []
for msg in msgs:
rspclass = msg._ResponseClass
rspcode = msg.ResponseCode
if rspclass == 'Error':
raise Exception('Error code: %s message: %s' % \
(rspcode, msg.MessageText))
if rspclass != 'Success':
raise Exception('Unknown response class: %s code: %s' % \
(rspclass, rspcode))
if rspcode != 'NoError':
raise Exception('Unknown response code: %s' % rspcode)
item = msg.Items.CalendarItem.ItemId
idlist.append((item._Id, item._ChangeKey))
return idlist
def deleteCalendarEvent(self, ids):
'''======================================
// Delete Calendar Event Items
//======================================
/* @param array $ids - array of event ids to delete
*
* @return object response
*/
'''
return self.deleteItems(ids)
def addTask(self, subject, on_behalf, due, body=None, reminderdue=None, reminderStart="30",\
importance="NORMAL", sensitivity="NORMAL", bodyType="TEXT", category="default"):
'''======================================
// Add Task
//======================================
/* @param string $subject - task subject
* @param string $body - task body
* @param string $onbehalf - "on behalf" seneder's email
* @param int $due - task due date timestamp
* @param int $reminderdue - reminder due date timestamp
* @param int $reminderStart - realtive negative offset for reminder start in nimutes
* @param string $importance - task importance
* @param string $sensitivity - task sensitivity
* @param string $bodytype - task body type (TEXT/HTML)
* @param string $category - task category
*
* @return object response
*/
'''
createitem = Element('m:CreateItem')
saveditemfolderid = Element('m:SavedItemFolderId')
distinguishedfolderid = Element('t:DistinguishedFolderId')
distinguishedfolderid.set('Id', self.types.EWSType_DistinguishedFolderIdNameType.TASKS)
if on_behalf is not None:
mailbox = Element('t:Mailbox')
emailaddress = Element('t:EmailAddress').setText(on_behalf)
mailbox.append(emailaddress)
distinguishedfolderid.append(mailbox)
saveditemfolderid.append(distinguishedfolderid)
createitem.append(saveditemfolderid)
tasks = Element('m:Items')
# Prepare objects for Task creation
task = Element('t:Task')
Subject = Element('t:Subject')
Sensitivity = Element('t:Sensitivity')
cats = None
if category:
cats = Element('t:Categories')
cats.append(Element('t:String').setText(category))
Importance = Element('t:Importance')
ReminderDueBy = Element('t:ReminderDueBy')
ReminderMinutesBeforeStart = Element('t:ReminderMinutesBeforeStart')
ReminderIsSet = Element('t:ReminderIsSet')
DueDate = Element('t:DueDate')
task.append(Subject.setText(subject))
task.append(Sensitivity.setText(getattr(self.types.EWSType_SensitivityChoicesType, sensitivity)))
if body:
Body = Element('t:Body')
Body.set('BodyType', getattr(self.types.EWSType_BodyTypeResponseType, bodyType))
task.append(Body.setText(body))
task.append(cats)
task.append(Importance.setText(getattr(self.types.EWSType_ImportanceChoicesType,importance)))
if(reminderdue):
task.append(ReminderDueBy.setText(reminderdue.ewsformat()))
task.append(ReminderIsSet.setText(1))
task.append(ReminderMinutesBeforeStart.setText(reminderStart))
task.append(DueDate.setText(due.ewsformat()))
#add item to items
tasks.append(task)
#add item to CreateItems
createitem.append(tasks)
#make the call
xml = self.wrap(createitem)
try:
result = self.client.service.CreateItem(__inject={'msg':xml})
except WebFault as e:
raise e
# Result may come as a single object or a list of objects
if type(result.CreateItemResponseMessage).__name__ == 'list':
msgs = result.CreateItemResponseMessage
else:
msgs = [result.CreateItemResponseMessage]
idlist = []
for msg in msgs:
rspclass = msg._ResponseClass
rspcode = msg.ResponseCode
if rspclass == 'Error':
raise Exception('Error code: %s message: %s' % \
(rspcode, msg.MessageText))
if rspclass != 'Success':
raise Exception('Unknown response class: %s code: %s' % \
(rspclass, rspcode))
if rspcode != 'NoError':
raise Exception('Unknown response code: %s' % rspcode)
item = msg.Items.Task.ItemId
idlist.append((item._Id, item._ChangeKey))
return idlist
def editTask(self, id, ckey, subject=None, body=None, bodytype='TEXT', due=None, \
reminderdue=None, reminderStart=None, status=None, percentComplete=None, \
sensitivity=None, importance=None, category=None):
'''======================================
// Edit Task
//======================================
/* @param string $id - event id
* @param string $ckey - event change key
* @param string $subject - event subject
* @param string $body - task body
* @param string $bodytype - task body type (TEXT/HTML)
* @param int $due - task due date timestamp
* @param int $reminderdue - reminder due date timestamp
* @param int $reminderStart - realtive negative offset for reminder start in nimutes
* @param string $status - task status (enumarted in TaskStatusType)
* @param int $percentComplete - task complitionprocentage
* @param string $sensitivity - task sensitivity (enumarted in SensitivityChoicesType)
* @param string $importance - task importance (enumarted in ImportanceChoicesType)
* @param string $category - task category
*
* @return object response
*/
'''
Updates = {
'task:DueDate' : due.ewsformat() if due else None,
'item:ReminderDueBy' : reminderdue.ewsformat() if reminderdue else None,
'item:ReminderMinutesBeforeStart' : reminderStart,
'item:ReminderIsSet' : 1 if (reminderStart or reminderdue) else 0,
'item:Subject' : subject,
'task:Status' : getattr(self.types.EWSType_TaskStatusType, status) if status else None,
'item:Sensitivity' : getattr(self.types.EWSType_SensitivityChoicesType, sensitivity) if sensitivity else None,
'item:Importance' : getattr(self.types.EWSType_ImportanceChoicesType, importance) if importance else None,
'task:PercentComplete' : percentComplete
}
updateitem = Element('m:UpdateItem')
updateitem.set('SendMeetingInvitationsOrCancellations', self.types.EWSType_CalendarItemCreateOrDeleteOperationType.SEND_TO_ALL_AND_SAVE_COPY)
updateitem.set('MessageDisposition', self.types.EWSType_MessageDispositionType.SAVEONLY)
updateitem.set('ConflictResolution', self.types.EWSType_ConflictResolutionType.ALWAYSOVERWRITE)
itemchanges = Element('m:ItemChanges')
itemchange = Element('t:ItemChange')
itemid = Element('t:ItemId')
itemid.set('Id', id)
itemid.set('ChangeKey', ckey)
itemchange.append(itemid)
updates = Element('t:Updates')
#popoulate update array
for key,value in Updates.items():
if value:
prop = key.split(':').pop()
itemfield = Element('t:SetItemField')
itemfield.append(Element('t:FieldURI'))
itemfield.children[0].set('FieldURI', key)
itemfield.append(Element('t:Task'))
itemfield.children[1].append(Element('t:'+prop).setText(value))
updates.append(itemfield)
if category:
itemfield = Element('t:SetItemField')
itemfield.append(Element('t:FieldURI'))
itemfield.children[0].set('FieldURI', 'item:Categories')
itemfield.append(Element('t:Task'))
categories = Element('t:Categories')
categories.append(Element('t:String').setText(category))
itemfield.children[1].append(categories)
updates.append(itemfield)
if body:
itemfield = Element('t:SetItemField')
itemfield.append(Element('t:FieldURI'))
itemfield.children[0].set('FieldURI', 'item:Body')
itemfield.append(Element('t:Task'))
Body = Element('t:Body')
Body.set('BodyType', getattr(self.types.EWSType_BodyTypeResponseType, bodytype))
Body.setText(body)
itemfield.children[1].append(Body)
updates.append(itemfield)
itemchange.append(updates)
itemchanges.append(itemchange)
updateitem.append(itemchanges)
#make the call
xml = self.wrap(updateitem)
try:
result = self.client.service.UpdateItem(__inject={'msg':xml})
except WebFault as e:
raise e
# Result may come as a single object or a list of objects
if type(result.UpdateItemResponseMessage).__name__ == 'list':
msgs = result.UpdateItemResponseMessage
else:
msgs = [result.UpdateItemResponseMessage]
idlist = []
for msg in msgs:
rspclass = msg._ResponseClass
rspcode = msg.ResponseCode
if rspclass == 'Error':
raise Exception('Error code: %s message: %s' % \
(rspcode, msg.MessageText))
if rspclass != 'Success':
raise Exception('Unknown response class: %s code: %s' % \
(rspclass, rspcode))
if rspcode != 'NoError':
raise Exception('Unknown response code: %s' % rspcode)
item = msg.Items.Task.ItemId
idlist.append((item._Id, item._ChangeKey))
return idlist
def deleteTask(self, ids):
'''======================================
// Delete Task Items
//======================================
/* @param array $ids - array of taks ids to delete
*
* @return object response
*/
'''
return self.deleteItems(ids)
def listTask(self, id=None, start=None, end=None, on_behalf=None, shape='DEFAULT_PROPERTIES', categories=None):
'''======================================
// List Calendar Events
//======================================
/* @param string id - item id. takes precendense over timeframe
* @param string $onbehalf - "on behalf" seneder's email
* @param int $start - event start timestamp
* @param int $end - event end time
*
* @return object response
*/
'''
type = 'TASKS'
return self.listItems(type, id, start, end, on_behalf, shape, categories)
def deleteItems(self, ids):
'''======================================
// Delete Items
//======================================
/* @param array $ids - list of item ids to delete
*
* @return list of tuples (success[True|False], errormessage)
*
'''
status = []
if not ids:
# Nothing to do
return status
deleteitem = Element('m:DeleteItem')
deleteitem.set('DeleteType', self.types.EWSType_DisposalType.MOVE_TO_DELETED_ITEMS)
deleteitem.set('SendMeetingCancellations', self.types.EWSType_CalendarItemCreateOrDeleteOperationType.SEND_TO_NONE)
deleteitem.set('AffectedTaskOccurrences', self.types.EWSType_AffectedTaskOccurrencesType.ALL_OCCURRENCES)
itemids = Element('m:ItemIds')
itemid = Element('t:ItemId')
# copy.deepcopy() is faster than having Element() inside the loop.
for iid in ids:
i = deepcopy(itemid)
i.set('Id', iid)
#i.set('ChangeKey', changekey)
itemids.append(i)
deleteitem.append(itemids)
xml = self.wrap(deleteitem)
try:
result = self.client.service.DeleteItem(__inject={'msg':xml})
except WebFault as e:
raise e
# Result may come as a single object or a list of objects
if type(result.DeleteItemResponseMessage).__name__ == 'list':
msgs = result.DeleteItemResponseMessage
else:
msgs = [result.DeleteItemResponseMessage]
msglen = len(msgs)
if len(msgs) != len(ids):
raise Exception('Returned response count doesn\'t match: \
got %s, expected %s' % (len(msgs), len(ids)))
for msg in msgs:
rspclass = msg._ResponseClass
rspcode = msg.ResponseCode
if rspclass == 'Error':
if rspcode == 'ErrorItemNotFound':
# Should not happen, so don't worry about performance
status.append( (False, '%s: %s' % ( msg.MessageText, \
ids[len(status)] ) ) )
continue
raise Exception('Error code: %s message: %s' % \
(rspcode, msg.MessageText))
if rspclass != 'Success':
raise Exception('Unknown response class: %s code: %s' % \
(rspclass, rspcode))
if not rspcode == 'NoError':
raise Exception('Unknown response code: %s' % rspcode)
status.append((True, None))
return status
def listItems(self, type, id=None, start=None, end=None, on_behalf=None, shape='ID_ONLY', categories=[], additional=[]):
'''======================================
// List Items
// Note: currenttly only Taska are
// searcheble by category
//======================================
/* @param string type - item type
* @param string id - item id. takes precendense over timeframe
* @param string $onbehalf - "on behalf": item owner email
* @param int $start - search start timestamp
* @param int $end - search end timestamp
*
* @return object response
*/
'''
if(id):
getitem = Element('m:GetItem')
itemshape = Element('m:ItemShape')
baseshape = Element('t:BaseShape').setText(getattr(self.types.EWSType_DefaultShapeNamesType, shape))
itemshape.append(baseshape)
if additional:
additionalproperties = Element('t:AdditionalProperties')
for URI in additional:
fielduri = Element('t:FieldURI')
fielduri.set('FieldURI', URI)
additionalproperties.append(fielduri)
itemshape.append(additionalproperties)
getitem.append(itemshape)
itemids = Element('m:ItemIds')
if isinstance(id, list):
for single in id:
itemid = Element('t:ItemId')
itemid.set('Id', single)
itemids.append(itemid)
else:
itemid = Element('t:ItemId')
itemid.set('Id', id)
itemids.append(itemid)
getitem.append(itemids)
xml = self.wrap(getitem)
try:
result = self.client.service.GetItem(__inject={'msg':xml})
except WebFault as e:
raise e
msg = result.GetItemResponseMessage
if isinstance(msg, list):
fullitems = []
for el in msg:
rspclass = el._ResponseClass
rspcode = el.ResponseCode
if rspclass == 'Error':
raise Exception('Error code: %s message: %s' % \
(rspcode, msg.MessageText))
if rspclass != 'Success':
raise Exception('Unknown response class: %s code: %s' % \
(rspclass, rspcode))
if rspcode != 'NoError':
raise Exception('Unknown response code: %s' % rspcode)
fullitems.extend([el.Items[0]])
#print fullitems
if not categories:
return fullitems
# Filter for category. Searching for categories only works with
# 'Or' operator, so we need to ignore items with only some
# but not all categories present.
items = []
for item in fullitems:
itemcats = item.Categories[0]
if set(categories).issubset(set(itemcats)):
items.append(item)
return items
else:
rspclass = msg._ResponseClass
rspcode = msg.ResponseCode
if rspclass == 'Error':
raise Exception('Error code: %s message: %s' % \
(rspcode, msg.MessageText))
if rspclass != 'Success':
raise Exception('Unknown response class: %s code: %s' % \
(rspclass, rspcode))
if rspcode != 'NoError':
raise Exception('Unknown response code: %s' % rspcode)
if isinstance(msg.Items[0], list):
fullitems = msg.RootFolder.Items[0]
else:
# Only one item returned
fullitems = [msg.Items].pop()
if not categories:
return fullitems
# Filter for category. Searching for categories only works with
# 'Or' operator, so we need to ignore items with only some
# but not all categories present.
items = []
for item in fullitems:
itemcats = item.Categories[0]
if set(categories).issubset(set(itemcats)):
items.append(item)
return items
else:
finditem = Element('m:FindItem')
finditem.set('Traversal', self.types.EWSType_FolderQueryTraversalType.SHALLOW)
itemshape = Element('m:ItemShape')
baseshape = Element('t:BaseShape').setText(getattr(self.types.EWSType_DefaultShapeNamesType, shape))
itemshape.append(baseshape)
additionalproperties = None
# additional properties
if categories:
additionalproperties = Element('t:AdditionalProperties')
fielduri = Element('t:FieldURI')
fielduri.set('FieldURI', 'item:Categories')
additionalproperties.append(fielduri)
#append all additional properties, if any
if additionalproperties:
itemshape.append(additionalproperties)
finditem.append(itemshape)
#type-sepcific options
if(type == 'CALENDAR'):
if start or end:
calendarview = Element('m:CalendarView')
if start:
calendarview.set('StartDate', start.ewsformat())
if end:
calendarview.set('EndDate', end.ewsformat())
finditem.append(calendarview)
elif(type == 'TASKS'):
# if start or end:
# res = Element('m:Restriction')
# restriction = Element('t:And')
# needed to replace the above with this - ok..?
res = Element('m:Restriction')
restriction = Element('t:And')
if not start:
start = EWSDateTime(0)
if not end:
end = EWSDateTime(2038, 1, 1, 0, 0, 0)
rcount = len([f for f in [start, end, categories] if f != None])
if categories:
if not res:
restriction = Element('m:Restriction')
if len(categories) == 1:
restriction.append(self._restriction('contains', 'item:Categories', categories[0]))
else:
ortype = Element('t:Or')
for cat in categories:
ortype.append(self._restriction('contains', 'item:Categories', cat))
restriction.append(ortype)
extended = self._mapi_reference('33029', 'SystemTime', 'Task')
gteq = self._restriction('>=', extended, start.ewsformat(), True)
restriction.append(gteq)
lseq = self._restriction('<=', extended, end.ewsformat(), True)
restriction.append(lseq)
if rcount > 1:
res.append(restriction)
finditem.append(res)
else:
finditem.append(restriction)
distinguishedfolderid = Element('t:DistinguishedFolderId')
distinguishedfolderid.set('Id', getattr(self.types.EWSType_DistinguishedFolderIdNameType, type))
parentfolderids = Element('m:ParentFolderIds')
mailbox = Element('t:Mailbox')
emailaddress = Element('t:EmailAddress').setText(on_behalf)
mailbox.append(emailaddress)
distinguishedfolderid.append(mailbox)
parentfolderids.append(distinguishedfolderid)
finditem.append(parentfolderids)
xml = self.wrap(finditem)
try:
result = self.client.service.FindItem(__inject={'msg':xml})
except WebFault as e:
raise e
msg = result.FindItemResponseMessage
rspclass = msg._ResponseClass
rspcode = msg.ResponseCode
if rspclass == 'Error':
raise Exception('Error code: %s message: %s' % \
(rspcode, msg.MessageText))
if rspclass != 'Success':
raise Exception('Unknown response class: %s code: %s' % \
(rspclass, rspcode))
if rspcode != 'NoError':
raise Exception('Unknown response code: %s' % rspcode)
if not msg.RootFolder._IncludesLastItemInRange:
raise Exception('Not all items were transferred')
if msg.RootFolder._TotalItemsInView == 0:
return []
if isinstance(msg.RootFolder.Items[0], list):
fullitems = msg.RootFolder.Items[0]
else:
# Only one item returned
fullitems = msg.RootFolder.Items
#if we have addtional proerties to fetch do so
#get ids from response
ids = self.getids(fullitems, False)
#call self with ids and required props
if type=="CALENDAR":
additional.append("calendar:RequiredAttendees")
additional.append("calendar:IsAllDayEvent")
additional.append("calendar:Duration")
additional.append("item:Categories")
additional.append("item:Body")
additional.append("item:Sensitivity")
additional.append("item:Importance")
extended_items = self.listItems(type="CALENDAR", id=ids, additional=additional)
#print extended_items
#print ids
#exit()
extended_add, extended_fday, extended_cats, extended_body = ({} for i in range(4))
extended_sen, extended_imp = ({} for i in range(2))
for i in range(0, len(extended_items)):
if hasattr(extended_items[i], 'RequiredAttendees'):
extended_add[extended_items[i].ItemId._Id] = extended_items[i].RequiredAttendees
else:
extended_add[extended_items[i].ItemId._Id] = []
extended_fday[extended_items[i].ItemId._Id] = extended_items[i].IsAllDayEvent
extended_cats[extended_items[i].ItemId._Id] = extended_items[i].Categories if hasattr(extended_items[i], "Categories") else None
extended_body[extended_items[i].ItemId._Id] = extended_items[i].Body if hasattr(extended_items[i], "Body") else None
extended_sen[extended_items[i].ItemId._Id] = extended_items[i].Sensitivity
extended_imp[extended_items[i].ItemId._Id] = extended_items[i].Importance
for i in range(0,len(fullitems)):
fullitems[i].RequiredAttendees = extended_add.get(fullitems[i].ItemId._Id)
fullitems[i].IsAllDayEvent = extended_fday.get(fullitems[i].ItemId._Id)
fullitems[i].Categories = extended_cats.get(fullitems[i].ItemId._Id)
fullitems[i].Body = extended_body.get(fullitems[i].ItemId._Id)
fullitems[i].Sensitivity = extended_sen.get(fullitems[i].ItemId._Id)
fullitems[i].Importance = extended_imp.get(fullitems[i].ItemId._Id)
if not categories:
return fullitems
# Filter for category. Searching for categories only works with
# 'Or' operator, so we need to ignore items with only some
# but not all categories present.
items = []
for item in fullitems:
itemcats = item.Categories[0]
if set(categories).issubset(set(itemcats)):
items.append(item)
return items
def listFolders(self, type, on_behalf, shape='DEFAULT_PROPERTIES', depth='SHALLOW'):
'''======================================
// List Folders
//======================================
/* @param string type - folder type (enumarted in DistinguishedFolderIdNameType)
* @param string $onbehalf - "on behalf": item owner email
* @param string $shape - detail level (enumarted in DefaultShapeNamesType)
* @param string $depth - list normal /include subfolders (enumarted in FolderQueryTraversalType)
*
* @return object response
*/
'''
# start building the find folder request
request = Element('m:FindFolder')
request.set('Traversal', getattr(self.types.EWSType_FolderQueryTraversalType, depth))
itemshape = Element('m:FolderShape')
baseshape = Element('t:BaseShape').setText(getattr(self.types.EWSType_DefaultShapeNamesType,shape))
itemshape.append(baseshape)
request.append(itemshape)
# configure the view
indexedpage = Element('m:IndexedPageFolderView')
indexedpage.set('BasePoint', 'Beginning')
indexedpage.set('Offset', '0')
request.append(indexedpage)
# set the starting folder as the inbox
distinguishedfolderid = Element('t:DistinguishedFolderId')
distinguishedfolderid.set('Id', getattr(self.types.EWSType_DistinguishedFolderIdNameType, type))
parentfolderids = Element('m:ParentFolderIds')
mailbox = Element('t:Mailbox')
emailaddress = Element('t:EmailAddress').setText(on_behalf)
mailbox.append(emailaddress)
distinguishedfolderid.append(mailbox)
parentfolderids.append(distinguishedfolderid)
request.append(parentfolderids)
xml = self.wrap(request)
try:
result = self.client.service.FindFolder(__inject={'msg':xml})
except WebFault as e:
raise e
msg = result.FindFolderResponseMessage
rspclass = msg._ResponseClass
rspcode = msg.ResponseCode
if rspclass == 'Error':
raise Exception('Error code: %s message: %s' % \
(rspcode, msg.MessageText))
if rspclass != 'Success':
raise Exception('Unknown response class: %s code: %s' % \
(rspclass, rspcode))
if rspcode != 'NoError':
raise Exception('Unknown response code: %s' % rspcode)
if not msg.RootFolder._IncludesLastItemInRange:
raise Exception('Not all items were transferred')
if msg.RootFolder._TotalItemsInView == 0:
return []
if isinstance(msg.RootFolder.Folders[0], list):
fullitems = msg.RootFolder.Folders[0]
else:
# Only one item returned
fullitems = [msg.RootFolder.Folders].pop()
return fullitems
def getids(self, items, include_change_key=True):
'''Takes a list of items with full or partial properties as
produced from getitems() and returns a list of (id, changekey)
tuples.'''
idlist = []
if len(items) > 1:
try:
for item in [i.ItemId for i in items]:
if(include_change_key):
idlist.append((item._Id, item._ChangeKey))
else:
idlist.append(item._Id)
except AttributeError:
for item in [i.ItemId for i in [j[0] for j in items]]:
if(include_change_key):
idlist.append((item._Id, item._ChangeKey))
else:
idlist.append(item._Id)
else:
try:
for item in [i.ItemId for i in [j[1] for j in items]]:
if(include_change_key):
idlist.append((item._Id, item._ChangeKey))
else:
idlist.append(item._Id)
except (IndexError,AttributeError):
for item in [j.ItemId for j in items]:
if(include_change_key):
idlist.append((item._Id, item._ChangeKey))
else:
idlist.append(item._Id)
return idlist
def synchFolder(self, type, on_behalf=None, num_to_synch=10, synch_state=None, shape='ID_ONLY', additional=[], ignored_items=None):
'''======================================
// Synch Folder - synchronizes given folder
//======================================
/* @param string type - folder type (enumarted in DistinguishedFolderIdNameType)
* @param string $onbehalf - "on behalf": item owner email
* @param int num_to_synch - how many items to synch in one go (max 500)
* @param string synch_state - current synch state, blank on initial synch
* @param string $shape - detail level (enumarted in DefaultShapeNamesType)
* @param string $ignored_items - list of IDs of ignored items (currently not implemented)
*
* @return object response
*/
'''
synchfolder = Element('m:SyncFolderItems')
itemshape = Element('m:ItemShape')
baseshape = Element('t:BaseShape').setText(getattr(self.types.EWSType_DefaultShapeNamesType, shape))
itemshape.append(baseshape)
synchfolder.append(itemshape)
distinguishedfolderid = Element('t:DistinguishedFolderId')
distinguishedfolderid.set('Id', getattr(self.types.EWSType_DistinguishedFolderIdNameType, type))
parentfolderids = Element('m:SyncFolderId')
mailbox = Element('t:Mailbox')
emailaddress = Element('t:EmailAddress').setText(on_behalf)
mailbox.append(emailaddress)
distinguishedfolderid.append(mailbox)
parentfolderids.append(distinguishedfolderid)
synchfolder.append(parentfolderids)
if synch_state is not None:
synchstate = Element('m:SyncState').setText(synch_state)
synchfolder.append(synchstate)
if ignored_items:
#@TODO : add ignored items...
pass
numresults = Element('m:MaxChangesReturned').setText(num_to_synch)
synchfolder.append(numresults)
xml = self.wrap(synchfolder)
try:
result = self.client.service.SyncFolderItems(__inject={'msg':xml})
except WebFault as e:
raise e
msg = result.SyncFolderItemsResponseMessage
rspclass = msg._ResponseClass
rspcode = msg.ResponseCode
if rspclass == 'Error':
raise Exception('Error code: %s message: %s' % \
(rspcode, msg.MessageText))
if rspclass != 'Success':
raise Exception('Unknown response class: %s code: %s' % \
(rspclass, rspcode))
if rspcode != 'NoError':
raise Exception('Unknown response code: %s' % rspcode)
if not msg.IncludesLastItemInRange:
self.hasMoreItems = True
else:
self.hasMoreItems = False
if msg.SyncState:
self.synchState = msg.SyncState
#if isinstance(msg.Changes[0], list):
# fullitems = msg.Changes[0]
#else:
# Only one item returned
changes = msg.Changes
fullitems = []
__fullitems = []
ids = []
fullitems_add = []
fullitems_update = []
#add/update events
if hasattr(changes, 'Create'):
if isinstance(changes.Create[0], list):
__fullitems.extend(changes.Create[0])
else:
__fullitems.extend(changes.Create)
#fix attributes
if len(__fullitems) > 1:
for elem in __fullitems:
fullitems.append(elem[0])
else:
for elem in __fullitems:
fullitems.append(elem[1])
#get ids from response
ids.extend(self.getids(fullitems, False))
fullitems_add = fullitems
fullitems = []
__fullitems = []
if hasattr(changes, 'Update'):
if isinstance(changes.Update[0], list):
__fullitems.extend(changes.Update[0])
else:
__fullitems.extend(changes.Update)
#fix attributes
if len(__fullitems) > 1:
for elem in __fullitems:
fullitems.append(elem[0])
else:
for elem in __fullitems:
fullitems.append(elem[1])
#get ids from response
ids.extend(self.getids(fullitems, False))
fullitems_update = fullitems
fullitems = []
__fullitems = []
#if we have addtional proerties to fetch do so
#join fulltimes toghether
fullitems = fullitems_add + fullitems_update
#call self with ids and required props
if type=="CALENDAR":
if ids:
additional.append("calendar:RequiredAttendees")
additional.append("calendar:IsAllDayEvent")
additional.append("calendar:Duration")
additional.append("item:Categories")
additional.append("item:Body")
additional.append("item:Sensitivity")
additional.append("item:Importance")
extended_items = self.listItems(type="CALENDAR", id=ids, additional=additional)
#print extended_items
#print ids
#exit()
extended_add, extended_fday, extended_cats, extended_body = ({} for i in range(4))
extended_sen, extended_imp = ({} for i in range(2))
for i in range(0, len(extended_items)):
if hasattr(extended_items[i], 'RequiredAttendees'):
extended_add[extended_items[i].ItemId._Id] = extended_items[i].RequiredAttendees
else:
extended_add[extended_items[i].ItemId._Id] = []
extended_fday[extended_items[i].ItemId._Id] = extended_items[i].IsAllDayEvent
extended_cats[extended_items[i].ItemId._Id] = extended_items[i].Categories if hasattr(extended_items[i], "Categories") else None
extended_body[extended_items[i].ItemId._Id] = extended_items[i].Body if hasattr(extended_items[i], "Body") else None
extended_sen[extended_items[i].ItemId._Id] = extended_items[i].Sensitivity
extended_imp[extended_items[i].ItemId._Id] = extended_items[i].Importance
try:
for i in range(0,len(fullitems)):
fullitems[i].RequiredAttendees = extended_add.get(fullitems[i].ItemId._Id)
fullitems[i].IsAllDayEvent = extended_fday.get(fullitems[i].ItemId._Id)
fullitems[i].Categories = extended_cats.get(fullitems[i].ItemId._Id)
fullitems[i].Body = extended_body.get(fullitems[i].ItemId._Id)
fullitems[i].Sensitivity = extended_sen.get(fullitems[i].ItemId._Id)
fullitems[i].Importance = extended_imp.get(fullitems[i].ItemId._Id)
except AttributeError:
fullitems.RequiredAttendees = extended_add.get(fullitems.ItemId._Id)
fullitems.IsAllDayEvent = extended_fday.get(fullitems.ItemId._Id)
fullitems.Categories = extended_cats.get(fullitems.ItemId._Id)
fullitems.Body = extended_body.get(fullitems.ItemId._Id)
fullitems.Sensitivity = extended_sen.get(fullitems.ItemId._Id)
fullitems.Importance = extended_imp.get(fullitems.ItemId._Id)
if(len(fullitems) < 2):
fullitems = [("EventSpoofedType",fullitems.pop())]
#allitems = type('', (object,), {})
allitems = lambda:0
allitems.addupdate = fullitems
if hasattr(changes, 'Delete'):
if len(changes.Delete) < 2:
allitems.delete = [changes.Delete]
else:
allitems.delete = changes.Delete
return allitems
class EWSDateTime(datetime):
'''Extends the normal datetime implementation to satisfy Exchange'''
def ewsformat(self):
'''ISO 8601 format to satisfy xs:datetime as interpreted by Exchange'''
return self.strftime('%Y-%m-%dT%H:%M:%S')
def midnightfix(self):
return self - timedelta(seconds=1)
def __new__(cls, y=None, m=None, d=None, h=None, mi=None, s=None):
if((y>=0) and (all(x is None for x in (m,d,h,mi,s)))):
t = datetime.fromtimestamp(y)
return super(EWSDateTime, cls).__new__(cls,t.year,t.month,t.day,t.hour,t.minute,t.second)
else:
return super(EWSDateTime, cls).__new__(cls,y,m,d,h,mi,s)
'''
/* EWSWrapper_py
* ====================================================
* @author Michal Korzeniowski <mko_san@lafiel.net>
* @version 0.1
* @date 10-2011
* @website http://ewswrapper.lafiel.net/
* ====================================================
* Desciption
* Provides enumerable types from Exchange Web Services
* for EWSWrapper. Accessible from EWSWrapper under
* self.types
*
* ==================================================*/
'''
class EWSType:
class EWSType_AffectedTaskOccurrencesType:
##
# Specifies that a DeleteItem request deletes the master task, and therefore all recurring tasks that are associated with the master task.
#
# @var string
#
ALL_OCCURRENCES='AllOccurrences'
##
# Specifies that a DeleteItem request deletes only the current occurrence of a task.
#
# @var string
#
SPECIFIED_OCCURRENCES_ONLY='SpecifiedOccurrenceOnly'
##
# Constructor
#
def __init__() :
pass
# end class CalendarItemCreateOrDeleteOperationType
class EWSType_BodyTypeResponseType:
##
# All properties are retured in the response
#
# @var string
#
BEST='Best'
##
# Default properties are returned in the respoonse
#
# @var string
#
HTML='HTML'
##
# Plain text body
#
# @var string
#
TEXT='Text'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_BodyTypeResponseType
class EWSType_CalendarItemCreateOrDeleteOperationType:
##
# Send to
#
# @var string
#
SEND_TO_NONE='SendToNone'
##
# Send to
#
# @var string
#
SEND_ONLY_TO_ALL='SendOnlyToAll'
##
# Send to
#
# @var string
#
SEND_TO_ALL_AND_SAVE_COPY='SendToAllAndSaveCopy'
##
# Constructor
#
def __init__() :
pass
# end class CalendarItemCreateOrDeleteOperationType
class EWSType_CalendarItemUpdateOperationType:
##
# Send to
#
# @var string
#
SEND_TO_NONE='SendToNone'
##
# Send to
#
# @var string
#
SEND_ONLY_TO_ALL='SendOnlyToAll'
##
# Send to
#
# @var string
#
SEND_TO_ALL_AND_SAVE_COPY='SendToAllAndSaveCopy'
##
# Send to
#
# @var string
#
SEND_ONLY_TO_CHANGED='SendOnlyToChanged'
##
# Send to
#
# @var string
#
SEND_TO_CHANGED_AND_SAVE_COPY='SendToChangedAndSaveCopy'
##
# Constructor
#
def __init__() :
pass
# end class CalendarItemUpdateOperationType
class EWSType_DefaultShapeNamesType:
##
# All properties are retured in the response
#
# @var string
#
ALL_PROPERTIES='AllProperties'
##
# Default properties are returned in the respoonse
#
# @var string
#
DEFAULT_PROPERTIES='Default'
##
# Only folder ids are returned in the response
#
# @var string
#
ID_ONLY='IdOnly'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_DefaultShapeNamesType
class EWSType_DistinguishedFolderIdNameType:
##
# Calendar folder
#
# @var string
#
CALENDAR='calendar'
##
# Contacts folder
#
# @var string
#
CONTACTS='contacts'
##
# Deleted Items folder (ie. trash)
#
# @var string
#
DELETED_ITEMS='deleteditems'
##
# Drafts folder
#
# @var string
#
DRAFTS='drafts'
##
# Inbox folder
#
# @var string
#
INBOX='inbox'
##
# Journal folder
#
# @var string
#
JOURNAL='journal'
##
# Notes folder
#
# @var string
#
NOTES='notes'
##
# Outbox folder
#
# @var string
#
OUTBOX='outbox'
##
# Sent Items folder
#
# @var string
#
SENT_ITEMS='sentitems'
##
# Tasks folder
#
# @var string
#
TASKS='tasks'
##
# Root of the message folders
#
# @var string
#
MESSAGE_FOLDER_ROOT='msgfolderroot'
##
# Root of the folders=''
#
# @var string
#
PUBLIC_FOLDERS_ROOT='publicfoldersroot'
##
# Root of the user's mailbox
#
# @var string
#
ROOT='root'
##
# Junk Email folder
#
# @var string
#
JUNK_EMAIL='junkemail'
##
# Search folders
#
# @var string
#
SEARCH_FOLDERS='searchfolders'
##
# Voicemail folder
#
# @var string
#
VOICEMAIL='voicemail'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_DistinguishedFolderIdNameType
class EWSType_DisposalType:
##
# Deletes the item irrevocably. Does not move the item to the Deleted Items
# Folder.
#
# @var string
#
HARD_DELETE='HardDelete'
##
# Does not actually delete the item, but instead simply moves it to the
# Deleted Items folder.
#
# @var string
#
MOVE_TO_DELETED_ITEMS='MoveToDeletedItems'
##
# "Deletes" the item so that it is no longer visible in the folder, but
# actually still exists there. Avoid using this because there is nothing
# that you can do with soft-deleted items from EWS aside from finding them.
#
# @var string
#
SOFT_DELETE='SoftDelete'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_DisposalType
class EWSType_IndexBasePointType:
##
# Specifies that a DeleteItem request deletes the master task, and therefore all recurring tasks that are associated with the master task.
#
# @var string
#
BEGINNING='Beginning'
##
# Specifies that a DeleteItem request deletes only the current occurrence of a task.
#
# @var string
#
END='End'
##
# Constructor
#
def __init__() :
pass
# end class CalendarItemCreateOrDeleteOperationType
class EWSType_ItemQueryTraversalType:
##
# Consider only folders that are direct children of the parent folder(s) in
# question
#
# @var string
#
SHALLOW='Shallow'
##
# Consider only those items that are soft deleted from the parent folders
# specified
#
# @var string
#
SOFT_DELETED='SoftDeleted'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_ItemQueryTraversalType
class EWSType_FolderQueryTraversalType:
##
# Consider both direct children as well as all subfolders contained within
# those children as well as the children's children, etc.
#
# @var string
#
DEEP='Deep'
##
# Consider only folders that are direct children of the parent folder(s) in
# question
#
# @var string
#
SHALLOW='Shallow'
##
# Consider only those items that are soft deleted from the parent folders
# specified
#
# @var string
#
SOFT_DELETED='SoftDeleted'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_FolderQueryTraversalType
class EWSType_FileAsMappingType:
##
# File as mapping for "company"
#
# @var string
#
COMPANY='Company'
##
# File as mapping for "last name, first name"
#
# @var
#
COMPANY_LAST_COMMA_FIRST='CompanyLastCommaFirst'
##
# File as mapping for "company last name first name"
#
# @var string
#
COMPANY_LAST_FIRST='CompanyLastFirst'
##
# File as mapping for "company last name first name"
#
# @var string
#
COMPANY_LAST_SPACE_FIRST='CompanyLastSpaceFirst'
##
# File as mapping for "first name last name"
#
# @var string
#
FIRST_SPACE_LAST='FirstSpaceLast'
##
# File as mapping for "last name first name"
#
# @var string
#
LAST_FIRST='LastFirst'
##
# File as mapping for "last name first name company"
#
# @var string
#
LAST_FIRST_COMPANY='LastFirstCompany'
##
# File as mapping for "last name first name suffix"
#
# @var string
#
LAST_FIRST_SUFFIX='LastFirstSuffix'
##
# File as mapping for "last name, first name"
#
# @var string
#
LAST_COMMA_FIRST='LastCommaFirst'
##
# File as mapping for "last name, first name company"
#
# @var string
#
LAST_COMMA_FIRST_COMPANY='LastCommaFirstCompany'
##
# File as mapping for "last name first name"
#
# @var string
#
LAST_SPACE_FIRST='LastSpaceFirst'
##
# File as mapping for "lasy name first name company"
#
# @var string
#
LAST_SPACE_FIRST_COMPANY='LastSpaceFirstCompany'
##
# File as mapping to use when no mapping is desired.
#
# @var string
#
NONE='None'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_FileAsMappingType
class EWSType_EmailAddressKeyType:
##
# Key for a contacts first email address
#
# @var string
#
EMAIL_ADDRESS_1='EmailAddress1'
##
# Key for a contacts second email address
#
# @var string
#
EMAIL_ADDRESS_2='EmailAddress2'
##
# Key for a contacts third email address
#
# @var string
#
EMAIL_ADDRESS_3='EmailAddress3'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_EmailAddressKeyType
class EWSType_PhysicalAddressKeyType:
##
# Business physical address type
#
# @var string
#
BUSINESS='Business'
##
# Home physical address type
#
# @var string
#
HOME='Home'
##
# Other physical address type
#
# @var string
#
OTHER='Other'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_PhysicalAddressKeyType
class EWSType_PhoneNumberKeyType:
##
# Phone number key for assistant phone number
#
# @var string
#
ASSISTANT_PHONE='AssistantPhone'
##
# Phone number key for business fax number
#
# @var string
#
BUSINESS_FAX='BusinessFax'
##
# Phone number key for business phone number
#
# @var string
#
BUSINESS_PHONE='BusinessPhone'
##
# Phone number key for second business phone number
#
# @var string
#
BUSINESS_PHONE_2='BusinessPhone2'
##
# Phone number key for callback
#
# @var string
#
CALLBACK='Callback'
##
# Phone number key for car phone
#
# @var string
#
CAR_PHONE='CarPhone'
##
# Phone number key for company main phone
#
# @var string
#
COMPANY_MAIN_PHONE='CompanyMainPhone'
##
# Phone number key for home fax number
#
# @var string
#
HOME_FAX='HomeFax'
##
# Phone number key for home phone number
#
# @var string
#
HOME_PHONE='HomePhone'
##
# Phone number key for second home phone number
#
# @var string
#
HOME_PHONE_2='HomePhone2'
##
# Phone number key for ISDN line
#
# @var string
#
ISDN='Isdn'
##
# Phone number key for mobile phone number
#
# @var string
#
MOBILE_PHONE='MobilePhone'
##
# Phone number key for other fax number
#
# @var string
#
OTHER_FAX='OtherFax'
##
# Phone number key for other phone number
#
# @var string
#
OTHER_PHONE='OtherTelephone'
##
# Phone number key for pager
#
# @var string
#
PAGER='Pager'
##
# Phone number key for primary phone number
#
# @var string
#
PRIMARY_PHONE='PrimaryPhone'
##
# Phone number key for radio phone number
#
# @var string
#
RADIO_PHONE='RadioPhone'
##
# Phone number key for telex
#
# @var string
#
TELEX='Telex'
##
# Phone number key for TTY TTD phone number
#
# @var string
#
TTY_TTD_PHONE='TtyTtdPhone'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_PhoneNumberKeyType
class EWSType_ImportanceChoicesType:
##
# Specifies low priority
#
# @var string
#
LOW='Low'
##
# Specifies normal priority
#
# @var string
#
NORMAL='Normal'
##
# Specifies high priority
#
# @var string
#
HIGH='High'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_ImportanceChoicesType
class EWSType_TaskStatusType:
##
# Specifies that the task is completed.
#
# @var string
#
COMPLETED='Completed'
##
# Specifies that the task is deferred.
#
# @var string
#
DEFERRED='Deferred'
##
# Specifies that the task is in progress.
#
# @var string
#
INPROGRESS='InProgress'
##
# Specifies that the task is in progress.
#
# @var string
#
NOTSTARTED='NotStarted'
##
# Specifies that the task is in progress.
#
# @var string
#
WAITINGONOTHERS='WaitingOnOthers'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_TaskStatusType
class EWSType_SortDirectionType:
##
# Items are sorted in ascending order
#
# @var string
#
ASCENDING='Ascending'
##
# Items are sorted in descending order
#
# @var string
#
DESCENDING='Descending'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_SortDirectionType
class EWSType_SensitivityChoicesType:
##
# Specifies normal confidentiality.
#
# @var string
#
NORMAL='Normal'
##
# Specifies personal confidentiality.
#
# @var string
#
PERSONAL='Personal'
##
# Specifies confidentiality.=''
#
# @var string
#
PRIVATESENSITIVITY='Private'
##
# Specifies confidential confidentiality.
#
# @var string
#
CONFIDENTIAL='Confidential'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_SensitivityChoicesType
class EWSType_MessageDispositionType:
##
# When used in the CreateItemType, the e-mail message item is saved in the folder that is specified by the SavedItemFolderId property, or in the Sent Items folder if SavedItemFolderId is not specified.
#
# @var string
#
SAVEONLY='SaveOnly'
##
# When used in the CreateItemType, the e-mail message item is sent and a copy is saved in the folder that is specified by the SavedItemFolderId property, or in the Sent Items folder if SavedItemFolderId is not specified.
#
# @var string
#
SENDANDSAVECOPY='SendAndSaveCopy'
##
#When used in the CreateItemType, the e-mail message item is sent but no copy is saved.
#
# @var string
#
SENDONLY='SendOnly'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_MessageDispositionType
class EWSType_ConflictResolutionType:
##
# If there is a conflict, the UpdateItem operation will overwrite information.
#
# @var string
#
ALWAYSOVERWRITE='AlwaysOverwrite'
##
# The UpdateItem operation automatically resolves any conflict. The AutoResolve option will in most cases overwrite the existing value for a property. In some cases, the new value is ignored and the original value is retained. For example, user A changes the Sensitivity property from Normal to Confidential. Then user B sets the value to Public. In this example, the Confidential setting is retained and user B's update is ignored.
#
# @var string
#
AUTORESOLVE='AutoResolve'
##
# If conflict exists, the UpdateItem operation fails and an error is returned.
# @var string
#
NEVEROVERWRITE='NeverOverwrite'
##
# Constructor
#
def __init__() :
pass
# end class EWSType_MessageDispositionType
##
# Definition of the CalendarItemType type
class EWSType_CalendarItemType:
def __init__(self, subject, start, end, body=None, location=None):
##
# UID property
#
# @var string
#
self.UID=''
##
# RecurrenceId property
#
# @var EWSType_dateTime
#
self.RecurrenceId=''
##
# DateTimeStamp property
#
# @var EWSType_dateTime
#
self.DateTimeStamp=''
##
# Start property
#
# @var EWSType_dateTime
#
self.Start=''
##
# End property
#
# @var EWSType_dateTime
#
self.End=''
##
# OriginalStart property
#
# @var EWSType_dateTime
#
self.OriginalStart=''
##
# IsAllDayEvent property
#
# @var EWSType_boolean
#
self.IsAllDayEvent=''
##
# LegacyFreeBusyStatus property
#
# @var EWSType_LegacyFreeBusyType
#
self.LegacyFreeBusyStatus=''
##
# Location property
#
# @var string
#
self.Location=''
##
# When property
#
# @var string
#
self.When=''
##
# IsMeeting property
#
# @var EWSType_boolean
#
self.IsMeeting=''
##
# IsCancelled property
#
# @var EWSType_boolean
#
self.IsCancelled=''
##
# IsRecurring property
#
# @var EWSType_boolean
#
self.IsRecurring=''
##
# MeetingRequestWasSent property
#
# @var EWSType_boolean
#
self.MeetingRequestWasSent=''
##
# IsResponseRequested property
#
# @var EWSType_boolean
#
self.IsResponseRequested=''
##
# CalendarItemType property
#
# @var EWSType_CalendarItemTypeType
#
self.CalendarItemType=''
##
# MyResponseType property
#
# @var EWSType_ResponseTypeType
#
self.MyResponseType=''
##
# Organizer property
#
# @var EWSType_SingleRecipientType
#
self.Organizer=''
##
# RequiredAttendees property
#
# @var EWSType_NonEmptyArrayOfAttendeesType
#
self.RequiredAttendees=''
##
# OptionalAttendees property
#
# @var EWSType_NonEmptyArrayOfAttendeesType
#
self.OptionalAttendees=''
##
# Resources property
#
# @var EWSType_NonEmptyArrayOfAttendeesType
#
self.Resources=''
##
# ConflictingMeetingCount property
#
# @var EWSType_int
#
self.ConflictingMeetingCount=''
##
# AdjacentMeetingCount property
#
# @var EWSType_int
#
self.AdjacentMeetingCount=''
##
# ConflictingMeetings property
#
# @var EWSType_NonEmptyArrayOfAllItemsType
#
self.ConflictingMeetings=''
##
# AdjacentMeetings property
#
# @var EWSType_NonEmptyArrayOfAllItemsType
#
self.AdjacentMeetings=''
##
# Duration property
#
# @var string
#
self.Duration=''
##
# TimeZone property
#
# @var string
#
self.TimeZone=''
##
# AppointmentReplyTime property
#
# @var EWSType_dateTime
#
self.AppointmentReplyTime=''
##
# AppointmentSequenceNumber property
#
# @var EWSType_int
#
self.AppointmentSequenceNumber=''
##
# AppointmentState property
#
# @var EWSType_int
#
self.AppointmentState=''
##
# Recurrence property
#
# @var EWSType_RecurrenceType
#
self.Recurrence=''
##
# FirstOccurrence property
#
# @var EWSType_OccurrenceInfoType
#
self.FirstOccurrence=''
##
# LastOccurrence property
#
# @var EWSType_OccurrenceInfoType
#
self.LastOccurrence=''
##
# ModifiedOccurrences property
#
# @var EWSType_NonEmptyArrayOfOccurrenceInfoType
#
self.ModifiedOccurrences=''
##
# DeletedOccurrences property
#
# @var EWSType_NonEmptyArrayOfDeletedOccurrencesType
#
self.DeletedOccurrences=''
##
# MeetingTimeZone property
#
# @var EWSType_TimeZoneType
#
self.MeetingTimeZone=''
##
# ConferenceType property
#
# @var EWSType_int
#
self.ConferenceType=''
##
# AllowNewTimeProposal property
#
# @var EWSType_boolean
#
self.AllowNewTimeProposal=''
##
# IsOnlineMeeting property
#
# @var EWSType_boolean
#
self.IsOnlineMeeting=''
##
# MeetingWorkspaceUrl property
#
# @var string
#
self.MeetingWorkspaceUrl=''
##
# NetShowUrl property
#
# @var string
#
self.NetShowUrl=''
self.Subject = subject
self.Start = start
self.End = end
self.Body = body
self.Location = location
#self.hasreminder = hasreminder
def __str__(self):
return '''Subject: %s
Start: %s
End: %s
Location: %s
Body: %s''' % (self.subject, self.start, self.end, self.location, self.body)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment