def calendar_home_set(self, url): if isinstance(url, CalendarSet): self._calendar_home_set = url else: if not is_url(url): self._calendar_home_set = CalendarSet(self.client, self.client.url.join(URL.objectify(url))) else: self.client.url = URL.objectify(url) self.client.handle = httplib.HTTPSConnection(self.client.url.hostname, self.client.url.port) self._calendar_home_set = CalendarSet(self.client, URL.objectify(url))
def __init__(self, client=None, url=None): """ url input is for backward compatibility and should normally be avoided. If url is not given, deduct principal path as well as calendar home set path from doing propfinds. """ self.client = client self._calendar_home_set = None ## backwards compatibility. if url is not None: self.url = client.url.join(URL.objectify(url)) else: self.url = self.client.url cup = self.get_properties([dav.CurrentUserPrincipal()]) self.url = self.client.url.join(URL.objectify(cup['{DAV:}current-user-principal']))
def __init__(self, client=None, url=None, parent=None, name=None, id=None, etag=None, **extra): """ Default constructor. Parameters: * client: A DAVClient instance * url: The url for this object. May be a full URL or a relative URL. * parent: The parent object - used when creating objects * name: A displayname * id: The resource id (UID for an Event) """ if client is None and parent is not None: client = parent.client self.client = client self.parent = parent self.name = name self.id = id self.etag = etag self.extra_init_options = extra ## url may be a path relative to the caldav root if client and url: self.url = client.url.join(url) else: self.url = URL.objectify(url)
def __init__(self, client=None, url=None, parent=None, name=None, id=None, **extra): """ Default constructor. Parameters: * client: A DAVClient instance * url: The url for this object. May be a full URL or a relative URL. * parent: The parent object - used when creating objects * name: A displayname * id: The resource id (UID for an Event) """ if client is None and parent is not None: client = parent.client self.client = client self.parent = parent self.name = name self.id = id self.extra_init_options = extra # url may be a path relative to the caldav root if client and url: self.url = client.url.join(url) else: self.url = URL.objectify(url)
def children(self, type=None): """ List children, using a propfind (resourcetype) on the parent object, at depth = 1. """ c = [] depth = 1 properties = {} props = [dav.ResourceType(), ] response = self._query_properties(props, depth) for r in response.tree.findall(dav.Response.tag): # We use canonicalized urls to index children href = str(self.url.join(URL.objectify(r.find(dav.Href.tag).text)).canonical()) assert(href) properties[href] = {} for p in props: t = r.find(".//" + p.tag) if len(list(t)) > 0: if type is not None: val = t.find(".//" + type) else: val = t.find(".//*") if val is not None: val = val.tag else: val = None else: val = t.text properties[href][p.tag] = val for path in properties.keys(): resource_type = properties[path][dav.ResourceType.tag] if resource_type == type or type is None: path = URL.objectify(path) ## TODO: investigate the RFCs thoroughly - why does a "get ## members of this collection"-request also return the collection URL itself? ## And why is the strip_trailing_slash-method needed? The collection URL ## should always end with a slash according to RFC 2518, section 5.2. if self.url.strip_trailing_slash() != path.strip_trailing_slash(): c.append((path, resource_type)) return c
def request(self, url, method="GET", body="", headers={}): """ Actually sends the request """ # objectify the url url = URL.objectify(url) proxies = None if self.proxy is not None: proxies = {url.scheme: self.proxy} log.debug("using proxy - %s" % (proxies)) # ensure that url is a unicode string url = str(url) combined_headers = dict(self.headers) combined_headers.update(headers) if body is None or body == "" and "Content-Type" in combined_headers: del combined_headers["Content-Type"] log.debug( "sending request - method={0}, url={1}, headers={2}\nbody:\n{3}" .format(method, url, combined_headers, body)) auth = None if self.auth is None and self.username is not None: auth = requests.auth.HTTPDigestAuth(self.username, self.password) else: auth = self.auth r = requests.request(method, url, data=to_wire(body), headers=combined_headers, proxies=proxies, auth=auth, verify=self.ssl_verify_cert) response = DAVResponse(r) # If server supports BasicAuth and not DigestAuth, let's try again: if response.status == 401 and self.auth is None and auth is not None: auth = requests.auth.HTTPBasicAuth(self.username, self.password) r = requests.request(method, url, data=to_wire(body), headers=combined_headers, proxies=proxies, auth=auth, verify=self.ssl_verify_cert) response = DAVResponse(r) # this is an error condition the application wants to know if response.status == requests.codes.forbidden or \ response.status == requests.codes.unauthorized: ex = error.AuthorizationError() ex.url = url ex.reason = response.reason raise ex # let's save the auth object and remove the user/pass information if not self.auth and auth: self.auth = auth del self.username del self.password return response
def __init__(self, url, proxy=None, username=None, password=None, auth=None, ssl_verify_cert=True, timeout=None): """ Sets up a HTTPConnection object towards the server in the url. Parameters: * url: A fully qualified url: `scheme://user:pass@hostname:port` * proxy: A string defining a proxy server: `hostname:port` * username and password should be passed as arguments or in the URL * auth and ssl_verify_cert is passed to requests.request. ** ssl_verify_cert can be the path of a CA-bundle or False. """ self.session = requests.Session() log.debug("url: " + str(url)) self.url = URL.objectify(url) self.timeout = timeout # Prepare proxy info if proxy is not None: self.proxy = proxy # requests library expects the proxy url to have a scheme if re.match('^.*://', proxy) is None: self.proxy = self.url.scheme + '://' + proxy # add a port is one is not specified # TODO: this will break if using basic auth and embedding # username:password in the proxy URL p = self.proxy.split(":") if len(p) == 2: self.proxy += ':8080' log.debug("init - proxy: %s" % (self.proxy)) # Build global headers self.headers = { "User-Agent": "Mozilla/5.0", "Content-Type": "text/xml", "Accept": "text/xml, text/calendar" } if self.url.username is not None: username = unquote(self.url.username) password = unquote(self.url.password) self.username = username self.password = password self.auth = auth # TODO: it's possible to force through a specific auth method here, # but no test code for this. self.ssl_verify_cert = ssl_verify_cert self.url = self.url.unauth() log.debug("self.url: " + str(url))
def calendar_home_set(self, url): if isinstance(url, CalendarSet): self._calendar_home_set = url return sanitized_url = URL.objectify(url) if sanitized_url.hostname and sanitized_url.hostname != self.client.url.hostname: ## icloud (and others?) having a load balanced system, where each principal resides on one named host self.client.url = sanitized_url self._calendar_home_set = CalendarSet(self.client, self.client.url.join(sanitized_url))
def calendar_home_set(self, url): if isinstance(url, CalendarSet): self._calendar_home_set = url return sanitized_url = URL.objectify(url) if sanitized_url.hostname and sanitized_url.hostname != self.client.url.hostname: ## icloud (and others?) having a load balanced system, where each principal resides on one named host self.client.url = sanitized_url self._calendar_home_set = CalendarSet( self.client, self.client.url.join(sanitized_url))
def __init__(self, client=None, url=None): """ Returns a Principal. Parameters: * client: a DAVClient() oject * url: Deprecated - for backwards compatibility purposes only. If url is not given, deduct principal path as well as calendar home set path from doing propfinds. """ self.client = client self._calendar_home_set = None ## backwards compatibility. if url is not None: self.url = client.url.join(URL.objectify(url)) else: self.url = self.client.url cup = self.get_properties([dav.CurrentUserPrincipal()]) self.url = self.client.url.join(URL.objectify(cup['{DAV:}current-user-principal']))
def __init__(self, client=None, url=None): """ Returns a Principal. Parameters: * client: a DAVClient() oject * url: Deprecated - for backwards compatibility purposes only. If url is not given, deduct principal path as well as calendar home set path from doing propfinds. """ self.client = client self._calendar_home_set = None ## backwards compatibility. if url is not None: self.url = client.url.join(URL.objectify(url)) else: self.url = self.client.url cup = self.get_properties([dav.CurrentUserPrincipal()]) self.url = self.client.url.join( URL.objectify(cup['{DAV:}current-user-principal']))
def children(self, type=None): """ List children, using a propfind (resourcetype) on the parent object, at depth = 1. """ c = [] depth = 1 properties = {} props = [dav.ResourceType(), ] response = self._query_properties(props, depth) for r in response.tree.findall(dav.Response.tag): # We use canonicalized urls to index children href = str(self.url.join(URL.objectify(r.find(dav.Href.tag).text)).canonical()) assert(href) properties[href] = {} for p in props: t = r.find(".//" + p.tag) if len(list(t)) > 0: if type is not None: val = t.find(".//" + type) else: val = t.find(".//*") if val is not None: val = val.tag else: val = None else: val = t.text properties[href][p.tag] = val for path in properties.keys(): resource_type = properties[path][dav.ResourceType.tag] if resource_type == type or type is None: if self.url != path: c.append((URL.objectify(path), resource_type)) return c
def save(self): """ The save method for a calendar is only used to create it, for now. We know we have to create it when we don't have a url. Returns: * self """ if self.url is None: self._create(name=self.name, id=self.id, **self.extra_init_options) if not self.url.endswith('/'): self.url = URL.objectify(str(self.url) + '/') return self
def save(self): """ The save method for a calendar is only used to create it, for now. We know we have to create it when we don't have a url. Returns: * self """ if self.url is None: self._create(self.name, self.id) if not self.url.endswith('/'): self.url = URL.objectify(str(self.url) + '/') return self
class DAVClientMock: """Mock class for DAVClient instance.""" url = URL.objectify("http://localhost") def mkcalendar(self, url): return True def delete(self, url): return True def proppatch(self, url, body, dummy=None): return Response(200) def request(self, url, method="GET", body="", headers={}): return Response(200)
def calendar_home_set(self, url): if isinstance(url, CalendarSet): self._calendar_home_set = url return sanitized_url = URL.objectify(url) ## TODO: sanitized_url should never be None, this needs more ## research. added here as it solves real-world issues, ref ## https://github.com/python-caldav/caldav/pull/56 if sanitized_url is not None: if (sanitized_url.hostname and sanitized_url.hostname != self.client.url.hostname): # icloud (and others?) having a load balanced system, # where each principal resides on one named host self.client.url = sanitized_url self._calendar_home_set = CalendarSet( self.client, self.client.url.join(sanitized_url))
def __init__(self, url, proxy=None, username=None, password=None, headers=None): """ Sets up a HTTPConnection object towards the server in the url. Parameters: * url: A fully qualified url: `scheme://user:pass@hostname:port` * proxy: A string defining a proxy server: `hostname:port` """ self.url = URL.objectify(url) # Prepare proxy info if proxy is not None: # TODO: this will break if using basic auth and embedding # username:password in the proxy URL self.proxy = proxy.split(":") if len(self.proxy) == 1: self.proxy.append(8080) else: self.proxy[1] = int(self.proxy[1]) # Build global headers self.headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "text/xml", "Accept": "text/xml"} if headers: self.headers.update(headers) if self.url.username is not None: username = urllib.unquote(self.url.username) password = urllib.unquote(self.url.password) if username is not None: hash = (("%s:%s" % (username, password)) .encode('base64')[:-1]) self.headers['authorization'] = "Basic %s" % hash # Connection proxy if self.proxy is not None: self.handle = httplib.HTTPConnection(*self.proxy) # direct, https # TODO: we shouldn't use SSL on http://weird.server.example.com:443/ elif self.url.port == 443 or self.url.scheme == 'https': self.handle = httplib.HTTPSConnection(self.url.hostname, self.url.port) # direct, http else: self.handle = httplib.HTTPConnection(self.url.hostname, self.url.port) self.url = self.url.unauth()
def object_by_uid(self, uid, comp_filter=None): """ Get one event from the calendar. Parameters: * uid: the event uid Returns: * Event() or None """ data = cdav.CalendarData() prop = dav.Prop() + data query = cdav.TextMatch(uid) query = cdav.PropFilter("UID") + query if comp_filter: query = comp_filter + query vcalendar = cdav.CompFilter("VCALENDAR") + query filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] response = self._query(root, 1, 'report') if response.status == 404: raise error.NotFoundError("%s not found on server" % uid) elif response.status == 400: raise error.ReportError(errmsg(response)) items_found = response.tree.findall(".//" + dav.Response.tag) for r in items_found: href = unquote(r.find(".//" + dav.Href.tag).text) data = unquote(r.find(".//" + cdav.CalendarData.tag).text) # Ref Lucas Verney, we've actually done a substring search, if the # uid given in the query is short (i.e. just "0") we're likely to # get false positives back from the server. # # Long uids are folded, so splice the lines together here before # attempting a match. item_uid = re.search(r'\nUID:((.|\n[ \t])*)\n', data) if (not item_uid or re.sub(r'\n[ \t]', '', item_uid.group(1)) != uid): continue return self._calendar_comp_class_by_data(data)( self.client, url=URL.objectify(href), data=data, parent=self) raise error.NotFoundError("%s not found on server" % uid)
def _create(self, data, id=None, path=None): if id is None and path is not None and str(path).endswith('.ics'): id = re.search('(/|^)([^/]*).ics',str(path)).group(2) elif id is None: id = self.instance.vevent.uid.value if path is None: path = id + ".ics" path = self.parent.url.join(path) r = self.client.put(path, data, {"Content-Type": 'text/calendar; charset="utf-8"'}) if r.status == 302: path = [x[1] for x in r.headers if x[0]=='location'][0] elif not (r.status in (204, 201)): raise error.PutError(r.raw) self.url = URL.objectify(path) self.id = id
def object_by_uid(self, uid, comp_filter=None): """ Get one event from the calendar. Parameters: * uid: the event uid Returns: * Event() or None """ data = cdav.CalendarData() prop = dav.Prop() + data query = cdav.TextMatch(uid) query = cdav.PropFilter("UID") + query if comp_filter: query = comp_filter + query vcalendar = cdav.CompFilter("VCALENDAR") + query filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] response = self._query(root, 1, 'report') if response.status == 404: raise error.NotFoundError(errmsg(response)) elif response.status == 400: raise error.ReportError(errmsg(response)) items_found = response.tree.findall(".//" + dav.Response.tag) for r in items_found: href = unquote(r.find(".//" + dav.Href.tag).text) data = unquote(r.find(".//" + cdav.CalendarData.tag).text) # Ref Lucas Verney, we've actually done a substring search, if the # uid given in the query is short (i.e. just "0") we're likely to # get false positives back from the server. if not "\nUID:%s\n" % uid in data: # TODO: optimistic assumption, uid line is not folded. We # need to unfold the content to be 100% sure that we won't # filter away true positives here. continue return self._calendar_comp_class_by_data(data)( self.client, url=URL.objectify(href), data=data, parent=self) raise error.NotFoundError(errmsg(response))
def _create(self, data, id=None, path=None): if id is None and path is not None and str(path).endswith('.ics'): id = re.search('(/|^)([^/]*).ics', str(path)).group(2) elif id is None: for obj_type in ('vevent', 'vtodo', 'vjournal', 'vfreebusy'): obj = None if hasattr(self.vobject_instance, obj_type): obj = getattr(self.vobject_instance, obj_type) elif self.vobject_instance.name.lower() == obj_type: obj = self.vobject_instance if obj is not None: try: id = obj.uid.value except AttributeError: id = str(uuid.uuid1()) obj.add('uid') obj.uid.value = id break else: for obj_type in ('vevent', 'vtodo', 'vjournal', 'vfreebusy'): obj = None if hasattr(self.vobject_instance, obj_type): obj = getattr(self.vobject_instance, obj_type) elif self.vobject_instance.name.lower() == obj_type: obj = self.vobject_instance if obj is not None: if not hasattr(obj, 'uid'): obj.add('uid') obj.uid.value = id break if path is None: path = quote(id) + ".ics" path = self.parent.url.join(path) r = self.client.put(path, data, {"Content-Type": 'text/calendar; charset="utf-8"'}) if r.status == 302: path = [x[1] for x in r.headers if x[0] == 'location'][0] elif not (r.status in (204, 201)): raise error.PutError(errmsg(r)) self.url = URL.objectify(path) self.id = id
def __init__(self, client=None, url=None, parent=None, name=None, id=None, etag=None): """ Default constructor. Parameters: * client: A DAVClient instance * url: The url for this object * parent: The parent object - used when creating objects * name: A displayname * id: The resource id (UID for an Event) """ if client is None and parent is not None: client = parent.client self.client = client self.parent = parent self.name = name self.id = id self.url = URL.objectify(url)
def __init__(self, url, proxy=None, username=None, password=None, auth=None, ssl_verify_cert=True): """ Sets up a HTTPConnection object towards the server in the url. Parameters: * url: A fully qualified url: `scheme://user:pass@hostname:port` * proxy: A string defining a proxy server: `hostname:port` * username and password should be passed as arguments or in the URL * auth and ssl_verify_cert is passed to requests.request. ** ssl_verify_cert can be the path of a CA-bundle or False. """ log.debug("url: " + str(url)) self.url = URL.objectify(url) # Prepare proxy info if proxy is not None: self.proxy = proxy if re.match('^.*://', proxy) is None: # requests library expects the proxy url to have a scheme self.proxy = self.url.scheme + '://' + proxy # add a port is one is not specified # TODO: this will break if using basic auth and embedding # username:password in the proxy URL p = self.proxy.split(":") if len(p) == 2: self.proxy += ':8080' log.debug("init - proxy: %s" % (self.proxy)) # Build global headers self.headers = {"User-Agent": "Mac+OS+X/10.11.2 (15C50) accountsd/113", "Content-Type": "text/xml", "Accept": "text/xml"} if self.url.username is not None: username = unquote(self.url.username) password = unquote(self.url.password) self.username = username self.password = password self.auth = auth ## TODO: it's possible to force through a specific auth method here, but no test code for this. self.ssl_verify_cert = ssl_verify_cert self.url = self.url.unauth() log.debug("self.url: " + str(url))
def event_by_uid(self, uid): """ Get one event from the calendar. Parameters: * uid: the event uid Returns: * Event() or None """ e = None data = cdav.CalendarData() etag = dav.ETag() prop = dav.Prop() + etag + data match = cdav.TextMatch(uid) propf = cdav.PropFilter("UID") + match vevent = cdav.CompFilter("VEVENT") + propf vcalendar = cdav.CompFilter("VCALENDAR") + vevent filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] q = etree.tostring(root.xmlelement(), encoding="utf-8", xml_declaration=True) response = self.client.report(self.url.path, q, 1) if response.status == 404: raise error.NotFoundError(response.raw) elif response.status == 400: raise error.ReportError(response.raw) r = response.tree.find(".//" + dav.Response.tag) if r is not None: href = URL.objectify(r.find(".//" + dav.Href.tag).text) data = r.find(".//" + cdav.CalendarData.tag).text etag = r.find(".//" + dav.ETag.tag).text e = Event(self.client, url=href, data=data, etag=etag, parent=self) else: raise error.NotFoundError(response.raw) return e
def event_by_uid(self, uid): """ Get one event from the calendar. Parameters: * uid: the event uid Returns: * Event() or None """ e = None data = cdav.CalendarData() prop = dav.Prop() + data match = cdav.TextMatch(uid) propf = cdav.PropFilter("UID") + match vevent = cdav.CompFilter("VEVENT") + propf vcalendar = cdav.CompFilter("VCALENDAR") + vevent filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] q = etree.tostring(root.xmlelement(), encoding="utf-8", xml_declaration=True) response = self.client.report(self.url, q, 1) if response.status == 404: raise error.NotFoundError(response.raw) elif response.status == 400: raise error.ReportError(response.raw) r = response.tree.find(".//" + dav.Response.tag) if r is not None: href = URL.objectify(r.find(".//" + dav.Href.tag).text) data = r.find(".//" + cdav.CalendarData.tag).text e = Event(self.client, url=href, data=data, parent=self) else: raise error.NotFoundError(response.raw) return e
def date_search(self, start, end=None): """ Search events by date in the calendar. Recurring events are expanded if they have an occurence during the specified time frame. Parameters: * start = datetime.today(). * end = same as above. Returns: * [Event(), ...] """ matches = [] # build the request expand = cdav.Expand(start, end) data = cdav.CalendarData() + expand prop = dav.Prop() + data range = cdav.TimeRange(start, end) vevent = cdav.CompFilter("VEVENT") + range vcalendar = cdav.CompFilter("VCALENDAR") + vevent filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] q = etree.tostring(root.xmlelement(), encoding="utf-8", xml_declaration=True) response = self.client.report(self.url, q, 1) for r in response.tree.findall(".//" + dav.Response.tag): status = r.find(".//" + dav.Status.tag) if status.text.endswith("200 OK"): href = URL.objectify(r.find(dav.Href.tag).text) href = self.url.join(href) data = r.find(".//" + cdav.CalendarData.tag).text e = Event(self.client, url=href, data=data, parent=self) matches.append(e) else: raise error.ReportError(response.raw) return matches
def request(self, url, method="GET", body="", headers={}): """ Actually sends the request """ url = URL.objectify(url) if self.proxy is not None: url = "%s://%s:%s%s" % (self.url.scheme, self.url.hostname, self.url.port, url.path) combined_headers = self.headers combined_headers.update(headers) if body is None or body == "" and "Content-Type" in combined_headers: del combined_headers["Content-Type"] if isinstance(body, unicode): body = body.encode('utf-8') try: logging.debug("########## SENDING REQUEST ###########") logging.debug("\nMETHOD=%s, \nURL=%s, \nHEADERS=%s\nBODY:\n%s" % (method, url, combined_headers, body)) self.handle.request(method, url, body, combined_headers) response = DAVResponse(self.handle.getresponse()) except httplib.BadStatusLine: # Try to reconnect self.handle.close() self.handle.connect() ## TODO: we're missing test code on this. (will need to ## mock up a server to test this) self.handle.request(method, url, body, combined_headers) response = DAVResponse(self.handle.getresponse(n)) # this is an error condition the application wants to know if response.status == httplib.FORBIDDEN or \ response.status == httplib.UNAUTHORIZED: ex = error.AuthorizationError() ex.url = url ex.reason = response.reason raise ex return response
def date_search(self, start, end=None): """ Search events by date in the calendar. Recurring events are expanded if they have an occurence during the specified time frame. Parameters: * start = datetime.today(). * end = same as above. Returns: * [Event(), ...] """ matches = [] # build the request expand = cdav.Expand(start, end) data = cdav.CalendarData() + expand prop = dav.Prop() + data range = cdav.TimeRange(start, end) vevent = cdav.CompFilter("VEVENT") + range vcalendar = cdav.CompFilter("VCALENDAR") + vevent filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] q = etree.tostring(root.xmlelement(), encoding="utf-8", xml_declaration=True) response = self.client.report(self.url.path, q, 1) for r in response.tree.findall(".//" + dav.Response.tag): status = r.find(".//" + dav.Status.tag) if status.text.endswith("200 OK"): href = URL.objectify(r.find(dav.Href.tag).text) href = self.url.join(href) data = r.find(".//" + cdav.CalendarData.tag).text e = Event(self.client, url=href, data=data, parent=self) matches.append(e) else: raise error.ReportError(response.raw) return matches
def _create(self, data, id=None, path=None): if id is None and path is not None and str(path).endswith('.ics'): id = re.search('(/|^)([^/]*).ics', str(path)).group(2) elif id is None: for obj_type in ('vevent', 'vtodo', 'vjournal', 'vfreebusy'): obj = None for subcomp in self.instance.subcomponents: if subcomp.name.lower() == obj_type: obj = subcomp break if obj is None and self.instance.name.lower == obj_type: obj = self.instance if obj is not None: id = obj["uid"] break else: for obj_type in ('vevent', 'vtodo', 'vjournal', 'vfreebusy'): obj = None for subcomp in self.instance.subcomponents: if subcomp.name.lower() == obj_type: obj = subcomp break if obj is None and self.instance.name.lower == obj_type: obj = self.instance if obj is not None: obj.add("uid", id) break if path is None: path = id + ".ics" path = self.parent.url.join(path) r = self.client.put(path, data, {"Content-Type": 'text/calendar; charset="utf-8"'}) if r.status == 302: path = [x[1] for x in r.headers if x[0] == 'location'][0] elif not (r.status in (204, 201)): raise error.PutError(errmsg(r)) self.url = URL.objectify(path) self.id = id
def object_by_uid(self, uid, comp_filter=None): """ Get one event from the calendar. Parameters: * uid: the event uid Returns: * Event() or None """ data = cdav.CalendarData() prop = dav.Prop() + data query = cdav.TextMatch(uid) query = cdav.PropFilter("UID") + query if comp_filter: query = comp_filter + query else: raise Exception("Need a comp_filter") vcalendar = cdav.CompFilter("VCALENDAR") + query filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] response = self._query(root, 1, 'report') if response.status == 404: raise error.NotFoundError(response.raw) elif response.status == 400: raise error.ReportError(response.raw) r = response.tree.find(".//" + dav.Response.tag) if r is not None: href = r.find(".//" + dav.Href.tag).text data = r.find(".//" + cdav.CalendarData.tag).text return self._calendar_comp_class_by_data(data)( self.client, url=URL.objectify(href), data=data, parent=self) else: raise error.NotFoundError(response.raw)
def children(self, type=None): """ List children, using a propfind (resourcetype) on the parent object, at depth = 1. """ c = [] depth = 1 properties = {} props = [dav.ResourceType(), ] if type: props.append(dav.DisplayName()) response = self._query_properties(props, depth) for r in response.tree.findall(dav.Response.tag): # We use canonicalized urls to index children try: href = str(self.url.join(URL.objectify(r.find(dav.Href.tag).text)).canonical()) assert(href) properties[href] = {} for p in props: t = r.find(".//" + p.tag) if len(list(t)) > 0: if type is not None: val = t.find(".//" + type) else: val = t.find(".//*") if val is not None: val = val.tag else: val = None else: val = t.text properties[href][p.tag] = val except Exception , e: logging.info("error %s" % e)
def object_by_uid(self, uid, comp_filter=None): """ Get one event from the calendar. Parameters: * uid: the event uid Returns: * Event() or None """ data = cdav.CalendarData() prop = dav.Prop() + data query = cdav.TextMatch(uid) query = cdav.PropFilter("UID") + query if comp_filter: query = comp_filter + query vcalendar = cdav.CompFilter("VCALENDAR") + query filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] response = self._query(root, 1, 'report') if response.status == 404: raise error.NotFoundError(response.raw) elif response.status == 400: raise error.ReportError(response.raw) r = response.tree.find(".//" + dav.Response.tag) if r is not None: href = r.find(".//" + dav.Href.tag).text data = r.find(".//" + cdav.CalendarData.tag).text return self._calendar_comp_class_by_data(data)(self.client, url=URL.objectify(href), data=data, parent=self) else: raise error.NotFoundError(response.raw)
def request(self, url, method="GET", body="", headers={}): """ Actually sends the request """ # objectify the url url = URL.objectify(url) proxies = None if self.proxy is not None: proxies = {url.scheme: self.proxy} log.debug("using proxy - %s" % (proxies)) # ensure that url is a unicode string url = str(url) combined_headers = self.headers combined_headers.update(headers) if body is None or body == "" and "Content-Type" in combined_headers: del combined_headers["Content-Type"] log.debug("sending request - method={0}, url={1}, headers={2}\nbody:\n{3}".format(method, url, combined_headers, body)) auth = None if self.auth is None and self.username is not None: auth = requests.auth.HTTPDigestAuth(self.username, self.password) else: auth = self.auth r = requests.request(method, url, data=to_wire(body), headers=combined_headers, proxies=proxies, auth=auth, verify=self.ssl_verify_cert) response = DAVResponse(r) ## If server supports BasicAuth and not DigestAuth, let's try again: if response.status == 401 and self.auth is None and auth is not None: # if there were redirects, we need to continue with them if r.history: url = r.url auth = requests.auth.HTTPBasicAuth(self.username, self.password) r = requests.request(method, url, data=to_wire(body), headers=combined_headers, proxies=proxies, auth=auth, verify=self.ssl_verify_cert) response = DAVResponse(r) if r.history: # requests do not redirect with body r = requests.request(method, r.url, data=to_wire(body), headers=combined_headers, proxies=proxies, auth=auth, verify=self.ssl_verify_cert) response = DAVResponse(r) self._last_response = r # this is an error condition the application wants to know if response.status == requests.codes.forbidden or \ response.status == requests.codes.unauthorized: ex = error.AuthorizationError() ex.url = url ex.reason = response.reason raise ex ## let's save the auth object and remove the user/pass information if not self.auth and auth: self.auth = auth del self.username del self.password return response
def calendar_home_set(self, url): if isinstance(url, CalendarSet): self._calendar_home_set = url else: self._calendar_home_set = CalendarSet(self.client, self.client.url.join(URL.objectify(url)))
def testURL(self): """Excersising the URL class""" ## 1) URL.objectify should return a valid URL object almost no matter what's thrown in url0 = URL.objectify(None) url0b = URL.objectify("") url1 = URL.objectify( "http://*****:*****@www.example.com:8080/caldav.php/?foo=bar") url2 = URL.objectify(url1) url3 = URL.objectify("/bar") url4 = URL.objectify(urlparse(str(url1))) url5 = URL.objectify(urlparse("/bar")) ## 2) __eq__ works well assert_equal(url1, url2) assert_equal(url1, url4) assert_equal(url3, url5) ## 3) str will always return the URL assert_equal( str(url1), "http://*****:*****@www.example.com:8080/caldav.php/?foo=bar") assert_equal(str(url3), "/bar") assert_equal( str(url4), "http://*****:*****@www.example.com:8080/caldav.php/?foo=bar") assert_equal(str(url5), "/bar") ## 4) join method url6 = url1.join(url2) url7 = url1.join(url3) url8 = url1.join(url4) url9 = url1.join(url5) urlA = url1.join("someuser/calendar") urlB = url5.join(url1) assert_equal(url6, url1) assert_equal(url7, "http://*****:*****@www.example.com:8080/bar") assert_equal(url8, url1) assert_equal(url9, url7) assert_equal( urlA, "http://*****:*****@www.example.com:8080/caldav.php/someuser/calendar") assert_equal(urlB, url1) assert_raises(ValueError, url1.join, "http://www.google.com") ## 4b) join method, with URL as input parameter url6 = url1.join(URL.objectify(url2)) url7 = url1.join(URL.objectify(url3)) url8 = url1.join(URL.objectify(url4)) url9 = url1.join(URL.objectify(url5)) urlA = url1.join(URL.objectify("someuser/calendar")) urlB = url5.join(URL.objectify(url1)) url6b = url6.join(url0) url6c = url6.join(url0b) url6d = url6.join(None) for url6alt in (url6b, url6c, url6d): assert_equal(url6, url6alt) assert_equal(url6, url1) assert_equal(url7, "http://*****:*****@www.example.com:8080/bar") assert_equal(url8, url1) assert_equal(url9, url7) assert_equal( urlA, "http://*****:*****@www.example.com:8080/caldav.php/someuser/calendar") assert_equal(urlB, url1) assert_raises(ValueError, url1.join, "http://www.google.com") ## 5) all urlparse methods will work. always. assert_equal(url1.scheme, 'http') assert_equal(url2.path, '/caldav.php/') assert_equal(url7.username, 'foo') assert_equal(url5.path, '/bar') urlC = URL.objectify("https://www.example.com:443/foo") assert_equal(urlC.port, 443) ## 6) is_auth returns True if the URL contains a username. assert_equal(urlC.is_auth(), False) assert_equal(url7.is_auth(), True) ## 7) unauth() strips username/password assert_equal(url7.unauth(), 'http://www.example.com:8080/bar')
def testURL(self): """Excersising the URL class""" ## 1) URL.objectify should return a valid URL object almost no matter what's thrown in url0 = URL.objectify(None) url0b= URL.objectify("") url1 = URL.objectify("http://*****:*****@www.example.com:8080/caldav.php/?foo=bar") url2 = URL.objectify(url1) url3 = URL.objectify("/bar") url4 = URL.objectify(urlparse.urlparse(str(url1))) url5 = URL.objectify(urlparse.urlparse("/bar")) ## 2) __eq__ works well assert_equal(url1, url2) assert_equal(url1, url4) assert_equal(url3, url5) ## 3) str will always return the URL assert_equal(str(url1), "http://*****:*****@www.example.com:8080/caldav.php/?foo=bar") assert_equal(str(url3), "/bar") assert_equal(str(url4), "http://*****:*****@www.example.com:8080/caldav.php/?foo=bar") assert_equal(str(url5), "/bar") ## 4) join method url6 = url1.join(url2) url7 = url1.join(url3) url8 = url1.join(url4) url9 = url1.join(url5) urlA = url1.join("someuser/calendar") urlB = url5.join(url1) assert_equal(url6, url1) assert_equal(url7, "http://*****:*****@www.example.com:8080/bar") assert_equal(url8, url1) assert_equal(url9, url7) assert_equal(urlA, "http://*****:*****@www.example.com:8080/caldav.php/someuser/calendar") assert_equal(urlB, url1) assert_raises(ValueError, url1.join, "http://www.google.com") ## 4b) join method, with URL as input parameter url6 = url1.join(URL.objectify(url2)) url7 = url1.join(URL.objectify(url3)) url8 = url1.join(URL.objectify(url4)) url9 = url1.join(URL.objectify(url5)) urlA = url1.join(URL.objectify("someuser/calendar")) urlB = url5.join(URL.objectify(url1)) url6b= url6.join(url0) url6c= url6.join(url0b) url6d= url6.join(None) for url6alt in (url6b, url6c, url6d): assert_equal(url6, url6alt) assert_equal(url6, url1) assert_equal(url7, "http://*****:*****@www.example.com:8080/bar") assert_equal(url8, url1) assert_equal(url9, url7) assert_equal(urlA, "http://*****:*****@www.example.com:8080/caldav.php/someuser/calendar") assert_equal(urlB, url1) assert_raises(ValueError, url1.join, "http://www.google.com") ## 5) all urlparse methods will work. always. assert_equal(url1.scheme, 'http') assert_equal(url2.path, '/caldav.php/') assert_equal(url7.username, 'foo') assert_equal(url5.path, '/bar') urlC = URL.objectify("https://www.example.com:443/foo") assert_equal(urlC.port, 443) ## 6) is_auth returns True if the URL contains a username. assert_equal(urlC.is_auth(), False) assert_equal(url7.is_auth(), True) ## 7) unauth() strips username/password assert_equal(url7.unauth(), 'http://www.example.com:8080/bar')