家庭服务器IPV6搭建无限邮箱系统指南

qq邮箱操作
// 邮箱配置信息
// 注意:使用QQ邮箱需要先开启IMAP服务并获取授权码
// 设置方法:登录QQ邮箱 -> 设置 -> 账户 -> 开启IMAP/SMTP服务 -> 生成授权码

服务器操作
fetchmail 同步QQ邮箱
nginx搭建web显示本地同步过来的邮箱

ssh端浏览邮箱
通过python脚本实现其他用户登录可浏览邮件

腾讯云
dns转给cloudflare

cloudflare
信件全局转发到QQ
AAAA解析到物理机IPV6

演示站点 fengche.site
博客 xoxome.online

下面是ssh端服务器脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import email
import email.header
import email.utils
import datetime
import subprocess
import tempfile

# 邮件目录
MAIL_DIR = "/home/y/Maildir/INBOX/new/new"

def clear_screen():
    """清屏"""
    os.system("clear")

def decode_header(header):
    """解码邮件头部信息"""
    if not header:
        return "未知"
    
    decoded_header = email.header.decode_header(header)
    result = ""
    for text, charset in decoded_header:
        if isinstance(text, bytes):
            try:
                if charset:
                    result += text.decode(charset)
                else:
                    result += text.decode("utf-8", "replace")
            except:
                result += text.decode("utf-8", "replace")
        else:
            result += text
    return result

def get_email_info(mail_file):
    """获取邮件信息"""
    with open(mail_file, "rb") as f:
        msg = email.message_from_binary_file(f)
    
    from_addr = decode_header(msg.get("From", "未知"))
    to_addr = decode_header(msg.get("To", "未知"))
    subject = decode_header(msg.get("Subject", "未知"))
    date_str = msg.get("Date", "")
    
    try:
        date = email.utils.parsedate_to_datetime(date_str)
        date_formatted = date.strftime("%Y-%m-%d %H:%M:%S")
    except:
        date_formatted = date_str
    
    return {
        "from": from_addr,
        "to": to_addr,
        "subject": subject,
        "date": date_formatted
    }

def html_to_text(html_content):
    """将HTML转换为可读文本"""
    # 使用临时文件保存HTML内容
    with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as f:
        f.write(html_content.encode('utf-8'))
        temp_filename = f.name
    
    try:
        # 尝试使用w3m将HTML转换为文本 
        try:
            result = subprocess.run(['w3m', '-dump', temp_filename], 
                                   capture_output=True, text=True, check=True)
            text = result.stdout
        except (subprocess.SubprocessError, FileNotFoundError):
            # 如果w3m不可用,尝试使用lynx
            try:
                result = subprocess.run(['lynx', '-dump', '-force_html', temp_filename],
                                       capture_output=True, text=True, check=True)
                text = result.stdout
            except (subprocess.SubprocessError, FileNotFoundError):
                # 如果lynx也不可用,使用简单的HTML标签移除
                text = html_content
                # 移除常见HTML标签
                tags_to_remove = ['', '', '', '', '', '',
                                 '', '']
                for tag in tags_to_remove:
                    text = text.replace(tag, '')
                # 将

替换为换行符 text = text.replace('
', '\n').replace('

', '\n').replace('

', '\n') # 移除其他HTML标签 in_tag = False result = "" for char in text: if char == '<': in_tag = True elif char == '>': in_tag = False elif not in_tag: result += char text = result return text finally: # 清理临时文件 try: os.unlink(temp_filename) except: pass def extract_email_content(mail_file): """提取邮件内容""" with open(mail_file, "rb") as f: msg = email.message_from_binary_file(f) content = "" html_content = "" if msg.is_multipart(): for part in msg.walk(): content_type = part.get_content_type() content_disposition = part.get("Content-Disposition", "") # 忽略附件 if "attachment" in content_disposition: continue # 获取文本内容 if content_type == "text/plain" and not content: payload = part.get_payload(decode=True) if payload: charset = part.get_content_charset() try: if charset: content += payload.decode(charset) else: content += payload.decode("utf-8", "replace") except: content += payload.decode("utf-8", "replace") # 获取HTML内容 elif content_type == "text/html" and not html_content: payload = part.get_payload(decode=True) if payload: charset = part.get_content_charset() try: if charset: html_content += payload.decode(charset) else: html_content += payload.decode("utf-8", "replace") except: html_content += payload.decode("utf-8", "replace") else: # 非多部分邮件 content_type = msg.get_content_type() payload = msg.get_payload(decode=True) if payload: charset = msg.get_content_charset() try: decoded = payload.decode(charset if charset else "utf-8", "replace") if content_type == "text/plain": content = decoded elif content_type == "text/html": html_content = decoded except: content = payload.decode("utf-8", "replace") # 如果有HTML内容但没有纯文本内容,转换HTML为文本 if html_content and not content: content = html_to_text(html_content) # 如果没有任何内容 if not content and not html_content: content = "【无法解析的邮件内容】" return content, html_content def list_emails(): """列出邮件""" clear_screen() print("欢迎使用邮件查看系统") print("=======================") print() if not os.path.isdir(MAIL_DIR): print("邮件目录不存在: " + MAIL_DIR) input("按Enter键退出...") sys.exit(1) mail_files = [] try: # 获取所有邮件文件,按修改时间排序 mail_files = sorted([f for f in os.listdir(MAIL_DIR) if os.path.isfile(os.path.join(MAIL_DIR, f))], key=lambda x: os.path.getmtime(os.path.join(MAIL_DIR, x)), reverse=True) except Exception as e: print("读取邮件目录出错: " + str(e)) input("按Enter键退出...") sys.exit(1) if not mail_files: print("没有新邮件") print("按Enter键同步邮件,按q退出:") choice = input().strip() if choice.lower() != "q": sync_mail() return list_emails() # 重新加载邮件列表 else: sys.exit(0) print("找到 " + str(len(mail_files)) + " 封新邮件:") print() # 显示最多5封邮件 displayed_files = [] for i, mail_file in enumerate(mail_files[:5]): full_path = os.path.join(MAIL_DIR, mail_file) try: info = get_email_info(full_path) displayed_files.append(mail_file) print(str(i+1) + ") " + mail_file) print(" 从: " + info["from"]) print(" 主题: " + info["subject"]) print(" 日期: " + info["date"]) print() except Exception as e: print("读取邮件 " + mail_file + " 出错: " + str(e)) print() return displayed_files def sync_mail(): """同步邮件""" clear_screen() print("正在使用y用户权限同步邮件...") try: # 使用sudo以y用户身份运行fetchmail result = subprocess.run(['sudo', '-u', 'y', 'fetchmail', '-v'], capture_output=True, text=True) output = result.stdout error = result.stderr if output: print(output) if error: print() print() # 检查是否成功同步了新邮件 if "reading message" in (output or "") or "messages" in (output or ""): print("成功同步了新邮件!") else: print("没有新邮件或同步失败。") except Exception as e: print("同步邮件出错: " + str(e)) print() input("按Enter键继续...") def view_email(mail_file): """查看邮件内容""" clear_screen() full_path = os.path.join(MAIL_DIR, mail_file) try: # 获取邮件信息 info = get_email_info(full_path) print("邮件: " + mail_file) print("=======================") print("从: " + info["from"]) print("收件人: " + info["to"]) print("主题: " + info["subject"]) print("日期: " + info["date"]) print("=======================") print() # 提取邮件内容 text_content, html_content = extract_email_content(full_path) if html_content: print("邮件内容 (转换自HTML):") print("=======================") print(text_content) print("=======================") print() print("选项: 1) 返回邮件列表 2) 查看原始HTML 3) 同步邮件 [1-3]:") view_choice = input().strip() if view_choice == "2": # 使用临时文件显示HTML with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as f: f.write(html_content.encode('utf-8')) temp_filename = f.name try: # 尝试使用不同的HTML查看器 browsers = [ ['w3m', temp_filename], ['lynx', temp_filename], ['less', temp_filename] ] for browser in browsers: try: subprocess.run(browser) break except (subprocess.SubprocessError, FileNotFoundError): continue finally: os.unlink(temp_filename) elif view_choice == "3": sync_mail() else: print("邮件内容:") print("=======================") print(text_content) print("=======================") print() print("选项: 1) 返回邮件列表 2) 同步邮件 [1-2]:") view_choice = input().strip() if view_choice == "2": sync_mail() return True except Exception as e: print("查看邮件出错: " + str(e)) print() input("按Enter键返回...") return False def main(): """主函数""" while True: displayed_files = list_emails() print("输入邮件编号查看内容,按Enter查看最新邮件,按s同步邮件,按q退出:") choice = input().strip() if choice.lower() == "q": print("谢谢使用,再见!") break elif choice.lower() == "s": sync_mail() continue mail_to_view = None if not choice or choice == "1": # 查看最新邮件 if displayed_files: mail_to_view = displayed_files[0] elif choice.isdigit(): # 查看选定邮件 idx = int(choice) - 1 if 0 <= idx < len(displayed_files): mail_to_view = displayed_files[idx] else: print("无效选择!") input("按Enter键继续...") continue else: print("无效选择!") input("按Enter键继续...") continue if mail_to_view: view_email(mail_to_view) if __name__ == "__main__": # 检查并安装必要的HTML渲染工具 try: for pkg in ['w3m', 'lynx']: try: subprocess.run(['which', pkg], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) break except: pass except: pass try: main() except KeyboardInterrupt: print("\n程序已退出") sys.exit(0) except Exception as e: print("程序发生错误: " + str(e)) input("按Enter键退出...") sys.exit(1)

你可能感兴趣的:(服务器,python,运维)