def check_resource_calendar_event(self, mailbox, uid=None): imap = IMAP() imap.connect() imap.set_acl(mailbox, "cyrus-admin", "lrs") imap.imap.m.select(imap.folder_quote(mailbox)) found = None retries = 10 while not found and retries > 0: retries -= 1 typ, data = imap.imap.m.search(None, '(UNDELETED HEADER SUBJECT "%s")' % (uid) if uid else '(UNDELETED HEADER X-Kolab-Type "application/x-vnd.kolab.event")') for num in data[0].split(): typ, data = imap.imap.m.fetch(num, '(RFC822)') event_message = message_from_string(data[0][1]) # return matching UID or first event found if uid and event_message['subject'] != uid: continue found = event_from_message(event_message) if found: break time.sleep(1) imap.disconnect() return found
def _synchronize(*args, **kw): log.info( _("Worker process %s handling %s") % (multiprocessing.current_process().name, kw['dn'])) entry = utils.normalize(kw) if not entry.has_key('mail'): return if not 'kolabinetorgperson' in entry['objectclass']: return imap = IMAP() imap.connect() if not imap.user_mailbox_exists(entry['mail']): if entry.has_key('mailhost'): server = entry['mailhost'] else: server = None imap.user_mailbox_create(entry['mail'], server=server) imap.disconnect()
def check_message_received(self, subject, from_addr=None, mailbox=None): if mailbox is None: mailbox = self.john['mailbox'] imap = IMAP() imap.connect() imap.set_acl(mailbox, "cyrus-admin", "lrs") imap.imap.m.select(mailbox) found = None retries = 10 while not found and retries > 0: retries -= 1 typ, data = imap.imap.m.search(None, '(UNDELETED HEADER FROM "%s")' % (from_addr) if from_addr else 'UNDELETED') for num in data[0].split(): typ, msg = imap.imap.m.fetch(num, '(RFC822)') message = message_from_string(msg[0][1]) if message['Subject'] == subject: found = message break time.sleep(1) imap.disconnect() return found
def purge_mailbox(self, mailbox): imap = IMAP() imap.connect() imap.set_acl(mailbox, "cyrus-admin", "lrwcdest") imap.imap.m.select(imap.folder_quote(mailbox)) typ, data = imap.imap.m.search(None, 'ALL') for num in data[0].split(): imap.imap.m.store(num, '+FLAGS', '\\Deleted') imap.imap.m.expunge() imap.disconnect()
class IMAPDataHandler(object): """ Collector handler to provide metadata from IMAP """ def __init__(self, *args, **kw): # load pykolab conf conf = pykolab.getConf() if not hasattr(conf, 'defaults'): conf.finalize_conf(fatal=False) self.imap = IMAP() def register(self, callback): interests = { 'GETMETADATA': { 'callback': self.get_imap_folder_metadata }, 'GETACL': { 'callback': self.get_imap_folder_acl } } callback(interests) 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) notification['metadata'] = metadata return json.dumps(notification)
def _synchronize(*args, **kw): log.info(_("Worker process %s handling %s") % (multiprocessing.current_process().name, kw['dn'])) entry = utils.normalize(entry) if not entry.has_key('mail'): return if not 'kolabinetorgperson' in entry['objectclass']: return imap = IMAP() imap.connect() if not imap.user_mailbox_exists(entry['mail']): if entry.has_key('mailhost'): server = entry['mailhost'] else: server = None imap.user_mailbox_create(entry['mail'], server=server) imap.disconnect()
def heartbeat(lastrun): global imap # run archival job every hour only now = int(time.time()) if lastrun == 0 or now - heartbeat._lastrun < 3600: return log.debug(_("module_resources.heartbeat(%d)") % (heartbeat._lastrun), level=8) # get a list of resource records from LDAP auth = Auth() auth.connect() resource_dns = auth.find_resource('*') # filter by resource_base_dn resource_base_dn = conf.get('ldap', 'resource_base_dn', None) if resource_base_dn is not None: resource_dns = [dn for dn in resource_dns if resource_base_dn in dn] if len(resource_dns) > 0: imap = IMAP() imap.connect() for resource_dn in resource_dns: resource_attrs = auth.get_entry_attributes(None, resource_dn, ['kolabtargetfolder']) if resource_attrs.has_key('kolabtargetfolder'): try: expunge_resource_calendar(resource_attrs['kolabtargetfolder']) except Exception, e: log.error(_("Expunge resource calendar for %s (%s) failed: %r") % ( resource_dn, resource_attrs['kolabtargetfolder'], e )) imap.disconnect()
def execute(*args, **kw): if not os.path.isdir(mybasepath): os.makedirs(mybasepath) for stage in ['incoming', 'ACCEPT', 'REJECT', 'HOLD', 'DEFER' ]: if not os.path.isdir(os.path.join(mybasepath, stage)): os.makedirs(os.path.join(mybasepath, stage)) log.debug(_("Resource Management called for %r, %r") % (args, kw), level=9) auth = Auth() auth.connect() imap = IMAP() imap.connect() # TODO: Test for correct call. filepath = args[0] if kw.has_key('stage'): log.debug( _("Issuing callback after processing to stage %s") % ( kw['stage'] ), level=8 ) log.debug(_("Testing cb_action_%s()") % (kw['stage']), level=8) if hasattr(modules, 'cb_action_%s' % (kw['stage'])): log.debug( _("Attempting to execute cb_action_%s()") % (kw['stage']), level=8 ) exec( 'modules.cb_action_%s(%r, %r)' % ( kw['stage'], 'resources', filepath ) ) return else: # Move to incoming new_filepath = os.path.join( mybasepath, 'incoming', os.path.basename(filepath) ) if not filepath == new_filepath: log.debug("Renaming %r to %r" % (filepath, new_filepath)) os.rename(filepath, new_filepath) filepath = new_filepath _message = json.load(open(filepath, 'r')) log.debug("Loaded message %r" % (_message), level=9) message = message_from_string(_message['data']) recipients = _message['to'] any_itips = False any_resources = False possibly_any_resources = True # An iTip message may contain multiple events. Later on, test if the message # is an iTip message by checking the length of this list. itip_events = itip_events_from_message(message) if not len(itip_events) > 0: log.info( _("Message is not an iTip message or does not contain any " + \ "(valid) iTip.") ) else: any_itips = True log.debug( _("iTip events attached to this message contain the " + \ "following information: %r") % (itip_events), level=9 ) if any_itips: # See if any iTip actually allocates a resource. if len([x['resources'] for x in itip_events if x.has_key('resources')]) == 0: if len([x['attendees'] for x in itip_events if x.has_key('attendees')]) == 0: possibly_any_resources = False else: possibly_any_resources = False if possibly_any_resources: for recipient in recipients: if not len(resource_record_from_email_address(recipient)) == 0: any_resources = True if any_resources: if not any_itips: log.debug(_("Not an iTip message, but sent to resource nonetheless. Reject message"), level=5) reject(filepath) return else: # Continue. Resources and iTips. We like. pass else: if not any_itips: log.debug(_("No itips, no resources, pass along"), level=5) accept(filepath) return else: log.debug(_("iTips, but no resources, pass along"), level=5) accept(filepath) return # A simple list of merely resource entry IDs that hold any relevance to the # iTip events resource_records = resource_records_from_itip_events(itip_events) # Get the resource details, which includes details on the IMAP folder resources = {} for resource_record in list(set(resource_records)): # Get the attributes for the record # See if it is a resource collection # If it is, expand to individual resources # If it is not, ... resource_attrs = auth.get_entry_attributes(None, resource_record, ['*']) if not 'kolabsharedfolder' in [x.lower() for x in resource_attrs['objectclass']]: if resource_attrs.has_key('uniquemember'): resources[resource_record] = resource_attrs for uniquemember in resource_attrs['uniquemember']: resource_attrs = auth.get_entry_attributes( None, uniquemember, ['*'] ) if 'kolabsharedfolder' in [x.lower() for x in resource_attrs['objectclass']]: resources[uniquemember] = resource_attrs resources[uniquemember]['memberof'] = resource_record else: resources[resource_record] = resource_attrs log.debug(_("Resources: %r") % (resources), level=8) # For each resource, determine if any of the events in question is in # conflict. # # Store the (first) conflicting event(s) alongside the resource information. start = time.time() for resource in resources.keys(): if not resources[resource].has_key('kolabtargetfolder'): continue mailbox = resources[resource]['kolabtargetfolder'] resources[resource]['conflict'] = False resources[resource]['conflicting_events'] = [] log.debug( _("Checking events in resource folder %r") % (mailbox), level=8 ) try: imap.imap.m.select(mailbox) except: log.error(_("Mailbox for resource %r doesn't exist") % (resource)) continue typ, data = imap.imap.m.search(None, 'ALL') num_messages = len(data[0].split()) for num in data[0].split(): # For efficiency, makes the routine non-deterministic if resources[resource]['conflict']: continue log.debug( _("Fetching message UID %r from folder %r") % (num, mailbox), level=9 ) typ, data = imap.imap.m.fetch(num, '(RFC822)') event_message = message_from_string(data[0][1]) if event_message.is_multipart(): for part in event_message.walk(): if part.get_content_type() == "application/calendar+xml": payload = part.get_payload(decode=True) event = pykolab.xml.event_from_string(payload) for itip in itip_events: _es = to_dt(event.get_start()) _is = to_dt(itip['start'].dt) _ee = to_dt(event.get_end()) _ie = to_dt(itip['end'].dt) if _es < _is: if _es <= _ie: if _ee <= _is: conflict = False else: conflict = True else: conflict = True elif _es == _is: conflict = True else: # _es > _is if _es <= _ie: conflict = True else: conflict = False if conflict: log.info( _("Event %r conflicts with event " + \ "%r") % ( itip['xml'].get_uid(), event.get_uid() ) ) resources[resource]['conflicting_events'].append(event) resources[resource]['conflict'] = True end = time.time() log.debug( _("start: %r, end: %r, total: %r, messages: %r") % ( start, end, (end-start), num_messages ), level=1 ) for resource in resources.keys(): log.debug(_("Polling for resource %r") % (resource), level=9) if not resources.has_key(resource): log.debug( _("Resource %r has been popped from the list") % (resource), level=9 ) continue if not resources[resource].has_key('conflicting_events'): log.debug(_("Resource is a collection"), level=9) continue if len(resources[resource]['conflicting_events']) > 0: # This is the event being conflicted with! for itip_event in itip_events: # Now we have the event that was conflicting if resources[resource]['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]: itip_event['xml'].set_attendee_participant_status( [a for a in itip_event['xml'].get_attendees() if a.get_email() == resources[resource]['mail']][0], "DECLINED" ) send_response(resources[resource]['mail'], itip_events) # TODO: Accept the message to the other attendees else: # This must have been a resource collection originally. # We have inserted the reference to the original resource # record in 'memberof'. if resources[resource].has_key('memberof'): original_resource = resources[resources[resource]['memberof']] _target_resource = resources[original_resource['uniquemember'][random.randint(0,(len(original_resource['uniquemember'])-1))]] # unset all for _r in original_resource['uniquemember']: del resources[_r] if original_resource['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]: itip_event['xml'].set_attendee_participant_status( [a for a in itip_event['xml'].get_attendees() if a.get_email() == original_resource['mail']][0], "DECLINED" ) send_response(original_resource['mail'], itip_events) # TODO: Accept the message to the other attendees else: # No conflicts, go accept for itip_event in itip_events: if resources[resource]['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]: itip_event['xml'].set_attendee_participant_status( [a for a in itip_event['xml'].get_attendees() if a.get_email() == resources[resource]['mail']][0], "ACCEPTED" ) log.debug( _("Adding event to %r") % ( resources[resource]['kolabtargetfolder'] ), level=9 ) imap.imap.m.setacl(resources[resource]['kolabtargetfolder'], "cyrus-admin", "lrswipkxtecda") imap.imap.m.append( resources[resource]['kolabtargetfolder'], None, None, itip_event['xml'].to_message().as_string() ) send_response(resources[resource]['mail'], itip_event) else: # This must have been a resource collection originally. # We have inserted the reference to the original resource # record in 'memberof'. if resources[resource].has_key('memberof'): original_resource = resources[resources[resource]['memberof']] # Randomly selects a target resource from the resource # collection. _target_resource = resources[original_resource['uniquemember'][random.randint(0,(len(original_resource['uniquemember'])-1))]] # Remove all resources from the collection. for _r in original_resource['uniquemember']: del resources[_r] if original_resource['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]: # # Delegate: # # - delegator: the original resource collection # - delegatee: the target resource # itip_event['xml'].delegate( original_resource['mail'], _target_resource['mail'] ) itip_event['xml'].set_attendee_participant_status( [a for a in itip_event['xml'].get_attendees() if a.get_email() == _target_resource['mail']][0], "ACCEPTED" ) log.debug( _("Adding event to %r") % ( _target_resource['kolabtargetfolder'] ), level=9 ) # TODO: The Cyrus IMAP (or Dovecot) Administrator login # name comes from configuration. imap.imap.m.setacl(_target_resource['kolabtargetfolder'], "cyrus-admin", "lrswipkxtecda") imap.imap.m.append( _target_resource['kolabtargetfolder'], None, None, itip_event['xml'].to_message().as_string() ) send_response(original_resource['mail'], itip_event) # Disconnect IMAP or we lock the mailbox almost constantly imap.disconnect() del imap os.unlink(filepath)
def setup_class(self, *args, **kw): # set language to default pykolab.translate.setUserLanguage(conf.get('kolab','default_locale')) self.itip_reply_subject = _('"%(summary)s" has been %(status)s') from tests.functional.purge_users import purge_users purge_users() self.john = { 'displayname': 'John Doe', 'mail': '*****@*****.**', 'dn': 'uid=doe,ou=People,dc=example,dc=org', 'preferredlanguage': 'en_US', 'mailbox': 'user/[email protected]', 'kolabcalendarfolder': 'user/john.doe/[email protected]', 'kolabtasksfolder': 'user/john.doe/[email protected]', 'kolabinvitationpolicy': ['ACT_UPDATE_AND_NOTIFY','ACT_MANUAL'] } self.jane = { 'displayname': 'Jane Manager', 'mail': '*****@*****.**', 'dn': 'uid=manager,ou=People,dc=example,dc=org', 'preferredlanguage': 'en_US', 'mailbox': 'user/[email protected]', 'kolabcalendarfolder': 'user/jane.manager/[email protected]', 'kolabtasksfolder': 'user/jane.manager/[email protected]', 'kolabconfidentialcalendar': 'user/jane.manager/Calendar/[email protected]', 'kolabinvitationpolicy': ['ACT_ACCEPT_IF_NO_CONFLICT','ACT_REJECT_IF_CONFLICT','TASK_ACCEPT','ACT_UPDATE'] } self.jack = { 'displayname': 'Jack Tentative', 'mail': '*****@*****.**', 'dn': 'uid=tentative,ou=People,dc=example,dc=org', 'preferredlanguage': 'en_US', 'mailbox': 'user/[email protected]', 'kolabcalendarfolder': 'user/jack.tentative/[email protected]', 'kolabtasksfolder': 'user/jack.tentative/[email protected]', 'kolabinvitationpolicy': ['ACT_TENTATIVE_IF_NO_CONFLICT','ALL_SAVE_TO_FOLDER','ACT_UPDATE'] } self.mark = { 'displayname': 'Mark German', 'mail': '*****@*****.**', 'dn': 'uid=german,ou=People,dc=example,dc=org', 'preferredlanguage': 'de_DE', 'mailbox': 'user/[email protected]', 'kolabcalendarfolder': 'user/mark.german/[email protected]', 'kolabtasksfolder': 'user/mark.german/[email protected]', 'kolabinvitationpolicy': ['ACT_ACCEPT','ACT_UPDATE_AND_NOTIFY'] } self.external = { 'displayname': 'Bob External', 'mail': '*****@*****.**' } from tests.functional.user_add import user_add user_add("John", "Doe", kolabinvitationpolicy=self.john['kolabinvitationpolicy'], preferredlanguage=self.john['preferredlanguage']) user_add("Jane", "Manager", kolabinvitationpolicy=self.jane['kolabinvitationpolicy'], preferredlanguage=self.jane['preferredlanguage']) user_add("Jack", "Tentative", kolabinvitationpolicy=self.jack['kolabinvitationpolicy'], preferredlanguage=self.jack['preferredlanguage']) user_add("Mark", "German", kolabinvitationpolicy=self.mark['kolabinvitationpolicy'], preferredlanguage=self.mark['preferredlanguage']) time.sleep(1) from tests.functional.synchronize import synchronize_once synchronize_once() # create confidential calendar folder for jane imap = IMAP() imap.connect(domain='example.org') # sets self.domain imap.user_mailbox_create_additional_folders(self.jane['mail'], { 'Calendar/Confidential': { 'annotations': { '/shared/vendor/kolab/folder-type': "event", '/private/vendor/kolab/folder-type': "event.confidential" } } }) imap.disconnect()