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