<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><style type="text/css"><!--
#msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre, #msg p { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; }
#msg ul { overflow: auto; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<title>[2248] CalDAVClientLibrary/trunk</title>
</head>
<body>

<div id="msg">
<dl>
<dt>Revision</dt> <dd><a href="http://trac.macosforge.org/projects/calendarserver/changeset/2248">2248</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2008-03-25 11:56:21 -0700 (Tue, 25 Mar 2008)</dd>
</dl>

<h3>Log Message</h3>
<pre>Initial import.</pre>

<h3>Added Paths</h3>
<ul>
<li><a href="#CalDAVClientLibrarytrunkproject">CalDAVClientLibrary/trunk/.project</a></li>
<li><a href="#CalDAVClientLibrarytrunkpydevproject">CalDAVClientLibrary/trunk/.pydevproject</a></li>
<li><a href="#CalDAVClientLibrarytrunkLICENSE">CalDAVClientLibrary/trunk/LICENSE</a></li>
<li><a href="#CalDAVClientLibrarytrunkREADME">CalDAVClientLibrary/trunk/README</a></li>
<li><a href="#CalDAVClientLibrarytrunkrunadminpy">CalDAVClientLibrary/trunk/runadmin.py</a></li>
<li><a href="#CalDAVClientLibrarytrunkrunshellpy">CalDAVClientLibrary/trunk/runshell.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksetuppy">CalDAVClientLibrary/trunk/setup.py</a></li>
<li>CalDAVClientLibrary/trunk/src/</li>
<li><a href="#CalDAVClientLibrarytrunksrc__init__py">CalDAVClientLibrary/trunk/src/__init__.py</a></li>
<li>CalDAVClientLibrary/trunk/src/admin/</li>
<li><a href="#CalDAVClientLibrarytrunksrcadmin__init__py">CalDAVClientLibrary/trunk/src/admin/__init__.py</a></li>
<li>CalDAVClientLibrary/trunk/src/admin/xmlaccounts/</li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccounts__init__py">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/__init__.py</a></li>
<li>CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/</li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountscommands__init__py">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountscommandsaddrecordpy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/addrecord.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountscommandschangepasswordpy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/changepassword.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountscommandscommandpy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/command.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountscommandslistrecordspy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/listrecords.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountscommandsremoverecordpy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/removerecord.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountsdirectorypy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/directory.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountsmanagepy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/manage.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountsrecordpy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/record.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountsrecordtypespy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/recordtypes.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountstagspy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tags.py</a></li>
<li>CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/</li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountstests__init__py">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountsteststest_directorypy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/test_directory.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountsteststest_recordpy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/test_record.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcadminxmlaccountstestsutilspy">CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/utils.py</a></li>
<li>CalDAVClientLibrary/trunk/src/browser/</li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowser__init__py">CalDAVClientLibrary/trunk/src/browser/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowserbaseshellpy">CalDAVClientLibrary/trunk/src/browser/baseshell.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandpy">CalDAVClientLibrary/trunk/src/browser/command.py</a></li>
<li>CalDAVClientLibrary/trunk/src/browser/commands/</li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommands__init__py">CalDAVClientLibrary/trunk/src/browser/commands/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandsaclpy">CalDAVClientLibrary/trunk/src/browser/commands/acl.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandscatpy">CalDAVClientLibrary/trunk/src/browser/commands/cat.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandscdpy">CalDAVClientLibrary/trunk/src/browser/commands/cd.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandshelppy">CalDAVClientLibrary/trunk/src/browser/commands/help.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandshistorypy">CalDAVClientLibrary/trunk/src/browser/commands/history.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandsloggingpy">CalDAVClientLibrary/trunk/src/browser/commands/logging.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandslspy">CalDAVClientLibrary/trunk/src/browser/commands/ls.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandsprincipalpy">CalDAVClientLibrary/trunk/src/browser/commands/principal.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandspropspy">CalDAVClientLibrary/trunk/src/browser/commands/props.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandsproxiespy">CalDAVClientLibrary/trunk/src/browser/commands/proxies.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandsquitpy">CalDAVClientLibrary/trunk/src/browser/commands/quit.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandsserverpy">CalDAVClientLibrary/trunk/src/browser/commands/server.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsercommandswhoamipy">CalDAVClientLibrary/trunk/src/browser/commands/whoami.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsershellpy">CalDAVClientLibrary/trunk/src/browser/shell.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowsersubshellpy">CalDAVClientLibrary/trunk/src/browser/subshell.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcbrowserutilspy">CalDAVClientLibrary/trunk/src/browser/utils.py</a></li>
<li>CalDAVClientLibrary/trunk/src/client/</li>
<li><a href="#CalDAVClientLibrarytrunksrcclient__init__py">CalDAVClientLibrary/trunk/src/client/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcclientaccountpy">CalDAVClientLibrary/trunk/src/client/account.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcclientcalendarpy">CalDAVClientLibrary/trunk/src/client/calendar.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcclientcalendaruseraddresspy">CalDAVClientLibrary/trunk/src/client/calendaruseraddress.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcclientclientsessionpy">CalDAVClientLibrary/trunk/src/client/clientsession.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcclientfullclientpy">CalDAVClientLibrary/trunk/src/client/fullclient.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcclientprincipalpy">CalDAVClientLibrary/trunk/src/client/principal.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcclientsimplepy">CalDAVClientLibrary/trunk/src/client/simple.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocol__init__py">CalDAVClientLibrary/trunk/src/protocol/__init__.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/caldav/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolcaldav__init__py">CalDAVClientLibrary/trunk/src/protocol/caldav/__init__.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolcaldavdefinitions__init__py">CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolcaldavdefinitionscaldavxmlpy">CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/caldavxml.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolcaldavdefinitionsheaderspy">CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/headers.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolcaldavdefinitionsmethodspy">CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/methods.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolcaldavmakecalendarpy">CalDAVClientLibrary/trunk/src/protocol/caldav/makecalendar.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolcaldavmultigetpy">CalDAVClientLibrary/trunk/src/protocol/caldav/multiget.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/caldav/tests/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolcaldavtests__init__py">CalDAVClientLibrary/trunk/src/protocol/caldav/tests/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolcaldavteststest_makecalendarpy">CalDAVClientLibrary/trunk/src/protocol/caldav/tests/test_makecalendar.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolcaldavteststest_multigetpy">CalDAVClientLibrary/trunk/src/protocol/caldav/tests/test_multiget.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/http/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttp__init__py">CalDAVClientLibrary/trunk/src/protocol/http/__init__.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/http/authentication/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpauthentication__init__py">CalDAVClientLibrary/trunk/src/protocol/http/authentication/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpauthenticationauthenticatorpy">CalDAVClientLibrary/trunk/src/protocol/http/authentication/authenticator.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpauthenticationbasicpy">CalDAVClientLibrary/trunk/src/protocol/http/authentication/basic.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpauthenticationdigestpy">CalDAVClientLibrary/trunk/src/protocol/http/authentication/digest.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/http/authentication/tests/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpauthenticationtests__init__py">CalDAVClientLibrary/trunk/src/protocol/http/authentication/tests/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpauthenticationteststest_basicpy">CalDAVClientLibrary/trunk/src/protocol/http/authentication/tests/test_basic.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/http/data/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpdata__init__py">CalDAVClientLibrary/trunk/src/protocol/http/data/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpdatadatapy">CalDAVClientLibrary/trunk/src/protocol/http/data/data.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpdatafilepy">CalDAVClientLibrary/trunk/src/protocol/http/data/file.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpdatastringpy">CalDAVClientLibrary/trunk/src/protocol/http/data/string.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/http/definitions/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpdefinitions__init__py">CalDAVClientLibrary/trunk/src/protocol/http/definitions/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpdefinitionsheaderspy">CalDAVClientLibrary/trunk/src/protocol/http/definitions/headers.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpdefinitionsmethodspy">CalDAVClientLibrary/trunk/src/protocol/http/definitions/methods.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpdefinitionsstatuscodespy">CalDAVClientLibrary/trunk/src/protocol/http/definitions/statuscodes.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttprequestresponsepy">CalDAVClientLibrary/trunk/src/protocol/http/requestresponse.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpsessionpy">CalDAVClientLibrary/trunk/src/protocol/http/session.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/http/tests/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttptests__init__py">CalDAVClientLibrary/trunk/src/protocol/http/tests/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpteststest_requestresponsepy">CalDAVClientLibrary/trunk/src/protocol/http/tests/test_requestresponse.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttpteststest_utilpy">CalDAVClientLibrary/trunk/src/protocol/http/tests/test_util.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolhttputilpy">CalDAVClientLibrary/trunk/src/protocol/http/util.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/tests/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocoltests__init__py">CalDAVClientLibrary/trunk/src/protocol/tests/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolteststest_urlpy">CalDAVClientLibrary/trunk/src/protocol/tests/test_url.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolurlpy">CalDAVClientLibrary/trunk/src/protocol/url.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/utils/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolutils__init__py">CalDAVClientLibrary/trunk/src/protocol/utils/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolutilsxmlhelperspy">CalDAVClientLibrary/trunk/src/protocol/utils/xmlhelpers.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/webdav/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdav__init__py">CalDAVClientLibrary/trunk/src/protocol/webdav/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavacepy">CalDAVClientLibrary/trunk/src/protocol/webdav/ace.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavaclpy">CalDAVClientLibrary/trunk/src/protocol/webdav/acl.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavcopypy">CalDAVClientLibrary/trunk/src/protocol/webdav/copy.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavcopymovebasepy">CalDAVClientLibrary/trunk/src/protocol/webdav/copymovebase.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavdefinitions__init__py">CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavdefinitionsdavxmlpy">CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/davxml.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavdefinitionsheaderspy">CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/headers.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavdefinitionsmethodspy">CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/methods.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavdefinitionsstatuscodespy">CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/statuscodes.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavdeletepy">CalDAVClientLibrary/trunk/src/protocol/webdav/delete.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavgetpy">CalDAVClientLibrary/trunk/src/protocol/webdav/get.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavgetbasepy">CalDAVClientLibrary/trunk/src/protocol/webdav/getbase.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavheadpy">CalDAVClientLibrary/trunk/src/protocol/webdav/head.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavlockpy">CalDAVClientLibrary/trunk/src/protocol/webdav/lock.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavmakecollectionpy">CalDAVClientLibrary/trunk/src/protocol/webdav/makecollection.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavmovepy">CalDAVClientLibrary/trunk/src/protocol/webdav/move.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavmultiresponseparserpy">CalDAVClientLibrary/trunk/src/protocol/webdav/multiresponseparser.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavoptionspy">CalDAVClientLibrary/trunk/src/protocol/webdav/options.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavprincipalmatchpy">CalDAVClientLibrary/trunk/src/protocol/webdav/principalmatch.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavpropallpy">CalDAVClientLibrary/trunk/src/protocol/webdav/propall.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavpropfindpy">CalDAVClientLibrary/trunk/src/protocol/webdav/propfind.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavpropfindbasepy">CalDAVClientLibrary/trunk/src/protocol/webdav/propfindbase.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavpropfindparserpy">CalDAVClientLibrary/trunk/src/protocol/webdav/propfindparser.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavpropnamespy">CalDAVClientLibrary/trunk/src/protocol/webdav/propnames.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavproppatchpy">CalDAVClientLibrary/trunk/src/protocol/webdav/proppatch.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavputpy">CalDAVClientLibrary/trunk/src/protocol/webdav/put.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavreportpy">CalDAVClientLibrary/trunk/src/protocol/webdav/report.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavrequestresponsepy">CalDAVClientLibrary/trunk/src/protocol/webdav/requestresponse.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavsessionpy">CalDAVClientLibrary/trunk/src/protocol/webdav/session.py</a></li>
<li>CalDAVClientLibrary/trunk/src/protocol/webdav/tests/</li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavtests__init__py">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_acepy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_ace.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_aclpy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_acl.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_copypy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_copy.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_deletepy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_delete.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_getpy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_get.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_headpy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_head.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_lockpy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_lock.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_movepy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_move.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_optionspy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_options.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_principalmatchpy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_principalmatch.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_propfindpy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propfind.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_propfindparserpy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propfindparser.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_propnamespy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propnames.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_putpy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_put.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_reportpy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_report.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_templatepy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_template.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavteststest_unlockpy">CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_unlock.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavunlockpy">CalDAVClientLibrary/trunk/src/protocol/webdav/unlock.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcprotocolwebdavxmlresponseparserpy">CalDAVClientLibrary/trunk/src/protocol/webdav/xmlresponseparser.py</a></li>
<li>CalDAVClientLibrary/trunk/src/ui/</li>
<li>CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/</li>
<li><a href="#CalDAVClientLibrarytrunksrcuiWebDAVBrowsernibclassesnib">CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/classes.nib</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcuiWebDAVBrowsernibinfonib">CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/info.nib</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcuiWebDAVBrowsernibkeyedobjectsnib">CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/keyedobjects.nib</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcuiWebDAVBrowserpy">CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcui__init__py">CalDAVClientLibrary/trunk/src/ui/__init__.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcuiresourcepy">CalDAVClientLibrary/trunk/src/ui/resource.py</a></li>
<li><a href="#CalDAVClientLibrarytrunksrcuisessionpy">CalDAVClientLibrary/trunk/src/ui/session.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalDAVClientLibrarytrunkproject"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/.project (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/.project                                (rev 0)
+++ CalDAVClientLibrary/trunk/.project        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,17 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;projectDescription&gt;
+        &lt;name&gt;CalDAVClientLibrary&lt;/name&gt;
+        &lt;comment&gt;&lt;/comment&gt;
+        &lt;projects&gt;
+        &lt;/projects&gt;
+        &lt;buildSpec&gt;
+                &lt;buildCommand&gt;
+                        &lt;name&gt;org.python.pydev.PyDevBuilder&lt;/name&gt;
+                        &lt;arguments&gt;
+                        &lt;/arguments&gt;
+                &lt;/buildCommand&gt;
+        &lt;/buildSpec&gt;
+        &lt;natures&gt;
+                &lt;nature&gt;org.python.pydev.pythonNature&lt;/nature&gt;
+        &lt;/natures&gt;
+&lt;/projectDescription&gt;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunkpydevproject"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/.pydevproject (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/.pydevproject                                (rev 0)
+++ CalDAVClientLibrary/trunk/.pydevproject        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,13 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;?eclipse-pydev version=&quot;1.0&quot;?&gt;
+
+&lt;pydev_project&gt;
+&lt;pydev_pathproperty name=&quot;org.python.pydev.PROJECT_SOURCE_PATH&quot;&gt;
+&lt;path&gt;/CalDAVClientLibrary/src&lt;/path&gt;
+&lt;/pydev_pathproperty&gt;
+&lt;pydev_property name=&quot;org.python.pydev.PYTHON_PROJECT_VERSION&quot;&gt;python 2.5&lt;/pydev_property&gt;
+&lt;pydev_pathproperty name=&quot;org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH&quot;&gt;
+&lt;path&gt;/Volumes/Data/Users/cyrusdaboo/Documents/Development/Apple/eclipse/PyCalendar/src&lt;/path&gt;
+&lt;path&gt;/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python/PyObjC&lt;/path&gt;
+&lt;/pydev_pathproperty&gt;
+&lt;/pydev_project&gt;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunkLICENSE"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/LICENSE (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/LICENSE                                (rev 0)
+++ CalDAVClientLibrary/trunk/LICENSE        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,202 @@
</span><ins>+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      &quot;License&quot; shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      &quot;Legal Entity&quot; shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      &quot;control&quot; means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      &quot;Source&quot; form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      &quot;Object&quot; form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      &quot;Work&quot; shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      &quot;Derivative Works&quot; shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      &quot;Contribution&quot; shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, &quot;submitted&quot;
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
+
+      &quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a &quot;NOTICE&quot; text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets &quot;[]&quot;
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same &quot;printed page&quot; as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunkREADME"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/README (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/README                                (rev 0)
+++ CalDAVClientLibrary/trunk/README        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,105 @@
</span><ins>+README for CalDAVClientLibrary
+
+INTRODUCTION
+
+CalDAVCLientLibrary is a Python library and tool for CalDAV. It is
+comprised of four main modules:
+
+protocol: this implements an HTTP/WebDAV/CalDAV protocol stack, using
+httplib to communicate with the server.
+
+client: this implements a CalDAV client session, with higher level
+functionality than the protocol module (e.g. 'get properties on resource
+X and return as a dict'). There is a higher level abstraction using an
+object model to repesent a session, accounts, principals and calendars
+as objects.
+
+browser: this implements a shell-like browser that lets you interact
+with the CalDAV server directly via protocol. You can 'cd' to different
+parts of the repository, 'ls' to list a collection, 'cat' to read
+resource data, 'props' to get properties. Then there are some higher
+level functions such as 'proxies' which let you manage (read and edit)
+the proxy list for a principal, and 'acl' which lets you manage ACLs
+directly on resources. For those, the tool takes care of mapping from
+principal paths to principal URLs etc. Help is provided for each command
+(type '?'). It is easily extensible by adding new commands.
+
+ui: a PyObjC application with a WebDAV browser GUI. This provides a file
+system like browser that allows properties and the data for a selected
+WebDAV resource do be displayed.
+
+admin: a user account administration tool. Currently works only with the
+XML file directory account.
+
+*** NB This package requires Python 2.5. ***
+
+The runshell.py script will launch the command line browser shell.
+The runadmin.py script will run the XML directory admin tool.
+
+
+SHELL TOOL
+
+-- COMMAND LINE OPTIONS
+
+    Usage: runshell [OPTIONS]
+    
+    Options:
+    
+    -l              start with HTTP logging on.
+    
+    --server=HOST   url of the server include http/https scheme and
+                    port [REQUIRED].
+                    
+    --user=USER     user name to login as - will be prompted if not
+                    present [OPTIONAL].
+                    
+    --pswd=PSWD     password for user - will be prompted if not
+                    present [OPTIONAL].
+
+-- QUICKSTART - COMMANDLINE
+
+To browse a calendar server on the local machine:
+
+    ./runshell.py --server http://localhost:8008
+    
+or, for SSL:
+
+    ./runshell.py --server https://localhost:8443
+
+Then type '?' followed by return to see the list of available commands.
+
+
+UI TOOL
+
+-- QUICKSTART - GUI
+
+Build the GUI app using:
+
+    python setup.py py2app
+
+The application will be placed in the 'dist' directory. Double-click
+that to launch it. One it it running, click the 'Server' toolbar button
+and specify a server, user id and password.
+
+The app will then display the top-level of the server resource hierarchy
+in the browser pane on the left. You can click and navigate through the
+resources via that pane (the 'Browser' toolbar buttons determine whether
+the browser uses a column or list view).
+
+When a resource is selected in the browser pane, its properties or data
+are display in the right hand pane. You can toggle between viewing
+properties or data by using the 'View' toolbar buttons.
+
+
+ADMIN TOOL
+
+-- QUICKSTART - COMMANDLINE
+
+To run the tool and see the list of available commands:
+
+    ./runadmin.py --help
+    
+
+TO DO
+
+Lots of error handling and documentation.
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunkrunadminpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/runadmin.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/runadmin.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/runadmin.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+#!/usr/bin/env python
+
+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+#
+# Runs the CalDAVTester test suite ensuring that required packages are available.
+#
+
+if __name__ == &quot;__main__&quot;:
+
+    import os
+    import sys
+
+    cwd = os.getcwd()
+    sys.path.append(os.path.join(cwd, &quot;src&quot;))
+
+    from admin.xmlaccounts import manage
+    manage.runit()
</ins><span class="cx">Property changes on: CalDAVClientLibrary/trunk/runadmin.py
</span><span class="cx">___________________________________________________________________
</span><span class="cx">Name: svn:executable
</span><span class="cx">   + *
</span></span></pre></div>
<a id="CalDAVClientLibrarytrunkrunshellpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/runshell.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/runshell.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/runshell.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+#!/usr/bin/env python
+
+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+#
+# Runs the CalDAVTester test suite ensuring that required packages are available.
+#
+
+if __name__ == &quot;__main__&quot;:
+
+    import os
+    import sys
+
+    cwd = os.getcwd()
+    sys.path.append(os.path.join(cwd, &quot;src&quot;))
+
+    from src.browser import shell
+    shell.runit()
</ins><span class="cx">Property changes on: CalDAVClientLibrary/trunk/runshell.py
</span><span class="cx">___________________________________________________________________
</span><span class="cx">Name: svn:executable
</span><span class="cx">   + *
</span></span></pre></div>
<a id="CalDAVClientLibrarytrunksetuppy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/setup.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/setup.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/setup.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+&quot;&quot;&quot;
+Script for building the UI app (OS X only).
+
+Usage:
+    python setup.py py2app
+&quot;&quot;&quot; 
+
+from distutils.core import setup
+import py2app
+
+plist = dict(NSMainNibFile=&quot;WebDAVBrowser&quot;)
+setup(
+    app=[&quot;src/ui/WebDAVBrowser.py&quot;],
+    data_files=[&quot;src/ui/WebDAVBrowser.nib&quot;, ],
+    options=dict(py2app=dict(plist=plist, includes=[&quot;urllib&quot;, &quot;sha&quot;, &quot;md5&quot;,], packages=[&quot;src/client&quot;, &quot;src/protocol&quot;, &quot;src/ui&quot;,])),
+)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrc__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadmin__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccounts__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountscommands__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,28 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from admin.xmlaccounts.commands.addrecord import AddRecord
+from admin.xmlaccounts.commands.changepassword import ChangePassword
+from admin.xmlaccounts.commands.listrecords import ListRecords
+from admin.xmlaccounts.commands.removerecord import RemoveRecord
+
+# Commands register themselves in this dict
+registered = {}
+
+registered[AddRecord.CMDNAME] = AddRecord
+registered[ChangePassword.CMDNAME] = ChangePassword
+registered[ListRecords.CMDNAME] = ListRecords
+registered[RemoveRecord.CMDNAME] = RemoveRecord
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountscommandsaddrecordpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/addrecord.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/addrecord.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/addrecord.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,102 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from admin.xmlaccounts.commands.command import Command
+from admin.xmlaccounts.record import XMLRecord
+from uuid import uuid4
+from admin.xmlaccounts import recordtypes
+
+class AddRecord(Command):
+    
+    CMDNAME = &quot;add&quot;
+
+    def __init__(self):
+        super(AddRecord, self).__init__(self.CMDNAME, &quot;Add a record of the specified type.&quot;)
+
+    def doCommand(self):
+        if self.doAdd():
+            return self.writeAccounts()
+        return 0
+    
+    def doAdd(self):
+        
+        # Prompt for each thing we need in the record
+        record = XMLRecord()
+        record.recordType = self.recordType
+        print &quot;Enter the fields for the record (ctrl-D to stop at any time)&quot;
+        try:
+            # uid
+            while True:
+                record.uid = raw_input(&quot;Id: &quot;)
+                if not record.uid:
+                    print &quot;A valid uid is required. Please try again.&quot;
+                elif self.directory.containsRecord(self.recordType, record.uid):
+                    print &quot;Record uid: '%s' of type: '%s' does already exists in the directory.&quot; % (record.uid, self.recordType,)
+                else:
+                    break
+
+            # guid
+            while True:
+                record.guid = raw_input(&quot;GUID [leave empty to auto-generate]: &quot;)
+                if record.guid and self.directory.containsGUID(record.guid):
+                    print &quot;GUID: '%s' already used in the directory&quot; % (record.guid,) 
+                else:
+                    break
+            
+            # password
+            record.password = self.promptPassword()
+
+            # name
+            record.name = raw_input(&quot;Name: &quot;)
+            
+            # members
+            if self.recordType in (recordtypes.recordType_groups,):
+                record.members = self.getMemberList(&quot;Enter members of this group&quot;, &quot;Member&quot;, &quot;members&quot;)
+
+            # cuaddr
+            while True:
+                cuaddr = raw_input(&quot;Calendar user address [leave empty to stop adding addresses]: &quot;)
+                if cuaddr:
+                    record.calendarUserAddresses.add(cuaddr)
+                else:
+                    break
+            
+            # auto-schedule
+            if self.recordType in (recordtypes.recordType_locations, recordtypes.recordType_resources,):
+                auto_schedule = raw_input(&quot;Turn on automatic scheduling [y/n]?: &quot;)
+                if auto_schedule == &quot;y&quot;:
+                    record.autoSchedule = True
+
+            # enabled for calendaring
+            if self.recordType in (recordtypes.recordType_users, recordtypes.recordType_groups,):
+                enable_calendars = raw_input(&quot;Create calendar account rather than access-only account [y/n]?: &quot;)
+                if enable_calendars == &quot;n&quot;:
+                    record.enabledForCalendaring = False
+
+            # proxies
+            if self.recordType in (recordtypes.recordType_locations, recordtypes.recordType_resources,):
+                record.proxies = self.getMemberList(&quot;Enter proxies of this location or resource&quot;, &quot;Proxy&quot;, &quot;proxies&quot;)
+
+        except EOFError:
+            return 0
+        
+        # Now validate the record and save it
+        if not record.guid:
+            record.guid = str(uuid4())
+            
+        self.directory.addRecord(record)
+
+        return 1
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountscommandschangepasswordpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/changepassword.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/changepassword.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/changepassword.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,93 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from admin.xmlaccounts.commands.command import Command
+import getopt
+
+class ChangePassword(Command):
+    
+    CMDNAME = &quot;passwd&quot;
+
+    def __init__(self):
+        super(ChangePassword, self).__init__(self.CMDNAME, &quot;Change the password for a record.&quot;)
+        self.uid = None
+
+    def usage(self):
+        print &quot;&quot;&quot;USAGE: %s TYPE [OPTIONS]
+
+TYPE: One of &quot;users&quot;, &quot;groups&quot;, &quot;locations&quot; or &quot;resources&quot;. Also,
+&quot;u&quot;, &quot;g&quot;, &quot;l&quot; or &quot;r&quot; as shortcuts.
+
+Options:
+    -f    file path to accounts.xml
+    --uid UID of record to change
+&quot;&quot;&quot; % (self.cmdname,)
+
+    def execute(self, argv):
+        
+        # Check first argument for type
+        argv = self.getTypeArgument(argv)
+        if argv is None:
+            return 0
+        
+        opts, args = getopt.getopt(argv, 'f:h', [&quot;help&quot;, &quot;uid=&quot;,])
+        
+        for name, value in opts:
+            if name == &quot;-f&quot;:
+                self.path = value
+            elif name in (&quot;-h&quot;, &quot;--help&quot;):
+                self.usage()
+                return 1
+            elif name == &quot;--uid&quot;:
+                self.uid = value
+            else:
+                print &quot;Unknown option: %s.&quot; % (name,)
+                self.usage()
+                return 0
+
+        if not self.path:
+            print &quot;Must specify a path.&quot;
+            self.usage()
+            return 0
+        if not self.uid:
+            print &quot;Must specify a UID.&quot;
+            self.usage()
+            return 0
+        if args:
+            print &quot;Arguments not allowed.&quot;
+            self.usage()
+            return 0
+        
+        if not self.loadAccounts():
+            return 0
+        return self.doCommand()
+
+    def doCommand(self):
+        if self.doChangePassword():
+            return self.writeAccounts()
+        return 0
+    
+    def doChangePassword(self):
+        
+        # First check record exists
+        record = self.directory.getRecord(self.recordType, self.uid)
+        if record is None:
+            print &quot;No '%s' record matching uid '%s'&quot; % (self.recordType, self.uid,)
+            return 0
+            
+        record.password = self.promptPassword()
+        return 1
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountscommandscommandpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/command.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/command.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/command.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,187 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from admin.xmlaccounts.directory import XMLDirectory
+from xml.etree.ElementTree import XML
+from StringIO import StringIO
+from protocol.utils.xmlhelpers import BetterElementTree
+from admin.xmlaccounts import recordtypes
+from getpass import getpass
+
+import getopt
+
+class Command(object):
+    
+    def __init__(self, cmdname, description):
+        self.path = None
+        self.cmdname = cmdname
+        self.description = description
+        self.recordType = None
+
+    def usage(self):
+        if self.allRecordsAllowed():
+            print &quot;&quot;&quot;USAGE: %s [TYPE] [OPTIONS]
+        
+TYPE: One of &quot;all&quot;, &quot;users&quot;, &quot;groups&quot;, &quot;locations&quot; or &quot;resources&quot;. Also,
+&quot;a&quot;, &quot;u&quot;, &quot;g&quot;, &quot;l&quot; or &quot;r&quot; as shortcuts. Invalid or missing type is
+treated as &quot;all&quot;.
+
+Options:
+    -f  file path to accounts.xml
+&quot;&quot;&quot; % (self.cmdname,)
+        else:
+            print &quot;&quot;&quot;USAGE: %s TYPE [OPTIONS]
+        
+TYPE: One of &quot;users&quot;, &quot;groups&quot;, &quot;locations&quot; or &quot;resources&quot;. Also,
+&quot;u&quot;, &quot;g&quot;, &quot;l&quot; or &quot;r&quot; as shortcuts.
+
+Options:
+    -f  file path to accounts.xml
+&quot;&quot;&quot; % (self.cmdname,)
+
+    def allRecordsAllowed(self):
+        return False
+
+    def execute(self, argv):
+        
+        # Check first argument for type
+        argv = self.getTypeArgument(argv)
+        if argv is None:
+            return 0
+        
+        opts, args = getopt.getopt(argv, 'f:h', [&quot;help&quot;, ])
+        
+        for name, value in opts:
+            if name == &quot;-f&quot;:
+                self.path = value
+            elif name in (&quot;-h&quot;, &quot;--help&quot;):
+                self.usage()
+                return 1
+            else:
+                print &quot;Unknown option: %s.&quot; % (name,)
+                self.usage()
+                return 0
+
+        if not self.path:
+            print &quot;Must specify a path.&quot;
+            self.usage()
+            return 0
+        if args:
+            print &quot;Arguments not allowed.&quot;
+            self.usage()
+            return 0
+        
+        if not self.loadAccounts():
+            return 0
+        return self.doCommand()
+
+    def getTypeArgument(self, argv):
+        # Check first argument for type
+        if len(argv) == 0:
+            print &quot;Must specify a record type.&quot;
+            self.usage()
+            return None
+        type = argv[0]
+        type = self.mapType(type)
+        if not type and not self.allRecordsAllowed():
+            print &quot;Invalid type '%s'.&quot; % (argv[0],)
+            self.usage()
+            return None
+        self.recordType = type if type else recordtypes.recordType_all
+        if type:
+            return argv[1:]
+        else:
+            return argv
+
+    def mapType(self, type):
+        return {
+            &quot;users&quot;    : recordtypes.recordType_users,
+            &quot;u&quot;        : recordtypes.recordType_users,
+            &quot;groups&quot;   : recordtypes.recordType_groups,
+            &quot;g&quot;        : recordtypes.recordType_groups,
+            &quot;locations&quot;: recordtypes.recordType_locations,
+            &quot;l&quot;        : recordtypes.recordType_locations,
+            &quot;resources&quot;: recordtypes.recordType_resources,
+            &quot;r&quot;        : recordtypes.recordType_resources,
+            &quot;all&quot;      : recordtypes.recordType_all,
+            &quot;a&quot;        : recordtypes.recordType_all,
+        }.get(type, None)
+        
+    def loadAccounts(self):
+        
+        f = open(self.path, &quot;r&quot;)
+        if not f:
+            print &quot;Could not open file: %s&quot; % (self.path,)
+            return 0
+        xmldata = f.read()
+        f.close()
+        self.directory = XMLDirectory()
+        self.directory.parseXML(XML(xmldata))
+        return 1
+
+    def writeAccounts(self):
+        
+        node = self.directory.writeXML()
+        os = StringIO()
+        xmldoc = BetterElementTree(node)
+        xmldoc.writeUTF8(os)
+        f = open(self.path, &quot;w&quot;)
+        if not f:
+            print &quot;Could not open file: %s for writing&quot; % (self.path,)
+            return 0
+        f.write(os.getvalue())
+        f.close()
+        return 1
+
+    def doCommand(self):
+        pass
+
+    def promptPassword(self):
+        &quot;&quot;&quot;
+        Prompt the user for a password.
+        &quot;&quot;&quot;
+        while True:
+            password = getpass(&quot;Password: &quot;)
+            temp = getpass(&quot;Password (again): &quot;)
+            if temp != password:
+                print &quot;Passwords do not match. Try again.&quot;
+            else:
+                return password
+
+    def getMemberList(self, prompt, title, type):
+        &quot;&quot;&quot;
+        Prompt the user for a list of members.
+        &quot;&quot;&quot;
+        results = []
+        print prompt
+        while True:
+            memberType = raw_input(&quot;%s type [u/g/l/r or leave empty to stop adding %s]: &quot; % (title, type,))
+            if memberType in (&quot;u&quot;, &quot;g&quot;, &quot;l&quot;, &quot;r&quot;,):
+                memberUid = raw_input(&quot;%s uid [leave empty to stop adding %s]: &quot; % (title, type,))
+                if memberUid:
+                    # Verify that member type exists
+                    recordType = self.mapType(memberType)
+                    if self.directory.containsRecord(recordType, memberUid):
+                        results.append((recordType, memberUid,))
+                    else:
+                        print &quot;Record uid: '%s 'of type: '%s' does not exist in the directory.&quot; % (memberUid, recordType,)
+                else:
+                    break
+            elif memberType:
+                print &quot;Member type must be one of 'u' (users), 'g' (groups), 'l' (locations) or 'r' (resources).&quot;
+            else:
+                break
+        return results
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountscommandslistrecordspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/listrecords.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/listrecords.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/listrecords.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,125 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from admin.xmlaccounts.commands.command import Command
+from admin.xmlaccounts import recordtypes
+import itertools
+
+class ListRecords(Command):
+    
+    CMDNAME = &quot;list&quot;
+
+    def __init__(self):
+        super(ListRecords, self).__init__(self.CMDNAME, &quot;List all records of the specified type.&quot;)
+
+    def allRecordsAllowed(self):
+        return True
+
+    def doCommand(self):
+        self.listRecords(self.recordType)
+
+    def listRecords(self, recordType):
+        
+        if recordType == recordtypes.recordType_all:
+            users = [l for l in self.directory.records.itervalues()]
+            print &quot;Full List\n&quot;
+        else:
+            users = (self.directory.records[recordType],)
+            print &quot;%s List\n&quot; % (recordType.capitalize(),)
+        
+        
+        table = [
+            [&quot;UID&quot;, &quot;GUID&quot;, &quot;Name&quot;, &quot;CUADDR&quot;,]
+        ]
+        if recordType == recordtypes.recordType_all:
+            table[0].insert(0, &quot;TYPE&quot;)
+            table[0].append(&quot;MEMBERS&quot;)
+            table[0].append(&quot;PROXIES&quot;)
+        elif recordType in (recordtypes.recordType_groups,):
+            table[0].append(&quot;MEMBERS&quot;)
+        elif recordType in (recordtypes.recordType_locations, recordtypes.recordType_resources,):
+            table[0].append(&quot;PROXIES&quot;)
+        for user in itertools.chain(*users):
+            if len(table) &gt; 1:
+                table.append(None)
+            cuaddrs = user.calendarUserAddresses if user.calendarUserAddresses else (&quot;&quot;,)
+            if user.recordType in (recordtypes.recordType_groups,):
+                members = user.members if user.members else (None,)
+            elif user.recordType in (recordtypes.recordType_locations, recordtypes.recordType_resources,):
+                members = user.proxies if user.proxies else (None,)
+            else:
+                members = (None,)
+            for ctr, items in enumerate(map(None, cuaddrs, members,)):
+                cuaddr, member = items
+                if cuaddr is None:
+                    cuaddr = &quot;&quot;
+                if member is None:
+                    member = &quot;&quot;
+                else:
+                    member = &quot;(%s) %s&quot; % (member[0], member[1],)
+                if recordType == recordtypes.recordType_all:
+                    row = (user.recordType,)
+                else:
+                    row = ()
+                row += (
+                    user.uid if not ctr else &quot;&quot;,
+                    user.guid if not ctr else &quot;&quot;,
+                    user.name if not ctr else &quot;&quot;,
+                    cuaddr,
+                )
+                if recordType == recordtypes.recordType_all:
+                    if user.recordType in (recordtypes.recordType_groups,):
+                        row += (member, &quot;&quot;)
+                    elif user.recordType in (recordtypes.recordType_locations, recordtypes.recordType_resources,):
+                        row += (&quot;&quot;, member,)
+                    else:
+                        row += (&quot;&quot;, &quot;&quot;)
+                elif user.recordType in (recordtypes.recordType_groups, recordtypes.recordType_locations, recordtypes.recordType_resources,):
+                    row += (member,)
+                table.append(row)
+        
+        self.printTable(table)
+        return 1
+        
+    def printTable(self, table):
+        
+        maxWidths = [0 for _ignore in table[0]]
+        for row in table:
+            if row is not None:
+                for ctr, col in enumerate(row):
+                    maxWidths[ctr] = max(maxWidths[ctr], len(col) if col else 0)
+        
+        self.printDivider(maxWidths, False)
+        for rowctr, row in enumerate(table):
+            if row is None:
+                self.printDivider(maxWidths)
+            else:
+                print &quot;|&quot;,
+                for colctr, col in enumerate(row):
+                    print &quot;%- *s&quot; % (maxWidths[colctr], col if col else &quot;&quot;),
+                    print &quot;|&quot;,
+                print &quot;&quot;
+            if not rowctr:
+                self.printDivider(maxWidths)
+        self.printDivider(maxWidths, False)
+
+    def printDivider(self, maxWidths, intermediate=True):
+        t = &quot;|&quot; if intermediate else &quot;+&quot;
+        for widthctr, width in enumerate(maxWidths):
+            t += &quot;-&quot;
+            t += &quot;-&quot; * width
+            t += &quot;-+&quot; if widthctr &lt; len(maxWidths) - 1 else (&quot;-|&quot; if intermediate else &quot;-+&quot;)
+        print t
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountscommandsremoverecordpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/removerecord.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/removerecord.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/commands/removerecord.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,97 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from admin.xmlaccounts.commands.command import Command
+import getopt
+
+class RemoveRecord(Command):
+    
+    CMDNAME = &quot;remove&quot;
+
+    def __init__(self):
+        super(RemoveRecord, self).__init__(self.CMDNAME, &quot;Remove a record of the specified type.&quot;)
+        self.uid = None
+
+    def usage(self):
+        print &quot;&quot;&quot;USAGE: %s TYPE [OPTIONS]
+
+TYPE: One of &quot;users&quot;, &quot;groups&quot;, &quot;locations&quot; or &quot;resources&quot;. Also,
+&quot;u&quot;, &quot;g&quot;, &quot;l&quot; or &quot;r&quot; as shortcuts.
+
+Options:
+    -f    file path to accounts.xml
+    --uid UID to remove
+&quot;&quot;&quot; % (self.cmdname,)
+
+    def execute(self, argv):
+        
+        # Check first argument for type
+        argv = self.getTypeArgument(argv)
+        if argv is None:
+            return 0
+        
+        opts, args = getopt.getopt(argv, 'f:h', [&quot;help&quot;, &quot;uid=&quot;,])
+        
+        for name, value in opts:
+            if name == &quot;-f&quot;:
+                self.path = value
+            elif name in (&quot;-h&quot;, &quot;--help&quot;):
+                self.usage()
+                return 1
+            elif name == &quot;--uid&quot;:
+                self.uid = value
+            else:
+                print &quot;Unknown option: %s.&quot; % (name,)
+                self.usage()
+                return 0
+
+        if not self.path:
+            print &quot;Must specify a path.&quot;
+            self.usage()
+            return 0
+        if not self.uid:
+            print &quot;Must specify a UID.&quot;
+            self.usage()
+            return 0
+        if args:
+            print &quot;Arguments not allowed.&quot;
+            self.usage()
+            return 0
+        
+        if not self.loadAccounts():
+            return 0
+        return self.doCommand()
+
+    def doCommand(self):
+        if self.doRemove():
+            return self.writeAccounts()
+        return 0
+    
+    def doRemove(self):
+        
+        # First check record exists
+        record = self.directory.getRecord(self.recordType, self.uid)
+        if record is None:
+            print &quot;No '%s' record matching uid '%s'&quot; % (self.recordType, self.uid,)
+            return 0
+            
+        
+        confirm = raw_input(&quot;Really delete the record for '%s' in '%s' [y/n]?&quot; % (self.uid, self.recordType,))
+        if confirm != &quot;y&quot;:
+            return 0
+        
+        self.directory.removeRecord(self.recordType, self.uid)
+        return 1
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountsdirectorypy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/directory.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/directory.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/directory.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,86 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from admin.xmlaccounts import recordtypes
+from admin.xmlaccounts import tags
+from admin.xmlaccounts.record import XMLRecord
+
+from xml.etree.ElementTree import Element
+
+class XMLDirectory(object):
+    
+    def __init__(self):
+        self.realm = &quot;&quot;
+        self.records = {}
+        for type in recordtypes.RECORD_TYPES:
+            self.records[type] = []
+    
+    def addRecord(self, record):
+        self.records[record.recordType].append(record)
+        
+    def containsRecord(self, recordType, uid):
+        for record in self.records[recordType]:
+            if record.uid == uid:
+                return True
+        else:
+            return False
+        
+    def containsGUID(self, guid):
+        for type in recordtypes.RECORD_TYPES:
+            for record in self.records[type]:
+                if record.guid == guid:
+                    return True
+        return False
+        
+    def getRecord(self, recordType, uid):
+        for record in self.records[recordType]:
+            if record.uid == uid:
+                return record
+        else:
+            return None
+        
+    def removeRecord(self, recordType, uid):
+        for record in self.records[recordType]:
+            if record.uid == uid:
+                self.records[recordType].remove(record)
+                return True
+        else:
+            return False
+        
+    def parseXML(self, node):
+        
+        if node.tag == tags.ELEMENT_ACCOUNTS:
+            self.realm = node.get(tags.ATTRIBUTE_REALM, &quot;&quot;)
+
+            for child in node.getchildren():
+                record = XMLRecord()
+                record.parseXML(child)
+                self.records[record.recordType].append(record)
+                
+            # Now resolve group and proxy references
+            
+
+    def writeXML(self):
+        root = Element(tags.ELEMENT_ACCOUNTS)
+        if self.realm:
+            root.set(tags.ATTRIBUTE_REALM, self.realm)
+        for type in recordtypes.RECORD_TYPES:
+            self.writeXMLRecords(root, self.records[type])
+        return root
+
+    def writeXMLRecords(self, root, records):
+        for record in records:
+            root.append(record.writeXML())
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountsmanagepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/manage.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/manage.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/manage.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,48 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+#import admin.xmlaccounts.commands
+from admin.xmlaccounts.commands import registered
+
+import sys
+
+def usage():
+    cmds = registered.keys()
+    cmds.sort()
+    print &quot;&quot;&quot;USAGE: manage CMD [OPTIONS]
+
+CMD: one of:
+%s
+    
+OPTIONS: specific to each command, use --help with the
+command to see what options are supported.
+&quot;&quot;&quot; % (&quot;\n&quot;.join([&quot;\t%s&quot; % (cmd,) for cmd in cmds]),)
+
+def runit():
+    # Dispatch a command based on the first argument
+    if len(sys.argv) == 1:
+        usage()
+        sys.exit(0)
+    
+    if registered.has_key(sys.argv[1]):
+        sys.exit(registered[sys.argv[1]]().execute(sys.argv[2:]))
+    else:
+        print &quot;No command called '%s' is available.&quot; % (sys.argv[1],)
+        usage()
+        sys.exit(0)
+
+if __name__ == '__main__':
+    runit()
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountsrecordpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/record.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/record.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/record.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,106 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from admin.xmlaccounts import recordtypes
+from admin.xmlaccounts import tags
+
+from protocol.utils.xmlhelpers import SubElementWithData
+
+from xml.etree.ElementTree import Element
+
+class XMLRecord(object):
+    
+    def __init__(self):
+        self.recordType = None
+        self.repeat = 0
+        self.uid = None
+        self.guid = None
+        self.password = None
+        self.name = None
+        self.members = set()
+        self.calendarUserAddresses = set()
+        self.autoSchedule = False
+        self.enabledForCalendaring = True
+        self.proxies = set()
+        self.proxyFor = set()
+    
+    def parseXML(self, node):
+        self.recordType = recordtypes.TAGS_TO_RECORD_TYPES[node.tag]
+        self.repeat = int(node.get(tags.ATTRIBUTE_REPEAT, &quot;0&quot;))
+        for child in node.getchildren():
+            if child.tag == tags.ELEMENT_UID:
+                self.uid = child.text
+            elif child.tag == tags.ELEMENT_GUID:
+                self.guid = child.text
+            elif child.tag == tags.ELEMENT_PASSWORD:
+                self.password = child.text
+            elif child.tag == tags.ELEMENT_NAME:
+                self.name = child.text
+            elif child.tag == tags.ELEMENT_MEMBERS:
+                self._parseMembers(child, self.members)
+            elif child.tag == tags.ELEMENT_CUADDR:
+                self.calendarUserAddresses.add(child.text)
+            elif child.tag == tags.ELEMENT_AUTOSCHEDULE:
+                # Only Resources &amp; Locations
+                if self.recordType not in (recordtypes.recordType_resources, recordtypes.recordType_locations,):
+                    raise ValueError(&quot;&lt;auto-schedule&gt; element only allowed for Resources and Locations: %s&quot; % (child.tag,))
+                self.autoSchedule = True
+            elif child.tag == tags.ELEMENT_DISABLECALENDAR:
+                # Only Groups
+                if self.recordType not in (recordtypes.recordType_users, recordtypes.recordType_groups,):
+                    raise ValueError(&quot;&lt;disable-calendar&gt; element only allowed for Groups: %s&quot; % (child.tag,))
+                self.enabledForCalendaring = False
+            elif child.tag == tags.ELEMENT_PROXIES:
+                # Only Resources &amp; Locations
+                if self.recordType not in (recordtypes.recordType_resources, recordtypes.recordType_locations,):
+                    raise ValueError(&quot;&lt;proxies&gt; element only allowed for Resources and Locations: %s&quot; % (child.tag,))
+                self._parseMembers(child, self.proxies)
+            else:
+                raise RuntimeError(&quot;Unknown account attribute: %s&quot; % (child.tag,))
+
+    def _parseMembers(self, node, addto):
+        for child in node.getchildren():
+            if child.tag == tags.ELEMENT_MEMBER:
+                recordType = child.get(tags.ATTRIBUTE_RECORDTYPE, recordtypes.recordType_users)
+                addto.add((recordType, child.text))
+
+    def writeXML(self):
+        
+        root = Element(recordtypes.RECORD_TYPES_TO_TAGS[self.recordType])
+        if self.repeat:
+            root.set(tags.ATTRIBUTE_REPEAT, str(self.repeat))
+        
+        SubElementWithData(root, tags.ELEMENT_UID, self.uid)
+        SubElementWithData(root, tags.ELEMENT_GUID, self.guid)
+        SubElementWithData(root, tags.ELEMENT_PASSWORD, self.password)
+        SubElementWithData(root, tags.ELEMENT_NAME, self.name)
+        if self.recordType == recordtypes.recordType_groups:
+            members = SubElementWithData(root, tags.ELEMENT_MEMBERS)
+            for member in self.members:
+                SubElementWithData(members, tags.ELEMENT_MEMBER, member[1], {tags.ATTRIBUTE_RECORDTYPE:member[0]})
+        if self.calendarUserAddresses:
+            for cuaddr in self.calendarUserAddresses:
+                SubElementWithData(root, tags.ELEMENT_CUADDR, cuaddr)
+        if self.recordType in (recordtypes.recordType_resources, recordtypes.recordType_locations,) and self.autoSchedule:
+            SubElementWithData(root, tags.ELEMENT_AUTOSCHEDULE)
+        if self.recordType in (recordtypes.recordType_users, recordtypes.recordType_groups,) and not self.enabledForCalendaring:
+            SubElementWithData(root, tags.ELEMENT_DISABLECALENDAR)
+        if self.recordType in (recordtypes.recordType_resources, recordtypes.recordType_locations,):
+            proxies = SubElementWithData(root, tags.ELEMENT_PROXIES)
+            for proxy in self.proxies:
+                SubElementWithData(proxies, tags.ELEMENT_MEMBER, proxy[1], {tags.ATTRIBUTE_RECORDTYPE:proxy[0]})
+        
+        return root
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountsrecordtypespy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/recordtypes.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/recordtypes.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/recordtypes.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,44 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from admin.xmlaccounts import tags
+
+recordType_users     = &quot;users&quot;
+recordType_groups    = &quot;groups&quot;
+recordType_locations = &quot;locations&quot;
+recordType_resources = &quot;resources&quot;            
+recordType_all       = &quot;all&quot;            
+
+RECORD_TYPES = (
+    recordType_users,
+    recordType_groups,
+    recordType_locations,
+    recordType_resources,            
+)
+
+RECORD_TYPES_TO_TAGS = {
+    recordType_users     : tags.ELEMENT_USER,
+    recordType_groups    : tags.ELEMENT_GROUP,
+    recordType_locations : tags.ELEMENT_LOCATION,
+    recordType_resources : tags.ELEMENT_RESOURCE,            
+}
+
+TAGS_TO_RECORD_TYPES = {
+    tags.ELEMENT_USER     : recordType_users,
+    tags.ELEMENT_GROUP    : recordType_groups,
+    tags.ELEMENT_LOCATION : recordType_locations,
+    tags.ELEMENT_RESOURCE : recordType_resources,            
+}
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountstagspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tags.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tags.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tags.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,36 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+ELEMENT_ACCOUNTS        = &quot;accounts&quot;
+ELEMENT_USER            = &quot;user&quot;
+ELEMENT_GROUP           = &quot;group&quot;
+ELEMENT_LOCATION        = &quot;location&quot;
+ELEMENT_RESOURCE        = &quot;resource&quot;
+
+ELEMENT_UID             = &quot;uid&quot;
+ELEMENT_GUID            = &quot;guid&quot;
+ELEMENT_PASSWORD        = &quot;password&quot;
+ELEMENT_NAME            = &quot;name&quot;
+ELEMENT_MEMBERS         = &quot;members&quot;
+ELEMENT_MEMBER          = &quot;member&quot;
+ELEMENT_CUADDR          = &quot;cuaddr&quot;
+ELEMENT_AUTOSCHEDULE    = &quot;auto-schedule&quot;
+ELEMENT_DISABLECALENDAR = &quot;disable-calendar&quot;
+ELEMENT_PROXIES         = &quot;proxies&quot;
+
+ATTRIBUTE_REALM         = &quot;realm&quot;
+ATTRIBUTE_REPEAT        = &quot;repeat&quot;
+ATTRIBUTE_RECORDTYPE    = &quot;type&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountstests__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountsteststest_directorypy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/test_directory.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/test_directory.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/test_directory.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,80 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from StringIO import StringIO
+from admin.xmlaccounts.directory import XMLDirectory
+from protocol.utils.xmlhelpers import BetterElementTree
+from xml.etree.ElementTree import XML
+
+import unittest
+
+class TestDirectory(unittest.TestCase):
+    
+    def checkXML(self, x):
+        
+        x = x.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+            
+        # Parse the XML data
+        a = XMLDirectory()
+        a.parseXML(XML(x))
+        
+        # Generate the XML data
+        node = a.writeXML()
+        os = StringIO()
+        xmldoc = BetterElementTree(node)
+        xmldoc.writeUTF8(os)
+        
+        # Verify data
+        self.assertEqual(os.getvalue(), x)
+
+    def test_accounts(self):
+        
+        self.checkXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;accounts realm=&quot;Test Realm&quot;&gt;
+  &lt;user&gt;
+    &lt;uid&gt;admin&lt;/uid&gt;
+    &lt;guid&gt;12345&lt;/guid&gt;
+    &lt;password&gt;admin&lt;/password&gt;
+    &lt;name&gt;Super User&lt;/name&gt;
+  &lt;/user&gt;
+  &lt;user&gt;
+    &lt;uid&gt;test&lt;/uid&gt;
+    &lt;guid /&gt;
+    &lt;password&gt;test&lt;/password&gt;
+    &lt;name&gt;Test User&lt;/name&gt;
+    &lt;cuaddr&gt;mailto:testuser@example.com&lt;/cuaddr&gt;
+  &lt;/user&gt;
+  &lt;group&gt;
+    &lt;uid&gt;users&lt;/uid&gt;
+    &lt;guid&gt;123456&lt;/guid&gt;
+    &lt;password&gt;users&lt;/password&gt;
+    &lt;name&gt;Users Group&lt;/name&gt;
+    &lt;members&gt;
+      &lt;member type=&quot;users&quot;&gt;test&lt;/member&gt;
+    &lt;/members&gt;
+  &lt;/group&gt;
+  &lt;location&gt;
+    &lt;uid&gt;mercury&lt;/uid&gt;
+    &lt;guid&gt;1234567&lt;/guid&gt;
+    &lt;password&gt;mercury&lt;/password&gt;
+    &lt;name&gt;Mecury Conference Room, Building 1, 2nd Floor&lt;/name&gt;
+    &lt;auto-schedule /&gt;
+    &lt;proxies&gt;
+      &lt;member type=&quot;users&quot;&gt;test&lt;/member&gt;
+    &lt;/proxies&gt;
+  &lt;/location&gt;
+&lt;/accounts&gt;
+&quot;&quot;&quot;)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountsteststest_recordpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/test_record.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/test_record.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/test_record.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,82 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from StringIO import StringIO
+from admin.xmlaccounts.record import XMLRecord
+from protocol.utils.xmlhelpers import BetterElementTree
+from xml.etree.ElementTree import XML
+
+import unittest
+
+class TestRecord(unittest.TestCase):
+    
+    def checkXML(self, x):
+        
+        x = x.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+            
+        # Parse the XML data
+        a = XMLRecord()
+        a.parseXML(XML(x))
+        
+        # Generate the XML data
+        node = a.writeXML()
+        os = StringIO()
+        xmldoc = BetterElementTree(node)
+        xmldoc.writeUTF8(os)
+        
+        # Verify data
+        self.assertEqual(os.getvalue(), x)
+
+    def test_user(self):
+        
+        self.checkXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;user&gt;
+  &lt;uid&gt;test&lt;/uid&gt;
+  &lt;guid /&gt;
+  &lt;password&gt;test&lt;/password&gt;
+  &lt;name&gt;Test User&lt;/name&gt;
+  &lt;cuaddr&gt;mailto:testuser@example.com&lt;/cuaddr&gt;
+&lt;/user&gt;
+&quot;&quot;&quot;)
+
+    def test_group(self):
+        
+        self.checkXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;group&gt;
+  &lt;uid&gt;users&lt;/uid&gt;
+  &lt;guid&gt;12345&lt;/guid&gt;
+  &lt;password&gt;users&lt;/password&gt;
+  &lt;name&gt;Users Group&lt;/name&gt;
+  &lt;members&gt;
+    &lt;member type=&quot;users&quot;&gt;test&lt;/member&gt;
+  &lt;/members&gt;
+&lt;/group&gt;
+&quot;&quot;&quot;)
+
+    def test_location(self):
+        
+        self.checkXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;location&gt;
+  &lt;uid&gt;mercury&lt;/uid&gt;
+  &lt;guid&gt;12345&lt;/guid&gt;
+  &lt;password&gt;mercury&lt;/password&gt;
+  &lt;name&gt;Mecury Conference Room, Building 1, 2nd Floor&lt;/name&gt;
+  &lt;auto-schedule /&gt;
+  &lt;proxies&gt;
+    &lt;member type=&quot;users&quot;&gt;test&lt;/member&gt;
+  &lt;/proxies&gt;
+&lt;/location&gt;
+&quot;&quot;&quot;)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcadminxmlaccountstestsutilspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/utils.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/utils.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/admin/xmlaccounts/tests/utils.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,41 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from StringIO import StringIO
+from admin.xmlaccounts.record import XMLRecord
+from protocol.utils.xmlhelpers import BetterElementTree
+from xml.etree.ElementTree import XML
+
+import unittest
+
+class TestCommon(unittest.TestCase):
+    
+    def checkXML(self, x):
+        
+        x = x.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+            
+        # Parse the XML data
+        a = XMLRecord()
+        a.parseXML(XML(x))
+        
+        # Generate the XML data
+        node = a.writeXML()
+        os = StringIO()
+        xmldoc = BetterElementTree(node)
+        xmldoc.writeUTF8(os)
+        
+        # Verify data
+        self.assertEqual(os.getvalue(), x)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowser__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowserbaseshellpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/baseshell.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/baseshell.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/baseshell.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,208 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser import utils
+from browser.command import CommandError
+from browser.command import UnknownCommand
+from protocol.url import URL
+from protocol.webdav.definitions import davxml
+import os
+import readline
+import traceback
+
+class BaseShell(object):
+    
+    def __init__(self, history_name):
+        
+        self.prefix = &quot;&quot;
+        self.commands = {}
+        self.history = []
+        self.history_name = history_name
+        self.preserve_history = False
+        self.last_wd_complete = (&quot;&quot;, ())
+        
+        self.readHistory()
+
+    def readHistory(self):
+        try:
+            readline.read_history_file(os.path.expanduser(&quot;~/.%s&quot; % (self.history_name,)))
+        except IOError:
+            pass
+
+    def saveHistory(self):
+        readline.write_history_file(os.path.expanduser(&quot;~/.%s&quot; % (self.history_name,)))
+
+    def registerCommands(self, cmds):
+        raise NotImplementedError
+
+    def registerCommand(self, command):
+        for cmd in command.getCmds():
+            self.commands[cmd] = command
+        command.setShell(self)
+
+    def run(self):
+        
+        # Preserve existing history
+        if self.preserve_history:
+            old_history = [readline.get_history_item(index) for index in xrange(readline.get_current_history_length())]
+            readline.clear_history()
+            map(readline.add_history, self.history)
+
+        readline.set_completer(self.complete)
+        readline.parse_and_bind(&quot;bind ^I rl_complete&quot;)
+
+        while True:
+            cmdline = raw_input(&quot;%s &gt; &quot; % (self.prefix,))
+            self.last_wd_complete = (&quot;&quot;, ())
+            if not cmdline:
+                continue
+
+            # Try to dispatch command
+            try:
+                self.execute(cmdline)
+            except SystemExit, e:
+                print &quot;Exiting shell: %s&quot; % (e.message,)
+                break
+            except UnknownCommand, e:
+                print &quot;Command '%s' unknown.&quot; % (e.message,)
+            except Exception, e:
+                traceback.print_exc()
+
+        # Restore previous history
+        if self.preserve_history:
+            self.saveHistory()
+            readline.clear_history()
+            map(readline.add_history, old_history)
+
+    def execute(self, cmdline):
+        
+        # Check for history recall
+        if cmdline == &quot;!!&quot; and self.history:
+            cmdline = self.history[-1]
+            print cmdline
+            if readline.get_current_history_length():
+                readline.replace_history_item(readline.get_current_history_length() - 1, cmdline)
+        elif cmdline.startswith(&quot;!&quot;):
+            try:
+                index = int(cmdline[1:])
+                if index&gt; 0 and index &lt;= len(self.history):
+                    cmdline = self.history[index-1]
+                    print cmdline
+                    if readline.get_current_history_length():
+                        readline.replace_history_item(readline.get_current_history_length() - 1, cmdline)
+                else:
+                    raise ValueError()
+            except ValueError:
+                print &quot;%s: event not found&quot; % (cmdline,)
+                return
+            
+        # split the command line into command and options
+        splits = cmdline.split(&quot; &quot;, 1)
+        cmd = splits[0]
+        options = splits[1] if len(splits) == 2 else &quot;&quot;
+        
+        # Find matching command
+        try:
+            if cmd not in self.commands:
+                self.history.append(cmdline)
+                raise UnknownCommand(cmd)
+            else:
+                self.commands[cmd].execute(cmd, options)
+        finally:
+            # Store in history
+            self.history.append(cmdline)
+    
+    def help(self, cmd=None):
+        
+        if cmd:
+            if cmd in self.commands:
+                cmds = ((cmd, self.commands[cmd]),)
+                full_help = True
+            else:
+                raise CommandError(&quot;Command could not be found: %s&quot; % (cmd,))
+        else:
+            cmds = self.commands.keys()
+            cmds.sort()
+            cmds = [(cmd, self.commands[cmd]) for cmd in cmds]
+            full_help = False
+        
+        if full_help:
+            if self.commands[cmd].hasHelp(cmd):
+                print self.commands[cmd].help(cmd)
+        else:
+            results = []
+            for name, cmd in cmds:
+                if cmd.hasHelp(name):
+                    results.append(cmd.helpListing(name))
+            utils.printTwoColumnList(results)
+
+    def complete(self, text, state):
+        
+        # If there is no space in the text we complete a command
+        #print &quot;complete: %s %d&quot; % (text, state)
+        results = []
+        check = readline.get_line_buffer()[:readline.get_endidx()].lstrip()
+        checklen = len(check)
+        if &quot; &quot; not in check:
+            for cmd in self.commands:
+                if cmd[:checklen] == check:
+                    results.append(cmd)
+        else:
+            cmd, rest = check.split(&quot; &quot;, 1)
+            if cmd in self.commands:
+                results = self.commands[cmd].complete(rest)
+
+        return results[state]
+
+    def wdcomplete(self, text):
+        
+        #print &quot;\nwdcomplete: %s&quot; % (text,)
+
+        # Look at cache and return that
+        if self.last_wd_complete[0] == text:
+            return self.last_wd_complete[1]
+
+        # Look for relative vs absolute
+        if text[0] == &quot;/&quot;:
+            dirname, _ignore_child = os.path.split(text)
+            path = dirname
+            if not path.endswith(&quot;/&quot;):
+                path += &quot;/&quot;
+            pathlen = 0
+        else:
+            path = self.wd
+            pathlen = len(path) + (0 if path.endswith(&quot;/&quot;) else 1)
+            dirname, _ignore_child = os.path.split(text)
+            if dirname:
+                path = os.path.join(path, dirname)
+            if not path.endswith(&quot;/&quot;):
+                path += &quot;/&quot;
+
+        #print &quot;pdc: %s, %s, %s, %s&quot; % (self.wd, path, dirname, child)
+        resource = URL(url=path)
+
+        props = (davxml.resourcetype,)
+        results = self.account.session.getPropertiesOnHierarchy(resource, props)
+        #print results.keys()
+        results = [result[pathlen:] for result in results.iterkeys() if len(result) &gt; pathlen]
+        #print results
+        if text:
+            textlen = len(text)
+            results = [result for result in results if result[:textlen] == text]
+            #print results
+        
+        self.last_wd_complete = (text, results,)
+        return results
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/command.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/command.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/command.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,62 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+class Command(object):
+    
+    def __init__(self):
+        
+        self.shell = None
+        self.cmds = ()
+        
+    def execute(self, name, options):
+        raise NotImplementedError
+
+    def usage(self, name):
+        raise NotImplementedError
+
+    def hasHelp(self, name):
+        return name in self.cmds
+
+    def help(self, name):
+        result = &quot;Command: %s\n&quot; % (name,)
+        result += &quot;Description: %s\n&quot; % (self.helpDescription(),)
+        result += self.usage(name)
+        return result
+        
+    def helpListing(self, name):
+        return (name, self.helpDescription())
+        
+    def helpDescription(self):
+        return &quot;&quot;
+    
+    def setShell(self, shell):
+        self.shell = shell
+        
+    def getCmds(self):
+        return self.cmds
+
+    def complete(self, text):
+        return ()
+
+class WrongOptions(Exception):
+    pass
+
+class UnknownCommand(Exception):
+    pass
+
+class CommandError(Exception):
+    pass
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommands__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+__all__ = [
+    &quot;acl&quot;,
+    &quot;cat&quot;,
+    &quot;cd&quot;,
+    &quot;help&quot;,
+    &quot;history&quot;,
+    &quot;logging&quot;,
+    &quot;ls&quot;,
+    &quot;principal&quot;,
+    &quot;props&quot;,
+    &quot;proxies&quot;,
+    &quot;quit&quot;,
+    &quot;server&quot;,
+    &quot;whoami&quot;,
+]
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandsaclpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/acl.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/acl.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/acl.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,372 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+from protocol.url import URL
+from protocol.webdav.definitions import davxml
+from browser.subshell import SubShell
+from browser import commands
+from protocol.webdav.ace import ACE
+from browser import utils
+from protocol.caldav.definitions import caldavxml
+from xml.etree.ElementTree import QName
+import readline
+import os
+import getopt
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;acl&quot;,)
+        self.subshell = None
+        
+    def execute(self, name, options):
+        
+        interactive = False
+        path = None
+
+        try:
+            opts, args = getopt.getopt(options.split(), 'i')
+        except getopt.GetoptError, e:
+            print str(e)
+            print self.usage(name)
+            raise WrongOptions
+            
+        for name, _ignore_value in opts:
+            
+            if name == &quot;-i&quot;:
+                interactive = True
+            else:
+                print &quot;Unknown option: %s&quot; % (name,)
+                print self.usage(name)
+                raise WrongOptions
+        
+        if len(args) &gt; 1:
+            print &quot;Wrong number of arguments: %d&quot; % (len(args),)
+            print self.usage(name)
+            raise WrongOptions
+        elif args:
+            path = args[0]
+            if not path.startswith(&quot;/&quot;):
+                path = os.path.join(self.shell.wd, path)
+        else:
+            path = self.shell.wd
+        if not path.endswith(&quot;/&quot;):
+            path += &quot;/&quot;
+        resource = URL(url=path)
+
+        results, bad = self.shell.account.session.getProperties(resource, (davxml.acl,))
+        if davxml.acl in bad:
+            print &quot;Could not retrieve DAV:acl property, status=%d&quot; % (bad[davxml.acl],)
+        else:
+            if interactive:
+                self.doInteractiveMode(resource, results[davxml.acl])
+            else:
+                aces = ACE.parseFromACL(results[davxml.acl])
+                print utils.printACEList(aces, self.shell.account)
+            
+        return True
+
+    def doInteractiveMode(self, resource, acls):
+        
+        print &quot;Entering ACL edit mode on resource: %s&quot; % (resource.relativeURL(),)
+        if not self.subshell:
+            self.subshell = SubShell(self.shell, &quot;ACL&quot;, (
+                commands.help.Cmd(),
+                commands.logging.Cmd(),
+                commands.quit.Cmd(),
+                Add(),
+                Change(),
+                Remove(),
+                List(),
+            ))
+        self.subshell.resource = resource
+        self.subshell.account = self.shell.account
+        self.subshell.run()
+        
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s [OPTIONS] [PATH]
+PATH is a relative or absolute path.
+
+Options:
+-i    interactive mode for adding, changing and deleting ACLs.
+    if not present, existing ACLs will be printed.
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Manage the access privileges of a directory or file.&quot;
+
+class CommonACLCommand(Command):
+    
+    def displayACEList(self):
+        # First list the current set
+        results, bad = self.shell.shell.account.session.getProperties(self.shell.resource, (davxml.acl,))
+        if davxml.acl in bad:
+            print &quot;Could not retrieve DAV:acl property, status=%d&quot; % (bad[davxml.acl],)
+            return None
+        else:
+            aces = ACE.parseFromACL(results[davxml.acl])
+            print utils.printACEList(aces, self.shell.shell.account)
+            return aces
+
+    def createACE(self, oldace=None):
+
+        ace = ACE()
+        print &quot;Principal Type:&quot;
+        print &quot;  1. Principal path&quot;
+        print &quot;  2. All&quot;
+        print &quot;  3. Authenticated&quot;
+        print &quot;  4. Unauthenticated&quot;
+        print &quot;  5. Property&quot;
+        insert = None
+        if oldace:
+            mapper = {
+                str(davxml.href):             &quot;1&quot;,
+                str(davxml.all):              &quot;2&quot;,
+                str(davxml.authenticated):    &quot;3&quot;,
+                str(davxml.unauthenticated):  &quot;4&quot;,
+                str(davxml.property):         &quot;5&quot;,
+            }
+            insert = mapper.get(oldace.principal)
+        choice = utils.numericInput(&quot;Select type: &quot;, 1, 5, insert=insert)
+        if choice == &quot;q&quot;:
+            return None
+        
+        if choice == 1:
+            href = utils.textInput(&quot;Enter principal path: &quot;, insert=oldace.data if oldace else None)
+            principal = self.shell.shell.account.getPrincipal(URL(url=href))
+            ace.principal = str(davxml.href)
+            ace.data = principal.principalURL.relativeURL()
+        elif choice == 2:
+            ace.principal = str(davxml.all)
+        elif choice == 3:
+            ace.principal = str(davxml.authenticated)
+        elif choice == 4:
+            ace.principal = str(davxml.unauthenticated)
+        elif choice == 5:
+            prop = utils.textInput(&quot;Enter property qname: &quot;, insert=str(oldace.data) if oldace else None)
+            ace.principal = str(davxml.property)
+            ace.data = QName(prop)
+        
+        invert = utils.yesNoInput(&quot;Invert principal [y/n]: &quot;, insert=(&quot;y&quot; if oldace.invert else &quot;n&quot;) if oldace else None)
+        ace.invert = (invert == &quot;y&quot;)
+        
+        grant = utils.choiceInput(&quot;Grant or Deny privileges [g/d]: &quot;, (&quot;g&quot;, &quot;d&quot;,), insert=(&quot;g&quot; if oldace.grant else &quot;d&quot;) if oldace else None)
+        ace.grant = (grant == &quot;g&quot;)
+        
+        print &quot;Privileges:&quot;
+        print &quot;  a. {DAV}read&quot;
+        print &quot;  b. {DAV}write&quot;
+        print &quot;  c. {DAV}write-properties&quot;
+        print &quot;  d. {DAV}write-content&quot;
+        print &quot;  e. {DAV}read-acl&quot;
+        print &quot;  f. {DAV}read-current-user-privilege-set&quot;
+        print &quot;  g. {DAV}write-acl&quot;
+        print &quot;  h. {DAV}bind&quot;
+        print &quot;  i. {DAV}unbind&quot;
+        print &quot;  j. {DAV}all&quot;
+        print &quot;  k. {CALDAV}read-free-busy&quot;
+        print &quot;  l. {CALDAV}schedule&quot;
+        print &quot;  q. quit without changes&quot;
+        choice = utils.multiChoiceInput(
+                     &quot;Select multiple items: &quot;,
+                     [char for char in &quot;abcdefghijklq&quot;],
+                 )
+        if &quot;q&quot; in choice:
+            return None
+        
+        mappedPrivs = {
+            'a': davxml.read,
+            'b': davxml.write,
+            'c': davxml.write_properties,
+            'd': davxml.write_content,
+            'e': davxml.read_acl,
+            'f': davxml.read_current_user_privilege_set,
+            'g': davxml.write_acl,
+            'h': davxml.bind,
+            'i': davxml.unbind,
+            'j': davxml.all,
+            'k': caldavxml.read_free_busy,
+            'l': caldavxml.schedule,
+        }
+        ace.privs = ()
+        for char in choice:
+            ace.privs += (mappedPrivs[char],)
+        
+        return ace
+        
+class Add(CommonACLCommand):
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;add&quot;,)
+    
+    def execute(self, name, options):
+        
+        # First list the current set
+        aces = self.displayACEList()
+        if aces:
+            # Ask user which one to delete
+            while True:
+                result = raw_input(&quot;Add ACL before [1 - %d] or cancel [q]: &quot; % (len(aces) + 1,))
+                if readline.get_current_history_length():
+                    readline.remove_history_item(readline.get_current_history_length() - 1)
+                if not result:
+                    continue
+                if result[0] == &quot;q&quot;:
+                    break
+                try:
+                    number = int(result)
+                    if number &gt; len(aces):
+                        number = len(aces)
+                except ValueError:
+                    print &quot;Invalid input, try again.&quot;
+                    continue
+                
+                # Try and get the new ace
+                ace = self.createACE()
+                if not ace:
+                    break
+                aces.insert(number, ace)
+
+                # Now remove those that cannot be edited
+                aces = [ace for ace in aces if ace.canChange()]
+                
+                # Now execute
+                self.shell.shell.account.session.setACL(self.shell.resource, aces)
+                break
+                
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Add ACL to existing resource.&quot;
+
+class Change(CommonACLCommand):
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;change&quot;,)
+    
+    def execute(self, name, options):
+
+        # First list the current set
+        aces = self.displayACEList()
+        if aces:
+            # Ask user which one to delete
+            while True:
+                result = raw_input(&quot;Change ACL at [1 - %d] or cancel [q]: &quot; % (len(aces),))
+                if readline.get_current_history_length():
+                    readline.remove_history_item(readline.get_current_history_length() - 1)
+                if not result:
+                    continue
+                if result[0] == &quot;q&quot;:
+                    break
+                try:
+                    number = int(result)
+                except ValueError:
+                    print &quot;Invalid input, try again.&quot;
+                    continue
+                
+                # Check that the targeted ace is editable
+                if not aces[number - 1].canChange():
+                    print &quot;You cannot change a protected or inherited ace.&quot;
+                    break
+                
+                # Try and get the new ace
+                ace = self.createACE(oldace=aces[number - 1])
+                if not ace:
+                    break
+                aces[number - 1] = ace
+
+                # Now remove those that cannot be edited
+                aces = [ace for ace in aces if ace.canChange()]
+                
+                # Now execute
+                self.shell.shell.account.session.setACL(self.shell.resource, aces)
+                break
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Change ACL on existing resource.&quot;
+
+class Remove(CommonACLCommand):
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;remove&quot;,)
+    
+    def execute(self, name, options):
+        
+        # First list the current set
+        aces = self.displayACEList()
+        if aces:
+            # Ask user which one to delete
+            while True:
+                result = raw_input(&quot;Remove ACL [1 - %d] or cancel [q]: &quot; % (len(aces),))
+                if readline.get_current_history_length():
+                    readline.remove_history_item(readline.get_current_history_length() - 1)
+                if not result:
+                    continue
+                if result[0] == &quot;q&quot;:
+                    break
+                try:
+                    number = int(result)
+                except ValueError:
+                    print &quot;Invalid input, try again.&quot;
+                    continue
+                
+                # Check that the targeted ace is editable
+                if not aces[number-1].canChange():
+                    print &quot;You cannot remove a protected or inherited ace.&quot;
+                    break
+                
+                # Remove the one we are removing
+                del aces[number-1]
+                
+                # Now remove those that cannot be edited
+                aces = [ace for ace in aces if ace.canChange()]
+                
+                # Now execute
+                self.shell.shell.account.session.setACL(self.shell.resource, aces)
+                break
+                
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Remove ACL on existing resource.&quot;
+
+class List(CommonACLCommand):
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;list&quot;,)
+    
+    def execute(self, name, options):
+        
+        self.displayACEList()
+        return True
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;List current ACLs on existing resource.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandscatpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/cat.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/cat.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/cat.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,55 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+from protocol.url import URL
+import os
+import getopt
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;cat&quot;,)
+        
+    def execute(self, name, options):
+        opts, args = getopt.getopt(options.split(), '')
+        if len(opts) or len(args) != 1:
+            print self.usage(name)
+            raise WrongOptions()
+
+        path = args[0]
+
+        if not path.startswith(&quot;/&quot;):
+            path = os.path.join(self.shell.wd, path)
+        resource = URL(url=path)
+
+        data, _ignore_etag = self.shell.account.session.readData(resource)
+        print data
+
+        return True
+
+    def complete(self, text):
+        return self.shell.wdcomplete(text)
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s PATH
+PATH is a relative or absolute path.
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Display contents of a file or directory.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandscdpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/cd.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/cd.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/cd.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,61 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+import os
+import getopt
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;cd&quot;,)
+        
+    def execute(self, name, options):
+        opts, args = getopt.getopt(options.split(), '')
+        if len(opts) or len(args) != 1:
+            print self.usage(name)
+            raise WrongOptions()
+
+        newpath = args[0]
+        oldpath = self.shell.wd
+        result = True
+
+        if newpath == &quot;..&quot;:
+            result = self.shell.setWD(os.path.dirname(oldpath))
+        elif newpath == &quot;.&quot;:
+            pass
+        elif newpath.startswith(&quot;/&quot;):
+            result = self.shell.setWD(newpath)
+        else:
+            result = self.shell.setWD(os.path.normpath(os.path.join(oldpath, newpath)))
+
+        if not result:
+            print &quot;%s: %s No such directory&quot; % (name, options,)
+            
+        return result
+
+    def complete(self, text):
+        return self.shell.wdcomplete(text)
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s PATH
+PATH is a relative or absolute path.
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Change working directory.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandshelppy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/help.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/help.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/help.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,44 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+import getopt
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;help&quot;, &quot;?&quot;,)
+        
+    def execute(self, name, options):
+        opts, args = getopt.getopt(options.split(), '')
+        if len(opts) or len(args) &gt; 1:
+            print self.usage(name)
+            raise WrongOptions()
+        self.shell.help(cmd = (None if len(args) == 0 else args[0]))
+        return True
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s [CMD]
+CMD is the name of a command.
+&quot;&quot;&quot; % (name,)
+
+    def hasHelp(self, name):
+        return name in (&quot;help&quot;,)
+
+    def helpDescription(self):
+        return &quot;Displays help about a command.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandshistorypy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/history.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/history.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/history.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,41 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;history&quot;, )
+        
+    def execute(self, name, options):
+        if options:
+            print self.usage(name)
+            raise WrongOptions()
+        
+        format = &quot;%%0%ds %%s&quot; % (len(self.shell.history),)
+        for ctr, cmd in enumerate(self.shell.history):
+            print format % (ctr+1, cmd,)
+        return True
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Displays the history of all commands used in this session.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandsloggingpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/logging.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/logging.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/logging.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,56 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+import getopt
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;logging&quot;, )
+        
+    def execute(self, name, options):
+        opts, args = getopt.getopt(options.split(), '')
+        if len(opts) or len(args) &gt; 1:
+            print self.usage(name)
+            raise WrongOptions()
+        if args and args[0] not in (&quot;on&quot;, &quot;off&quot;,):
+            print self.usage(name)
+            raise WrongOptions()
+        
+        if args:
+            state = args[0]
+        else:
+            state = (&quot;off&quot; if self.shell.account.session.loghttp else &quot;on&quot;)
+        if state == &quot;on&quot;:
+            self.shell.account.session.loghttp = True
+            print &quot;HTTP logging turned on&quot;
+        else:
+            self.shell.account.session.loghttp = False
+            print &quot;HTTP logging turned off&quot;
+        return True
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s [on|off]
+on  - turn HTTP protocol logging on
+off - turn HTTP protocol logging off
+without either argument - toggle the state of logging
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Changes the current state of HTTP logging.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandslspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/ls.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/ls.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/ls.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,93 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+from protocol.webdav.definitions import davxml
+from protocol.url import URL
+import os
+import getopt
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;ls&quot;,)
+        
+    def execute(self, name, options):
+        
+        longlist = False
+        path = None
+
+        opts, args = getopt.getopt(options.split(), 'l')
+
+        for name, _ignore_value in opts:
+            
+            if name == &quot;-l&quot;:
+                longlist = True
+            else:
+                print &quot;Unknown option: %s&quot; % (name,)
+                print self.usage(name)
+                raise WrongOptions
+        
+        if len(args) &gt; 1:
+            print &quot;Wrong number of arguments: %d&quot; % (len(args),)
+            print self.usage(name)
+            raise WrongOptions
+        elif args:
+            path = args[0]
+            if not path.startswith(&quot;/&quot;):
+                path = os.path.join(self.shell.wd, path)
+        else:
+            path = self.shell.wd
+        if not path.endswith(&quot;/&quot;):
+            path += &quot;/&quot;
+        resource = URL(url=path)
+
+        props = (davxml.resourcetype,)
+        if longlist:
+            props += (davxml.getcontentlength, davxml.getlastmodified,)
+        results = self.shell.account.session.getPropertiesOnHierarchy(resource, props)
+        items = results.keys()
+        items.sort()
+        for rurl in items:
+            if rurl == path:
+                continue
+            if longlist:
+                props = results[rurl]
+                size = props.get(davxml.getcontentlength, &quot;-&quot;)
+                if not size:
+                    size = &quot;0&quot;
+                modtime = props.get(davxml.getlastmodified, &quot;-&quot;)
+                print &quot;% 8s %s %s&quot; % (size, modtime, rurl[len(path):])
+            else:
+                print rurl[len(path):]
+            
+        return True
+
+    def complete(self, text):
+        return self.shell.wdcomplete(text)
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s [OPTIONS] [PATH]
+PATH is a relative or absolute path.
+
+Options:
+-l   long listing
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;List the contents of a directory.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandsprincipalpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/principal.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/principal.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/principal.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,100 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+from protocol.url import URL
+from browser import utils
+import getopt
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;principal&quot;, )
+        
+    def execute(self, name, options):
+        refresh = False
+        resolve = True
+        principal = None
+        print_proxies = False
+
+        opts, args = getopt.getopt(options.split(), 'fnp:x')
+
+        for name, value in opts:
+            
+            if name == &quot;-f&quot;:
+                refresh = True
+            elif name == &quot;-n&quot;:
+                resolve = False
+            elif name == &quot;-p&quot;:
+                principal = self.shell.account.getPrincipal(URL(url=value), refresh=refresh)
+            elif name == &quot;-x&quot;:
+                print_proxies = True
+            else:
+                print &quot;Unknown option: %s&quot; % (name,)
+                print self.usage(name)
+                raise WrongOptions
+
+        if len(args) &gt; 0:
+            print &quot;Wrong number of arguments: %d&quot; % (len(args),)
+            print self.usage(name)
+            raise WrongOptions
+
+        if not principal:
+            principal = self.shell.account.getPrincipal(refresh=refresh)
+
+        print &quot;&quot;&quot;
+    Principal Path    : %s
+    Display Name      : %s
+    Principal URL     : %s
+    Alternate URLs    : %s
+    Group Members     : %s
+    Memberships       : %s
+    Calendar Homes    : %s
+    Outbox URL        : %s
+    Inbox URL         : %s
+    Calendar Addresses: %s
+&quot;&quot;&quot; % (
+          principal.principalPath,
+          principal.getSmartDisplayName(),
+          principal.principalURL,
+          utils.printList(principal.alternateURIs),
+          utils.printPrincipalPaths(self.shell.account, principal.memberset, resolve, refresh),
+          utils.printPrincipalPaths(self.shell.account, principal.memberships, resolve, refresh),
+          utils.printList(principal.homeset),
+          principal.outboxURL,
+          principal.inboxURL,
+          utils.printList(principal.cuaddrs),
+      ),
+
+        if print_proxies:
+            utils.printProxyPrincipals(self.shell.account, principal, True, True, resolve, False, refresh)
+
+        return True
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s [OPTIONS]
+Options:
+    -f force reload of all cached principals to be returned
+    -p principal path to request proxies for [OPTIONAL]
+        if not present, the current user's principal is used.
+    -n do not resolve references to other principals.
+    -x print proxy details as well.
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Displays the current server login id.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandspropspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/props.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/props.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/props.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,95 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+from protocol.url import URL
+from browser import utils
+import os
+import getopt
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;props&quot;,)
+        
+    def execute(self, name, options):
+        
+        names = False
+        all = False
+        path = None
+
+        opts, args = getopt.getopt(options.split(), 'an')
+
+        for name, _ignore_value in opts:
+            
+            if name == &quot;-a&quot;:
+                all = True
+            elif name == &quot;-n&quot;:
+                names = True
+            else:
+                print &quot;Unknown option: %s&quot; % (name,)
+                print self.usage(name)
+                raise WrongOptions
+        
+        if len(args) &gt; 1:
+            print &quot;Wrong number of arguments: %d&quot; % (len(args),)
+            print self.usage(name)
+            raise WrongOptions
+        elif args:
+            path = args[0]
+            if not path.startswith(&quot;/&quot;):
+                path = os.path.join(self.shell.wd, path)
+        else:
+            path = self.shell.wd
+        if not path.endswith(&quot;/&quot;):
+            path += &quot;/&quot;
+        resource = URL(url=path)
+
+        if names:
+            results = self.shell.account.session.getPropertyNames(resource)
+            print &quot;    Properties: %s&quot; % (utils.printList(results),)
+        else:
+            if all:
+                props = None
+            else:
+                props = self.shell.account.session.getPropertyNames(resource)
+            results, bad = self.shell.account.session.getProperties(resource, props)
+            print &quot;OK Properties:&quot;
+            utils.printProperties(results)
+            if bad:
+                print &quot;Failed Properties:&quot;
+                utils.printProperties(bad)
+            
+        return True
+
+    def complete(self, text):
+        return self.shell.wdcomplete(text)
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s [OPTIONS] [PATH]
+PATH is a relative or absolute path.
+
+Options:
+-n    list property names only
+-a    list all properties via allprop
+    if neither of the above are set then property names are first listed, and then values of those looked up.
+    only one of -n and -a can be set.
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;List the properties of a directory or file.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandsproxiespy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/proxies.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/proxies.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/proxies.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,245 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+from protocol.url import URL
+from browser import utils
+from browser.subshell import SubShell
+from browser import commands
+import getopt
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;proxies&quot;, )
+        self.subshell = None
+        
+    def execute(self, name, options):
+
+        interactive = False
+        read = False
+        write = False
+        principal = self.shell.account.getPrincipal()
+
+        try:
+            opts, args = getopt.getopt(options.split(), 'irwp:')
+        except getopt.GetoptError, e:
+            print str(e)
+            print self.usage(name)
+            raise WrongOptions
+
+        for name, value in opts:
+            
+            if name == &quot;-i&quot;:
+                interactive = True
+            elif name == &quot;-r&quot;:
+                read = True
+            elif name == &quot;-w&quot;:
+                write = True
+            elif name == &quot;-p&quot;:
+                principal = self.shell.account.getPrincipal(URL(url=value))
+            else:
+                print &quot;Unknown option: %s&quot; % (name,)
+                print self.usage(name)
+                raise WrongOptions
+        
+        if len(args) &gt; 0:
+            print &quot;Wrong number of arguments: %d&quot; % (len(args),)
+            print self.usage(name)
+            raise WrongOptions
+
+        if interactive:
+            self.doInteractiveMode(principal)
+        else:
+            utils.printProxyPrincipals(self.shell.account, principal, read or not write, write or not read, True)
+
+        return True
+
+    def doInteractiveMode(self, principal):
+        
+        print &quot;Entering Proxy edit mode on principal: %s (%s)&quot; % (principal.getSmartDisplayName(), principal.principalURL)
+        if not self.subshell:
+            self.subshell = SubShell(self.shell, &quot;Proxy&quot;, (
+                commands.help.Cmd(),
+                commands.logging.Cmd(),
+                commands.quit.Cmd(),
+                Add(),
+                Remove(),
+                List(),
+            ))
+        self.subshell.principal = principal
+        self.subshell.account = self.shell.account
+        self.subshell.run()
+        
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s [OPTIONS]
+PRINCIPAL - principal path to request proxies for.

+Options:
+    -i interactive mode for adding, changing and deleting proxies.
+    -r read proxies [OPTIONAL]
+    -w write proxies [OPTIONAL]
+        if neither is present, both are displayed.
+        
+    -p principal path to request proxies for [OPTIONAL]
+        if not present, the current user's principal is used.
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Displays the delegates for the chosen user.&quot;
+
+class CommonProxiesCommand(Command):
+
+    def parseOptions(self, name, options):
+        read = False
+        write = False
+
+        try:
+            opts, args = getopt.getopt(options.split(), 'rw')
+        except getopt.GetoptError, e:
+            print str(e)
+            print self.usage(name)
+            raise WrongOptions
+
+        for name, _ignore_value in opts:
+            
+            if name == &quot;-r&quot;:
+                read = True
+                if write:
+                    print &quot;Only one of -r and -w may be specified.&quot;
+                    print self.usage(name)
+                    raise WrongOptions
+            elif name == &quot;-w&quot;:
+                write = True
+                if read:
+                    print &quot;Only one of -r and -w may be specified.&quot;
+                    print self.usage(name)
+                    raise WrongOptions
+            else:
+                print &quot;Unknown option: %s&quot; % (name,)
+                print self.usage(name)
+                raise WrongOptions
+        
+        if not read and not write:
+            print &quot;One of -r and -w must be specified.&quot;
+            print self.usage(name)
+            raise WrongOptions
+
+        if len(args) &gt; 0:
+            print &quot;Wrong number of arguments: %d&quot; % (len(args),)
+            print self.usage(name)
+            raise WrongOptions
+        
+        return read
+
+    def printProxyList(self, read):
+        
+        if read:
+            principals = self.shell.principal.getReadProxies()
+        else:
+            principals = self.shell.principal.getWriteProxies()
+        if principals:
+            print utils.printPrincipalList(principals)
+        else:
+            print &quot;There are no proxies of the specified type.&quot;
+        return principals
+
+class Add(CommonProxiesCommand):
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;add&quot;,)
+    
+    def execute(self, name, options):
+
+        read = self.parseOptions(name, options)
+
+        principals = self.printProxyList(read)
+
+        choice = utils.textInput(&quot;New principal [q - quit]: &quot;)
+        if choice == &quot;q&quot;:
+            return None
+        principal = self.shell.account.getPrincipal(URL(url=choice))
+        if principal:
+            principals.append(principal)
+            principals = [principal.principalURL for principal in principals]
+            if read:
+                self.shell.principal.setReadProxies(principals)
+            else:
+                self.shell.principal.setWriteProxies(principals)
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s [OPTIONS]
+    
+Options:
+    -r add to read-only proxy list
+    -w add to read-write proxy list
+        one of -r or -w must be present.
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Add proxies on principal.&quot;
+        
+class Remove(CommonProxiesCommand):
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;remove&quot;,)
+    
+    def execute(self, name, options):
+
+        read = self.parseOptions(name, options)
+
+        principals = self.printProxyList(read)
+
+        choice = utils.numericInput(&quot;Select principal: &quot;, 1, len(principals), allow_q=True)
+        if choice == &quot;q&quot;:
+            return None
+        del principals[choice-1]
+        principals = [principal.principalURL for principal in principals]
+        if read:
+            self.shell.principal.setReadProxies(principals)
+        else:
+            self.shell.principal.setWriteProxies(principals)
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s [OPTIONS]
+    
+Options:
+    -r remove from read-only proxy list
+    -w remove from read-write proxy list
+        one of -r or -w must be present.
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Remove proxies on principal.&quot;
+        
+class List(CommonProxiesCommand):
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;list&quot;,)
+    
+    def execute(self, name, options):
+        
+        utils.printProxyPrincipals(self.shell.account, self.shell.principal, True, True, True, True)
+        return True
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;List current ACLs on existing resource.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandsquitpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/quit.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/quit.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/quit.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;quit&quot;, &quot;exit&quot;,)
+        
+    def execute(self, name, options):
+        if options:
+            print self.usage(name)
+            raise WrongOptions()
+        raise SystemExit(&quot;quitting&quot;)
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Terminates this session.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandsserverpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/server.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/server.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/server.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,38 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;server&quot;, )
+        
+    def execute(self, name, options):
+        if options:
+            print self.usage(name)
+            raise WrongOptions()
+        print self.shell.server
+        return True
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Displays the current server.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsercommandswhoamipy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/commands/whoami.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/commands/whoami.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/commands/whoami.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,38 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.command import WrongOptions
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = (&quot;whoami&quot;, )
+        
+    def execute(self, name, options):
+        if options:
+            print self.usage(name)
+            raise WrongOptions()
+        print self.shell.user
+        return True
+
+    def usage(self, name):
+        return &quot;&quot;&quot;Usage: %s
+&quot;&quot;&quot; % (name,)
+
+    def helpDescription(self):
+        return &quot;Displays the current server login id.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsershellpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/shell.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/shell.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/shell.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,110 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.baseshell import BaseShell
+from browser.command import Command
+from client.account import CalDAVAccount
+from getpass import getpass
+from protocol.url import URL
+import browser.commands
+import atexit
+import getopt
+import sys
+
+class Shell(BaseShell):
+    
+    def __init__(self, server, user, pswd, logging):
+        
+        super(Shell, self).__init__(&quot;caldav_client&quot;)
+        self.prefix = self.wd = &quot;/&quot;
+        self.server = server
+        self.user = user
+        self.pswd = pswd
+    
+        self.registerCommands()
+        
+        # Create the account
+        ssl = server.startswith(&quot;https://&quot;)
+        server = server[8:] if ssl else server[7:]
+        paths = &quot;/principals/users/%s/&quot; % (self.user,)
+        self.account = CalDAVAccount(server, ssl=ssl, user=self.user, pswd=self.pswd, root=paths, principal=paths, logging=logging)
+        
+        atexit.register(self.saveHistory)
+
+    def registerCommands(self):
+        module = browser.commands
+        for item in module.__all__:
+            mod = __import__(&quot;browser.commands.&quot; + item, globals(), locals(), [&quot;Cmd&quot;,])
+            cmd_class = mod.Cmd
+            if type(cmd_class) is type and issubclass(cmd_class, Command):
+                self.registerCommand(cmd_class())
+
+    def setWD(self, newwd):
+        
+        # Check that the new one exists
+        resource = (newwd if newwd.endswith(&quot;/&quot;) else newwd + &quot;/&quot;)
+        if not self.account.session.testResource(URL(url=resource)):
+            return False
+        self.prefix = self.wd = newwd
+        return True
+
+def usage():
+    return &quot;&quot;&quot;Usage: shell [OPTIONS]
+
+Options:
+-l              start with HTTP logging on.
+--server=HOST   url of the server include http/https scheme and port [REQUIRED].
+--user=USER     user name to login as - will be prompted if not prsent [OPTIONAL].
+--pswd=PSWD     password for user - will be prompted if not prsent [OPTIONAL].
+&quot;&quot;&quot;
+
+def runit():
+    logging = False
+    server = None
+    user = None
+    pswd = None
+
+    opts, _ignore_args = getopt.getopt(sys.argv[1:], 'lh', [&quot;help&quot;, &quot;server=&quot;, &quot;user=&quot;, &quot;pswd=&quot;])
+    
+    for name, value in opts:
+        
+        if name == &quot;-l&quot;:
+            logging = True
+        elif name == &quot;--server&quot;:
+            server = value
+        elif name == &quot;--user&quot;:
+            user = value
+        elif name == &quot;--pswd&quot;:
+            pswd = value
+        else:
+            print usage()
+            raise SystemExit()
+
+    if not server or not (server.startswith(&quot;http://&quot;) or server.startswith(&quot;https://&quot;)):
+        print usage()
+        raise SystemExit()
+    
+    if not user:
+        user = raw_input(&quot;User: &quot;)
+    if not pswd:
+        pswd = getpass(&quot;Password: &quot;)
+
+    shell = Shell(server, user, pswd, logging)
+    shell.run()
+
+if __name__ == '__main__':
+
+    runit()
</ins><span class="cx">Property changes on: CalDAVClientLibrary/trunk/src/browser/shell.py
</span><span class="cx">___________________________________________________________________
</span><span class="cx">Name: svn:executable
</span><span class="cx">   + *
</span></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowsersubshellpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/subshell.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/subshell.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/subshell.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,34 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from browser.command import Command
+from browser.baseshell import BaseShell
+
+class SubShell(BaseShell):
+    
+    def __init__(self, shell, prefix, cmds):
+        
+        super(SubShell, self).__init__(&quot;caldav_client.%s&quot; % (prefix,))
+        self.shell = shell
+        self.prefix = prefix
+        self.preserve_history = True
+        self.registerCommands(cmds)
+
+    def registerCommands(self, cmds):
+        for cmd in cmds:
+            if isinstance(cmd, Command):
+                self.registerCommand(cmd)
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcbrowserutilspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/browser/utils.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/browser/utils.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/browser/utils.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,188 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.url import URL
+from protocol.webdav.definitions import davxml
+import readline
+import types
+
+
+def printPrincipalPaths(account, principals, resolve, refresh):
+
+    result = &quot;&quot;
+    if resolve:
+        results = [account.getPrincipal(item, refresh=refresh) for item in principals]
+        results.sort(key=lambda x: x.getSmartDisplayName())
+        strlen = reduce(lambda x,y: max(x, len(y.getSmartDisplayName()) + 1), results, 0)
+        results = [&quot;%- *s (%s)&quot; % (strlen, principal.getSmartDisplayName(), principal.principalURL) for principal in results]
+    else:
+        results = [item.relativeURL() for item in principals]
+        results.sort()
+
+    if len(results) == 1:
+        return results[0]
+    else:
+        for item in results:
+            result += &quot;\n        %s&quot; % (item,)
+    
+    return result
+
+def printPrincipals(account, principals, resolve, refresh):
+
+    result = &quot;&quot;
+    if resolve:
+        results = list(principals)
+        if refresh:
+            for principal in results:
+                principal.loadDetails(True)
+        results.sort(key=lambda x: x.getSmartDisplayName())
+        strlen = reduce(lambda x,y: max(x, len(y.getSmartDisplayName()) + 1), results, 0)
+        results = [&quot;%- *s (%s)&quot; % (strlen, principal.getSmartDisplayName(), principal.principalURL) for principal in results]
+    else:
+        results = [item.principalURL for item in principals]
+        results.sort()
+
+    if len(results) == 1:
+        return results[0]
+    else:
+        for item in results:
+            result += &quot;\n        %s&quot; % (item,)
+    
+    return result
+
+def printProxyPrincipals(account, principal, read=True, write=True, resolve=True, refresh_main=False, refresh=False):
+    if read:
+        print &quot;    Read-Only Proxies: %s&quot; % printPrincipals(account, principal.getReadProxies(refresh_main), resolve, refresh)
+        refresh_main = False
+    if write:
+        print &quot;    Read-Write Proxies: %s&quot; % printPrincipals(account, principal.getWriteProxies(refresh_main), resolve, refresh)
+
+def printProperties(items):
+    sorted = items.keys()
+    sorted.sort()
+    for key in sorted:
+        value = items[key]
+        if type(value) in (types.StringType, types.UnicodeType, types.IntType):
+            print &quot;    %s: %s&quot; % (key, value,)
+        elif type(value) in (types.ListType, types.TupleType,):
+            print &quot;    %s: %s&quot; % (key, printList(value),)
+        else:
+            print &quot;    %s: %s&quot; % (key, value,)
+
+def printList(items):
+    result = &quot;&quot;
+    if len(items) == 1:
+        return items[0]
+    else:
+        sorted = list(items)
+        sorted.sort()
+        for item in sorted:
+            result += &quot;\n        %s&quot; % (item,)
+        return result
+
+def printTwoColumnList(items, indent=0):
+    
+    strlen = reduce(lambda x,y: max(x, len(y[0]) + 1), items, 0)
+    sorted = list(items)
+    sorted.sort(key=lambda x: x[0])
+    for col1, col2 in sorted:
+        print &quot;%s%- *s - %s&quot; % (&quot; &quot;*indent, strlen, col1, col2,)
+
+def printPrincipalList(principals):
+    
+    result = &quot;&quot;
+    for ctr, principal in enumerate(principals):
+        result += &quot;\n% 2d. %s (%s)&quot; % (ctr+1, principal.getSmartDisplayName(), principal.principalURL)
+    return result
+
+def printACEList(aces, account):
+    
+    result = &quot;&quot;
+    for ctr, ace in enumerate(aces):
+        result += &quot;\n% 2d. %s&quot; % (ctr+1, printACE(ace, account))
+    return result
+
+def printACE(ace, account):
+    
+    principalText = ace.principal
+    if principalText == str(davxml.href):
+        principal = account.getPrincipal(URL(url=ace.data))
+        principalText = &quot;%s (%s)&quot; % (principal.getSmartDisplayName(), principal.principalURL)
+    elif principalText == str(davxml.all):
+        principalText = &quot;ALL&quot;
+    elif principalText == str(davxml.authenticated):
+        principalText = &quot;AUTHENTICATED&quot;
+    elif principalText == str(davxml.unauthenticated):
+        principalText = &quot;UNAUTHENTICATED&quot;
+    elif principalText == str(davxml.property):
+        principalText = &quot;PROPERTY: %s&quot; % (ace.data,)
+    result = &quot;Principal: %s\n&quot; % (principalText,)
+    if ace.invert or ace.protected or ace.inherited:
+        result += &quot;    Status:&quot;
+        if ace.invert:
+            result += &quot; INVERTED&quot;
+        if ace.protected:
+            result += &quot; PROTECTED&quot;
+        if ace.inherited:
+            result += &quot; INHERITED&quot;
+        result += &quot;\n&quot;
+    result += &quot;    Grant: &quot; if ace.grant else &quot;    Deny: &quot;
+    result += &quot;, &quot;.join(ace.privs)
+    result += &quot;\n&quot;
+    return result
+
+def textInput(title, insert=None):
+    if insert:
+        title = &quot;%s [%s]:&quot; % (title, insert,)
+    result = raw_input(title)
+    if readline.get_current_history_length():
+        readline.remove_history_item(readline.get_current_history_length() - 1)
+    if not result:
+        result = insert
+    return result
+    
+def numericInput(title, low, high, allow_q = False, insert=None):
+    if allow_q:
+        result = choiceInput(title, [str(num) for num in range(low, high+1)] + [&quot;q&quot;,], insert)
+        if result != &quot;q&quot;:
+            result = int(result)
+        return result
+    else:
+        return int(choiceInput(title, [str(num) for num in range(low, high+1)], insert))
+    
+def yesNoInput(title, insert=None):
+    return choiceInput(title, (&quot;y&quot;, &quot;n&quot;), insert)
+    
+def choiceInput(title, choices, insert=None):
+    while True:
+        result = textInput(title, insert)
+        if not result:
+            continue
+        if result in choices:
+            return result
+        print &quot;Invalid input. Try again.&quot;
+    
+def multiChoiceInput(title, choices, insert=None):
+    while True:
+        result = textInput(title, insert)
+        if not result:
+            continue
+        for char in result:
+            if char not in choices:
+                break
+        else:
+            return result
+        print &quot;Invalid input. Try again.&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcclient__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/client/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/client/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/client/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcclientaccountpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/client/account.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/client/account.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/client/account.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from client.clientsession import CalDAVSession
+from client.principal import principalCache
+
+class CalDAVAccount(object):
+    
+    def __init__(self, server, port=None, ssl=False, user=&quot;&quot;, pswd=&quot;&quot;, principal=None, root=None, logging=False):
+        self.session = CalDAVSession(server, port, ssl, user, pswd, principal, root, logging)
+        self.principal = principalCache.getPrincipal(self.session, self.session.principalPath)
+    
+    def getPrincipal(self, path=None, refresh=False):
+        if path:
+            return principalCache.getPrincipal(self.session, path, refresh=refresh)
+        elif refresh:
+            self.principal = principalCache.getPrincipal(self.session, self.session.principalPath, refresh=refresh)
+
+        return self.principal
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcclientcalendarpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/client/calendar.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/client/calendar.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/client/calendar.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,67 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.definitions import davxml
+from protocol.url import URL
+from protocol.caldav.definitions import caldavxml
+
+class Calendar(object):
+    
+    def __init__(self, path=None, session=None):
+        self.path = path
+        if not path.endswith(&quot;/&quot;):
+            self.path += &quot;/&quot;
+        self.session = session
+        self.displayname = None
+        self.description = None
+        self.timezone = None
+
+    def __str__(self):
+        return &quot;Calendar: %s&quot; % (self.path,)
+
+    def __repr__(self):
+        return &quot;Calendar: %s&quot; % (self.path,)
+
+    def exists(self):
+        return self.session.testResource(URL(url=self.path))
+
+    def readCalendar(self):
+        pass
+    def writeCalendar(self, calendar):
+        pass
+
+    def readComponent(self, name=None, uid=None):
+        pass
+    def writeComponent(self, component, name=None):
+        pass
+
+    def getDisplayName(self):
+        if self.displayname is None and self.session:
+            self._getProperties()
+        return self.displayname
+
+    def getDescription(self):
+        if self.description is None and self.session:
+            self._getProperties()
+        return self.description
+    
+    def _getProperties(self):
+        assert(self.session is not None)
+        
+        results, _ignore_bad = self.session.getProperties(URL(url=self.path), (davxml.displayname, caldavxml.calendar_description, caldavxml.calendar_timezone,))
+        self.displayname = results.get(davxml.displayname, &quot;&quot;)
+        self.description = results.get(caldavxml.calendar_description, &quot;&quot;)
+        self.timezone = results.get(caldavxml.calendar_timezone, None)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcclientcalendaruseraddresspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/client/calendaruseraddress.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/client/calendaruseraddress.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/client/calendaruseraddress.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,45 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+
+class CalendarUserAddress(object):
+    
+    def __init__(self, cuaddr=None, name=None, attendee=None):
+        self.cuaddr = cuaddr
+        self.name = name
+        if attendee:
+            self.setAttendee(attendee)
+
+    def getCUAddr(self):
+        return self.cuaddr
+
+    def setCUAddr(self, value):
+        self.cuaddr = value
+
+    def getName(self):
+        return self.name
+
+    def setCn(self, value):
+        self.name = value
+        
+    def getFullText(self):
+        return (&quot;%s &lt;%s&gt;&quot; % (self.name, self.cuaddr,)) if self.name else (&quot;&lt;%s&gt;&quot; % (self.cuaddr,))
+    
+    def getAttendeeProperty(self):
+        pass
+    
+    def setAttendee(self, attendee):
+        pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcclientclientsessionpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/client/clientsession.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/client/clientsession.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/client/clientsession.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,675 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.session import Session
+from protocol.webdav.propfind import PropFind
+from protocol.webdav.definitions import davxml
+from protocol.caldav.definitions import headers
+from protocol.http.data.string import ResponseDataString
+from protocol.url import URL
+from protocol.webdav.definitions import statuscodes
+from protocol.webdav.propfindparser import PropFindParser
+from protocol.webdav.principalmatch import PrincipalMatch
+from protocol.http.authentication.basic import Basic
+from protocol.http.authentication.digest import Digest
+from protocol.webdav.proppatch import PropPatch
+from xml.etree.ElementTree import Element
+from protocol.webdav.get import Get
+from protocol.webdav.propnames import PropNames
+from protocol.webdav.propall import PropAll
+from protocol.webdav.acl import ACL
+import types
+import httplib
+
+class CalDAVSession(Session):
+    
+    class logger(object):
+        
+        def write(self, data):
+            print data.replace(&quot;\r\n&quot;, &quot;\n&quot;),
+
+    def __init__(self, server, port=None, ssl=False, user=&quot;&quot;, pswd=&quot;&quot;, principal=None, root=None, logging=False):
+        super(CalDAVSession, self).__init__(server, port, ssl, log=CalDAVSession.logger())
+
+        self.loghttp = logging
+
+        self.user = user
+        self.pswd = pswd
+        
+        # Initialize state
+        self.connect = None
+
+        # Paths
+        self.rootPath = URL(url=root)
+        self.principalPath = URL(url=principal)
+        
+        self._initCalDAVState()
+
+    def _initCalDAVState(self):
+
+        # We need to cache the server capabilities and properties
+        if not self.principalPath:
+            self._discoverPrincipal()
+        
+    def _discoverPrincipal(self):
+        
+        hrefs = self.getHrefListProperty(self.rootPath, davxml.principal_collection_set)
+        if not hrefs:
+            return
+
+        # For each principal collection find one that matches self
+        for href in hrefs:
+
+            results = self.getSelfHrefs(href)
+            if results:
+                self.principalPath = results[0]
+                if self.log:
+                    self.log.write(&quot;Found principal path: %s&quot; % (self.principalPath.absoluteURL(),))
+                return
+    
+    def testResource(self, rurl):
+
+        assert(isinstance(rurl, URL))
+
+        request = PropFind(self, rurl.relativeURL(), headers.Depth0, (davxml.resourcetype,))
+    
+        # Process it
+        self.runSession(request)
+        
+        return request.getStatusCode() == statuscodes.MultiStatus
+
+    def getPropertyNames(self, rurl):
+
+        assert(isinstance(rurl, URL))
+
+        results = ()
+
+        # Create WebDAV propfind
+        request = PropNames(self, rurl.relativeURL(), headers.Depth0)
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+
+            parser = PropFindParser()
+            parser.parseData(result.getData())
+    
+            # Look at each propfind result
+            for item in parser.getResults().itervalues():
+
+                # Get child element name (decode URL)
+                name = URL(url=item.getResource(), decode=True)
+            
+                # Must match rurl
+                if name.equalRelative(rurl):
+
+                    results = tuple([name for name in item.getNodeProperties().iterkeys()])
+
+        else:
+            self.handleHTTPError(request)
+    
+        return results
+
+    def getProperties(self, rurl, props):
+
+        assert(isinstance(rurl, URL))
+
+        results = {}
+        bad = None
+
+        # Create WebDAV propfind
+        if props:
+            request = PropFind(self, rurl.relativeURL(), headers.Depth0, props)
+        else:
+            request = PropAll(self, rurl.relativeURL(), headers.Depth0)
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+
+            parser = PropFindParser()
+            parser.parseData(result.getData())
+    
+            # Look at each propfind result
+            for item in parser.getResults().itervalues():
+
+                # Get child element name (decode URL)
+                name = URL(url=item.getResource(), decode=True)
+            
+                # Must match rurl
+                if name.equalRelative(rurl):
+                    for name, value in item.getTextProperties().iteritems():
+                        results[name] = value
+                    for name, value in item.getHrefProperties().iteritems():
+                        if name not in results:
+                            results[name] = value
+                    for name, value in item.getNodeProperties().iteritems():
+                        if name not in results:
+                            results[name] = value
+                    bad = item.getBadProperties()
+        else:
+            self.handleHTTPError(request)
+    
+        return results, bad
+
+    def getPropertiesOnHierarchy(self, rurl, props):
+
+        assert(isinstance(rurl, URL))
+
+        results = {}
+
+        # Create WebDAV propfind
+        request = PropFind(self, rurl.relativeURL(), headers.Depth1, props)
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+
+            parser = PropFindParser()
+            parser.parseData(result.getData())
+    
+            # Look at each propfind result
+            for item in parser.getResults().itervalues():
+
+                # Get child element name (decode URL)
+                name = URL(url=item.getResource(), decode=True)
+                propresults = {}
+                results[name.relativeURL()] = propresults
+            
+                for prop in props:
+
+                    if item.getTextProperties().has_key(str(prop)):
+                        propresults[prop] = item.getTextProperties().get(str(prop))
+
+                    elif item.getNodeProperties().has_key(str(prop)):
+                        propresults[prop] = item.getNodeProperties()[str(prop)]
+        else:
+            self.handleHTTPError(request)
+    
+        return results
+
+    def getHrefListProperty(self, rurl, propname):
+
+        assert(isinstance(rurl, URL))
+
+        results = ()
+
+        # Create WebDAV propfind
+        request = PropFind(self, rurl.relativeURL(), headers.Depth0, (propname,))
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+
+            parser = PropFindParser()
+            parser.parseData(result.getData())
+    
+            # Look at each propfind result and extract any Hrefs
+            for item in parser.getResults().itervalues():
+
+                # Get child element name (decode URL)
+                name = URL(url=item.getResource(), decode=True)
+            
+                # Must match rurl
+                if name.equalRelative(rurl):
+
+                    if item.getNodeProperties().has_key(str(propname)):
+                        
+                        propnode = item.getNodeProperties()[str(propname)]
+                        results += tuple([URL(url=href.text, decode=True) for href in propnode.findall(str(davxml.href)) if href.text])
+        else:
+            self.handleHTTPError(request)
+    
+        return results
+
+    # Do principal-match report with self on the passed in url
+    def getSelfProperties(self, rurl, props):
+        
+        assert(isinstance(rurl, URL))
+
+        results = {}
+    
+        # Create WebDAV principal-match
+        request = PrincipalMatch(self, rurl.relativeURL(), props)
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+
+            parser = PropFindParser()
+            parser.parseData(result.getData())
+    
+            # Look at each principal-match result and return first one that is appropriate
+            for item in parser.getResults().itervalues():
+
+                for prop in props:
+
+                    if item.getNodeProperties().has_key(str(prop)):
+
+                        href = item.getNodeProperties()[str(prop)].find(str(davxml.href))
+                        
+                        if href is not None:
+                            results[prop] = URL(url=href.text, decode=True)
+                
+                # We'll take the first one, whatever that is
+                break
+
+        else:
+            self.handleHTTPError(request)
+            return None
+        
+        return results
+
+    # Do principal-match report with self on the passed in url
+    def getSelfHrefs(self, rurl):
+
+        assert(isinstance(rurl, URL))
+
+        results = ()
+
+        # Create WebDAV principal-match
+        request = PrincipalMatch(self, rurl.realtiveURL(), (davxml.principal_URL,))
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+
+            parser = PropFindParser()
+            parser.parseData(result.getData())
+    
+            # Look at each propfind result and extract any Hrefs
+            for item in parser.getResults().itervalues():
+
+                # Get child element name (decode URL)
+                name = URL(url=item.getResource(), decode=True)
+                results += (name.path,)
+
+        else:
+            self.handleHTTPError(request)
+            return None
+        
+        return results
+    
+    # Do principal-match report with self on the passed in url
+    def getSelfPrincipalResource(self, rurl):
+        
+        assert(isinstance(rurl, URL))
+
+        hrefs = self.getHrefListProperty(rurl, davxml.principal_collection_set)
+        if not hrefs:
+            return None
+        
+        # For each principal collection find one that matches self
+        for href in hrefs:
+
+            results = self.getSelfHrefs(href)
+            if results:
+                return results[0]
+        
+        return None
+
+    def setProperties(self, rurl, props):
+
+        assert(isinstance(rurl, URL))
+
+        results = ()
+
+        # Convert property data into something sensible
+        converted = []
+        for name, value in props:
+            node = None
+            if isinstance(value, types.StringType):
+                node = Element(name)
+                node.text = value
+            elif isinstance(value, URL):
+                node = Element(davxml.href)
+                node.text = value.absoluteURL()
+            elif isinstance(value, types.ListType) or isinstance(value, types.TupleType):
+                hrefs = []
+                for item in value:
+                    if isinstance(item, URL):
+                        href = Element(davxml.href)
+                        href.text = item.relativeURL()
+                        hrefs.append(href)
+                    else:
+                        break
+                else:
+                    node = Element(name)
+                    map(node.append, hrefs)
+            
+            if node is not None:
+                converted.append(node)
+
+        # Create WebDAV propfind
+        request = PropPatch(self, rurl.relativeURL(), converted)
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+
+            parser = PropFindParser()
+            parser.parseData(result.getData())
+    
+            # Look at each propfind result
+            for item in parser.getResults().itervalues():
+
+                # Get child element name (decode URL)
+                name = URL(url=item.getResource(), decode=True)
+            
+                # Must match rurl
+                if name.equalRelative(rurl):
+
+                    for prop in item.getNodeProperties():
+                        results += (prop,)
+
+        else:
+            self.handleHTTPError(request)
+    
+        return results
+
+    def setACL(self, rurl, aces):
+        
+        assert(isinstance(rurl, URL))
+
+        # Create WebDAV ACL
+        request = ACL(self, rurl.relativeURL(), aces)
+    
+        # Process it
+        self.runSession(request)
+        
+        if request.getStatusCode() not in (statuscodes.OK, statuscodes.Created, statuscodes.NoContent):
+            self.handleHTTPError(request)
+
+    def readData(self, rurl):
+
+        assert(isinstance(rurl, URL))
+
+        # Create WebDAV GET
+        request = Get(self, rurl.relativeURL())
+        dout = ResponseDataString()
+        request.setData(dout)
+    
+        # Process it
+        self.runSession(request)
+    
+        # Check response status
+        if request.getStatusCode() != statuscodes.OK:
+            self.handleHTTPError(request)
+            return None
+    
+        # Look for ETag
+        if request.getNewETag() is not None:
+
+            etag = request.getNewETag()
+    
+            # Handle server bug: ETag value MUST be quoted per HTTP/1.1 S3.11
+            if not etag.startswith('&quot;'):
+                etag = &quot;\&quot;%s\&quot;&quot; % (etag,)
+        else:
+            etag = None
+    
+        # Return data as a string and etag
+        return dout.getData(), etag
+
+    def openSession(self):
+        # Create connection
+        if self.ssl:
+            self.connect = httplib.HTTPSConnection(self.server, self.port)
+        else:
+            self.connect = httplib.HTTPConnection(self.server, self.port)
+        self.connect.connect()
+        
+        # Write to log file
+        if self.loghttp and self.log:
+            self.log.write(&quot;\n        &lt;-------- BEGIN HTTP CONNECTION --------&gt;\n&quot;)
+            self.log.write(&quot;Server: %s\n&quot; % (self.server,))
+
+    def closeSession(self):
+        if self.connect:
+            self.connect.close()
+            self.connect = None
+
+            # Write to log file
+            if self.loghttp and self.log:
+                self.log.write(&quot;\n        &lt;-------- END HTTP CONNECTION --------&gt;\n&quot;)
+    
+    def runSession(self, request):
+        
+        ctr = 5
+        while ctr:
+            ctr -= 1
+            
+            self.doSession(request)
+            
+            if request and request.isRedirect():
+                location = request.getResponseHeader(headers.Location)
+                if location:
+                    u = URL(location)
+                    if not u.scheme or u.scheme in (&quot;http&quot;, &quot;https&quot;,):
+                        # Get new server and base RURL
+                        different_server = (self.server != u.server) if u.server else False
+                        
+                        # Use new host in this session
+                        if different_server:
+                            self.setServer(u.server)
+                        
+                        # Reset the request with new info
+                        request.setURL(u.relativeURL())
+                        request.clearResponse()
+    
+                        # Write to log file
+                        if self.loghttp and self.log:
+                            self.log.write(&quot;\n        &lt;-------- HTTP REDIRECT --------&gt;\n&quot;)
+                            self.log.write(&quot;Location: %s\n&quot; % (location,))
+    
+                        # Recyle through loop
+                        continue
+
+            # Exit when redirect does not occur
+            break
+        
+    def doSession(self, request):
+        # Do initialisation if not already done
+        if not self.initialised:
+        
+            if not self.initialise(self.server, self.rootPath.relativeURL()):
+            
+                # Break connection with server
+                self.closeConnection()
+                return
+
+        # Do the request if present
+        if request:
+        
+            # Handle delayed authorization
+            first_time = True
+            while True:
+            
+                # Run the request actions - this will make any connection that is needed
+                self.sendRequest(request)
+                
+                # Check for auth failure if none before
+                if request.getStatusCode() == statuscodes.Unauthorized:
+                
+                    # If we had authorization before, then chances are auth details are wrong - so delete and try again with new auth
+                    if self.hasAuthorization():
+                    
+                        self.authorization = None
+                        
+                        # Display error so user knows why the prompt occurs again - but not the first time
+                        # as we might have a digest re-auth.
+                        if not first_time:
+                            self.displayHTTPError(request)
+                    
+    
+                    # Get authorization object (prompt the user) and redo the request
+                    self.authorization, cancelled = self.getAuthorizor(first_time, request.getResponseHeaders(headers.WWWAuthenticate))
+                    
+                    # Check for auth cancellation
+                    if cancelled:
+                        self.authorization = None
+                    
+                    else:
+                        first_time = False
+                        
+                        request.clearResponse()
+                        
+                        # Repeat the request loop with new authorization
+                        continue
+
+                # If we get here we are complete with auth loop
+                break
+
+        # Now close it - eventually we will do keep-alive support
+    
+        # Break connection with server
+        self.closeConnection()
+
+    def doRequest(self, request):
+    
+        # Write request headers
+        self.connect.putrequest(request.method, request.url, skip_host=True, skip_accept_encoding=True)
+        hdrs = request.getRequestHeaders()
+        for header, value in hdrs:
+            self.connect.putheader(header, value)
+        self.connect.endheaders()
+
+        # Write to log file
+        if self.loghttp and self.log:
+            self.log.write(&quot;\n        &lt;-------- BEGIN HTTP REQUEST --------&gt;\n&quot;)
+            self.log.write(&quot;%s\n&quot; % (request.getRequestStartLine(),))
+            for header, value in hdrs:
+                self.log.write(&quot;%s: %s\n&quot; % (header, value))
+            self.log.write(&quot;\n&quot;)
+    
+        # Write the data
+        self.writeRequestData(request)
+    
+        # Blank line in log between 
+        if self.loghttp and self.log:
+            self.log.write(&quot;\n        &lt;-------- BEGIN HTTP RESPONSE --------&gt;\n&quot;)
+
+        # Get response
+        response = self.connect.getresponse()
+        
+        # Get response headers
+        request.setResponseStatus(response.version, response.status, response.reason)
+        request.setResponseHeaders(response.msg.headers)
+        if self.loghttp and self.log:
+            self.log.write(&quot;HTTP/%s %s %s\r\n&quot; % (
+                {11:&quot;1.1&quot;,10:&quot;1.0&quot;,9:&quot;0.9&quot;}.get(response.version, &quot;?&quot;),
+                response.status,
+                response.reason
+            ))
+            for hdr in response.msg.headers:
+                self.log.write(hdr)
+
+        # Now get the data
+        self.readResponseData(request, response)
+    
+        # Trailer in log 
+        if self.loghttp and self.log:
+            self.log.write(&quot;\n        &lt;-------- END HTTP RESPONSE --------&gt;\n&quot;)
+
+    def handleHTTPError(self, request):
+        print &quot;Ignoring error&quot;
+
+    def getAuthorizor(self, first_time, wwwhdrs):
+        
+        for item in wwwhdrs:
+            if item.lower().startswith(&quot;basic&quot;):
+                return Basic(self.user, self.pswd), False
+            elif item.lower().startswith(&quot;digest&quot;):
+                return Digest(self.user, self.pswd, wwwhdrs), False
+        else:
+            return None, True
+
+    def writeRequestData(self, request):
+    
+        # Write the data if any present
+        if request.hasRequestData():
+        
+            stream = request.getRequestData()
+            if stream:
+                # Tell data we are using it
+                stream.start()
+                
+                # Buffered write from stream
+                more = True
+                while more:
+                    data, more = stream.read()
+                    if data:
+                        self.connect.send(data)
+                        
+                        if self.loghttp and self.log:
+                            self.log.write(data)
+
+                # Tell data we are done using it
+                stream.stop()
+    
+    def readResponseData(self, request, response):
+    
+        # Check for data and write it
+        data = response.read()
+        
+        if request.hasResponseData():
+            stream = request.getResponseData()
+            stream.start()
+            stream.write(data)
+            stream.stop()
+        else:
+            response.read()
+            
+        if self.loghttp and self.log:
+            self.log.write(data)    
+
+    def setServerType(self, type):
+        self.type = type
+    
+    def setServerDescriptor(self, txt):
+        self.descriptor = txt
+    
+    def setServerCapability(self, txt):
+        self.capability = txt
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcclientfullclientpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/client/fullclient.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/client/fullclient.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/client/fullclient.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,46 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from client.account import CalDAVAccount
+
+if __name__ == '__main__':
+    account = CalDAVAccount(&quot;&quot;, ssl=True, user=&quot;&quot;, pswd=&quot;&quot;, root=&quot;&quot;, principal=&quot;&quot;)
+
+    print account.getPrincipal()
+
+#    memberships = [CalDAVPrincipal(account.session, path) for path in account.getPrincipal().memberships]
+#    for member in memberships:
+#        member.loadDetails()
+#    memberships = [member.displayname for member in memberships]
+#    print &quot;Memberships: %s&quot; % (memberships,)
+
+#    calendars = account.getPrincipal().listCalendars()
+#    for calendar in calendars:
+#        print &quot;%s:&quot; % (calendar,)
+#        txt = calendar.getDisplayName()
+#        if txt:
+#            print &quot;  Display Name: %s&quot; % (txt,)
+#        txt = calendar.getDescription()
+#        if txt:
+#            print &quot;  Description: %s&quot; % (txt,)
+
+#    fbset = account.getPrincipal().listFreeBusySet()
+#    print fbset
+#    account.getPrincipal().cleanFreeBusySet()
+
+    proxies = account.getPrincipal().getReadProxies()
+    for proxy in proxies:
+        print proxy
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcclientprincipalpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/client/principal.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/client/principal.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/client/principal.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,227 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.caldav.definitions import caldavxml
+from client.calendar import Calendar
+from protocol.url import URL
+from protocol.webdav.definitions import davxml
+from protocol.caldav.definitions import headers
+import types
+
+
+class PrincipalCache(object):
+    
+    def __init__(self):
+        self.cache = {}
+        
+    def getPrincipal(self, session, path, refresh=False):
+        if path.toString() not in self.cache:
+            principal = CalDAVPrincipal(session, path)
+            principal.loadDetails()
+            self.cache[path.toString()] = principal
+            self.cache[principal.principalURL.toString()] = principal
+            for uri in principal.alternateURIs:
+                self.cache[uri.toString()] = principal
+        elif refresh:
+            self.cache[path.toString()].loadDetails(refresh=True)
+        return self.cache[path.toString()]
+
+principalCache = PrincipalCache()
+
+class CalDAVPrincipal(object):
+    
+    def __init__(self, session, path):
+        
+        self.session = session
+        self.principalPath = path
+        self._initFields()
+
+    def __str__(self):
+        return &quot;&quot;&quot;
+    Principal Path    : %s
+    Display Name      : %s
+    Principal URL     : %s
+    Alternate URLs    : %s
+    Group Members     : %s
+    Memberships       : %s
+    Calendar Homes    : %s
+    Outbox URL        : %s
+    Inbox URL         : %s
+    Calendar Addresses: %s
+&quot;&quot;&quot; % (
+          self.principalPath,
+          self.displayname,
+          self.principalURL,
+          self.alternateURIs,
+          self.memberset,
+          self.memberships,
+          self.homeset,
+          self.outboxURL,
+          self.inboxURL,
+          self.cuaddrs
+      )
+
+    def _initFields(self):
+        self.loaded = False
+        self.valid = False
+        self.displayname = &quot;Invalid Principal&quot;
+        self.principalURL = &quot;&quot;
+        self.alternateURIs = ()
+        self.memberset = ()
+        self.memberships = ()
+        self.homeset = ()
+        self.outboxURL = &quot;&quot;
+        self.inboxURL = &quot;&quot;
+        self.cuaddrs = ()
+        
+        self.proxyFor = None
+        self.proxyreadURL = &quot;&quot;
+        self.proxywriteURL = &quot;&quot;
+        
+    def loadDetails(self, refresh=False):
+        if self.loaded and not refresh:
+            return
+        self._initFields()
+
+        results, _ignore_bad = self.session.getProperties(
+            self.principalPath,
+            (
+                davxml.resourcetype,
+                davxml.displayname,
+                davxml.principal_URL,
+                davxml.alternate_URI_set,
+                davxml.group_member_set,
+                davxml.group_membership,
+                caldavxml.calendar_home_set,
+                caldavxml.schedule_outbox_URL,
+                caldavxml.schedule_inbox_URL,
+                caldavxml.calendar_user_address_set,
+            ),
+        )
+        if results:
+            # First check that we have a valid principal and see if its a proxy principal too
+            type = results.get(davxml.resourcetype, None)
+            self.valid = type.find(str(davxml.principal)) is not None
+            if (self.session.hasDAVVersion(headers.calendar_proxy) and
+                (type.find(str(caldavxml.calendar_proxy_read)) is not None or
+                 type.find(str(caldavxml.calendar_proxy_write)) is not None)):
+                parentPath = self.principalPath.dirname()
+                self.proxyFor = principalCache.getPrincipal(self.session, parentPath, refresh)
+                
+            if self.valid:
+                self.displayname = results.get(davxml.displayname, None)
+                self.principalURL = results.get(davxml.principal_URL, None)
+                self.alternateURIs = results.get(davxml.alternate_URI_set, None)
+                self.memberset = results.get(davxml.group_member_set, None)
+                self.memberships = results.get(davxml.group_membership, None)
+                self.homeset = results.get(caldavxml.calendar_home_set, None)
+                self.outboxURL = results.get(caldavxml.schedule_outbox_URL, None)
+                self.inboxURL = results.get(caldavxml.schedule_inbox_URL, None)
+                self.cuaddrs = results.get(caldavxml.calendar_user_address_set, None)
+
+        # Get proxy resource details if proxy support is available
+        if self.session.hasDAVVersion(headers.calendar_proxy) and not self.proxyFor:
+            results = self.session.getPropertiesOnHierarchy(self.principalPath, (davxml.resourcetype,))
+            for path, items in results.iteritems():
+                if davxml.resourcetype in items:
+                    rtype = items[davxml.resourcetype]
+                    if rtype.find(str(caldavxml.calendar_proxy_read)) is not None:
+                        self.proxyreadURL = URL(url=path)
+                    elif rtype.find(str(caldavxml.calendar_proxy_write)) is not None:
+                        self.proxywriteURL = URL(url=path)
+                        
+        self.loaded = True
+
+    def getSmartDisplayName(self):
+        if self.proxyFor:
+            return &quot;%s#%s&quot; % (self.proxyFor.displayname, self.displayname,)
+        else:
+            return self.displayname
+
+    def listCalendars(self, root=None):
+        calendars = []
+        home = self.homeset[0] if type(self.homeset) in (types.TupleType,) else self.homeset
+        if not home.path.endswith(&quot;/&quot;):
+            home.path += &quot;/&quot;
+
+        results = self.session.getPropertiesOnHierarchy(home, (davxml.resourcetype,))
+        for path, items in results.iteritems():
+            if davxml.resourcetype in items:
+                rtype = items[davxml.resourcetype]
+                if rtype.find(str(davxml.collection)) is not None and rtype.find(str(caldavxml.calendar)) is not None:
+                    calendars.append(Calendar(path=path, session=self.session))
+        return calendars
+
+    def listFreeBusySet(self):
+        return self._getFreeBusySet()
+    
+    def addToFreeBusySet(self, calendars):
+        current = self._getFreeBusySet()
+        for calendar in calendars:
+            current.append(calendar)
+        self._setFreeBusySet(current)
+    
+    def removeFromFreeBusySet(self, calendars):
+        calendar_paths = [calendar.path for calendar in calendars]
+        current = self._getFreeBusySet()
+        current = [cal for cal in current if cal.path not in calendar_paths]
+        self._setFreeBusySet(current)
+
+    def cleanFreeBusySet(self):
+        fbset = self.listFreeBusySet()
+        badfbset = []
+        for calendar in fbset:
+            if not calendar.exists():
+                badfbset.append(calendar)
+    
+        if badfbset:
+            self.removeFromFreeBusySet(badfbset)
+
+    def _getFreeBusySet(self):
+        hrefs = self.session.getHrefListProperty(self.inboxURL, caldavxml.calendar_free_busy_set)
+        return [Calendar(href.relativeURL(), session=self.session) for href in hrefs]
+    
+    def _setFreeBusySet(self, calendars):
+        hrefs = [URL(url=calendar.path) for calendar in calendars]
+        self.session.setProperties(self.inboxURL, ((caldavxml.calendar_free_busy_set, hrefs),))
+
+    def getReadProxies(self, refresh=True):
+        if not self.proxyreadURL:
+            return ()
+        
+        principal = principalCache.getPrincipal(self.session, self.proxyreadURL, refresh=refresh)
+        return [principalCache.getPrincipal(self.session, member) for member in principal.memberset]
+            
+    
+    def setReadProxies(self, principals):
+        if not self.proxyreadURL:
+            return ()
+        
+        self.session.setProperties(self.proxyreadURL, ((davxml.group_member_set, principals),))            
+    
+    def getWriteProxies(self, refresh=True):
+        if not self.proxywriteURL:
+            return ()
+        
+        principal = principalCache.getPrincipal(self.session, self.proxywriteURL, refresh=refresh)
+        return [principalCache.getPrincipal(self.session, member) for member in principal.memberset]
+
+    def setWriteProxies(self, principals):
+        if not self.proxywriteURL:
+            return ()
+        
+        self.session.setProperties(self.proxywriteURL, ((davxml.group_member_set, principals),))            
+    
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcclientsimplepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/client/simple.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/client/simple.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/client/simple.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,61 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.session import Session
+from protocol.webdav.options import Options
+import httplib
+
+def run(session, request):
+    
+    # Create connection
+    if session.ssl:
+        connect = httplib.HTTPSConnection(session.server, session.port)
+    else:
+        connect = httplib.HTTPConnection(session.server, session.port)
+    connect.set_debuglevel(1)
+
+    # Do headers
+    connect.putrequest(request.method, request.url, skip_host=True, skip_accept_encoding=True)
+    hdrs = request.getRequestHeaders()
+    for header, value in hdrs.iteritems():
+        connect.putheader(header, value)
+    connect.endheaders()
+    
+    # Do request body
+    stream = request.getRequestDataStream()
+    if stream:
+        stream.start()
+        more = True
+        while more:
+            data, more = stream.read()
+            if data:
+                connect.send(data)
+        stream.stop()
+
+    # Get response
+    response = connect.getresponse()
+    
+    # Get response headers
+    request.setResponseStatus(response.version, response.status, response.reason)
+    request.setResponseHeaders(response.getheaders())
+    
+    # Get response body
+
+if __name__ == '__main__':
+    session = Session(&quot;www.mulberrymail.com&quot;)
+    request = Options(session, &quot;/&quot;)
+    
+    run(session, request)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocol__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolcaldav__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/caldav/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/caldav/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/caldav/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,28 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.propfindparser import PropFindParser
+from protocol.webdav.definitions import davxml
+from protocol.caldav.definitions import caldavxml
+
+PropFindParser.textProperties.add(caldavxml.calendar_description)
+PropFindParser.textProperties.add(caldavxml.calendar_timezone)
+
+PropFindParser.hrefListProperties.add(caldavxml.calendar_home_set)
+PropFindParser.hrefListProperties.add(caldavxml.calendar_user_address_set)
+PropFindParser.hrefListProperties.add(caldavxml.calendar_free_busy_set)
+PropFindParser.hrefProperties.add(caldavxml.schedule_outbox_URL)
+PropFindParser.hrefProperties.add(caldavxml.schedule_inbox_URL)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolcaldavdefinitions__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolcaldavdefinitionscaldavxmlpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/caldavxml.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/caldavxml.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/caldavxml.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,83 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from xml.etree.ElementTree import QName
+
+CalDAVNamespace = &quot;urn:ietf:params:xml:ns:caldav&quot;
+
+# RFC4791
+
+mkcalendar           = QName(CalDAVNamespace, &quot;mkcalendar&quot;)
+mkcalendar_response  = QName(CalDAVNamespace, &quot;mkcalendar-response&quot;)
+
+calendar             = QName(CalDAVNamespace, &quot;calendar&quot;)
+
+calendar_description             = QName(CalDAVNamespace, &quot;calendar-description&quot;)
+calendar_timezone                = QName(CalDAVNamespace, &quot;calendar-timezone&quot;)
+supported_calendar_component_set = QName(CalDAVNamespace, &quot;supported-calendar-component-set&quot;)
+supported_calendar_data          = QName(CalDAVNamespace, &quot;supported-calendar-data&quot;)
+max_resource_size                = QName(CalDAVNamespace, &quot;max-resource-size&quot;)
+min_date_time                    = QName(CalDAVNamespace, &quot;min-date-time&quot;)
+max_date_time                    = QName(CalDAVNamespace, &quot;max-date-time&quot;)
+max_instances                    = QName(CalDAVNamespace, &quot;max-instances&quot;)
+max_attendees_per_instance       = QName(CalDAVNamespace, &quot;max-attendees-per-instance&quot;)
+
+read_free_busy       = QName(CalDAVNamespace, &quot;read-free-busy&quot;)
+calendar_home_set    = QName(CalDAVNamespace, &quot;calendar-home-set&quot;)
+
+supported_collation  = QName(CalDAVNamespace, &quot;supported-collation&quot;)
+
+calendar_query       = QName(CalDAVNamespace, &quot;calendar-query&quot;)
+calendar_data        = QName(CalDAVNamespace, &quot;calendar-data&quot;)
+comp                 = QName(CalDAVNamespace, &quot;comp&quot;)
+allcomp              = QName(CalDAVNamespace, &quot;allcomp&quot;)
+prop                 = QName(CalDAVNamespace, &quot;prop&quot;)
+expand               = QName(CalDAVNamespace, &quot;expand&quot;)
+limit_recurrence_set = QName(CalDAVNamespace, &quot;limit-recurrence-set&quot;)
+limit_freebusy_set   = QName(CalDAVNamespace, &quot;limit-freebusy-set&quot;)
+filter               = QName(CalDAVNamespace, &quot;filter&quot;)
+comp_filter          = QName(CalDAVNamespace, &quot;comp-filter&quot;)
+prop_filter          = QName(CalDAVNamespace, &quot;prop-filter&quot;)
+param_filter         = QName(CalDAVNamespace, &quot;param-filter&quot;)
+is_not_defined       = QName(CalDAVNamespace, &quot;is-not-defined&quot;)
+text_match           = QName(CalDAVNamespace, &quot;text-match&quot;)
+timezone             = QName(CalDAVNamespace, &quot;timezone&quot;)
+time_range           = QName(CalDAVNamespace, &quot;time-range&quot;)
+
+calendar_multiget    = QName(CalDAVNamespace, &quot;calendar-multiget&quot;)
+
+free_busy_query      = QName(CalDAVNamespace, &quot;free-busy-query&quot;)
+
+# draft caldav-schedule
+calendar_free_busy_set    = QName(CalDAVNamespace, &quot;calendar-free-busy-set&quot;)
+originator                = QName(CalDAVNamespace, &quot;originator&quot;)
+recipient                 = QName(CalDAVNamespace, &quot;recipient&quot;)
+schedule                  = QName(CalDAVNamespace, &quot;schedule&quot;)
+
+schedule_inbox_URL        = QName(CalDAVNamespace, &quot;schedule-inbox-URL&quot;)
+schedule_outbox_URL       = QName(CalDAVNamespace, &quot;schedule-outbox-URL&quot;)
+calendar_user_address_set = QName(CalDAVNamespace, &quot;calendar-user-address-set&quot;)
+
+schedule_response         = QName(CalDAVNamespace, &quot;schedule-response&quot;)
+response                  = QName(CalDAVNamespace, &quot;timezone&quot;)
+request_status            = QName(CalDAVNamespace, &quot;request-status&quot;)
+
+# Extensions
+
+CalendarServerNamespace   = &quot;http://calendarserver.org/ns/&quot;
+
+calendar_proxy_read       = QName(CalendarServerNamespace, &quot;calendar-proxy-read&quot;)
+calendar_proxy_write      = QName(CalendarServerNamespace, &quot;calendar-proxy-write&quot;)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolcaldavdefinitionsheaderspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/headers.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/headers.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/headers.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,30 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.definitions.headers import * #@UnusedWildImport
+
+# RFC4791
+CalendarAccess   = &quot;calendar-access&quot;
+
+# draft caldav-schedule
+CalendarSchedule = &quot;calendar-schedule&quot;
+
+Originator = &quot;Originator&quot;
+Recipient  = &quot;Recipient&quot;
+
+# Extensions
+
+calendar_proxy = &quot;calendar-proxy&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolcaldavdefinitionsmethodspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/methods.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/methods.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/caldav/definitions/methods.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,21 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.definitions.methods import * #@UnusedWildImport
+
+# RFC4791 - CalDAV Request Methods
+
+MKCALENDAR = &quot;MKCALENDAR&quot;;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolcaldavmakecalendarpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/caldav/makecalendar.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/caldav/makecalendar.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/caldav/makecalendar.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,76 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.caldav.definitions import methods
+from StringIO import StringIO
+from protocol.http.data.string import RequestDataString
+from xml.etree.ElementTree import Element
+from protocol.caldav.definitions import caldavxml
+from xml.etree.ElementTree import SubElement
+from protocol.webdav.definitions import davxml
+from protocol.utils.xmlhelpers import BetterElementTree
+
+class MakeCalendar(RequestResponse):
+
+    def __init__(self, session, url, displayname=None, description=None, timezone=None):
+        super(MakeCalendar, self).__init__(session, methods.MKCALENDAR, url)
+        self.displayname = displayname
+        self.description = description
+        self.timezone = timezone
+        
+        self.initRequestData()
+
+    def initRequestData(self):
+        if self.displayname or self.description or self.timezone:
+            # Write XML info to a string
+            os = StringIO()
+            self.generateXML(os)
+            self.request_data = RequestDataString(os.getvalue(), &quot;text/xml charset=utf-8&quot;)
+    
+    def generateXML(self, os):
+        # Structure of document is:
+        #
+        # &lt;CALDAV:mkcalendar&gt;
+        #   &lt;DAV:prop&gt;
+        #     &lt;&lt;each property as elements&gt;&gt;
+        #   &lt;/DAV:prop&gt;
+        # &lt;/CALDAV:mkcalendar&gt;
+
+        # &lt;CALDAV:mkcalendar&gt; element
+        mkcalendar = Element(caldavxml.mkcalendar)
+
+        # &lt;DAV:prop&gt; element
+        prop = SubElement(mkcalendar, davxml.prop)
+        
+        # &lt;DAV:displayname&gt; element
+        if self.displayname:
+            displayname = SubElement(prop, davxml.displayname)
+            displayname.text = self.displayname
+        
+        # &lt;CalDAV:calendar-description&gt; element
+        if self.description:
+            description = SubElement(prop, caldavxml.calendar_description)
+            description.text = self.description
+        
+        # &lt;CalDAV:timezone&gt; element
+        if self.timezone:
+            timezone = SubElement(prop, caldavxml.calendar_timezone)
+            timezone.text = self.timezone
+        
+        # Now we have the complete document, so write it out (no indentation)
+        xmldoc = BetterElementTree(mkcalendar)
+        xmldoc.writeUTF8(os)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolcaldavmultigetpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/caldav/multiget.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/caldav/multiget.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/caldav/multiget.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,71 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.report import Report
+from StringIO import StringIO
+from protocol.http.data.string import RequestDataString
+from protocol.caldav.definitions import caldavxml
+from xml.etree.ElementTree import Element
+from xml.etree.ElementTree import SubElement
+from protocol.webdav.definitions import davxml
+from protocol.utils.xmlhelpers import BetterElementTree
+
+class Multiget(Report):
+
+    def __init__(self, session, url, hrefs, props=()):
+        super(Multiget, self).__init__(session, url)
+        self.props = props
+        self.hrefs = hrefs
+        pass
+
+    def initRequestData(self):
+        if self.displayname or self.description or self.timezone:
+            # Write XML info to a string
+            os = StringIO()
+            self.generateXML(os)
+            self.request_data = RequestDataString(os.getvalue(), &quot;text/xml charset=utf-8&quot;)
+    
+    def generateXML(self, os):
+        # Structure of document is:
+        #
+        # &lt;CalDAV:calendar-multiget&gt;
+        #   &lt;DAV:prop&gt;
+        #     &lt;&lt;names of each property as elements&gt;&gt;
+        #   &lt;/DAV:prop&gt;
+        #   &lt;DAV:href&gt;...&lt;/DAV:href&gt;
+        #   ...
+        # &lt;/CalDAV:calendar-multiget&gt;
+        
+        # &lt;CalDAV:calendar-multiget&gt; element
+        multiget = Element(caldavxml.calendar_multiget)
+        
+        if self.props:
+            # &lt;DAV:prop&gt; element
+            prop = SubElement(multiget, davxml.prop)
+            
+            # Now add each property
+            for propname in self.props:
+                # Add property element taking namespace into account
+                SubElement(prop, propname)
+        
+        # Now add each href
+        for href in self.hrefs:
+            # Add href elements
+            SubElement(multiget, davxml.href).text = href
+        
+        # Now we have the complete document, so write it out (no indentation)
+        xmldoc = BetterElementTree(multiget)
+        xmldoc.writeUTF8(os)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolcaldavtests__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/caldav/tests/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/caldav/tests/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/caldav/tests/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolcaldavteststest_makecalendarpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/caldav/tests/test_makecalendar.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/caldav/tests/test_makecalendar.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/caldav/tests/test_makecalendar.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,111 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.session import Session
+from protocol.caldav.makecalendar import MakeCalendar
+from StringIO import StringIO
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = MakeCalendar(server, &quot;/&quot;)
+        self.assertEqual(request.getMethod(), &quot;MKCALENDAR&quot;)
+    
+class TestRequestHeaders(unittest.TestCase):
+    pass
+
+class TestRequestBody(unittest.TestCase):

+    def test_GenerateXMLDisplayname(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = MakeCalendar(server, &quot;/&quot;, &quot;home&quot;)
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:mkcalendar xmlns:ns0=&quot;urn:ietf:params:xml:ns:caldav&quot;&gt;
+  &lt;ns1:prop xmlns:ns1=&quot;DAV:&quot;&gt;
+    &lt;ns1:displayname&gt;home&lt;/ns1:displayname&gt;
+  &lt;/ns1:prop&gt;
+&lt;/ns0:mkcalendar&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+    def test_GenerateXMLMultipleProperties(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = MakeCalendar(server, &quot;/&quot;, &quot;home&quot;, &quot;my personal calendar&quot;)
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:mkcalendar xmlns:ns0=&quot;urn:ietf:params:xml:ns:caldav&quot;&gt;
+  &lt;ns1:prop xmlns:ns1=&quot;DAV:&quot;&gt;
+    &lt;ns1:displayname&gt;home&lt;/ns1:displayname&gt;
+    &lt;ns0:calendar-description&gt;my personal calendar&lt;/ns0:calendar-description&gt;
+  &lt;/ns1:prop&gt;
+&lt;/ns0:mkcalendar&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+    def test_GenerateXMLCDATAProperty(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        timezone = &quot;&quot;&quot;BEGIN:VCALENDAR
+PRODID:-//Example Corp.//CalDAV Client//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:US-Eastern
+LAST-MODIFIED:19870101T000000Z
+BEGIN:STANDARD
+DTSTART:19671029T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:Eastern Standard Time (US &amp; Canada)
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19870405T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:Eastern Daylight Time (US &amp; Canada)
+END:DAYLIGHT
+END:VTIMEZONE
+END:VCALENDAR
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+        request = MakeCalendar(server, &quot;/&quot;, timezone=timezone)
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:mkcalendar xmlns:ns0=&quot;urn:ietf:params:xml:ns:caldav&quot;&gt;
+  &lt;ns1:prop xmlns:ns1=&quot;DAV:&quot;&gt;
+    &lt;ns0:calendar-timezone&gt;%s&lt;/ns0:calendar-timezone&gt;
+  &lt;/ns1:prop&gt;
+&lt;/ns0:mkcalendar&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;) % (timezone.replace(&quot;&amp;&quot;, &quot;&amp;amp;&quot;),)
+)
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolcaldavteststest_multigetpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/caldav/tests/test_multiget.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/caldav/tests/test_multiget.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/caldav/tests/test_multiget.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,105 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.session import Session
+from protocol.caldav.multiget import Multiget
+from StringIO import StringIO
+from protocol.webdav.definitions import davxml
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Multiget(server, &quot;/&quot;, ())
+        self.assertEqual(request.getMethod(), &quot;REPORT&quot;)
+    
+class TestRequestHeaders(unittest.TestCase):
+    pass
+
+class TestRequestBody(unittest.TestCase):

+    def test_GenerateXMLOneHrefOnly(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Multiget(server, &quot;/&quot;, (&quot;/a&quot;,))
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:calendar-multiget xmlns:ns0=&quot;urn:ietf:params:xml:ns:caldav&quot;&gt;
+  &lt;ns1:href xmlns:ns1=&quot;DAV:&quot;&gt;/a&lt;/ns1:href&gt;
+&lt;/ns0:calendar-multiget&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+    def test_GenerateXMLMultipleHrefsOnly(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Multiget(server, &quot;/&quot;, (&quot;/a&quot;, &quot;/b&quot;,))
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:calendar-multiget xmlns:ns0=&quot;urn:ietf:params:xml:ns:caldav&quot;&gt;
+  &lt;ns1:href xmlns:ns1=&quot;DAV:&quot;&gt;/a&lt;/ns1:href&gt;
+  &lt;ns1:href xmlns:ns1=&quot;DAV:&quot;&gt;/b&lt;/ns1:href&gt;
+&lt;/ns0:calendar-multiget&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+    def test_GenerateXMLMultipleHrefsOneProperty(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Multiget(server, &quot;/&quot;, (&quot;/a&quot;, &quot;/b&quot;,), (davxml.getetag,))
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:calendar-multiget xmlns:ns0=&quot;urn:ietf:params:xml:ns:caldav&quot;&gt;
+  &lt;ns1:prop xmlns:ns1=&quot;DAV:&quot;&gt;
+    &lt;ns1:getetag /&gt;
+  &lt;/ns1:prop&gt;
+  &lt;ns1:href xmlns:ns1=&quot;DAV:&quot;&gt;/a&lt;/ns1:href&gt;
+  &lt;ns1:href xmlns:ns1=&quot;DAV:&quot;&gt;/b&lt;/ns1:href&gt;
+&lt;/ns0:calendar-multiget&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+    def test_GenerateXMLMultipleHrefsMultipleProperties(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Multiget(server, &quot;/&quot;, (&quot;/a&quot;, &quot;/b&quot;,), (davxml.getetag, davxml.displayname,))
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:calendar-multiget xmlns:ns0=&quot;urn:ietf:params:xml:ns:caldav&quot;&gt;
+  &lt;ns1:prop xmlns:ns1=&quot;DAV:&quot;&gt;
+    &lt;ns1:getetag /&gt;
+    &lt;ns1:displayname /&gt;
+  &lt;/ns1:prop&gt;
+  &lt;ns1:href xmlns:ns1=&quot;DAV:&quot;&gt;/a&lt;/ns1:href&gt;
+  &lt;ns1:href xmlns:ns1=&quot;DAV:&quot;&gt;/b&lt;/ns1:href&gt;
+&lt;/ns0:calendar-multiget&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttp__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpauthentication__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/authentication/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/authentication/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/authentication/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpauthenticationauthenticatorpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/authentication/authenticator.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/authentication/authenticator.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/authentication/authenticator.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,20 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+class Authenticator(object):
+
+    def addHeaders(self, hdrs, request):
+        raise NotImplementedError
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpauthenticationbasicpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/authentication/basic.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/authentication/basic.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/authentication/basic.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,36 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.authentication.authenticator import Authenticator
+from protocol.http.definitions import headers
+
+class Basic(Authenticator):
+
+    def __init__(self, user, pswd):
+        self.user = user
+        self.pswd = pswd
+
+    def setDetails(self, user, pswd):
+        self.user = user
+        self.pswd = pswd
+
+    def addHeaders(self, hdrs, request):
+        # Generate the base64 encoded string
+        encode = self.user + &quot;:&quot; + self.pswd
+        base64 = encode.encode(&quot;base64&quot;).strip()
+    
+        # Generate header
+        hdrs.append((headers.Authorization, &quot;Basic %s&quot; % (base64,)))
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpauthenticationdigestpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/authentication/digest.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/authentication/digest.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/authentication/digest.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,218 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.authentication.authenticator import Authenticator
+from protocol.http.util import parsetoken
+from protocol.http.definitions import headers
+from StringIO import StringIO
+import sha
+import md5
+
+class Digest(Authenticator):
+
+    def __init__(self, user, pswd, www_authenticate):
+        self.fields = {}
+        self.fields['username'] = user
+        self.fields['password'] = pswd
+        self.parseAuthenticateHeader(www_authenticate)
+        self.stale = False
+        self.clientCount = 0
+
+    def setDetails(self, user, pswd, www_authenticate):
+        self.fields['username'] = user
+        self.fields['password'] = pswd
+        self.parseAuthenticateHeader(www_authenticate)
+
+    def addHeaders(self, hdrs, request):
+        # Generate response
+        self.generateResponse(request)
+        
+        # Generate header
+        os = StringIO()
+        os.write(&quot;Digest username=\&quot;%s\&quot;,&quot; % (self.fields['username'],))
+        os.write(&quot; realm=\&quot;%s\&quot;,&quot; % (self.fields['realm'],))
+        os.write(&quot; nonce=\&quot;%s\&quot;,&quot; % (self.fields['nonce'],))
+        os.write(&quot; uri=\&quot;%s\&quot;,&quot; % (request.getURL(),))
+        if &quot;qop&quot; in self.fields:
+            os.write(&quot; qop=auth,&quot;)
+            os.write(&quot; nc=\&quot;%s\&quot;&quot; % (self.fields['nc'],))
+            os.write(&quot; cnonce=\&quot;%s\&quot;&quot; % (self.fields['cnonce'],))
+        os.write(&quot; response=\&quot;%s\&quot;&quot; % (self.response,))
+        
+        if &quot;algorithm&quot; in self.fields:
+            os.write(&quot;, algorithm=\&quot;%s\&quot;&quot; % (self.fields['algorithm'],))
+        if &quot;opaque&quot; in self.fields:
+            os.write(&quot;, opaque=\&quot;%s\&quot;&quot; % (self.fields['opaque'],))
+        
+        hdrs.append((headers.Authorization, os.getvalue()))
+
+    def parseAuthenticateHeader(self, hdrs):
+        for hdr in hdrs:
+
+            # Strip any space
+            hdr = hdr.strip()
+            
+            # Must have Digest token
+            if hdr[:7].lower() != &quot;digest &quot;:
+                continue
+            else:
+                hdr = hdr[7:]
+            
+            # Get each name/value pair
+            while True:
+                name, hdr = parsetoken(hdr, &quot; \t=&quot;)
+
+                if not name or not hdr:
+                    return
+                name = name.lower()
+                
+                value, hdr = parsetoken(hdr, &quot;, &quot;)
+                if not value:
+                    return
+                
+                if name in (&quot;realm&quot;, &quot;domain&quot;, &quot;nonce&quot;, &quot;opaque&quot;, &quot;algorithm&quot;, &quot;qop&quot;):
+                    self.fields[name] = value
+
+                elif name == &quot;stale&quot;:
+                    self.stale = (value.lower() != &quot;false&quot;)
+
+                else:
+                    # Unknown token - ignore
+                    pass
+                
+                # Punt over space
+                hdr = hdr.strip()
+            
+            break
+
+    algorithms = {
+        'md5': md5.new,
+        'md5-sess': md5.new,
+        'sha': sha.new,
+    }
+    
+    # DigestCalcHA1
+    @staticmethod
+    def calcHA1(
+        pszAlg,
+        pszUserName,
+        pszRealm,
+        pszPassword,
+        pszNonce,
+        pszCNonce,
+        preHA1=None
+    ):
+        &quot;&quot;&quot;
+        @param pszAlg: The name of the algorithm to use to calculate the Digest.
+            Currently supported are md5 md5-sess and sha.
+    
+        @param pszUserName: The username
+        @param pszRealm: The realm
+        @param pszPassword: The password
+        @param pszNonce: The nonce
+        @param pszCNonce: The cnonce
+    
+        @param preHA1: If available this is a str containing a previously
+            calculated HA1 as a hex string. If this is given then the values for
+            pszUserName, pszRealm, and pszPassword are ignored.
+        &quot;&quot;&quot;
+    
+        if (preHA1 and (pszUserName or pszRealm or pszPassword)):
+            raise TypeError((&quot;preHA1 is incompatible with the pszUserName, &quot;
+                             &quot;pszRealm, and pszPassword arguments&quot;))
+    
+        if preHA1 is None:
+            # We need to calculate the HA1 from the username:realm:password
+            m = Digest.algorithms[pszAlg]()
+            m.update(pszUserName)
+            m.update(&quot;:&quot;)
+            m.update(pszRealm)
+            m.update(&quot;:&quot;)
+            m.update(pszPassword)
+            HA1 = m.digest()
+        else:
+            # We were given a username:realm:password
+            HA1 = preHA1.decode('hex')
+    
+        if pszAlg == &quot;md5-sess&quot;:
+            m = Digest.algorithms[pszAlg]()
+            m.update(HA1)
+            m.update(&quot;:&quot;)
+            m.update(pszNonce)
+            m.update(&quot;:&quot;)
+            m.update(pszCNonce)
+            HA1 = m.digest()
+    
+        return HA1.encode('hex')
+    
+    # DigestCalcResponse
+    @staticmethod
+    def calcResponse(
+        HA1,
+        algo,
+        pszNonce,
+        pszNonceCount,
+        pszCNonce,
+        pszQop,
+        pszMethod,
+        pszDigestUri,
+        pszHEntity,
+    ):
+        m = Digest.algorithms[algo]()
+        m.update(pszMethod)
+        m.update(&quot;:&quot;)
+        m.update(pszDigestUri)
+        if pszQop == &quot;auth-int&quot;:
+            m.update(&quot;:&quot;)
+            m.update(pszHEntity)
+        HA2 = m.digest().encode('hex')
+    
+        m = Digest.algorithms[algo]()
+        m.update(HA1)
+        m.update(&quot;:&quot;)
+        m.update(pszNonce)
+        m.update(&quot;:&quot;)
+        if pszNonceCount and pszCNonce: # pszQop:
+            m.update(pszNonceCount)
+            m.update(&quot;:&quot;)
+            m.update(pszCNonce)
+            m.update(&quot;:&quot;)
+            m.update(pszQop)
+            m.update(&quot;:&quot;)
+        m.update(HA2)
+        respHash = m.digest().encode('hex')
+        return respHash
+
+    def generateResponse(self, request):
+        self.response = Digest.calcResponse(
+            Digest.calcHA1(
+                self.fields.get(&quot;algorithm&quot;, &quot;md5&quot;),
+                self.fields.get(&quot;username&quot;, &quot;&quot;),
+                self.fields.get(&quot;realm&quot;, &quot;&quot;),
+                self.fields.get(&quot;password&quot;, &quot;&quot;),
+                self.fields.get(&quot;nonce&quot;, &quot;&quot;),
+                self.fields.get(&quot;cnonce&quot;, &quot;&quot;),
+            ),
+            self.fields.get(&quot;algorithm&quot;, &quot;md5&quot;),
+            self.fields.get(&quot;nonce&quot;, &quot;&quot;),
+            self.fields.get(&quot;nc&quot;, &quot;&quot;),
+            self.fields.get(&quot;cnonce&quot;, &quot;&quot;),
+            self.fields.get(&quot;qop&quot;, &quot;&quot;),
+            request.method,
+            request.url,
+            None,
+        )
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpauthenticationtests__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/authentication/tests/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/authentication/tests/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/authentication/tests/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpauthenticationteststest_basicpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/authentication/tests/test_basic.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/authentication/tests/test_basic.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/authentication/tests/test_basic.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,29 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.authentication.basic import Basic
+from protocol.http.definitions import headers
+
+import unittest
+
+class TestBasic(unittest.TestCase):
+    
+    def testBasic(self):
+        
+        auther = Basic(&quot;user&quot;, &quot;pswd&quot;)
+        hdrs = []
+        auther.addHeaders(hdrs, None)
+        self.assertTrue((headers.Authorization, &quot;Basic dXNlcjpwc3dk&quot;) in hdrs)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpdata__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/data/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/data/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/data/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpdatadatapy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/data/data.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/data/data.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/data/data.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,46 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+
+class Data(object):
+    
+    def __init__(self):
+        pass
+        
+    def start(self):
+        pass
+    
+    def stop(self):
+        pass
+
+class RequestData(Data):
+
+    def getContentLength(self):
+        return self.content_length
+
+    def getContentType(self):
+        return self.content_type
+    
+    def read(self):
+        raise NotImplementedError
+
+class ResponseData(Data):
+    
+    def write(self, data):
+        raise NotImplementedError
+
+    def clear(self):
+        raise NotImplementedError
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpdatafilepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/data/file.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/data/file.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/data/file.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,68 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.data.data import ResponseData
+from protocol.http.data.data import RequestData
+import stat
+import os
+
+class RequestDataFile(RequestData):
+    
+    def __init__(self, fname, content_type):
+
+        # Cache file name
+        self.fname = fname
+    
+        # Determine size of stream
+        self.content_length = os.stat(self.fname)[stat.ST_SIZE]
+
+        self.content_type = content_type
+
+    def start(self):
+        # Create an input file stream
+        self.stream = open(self.fname, &quot;r&quot;)
+
+    def stop(self):
+        self.stream.close()
+        self.stream = None
+
+    def read(self):
+        data = self.stream.read(8192)
+        if data:
+            return data, True
+        else:
+            return data, False
+
+class ResponseDataFile(ResponseData):
+
+    def __init__(self, fname):
+        self.fname = fname
+
+    def start(self):
+        # Create an input file stream
+        self.stream = open(self.fname, &quot;w&quot;)
+
+    def stop(self):
+        self.stream.close()
+        self.stream = None
+
+    def write(self, data):
+        self.stream.write(data)
+
+    def clear(self):
+        # Throw out existing data and start from scratch
+        self.stop()
+        self.start()
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpdatastringpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/data/string.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/data/string.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/data/string.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.data.data import ResponseData
+from protocol.http.data.data import RequestData
+from StringIO import StringIO
+
+class RequestDataString(RequestData):
+    
+    def __init__(self, text, content_type):
+
+        # Cache file name
+        self.text = text
+    
+        # Determine size of stream
+        self.content_length = len(text)
+
+        self.content_type = content_type
+
+    def read(self):
+        return self.text, False
+        
+class ResponseDataString(ResponseData):
+
+    def __init__(self):
+        self.stream = StringIO()
+
+    def getData(self):
+        return self.stream.getvalue()
+
+    def write(self, data):
+        self.stream.write(data)
+
+    def clear(self):
+        # Throw out existing data and start from scratch
+        self.stream = StringIO()
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpdefinitions__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/definitions/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/definitions/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/definitions/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpdefinitionsheaderspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/definitions/headers.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/definitions/headers.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/definitions/headers.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,45 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+# HTTP protocol headers
+
+# RFC2616 4.5 - General Header fields (only the ones we need)
+
+Connection              = &quot;Connection&quot;
+ConnectionClose         = &quot;close&quot;
+Date                    = &quot;Date&quot;
+TransferEncoding        = &quot;Transfer-Encoding&quot;
+TransferEncodingChunked = &quot;chunked&quot;
+
+# RFC2616 5.3 - Request Header fields (only the ones we need)
+
+Authorization = &quot;Authorization&quot;
+Host          = &quot;Host&quot;
+IfMatch       = &quot;If-Match&quot;
+IfNoneMatch   = &quot;If-None-Match&quot;
+
+# RFC2616 6.2 - Response Header fields (only the ones we need)
+
+ETag            = &quot;ETag&quot;
+Location        = &quot;Location&quot;
+Server          = &quot;Server&quot;
+WWWAuthenticate = &quot;WWW-Authenticate&quot;
+
+# RFC2616 7.1 - Entity Header fields (only the ones we need)
+
+Allow         = &quot;Allow&quot;
+ContentLength = &quot;Content-Length&quot;
+ContentType   = &quot;Content-Type&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpdefinitionsmethodspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/definitions/methods.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/definitions/methods.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/definitions/methods.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,27 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+
+# RFC2616 5.1.1 - Request Methods
+
+OPTIONS = &quot;OPTIONS&quot;
+GET     = &quot;GET&quot;
+HEAD    = &quot;HEAD&quot;
+POST    = &quot;POST&quot;
+PUT     = &quot;PUT&quot;
+DELETE  = &quot;DELETE&quot;
+TRACE   = &quot;TRACE&quot;
+CONNECT = &quot;CONNECT&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpdefinitionsstatuscodespy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/definitions/statuscodes.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/definitions/statuscodes.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/definitions/statuscodes.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,58 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+
+Unknown = 0
+Continue = 100
+SwitchingProtocols = 101
+OK = 200
+Created = 201
+Accepted = 202
+NonAuthoritativeInformation = 203
+NoContent = 204
+ResetContent = 205
+PartialContent = 206
+MultipleChoices = 300
+MovedPermanently = 301
+Found = 302
+SeeOther = 303
+NotModified = 304
+UseProxy = 305
+TemporaryRedirect = 307
+BadRequest = 400
+Unauthorized = 401
+PaymentRequired = 402
+Forbidden = 403
+NotFound = 404
+MethodNotAllowed = 405
+NotAcceptable = 406
+ProxyAuthenticationRequired = 407
+RequestTimeout = 408
+Conflict = 409
+Gone = 410
+LengthRequired = 411
+PreconditionFailed = 412
+RequestEntityTooLarge = 413
+RequestURITooLarge = 414
+UnsupportedMediaType = 415
+RequestedRangeNotSatisfiable = 416
+ExpectationFailed = 417
+InternalServerError = 500
+NotImplemented = 501
+BadGateway = 502
+ServiceUnavailable = 503
+GatewayTimeout = 504
+HTTPVersionNotSupported = 505
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttprequestresponsepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/requestresponse.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/requestresponse.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/requestresponse.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,274 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from StringIO import StringIO
+from protocol.http.definitions import headers
+from protocol.http.definitions import methods
+from protocol.http.definitions import statuscodes
+
+class ResponseError(Exception):
+    pass
+
+class RequestResponse(object):
+    
+    def __init__(self, session, method, url, etag=None, etag_match=False):
+        self._initResponse()
+        self.session = session
+        self.method = method
+        self.url = url
+        self.etag = etag
+        self.etag_match = etag_match
+
+    def _initResponse(self):
+        self.session = None
+        self.request_data = None
+        self.response_data = None
+        self.method = methods.GET
+        self.url = None
+        self.etag = None
+        self.etag_match = False
+        self.status_code = statuscodes.Unknown
+        self.status_reason = None
+        self.headers = {}
+        self.connection_close = False
+        self.content_length = 0
+        self.chunked = False
+        self.completed = False
+
+    def setSession(self, session):
+        self.session = session
+    def getSession(self):
+        return self.session
+
+    def getMethod(self):
+        return self.method
+
+    def setURL(self, ruri):
+        self.url = ruri
+    def getURL(self):
+        return self.url
+
+    def setETag(self, etag, etag_match):
+        self.etag = etag
+        self.etag_match = etag_match
+
+    def queuedForSending(self, session):
+        self.session = session
+    
+    def setData(self, request_data, response_data):
+        self.request_data = request_data
+        self.response_data = response_data
+
+    def hasRequestData(self):
+        return self.request_data != None
+
+    def hasResponseData(self):
+        return self.response_data != None
+
+    def getRequestData(self):
+        if self.request_data:
+            return self.request_data
+        else:
+            return None
+
+    def getResponseData(self):
+        if self.response_data:
+            return self.response_data
+        else:
+            return None
+    
+    def getRequestStartLine(self):
+        return &quot;%s %s HTTP/1.1&quot; % (self.method, self.url,)
+
+    def getRequestHeaders(self):
+        # This will be overridden by sub-classes that add headers - those classes should
+        # call this class's implementation to write out the basic set of headers
+        result = []
+        self.addHeaders(result)
+        return tuple(result)
+
+    def generateRequestHeader(self):
+        os = StringIO()
+        os.write(&quot;%s\r\n&quot; % (self.getRequestStartLine(),))
+        for header, value in self.getRequestHeaders():
+            os.write(&quot;%s: %s\r\n&quot; % (header, value,))
+        os.write(&quot;\r\n&quot;)
+        return os.getvalue()
+
+    def addHeaders(self, hdrs):
+
+        # Write host
+        hdrs.append((headers.Host, self.session.server))
+        
+        # Do ETag matching
+        if self.etag:
+            if self.etag_match:
+                hdrs.append((headers.IfMatch, self.etag))
+            else:
+                hdrs.append((headers.IfNoneMatch, self.etag))
+    
+        # Do session global headers
+        self.session.addHeaders(hdrs, self)
+        
+        # Check for content
+        self.addContentHeaders(hdrs)
+
+    def addContentHeaders(self, hdrs):
+        # Check for content
+        if self.hasRequestData():
+            hdrs.append((headers.ContentLength, self.request_data.getContentLength()))
+            hdrs.append((headers.ContentType, self.request_data.getContentType()))
+
+    def setResponseStatus(self, version, status, reason):
+        self.status_code = status
+        self.status_reason = reason
+
+    def setResponseHeaders(self, hdrs):
+        for header in hdrs:
+            splits = header.split(&quot;:&quot;)
+            self.headers.setdefault(splits[0].strip().lower(), []).append(splits[1].strip())
+
+        # Now cache some useful header values
+        self.cacheHeaders()
+
+    def clearResponse(self):
+        self.etag_match = False
+        self.status_code = statuscodes.Unknown
+        self.status_reason = None
+        self.headers = {}
+        self.connection_close = False
+        self.content_length = 0
+        self.chunked = False
+        self.completed = False
+    
+        if self.response_data:
+            self.response_data.clear()
+
+    def getStatusCode(self):
+        return self.status_code
+    def getStatusReason(self):
+        return self.status_reason
+
+    def getConnectionClose(self):
+        return self.connection_close
+
+    def getContentLength(self):
+        return self.content_length
+    def getChunked(self):
+        return self.chunked
+
+    def setComplete(self):
+        self.completed = True
+    def getCompleted(self):
+        return self.completed
+
+    def hasResponseHeader(self, hdr):
+        return self.headers.has_key(hdr.lower())
+    
+    def getResponseHeader(self, hdr):
+        if self.headers.has_key(hdr.lower()):
+            return self.headers[hdr.lower()][0]
+        else:
+            return &quot;&quot;
+    
+    def getResponseHeaders(self, hdr=None):
+        if hdr:
+            if self.headers.has_key(hdr.lower()):
+                return self.headers[hdr.lower()]
+            else:
+                return ()
+        else:
+            return self.headers
+    
+    def isRedirect(self):
+        # Only these are allowed
+        return self.status_code in (statuscodes.MovedPermanently, statuscodes.Found, statuscodes.TemporaryRedirect)
+
+    def parseStatusLine(self, line):
+        
+        # Must have 'HTTP/' version at start
+        if line[0:5] != &quot;HTTP/&quot;:
+            raise ResponseError(&quot;status line incorrect at start&quot;)
+        
+        # Must have version '1.1 '
+        if line[5:9] != &quot;1.1 &quot;:
+            raise ResponseError(&quot;incorrect http version in status line&quot;)
+        
+        # Must have three digits followed by nothing or one space
+        if not line[9:12].isdigit() or (len(line) &gt; 12 and line[12] != &quot; &quot;):
+            raise ResponseError(&quot;invalid status response code syntax&quot;)
+        
+        # Read in the status code
+        self.status_code = int(line[9:12])
+        
+        # Remainder is reason
+        if len(line) &gt; 13:
+            self.status_reason = line[13:]
+
+    def readFoldedLine(self, instream, line1, line2, log):
+        # If line2 already has data, transfer that into line1
+        if line2 or line1:
+            line1 = line2
+        else:
+            # Fill first line
+            line1 = instream.readline()
+            if not line1:
+                return False, line1, line2
+            line1 = line1.rstrip(&quot;\r\n&quot;)
+    
+            if log:
+                log.write(&quot;%s\n&quot; % (line1,))
+    
+        # Terminate on blank line which is end of headers
+        if not line1:
+            return True, line1, line2
+    
+        # Now loop looking ahead at the next line to see if it is folded
+        while True:
+            # Get next line
+            line2 = instream.readline()
+            if not line2:
+                return True, line1, line2
+            line2 = line2.rstrip(&quot;\r\n&quot;)
+    
+            if log:
+                log.write(&quot;%s\n&quot; % (line2,))
+            
+            # Does it start with a space =&gt; folded
+            if line2 and line2[0].isspace():
+                # Copy folded line (without space) to current line and cycle for more
+                line1 += line2[1:]
+            else:
+                # Not folded - just exit loop
+                break
+    
+        return True, line1, line2
+
+    def cacheHeaders(self):
+        # Connection
+        if self.headers.has_key(headers.Connection):
+            value = self.headers[headers.Connection][0]
+            self.connection_close = (value.lower() == headers.ConnectionClose)
+
+        # Content-Length
+        if self.headers.has_key(headers.ContentLength):
+            value = self.headers[headers.ContentLength][0]
+            self.content_length = int(value)
+        
+        # Transfer encoding
+        if self.headers.has_key(headers.TransferEncoding):
+            value = self.headers[headers.TransferEncoding][0]
+            self.chunked = (value == headers.TransferEncodingChunked)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpsessionpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/session.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/session.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/session.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,122 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from httplib import InvalidURL
+import httplib
+
+class Session(object):
+    
+    STATE_OPEN   = 0
+    STATE_CLOSED = 1
+    
+    def __init__(self, server, port=None, ssl=False, log=None):
+        
+        self.server = server
+        self.port = port
+        self.ssl = ssl
+        if not self.port:
+            self.port = httplib.HTTPS_PORT if ssl else httplib.HTTP_PORT
+        self.authorization = None
+        self.connection_state = Session.STATE_CLOSED
+        self.log = log
+        
+    def hasAuthorization(self):
+        return self.authorization != None
+    
+    def getAuthorization(self):
+        return self.authorization
+    
+    def addHeaders(self, hdrs, request):
+        if self.hasAuthorization():
+            self.getAuthorization().addHeaders(hdrs, request)
+    
+    def setServer(self, server, port=None):
+        
+        if port is None:
+            i = server.rfind(':')
+            j = server.rfind(']')
+            if i &gt; j:
+                try:
+                    port = int(server[i+1:])
+                except ValueError:
+                    raise InvalidURL(&quot;nonnumeric port: '%s'&quot; % server[i+1:])
+                server = server[:i]
+            else:
+                port = httplib.HTTPS_PORT if self.ssl else httplib.HTTP_PORT
+            if server and server[0] == '[' and server[-1] == ']':
+                server = server[1:-1]
+
+        if self.server != server:
+            self.server = server
+            self.port = port
+            
+            # Always clear out authorization when host changes
+            self.authorization = None
+    
+    def sendRequest(self, request):
+        try:
+
+            # First need a connection
+            self.needConnection();
+            
+            # Now do the connection
+            self.doRequest(request);
+            
+            # Check the final connection state and close if that's what the server wants
+            if request.getConnectionClose():
+                self.closeConnection()
+
+        except Exception:
+
+            # log.err(e)
+            self.connection_state = Session.STATE_CLOSED
+            raise
+    
+    def handleHTTPError(self, request):
+        raise NotImplementedError
+    
+    def displayHTTPError(self, request):
+        raise NotImplementedError
+    
+    def isConnectionOpen(self):
+        return self.connection_state == Session.STATE_OPEN
+
+    def needConnection(self):
+        if not self.isConnectionOpen():
+            self.openConnection()
+
+    def openConnection(self):
+        if not self.isConnectionOpen():
+            self.openSession()
+            self.connection_state = Session.STATE_OPEN
+
+    def closeConnection(self):
+        if self.isConnectionOpen():
+            self.closeSession();
+            self.connection_state = Session.STATE_CLOSED
+
+    def openSession(self):
+        raise NotImplementedError
+    
+    def closeSession(self):
+        raise NotImplementedError
+    
+    def runSession(self, request):
+        raise NotImplementedError
+        
+    def doRequest(self, request):
+        raise NotImplementedError
+    
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttptests__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/tests/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/tests/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/tests/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpteststest_requestresponsepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/tests/test_requestresponse.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/tests/test_requestresponse.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/tests/test_requestresponse.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,89 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.requestresponse import RequestResponse
+from protocol.http.session import Session
+from protocol.http.data.string import RequestDataString
+from protocol.http.authentication.basic import Basic
+from protocol.http.definitions import methods
+
+import unittest
+
+class TestRequestHeaders(unittest.TestCase):
+    
+    def test_NoEtag(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = RequestResponse(server, methods.GET, &quot;/&quot;)
+        self.assertEqual(request.generateRequestHeader(), &quot;&quot;&quot;GET / HTTP/1.1
+Host: www.example.com
+
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)

+    def test_EtagMatch(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = RequestResponse(server, methods.GET, &quot;/&quot;, &quot;\&quot;etag\&quot;&quot;, True)
+        self.assertEqual(request.generateRequestHeader(), &quot;&quot;&quot;GET / HTTP/1.1
+Host: www.example.com
+If-Match: &quot;etag&quot;
+
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)

+    def test_EtagNoneMatch(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = RequestResponse(server, methods.GET, &quot;/&quot;, &quot;\&quot;etag\&quot;&quot;, False)
+        self.assertEqual(request.generateRequestHeader(), &quot;&quot;&quot;GET / HTTP/1.1
+Host: www.example.com
+If-None-Match: &quot;etag&quot;
+
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)

+    def test_Content(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = RequestResponse(server, methods.GET, &quot;/&quot;)
+        rawdata = &quot;Here is some data\r\non multiple lines.&quot;
+        data = RequestDataString(rawdata, &quot;text/plain&quot;)
+        request.setData(data, None)
+        self.assertEqual(request.generateRequestHeader(), &quot;&quot;&quot;GET / HTTP/1.1
+Host: www.example.com
+Content-Length: %d
+Content-Type: text/plain
+
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;) % (len(rawdata),)
+)
+
+    def test_ContentAndAuthorization(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        server.authorization = Basic(&quot;user&quot;, &quot;pswd&quot;)
+        request = RequestResponse(server, methods.GET, &quot;/&quot;)
+        rawdata = &quot;Here is some data\r\non multiple lines.&quot;
+        data = RequestDataString(rawdata, &quot;text/plain&quot;)
+        request.setData(data, None)
+        self.assertEqual(request.generateRequestHeader(), &quot;&quot;&quot;GET / HTTP/1.1
+Host: www.example.com
+Authorization: Basic dXNlcjpwc3dk
+Content-Length: %d
+Content-Type: text/plain
+
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;) % (len(rawdata),)
+)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttpteststest_utilpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/tests/test_util.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/tests/test_util.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/tests/test_util.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,107 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.util import parsequoted
+from protocol.http.util import parsetoken
+from protocol.http.util import parseStatusLine
+
+import unittest
+
+class TestParseQuoted(unittest.TestCase):
+    
+    def testParseQuotedOK(self):
+        
+        data = {
+            &quot;\&quot;\&quot;&quot;                                  : (&quot;&quot;,                         &quot;&quot;),
+            &quot;\&quot;quoted\&quot;&quot;                            : (&quot;quoted&quot;,                   &quot;&quot;),
+            &quot;\&quot;quoted words\&quot;&quot;                      : (&quot;quoted words&quot;,             &quot;&quot;),
+            &quot;\&quot;quoting a \\\&quot;word\\\&quot;\&quot;&quot;            : (&quot;quoting a \&quot;word\&quot;&quot;,       &quot;&quot;),
+            &quot;\&quot;\&quot; after&quot;                            : (&quot;&quot;,                         &quot;after&quot;),
+            &quot;\&quot;quoted\&quot; after&quot;                      : (&quot;quoted&quot;,                   &quot;after&quot;),
+            &quot;\&quot;quoted words\&quot; after&quot;                : (&quot;quoted words&quot;,             &quot;after&quot;),
+            &quot;\&quot;quoting a \\\&quot;word\\\&quot;\&quot; after&quot;      : (&quot;quoting a \&quot;word\&quot;&quot;,       &quot;after&quot;),
+            &quot;\&quot;quoting a \\\&quot;word\\\&quot; after\&quot; after&quot;: (&quot;quoting a \&quot;word\&quot; after&quot;, &quot;after&quot;),
+            &quot;\&quot;quoted\&quot;after&quot;                       : (&quot;quoted&quot;,                   &quot;after&quot;),
+            &quot;\&quot;&quot;                                    : (&quot;&quot;,                         &quot;&quot;),
+            &quot;\&quot;unterminated&quot;                        : (&quot;unterminated&quot;,             &quot;&quot;),
+            &quot;\&quot;unterminated words&quot;                  : (&quot;unterminated words&quot;,       &quot;&quot;),
+            &quot;\&quot;unterminated a \\\&quot;word\\\&quot;&quot;         : (&quot;unterminated a \&quot;word\&quot;&quot;,  &quot;&quot;),
+         }
+        
+        for input, result in data.iteritems():
+            self.assertEqual(parsequoted(input), result)
+        
+    def testParseQuotedBAD(self):
+        
+        data = (
+            &quot;&quot;,
+            &quot;unquoted&quot;,
+            &quot;unquoted \&quot;quoted\&quot;&quot;,
+        )
+        
+        for input in data:
+            self.assertRaises(AssertionError, parsequoted, input)
+
+class TestParseToken(unittest.TestCase):
+    
+    def testParseTokenOK(self):
+        
+        data = {
+            &quot;&quot;                                      : (&quot;&quot;,          &quot;&quot;),
+            &quot;unquoted&quot;                              : (&quot;unquoted&quot;,  &quot;&quot;),
+            &quot;unquoted words&quot;                        : (&quot;unquoted&quot;,  &quot;words&quot;),
+            &quot;unquoted  words&quot;                       : (&quot;unquoted&quot;,  &quot;words&quot;),
+            &quot;unquoting a \&quot;word\&quot;&quot;                  : (&quot;unquoting&quot;, &quot;a \&quot;word\&quot;&quot;),
+            &quot;unquoted\twords&quot;                       : (&quot;unquoted&quot;,  &quot;words&quot;),
+            &quot;unquoting\ta \&quot;word\&quot;&quot;                 : (&quot;unquoting&quot;, &quot;a \&quot;word\&quot;&quot;),
+            &quot;unquoted: words&quot;                       : (&quot;unquoted&quot;,  &quot;words&quot;),
+            &quot;unquoting: a \&quot;word\&quot;&quot;                 : (&quot;unquoting&quot;, &quot;a \&quot;word\&quot;&quot;),
+
+            &quot;\&quot;\&quot;&quot;                                  : (&quot;&quot;,                         &quot;&quot;),
+            &quot;\&quot;quoted\&quot;&quot;                            : (&quot;quoted&quot;,                   &quot;&quot;),
+            &quot;\&quot;quoted words\&quot;&quot;                      : (&quot;quoted words&quot;,             &quot;&quot;),
+            &quot;\&quot;quoting a \\\&quot;word\\\&quot;\&quot;&quot;            : (&quot;quoting a \&quot;word\&quot;&quot;,       &quot;&quot;),
+            &quot;\&quot;\&quot; after&quot;                            : (&quot;&quot;,                         &quot;after&quot;),
+            &quot;\&quot;quoted\&quot; after&quot;                      : (&quot;quoted&quot;,                   &quot;after&quot;),
+            &quot;\&quot;quoted words\&quot; after&quot;                : (&quot;quoted words&quot;,             &quot;after&quot;),
+            &quot;\&quot;quoting a \\\&quot;word\\\&quot;\&quot; after&quot;      : (&quot;quoting a \&quot;word\&quot;&quot;,       &quot;after&quot;),
+            &quot;\&quot;quoting a \\\&quot;word\\\&quot; after\&quot; after&quot;: (&quot;quoting a \&quot;word\&quot; after&quot;, &quot;after&quot;),
+            &quot;\&quot;quoted\&quot;after&quot;                       : (&quot;quoted&quot;,                   &quot;after&quot;),
+            &quot;\&quot;&quot;                                    : (&quot;&quot;,                         &quot;&quot;),
+            &quot;\&quot;unterminated&quot;                        : (&quot;unterminated&quot;,             &quot;&quot;),
+            &quot;\&quot;unterminated words&quot;                  : (&quot;unterminated words&quot;,       &quot;&quot;),
+            &quot;\&quot;unterminated a \\\&quot;word\\\&quot;&quot;         : (&quot;unterminated a \&quot;word\&quot;&quot;,  &quot;&quot;),
+        }
+        
+        for input, result in data.iteritems():
+            self.assertEqual(parsetoken(input, &quot; \t:&quot;), result)
+
+class TestParseStatusLine(unittest.TestCase):
+    
+    def testParseTokenOK(self):
+        self.assertEqual(parseStatusLine(&quot;HTTP/1.1 200 OK&quot;), 200)
+    
+    def testParseTokenBadStatus(self):
+        self.assertEqual(parseStatusLine(&quot;HTTP/1.2 2001 OK&quot;), 0)
+    
+    def testParseTokenBadVersion(self):
+        self.assertEqual(parseStatusLine(&quot;HTTP/1.2 200 OK&quot;), 0)
+    
+    def testParseTokenBadNumber(self):
+        self.assertEqual(parseStatusLine(&quot;HTTP/1.1 OK&quot;), 0)
+    
+    def testParseTokenBad(self):
+        self.assertEqual(parseStatusLine(&quot;HTTP/1.1&quot;), 0)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolhttputilpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/http/util.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/http/util.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/http/util.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,74 @@
</span><ins>+##
+# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+
+def parsetoken(text, delimiters=&quot; \t&quot;):
+    
+    if not text:
+        return &quot;&quot;, &quot;&quot;
+
+    if text[0] == '&quot;':
+        return parsequoted(text, delimiters)
+    else:
+        for pos, c in enumerate(text):
+            if c in delimiters:
+                token = text[0:pos]
+                break
+        else:
+            return text, &quot;&quot;
+        
+        return token, lstripdelimiters(text[pos:], delimiters)
+        
+def parsequoted(text, delimiters=&quot; \t&quot;):
+    
+    assert(text)
+    assert(text[0] == '&quot;')
+    
+    pos = 1
+    while True:
+        next_pos = text.find('&quot;', pos)
+        if next_pos == -1:
+            return text[1:].replace(&quot;\\\\&quot;, &quot;\\&quot;).replace(&quot;\\\&quot;&quot;, &quot;\&quot;&quot;), &quot;&quot;
+        if text[next_pos - 1] == '\\':
+            pos = next_pos + 1
+        else:
+            return (
+                text[1:next_pos].replace(&quot;\\\\&quot;, &quot;\\&quot;).replace(&quot;\\\&quot;&quot;, &quot;\&quot;&quot;),
+                lstripdelimiters(text[next_pos+1:], delimiters)
+            )
+
+def lstripdelimiters(text, delimiters):
+    for pos, c in enumerate(text):
+        if c not in delimiters:
+            return text[pos:]
+    else:
+        return &quot;&quot;
+
+def parseStatusLine(status):
+
+    status = status.strip()
+
+    # Must have 'HTTP/1.1' version at start
+    if status[0:9] != &quot;HTTP/1.1 &quot;:
+        return 0
+    
+    # Must have three digits followed by nothing or one space
+    if not status[9:12].isdigit() or (len(status) &gt; 12 and status[12] != &quot; &quot;):
+        return 0
+    
+    # Read in the status code
+    return int(status[9:12])
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocoltests__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/tests/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/tests/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/tests/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolteststest_urlpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/tests/test_url.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/tests/test_url.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/tests/test_url.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.url import URL
+
+import unittest
+
+class TestURLParse(unittest.TestCase):
+    
+    def verifyParts(self, u, s, scheme, server, path, extended):
+        self.assertEqual(u.toString(), s)
+        self.assertEqual(u.scheme, scheme)
+        self.assertEqual(u.server, server)
+        self.assertEqual(u.path, path)
+        self.assertEqual(u.extended, extended)
+
+    def test_ParsePlain(self):
+        
+        s = &quot;http://www.example.com&quot;
+        u = URL(url=s)
+        self.verifyParts(u, s, &quot;http&quot;, &quot;www.example.com&quot;, &quot;&quot;, &quot;&quot;)
+
+    def test_ParsePlainPath(self):
+        
+        s = &quot;http://www.example.com/principals/users&quot;
+        u = URL(url=s)
+        self.verifyParts(u, s, &quot;http&quot;, &quot;www.example.com&quot;, &quot;/principals/users&quot;, &quot;&quot;)
+
+    def test_ParsePlainPathExtended(self):
+        
+        s = &quot;http://www.example.com/principals/users?test=true&quot;
+        u = URL(url=s)
+        self.verifyParts(u, s, &quot;http&quot;, &quot;www.example.com&quot;, &quot;/principals/users&quot;, &quot;?test=true&quot;)
+
+    def test_ParseMailto(self):
+        
+        s = &quot;mailto:user@example.com&quot;
+        u = URL(url=s)
+        self.verifyParts(u, s, &quot;mailto&quot;, &quot;user@example.com&quot;, &quot;&quot;, &quot;&quot;)
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolurlpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/url.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/url.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/url.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,219 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+import os
+import urllib
+
+class URL(object):
+    
+    eAbsolute = 0
+    eRelative = 1
+    eLastPath = 2
+
+    URLEscape = '%'
+    URLReserved = &quot;/?:@&amp;=&quot;
+    URLUnreserved = ( # Allowable URL chars
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 0 - 15
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 16 - 31
+        0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,        # 32 - 47
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,        # 48 - 63
+        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,        # 64 - 79
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1,        # 80 - 95
+        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,        # 96 - 111
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,        # 112 - 127
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 128 - 143
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 144 - 159
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 160 - 175
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 176 - 191
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 192 - 207
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 208 - 223
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 224 - 239
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         # 240 - 255
+    )
+    
+    URLCharacter = ( # Allowable URL chars -- all
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 0 - 15
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 16 - 31
+        0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,        # 32 - 47
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,        # 48 - 63
+        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,        # 64 - 79
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1,        # 80 - 95
+        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,        # 96 - 111
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,        # 112 - 127
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 128 - 143
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 144 - 159
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 160 - 175
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 176 - 191
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 192 - 207
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 208 - 223
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 224 - 239
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 240 - 255
+    )
+    
+    URLXCharacter = ( # Allowable URL chars (all)
+          # RFC2732 uses '[...]' for IPv6 addressing - [] are now allowed
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 0 - 15
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 16 - 31
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,        # 32 - 47
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,        # 48 - 63
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,        # 64 - 79
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,        # 80 - 95
+        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,        # 96 - 111
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,        # 112 - 127
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 128 - 143
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 144 - 159
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 160 - 175
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 176 - 191
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 192 - 207
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 208 - 223
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 224 - 239
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        # 240 - 255
+    )
+
+    URLSchemeDoubleSlash = (&quot;http&quot;, &quot;https&quot;, &quot;webcal&quot;,)
+
+    def __init__(self, url=None, scheme=None, server=None, path=None, extended=None, decode=False):
+        
+        self.scheme = &quot;&quot;
+        self.server = &quot;&quot;
+        self.path = &quot;&quot;
+        self.extended = &quot;&quot;
+
+        if not url:
+            self.scheme = scheme
+            self.server = server
+            self.path = path
+            if self.path and decode:
+                self.path = urllib.unquote(self.path)
+            self.extended = extended
+            if self.extended and decode:
+                self.extended = urllib.unquote_plus(self.extended)
+        else:
+            self._parse(url, decode)
+    
+    def __str__(self):
+        return &quot;URL: %s&quot; % (self.toString(),)
+
+    def __repr__(self):
+        return &quot;URL: %s&quot; % (self.toString(),)
+
+    def __cmp__(self, other):
+        return cmp(self.toString(), other.toString())
+
+    def absoluteURL(self):
+        return self.toString()
+    
+    def relativeURL(self):
+        return self.toString(conversion=URL.eRelative)
+
+    def toString(self, conversion=eAbsolute, encode=True):
+
+        result = &quot;&quot;
+    
+        # Add scheme &amp; host if not relative
+        if conversion == URL.eAbsolute and self.scheme and self.server:
+            result += self.scheme + &quot;:&quot;
+            if self.scheme in URL.URLSchemeDoubleSlash:
+                result += &quot;//&quot;
+            result += self.server
+    
+        # Get path (or last part of it if required)
+        if self.path and conversion == URL.eLastPath:
+            path = self.path[self.path.rfind(&quot;/&quot;):]
+        else:
+            path = self.path
+    
+        # Now encode if required
+        if path:
+            result += (urllib.quote(path) if encode else path)
+        
+        if self.extended:
+            result += (urllib.quote_plus(self.extended, &quot;?=&quot;) if encode else self.extended)
+        
+        return result
+
+    def equal(self, comp):
+        # Compare each component
+
+        if self.scheme != comp.scheme:
+            return False
+        
+        if self.server != comp.server:
+            return False
+        
+        # Ignore trailing slash
+        if self.path.rstrip(&quot;/&quot;) != comp.path.rstrip(&quot;/&quot;):
+            return False
+
+        return True
+
+    def equalRelative(self, comp):
+        # Must be relative
+        if comp.server:
+            return False
+    
+        # Just compare paths, ignore trailing slash
+        return self.path.rstrip(&quot;/&quot;) == comp.path.rstrip(&quot;/&quot;)
+    
+    def dirname(self):
+        if self.path:
+            newpath = os.path.dirname(self.path.rstrip(&quot;/&quot;)) + &quot;/&quot;
+        return URL(scheme=self.scheme, server=self.server, path=newpath)
+
+    def _parse(self, url, decode=False):
+    
+        # Strip off main scheme
+        if url.lower().startswith(&quot;url:&quot;):
+            url = url[4:]
+    
+        # Special - if it starts with &quot;/&quot; its a relative HTTP url
+        if url[0] == '/':
+            self.scheme = &quot;http&quot;
+            self.server = None
+            self._parsePath(url, decode)
+        else:
+            # Get protocol scheme
+            self.scheme = url[:url.find(&quot;:&quot;)].lower()
+            url = url[len(self.scheme):]
+        
+            if self.scheme in URL.URLSchemeDoubleSlash:
+    
+                assert(url.startswith(&quot;://&quot;))
+    
+                # Look for server
+                splits = url[3:].split(&quot;/&quot;, 1)
+                self.server = splits[0]
+                if len(splits) == 2:
+                    self._parsePath(&quot;/&quot; + splits[1], decode)
+            
+            elif self.scheme in (&quot;mailto&quot;, &quot;urn&quot;,):
+    
+                assert(url.startswith(&quot;:&quot;))
+    
+                # Look for server
+                self.server = url[1:]
+
+    def _parsePath(self, path, decode=False):
+        
+        # Look for extended bits
+        splits = path.split(&quot;?&quot;, 1)
+        self.path = splits[0]
+        if decode:
+            self.path = urllib.unquote(self.path)
+        if len(splits) == 2:
+            self.extended = &quot;?&quot; + splits[1]
+            if decode:
+                self.extended = urllib.unquote_plus(self.extended)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolutils__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/utils/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/utils/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/utils/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolutilsxmlhelperspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/utils/xmlhelpers.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/utils/xmlhelpers.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/utils/xmlhelpers.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,107 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from xml.etree.ElementTree import ElementTree
+from xml.etree.ElementTree import Comment
+from xml.etree.ElementTree import _escape_cdata
+from xml.etree.ElementTree import ProcessingInstruction
+from xml.etree.ElementTree import QName
+from xml.etree.ElementTree import fixtag
+from xml.etree.ElementTree import _raise_serialization_error
+from xml.etree.ElementTree import _encode
+from xml.etree.ElementTree import _escape_attrib
+from StringIO import StringIO
+from xml.etree.ElementTree import SubElement
+
+def SubElementWithData(parent, tag, data=None, attrs={}):
+    element = SubElement(parent, tag, attrs)
+    if data:
+        element.text = data
+    return element
+
+class BetterElementTree(ElementTree):
+    
+    def writeUTF8(self, file):
+        assert self._root is not None
+        if not hasattr(file, &quot;write&quot;):
+            file = open(file, &quot;wb&quot;)
+        encoding = &quot;utf-8&quot;
+        file.write(&quot;&lt;?xml version='1.0' encoding='%s'?&gt;&quot; % encoding)
+        self._prettywrite(file, self._root, encoding, {})
+        file.write(&quot;\r\n&quot;)
+
+    def _prettywrite(self, file, node, encoding, namespaces, depth=0):
+        # write XML to file
+        tag = node.tag
+        if tag is Comment:
+            file.write(&quot;\r\n&quot; + &quot;  &quot; * depth)
+            file.write(&quot;&lt;!-- %s --&gt;&quot; % _escape_cdata(node.text, encoding))
+        elif tag is ProcessingInstruction:
+            file.write(&quot;\r\n&quot; + &quot;  &quot; * depth)
+            file.write(&quot;&lt;?%s?&gt;&quot; % _escape_cdata(node.text, encoding))
+        else:
+            items = node.items()
+            xmlns_items = [] # new namespaces in this scope
+            try:
+                if isinstance(tag, QName) or tag[:1] == &quot;{&quot;:
+                    tag, xmlns = fixtag(tag, namespaces)
+                    if xmlns: xmlns_items.append(xmlns)
+            except TypeError:
+                _raise_serialization_error(tag)
+            file.write(&quot;\r\n&quot; + &quot;  &quot; * depth)
+            file.write(&quot;&lt;&quot; + _encode(tag, encoding))
+            if items or xmlns_items:
+                items.sort() # lexical order
+                for k, v in items:
+                    try:
+                        if isinstance(k, QName) or k[:1] == &quot;{&quot;:
+                            k, xmlns = fixtag(k, namespaces)
+                            if xmlns: xmlns_items.append(xmlns)
+                    except TypeError:
+                        _raise_serialization_error(k)
+                    try:
+                        if isinstance(v, QName):
+                            v, xmlns = fixtag(v, namespaces)
+                            if xmlns: xmlns_items.append(xmlns)
+                    except TypeError:
+                        _raise_serialization_error(v)
+                    file.write(&quot; %s=\&quot;%s\&quot;&quot; % (_encode(k, encoding),
+                                               _escape_attrib(v, encoding)))
+                for k, v in xmlns_items:
+                    file.write(&quot; %s=\&quot;%s\&quot;&quot; % (_encode(k, encoding),
+                                               _escape_attrib(v, encoding)))
+            if node.text or len(node):
+                file.write(&quot;&gt;&quot;)
+                if node.text:
+                    file.write(_escape_cdata(node.text, encoding))
+                for n in node:
+                    self._prettywrite(file, n, encoding, namespaces, depth=depth+1)
+                if not node.text or len(node):
+                    file.write(&quot;\r\n&quot; + &quot;  &quot; * depth)
+                file.write(&quot;&lt;/&quot; + _encode(tag, encoding) + &quot;&gt;&quot;)
+            else:
+                file.write(&quot; /&gt;&quot;)
+            for k, v in xmlns_items:
+                del namespaces[v]
+        if node.tail:
+            file.write(_escape_cdata(node.tail, encoding))
+
+def elementToString(element):
+    os = StringIO()
+    xmldoc = BetterElementTree(element)
+    xmldoc.writeUTF8(os)
+    return os.getvalue()
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdav__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.propfindparser import PropFindParser
+from protocol.webdav.definitions import davxml
+
+PropFindParser.textProperties.add(davxml.creationdate)
+PropFindParser.textProperties.add(davxml.displayname)
+PropFindParser.textProperties.add(davxml.getcontentlanguage)
+PropFindParser.textProperties.add(davxml.getcontentlength)
+PropFindParser.textProperties.add(davxml.getcontenttype)
+PropFindParser.textProperties.add(davxml.getetag)
+PropFindParser.textProperties.add(davxml.getlastmodified)
+
+PropFindParser.hrefListProperties.add(davxml.principal_collection_set)
+PropFindParser.hrefProperties.add(davxml.principal_URL)
+PropFindParser.hrefListProperties.add(davxml.alternate_URI_set)
+PropFindParser.hrefListProperties.add(davxml.group_member_set)
+PropFindParser.hrefListProperties.add(davxml.group_membership)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavacepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/ace.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/ace.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/ace.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,169 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from xml.etree.ElementTree import QName
+from protocol.webdav.definitions import davxml
+from xml.etree.ElementTree import SubElement
+
+class ACE(object):
+
+    def __init__(self):
+
+        self.principal = None
+        self.data = None
+        self.invert = False
+        self.grant = True
+        self.privs = ()
+        self.protected = False
+        self.inherited = False
+
+    def getPrincipal(self):
+        return self.principal
+
+    def setPrincipal(self, principal, data=None):
+        self.principal = principal
+        self.data = data
+
+    def canChange(self):
+        return not self.protected and not self.inherited
+
+    @staticmethod
+    def parseFromACL(aclnode):
+
+        aces = []
+        acenodes = aclnode.findall(str(davxml.ace))
+        for node in acenodes:
+            newace = ACE()
+            newace.parseACE(node)
+            aces.append(newace)
+        return aces
+         
+    def parseACE(self, acenode):
+        
+        assert(acenode and acenode.tag == davxml.ace)
+
+        # Get invert
+        self.invert = False
+        principal_parent = acenode
+        invert = acenode.find(str(davxml.invert))
+        if invert:
+            self.invert = True
+            principal_parent = invert
+
+        # Get the principal
+        principal = principal_parent.find(str(davxml.principal))
+        if not principal or len(principal.getchildren()) != 1:
+            return False
+        
+        # Determine principal info
+        child = principal.getchildren()[0]
+        if child.tag == davxml.href:
+            self.setPrincipal(child.tag, child.text)
+
+        elif child.tag in (davxml.all, davxml.authenticated, davxml.unauthenticated, davxml.self,):
+            self.setPrincipal(child.tag)
+
+        elif child.tag == davxml.property:
+            if len(child.getchildren()) == 1:
+                self.setPrincipal(child.tag, QName(child.getchildren()[0].tag))
+            else:
+                self.setPrincipal(child.tag)
+        
+        # Determine rights
+        self.grant = True
+        child = acenode.find(str(davxml.grant))
+        if not child:
+            child = acenode.find(str(davxml.deny))
+            if child:
+                self.grant = False
+        if child:
+            self.parsePrivileges(child)
+    
+        # Determine protected/inherited state
+        self.protected = acenode.find(str(davxml.protected)) is not None
+        self.inherited = acenode.find(str(davxml.inherited)) is not None
+        
+        return True
+
+    def parsePrivileges(self, parent):
+        
+        assert(parent.tag in (davxml.grant, davxml.deny,))
+        
+        # Parent node contains one of more privilege nodes which we parse
+        self.privs = ()
+        for privilege in parent.getchildren():
+            # Look for privilege
+            if privilege.tag != davxml.privilege or len(privilege.getchildren()) != 1:
+                continue
+
+            # Now get rights within the privilege
+            self.privs += (privilege.getchildren()[0].tag,)
+
+    def generateACE(self, aclnode):
+        # Structure of ace is:
+        #
+        #   &lt;DAV:ace&gt;
+        #     &lt;DAV:principal&gt;...&lt;/DAV:principal&gt;
+        #       &lt;DAV:grant&gt;...&lt;/DAV:grant&gt;
+        #   &lt;/DAV:ace&gt;
+    
+        # &lt;DAV:ace&gt; element
+        ace = SubElement(aclnode, davxml.ace)
+        
+        if self.invert:
+            invert = SubElement(ace, davxml.invert)
+
+        # &lt;DAV:principal&gt; element
+        principal = SubElement(invert if self.invert else ace, davxml.principal)
+        
+        # Principal type
+        if self.principal == davxml.href:
+
+            # &lt;DAV:href&gt; element
+            href = SubElement(principal, davxml.href)
+            href.text = self.data
+
+        elif self.principal in (davxml.all, davxml.authenticated, davxml.unauthenticated, davxml.self,):
+
+            # &lt;DAV:all&gt;/&lt;DAV:authenticated&gt;/&lt;DAV:unauthenticated&gt;/&lt;DAV:self&gt; elements
+            SubElement(principal, self.principal)
+
+        elif self.principal == davxml.property:
+
+            # &lt;DAV:property&gt; element - the UID is the property element name
+            property = SubElement(principal, davxml.property)
+            SubElement(property, self.data)
+        
+        # Do grant rights for each one set
+        if self.grant:
+            # &lt;DAV:grant&gt; element
+            privs = SubElement(ace, davxml.grant)
+        
+        # Do deny rights for each one set
+        else:
+            # &lt;DAV:deny&gt; element
+            privs = SubElement(ace, davxml.deny)
+        
+        for item in self.privs:
+            priv = SubElement(privs, davxml.privilege)
+            SubElement(priv, item)
+
+        # &lt;DAV:protected&gt; and &lt;DAV:inherited&gt;
+        if self.protected:
+            SubElement(ace, davxml.protected)
+        if self.inherited:
+            SubElement(ace, davxml.inherited)
+            
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavaclpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/acl.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/acl.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/acl.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,67 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+from StringIO import StringIO
+from protocol.http.data.string import RequestDataString
+from protocol.webdav.definitions import davxml
+from xml.etree.ElementTree import Element
+from protocol.utils.xmlhelpers import BetterElementTree
+
+class ACL(RequestResponse):
+
+    def __init__(self, session, url, acls):
+        super(ACL, self).__init__(session, methods.ACL, url)
+        self.acls = acls
+        
+        self.initRequestData()
+
+    def initRequestData(self):
+        # Write XML info to a string
+        os = StringIO()
+        self.generateXML(os)
+        self.request_data = RequestDataString(os.getvalue(), &quot;text/xml charset=utf-8&quot;)
+    
+    def generateXML(self, os):
+        # Structure of document is:
+        #
+        # &lt;DAV:acl&gt;
+        #   &lt;DAV:ace&gt;
+        #     &lt;S:inheritable xmlns:S=&quot;http:#jakarta.apache.org/slide/&quot;&gt; / &lt;S:non-inheritable xmlns:S=&quot;http:#jakarta.apache.org/slide/&quot;&gt;
+        #     &lt;DAV:principal&gt;...&lt;/DAV:principal&gt;
+        #       &lt;DAV:grant&gt;...&lt;/DAV:grant&gt;
+        #   &lt;/DAV:ace&gt;
+        #   ...
+        # &lt;/DAV:acl&gt;
+        
+        # &lt;DAV:acl&gt; element
+        acl = Element(davxml.acl)
+        
+        # Do for each ACL
+        if self.acls:
+
+            for ace in self.acls:
+                # Cannot do if change not allowed
+                if not ace.canChange():
+                    continue
+                
+                # &lt;DAV:ace&gt; element
+                ace.generateACE(acl)
+        
+        # Now we have the complete document, so write it out (no indentation)
+        xmldoc = BetterElementTree(acl)
+        xmldoc.writeUTF8(os)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavcopypy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/copy.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/copy.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/copy.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,22 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.copymovebase import CopyMoveBase
+
+class Copy(CopyMoveBase):
+
+    def __init__(self, session, url_old, absurl_new, overwrite=False):
+        super(Copy, self).__init__(session, url_old, absurl_new, overwrite=overwrite, delete_original=False)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavcopymovebasepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/copymovebase.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/copymovebase.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/copymovebase.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,45 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+from protocol.webdav.definitions import headers
+
+class CopyMoveBase(RequestResponse):
+
+    def __init__(self, session, url_old, absurl_new, overwrite=False, delete_original=True):
+        super(CopyMoveBase, self).__init__(session, methods.MOVE if delete_original else methods.COPY, url_old)
+        self.absurl_new = absurl_new
+        self.overwrite = overwrite
+
+    def setData(self, etag):
+        self.request_data = None
+        self.response_data = None
+
+        # Must have matching ETag
+        if etag:
+            self.etag = etag
+            self.etag_match = True
+
+    def addHeaders(self, hdrs):
+        # Do default
+        super(CopyMoveBase, self).addHeaders(hdrs)
+        
+        # Add Destination header
+        hdrs.append((headers.Destination, self.absurl_new))
+        
+        # Add Overwrite header
+        hdrs.append((headers.Overwrite, headers.OverwriteTrue if self.overwrite else headers.OverwriteFalse))
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavdefinitions__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavdefinitionsdavxmlpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/davxml.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/davxml.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/davxml.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,87 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from xml.etree.ElementTree import QName
+
+DAVNamespace = &quot;DAV:&quot;
+
+propfind       = QName(DAVNamespace, &quot;propfind&quot;)
+propname       = QName(DAVNamespace, &quot;propname&quot;)
+allprop        = QName(DAVNamespace, &quot;allprop&quot;)
+prop           = QName(DAVNamespace, &quot;prop&quot;)
+propstat       = QName(DAVNamespace, &quot;propstat&quot;)
+propertyupdate = QName(DAVNamespace, &quot;propertyupdate&quot;)
+remove         = QName(DAVNamespace, &quot;remove&quot;)
+set            = QName(DAVNamespace, &quot;set&quot;)
+
+getetag            = QName(DAVNamespace, &quot;getetag&quot;)
+creationdate       = QName(DAVNamespace, &quot;creationdate&quot;)
+displayname        = QName(DAVNamespace, &quot;displayname&quot;)
+getcontentlanguage = QName(DAVNamespace, &quot;getcontentlanguage&quot;)
+getcontentlength   = QName(DAVNamespace, &quot;getcontentlength&quot;)
+getcontenttype     = QName(DAVNamespace, &quot;getcontenttype&quot;)
+getlastmodified    = QName(DAVNamespace, &quot;getlastmodified&quot;)
+resourcetype       = QName(DAVNamespace, &quot;resourcetype&quot;)
+collection         = QName(DAVNamespace, &quot;collection&quot;)
+
+lockinfo      = QName(DAVNamespace, &quot;lockinfo&quot;)
+lockscope     = QName(DAVNamespace, &quot;lockscope&quot;)
+locktype      = QName(DAVNamespace, &quot;locktype&quot;)
+owner         = QName(DAVNamespace, &quot;owner&quot;)
+exclusive     = QName(DAVNamespace, &quot;exclusive&quot;)
+shared        = QName(DAVNamespace, &quot;shared&quot;)
+write         = QName(DAVNamespace, &quot;write&quot;)
+
+acl           = QName(DAVNamespace, &quot;acl&quot;)
+ace           = QName(DAVNamespace, &quot;ace&quot;)
+invert        = QName(DAVNamespace, &quot;invert&quot;)
+principal     = QName(DAVNamespace, &quot;principal&quot;)
+privilege     = QName(DAVNamespace, &quot;privilege&quot;)
+grant         = QName(DAVNamespace, &quot;grant&quot;)
+deny          = QName(DAVNamespace, &quot;deny&quot;)
+protected     = QName(DAVNamespace, &quot;protected&quot;)
+inherited     = QName(DAVNamespace, &quot;inherited&quot;)
+
+href                            = QName(DAVNamespace, &quot;href&quot;)
+all                             = QName(DAVNamespace, &quot;all&quot;)
+authenticated                   = QName(DAVNamespace, &quot;authenticated&quot;)
+unauthenticated                 = QName(DAVNamespace, &quot;unauthenticated&quot;)
+property                        = QName(DAVNamespace, &quot;property&quot;)
+self                            = QName(DAVNamespace, &quot;self&quot;)
+read                            = QName(DAVNamespace, &quot;read&quot;)
+write                           = QName(DAVNamespace, &quot;write&quot;)
+write_properties                = QName(DAVNamespace, &quot;write-properties&quot;)
+write_content                   = QName(DAVNamespace, &quot;write-content&quot;)
+read_acl                        = QName(DAVNamespace, &quot;read-acl&quot;)
+read_current_user_privilege_set = QName(DAVNamespace, &quot;read-current-user-privilege-set&quot;)
+write_acl                       = QName(DAVNamespace, &quot;write-acl&quot;)
+bind                            = QName(DAVNamespace, &quot;bind&quot;)
+unbind                          = QName(DAVNamespace, &quot;unbind&quot;)
+all                             = QName(DAVNamespace, &quot;all&quot;)
+
+multistatus         = QName(DAVNamespace, &quot;multistatus&quot;)
+response            = QName(DAVNamespace, &quot;response&quot;)
+responsedescription = QName(DAVNamespace, &quot;responsedescription&quot;)
+status              = QName(DAVNamespace, &quot;status&quot;)
+
+principal_match     = QName(DAVNamespace, &quot;principal-match&quot;)
+
+principal_collection_set = QName(DAVNamespace, &quot;principal-collection-set&quot;)
+
+alternate_URI_set   = QName(DAVNamespace, &quot;alternate-URI-set&quot;)
+principal_URL       = QName(DAVNamespace, &quot;principal-URL&quot;)
+group_member_set    = QName(DAVNamespace, &quot;group-member-set&quot;)
+group_membership    = QName(DAVNamespace, &quot;group-membership&quot;)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavdefinitionsheaderspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/headers.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/headers.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/headers.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,39 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+# RFC2518 9 - Request Header fields (only the ones we need)
+
+from protocol.http.definitions.headers import * #@UnusedWildImport
+
+DAV = &quot;DAV&quot;
+DAV1 = &quot;1&quot;
+DAV2 = &quot;2&quot;
+DAVbis = &quot;bis&quot;
+DAVACL = &quot;access-control&quot;        # ACL extension RFC3744
+Depth = &quot;Depth&quot;
+Depth0 = &quot;0&quot;
+Depth1 = &quot;1&quot;
+DepthInfinity = &quot;infinity&quot;
+Destination = &quot;Destination&quot;
+If = &quot;If&quot;
+ForceAuthentication = &quot;Force-Authentication&quot;
+LockToken = &quot;Lock-Token&quot;
+Overwrite = &quot;Overwrite&quot;
+OverwriteTrue = &quot;T&quot;
+OverwriteFalse = &quot;F&quot;
+Timeout = &quot;Timeout&quot;
+TimeoutSeconds = &quot;Second-&quot;
+TimeoutInfinite = &quot;Infinite&quot;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavdefinitionsmethodspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/methods.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/methods.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/methods.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,29 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.definitions.methods import * #@UnusedWildImport
+
+# RFC2518 - WebDAV Request Methods
+
+MKCOL     = &quot;MKCOL&quot;
+MOVE      = &quot;MOVE&quot;
+COPY      = &quot;COPY&quot;
+PROPFIND  = &quot;PROPFIND&quot;
+PROPPATCH = &quot;PROPPATCH&quot;
+LOCK      = &quot;LOCK&quot;
+UNLOCK    = &quot;UNLOCK&quot;
+REPORT    = &quot;REPORT&quot;           # RFC3253
+ACL       = &quot;ACL&quot;              # RFC3744
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavdefinitionsstatuscodespy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/statuscodes.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/statuscodes.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/definitions/statuscodes.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,24 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.definitions.statuscodes import * #@UnusedWildImport
+
+Processing = 102
+MultiStatus = 207
+UnprocessableEntity = 422
+Locked = 423
+FailedDependency = 424
+InsufficientStorage = 507
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavdeletepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/delete.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/delete.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/delete.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+
+class Delete(RequestResponse):
+
+    def __init__(self, session, url):
+        super(Delete, self).__init__(session, methods.DELETE, url)
+
+    def setData(self, etag=None):
+        self.request_data = None
+        self.response_data = None
+
+        # Must have matching ETag
+        if etag:
+            self.etag = etag
+            self.etag_match = True
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavgetpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/get.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/get.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/get.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,22 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.getbase import GetBase
+
+class Get(GetBase):
+
+    def __init__(self, session, url, lock=None):
+        super(Get, self).__init__(session, url, lock=lock, head=False)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavgetbasepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/getbase.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/getbase.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/getbase.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,50 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+from protocol.webdav.definitions import headers
+
+
+class GetBase(RequestResponse):
+
+    def __init__(self, session, url, lock=None, head=False):
+        super(GetBase, self).__init__(session, methods.HEAD if head else methods.GET, url, lock=lock)
+        self.head = head
+
+    def setData(self, response_data, etag=None):
+        self.request_data = None
+        self.response_data = response_data
+
+        # Must have matching ETag
+        if etag:
+            self.etag = etag
+            self.etag_match = True
+
+    def getNewETag(self):
+        # Get the ETag header from response headers
+        if self.hasResponseHeader(headers.ETag):
+            return self.getResponseHeader(headers.ETag)
+        else:
+            return None
+
+    def getContentLength(self):
+        # Always zero to prevent attempt to read response
+        return 0 if self.head else self.content_length
+    
+    def getChunked(self):
+        # Always false to prevent attempt to read response
+        return False if self.head else self.chunked
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavheadpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/head.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/head.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/head.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,22 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.getbase import GetBase
+
+class Head(GetBase):
+
+    def __init__(self, session, url, lock=None):
+        super(Head, self).__init__(session, url, lock=lock, head=True)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavlockpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/lock.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/lock.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/lock.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,137 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+from protocol.webdav.definitions import headers
+from StringIO import StringIO
+from protocol.http.data.string import RequestDataString
+from protocol.webdav.definitions import davxml
+from xml.etree.ElementTree import Element
+from protocol.utils.xmlhelpers import BetterElementTree
+
+class Lock(RequestResponse):
+
+    eExclusive = 0
+    eShared    = 1
+
+    eResourceMustExist     = 0
+    eResourceMustNotExist  = 1
+    eResourceMayExist      = 2
+
+    def __init__(self, session, url, depth, scope, owner, timeout, exists=eResourceMustExist):
+        
+        assert(depth in (headers.Depth0, headers.Depth1, headers.DepthInfinity))
+        assert(scope in (Lock.eExclusive, Lock.eShared,))
+        assert(exists in (Lock.eResourceMustExist, Lock.eResourceMustNotExist, Lock.eResourceMayExist))
+
+        super(Lock, self).__init__(session, methods.LOCK, url)
+
+        self.depth = depth
+        self.scope = scope
+        self.owner = owner
+        self.timeout = timeout
+    
+        # Do appropriate etag based on exists
+        if exists == Lock.eResourceMustExist:
+            self.etag = &quot;*&quot;
+            self.etag_match = True
+        elif exists == Lock.eResourceMustNotExist:
+            self.etag = &quot;*&quot;
+            self.etag_match = False
+        elif exists == Lock.eResourceMayExist:
+            pass
+    
+        self.initRequestData()
+
+    def initRequestData(self):
+        # Write XML info to a string
+        os = StringIO()
+        self.generateXML(os)
+        self.request_data = RequestDataString(os.getvalue(), &quot;text/xml charset=utf-8&quot;)
+
+    def addHeaders(self, hdrs):
+        # Do default
+        super(Lock, self).addHeaders(hdrs)
+    
+        # Add depth header
+        hdrs.append((headers.Depth, self.depth))
+    
+        # Add timeout header
+        if self.timeout == -1:
+            hdrs.append((headers.Timeout, headers.TimeoutInfinite))
+        elif self.timeout &gt; 0:
+            hdrs.append((headers.Timeout, &quot;%s%d&quot; % (headers.TimeoutSeconds, self.timeout)))
+
+    def generateXML(self, os):
+        # Structure of document is:
+        #
+        # &lt;DAV:lockinfo&gt;
+        #   &lt;DAV:lockscope&gt;
+        #     &lt;DAV:exclusive/&gt; | &lt;DAV:shared/&gt;
+        #   &lt;/DAV:lockscope&gt;
+        #   &lt;DAV:locktype&gt;
+        #     &lt;DAV:write/&gt;
+        #   &lt;/DAV:locktype&gt;
+        #   &lt;DAV:owner&gt;
+        #     &lt;&lt;owner&gt;&gt;
+        #   &lt;/DAV:owner&gt;
+        # &lt;/DAV:lockinfo&gt;
+        
+        # &lt;DAV:lockinfo&gt; element
+        lockinfo = Element(davxml.lockinfo)
+        
+        # &lt;DAV:lockscope&gt; element
+        lockscope = Element(davxml.lockscope)
+        lockinfo.append(lockscope)
+        
+        # &lt;DAV:exclusive/&gt; | &lt;DAV:shared/&gt; element
+        lockscope.append(Element(davxml.exclusive if self.scope == Lock.eExclusive else davxml.shared))
+        
+        # &lt;DAV:locktype&gt; element
+        locktype = Element(davxml.locktype)
+        lockinfo.append(locktype)
+        
+        # &lt;DAV:write/&gt; element
+        locktype.append(Element(davxml.write))
+        
+        # &lt;DAV:owner&gt; element is optional
+        if self.owner:
+            # &lt;DAV:owner&gt; element
+            owner = Element(davxml.owner)
+            owner.text = self.owner
+            lockinfo.append(owner)
+    
+        # Now we have the complete document, so write it out (no indentation)
+        BetterElementTree(lockinfo).writeUTF8(os)
+
+    def getLockToken(self):
+    
+        # Get the Lock-Token header from response headers
+        result = &quot;&quot;
+        if self.hasResponseHeader(headers.LockToken):
+
+            # Get Coded-URL
+            codedurl = self.getResponseHeader(headers.LockToken)
+            
+            # Strip leading/trailing &lt;&gt;
+            codeurl = codedurl.strip()
+            if codeurl.startswith(&quot;&lt;&quot;) and codeurl.endswith(&quot;&gt;&quot;):
+                result = codeurl[1:-1]
+            else:
+                result = codeurl
+
+        return result
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavmakecollectionpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/makecollection.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/makecollection.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/makecollection.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,23 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+
+class MakeCollection(RequestResponse):
+
+    def __init__(self, session, url):
+        super(MakeCollection, self).__init__(session, methods.MKCOL, url)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavmovepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/move.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/move.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/move.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,22 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.copymovebase import CopyMoveBase
+
+class Move(CopyMoveBase):
+
+    def __init__(self, session, url_old, absurl_new, overwrite=False):
+        super(Move, self).__init__(session, url_old, absurl_new, overwrite=overwrite, delete_original=True)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavmultiresponseparserpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/multiresponseparser.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/multiresponseparser.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/multiresponseparser.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,36 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.xmlresponseparser import XMLResponseParser
+from protocol.webdav.definitions import davxml
+
+class MultiResponseParser(XMLResponseParser):
+
+    def parse(self, multistatus_node):
+        # Must have a node
+        if not multistatus_node:
+            return
+        
+        # Verify that the node is the correct element &lt;DAV:multistatus&gt;
+        if multistatus_node.tag != davxml.multistatus:
+            return
+        
+        # Node is the right type, so iterator over all child response nodes and process each one
+        for response in multistatus_node.getchildren():
+            self.parseResponse(response)
+    
+    def parseResponse(self, response):
+        raise NotImplementedError
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavoptionspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/options.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/options.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/options.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,51 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+from protocol.http.util import parsetoken
+from protocol.webdav.definitions import headers
+
+class Options(RequestResponse):
+
+    def __init__(self, session, url):
+        super(Options, self).__init__(session, methods.OPTIONS, url)
+
+    def getAllowed(self):
+
+        methods = ()
+        if self.hasResponseHeader(headers.Allow):
+
+            # Look at each one and accumlate allowed methods
+            for value in self.getResponseHeaders(headers.Allow):
+                while value:
+                    token, value = parsetoken(value, &quot;, \t&quot;)
+                    methods += (token,)
+                    
+        return methods
+
+    def isAllowed(self, method):
+        methods = ()
+        if self.hasResponseHeader(headers.Allow):
+
+            # Look at each one and accumlate allowed methods
+            for value in self.getResponseHeaders(headers.Allow):
+                while value:
+                    token, value = parsetoken(value, &quot;, \t&quot;)
+                    if method == token:
+                        return True
+                    
+        return False
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavprincipalmatchpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/principalmatch.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/principalmatch.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/principalmatch.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,73 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.propfindbase import PropFindBase
+from protocol.webdav.definitions import headers
+from protocol.webdav.definitions import methods
+from StringIO import StringIO
+from protocol.http.data.string import RequestDataString
+from protocol.webdav.definitions import davxml
+from xml.etree.ElementTree import Element
+from xml.etree.ElementTree import SubElement
+from protocol.utils.xmlhelpers import BetterElementTree
+
+class PrincipalMatch(PropFindBase):
+
+    def __init__(self, session, url, props):
+        super(PrincipalMatch, self).__init__(session, url, headers.Depth0)
+        self.props = props
+        self.method = methods.REPORT
+        
+        self.initRequestData()
+
+    def initRequestData(self):
+        # Write XML info to a string
+        os = StringIO()
+        self.generateXML(os)
+        self.request_data = RequestDataString(os.getvalue(), &quot;text/xml charset=utf-8&quot;)
+    
+    def generateXML(self, os):
+        # Structure of document is:
+        #
+        # &lt;DAV:principal-match&gt;
+        #   &lt;DAV:self/&gt;
+        #   &lt;DAV:prop&gt;
+        #     &lt;&lt;names of each property as elements&gt;&gt;
+        #   &lt;/DAV:prop&gt;
+        # &lt;/DAV:principal-match&gt;
+        
+        # &lt;DAV:principal-match&gt; element
+        principalmatch = Element(davxml.principal_match)
+        
+        # &lt;DAV:self&gt; element
+        SubElement(principalmatch, davxml.self)
+        
+        if self.props:
+
+            # &lt;DAV:prop&gt; element
+            prop = SubElement(principalmatch, davxml.prop)
+            
+            # Now add each property
+            for item in self.props:
+
+                # Add property element taking namespace into account
+    
+                # Look for DAV namespace and reuse that one
+                SubElement(prop, item)
+        
+        # Now we have the complete document, so write it out (no indentation)
+        xmldoc = BetterElementTree(principalmatch)
+        xmldoc.writeUTF8(os)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavpropallpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/propall.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/propall.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/propall.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,43 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.propfindbase import PropFindBase
+from xml.etree.ElementTree import Element
+from protocol.webdav.definitions import davxml
+from protocol.utils.xmlhelpers import BetterElementTree
+
+class PropAll(PropFindBase):
+
+    def __init__(self, session, url, depth):
+        super(PropAll, self).__init__(session, url, depth)
+        self.initRequestData()
+
+    def generateXML(self, os):
+        # Structure of document is:
+        #
+        # &lt;DAV:propfind&gt;
+        #   &lt;DAV:allprop/&gt;
+        # &lt;/DAV:propfind&gt;
+        
+        # &lt;DAV:propfind&gt; element
+        propfind = Element(davxml.propfind)
+        
+        # &lt;DAV:propname&gt; element
+        propfind.append(Element(davxml.allprop))
+        
+        # Now we have the complete document, so write it out (no indentation)
+        xmldoc = BetterElementTree(propfind)
+        xmldoc.writeUTF8(os)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavpropfindpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/propfind.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/propfind.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/propfind.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.propfindbase import PropFindBase
+from xml.etree.ElementTree import Element
+from protocol.webdav.definitions import davxml
+from protocol.utils.xmlhelpers import BetterElementTree
+
+class PropFind(PropFindBase):
+
+    def __init__(self, session, url, depth, props):
+        super(PropFind, self).__init__(session, url, depth)
+        self.props = props
+        
+        self.initRequestData()
+
+    def generateXML(self, os):
+        # Structure of document is:
+        #
+        # &lt;DAV:propfind&gt;
+        #   &lt;DAV:prop&gt;
+        #     &lt;&lt;names of each property as elements&gt;&gt;
+        #   &lt;/DAV:prop&gt;
+        # &lt;/DAV:propfind&gt;
+        
+        # &lt;DAV:propfind&gt; element
+        propfind = Element(davxml.propfind)
+        
+        # &lt;DAV:prop&gt; element
+        prop = Element(davxml.prop)
+        propfind.append(prop)
+        
+        # Now add each property
+        for propname in self.props:
+            # Add property element taking namespace into account
+            prop.append(Element(propname))
+        
+        # Now we have the complete document, so write it out (no indentation)
+        xmldoc = BetterElementTree(propfind)
+        xmldoc.writeUTF8(os)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavpropfindbasepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/propfindbase.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/propfindbase.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/propfindbase.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,48 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+from protocol.webdav.definitions import headers
+from protocol.http.data.string import RequestDataString
+from StringIO import StringIO
+
+class PropFindBase(RequestResponse):
+
+    def __init__(self, session, url, depth):
+        assert(depth in (headers.Depth0, headers.Depth1, headers.DepthInfinity))
+
+        super(PropFindBase, self).__init__(session, methods.PROPFIND, url)
+        self.depth = depth
+
+    def initRequestData(self):
+        # Write XML info to a string
+        os = StringIO()
+        self.generateXML(os);
+        self.request_data = RequestDataString(os.getvalue(), &quot;text/xml; charset=utf-8&quot;);
+    
+    def setOutput(self, response_data):
+        self.response_data = response_data
+
+    def addHeaders(self, hdrs):
+        # Do default
+        super(PropFindBase, self).addHeaders(hdrs)
+        
+        # Add depth header
+        hdrs.append((headers.Depth, self.depth))
+
+    def generateXML(self, os):
+        raise NotImplementedError
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavpropfindparserpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/propfindparser.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/propfindparser.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/propfindparser.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,147 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.multiresponseparser import MultiResponseParser
+from protocol.webdav.definitions import davxml
+from protocol.http.util import parseStatusLine
+from xml.etree.ElementTree import QName
+from protocol.url import URL
+
+class PropFindParser(MultiResponseParser):
+
+    textProperties = set()
+    hrefProperties = set()
+    hrefListProperties = set()
+
+    class PropFindResult(object):
+        
+        def __init__(self):
+            self.textProperties = {}
+            self.hrefProperties = {}
+            self.nodeProperties = {}
+            self.badProperties = {}
+
+        def setResource(self, resource):
+            self.resource = resource
+        def getResource(self):
+            return self.resource
+        
+        def addTextProperty(self, name, value):
+            self.textProperties[name] = value
+        def getTextProperties(self):
+            return self.textProperties
+
+        def addHrefProperty(self, name, value):
+            self.hrefProperties[name] = value
+        def getHrefProperties(self):
+            return self.hrefProperties
+
+        def addNodeProperty(self, name, node):
+            self.nodeProperties[name] = node
+        def getNodeProperties(self):
+            return self.nodeProperties
+
+        def addBadProperty(self, name, status):
+            self.badProperties[name] = status
+        def getBadProperties(self):
+            return self.badProperties
+
+    def __init__(self):
+        self.results = {}
+    
+    def getResults(self):
+        return self.results
+
+    # Parse the response element down to the properties
+    def parseResponse(self, response):
+        # Verify that the node is the correct element &lt;DAV:response&gt;
+        if response.tag != davxml.response:
+            return
+        
+        # Node is the right type, so iterate over all child response nodes and process each one
+        result = PropFindParser.PropFindResult()
+        for child in response.getchildren():
+
+            # Is it the href
+            if child.tag == davxml.href:
+                result.setResource(child.text)
+            
+            # Is it propstat
+            elif child.tag == davxml.propstat:
+                self.parsePropStat(child, result)
+        
+        # Add the resource only if we got one
+        if result.getResource():
+            self.results[result.getResource()] = result
+
+    def parsePropStat(self, propstat, result):
+        # Scan the propstat node the status - we only process OK status
+    
+        # Now look for a &lt;DAV:status&gt; element in its children
+        status = propstat.find(str(davxml.status))
+        
+        # Now parse the response and dispatch accordingly
+        if status is not None:
+
+            status_result = parseStatusLine(status.text)
+            badstatus = status_result if status_result / 100 != 2 else None
+
+            # Now look for a &lt;DAV:prop&gt; element in its children
+            for item in propstat.findall(str(davxml.prop)):
+                self.parseProp(item, result, badstatus)                
+
+    def parseProp(self, property, result, badstatus):
+        # Scan the prop node - each child is processed
+        for item in property.getchildren():
+            self.parsePropElement(item, result, badstatus)
+    
+    # Parsing of property elements
+    def parsePropElement(self, prop, result, badstatus):
+        # Here we need to detect the type of element and dispatch accordingly
+        if badstatus:
+            result.addBadProperty(QName(prop.tag), badstatus)
+
+        elif prop.tag in PropFindParser.textProperties:
+            self.parsePropElementText(prop, result)
+
+        elif prop.tag in PropFindParser.hrefProperties:
+            self.parsePropElementHref(prop, result, False)
+
+        elif prop.tag in PropFindParser.hrefListProperties:
+            self.parsePropElementHref(prop, result, True)
+
+        else:
+            self.parsePropElementUnknown(prop, result)
+
+    def parsePropElementText(self, prop, result):
+        # Grab the element data
+        result.addTextProperty(QName(prop.tag), prop.text if prop.text else &quot;&quot;)
+        result.addNodeProperty(QName(prop.tag), prop)
+
+    def parsePropElementHref(self, prop, result, is_list):
+        # Grab the element data
+        hrefs = tuple([URL(url=href.text, decode=True) for href in prop.findall(str(davxml.href))])
+        if not is_list:
+            if len(hrefs) == 1:
+                hrefs = hrefs[0]
+            else:
+                hrefs = &quot;&quot;
+        result.addHrefProperty(QName(prop.tag), hrefs)
+        result.addNodeProperty(QName(prop.tag), prop)
+
+    def parsePropElementUnknown(self, prop, result):
+        # Just add the node
+        result.addNodeProperty(QName(prop.tag), prop)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavpropnamespy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/propnames.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/propnames.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/propnames.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,43 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.propfindbase import PropFindBase
+from xml.etree.ElementTree import Element
+from protocol.webdav.definitions import davxml
+from protocol.utils.xmlhelpers import BetterElementTree
+
+class PropNames(PropFindBase):
+
+    def __init__(self, session, url, depth):
+        super(PropNames, self).__init__(session, url, depth)
+        self.initRequestData()
+
+    def generateXML(self, os):
+        # Structure of document is:
+        #
+        # &lt;DAV:propfind&gt;
+        #   &lt;DAV:propname/&gt;
+        # &lt;/DAV:propfind&gt;
+        
+        # &lt;DAV:propfind&gt; element
+        propfind = Element(davxml.propfind)
+        
+        # &lt;DAV:propname&gt; element
+        propfind.append(Element(davxml.propname))
+        
+        # Now we have the complete document, so write it out (no indentation)
+        xmldoc = BetterElementTree(propfind)
+        xmldoc.writeUTF8(os)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavproppatchpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/proppatch.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/proppatch.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/proppatch.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,75 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from xml.etree.ElementTree import Element
+from protocol.webdav.definitions import davxml
+from protocol.utils.xmlhelpers import BetterElementTree
+from protocol.webdav.requestresponse import RequestResponse
+from xml.etree.ElementTree import SubElement
+from protocol.webdav.definitions import methods
+from protocol.http.data.string import RequestDataString
+from StringIO import StringIO
+
+class PropPatch(RequestResponse):
+
+    def __init__(self, session, url, setprops=None, delprops=None):
+        super(PropPatch, self).__init__(session, methods.PROPPATCH, url)
+        self.setprops = setprops
+        self.delprops = delprops
+        
+        self.initRequestData()
+
+    def initRequestData(self):
+        # Write XML info to a string
+        os = StringIO()
+        self.generateXML(os);
+        self.request_data = RequestDataString(os.getvalue(), &quot;text/xml; charset=utf-8&quot;);
+    
+    def setOutput(self, response_data):
+        self.response_data = response_data
+
+    def generateXML(self, os):
+        # Structure of document is:
+        #
+        # &lt;DAV:propertyupdate&gt;
+        #   &lt;DAV:set&gt;
+        #     &lt;&lt;names/values of each property as elements&gt;&gt;
+        #   &lt;/DAV:set&gt;
+        #   &lt;DAV:remove&gt;
+        #     &lt;&lt;names of each property as elements&gt;&gt;
+        #   &lt;/DAV:remove&gt;
+        # &lt;/DAV:propertyupdate&gt;
+        
+        # &lt;DAV:propertyupdate&gt; element
+        propertyupdate = Element(davxml.propertyupdate)
+        
+        # &lt;DAV:set&gt; element
+        if self.setprops:
+            set = SubElement(propertyupdate, davxml.set)
+            propel = SubElement(set, davxml.prop)
+            for prop in self.setprops:
+                propel.append(prop)
+
+        # &lt;DAV:remove&gt; element
+        if self.delprops:
+            remove = SubElement(propertyupdate, davxml.remove)
+            propel = SubElement(remove, davxml.prop)
+            for prop in self.delprops:
+                propel.append(prop)
+
+        # Now we have the complete document, so write it out (no indentation)
+        xmldoc = BetterElementTree(propertyupdate)
+        xmldoc.writeUTF8(os)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavputpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/put.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/put.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/put.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,46 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+from protocol.webdav.definitions import headers
+
+class Put(RequestResponse):
+
+    def __init__(self, session, url, lock=None):
+        super(Put, self).__init__(session, methods.PUT, url, lock=lock)
+
+    def setData(self, request_data, response_data, etag=None, new_item=False):
+        assert(not (etag and new_item))
+        self.request_data = request_data
+        self.response_data = response_data
+    
+        # Must have matching ETag
+        if etag:
+            self.etag = etag
+            self.etag_match = True
+
+        # ETag should be '*' and we add If-None-Match header to ensure we do not overwrite something already there
+        if new_item:
+            self.etag = &quot;*&quot;
+            self.etag_match = False
+
+    def getNewETag(self):
+        # Get the ETag header from response headers
+        if self.hasResponseHeader(headers.ETag):
+            return self.getResponseHeader(headers.ETag)
+        else:
+            return None
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavreportpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/report.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/report.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/report.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,26 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+
+class Report(RequestResponse):
+
+    def __init__(self, session, url):
+        super(Report, self).__init__(session, methods.REPORT, url)
+
+    def _setOutput(self, response_data):
+        self.response_data = response_data;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavrequestresponsepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/requestresponse.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/requestresponse.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/requestresponse.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.requestresponse import RequestResponse as HTTPRequestResponse
+from protocol.http.requestresponse import RequestResponse
+from protocol.webdav.definitions import headers
+
+class RequestResponse(HTTPRequestResponse):
+
+    def __init__(self, session, method, ruri, etag=None, etag_match=False, lock=None):
+        super(RequestResponse, self).__init__(session, method, ruri, etag, etag_match)
+        self.lock = lock
+
+    def setLock(self, lock):
+        self.lock = lock
+
+    def addHeaders(self, hdrs):
+        # Do inherited
+        super(RequestResponse, self).addHeaders(hdrs)
+
+        # Do Lock matching
+        if self.lock:
+            # This is an untagged token - i.e. it applies to the resource being addressed
+            hdrs.append((headers.If, &quot;(&lt;%s&gt;)&quot; % (self.lock,)))
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavsessionpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/session.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/session.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/session.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,141 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session as HTTPSession
+from protocol.http.requestresponse import RequestResponse
+from protocol.http.data.string import ResponseDataString
+from protocol.http.definitions import methods
+from protocol.http.definitions import statuscodes
+from protocol.webdav.definitions import headers
+
+class Session(HTTPSession):
+    
+    def __init__(self, server, port=None, ssl=False, log=None):
+        super(Session, self).__init__(server, port, ssl, log)
+        self.initialised = False
+        self.version = ()
+
+    def initialise(self, host, base_uri):
+        # Set host change
+        self.setServer(host)
+        
+        # Loop repeating until we can do it or get a fatal error
+        first_time = True
+        while True:
+
+            # Create OPTIONS request for the base_uri
+            request = RequestResponse(self, methods.OPTIONS, base_uri)
+            request.setSession(self)
+            sout = ResponseDataString()
+            request.setData(None, sout)
+    
+            # Add request and process it
+            self.sendRequest(request)
+            
+            # Check response
+            if request.getStatusCode() == statuscodes.Unauthorized:
+
+                # If we had authorization before, then chances are auth details are wrong - so delete and try again with new auth
+                if self.hasAuthorization():
+
+                    self.authorization = None
+                    
+                    # Display error so user knows why the prompt occurs again
+                    self.displayHTTPError(request)
+    
+                # Get authorization object (prompt the user) and redo the request
+                self.authorization, cancelled = self.getAuthorizor(first_time, request.getResponseHeaders(headers.WWWAuthenticate))
+                
+                # Check for auth cancellation
+                if cancelled:
+
+                    self.authorization = None
+                    return False
+    
+                first_time = False
+                
+                # Repeat the request loop with new authorization
+                continue
+            
+            # Look for success and exit loop for further processing
+            if request.getStatusCode() in (statuscodes.OK, statuscodes.NoContent):
+
+                # Grab the server string
+                if request.hasResponseHeader(headers.Server):
+                    self.setServerDescriptor = self.setServerDescriptor(request.getResponseHeader(headers.Server))
+                
+                # Now check the response headers for a DAV version (may be more than one)
+                self.version = ()
+                for dav_version in request.getResponseHeaders(headers.DAV):
+                
+                    # Tokenize on commas
+                    for token in dav_version.split(&quot;,&quot;):
+
+                        token = token.strip()
+                        self.addVersion(token)
+
+                self.setServerType(self.version)
+    
+                # Put other strings into capability
+                capa = &quot;&quot;
+                for name, value in request.getResponseHeaders().iteritems():
+
+                    if (not name.lower().startswith(headers.Server) and
+                        not name.lower().startswith(headers.Date) and
+                        name.lower().startswith(&quot;Content-&quot;)):
+
+                        capa += &quot;%s: %s\n&quot; % (name, value,)
+
+                self.setServerCapability(capa)
+    
+                # Just assume any version is fine for now
+                break
+            
+            # If we get here we had some kind of fatal error
+            self.handleHTTPError(request)
+            return False
+        
+        self.initialised = True
+    
+        return True
+
+    def addVersion(self, token):
+        self.version += (token,)
+
+    def hasDAVVersion(self, version):
+        return version in self.version
+
+    def hasDAV(self):
+        return self.hasDAVVersion(headers.DAV1)
+
+    def hasDAVLocking(self):
+        return self.hasDAVVersion(headers.DAV2) or self.hasDAVVersion(headers.DAVbis)
+
+    def hasDAVACL(self):
+        return self.hasDAVVersion(headers.DAVACL)
+
+    def getAuthorizor(self, first_time, www_authenticate):
+        raise NotImplementedError
+    
+    def setServerType(self, type):
+        raise NotImplementedError
+    
+    def setServerDescriptor(self, txt):
+        raise NotImplementedError
+    
+    def setServerCapability(self, txt):
+        raise NotImplementedError
+    
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavtests__init__py"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/__init__.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/__init__.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/__init__.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_acepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_ace.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_ace.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_ace.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,134 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.ace import ACE
+from xml.etree.ElementTree import XML
+from protocol.webdav.definitions import davxml
+from xml.etree.ElementTree import Element
+from protocol.utils.xmlhelpers import BetterElementTree
+from StringIO import StringIO
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def verifyXML(self, x, y=None):
+        
+        x = x.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+        if y:
+            y = y.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+            
+        # Parse the XML data
+        a = ACE()
+        a.parseACE(XML(x))
+        
+        # Generate the XML data
+        aclnode = Element(davxml.acl)
+        a.generateACE(aclnode)
+        os = StringIO()
+        xmldoc = BetterElementTree(aclnode.getchildren()[0])
+        xmldoc.writeUTF8(os)
+        
+        # Verify data
+        self.assertEqual(os.getvalue(), y if y else x)
+
+    def test_XML1(self):
+        
+        self.verifyXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:ace xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:principal&gt;
+    &lt;ns0:href&gt;/principals/users/a&lt;/ns0:href&gt;
+  &lt;/ns0:principal&gt;
+  &lt;ns0:grant&gt;
+    &lt;ns0:privilege&gt;
+      &lt;ns0:read /&gt;
+    &lt;/ns0:privilege&gt;
+  &lt;/ns0:grant&gt;
+&lt;/ns0:ace&gt;
+&quot;&quot;&quot;)
+
+    def test_XML2(self):
+        
+        self.verifyXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:ace xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:principal&gt;
+    &lt;ns0:unauthenticated /&gt;
+  &lt;/ns0:principal&gt;
+  &lt;ns0:deny&gt;
+    &lt;ns0:privilege&gt;
+      &lt;ns0:read /&gt;
+    &lt;/ns0:privilege&gt;
+    &lt;ns0:privilege&gt;
+      &lt;ns0:write /&gt;
+    &lt;/ns0:privilege&gt;
+  &lt;/ns0:deny&gt;
+&lt;/ns0:ace&gt;
+&quot;&quot;&quot;)
+
+    def test_XML3(self):
+        
+        self.verifyXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:ace xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:principal&gt;
+    &lt;ns0:href&gt;/principals/users/a&lt;/ns0:href&gt;
+  &lt;/ns0:principal&gt;
+  &lt;ns0:grant&gt;
+    &lt;ns0:privilege&gt;
+      &lt;ns0:read /&gt;
+    &lt;/ns0:privilege&gt;
+  &lt;/ns0:grant&gt;
+  &lt;ns0:protected /&gt;
+  &lt;ns0:inherited /&gt;
+&lt;/ns0:ace&gt;
+&quot;&quot;&quot;)
+
+    def test_XML4(self):
+        
+        self.verifyXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:ace xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:invert&gt;
+    &lt;ns0:principal&gt;
+      &lt;ns0:self /&gt;
+    &lt;/ns0:principal&gt;
+  &lt;/ns0:invert&gt;
+  &lt;ns0:grant&gt;
+    &lt;ns0:privilege&gt;
+      &lt;ns0:read /&gt;
+    &lt;/ns0:privilege&gt;
+  &lt;/ns0:grant&gt;
+  &lt;ns0:protected /&gt;
+  &lt;ns0:inherited /&gt;
+&lt;/ns0:ace&gt;
+&quot;&quot;&quot;)
+
+    def test_XML5(self):
+        
+        self.verifyXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:ace xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:principal&gt;
+    &lt;ns0:property&gt;
+      &lt;ns0:owner /&gt;
+    &lt;/ns0:property&gt;
+  &lt;/ns0:principal&gt;
+  &lt;ns0:grant&gt;
+    &lt;ns0:privilege&gt;
+      &lt;ns0:read /&gt;
+    &lt;/ns0:privilege&gt;
+  &lt;/ns0:grant&gt;
+&lt;/ns0:ace&gt;
+&quot;&quot;&quot;)
+
+
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_aclpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_acl.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_acl.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_acl.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,43 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.acl import ACL
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = ACL(server, &quot;/&quot;, ())
+        self.assertEqual(request.getMethod(), &quot;ACL&quot;)
+    
+class TestRequestHeaders(unittest.TestCase):
+    pass
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_copypy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_copy.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_copy.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_copy.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,72 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.copy import Copy
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Copy(server, &quot;/a&quot;, &quot;http://www.example.com/b&quot;)
+        self.assertEqual(request.getMethod(), &quot;COPY&quot;)
+
+class TestRequestHeaders(unittest.TestCase):
+    def test_NoSpecialHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Copy(server, &quot;/a&quot;, &quot;http://www.example.com/b&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+        self.assertTrue(&quot;Overwrite: F&quot; in hdrs)
+        self.assertTrue(&quot;Destination: http://www.example.com/b&quot; in hdrs)
+    
+    def test_IfMatchHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Copy(server, &quot;/a&quot;, &quot;http://www.example.com/b&quot;)
+        request.setData(etag=&quot;\&quot;12345\&quot;&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertTrue(&quot;If-Match: \&quot;12345\&quot;&quot; in hdrs)
+        self.assertTrue(&quot;Overwrite: F&quot; in hdrs)
+        self.assertTrue(&quot;Destination: http://www.example.com/b&quot; in hdrs)
+    
+    def test_OverwriteHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Copy(server, &quot;/a&quot;, &quot;http://www.example.com/b&quot;, overwrite=True)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+        self.assertTrue(&quot;Overwrite: T&quot; in hdrs)
+        self.assertTrue(&quot;Destination: http://www.example.com/b&quot; in hdrs)
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_deletepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_delete.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_delete.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_delete.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,58 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.delete import Delete
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Delete(server, &quot;/&quot;)
+        self.assertEqual(request.getMethod(), &quot;DELETE&quot;)
+
+class TestRequestHeaders(unittest.TestCase):
+    def test_NoSpecialHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Delete(server, &quot;/&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+    
+    def test_IfMatchHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Delete(server, &quot;/&quot;)
+        request.setData(etag=&quot;\&quot;12345\&quot;&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertTrue(&quot;If-Match: \&quot;12345\&quot;&quot; in hdrs)
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_getpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_get.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_get.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_get.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,58 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.get import Get
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Get(server, &quot;/&quot;)
+        self.assertEqual(request.getMethod(), &quot;GET&quot;)
+
+class TestRequestHeaders(unittest.TestCase):
+    def test_NoSpecialHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Get(server, &quot;/&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+    
+    def test_IfMatchHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Get(server, &quot;/&quot;)
+        request.setData(None, etag=&quot;\&quot;12345\&quot;&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertTrue(&quot;If-Match: \&quot;12345\&quot;&quot; in hdrs)
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_headpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_head.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_head.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_head.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,58 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.head import Head
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Head(server, &quot;/&quot;)
+        self.assertEqual(request.getMethod(), &quot;HEAD&quot;)
+
+class TestRequestHeaders(unittest.TestCase):
+    def test_NoSpecialHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Head(server, &quot;/&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+    
+    def test_IfMatchHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Head(server, &quot;/&quot;)
+        request.setData(None, etag=&quot;\&quot;12345\&quot;&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertTrue(&quot;If-Match: \&quot;12345\&quot;&quot; in hdrs)
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_lockpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_lock.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_lock.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_lock.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,119 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.lock import Lock
+from protocol.webdav.definitions import headers
+from StringIO import StringIO
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Lock(server, &quot;/&quot;, headers.Depth0, Lock.eExclusive, &quot;user@example.com&quot;, -1)
+        self.assertEqual(request.getMethod(), &quot;LOCK&quot;)
+
+class TestRequestHeaders(unittest.TestCase):
+
+    def test_NoSpecialHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Lock(server, &quot;/&quot;, headers.Depth0, Lock.eExclusive, &quot;user@example.com&quot;, -1, exists=Lock.eResourceMayExist)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+    
+    def test_IfMatchHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Lock(server, &quot;/&quot;, headers.Depth0, Lock.eExclusive, &quot;user@example.com&quot;, -1, exists=Lock.eResourceMustExist)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertTrue(&quot;If-Match: *&quot; in hdrs)
+    
+    def test_IfNoneMatchHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Lock(server, &quot;/&quot;, headers.Depth0, Lock.eExclusive, &quot;user@example.com&quot;, -1, exists=Lock.eResourceMustNotExist)
+        hdrs = request.generateRequestHeader()
+        self.assertTrue(&quot;If-None-Match: *&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+
+    def test_Depth0Headers(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Lock(server, &quot;/&quot;, headers.Depth0, Lock.eExclusive, &quot;user@example.com&quot;, -1, exists=Lock.eResourceMustNotExist)
+        hdrs = request.generateRequestHeader()
+        self.assertTrue(&quot;Depth: 0&quot; in hdrs)
+        self.assertFalse(&quot;Depth: 1&quot; in hdrs)
+        self.assertFalse(&quot;Depth: infinity&quot; in hdrs)
+
+    def test_Depth1Headers(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Lock(server, &quot;/&quot;, headers.Depth1, Lock.eExclusive, &quot;user@example.com&quot;, -1, exists=Lock.eResourceMustNotExist)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;Depth: 0&quot; in hdrs)
+        self.assertTrue(&quot;Depth: 1&quot; in hdrs)
+        self.assertFalse(&quot;Depth: infinity&quot; in hdrs)
+
+    def test_DepthInfinityHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Lock(server, &quot;/&quot;, headers.DepthInfinity, Lock.eExclusive, &quot;user@example.com&quot;, -1, exists=Lock.eResourceMustNotExist)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;Depth: 0&quot; in hdrs)
+        self.assertFalse(&quot;Depth: 1&quot; in hdrs)
+        self.assertTrue(&quot;Depth: infinity&quot; in hdrs)
+
+
+class TestRequestBody(unittest.TestCase):
+    
+    def test_GenerateXML(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Lock(server, &quot;/&quot;, headers.Depth0, Lock.eExclusive, &quot;user@example.com&quot;, -1)
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:lockinfo xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:lockscope&gt;
+    &lt;ns0:exclusive /&gt;
+  &lt;/ns0:lockscope&gt;
+  &lt;ns0:locktype&gt;
+    &lt;ns0:write /&gt;
+  &lt;/ns0:locktype&gt;
+  &lt;ns0:owner&gt;user@example.com&lt;/ns0:owner&gt;
+&lt;/ns0:lockinfo&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+
+    def test_OneHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Lock(server, &quot;/&quot;, headers.Depth0, Lock.eExclusive, &quot;user@example.com&quot;, -1)
+        request.getResponseHeaders().update({
+            &quot;Lock-Token&quot;: (&quot;&lt;user@example.com&gt;&quot;,),
+        })
+        self.assertEqual(request.getLockToken(), &quot;user@example.com&quot;)
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_movepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_move.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_move.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_move.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,72 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.move import Move
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Move(server, &quot;/a&quot;, &quot;http://www.example.com/b&quot;)
+        self.assertEqual(request.getMethod(), &quot;MOVE&quot;)
+
+class TestRequestHeaders(unittest.TestCase):
+    def test_NoSpecialHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Move(server, &quot;/a&quot;, &quot;http://www.example.com/b&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+        self.assertTrue(&quot;Overwrite: F&quot; in hdrs)
+        self.assertTrue(&quot;Destination: http://www.example.com/b&quot; in hdrs)
+    
+    def test_IfMatchHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Move(server, &quot;/a&quot;, &quot;http://www.example.com/b&quot;)
+        request.setData(etag=&quot;\&quot;12345\&quot;&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertTrue(&quot;If-Match: \&quot;12345\&quot;&quot; in hdrs)
+        self.assertTrue(&quot;Overwrite: F&quot; in hdrs)
+        self.assertTrue(&quot;Destination: http://www.example.com/b&quot; in hdrs)
+    
+    def test_OverwriteHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Move(server, &quot;/a&quot;, &quot;http://www.example.com/b&quot;, overwrite=True)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+        self.assertTrue(&quot;Overwrite: T&quot; in hdrs)
+        self.assertTrue(&quot;Destination: http://www.example.com/b&quot; in hdrs)
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_optionspy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_options.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_options.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_options.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,68 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.options import Options
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Options(server, &quot;/&quot;)
+        self.assertEqual(request.getMethod(), &quot;OPTIONS&quot;)
+    
+class TestRequestHeaders(unittest.TestCase):
+    pass
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+
+    def test_OneHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Options(server, &quot;/&quot;)
+        request.getResponseHeaders().update({
+            &quot;Allow&quot;: (&quot;GET, PUT, OPTIONS, HEAD&quot;,),
+        })
+        self.assertEqual(set(request.getAllowed()), set((&quot;GET&quot;, &quot;PUT&quot;, &quot;OPTIONS&quot;, &quot;HEAD&quot;)))
+        self.assertTrue(request.isAllowed(&quot;GET&quot;))
+        self.assertTrue(request.isAllowed(&quot;PUT&quot;))
+        self.assertTrue(request.isAllowed(&quot;OPTIONS&quot;))
+        self.assertTrue(request.isAllowed(&quot;HEAD&quot;))
+    
+    def test_MultipleHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Options(server, &quot;/&quot;)
+        request.getResponseHeaders().update({
+            &quot;Allow&quot;: (&quot;GET, PUT&quot;, &quot;OPTIONS, HEAD&quot;,),
+        })
+        self.assertEqual(set(request.getAllowed()), set((&quot;GET&quot;, &quot;PUT&quot;, &quot;OPTIONS&quot;, &quot;HEAD&quot;)))
+        self.assertTrue(request.isAllowed(&quot;GET&quot;))
+        self.assertTrue(request.isAllowed(&quot;PUT&quot;))
+        self.assertTrue(request.isAllowed(&quot;OPTIONS&quot;))
+        self.assertTrue(request.isAllowed(&quot;HEAD&quot;))
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_principalmatchpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_principalmatch.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_principalmatch.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_principalmatch.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,84 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.session import Session
+from protocol.webdav.principalmatch import PrincipalMatch
+from StringIO import StringIO
+from protocol.webdav.definitions import davxml
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PrincipalMatch(server, &quot;/&quot;, ())
+        self.assertEqual(request.getMethod(), &quot;REPORT&quot;)
+    
+class TestRequestHeaders(unittest.TestCase):
+    
+    def test_Depth0Headers(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PrincipalMatch(server, &quot;/&quot;, ())
+        hdrs = request.generateRequestHeader()
+        self.assertTrue(&quot;Depth: 0&quot; in hdrs)
+        self.assertFalse(&quot;Depth: 1&quot; in hdrs)
+        self.assertFalse(&quot;Depth: infinity&quot; in hdrs)
+
+class TestRequestBody(unittest.TestCase):

+    def test_GenerateXMLOneProperty(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PrincipalMatch(server, &quot;/&quot;, (davxml.getetag,))
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:principal-match xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:self /&gt;
+  &lt;ns0:prop&gt;
+    &lt;ns0:getetag /&gt;
+  &lt;/ns0:prop&gt;
+&lt;/ns0:principal-match&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+    def test_GenerateXMLMultipleProperties(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PrincipalMatch(server, &quot;/&quot;, (davxml.getetag, davxml.displayname,))
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:principal-match xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:self /&gt;
+  &lt;ns0:prop&gt;
+    &lt;ns0:getetag /&gt;
+    &lt;ns0:displayname /&gt;
+  &lt;/ns0:prop&gt;
+&lt;/ns0:principal-match&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_propfindpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propfind.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propfind.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propfind.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,87 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.session import Session
+from protocol.webdav.propfind import PropFind
+from StringIO import StringIO
+from protocol.webdav.definitions import davxml
+from xml.etree.ElementTree import QName
+from protocol.webdav.definitions import headers
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PropFind(server, &quot;/&quot;, headers.Depth0, (davxml.getetag, QName(&quot;http://example.com/ns/&quot;, &quot;taggy&quot;)))
+        self.assertEqual(request.getMethod(), &quot;PROPFIND&quot;)
+
+class TestRequestHeaders(unittest.TestCase):
+    
+    def test_Depth0Headers(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PropFind(server, &quot;/&quot;, headers.Depth0, (davxml.getetag, QName(&quot;http://example.com/ns/&quot;, &quot;taggy&quot;)))
+        hdrs = request.generateRequestHeader()
+        self.assertTrue(&quot;Depth: 0&quot; in hdrs)
+        self.assertFalse(&quot;Depth: 1&quot; in hdrs)
+        self.assertFalse(&quot;Depth: infinity&quot; in hdrs)
+
+    def test_Depth1Headers(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PropFind(server, &quot;/&quot;, headers.Depth1, (davxml.getetag, QName(&quot;http://example.com/ns/&quot;, &quot;taggy&quot;)))
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;Depth: 0&quot; in hdrs)
+        self.assertTrue(&quot;Depth: 1&quot; in hdrs)
+        self.assertFalse(&quot;Depth: infinity&quot; in hdrs)
+
+    def test_DepthInfinityHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PropFind(server, &quot;/&quot;, headers.DepthInfinity, (davxml.getetag, QName(&quot;http://example.com/ns/&quot;, &quot;taggy&quot;)))
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;Depth: 0&quot; in hdrs)
+        self.assertFalse(&quot;Depth: 1&quot; in hdrs)
+        self.assertTrue(&quot;Depth: infinity&quot; in hdrs)
+
+class TestRequestBody(unittest.TestCase):

+    def test_GenerateXML(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PropFind(server, &quot;/&quot;, headers.Depth0, (davxml.getetag, QName(&quot;http://example.com/ns/&quot;, &quot;taggy&quot;)))
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:propfind xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:prop&gt;
+    &lt;ns0:getetag /&gt;
+    &lt;ns1:taggy xmlns:ns1=&quot;http://example.com/ns/&quot; /&gt;
+  &lt;/ns0:prop&gt;
+&lt;/ns0:propfind&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_propfindparserpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propfindparser.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propfindparser.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propfindparser.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,121 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.propfindparser import PropFindParser
+from xml.etree.ElementTree import XML
+from protocol.webdav.definitions import davxml
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def parseXML(self, x):
+        
+        x = x.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+            
+        # Parse the XML data
+        p = PropFindParser()
+        p.parse(XML(x))
+        return p
+
+    def checkResource(self, parser, resource, properties):
+        result = parser.getResults().get(resource, None)
+        self.assertTrue(result is not None)
+        for prop, value in properties:
+            self.assertEqual(result.getTextProperties().get(prop, None), value)
+
+    def test_SinglePropSingleResource(self):
+        
+        parser = self.parseXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:multistatus xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:response&gt;
+    &lt;ns0:href&gt;/principals/users/a&lt;/ns0:href&gt;
+    &lt;ns0:propstat&gt;
+      &lt;ns0:prop&gt;
+        &lt;ns0:getetag&gt;12345&lt;/ns0:getetag&gt;
+      &lt;/ns0:prop&gt;
+      &lt;ns0:status&gt;HTTP/1.1 200 OK&lt;/ns0:status&gt;
+    &lt;/ns0:propstat&gt;
+  &lt;/ns0:response&gt;
+&lt;/ns0:multistatus&gt;
+&quot;&quot;&quot;)
+        
+        self.checkResource(parser, &quot;/principals/users/a&quot;, (
+            (davxml.getetag, &quot;12345&quot;,),
+        ))
+
+    def test_MultiplePropsSingleResource(self):
+        
+        parser = self.parseXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:multistatus xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:response&gt;
+    &lt;ns0:href&gt;/principals/users/a&lt;/ns0:href&gt;
+    &lt;ns0:propstat&gt;
+      &lt;ns0:prop&gt;
+        &lt;ns0:displayname&gt;Name&lt;/ns0:displayname&gt;
+        &lt;ns0:getetag&gt;12345&lt;/ns0:getetag&gt;
+      &lt;/ns0:prop&gt;
+      &lt;ns0:status&gt;HTTP/1.1 200 OK&lt;/ns0:status&gt;
+    &lt;/ns0:propstat&gt;
+  &lt;/ns0:response&gt;
+&lt;/ns0:multistatus&gt;
+&quot;&quot;&quot;)
+        
+        result = parser.getResults().get(&quot;/principals/users/a&quot;, None)
+        self.assertTrue(result is not None)
+        self.assertEqual(result.getTextProperties().get(davxml.getetag, None), &quot;12345&quot;)
+        self.assertEqual(result.getTextProperties().get(davxml.displayname, None), &quot;Name&quot;)
+
+        self.checkResource(parser, &quot;/principals/users/a&quot;, (
+            (davxml.getetag,     &quot;12345&quot;,),
+            (davxml.displayname, &quot;Name&quot;,),
+        ))
+
+    def test_MultiplePropsSingleMultipleResources(self):
+        
+        parser = self.parseXML(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:multistatus xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:response&gt;
+    &lt;ns0:href&gt;/principals/users/a&lt;/ns0:href&gt;
+    &lt;ns0:propstat&gt;
+      &lt;ns0:prop&gt;
+        &lt;ns0:displayname&gt;Name1&lt;/ns0:displayname&gt;
+        &lt;ns0:getetag&gt;1&lt;/ns0:getetag&gt;
+      &lt;/ns0:prop&gt;
+      &lt;ns0:status&gt;HTTP/1.1 200 OK&lt;/ns0:status&gt;
+    &lt;/ns0:propstat&gt;
+  &lt;/ns0:response&gt;
+  &lt;ns0:response&gt;
+    &lt;ns0:href&gt;/principals/users/b&lt;/ns0:href&gt;
+    &lt;ns0:propstat&gt;
+      &lt;ns0:prop&gt;
+        &lt;ns0:displayname&gt;Name2&lt;/ns0:displayname&gt;
+        &lt;ns0:getetag&gt;2&lt;/ns0:getetag&gt;
+      &lt;/ns0:prop&gt;
+      &lt;ns0:status&gt;HTTP/1.1 200 OK&lt;/ns0:status&gt;
+    &lt;/ns0:propstat&gt;
+  &lt;/ns0:response&gt;
+&lt;/ns0:multistatus&gt;
+&quot;&quot;&quot;)
+
+        self.checkResource(parser, &quot;/principals/users/a&quot;, (
+            (davxml.getetag,     &quot;1&quot;,),
+            (davxml.displayname, &quot;Name1&quot;,),
+        ))
+
+        self.checkResource(parser, &quot;/principals/users/b&quot;, (
+            (davxml.getetag,     &quot;2&quot;,),
+            (davxml.displayname, &quot;Name2&quot;,),
+        ))
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_propnamespy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propnames.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propnames.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_propnames.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,82 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.session import Session
+from protocol.webdav.propnames import PropNames
+from StringIO import StringIO
+from protocol.webdav.definitions import headers
+import unittest
+
+class TestPropNames(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PropNames(server, &quot;/&quot;, headers.Depth0)
+        self.assertEqual(request.getMethod(), &quot;PROPFIND&quot;)
+    
+class TestRequestHeaders(unittest.TestCase):
+    
+    def test_Depth0Headers(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PropNames(server, &quot;/&quot;, headers.Depth0)
+        hdrs = request.generateRequestHeader()
+        self.assertTrue(&quot;Depth: 0&quot; in hdrs)
+        self.assertFalse(&quot;Depth: 1&quot; in hdrs)
+        self.assertFalse(&quot;Depth: infinity&quot; in hdrs)
+
+    def test_Depth1Headers(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PropNames(server, &quot;/&quot;, headers.Depth1)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;Depth: 0&quot; in hdrs)
+        self.assertTrue(&quot;Depth: 1&quot; in hdrs)
+        self.assertFalse(&quot;Depth: infinity&quot; in hdrs)
+
+    def test_DepthInfinityHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PropNames(server, &quot;/&quot;, headers.DepthInfinity)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;Depth: 0&quot; in hdrs)
+        self.assertFalse(&quot;Depth: 1&quot; in hdrs)
+        self.assertTrue(&quot;Depth: infinity&quot; in hdrs)
+
+class TestRequestBody(unittest.TestCase):

+    def test_GenerateXML(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = PropNames(server, &quot;/&quot;, headers.Depth0)
+        os = StringIO()
+        request.generateXML(os)
+        self.assertEqual(os.getvalue(), &quot;&quot;&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;
+&lt;ns0:propfind xmlns:ns0=&quot;DAV:&quot;&gt;
+  &lt;ns0:propname /&gt;
+&lt;/ns0:propfind&gt;
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
+)
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_putpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_put.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_put.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_put.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,75 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.put import Put
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Put(server, &quot;/&quot;)
+        self.assertEqual(request.getMethod(), &quot;PUT&quot;)
+    
+class TestRequestHeaders(unittest.TestCase):
+
+    def test_NoSpecialHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Put(server, &quot;/&quot;)
+        request.setData(None, None)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+    
+    def test_IfMatchHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Put(server, &quot;/&quot;)
+        request.setData(None, None, etag=&quot;\&quot;12345\&quot;&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertFalse(&quot;If-None-Match:&quot; in hdrs)
+        self.assertTrue(&quot;If-Match: \&quot;12345\&quot;&quot; in hdrs)
+    
+    def test_IfNoneMatchHeader(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Put(server, &quot;/&quot;)
+        request.setData(None, None, new_item=True)
+        hdrs = request.generateRequestHeader()
+        self.assertTrue(&quot;If-None-Match: *&quot; in hdrs)
+        self.assertFalse(&quot;If-Match:&quot; in hdrs)
+    
+    def test_Bad(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Put(server, &quot;/&quot;)
+        self.assertRaises(AssertionError, request.setData, None, None, **{&quot;etag&quot;: &quot;\&quot;12345\&quot;&quot;, &quot;new_item&quot;: True})
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_reportpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_report.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_report.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_report.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,43 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.report import Report
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Report(server, &quot;/&quot;)
+        self.assertEqual(request.getMethod(), &quot;REPORT&quot;)
+    
+class TestRequestHeaders(unittest.TestCase):
+    pass
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_templatepy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_template.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_template.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_template.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,35 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    pass
+
+class TestRequestHeaders(unittest.TestCase):
+    pass
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavteststest_unlockpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_unlock.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_unlock.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/tests/test_unlock.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.http.session import Session
+from protocol.webdav.unlock import Unlock
+
+import unittest
+
+class TestRequest(unittest.TestCase):
+    
+    def test_Method(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Unlock(server, &quot;/&quot;, &quot;locked-up-in-chains&quot;)
+        self.assertEqual(request.getMethod(), &quot;UNLOCK&quot;)
+
+class TestRequestHeaders(unittest.TestCase):
+
+    def test_LockTokenHeaders(self):
+        
+        server = Session(&quot;www.example.com&quot;)
+        request = Unlock(server, &quot;/&quot;, &quot;locked-up-in-chains&quot;)
+        hdrs = request.generateRequestHeader()
+        self.assertTrue(&quot;Lock-Token: &lt;locked-up-in-chains&gt;&quot; in hdrs)
+
+class TestRequestBody(unittest.TestCase):
+    pass
+
+class TestResponse(unittest.TestCase):
+    pass
+
+class TestResponseHeaders(unittest.TestCase):
+    pass
+
+class TestResponseBody(unittest.TestCase):
+    pass
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavunlockpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/unlock.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/unlock.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/unlock.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,34 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from protocol.webdav.requestresponse import RequestResponse
+from protocol.webdav.definitions import methods
+from protocol.webdav.definitions import headers
+
+class Unlock(RequestResponse):
+
+    def __init__(self, session, url, lock_token):
+        
+        super(Unlock, self).__init__(session, methods.UNLOCK, url)
+
+        self.lock_token = lock_token
+
+    def addHeaders(self, hdrs):
+        # Do default
+        super(Unlock, self).addHeaders(hdrs)
+    
+        # Add lock-token header
+        hdrs.append((headers.LockToken, &quot;&lt;%s&gt;&quot; % (self.lock_token,)))
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcprotocolwebdavxmlresponseparserpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/protocol/webdav/xmlresponseparser.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/protocol/webdav/xmlresponseparser.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/protocol/webdav/xmlresponseparser.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from xml.etree.ElementTree import XML
+
+class XMLResponseParser(object):
+
+    def parseData(self, data):
+        # XML parse the data
+        self.parse(XML(data))
+
+    def parseFile(self, fpath):
+        fp = open(fpath, &quot;r&quot;)
+        self.parse(XML(fp.read()))
+        fp.close()
+
+    def parse(self, root_node):
+        raise NotImplementedError
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcuiWebDAVBrowsernibclassesnib"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/classes.nib (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/classes.nib                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/classes.nib        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,91 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+&lt;dict&gt;
+        &lt;key&gt;IBClasses&lt;/key&gt;
+        &lt;array&gt;
+                &lt;dict&gt;
+                        &lt;key&gt;CLASS&lt;/key&gt;
+                        &lt;string&gt;FirstResponder&lt;/string&gt;
+                        &lt;key&gt;LANGUAGE&lt;/key&gt;
+                        &lt;string&gt;ObjC&lt;/string&gt;
+                        &lt;key&gt;SUPERCLASS&lt;/key&gt;
+                        &lt;string&gt;NSObject&lt;/string&gt;
+                &lt;/dict&gt;
+                &lt;dict&gt;
+                        &lt;key&gt;CLASS&lt;/key&gt;
+                        &lt;string&gt;NSTableView&lt;/string&gt;
+                        &lt;key&gt;LANGUAGE&lt;/key&gt;
+                        &lt;string&gt;ObjC&lt;/string&gt;
+                        &lt;key&gt;SUPERCLASS&lt;/key&gt;
+                        &lt;string&gt;NSControl&lt;/string&gt;
+                &lt;/dict&gt;
+                &lt;dict&gt;
+                        &lt;key&gt;ACTIONS&lt;/key&gt;
+                        &lt;dict&gt;
+                                &lt;key&gt;browserAction&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;changeBrowserView&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;changeDataView&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;resetServer&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;startupCancelAction&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;startupOKAction&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;tableAction&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                        &lt;/dict&gt;
+                        &lt;key&gt;CLASS&lt;/key&gt;
+                        &lt;string&gt;WebDAVBrowserDelegate&lt;/string&gt;
+                        &lt;key&gt;LANGUAGE&lt;/key&gt;
+                        &lt;string&gt;ObjC&lt;/string&gt;
+                        &lt;key&gt;OUTLETS&lt;/key&gt;
+                        &lt;dict&gt;
+                                &lt;key&gt;browser&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;columnView&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;dataView&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;list&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;listView&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;mainSplitterView&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;passwordText&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;pathLabel&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;progress&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;propertiesView&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;serverText&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;startupSheet&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;table&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;text&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;toolbarBrowserViewButton&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;toolbarDataViewButton&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;userText&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;webView&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                                &lt;key&gt;window&lt;/key&gt;
+                                &lt;string&gt;id&lt;/string&gt;
+                        &lt;/dict&gt;
+                &lt;/dict&gt;
+        &lt;/array&gt;
+        &lt;key&gt;IBVersion&lt;/key&gt;
+        &lt;string&gt;1&lt;/string&gt;
+&lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcuiWebDAVBrowsernibinfonib"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/info.nib (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/info.nib                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/info.nib        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+&lt;dict&gt;
+        &lt;key&gt;IBFramework Version&lt;/key&gt;
+        &lt;string&gt;629&lt;/string&gt;
+        &lt;key&gt;IBOldestOS&lt;/key&gt;
+        &lt;integer&gt;5&lt;/integer&gt;
+        &lt;key&gt;IBOpenObjects&lt;/key&gt;
+        &lt;array/&gt;
+        &lt;key&gt;IBSystem Version&lt;/key&gt;
+        &lt;string&gt;9B18&lt;/string&gt;
+        &lt;key&gt;targetFramework&lt;/key&gt;
+        &lt;string&gt;IBCocoaFramework&lt;/string&gt;
+&lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalDAVClientLibrarytrunksrcuiWebDAVBrowsernibkeyedobjectsnib"></a>
<div class="binary"><h4>Added: CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/keyedobjects.nib</h4>
<pre class="diff"><span>
<span class="cx">(Binary files differ)
</span></span></pre></div>
<span class="cx">Property changes on: CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.nib/keyedobjects.nib
</span><span class="cx">___________________________________________________________________
</span><span class="cx">Name: svn:mime-type
</span><span class="cx">   + application/octet-stream
</span><a id="CalDAVClientLibrarytrunksrcuiWebDAVBrowserpy"></a>
<div class="addfile"><h4>Added: CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.py (0 => 2248)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.py                                (rev 0)
+++ CalDAVClientLibrary/trunk/src/ui/WebDAVBrowser.py        2008-03-25 18:56:21 UTC (rev 2248)
</span><span class="lines">@@ -0,0 +1,574 @@
</span><ins>+##
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+&quot;&quot;&quot;
+Code based on PyObjC examples included with Apple Developer tools.
+&quot;&quot;&quot;
+
+import Foundation, AppKit, WebKit #@UnusedImport
+
+from AppKit import * #@UnusedWildImport
+from AppKit import NSApplication #@UnresolvedImport
+from AppKit import NSImage #@UnresolvedImport
+from AppKit import NSMenuItem #@UnresolvedImport
+from AppKit import NSObject #@UnresolvedImport
+from AppKit import NSPlainFileType #@UnresolvedImport
+from AppKit import NSSize #@UnresolvedImport
+from AppKit import NSToolbar #@UnresolvedImport
+from AppKit import NSToolbarCustomizeToolbarItemIdentifier #@UnresolvedImport
+from AppKit import NSToolbarFlexibleSpaceItemIdentifier #@UnresolvedImport
+from AppKit import NSToolbarItem #@UnresolvedImport
+from AppKit import NSToolbarPrintItemIdentifier #@UnresolvedImport
+from AppKit import NSToolbarSeparatorItemIdentifier #@UnresolvedImport
+from AppKit import NSToolbarSpaceItemIdentifier #@UnresolvedImport
+from AppKit import NSURL #@UnresolvedImport
+from AppKit import NSUserDefaults #@UnresolvedImport
+from AppKit import NSWorkspace #@UnresolvedImport
+
+from Foundation import * #@UnusedWildImport
+
+from PyObjCTools import NibClassBuilder, AppHelper
+
+from objc import IBOutlet #@UnusedImport
+from objc import YES, NO
+from objc import selector #@UnusedImport
+
+from protocol.utils import xmlhelpers
+from ui.session import Session
+from xml.etree.ElementTree import _ElementInterface
+import types
+
+NibClassBuilder.extractClasses(&quot;WebDAVBrowser&quot;)
+
+kServerToolbarItemIdentifier = &quot;WDB: Server Toolbar Identifier&quot;
+kRefreshToolbarItemIdentifier = &quot;WDB: Refresh Toolbar Identifier&quot;
+kBrowserViewToolbarItemIdentifier = &quot;WDB: Browser View Toolbar Identifier&quot;
+kDataViewToolbarItemIdentifier = &quot;WDB: Data View Toolbar Identifier&quot;
+
+def addToolbarItem(aController, anIdentifier, aLabel, aPaletteLabel,
+                   aToolTip, aTarget, anAction, anItemContent, aMenu):
+    &quot;&quot;&quot;
+    Add a toolbar button of some kind.
+    &quot;&quot;&quot;
+    toolbarItem = NSToolbarItem.alloc().initWithItemIdentifier_(anIdentifier)
+
+    toolbarItem.setLabel_(aLabel)
+    toolbarItem.setPaletteLabel_(aPaletteLabel)
+    toolbarItem.setToolTip_(aToolTip)
+    toolbarItem.setTarget_(aTarget)
+    if anAction:
+        toolbarItem.setAction_(anAction)
+
+    if type(anItemContent) == NSImage:
+        toolbarItem.setImage_(anItemContent)
+    else:
+        toolbarItem.setView_(anItemContent)
+        bounds = anItemContent.bounds()
+        minSize = (bounds[1][0], bounds[1][1])
+        maxSize = (bounds[1][0], bounds[1][1])
+        toolbarItem.setMinSize_( minSize )
+        toolbarItem.setMaxSize_( maxSize )
+
+    if aMenu:
+        menuItem = NSMenuItem.alloc().init()
+        menuItem.setSubmenu_(aMenu)
+        menuItem.setTitle_( aMenu.title() )
+        toolbarItem.setMenuFormRepresentation_(menuItem)
+
+    aController._toolbarItems[anIdentifier] = toolbarItem
+
+WRAPPED={}
+class Wrapper(NSObject):
+    &quot;&quot;&quot;
+    NSOutlineView doesn't retain values, which means we cannot use normal
+    python values as values in an outline view.
+    &quot;&quot;&quot;
+    def init_(self, value):
+        self.value = value
+        return self
+
+    def __str__(self):
+        return '&lt;Wrapper for %s&gt;' % self.value
+
+    def description(self):
+        return str(self)
+
+def wrap_object(obj):
+   &