1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <ip.h> 5 #include <plumb.h> 6 #include <thread.h> 7 #include <fcall.h> 8 #include <9p.h> 9 #include "dat.h" 10 #include "fns.h" 11 12 int nclient; 13 Client **client; 14 15 static void clientthread(void*); 16 int 17 newclient(int plumbed) 18 { 19 int i; 20 Client *c; 21 22 for(i=0; i<nclient; i++) 23 if(client[i]->ref==0) 24 return i; 25 26 c = emalloc(sizeof(Client)); 27 c->plumbed = plumbed; 28 c->creq = chancreate(sizeof(Req*), 8); 29 threadcreate(clientthread, c, STACK); 30 31 c->io = ioproc(); 32 c->num = nclient; 33 c->ctl = globalctl; 34 clonectl(&c->ctl); 35 if(nclient%16 == 0) 36 client = erealloc(client, (nclient+16)*sizeof(client[0])); 37 client[nclient++] = c; 38 return nclient-1; 39 } 40 41 void 42 closeclient(Client *c) 43 { 44 if(--c->ref == 0){ 45 if(c->bodyopened){ 46 if(c->url && c->url->close) 47 (*c->url->close)(c); 48 c->bodyopened = 0; 49 } 50 free(c->contenttype); 51 c->contenttype = nil; 52 free(c->postbody); 53 c->postbody = nil; 54 freeurl(c->url); 55 c->url = nil; 56 free(c->redirect); 57 c->redirect = nil; 58 c->npostbody = 0; 59 c->havepostbody = 0; 60 c->bodyopened = 0; 61 } 62 } 63 64 void 65 clonectl(Ctl *c) 66 { 67 if(c->useragent) 68 c->useragent = estrdup(c->useragent); 69 } 70 71 void 72 clientbodyopen(Client *c, Req *r) 73 { 74 char e[ERRMAX], *next; 75 int i; 76 Url *u; 77 78 next = nil; 79 for(i=0; i<=c->ctl.redirectlimit; i++){ 80 if(c->url == nil){ 81 werrstr("nil url"); 82 goto Error; 83 } 84 if(c->url->open == nil){ 85 werrstr("unsupported url type"); 86 goto Error; 87 } 88 if(fsdebug) 89 fprint(2, "try %s\n", c->url->url); 90 if(c->url->open(c, c->url) < 0){ 91 Error: 92 if(next) 93 fprint(2, "next %s (but for error)\n", next); 94 free(next); 95 rerrstr(e, sizeof e); 96 c->iobusy = 0; 97 if(r != nil) 98 r->fid->omode = -1; 99 closeclient(c); /* not opening */ 100 if(r != nil) 101 respond(r, e); 102 return; 103 } 104 if(!c->redirect) 105 break; 106 next = c->redirect; 107 c->redirect = nil; 108 if(i==c->ctl.redirectlimit){ 109 werrstr("redirect limit reached"); 110 goto Error; 111 } 112 if((u = parseurl(next, c->url)) == nil) 113 goto Error; 114 if(urldebug) 115 fprint(2, "parseurl %s got scheme %d\n", next, u->ischeme); 116 if(u->ischeme == USunknown){ 117 werrstr("redirect with unknown URL scheme"); 118 goto Error; 119 } 120 if(u->ischeme == UScurrent){ 121 werrstr("redirect to URL relative to current document"); 122 goto Error; 123 } 124 freeurl(c->url); 125 c->url = u; 126 } 127 free(next); 128 c->iobusy = 0; 129 if(r != nil) 130 respond(r, nil); 131 } 132 133 void 134 plumburl(char *url, char *base) 135 { 136 int i; 137 Client *c; 138 139 i = newclient(1); 140 c = client[i]; 141 c->ref++; 142 if(base != nil) 143 c->baseurl = parseurl(base, nil); 144 c->url = parseurl(url, c->baseurl); 145 sendp(c->creq, nil); 146 } 147 148 void 149 clientbodyread(Client *c, Req *r) 150 { 151 char e[ERRMAX]; 152 153 if(c->url->read == nil){ 154 respond(r, "unsupported url type"); 155 return; 156 } 157 if(c->url->read(c, r) < 0){ 158 rerrstr(e, sizeof e); 159 c->iobusy = 0; 160 respond(r, e); 161 return; 162 } 163 c->iobusy = 0; 164 respond(r, nil); 165 } 166 167 static void 168 clientthread(void *a) 169 { 170 Client *c; 171 Req *r; 172 173 c = a; 174 if(c->plumbed) { 175 recvp(c->creq); 176 clientbodyopen(c, nil); 177 replumb(c); 178 } 179 while((r = recvp(c->creq)) != nil){ 180 if(fsdebug) 181 fprint(2, "clientthread %F\n", &r->ifcall); 182 switch(r->ifcall.type){ 183 case Topen: 184 if(c->plumbed) { 185 c->plumbed = 0; 186 c->ref--; /* from plumburl() */ 187 respond(r, nil); 188 } 189 else 190 clientbodyopen(c, r); 191 break; 192 case Tread: 193 clientbodyread(c, r); 194 break; 195 case Tflush: 196 respond(r, nil); 197 } 198 if(fsdebug) 199 fprint(2, "clientthread finished req\n"); 200 } 201 } 202 203 enum 204 { 205 Bool, 206 Int, 207 String, 208 XUrl, 209 Fn, 210 }; 211 212 typedef struct Ctab Ctab; 213 struct Ctab { 214 char *name; 215 int type; 216 void *offset; 217 }; 218 219 Ctab ctltab[] = { 220 "acceptcookies", Bool, (void*)offsetof(Ctl, acceptcookies), 221 "sendcookies", Bool, (void*)offsetof(Ctl, sendcookies), 222 "redirectlimit", Int, (void*)offsetof(Ctl, redirectlimit), 223 "useragent", String, (void*)offsetof(Ctl, useragent), 224 }; 225 226 Ctab globaltab[] = { 227 "chatty9p", Int, &chatty9p, 228 "fsdebug", Int, &fsdebug, 229 "cookiedebug", Int, &cookiedebug, 230 "urldebug", Int, &urldebug, 231 "httpdebug", Int, &httpdebug, 232 }; 233 234 Ctab clienttab[] = { 235 "baseurl", XUrl, (void*)offsetof(Client, baseurl), 236 "url", XUrl, (void*)offsetof(Client, url), 237 }; 238 239 static Ctab* 240 findcmd(char *cmd, Ctab *tab, int ntab) 241 { 242 int i; 243 244 for(i=0; i<ntab; i++) 245 if(strcmp(tab[i].name, cmd) == 0) 246 return &tab[i]; 247 return nil; 248 } 249 250 static void 251 parseas(Req *r, char *arg, int type, void *a) 252 { 253 Url *u; 254 char e[ERRMAX]; 255 256 switch(type){ 257 case Bool: 258 if(strcmp(arg, "on")==0 || strcmp(arg, "1")==0) 259 *(int*)a = 1; 260 else 261 *(int*)a = 0; 262 break; 263 case String: 264 free(*(char**)a); 265 *(char**)a = estrdup(arg); 266 break; 267 case XUrl: 268 u = parseurl(arg, nil); 269 if(u == nil){ 270 snprint(e, sizeof e, "parseurl: %r"); 271 respond(r, e); 272 return; 273 } 274 freeurl(*(Url**)a); 275 *(Url**)a = u; 276 break; 277 case Int: 278 if(strcmp(arg, "on")==0) 279 *(int*)a = 1; 280 else 281 *(int*)a = atoi(arg); 282 break; 283 } 284 respond(r, nil); 285 } 286 287 int 288 ctlwrite(Req *r, Ctl *ctl, char *cmd, char *arg) 289 { 290 void *a; 291 Ctab *t; 292 293 if((t = findcmd(cmd, ctltab, nelem(ctltab))) == nil) 294 return 0; 295 a = (void*)((ulong)ctl+(int)t->offset); 296 parseas(r, arg, t->type, a); 297 return 1; 298 } 299 300 int 301 clientctlwrite(Req *r, Client *c, char *cmd, char *arg) 302 { 303 void *a; 304 Ctab *t; 305 306 if((t = findcmd(cmd, clienttab, nelem(clienttab))) == nil) 307 return 0; 308 a = (void*)((ulong)c+(int)t->offset); 309 parseas(r, arg, t->type, a); 310 return 1; 311 } 312 313 int 314 globalctlwrite(Req *r, char *cmd, char *arg) 315 { 316 void *a; 317 Ctab *t; 318 319 if((t = findcmd(cmd, globaltab, nelem(globaltab))) == nil) 320 return 0; 321 a = t->offset; 322 parseas(r, arg, t->type, a); 323 return 1; 324 } 325 326 static void 327 ctlfmt(Ctl *c, char *s) 328 { 329 int i; 330 void *a; 331 char *t; 332 333 for(i=0; i<nelem(ctltab); i++){ 334 a = (void*)((ulong)c+(int)ctltab[i].offset); 335 switch(ctltab[i].type){ 336 case Bool: 337 s += sprint(s, "%s %s\n", ctltab[i].name, *(int*)a ? "on" : "off"); 338 break; 339 case Int: 340 s += sprint(s, "%s %d\n", ctltab[i].name, *(int*)a); 341 break; 342 case String: 343 t = *(char**)a; 344 if(t != nil) 345 s += sprint(s, "%s %.*s%s\n", ctltab[i].name, utfnlen(t, 100), t, strlen(t)>100 ? "..." : ""); 346 break; 347 } 348 } 349 } 350 351 void 352 ctlread(Req *r, Client *c) 353 { 354 char buf[1024]; 355 356 sprint(buf, "%11d \n", c->num); 357 ctlfmt(&c->ctl, buf+strlen(buf)); 358 readstr(r, buf); 359 respond(r, nil); 360 } 361 362 void 363 globalctlread(Req *r) 364 { 365 char buf[1024], *s; 366 int i; 367 368 s = buf; 369 for(i=0; i<nelem(globaltab); i++) 370 s += sprint(s, "%s %d\n", globaltab[i].name, *(int*)globaltab[i].offset); 371 ctlfmt(&globalctl, s); 372 readstr(r, buf); 373 respond(r, nil); 374 } 375