summaryrefslogtreecommitdiff
path: root/appl/svc/webget/ftp.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/svc/webget/ftp.b')
-rw-r--r--appl/svc/webget/ftp.b227
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);
+}