mirror of
https://github.com/migatu/conjurer.git
synced 2026-07-14 21:38:38 +00:00
100 lines
3.0 KiB
Python
Executable File
100 lines
3.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import os
|
|
import re
|
|
import time
|
|
import json
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# — CONFIGURATION — adjust as needed —
|
|
APACHE_LOG = '/var/log/apache2/share_access.log' # <- point to your share-specific log!
|
|
SHARE_DIR = Path('/var/www/html/share')
|
|
METADATA_FILE = SHARE_DIR / '.downloads.json'
|
|
STATE_FILE = SHARE_DIR / '.logpos'
|
|
# Regex to match lines like: "GET /share/abcdef1234... HTTP/1.1"
|
|
TOKEN_REGEX = re.compile(r'"\s*GET\s+/share/([0-9a-f]{32})\s+HTTP/')
|
|
|
|
def debug(msg, args):
|
|
if args.debug:
|
|
print(f"[DEBUG] {msg}", file=sys.stderr)
|
|
|
|
def load_metadata(args):
|
|
if METADATA_FILE.exists():
|
|
data = json.loads(METADATA_FILE.read_text())
|
|
debug(f"Loaded metadata: {data}", args)
|
|
return data
|
|
return {}
|
|
|
|
def save_metadata(md, args):
|
|
METADATA_FILE.write_text(json.dumps(md))
|
|
debug(f"Saved metadata: {md}", args)
|
|
|
|
def load_state(args):
|
|
if STATE_FILE.exists():
|
|
pos = int(STATE_FILE.read_text())
|
|
debug(f"Loaded last log position: {pos}", args)
|
|
return pos
|
|
return 0
|
|
|
|
def save_state(pos, args):
|
|
STATE_FILE.write_text(str(pos))
|
|
debug(f"Saved new log position: {pos}", args)
|
|
|
|
def scan_log(since_pos, args):
|
|
try:
|
|
with open(APACHE_LOG, 'r') as f:
|
|
f.seek(since_pos)
|
|
lines = f.readlines()
|
|
newpos = f.tell()
|
|
debug(f"Read {len(lines)} new lines", args)
|
|
return lines, newpos
|
|
except Exception as e:
|
|
print(f"Error reading log: {e}", file=sys.stderr)
|
|
return [], since_pos
|
|
|
|
def record_downloads(lines, metadata, args):
|
|
now = time.time()
|
|
for line in lines:
|
|
m = TOKEN_REGEX.search(line)
|
|
if m:
|
|
tok = m.group(1)
|
|
if tok not in metadata:
|
|
metadata[tok] = now
|
|
debug(f"Recorded download of token {tok} at {now}", args)
|
|
return metadata
|
|
|
|
def revoke_old(metadata, args):
|
|
now = time.time()
|
|
to_remove = [tok for tok, ts in metadata.items() if now - ts >= 3600]
|
|
for tok in to_remove:
|
|
link = SHARE_DIR / tok
|
|
try:
|
|
link.unlink()
|
|
debug(f"Unlinked token {tok}", args)
|
|
except FileNotFoundError:
|
|
debug(f"Link {link} not found when revoking", args)
|
|
metadata.pop(tok, None)
|
|
return bool(to_remove)
|
|
|
|
def main():
|
|
p = argparse.ArgumentParser(description="Revoke share links 2min after download")
|
|
p.add_argument('--debug', action='store_true', help='print debug info to stderr')
|
|
args = p.parse_args()
|
|
|
|
SHARE_DIR.mkdir(parents=True, exist_ok=True)
|
|
metadata = load_metadata(args)
|
|
state = load_state(args)
|
|
|
|
lines, newpos = scan_log(state, args)
|
|
metadata = record_downloads(lines, metadata, args)
|
|
save_metadata(metadata, args) # always persist new downloads
|
|
|
|
if revoke_old(metadata, args):
|
|
save_metadata(metadata, args) # persist removals
|
|
|
|
save_state(newpos, args)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|