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