コード例 #1
0
 def test_delete_tenant(self):
     tenant_name = "tenant1"
     members = ["*****@*****.**"]
     by = "*****@*****.**"
     db.create_tenant(tenant_name, "hoge", self.config)
     config = {
         "admins": {"hoge"},
         "charset": "iso-2022-jp",
         "ml_name_format": "ml1-%06d",
         "new_ml_account": "ml1-new",
         "days_to_close": 7,
         "days_to_orphan": 7,
         "welcome_msg": "welcome_msg",
         "readme_msg": "readme_msg",
         "add_msg": "add_msg",
         "remove_msg": "remove_msg",
         "reopen_msg": "reopen_msg",
         "goodbye_msg": "goodbye_msg",
         "report_subject": "report_subject",
         "report_msg": "report_msg",
         "orphaned_subject": "orphaned_subject",
         "orphaned_msg": "orphaned_msg",
         "closed_subject": "closed_subject",
         "closed_msg": "closed_msg",
     }
     db.create_ml(tenant_name, "ml1", "hoge", members, by)
     config['new_ml_account'] = 'ml2-new'
     db.create_ml(tenant_name, "ml2", "hoge", members, by)
     ret = db.find_mls({"tenant_name": tenant_name})
     self.assertEqual(len(list(ret)), 2)
     db.delete_tenant(tenant_name)
     ret = db.find_mls({"tenant_name": tenant_name})
     self.assertEqual(len(list(ret)), 0)
コード例 #2
0
    def test_add_and_del_members(self):
        ml_name = ML_NAME % db.increase_counter(self.tenant_name)
        db.create_ml(self.tenant_name, ml_name, "hoge", set(), "xyz")
        self.assertEqual(db.get_members(ml_name), set())

        db.add_members(ml_name, {"abc", "def"}, "xyz")
        self.assertEqual(db.get_members(ml_name), {"abc", "def"})

        db.add_members(ml_name, {"abc", "ghi"}, "xyz")
        self.assertEqual(db.get_members(ml_name), {"abc", "def", "ghi"})

        db.del_members(ml_name, {"abc", "ghi"}, "xyz")
        self.assertEqual(db.get_members(ml_name), {"def"})

        db.del_members(ml_name, {"abc", "def"}, "xyz")
        self.assertEqual(db.get_members(ml_name), set())
        logs = [
            {
                'op': const.OP_CREATE,
                'by': "xyz",
                'members': set(),
            },
            {
                'op': const.OP_ADD_MEMBERS,
                'by': "xyz",
                'members': {"abc", "def"},
            },
            {
                'op': const.OP_ADD_MEMBERS,
                'by': "xyz",
                'members': {"abc", "ghi"},
            },
            {
                'op': const.OP_DEL_MEMBERS,
                'by': "xyz",
                'members': {"abc", "ghi"},
            },
            {
                'op': const.OP_DEL_MEMBERS,
                'by': "xyz",
                'members': {"abc", "def"},
            },
        ]
        self.assertEqual(db.get_logs(ml_name), logs)
コード例 #3
0
    def test_change_ml_status(self):
        ml_name = ML_NAME % db.increase_counter(self.tenant_name)
        db.create_ml(self.tenant_name, ml_name, "hoge", set(), "xyz")
        self.assertEqual(db.get_members(ml_name), set())

        db.change_ml_status(ml_name, const.STATUS_ORPHANED, "xxx")
        ml = db.get_ml(ml_name)
        self.assertEqual(ml['status'], const.STATUS_ORPHANED)
        self.assertEqual(ml['logs'][-1]['op'], const.OP_ORPHAN)

        db.change_ml_status(ml_name, const.STATUS_CLOSED, "xxx")
        ml = db.get_ml(ml_name)
        self.assertEqual(ml['status'], const.STATUS_CLOSED)
        self.assertEqual(ml['logs'][-1]['op'], const.OP_CLOSE)

        db.change_ml_status(ml_name, const.STATUS_OPEN, "xxx")
        ml = db.get_ml(ml_name)
        self.assertEqual(ml['status'], const.STATUS_OPEN)
        self.assertEqual(ml['logs'][-1]['op'], const.OP_REOPEN)
コード例 #4
0
    def test_create_ml(self):
        members = ["abc", "def", "ghi"]
        by = "xyz"

        for i in range(1, 4):
            ml_name = ML_NAME % db.increase_counter(self.tenant_name)
            db.create_ml(self.tenant_name, ml_name, "hoge", members, by)
            ml = db.get_ml(ml_name)
            self.assertEqual(ml['ml_name'], ml_name)
            self.assertEqual(ml['subject'], "hoge")
            self.assertEqual(ml['members'], members)
            self.assertEqual(ml['by'], by)
            self.assertEqual(ml['status'], const.STATUS_NEW)
            logs = [{
                'op': const.OP_CREATE,
                'by': by,
                'members': members,
            }]
            self.assertEqual(ml['logs'], logs)
コード例 #5
0
    def test_mark_mls_orphaned_and_closed(self):
        ml1_name = ML_NAME % db.increase_counter(self.tenant_name)
        db.create_ml(self.tenant_name, ml1_name, "hoge", [], "xyz")

        ml2_name = ML_NAME % db.increase_counter(self.tenant_name)
        db.create_ml(self.tenant_name, ml2_name, "hoge", [], "xyz")
        db.change_ml_status(ml2_name, const.STATUS_OPEN, "XYZ")

        time.sleep(1)
        now = datetime.now()

        time.sleep(1)
        ml3_name = ML_NAME % db.increase_counter(self.tenant_name)
        db.create_ml(self.tenant_name, ml3_name, "hoge", [], "xyz")
        db.change_ml_status(ml3_name, const.STATUS_OPEN, "XYZ")

        db.mark_mls_orphaned(now, "XYZ")
        ml1 = db.get_ml(ml1_name)
        ml2 = db.get_ml(ml2_name)
        ml3 = db.get_ml(ml3_name)
        self.assertEqual(ml1['status'], const.STATUS_NEW)
        self.assertEqual(ml2['status'], const.STATUS_ORPHANED)
        self.assertEqual(ml2['by'], "XYZ")
        self.assertEqual(ml3['status'], const.STATUS_OPEN)

        time.sleep(1)
        db.mark_mls_closed(datetime.now(), "XYZ")
        ml1 = db.get_ml(ml1_name)
        ml2 = db.get_ml(ml2_name)
        ml3 = db.get_ml(ml3_name)
        self.assertEqual(ml1['status'], const.STATUS_NEW)
        self.assertEqual(ml2['status'], const.STATUS_CLOSED)
        self.assertEqual(ml3['status'], const.STATUS_OPEN)
        self.assertEqual(ml2['by'], "XYZ")
        logs = [
            {
                'op': const.OP_CREATE,
                'by': "xyz",
                'members': [],
            },
            {
                'op': const.OP_REOPEN,
                'by': "XYZ",
            },
            {
                'op': const.OP_ORPHAN,
                'by': "XYZ",
            },
            {
                'op': const.OP_CLOSE,
                'by': "XYZ",
            },
        ]
        self.assertEqual(ml2['logs'], logs)
コード例 #6
0
    def test_find_mls(self):
        db.create_ml(self.tenant_name, "a", "hoge1", set(), "xyz")
        time.sleep(1)
        db.create_ml(self.tenant_name, "b", "hoge2", set(), "xyz")
        time.sleep(1)
        db.create_ml(self.tenant_name, "c", "hoge1", set(), "xyz")

        ret = db.find_mls({"subject": "hoge1"})
        self.assertEqual(len(list(ret)), 2)
        ret = db.find_mls({"subject": "hoge1"}, sortkey='created')
        self.assertEqual([_['ml_name'] for _ in ret], ["a", "c"])
        ret = db.find_mls({}, sortkey='created', reverse=True)
        self.assertEqual([_['ml_name'] for _ in ret], ["c", "b", "a"])
コード例 #7
0
    def process_message(self, peer, mailfrom, rcpttos, data):
        message = email.message_from_string(data)

        from_str = message.get('From', "").strip()
        to_str = message.get('To', "").strip()
        cc_str = message.get('Cc', "").strip()
        subject = message.get('Subject', "").strip()
        try:
            subject = str(make_header(decode_header(subject)))
        except:
            pass
        command = subject.strip().lower()
        logging.info("Processing: from=%s|to=%s|cc=%s|subject=%s|", from_str,
                     to_str, cc_str, subject)

        _from = normalize([from_str])
        to = normalize(to_str.split(','))
        cc = normalize(cc_str.split(','))

        # Quick hack
        mailfrom = list(_from)[0]

        # Check cross-post
        mls = [_ for _ in (to | cc) if _.endswith(self.at_domain)]
        if len(mls) == 0:
            logging.error("No ML specified")
            return const.SMTP_STATUS_NO_ML_SPECIFIED
        elif len(mls) > 1:
            logging.error("Can't cross-post a message")
            return const.SMTP_STATUS_CANT_CROSS_POST

        # Aquire the ML name
        ml_address = mls[0]
        ml_name = ml_address.replace(self.at_domain, "")
        params = dict(ml_name=ml_name,
                      ml_address=ml_address,
                      mailfrom=mailfrom)

        # Remove the ML name from to'd and cc'd address lists
        if ml_address in to:
            to.remove(ml_address)
        if ml_address in cc:
            cc.remove(ml_address)

        # Is an error mail?
        if ml_name.endswith(ERROR_SUFFIX):
            ml_name = ml_name.replace(ERROR_SUFFIX, "")
            error_str = REMOVE_RFC822.sub(
                "", message.get('Original-Recipient', ""))
            error = normalize(error_str.split(','))
            if len(error) > 0 and len(ml_name) > 0:
                logging.error("not delivered to %s for %s", error, ml_name)
            return

        # Aquire current tenants
        tenants = db.find_tenants({"status": const.TENANT_STATUS_ENABLED})

        # Want a new ML?
        for config in tenants:
            if ml_name == config['new_ml_account']:
                tenant_name = config['tenant_name']
                ml_name = config['ml_name_format'] % \
                    db.increase_counter(config['tenant_name'])
                members = (to | cc | _from) - config['admins']
                db.create_ml(tenant_name, ml_name, subject, members, mailfrom)
                ml_address = ml_name + self.at_domain
                params = dict(ml_name=ml_name,
                              ml_address=ml_address,
                              mailfrom=mailfrom,
                              members=members)
                message = ensure_multipart(message, config['charset'])
                self.send_message(config, ml_name, message, mailfrom, params,
                                  config['welcome_msg'], 'Welcome.txt')
                return

        # Post a message to an existing ML

        # Check ML exists
        ml = db.get_ml(ml_name)
        if ml is None:
            logging.error("No such ML: %s", ml_name)
            return const.SMTP_STATUS_NO_SUCH_ML

        # Set config variable
        for config in tenants:
            if ml['tenant_name'] == config['new_ml_account']:
                break
        else:
            if config is None:
                logging.error("No such tenant: %s", ml['tenant_name'])
                return const.SMTP_STATUS_NO_SUCH_TENANT

        message = ensure_multipart(message, config['charset'])

        # Checking whether the sender is one of the ML members
        members = db.get_members(ml_name)
        if mailfrom not in (members | config['admins']):
            logging.error("Non-member post")
            return const.SMTP_STATUS_NOT_MEMBER

        # Update parameters
        new_ml_address = config['new_ml_account'] + self.at_domain
        params = dict(ml_name=ml_name,
                      ml_address=ml_address,
                      mailfrom=mailfrom,
                      new_ml_address=new_ml_address,
                      members=members)

        # Check ML status
        ml_status = ml['status']
        if ml_status == const.STATUS_CLOSED:
            if command == "reopen":
                self.send_message(config, ml_name, message, mailfrom, params,
                                  config['reopen_msg'], 'Reopen.txt')
                db.change_ml_status(ml_name, const.STATUS_OPEN, mailfrom)
                logging.info("reopened %s by %s", ml_name, mailfrom)
                return

            logging.error("ML is closed: %s", ml_name)
            return const.SMTP_STATUS_CLOSED_ML

        elif command == "close":
            self.send_message(config, ml_name, message, mailfrom, params,
                              config['goodbye_msg'], 'Goodbye.txt')
            db.change_ml_status(ml_name, const.STATUS_CLOSED, mailfrom)
            logging.info("closed %s by %s", ml_name, mailfrom)
            return

        if ml_status != const.STATUS_OPEN:
            db.change_ml_status(ml_name, const.STATUS_OPEN, mailfrom)

        # Remove admin members from cc
        cc -= config['admins']
        params["cc"] = cc

        # Remove cc'd members from the ML members if the subject is empty
        if command == "":
            if len(cc) > 0:
                params['members'] = members - cc
                self.send_message(config, ml_name, message, mailfrom, params,
                                  config['remove_msg'], 'RemoveMembers.txt')
                db.del_members(ml_name, cc, mailfrom)
                logging.info("removed %s from %s", cc, ml_name)
            return

        # Checking Cc:
        if len(cc) > 0:
            db.add_members(ml_name, cc, mailfrom)
            logging.info("added %s into %s", cc, ml_name)
            members = db.get_members(ml_name)
            params['members'] = members
            self.send_message(config, ml_name, message, mailfrom, params,
                              config['add_msg'], 'AddMembers.txt')
            return

        # Attach readme and send the post
        self.send_message(config, ml_name, message, mailfrom, params,
                          config['readme_msg'], 'Readme.txt')