# To unbundle, run this file echo pop.b sed 's/.//' >pop.b <<'//GO.SYSIN DD pop.b' -# (C) Copyright Boyd Roberts, Bruce Ellis, November 1998 - -implement Pop; - -include "sys.m"; -include "bufio.m"; -include "string.m"; -include "pop.m"; - -sys: Sys; -bufio: Bufio; -str: String; - -Iobuf: import bufio; - -stderr: ref Sys->FD; -rx, tx: ref Iobuf; - -debug: con 0; - -init(): string -{ - sys = load Sys Sys->PATH; - if (sys == nil) - return "load $Sys failed"; - stderr = sys->fildes(2); - bufio = load Bufio Bufio->PATH; - if (bufio == nil) - return sys->sprint("could not load %s: %r", Bufio->PATH); - str = load String String->PATH; - if (str == nil) - return sys->sprint("could not load %s: %r", String->PATH); - return nil; -} - -fetch(user, host, password, dir: string, del: int): (string, big) -{ - dest := sys->sprint("tcp!%s!%d", host, Pop->POP3); - (ok, C) := sys->dial(dest, nil); - if (ok < 0) - return (sys->sprint("dial '%s' failed: %r", dest), big 0); - - rx = bufio->fopen(C.dfd, Sys->OREAD); - if (rx == nil) - return (sys->sprint("bufio read open '%s' failed: %r", dest), big 0); - tx = bufio->fopen(C.dfd, Sys->OWRITE); - if (tx == nil) - return (sys->sprint("bufio write open '%s' failed: %r", dest), big 0); - - (e, s) := reply(0); - case e { - ERR => - return(s, big 0); - OK => - if (debug) - sys->print("%s\n", s); - } - - (e, s) = request(CMD_USER, user); - case e { - ERR => - return(s, big 0); - OK => - if (debug) - sys->print("%s\n", s); - } - - (e, s) = request(CMD_PASS, password); - case e { - ERR => - return(s, big 0); - OK => - if (debug) - sys->print("%s\n", s); - } - - (e, s) = request(CMD_STAT, nil); - case e { - ERR => - return(s, big 0); - OK => - if (debug) - sys->print("%s\n", s); - } - - messages := big 0; - size := big 0; - (n, l) := sys->tokenize(s, " "); - case n { - 1 => - sys->print("%d message%s for '%s@%s', maildrop size unknown.\n", int messages, plural(messages), user, host); - messages = big hd l; - - 2 => - messages = big hd l; - size = big hd tl l; - - * => - return (sys->sprint("unknown number of messages for '%s@%s'.\n", user, host), big 0); - } - - fetched: list of string; - err := ""; -getem: - for (i := big 1; i <= messages; i++) { - (e, s) = request(CMD_RETR, string i); - case e { - ERR => - return(s, big 0); - OK => - if (debug) - sys->print("%s\n", s); - } - f := filename(dir, string i); - fd := sys->create(f, Sys->OWRITE, 0); - if (fd == nil) { - err = sys->sprint("could not create '%s'", f); - break; - } - - for (;;) { - (e, s) = reply(1); - case e { - ERR => - err = s; - break getem; - DATA => - if (debug) - sys->print("%s\n", s); - if (sys->fprint(fd, "%s\n", s) < 0) { - err = sys->sprint("could not write '%s': %r", f); - sys->remove(f); - break getem; - } - - END => - s = readable(f, fd); - if (s != nil) - sys->fprint(stderr, "warning: %s\n", s); - fd = nil; - fetched = string i :: fetched; - continue getem; - } - } - } - - messages = big 0; - - if (del) - { - while (fetched != nil) - { - s = hd fetched; - fetched = tl fetched; - - (e, s) = request(CMD_DELE, s); - case e { - ERR => - sys->remove(filename(dir, s)); - OK => - messages++; - } - } - } - - (e, s) = request(CMD_QUIT, nil); - if (e != OK && err == nil) { - if (messages != big 0) - err = sys->sprint("%bd message%s: ", messages, plural(messages)); - err = sys->sprint("%swarning: %s", err, s); - } - - return (err, messages); -} - -reply(data: int): (int, string) -{ - r := rx.gets('\n'); - if (r == nil) - return (ERR, sys->sprint("pop server read failed: %r")); - (n, l) := sys->tokenize(r, "\r\n"); - if (n != 1) { - if (n == 0 && data) - return (DATA, nil); - return (ERR, "reply missing CRLF"); - } - line := hd l; - if (debug) - sys->print("<-- %s\n", line); - - if (data) { - if (line == REP_TCHAR) - return (END, nil); - return (DATA, line); - } - - (cmd, rest) := str->splitl(line, " "); - rest = rest[1:]; - case cmd { - REP_OK => - return (OK, rest); - REP_ERR => - return (ERR, rest); - * => - return (ERR, "protocol error"); - } -} - -request(cmd, arg: string): (int, string) -{ - s := cmd; - if (arg != nil) - s += " " + arg; - if (debug) - sys->print("--> %s\n", s); - if (tx.puts(s + "\r\n") < 0 || tx.flush() < 0) - return (ERR, sys->sprint("pop server write failed: %r")); - return reply(0); -} - -plural(n: big): string -{ - if (n == big 1) - return nil; - - return "s"; -} - -readable(f: string, fd: ref Sys->FD): string -{ - (ok, d) := sys->fstat(fd); - if (ok < 0) - return sys->sprint("could not fstat '%s': %r", f); - - d.mode |= 8r600; - if (sys->wstat(f, d) < 0) - return sys->sprint("could not wstat '%s': %r", f); - - return nil; -} - -filename(dir, file: string): string -{ - return dir + "/" + file; -} //GO.SYSIN DD pop.b echo pop.m sed 's/.//' >pop.m <<'//GO.SYSIN DD pop.m' -# (C) Copyright Boyd Roberts, Bruce Ellis, November 1998 - -Pop: module -{ - PATH: con "pop.dis"; - POP3: con 110; - OK: con 0; - END: con 1; - DATA: con 2; - ERR: con -1; - - CMD_DELE: con "DELE"; - CMD_LAST: con "LAST"; - CMD_NOOP: con "NOOP"; - CMD_PASS: con "PASS"; - CMD_QUIT: con "QUIT"; - CMD_RETR: con "RETR"; - CMD_RSET: con "RSET"; - CMD_STAT: con "STAT"; - CMD_USER: con "USER"; - - REP_OK: con "+OK"; - REP_ERR: con "-ERR"; - REP_TCHAR: con "."; - - init: fn(): string; - fetch: fn(user, host, password, dir: string, del: int): (string, big); - plural: fn(n: big): string; -}; //GO.SYSIN DD pop.m echo test.b sed 's/.//' >test.b <<'//GO.SYSIN DD test.b' -# (C) Copyright Boyd Roberts, Bruce Ellis, November 1998 - -implement Test; - -include "sys.m"; -include "draw.m"; -include "pop.m"; - -sys: Sys; -pop: Pop; - -stderr: ref Sys->FD; - -Test: module -{ - init: fn(nil: ref Draw->Context, nil: list of string); -}; - -init(nil: ref Draw->Context, nil: list of string) -{ - sys = load Sys Sys->PATH; - stderr = sys->fildes(2); - pop = load Pop Pop->PATH; - if (pop == nil) - exits(sys->sprint("could not load %s: %r\n", Pop->PATH)); - s := pop->init(); - if (s != nil) - exits(s); - n: big; - (s, n) = pop->fetch("user", "host", "password", ".", 0); - if (s != nil) - exits(s); - if (n != big 0) - sys->print("%bd message%s\n", n, pop->plural(n)); -} - -exits(s: string) -{ - sys->raise("fail: " + s); -} //GO.SYSIN DD test.b