| Home | Trees | Indices | Help |
|---|
|
|
1 ## 2 # Copyright (c) 2006-2016 Apple Inc. All rights reserved. 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 ## 16 17 from caldavclientlibrary.client.httpshandler import SmartHTTPConnection 18 from caldavclientlibrary.protocol.caldav.definitions import headers 19 from caldavclientlibrary.protocol.caldav.makecalendar import MakeCalendar 20 from caldavclientlibrary.protocol.carddav.makeaddressbook import MakeAddressBook 21 from caldavclientlibrary.protocol.http.authentication.basic import Basic 22 from caldavclientlibrary.protocol.http.authentication.digest import Digest 23 try: 24 from caldavclientlibrary.protocol.http.authentication.gssapi import Kerberos 25 except ImportError: 26 Kerberos = None 27 from caldavclientlibrary.protocol.http.data.string import ResponseDataString, RequestDataString 28 from caldavclientlibrary.protocol.url import URL 29 from caldavclientlibrary.protocol.webdav.acl import ACL 30 from caldavclientlibrary.protocol.webdav.definitions import davxml 31 from caldavclientlibrary.protocol.webdav.definitions import statuscodes 32 from caldavclientlibrary.protocol.webdav.delete import Delete 33 from caldavclientlibrary.protocol.webdav.get import Get 34 from caldavclientlibrary.protocol.webdav.makecollection import MakeCollection 35 from caldavclientlibrary.protocol.webdav.move import Move 36 from caldavclientlibrary.protocol.webdav.post import Post 37 from caldavclientlibrary.protocol.webdav.principalmatch import PrincipalMatch 38 from caldavclientlibrary.protocol.webdav.propall import PropAll 39 from caldavclientlibrary.protocol.webdav.propfind import PropFind 40 from caldavclientlibrary.protocol.webdav.propfindparser import PropFindParser 41 from caldavclientlibrary.protocol.webdav.propnames import PropNames 42 from caldavclientlibrary.protocol.webdav.proppatch import PropPatch 43 from caldavclientlibrary.protocol.webdav.put import Put 44 from caldavclientlibrary.protocol.webdav.session import Session 45 from xml.etree.ElementTree import Element, tostring 46 import types 4749 5479255 - def __init__(self, server, port=None, ssl=False, user="", pswd="", principal=None, root=None, logging=False):56 super(CalDAVSession, self).__init__(server, port, ssl, log=CalDAVSession.logger()) 57 58 self.loghttp = logging 59 60 self.user = user 61 self.pswd = pswd 62 63 # Initialize state 64 self.connect = None 65 66 # Paths 67 self.rootPath = URL(url=root) 68 self.principalPath = URL(url=principal) 69 70 self._initCalDAVState()7173 74 # We need to cache the server capabilities and properties 75 if not self.principalPath: 76 self._discoverPrincipal()7779 80 hrefs = self.getHrefListProperty(self.rootPath, davxml.principal_collection_set) 81 if not hrefs: 82 return 83 84 # For each principal collection find one that matches self 85 for href in hrefs: 86 87 results = self.getSelfHrefs(href) 88 if results: 89 self.principalPath = results[0] 90 if self.log: 91 self.log.write("Found principal path: %s" % (self.principalPath.absoluteURL(),)) 92 return93 99101 102 assert(isinstance(rurl, URL)) 103 104 request = PropFind(self, rurl.relativeURL(), headers.Depth0, (davxml.resourcetype,)) 105 106 # Process it 107 self.runSession(request) 108 109 return request.getStatusCode() == statuscodes.MultiStatus110112 113 assert(isinstance(rurl, URL)) 114 115 results = () 116 117 # Create WebDAV propfind 118 request = PropNames(self, rurl.relativeURL(), headers.Depth0) 119 result = ResponseDataString() 120 request.setOutput(result) 121 122 # Process it 123 self.runSession(request) 124 125 # If its a 207 we want to parse the XML 126 if request.getStatusCode() == statuscodes.MultiStatus: 127 128 parser = PropFindParser() 129 parser.parseData(result.getData()) 130 131 # Look at each propfind result 132 for item in parser.getResults().itervalues(): 133 134 # Get child element name (decode URL) 135 name = URL(url=item.getResource(), decode=True) 136 137 # Must match rurl 138 if name.equalRelative(rurl): 139 140 results = tuple([name for name in item.getNodeProperties().iterkeys()]) 141 142 else: 143 self.handleHTTPError(request) 144 145 return results146148 149 assert(isinstance(rurl, URL)) 150 151 results = {} 152 bad = None 153 154 # Create WebDAV propfind 155 if props: 156 request = PropFind(self, rurl.relativeURL(), headers.Depth0, props) 157 else: 158 request = PropAll(self, rurl.relativeURL(), headers.Depth0) 159 result = ResponseDataString() 160 request.setOutput(result) 161 162 # Process it 163 self.runSession(request) 164 165 # If its a 207 we want to parse the XML 166 if request.getStatusCode() == statuscodes.MultiStatus: 167 168 parser = PropFindParser() 169 parser.parseData(result.getData()) 170 171 # Look at each propfind result 172 for item in parser.getResults().itervalues(): 173 174 # Get child element name (decode URL) 175 name = URL(url=item.getResource(), decode=True) 176 177 # Must match rurl 178 if name.equalRelative(rurl): 179 for name, value in item.getTextProperties().iteritems(): 180 results[name] = value 181 for name, value in item.getHrefProperties().iteritems(): 182 if name not in results: 183 results[name] = value 184 for name, value in item.getNodeProperties().iteritems(): 185 if name not in results: 186 results[name] = tostring(value) if xmldata else value 187 bad = item.getBadProperties() 188 else: 189 self.handleHTTPError(request) 190 191 return results, bad192194 195 assert(isinstance(rurl, URL)) 196 197 results = {} 198 199 # Create WebDAV propfind 200 request = PropFind(self, rurl.relativeURL(), headers.Depth1, props) 201 result = ResponseDataString() 202 request.setOutput(result) 203 204 # Process it 205 self.runSession(request) 206 207 # If its a 207 we want to parse the XML 208 if request.getStatusCode() == statuscodes.MultiStatus: 209 210 parser = PropFindParser() 211 parser.parseData(result.getData()) 212 213 # Look at each propfind result 214 for item in parser.getResults().itervalues(): 215 216 # Get child element name (decode URL) 217 name = URL(url=item.getResource(), decode=True) 218 propresults = {} 219 results[name.relativeURL()] = propresults 220 221 for prop in props: 222 223 if item.getTextProperties().has_key(str(prop)): 224 propresults[prop] = item.getTextProperties().get(str(prop)) 225 226 elif item.getNodeProperties().has_key(str(prop)): 227 propresults[prop] = item.getNodeProperties()[str(prop)] 228 else: 229 self.handleHTTPError(request) 230 231 return results232234 235 assert(isinstance(rurl, URL)) 236 237 results = () 238 239 # Create WebDAV propfind 240 request = PropFind(self, rurl.relativeURL(), headers.Depth0, (propname,)) 241 result = ResponseDataString() 242 request.setOutput(result) 243 244 # Process it 245 self.runSession(request) 246 247 # If its a 207 we want to parse the XML 248 if request.getStatusCode() == statuscodes.MultiStatus: 249 250 parser = PropFindParser() 251 parser.parseData(result.getData()) 252 253 # Look at each propfind result and extract any Hrefs 254 for item in parser.getResults().itervalues(): 255 256 # Get child element name (decode URL) 257 name = URL(url=item.getResource(), decode=True) 258 259 # Must match rurl 260 if name.equalRelative(rurl): 261 262 if item.getNodeProperties().has_key(str(propname)): 263 264 propnode = item.getNodeProperties()[str(propname)] 265 results += tuple([URL(url=href.text, decode=True) for href in propnode.findall(str(davxml.href)) if href.text]) 266 else: 267 self.handleHTTPError(request) 268 269 return results270 271 # Do principal-match report with self on the passed in url273 274 assert(isinstance(rurl, URL)) 275 276 results = {} 277 278 # Create WebDAV principal-match 279 request = PrincipalMatch(self, rurl.relativeURL(), props) 280 result = ResponseDataString() 281 request.setOutput(result) 282 283 # Process it 284 self.runSession(request) 285 286 # If its a 207 we want to parse the XML 287 if request.getStatusCode() == statuscodes.MultiStatus: 288 289 parser = PropFindParser() 290 parser.parseData(result.getData()) 291 292 # Look at each principal-match result and return first one that is appropriate 293 for item in parser.getResults().itervalues(): 294 295 for prop in props: 296 297 if item.getNodeProperties().has_key(str(prop)): 298 299 href = item.getNodeProperties()[str(prop)].find(str(davxml.href)) 300 301 if href is not None: 302 results[prop] = URL(url=href.text, decode=True) 303 304 # We'll take the first one, whatever that is 305 break 306 307 else: 308 self.handleHTTPError(request) 309 return None 310 311 return results312 313 # Do principal-match report with self on the passed in url315 316 assert(isinstance(rurl, URL)) 317 318 results = () 319 320 # Create WebDAV principal-match 321 request = PrincipalMatch(self, rurl.realtiveURL(), (davxml.principal_URL,)) 322 result = ResponseDataString() 323 request.setOutput(result) 324 325 # Process it 326 self.runSession(request) 327 328 # If its a 207 we want to parse the XML 329 if request.getStatusCode() == statuscodes.MultiStatus: 330 331 parser = PropFindParser() 332 parser.parseData(result.getData()) 333 334 # Look at each propfind result and extract any Hrefs 335 for item in parser.getResults().itervalues(): 336 337 # Get child element name (decode URL) 338 name = URL(url=item.getResource(), decode=True) 339 results += (name.path,) 340 341 else: 342 self.handleHTTPError(request) 343 return None 344 345 return results346 347 # Do principal-match report with self on the passed in url349 350 assert(isinstance(rurl, URL)) 351 352 hrefs = self.getHrefListProperty(rurl, davxml.principal_collection_set) 353 if not hrefs: 354 return None 355 356 # For each principal collection find one that matches self 357 for href in hrefs: 358 359 results = self.getSelfHrefs(href) 360 if results: 361 return results[0] 362 363 return None364366 367 assert(isinstance(rurl, URL)) 368 369 results = () 370 371 # Convert property data into something sensible 372 converted = [] 373 for name, value in props: 374 node = None 375 if isinstance(value, types.StringType): 376 node = Element(name) 377 node.text = value 378 elif isinstance(value, URL): 379 node = Element(davxml.href) 380 node.text = value.absoluteURL() 381 elif isinstance(value, types.ListType) or isinstance(value, types.TupleType): 382 hrefs = [] 383 for item in value: 384 if isinstance(item, URL): 385 href = Element(davxml.href) 386 href.text = item.relativeURL() 387 hrefs.append(href) 388 else: 389 break 390 else: 391 node = Element(name) 392 map(node.append, hrefs) 393 394 if node is not None: 395 converted.append(node) 396 397 # Create WebDAV propfind 398 request = PropPatch(self, rurl.relativeURL(), converted) 399 result = ResponseDataString() 400 request.setOutput(result) 401 402 # Process it 403 self.runSession(request) 404 405 # If its a 207 we want to parse the XML 406 if request.getStatusCode() == statuscodes.MultiStatus: 407 408 parser = PropFindParser() 409 parser.parseData(result.getData()) 410 411 # Look at each propfind result 412 for item in parser.getResults().itervalues(): 413 414 # Get child element name (decode URL) 415 name = URL(url=item.getResource(), decode=True) 416 417 # Must match rurl 418 if name.equalRelative(rurl): 419 420 for prop in item.getNodeProperties(): 421 results += (prop,) 422 423 else: 424 self.handleHTTPError(request) 425 426 return results427429 430 assert(isinstance(rurl, URL)) 431 432 # Create WebDAV ACL 433 request = ACL(self, rurl.relativeURL(), aces) 434 435 # Process it 436 self.runSession(request) 437 438 if request.getStatusCode() not in (statuscodes.OK, statuscodes.Created, statuscodes.NoContent): 439 self.handleHTTPError(request)440442 443 assert(isinstance(rurl, URL)) 444 445 # Create WebDAV MKCOL 446 request = MakeCollection(self, rurl.relativeURL()) 447 448 # Process it 449 self.runSession(request) 450 451 if request.getStatusCode() not in (statuscodes.OK, statuscodes.Created, statuscodes.NoContent): 452 self.handleHTTPError(request)453455 456 assert(isinstance(rurl, URL)) 457 458 # Create WebDAV MKCALENDAR 459 request = MakeCalendar(self, rurl.relativeURL(), displayname, description) 460 461 # Process it 462 self.runSession(request) 463 464 if request.getStatusCode() not in (statuscodes.OK, statuscodes.Created, statuscodes.NoContent): 465 self.handleHTTPError(request)466468 469 assert(isinstance(rurl, URL)) 470 471 # Create WebDAV extended MKCOL 472 request = MakeAddressBook(self, rurl.relativeURL(), displayname, description) 473 474 # Process it 475 self.runSession(request) 476 477 if request.getStatusCode() not in (statuscodes.OK, statuscodes.Created, statuscodes.NoContent): 478 self.handleHTTPError(request)479481 482 assert(isinstance(rurl, URL)) 483 484 # Create WebDAV DELETE 485 request = Delete(self, rurl.relativeURL()) 486 487 # Process it 488 self.runSession(request) 489 490 if request.getStatusCode() not in (statuscodes.OK, statuscodes.NoContent): 491 self.handleHTTPError(request)492494 495 assert(isinstance(rurlFrom, URL)) 496 assert(isinstance(rurlTo, URL)) 497 498 # Create WebDAV MOVE 499 request = Move(self, rurlFrom.relativeURL(), rurlTo.absoluteURL()) 500 501 # Process it 502 self.runSession(request) 503 504 if request.getStatusCode() not in (statuscodes.OK, statuscodes.Created, statuscodes.NoContent): 505 self.handleHTTPError(request)506508 509 assert(isinstance(rurl, URL)) 510 511 # Create WebDAV GET 512 request = Get(self, rurl.relativeURL()) 513 dout = ResponseDataString() 514 request.setData(dout) 515 516 # Process it 517 self.runSession(request) 518 519 # Check response status 520 if request.getStatusCode() != statuscodes.OK: 521 self.handleHTTPError(request) 522 return None 523 524 # Look for ETag 525 if request.getNewETag() is not None: 526 527 etag = request.getNewETag() 528 529 # Handle server bug: ETag value MUST be quoted per HTTP/1.1 S3.11 530 if not etag.startswith('"'): 531 etag = "\"%s\"" % (etag,) 532 else: 533 etag = None 534 535 # Return data as a string and etag 536 return dout.getData(), etag537539 540 assert(isinstance(rurl, URL)) 541 542 # Create WebDAV PUT 543 request = Put(self, rurl.relativeURL()) 544 dout = RequestDataString(data, contentType) 545 request.setData(dout, None) 546 547 # Process it 548 self.runSession(request) 549 550 # Check response status 551 if request.getStatusCode() not in (statuscodes.OK, statuscodes.Created, statuscodes.NoContent,): 552 self.handleHTTPError(request)553555 556 assert(isinstance(rurl, URL)) 557 558 # Create WebDAV POST 559 request = Post(self, rurl.relativeURL()) 560 dout = RequestDataString(data, contentType) 561 request.setData(dout, None) 562 563 # Process it 564 self.runSession(request) 565 566 # Check response status 567 if request.getStatusCode() not in (statuscodes.OK, statuscodes.MultiStatus, statuscodes.NoContent,): 568 self.handleHTTPError(request)569 572574 # Create connection 575 self.connect = SmartHTTPConnection(self.server, self.port, self.ssl) 576 577 # Write to log file 578 if self.loghttp and self.log: 579 self.log.write("\n <-------- BEGIN HTTP CONNECTION -------->\n") 580 self.log.write("Server: %s\n" % (self.server,))581583 if self.connect: 584 self.connect.close() 585 self.connect = None 586 587 # Write to log file 588 if self.loghttp and self.log: 589 self.log.write("\n <-------- END HTTP CONNECTION -------->\n")590592 593 ctr = 5 594 while ctr: 595 ctr -= 1 596 597 self.doSession(request) 598 599 if request and request.isRedirect(): 600 location = request.getResponseHeader(headers.Location) 601 if location: 602 u = URL(location) 603 if not u.scheme or u.scheme in ("http", "https",): 604 # Get new server and base RURL 605 different_server = (self.server != u.server) if u.server else False 606 607 # Use new host in this session 608 if different_server: 609 self.setServer(u.server) 610 611 # Reset the request with new info 612 request.setURL(u.relativeURL()) 613 request.clearResponse() 614 615 # Write to log file 616 if self.loghttp and self.log: 617 self.log.write("\n <-------- HTTP REDIRECT -------->\n") 618 self.log.write("Location: %s\n" % (location,)) 619 620 # Recyle through loop 621 continue 622 623 # Exit when redirect does not occur 624 break625627 # Do initialisation if not already done 628 if not self.initialised: 629 630 if not self.initialise(self.server, self.rootPath.relativeURL()): 631 632 # Break connection with server 633 self.closeConnection() 634 return 635 636 # Do the request if present 637 if request: 638 639 # Handle delayed authorization 640 first_time = True 641 while True: 642 643 # Run the request actions - this will make any connection that is needed 644 self.sendRequest(request) 645 646 # Check for auth failure if none before 647 if request.getStatusCode() == statuscodes.Unauthorized: 648 649 # If we had authorization before, then chances are auth details are wrong - so delete and try again with new auth 650 if self.hasAuthorization(): 651 652 self.authorization = None 653 654 # Display error so user knows why the prompt occurs again - but not the first time 655 # as we might have a digest re-auth. 656 if not first_time: 657 self.displayHTTPError(request) 658 659 660 # Get authorization object (prompt the user) and redo the request 661 self.authorization, cancelled = self.getAuthorizor(first_time, request.getResponseHeaders(headers.WWWAuthenticate)) 662 663 # Check for auth cancellation 664 if cancelled: 665 self.authorization = None 666 667 else: 668 first_time = False 669 670 request.clearResponse() 671 672 # Repeat the request loop with new authorization 673 continue 674 675 # If we get here we are complete with auth loop 676 break 677 678 # Now close it - eventually we will do keep-alive support 679 680 # Break connection with server 681 self.closeConnection()682684 685 # Write request headers 686 self.connect.putrequest(request.method, request.url, skip_host=True, skip_accept_encoding=True) 687 hdrs = request.getRequestHeaders() 688 for header, value in hdrs: 689 self.connect.putheader(header, value) 690 self.connect.endheaders() 691 692 # Write to log file 693 if self.loghttp and self.log: 694 self.log.write("\n <-------- BEGIN HTTP REQUEST -------->\n") 695 self.log.write("%s\n" % (request.getRequestStartLine(),)) 696 for header, value in hdrs: 697 self.log.write("%s: %s\n" % (header, value)) 698 self.log.write("\n") 699 700 # Write the data 701 self.writeRequestData(request) 702 703 # Blank line in log between 704 if self.loghttp and self.log: 705 self.log.write("\n <-------- BEGIN HTTP RESPONSE -------->\n") 706 707 # Get response 708 response = self.connect.getresponse() 709 710 # Get response headers 711 request.setResponseStatus(response.version, response.status, response.reason) 712 request.setResponseHeaders(response.msg.headers) 713 if self.loghttp and self.log: 714 self.log.write("HTTP/%s %s %s\r\n" % ( 715 {11:"1.1",10:"1.0",9:"0.9"}.get(response.version, "?"), 716 response.status, 717 response.reason 718 )) 719 for hdr in response.msg.headers: 720 self.log.write(hdr) 721 self.log.write("\n") 722 723 # Now get the data 724 self.readResponseData(request, response) 725 726 # Trailer in log 727 if self.loghttp and self.log: 728 self.log.write("\n <-------- END HTTP RESPONSE -------->\n")729731 print "Ignoring error: %d" % (request.getStatusCode(),)732734 735 for item in wwwhdrs: 736 if item.lower().startswith("basic"): 737 return Basic(self.user, self.pswd), False 738 elif item.lower().startswith("digest"): 739 return Digest(self.user, self.pswd, wwwhdrs), False 740 elif item.lower().startswith("negotiate") and Kerberos is not None: 741 return Kerberos(self.user), False 742 else: 743 return None, True744746 747 # Write the data if any present 748 if request.hasRequestData(): 749 750 stream = request.getRequestData() 751 if stream: 752 # Tell data we are using it 753 stream.start() 754 755 # Buffered write from stream 756 more = True 757 while more: 758 data, more = stream.read() 759 if data: 760 self.connect.send(data) 761 762 if self.loghttp and self.log: 763 self.log.write(data) 764 765 # Tell data we are done using it 766 stream.stop()767769 770 # Check for data and write it 771 data = response.read() 772 773 if request.hasResponseData(): 774 stream = request.getResponseData() 775 stream.start() 776 stream.write(data) 777 stream.stop() 778 else: 779 response.read() 780 781 if self.loghttp and self.log: 782 self.log.write(data)783785 self.type = type786788 self.descriptor = txt789791 self.capability = txt
| Home | Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Thu Jul 7 15:01:48 2011 | http://epydoc.sourceforge.net |