Beispiel #1
0
    def email_rt_delete(self, operator, queuename):
        """ Delete RT list. """
        queue, host = self._resolve_rt_name(queuename)
        rt_dom = self._get_email_domain_from_str(host)
        self.ba.can_rt_delete(operator.get_entity_id(), domain=rt_dom)
        et = Email.EmailTarget(self.db)
        ea = Email.EmailAddress(self.db)
        epat = Email.EmailPrimaryAddressTarget(self.db)
        result = []

        for target_id in self.__get_all_related_rt_targets(queuename):
            try:
                et.clear()
                et.find(target_id)
            except Errors.NotFoundError:
                continue

            epat.clear()
            try:
                epat.find(et.entity_id)
            except Errors.NotFoundError:
                pass
            else:
                epat.delete()
            for r in et.get_addresses():
                addr = '%(local_part)s@%(domain)s' % r
                ea.clear()
                ea.find_by_address(addr)
                ea.delete()
                result.append({'address': addr})
            et.delete()

        return result
Beispiel #2
0
    def _get_person_owner_attributes(self, owner):
        """Return a dict with person tidbits for LDAP.

        FIXME: THIS MUST NOT FAIL with NotFoundError.
        """
        def extract_email_from_account(account_id):
            try:
                et = Email.EmailTarget(self._db)
                et.find_by_target_entity(account_id)
                return [
                    "%s@%s" % (r['local_part'], r['domain'])
                    for r in et.get_addresses(special=False)
                ]
            except Errors.NotFoundError:
                return []

        # end extract_email_from_account

        result = dict()
        # uid
        try:
            account_id = owner.get_primary_account()
            acc = Factory.get("Account")(self._db)
            acc.find(account_id)
            result["uid"] = acc.account_name
        except Errors.NotFoundError:
            result["uid"] = None

        # ALL unames must go into 'voipSipUri'. And so must all e-mail
        # addresses.
        result["voipSipUri"] = list()
        for row in acc.search(owner_id=owner.entity_id):
            result["voipSipUri"].append(self._voipify(row["name"], None))
            for address in extract_email_from_account(row["account_id"]):
                mangled = self._voipify(address, None)
                result["voipSipUri"].append(mangled)

        # mail - primary e-mail address.
        if result['uid']:
            try:
                et = Email.EmailTarget(self._db)
                et.find_by_target_entity(acc.entity_id)
                epat = Email.EmailPrimaryAddressTarget(self._db)
                epat.find(et.entity_id)
                ea = Email.EmailAddress(self._db)
                ea.find(epat.get_address_id())
                result["mail"] = ea.get_address()
            except Errors.NotFoundError:
                result["mail"] = None

        # cn - grab system cached
        try:
            p_name = owner.get_name(
                self.const.system_cached,
                getattr(self.const, cereconf.DEFAULT_GECOS_NAME))
        except Errors.NotFoundError:
            p_name = None

        result["cn"] = p_name
        return result
 def _create_distgroup_mailaddr(self, et):
     ea = Email.EmailAddress(self._db)
     # move this to a variable
     # no need to wash the address, group will not be created
     # if the name is not valid for Exchange
     lp = "%s%s" % (cereconf.DISTGROUP_PRIMARY_ADDR_PREFIX, self.group_name)
     dom = Email.EmailDomain(self._db)
     dom.find_by_domain(cereconf.DISTGROUP_DEFAULT_DOMAIN)
     addr_str = lp + '@' + cereconf.DISTGROUP_DEFAULT_DOMAIN
     try:
         ea.find_by_local_part_and_domain(lp, dom.entity_id)
         if ea.email_addr_target_id != et.entity_id:
             raise self._db.IntegrityError("Address %s is already taken!" %
                                           addr_str)
     except Errors.NotFoundError:
         ea.populate(lp, dom.entity_id, et.entity_id, expire=None)
     ea.write_db()
     epat = Email.EmailPrimaryAddressTarget(self._db)
     try:
         epat.find(ea.email_addr_target_id)
         epat.populate(ea.entity_id)
     except Errors.NotFoundError:
         epat.clear()
         epat.populate(ea.entity_id, parent=et)
     epat.write_db()
Beispiel #4
0
    def delete(self):
        """Delete the group, along with its EmailTarget."""
        et = Email.EmailTarget(self._db)
        ea = Email.EmailAddress(self._db)
        epat = Email.EmailPrimaryAddressTarget(self._db)

        # If there is not associated an EmailTarget with the group, call delete
        # from the superclass.
        try:
            et.find_by_target_entity(self.entity_id)
        except Errors.NotFoundError:
            return super(GroupUiOMixin, self).delete()

        # An EmailTarget exists, so we try to delete its primary address.
        try:
            epat.find(et.entity_id)
            epat.delete()
        except Errors.NotFoundError:
            pass

        # We remove all the EmailTargets addresses.
        try:
            for row in et.get_addresses():
                ea.clear()
                ea.find(row['address_id'])
                ea.delete()
        except Errors.NotFoundError:
            pass

        # Delete the EmailTarget
        et.delete()

        # Finally! Delete the group!
        super(GroupUiOMixin, self).delete()
    def email_move_domain_addresses(self, operator, source_uname, dest_uname,
                                    domain_str, move_primary):
        """Move an account's e-mail addresses to another account

        :param domain_str: email domain to be affected
        :param move_primary: move primary email address
        """
        if not self.ba.is_superuser(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to superusers")

        move_primary = self._get_boolean(move_primary)
        source_account = self._get_account(source_uname)
        source_et = self._get_email_target_for_account(source_account)
        dest_account = self._get_account(dest_uname)
        dest_et = self._get_email_target_for_account(dest_account)
        epat = Email.EmailPrimaryAddressTarget(self.db)

        try:
            epat.find(source_et.entity_id)
        except Errors.NotFoundError:
            epat.clear()

        reassigned_addresses = []
        for address in source_et.get_addresses():
            if address['domain'] == domain_str:

                if address['address_id'] == epat.email_primaddr_id:
                    if move_primary:
                        self._move_primary_email_address(address,
                                                         reassigned_addresses,
                                                         dest_et,
                                                         epat)
                else:
                    self._move_email_address(address, reassigned_addresses,
                                             dest_et)
        # Managing ad_email
        ad_emails_added = ""
        if domain_str == cereconf.NO_MAILBOX_DOMAIN_EMPLOYEES:
            ad = ad_email.AdEmail(self.db)

            if move_primary:
                ad_emails = ad.search_ad_email(account_name=source_uname)
                if len(ad_emails) == 1:
                    ad_emails_added = self._move_ad_email(ad_emails[0],
                                                          dest_uname)
            # TODO:
            #  If this command is called with move_primary=False,
            #  the source account's primary email address will be left
            #  intact, but it's corresponding ad_email will be deleted.
            #  This mimics the functionality of the uit-script move_emails.py,
            #  but is it really what we want?
            ad.delete_ad_email(account_name=source_uname)

        return ("OK, reassigned {}. ".format(reassigned_addresses)
                + ad_emails_added)
Beispiel #6
0
    def test_phonenumberchange_delay(self):
        """Fresh phone numbers should not be used if config says so."""
        cereconf.INDIVIDUATION_PHONE_TYPES['system_fs']['types']['contact_mobile_phone']['delay'] = 7
        cereconf.INDIVIDUATION_PHONE_TYPES['system_fs']['types']['contact_private_mobile']['delay'] = 7

        ou = Factory.get('OU')(self.db)
        co = Factory.get('Constants')(self.db)
        ou.find_stedkode(0, 0, 0, 0)
        pe = self.createPerson(first_name='Miss', last_name='Test', studnr='007')
        ac = self.createAccount(pe, 'mstest')

        pe.populate_affiliation(source_system=co.system_fs, ou_id=ou.entity_id,
                affiliation=co.affiliation_student,
                status=co.affiliation_status_student_aktiv)
        pe.write_db()
        pe.populate_contact_info(source_system=co.system_fs,
                                 type=co.contact_mobile_phone,
                                 value='98012345')
        pe.write_db()

        # Create e-mail address for user
        ed = Email.EmailDomain(self.db)
        ed.populate('example.com', 'For testing')
        ed.write_db()
        et = Email.EmailTarget(self.db)
        et.populate(co.email_target_account, ac.entity_id, co.entity_account)
        et.write_db()
        ea = Email.EmailAddress(self.db)
        ea.populate('test', ed.entity_id, et.entity_id)
        ea.write_db()
        epa = Email.EmailPrimaryAddressTarget(self.db)
        epa.populate(ea.entity_id, et)
        epa.write_db()

        self.db.commit()

        # hack the create_date in change_log
        self.db.execute("""
            UPDATE [:table schema=cerebrum name=change_log]
            SET tstamp = :tid 
            WHERE 
                subject_entity = :s_id AND
                change_type_id = :ct_id""", 
                    {'s_id': pe.entity_id,
                    'ct_id': co.person_create,
                    'tid': DateTime(2009, 10, 4),})
        self.db.commit()

        d = self.client.callRemote('generate_token',
                id_type="externalid_studentnr", ext_id='007',
                username='******', phone_no='98012345',
                browser_token='a')
        d = self.assertFailure(d, error.Error)
        # TODO: test that sendmail is called with the correct to-address
        return d
 def _get_email_target_and_address(self, address):
     # Support DistributionGroup email target lookup
     try:
         return super(EmailCommands,
                      self)._get_email_target_and_address(address)
     except CerebrumError as e:
         # Not found, maybe distribution group?
         try:
             dlgroup = Utils.Factory.get("DistributionGroup")(self.db)
             dlgroup.find_by_name(address)
             et = Email.EmailTarget(self.db)
             et.find_by_target_entity(dlgroup.entity_id)
             epa = Email.EmailPrimaryAddressTarget(self.db)
             epa.find(et.entity_id)
             ea = Email.EmailAddress(self.db)
             ea.find(epa.email_primaddr_id)
             return et, ea
         except Errors.NotFoundError:
             raise e
Beispiel #8
0
def delete_email_address(address):
    et, ea = get_email_target_and_address(address)
    if et is None:
        logger.debug("Would delete <%s>", address)
        return
    logger.info("Deleting <%s>", address)

    # We can't delete this EA, if there is an epat attached to it.
    # Before we can drop ea, remove epat (or we'll get constraint violation
    # from the db)
    try:
        epat = Email.EmailPrimaryAddressTarget(db)
        epat.find(ea.email_addr_target_id)
        epat.delete()
        logger.debug("Deleted *primary* address <%s>", address)
    except Errors.NotFoundError:
        pass

    ea.delete()
    for r in et.get_addresses():
        logger.info("There are addresses left")
        return
    logger.debug("Deleting target as well")
    et.delete()
Beispiel #9
0
    def update_email_addresses(self):
        # Find, create or update a proper EmailTarget for this
        # account.
        et = Email.EmailTarget(self._db)
        old_server = None
        target_type = self.const.email_target_account
        if self.is_deleted() or self.is_reserved():
            target_type = self.const.email_target_deleted
        try:
            et.find_by_email_target_attrs(target_entity_id = self.entity_id)
            et.email_target_type = target_type
        except Errors.NotFoundError:
            # We don't want to create e-mail targets for reserved or
            # deleted accounts, but we do convert the type of existing
            # e-mail targets above.
            if target_type == self.const.email_target_deleted:
                return
            et.populate(target_type, self.entity_id, self.const.entity_account)
        et.write_db()
        # For deleted/reserved users, set expire_date for all of the
        # user's addresses, and don't allocate any new addresses.
        ea = Email.EmailAddress(self._db)
        if target_type == self.const.email_target_deleted:
            expire_date = self._db.DateFromTicks(time.time() +
                                                 60 * 60 * 24 * 180)
            for row in et.get_addresses():
                ea.clear()
                ea.find(row['address_id'])
                if ea.email_addr_expire_date is None:
                    ea.email_addr_expire_date = expire_date
                ea.write_db()
            return
        # if an account without email_server_target is found assign
        # the appropriate server
        old_server = et.email_server_id
        acc_types = self.get_account_types()
        entity = Factory.get("Entity")(self._db)
        try:
            entity.clear()
            entity.find(self.owner_id)
        except Errors.NotFoundError:
            pass

        if not old_server:
            # we should update servers for employees as well, but we
            # cannot do that for now as there are no clear criteria
            # for when we should consider someone av f*g-employee or
            # adm-employee. we will therefore update servers for students
            # only
            # if self.is_fag_employee():
            #    self._update_email_server('mail.f*g.hiof.no')
            # elif self.is_adm_employee():
            #    self._update_email_server('mail.adm.hiof.no')
            if entity.entity_type != self.const.entity_group:
                if self.is_student():
                    self._update_email_server('epost.hiof.no')
                else:
                    # do not set email_server_target until account_type is registered
                    return
        # Figure out which domain(s) the user should have addresses
        # in.  Primary domain should be at the front of the resulting
        # list.
        # if the only address found is in EMAIL_DEFAULT_DOMAIN
        # don't set default address. This is done in order to prevent
        # adresses in default domain being sat as primary
        # TODO: account_types affiliated to OU's  without connected
        # email domain don't get a default address
        primary_set = False
        ed = Email.EmailDomain(self._db)
        ed.find(self.get_primary_maildomain())
        domains = [ed.email_domain_name]
        if cereconf.EMAIL_DEFAULT_DOMAIN not in domains:
            domains.append(cereconf.EMAIL_DEFAULT_DOMAIN)
        # Iterate over the available domains, testing various
        # local_parts for availability.  Set user's primary address to
        # the first one found to be available.
        try:
            self.get_primary_mailaddress()
        except Errors.NotFoundError:
            pass
        epat = Email.EmailPrimaryAddressTarget(self._db)
        for domain in domains:
            if ed.email_domain_name != domain:
                ed.clear()
                ed.find_by_domain(domain)
            # Check for 'cnaddr' category before 'uidaddr', to prefer
            # 'cnaddr'-style primary addresses for users in
            # maildomains that have both categories.
            ctgs = [int(r['category']) for r in ed.get_categories()]
            local_parts = []
            if int(self.const.email_domain_category_cnaddr) in ctgs:
                local_parts.append(self.get_email_cn_local_part(given_names=1, max_initials=1))
                local_parts.append(self.account_name)
            elif int(self.const.email_domain_category_uidaddr) in ctgs:
                local_parts.append(self.account_name)
            for lp in local_parts:
                lp = self.wash_email_local_part(lp)
                # Is the address taken?
                ea.clear()
                try:
                   ea.find_by_local_part_and_domain(lp, ed.entity_id)
                   if ea.email_addr_target_id != et.entity_id:
                       # Address already exists, and points to a
                       # target not owned by this Account.
                       continue
                   # Address belongs to this account; make sure
                   # there's no expire_date set on it.
                   ea.email_addr_expire_date = None
                except Errors.NotFoundError:
                    # Address doesn't exist; create it.
                    ea.populate(lp, ed.entity_id, et.entity_id, expire=None)
                ea.write_db()
                # HiØ do not want the primary adress to change automatically
                if not primary_set:
                    epat.clear()
                    try:
                        epat.find(ea.email_addr_target_id)
                    except Errors.NotFoundError:
                        epat.clear()
                        epat.populate(ea.entity_id, parent=et)
                        epat.write_db()
                    primary_set = True
Beispiel #10
0
    def _register_sympa_list_addresses(self, listname, local_part, domain,
                                       delivery_host):
        """ Register all neccessary sympa addresses.

        Add list, request, editor, owner, subscribe and unsubscribe addresses
        to a sympa mailing list.

        @type listname: basestring
        @param listname:
            Sympa listname that the operation is about. listname is typically
            different from local_part@domain when we are creating an alias.
            local_part@domain is the alias, listname is the original listname.
            And since aliases should point to the 'original' ETs, we have to
            use listname to locate the ETs.

        @type local_part: basestring
        @param local_part: See domain

        @type domain: basestring
        @param domain:
            L{local_part} and domain together represent a new list address that
            we want to create.

        @type delivery_host: EmailServer instance.
        @param delivery_host:
            EmailServer where e-mail to L{listname} is to be delivered through.

        """

        if (delivery_host.email_server_type !=
                self.const.email_server_type_sympa):
            raise CerebrumError(
                "Delivery host %s has wrong type (%s) for sympa list %s" %
                (delivery_host.get_name(self.const.host_namespace),
                 self.const.EmailServerType(
                     delivery_host.email_server_type), listname))

        ed = Email.EmailDomain(self.db)
        ed.find_by_domain(domain)

        et = Email.EmailTarget(self.db)
        ea = Email.EmailAddress(self.db)
        epat = Email.EmailPrimaryAddressTarget(self.db)
        try:
            ea.find_by_local_part_and_domain(local_part, ed.entity_id)
        except Errors.NotFoundError:
            pass
        else:
            raise CerebrumError("The address %s@%s is already in use" %
                                (local_part, domain))

        sympa = self._get_account('sympa', idtype='name', actype='PosixUser')
        primary_ea_created = False
        listname_lp, listname_domain = listname.split("@")

        # For each of the addresses we are supposed to create...
        for pattern, pipe_destination in self._sympa_addr2alias:
            address = pattern % locals()
            address_lp, address_domain = address.split("@")

            # pipe has to be derived from the original listname, since it's
            # used to locate the ET.
            pipe = pipe_destination % {
                'local_part': listname_lp,
                'domain': listname_domain,
                'listname': listname
            }

            # First check whether the address already exist. It should not.
            try:
                ea.clear()
                ea.find_by_local_part_and_domain(address_lp, ed.entity_id)
                raise CerebrumError("Can't add list %s as the address %s "
                                    "is already in use" % (listname, address))
            except Errors.NotFoundError:
                pass

            # Then find the target for this particular email address. The
            # target may already exist, though.
            et.clear()
            try:
                et.find_by_alias_and_account(pipe, sympa.entity_id)
            except Errors.NotFoundError:
                et.populate(self.const.email_target_Sympa,
                            alias=pipe,
                            using_uid=sympa.entity_id,
                            server_id=delivery_host.entity_id)
                et.write_db()

            # Then create the email address and associate it with the ET.
            ea.clear()
            ea.populate(address_lp, ed.entity_id, et.entity_id)
            ea.write_db()

            # And finally, the primary address. The first entry in
            # _sympa_addr2alias will match. Do not reshuffle that tuple!
            if not primary_ea_created:
                epat.clear()
                try:
                    epat.find(et.entity_id)
                except Errors.NotFoundError:
                    epat.clear()
                    epat.populate(ea.entity_id, parent=et)
                    epat.write_db()
                primary_ea_created = True
Beispiel #11
0
    def sympa_remove_list(self, operator, run_host, listname, force_yes_no):
        """ Remove a sympa list from cerebrum.

        @type force_request: bool
        @param force_request:
          Controls whether a bofhd request should be issued. This may come in
          handy, if we want to delete a sympa list from Cerebrum only and not
          issue any requests. misc cancel_request would have worked too, but
          it's better to merge this into one command.

        """
        force_request = self._is_yes(force_yes_no)

        # Check that the command exec host is sane
        if run_host not in cereconf.SYMPA_RUN_HOSTS:
            raise CerebrumError("run-host '%s' for list '%s' is not valid" %
                                (run_host, listname))

        et, ea = self._get_email_target_and_address(listname)
        self.ba.can_email_list_delete(operator.get_entity_id(), ea)

        if et.email_target_type != self.const.email_target_Sympa:
            raise CerebrumError(
                "'%s' is not a sympa list (type: %s)" %
                (listname, self.const.EmailTarget(et.email_target_type)))

        epat = Email.EmailPrimaryAddressTarget(self.db)
        list_id = ea.entity_id
        # Now, there are *many* ETs/EAs associated with one sympa list. We
        # have to wipe them all out.
        if not self._validate_sympa_list(listname):
            raise CerebrumError("Illegal sympa list name: '%s'", listname)

        deleted_EA = self.email_info(operator, listname)
        # needed for pattern interpolation below (these are actually used)
        local_part, domain = self._split_email_address(listname)
        for pattern, pipe_destination in self._sympa_addr2alias:
            address = pattern % locals()
            # For each address, find the target, and remove all email
            # addresses for that target (there may be many addresses for the
            # same target).
            try:
                ea.clear()
                ea.find_by_address(address)
                et.clear()
                et.find(ea.get_target_id())
                epat.clear()
                try:
                    epat.find(et.entity_id)
                except Errors.NotFoundError:
                    pass
                else:
                    epat.delete()
                # Wipe all addresses...
                for row in et.get_addresses():
                    addr = '%(local_part)s@%(domain)s' % row
                    ea.clear()
                    ea.find_by_address(addr)
                    ea.delete()
                et.delete()
            except Errors.NotFoundError:
                pass

        if cereconf.INSTITUTION_DOMAIN_NAME == 'uio.no':
            self._report_deleted_EA(deleted_EA)
        if not force_request:
            return {'listname': listname, 'request': False}

        br = BofhdRequests(self.db, self.const)
        state = {'run_host': run_host, 'listname': listname}
        br.add_request(
            operator.get_entity_id(),
            # IVR 2008-08-04 +1 hour to allow changes to spread to
            # LDAP. This way we'll have a nice SMTP-error, rather
            # than a confusing error burp from sympa.
            DateTime.now() + DateTime.DateTimeDelta(0, 1),
            self.const.bofh_sympa_remove,
            list_id,
            None,
            state_data=pickle.dumps(state))

        return {'listname': listname, 'request': True}
Beispiel #12
0
    def email_rt_create(self, operator, queuename, addr, force="No"):
        """ Create rt queue. """

        queue, host = self._resolve_rt_name(queuename)
        rt_dom = self._get_email_domain_from_str(host)
        op = operator.get_entity_id()
        self.ba.can_rt_create(op, domain=rt_dom)
        try:
            self._get_rt_email_target(queue, host)
        except CerebrumError:
            pass
        else:
            raise CerebrumError("RT queue %s already exists" % queuename)
        addr_lp, addr_domain_name = self._split_email_address(addr)
        addr_dom = self._get_email_domain_from_str(addr_domain_name)
        if addr_domain_name != host:
            self.ba.can_email_address_add(operator.get_entity_id(),
                                          domain=addr_dom)
        replaced_lists = []

        # Unusual characters will raise an exception, a too short name
        # will return False, which we ignore for the queue name.
        self._is_ok_mailing_list_name(queue)

        # The submission address is only allowed to be short if it is
        # equal to the queue name, or the operator is a global
        # postmaster.
        if not (self._is_ok_mailing_list_name(addr_lp)
                or addr == queue + "@" + host or self.ba.is_postmaster(op)):
            raise CerebrumError("Illegal address for submission: %s" % addr)

        # Check if list exists and is replaceable
        try:
            et, ea = self._get_email_target_and_address(addr)
        except CerebrumError:
            pass
        else:
            raise CerebrumError("Address <{}> is in use".format(addr))

        acc = self._get_account("exim")
        et = Email.EmailTarget(self.db)
        ea = Email.EmailAddress(self.db)
        cmd = self._rt_pipe % {
            'action': "correspond",
            'queue': queue,
            'host': host
        }
        et.populate(self.const.email_target_RT,
                    alias=cmd,
                    using_uid=acc.entity_id)
        et.write_db()

        # Add primary address
        ea.populate(addr_lp, addr_dom.entity_id, et.entity_id)
        ea.write_db()
        epat = Email.EmailPrimaryAddressTarget(self.db)
        epat.populate(ea.entity_id, parent=et)
        epat.write_db()
        for alias in replaced_lists:
            if alias == addr:
                continue
            lp, dom = self._split_email_address(alias)
            alias_dom = self._get_email_domain_from_str(dom)
            ea.clear()
            ea.populate(lp, alias_dom.entity_id, et.entity_id)
            ea.write_db()

        # Add RT internal address
        if addr_lp != queue or addr_domain_name != host:
            ea.clear()
            ea.populate(queue, rt_dom.entity_id, et.entity_id)
            ea.write_db()

        # Moving on to the comment address
        et.clear()
        cmd = self._rt_pipe % {
            'queue': queue,
            'action': "comment",
            'host': host
        }
        et.populate(self.const.email_target_RT,
                    alias=cmd,
                    using_uid=acc.entity_id)
        et.write_db()
        ea.clear()
        ea.populate("%s-comment" % queue, rt_dom.entity_id, et.entity_id)
        ea.write_db()
        msg = "RT queue %s on %s added" % (queue, host)
        if replaced_lists:
            msg += ", replacing mailing list(s) %s" % ", ".join(replaced_lists)
        addr = queue + "@" + host
        self._register_spam_settings(addr, self.const.email_target_RT)
        self._register_filter_settings(addr, self.const.email_target_RT)
        return msg
Beispiel #13
0
    def _get_sympa_list(self, listname):
        """ Try to return the 'official' sympa mailing list name, if it can at
        all be derived from listname.

        The problem here is that some lists are actually called
        foo-admin@domain (and their admin address is foo-admin-admin@domain).

        Since the 'official' names are not tagged in any way, we try to
        guess. The guesswork proceeds as follows:

        1) if listname points to a sympa ET that has a primary address, we are
           done, listname *IS* the official list name
        2) if not, then there must be a prefix/suffix (like -request) and if
           we chop it off, we can checked the chopped off part for being an
           official sympa list. The chopping off continues until we run out of
           special prefixes/suffixes.
        """

        ea = Email.EmailAddress(self.db)
        et = Email.EmailTarget(self.db)
        epat = Email.EmailPrimaryAddressTarget(self.db)

        def has_prefix(address):
            local_part, domain = self._split_email_address(address)
            return True in [
                local_part.startswith(x) for x in self._sympa_address_prefixes
            ]

        def has_suffix(address):
            local_part, domain = self._split_email_address(address)
            return True in [
                local_part.endswith(x) for x in self._sympa_address_suffixes
            ]

        def has_primary_to_me(address):
            try:
                ea.clear()
                ea.find_by_address(address)
                epat.clear()
                epat.find(ea.get_target_id())
                return True
            except Errors.NotFoundError:
                return False

        def I_am_sympa(address, check_suffix_prefix=True):
            try:
                ea.clear()
                ea.find_by_address(address)
            except Errors.NotFoundError:
                # If it does not exist, it cannot be sympa
                return False

            et.clear()
            et.find(ea.get_target_id())
            if ((not et.email_target_alias)
                    or et.email_target_type != self.const.email_target_Sympa):
                # if it's not a Sympa ET, address cannot be sympa
                return False
            return True

        not_sympa_error = CerebrumError("%s is not a Sympa list" % listname)
        # Simplest case -- listname is actually a sympa ML directly. It does
        # not matter whether it has a funky prefix/suffix.
        if I_am_sympa(listname) and has_primary_to_me(listname):
            return listname

        # However, if listname does not have a prefix/suffix AND it is not a
        # sympa address with a primary address, them it CANNOT be a sympa
        # address.
        if not (has_prefix(listname) or has_suffix(listname)):
            raise not_sympa_error

        # There is a funky suffix/prefix. Is listname actually such a
        # secondary address? Try to chop off the funky part and test.
        local_part, domain = self._split_email_address(listname)
        for prefix in self._sympa_address_prefixes:
            if not local_part.startswith(prefix):
                continue

            lp_tmp = local_part[len(prefix):]
            addr_to_test = lp_tmp + "@" + domain
            try:
                self._get_sympa_list(addr_to_test)
                return addr_to_test
            except CerebrumError:
                pass

        for suffix in self._sympa_address_suffixes:
            if not local_part.endswith(suffix):
                continue

            lp_tmp = local_part[:-len(suffix)]
            addr_to_test = lp_tmp + "@" + domain
            try:
                self._get_sympa_list(addr_to_test)
                return addr_to_test
            except CerebrumError:
                pass

        raise not_sympa_error
Beispiel #14
0
 def update_email_addresses(self, set_primary=False):
     # check if an e-mail spread is registered yet, if not don't
     # update
     email_spreads = (self.const.spread_exchange_account,
                      self.const.spread_exchange_acc_old,
                      self.const.spread_hia_email,
                      self.const.spread_uia_office_365,
                      self.const.spread_uia_forward)
     if not any([self.has_spread(spread) for spread in email_spreads]):
         # CRB-742: If spread_uia_office_365 is removed
         #  MailTarget targettype should be set as "deleted"
         try:
             et = Email.EmailTarget(self._db)
             et.find_by_email_target_attrs(target_entity_id=self.entity_id)
             if et.email_target_type != self.const.email_target_deleted:
                 et.email_target_type = self.const.email_target_deleted
                 et.write_db()
         except Errors.NotFoundError:
             pass
         return
     # Find, create or update a proper EmailTarget for this
     # account.
     et = Email.EmailTarget(self._db)
     target_type = self.const.email_target_account
     if self.has_spread(self.const.spread_uia_forward):
         target_type = self.const.email_target_forward
     if self.is_deleted() or self.is_reserved():
         target_type = self.const.email_target_deleted
     try:
         et.find_by_email_target_attrs(target_entity_id=self.entity_id)
         et.email_target_type = target_type
     except Errors.NotFoundError:
         # We don't want to create e-mail targets for reserved or
         # deleted accounts, but we do convert the type of existing
         # e-mail targets above.
         if target_type == self.const.email_target_deleted:
             return
         et.populate(target_type, self.entity_id, self.const.entity_account)
     et.write_db()
     # For deleted/reserved users, set expire_date for all of the
     # user's addresses, and don't allocate any new addresses.
     ea = Email.EmailAddress(self._db)
     if target_type == self.const.email_target_deleted:
         expire_date = self._db.DateFromTicks(time.time() +
                                              60 * 60 * 24 * 1)
         for row in et.get_addresses():
             ea.clear()
             ea.find(row['address_id'])
             if ea.email_addr_expire_date is None:
                 ea.email_addr_expire_date = expire_date
             ea.write_db()
         return
     # if an account email_target without email_server is found assign
     # the appropriate server based on spread and account_type
     spread = None
     if not et.email_server_id:
         if self.get_account_types() or \
            self.owner_type == self.const.entity_group:
             for s in self.get_spread():
                 if s['spread'] in (int(self.const.spread_exchange_account),
                                    int(self.const.spread_exchange_acc_old),
                                    int(self.const.spread_hia_email)):
                     spread = s['spread']
             et = self._update_email_server(spread)
         else:
             # do not set email_server_target
             # until account_type is registered
             return
     # Figure out which domain(s) the user should have addresses
     # in.  Primary domain should be at the front of the resulting
     # list.
     # if the only address found is in EMAIL_DEFAULT_DOMAIN
     # don't set default address. This is done in order to prevent
     # adresses in default domain being sat as primary
     # TODO: account_types affiliated to OU's  without connected
     # email domain don't get a default address
     primary_set = False
     ed = Email.EmailDomain(self._db)
     ed.find(self.get_primary_maildomain())
     domains = [ed.email_domain_name]
     if ed.email_domain_name == cereconf.EMAIL_DEFAULT_DOMAIN:
         if not self.owner_type == self.const.entity_group:
             primary_set = True
     if cereconf.EMAIL_DEFAULT_DOMAIN not in domains:
         domains.append(cereconf.EMAIL_DEFAULT_DOMAIN)
     # Iterate over the available domains, testing various
     # local_parts for availability.  Set user's primary address to
     # the first one found to be available.
     # Never change any existing email addresses
     try:
         self.get_primary_mailaddress()
         primary_set = True
     except Errors.NotFoundError:
         pass
     epat = Email.EmailPrimaryAddressTarget(self._db)
     for domain in domains:
         if ed.email_domain_name != domain:
             ed.clear()
             ed.find_by_domain(domain)
         # Check for 'cnaddr' category before 'uidaddr', to prefer
         # 'cnaddr'-style primary addresses for users in
         # maildomains that have both categories.
         ctgs = [int(r['category']) for r in ed.get_categories()]
         local_parts = []
         if int(self.const.email_domain_category_cnaddr) in ctgs:
             local_parts.append(
                 self.get_email_cn_local_part(given_names=1,
                                              max_initials=1))
             local_parts.append(self.account_name)
         elif int(self.const.email_domain_category_uidaddr) in ctgs:
             local_parts.append(self.account_name)
         for lp in local_parts:
             lp = self.wash_email_local_part(lp)
             # Is the address taken?
             ea.clear()
             try:
                 ea.find_by_local_part_and_domain(lp, ed.entity_id)
                 if ea.email_addr_target_id != et.entity_id:
                     # Address already exists, and points to a
                     # target not owned by this Account.
                     continue
                 # Address belongs to this account; make sure
                 # there's no expire_date set on it.
                 ea.email_addr_expire_date = None
             except Errors.NotFoundError:
                 # Address doesn't exist; create it.
                 ea.populate(lp, ed.entity_id, et.entity_id, expire=None)
             ea.write_db()
             if not primary_set:
                 epat.clear()
                 try:
                     epat.find(ea.email_addr_target_id)
                     epat.populate(ea.entity_id)
                 except Errors.NotFoundError:
                     epat.clear()
                     epat.populate(ea.entity_id, parent=et)
                 epat.write_db()
                 primary_set = True
             self.update_email_quota()
Beispiel #15
0
def process_mail(account_id, type, addr):
    et = Email.EmailTarget(db)
    ea = Email.EmailAddress(db)
    edom = Email.EmailDomain(db)
    epat = Email.EmailPrimaryAddressTarget(db)

    addr = string.lower(addr)

    fld = addr.split('@')
    if len(fld) != 2:
        logger.error("Bad address: %s. Skipping", addr)
        return None
    # fi

    lp, dom = fld
    try:
        edom.find_by_domain(dom)
        logger.debug("Domain found: %s: %d", dom, edom.entity_id)
    except Errors.NotFoundError:
        edom.populate(dom, "Generated by import_uname_mail.")
        edom.write_db()
        logger.debug("Domain created: %s: %d", dom, edom.entity_id)
    # yrt

    try:
        et.find_by_target_entity(int(account_id))
        logger.debug("EmailTarget found(accound): %s: %d", account_id,
                     et.entity_id)
    except Errors.NotFoundError:
        et.populate(constants.email_target_account,
                    entity_id=int(account_id),
                    entity_type=constants.entity_account)
        et.write_db()
        logger.debug("EmailTarget created: %s: %d", account_id, et.entity_id)
    # yrt

    try:
        ea.find_by_address(addr)
        logger.debug("EmailAddress found: %s: %d", addr, ea.entity_id)
    except Errors.NotFoundError:
        ea.populate(lp, edom.entity_id, et.entity_id)
        ea.write_db()
        logger.debug("EmailAddress created: %s: %d", addr, ea.entity_id)
    # yrt

    if type == "defaultmail":
        try:
            epat.find(et.entity_id)
            logger.debug("EmailPrimary found: %s: %d", addr, epat.entity_id)
        except Errors.NotFoundError:
            if ea.email_addr_target_id == et.entity_id:
                epat.clear()
                epat.populate(ea.entity_id, parent=et)
                epat.write_db()
                logger.debug("EmailPrimary created: %s: %d", addr,
                             epat.entity_id)
            else:
                logger.error("EmailTarget mismatch: ea: %d, et: %d",
                             ea.email_addr_target_id, et.entity_id)
            # fi
        # yrt
    # fi

    et.clear()
    ea.clear()
    edom.clear()
    epat.clear()
Beispiel #16
0
 def read_prim(self):
     mail_prim = Email.EmailPrimaryAddressTarget(self._db)
     for row in mail_prim.list_email_primary_address_targets():
         self.targ2prim[int(row['target_id'])] = int(row['address_id'])
Beispiel #17
0
 def _update_email_address_domains(self, et):
     # Figure out which domain(s) the user should have addresses
     # in.  Primary domain should be at the front of the resulting
     # list.
     ed = Email.EmailDomain(self._db)
     ea = Email.EmailAddress(self._db)
     try:
         ed.find(self.get_primary_maildomain(use_default_domain=False))
     except Errors.NotFoundError:
         # no appropriate primary domain was found, no valid address may
         # be generated
         return
     domains = self.get_prospect_maildomains(use_default_domain=False)
     # Iterate over the available domains, testing various
     # local_parts for availability.  Set user's primary address to
     # the first one found to be available.
     primary_set = False
     # Never change any existing email addresses
     try:
         self.get_primary_mailaddress()
         primary_set = True
     except Errors.NotFoundError:
         pass
     epat = Email.EmailPrimaryAddressTarget(self._db)
     if not domains:
         # no valid domain has been found and no e-mail address
         # can be assigned
         return
     for domain in domains:
         if ed.entity_id != domain:
             ed.clear()
             ed.find(domain)
         # Check for 'cnaddr' category before 'uidaddr', to prefer
         # 'cnaddr'-style primary addresses for users in
         # maildomains that have both categories.
         ctgs = [int(r['category']) for r in ed.get_categories()]
         local_parts = []
         if int(self.const.email_domain_category_cnaddr) in ctgs:
             local_parts.append(self.get_email_cn_local_part())
             local_parts.append(self.account_name)
         elif int(self.const.email_domain_category_uidaddr) in ctgs:
             local_parts.append(self.account_name)
         for lp in local_parts:
             lp = self.wash_email_local_part(lp)
             # Is the address taken?
             ea.clear()
             try:
                 ea.find_by_local_part_and_domain(lp, ed.entity_id)
                 if ea.email_addr_target_id != et.entity_id:
                     # Address already exists, and points to a
                     # target not owned by this Account.
                     #
                     # TODO: An expired address gets removed by a
                     # database cleaning job, and when it's gone,
                     # the address will eventually be recreated
                     # connected to this target.
                     continue
             except Errors.NotFoundError:
                 # Address doesn't exist; create it.
                 ea.populate(lp, ed.entity_id, et.entity_id, expire=None)
             ea.write_db()
             if not primary_set:
                 epat.clear()
                 try:
                     epat.find(ea.email_addr_target_id)
                     epat.populate(ea.entity_id)
                 except Errors.NotFoundError:
                     epat.clear()
                     epat.populate(ea.entity_id, parent=et)
                 epat.write_db()
                 primary_set = True
Beispiel #18
0
    def update_email_addresses(self):
        # Find, create or update a proper EmailTarget for this
        # account.
        et = Email.EmailTarget(self._db)
        target_type = self.const.email_target_account
        if self.is_expired():
            target_type = self.const.email_target_deleted
        changed = False
        try:
            et.find_by_email_target_attrs(target_entity_id=self.entity_id)
            if et.email_target_type != target_type:
                changed = True
                et.email_target_type = target_type
        except Errors.NotFoundError:
            # We don't want to create e-mail targets for reserved or
            # deleted accounts, but we do convert the type of existing
            # e-mail targets above.
            if target_type == self.const.email_target_deleted:
                return
            et.populate(target_type, self.entity_id, self.const.entity_account)
        et.write_db()
        # For deleted/reserved users, set expire_date for all of the
        # user's addresses, and don't allocate any new addresses.
        ea = Email.EmailAddress(self._db)
        if changed and cereconf.EMAIL_EXPIRE_ADDRESSES is not False:
            if target_type == self.const.email_target_deleted:
                seconds = cereconf.EMAIL_EXPIRE_ADDRESSES * 86400
                expire_date = self._db.DateFromTicks(time.time() + seconds)
            else:
                expire_date = None
            for row in et.get_addresses():
                ea.clear()
                ea.find(row['address_id'])
                ea.email_addr_expire_date = expire_date
                ea.write_db()
        # Active accounts shouldn't have an alias value (it is used
        # for failure messages)
        if changed and target_type == self.const.email_target_account:
            if et.email_target_alias is not None:
                et.email_target_alias = None
                et.write_db()

        if target_type == self.const.email_target_deleted:
            return
        # Figure out which domain(s) the user should have addresses
        # in.  Primary domain should be at the front of the resulting
        # list.
        ed = Email.EmailDomain(self._db)
        ed.find(self.get_primary_maildomain())
        domains = [ed.email_domain_name]

        # Add the default domains if missing
        for domain in Email.get_default_email_domains():
            if domain not in domains:
                domains.append(domain)

        # Iterate over the available domains, testing various
        # local_parts for availability.  Set user's primary address to
        # the first one found to be available.
        primary_set = False
        epat = Email.EmailPrimaryAddressTarget(self._db)
        for domain in domains:
            if ed.email_domain_name != domain:
                ed.clear()
                ed.find_by_domain(domain)
            # Check for 'uidaddr' category before 'cnaddr', to prefer
            # 'uidaddr'-style primary addresses for users in
            # maildomains that have both categories.
            ctgs = [int(r['category']) for r in ed.get_categories()]
            local_parts = []
            if int(self.const.email_domain_category_uidaddr) in ctgs:
                local_parts.append(self.account_name)
            elif int(self.const.email_domain_category_cnaddr) in ctgs:
                local_parts.append(self.get_email_cn_local_part())
                local_parts.append(self.account_name)
            for lp in local_parts:
                lp = self.wash_email_local_part(lp)
                # Is the address taken?
                ea.clear()
                try:
                    ea.find_by_local_part_and_domain(lp, ed.entity_id)
                    if ea.email_addr_target_id != et.entity_id:
                        # Address already exists, and points to a
                        # target not owned by this Account.
                        #
                        # TODO: An expired address gets removed by a
                        # database cleaning job, and when it's gone,
                        # the address will eventually be recreated
                        # connected to this target.
                        continue
                except Errors.NotFoundError:
                    # Address doesn't exist; create it.
                    ea.populate(lp, ed.entity_id, et.entity_id, expire=None)
                ea.write_db()
                if not primary_set:
                    epat.clear()
                    try:
                        epat.find(ea.email_addr_target_id)
                        epat.populate(ea.entity_id)
                    except Errors.NotFoundError:
                        epat.clear()
                        epat.populate(ea.entity_id, parent=et)
                    epat.write_db()
                    primary_set = True
def process_mail(account, mtype, addr, spread=None, homemdb=None):
    et = Email.EmailTarget(db)
    ea = Email.EmailAddress(db)
    edom = Email.EmailDomain(db)
    epat = Email.EmailPrimaryAddressTarget(db)

    addr = addr.lower()
    account_id = account.entity_id

    fld = addr.split('@')
    if len(fld) != 2:
        logger.error("Bad address: %s. Skipping", addr)
        return None

    lp, dom = fld
    try:
        edom.find_by_domain(dom)
    except Errors.NotFoundError:
        logger.error("Domain non-existent: %s", lp + '@' + dom)
        return None

    try:
        et.find_by_target_entity(int(account_id))
    except Errors.NotFoundError:
        et.populate(constants.email_target_account,
                    target_entity_id=int(account_id),
                    target_entity_type=constants.entity_account)
        et.write_db()
        logger.debug("EmailTarget created: %s: %d", account_id, et.entity_id)

    try:
        ea.find_by_address(addr)
    except Errors.NotFoundError:
        ea.populate(lp, edom.entity_id, et.entity_id)
        ea.write_db()
        logger.debug("EmailAddress created: %s: %d", addr, ea.entity_id)
        # if specified, add an email spread for users with email address
        if spread and not account.has_spread(spread):
            account.add_spread(spread)
            logger.debug("Added spread %s for account %s", spread, account_id)

    if mtype == "defaultmail":
        try:
            epat.find(et.entity_id)
            logger.debug("EmailPrimary found: %s: %d", addr, epat.entity_id)
        except Errors.NotFoundError:
            if ea.email_addr_target_id == et.entity_id:
                epat.clear()
                epat.populate(ea.entity_id, parent=et)
                epat.write_db()
                logger.debug("EmailPrimary created: %s: %d", addr,
                             epat.entity_id)
            else:
                logger.error("EmailTarget mismatch: ea: %d, et: %d",
                             ea.email_addr_target_id, et.entity_id)
        if homemdb:
            logger.info("Added exchange-mbd %s\n", homemdb)
            account.populate_trait(constants.trait_exchange_mdb,
                                   strval=homemdb)
            account.write_db()

    et.clear()
    ea.clear()
    edom.clear()
    epat.clear()
    def get_distgroup_attributes_and_targetdata(self,
                                                display_name_lang='nb',
                                                roomlist=False):
        all_data = {}
        ea = Email.EmailAddress(self._db)
        ed = Email.EmailDomain(self._db)
        et = Email.EmailTarget(self._db)
        epat = Email.EmailPrimaryAddressTarget(self._db)
        primary_address = ""
        display_name = ""
        name_language = ""
        addrs = []
        name_variant = self.const.dl_group_displ_name
        if display_name_lang == 'nb' or \
                not hasattr(self.const, 'dl_group_displ_name'):
            # code that uses this methos should probably take
            # care of getting the language right?
            name_language = self.const.language_nb
        else:
            name_language = int(_LanguageCode(display_name_lang))
        display_name = self.get_name_with_language(name_variant,
                                                   name_language,
                                                   default=self.group_name)
        # in roomlists we only care about name, description,
        # displayname and the roomlist-status, the other attributes
        # don't need to be set in Exchange
        if roomlist:
            all_data = {
                'name': self.group_name,
                'description': self.description,
                'displayname': display_name,
                'group_id': self.entity_id,
                'roomlist': self.roomlist
            }
            return all_data

        try:
            et.find_by_target_entity(self.entity_id)
        except Errors.NotFoundError:
            # could not find e-mail target for group. this should
            # normally not happen
            return None
        try:
            epat.find(et.entity_id)
        except Errors.NotFoundError:
            # could not find primary address for the e-mail target
            # this happens from time to time, and we should be able
            # to identify the error
            raise self._db.IntegrityError(
                "No primary address registered for {}".format(self.group_name))
        ea.clear()
        ea.find(epat.email_primaddr_id)
        ed.clear()
        ed.find(ea.email_addr_domain_id)
        primary_address = "%s@%s" % (ea.email_addr_local_part,
                                     ed.email_domain_name)
        for r in et.get_addresses(special=True):
            ad = "%s@%s" % (r['local_part'], r['domain'])
            addrs.append(ad)
        # name is expanded with prefix 'dl-' by the export
        all_data = {
            'name': self.group_name,
            'description': self.description,
            'displayname': display_name,
            'group_id': self.entity_id,
            'roomlist': self.roomlist,
            'hidden': self.hidden,
            'primary': primary_address,
            'aliases': addrs
        }
        return all_data