def check_outlook_message_id(self, msg, target=None): message_id = msg.msg.get("Message-ID") if not message_id: return msg_regex = Regex(r"^<[0-9a-f]{4}([0-9a-f]{8})\$[0-9a-f]{8}\$[" r"0-9a-f]{8}\@") regex = msg_regex.search(message_id) if not regex: return False timetocken = int(regex.group(1), 16) date = msg.msg.get("Date") x = 0.0023283064365387 y = 27111902.8329849 mail_date = time.mktime(email.utils.parsedate(date)) expected = int((mail_date * x) + y) if abs(timetocken - expected) < 250: return False received = msg.msg.get("Received") received_regex = Regex(r"(\s.?\d+ \S\S\S \d+ \d+:\d+:\d+ \S+).*?$") regex = received_regex.search(received) received_date = 0 if regex: received_date = time.mktime(email.utils.parsedate(regex.group())) expected = int((received_date * x) + y) return abs(timetocken - expected) >= 250
def check_for_forged_yahoo_received_headers(self, msg, target=None): """Check for forged yahoo received headers""" from_addr = ''.join(msg.get_all_addr_header("From")) rcvd = ''.join(msg.get_decoded_header("Received")) if "yahoo.com" not in from_addr: return False if (msg.get_decoded_header("Resent-From") and msg.get_decoded_header("Resent-To")): xrcvd = ''.join(msg.get_decoded_header("X-Received")) rcvd = xrcvd if xrcvd else rcvd if self.gated_through_received_hdr_remover(msg): return False for relay in msg.untrusted_relays + msg.trusted_relays: rdns = relay.get("rdns") if rdns and "yahoo.com" in rdns: return False if Regex(r"by web\S+\.mail\S*\.yahoo\.com via HTTP").search(rcvd): return False if Regex(r"by smtp\S+\.yahoo\.com with SMTP").search(rcvd): return False yahoo_ip_re = Regex( r"from\s+\[{}\]\s+by\s+\S+\." r"(?:groups|scd|dcn)\.yahoo\.com\s+with\s+NNFMP".format( IP_ADDRESS.pattern), re.X) if yahoo_ip_re.search(rcvd): return False if (Regex(r"\bmailer\d+\.bulk\.scd\.yahoo\.com\b").search(rcvd) and from_addr.rsplit("@", 1)[-1] == "reply.yahoo.com"): return False if Regex("by \w+\.\w+\.yahoo\.com \(\d+\.\d+\.\d+\/\d+\.\d+\.\d+\)" "(?: with ESMTP)? id \w+").search(rcvd): return False return True
def check_for_to_in_subject(self, msg, test, target=None): """ Check if to address is in Subject field. If it is called with 'address', check if full address is in subject, else if the parameter is 'user', then check if user name is in subject. """ full_to = msg.get_all_addr_header('To') if not full_to: return False subject = msg.msg.get('Subject', "") for to in full_to: if test == "address": subject_regex = Regex(r".*" + re.escape(to) + r".*", re.I) if subject_regex.search(subject): return True elif test == "user": regex = re.match("(\S+)@.*", to) if regex: to = regex.group(1) if Regex(r"^" + re.escape(to) + "$").search(subject): return True if Regex(r"(?:re|fw):\s*(?:\w+\s+)?" + re.escape(to) + "$")\ .search(subject): return True if Regex(r"\s*" + re.escape(to) + "[,:;!?-]$")\ .search(subject): return True if Regex(r"^" + re.escape(to) + "\s*[,:;!?-](\s).*")\ .search(subject): return True return False
def check_for_msn_groups_headers(self, msg, target=None): """Check if the email's destination is a msn group""" to = ''.join(msg.get_decoded_header('To')) if not Regex(r"<(\S+)\@groups\.msn\.com>").search(to): return False listname = Regex(r"<(\S+)\@groups\.msn\.com>").match(to).groups()[0] server_rgx = Regex(r"from mail pickup service by " r"((?:p\d\d\.)groups\.msn\.com)\b") server = '' for rcvd in msg.get_decoded_header('Received'): if server_rgx.search(rcvd): server = server_rgx.search(rcvd).groups()[0] break if not server: return False message_id = ''.join(msg.get_decoded_header('Message-Id')) if listname == "notifications": if not Regex(r"^<\S+\@{0}".format(server)).search(message_id): return False else: msn_addr = Regex(r"^<{0}-\S+\@groups\.msn\.com>".format(listname)) if not msn_addr.search(message_id): return False msn_addr = "{0}[email protected]".format(listname) if msg.sender_address != msn_addr: return False return True
def hostname_to_domain(hostname): if not Regex(r"[a-zA-Z]").match(hostname): return hostname parts = hostname.split(".") if len(parts) > 1 and Regex(r"(?:\S{3,}|ie|fr|de)").match(parts[-1]): return ".".join(parts[-2:]) elif len(parts) > 2: return ".".join(parts[-3:]) else: return hostname
def check_unresolved_template(self, msg, target=None): message = msg.raw_msg headers = message.split("\n") for header in headers: if Regex(r"%[A-Z][A-Z_-]").search(header) and not \ Regex(r"^(?:x-vms-to|x-uidl|x-face|to|cc|from|subject|" r"references|in-reply-to|(?:x-|resent-|" r"x-original-)?message-id):").search(header.lower()): return True return False
def check_for_fake_aol_relay_in_rcvd(self, msg, target=None): """Check for common AOL fake received header.""" for recv in msg.get_decoded_header("Received"): if not Regex(r" rly-[a-z][a-z]\d\d\.", re.I).search(recv): continue if Regex(r"\/AOL-\d+\.\d+\.\d+\)").search(recv): continue if Regex(r"ESMTP id (?:RELAY|MAILRELAY|MAILIN)").search(recv): continue return True return False
def check_ratware_name_id(self, msg, target=None): """Check if message-id is ratware or not.""" message_id = msg.msg.get("Message-Id") from_header = msg.msg.get("From") if not message_id and not from_header: return False regex = Regex(r"<[A-Z]{28}\.([^>]+?)>").search(message_id) if regex: if Regex(r"\"[^\"]+\"\s*<" + regex.group(1) + ">").search( from_header): return True return False
def check_messageid_not_usable(self, msg, target=None): list_unsubscribe = msg.msg.get("List-Unsubscribe") if list_unsubscribe: if Regex(r"<mailto:(?:leave-\S+|\S+-unsubscribe)\@\S+>$").search( list_unsubscribe): return True if self.gated_through_received_hdr_remover(msg): return True received = msg.msg.get("Received") if Regex(r"/CWT/DCE\)").search(received): return True if Regex(r"iPlanet Messaging Server").search(received): return True return False
def _get_authors(self, msg): self.author_addresses = msg.get_addr_header("From") for header in self.author_addresses: match_domain = Regex("@([^@]+?)[ \t]*$").search(header) if match_domain: domain = match_domain.group(1) self.author_domains.append(domain.encode())
def _update_mime_text_info(self, msg, payload, part, text): charset = part.get_charset() text_count = self.get_local(msg, "mime_body_text_count") self.set_local(msg, "mime_body_text_count", text_count + 1) if part.get_content_subtype() == "plain": plain_characters_count = self.get_local(msg, "plain_characters_count") self.set_local(msg, "plain_characters_count", plain_characters_count + len(text)) ascii_count = self.get_local(msg, "ascii_count") ascii_count += len(text) self.set_local(msg, "ascii_count", ascii_count) unicode_chars = Regex(r"(&\#x[0-9A-F]{4};)", re.X).search(text) unicode_count = 0 if unicode_chars: unicode_count = self.get_local(msg, "unicode_count") unicode_count += len(unicode_chars.groups()) self.set_local(msg, "unicode_count", unicode_count) # XXX This does not work properly anymore if not charset or charset == r"us-ascii": try: payload.encode("ascii") except (UnicodeEncodeError, UnicodeDecodeError): self.set_local(msg, "mime_ascii_text_illegal", True) if len(re.split("--", msg.raw_msg)) <= 4: self.set_local(msg, "mime_missing_boundary", True)
def _check_rbl(self, msg, rbl_server, qtype="A", subtest=None): """Checks all the IPs of this message on the specified list. :param msg: The message that we perform the check on. :param rbl_server: The RBL list to check :param qtype: The DNS record type to check :param subtest: If specified then an additional check is done on the result of the DNS lookup by matching this regular expression against the result. :return: True if there is a match and the subtest passes and False otherwise. """ if self.ctxt.skip_rbl_checks: return False if subtest is not None: try: subtest = Regex(subtest) except re.error as e: self.ctxt.err("Invalid regex %s: %s", subtest, e) return False for ip in msg.get_untrusted_ips(): rev = self.ctxt.dns.reverse_ip(ip) results = self.ctxt.dns.query("%s.%s" % (rev, rbl_server), qtype) if results and not subtest: return True for result in results: if subtest.match(str(result)): return True return False
def gated_through_received_hdr_remover(self, msg, target=None): """Check if the email is gated through ezmlm""" txt = ''.join(msg.get_decoded_header("Mailing-List")) rcvd = ''.join(msg.get_decoded_header("Received")) if Regex(r"^contact \S+\@\S+\; run by ezmlm$").search(txt): dlto = ''.join(msg.get_decoded_header("Delivered-To")) mailing_list_re = Regex(r"^mailing list \S+\@\S+") qmail_re = Regex(r"qmail \d+ invoked (?:from " r"network|by .{3,20})\); \d+ ... \d+") if mailing_list_re.search(dlto) and qmail_re.search(rcvd): return True if not rcvd: return True if Regex(r"from groups\.msn\.com \(\S+\.msn\.com ").search(rcvd): return True return False
def check_address_in_list(self, addresses, list_name): """Check if addresses match the regexes from list_name. """ for address in addresses: for regex in self[list_name]: if Regex(regex).search(address): return True return False
def _check_for_forged_received(self, msg): mismatch_from = 0 mismatch_ip_helo = 0 hostname_re = Regex(r"^\w+(?:[\w.-]+\.)+\w+$") ip_re = Regex(r"^(\d+\.\d+)\.\d+\.\d+") for index, relay in enumerate(msg.untrusted_relays): from_ip = relay.get("ip") from_host = self.hostname_to_domain(relay.get("rdns")) by_host = self.hostname_to_domain(relay.get("by")) helo_host = self.hostname_to_domain(relay.get("helo")) if not by_host or not hostname_re.match(by_host): continue if from_host and from_ip == '127.0.0.1': from_host = "undef" self.ctxt.log.debug("eval: forged-HELO: from=%s helo=%s by=%s" % ( from_host if from_host else "(undef)", helo_host if helo_host else "(undef)", by_host if by_host else "(undef)" )) try: ip_netmask_16 = ipaddress.IPv4Network(from_ip).supernet(16) except ValueError: ip_netmask_16 = "" try: helo_netmask_16 = ipaddress.IPv4Network(helo_host).supernet(16) except ValueError: helo_netmask_16 = "" if ip_netmask_16 and helo_netmask_16 and from_ip != helo_host: if (ip_netmask_16 != helo_netmask_16 and not IP_PRIVATE.match(helo_host)): self.ctxt.log.debug("eval: forged-HELO: massive mismatch " "on IP-addr HELO: %s != %s" % (helo_host, from_ip)) mismatch_ip_helo += 1 prev = msg.untrusted_relays[index - 1] if prev and index > 0: prev_from_host = prev.get("rdns") if (hostname_re.match(prev_from_host) and by_host != prev_from_host and not self._helo_forgery_whitelisted(by_host, prev_from_host)): self.ctxt.log.debug("eval: forged-HELO: mismatch on from: " "%s != %s" % (prev_from_host, by_host)) mismatch_from += 1 self.set_global("mismatch_from", mismatch_from) self.set_global("mismatch_ip_helo", mismatch_ip_helo)
def check_ratware_envelope_from(self, msg, target=None): """Check if envelope-from address is ratware or not.""" to_header = msg.msg.get("To") envelope_from = msg.sender_address if not to_header or not envelope_from: return False if Regex(r"^SRS\d=").search(envelope_from): return False regex = Regex(r"^([^@]+)@(.+)$").search(to_header) if regex: user = regex.group(1) dom = regex.group(2) if not self.is_domain_valid(dom): return False if Regex(r"\b" + dom + "." + user + "@").search(envelope_from): return True return False
def check_found_forged(self, address, found_forged): """If it is forged, check the address in list """ if found_forged: wlist = self['parsed_whitelist_allow_relays'] for addr in wlist: if Regex(addr).search(address): found_forged = 0 break return found_forged
def check_freemail_header(self, msg, header, regex=None, target=None): """Check all possible 'from' headers to see if sender is freemail. It is possible to provide a regex rule to match against too. Returns True if it is or False otherwise """ self.ctxt.log.debug("FreeMail::Plugin check_freemail_header" " %s", 'with regex: ' + regex if regex else '') if not header: self.ctxt.log.warn("FreeMail::Plugin check_freemail_header" " requires an argument") return False if regex: try: check_re = Regex(regex).compile() except re.error: self.ctxt.log.warn("FreeMail::Plugin check_freemail_header" " regex error") return False else: check_re = None if not msg.msg.get(header, None): self.ctxt.log.debug( "FreeMail::Plugin check_freemail_header" " header: %s not found", header) return False header_emails = self.get_global('email_re').findall(msg.msg[header]) if not header_emails: self.ctxt.log.debug( "FreeMail::Plugin check_freemail_header" " no emails found in header: %s", header) return False for email in header_emails: if self._is_freemail(email): if check_re and not check_re.search(email): return False elif check_re and check_re.search(email): self.ctxt.log.debug( "FreeMail::Plugin check_freemail_header" " HIT! %s is freemail and matches regex", email) result = ("Header " + header + " is freemail and matches regex") if self["freemail_add_describe_email"]: _email = "(" + email.replace("@", "[at]") + ")" result = result + "\n\t" + _email return str(result) self.ctxt.log.debug( "FreeMail::Plugin check_freemail_header" " HIT! %s is freemail", email) result = "Header " + header + " is freemail" if self["freemail_add_describe_email"]: _email = "(" + email.replace("@", "[at]") + ")" result = result + "\n\t" + _email return str(result) return False
def check_freemail_from(self, msg, regex=None, target=None): """Check if in specified header gave as parameter is a freemail or no. It is possible to provide a regex rule to match against too. Returns True if it is or False otherwise """ self.ctxt.log.debug( "FreeMail::Plugin Eval rule check_freemail_from" " %s", 'with regex: ' + regex if regex else '') all_from_headers = [ 'From', 'Envelope-Sender', 'Resent-Sender', 'X-Envelope-From', 'EnvelopeFrom', 'Resent-From' ] header_emails = [] if regex: try: check_re = Regex(regex) except re.error: self.ctxt.log.warn("FreeMail::Plugin check_freemail_from" " regex error") return False else: check_re = None header_emails = msg.get_all_from_headers_addr() header_emails = sorted(set(header_emails)) if not header_emails: self.ctxt.log.debug( "FreeMail::Plugin check_freemail_from" " no emails found in from headers: %s", all_from_headers) return False for email in header_emails: if self._is_freemail(email): if check_re and not check_re.search(email): return False elif check_re and check_re.search(email): self.ctxt.log.debug( "FreeMail::Plugin check_freemail_from" " HIT! %s is freemail and matches regex", email) result = "Sender address is freemail and matches regex" if self["freemail_add_describe_email"]: _email = "(" + email.replace("@", "[at]") + ")" result = result + "\n\t" + _email return str(result) self.ctxt.log.debug( "FreeMail::Plugin check_freemail_from" " HIT! %s is freemail", email) result = "Sender address is freemail" if self["freemail_add_describe_email"]: _email = "(" + email.replace("@", "[at]") + ")" result = result + "\n\t" + _email return str(result) return False
def check_for_forged_juno_received_headers(self, msg, target=None): from_addr = ''.join(msg.get_all_addr_header("From")) if not from_addr.rsplit("@", 1)[-1].endswith("juno.com"): return False if self.gated_through_received_hdr_remover(msg): return False xorig = ''.join(msg.get_decoded_header("X-Originating-IP")) xmailer = ''.join(msg.get_decoded_header("X-Mailer")) rcvd = ''.join(msg.get_decoded_header("Received")) if xorig != "": juno_re = Regex(r"from.*\b(?:juno|untd)\.com.*" r"[\[\(]{0}[\]\)].*by".format(IP_ADDRESS.pattern), re.X) cookie_re = Regex(r" cookie\.(?:juno|untd)\.com ") if not juno_re.search(rcvd) and not cookie_re.search(rcvd): return True if "Juno " not in xmailer: return True else: mail_com_re = Regex(r"from.*\bmail\.com.*\[{}\].*by".format( IP_ADDRESS.pattern), re.X) untd_com_re = Regex(r"from\s+(webmail\S+\.untd" r"\.com)\s+\(\1\s+\[{}\]\)\s+by".format( IP_ADDRESS.pattern), re.X) if mail_com_re.search(rcvd) and not Regex(r"\bmail\.com").search( xmailer): return True elif untd_com_re.search(rcvd) and not Regex( r"^Webmail Version \d").search(xmailer): return True else: return True return False
def check_start(self, msg): """Verify that the domains are valid and separate wildcard domains from the rest.""" domain_re = Regex(r'^[a-z0-9.*?-]+$') freemail_domains = self.get_global('freemail_domains') freemail_temp_wc = [] for domain in freemail_domains[:]: if not domain_re.search(domain): freemail_domains.remove(domain) self.ctxt.log.warn( "FreeMail::Plugin Invalid freemail domain: %s", domain) if '*' in domain: temp = domain.replace('.', '\.') temp = temp.replace('?', '.') temp = temp.replace('*', '[^.]*') freemail_temp_wc.append(temp) if freemail_temp_wc: wild_doms = r'\@(?:{0})$'.format('|'.join(freemail_temp_wc)) self.set_global('freemail_domains_re', Regex(wild_doms)) self.set_global('freemail_domains', freemail_domains) valid_tlds = (self.get_global('util_rb_tld') + self.get_global('util_rb_2tld') + self.get_global('util_rb_3tld')) tlds_re = r'(?:{0})'.format("|".join(valid_tlds)) email_re = Regex( r""" (?=.{{0,64}}\@) # limit userpart to 64 chars (and speed up searching?) (?<![a-z0-9!#\$%&'*+\/=?^_`{{|}}~-]) # start boundary ( # capture email [a-z0-9!#\$%&'*+\/=?^_`{{|}}~-]+ # no dot in beginning (?:\.[a-z0-9!#\$%&'*+\/=?^_`{{|}}~-]+)* # no consecutive dots, no ending dot \@ (?:[a-z0-9](?:[a-z0-9-]{{0,59}}[a-z0-9])?\.){{1,4}} # max 4x61 char parts (should be enough?) {tld} # ends with valid tld ) (?!(?:[a-z0-9-]|\.[a-z0-9])) # make sure domain ends here """.format(tld=tlds_re), re.X | re.I) self.set_global('email_re', email_re) self.set_global('body_emails', set()) self.set_global("check_if_parsed", False)
def check_for_unique_subject_id(self, msg, target=None): """Check if in subject appears an unique id""" subject = "".join(msg.get_decoded_header("Subject")) id = None unique_id_re_list = [ r"[-_\.\s]{7,}([-a-z0-9]{4,})$", r"\s{10,}(?:\S\s)?(\S+)$", r"\s{3,}[-:\#\(\[]+([-a-z0-9]{4,})[\]\)]+$", r"\s{3,}[-:\#]([a-z0-9]{5,})$", r"[\s._]{3,}([^0\s._]\d{3,})$", r"[\s._]{3,}\[(\S+)\]$", # (7217vPhZ0-478TLdy5829qicU9-0@26) and similar r"\(([-\w]{7,}\@\d+)\)$", r"\b(\d{7,})\s*$", # stuff at end of line after "!" or "?" is usually an id r"[!\?]\s*(\d{4,}|\w+(-\w+)+)\s*$", # 9095IPZK7-095wsvp8715rJgY8-286-28 and similar # excluding 'Re:', etc and the first word r"(?:\w{2,3}:\s)?\w+\s+(\w{7,}-\w{7,}(-\w+)*)\s*$", # #30D7 and similar r"\s#\s*([a-f0-9]{4,})\s*$" ] for rgx in unique_id_re_list: match = Regex(rgx, re.I).search(subject) if match: id = match.group() break if not id: return False comercial_re = Regex(r"(?:item|invoice|order|number|confirmation)" r".{1,6}%s\s*$" % id, re.X | re.I) if Regex(r"\d{5,}").search(id) and comercial_re.search(subject): return False return True
def parse_config(self, key, value): """ Parse a config line, instead of using the regular `set_?_option` we need to use set_append_option because we need to append the setting to the current existing one instead of adding one more. """ # Need to check if the option is a valid regular expression. if key in self.options: try: Regex(value.strip()).compile() except re.error: return self.set_append_option(key, value) self.inhibit_further_callbacks()
def _check_for_forged_hotmail_received_headers(self, msg): self.hotmail_addr_but_no_hotmail_received = 0 self.hotmail_addr_with_forged_hotmail_received = 0 rcvd = msg.msg.get("Received") if not rcvd: return False pickup_service_regex = Regex(r"from mail pickup service by hotmail" r"\.com with Microsoft SMTPSVC;") if pickup_service_regex.search(rcvd): return False if self.check_for_msn_groups_headers(msg): return False ip_header = msg.msg.get("X-ORIGINATING-IP") if ip_header and IP_ADDRESS.search(ip_header): FORGED_REGEX = Regex( r"from\s+(?:\S*\.)?hotmail.com\s+\(\S+\.hotmail(" r"?:\.msn)?\.com[\)]|" r"from\s+\S*\.hotmail\.com\s+\(\[{IP_ADDRESS}\]|" r"from\s+\S+\s+by\s+\S+\.hotmail(?:\.msn)?\.com\s+with\s+ " r"HTTP\;|" r"from\s+\[66\.218.\S+\]\s+by\s+\S+\.yahoo\.com" r"".format(IP_ADDRESS=IP_ADDRESS.pattern), re.I | re.X) if FORGED_REGEX.search(rcvd): return False if self.gated_through_received_hdr_remover(msg): return False helo_hotmail_regex = Regex(r"(?:from |HELO |helo=)\S*hotmail\.com\b") if helo_hotmail_regex.search(rcvd): self.hotmail_addr_with_forged_hotmail_received = 1 else: from_address = msg.msg.get("From") if not from_address: from_address = "" if "hotmail.com" not in from_address: return False self.hotmail_addr_but_no_hotmail_received = 1
def check_for_forged_eudoramail_received_headers(self, msg, target=None): """Check if the email has forged eudoramail received header""" from_addr = ''.join(msg.get_all_addr_header("From")) if from_addr.rsplit("@", 1)[-1] != "eudoramail.com": return False rcvd = ''.join(msg.get_decoded_header("Received")) ip = ''.join(msg.get_decoded_header("X-Sender-Ip")) if ip and IP_ADDRESS.search(ip): ip = True else: ip = False if self.gated_through_received_hdr_remover(msg): return False if Regex(r"by \S*whowhere.com\;").search(rcvd) and ip: return False return True
def check_for_forged_gw05_received_headers(self, msg, target=None): gw05_re = Regex(r"from\s(\S+)\sby\s(\S+)\swith\sESMTP\;\s+\S\S\S," r"\s+\d+\s+\S\S\S\s+\d{4}\s+\d\d:\d\d:\d\d\s+[-+]*" r"\d{4}", re.X | re.I) for rcv in msg.get_decoded_header("Received"): h1 = "" h2 = "" try: match = gw05_re.match(rcv) if match: h1, h2 = match.groups() if h1 and h2 and h2 != ".": return True except IndexError: continue return False
def check_freemail_body(self, msg, regex=None, target=None): """ Check if there are free emails in body parts of the message """ self.ctxt.log.debug("FreeMail::Plugin check_freemail_body" " %s", 'with regex: ' + regex if regex else '') body_emails = self.get_global('body_emails') if not len(body_emails): self.ctxt.log.debug("FreeMail::Plugin check_freemail_body " "No emails found in body of the message") return False if regex: try: check_re = Regex(regex).compile() except re.error: self.ctxt.log.warn("FreeMail::Plugin check_freemail_from" " regex error") return False else: check_re = None if not self._parse_body(): return False if check_re: for email in self.get_global("freemail_body_emails"): if check_re.search(email): self.ctxt.log.debug( "FreeMail::Plugin check_freemail_body" " HIT! %s is freemail and matches regex", email) result = "Address from body is freemail and matches regex" if self["freemail_add_describe_email"]: _email = "(" + email.replace("@", "[at]") + ")" result = result + "\n\t" + _email return str(result) else: if len(self.get_global("freemail_body_emails")): emails = " ,".join(self.get_global("freemail_body_emails")) self.ctxt.log.debug( "FreeMail::Plugin check_freemail_body" " HIT! body has freemails: %s", emails) result = "Body has freemails" if self["freemail_add_describe_email"]: _emails = "(" + emails.replace("@", "[at]") + ")" result = result + "\n\t" + _emails return str(result) return False
def _update_base64_text_stats(self, msg, content_type, content_transfer_encoding, content_disposition, charset): text_charset_re = Regex(r"(us-ascii|ansi_x3\.4-1968|iso-ir-6|" r"ansi_x3\.4-1986|iso_646\.irv:1991|" r"ascii|iso646-us|us|ibm367|cp367|" r"csascii)") charset_check = not charset or text_charset_re.search(charset) cdisposition_check = not (content_disposition and content_disposition.strip() in ("inline", "attachment")) if ("base64" in content_transfer_encoding and charset_check and cdisposition_check): self.set_local(msg, "mime_base64_encoded_text", True)
def check_in_list(self, msg, addresses, list_name): """Check if addresses match the regexes from list_name and modify "from_in_whitelist" msg value based on the list name """ param = "from_in_whitelist" for address in addresses: for regex in self[list_name]: if Regex(regex).search(address): self.set_local(msg, param, 1) return True wh = self.check_whitelist_rcvd(msg, "parsed_whitelist_from_rcvd", address) if wh == 1: self.set_local(msg, param, 1) return True elif wh == -1: self.set_local(msg, param, -1) return False
def _update_quopri_stats(self, msg, part): max_line_len = 79 qp_count = self.get_local(msg, "mime_qp_count") qp_bytes = self.get_local(msg, "qp_bytes") qp_chars = self.get_local(msg, "qp_chars") quoted_printables = Regex(r"=(?:09|3[0-9ABCEF]|[2456][0-9A-F]|7[" r"0-9A-E])").search(part.get_payload()) qp_bytes += len(part.get_payload()) self.set_local(msg, "qp_bytes", qp_bytes) if quoted_printables: qp_chars += len(quoted_printables.groups()) self.set_local(msg, "qp_chars", qp_chars) self.set_local(msg, "mime_qp_count", qp_count + 1) raw = msg.translate_line_breaks(part.as_string()) has_long_line = self.get_local(msg, "mime_qp_long_line") if not has_long_line: has_long_line = any( len("".join(line.split(":")[1:])) > max_line_len and not line.startswith("SPAM") for line in raw.splitlines()) self.set_local(msg, "mime_qp_long_line", has_long_line)