# 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
|