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