Changeset View
Changeset View
Standalone View
Standalone View
cyruslib.py
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | except ImportError as e: | ||||
exit(1) | exit(1) | ||||
Commands = { | Commands = { | ||||
'RECONSTRUCT' : ('AUTH',), | 'RECONSTRUCT' : ('AUTH',), | ||||
'DUMP' : ('AUTH',), # To check admin status | 'DUMP' : ('AUTH',), # To check admin status | ||||
'ID' : ('AUTH',), # Only one ID allowed in non auth mode | 'ID' : ('AUTH',), # Only one ID allowed in non auth mode | ||||
'GETANNOTATION': ('AUTH',), | 'GETANNOTATION': ('AUTH',), | ||||
'SETANNOTATION': ('AUTH',), | 'SETANNOTATION': ('AUTH',), | ||||
'GETMETADATA': ('AUTH',), | |||||
'SETMETADATA': ('AUTH',), | |||||
'XFER' : ('AUTH',) | 'XFER' : ('AUTH',) | ||||
} | } | ||||
imaplib.Commands.update(Commands) | imaplib.Commands.update(Commands) | ||||
DEFAULT_SEP = '.' | DEFAULT_SEP = '.' | ||||
QUOTE = '"' | QUOTE = '"' | ||||
DQUOTE = '""' | DQUOTE = '""' | ||||
Show All 40 Lines | |||||
def getflags(test): | def getflags(test): | ||||
flags = [] | flags = [] | ||||
for flag in test.split('\\'): | for flag in test.split('\\'): | ||||
flag = flag.strip() | flag = flag.strip() | ||||
if len(flag): flags.append(flag) | if len(flag): flags.append(flag) | ||||
return flags | return flags | ||||
def parseToken(data, offset): | |||||
i = offset | |||||
while i < len(data): | |||||
c = data[i:i+1] | |||||
if c == b' ': | |||||
return data[offset:i], i + 1 | |||||
if c == b')': | |||||
return data[offset:i], i + 1 | |||||
i += 1 | |||||
# TODO handle escape sequences? | |||||
# TODO handle literal continuatins | |||||
def parseLiteral(data, offset): | |||||
i = offset | |||||
while i < len(data): | |||||
c = data[i:i+1] | |||||
if c == b'"': | |||||
return data[offset:i], i + 1 | |||||
i += 1 | |||||
def parse(data, offset): | |||||
result = [] | |||||
i = offset | |||||
while i < len(data): | |||||
c = data[i:i+1] | |||||
print(c) | |||||
if c == b'(': | |||||
res, newOffset = parse(data, i + 1); | |||||
result.append(res) | |||||
i = newOffset | |||||
continue | |||||
if c == b')': | |||||
return result, i + 1 | |||||
if c == b'"': | |||||
res, newOffset = parseLiteral(data, i + 1); | |||||
print("Found literal", res, newOffset) | |||||
result.append(res) | |||||
i = newOffset | |||||
continue | |||||
if c != b' ': | |||||
res, newOffset = parseToken(data, i); | |||||
print("Found token", res, newOffset) | |||||
result.append(res) | |||||
i = newOffset | |||||
continue | |||||
i += 1 | |||||
return result, i | |||||
def tokenize(data): | |||||
result, _ = parse(data, 0) | |||||
return result | |||||
### A smart function to return an array of split strings | ### A smart function to return an array of split strings | ||||
### and honours quoted strings | ### and honours quoted strings | ||||
def splitquote(text): | def splitquote(text): | ||||
data = text.split(QUOTE) | data = text.split(QUOTE) | ||||
if len(data) == 1: # no quotes | if len(data) == 1: # no quotes | ||||
res = data[0].split() | res = data[0].split() | ||||
else: | else: | ||||
res = [] | res = [] | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | def setannotation(self, mailbox, desc, value, shared=False): | ||||
value = "NIL" | value = "NIL" | ||||
if shared: | if shared: | ||||
typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.shared'), value) ) | typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.shared'), value) ) | ||||
else: | else: | ||||
typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.priv'), value) ) | typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.priv'), value) ) | ||||
return self._untagged_response(typ, dat, 'ANNOTATION') | return self._untagged_response(typ, dat, 'ANNOTATION') | ||||
def setmetadata(self, mailbox, desc, value, shared=False): | |||||
if value: | |||||
value = value.join(['"', '"']) | |||||
else: | |||||
value = "NIL" | |||||
if shared: | |||||
typ, dat = self._simple_command('SETMETADATA', mailbox, | |||||
"(/shared%s %s)" % (desc,value)) | |||||
else: | |||||
typ, dat = self._simple_command('SETMETADATA', mailbox, | |||||
"(/private%s %s)" % (desc,value)) | |||||
return self._untagged_response(typ, dat, 'METADATA') | |||||
def setquota(self, mailbox, limit): | def setquota(self, mailbox, limit): | ||||
"""Set quota of a mailbox""" | """Set quota of a mailbox""" | ||||
if limit == 0: | if limit == 0: | ||||
quota = '()' | quota = '()' | ||||
else: | else: | ||||
quota = '(STORAGE %s)' % limit | quota = '(STORAGE %s)' % limit | ||||
return self._simple_command('SETQUOTA', mailbox, quota) | return self._simple_command('SETQUOTA', mailbox, quota) | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | def setannotation(self, mailbox, desc, value, shared=False): | ||||
value = "NIL" | value = "NIL" | ||||
if shared: | if shared: | ||||
typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.shared'), value) ) | typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.shared'), value) ) | ||||
else: | else: | ||||
typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.priv'), value) ) | typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.priv'), value) ) | ||||
return self._untagged_response(typ, dat, 'ANNOTATION') | return self._untagged_response(typ, dat, 'ANNOTATION') | ||||
def getmetadata(self, mailbox, pattern='*', shared=None): | |||||
# If pattern is '*' clean pattern and search all entries under /shared | |||||
# and/or /private (depens on the shared parameter value) to emulate the | |||||
# ANNOTATEMORE behaviour | |||||
if pattern == '*': | |||||
pattern = '' | |||||
options = '(DEPTH infinity)' | |||||
else: | |||||
options = '(DEPTH 0)' | |||||
if shared == None: | |||||
entries = '(/shared%s /private%s)' % (pattern, pattern) | |||||
elif shared: | |||||
entries = "/shared%s" % pattern | |||||
else: | |||||
entries = "/private%s" % pattern | |||||
typ, dat = self._simple_command('GETMETADATA', mailbox, options, entries) | |||||
return self._untagged_response(typ, dat, 'METADATA') | |||||
def setmetadata(self, mailbox, desc, value, shared=False): | |||||
if value: | |||||
value = value.join(['"', '"']) | |||||
else: | |||||
value = "NIL" | |||||
if shared: | |||||
typ, dat = self._simple_command('SETMETADATA', mailbox, | |||||
"(/shared%s %s)" % (desc,value)) | |||||
else: | |||||
typ, dat = self._simple_command('SETMETADATA', mailbox, | |||||
"(/private%s %s)" % (desc,value)) | |||||
return self._untagged_response(typ, dat, 'METADATA') | |||||
def setquota(self, mailbox, limit): | def setquota(self, mailbox, limit): | ||||
"""Set quota of a mailbox""" | """Set quota of a mailbox""" | ||||
if limit == 0: | if limit == 0: | ||||
quota = '()' | quota = '()' | ||||
else: | else: | ||||
quota = '(STORAGE %s)' % limit | quota = '(STORAGE %s)' % limit | ||||
return self._simple_command('SETQUOTA', mailbox, quota) | return self._simple_command('SETQUOTA', mailbox, quota) | ||||
▲ Show 20 Lines • Show All 398 Lines • ▼ Show 20 Lines | def sq(self, mailbox, limit): | ||||
try: | try: | ||||
limit = int(limit) | limit = int(limit) | ||||
except ValueError: | except ValueError: | ||||
self.__verbose( '[SETQUOTA %s] BAD: %s %s' % (mailbox, self.ERROR.get("SETQUOTA")[1], limit) ) | self.__verbose( '[SETQUOTA %s] BAD: %s %s' % (mailbox, self.ERROR.get("SETQUOTA")[1], limit) ) | ||||
raise self.__doraise("SETQUOTA") | raise self.__doraise("SETQUOTA") | ||||
res, msg = self.__docommand("setquota", self.decode(mailbox), limit) | res, msg = self.__docommand("setquota", self.decode(mailbox), limit) | ||||
self.__verbose( '[SETQUOTA %s %s] %s: %s' % (mailbox, limit, res, msg[0]) ) | self.__verbose( '[SETQUOTA %s %s] %s: %s' % (mailbox, limit, res, msg[0]) ) | ||||
def getannotation(self, mailbox, pattern='*'): | def getmetadata(self, mailbox, pattern='*', shared=None): | ||||
"""Get Annotation""" | """Get Metadata""" | ||||
self.__prepare('GETANNOTATION') | # This test needs to be reviewed | ||||
res, data = self.__docommand('getannotation', self.decode(mailbox), pattern) | #if not self.metadata: | ||||
# return {} | |||||
# Annotations vs. Metadata fix ... we set a pattern that we know is | |||||
# good enough for our purposes for now, but the fact is that the | |||||
# calling programs should be fixed instead. | |||||
res, data = self.__docommand("getmetadata", self.decode(mailbox), pattern, shared) | |||||
if (len(data) == 1) and data[0] is None: | if (len(data) == 1) and data[0] is None: | ||||
self.__verbose( '[GETANNOTATION %s] No results' % (mailbox) ) | self.__verbose( '[GETMETADATA %s] No results' % (mailbox) ) | ||||
return {} | return {} | ||||
ann = {} | ann = {} | ||||
annotations = [] | annotations = [] | ||||
empty_values = [ "NIL", '" "', None, '', ' ' ] | |||||
# Deal with partial metadata responses | |||||
concat_items = [] | concat_items = [] | ||||
for item in data: | for item in data: | ||||
if isinstance(item, tuple): | if isinstance(item, tuple): | ||||
item = ' '.join([str(x) for x in item]) | item = ' '.join([str(x) for x in item]) | ||||
if len(concat_items) > 0: | if len(concat_items) > 0: | ||||
concat_items.append(item) | concat_items.append(item) | ||||
joined = b''.join(concat_items) | |||||
if ''.join(concat_items).count('(') == ''.join(concat_items).count(')'): | if joined.count(b'(') == joined.count(b')'): | ||||
annotations.append(''.join(concat_items)) | annotations.append(joined) | ||||
concat_items = [] | concat_items = [] | ||||
continue | continue | ||||
else: | else: | ||||
if item.count(b'(') == item.count(b')'): | |||||
if item.count('(') == item.count(')'): | |||||
annotations.append(item) | annotations.append(item) | ||||
continue | continue | ||||
else: | else: | ||||
concat_items.append(item) | concat_items.append(item) | ||||
continue | continue | ||||
for annotation in annotations: | for annotation in annotations: | ||||
annotation = annotation.strip() | annotation = annotation.strip() | ||||
tokens = tokenize(annotation) | |||||
folder = tokens[0] | |||||
if not annotation[0] == '"': | if folder != mailbox: | ||||
folder = annotation.split('"')[0].replace('"','').strip() | quoted_mailbox = "\"%s\"" % (mailbox) | ||||
key = annotation.split('"')[1].replace('"','').replace("'","").strip() | if folder != quoted_mailbox: | ||||
_annot = annotation.split('(')[1].split(')')[0].strip() | print("mismatch") | ||||
else: | print(quoted_mailbox) | ||||
folder = annotation.split('"')[1].replace('"','').strip() | self.__verbose( | ||||
key = annotation.split('"')[3].replace('"','').replace("'","").strip() | '[GETMETADATA %s] Mailbox \'%s\' is not the same as \'%s\'' \ | ||||
_annot = annotation.split('(')[1].split(')')[0].strip() | % (mailbox, quoted_mailbox, folder) | ||||
) | |||||
return {} | |||||
if folder not in ann: | if folder not in ann: | ||||
ann[folder] = {} | ann[folder] = {} | ||||
# Iterate over list, two items at a time | |||||
try: | for key, value in zip(*[iter(tokens[1])] * 2): | ||||
value_priv = _annot[(_annot.index('"value.priv"')+len('"value.priv"')):_annot.index('"size.priv"')].strip() | print(key) | ||||
except ValueError: | print(value) | ||||
value_priv = None | if value != b'NIL': | ||||
ann[folder][key] = value | |||||
try: | |||||
size_priv = _annot[(_annot.index('"size.priv"')+len('"size.priv"')):].strip().split('"')[1].strip() | |||||
try: | |||||
value_priv = value_priv[value_priv.index('{%s}' % (size_priv))+len('{%s}' % (size_priv)):].strip() | |||||
except Exception: | |||||
pass | |||||
except Exception: | |||||
pass | |||||
if value_priv in empty_values: | |||||
value_priv = None | |||||
else: | |||||
try: | |||||
value_priv = value_priv[:value_priv.index('"content-type.priv"')].strip() | |||||
except: | |||||
pass | |||||
try: | |||||
value_priv = value_priv[:value_priv.index('"modifiedsince.priv"')].strip() | |||||
except: | |||||
pass | |||||
if value_priv.startswith('"'): | |||||
value_priv = value_priv[1:] | |||||
if value_priv.endswith('"'): | |||||
value_priv = value_priv[:-1] | |||||
if value_priv in empty_values: | |||||
value_priv = None | |||||
try: | |||||
value_shared = _annot[(_annot.index('"value.shared"')+len('"value.shared"')):_annot.index('"size.shared"')].strip() | |||||
except ValueError: | |||||
value_shared = None | |||||
try: | |||||
size_shared = _annot[(_annot.index('"size.shared"')+len('"size.shared"')):].strip().split('"')[1].strip() | |||||
try: | |||||
value_shared = value_shared[value_shared.index('{%s}' % (size_shared))+len('{%s}' % (size_shared)):].strip() | |||||
except Exception: | |||||
pass | |||||
except Exception: | |||||
pass | |||||
if value_shared in empty_values: | |||||
value_shared = None | |||||
else: | |||||
try: | |||||
value_shared = value_shared[:value_shared.index('"content-type.shared"')].strip() | |||||
except: | |||||
pass | |||||
try: | |||||
value_shared = value_shared[:value_shared.index('"modifiedsince.shared"')].strip() | |||||
except: | |||||
pass | |||||
if value_shared.startswith('"'): | |||||
value_shared = value_shared[1:] | |||||
if value_shared.endswith('"'): | |||||
value_shared = value_shared[:-1] | |||||
if value_shared in empty_values: | |||||
value_shared = None | |||||
if not value_priv == None: | |||||
ann[folder]['/private' + key] = value_priv | |||||
if not value_shared == None: | |||||
ann[folder]['/shared' + key] = value_shared | |||||
return ann | return ann | ||||
def setannotation(self, mailbox, annotation, value, shared=False): | def setmetadata(self, mailbox, desc, value, shared=False): | ||||
"""Set Annotation""" | """Set METADADATA""" | ||||
self.__prepare('SETANNOTATION') | res, msg = self.__docommand("setmetadata", self.decode(mailbox), desc, value, shared) | ||||
res, msg = self.__docommand("setannotation", self.decode(mailbox), annotation, value, shared) | self.__verbose( '[SETMETADATA %s] %s: %s' % (mailbox, res, msg[0]) ) | ||||
self.__verbose( '[SETANNOTATION %s] %s: %s' % (mailbox, res, msg[0]) ) | |||||
def __reconstruct(self, mailbox): | def __reconstruct(self, mailbox): | ||||
if not mailbox: | if not mailbox: | ||||
return True | return True | ||||
res, msg = self.__docommand("reconstruct", self.decode(mailbox)) | res, msg = self.__docommand("reconstruct", self.decode(mailbox)) | ||||
self.__verbose( '[RECONSTRUCT %s] %s: %s' % (mailbox, res, msg[0]) ) | self.__verbose( '[RECONSTRUCT %s] %s: %s' % (mailbox, res, msg[0]) ) | ||||
def reconstruct(self, mailbox, recursive=True): | def reconstruct(self, mailbox, recursive=True): | ||||
▲ Show 20 Lines • Show All 46 Lines • Show Last 20 Lines |