implement Pop3; include "sys.m"; sys : Sys; include "draw.m"; include "bufio.m"; bufio : Bufio; include "dial.m"; dial: Dial; include "pop3.m"; FD: import sys; Iobuf : import bufio; Connection: import dial; ibuf, obuf : ref Bufio->Iobuf; conn : int = 0; inited : int = 0; rpid : int = -1; cread : chan of (int, string); DEBUG : con 0; open(user, password, server : string): (int, string) { s : string; if (!inited) { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; dial = load Dial Dial->PATH; inited = 1; } if (conn) return (-1, "connection is already open"); if (server == nil) server = "$pop3"; else server = dial->netmkaddr(server, "net", "110"); c := dial->dial(server, nil); if (c == nil) return (-1, "dialup failed"); ibuf = bufio->fopen(c.dfd, Bufio->OREAD); obuf = bufio->fopen(c.dfd, Bufio->OWRITE); if (ibuf == nil || obuf == nil) return (-1, "failed to open bufio"); cread = chan of (int, string); spawn mreader(cread); (rpid, nil) = <- cread; ok: int; (ok, s) = mread(); if (ok < 0) return (-1, s); (ok, s) = mcmd("USER " + user); if (ok < 0) return (-1, s); (ok, s) = mcmd("PASS " + password); if (ok < 0) return (-1, s); conn = 1; return (1, nil); } stat() : (int, string, int, int) { if (!conn) return (-1, "not connected", 0, 0); (ok, s) := mcmd("STAT"); if (ok < 0) return (-1, s, 0, 0); (n, ls) := sys->tokenize(s, " "); if (n == 3) return (1, nil, int hd tl ls, int hd tl tl ls); return (-1, "stat failed", 0, 0); } msglist() : (int, string, list of (int, int)) { ls : list of (int, int); if (!conn) return (-1, "not connected", nil); (ok, s) := mcmd("LIST"); if (ok < 0) return (-1, s, nil); for (;;) { (ok, s) = mread(); if (ok < 0) return (-1, s, nil); if (len s < 3) { if (len s > 0 && s[0] == '.') return (1, nil, rev2(ls)); else return (-1, s, nil); } else { (n, sl) := sys->tokenize(s, " "); if (n == 2) ls = (int hd sl, int hd tl sl) :: ls; else return (-1, "bad list format", nil); } } } msgnolist() : (int, string, list of int) { ls : list of int; if (!conn) return (-1, "not connected", nil); (ok, s) := mcmd("LIST"); if (ok < 0) return (-1, s, nil); for (;;) { (ok, s) = mread(); if (ok < 0) return (-1, s, nil); if (len s < 3) { if (len s > 0 && s[0] == '.') return (1, nil, rev1(ls)); else return (-1, s, nil); } else { (n, sl) := sys->tokenize(s, " "); if (n == 2) ls = int hd sl :: ls; else return (-1, "bad list format", nil); } } } top(m : int) : (int, string, string) { if (!conn) return (-1, "not connected", nil); (ok, s) := mcmd("TOP " + string m + " 1"); if (ok < 0) return (-1, s, nil); return getbdy(); } get(m : int) : (int, string, string) { if (!conn) return (-1, "not connected", nil); (ok, s) := mcmd("RETR " + string m); if (ok < 0) return (-1, s, nil); return getbdy(); } getbdy() : (int, string, string) { b : string; for (;;) { (ok, s) := mread(); if (ok < 0) return (-1, s, nil); if (s == ".") break; if (len s > 1 && s[0] == '.' && s[1] == '.') s = s[1:]; b = b + s + "\n"; } return (1, nil, b); } delete(m : int) : (int, string) { if (!conn) return (-1, "not connected"); return mcmd("DELE " + string m); } close(): (int, string) { if (!conn) return (-1, "connection not open"); ok := mwrite("QUIT"); kill(rpid); ibuf.close(); obuf.close(); conn = 0; if (ok < 0) return (-1, "failed to close connection"); return (1, nil); } SLPTIME : con 100; MAXSLPTIME : con 10000; mread() : (int, string) { t := 0; while (t < MAXSLPTIME) { alt { (ok, s) := <- cread => return (ok, s); * => t += SLPTIME; sys->sleep(SLPTIME); } } kill(rpid); return (-1, "smtp timed out\n"); } mreader(c : chan of (int, string)) { c <- = (sys->pctl(0, nil), nil); for (;;) { line := ibuf.gets('\n'); if (DEBUG) sys->print("mread : %s", line); if (line == nil) { c <- = (-1, "could not read response from server"); continue; } l := len line; if (line[l-1] == '\n') l--; if (line[l-1] == '\r') l--; c <- = (1, line[0:l]); } } mwrite(s : string): int { s += "\r\n"; if (DEBUG) sys->print("mwrite : %s", s); b := array of byte s; l := len b; nb := obuf.write(b, l); obuf.flush(); if (nb != l) return -1; return 1; } mcmd(s : string) : (int, string) { ok : int; r : string; ok = mwrite(s); if (ok < 0) return (-1, err(s) + " send failed"); (ok, r) = mread(); if (ok < 0) return (-1, err(s) + " receive failed (" + r + ")"); if (len r > 1 && r[0] == '+') return (1, r); return (-1, r); } rev1(l1 : list of int) : list of int { l2 : list of int; for ( ; l1 != nil; l1 = tl l1) l2 = hd l1 :: l2; return l2; } rev2(l1 : list of (int, int)) : list of (int, int) { l2 : list of (int, int); for ( ; l1 != nil; l1 = tl l1) l2 = hd l1 :: l2; return l2; } err(s : string) : string { for (i := 0; i < len s; i++) if (s[i] == ' ' || s[i] == ':') return s[0:i]; return s; } kill(pid : int) : int { if (pid < 0) return 0; fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE); if (fd == nil || sys->fprint(fd, "kill") < 0) return -1; return 0; }