calcurse-caldav: Add a debug mode

Dump all communication with the server to stdout if --debug is
specified.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
This commit is contained in:
Lukas Fleischer 2016-01-23 12:55:25 +01:00
parent 448d470b61
commit badbd71275

View File

@ -80,14 +80,35 @@ def get_auth_headers():
headers = { 'Authorization' : 'Basic %s' % user_password } headers = { 'Authorization' : 'Basic %s' % user_password }
return headers return headers
def get_headers(): def remote_query(cmd, path, additional_headers, body):
headers = get_auth_headers() headers = get_auth_headers()
headers['Content-Type'] = 'Content-Type: text/calendar; charset=utf-8' headers['Content-Type'] = 'Content-Type: text/calendar; charset=utf-8'
return headers headers.update(additional_headers)
if debug:
print("> " + repr(headers))
if body:
for line in body.splitlines():
print("> " + line)
conn.request(cmd, path, headers=headers, body=body)
resp = conn.getresponse()
if not resp:
return (None, None)
headers = resp.getheaders()
body = resp.read().decode('utf-8')
if debug:
print("< " + repr(headers))
for line in body.splitlines():
print("< " + line)
return (headers, body)
def get_hrefmap(conn, uid=None): def get_hrefmap(conn, uid=None):
headers = get_headers()
if uid: if uid:
propfilter = '<c:prop-filter name="UID">' +\ propfilter = '<c:prop-filter name="UID">' +\
'<c:text-match collation="i;octet" >%s</c:text-match>' +\ '<c:text-match collation="i;octet" >%s</c:text-match>' +\
@ -99,16 +120,10 @@ def get_hrefmap(conn, uid=None):
'<d:prop><d:getetag /></d:prop><c:filter>' +\ '<d:prop><d:getetag /></d:prop><c:filter>' +\
'<c:comp-filter name="VCALENDAR">' + propfilter + '</c:comp-filter>' +\ '<c:comp-filter name="VCALENDAR">' + propfilter + '</c:comp-filter>' +\
'</c:filter></c:calendar-query>' '</c:filter></c:calendar-query>'
headers, body = remote_query("REPORT", path, {}, body)
conn.request("REPORT", path, body=body, headers=headers) if not headers:
resp = conn.getresponse()
resp = resp.read().decode('utf-8')
if not resp:
return {} return {}
root = etree.fromstring(body)
root = etree.fromstring(resp)
hrefmap = {} hrefmap = {}
for node in root.findall(".//D:response", namespaces=nsmap): for node in root.findall(".//D:response", namespaces=nsmap):
@ -132,12 +147,7 @@ def remote_wipe(conn):
if dry_run: if dry_run:
return return
headers = get_headers() remote_query("DELETE", path, headers, None)
conn.request("DELETE", path, headers=headers)
resp = conn.getresponse()
if resp:
resp.read()
def get_syncdb(fn): def get_syncdb(fn):
if not os.path.exists(fn): if not os.path.exists(fn):
@ -165,18 +175,13 @@ def save_syncdb(fn, syncdb):
print("%s %s" % (etag, objhash), file=f) print("%s %s" % (etag, objhash), file=f)
def push_object(conn, objhash): def push_object(conn, objhash):
href = path + objhash + ".ics"
headers = get_headers()
body = calcurse_export(objhash) body = calcurse_export(objhash)
conn.request("PUT", href, body=body, headers=headers) headers, body = remote_query("PUT", path + objhash + ".ics", {}, body)
resp = conn.getresponse() if not 'ETag' in headers:
if not resp:
return None return None
resp.read()
etag = resp.getheader('ETag') etag = headers['ETag']
while not etag: while not etag:
hrefmap = get_hrefmap(conn, objhash) hrefmap = get_hrefmap(conn, objhash)
if len(hrefmap.keys()) > 0: if len(hrefmap.keys()) > 0:
@ -186,13 +191,8 @@ def push_object(conn, objhash):
return etag return etag
def remove_remote_object(conn, etag, href): def remove_remote_object(conn, etag, href):
headers = get_headers() headers = { 'If-Match' : '"' + etag + '"' }
headers['If-Match'] = '"' + etag + '"' remote_query("DELETE", href, headers, None)
conn.request("DELETE", href, headers=headers)
resp = conn.getresponse()
if resp:
resp.read()
def push_objects(conn, syncdb, hrefmap): def push_objects(conn, syncdb, hrefmap):
objhashes = calcurse_hashset() objhashes = calcurse_hashset()
@ -240,19 +240,14 @@ def pull_objects(conn, syncdb, hrefmap):
orphan = set(syncdb.keys()) - set(hrefmap.keys()) orphan = set(syncdb.keys()) - set(hrefmap.keys())
# Download and import new objects from the server. # Download and import new objects from the server.
headers = get_headers()
body = '<c:calendar-multiget xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">' +\ body = '<c:calendar-multiget xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">' +\
'<d:prop><d:getetag /><c:calendar-data /></d:prop>' '<d:prop><d:getetag /><c:calendar-data /></d:prop>'
for etag in missing: for etag in missing:
body += '<d:href>%s</d:href>' % (hrefmap[etag]) body += '<d:href>%s</d:href>' % (hrefmap[etag])
body += '</c:calendar-multiget>' body += '</c:calendar-multiget>'
conn.request("REPORT", path, body=body, headers=headers) headers, body = remote_query("REPORT", path, {}, body)
resp = conn.getresponse() root = etree.fromstring(body)
if not resp:
return
resp = resp.read().decode('utf-8')
root = etree.fromstring(resp)
added = deleted = 0 added = deleted = 0
@ -316,6 +311,8 @@ parser.add_argument('--syncdb', action='store', dest='syncdbfn',
parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
default=False, default=False,
help='print status messages to stdout') help='print status messages to stdout')
parser.add_argument('--debug', action='store_true', dest='debug',
default=False, help='print debug messages to stdout')
args = parser.parse_args() args = parser.parse_args()
init = args.init is not None init = args.init is not None
@ -323,6 +320,7 @@ configfn = args.configfn
lockfn = args.lockfn lockfn = args.lockfn
syncdbfn = args.syncdbfn syncdbfn = args.syncdbfn
verbose = args.verbose verbose = args.verbose
debug = args.debug
# Read configuration. # Read configuration.
config = configparser.RawConfigParser() config = configparser.RawConfigParser()
@ -354,6 +352,9 @@ else:
if not verbose and config.has_option('General', 'Verbose'): if not verbose and config.has_option('General', 'Verbose'):
verbose = config.getboolean('General', 'Verbose') verbose = config.getboolean('General', 'Verbose')
if not debug and config.has_option('General', 'Debug'):
debug = config.getboolean('General', 'Debug')
if config.has_option('Auth', 'UserName'): if config.has_option('Auth', 'UserName'):
username = config.get('Auth', 'UserName') username = config.get('Auth', 'UserName')
else: else:
@ -388,6 +389,8 @@ open(lockfn, 'w')
try: try:
# Connect to the server via HTTPs. # Connect to the server via HTTPs.
if verbose:
print('Connecting to ' + hostname + '...')
if insecure_ssl: if insecure_ssl:
try: try:
context = ssl._create_unverified_context() context = ssl._create_unverified_context()
@ -400,8 +403,6 @@ try:
conn = http.client.HTTPSConnection(hostname) conn = http.client.HTTPSConnection(hostname)
else: else:
conn = http.client.HTTPSConnection(hostname) conn = http.client.HTTPSConnection(hostname)
if verbose:
print('Connecting to ' + hostname + '...')
if init: if init:
# In initialization mode, start with an empty synchronization database. # In initialization mode, start with an empty synchronization database.