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