def run(self, notification): # call super for some basic notification processing (notification, jobs) = super(MessageHandlerBase, self).run(notification) relevant = False if 'archive' in self.features: relevant = True if 'backup' in self.features: relevant = True if not relevant: return (notification, jobs) # Insert the URI UID (if it exists) in to uidset for further handlers if notification.has_key('uri') and notification.has_key('uidset'): uri = parse_imap_uri(notification['uri']) if uri.has_key('UID'): notification['uidset'] = [ uri['UID'] ] # message notifications require message headers if not notification.has_key('messageHeaders'): self.log.debug("Adding HEADER job for " + self.event, level=8) jobs.append(b"HEADER") return (notification, jobs) return (notification, jobs)
def test_parse_imap_uri(self): url = parse_imap_uri("imap://[email protected]@kolab.example.org/Calendar/Personal%20Calendar;UIDVALIDITY=1411487702/;UID=3") self.assertEqual(url['host'], 'kolab.example.org') self.assertEqual(url['user'], 'john.doe') self.assertEqual(url['domain'], 'example.org') self.assertEqual(url['path'], 'Calendar/Personal Calendar') self.assertEqual(url['UID'], '3')
def get_imap_folder_acl(self, notification): notification = json.loads(notification) log.debug("GETACL for %r" % (notification), level=9) # split the uri parameter into useful parts uri = parse_imap_uri(notification['uri']) folder_path = imap_folder_path(uri) # get folder acls using pykolab's imap module acls = {} try: self.imap.connect() acls = self.imap.list_acls(folder_path) self.imap.disconnect() except Exception, e: log.warning("Failed to get ACLs for %r: %r", folder_path, e)
def get_imap_folder_metadata(self, notification): notification = json.loads(notification) log.debug("GETMETADATA for %r" % (notification), level=9) # split the uri parameter into useful parts uri = parse_imap_uri(notification['uri']) folder_path = imap_folder_path(uri) # get metadata using pykolab's imap module metadata = {} try: self.imap.connect() metadata = self.imap.get_metadata(folder_path)[folder_path] self.imap.disconnect() except Exception, e: log.warning("Failed to get metadata for %r: %r", folder_path, e)
def notificaton2folder(self, notification, attrib='uri'): """ Turn the given notification record into a folder document. including the computation of a unique identifier which is a checksum of the (relevant) folder properties. """ # split the uri parameter into useful parts uri = parse_imap_uri(notification[attrib]) # re-compose folder uri templ = "imap://%(user)s@%(domain)s@%(host)s/" if uri['user'] is None: templ = "imap://%(host)s/" folder_uri = templ % uri + urllib.quote(uri['path']) if not notification.has_key('metadata'): return False if not notification.has_key('folder_uniqueid') and \ notification['metadata'].has_key( '/shared/vendor/cmu/cyrus-imapd/uniqueid' ): notification['folder_uniqueid'] = notification['metadata']['/shared/vendor/cmu/cyrus-imapd/uniqueid'] if not notification.has_key('folder_uniqueid'): notification['folder_uniqueid'] = hashlib.md5( notification[attrib] ).hexdigest() body = { '@version': bonnie.API_VERSION, '@timestamp': datetime.datetime.now( tzutc() ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), 'uniqueid': notification['folder_uniqueid'], 'metadata': notification['metadata'], 'acl': dict( (self.resolve_username(k, force=True),v) for k,v in notification['acl'].iteritems() ), 'type': notification['metadata']['/shared/vendor/kolab/folder-type'] if notification['metadata'].has_key('/shared/vendor/kolab/folder-type') else 'mail', 'owner': uri['user'] + '@' + uri['domain'] if uri['user'] is not None else 'nobody', 'server': uri['host'], 'name': re.sub('@.+$', '', uri['path']), 'uri': folder_uri, } # compute folder object signature and the unique identifier ignore_metadata = [ '/shared/vendor/cmu/cyrus-imapd/lastupdate', '/shared/vendor/cmu/cyrus-imapd/pop3newuidl', '/shared/vendor/cmu/cyrus-imapd/size' ] signature = { '@version': bonnie.API_VERSION, 'owner': body['owner'], 'server': body['server'], 'uniqueid': notification['folder_uniqueid'], 'metadata': [ (k,v) for k,v in sorted(body['metadata'].iteritems()) if k not in ignore_metadata ], 'acl': [ (k,v) for k,v in sorted(body['acl'].iteritems()) ], } serialized = ";".join( "%s:%s" % (k,v) for k,v in sorted(signature.iteritems()) ) folder_id = hashlib.md5(serialized).hexdigest() return dict(id=folder_id, body=body)
def _retrieve_contents_for_messages(self, notification, headers_only=False): """ Helper method to deliver message contents/headers """ messageContents = {} messageHeaders = {} # split the uri parameter into useful parts uri = parse_imap_uri(notification['uri']) if notification.has_key('uidset'): message_uids = expand_uidset(notification['uidset']) elif notification.has_key('vnd.cmu.oldUidset'): message_uids = expand_uidset(notification['vnd.cmu.oldUidset']) elif uri.has_key('UID'): message_uids = [ uri['UID'] ] notification['uidset'] = ','.join(message_uids) # resolve uri into a mailbox path on the local file stystem mailbox_path = utils.imap_mailbox_fs_path(uri) log.debug("Using mailbox path: %r" % (mailbox_path), level=8) # mailbox exists, try reading the message files if os.path.exists(mailbox_path): for message_uid in message_uids: # using message file path like /var/spool/imap/domain/e/example.org/k/lists/kolab^org/devel/lists/kolab.org/[email protected]/1. message_file_path = "%s/%s." % (mailbox_path, message_uid) log.debug("Open message file: %r" % (message_file_path), level=8) attempts = 5 while attempts > 0: attempts -= 1 if os.access(message_file_path, os.R_OK): fp = open(message_file_path, 'r') if headers_only: data = '' for line in fp: data += line if line.strip() == '': break; data += "\r\n" else: data = fp.read() fp.close() # use email lib to parse message headers try: # find header delimiter pos = data.find("\r\n\r\n") message = message_from_string(data[0:pos]) headers = decode_message_headers(message) except: headers = dict() messageHeaders[message_uid] = headers # append raw message data and parsed headers messageContents[message_uid] = data messageHeaders[message_uid] = headers break elif attempts > 0: log.debug("Failed to open message file %r; retry %d more times" % ( message_file_path, attempts ), level=5) else: log.warning("Failed to open message file %r for uri=%s; uid=%s" % ( message_file_path, notification, message_uid )) time.sleep(1) # end while # end for else: log.warning("Mailbox path %r does not exits for uri=%s" % (mailbox_path, notification['uri'])) return (messageContents, messageHeaders)