def recv_captcha(self, mailfrom, msg): """ Receives and verifies captcha. """ subject = msg['Subject'] orgidentifier = string.split(subject,' ')[-1] logging.debug('Orig CAPTCHA identifier\t: %s', orgidentifier) # TODO: Reject if original identifier is not in DB try: if msg.is_multipart(): answer = msg.get_payload(0).get_payload().splitlines()[0].strip() else: answer = msg.get_payload().splitlines()[0].strip() except: return identifier = self.db.hash_data(Address(mailfrom).address, answer) match = self.db.get_captcha_word(identifier) if match != None and match['word'] == answer: # Update captcha status to CAPTCHA_APPROVED cid, rid, word = match adata = self.db.get_alias_data(rid) aobj = Alias(**adata) user = User(**self.db.get_user(uid=aobj.get_uid())) # send message to recipient's alias requesting mailfrom's permission to send msg = UserMessage('senderverify.senduserreq', \ fromaddx = self.cfg.SVCALIAS, \ aliasaddx = aobj.get_alias_address(), \ useraddx = user.get_account_address(), \ requestor = mailfrom) msg.generate_message_id(self.cfg.DOMAIN) self.db.set_captcha(msg['Message-ID'], '', cid, rid, self.db.CAPTCHA_APPROVED) logging.debug('Sending approval request to user %s', user.get_username()) self.send(msg['From'], [user.get_forwarding_address()], msg) # Delete identifier from database #self.db.delete_captcha_identifier(identifier) else: # TOFIX: should replace with new captcha and increment numtries; pass
def forward(self, mailfrom, rcpttos, msg): """ Handles Case 2, where email is not from a service user and so needs to be forwarded to various aliases. """ for rcpt in rcpttos: prcpt = Address(rcpt) alias_pair = prcpt.parse_alias_address() logging.debug(rcpt) if not alias_pair: # if the domain is SERVERNAME, sender screwed up; return error to sender... if prcpt.is_servername(): logging.info('Encountered improperly formatted ' 'address "%s" in recipients field', prcpt.address) # Create error response message err = ErrorMessage('forward.badformat', fromaddx = self.cfg.SVCALIAS, toaddx = mailfrom, badalias = prcpt.address) self.send(err['From'], [mailfrom], err) # ... otherwise ignore; not our job to send to non-users logging.info('Encountered recipient outside our domain; ignoring') else: alias_data = self.db.get_alias_data(*alias_pair) if alias_data: fwd_alias = Alias(**alias_data) userdata = self.db.get_user(uid=fwd_alias.get_uid()) assert userdata is not None user = User(**userdata) logging.debug('is trusted? %s', fwd_alias.is_trusted()) # handle trustedness here; if not fwd_alias.is_trusted(): mfrom = Address(mailfrom) # if sender is in trusted group, then it's all right; if self.db.is_trusted_correspondent(mfrom, \ user.get_salt(), \ fwd_alias.get_rid(), \ fwd_alias.get_trusted_timestamp()): pass # TODO: send/append something about newer alias to send to? else: capstat = self.db.get_capstat(mfrom, \ user.get_salt(), \ fwd_alias.get_rid()) logging.debug('capstat=%s', capstat) if capstat < self.db.CAPTCHA_PENDING: logging.debug('captcha not yet sent; trying to send one') # If not approved, send captcha to sender and drop mail. # TODO: Perhaps we can cache the mail somewhere. cid = self.db.get_cid(mfrom, user.get_salt()) self.send_captcha(mailfrom, cid, fwd_alias) #self.db.set_capstat(cid, # fwd_alias.get_rid(), # self.db.CAPTCHA_PENDING) logging.debug('done sending captcha') elif capstat == self.db.CAPTCHA_PENDING: logging.debug('captcha was already sent; still waiting for solution') elif capstat == self.db.CAPTCHA_APPROVED: logging.debug('captcha approved, but not yet user approved') # if user denied, # TODO: just ignore? or do something more? # pass # if user judgement pending, send message # informing them they must wait for user's approval? if capstat == self.db.USER_PENDING: pass # TODO: send message return # TODO: can consult a whitelist/blacklist/etc. here fwd_addx = Address(user.get_forwarding_address()) fwd_addx.realname = prcpt.realname logging.info('Found alias for account (%s) Forwarding message to %s', \ user.get_username(), fwd_addx.address) # Add hint as recipient name. The hint/domain is used as a reminder # to the user where this email address was originally created for. # But since we did not update Reply-To, it will drop off when the # user replies to the message. rcptaddr = Address(rcpt) if rcptaddr.get_realname() == '': if fwd_alias.get_hint() != None: rcptaddr.set_realname(fwd_alias.get_hint()) elif fwd_alias.get_domain() != None: rcptaddr.set_realname(fwd_alias.get_domain()) msg.replace_address('To', rcpt, rcptaddr) acct_addx = Address(user.get_account_address()) acct_addx.realname = prcpt.realname #del msg['message-id'] #del msg['DKIM-Signature'] if 'To' in msg: msg.replace_header('To', msg['To'] + ', ' + str(acct_addx)) if 'Reply-To' in msg: msg.replace_header('Reply-To', msg['reply-to'] + ', ' + rcpt); else: msg.add_header('Reply-To', mailfrom + ', ' + rcpt); if 'Message-ID' not in msg: msg.generate_message_id(self.cfg.DOMAIN) self.send(mailfrom, [str(fwd_addx)], msg) self.db.add_history(fwd_alias.get_rid(), False, [Address(mailfrom)], msg['Message-ID'], user.get_salt()) else: logging.info("Couldn't find data for alias (%s,%d)", *alias_pair) return