def close(self): try: self.connection.close() return True except: Logger.error(traceback.format_exc()) return False
def get_new_mail_ids(self): try: # 连接到POP3服务器: self.pop3_session = poplib.POP3(self.pop3_server) # 身份认证: self.pop3_session.user(self.r_account) self.pop3_session.pass_(self.r_password) # 获取邮件唯一编号 resp, mails, octets = self.pop3_session.uidl() # 获取邮件唯一编号和在服务器中的索引 mail_dict = dict() for mail_idx_id in mails: twice = mail_idx_id.split() mail_idx = int(twice[0]) mail_id = twice[1] mail_dict[mail_idx] = mail_id # 过虑已读邮件 for mail_idx, mail_id in mail_dict.items(): if self.recorder.exist(mail_id): del mail_dict[mail_idx] # 返回结果 return mail_dict except: Logger.error(traceback.format_exc()) return None
def __init__(self, file_name, stay_seconds): try: self.file_name = file_name self.stay_seconds = stay_seconds self.dict = dict() self.load_from_disk() self.update = False except: Logger.error(traceback.format_exc())
def put(self, a_key): try: if isinstance(a_key, str): a_key = a_key.decode('utf-8') self.dict[a_key] = time.time() self.update = True return True except: Logger.error(traceback.format_exc()) return False
def filter(self, mail_info): try: if not self.__is_leave(mail_info): return False if self.__is_discard(mail_info): return False return True except: Logger.error(traceback.format_exc()) return True
def load_from_disk(self): try: with open(self.file_name, 'ab+') as content_file: content_file.seek(0, 0) content = content_file.read() if content: self.dict = json.loads(content) return True except: Logger.error(traceback.format_exc()) return False
def put(self, a_key): try: language = 'INSERT OR REPLACE INTO mails(key, time) VALUES("%s", "%s")' % ( a_key, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) cursor = self.connection.execute(language) cursor.execute(language) self.connection.commit() return True except: Logger.error(traceback.format_exc()) return False
def flush_to_disk(self): try: if not self.update: return True self.update = False content = json.dumps(self.dict, ensure_ascii=False) with open(self.file_name, 'w') as content_file: content_file.write(content) return True except: Logger.error(traceback.format_exc()) return False
def load(self, xml_file): try: root = xml.etree.ElementTree.parse(xml_file).getroot() for child in root: if child.tag == 'leave': self.__parse(child, leave=True) if child.tag == 'discard': self.__parse(child, leave=False) return True except: Logger.error(traceback.format_exc()) return False
def clear_dead(self): try: now = time.time() for key, value in self.dict.items(): stay = now - value if stay > self.stay_seconds: del self.dict[key] self.update = True Logger.info('del self.dict[%s]' % key) return True except: Logger.error(traceback.format_exc()) return False
def guess_charset(cls, root): try: # 先从root对象获取编码: charset = root.get_charset() if charset is None: # 如果获取不到,再从Content-Type字段获取: content_type = root.get('Content-Type', '').lower() pos = content_type.find('charset=') if pos >= 0: charset = content_type[pos + 8:].strip() return charset except: Logger.error(traceback.format_exc()) return None
def handle(self): try: # 获取邮件唯一编号 mail_dict = self.get_new_mail_ids() # 依次处理所有邮件 for mail_idx, mail_id in mail_dict.items(): self.handle_one(mail_idx, mail_id) # recorder内存刷到磁盘 Logger.info("close sqlite") self.recorder.close() except: Logger.error(traceback.format_exc())
def __parse(self, e, leave): for item in e: rule = Rule(leave) for child in item: if child.tag == 'from': rule.from_address = child.text if child.tag == 'to': rule.to_address = child.text if child.tag == 'cc': rule.cc_address = child.text if child.tag == 'subject': rule.subject = child.text if child.tag == 'content': rule.content = child.text if leave: self.__leave.append(rule) else: self.__discard.append(rule) Logger.info(str(rule))
def handle_one(self, mail_idx, mail_id): try: Logger.info("get mail[%s]" % mail_idx) resp, lines, octets = self.pop3_session.retr(mail_idx) msg_content = '\r\n'.join(lines) root = Parser().parsestr(msg_content) # 记录邮件 self.recorder.put(mail_id) # 解析邮件 mail_info = self.parse_mail(root) if not mail_info: return # 过滤邮件 msg = "idx[{}], id[{}], from[{}], to[{}], cc[{}], subject[{}]". \ format(mail_idx, mail_id, mail_info.from_address, mail_info.to_address, mail_info.cc_address, mail_info.subject) if not self.filters.filter(mail_info): Logger.report("filter: " + msg) return # 发送邮件 Logger.report("send: " + msg) if self.send_flag: self.send_mime(root) else: Logger.report(mail_info.content) except: Logger.error(traceback.format_exc())
def loop_once(r_account, r_password, pop3_server, s_account, s_password, smpt_server, recorder_file, filter_file, send_flag, target_account, idx): """ :type r_account: str :type r_password: str :type pop3_server: str :type s_account: str :type s_password: str :type recorder_file: str :type filter_file: str :type smpt_server: str :type send_flag: bool :type target_account: str :type idx: int :return: """ try: # 打印标志 Logger.info("-----------------------%s--------------------------" % idx) # 初始化已读邮件记录 recorder = DBDict(recorder_file) # 初始化过滤策略 filters = Filter() if not filters.load(filter_file): Logger.error("init mail filter error") return False # 初始化邮件 proxy proxy = MailFilterProxy(r_account, r_password, pop3_server, s_account, s_password, smpt_server, recorder, filters, send_flag, target_account) proxy.handle() except: Logger.error(traceback.format_exc())
def send_mime(self, root): """ :type root: MIMEBase """ try: # 显示原始信息 for key in root.keys(): raw_header = 'old_key[%s] ====> [%s]' % (key, root.get(key)) Logger.info(raw_header) # 仅保留以下 Header left_header = ('from', 'boundary', 'content-type', 'mime-version', 'subject', 'date', 'message-id', 'content-transfer-encoding') for key in root.keys(): little = key.lower() if little not in left_header: del root[key] Logger.info("delete key[%s]" % key) root['to'] = self.target_account # 打印新key # for key in root.keys(): # raw_header = 'new_key[%s] ====> [%s]' % (key, root.get(key)) # Logger.info(raw_header) # 发送邮件 server = smtplib.SMTP(self.smpt_server, 25) server.login(self.s_account, self.s_password) server.sendmail(self.s_account, [self.target_account], root.as_string()) server.quit() except: Logger.error(traceback.format_exc())
def __init__(self, file_name): try: self.file_name = file_name self.connection = sqlite3.connect(file_name) except: Logger.error(traceback.format_exc())
def parse_mail(self, root): try: mail_info = MailInfo() # from value = root.get('From', '') values = self.parse_address(value) if values: for (header, address) in values: name = self.decode_str(header) mail_info.from_name.add(name) mail_info.from_address.add(address) # to value = root.get('To', '') values = self.parse_address(value) if values: for (header, address) in values: name = self.decode_str(header) mail_info.to_name.add(name) mail_info.to_address.add(address) # cc value = root.get('Cc', '') values = self.parse_address(value) if values: for (header, address) in values: name = self.decode_str(header) mail_info.cc_name.add(name) mail_info.cc_address.add(address) # subject value = root.get('Subject', '') if value: mail_info.subject = self.decode_str(value) # child MIMEMultipart if root.is_multipart(): parts = root.get_payload() for n, part in enumerate(parts): sub_mail_info = self.parse_mail(part) mail_info.child_mail.append(sub_mail_info) else: content_type = root.get_content_type() if content_type == 'text/plain' or content_type == 'text/html': # 纯文本或HTML内容: content = root.get_payload(decode=True) # 要检测文本编码: charset = self.guess_charset(root) if charset: content = content.decode(charset) mail_info.content = content # 调试用 # Logger.report(mail_info.content) else: # 不是文本,作为附件处理 pass return mail_info except: Logger.error(traceback.format_exc()) return None
def __main__(): # utf-8 reload(sys) sys.setdefaultencoding('utf-8') # 日志目录 log_target = "logs" # 邮件过滤策 filter_file = "config/filter.xml" # 收件账号 r_account = raw_input("receive mail account: ") # 收件密码 r_password = getpass.getpass('receive mail password: '******'send mail password: '******'receive mail password: '******'send mail password: '******'Y' and input_flag != 'N': print >> sys.stderr, "Invalid send_flag values, it must be Y or N" return False send_flag = True if input_flag == 'Y' else False print >> sys.stdout, "program is running in background, please check the running log!" # 初始化日志 Logger.init(LogEnv.develop, log_target, "result", max_file_count=10) Logger.info("program is starting......") # 非调试状态时在后台启动 try: pid = os.fork() if pid > 0: Logger.info("#1 parent exit") os._exit(0) except: Logger.error(traceback.format_exc()) try: pid = os.fork() if pid > 0: Logger.info("#2 parent exit") Logger.info("pid[%s] is running..." % pid) os._exit(0) except: Logger.error(traceback.format_exc()) # 邮件记录文件 self_dir = os.path.dirname(os.path.abspath(__file__)) recorder_dir = os.path.join(self_dir, 'data') if not os.path.exists(recorder_dir): os.mkdir(recorder_dir) recorder_file = os.path.join(self_dir, 'data', 'transfer.db') # 循环接收邮件 idx = 0 while True: try: loop_once(r_account, r_password, pop3_server, s_account, s_password, smpt_server, recorder_file, filter_file, send_flag, target_account, idx) idx += 1 # 非调试状态时循环转发邮件 time.sleep(60) # 调试用 # break except: Logger.error(traceback.format_exc())
def __del__(self): try: self.clear_dead() self.flush_to_disk() except: Logger.error(traceback.format_exc())