Created
May 30, 2025 00:21
-
-
Save jonasborn/69cc30d18756c170013be6fbdc906274 to your computer and use it in GitHub Desktop.
IMAP-over-PHP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="de"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <title>IMAP-TLS (letzte 5 Mails)</title> | |
| <style> | |
| body{font-family:sans-serif} | |
| pre {background:#111;color:#0f0;padding:1em;height:60vh;overflow:auto} | |
| </style> | |
| </head> | |
| <body> | |
| <h1>IMAP-TLS POC</h1> | |
| <p>Hole die letzten fünf Nachrichten …</p> | |
| <pre id="log"></pre> | |
| <script> | |
| /* ---------- Session-ID ---------- */ | |
| function newSID(){ | |
| const a = new Uint8Array(12); | |
| crypto.getRandomValues(a); | |
| return Array.from(a,x=>x.toString(16).padStart(2,"0")).join(""); | |
| } | |
| const SID = newSID(); | |
| /* ---------- Passwort ---------- */ | |
| if(!sessionStorage.getItem("pwd")) | |
| sessionStorage.setItem("pwd", prompt("Passwort?")||""); | |
| const w = new Worker("./worker.js"); | |
| w.onmessage = e=>{ | |
| const {type,text}=e.data; | |
| document.getElementById("log").textContent += text + "\n"; | |
| }; | |
| /* ---------- Worker starten ---------- */ | |
| w.postMessage({ | |
| type:"start", | |
| sid: SID, | |
| user: sessionStorage.getItem("usr"), //Place yourself | |
| password: sessionStorage.getItem("pwd") //Place yourself | |
| }); | |
| /* ---------- Tab-Close: Worker sauber abbrechen ---------- */ | |
| window.addEventListener("beforeunload", ()=>w.terminate()); | |
| </script> | |
| </body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /* IMAP-Tunnel pro Session – beendet sich nach 15 s Inaktivität */ | |
| set_time_limit(0); | |
| ini_set('output_buffering',0); | |
| ini_set('implicit_flush',1); | |
| while(ob_get_level())ob_end_flush(); | |
| header("Content-Type: application/octet-stream"); | |
| $sid = preg_replace('/[^a-f0-9]/i','',$_GET['sid'] ?? ''); | |
| if($sid===''){ http_response_code(400); exit("no sid\n"); } | |
| $queue = __DIR__."/queue_$sid.txt"; | |
| touch($queue); | |
| $imap = stream_socket_client('tcp://<SERVER HOST HERE>:993',$e,$s,15); | |
| if(!$imap){ echo "\nIMAP connect error $e/$s\n"; exit; } | |
| stream_set_blocking($imap,false); | |
| echo " "; flush(); // löst fetch() im Browser aus | |
| $last = time(); // Aktivitäts-Timer | |
| for(;;){ | |
| /* ----- Browser → IMAP ----- */ | |
| $fp = fopen($queue,'c+b'); | |
| flock($fp,LOCK_EX); | |
| rewind($fp); | |
| $buf = stream_get_contents($fp); | |
| ftruncate($fp,0); | |
| fflush($fp); | |
| flock($fp,LOCK_UN); | |
| fclose($fp); | |
| if($buf!==''){ | |
| foreach(explode("\n",$buf) as $line){ | |
| $bin = base64_decode(trim($line),true); | |
| if($bin!=='') fwrite($imap,$bin); | |
| } | |
| $last = time(); // Aktivität registriert | |
| } | |
| /* ----- IMAP → Browser ----- */ | |
| $in = fread($imap,8192); | |
| if($in!==false && $in!==''){ | |
| echo $in; flush(); | |
| $last = time(); // Aktivität registriert | |
| } | |
| /* ----- Timeout? ------------- */ | |
| if(time() - $last > 15){ break; } // 15 s idle → Ende | |
| usleep(100_000); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| $sid = preg_replace('/[^a-f0-9]/i','',$_GET['sid'] ?? ''); | |
| if($sid===''){ http_response_code(400); exit('no sid'); } | |
| $queue = __DIR__."/queue_$sid.txt"; | |
| $line = rtrim(file_get_contents('php://input'),"\r\n"); | |
| if($line===''){ http_response_code(400); exit('empty'); } | |
| $fp = fopen($queue,'ab'); | |
| flock($fp,LOCK_EX); | |
| fwrite($fp,$line.PHP_EOL); | |
| flock($fp,LOCK_UN); | |
| fclose($fp); | |
| echo "OK"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| self.window = self; // forge erwartet window | |
| self.navigator = { userAgent:"Worker" }; | |
| importScripts("./forge.min.js"); | |
| const post = (t,x)=>self.postMessage({type:t,text:x}); | |
| /* ------------------------------------------------------------------ */ | |
| self.onmessage = ({data})=>{ | |
| if(data.type==="start") run(data.sid,data.user,data.password); | |
| }; | |
| function run(sid,user,pwd){ | |
| const relayURL = `./relay.php?sid=${sid}`; | |
| const sendURL = `./send.php?sid=${sid}`; | |
| /* ----------- Stream zum Relay ---------- */ | |
| fetch(relayURL,{cache:"no-store"}) | |
| .then(r=>streamPump(r.body.getReader())) // ← richtiger Funktionsname | |
| .catch(e=>post("log","relay fetch fail: "+e)); | |
| /* ----------- forge-TLS Engine ----------- */ | |
| const tls = forge.tls.createConnection({ | |
| server:false, verify:() => true, | |
| connected: () => post("log","TLS ready"), | |
| tlsDataReady:c => sendRaw(c.tlsData.getBytes()), | |
| dataReady: c => imapClear(c.data.getBytes()), | |
| closed: () => post("log","TLS closed"), | |
| error: (c,e)=> post("log","TLS err: "+e) | |
| }); | |
| tls.handshake(); | |
| /* ----------- TLS-Bytes → send.php ------- */ | |
| function sendRaw(bin){ | |
| if(!bin) return; | |
| fetch(sendURL,{ | |
| method:"POST", | |
| headers:{"Content-Type":"text/plain"}, | |
| body: btoa(bin)+"\n" // eine Zeile Base64 | |
| }).catch(e=>post("log","send fail: "+e)); | |
| } | |
| /* ----------- IMAP Mini-FSM -------------- */ | |
| let phase=0,uids=[]; | |
| const send = cmd => tls.prepare(cmd+"\r\n"); | |
| function imapClear(txt){ | |
| post("log",txt.trim()); | |
| if(phase===0 && /^\* OK/.test(txt)){ send(`A1 LOGIN "${user}" "${pwd}"`); phase=1; return;} | |
| if(phase===1 && /A1 OK/.test(txt)){ send("A2 SELECT INBOX"); phase=2; return;} | |
| if(phase===2 && /A2 OK/.test(txt)){ send("A3 UID SEARCH ALL"); phase=3; return;} | |
| if(phase===3 && txt.startsWith("* SEARCH")){ | |
| uids = txt.replace("* SEARCH","").trim().split(/\s+/).map(Number); return; | |
| } | |
| if(phase===3 && /A3 OK/.test(txt)){ | |
| send(`A4 UID FETCH ${uids.slice(-5).join(",")} (BODY.PEEK[HEADER.FIELDS (SUBJECT FROM DATE)] RFC822.SIZE)`); | |
| phase=4; return; | |
| } | |
| if(phase===4 && /A4 OK/.test(txt)){ post("result",txt); send("A5 LOGOUT"); phase=5; } | |
| } | |
| /* ----------- Stream-Pump (debug-Filter) -- */ | |
| function streamPump(reader){ | |
| (async function(){ | |
| let started=false; | |
| for(;;){ | |
| const {value,done}=await reader.read(); | |
| if(done) break; | |
| if(!value||!value.length) continue; | |
| let chunk=value; | |
| if(!started){ | |
| const i = chunk.findIndex(b=>b===0x14||b===0x15||b===0x16||b===0x17); | |
| if(i===-1) continue; // nur Preamble | |
| if(i>0) chunk = chunk.slice(i); | |
| started = true; | |
| } | |
| tls.process(String.fromCharCode(...chunk)); | |
| } | |
| })(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment