Updated to support base64 email attachments.

This commit is contained in:
Discsearcher
2026-09-02 00:19:24 -04:00
parent 80927a1400
commit 6bc52c6040
+97 -1
View File
@@ -7,7 +7,7 @@ import os
import json
from pathlib import Path
from datetime import datetime
from flask import Flask, render_template, request, jsonify, send_file, session, redirect, url_for
from flask import Flask, render_template, request, jsonify, send_file, session, redirect, url_for, make_response
from werkzeug.security import generate_password_hash, check_password_hash
import markdown
import logging
@@ -238,6 +238,45 @@ def rewrite_image_paths(md_content, filepath):
md_content = re.sub(r'!\[([^\]]*)\]\(([^\)]+)\)', replace_image_path, md_content)
return md_content
def rewrite_eml_file_links(md_content, filepath):
"""Rewrite relative .eml file links to be served by Flask's /file/ route."""
# Get the directory of the current file
file_dir = os.path.dirname(filepath)
# Pattern to match markdown link syntax: [text](path)
def replace_link_path(match):
link_text = match.group(1)
link_path = match.group(2)
# Only process .eml files
if not link_path.lower().endswith('.eml'):
return match.group(0)
# Skip absolute URLs
if link_path.startswith(('http://', 'https://', '/file/')):
return match.group(0)
# Resolve relative paths
if link_path.startswith('/'):
# Absolute path from content root
resolved_path = link_path.lstrip('/')
else:
# Relative path - resolve it relative to the file's directory
if file_dir:
resolved_path = os.path.normpath(os.path.join(file_dir, link_path))
else:
resolved_path = link_path
# Ensure forward slashes for URL
resolved_path = resolved_path.replace(os.sep, '/')
file_url = f"/file/{resolved_path}"
return f"[{link_text}]({file_url})"
# Replace all markdown link references to .eml files
md_content = re.sub(r'\[([^\]]+)\]\(([^\)]+\.eml)\)', replace_link_path, md_content)
return md_content
def build_directory_tree():
"""Build a hierarchical tree of markdown files organized by directory."""
tree = {}
@@ -370,6 +409,60 @@ def serve_image(filepath):
logger.error(f"Error serving image {file_path}: {e}")
return f"Error serving image: {e}", 500
@app.route('/file/<path:filepath>')
def serve_file(filepath):
"""Serve files from content directory, with base64 decoding for .eml files if needed."""
# Security: prevent directory traversal
if '..' in filepath or filepath.startswith('/'):
return "Invalid path", 400
file_path = os.path.join(CONTENT_DIR, filepath)
# Ensure the file is within the content directory
try:
file_path = os.path.realpath(file_path)
content_path = os.path.realpath(CONTENT_DIR)
if not file_path.startswith(content_path):
return "Access denied", 403
except Exception:
return "Invalid path", 400
if not os.path.exists(file_path):
logger.warning(f"File not found: {file_path}")
return "File not found", 404
try:
# Check if this is an .eml file
if file_path.lower().endswith('.eml'):
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# Check if content is base64 encoded
# Base64 content is ASCII and contains only base64 characters
is_base64 = False
try:
if content.strip() and all(c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r' for c in content):
decoded = base64.b64decode(content, validate=True).decode('utf-8', errors='ignore')
# If it looks like an email (has headers), use decoded version
if 'From:' in decoded or 'To:' in decoded or 'Subject:' in decoded:
content = decoded
is_base64 = True
except Exception:
# Not valid base64, use original content
pass
# Return as text/plain for viewing in browser
response = make_response(content)
response.headers['Content-Type'] = 'text/plain; charset=utf-8'
response.headers['Content-Disposition'] = f'inline; filename="{os.path.basename(file_path)}"'
return response
else:
# For other file types, just serve as-is
return send_file(file_path)
except Exception as e:
logger.error(f"Error serving file {file_path}: {e}")
return f"Error serving file: {e}", 500
@app.route('/api/search', methods=['GET'])
def search():
"""Search markdown files."""
@@ -462,6 +555,9 @@ def view_file(filepath):
# Rewrite image paths to be served by Flask
md_content = rewrite_image_paths(md_content, filepath)
# Rewrite .eml file links to be served by Flask with base64 decoding
md_content = rewrite_eml_file_links(md_content, filepath)
# Normalize list indentation for proper markdown parsing
md_content = normalize_list_indentation(md_content)