URI:
       tfs.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tfs.c (32719B)
       ---
            1 #include "a.h"
            2 
            3 enum
            4 {
            5         Qroot = 0,        //  /smug/
            6         Qctl,             //  /smug/ctl
            7         Qrpclog,          //  /smug/rpclog
            8         Quploads,                // /smug/uploads
            9         Qnick,            //  /smug/nick/
           10         Qnickctl,         //  /smug/nick/ctl
           11         Qalbums,          //  /smug/nick/albums/
           12         Qalbumsctl,       //  /smug/nick/albums/ctl
           13         Qcategory,        //  /smug/nick/Category/
           14         Qcategoryctl,     //  /smug/nick/Category/ctl
           15         Qalbum,           //  /smug/nick/Category/Album/
           16         Qalbumctl,        //  /smug/nick/Category/Album/ctl
           17         Qalbumsettings,   //  /smug/nick/Category/Album/settings
           18         Quploadfile,      //  /smug/nick/Category/Album/upload/file.jpg
           19         Qimage,           //  /smug/nick/Category/Album/Image/
           20         Qimagectl,        //  /smug/nick/Category/Album/Image/ctl
           21         Qimageexif,       //  /smug/nick/Category/Album/Image/exif
           22         Qimagesettings,   //  /smug/nick/Category/Album/Image/settings
           23         Qimageurl,        //  /smug/nick/Category/Album/Image/url
           24         Qimagefile,       //  /smug/nick/Category/Album/Image/file.jpg
           25 };
           26 
           27 void
           28 mylock(Lock *lk)
           29 {
           30         lock(lk);
           31         fprint(2, "locked from %p\n", getcallerpc(&lk));
           32 }
           33 
           34 void
           35 myunlock(Lock *lk)
           36 {
           37         unlock(lk);
           38         fprint(2, "unlocked from %p\n", getcallerpc(&lk));
           39 }
           40 
           41 //#define lock mylock
           42 //#define unlock myunlock
           43 
           44 typedef struct Upload Upload;
           45 
           46 typedef struct SmugFid SmugFid;
           47 struct SmugFid
           48 {
           49         int type;
           50         int nickid;
           51         vlong category;  // -1 for "albums"
           52         vlong album;
           53         char *albumkey;
           54         vlong image;
           55         char *imagekey;
           56         Upload *upload;
           57         int upwriter;
           58 };
           59 
           60 #define QTYPE(p) ((p)&0xFF)
           61 #define QARG(p) ((p)>>8)
           62 #define QPATH(p, q) ((p)|((q)<<8))
           63 
           64 char **nick;
           65 int nnick;
           66 
           67 struct Upload
           68 {
           69         Lock lk;
           70         int fd;
           71         char *name;
           72         char *file;
           73         vlong album;
           74         vlong length;
           75         char *albumkey;
           76         int size;
           77         int ready;
           78         int nwriters;
           79         int uploaded;
           80         int ref;
           81         int uploading;
           82 };
           83 
           84 Upload **up;
           85 int nup;
           86 QLock uploadlock;
           87 Rendez uploadrendez;
           88 
           89 void uploader(void*);
           90 
           91 Upload*
           92 newupload(SmugFid *sf, char *name)
           93 {
           94         Upload *u;
           95         int fd, i;
           96         char tmp[] = "/var/tmp/smugfs.XXXXXX";
           97 
           98         if((fd = opentemp(tmp, ORDWR)) < 0)
           99                 return nil;
          100         qlock(&uploadlock);
          101         for(i=0; i<nup; i++){
          102                 u = up[i];
          103                 lock(&u->lk);
          104                 if(u->ref == 0){
          105                         u->ref = 1;
          106                         goto Reuse;
          107                 }
          108                 unlock(&u->lk);
          109         }
          110         if(nup == 0){
          111                 uploadrendez.l = &uploadlock;
          112                 proccreate(uploader, nil, STACKSIZE);
          113         }
          114         u = emalloc(sizeof *u);
          115         lock(&u->lk);
          116         u->ref = 1;
          117         up = erealloc(up, (nup+1)*sizeof up[0]);
          118         up[nup++] = u;
          119 Reuse:
          120         qunlock(&uploadlock);
          121         u->fd = fd;
          122         u->name = estrdup(name);
          123         u->file = estrdup(tmp);
          124         u->album = sf->album;
          125         u->albumkey = estrdup(sf->albumkey);
          126         u->nwriters = 1;
          127         unlock(&u->lk);
          128         return u;
          129 }
          130 
          131 void
          132 closeupload(Upload *u)
          133 {
          134         lock(&u->lk);
          135         if(--u->ref > 0){
          136                 unlock(&u->lk);
          137                 return;
          138         }
          139         if(u->ref < 0)
          140                 abort();
          141         if(u->fd >= 0){
          142                 close(u->fd);
          143                 u->fd = -1;
          144         }
          145         if(u->name){
          146                 free(u->name);
          147                 u->name = nil;
          148         }
          149         if(u->file){
          150                 remove(u->file);
          151                 free(u->file);
          152                 u->file = nil;
          153         }
          154         u->album = 0;
          155         if(u->albumkey){
          156                 free(u->albumkey);
          157                 u->albumkey = nil;
          158         }
          159         u->size = 0;
          160         u->ready = 0;
          161         u->nwriters = 0;
          162         u->uploaded = 0;
          163         u->uploading = 0;
          164         u->length = 0;
          165         unlock(&u->lk);
          166 }
          167 
          168 Upload*
          169 getuploadindex(SmugFid *sf, int *index)
          170 {
          171         int i;
          172         Upload *u;
          173 
          174         qlock(&uploadlock);
          175         for(i=0; i<nup; i++){
          176                 u = up[i];
          177                 lock(&u->lk);
          178                 if(u->ref > 0 && !u->uploaded && u->album == sf->album && (*index)-- == 0){
          179                         qunlock(&uploadlock);
          180                         u->ref++;
          181                         unlock(&u->lk);
          182                         return u;
          183                 }
          184                 unlock(&u->lk);
          185         }
          186         qunlock(&uploadlock);
          187         return nil;
          188 }
          189 
          190 Upload*
          191 getuploadname(SmugFid *sf, char *name)
          192 {
          193         int i;
          194         Upload *u;
          195 
          196         qlock(&uploadlock);
          197         for(i=0; i<nup; i++){
          198                 u = up[i];
          199                 lock(&u->lk);
          200                 if(u->ref > 0 && !u->uploaded && u->album == sf->album && strcmp(name, u->name) == 0){
          201                         qunlock(&uploadlock);
          202                         u->ref++;
          203                         unlock(&u->lk);
          204                         return u;
          205                 }
          206                 unlock(&u->lk);
          207         }
          208         qunlock(&uploadlock);
          209         return nil;
          210 }
          211 
          212 void doupload(Upload*);
          213 
          214 void
          215 uploader(void *v)
          216 {
          217         int i, did;
          218         Upload *u;
          219 
          220         qlock(&uploadlock);
          221         for(;;){
          222                 did = 0;
          223                 for(i=0; i<nup; i++){
          224                         u = up[i];
          225                         lock(&u->lk);
          226                         if(u->ref > 0 && u->ready && !u->uploading && !u->uploaded){
          227                                 u->uploading = 1;
          228                                 unlock(&u->lk);
          229                                 qunlock(&uploadlock);
          230                                 doupload(u);
          231                                 closeupload(u);
          232                                 did = 1;
          233                                 qlock(&uploadlock);
          234                         }else
          235                                 unlock(&u->lk);
          236                 }
          237                 if(!did)
          238                         rsleep(&uploadrendez);
          239         }
          240 }
          241 
          242 void
          243 kickupload(Upload *u)
          244 {
          245         Dir *d;
          246 
          247         lock(&u->lk);
          248         if((d = dirfstat(u->fd)) != nil)
          249                 u->length = d->length;
          250         close(u->fd);
          251         u->fd = -1;
          252         u->ref++;
          253         u->ready = 1;
          254         unlock(&u->lk);
          255         qlock(&uploadlock);
          256         rwakeup(&uploadrendez);
          257         qunlock(&uploadlock);
          258 }
          259 
          260 void
          261 doupload(Upload *u)
          262 {
          263         Dir *d;
          264         vlong datalen;
          265         Fmt fmt;
          266         char *req;
          267         char buf[8192];
          268         int n, total;
          269         uchar digest[MD5dlen];
          270         DigestState ds;
          271         Json *jv;
          272 
          273         if((u->fd = open(u->file, OREAD)) < 0){
          274                 fprint(2, "cannot reopen temporary file %s: %r\n", u->file);
          275                 return;
          276         }
          277         if((d = dirfstat(u->fd)) == nil){
          278                 fprint(2, "fstat: %r\n");
          279                 return;
          280         }
          281         datalen = d->length;
          282         free(d);
          283 
          284         memset(&ds, 0, sizeof ds);
          285         seek(u->fd, 0, 0);
          286         total = 0;
          287         while((n = read(u->fd, buf, sizeof buf)) > 0){
          288                 md5((uchar*)buf, n, nil, &ds);
          289                 total += n;
          290         }
          291         if(total != datalen){
          292                 fprint(2, "bad total: %lld %lld\n", total, datalen);
          293                 return;
          294         }
          295         md5(nil, 0, digest, &ds);
          296 
          297         fmtstrinit(&fmt);
          298         fmtprint(&fmt, "PUT /%s HTTP/1.0\r\n", u->name);
          299         fmtprint(&fmt, "Content-Length: %lld\r\n", datalen);
          300         fmtprint(&fmt, "Content-MD5: %.16lH\r\n", digest);
          301         fmtprint(&fmt, "X-Smug-SessionID: %s\r\n", sessid);
          302         fmtprint(&fmt, "X-Smug-Version: %s\r\n", API_VERSION);
          303         fmtprint(&fmt, "X-Smug-ResponseType: JSON\r\n");
          304         // Can send X-Smug-ImageID instead to replace existing files.
          305         fmtprint(&fmt, "X-Smug-AlbumID: %lld\r\n", u->album);
          306         fmtprint(&fmt, "X-Smug-FileName: %s\r\n", u->name);
          307         fmtprint(&fmt, "\r\n");
          308         req = fmtstrflush(&fmt);
          309 
          310         seek(u->fd, 0, 0);
          311         jv = jsonupload(&http, UPLOAD_HOST, req, u->fd, datalen);
          312         free(req);
          313         if(jv == nil){
          314                 fprint(2, "upload: %r\n");
          315                 return;
          316         }
          317 
          318         close(u->fd);
          319         remove(u->file);
          320         free(u->file);
          321         u->file = nil;
          322         u->fd = -1;
          323         u->uploaded = 1;
          324         rpclog("uploaded: %J", jv);
          325         jclose(jv);
          326 }
          327 
          328 int
          329 nickindex(char *name)
          330 {
          331         int i;
          332         Json *v;
          333 
          334         for(i=0; i<nnick; i++)
          335                 if(strcmp(nick[i], name) == 0)
          336                         return i;
          337         v = smug("smugmug.users.getTree", "NickName", name, nil);
          338         if(v == nil)
          339                 return -1;
          340         nick = erealloc(nick, (nnick+1)*sizeof nick[0]);
          341         nick[nnick] = estrdup(name);
          342         return nnick++;
          343 }
          344 
          345 char*
          346 nickname(int i)
          347 {
          348         if(i < 0 || i >= nnick)
          349                 return nil;
          350         return nick[i];
          351 }
          352 
          353 void
          354 responderrstr(Req *r)
          355 {
          356         char err[ERRMAX];
          357 
          358         rerrstr(err, sizeof err);
          359         respond(r, err);
          360 }
          361 
          362 static char*
          363 xclone(Fid *oldfid, Fid *newfid)
          364 {
          365         SmugFid *sf;
          366 
          367         if(oldfid->aux == nil)
          368                 return nil;
          369 
          370         sf = emalloc(sizeof *sf);
          371         *sf = *(SmugFid*)oldfid->aux;
          372         sf->upload = nil;
          373         sf->upwriter = 0;
          374         if(sf->albumkey)
          375                 sf->albumkey = estrdup(sf->albumkey);
          376         if(sf->imagekey)
          377                 sf->imagekey = estrdup(sf->imagekey);
          378         newfid->aux = sf;
          379         return nil;
          380 }
          381 
          382 static void
          383 xdestroyfid(Fid *fid)
          384 {
          385         SmugFid *sf;
          386 
          387         sf = fid->aux;
          388         free(sf->albumkey);
          389         free(sf->imagekey);
          390         if(sf->upload){
          391                 if(sf->upwriter && --sf->upload->nwriters == 0){
          392                         fprint(2, "should upload %s\n", sf->upload->name);
          393                         kickupload(sf->upload);
          394                 }
          395                 closeupload(sf->upload);
          396                 sf->upload = nil;
          397         }
          398         free(sf);
          399 }
          400 
          401 static Json*
          402 getcategories(SmugFid *sf)
          403 {
          404         Json *v, *w;
          405 
          406         v = smug("smugmug.categories.get", "NickName", nickname(sf->nickid), nil);
          407         w = jincref(jwalk(v, "Categories"));
          408         jclose(v);
          409         return w;
          410 }
          411 
          412 static Json*
          413 getcategorytree(SmugFid *sf)
          414 {
          415         Json *v, *w;
          416 
          417         v = smug("smugmug.users.getTree", "NickName", nickname(sf->nickid), nil);
          418         w = jincref(jwalk(v, "Categories"));
          419         jclose(v);
          420         return w;
          421 }
          422 
          423 static Json*
          424 getcategory(SmugFid *sf, vlong id)
          425 {
          426         int i;
          427         Json *v, *w;
          428 
          429         v = getcategorytree(sf);
          430         if(v == nil)
          431                 return nil;
          432         for(i=0; i<v->len; i++){
          433                 if(jint(jwalk(v->value[i], "id")) == id){
          434                         w = jincref(v->value[i]);
          435                         jclose(v);
          436                         return w;
          437                 }
          438         }
          439         jclose(v);
          440         return nil;
          441 }
          442 
          443 static vlong
          444 getcategoryid(SmugFid *sf, char *name)
          445 {
          446         int i;
          447         vlong id;
          448         Json *v;
          449 
          450         v = getcategories(sf);
          451         if(v == nil)
          452                 return -1;
          453         for(i=0; i<v->len; i++){
          454                 if(jstrcmp(jwalk(v->value[i], "Name"), name) == 0){
          455                         id = jint(jwalk(v->value[i], "id"));
          456                         if(id < 0){
          457                                 jclose(v);
          458                                 return -1;
          459                         }
          460                         jclose(v);
          461                         return id;
          462                 }
          463         }
          464         jclose(v);
          465         return -1;
          466 }
          467 
          468 static vlong
          469 getcategoryindex(SmugFid *sf, int i)
          470 {
          471         Json *v;
          472         vlong id;
          473 
          474         v = getcategories(sf);
          475         if(v == nil)
          476                 return -1;
          477         if(i < 0 || i >= v->len){
          478                 jclose(v);
          479                 return -1;
          480         }
          481         id = jint(jwalk(v->value[i], "id"));
          482         jclose(v);
          483         return id;
          484 }
          485 
          486 static Json*
          487 getalbum(SmugFid *sf, vlong albumid, char *albumkey)
          488 {
          489         char id[50];
          490         Json *v, *w;
          491 
          492         snprint(id, sizeof id, "%lld", albumid);
          493         v = smug("smugmug.albums.getInfo",
          494                 "AlbumID", id, "AlbumKey", albumkey,
          495                 "NickName", nickname(sf->nickid), nil);
          496         w = jincref(jwalk(v, "Album"));
          497         jclose(v);
          498         return w;
          499 }
          500 
          501 static Json*
          502 getalbums(SmugFid *sf)
          503 {
          504         Json *v, *w;
          505 
          506         if(sf->category >= 0)
          507                 v = getcategory(sf, sf->category);
          508         else
          509                 v = smug("smugmug.albums.get",
          510                         "NickName", nickname(sf->nickid), nil);
          511         w = jincref(jwalk(v, "Albums"));
          512         jclose(v);
          513         return w;
          514 }
          515 
          516 static vlong
          517 getalbumid(SmugFid *sf, char *name, char **keyp)
          518 {
          519         int i;
          520         vlong id;
          521         Json *v;
          522         char *key;
          523 
          524         v = getalbums(sf);
          525         if(v == nil)
          526                 return -1;
          527         for(i=0; i<v->len; i++){
          528                 if(jstrcmp(jwalk(v->value[i], "Title"), name) == 0){
          529                         id = jint(jwalk(v->value[i], "id"));
          530                         key = jstring(jwalk(v->value[i], "Key"));
          531                         if(id < 0 || key == nil){
          532                                 jclose(v);
          533                                 return -1;
          534                         }
          535                         if(keyp)
          536                                 *keyp = estrdup(key);
          537                         jclose(v);
          538                         return id;
          539                 }
          540         }
          541         jclose(v);
          542         return -1;
          543 }
          544 
          545 static vlong
          546 getalbumindex(SmugFid *sf, int i, char **keyp)
          547 {
          548         vlong id;
          549         Json *v;
          550         char *key;
          551 
          552         v = getalbums(sf);
          553         if(v == nil)
          554                 return -1;
          555         if(i < 0 || i >= v->len){
          556                 jclose(v);
          557                 return -1;
          558         }
          559         id = jint(jwalk(v->value[i], "id"));
          560         key = jstring(jwalk(v->value[i], "Key"));
          561         if(id < 0 || key == nil){
          562                 jclose(v);
          563                 return -1;
          564         }
          565         if(keyp)
          566                 *keyp = estrdup(key);
          567         jclose(v);
          568         return id;
          569 }
          570 
          571 static Json*
          572 getimages(SmugFid *sf, vlong albumid, char *albumkey)
          573 {
          574         char id[50];
          575         Json *v, *w;
          576 
          577         snprint(id, sizeof id, "%lld", albumid);
          578         v = smug("smugmug.images.get",
          579                 "AlbumID", id, "AlbumKey", albumkey,
          580                 "NickName", nickname(sf->nickid), nil);
          581         w = jincref(jwalk(v, "Images"));
          582         jclose(v);
          583         return w;
          584 }
          585 
          586 static vlong
          587 getimageid(SmugFid *sf, char *name, char **keyp)
          588 {
          589         int i;
          590         vlong id;
          591         Json *v;
          592         char *p;
          593         char *key;
          594 
          595         id = strtol(name, &p, 10);
          596         if(*p != 0 || *name == 0)
          597                 return -1;
          598 
          599         v = getimages(sf, sf->album, sf->albumkey);
          600         if(v == nil)
          601                 return -1;
          602         for(i=0; i<v->len; i++){
          603                 if(jint(jwalk(v->value[i], "id")) == id){
          604                         key = jstring(jwalk(v->value[i], "Key"));
          605                         if(key == nil){
          606                                 jclose(v);
          607                                 return -1;
          608                         }
          609                         if(keyp)
          610                                 *keyp = estrdup(key);
          611                         jclose(v);
          612                         return id;
          613                 }
          614         }
          615         jclose(v);
          616         return -1;
          617 }
          618 
          619 static Json*
          620 getimageinfo(SmugFid *sf, vlong imageid, char *imagekey)
          621 {
          622         char id[50];
          623         Json *v, *w;
          624 
          625         snprint(id, sizeof id, "%lld", imageid);
          626         v = smug("smugmug.images.getInfo",
          627                 "ImageID", id, "ImageKey", imagekey,
          628                 "NickName", nickname(sf->nickid), nil);
          629         w = jincref(jwalk(v, "Image"));
          630         jclose(v);
          631         return w;
          632 }
          633 
          634 static Json*
          635 getimageexif(SmugFid *sf, vlong imageid, char *imagekey)
          636 {
          637         char id[50];
          638         Json *v, *w;
          639 
          640         snprint(id, sizeof id, "%lld", imageid);
          641         v = smug("smugmug.images.getEXIF",
          642                 "ImageID", id, "ImageKey", imagekey,
          643                 "NickName", nickname(sf->nickid), nil);
          644         w = jincref(jwalk(v, "Image"));
          645         jclose(v);
          646         return w;
          647 }
          648 
          649 static vlong
          650 getimageindex(SmugFid *sf, int i, char **keyp)
          651 {
          652         vlong id;
          653         Json *v;
          654         char *key;
          655 
          656         v = getimages(sf, sf->album, sf->albumkey);
          657         if(v == nil)
          658                 return -1;
          659         if(i < 0 || i >= v->len){
          660                 jclose(v);
          661                 return -1;
          662         }
          663         id = jint(jwalk(v->value[i], "id"));
          664         key = jstring(jwalk(v->value[i], "Key"));
          665         if(id < 0 || key == nil){
          666                 jclose(v);
          667                 return -1;
          668         }
          669         if(keyp)
          670                 *keyp = estrdup(key);
          671         jclose(v);
          672         return id;
          673 }
          674 
          675 static char*
          676 categoryname(SmugFid *sf)
          677 {
          678         Json *v;
          679         char *s;
          680 
          681         v = getcategory(sf, sf->category);
          682         s = jstring(jwalk(v, "Name"));
          683         if(s)
          684                 s = estrdup(s);
          685         jclose(v);
          686         return s;
          687 }
          688 
          689 static char*
          690 albumname(SmugFid *sf)
          691 {
          692         Json *v;
          693         char *s;
          694 
          695         v = getalbum(sf, sf->album, sf->albumkey);
          696         s = jstring(jwalk(v, "Title"));
          697         if(s)
          698                 s = estrdup(s);
          699         jclose(v);
          700         return s;
          701 }
          702 
          703 static char*
          704 imagename(SmugFid *sf)
          705 {
          706         char *s;
          707         Json *v;
          708 
          709         v = getimageinfo(sf, sf->image, sf->imagekey);
          710         s = jstring(jwalk(v, "FileName"));
          711         if(s && s[0])
          712                 s = estrdup(s);
          713         else
          714                 s = smprint("%lld.jpg", sf->image);        // TODO: use Format
          715         jclose(v);
          716         return s;
          717 }
          718 
          719 static vlong
          720 imagelength(SmugFid *sf)
          721 {
          722         vlong length;
          723         Json *v;
          724 
          725         v = getimageinfo(sf, sf->image, sf->imagekey);
          726         length = jint(jwalk(v, "Size"));
          727         jclose(v);
          728         return length;
          729 }
          730 
          731 static struct {
          732         char *key;
          733         char *name;
          734 } urls[] = {
          735         "AlbumURL", "album",
          736         "TinyURL", "tiny",
          737         "ThumbURL", "thumb",
          738         "SmallURL", "small",
          739         "MediumURL", "medium",
          740         "LargeURL", "large",
          741         "XLargeURL", "xlarge",
          742         "X2LargeURL", "xxlarge",
          743         "X3LargeURL", "xxxlarge",
          744         "OriginalURL", "original",
          745 };
          746 
          747 static char*
          748 imageurl(SmugFid *sf)
          749 {
          750         Json *v;
          751         char *s;
          752         int i;
          753 
          754         v = getimageinfo(sf, sf->image, sf->imagekey);
          755         for(i=nelem(urls)-1; i>=0; i--){
          756                 if((s = jstring(jwalk(v, urls[i].key))) != nil){
          757                         s = estrdup(s);
          758                         jclose(v);
          759                         return s;
          760                 }
          761         }
          762         jclose(v);
          763         return nil;
          764 }
          765 
          766 static char* imagestrings[] =
          767 {
          768         "Caption",
          769         "LastUpdated",
          770         "FileName",
          771         "MD5Sum",
          772         "Watermark",
          773         "Format",
          774         "Keywords",
          775         "Date",
          776         "AlbumURL",
          777         "TinyURL",
          778         "ThumbURL",
          779         "SmallURL",
          780         "MediumURL",
          781         "LargeURL",
          782         "XLargeURL",
          783         "X2LargeURL",
          784         "X3LargeURL",
          785         "OriginalURL",
          786         "Album",
          787 };
          788 
          789 static char* albumbools[] =
          790 {
          791         "Public",
          792         "Printable",
          793         "Filenames",
          794         "Comments",
          795         "External",
          796         "Originals",
          797         "EXIF",
          798         "Share",
          799         "SortDirection",
          800         "FamilyEdit",
          801         "FriendEdit",
          802         "HideOwner",
          803         "CanRank",
          804         "Clean",
          805         "Geography",
          806         "SmugSearchable",
          807         "WorldSearchable",
          808         "SquareThumbs",
          809         "X2Larges",
          810         "X3Larges",
          811 };
          812 
          813 static char* albumstrings[] =
          814 {
          815         "Description"
          816         "Keywords",
          817         "Password",
          818         "PasswordHint",
          819         "SortMethod",
          820         "LastUpdated",
          821 };
          822 
          823 static char*
          824 readctl(SmugFid *sf)
          825 {
          826         int i;
          827         Upload *u;
          828         char *s;
          829         Json *v, *vv;
          830         Fmt fmt;
          831 
          832         v = nil;
          833         switch(sf->type){
          834         case Qctl:
          835                 return smprint("%#J\n", userinfo);
          836 
          837         case Quploads:
          838                 fmtstrinit(&fmt);
          839                 qlock(&uploadlock);
          840                 for(i=0; i<nup; i++){
          841                         u = up[i];
          842                         lock(&u->lk);
          843                         if(u->ready && !u->uploaded && u->ref > 0)
          844                                 fmtprint(&fmt, "%s %s%s\n", u->name, u->file, u->uploading ? " [uploading]" : "");
          845                         unlock(&u->lk);
          846                 }
          847                 qunlock(&uploadlock);
          848                 return fmtstrflush(&fmt);
          849 
          850         case Qnickctl:
          851                 v = getcategories(sf);
          852                 break;
          853 
          854         case Qcategoryctl:
          855                 v = getcategory(sf, sf->category);
          856                 break;
          857 
          858         case Qalbumctl:
          859                 v = getimages(sf, sf->album, sf->albumkey);
          860                 break;
          861 
          862         case Qalbumsctl:
          863                 v = getalbums(sf);
          864                 break;
          865 
          866         case Qimagectl:
          867                 v = getimageinfo(sf, sf->image, sf->imagekey);
          868                 break;
          869 
          870         case Qimageurl:
          871                 v = getimageinfo(sf, sf->image, sf->imagekey);
          872                 fmtstrinit(&fmt);
          873                 for(i=0; i<nelem(urls); i++)
          874                         if((s = jstring(jwalk(v, urls[i].key))) != nil)
          875                                 fmtprint(&fmt, "%s %s\n", urls[i].name, s);
          876                 jclose(v);
          877                 return fmtstrflush(&fmt);
          878 
          879         case Qimageexif:
          880                 v = getimageexif(sf, sf->image, sf->imagekey);
          881                 break;
          882 
          883         case Qalbumsettings:
          884                 v = getalbum(sf, sf->album, sf->albumkey);
          885                 fmtstrinit(&fmt);
          886                 fmtprint(&fmt, "id\t%lld\n", jint(jwalk(v, "id")));
          887                 // TODO: Category/id
          888                 // TODO: SubCategory/id
          889                 // TODO: Community/id
          890                 // TODO: Template/id
          891                 fmtprint(&fmt, "Highlight\t%lld\n", jint(jwalk(v, "Highlight/id")));
          892                 fmtprint(&fmt, "Position\t%lld\n", jint(jwalk(v, "Position")));
          893                 fmtprint(&fmt, "ImageCount\t%lld\n", jint(jwalk(v, "ImageCount")));
          894                 for(i=0; i<nelem(albumbools); i++){
          895                         vv = jwalk(v, albumbools[i]);
          896                         if(vv)
          897                                 fmtprint(&fmt, "%s\t%J\n", albumbools[i], vv);
          898                 }
          899                 for(i=0; i<nelem(albumstrings); i++){
          900                         s = jstring(jwalk(v, albumstrings[i]));
          901                         if(s)
          902                                 fmtprint(&fmt, "%s\t%s\n", albumstrings[i], s);
          903                 }
          904                 s = fmtstrflush(&fmt);
          905                 jclose(v);
          906                 return s;
          907 
          908         case Qimagesettings:
          909                 v = getimageinfo(sf, sf->image, sf->imagekey);
          910                 fmtstrinit(&fmt);
          911                 fmtprint(&fmt, "id\t%lld\n", jint(jwalk(v, "id")));
          912                 fmtprint(&fmt, "Position\t%lld\n", jint(jwalk(v, "Position")));
          913                 fmtprint(&fmt, "Serial\t%lld\n", jint(jwalk(v, "Serial")));
          914                 fmtprint(&fmt, "Size\t%lld\t%lldx%lld\n",
          915                         jint(jwalk(v, "Size")),
          916                         jint(jwalk(v, "Width")),
          917                         jint(jwalk(v, "Height")));
          918                 vv = jwalk(v, "Hidden");
          919                 fmtprint(&fmt, "Hidden\t%J\n", vv);
          920                 // TODO: Album/id
          921                 for(i=0; i<nelem(imagestrings); i++){
          922                         s = jstring(jwalk(v, imagestrings[i]));
          923                         if(s)
          924                                 fmtprint(&fmt, "%s\t%s\n", imagestrings[i], s);
          925                 }
          926                 s = fmtstrflush(&fmt);
          927                 jclose(v);
          928                 return s;
          929         }
          930 
          931         if(v == nil)
          932                 return estrdup("");
          933         s = smprint("%#J\n", v);
          934         jclose(v);
          935         return s;
          936 }
          937 
          938 
          939 static void
          940 dostat(SmugFid *sf, Qid *qid, Dir *dir)
          941 {
          942         Qid q;
          943         char *name;
          944         int freename;
          945         ulong mode;
          946         char *uid;
          947         char *s;
          948         vlong length;
          949 
          950         memset(&q, 0, sizeof q);
          951         name = nil;
          952         freename = 0;
          953         uid = "smugfs";
          954         q.type = 0;
          955         q.vers = 0;
          956         q.path = QPATH(sf->type, sf->nickid);
          957         length = 0;
          958         mode = 0444;
          959 
          960         switch(sf->type){
          961         case Qroot:
          962                 name = "/";
          963                 q.type = QTDIR;
          964                 break;
          965         case Qctl:
          966                 name = "ctl";
          967                 mode |= 0222;
          968                 break;
          969         case Quploads:
          970                 name = "uploads";
          971                 s = readctl(sf);
          972                 if(s){
          973                         length = strlen(s);
          974                         free(s);
          975                 }
          976                 break;
          977         case Qrpclog:
          978                 name = "rpclog";
          979                 break;
          980         case Qnick:
          981                 name = nickname(sf->nickid);
          982                 q.type = QTDIR;
          983                 break;
          984         case Qnickctl:
          985                 name = "ctl";
          986                 mode |= 0222;
          987                 break;
          988         case Qalbums:
          989                 name = "albums";
          990                 q.type = QTDIR;
          991                 break;
          992         case Qalbumsctl:
          993                 name = "ctl";
          994                 mode |= 0222;
          995                 break;
          996         case Qcategory:
          997                 name = categoryname(sf);
          998                 freename = 1;
          999                 q.path |= QPATH(0, sf->category << 8);
         1000                 q.type = QTDIR;
         1001                 break;
         1002         case Qcategoryctl:
         1003                 name = "ctl";
         1004                 mode |= 0222;
         1005                 q.path |= QPATH(0, sf->category << 8);
         1006                 break;
         1007         case Qalbum:
         1008                 name = albumname(sf);
         1009                 freename = 1;
         1010                 q.path |= QPATH(0, sf->album << 8);
         1011                 q.type = QTDIR;
         1012                 break;
         1013         case Qalbumctl:
         1014                 name = "ctl";
         1015                 mode |= 0222;
         1016                 q.path |= QPATH(0, sf->album << 8);
         1017                 break;
         1018         case Qalbumsettings:
         1019                 name = "settings";
         1020                 mode |= 0222;
         1021                 q.path |= QPATH(0, sf->album << 8);
         1022                 break;
         1023         case Quploadfile:
         1024                 q.path |= QPATH(0, (uintptr)sf->upload << 8);
         1025                 if(sf->upload){
         1026                         Dir *dd;
         1027                         name = sf->upload->name;
         1028                         if(sf->upload->fd >= 0){
         1029                                 dd = dirfstat(sf->upload->fd);
         1030                                 if(dd){
         1031                                         length = dd->length;
         1032                                         free(dd);
         1033                                 }
         1034                         }else
         1035                                 length = sf->upload->length;
         1036                         if(!sf->upload->ready)
         1037                                 mode |= 0222;
         1038                 }
         1039                 break;
         1040         case Qimage:
         1041                 name = smprint("%lld", sf->image);
         1042                 freename = 1;
         1043                 q.path |= QPATH(0, sf->image << 8);
         1044                 q.type = QTDIR;
         1045                 break;
         1046         case Qimagectl:
         1047                 name = "ctl";
         1048                 mode |= 0222;
         1049                 q.path |= QPATH(0, sf->image << 8);
         1050                 break;
         1051         case Qimagesettings:
         1052                 name = "settings";
         1053                 mode |= 0222;
         1054                 q.path |= QPATH(0, sf->image << 8);
         1055                 break;
         1056         case Qimageexif:
         1057                 name = "exif";
         1058                 q.path |= QPATH(0, sf->image << 8);
         1059                 break;
         1060         case Qimageurl:
         1061                 name = "url";
         1062                 q.path |= QPATH(0, sf->image << 8);
         1063                 break;
         1064         case Qimagefile:
         1065                 name = imagename(sf);
         1066                 freename = 1;
         1067                 q.path |= QPATH(0, sf->image << 8);
         1068                 length = imagelength(sf);
         1069                 break;
         1070         default:
         1071                 name = "?egreg";
         1072                 q.path = 0;
         1073                 break;
         1074         }
         1075 
         1076         if(name == nil){
         1077                 name = "???";
         1078                 freename = 0;
         1079         }
         1080 
         1081         if(qid)
         1082                 *qid = q;
         1083         if(dir){
         1084                 memset(dir, 0, sizeof *dir);
         1085                 dir->name = estrdup9p(name);
         1086                 dir->muid = estrdup9p("muid");
         1087                 mode |= q.type<<24;
         1088                 if(mode & DMDIR)
         1089                         mode |= 0755;
         1090                 dir->mode = mode;
         1091                 dir->uid = estrdup9p(uid);
         1092                 dir->gid = estrdup9p("smugfs");
         1093                 dir->qid = q;
         1094                 dir->length = length;
         1095         }
         1096         if(freename)
         1097                 free(name);
         1098 }
         1099 
         1100 static char*
         1101 xwalk1(Fid *fid, char *name, Qid *qid)
         1102 {
         1103         int dotdot, i;
         1104         vlong id;
         1105         char *key;
         1106         SmugFid *sf;
         1107         char *x;
         1108         Upload *u;
         1109 
         1110         dotdot = strcmp(name, "..") == 0;
         1111         sf = fid->aux;
         1112         switch(sf->type){
         1113         default:
         1114         NotFound:
         1115                 return "file not found";
         1116 
         1117         case Qroot:
         1118                 if(dotdot)
         1119                         break;
         1120                 if(strcmp(name, "ctl") == 0){
         1121                         sf->type = Qctl;
         1122                         break;
         1123                 }
         1124                 if(strcmp(name, "uploads") == 0){
         1125                         sf->type = Quploads;
         1126                         break;
         1127                 }
         1128                 if(strcmp(name, "rpclog") == 0){
         1129                         sf->type = Qrpclog;
         1130                         break;
         1131                 }
         1132                 if((i = nickindex(name)) >= 0){
         1133                         sf->nickid = i;
         1134                         sf->type = Qnick;
         1135                         break;
         1136                 }
         1137                 goto NotFound;
         1138 
         1139         case Qnick:
         1140                 if(dotdot){
         1141                         sf->type = Qroot;
         1142                         sf->nickid = 0;
         1143                         break;
         1144                 }
         1145                 if(strcmp(name, "ctl") == 0){
         1146                         sf->type = Qnickctl;
         1147                         break;
         1148                 }
         1149                 if(strcmp(name, "albums") == 0){
         1150                         sf->category = -1;
         1151                         sf->type = Qalbums;
         1152                         break;
         1153                 }
         1154                 if((id = getcategoryid(sf, name)) >= 0){
         1155                         sf->category = id;
         1156                         sf->type = Qcategory;
         1157                         break;
         1158                 }
         1159                 goto NotFound;
         1160 
         1161         case Qalbums:
         1162         case Qcategory:
         1163                 if(dotdot){
         1164                         sf->category = 0;
         1165                         sf->type = Qnick;
         1166                         break;
         1167                 }
         1168                 if(strcmp(name, "ctl") == 0){
         1169                         sf->type++;
         1170                         break;
         1171                 }
         1172                 if((id = getalbumid(sf, name, &key)) >= 0){
         1173                         sf->album = id;
         1174                         sf->albumkey = key;
         1175                         sf->type = Qalbum;
         1176                         break;
         1177                 }
         1178                 goto NotFound;
         1179 
         1180         case Qalbum:
         1181                 if(dotdot){
         1182                         free(sf->albumkey);
         1183                         sf->albumkey = nil;
         1184                         sf->album = 0;
         1185                         if(sf->category == -1)
         1186                                 sf->type = Qalbums;
         1187                         else
         1188                                 sf->type = Qcategory;
         1189                         break;
         1190                 }
         1191                 if(strcmp(name, "ctl") == 0){
         1192                         sf->type = Qalbumctl;
         1193                         break;
         1194                 }
         1195                 if(strcmp(name, "settings") == 0){
         1196                         sf->type = Qalbumsettings;
         1197                         break;
         1198                 }
         1199                 if((id = getimageid(sf, name, &key)) >= 0){
         1200                         sf->image = id;
         1201                         sf->imagekey = key;
         1202                         sf->type = Qimage;
         1203                         break;
         1204                 }
         1205                 if((u = getuploadname(sf, name)) != nil){
         1206                         sf->upload = u;
         1207                         sf->type = Quploadfile;
         1208                         break;
         1209                 }
         1210                 goto NotFound;
         1211 
         1212         case Qimage:
         1213                 if(dotdot){
         1214                         free(sf->imagekey);
         1215                         sf->imagekey = nil;
         1216                         sf->image = 0;
         1217                         sf->type = Qalbum;
         1218                         break;
         1219                 }
         1220                 if(strcmp(name, "ctl") == 0){
         1221                         sf->type = Qimagectl;
         1222                         break;
         1223                 }
         1224                 if(strcmp(name, "url") == 0){
         1225                         sf->type = Qimageurl;
         1226                         break;
         1227                 }
         1228                 if(strcmp(name, "settings") == 0){
         1229                         sf->type = Qimagesettings;
         1230                         break;
         1231                 }
         1232                 if(strcmp(name, "exif") == 0){
         1233                         sf->type = Qimageexif;
         1234                         break;
         1235                 }
         1236                 x = imagename(sf);
         1237                 if(x && strcmp(name, x) == 0){
         1238                         free(x);
         1239                         sf->type = Qimagefile;
         1240                         break;
         1241                 }
         1242                 free(x);
         1243                 goto NotFound;
         1244         }
         1245         dostat(sf, qid, nil);
         1246         fid->qid = *qid;
         1247         return nil;
         1248 }
         1249 
         1250 static int
         1251 dodirgen(int i, Dir *d, void *v)
         1252 {
         1253         SmugFid *sf, xsf;
         1254         char *key;
         1255         vlong id;
         1256         Upload *u;
         1257 
         1258         sf = v;
         1259         xsf = *sf;
         1260         if(i-- == 0){
         1261                 xsf.type++;        // ctl in every directory
         1262                 dostat(&xsf, nil, d);
         1263                 return 0;
         1264         }
         1265 
         1266         switch(sf->type){
         1267         default:
         1268                 return -1;
         1269 
         1270         case Qroot:
         1271                 if(i-- == 0){
         1272                         xsf.type = Qrpclog;
         1273                         dostat(&xsf, nil, d);
         1274                         return 0;
         1275                 }
         1276                 if(i < 0 || i >= nnick)
         1277                         return -1;
         1278                 xsf.type = Qnick;
         1279                 xsf.nickid = i;
         1280                 dostat(&xsf, nil, d);
         1281                 return 0;
         1282 
         1283         case Qnick:
         1284                 if(i-- == 0){
         1285                         xsf.type = Qalbums;
         1286                         dostat(&xsf, nil, d);
         1287                         return 0;
         1288                 }
         1289                 if((id = getcategoryindex(sf, i)) < 0)
         1290                         return -1;
         1291                 xsf.type = Qcategory;
         1292                 xsf.category = id;
         1293                 dostat(&xsf, nil, d);
         1294                 return 0;
         1295 
         1296         case Qalbums:
         1297         case Qcategory:
         1298                 if((id = getalbumindex(sf, i, &key)) < 0)
         1299                         return -1;
         1300                 xsf.type = Qalbum;
         1301                 xsf.album = id;
         1302                 xsf.albumkey = key;
         1303                 dostat(&xsf, nil, d);
         1304                 free(key);
         1305                 return 0;
         1306 
         1307         case Qalbum:
         1308                 if(i-- == 0){
         1309                         xsf.type = Qalbumsettings;
         1310                         dostat(&xsf, nil, d);
         1311                         return 0;
         1312                 }
         1313                 if((u = getuploadindex(sf, &i)) != nil){
         1314                         xsf.upload = u;
         1315                         xsf.type = Quploadfile;
         1316                         dostat(&xsf, nil, d);
         1317                         closeupload(u);
         1318                         return 0;
         1319                 }
         1320                 if((id = getimageindex(sf, i, &key)) < 0)
         1321                         return -1;
         1322                 xsf.type = Qimage;
         1323                 xsf.image = id;
         1324                 xsf.imagekey = key;
         1325                 dostat(&xsf, nil, d);
         1326                 free(key);
         1327                 return 0;
         1328 
         1329         case Qimage:
         1330                 if(i-- == 0){
         1331                         xsf.type = Qimagefile;
         1332                         dostat(&xsf, nil, d);
         1333                         return 0;
         1334                 }
         1335                 if(i-- == 0){
         1336                         xsf.type = Qimageexif;
         1337                         dostat(&xsf, nil, d);
         1338                         return 0;
         1339                 }
         1340                 if(i-- == 0){
         1341                         xsf.type = Qimagesettings;
         1342                         dostat(&xsf, nil, d);
         1343                         return 0;
         1344                 }
         1345                 if(i-- == 0){
         1346                         xsf.type = Qimageurl;
         1347                         dostat(&xsf, nil, d);
         1348                         return 0;
         1349                 }
         1350                 return -1;
         1351         }
         1352 }
         1353 
         1354 static void
         1355 xstat(Req *r)
         1356 {
         1357         dostat(r->fid->aux, nil, &r->d);
         1358         respond(r, nil);
         1359 }
         1360 
         1361 static void
         1362 xwstat(Req *r)
         1363 {
         1364         SmugFid *sf;
         1365         Json *v;
         1366         char *s;
         1367         char strid[50];
         1368 
         1369         sf = r->fid->aux;
         1370         if(r->d.uid[0] || r->d.gid[0] || r->d.muid[0] || ~r->d.mode != 0
         1371         || ~r->d.atime != 0 || ~r->d.mtime != 0 || ~r->d.length != 0){
         1372                 respond(r, "invalid wstat");
         1373                 return;
         1374         }
         1375         if(r->d.name[0]){
         1376                 switch(sf->type){
         1377                 default:
         1378                         respond(r, "invalid wstat");
         1379                         return;
         1380                 // TODO: rename category
         1381                 case Qalbum:
         1382                         snprint(strid, sizeof strid, "%lld", sf->album);
         1383                         v = ncsmug("smugmug.albums.changeSettings",
         1384                                 "AlbumID", strid, "Title", r->d.name, nil);
         1385                         if(v == nil)
         1386                                 responderrstr(r);
         1387                         else
         1388                                 respond(r, nil);
         1389                         s = smprint("&AlbumID=%lld&", sf->album);
         1390                         jcacheflush(s);
         1391                         free(s);
         1392                         jcacheflush("smugmug.albums.get&");
         1393                         return;
         1394                 }
         1395         }
         1396         respond(r, "invalid wstat");
         1397 }
         1398 
         1399 static void
         1400 xattach(Req *r)
         1401 {
         1402         SmugFid *sf;
         1403 
         1404         sf = emalloc(sizeof *sf);
         1405         r->fid->aux = sf;
         1406         sf->type = Qroot;
         1407         dostat(sf, &r->ofcall.qid, nil);
         1408         r->fid->qid = r->ofcall.qid;
         1409         respond(r, nil);
         1410 }
         1411 
         1412 void
         1413 xopen(Req *r)
         1414 {
         1415         SmugFid *sf;
         1416 
         1417         if((r->ifcall.mode&~OTRUNC) > 2){
         1418                 respond(r, "permission denied");
         1419                 return;
         1420         }
         1421 
         1422         sf = r->fid->aux;
         1423         switch(sf->type){
         1424         case Qctl:
         1425         case Qnickctl:
         1426         case Qalbumsctl:
         1427         case Qcategoryctl:
         1428         case Qalbumctl:
         1429         case Qimagectl:
         1430         case Qalbumsettings:
         1431         case Qimagesettings:
         1432                 break;
         1433 
         1434         case Quploadfile:
         1435                 if(r->ifcall.mode != OREAD){
         1436                         lock(&sf->upload->lk);
         1437                         if(sf->upload->ready){
         1438                                 unlock(&sf->upload->lk);
         1439                                 respond(r, "permission denied");
         1440                                 return;
         1441                         }
         1442                         sf->upwriter = 1;
         1443                         sf->upload->nwriters++;
         1444                         unlock(&sf->upload->lk);
         1445                 }
         1446                 break;
         1447 
         1448         default:
         1449                 if(r->ifcall.mode != OREAD){
         1450                         respond(r, "permission denied");
         1451                         return;
         1452                 }
         1453                 break;
         1454         }
         1455 
         1456         r->ofcall.qid = r->fid->qid;
         1457         respond(r, nil);
         1458 }
         1459 
         1460 void
         1461 xcreate(Req *r)
         1462 {
         1463         SmugFid *sf;
         1464         Json *v;
         1465         vlong id;
         1466         char strid[50], *key;
         1467         Upload *u;
         1468 
         1469         sf = r->fid->aux;
         1470         switch(sf->type){
         1471         case Qnick:
         1472                 // Create new category.
         1473                 if(!(r->ifcall.perm&DMDIR))
         1474                         break;
         1475                 v = ncsmug("smugmug.categories.create",
         1476                         "Name", r->ifcall.name, nil);
         1477                 if(v == nil){
         1478                         responderrstr(r);
         1479                         return;
         1480                 }
         1481                 id = jint(jwalk(v, "Category/id"));
         1482                 if(id < 0){
         1483                         fprint(2, "Create category: %J\n", v);
         1484                         jclose(v);
         1485                         responderrstr(r);
         1486                         return;
         1487                 }
         1488                 sf->type = Qcategory;
         1489                 sf->category = id;
         1490                 jcacheflush("method=smugmug.users.getTree&");
         1491                 jcacheflush("method=smugmug.categories.get&");
         1492                 dostat(sf, &r->ofcall.qid, nil);
         1493                 respond(r, nil);
         1494                 return;
         1495 
         1496         case Qcategory:
         1497                 // Create new album.
         1498                 if(!(r->ifcall.perm&DMDIR))
         1499                         break;
         1500                 snprint(strid, sizeof strid, "%lld", sf->category);
         1501                 // Start with most restrictive settings.
         1502                 v = ncsmug("smugmug.albums.create",
         1503                         "Title", r->ifcall.name,
         1504                         "CategoryID", strid,
         1505                         "Public", "0",
         1506                         "WorldSearchable", "0",
         1507                         "SmugSearchable", "0",
         1508                         nil);
         1509                 if(v == nil){
         1510                         responderrstr(r);
         1511                         return;
         1512                 }
         1513                 id = jint(jwalk(v, "Album/id"));
         1514                 key = jstring(jwalk(v, "Album/Key"));
         1515                 if(id < 0 || key == nil){
         1516                         fprint(2, "Create album: %J\n", v);
         1517                         jclose(v);
         1518                         responderrstr(r);
         1519                         return;
         1520                 }
         1521                 sf->type = Qalbum;
         1522                 sf->album = id;
         1523                 sf->albumkey = estrdup(key);
         1524                 jclose(v);
         1525                 jcacheflush("method=smugmug.users.getTree&");
         1526                 dostat(sf, &r->ofcall.qid, nil);
         1527                 respond(r, nil);
         1528                 return;
         1529 
         1530         case Qalbum:
         1531                 // Upload image to album.
         1532                 if(r->ifcall.perm&DMDIR)
         1533                         break;
         1534                 u = newupload(sf, r->ifcall.name);
         1535                 if(u == nil){
         1536                         responderrstr(r);
         1537                         return;
         1538                 }
         1539                 sf->upload = u;
         1540                 sf->upwriter = 1;
         1541                 sf->type = Quploadfile;
         1542                 dostat(sf, &r->ofcall.qid, nil);
         1543                 respond(r, nil);
         1544                 return;
         1545         }
         1546         respond(r, "permission denied");
         1547 }
         1548 
         1549 static int
         1550 writetofd(Req *r, int fd)
         1551 {
         1552         int total, n;
         1553 
         1554         total = 0;
         1555         while(total < r->ifcall.count){
         1556                 n = pwrite(fd, (char*)r->ifcall.data+total, r->ifcall.count-total, r->ifcall.offset+total);
         1557                 if(n <= 0)
         1558                         return -1;
         1559                 total += n;
         1560         }
         1561         r->ofcall.count = r->ifcall.count;
         1562         return 0;
         1563 }
         1564 
         1565 static void
         1566 readfromfd(Req *r, int fd)
         1567 {
         1568         int n;
         1569         n = pread(fd, r->ofcall.data, r->ifcall.count, r->ifcall.offset);
         1570         if(n < 0)
         1571                 n = 0;
         1572         r->ofcall.count = n;
         1573 }
         1574 
         1575 void
         1576 xread(Req *r)
         1577 {
         1578         SmugFid *sf;
         1579         char *data;
         1580         int fd;
         1581         HTTPHeader hdr;
         1582         char *url;
         1583 
         1584         sf = r->fid->aux;
         1585         r->ofcall.count = 0;
         1586         switch(sf->type){
         1587         default:
         1588                 respond(r, "not implemented");
         1589                 return;
         1590         case Qroot:
         1591         case Qnick:
         1592         case Qalbums:
         1593         case Qcategory:
         1594         case Qalbum:
         1595         case Qimage:
         1596                 dirread9p(r, dodirgen, sf);
         1597                 break;
         1598         case Qrpclog:
         1599                 rpclogread(r);
         1600                 return;
         1601         case Qctl:
         1602         case Qnickctl:
         1603         case Qalbumsctl:
         1604         case Qcategoryctl:
         1605         case Qalbumctl:
         1606         case Qimagectl:
         1607         case Qimageurl:
         1608         case Qimageexif:
         1609         case Quploads:
         1610         case Qimagesettings:
         1611         case Qalbumsettings:
         1612                 data = readctl(sf);
         1613                 readstr(r, data);
         1614                 free(data);
         1615                 break;
         1616         case Qimagefile:
         1617                 url = imageurl(sf);
         1618                 if(url == nil || (fd = download(url, &hdr)) < 0){
         1619                         free(url);
         1620                         responderrstr(r);
         1621                         return;
         1622                 }
         1623                 readfromfd(r, fd);
         1624                 free(url);
         1625                 close(fd);
         1626                 break;
         1627         case Quploadfile:
         1628                 if(sf->upload)
         1629                         readfromfd(r, sf->upload->fd);
         1630                 break;
         1631         }
         1632         respond(r, nil);
         1633 }
         1634 
         1635 void
         1636 xwrite(Req *r)
         1637 {
         1638         int sync;
         1639         char *s, *t, *p;
         1640         Json *v;
         1641         char strid[50];
         1642         SmugFid *sf;
         1643 
         1644         sf = r->fid->aux;
         1645         r->ofcall.count = r->ifcall.count;
         1646         sync = (r->ifcall.count==4 && memcmp(r->ifcall.data, "sync", 4) == 0);
         1647         switch(sf->type){
         1648         case Qctl:
         1649                 if(sync){
         1650                         jcacheflush(nil);
         1651                         respond(r, nil);
         1652                         return;
         1653                 }
         1654                 break;
         1655         case Qnickctl:
         1656                 if(sync){
         1657                         s = smprint("&NickName=%s&", nickname(sf->nickid));
         1658                         jcacheflush(s);
         1659                         free(s);
         1660                         respond(r, nil);
         1661                         return;
         1662                 }
         1663                 break;
         1664         case Qalbumsctl:
         1665         case Qcategoryctl:
         1666                 jcacheflush("smugmug.categories.get");
         1667                 break;
         1668         case Qalbumctl:
         1669                 if(sync){
         1670                         s = smprint("&AlbumID=%lld&", sf->album);
         1671                         jcacheflush(s);
         1672                         free(s);
         1673                         respond(r, nil);
         1674                         return;
         1675                 }
         1676                 break;
         1677         case Qimagectl:
         1678                 if(sync){
         1679                         s = smprint("&ImageID=%lld&", sf->image);
         1680                         jcacheflush(s);
         1681                         free(s);
         1682                         respond(r, nil);
         1683                         return;
         1684                 }
         1685                 break;
         1686         case Quploadfile:
         1687                 if(sf->upload){
         1688                         if(writetofd(r, sf->upload->fd) < 0){
         1689                                 responderrstr(r);
         1690                                 return;
         1691                         }
         1692                         respond(r, nil);
         1693                         return;
         1694                 }
         1695                 break;
         1696         case Qimagesettings:
         1697         case Qalbumsettings:
         1698                 s = (char*)r->ifcall.data;        // lib9p nul-terminated it
         1699                 t = strpbrk(s, " \r\t\n");
         1700                 if(t == nil)
         1701                         t = "";
         1702                 else{
         1703                         *t++ = 0;
         1704                         while(*t == ' ' || *t == '\r' || *t == '\t' || *t == '\n')
         1705                                 t++;
         1706                 }
         1707                 p = strchr(t, '\n');
         1708                 if(p && p[1] == 0)
         1709                         *p = 0;
         1710                 else if(p){
         1711                         respond(r, "newline in argument");
         1712                         return;
         1713                 }
         1714                 if(sf->type == Qalbumsettings)
         1715                         goto Albumsettings;
         1716                 snprint(strid, sizeof strid, "%lld", sf->image);
         1717                 v = ncsmug("smugmug.images.changeSettings",
         1718                         "ImageID", strid,
         1719                         s, t, nil);
         1720                 if(v == nil)
         1721                         responderrstr(r);
         1722                 else
         1723                         respond(r, nil);
         1724                 s = smprint("&ImageID=%lld&", sf->image);
         1725                 jcacheflush(s);
         1726                 free(s);
         1727                 return;
         1728         Albumsettings:
         1729                 snprint(strid, sizeof strid, "%lld", sf->album);
         1730                 v = ncsmug("smugmug.albums.changeSettings",
         1731                         "AlbumID", strid, s, t, nil);
         1732                 if(v == nil)
         1733                         responderrstr(r);
         1734                 else
         1735                         respond(r, nil);
         1736                 s = smprint("&AlbumID=%lld&", sf->album);
         1737                 jcacheflush(s);
         1738                 free(s);
         1739                 return;
         1740         }
         1741         respond(r, "invalid control message");
         1742         return;
         1743 }
         1744 
         1745 void
         1746 xremove(Req *r)
         1747 {
         1748         char id[100];
         1749         SmugFid *sf;
         1750         Json *v;
         1751 
         1752         sf = r->fid->aux;
         1753         switch(sf->type){
         1754         default:
         1755                 respond(r, "permission denied");
         1756                 return;
         1757         case Qcategoryctl:
         1758         case Qalbumctl:
         1759         case Qalbumsettings:
         1760         case Qimagectl:
         1761         case Qimagesettings:
         1762         case Qimageexif:
         1763         case Qimageurl:
         1764         case Qimagefile:
         1765                 /* ignore remove request, but no error, so rm -r works */
         1766                 /* you can pretend they get removed and immediately grow back! */
         1767                 respond(r, nil);
         1768                 return;
         1769         case Qcategory:
         1770                 v = getalbums(sf);
         1771                 if(v && v->len > 0){
         1772                         respond(r, "directory not empty");
         1773                         return;
         1774                 }
         1775                 snprint(id, sizeof id, "%lld", sf->category);
         1776                 v = ncsmug("smugmug.categories.delete",
         1777                         "CategoryID", id, nil);
         1778                 if(v == nil)
         1779                         responderrstr(r);
         1780                 else{
         1781                         jclose(v);
         1782                         jcacheflush("smugmug.users.getTree");
         1783                         jcacheflush("smugmug.categories.get");
         1784                         respond(r, nil);
         1785                 }
         1786                 return;
         1787         case Qalbum:
         1788                 v = getimages(sf, sf->album, sf->albumkey);
         1789                 if(v && v->len > 0){
         1790                         respond(r, "directory not empty");
         1791                         return;
         1792                 }
         1793                 snprint(id, sizeof id, "%lld", sf->album);
         1794                 v = ncsmug("smugmug.albums.delete",
         1795                         "AlbumID", id, nil);
         1796                 if(v == nil)
         1797                         responderrstr(r);
         1798                 else{
         1799                         jclose(v);
         1800                         jcacheflush("smugmug.users.getTree");
         1801                         jcacheflush("smugmug.categories.get");
         1802                         jcacheflush("smugmug.albums.get");
         1803                         respond(r, nil);
         1804                 }
         1805                 return;
         1806 
         1807         case Qimage:
         1808                 snprint(id, sizeof id, "%lld", sf->image);
         1809                 v = ncsmug("smugmug.images.delete",
         1810                         "ImageID", id, nil);
         1811                 if(v == nil)
         1812                         responderrstr(r);
         1813                 else{
         1814                         jclose(v);
         1815                         snprint(id, sizeof id, "ImageID=%lld&", sf->image);
         1816                         jcacheflush(id);
         1817                         jcacheflush("smugmug.images.get&");
         1818                         respond(r, nil);
         1819                 }
         1820                 return;
         1821         }
         1822 }
         1823 
         1824 void
         1825 xflush(Req *r)
         1826 {
         1827         rpclogflush(r->oldreq);
         1828         respond(r, nil);
         1829 }
         1830 
         1831 Srv xsrv;
         1832 
         1833 void
         1834 xinit(void)
         1835 {
         1836         xsrv.attach = xattach;
         1837         xsrv.open = xopen;
         1838         xsrv.create = xcreate;
         1839         xsrv.read = xread;
         1840         xsrv.stat = xstat;
         1841         xsrv.walk1 = xwalk1;
         1842         xsrv.clone = xclone;
         1843         xsrv.destroyfid = xdestroyfid;
         1844         xsrv.remove = xremove;
         1845         xsrv.write = xwrite;
         1846         xsrv.flush = xflush;
         1847         xsrv.wstat = xwstat;
         1848 }