Ejemplo n.º 1
0
 def test_002_resource_collection(self):
     auth = Auth()
     auth.connect()
     attrs = auth.get_entry_attributes(None, self.cars['dn'], ['*'])
     self.assertIn('groupofuniquenames', attrs['objectclass'])
     self.assertEqual(len(attrs['uniquemember']), 3)
     self.assertEqual(attrs['kolabinvitationpolicy'], 'ACT_ACCEPT')
Ejemplo n.º 2
0
 def test_002_resource_collection(self):
     auth = Auth()
     auth.connect()
     attrs = auth.get_entry_attributes(None, self.cars['dn'], ['*'])
     self.assertIn('groupofuniquenames', attrs['objectclass'])
     self.assertEqual(len(attrs['uniquemember']), 3)
     self.assertEqual(attrs['kolabinvitationpolicy'], 'ACT_ACCEPT')
Ejemplo n.º 3
0
class LDAPDataHandler(object):
    """
        Collector handler to provide user data from LDAP
    """

    def __init__(self, *args, **kw):
        # load pykolab conf
        self.pykolab_conf = pykolab.getConf()
        if not hasattr(self.pykolab_conf, 'defaults'):
            self.pykolab_conf.finalize_conf(fatal=False)

        self.ldap = Auth()
        self.ldap.connect()

    def register(self, callback):
        interests = {
                'GETUSERDATA': { 'callback': self.get_user_data }
            }

        callback(interests)

    def get_user_data(self, notification):
        notification = json.loads(notification)
        log.debug("GETUSERDATA for %r" % (notification), level=9)

        if notification.has_key('user'):
            try:
                user_dn = self.ldap.find_user_dn(notification['user'], True)
                log.debug("User DN for %s: %r" % (notification['user'], user_dn), level=8)
            except Exception, e:
                log.error("LDAP connection error: %r", e)
                user_dn = None

            if user_dn:
                unique_attr = self.pykolab_conf.get('ldap', 'unique_attribute', 'nsuniqueid')
                user_rec = self.ldap.get_entry_attributes(None, user_dn, [unique_attr, 'cn'])
                log.debug("User attributes: %r" % (user_rec), level=8)

                if user_rec and user_rec.has_key(unique_attr):
                    user_rec['dn'] = user_dn
                    user_rec['id'] = user_rec[unique_attr]
                    del user_rec[unique_attr]
            else:
                user_rec = None

            notification['user_data'] = user_rec

        return json.dumps(notification)
Ejemplo n.º 4
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()
Ejemplo n.º 5
0
def get_resource_owner(resource):
    """
        Get this resource's owner record
    """
    global auth

    if not auth:
        auth = Auth()
        auth.connect()

    owners = []

    if resource.has_key('owner'):
        if not isinstance(resource['owner'], list):
            owners = [ resource['owner'] ]
        else:
            owners = resource['owner']

    else:
        # get owner attribute from collection
        collections = auth.search_entry_by_attribute('uniquemember', resource['dn'])
        if not isinstance(collections, list):
            collections = [ collections ]

        for dn,collection in collections:
            if collection.has_key('owner') and isinstance(collection['owner'], list):
                owners += collection['owner']
            elif collection.has_key('owner'):
                owners.append(collection['owner'])

    for dn in owners:
        owner = auth.get_entry_attributes(None, dn, ['cn','mail','telephoneNumber'])
        if owner is not None:
            return owner

    return None
Ejemplo n.º 6
0
def execute(*args, **kw):

    try:
        primary_rcpt_address = conf.cli_args.pop(0)
        try:
            secondary_rcpt_address = conf.cli_args.pop(0)
        except:
            print >> sys.stderr, _("Specify the (new) alias address")
            sys.exit(1)
    except:
        print >> sys.stderr, _("Specify the existing recipient address")
        sys.exit(1)

    if len(primary_rcpt_address.split('@')) > 1:
        primary_rcpt_domain = primary_rcpt_address.split('@')[-1]
    else:
        primary_rcpt_domain = conf.get('kolab', 'primary_domain')

    auth = Auth(domain=primary_rcpt_domain)

    domains = auth.list_domains()

    #print domains

    if len(secondary_rcpt_address.split('@')) > 1:
        secondary_rcpt_domain = secondary_rcpt_address.split('@')[-1]
    else:
        secondary_rcpt_domain = conf.get('kolab', 'primary_domain')

    # Check if either is in fact a domain
    if not primary_rcpt_domain.lower() in domains.keys():
        print >> sys.stderr, _("Domain %r is not a local domain") % (
            primary_rcpt_domain)
        sys.exit(1)

    if not secondary_rcpt_domain.lower() in domains.keys():
        print >> sys.stderr, _("Domain %r is not a local domain") % (
            secondary_rcpt_domain)
        sys.exit(1)

    if not primary_rcpt_domain == secondary_rcpt_domain:
        if not domains[primary_rcpt_domain] == domains[secondary_rcpt_domain]:
            print >> sys.stderr, _(
                "Primary and secondary domain do not have the same parent domain"
            )
            sys.exit(1)

    primary_recipient_dn = auth.find_recipient(primary_rcpt_address)

    if primary_recipient_dn == [] or len(primary_recipient_dn) == 0:
        print >> sys.stderr, _("No such recipient %r") % (primary_rcpt_address)
        sys.exit(1)

    secondary_recipient_dn = auth.find_recipient(secondary_rcpt_address)

    if not secondary_recipient_dn == [] and not len(
            secondary_recipient_dn) == 0:
        print >> sys.stderr, _("Recipient for alias %r already exists") % (
            secondary_rcpt_address)
        sys.exit(1)

    rcpt_attrs = conf.get_list('ldap', 'mail_attributes')

    primary_rcpt_attr = rcpt_attrs[0]

    if len(rcpt_attrs) >= 2:
        secondary_rcpt_attr = rcpt_attrs[1]
    else:
        print >> sys.stderr, _("Environment is not configured for " + \
            "users to hold secondary mail attributes")

        sys.exit(1)

    primary_recipient = auth.get_entry_attributes(primary_rcpt_domain,
                                                  primary_recipient_dn,
                                                  rcpt_attrs)

    if not primary_recipient.has_key(primary_rcpt_attr):
        print >> sys.stderr, _(
            "Recipient %r is not the primary recipient for address %r") % (
                primary_recipient, primary_rcpt_address)
        sys.exit(1)

    if not primary_recipient.has_key(secondary_rcpt_attr):
        auth.set_entry_attributes(
            primary_rcpt_domain, primary_recipient_dn,
            {secondary_rcpt_attr: [secondary_rcpt_address]})
    else:
        if isinstance(primary_recipient[secondary_rcpt_attr], basestring):
            new_secondary_rcpt_attrs = [
                primary_recipient[secondary_rcpt_attr], secondary_rcpt_address
            ]

        else:
            new_secondary_rcpt_attrs = \
                    primary_recipient[secondary_rcpt_attr] + \
                    [ secondary_rcpt_address ]

        auth.set_entry_attributes(
            primary_rcpt_domain, primary_recipient_dn,
            {secondary_rcpt_attr: new_secondary_rcpt_attrs})
Ejemplo n.º 7
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)
Ejemplo n.º 8
0
def execute(*args, **kw):
    try:
        address = conf.cli_args.pop(0)
    except:
        address = utils.ask_question(_("Email Address"))

    auth = Auth()
    auth.connect()

    user = auth.find_recipient(address)

    # Get the main, default backend
    backend = conf.get('kolab', 'imap_backend')

    if len(address.split('@')) > 1:
        domain = address.split('@')[1]
    else:
        domain = conf.get('kolab', 'primary_domain')

    if conf.has_section(domain) and conf.has_option(domain, 'imap_backend'):
        backend = conf.get(domain, 'imap_backend')

    if conf.has_section(domain) and conf.has_option(domain, 'imap_uri'):
        uri = conf.get(domain, 'imap_uri')
    else:
        uri = conf.get(backend, 'uri')

    hostname = None
    port = None

    result = urlparse(uri)

    if hasattr(result, 'hostname'):
        hostname = result.hostname
    else:
        scheme = uri.split(':')[0]
        (hostname, port) = uri.split('/')[2].split(':')

    port = 4190

    # Get the credentials
    admin_login = conf.get(backend, 'admin_login')
    admin_password = conf.get(backend, 'admin_password')

    import sievelib.managesieve

    sieveclient = sievelib.managesieve.Client(hostname, port,
                                              conf.debuglevel > 8)
    sieveclient.connect(None, None, True)
    sieveclient._plain_authentication(admin_login, admin_password, address)
    sieveclient.authenticated = True

    result = sieveclient.listscripts()

    if result == None:
        active = None
        scripts = []
    else:
        active, scripts = result

    log.debug(_("Found the following scripts for user %s: %s") %
              (address, ','.join(scripts)),
              level=8)
    log.debug(_("And the following script is active for user %s: %s") %
              (address, active),
              level=8)

    mgmt_required_extensions = []

    mgmt_script = """#
# MANAGEMENT
#
"""

    user = auth.get_entry_attributes(domain, user, ['*'])

    #
    # Vacation settings (a.k.a. Out of Office)
    #
    vacation_active = None
    vacation_text = None
    vacation_uce = None
    vacation_noreact_domains = None
    vacation_react_domains = None

    vacation_active_attr = conf.get('sieve', 'vacation_active_attr')
    vacation_text_attr = conf.get('sieve', 'vacation_text_attr')
    vacation_uce_attr = conf.get('sieve', 'vacation_uce_attr')
    vacation_noreact_domains_attr = conf.get('sieve',
                                             'vacation_noreact_domains_attr')
    vacation_react_domains_attr = conf.get('sieve',
                                           'vacation_react_domains_attr')

    if not vacation_text_attr == None:

        if user.has_key(vacation_active_attr):
            vacation_active = utils.true_or_false(user[vacation_active_attr])
        else:
            vacation_active = False

        if user.has_key(vacation_text_attr):
            vacation_text = user[vacation_text_attr]
        else:
            vacation_active = False

        if user.has_key(vacation_uce_attr):
            vacation_uce = utils.true_or_false(user[vacation_uce_attr])
        else:
            vacation_uce = False

        if user.has_key(vacation_react_domains_attr):
            if isinstance(user[vacation_react_domains_attr], list):
                vacation_react_domains = user[vacation_react_domains_attr]
            else:
                vacation_react_domains = [user[vacation_react_domains_attr]]
        else:
            if user.has_key(vacation_noreact_domains_attr):
                if isinstance(user[vacation_noreact_domains_attr], list):
                    vacation_noreact_domains = user[
                        vacation_noreact_domains_attr]
                else:
                    vacation_noreact_domains = [
                        user[vacation_noreact_domains_attr]
                    ]
            else:
                vacation_noreact_domains = []

    #
    # Delivery to Folder
    #
    dtf_active_attr = conf.get('sieve', 'deliver_to_folder_active')
    if not dtf_active_attr == None:
        if user.has_key(dtf_active_attr):
            dtf_active = utils.true_or_false(user[dtf_active_attr])
        else:
            dtf_active = False
    else:
        # TODO: Not necessarily de-activated, the *Active attributes are
        # not supposed to charge this - check the deliver_to_folder_attr
        # attribute value for a value.
        dtf_active = False

    if dtf_active:
        dtf_folder_name_attr = conf.get('sieve', 'deliver_to_folder_attr')
        if not dtf_folder_name_attr == None:
            if user.has_key(dtf_folder_name_attr):
                dtf_folder = user[dtf_folder_name_attr]
            else:
                log.warning(
                    _("Delivery to folder active, but no folder name attribute available for user %r"
                      ) % (user))
                dtf_active = False
        else:
            log.error(
                _("Delivery to folder active, but no folder name attribute configured"
                  ))
            dtf_active = False

    #
    # Folder name to delivery spam to.
    #
    # Global or local.
    #
    sdf_filter = True
    sdf = conf.get('sieve', 'spam_global_folder')

    if sdf == None:
        sdf = conf.get('sieve', 'spam_personal_folder')
        if sdf == None:
            sdf_filter = False

    #
    # Mail forwarding
    #
    forward_active = None
    forward_addresses = []
    forward_keepcopy = None
    forward_uce = None

    forward_active_attr = conf.get('sieve', 'forward_address_active')
    if not forward_active_attr == None:
        if user.has_key(forward_active_attr):
            forward_active = utils.true_or_false(user[forward_active_attr])
        else:
            forward_active = False

    if not forward_active == False:
        forward_address_attr = conf.get('sieve', 'forward_address_attr')
        if user.has_key(forward_address_attr):
            if isinstance(user[forward_address_attr], basestring):
                forward_addresses = [user[forward_address_attr]]
            elif isinstance(user[forward_address_attr], str):
                forward_addresses = [user[forward_address_attr]]
            else:
                forward_addresses = user[forward_address_attr]

        if len(forward_addresses) == 0:
            forward_active = False

        forward_keepcopy_attr = conf.get('sieve', 'forward_keepcopy_active')
        if not forward_keepcopy_attr == None:
            if user.has_key(forward_keepcopy_attr):
                forward_keepcopy = utils.true_or_false(
                    user[forward_keepcopy_attr])
            else:
                forward_keepcopy = False

        forward_uce_attr = conf.get('sieve', 'forward_uce_active')
        if not forward_uce_attr == None:
            if user.has_key(forward_uce_attr):
                forward_uce = utils.true_or_false(user[forward_uce_attr])
            else:
                forward_uce = False

    if vacation_active:
        mgmt_required_extensions.append('vacation')
        mgmt_required_extensions.append('envelope')

    if dtf_active:
        mgmt_required_extensions.append('fileinto')

    if forward_active and (len(forward_addresses) > 1 or forward_keepcopy):
        mgmt_required_extensions.append('copy')

    if sdf_filter:
        mgmt_required_extensions.append('fileinto')

    import sievelib.factory

    mgmt_script = sievelib.factory.FiltersSet("MANAGEMENT")

    for required_extension in mgmt_required_extensions:
        mgmt_script.require(required_extension)

    mgmt_script.require('fileinto')

    if vacation_active:
        if not vacation_react_domains == None and len(
                vacation_react_domains) > 0:
            mgmt_script.addfilter(
                'vacation',
                [('envelope', ':domain', ":is", "from", vacation_react_domains)
                 ],
                [(
                    "vacation",
                    ":days",
                    1,
                    ":subject",
                    "Out of Office",
                    # ":handle", see http://tools.ietf.org/html/rfc5230#page-4
                    # ":mime", to indicate the reason is in fact MIME
                    vacation_text)])

        elif not vacation_noreact_domains == None and len(
                vacation_noreact_domains) > 0:
            mgmt_script.addfilter(
                'vacation',
                [('not', ('envelope', ':domain', ":is", "from",
                          vacation_noreact_domains))],
                [(
                    "vacation",
                    ":days",
                    1,
                    ":subject",
                    "Out of Office",
                    # ":handle", see http://tools.ietf.org/html/rfc5230#page-4
                    # ":mime", to indicate the reason is in fact MIME
                    vacation_text)])

        else:
            mgmt_script.addfilter(
                'vacation',
                [('true', )],
                [(
                    "vacation",
                    ":days",
                    1,
                    ":subject",
                    "Out of Office",
                    # ":handle", see http://tools.ietf.org/html/rfc5230#page-4
                    # ":mime", to indicate the reason is in fact MIME
                    vacation_text)])

    if forward_active:
        forward_rules = []

        # Principle can be demonstrated by:
        #
        # python -c "print ','.join(['a','b','c'][:-1])"
        #
        for forward_copy in forward_addresses[:-1]:
            forward_rules.append(("redirect", ":copy", forward_copy))

        if forward_keepcopy:
            # Principle can be demonstrated by:
            #
            # python -c "print ','.join(['a','b','c'][-1])"
            #
            if forward_uce:
                rule_name = 'forward-uce-keepcopy'
            else:
                rule_name = 'forward-keepcopy'

            forward_rules.append(("redirect", ":copy", forward_addresses[-1]))
        else:
            if forward_uce:
                rule_name = 'forward-uce'
            else:
                rule_name = 'forward'

            forward_rules.append(("redirect", forward_addresses[-1]))
            forward_rules.append(("stop"))

        if forward_uce:
            mgmt_script.addfilter(rule_name, ['true'], forward_rules)

        else:
            # NOTE: Messages with no X-Spam-Status header need to be matched
            # too, and this does exactly that.
            mgmt_script.addfilter(rule_name,
                                  [("not",
                                    ("X-Spam-Status", ":matches", "Yes,*"))],
                                  forward_rules)

    if sdf_filter:
        mgmt_script.addfilter('spam_delivery_folder',
                              [("X-Spam-Status", ":matches", "Yes,*")],
                              [("fileinto", "INBOX/Spam"), ("stop")])

    if dtf_active:
        mgmt_script.addfilter('delivery_to_folder', ['true'],
                              [("fileinto", dtf_folder)])

    mgmt_script = mgmt_script.__str__()

    log.debug(_("MANAGEMENT script for user %s contents: %r") %
              (address, mgmt_script),
              level=9)

    result = sieveclient.putscript("MANAGEMENT", mgmt_script)

    if not result:
        log.error(
            _("Uploading script MANAGEMENT failed for user %s") % (address))
    else:
        log.debug(_("Uploading script MANAGEMENT for user %s succeeded") %
                  (address),
                  level=8)

    user_script = """#
# User
#

require ["include"];
"""

    for script in scripts:
        if not script in ["MASTER", "MANAGEMENT", "USER"]:
            log.debug(_("Including script %s in USER (for user %s)") %
                      (script, address),
                      level=8)
            user_script = """%s

include :personal "%s";
""" % (user_script, script)

    result = sieveclient.putscript("USER", user_script)

    if not result:
        log.error(_("Uploading script USER failed for user %s") % (address))
    else:
        log.debug(_("Uploading script USER for user %s succeeded") % (address),
                  level=8)

    result = sieveclient.putscript(
        "MASTER", """#
# MASTER
#
# This file is authoritative for your system and MUST BE KEPT ACTIVE.
#
# Altering it is likely to render your account dysfunctional and may
# be violating your organizational or corporate policies.
#
# For more information on the mechanism and the conventions behind
# this script, see http://wiki.kolab.org/KEP:14
#

require ["include"];

# OPTIONAL: Includes for all or a group of users
# include :global "all-users";
# include :global "this-group-of-users";

# The script maintained by the general management system
include :personal "MANAGEMENT";

# The script(s) maintained by one or more editors available to the user
include :personal "USER";
""")

    if not result:
        log.error(_("Uploading script MASTER failed for user %s") % (address))
    else:
        log.debug(_("Uploading script MASTER for user %s succeeded") %
                  (address),
                  level=8)

    sieveclient.setactive("MASTER")
Ejemplo n.º 9
0
        log.error(_("No recipient found for email address %r") % (email_address))
        sys.exit(1)

    log.debug(_("Found the following recipient(s): %r") % (recipients), level=8)

    mail_attributes = conf.get_list(domain, 'mail_attributes')
    if mail_attributes == None or len(mail_attributes) < 1:
        mail_attributes = conf.get_list(conf.get('kolab', 'auth_mechanism'), 'mail_attributes')

    log.debug(_("Using the following mail attributes: %r") % (mail_attributes), level=8)

    if isinstance(recipients, basestring):
        recipient = recipients

        # Only a single recipient found, remove the address
        attributes = auth.get_entry_attributes(domain, recipient, mail_attributes)

        # See which attribute holds the value we're trying to remove
        for attribute in attributes.keys():
            if isinstance(attributes[attribute], list):
                if email_address in attributes[attribute]:
                    attributes[attribute].pop(attributes[attribute].index(email_address))
                    replace_attributes = {
                            attribute: attributes[attribute]
                        }

                    auth.set_entry_attributes(domain, recipient, replace_attributes)
            else:
                if email_address == attributes[attribute]:
                    auth.set_entry_attributes(domain, recipient, {attribute: None})
        pass
Ejemplo n.º 10
0
def execute(*args, **kw):

    try:
        primary_rcpt_address = conf.cli_args.pop(0)
        try:
            secondary_rcpt_address = conf.cli_args.pop(0)
        except:
            print >> sys.stderr, _("Specify the (new) alias address")
            sys.exit(1)
    except:
        print >> sys.stderr, _("Specify the existing recipient address")
        sys.exit(1)

    if len(primary_rcpt_address.split('@')) > 1:
        primary_rcpt_domain = primary_rcpt_address.split('@')[-1]
    else:
        primary_rcpt_domain = conf.get('kolab', 'primary_domain')

    auth = Auth(domain=primary_rcpt_domain)

    domains = auth.list_domains()

    #print domains

    if len(secondary_rcpt_address.split('@')) > 1:
        secondary_rcpt_domain = secondary_rcpt_address.split('@')[-1]
    else:
        secondary_rcpt_domain = conf.get('kolab', 'primary_domain')

    # Check if either is in fact a domain
    if not primary_rcpt_domain.lower() in domains.keys():
        print >> sys.stderr, _("Domain %r is not a local domain") % (primary_rcpt_domain)
        sys.exit(1)

    if not secondary_rcpt_domain.lower() in domains.keys():
        print >> sys.stderr, _("Domain %r is not a local domain") % (secondary_rcpt_domain)
        sys.exit(1)

    if not primary_rcpt_domain == secondary_rcpt_domain:
        if not domains[primary_rcpt_domain] == domains[secondary_rcpt_domain]:
            print >> sys.stderr, _("Primary and secondary domain do not have the same parent domain")
            sys.exit(1)

    primary_recipient_dn = auth.find_recipient(primary_rcpt_address)

    if primary_recipient_dn == [] or len(primary_recipient_dn) == 0:
        print >> sys.stderr, _("No such recipient %r") % (primary_rcpt_address)
        sys.exit(1)

    secondary_recipient_dn = auth.find_recipient(secondary_rcpt_address)

    if not secondary_recipient_dn == [] and not len(secondary_recipient_dn) == 0:
        print >> sys.stderr, _("Recipient for alias %r already exists") % (secondary_rcpt_address)
        sys.exit(1)

    rcpt_attrs = conf.get_list('ldap', 'mail_attributes')

    primary_rcpt_attr = rcpt_attrs[0]

    if len(rcpt_attrs) >= 2:
        secondary_rcpt_attr = rcpt_attrs[1]
    else:
        print >> sys.stderr, _("Environment is not configured for " + \
            "users to hold secondary mail attributes")

        sys.exit(1)

    primary_recipient = auth.get_entry_attributes(primary_rcpt_domain, primary_recipient_dn, rcpt_attrs)

    if not primary_recipient.has_key(primary_rcpt_attr):
        print >> sys.stderr, _("Recipient %r is not the primary recipient for address %r") % (primary_recipient, primary_rcpt_address)
        sys.exit(1)

    if not primary_recipient.has_key(secondary_rcpt_attr):
        auth.set_entry_attributes(primary_rcpt_domain, primary_recipient_dn, {secondary_rcpt_attr: [ secondary_rcpt_address ] })
    else:
        if isinstance(primary_recipient[secondary_rcpt_attr], basestring):
            new_secondary_rcpt_attrs = [
                    primary_recipient[secondary_rcpt_attr],
                    secondary_rcpt_address
                ]

        else:
            new_secondary_rcpt_attrs = \
                    primary_recipient[secondary_rcpt_attr] + \
                    [ secondary_rcpt_address ]

        auth.set_entry_attributes(
                primary_rcpt_domain,
                primary_recipient_dn,
                {
                        secondary_rcpt_attr: new_secondary_rcpt_attrs
                    }
            )
Ejemplo n.º 11
0
def execute(*args, **kw):
    try:
        address = conf.cli_args.pop(0)
    except:
        address = utils.ask_question(_("Email Address"))

    auth = Auth()
    auth.connect()

    user = auth.find_recipient(address)

    # Get the main, default backend
    backend = conf.get('kolab', 'imap_backend')

    if len(address.split('@')) > 1:
        domain = address.split('@')[1]
    else:
        domain = conf.get('kolab', 'primary_domain')

    if conf.has_section(domain) and conf.has_option(domain, 'imap_backend'):
        backend = conf.get(domain, 'imap_backend')

    if conf.has_section(domain) and conf.has_option(domain, 'imap_uri'):
        uri = conf.get(domain, 'imap_uri')
    else:
        uri = conf.get(backend, 'uri')

    hostname = None
    port = None

    result = urlparse(uri)

    if hasattr(result, 'hostname'):
        hostname = result.hostname
    else:
        scheme = uri.split(':')[0]
        (hostname, port) = uri.split('/')[2].split(':')

    port = 4190

    # Get the credentials
    admin_login = conf.get(backend, 'admin_login')
    admin_password = conf.get(backend, 'admin_password')

    import sievelib.managesieve

    sieveclient = sievelib.managesieve.Client(hostname, port, conf.debuglevel > 8)
    sieveclient.connect(None, None, True)
    sieveclient._plain_authentication(admin_login, admin_password, address)
    sieveclient.authenticated = True

    result = sieveclient.listscripts()

    if result == None:
        active = None
        scripts = []
    else:
        active, scripts = result

    log.debug(_("Found the following scripts for user %s: %s") % (address, ','.join(scripts)), level=8)
    log.debug(_("And the following script is active for user %s: %s") % (address, active), level=8)

    mgmt_required_extensions = []

    mgmt_script = """#
# MANAGEMENT
#
"""

    user = auth.get_entry_attributes(domain, user, ['*'])

    #
    # Vacation settings (a.k.a. Out of Office)
    #
    vacation_active = None
    vacation_text = None
    vacation_uce = None
    vacation_noreact_domains = None
    vacation_react_domains = None

    vacation_active_attr = conf.get('sieve', 'vacation_active_attr')
    vacation_text_attr = conf.get('sieve', 'vacation_text_attr')
    vacation_uce_attr = conf.get('sieve', 'vacation_uce_attr')
    vacation_noreact_domains_attr = conf.get('sieve', 'vacation_noreact_domains_attr')
    vacation_react_domains_attr = conf.get('sieve', 'vacation_react_domains_attr')

    if not vacation_text_attr == None:

        if user.has_key(vacation_active_attr):
            vacation_active = utils.true_or_false(user[vacation_active_attr])
        else:
            vacation_active = False

        if user.has_key(vacation_text_attr):
            vacation_text = user[vacation_text_attr]
        else:
            vacation_active = False

        if user.has_key(vacation_uce_attr):
            vacation_uce = utils.true_or_false(user[vacation_uce_attr])
        else:
            vacation_uce = False

        if user.has_key(vacation_react_domains_attr):
            if isinstance(user[vacation_react_domains_attr], list):
                vacation_react_domains = user[vacation_react_domains_attr]
            else:
                vacation_react_domains = [ user[vacation_react_domains_attr] ]
        else:
            if user.has_key(vacation_noreact_domains_attr):
                if isinstance(user[vacation_noreact_domains_attr], list):
                    vacation_noreact_domains = user[vacation_noreact_domains_attr]
                else:
                    vacation_noreact_domains = [ user[vacation_noreact_domains_attr] ]
            else:
                vacation_noreact_domains = []

    #
    # Delivery to Folder
    #
    dtf_active_attr = conf.get('sieve', 'deliver_to_folder_active')
    if not dtf_active_attr == None:
        if user.has_key(dtf_active_attr):
            dtf_active = utils.true_or_false(user[dtf_active_attr])
        else:
            dtf_active = False
    else:
        # TODO: Not necessarily de-activated, the *Active attributes are
        # not supposed to charge this - check the deliver_to_folder_attr
        # attribute value for a value.
        dtf_active = False

    if dtf_active:
        dtf_folder_name_attr = conf.get('sieve', 'deliver_to_folder_attr')
        if not dtf_folder_name_attr == None:
            if user.has_key(dtf_folder_name_attr):
                dtf_folder = user[dtf_folder_name_attr]
            else:
                log.warning(_("Delivery to folder active, but no folder name attribute available for user %r") % (user))
                dtf_active = False
        else:
            log.error(_("Delivery to folder active, but no folder name attribute configured"))
            dtf_active = False

    #
    # Folder name to delivery spam to.
    #
    # Global or local.
    #
    sdf_filter = True
    sdf = conf.get('sieve', 'spam_global_folder')

    if sdf == None:
        sdf = conf.get('sieve', 'spam_personal_folder')
        if sdf == None:
            sdf_filter = False

    #
    # Mail forwarding
    #
    forward_active = None
    forward_addresses = []
    forward_keepcopy = None
    forward_uce = None

    forward_active_attr = conf.get('sieve', 'forward_address_active')
    if not forward_active_attr == None:
        if user.has_key(forward_active_attr):
            forward_active = utils.true_or_false(user[forward_active_attr])
        else:
            forward_active = False

    if not forward_active == False:
        forward_address_attr = conf.get('sieve', 'forward_address_attr')
        if user.has_key(forward_address_attr):
            if isinstance(user[forward_address_attr], basestring):
                forward_addresses = [ user[forward_address_attr] ]
            elif isinstance(user[forward_address_attr], str):
                forward_addresses = [ user[forward_address_attr] ]
            else:
                forward_addresses = user[forward_address_attr]

        if len(forward_addresses) == 0:
            forward_active = False

        forward_keepcopy_attr = conf.get('sieve', 'forward_keepcopy_active')
        if not forward_keepcopy_attr == None:
            if user.has_key(forward_keepcopy_attr):
                forward_keepcopy = utils.true_or_false(user[forward_keepcopy_attr])
            else:
                forward_keepcopy = False

        forward_uce_attr = conf.get('sieve', 'forward_uce_active')
        if not forward_uce_attr == None:
            if user.has_key(forward_uce_attr):
                forward_uce = utils.true_or_false(user[forward_uce_attr])
            else:
                forward_uce = False

    if vacation_active:
        mgmt_required_extensions.append('vacation')
        mgmt_required_extensions.append('envelope')

    if dtf_active:
        mgmt_required_extensions.append('fileinto')

    if forward_active and (len(forward_addresses) > 1 or forward_keepcopy):
        mgmt_required_extensions.append('copy')

    if sdf_filter:
        mgmt_required_extensions.append('fileinto')

    import sievelib.factory

    mgmt_script = sievelib.factory.FiltersSet("MANAGEMENT")

    for required_extension in mgmt_required_extensions:
        mgmt_script.require(required_extension)

    mgmt_script.require('fileinto')

    if vacation_active:
        if not vacation_react_domains == None and len(vacation_react_domains) > 0:
            mgmt_script.addfilter(
                    'vacation',
                    [('envelope', ':domain', ":is", "from", vacation_react_domains)],
                    [
                            (
                                    "vacation",
                                    ":days", 1,
                                    ":subject",
                                    "Out of Office",
                                    # ":handle", see http://tools.ietf.org/html/rfc5230#page-4
                                    # ":mime", to indicate the reason is in fact MIME
                                    vacation_text
                                )
                        ]
                )

        elif not vacation_noreact_domains == None and len(vacation_noreact_domains) > 0:
            mgmt_script.addfilter(
                    'vacation',
                    [('not', ('envelope', ':domain', ":is", "from", vacation_noreact_domains))],
                    [
                            (
                                    "vacation",
                                    ":days", 1,
                                    ":subject",
                                    "Out of Office",
                                    # ":handle", see http://tools.ietf.org/html/rfc5230#page-4
                                    # ":mime", to indicate the reason is in fact MIME
                                    vacation_text
                                )
                        ]
                )

        else:
            mgmt_script.addfilter(
                    'vacation',
                    [('true',)],
                    [
                            (
                                    "vacation",
                                    ":days", 1,
                                    ":subject",
                                    "Out of Office",
                                    # ":handle", see http://tools.ietf.org/html/rfc5230#page-4
                                    # ":mime", to indicate the reason is in fact MIME
                                    vacation_text
                                )
                        ]
                )

    if forward_active:
        forward_rules = []

        # Principle can be demonstrated by:
        #
        # python -c "print ','.join(['a','b','c'][:-1])"
        #
        for forward_copy in forward_addresses[:-1]:
            forward_rules.append(("redirect", ":copy", forward_copy))

        if forward_keepcopy:
            # Principle can be demonstrated by:
            #
            # python -c "print ','.join(['a','b','c'][-1])"
            #
            if forward_uce:
                rule_name = 'forward-uce-keepcopy'
            else:
                rule_name = 'forward-keepcopy'

            forward_rules.append(("redirect", ":copy", forward_addresses[-1]))
        else:
            if forward_uce:
                rule_name = 'forward-uce'
            else:
                rule_name = 'forward'

            forward_rules.append(("redirect", forward_addresses[-1]))
            forward_rules.append(("stop"))

        if forward_uce:
            mgmt_script.addfilter(rule_name, ['true'], forward_rules)

        else:
            # NOTE: Messages with no X-Spam-Status header need to be matched
            # too, and this does exactly that.
            mgmt_script.addfilter(rule_name, [("not", ("X-Spam-Status", ":matches", "Yes,*"))], forward_rules)

    if sdf_filter:
        mgmt_script.addfilter('spam_delivery_folder', [("X-Spam-Status", ":matches", "Yes,*")], [("fileinto", "INBOX/Spam"), ("stop")])

    if dtf_active:
        mgmt_script.addfilter('delivery_to_folder', ['true'], [("fileinto", dtf_folder)])

    mgmt_script = mgmt_script.__str__()

    log.debug(_("MANAGEMENT script for user %s contents: %r") % (address,mgmt_script), level=9)

    result = sieveclient.putscript("MANAGEMENT", mgmt_script)

    if not result:
        log.error(_("Uploading script MANAGEMENT failed for user %s") % (address))
    else:
        log.debug(_("Uploading script MANAGEMENT for user %s succeeded") % (address), level=8)

    user_script = """#
# User
#

require ["include"];
"""

    for script in scripts:
        if not script in [ "MASTER", "MANAGEMENT", "USER" ]:
            log.debug(_("Including script %s in USER (for user %s)") % (script,address) ,level=8)
            user_script = """%s

include :personal "%s";
""" % (user_script, script)

    result = sieveclient.putscript("USER", user_script)

    if not result:
        log.error(_("Uploading script USER failed for user %s") % (address))
    else:
        log.debug(_("Uploading script USER for user %s succeeded") % (address), level=8)

    result = sieveclient.putscript("MASTER", """#
# MASTER
#
# This file is authoritative for your system and MUST BE KEPT ACTIVE.
#
# Altering it is likely to render your account dysfunctional and may
# be violating your organizational or corporate policies.
#
# For more information on the mechanism and the conventions behind
# this script, see http://wiki.kolab.org/KEP:14
#

require ["include"];

# OPTIONAL: Includes for all or a group of users
# include :global "all-users";
# include :global "this-group-of-users";

# The script maintained by the general management system
include :personal "MANAGEMENT";

# The script(s) maintained by one or more editors available to the user
include :personal "USER";
""")

    if not result:
        log.error(_("Uploading script MASTER failed for user %s") % (address))
    else:
        log.debug(_("Uploading script MASTER for user %s succeeded") % (address), level=8)

    sieveclient.setactive("MASTER")