Exemple #1
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 _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()
Exemple #3
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
Exemple #4
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
Exemple #5
0
    def _split_email_address(self, addr, with_checks=True):
        """Split an e-mail address into local part and domain.

        Additionally, perform certain basic checks to ensure that the address
        looks sane.

        @type addr: basestring
        @param addr:
          E-mail address to split, spelled as 'foo@domain'.

        @type with_checks: bool
        @param with_checks:
          Controls whether to perform local part checks on the
          address. Occasionally we may want to sidestep this (e.g. when
          *removing* things from the database).

        @rtype: tuple of (basestring, basestring)
        @return:
          A pair, local part and domain extracted from the L{addr}.
        """
        from Cerebrum.modules import Email
        if addr.count('@') == 0:
            raise CerebrumError("E-mail address (%s) must include domain" %
                                addr)
        lp, dom = addr.split('@')
        if addr != addr.lower() and \
           dom not in cereconf.LDAP['rewrite_email_domain']:
            raise CerebrumError(
                "E-mail address (%s) can't contain upper case letters" % addr)
        if not with_checks:
            return lp, dom
        ea = Email.EmailAddress(self.db)
        if not ea.validate_localpart(lp):
            raise CerebrumError("Invalid localpart '%s'" % lp)
        return lp, dom
Exemple #6
0
    def sympa_create_list_alias(self,
                                operator,
                                listname,
                                address,
                                yes_no_force='No'):
        """ Create a secondary name for an existing Sympa list. """
        force = self._is_yes(yes_no_force)
        # The first thing we have to do is to locate the delivery
        # host. Postmasters do NOT want to allow people to specify a different
        # delivery host for alias than for the list that is being aliased. So,
        # find the ml's ET and fish out the server_id.
        self._validate_sympa_list(listname)
        local_part, domain = self._split_email_address(listname)
        ed = self._get_email_domain_from_str(domain)
        email_address = Email.EmailAddress(self.db)
        email_address.find_by_local_part_and_domain(local_part, ed.entity_id)
        try:
            delivery_host = self._get_server_from_address(email_address)
        except CerebrumError:
            raise CerebrumError("Cannot alias list %s (missing delivery host)",
                                listname)

        # TODO: Look at perms (are now done by _register)
        self._create_sympa_list_alias(operator,
                                      listname,
                                      address,
                                      delivery_host,
                                      force_alias=force)
        return {
            'target': listname,
            'alias': address,
        }
Exemple #7
0
def update_email_address(address, group):
    et, ea = get_email_target_and_address(address)
    if et:
        if et.email_target_type != co.email_target_multi:
            logger.error("Wrong existing target type for <%s>", address)
            return
        if et.email_target_entity_id == group.entity_id:
            logger.debug("Up-to-date <%s>", address)
            return
        et.email_target_entity_id = group.entity_id
        logger.info("Updating <%s>", address)
        et.write_db()
    else:
        et = Email.EmailTarget(db)
        try:
            et.find_by_target_entity(group.entity_id)
            logger.info("Added address <%s>", address)
        except Errors.NotFoundError:
            et.populate(co.email_target_multi,
                        target_entity_type=co.entity_group,
                        target_entity_id=group.entity_id)
            et.write_db()
            logger.info("Created <%s>", address)
        ed = Email.EmailDomain(db)
        lp, dom = address.split('@')
        ed.find_by_domain(dom)
        ea = Email.EmailAddress(db)
        ea.populate(lp, ed.entity_id, et.entity_id)
        ea.write_db()
def get_address(address_id):
    ea = Email.EmailAddress(db)
    ea.find(address_id)
    ed = Email.EmailDomain(db)
    ed.find(ea.email_addr_domain_id)
    return "%s@%s" % (ea.email_addr_local_part,
                      ed.rewrite_special_domains(ed.email_domain_name))
Exemple #9
0
 def validate_new_uname(self, domain, uname):
     """Check that the requested username is legal and free"""
     # TBD: will domain ever be anything else?
     if domain == self.const.account_namespace:
         ea = Email.EmailAddress(self._db)
         for row in ea.search(local_part=uname, filter_expired=False):
             return False
     return self.__super.validate_new_uname(domain, uname)
Exemple #10
0
 def read_addr(self):
     mail_addr = Email.EmailAddress(self._db)
     for row in mail_addr.list_email_addresses_ext():
         a_id, t_id = int(row['address_id']), int(row['target_id'])
         lp, dom = row['local_part'], row['domain']
         addr = self._build_addr(lp, dom)
         self.targ2addr[t_id].add(addr)
         self.aid2addr[a_id] = addr
Exemple #11
0
def get_email_target_and_address(address):
    ea = Email.EmailAddress(db)
    try:
        ea.find_by_address(address)
    except Errors.NotFoundError:
        return (None, None)
    et = Email.EmailTarget(db)
    et.find(ea.email_addr_target_id)
    return (et, ea)
Exemple #12
0
    def _request_list_localpart_domain(self, operator, listname, force=False):
        """ Request localpart and domain for new listname.

        This method checks if the given user can create a list with the given
        name. It will raise an error if the list cannot be created for the
        following reasons:
         - Mailing list exists
         - Mailing list name is illegal
         - Local part is a user, and the operator is not permitted to override
           this

        @param operator: The calling operator
        @param listname:
            The listname to validate and split into localpart and domain.
        @param force:
            True if the operation should be forced, even if a user exists with
            the same localpart.

        @return: tuple(str(localpart), str(somain))

        """
        local_part, domain = self._split_email_address(listname)
        ed = self._get_email_domain_from_str(domain)
        operator_id = operator.get_entity_id()
        self.ba.can_email_list_create(operator_id, ed)
        email_address = Email.EmailAddress(self.db)
        # First, check whether the address already exists
        try:
            email_address.find_by_local_part_and_domain(
                local_part, ed.entity_id)
        except Errors.NotFoundError:
            pass
        else:
            raise CerebrumError("Mail address %s already exists" % listname)

        # Then check whether the mailing list name is a legal one.
        if not (self._is_ok_mailing_list_name(local_part)
                or self.ba.is_postmaster(operator_id)):
            raise CerebrumError("Illegal mailing list name '%s'" % listname)

        # Then, check whether there is a user name equal to local_part.
        try:
            self._get_account(local_part, idtype='name')
        except CerebrumError:
            pass
        else:
            if not (local_part in ('drift', ) or
                    (self.ba.is_postmaster(operator_id) and force)):
                # TBD: This exception list should probably not be hardcoded
                # here -- but it's not obvious whether it should be a cereconf
                # value (implying that only site admins can modify the list)
                # or a database table.
                raise CerebrumError("%s is an existing username" % local_part)

        return (local_part, domain)
Exemple #13
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
Exemple #14
0
    def read_addr(self):
        mail_addr = Email.EmailAddress(self._db)
        # Handle "magic" domains.
        #   local_part@magic_domain
        # defines
        #   local_part@[all domains with category magic_domains],
        # overriding any entry for that local_part in any of those
        # domains.
        glob_addr = {}
        for domain_category in (
                self.const.email_domain_category_uio_globals, ):
            domain = str(domain_category)
            lp_dict = {}
            glob_addr[domain] = lp_dict
            # Fill glob_addr[magic domain][local_part]
            for row in mail_addr.list_email_addresses_ext(domain):
                lp_dict[row['local_part']] = row
            for row in self.mail_dom.list_email_domains_with_category(
                    domain_category):
                # Alias glob_addr[domain name] to glob_addr[magic domain],
                # for later "was local_part@domain overridden" check.
                glob_addr[row['domain']] = lp_dict
                # Update dicts 'targ2addr' and 'aid2addr' with the
                # override addresses.
                for lp, row2 in lp_dict.items():
                    # Use 'row', and not 'row2', for domain.  Using 'dom2'
                    # would give us 'UIO_GLOBALS'.
                    addr = self._build_addr(lp, row['domain'])
                    t_id = int(row2['target_id'])
                    self.targ2addr[t_id].add(addr)
                    # Note: We don't need to update aid2addr here, as
                    # addresses @UIO_GLOBALS aren't allowed to be primary
                    # addresses.
        for row in mail_addr.list_email_addresses_ext():
            lp, dom = row['local_part'], row['domain']
            if (dom == "ulrik.uio.no"):
                self.targ2ulrik_addr[int(row["target_id"])] = lp + "@" + dom
            # If this address is in a domain that is subject to overrides
            # from "magic" domains, and the local_part was overridden, skip
            # this row from being added to targ2addr.
            addr = self._build_addr(lp, dom)
            a_id, t_id = [int(row[x]) for x in ('address_id', 'target_id')]
            self.aid2addr[a_id] = addr
            if dom in glob_addr and lp in glob_addr[dom]:
                continue
            self.targ2addr[t_id].add(addr)

        # look for primary email changes that are still pending in the event
        # log
        self.read_pending_primary_email()
Exemple #15
0
def find_email_address(address):
    """Looks up an email address.

    :param int/str address: Email address entity ID or FQDA
    :rtype Email.EmailAddress:
    :return: the email address object
    """
    ea = Email.EmailAddress(db.connection)
    lookup = ea.find_by_address
    if isinstance(address, (int, long)):
        lookup = ea.find
    try:
        lookup(address)
    except (Errors.NotFoundError, ValueError):
        abort(404, message=u"No such email address {}".format(address))
    return ea
Exemple #16
0
    def update_email_addresses(self):
        # Overriding default update_email_addresses as NIH does not require
        # email_server, Jazz, 2011-05-22
        # 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() or self.is_reserved():
            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
        self._update_email_address_domains(et)
 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
Exemple #18
0
 def update_email_addresses(self):
     """Update an accounts email addresses and quotas."""
     spreads = [r['spread'] for r in self.get_spread()]
     if self.const.spread_uio_imap in spreads:
         # Make sure the email target of this account is associated
         # with an appropriate email server.  We must do this before
         # super, since without an email server, no target or address
         # will be created.
         self._UiO_update_email_server(self.const.email_server_type_cyrus)
         self.update_email_quota(spread=self.const.spread_uio_imap)
         return self.__super.update_email_addresses()
     elif self.const.spread_exchange_account in spreads:
         self.__super.update_email_addresses()
         # Append the default domain for exchange accounts! This should
         # probably be done elsewhere, but the code is so complex, that
         # we'll have to live with this solution, until we redesign the
         # email-module, or force the postmasters to write their own
         # address-management system.
         et = Factory.get('EmailTarget')(self._db)
         ea = Email.EmailAddress(self._db)
         ed = Email.EmailDomain(self._db)
         try:
             ed.find_by_domain(
                 cereconf.EXCHANGE_DEFAULT_ADDRESS_PLACEHOLDER)
             et.find_by_target_entity(self.entity_id)
         except Errors.NotFoundError:
             return
         else:
             try:
                 ea.find_by_local_part_and_domain(self.account_name,
                                                  ed.entity_id)
             except Errors.NotFoundError:
                 ea.populate(self.account_name,
                             ed.entity_id,
                             et.entity_id,
                             expire=None)
                 ea.write_db()
         return self.update_email_quota(
             spread=self.const.spread_exchange_account)
Exemple #19
0
    def sympa_remove_list_alias(self, operator, alias):
        """ Remove Sympa list aliases. """
        lp, dom = self._split_email_address(alias, with_checks=False)
        ed = self._get_email_domain_from_str(dom)
        self.ba.can_email_list_create(operator.get_entity_id(), ed)

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

        for addr_format, pipe in self._sympa_addr2alias:
            addr = addr_format % {
                "local_part": lp,
                "domain": dom,
            }
            try:
                ea.clear()
                ea.find_by_address(addr)
            except Errors.NotFoundError:
                # Even if one of the addresses is missing, it does not matter
                # -- we are removing the alias anyway. The right thing to do
                # here is to continue, as if deletion worked fine. Note that
                # the ET belongs to the original address, not the alias, so if
                # we don't delete it when the *alias* is removed, we should
                # still be fine.
                continue

            try:
                et.clear()
                et.find(ea.email_addr_target_id)
            except Errors.NotFoundError:
                raise CerebrumError("Could not find e-mail target for %s" %
                                    addr)

            # nuke the address, and, if it's the last one, nuke the target as
            # well.
            self._remove_email_address(et, addr)
        return {'alias': alias}
Exemple #20
0
 def email_rt_add_address(self, operator, queuename, address):
     """ RT add address. """
     queue, host = self._resolve_rt_name(queuename)
     rt_dom = self._get_email_domain_from_str(host)
     self.ba.can_rt_address_add(operator.get_entity_id(), domain=rt_dom)
     et = self._get_rt_email_target(queue, host)
     lp, dom = self._split_email_address(address)
     ed = self._get_email_domain_from_str(dom)
     if host != dom:
         self.ba.can_email_address_add(operator.get_entity_id(), domain=ed)
     ea = Email.EmailAddress(self.db)
     try:
         ea.find_by_local_part_and_domain(lp, ed.entity_id)
         raise CerebrumError("Address already exists (%s)" % address)
     except Errors.NotFoundError:
         pass
     if not (self._is_ok_mailing_list_name(lp)
             or self.ba.is_postmaster(operator.get_entity_id())):
         raise CerebrumError("Illegal queue address: %s" % address)
     ea.clear()
     ea.populate(lp, ed.entity_id, et.entity_id)
     ea.write_db()
     return ("OK, added '%s' as e-mail address for '%s'" %
             (address, queuename))
 def _move_email_address(self, address, reassigned_addresses, dest_et):
     ea = Email.EmailAddress(self.db)
     ea.find(address['address_id'])
     ea.email_addr_target_id = dest_et.entity_id
     ea.write_db()
     reassigned_addresses.append(ea.get_address())
Exemple #22
0
def destroy_group(group_id, max_recurse):
    if max_recurse is None:
        logger.fatal("destroy_group(%r) vil ikke slette permanent gruppe.",
                     group_id)
        sys.exit(1)
    gr = get_group(group_id)
    logger.debug("destroy_group(%s/%d, %d) [After get_group]", gr.group_name,
                 gr.entity_id, max_recurse)
    if max_recurse < 0:
        logger.fatal("destroy_group(%s): Recursion too deep", gr.group_name)
        sys.exit(3)

    if gr.get_extensions():
        logger.fatal("destroy_group(%s): Group is %r", gr.group_name,
                     gr.get_extensions())
        sys.exit(4)

    # If this group is a member of other groups, remove those
    # memberships.
    for r in gr.search(member_id=gr.entity_id, indirect_members=False):
        parent = get_group(r['group_id'])
        logger.debug("removing %s from group %s", gr.group_name,
                     parent.group_name)
        parent.remove_member(gr.entity_id)

    # If a e-mail target is of type multi and has this group as its
    # destination, delete the e-mail target and any associated
    # addresses.  There can only be one target per group.
    et = Email.EmailTarget(db)
    try:
        et.find_by_email_target_attrs(target_type=const.email_target_multi,
                                      target_entity_id=gr.entity_id)
    except Errors.NotFoundError:
        pass
    else:
        logger.debug("found email target referencing %s", gr.group_name)
        ea = Email.EmailAddress(db)
        for r in et.get_addresses():
            ea.clear()
            ea.find(r['address_id'])
            logger.debug("deleting address %s@%s", r['local_part'],
                         r['domain'])
            ea.delete()
        et.delete()
    # Fetch group's members (*before* the group is deleted)
    members = [
        int(x["member_id"])
        for x in gr.search_members(group_id=gr.entity_id,
                                   member_type=const.entity_group)
    ]
    logger.debug("destroy_group() subgroups: %r", members)

    # Remove any spreads the group has
    for row in gr.get_spread():
        gr.delete_spread(row['spread'])
    # Delete the parent group (which implicitly removes all membership
    # entries representing direct members of the parent group)
    gr.delete()
    # Destroy any subgroups (down to level max_recurse).  This needs
    # to be done after the parent group has been deleted, in order for
    # the subgroups not to be members of the parent anymore.
    for subg_id in members:
        destroy_group(subg_id, max_recurse - 1)
Exemple #23
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
Exemple #24
0
    def _get_entity_name(self, entity_id, entity_type=None):
        """Fetch a human-friendly name for the specified entity.

        @type entity_id: int
        @param entity_id:
          entity_id we are looking for.

        @type entity_type: const.EntityType instance (or None)
        @param entity_type:
          Restrict the search to the specifide entity. This parameter is
          really a speed-up only -- entity_id in Cerebrum uniquely determines
          the entity_type. However, should we know it, we save 1 db lookup.

        @rtype: str
        @return:
          Entity's name, obviously :) If none is found a magic string
          'notfound:<entity id>' is returned (it's not perfect, but it's better
          than nothing at all).
        """
        if entity_type is None:
            ety = Entity.Entity(self.db)
            try:
                ety.find(entity_id)
                entity_type = self.const.EntityType(ety.entity_type)
            except Errors.NotFoundError:
                return "notfound:%d" % entity_id
        if entity_type == self.const.entity_account:
            acc = self._get_account(entity_id, idtype='id')
            return acc.account_name
        elif entity_type in (self.const.entity_group, ):
            group = self._get_group(entity_id, idtype='id')
            return group.group_name
        elif entity_type == self.const.entity_disk:
            disk = Factory.get('Disk')(self.db)
            disk.find(entity_id)
            return disk.path
        elif entity_type == self.const.entity_host:
            host = Factory.get('Host')(self.db)
            host.find(entity_id)
            return host.name
        elif entity_type == self.const.entity_person:
            person = Factory.get('Person')(self.db)
            person.find(entity_id)
            return person.get_name(self.const.system_cached,
                                   self.const.name_full)

        # TODO: This should NOT be put in bofhd_core, as it should not require
        # the Email module! Subclassing? This is only a quickfix:
        if hasattr(self.const, 'entity_email_target'):
            if entity_type == self.const.entity_email_target:
                etarget = Factory.get('EmailTarget')(self.db)
                etarget.find(entity_id)
                return '%s:%s' % (str(etarget.get_target_type_name()),
                                  self._get_entity_name(
                                      etarget.get_target_entity_id()))
            elif entity_type == self.const.entity_email_address:
                ea = Email.EmailAddress(self.db)
                ea.find(entity_id)
                return ea.get_address()

        # Okey, we've run out of good options. Let's try a sensible fallback:
        # many entities have a generic name in entity_name. Let's use that:
        try:
            etname = type("entity_with_name",
                          (Entity.EntityName, Entity.Entity), dict())
            etname = etname(self.db)
            etname.find(entity_id)
            if etname.get_names():
                # just grab any name. We don't really have a lot of choice.
                return etname.get_names()[0]["name"]
            else:
                # ... and if it does not exist -- return the id. We are out of
                # options at this point.
                return "%s:%s" % (entity_type, entity_id)
        except Errors.NotFoundError:
            return "notfound:%d" % entity_id
        # NOTREACHED
        assert False
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()
Exemple #26
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
Exemple #27
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
Exemple #28
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
Exemple #29
0
    def sympa_create_list(self,
                          operator,
                          run_host,
                          delivery_host,
                          listname,
                          admins,
                          list_profile,
                          list_description,
                          yes_no_force="No"):
        """ Create a sympa list in Cerebrum and on the sympa server(s).

        Registers all the necessary cerebrum information and make a bofhd
        request for the actual list creation.

        """
        # Check that the profile is legal
        if list_profile not in cereconf.SYMPA_PROFILES:
            raise CerebrumError("Profile %s for sympa list %s is not valid" %
                                (list_profile, listname))

        # 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))

        metachars = "'\"$&()*;<>?[\\]`{|}~\n"

        def has_meta(s1, s2=metachars):
            """Check if any char of s1 is in s2"""
            for c in s1:
                if c in s2:
                    return True
            return False

        # Sympa list creation command will be passed through multiple
        # exec/shells. Better be restrictive.
        if True in [
                has_meta(x) for x in (run_host, delivery_host, listname,
                                      admins, list_profile, list_description)
        ]:
            raise CerebrumError(
                "Illegal metacharacter in list parameter. Allowed: '%s'" %
                metachars)

        delivery_host = self._get_email_server(delivery_host)
        force = self._is_yes(yes_no_force)
        self._create_sympa_list(operator, listname, delivery_host, force=force)
        # Now make a bofhd request to create the list itself
        admin_list = list()
        for item in admins.split(","):
            # it's a user name. That username must exist in Cerebrum
            if "@" not in item:
                self._get_account(item)
                # TODO: Not good, this is in use by UIA
                item = item + "@ulrik.uio.no"
            admin_list.append(item)

        # Make the request.
        lp, dom = self._split_email_address(listname)
        ed = self._get_email_domain_from_str(dom)
        ea = Email.EmailAddress(self.db)
        ea.clear()
        ea.find_by_local_part_and_domain(lp, ed.entity_id)
        list_id = ea.entity_id
        # IVR 2008-08-01 TBD: this is a big ugly. We need to pass several
        # arguments to p_b_r, but we cannot really store them anywhere :( The
        # idea is then to take a small dict, pickle it, shove into state_data,
        # unpickle in p_b_r and be on our merry way. It is at the very best
        # suboptimal.
        state = {
            "runhost": run_host,  # IVR 2008-08-01 FIXME: non-fqdn? force?
            # check?
            "admins": admin_list,
            "profile": list_profile,
            "description": list_description,
        }
        br = BofhdRequests(self.db, self.const)

        # IVR 2009-04-17 +30 minute delay to allow changes to spread to
        # LDAP. The postmasters are nagging for that delay. All questions
        # should be directed to them (this is similar to delaying a delete
        # request).
        br.add_request(operator.get_entity_id(),
                       DateTime.now() + DateTime.DateTimeDelta(0, 0, 30),
                       self.const.bofh_sympa_create,
                       list_id,
                       ea.entity_id,
                       state_data=pickle.dumps(state))
        return {'listname': listname}
Exemple #30
0
    def _email_info_sympa(self, operator, et, addr):
        """ Collect Sympa-specific information for a ML L{addr}. """
        def fish_information(suffix, local_part, domain, listname):
            """Generate an entry for sympa info for the specified address.

            @type address: basestring
            @param address:
              Is the address we are looking for (we locate ETs based on the
              alias value in _sympa_addr2alias).
            @type et: EmailTarget instance

            @rtype: sequence (of dicts of basestring to basestring)
            @return:
              A sequence of dicts suitable for merging into return value from
              email_info_sympa.
            """

            result = []
            address = "%(local_part)s-%(suffix)s@%(domain)s" % locals()
            target_alias = None
            for a, alias in self._sympa_addr2alias:
                a = a % locals()
                if a == address:
                    target_alias = alias % locals()
                    break

            # IVR 2008-08-05 TBD Is this an error? All sympa ETs must have an
            # alias in email_target.
            if target_alias is None:
                return result

            try:
                # Do NOT change et's (parameter's) state.
                et_tmp = Email.EmailTarget(self.db)
                et_tmp.clear()
                et_tmp.find_by_alias(target_alias)
            except Errors.NotFoundError:
                return result

            addrs = et_tmp.get_addresses()
            if not addrs:
                return result

            pattern = '%(local_part)s@%(domain)s'
            result.append({'sympa_' + suffix + '_1': pattern % addrs[0]})
            for idx in range(1, len(addrs)):
                result.append({'sympa_' + suffix: pattern % addrs[idx]})
            return result

        # end fish_information

        # listname may be one of the secondary addresses.
        # email info sympatest@domain MUST be equivalent to
        # email info sympatest-admin@domain.
        listname = self._get_sympa_list(addr)
        ret = [{"sympa_list": listname}]
        if listname.count('@') == 0:
            lp, dom = listname, addr.split('@')[1]
        else:
            lp, dom = listname.split('@')

        ed = Email.EmailDomain(self.db)
        ed.find_by_domain(dom)
        ea = Email.EmailAddress(self.db)
        try:
            ea.find_by_local_part_and_domain(lp, ed.entity_id)
        except Errors.NotFoundError:
            raise CerebrumError(
                "Address %s exists, but the list it points to, %s, does not" %
                (addr, listname))
        # now find all e-mail addresses
        et_sympa = Email.EmailTarget(self.db)
        et_sympa.clear()
        et_sympa.find(ea.email_addr_target_id)
        addrs = self._get_valid_email_addrs(et_sympa, sort=True)
        # IVR 2008-08-21 According to postmasters, only superusers should see
        # forwarding and delivery host information
        if self.ba.is_postmaster(operator.get_entity_id()):
            if et_sympa.email_server_id is None:
                delivery_host = "N/A (this is an error)"
            else:
                delivery_host = self._get_email_server(
                    et_sympa.email_server_id).name
            ret.append({"sympa_delivery_host": delivery_host})
        ret += self._email_info_forwarding(et_sympa, addrs)
        aliases = []
        for row in et_sympa.get_addresses():
            a = "%(local_part)s@%(domain)s" % row
            if a == listname:
                continue
            aliases.append(a)
        if aliases:
            ret.append({"sympa_alias_1": aliases[0]})
        for next_alias in aliases[1:]:
            ret.append({"sympa_alias": next_alias})

        for suffix in ("owner", "request", "editor", "subscribe",
                       "unsubscribe"):
            ret.extend(fish_information(suffix, lp, dom, listname))

        return ret