def test_mail_content(): """Test mail_content function.""" with pytest.raises(email.errors.MessageError, message="mail 'None' is not a email.message.Message."): imaputils.mail_content(None) fmail = open('examples/spam.from.spamassassin.eml', 'rb') ftext = fmail.read() mail = imaputils.new_message(ftext) fmail.close() assert isinstance(imaputils.mail_content(mail), (str, bytes))
def test_mail_content(): """Test mail_content function.""" with pytest.raises(email.errors.MessageError): imaputils.mail_content(None) pytest.fail("mail 'None' is not a email.message.Message.") fmail = open('examples/spam.from.spamassassin.eml', 'rb') ftext = fmail.read() mail = imaputils.new_message(ftext) fmail.close() assert isinstance(imaputils.mail_content(mail), (str, bytes))
def test_mail(mail, spamc=False, cmd=False): """Test a email with spamassassin.""" score = "0/0\n" orig_code = None spamassassin_result = None returncode = None if cmd: satest = cmd elif spamc: # let spamc process mails larger than 500 KB if ISBG forwards them satest = ["spamc", "-E", "--max-size=268435450"] else: satest = ["spamassassin", "--exit-code"] proc = utils.popen(satest) try: spamassassin_result = proc.communicate(imaputils.mail_content(mail))[0] returncode = proc.returncode proc.stdin.close() score = utils.score_from_mail(spamassassin_result.decode(errors='ignore')) except Exception: # pylint: disable=broad-except score = "-9999" return score, returncode, spamassassin_result
def test_mail(mail, spamc=False, cmd=False): """Test a email with spamassassin.""" score = "0/0\n" orig_code = None spamassassin_result = None returncode = None if cmd: satest = cmd elif spamc: satest = ["spamc", "-E"] else: satest = ["spamassassin", "--exit-code"] proc = utils.popen(satest) try: spamassassin_result = proc.communicate(imaputils.mail_content(mail))[0] returncode = proc.returncode proc.stdin.close() score = utils.score_from_mail( spamassassin_result.decode(errors='ignore')) except Exception: # pylint: disable=broad-except score = "-9999" return score, returncode, spamassassin_result
def _process_spam(self, uid, score, mail, spamdeletelist, code, spamassassin_result): self.logger.debug(__("{} is spam".format(uid))) if (self.deletehigherthan is not None and float(score.split('/')[0]) > self.deletehigherthan): spamdeletelist.append(uid) return False # do we want to include the spam report if self.noreport is False: if self.dryrun: self.logger.info("Skipping report because of --dryrun") else: new_mail = spamassassin_result if new_mail == u"-9999": self.logger.exception( '{} error for mail {} (ret code {})'.format( self.cmd_save, uid, code)) self.logger.debug(repr(imaputils.mail_content(mail))) if uid in spamdeletelist: spamdeletelist.remove(uid) return False res = self.imap.append(self.imapsets.spaminbox, None, None, new_mail) # The above will fail on some IMAP servers for various # reasons. We print out what happened and continue # processing if res[0] != 'OK': self.logger.error( __(("{} failed for uid {}: {}. Leaving original" + "message alone.").format( repr([ "append", self.imapsets.spaminbox, "{email}" ]), repr(uid), repr(res)))) if uid in spamdeletelist: spamdeletelist.remove(uid) return False else: # No report: if self.dryrun: self.logger.info("Skipping copy to spambox because" + " of --dryrun") else: # just copy it as is self.imap.uid("COPY", uid, self.imapsets.spaminbox) return True
def _process_spam(self, uid, score, mail, spamdeletelist, code, spamassassin_result): self.logger.debug(__("{} is spam".format(uid))) if (self.deletehigherthan is not None and float(score.split('/')[0]) > self.deletehigherthan): spamdeletelist.append(uid) return False # do we want to include the spam report if self.noreport is False: if self.dryrun: self.logger.info("Skipping report because of --dryrun") else: new_mail = spamassassin_result if new_mail == u"-9999": self.logger.exception( '{} error for mail {} (ret code {})'.format( self.cmd_save, uid, code)) self.logger.debug(repr(imaputils.mail_content(mail))) if uid in spamdeletelist: spamdeletelist.remove(uid) return False res = self.imap.append(self.imapsets.spaminbox, None, None, new_mail) # The above will fail on some IMAP servers for various # reasons. We print out what happened and continue # processing if res[0] != 'OK': self.logger.error(__( ("{} failed for uid {}: {}. Leaving original" + "message alone.").format( repr(["append", self.imapsets.spaminbox, "{email}"]), repr(uid), repr(res)))) if uid in spamdeletelist: spamdeletelist.remove(uid) return False else: # No report: if self.dryrun: self.logger.info("Skipping copy to spambox because" + " of --dryrun") else: # just copy it as is self.imap.uid("COPY", uid, self.imapsets.spaminbox) return True
def learn_mail(mail, learn_type): """Process a email and try to learn or unlearn it. Args: mail (email.message.Message): email to learn. learn_type (str): ```spam``` to learn spam, ```ham``` to learn nonspam or ```forget```. Returns: int, int: It returns a pair of `int` The first integer: A return code of ``6`` means it was already learned or forgotten, a return code of ``5`` means it has been learned or forgotten, a ``-9999`` means an error communicating with ``spamc``. If ``spamc`` returns an exit code, it returns it. The second integer: It's the original exit code from ``spamc`` Notes: See `Exit Codes` section of the man page of ``spamc`` for more information about other exit codes. """ out = "" orig_code = None size = len(mail.as_bytes()) proc = utils.popen(["spamc", "--learntype=" + learn_type, "--max-size="+str(size+1)]) try: out = proc.communicate(imaputils.mail_content(mail)) code = int(proc.returncode) orig_code = code except Exception: # pylint: disable=broad-except code = -9999 proc.stdin.close() if code == 0: out = out[0].decode(errors='ignore').strip() if out == __spamc_msg__['already']: code = 6 elif out == __spamc_msg__['success']: code = 5 return code, orig_code
def learn_mail(mail, learn_type): """Process a email and try to learn or unlearn it. Args: mail (email.message.Message): email to learn. learn_type (str): ```spam``` to learn spam, ```ham``` to learn nonspam or ```forget```. Returns: int, int: It returns a pair of `int` The first integer: A return code of ``6`` means it was already learned or forgotten, a return code of ``5`` means it has been learned or forgotten, a ``-9999`` means an error communicating with ``spamc``. If ``spamc`` returns an exit code, it returns it. The second integer: It's the original exit code from ``spamc`` Notes: See `Exit Codes` section of the man page of ``spamc`` for more information about other exit codes. """ out = "" orig_code = None proc = utils.popen(["spamc", "--learntype=" + learn_type]) try: out = proc.communicate(imaputils.mail_content(mail)) code = int(proc.returncode) orig_code = code except Exception: # pylint: disable=broad-except code = -9999 proc.stdin.close() if code == 0: out = out[0].decode(errors='ignore').strip() if out == __spamc_msg__['already']: code = 6 elif out == __spamc_msg__['success']: code = 5 return code, orig_code
def feed_mail(mail, spamc=False, cmd=False): """Feed a email with spamassassin report.""" new_mail = "" orig_code = None if cmd: sasave = cmd elif spamc: sasave = ["spamc"] else: sasave = ["spamassassin"] proc = utils.popen(sasave) try: new_mail = proc.communicate(imaputils.mail_content(mail))[0] orig_code = proc.returncode except Exception: # pylint: disable=broad-except new_mail = u"-9999" proc.stdin.close() return new_mail, orig_code
def learn(self, folder, learn_type, move_to, origpastuids): """Learn the spams (and if requested deleted or move them). Args: folder (str): The IMAP folder. leart_type (str): ```spam``` to learn spam, ```ham``` to learn nonspam. move_to (str): If not ```None```, the imap folder where the emails will be moved. origpastuids (list(int)): ``uids`` to not process. Returns: Sa_Learn: It contains the information about the result of the process. It will call ``spamc`` to learn the emails. Raises: isbg.ISBGError: if learn_type is unknown. TODO: Add suport to ``learn_type=forget``. """ sa_learning = Sa_Learn() # Sanity checks: if learn_type not in ['spam', 'ham']: raise isbg.ISBGError(-1, message="Unknown learn_type") if self.imap is None: raise isbg.ISBGError(-1, message="Imap is required") self.logger.debug(__( "Teach {} to SA from: {}".format(learn_type, folder))) self.imap.select(folder) if self.learnunflagged: _, uids = self.imap.uid("SEARCH", None, "UNFLAGGED") elif self.learnflagged: _, uids = self.imap.uid("SEARCH", None, "(FLAGGED)") else: _, uids = self.imap.uid("SEARCH", None, "ALL") uids, sa_learning.newpastuids = SpamAssassin.get_formated_uids( uids, origpastuids, self.partialrun) sa_learning.tolearn = len(uids) for uid in uids: mail = imaputils.get_message(self.imap, uid, logger=self.logger) # Unwrap spamassassin reports unwrapped = sa_unwrap.unwrap(mail) if unwrapped is not None: self.logger.debug(__("{} Unwrapped: {}".format( uid, utils.shorten(imaputils.mail_content( unwrapped[0]), 140)))) if unwrapped is not None and unwrapped: # len(unwrapped)>0 mail = unwrapped[0] if self.dryrun: self.logger.warning("Skipped learning due to dryrun!") continue else: code, code_orig = learn_mail(mail, learn_type) if code == -9999: # error processing email, try next. self.logger.exception(__( 'spamc error for mail {}'.format(uid))) self.logger.debug(repr(imaputils.mail_content(mail))) continue if code in [69, 74]: raise isbg.ISBGError( isbg.__exitcodes__['flags'], "spamassassin is misconfigured (use --allow-tell)") if code == 5: # learned. sa_learning.learned += 1 self.logger.debug(__( "Learned {} (spamc return code {})".format(uid, code_orig))) elif code == 6: # already learned. self.logger.debug(__( "Already learned {} (spamc return code {})".format( uid, code_orig))) elif code == 98: # too big. self.logger.warning(__( "{} is too big (spamc return code {})".format( uid, code_orig))) else: raise isbg.ISBGError(-1, ("{}: Unknown return code {} from " + "spamc").format(uid, code_orig)) sa_learning.uids.append(int(uid)) if not self.dryrun: if self.learnthendestroy: if self.gmail: self.imap.uid("COPY", uid, "[Gmail]/Trash") else: self.imap.uid("STORE", uid, self.spamflagscmd, "(\\Deleted)") elif move_to is not None: self.imap.uid("COPY", uid, move_to) elif self.learnthenflag: self.imap.uid("STORE", uid, self.spamflagscmd, "(\\Flagged)") return sa_learning
def learn(self, folder, learn_type, move_to, origpastuids): """Learn the spams (and if requested deleted or move them). Args: folder (str): The IMAP folder. leart_type (str): ```spam``` to learn spam, ```ham``` to learn nonspam. move_to (str): If not ```None```, the imap folder where the emails will be moved. origpastuids (list(int)): ``uids`` to not process. Returns: Sa_Learn: It contains the information about the result of the process. It will call ``spamc`` to learn the emails. Raises: isbg.ISBGError: if learn_type is unknown. TODO: Add suport to ``learn_type=forget``. """ sa_learning = Sa_Learn() # Sanity checks: if learn_type not in ['spam', 'ham']: raise isbg.ISBGError(-1, message="Unknown learn_type") if self.imap is None: raise isbg.ISBGError(-1, message="Imap is required") self.logger.debug( __("Teach {} to SA from: {}".format(learn_type, folder))) self.imap.select(folder) if self.learnunflagged: _, uids = self.imap.uid("SEARCH", None, "UNFLAGGED") elif self.learnflagged: _, uids = self.imap.uid("SEARCH", None, "(FLAGGED)") else: _, uids = self.imap.uid("SEARCH", None, "ALL") uids, sa_learning.newpastuids = SpamAssassin.get_formated_uids( uids, origpastuids, self.partialrun) sa_learning.tolearn = len(uids) for uid in uids: mail = imaputils.get_message(self.imap, uid, logger=self.logger) # Unwrap spamassassin reports unwrapped = sa_unwrap.unwrap(mail) if unwrapped is not None: self.logger.debug( __("{} Unwrapped: {}".format( uid, utils.shorten(imaputils.mail_content(unwrapped[0]), 140)))) if unwrapped is not None and unwrapped: # len(unwrapped)>0 mail = unwrapped[0] if self.dryrun: self.logger.warning("Skipped learning due to dryrun!") continue else: code, code_orig = learn_mail(mail, learn_type) if code == -9999: # error processing email, try next. self.logger.exception(__( 'spamc error for mail {}'.format(uid))) self.logger.debug(repr(imaputils.mail_content(mail))) continue if code in [69, 74]: raise isbg.ISBGError( isbg.__exitcodes__['flags'], "spamassassin is misconfigured (use --allow-tell)") if code == 5: # learned. sa_learning.learned += 1 self.logger.debug( __("Learned {} (spamc return code {})".format( uid, code_orig))) elif code == 6: # already learned. self.logger.debug( __("Already learned {} (spamc return code {})".format( uid, code_orig))) elif code == 98: # too big. self.logger.warning( __("{} is too big (spamc return code {})".format( uid, code_orig))) else: raise isbg.ISBGError(-1, ("{}: Unknown return code {} from " + "spamc").format(uid, code_orig)) sa_learning.uids.append(int(uid)) if not self.dryrun: if self.learnthendestroy: if self.gmail: self.imap.uid("COPY", uid, "[Gmail]/Trash") else: self.imap.uid("STORE", uid, self.spamflagscmd, "(\\Deleted)") elif move_to is not None: self.imap.uid("COPY", uid, move_to) elif self.learnthenflag: self.imap.uid("STORE", uid, self.spamflagscmd, "(\\Flagged)") return sa_learning