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
Пример #2
0
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()
    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()
Пример #6
0
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)
Пример #7
0
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()
Пример #8
0
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()
Пример #9
0
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)
Пример #10
0
    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()