diff options
Diffstat (limited to 'appl/svc/webget/ftp.b')
| -rw-r--r-- | appl/svc/webget/ftp.b | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/appl/svc/webget/ftp.b b/appl/svc/webget/ftp.b new file mode 100644 index 00000000..9162fcb7 --- /dev/null +++ b/appl/svc/webget/ftp.b @@ -0,0 +1,227 @@ +implement Transport; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "string.m"; + S: String; + +include "bufio.m"; + B : Bufio; + Iobuf: import Bufio; + +include "message.m"; + M: Message; + Msg, Nameval: import M; + +include "url.m"; + U: Url; + ParsedUrl: import U; + +include "webget.m"; + +include "wgutils.m"; + W: WebgetUtils; + Fid, Req: import WebgetUtils; + +include "transport.m"; + +FTPPORT: con "21"; +DEBUG: con 1; + +# Return codes +Extra, Success, Incomplete, TempFail, PermFail : con (1+iota); + +init(w: WebgetUtils) +{ + sys = load Sys Sys->PATH; + W = w; + M = W->M; + S = W->S; + B = W->B; + U = W->U; +} + +connect(c: ref Fid, r: ref Req, donec: chan of ref Fid) +{ + mrep: ref Msg = nil; + io, dio: ref Iobuf = nil; + err := ""; + u := r.url; + port := u.port; + if(port == "") + port = FTPPORT; + addr := "tcp!" + u.host + "!" + port; + +dummyloop: # just for breaking out of on error + for(;;) { + W->log(c, sys->sprint("ftp: dialing %s", addr)); + (ok, net) := sys->dial(addr, nil); + if(ok < 0) { + err = sys->sprint("dial error: %r"); + break dummyloop; + } + io = B->fopen(net.dfd, sys->ORDWR); + if(io == nil) { + err = "cannot open network via bufio"; + break dummyloop; + } + + # look for Hello + (code, msg) := getreply(c, io); + if(code != Success) { + err = "instead of hello: " + msg; + break dummyloop; + } + # logon + err = sendrequest(c, io, "USER anonymous"); + if(err != "") + break dummyloop; + (code, msg) = getreply(c, io); + if(code != Success) { + if(code == Incomplete) { + # need password + err = sendrequest(c, io, "PASS webget@webget.com"); + (code, msg) = getreply(c, io); + if(code != Success) { + err = "login failed: " + msg; + break dummyloop; + } + } + else { + err = "login failed: " + msg; + break dummyloop; + } + } + # image type + err = sendrequest(c, io, "TYPE I"); + (code, msg) = getreply(c, io); + if(code != Success) { + err = "can't set type I: " + msg; + break dummyloop; + } + # passive mode + err = sendrequest(c, io, "PASV"); + (code, msg) = getreply(c, io); + if(code != Success) { + err = "can't use passive mode: " + msg; + break dummyloop; + } + (paddr, pport) := passvap(msg); + if(paddr == "") { + err = "passive mode protocol botch: " + msg; + break dummyloop; + } + # dial data port + daddr := "tcp!" + paddr + "!" + pport; + W->log(c, sys->sprint("ftp: dialing data %s", daddr)); + (ok2, dnet) := sys->dial(daddr, nil); + if(ok2 < 0) { + err = sys->sprint("data dial error: %r"); + break dummyloop; + } + dio = B->fopen(dnet.dfd, sys->ORDWR); + if(dio == nil) { + err = "cannot open network via bufio"; + break dummyloop; + } + # tell remote to send file + err = sendrequest(c, io, "RETR " + u.path); + (code, msg) = getreply(c, io); + if(code != Extra) { + err = "passive mode retrieve failed: " + msg; + break dummyloop; + } + + mrep = Msg.newmsg(); +W->log(c, "reading from dio now"); + err = W->getdata(dio, mrep, W->fixaccept(r.types), u); +W->log(c, "done reading from dio now, err=" + err); + B->dio.close(); + if(err == "") + W->okprefix(r, mrep); + break dummyloop; + } + if(io != nil) + B->io.close(); + if(dio != nil) + B->dio.close(); + if(err != "") + mrep = W->usererr(r, err); + if(mrep != nil) { + W->log(c, "ftp: reply ready for " + r.reqid + ": " + mrep.prefixline); + r.reply = mrep; + donec <-= c; + } +} + +getreply(c: ref Fid, io: ref Iobuf) : (int, string) +{ + for(;;) { + line := B->io.gets('\n'); + n := len line; + if(n == 0) + break; + if(DEBUG) + W->log(c, "ftp: got reply: " + line); + if(line[n-1] == '\n') { + if(n > 2 && line[n-2] == '\r') + line = line[0:n-2]; + else + line = line[0:n-1]; + } + rv := int line; + if(rv >= 100 && rv < 600) { + # if line is like '123-stuff' + # then there will be more lines until + # '123 stuff' + if(len line<4 || line[3]==' ') + return (rv/100, line); + } + } + return (-1, ""); +} + +sendrequest(c: ref Fid, io: ref Iobuf, cmd: string) : string +{ + if(DEBUG) + W->log(c, "ftp: send request: " + cmd); + cmd = cmd + "\r\n"; + buf := array of byte cmd; + n := len buf; + if(B->io.write(buf, n) != n) + return sys->sprint("write error: %r"); + return ""; +} + +passvap(s: string) : (string, string) +{ + # Parse reply to PASSV to find address and port numbers. + # This is AI + addr := ""; + port := ""; + (nil, v) := S->splitl(s, "("); + if(v != "") + s = v[1:]; + else + (nil, s) = S->splitl(s, "0123456789"); + if(s != "") { + (n, l) := sys->tokenize(s, ","); + if(n >= 6) { + addr = hd l + "."; + l = tl l; + addr += hd l + "."; + l = tl l; + addr += hd l + "."; + l = tl l; + addr += hd l; + l = tl l; + p1 := int hd l; + p2 := int hd tl l; + port = string (((p1&255)<<8)|(p2&255)); + } + } + return (addr, port); +} |
