1implement Transport; 2 3include "sys.m"; 4 sys: Sys; 5 6include "draw.m"; 7 8include "string.m"; 9 S: String; 10 11include "bufio.m"; 12 B : Bufio; 13 Iobuf: import Bufio; 14 15include "message.m"; 16 M: Message; 17 Msg, Nameval: import M; 18 19include "url.m"; 20 U: Url; 21 ParsedUrl: import U; 22 23include "webget.m"; 24 25include "dial.m"; 26 DI: Dial; 27 28include "wgutils.m"; 29 W: WebgetUtils; 30 Fid, Req: import WebgetUtils; 31 32include "transport.m"; 33 34FTPPORT: con "21"; 35DEBUG: con 1; 36 37# Return codes 38Extra, Success, Incomplete, TempFail, PermFail : con (1+iota); 39 40init(w: WebgetUtils) 41{ 42 sys = load Sys Sys->PATH; 43 W = w; 44 M = W->M; 45 S = W->S; 46 B = W->B; 47 U = W->U; 48 DI = W->DI; 49} 50 51connect(c: ref Fid, r: ref Req, donec: chan of ref Fid) 52{ 53 mrep: ref Msg = nil; 54 io, dio: ref Iobuf = nil; 55 err := ""; 56 u := r.url; 57 port := u.port; 58 if(port == "") 59 port = FTPPORT; 60 addr := DI->netmkaddr(u.host, "tcp", port); 61 62dummyloop: # just for breaking out of on error 63 for(;;) { 64 W->log(c, sys->sprint("ftp: dialing %s", addr)); 65 net := DI->dial(addr, nil); 66 if(net == nil) { 67 err = sys->sprint("dial error: %r"); 68 break dummyloop; 69 } 70 io = B->fopen(net.dfd, sys->ORDWR); 71 if(io == nil) { 72 err = "cannot open network via bufio"; 73 break dummyloop; 74 } 75 76 # look for Hello 77 (code, msg) := getreply(c, io); 78 if(code != Success) { 79 err = "instead of hello: " + msg; 80 break dummyloop; 81 } 82 # logon 83 err = sendrequest(c, io, "USER anonymous"); 84 if(err != "") 85 break dummyloop; 86 (code, msg) = getreply(c, io); 87 if(code != Success) { 88 if(code == Incomplete) { 89 # need password 90 err = sendrequest(c, io, "PASS webget@webget.com"); 91 (code, msg) = getreply(c, io); 92 if(code != Success) { 93 err = "login failed: " + msg; 94 break dummyloop; 95 } 96 } 97 else { 98 err = "login failed: " + msg; 99 break dummyloop; 100 } 101 } 102 # image type 103 err = sendrequest(c, io, "TYPE I"); 104 (code, msg) = getreply(c, io); 105 if(code != Success) { 106 err = "can't set type I: " + msg; 107 break dummyloop; 108 } 109 # passive mode 110 err = sendrequest(c, io, "PASV"); 111 (code, msg) = getreply(c, io); 112 if(code != Success) { 113 err = "can't use passive mode: " + msg; 114 break dummyloop; 115 } 116 (paddr, pport) := passvap(msg); 117 if(paddr == "") { 118 err = "passive mode protocol botch: " + msg; 119 break dummyloop; 120 } 121 # dial data port 122 daddr := "tcp!" + paddr + "!" + pport; 123 W->log(c, sys->sprint("ftp: dialing data %s", daddr)); 124 (ok2, dnet) := sys->dial(daddr, nil); 125 if(ok2 < 0) { 126 err = sys->sprint("data dial error: %r"); 127 break dummyloop; 128 } 129 dio = B->fopen(dnet.dfd, sys->ORDWR); 130 if(dio == nil) { 131 err = "cannot open network via bufio"; 132 break dummyloop; 133 } 134 # tell remote to send file 135 err = sendrequest(c, io, "RETR " + u.path); 136 (code, msg) = getreply(c, io); 137 if(code != Extra) { 138 err = "passive mode retrieve failed: " + msg; 139 break dummyloop; 140 } 141 142 mrep = Msg.newmsg(); 143W->log(c, "reading from dio now"); 144 err = W->getdata(dio, mrep, W->fixaccept(r.types), u); 145W->log(c, "done reading from dio now, err=" + err); 146 B->dio.close(); 147 if(err == "") 148 W->okprefix(r, mrep); 149 break dummyloop; 150 } 151 if(io != nil) 152 B->io.close(); 153 if(dio != nil) 154 B->dio.close(); 155 if(err != "") 156 mrep = W->usererr(r, err); 157 if(mrep != nil) { 158 W->log(c, "ftp: reply ready for " + r.reqid + ": " + mrep.prefixline); 159 r.reply = mrep; 160 donec <-= c; 161 } 162} 163 164getreply(c: ref Fid, io: ref Iobuf) : (int, string) 165{ 166 for(;;) { 167 line := B->io.gets('\n'); 168 n := len line; 169 if(n == 0) 170 break; 171 if(DEBUG) 172 W->log(c, "ftp: got reply: " + line); 173 if(line[n-1] == '\n') { 174 if(n > 2 && line[n-2] == '\r') 175 line = line[0:n-2]; 176 else 177 line = line[0:n-1]; 178 } 179 rv := int line; 180 if(rv >= 100 && rv < 600) { 181 # if line is like '123-stuff' 182 # then there will be more lines until 183 # '123 stuff' 184 if(len line<4 || line[3]==' ') 185 return (rv/100, line); 186 } 187 } 188 return (-1, ""); 189} 190 191sendrequest(c: ref Fid, io: ref Iobuf, cmd: string) : string 192{ 193 if(DEBUG) 194 W->log(c, "ftp: send request: " + cmd); 195 cmd = cmd + "\r\n"; 196 buf := array of byte cmd; 197 n := len buf; 198 if(B->io.write(buf, n) != n) 199 return sys->sprint("write error: %r"); 200 return ""; 201} 202 203passvap(s: string) : (string, string) 204{ 205 # Parse reply to PASSV to find address and port numbers. 206 # This is AI 207 addr := ""; 208 port := ""; 209 (nil, v) := S->splitl(s, "("); 210 if(v != "") 211 s = v[1:]; 212 else 213 (nil, s) = S->splitl(s, "0123456789"); 214 if(s != "") { 215 (n, l) := sys->tokenize(s, ","); 216 if(n >= 6) { 217 addr = hd l + "."; 218 l = tl l; 219 addr += hd l + "."; 220 l = tl l; 221 addr += hd l + "."; 222 l = tl l; 223 addr += hd l; 224 l = tl l; 225 p1 := int hd l; 226 p2 := int hd tl l; 227 port = string (((p1&255)<<8)|(p2&255)); 228 } 229 } 230 return (addr, port); 231} 232