#!/usr/bin/env python3
"""
TextMe Token Capture Script for mitmproxy

This script intercepts TextMe mobile app traffic to capture JWT tokens
and discover API endpoints.

Usage:
    mitmproxy -s capture-token.py
    
Then configure your phone to use the proxy (usually your computer's IP:8080)
and install mitmproxy's CA certificate on your device.

Captured tokens are saved to ~/.textme/captured-token.json
"""

import json
import os
import re
from datetime import datetime
from pathlib import Path
from mitmproxy import http, ctx

# Domains to intercept
TEXTME_DOMAINS = [
    "api.textme-app.com",
    "textme-app.com",
    "textmeup.com",
    "go-text.me",
]

# Storage path
TOKEN_PATH = Path.home() / ".textme" / "captured-token.json"
ENDPOINTS_PATH = Path.home() / ".textme" / "discovered-endpoints.json"

# Track discovered endpoints
discovered_endpoints = set()


def save_token(token: str, source: str):
    """Save captured token to file."""
    TOKEN_PATH.parent.mkdir(parents=True, exist_ok=True)
    
    data = {
        "token": token,
        "captured_at": datetime.now().isoformat(),
        "source": source,
    }
    
    TOKEN_PATH.write_text(json.dumps(data, indent=2))
    ctx.log.info(f"🎉 TOKEN CAPTURED! Saved to {TOKEN_PATH}")
    ctx.log.info(f"Token preview: {token[:50]}...")


def save_endpoints():
    """Save discovered endpoints to file."""
    ENDPOINTS_PATH.parent.mkdir(parents=True, exist_ok=True)
    
    data = {
        "discovered_at": datetime.now().isoformat(),
        "endpoints": sorted(list(discovered_endpoints)),
    }
    
    ENDPOINTS_PATH.write_text(json.dumps(data, indent=2))


def request(flow: http.HTTPFlow) -> None:
    """Intercept requests to TextMe domains."""
    host = flow.request.host
    
    # Check if this is a TextMe domain
    if not any(domain in host for domain in TEXTME_DOMAINS):
        return
    
    # Log the endpoint
    method = flow.request.method
    path = flow.request.path
    endpoint = f"{method} {path.split('?')[0]}"
    
    if endpoint not in discovered_endpoints:
        discovered_endpoints.add(endpoint)
        ctx.log.info(f"📍 New endpoint: {endpoint}")
        save_endpoints()
    
    # Check for JWT in Authorization header
    auth_header = flow.request.headers.get("Authorization", "")
    
    if auth_header.startswith("JWT "):
        token = auth_header[4:]
        save_token(token, f"Request to {host}{path}")
    
    elif auth_header.startswith("Bearer "):
        token = auth_header[7:]
        save_token(token, f"Request to {host}{path}")
    
    # Log request details
    ctx.log.info(f"→ {method} https://{host}{path}")
    
    # Log request body for POST/PUT
    if method in ["POST", "PUT", "PATCH"] and flow.request.content:
        try:
            body = flow.request.content.decode('utf-8')
            if len(body) < 500:
                ctx.log.info(f"  Body: {body}")
        except:
            pass


def response(flow: http.HTTPFlow) -> None:
    """Intercept responses from TextMe domains."""
    host = flow.request.host
    
    # Check if this is a TextMe domain
    if not any(domain in host for domain in TEXTME_DOMAINS):
        return
    
    # Log response
    status = flow.response.status_code
    ctx.log.info(f"← {status} https://{host}{flow.request.path}")
    
    # Check for token in response body
    if flow.response.content:
        try:
            body = flow.response.content.decode('utf-8')
            
            # Try to parse as JSON
            try:
                data = json.loads(body)
                
                # Look for token fields
                for key in ["token", "access", "access_token", "jwt", "auth_token"]:
                    if key in data:
                        save_token(data[key], f"Response from {host}{flow.request.path}")
                        break
                
                # Log interesting response data
                if len(body) < 1000:
                    ctx.log.info(f"  Response: {body}")
                    
            except json.JSONDecodeError:
                pass
                
        except:
            pass


def done():
    """Called when mitmproxy shuts down."""
    ctx.log.info(f"\n{'='*50}")
    ctx.log.info("TextMe Token Capture Summary")
    ctx.log.info(f"{'='*50}")
    ctx.log.info(f"Discovered {len(discovered_endpoints)} endpoints")
    
    if TOKEN_PATH.exists():
        ctx.log.info(f"✅ Token saved to: {TOKEN_PATH}")
    else:
        ctx.log.info("❌ No token captured")
    
    ctx.log.info(f"Endpoints saved to: {ENDPOINTS_PATH}")
