#!/bin/sh /etc/rc.common
# SPDX-License-Identifier: Apache-2.0
# CDN Cache Proxy Init Script - Squid Backend
# Copyright (C) 2025 CyberMind.fr

START=90
STOP=10
USE_PROCD=1

SQUID_CONF="/var/etc/cdn-cache-squid.conf"
CACHE_DIR="/var/cache/cdn-squid"
LOG_DIR="/var/log/cdn-cache"
SSL_DB="/var/lib/ssl_db"
CA_DIR="/etc/squid/ssl"
CA_CERT="$CA_DIR/ca.pem"
CA_KEY="$CA_DIR/ca.key"

validate_section() {
	uci_load_validate cdn-cache main "$1" "$2" \
		'enabled:bool:0' \
		'cache_dir:string:/var/cache/cdn-squid' \
		'cache_size:uinteger:2048' \
		'max_object_size:uinteger:1024' \
		'cache_valid:uinteger:10080' \
		'listen_port:port:3128' \
		'transparent:bool:1' \
		'ssl_bump:bool:0' \
		'log_level:string:1'
}

# Generate CA certificate for SSL bumping
generate_ca_cert() {
	mkdir -p "$CA_DIR"

	if [ ! -f "$CA_CERT" ] || [ ! -f "$CA_KEY" ]; then
		logger -t cdn-cache "Generating CA certificate for SSL bumping..."

		# Generate CA private key
		openssl genrsa -out "$CA_KEY" 4096 2>/dev/null

		# Generate self-signed CA certificate
		openssl req -new -x509 -days 3650 -key "$CA_KEY" -out "$CA_CERT" \
			-subj "/C=FR/ST=Paris/L=Paris/O=SecuBox/OU=CDN-Cache/CN=SecuBox CDN Cache CA" 2>/dev/null

		# Set proper permissions
		chmod 600 "$CA_KEY"
		chmod 644 "$CA_CERT"
		chown squid:squid "$CA_KEY" "$CA_CERT" 2>/dev/null

		logger -t cdn-cache "CA certificate generated at $CA_CERT"
	fi
}

# Initialize SSL certificate database
init_ssl_db() {
	if [ ! -d "$SSL_DB/certs" ]; then
		logger -t cdn-cache "Initializing SSL certificate database..."
		rm -rf "$SSL_DB"
		mkdir -p "$SSL_DB"
		# Note: Size must be in bytes, not MB
		/usr/lib/squid/security_file_certgen -s "$SSL_DB" -M 4194304 -c 2>/dev/null
		chown -R squid:squid "$SSL_DB" 2>/dev/null
	fi
}

generate_squid_config() {
	local cache_dir="$1"
	local cache_size="$2"
	local max_object="$3"
	local cache_valid="$4"
	local listen_port="$5"
	local transparent="$6"
	local ssl_bump="$7"

	mkdir -p "$(dirname $SQUID_CONF)"
	mkdir -p "$cache_dir"
	mkdir -p "$LOG_DIR"
	chown squid:squid "$cache_dir" "$LOG_DIR" 2>/dev/null

	# Build listen directives
	# Need both a regular port (for client configuration) and optionally intercept port
	local intercept_port=$((listen_port + 1))
	local ssl_bump_port=$((listen_port + 2))

	cat > "$SQUID_CONF" << EOF
# CDN Cache Squid Configuration
# Auto-generated by SecuBox - Do not edit manually

# === NETWORK ===
# Regular forward proxy port
http_port $listen_port
EOF

	if [ "$transparent" = "1" ]; then
		cat >> "$SQUID_CONF" << EOF
# Transparent proxy intercept port (HTTP)
http_port $intercept_port intercept
EOF
	fi

	# SSL Bump configuration for HTTPS caching
	if [ "$ssl_bump" = "1" ] && [ -f "$CA_CERT" ] && [ -f "$CA_KEY" ]; then
		cat >> "$SQUID_CONF" << EOF

# SSL Bump (HTTPS intercept) port
https_port $ssl_bump_port intercept ssl-bump \\
    cert=$CA_CERT \\
    key=$CA_KEY \\
    generate-host-certificates=on \\
    dynamic_cert_mem_cache_size=4MB

# SSL certificate database (size in bytes)
sslcrtd_program /usr/lib/squid/security_file_certgen -s $SSL_DB -M 4194304
sslcrtd_children 5

# SSL Bump ACLs
acl step1 at_step SslBump1
acl step2 at_step SslBump2
acl step3 at_step SslBump3

# Sites to NOT intercept (security sensitive)
acl ssl_exclude_domains ssl::server_name .paypal.com .stripe.com .braintreegateway.com
acl ssl_exclude_domains ssl::server_name .bank .banking .hsbc.com .chase.com .wellsfargo.com
acl ssl_exclude_domains ssl::server_name .google.com/accounts .accounts.google.com
acl ssl_exclude_domains ssl::server_name .apple.com/id .icloud.com
acl ssl_exclude_domains ssl::server_name .github.com .gitlab.com

# Sites to bump (cacheable content)
acl ssl_bump_domains ssl::server_name .windowsupdate.com .microsoft.com .officecdn.microsoft.com
acl ssl_bump_domains ssl::server_name .steamcontent.com .steampowered.com .steamcdn-a.akamaihd.net
acl ssl_bump_domains ssl::server_name .epicgames.com .epicgames-download1.akamaized.net
acl ssl_bump_domains ssl::server_name .download.nvidia.com .cdn.jsdelivr.net .cdnjs.cloudflare.com
acl ssl_bump_domains ssl::server_name .archive.ubuntu.com .security.ubuntu.com
acl ssl_bump_domains ssl::server_name .downloads.openwrt.org

# SSL bump rules
ssl_bump peek step1 all
ssl_bump splice ssl_exclude_domains
ssl_bump bump ssl_bump_domains
ssl_bump splice all
EOF
	fi

	cat >> "$SQUID_CONF" << EOF
visible_hostname cdn-cache.secubox.local

# === ACCESS CONTROL ===
acl localnet src 10.0.0.0/8
acl localnet src 172.16.0.0/12
acl localnet src 192.168.0.0/16
acl localnet src fc00::/7
acl localnet src fe80::/10

acl SSL_ports port 443
acl Safe_ports port 80 21 443 70 210 280 488 591 777 1025-65535
acl CONNECT method CONNECT

http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localnet
http_access allow localhost
http_access deny all

# === CACHE STORAGE ===
cache_dir ufs $cache_dir $cache_size 16 256
cache_mem 128 MB
maximum_object_size ${max_object} MB
minimum_object_size 0 KB
cache_swap_low 90
cache_swap_high 95

# === CDN CONTENT REFRESH PATTERNS ===
# Windows Updates - cache for 7 days
refresh_pattern -i windowsupdate.com/.*\.(exe|msu|cab|msi)$ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-store ignore-private
refresh_pattern -i download.microsoft.com/.*\.(exe|msu|cab|msi)$ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-store ignore-private
refresh_pattern -i officecdn.microsoft.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload

# Linux Package Repositories
refresh_pattern -i (deb|rpm|pkg\.tar\.(gz|xz|zst))$ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i archive\.ubuntu\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i security\.ubuntu\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i dl\.fedoraproject\.org/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i mirror\.centos\.org/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i downloads\.openwrt\.org/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload

# Android/Play Store
refresh_pattern -i play\.googleapis\.com/.*\.apk$ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i android\.clients\.google\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload

# Steam/Gaming
refresh_pattern -i \.steampowered\.com/depot/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-store ignore-private
refresh_pattern -i steamcdn-a\.akamaihd\.net/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-store ignore-private
refresh_pattern -i cdn\.cloudflare\.steamstatic\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i epicgames-download1\.akamaized\.net/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i origin-a\.akamaihd\.net/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i uplaypc-s-ubisoft\.cdn\.ubi\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload

# Apple Updates
refresh_pattern -i swcdn\.apple\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i swscan\.apple\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i \.itunes\.apple\.com/.*\.(ipa|pkg)$ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload

# Static Content CDNs
refresh_pattern -i \.cloudfront\.net/.*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ 1440 100% 10080 override-expire override-lastmod
refresh_pattern -i cdn\.jsdelivr\.net/ 1440 100% 10080 override-expire override-lastmod
refresh_pattern -i cdnjs\.cloudflare\.com/ 1440 100% 10080 override-expire override-lastmod
refresh_pattern -i ajax\.googleapis\.com/ 1440 100% 10080 override-expire override-lastmod
refresh_pattern -i fonts\.googleapis\.com/ 1440 100% 10080 override-expire override-lastmod
refresh_pattern -i fonts\.gstatic\.com/ 1440 100% 10080 override-expire override-lastmod

# General static assets
refresh_pattern -i \.(js|css)(\?.*)?$ 1440 50% 10080
refresh_pattern -i \.(png|jpg|jpeg|gif|ico|svg|webp)(\?.*)?$ 1440 50% 10080
refresh_pattern -i \.(woff|woff2|ttf|eot|otf)(\?.*)?$ 4320 50% 43200
refresh_pattern -i \.(mp3|mp4|webm|ogg|flac)(\?.*)?$ 1440 50% 10080
refresh_pattern -i \.(zip|tar|gz|bz2|xz|7z|rar)(\?.*)?$ 1440 50% 10080

# Default patterns
refresh_pattern ^ftp:		1440	20%	10080
refresh_pattern ^gopher:	1440	0%	1440
refresh_pattern -i (/cgi-bin/|\?) 0	0%	0
refresh_pattern .		0	20%	4320

# === EXCLUSIONS (Never Cache) ===
# Banking/Financial - security
acl nocache_banking dstdomain .paypal.com .stripe.com .braintreegateway.com
acl nocache_banking dstdomain .visa.com .mastercard.com .americanexpress.com

# Video Streaming - too large, DRM protected
acl nocache_streaming dstdomain .netflix.com .nflxvideo.net
acl nocache_streaming dstdomain .youtube.com .googlevideo.com .ytimg.com
acl nocache_streaming dstdomain .twitch.tv .twitchcdn.net
acl nocache_streaming dstdomain .hulu.com .hulustream.com
acl nocache_streaming dstdomain .disneyplus.com .dssott.com
acl nocache_streaming dstdomain .hbomax.com .max.com
acl nocache_streaming dstdomain .primevideo.com .aiv-cdn.net

# Real-time APIs
acl nocache_realtime dstdomain .api.github.com .api.gitlab.com
acl nocache_realtime dstdomain .amazonaws.com .azure.com

cache deny nocache_banking
cache deny nocache_streaming
cache deny nocache_realtime

# === LOGGING ===
access_log daemon:$LOG_DIR/access.log squid
cache_log $LOG_DIR/cache.log
cache_store_log none
debug_options ALL,$log_level

# === PERFORMANCE ===
dns_nameservers 8.8.8.8 8.8.4.4
connect_timeout 30 seconds
read_timeout 90 seconds
request_timeout 60 seconds
client_lifetime 1 day
half_closed_clients off
pconn_timeout 60 seconds
quick_abort_min 0 KB
quick_abort_max 0 KB
quick_abort_pct 95
negative_ttl 5 minutes

# === CACHE MANAGER ===
cache_mgr secubox@local
cachemgr_passwd none all

# === MISC ===
cache_effective_user squid
coredump_dir /var/spool/squid
pid_filename none
shutdown_lifetime 3 seconds
EOF
}

setup_transparent() {
	local listen_port="$1"
	local transparent="$2"
	local ssl_bump="$3"

	# Intercept ports
	local intercept_port=$((listen_port + 1))
	local ssl_bump_port=$((listen_port + 2))

	# Setup firewall redirect rules via UCI
	if [ "$transparent" = "1" ]; then
		# HTTP redirect rule
		uci -q delete firewall.cdn_cache_redirect
		uci set firewall.cdn_cache_redirect=redirect
		uci set firewall.cdn_cache_redirect.name='CDN-Cache-HTTP'
		uci set firewall.cdn_cache_redirect.src='lan'
		uci set firewall.cdn_cache_redirect.proto='tcp'
		uci set firewall.cdn_cache_redirect.src_dport='80'
		uci set firewall.cdn_cache_redirect.dest_port="$intercept_port"
		uci set firewall.cdn_cache_redirect.target='DNAT'
		uci set firewall.cdn_cache_redirect.enabled='1'
		logger -t cdn-cache "HTTP transparent proxy on port $intercept_port"
	else
		uci -q set firewall.cdn_cache_redirect.enabled='0'
	fi

	# HTTPS/SSL bump redirect rule
	if [ "$ssl_bump" = "1" ] && [ "$transparent" = "1" ]; then
		uci -q delete firewall.cdn_cache_ssl_redirect
		uci set firewall.cdn_cache_ssl_redirect=redirect
		uci set firewall.cdn_cache_ssl_redirect.name='CDN-Cache-HTTPS'
		uci set firewall.cdn_cache_ssl_redirect.src='lan'
		uci set firewall.cdn_cache_ssl_redirect.proto='tcp'
		uci set firewall.cdn_cache_ssl_redirect.src_dport='443'
		uci set firewall.cdn_cache_ssl_redirect.dest_port="$ssl_bump_port"
		uci set firewall.cdn_cache_ssl_redirect.target='DNAT'
		uci set firewall.cdn_cache_ssl_redirect.enabled='1'
		logger -t cdn-cache "HTTPS SSL bump on port $ssl_bump_port"
	else
		uci -q set firewall.cdn_cache_ssl_redirect.enabled='0'
	fi

	uci commit firewall
	/etc/init.d/firewall reload 2>/dev/null
}

remove_transparent() {
	# Disable the firewall redirect rules
	uci -q set firewall.cdn_cache_redirect.enabled='0'
	uci -q set firewall.cdn_cache_ssl_redirect.enabled='0'
	uci commit firewall
	/etc/init.d/firewall reload 2>/dev/null
}

start_service() {
	validate_section main start_cdn_cache
}

start_cdn_cache() {
	[ "$2" = 0 ] || {
		echo "Validation failed" >&2
		return 1
	}

	[ "$enabled" = "1" ] || {
		echo "CDN Cache is disabled"
		return 0
	}

	# Check if Squid is installed
	if ! command -v squid >/dev/null 2>&1; then
		logger -t cdn-cache "ERROR: Squid is not installed"
		echo "Squid is not installed. Install with: opkg install squid"
		return 1
	fi

	# Setup required directories
	mkdir -p "$cache_dir" "$LOG_DIR" /var/spool/squid
	chown -R squid:squid "$cache_dir" "$LOG_DIR" /var/spool/squid 2>/dev/null

	# Setup SSL if enabled
	if [ "$ssl_bump" = "1" ]; then
		generate_ca_cert
		init_ssl_db
	fi

	generate_squid_config "$cache_dir" "$cache_size" "$max_object_size" \
		"$cache_valid" "$listen_port" "$transparent" "$ssl_bump"

	# Initialize cache if needed (blocking call, run once)
	if [ ! -d "$cache_dir/00" ]; then
		logger -t cdn-cache "Initializing Squid cache directory..."
		/usr/sbin/squid -f "$SQUID_CONF" -z -N 2>/dev/null
	fi

	# Setup transparent proxy if enabled
	setup_transparent "$listen_port" "$transparent" "$ssl_bump"

	# Remove any stale PID file
	rm -f /var/run/cdn-cache.pid

	procd_open_instance cdn-cache
	procd_set_param command /usr/sbin/squid -f "$SQUID_CONF" -sYC -N
	procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
	procd_set_param stdout 1
	procd_set_param stderr 1
	procd_close_instance

	logger -t cdn-cache "CDN Cache (Squid) started on port $listen_port"
}

stop_service() {
	remove_transparent
	logger -t cdn-cache "CDN Cache proxy stopped"
}

reload_service() {
	# Send reconfigure signal to Squid
	if [ -f /var/run/cdn-cache.pid ]; then
		kill -HUP $(cat /var/run/cdn-cache.pid) 2>/dev/null
		logger -t cdn-cache "CDN Cache configuration reloaded"
	else
		stop
		start
	fi
}

service_triggers() {
	procd_add_reload_trigger "cdn-cache"
}
