| #include "a.h" |
| |
| enum |
| { |
| Qroot = 0, // /smug/ |
| Qctl, // /smug/ctl |
| Qrpclog, // /smug/rpclog |
| Quploads, // /smug/uploads |
| Qnick, // /smug/nick/ |
| Qnickctl, // /smug/nick/ctl |
| Qalbums, // /smug/nick/albums/ |
| Qalbumsctl, // /smug/nick/albums/ctl |
| Qcategory, // /smug/nick/Category/ |
| Qcategoryctl, // /smug/nick/Category/ctl |
| Qalbum, // /smug/nick/Category/Album/ |
| Qalbumctl, // /smug/nick/Category/Album/ctl |
| Qalbumsettings, // /smug/nick/Category/Album/settings |
| Quploadfile, // /smug/nick/Category/Album/upload/file.jpg |
| Qimage, // /smug/nick/Category/Album/Image/ |
| Qimagectl, // /smug/nick/Category/Album/Image/ctl |
| Qimageexif, // /smug/nick/Category/Album/Image/exif |
| Qimagesettings, // /smug/nick/Category/Album/Image/settings |
| Qimageurl, // /smug/nick/Category/Album/Image/url |
| Qimagefile, // /smug/nick/Category/Album/Image/file.jpg |
| }; |
| |
| void |
| mylock(Lock *lk) |
| { |
| lock(lk); |
| fprint(2, "locked from %p\n", getcallerpc(&lk)); |
| } |
| |
| void |
| myunlock(Lock *lk) |
| { |
| unlock(lk); |
| fprint(2, "unlocked from %p\n", getcallerpc(&lk)); |
| } |
| |
| //#define lock mylock |
| //#define unlock myunlock |
| |
| typedef struct Upload Upload; |
| |
| typedef struct SmugFid SmugFid; |
| struct SmugFid |
| { |
| int type; |
| int nickid; |
| vlong category; // -1 for "albums" |
| vlong album; |
| char *albumkey; |
| vlong image; |
| char *imagekey; |
| Upload *upload; |
| int upwriter; |
| }; |
| |
| #define QTYPE(p) ((p)&0xFF) |
| #define QARG(p) ((p)>>8) |
| #define QPATH(p, q) ((p)|((q)<<8)) |
| |
| char **nick; |
| int nnick; |
| |
| struct Upload |
| { |
| Lock lk; |
| int fd; |
| char *name; |
| char *file; |
| vlong album; |
| vlong length; |
| char *albumkey; |
| int size; |
| int ready; |
| int nwriters; |
| int uploaded; |
| int ref; |
| int uploading; |
| }; |
| |
| Upload **up; |
| int nup; |
| QLock uploadlock; |
| Rendez uploadrendez; |
| |
| void uploader(void*); |
| |
| Upload* |
| newupload(SmugFid *sf, char *name) |
| { |
| Upload *u; |
| int fd, i; |
| char tmp[] = "/var/tmp/smugfs.XXXXXX"; |
| |
| if((fd = opentemp(tmp, ORDWR)) < 0) |
| return nil; |
| qlock(&uploadlock); |
| for(i=0; i<nup; i++){ |
| u = up[i]; |
| lock(&u->lk); |
| if(u->ref == 0){ |
| u->ref = 1; |
| goto Reuse; |
| } |
| unlock(&u->lk); |
| } |
| if(nup == 0){ |
| uploadrendez.l = &uploadlock; |
| proccreate(uploader, nil, STACKSIZE); |
| } |
| u = emalloc(sizeof *u); |
| lock(&u->lk); |
| u->ref = 1; |
| up = erealloc(up, (nup+1)*sizeof up[0]); |
| up[nup++] = u; |
| Reuse: |
| qunlock(&uploadlock); |
| u->fd = fd; |
| u->name = estrdup(name); |
| u->file = estrdup(tmp); |
| u->album = sf->album; |
| u->albumkey = estrdup(sf->albumkey); |
| u->nwriters = 1; |
| unlock(&u->lk); |
| return u; |
| } |
| |
| void |
| closeupload(Upload *u) |
| { |
| lock(&u->lk); |
| fprint(2, "close %p from %p: %d\n", u, getcallerpc(&u), u->ref); |
| if(--u->ref > 0){ |
| unlock(&u->lk); |
| return; |
| } |
| if(u->ref < 0) |
| abort(); |
| if(u->fd >= 0){ |
| close(u->fd); |
| u->fd = -1; |
| } |
| if(u->name){ |
| free(u->name); |
| u->name = nil; |
| } |
| if(u->file){ |
| remove(u->file); |
| free(u->file); |
| u->file = nil; |
| } |
| u->album = 0; |
| if(u->albumkey){ |
| free(u->albumkey); |
| u->albumkey = nil; |
| } |
| u->size = 0; |
| u->ready = 0; |
| u->nwriters = 0; |
| u->uploaded = 0; |
| u->uploading = 0; |
| u->length = 0; |
| unlock(&u->lk); |
| } |
| |
| Upload* |
| getuploadindex(SmugFid *sf, int *index) |
| { |
| int i; |
| Upload *u; |
| |
| qlock(&uploadlock); |
| for(i=0; i<nup; i++){ |
| u = up[i]; |
| lock(&u->lk); |
| if(u->ref > 0 && !u->uploaded && u->album == sf->album && (*index)-- == 0){ |
| qunlock(&uploadlock); |
| u->ref++; |
| fprint(2, "bump %p from %p: %d\n", u, getcallerpc(&sf), u->ref); |
| unlock(&u->lk); |
| return u; |
| } |
| unlock(&u->lk); |
| } |
| qunlock(&uploadlock); |
| return nil; |
| } |
| |
| Upload* |
| getuploadname(SmugFid *sf, char *name) |
| { |
| int i; |
| Upload *u; |
| |
| qlock(&uploadlock); |
| for(i=0; i<nup; i++){ |
| u = up[i]; |
| lock(&u->lk); |
| if(u->ref > 0 && !u->uploaded && u->album == sf->album && strcmp(name, u->name) == 0){ |
| qunlock(&uploadlock); |
| u->ref++; |
| fprint(2, "bump %p from %p: %d\n", u, getcallerpc(&sf), u->ref); |
| unlock(&u->lk); |
| return u; |
| } |
| unlock(&u->lk); |
| } |
| qunlock(&uploadlock); |
| return nil; |
| } |
| |
| void doupload(Upload*); |
| |
| void |
| uploader(void *v) |
| { |
| int i, did; |
| Upload *u; |
| |
| qlock(&uploadlock); |
| for(;;){ |
| did = 0; |
| for(i=0; i<nup; i++){ |
| u = up[i]; |
| lock(&u->lk); |
| if(u->ref > 0 && u->ready && !u->uploading && !u->uploaded){ |
| u->uploading = 1; |
| unlock(&u->lk); |
| qunlock(&uploadlock); |
| doupload(u); |
| closeupload(u); |
| fprint(2, "done %d\n", u->ref); |
| did = 1; |
| qlock(&uploadlock); |
| }else |
| unlock(&u->lk); |
| } |
| if(!did) |
| rsleep(&uploadrendez); |
| } |
| } |
| |
| void |
| kickupload(Upload *u) |
| { |
| Dir *d; |
| |
| lock(&u->lk); |
| if((d = dirfstat(u->fd)) != nil) |
| u->length = d->length; |
| close(u->fd); |
| u->fd = -1; |
| u->ref++; |
| fprint(2, "kick %p from %p: %d\n", u, getcallerpc(&u), u->ref); |
| u->ready = 1; |
| unlock(&u->lk); |
| qlock(&uploadlock); |
| rwakeup(&uploadrendez); |
| qunlock(&uploadlock); |
| } |
| |
| void |
| doupload(Upload *u) |
| { |
| Dir *d; |
| vlong datalen; |
| Fmt fmt; |
| char *req; |
| char buf[8192]; |
| int n, total; |
| uchar digest[MD5dlen]; |
| DigestState ds; |
| Json *jv; |
| |
| if((u->fd = open(u->file, OREAD)) < 0){ |
| fprint(2, "cannot reopen temporary file %s: %r\n", u->file); |
| return; |
| } |
| if((d = dirfstat(u->fd)) == nil){ |
| fprint(2, "fstat: %r\n"); |
| return; |
| } |
| datalen = d->length; |
| free(d); |
| |
| memset(&ds, 0, sizeof ds); |
| seek(u->fd, 0, 0); |
| total = 0; |
| while((n = read(u->fd, buf, sizeof buf)) > 0){ |
| md5((uchar*)buf, n, nil, &ds); |
| total += n; |
| } |
| if(total != datalen){ |
| fprint(2, "bad total: %lld %lld\n", total, datalen); |
| return; |
| } |
| md5(nil, 0, digest, &ds); |
| |
| fmtstrinit(&fmt); |
| fmtprint(&fmt, "PUT /%s HTTP/1.0\r\n", u->name); |
| fmtprint(&fmt, "Content-Length: %lld\r\n", datalen); |
| fmtprint(&fmt, "Content-MD5: %.16lH\r\n", digest); |
| fmtprint(&fmt, "X-Smug-SessionID: %s\r\n", sessid); |
| fmtprint(&fmt, "X-Smug-Version: %s\r\n", API_VERSION); |
| fmtprint(&fmt, "X-Smug-ResponseType: JSON\r\n"); |
| // Can send X-Smug-ImageID instead to replace existing files. |
| fmtprint(&fmt, "X-Smug-AlbumID: %lld\r\n", u->album); |
| fmtprint(&fmt, "X-Smug-FileName: %s\r\n", u->name); |
| fmtprint(&fmt, "\r\n"); |
| req = fmtstrflush(&fmt); |
| |
| seek(u->fd, 0, 0); |
| jv = jsonupload(&http, UPLOAD_HOST, req, u->fd, datalen); |
| free(req); |
| if(jv == nil){ |
| fprint(2, "upload: %r\n"); |
| return; |
| } |
| |
| close(u->fd); |
| remove(u->file); |
| free(u->file); |
| u->file = nil; |
| u->fd = -1; |
| u->uploaded = 1; |
| rpclog("uploaded: %J", jv); |
| jclose(jv); |
| } |
| |
| int |
| nickindex(char *name) |
| { |
| int i; |
| Json *v; |
| |
| for(i=0; i<nnick; i++) |
| if(strcmp(nick[i], name) == 0) |
| return i; |
| v = smug("smugmug.users.getTree", "NickName", name, nil); |
| if(v == nil) |
| return -1; |
| nick = erealloc(nick, (nnick+1)*sizeof nick[0]); |
| nick[nnick] = estrdup(name); |
| return nnick++; |
| } |
| |
| char* |
| nickname(int i) |
| { |
| if(i < 0 || i >= nnick) |
| return nil; |
| return nick[i]; |
| } |
| |
| void |
| responderrstr(Req *r) |
| { |
| char err[ERRMAX]; |
| |
| rerrstr(err, sizeof err); |
| respond(r, err); |
| } |
| |
| static char* |
| xclone(Fid *oldfid, Fid *newfid) |
| { |
| SmugFid *sf; |
| |
| if(oldfid->aux == nil) |
| return nil; |
| |
| sf = emalloc(sizeof *sf); |
| *sf = *(SmugFid*)oldfid->aux; |
| sf->upload = nil; |
| sf->upwriter = 0; |
| if(sf->albumkey) |
| sf->albumkey = estrdup(sf->albumkey); |
| if(sf->imagekey) |
| sf->imagekey = estrdup(sf->imagekey); |
| newfid->aux = sf; |
| return nil; |
| } |
| |
| static void |
| xdestroyfid(Fid *fid) |
| { |
| SmugFid *sf; |
| |
| sf = fid->aux; |
| free(sf->albumkey); |
| free(sf->imagekey); |
| if(sf->upload){ |
| if(sf->upwriter && --sf->upload->nwriters == 0){ |
| fprint(2, "should upload %s\n", sf->upload->name); |
| kickupload(sf->upload); |
| } |
| closeupload(sf->upload); |
| sf->upload = nil; |
| } |
| free(sf); |
| } |
| |
| static Json* |
| getcategories(SmugFid *sf) |
| { |
| Json *v, *w; |
| |
| v = smug("smugmug.categories.get", "NickName", nickname(sf->nickid), nil); |
| w = jincref(jwalk(v, "Categories")); |
| jclose(v); |
| return w; |
| } |
| |
| static Json* |
| getcategorytree(SmugFid *sf) |
| { |
| Json *v, *w; |
| |
| v = smug("smugmug.users.getTree", "NickName", nickname(sf->nickid), nil); |
| w = jincref(jwalk(v, "Categories")); |
| jclose(v); |
| return w; |
| } |
| |
| static Json* |
| getcategory(SmugFid *sf, vlong id) |
| { |
| int i; |
| Json *v, *w; |
| |
| v = getcategorytree(sf); |
| if(v == nil) |
| return nil; |
| for(i=0; i<v->len; i++){ |
| if(jint(jwalk(v->value[i], "id")) == id){ |
| w = jincref(v->value[i]); |
| jclose(v); |
| return w; |
| } |
| } |
| jclose(v); |
| return nil; |
| } |
| |
| static vlong |
| getcategoryid(SmugFid *sf, char *name) |
| { |
| int i; |
| vlong id; |
| Json *v; |
| |
| v = getcategories(sf); |
| if(v == nil) |
| return -1; |
| for(i=0; i<v->len; i++){ |
| if(jstrcmp(jwalk(v->value[i], "Name"), name) == 0){ |
| id = jint(jwalk(v->value[i], "id")); |
| if(id < 0){ |
| jclose(v); |
| return -1; |
| } |
| jclose(v); |
| return id; |
| } |
| } |
| jclose(v); |
| return -1; |
| } |
| |
| static vlong |
| getcategoryindex(SmugFid *sf, int i) |
| { |
| Json *v; |
| vlong id; |
| |
| v = getcategories(sf); |
| if(v == nil) |
| return -1; |
| if(i < 0 || i >= v->len){ |
| jclose(v); |
| return -1; |
| } |
| id = jint(jwalk(v->value[i], "id")); |
| jclose(v); |
| return id; |
| } |
| |
| static Json* |
| getalbum(SmugFid *sf, vlong albumid, char *albumkey) |
| { |
| char id[50]; |
| Json *v, *w; |
| |
| snprint(id, sizeof id, "%lld", albumid); |
| v = smug("smugmug.albums.getInfo", |
| "AlbumID", id, "AlbumKey", albumkey, |
| "NickName", nickname(sf->nickid), nil); |
| w = jincref(jwalk(v, "Album")); |
| jclose(v); |
| return w; |
| } |
| |
| static Json* |
| getalbums(SmugFid *sf) |
| { |
| Json *v, *w; |
| |
| if(sf->category >= 0) |
| v = getcategory(sf, sf->category); |
| else |
| v = smug("smugmug.albums.get", |
| "NickName", nickname(sf->nickid), nil); |
| w = jincref(jwalk(v, "Albums")); |
| jclose(v); |
| return w; |
| } |
| |
| static vlong |
| getalbumid(SmugFid *sf, char *name, char **keyp) |
| { |
| int i; |
| vlong id; |
| Json *v; |
| char *key; |
| |
| v = getalbums(sf); |
| if(v == nil) |
| return -1; |
| for(i=0; i<v->len; i++){ |
| if(jstrcmp(jwalk(v->value[i], "Title"), name) == 0){ |
| id = jint(jwalk(v->value[i], "id")); |
| key = jstring(jwalk(v->value[i], "Key")); |
| if(id < 0 || key == nil){ |
| jclose(v); |
| return -1; |
| } |
| if(keyp) |
| *keyp = estrdup(key); |
| jclose(v); |
| return id; |
| } |
| } |
| jclose(v); |
| return -1; |
| } |
| |
| static vlong |
| getalbumindex(SmugFid *sf, int i, char **keyp) |
| { |
| vlong id; |
| Json *v; |
| char *key; |
| |
| v = getalbums(sf); |
| if(v == nil) |
| return -1; |
| if(i < 0 || i >= v->len){ |
| jclose(v); |
| return -1; |
| } |
| id = jint(jwalk(v->value[i], "id")); |
| key = jstring(jwalk(v->value[i], "Key")); |
| if(id < 0 || key == nil){ |
| jclose(v); |
| return -1; |
| } |
| if(keyp) |
| *keyp = estrdup(key); |
| jclose(v); |
| return id; |
| } |
| |
| static Json* |
| getimages(SmugFid *sf, vlong albumid, char *albumkey) |
| { |
| char id[50]; |
| Json *v, *w; |
| |
| snprint(id, sizeof id, "%lld", albumid); |
| v = smug("smugmug.images.get", |
| "AlbumID", id, "AlbumKey", albumkey, |
| "NickName", nickname(sf->nickid), nil); |
| w = jincref(jwalk(v, "Images")); |
| jclose(v); |
| return w; |
| } |
| |
| static vlong |
| getimageid(SmugFid *sf, char *name, char **keyp) |
| { |
| int i; |
| vlong id; |
| Json *v; |
| char *p; |
| char *key; |
| |
| id = strtol(name, &p, 10); |
| if(*p != 0 || *name == 0) |
| return -1; |
| |
| v = getimages(sf, sf->album, sf->albumkey); |
| if(v == nil) |
| return -1; |
| for(i=0; i<v->len; i++){ |
| if(jint(jwalk(v->value[i], "id")) == id){ |
| key = jstring(jwalk(v->value[i], "Key")); |
| if(key == nil){ |
| jclose(v); |
| return -1; |
| } |
| if(keyp) |
| *keyp = estrdup(key); |
| jclose(v); |
| return id; |
| } |
| } |
| jclose(v); |
| return -1; |
| } |
| |
| static Json* |
| getimageinfo(SmugFid *sf, vlong imageid, char *imagekey) |
| { |
| char id[50]; |
| Json *v, *w; |
| |
| snprint(id, sizeof id, "%lld", imageid); |
| v = smug("smugmug.images.getInfo", |
| "ImageID", id, "ImageKey", imagekey, |
| "NickName", nickname(sf->nickid), nil); |
| w = jincref(jwalk(v, "Image")); |
| jclose(v); |
| return w; |
| } |
| |
| static Json* |
| getimageexif(SmugFid *sf, vlong imageid, char *imagekey) |
| { |
| char id[50]; |
| Json *v, *w; |
| |
| snprint(id, sizeof id, "%lld", imageid); |
| v = smug("smugmug.images.getEXIF", |
| "ImageID", id, "ImageKey", imagekey, |
| "NickName", nickname(sf->nickid), nil); |
| w = jincref(jwalk(v, "Image")); |
| jclose(v); |
| return w; |
| } |
| |
| static vlong |
| getimageindex(SmugFid *sf, int i, char **keyp) |
| { |
| vlong id; |
| Json *v; |
| char *key; |
| |
| v = getimages(sf, sf->album, sf->albumkey); |
| if(v == nil) |
| return -1; |
| if(i < 0 || i >= v->len){ |
| jclose(v); |
| return -1; |
| } |
| id = jint(jwalk(v->value[i], "id")); |
| key = jstring(jwalk(v->value[i], "Key")); |
| if(id < 0 || key == nil){ |
| jclose(v); |
| return -1; |
| } |
| if(keyp) |
| *keyp = estrdup(key); |
| jclose(v); |
| return id; |
| } |
| |
| static char* |
| categoryname(SmugFid *sf) |
| { |
| Json *v; |
| char *s; |
| |
| v = getcategory(sf, sf->category); |
| s = jstring(jwalk(v, "Name")); |
| if(s) |
| s = estrdup(s); |
| jclose(v); |
| return s; |
| } |
| |
| static char* |
| albumname(SmugFid *sf) |
| { |
| Json *v; |
| char *s; |
| |
| v = getalbum(sf, sf->album, sf->albumkey); |
| s = jstring(jwalk(v, "Title")); |
| if(s) |
| s = estrdup(s); |
| jclose(v); |
| return s; |
| } |
| |
| static char* |
| imagename(SmugFid *sf) |
| { |
| char *s; |
| Json *v; |
| |
| v = getimageinfo(sf, sf->image, sf->imagekey); |
| s = jstring(jwalk(v, "FileName")); |
| if(s && s[0]) |
| s = estrdup(s); |
| else |
| s = smprint("%lld.jpg", sf->image); // TODO: use Format |
| jclose(v); |
| return s; |
| } |
| |
| static vlong |
| imagelength(SmugFid *sf) |
| { |
| vlong length; |
| Json *v; |
| |
| v = getimageinfo(sf, sf->image, sf->imagekey); |
| length = jint(jwalk(v, "Size")); |
| jclose(v); |
| return length; |
| } |
| |
| static struct { |
| char *key; |
| char *name; |
| } urls[] = { |
| "AlbumURL", "album", |
| "TinyURL", "tiny", |
| "ThumbURL", "thumb", |
| "SmallURL", "small", |
| "MediumURL", "medium", |
| "LargeURL", "large", |
| "XLargeURL", "xlarge", |
| "X2LargeURL", "xxlarge", |
| "X3LargeURL", "xxxlarge", |
| "OriginalURL", "original", |
| }; |
| |
| static char* |
| imageurl(SmugFid *sf) |
| { |
| Json *v; |
| char *s; |
| int i; |
| |
| v = getimageinfo(sf, sf->image, sf->imagekey); |
| for(i=nelem(urls)-1; i>=0; i--){ |
| if((s = jstring(jwalk(v, urls[i].key))) != nil){ |
| s = estrdup(s); |
| jclose(v); |
| return s; |
| } |
| } |
| jclose(v); |
| return nil; |
| } |
| |
| static char* imagestrings[] = |
| { |
| "Caption", |
| "LastUpdated", |
| "FileName", |
| "MD5Sum", |
| "Watermark", |
| "Format", |
| "Keywords", |
| "Date", |
| "AlbumURL", |
| "TinyURL", |
| "ThumbURL", |
| "SmallURL", |
| "MediumURL", |
| "LargeURL", |
| "XLargeURL", |
| "X2LargeURL", |
| "X3LargeURL", |
| "OriginalURL", |
| "Album", |
| }; |
| |
| static char* albumbools[] = |
| { |
| "Public", |
| "Printable", |
| "Filenames", |
| "Comments", |
| "External", |
| "Originals", |
| "EXIF", |
| "Share", |
| "SortDirection", |
| "FamilyEdit", |
| "FriendEdit", |
| "HideOwner", |
| "CanRank", |
| "Clean", |
| "Geography", |
| "SmugSearchable", |
| "WorldSearchable", |
| "SquareThumbs", |
| "X2Larges", |
| "X3Larges", |
| }; |
| |
| static char* albumstrings[] = |
| { |
| "Description" |
| "Keywords", |
| "Password", |
| "PasswordHint", |
| "SortMethod", |
| "LastUpdated", |
| }; |
| |
| static char* |
| readctl(SmugFid *sf) |
| { |
| int i; |
| Upload *u; |
| char *s; |
| Json *v, *vv; |
| Fmt fmt; |
| |
| v = nil; |
| switch(sf->type){ |
| case Qctl: |
| return smprint("%#J\n", userinfo); |
| |
| case Quploads: |
| fmtstrinit(&fmt); |
| qlock(&uploadlock); |
| for(i=0; i<nup; i++){ |
| u = up[i]; |
| lock(&u->lk); |
| if(u->ready && !u->uploaded && u->ref > 0) |
| fmtprint(&fmt, "%s %s%s\n", u->name, u->file, u->uploading ? " [uploading]" : ""); |
| unlock(&u->lk); |
| } |
| qunlock(&uploadlock); |
| return fmtstrflush(&fmt); |
| |
| case Qnickctl: |
| v = getcategories(sf); |
| break; |
| |
| case Qcategoryctl: |
| v = getcategory(sf, sf->category); |
| break; |
| |
| case Qalbumctl: |
| v = getimages(sf, sf->album, sf->albumkey); |
| break; |
| |
| case Qalbumsctl: |
| v = getalbums(sf); |
| break; |
| |
| case Qimagectl: |
| v = getimageinfo(sf, sf->image, sf->imagekey); |
| break; |
| |
| case Qimageurl: |
| v = getimageinfo(sf, sf->image, sf->imagekey); |
| fmtstrinit(&fmt); |
| for(i=0; i<nelem(urls); i++) |
| if((s = jstring(jwalk(v, urls[i].key))) != nil) |
| fmtprint(&fmt, "%s %s\n", urls[i].name, s); |
| jclose(v); |
| return fmtstrflush(&fmt); |
| |
| case Qimageexif: |
| v = getimageexif(sf, sf->image, sf->imagekey); |
| break; |
| |
| case Qalbumsettings: |
| v = getalbum(sf, sf->album, sf->albumkey); |
| fmtstrinit(&fmt); |
| fmtprint(&fmt, "id\t%lld\n", jint(jwalk(v, "id"))); |
| // TODO: Category/id |
| // TODO: SubCategory/id |
| // TODO: Community/id |
| // TODO: Template/id |
| fmtprint(&fmt, "Highlight\t%lld\n", jint(jwalk(v, "Highlight/id"))); |
| fmtprint(&fmt, "Position\t%lld\n", jint(jwalk(v, "Position"))); |
| fmtprint(&fmt, "ImageCount\t%lld\n", jint(jwalk(v, "ImageCount"))); |
| for(i=0; i<nelem(albumbools); i++){ |
| vv = jwalk(v, albumbools[i]); |
| if(vv) |
| fmtprint(&fmt, "%s\t%J\n", albumbools[i], vv); |
| } |
| for(i=0; i<nelem(albumstrings); i++){ |
| s = jstring(jwalk(v, albumstrings[i])); |
| if(s) |
| fmtprint(&fmt, "%s\t%s\n", albumstrings[i], s); |
| } |
| s = fmtstrflush(&fmt); |
| jclose(v); |
| return s; |
| |
| case Qimagesettings: |
| v = getimageinfo(sf, sf->image, sf->imagekey); |
| fmtstrinit(&fmt); |
| fmtprint(&fmt, "id\t%lld\n", jint(jwalk(v, "id"))); |
| fmtprint(&fmt, "Position\t%lld\n", jint(jwalk(v, "Position"))); |
| fmtprint(&fmt, "Serial\t%lld\n", jint(jwalk(v, "Serial"))); |
| fmtprint(&fmt, "Size\t%lld\t%lldx%lld\n", |
| jint(jwalk(v, "Size")), |
| jint(jwalk(v, "Width")), |
| jint(jwalk(v, "Height"))); |
| vv = jwalk(v, "Hidden"); |
| fmtprint(&fmt, "Hidden\t%J\n", vv); |
| // TODO: Album/id |
| for(i=0; i<nelem(imagestrings); i++){ |
| s = jstring(jwalk(v, imagestrings[i])); |
| if(s) |
| fmtprint(&fmt, "%s\t%s\n", imagestrings[i], s); |
| } |
| s = fmtstrflush(&fmt); |
| jclose(v); |
| return s; |
| } |
| |
| if(v == nil) |
| return estrdup(""); |
| s = smprint("%#J\n", v); |
| jclose(v); |
| return s; |
| } |
| |
| |
| static void |
| dostat(SmugFid *sf, Qid *qid, Dir *dir) |
| { |
| Qid q; |
| char *name; |
| int freename; |
| ulong mode; |
| char *uid; |
| char *s; |
| vlong length; |
| |
| memset(&q, 0, sizeof q); |
| name = nil; |
| freename = 0; |
| uid = "smugfs"; |
| q.type = 0; |
| q.vers = 0; |
| q.path = QPATH(sf->type, sf->nickid); |
| length = 0; |
| mode = 0444; |
| |
| switch(sf->type){ |
| case Qroot: |
| name = "/"; |
| q.type = QTDIR; |
| break; |
| case Qctl: |
| name = "ctl"; |
| mode |= 0222; |
| break; |
| case Quploads: |
| name = "uploads"; |
| s = readctl(sf); |
| if(s){ |
| length = strlen(s); |
| free(s); |
| } |
| break; |
| case Qrpclog: |
| name = "rpclog"; |
| break; |
| case Qnick: |
| name = nickname(sf->nickid); |
| q.type = QTDIR; |
| break; |
| case Qnickctl: |
| name = "ctl"; |
| mode |= 0222; |
| break; |
| case Qalbums: |
| name = "albums"; |
| q.type = QTDIR; |
| break; |
| case Qalbumsctl: |
| name = "ctl"; |
| mode |= 0222; |
| break; |
| case Qcategory: |
| name = categoryname(sf); |
| freename = 1; |
| q.path |= QPATH(0, sf->category << 8); |
| q.type = QTDIR; |
| break; |
| case Qcategoryctl: |
| name = "ctl"; |
| mode |= 0222; |
| q.path |= QPATH(0, sf->category << 8); |
| break; |
| case Qalbum: |
| name = albumname(sf); |
| freename = 1; |
| q.path |= QPATH(0, sf->album << 8); |
| q.type = QTDIR; |
| break; |
| case Qalbumctl: |
| name = "ctl"; |
| mode |= 0222; |
| q.path |= QPATH(0, sf->album << 8); |
| break; |
| case Qalbumsettings: |
| name = "settings"; |
| mode |= 0222; |
| q.path |= QPATH(0, sf->album << 8); |
| break; |
| case Quploadfile: |
| q.path |= QPATH(0, (uintptr)sf->upload << 8); |
| if(sf->upload){ |
| Dir *dd; |
| name = sf->upload->name; |
| if(sf->upload->fd >= 0){ |
| dd = dirfstat(sf->upload->fd); |
| if(dd){ |
| length = dd->length; |
| free(dd); |
| } |
| }else |
| length = sf->upload->length; |
| if(!sf->upload->ready) |
| mode |= 0222; |
| } |
| break; |
| case Qimage: |
| name = smprint("%lld", sf->image); |
| freename = 1; |
| q.path |= QPATH(0, sf->image << 8); |
| q.type = QTDIR; |
| break; |
| case Qimagectl: |
| name = "ctl"; |
| mode |= 0222; |
| q.path |= QPATH(0, sf->image << 8); |
| break; |
| case Qimagesettings: |
| name = "settings"; |
| mode |= 0222; |
| q.path |= QPATH(0, sf->image << 8); |
| break; |
| case Qimageexif: |
| name = "exif"; |
| q.path |= QPATH(0, sf->image << 8); |
| break; |
| case Qimageurl: |
| name = "url"; |
| q.path |= QPATH(0, sf->image << 8); |
| break; |
| case Qimagefile: |
| name = imagename(sf); |
| freename = 1; |
| q.path |= QPATH(0, sf->image << 8); |
| length = imagelength(sf); |
| break; |
| default: |
| name = "?egreg"; |
| q.path = 0; |
| break; |
| } |
| |
| if(name == nil){ |
| name = "???"; |
| freename = 0; |
| } |
| |
| if(qid) |
| *qid = q; |
| if(dir){ |
| memset(dir, 0, sizeof *dir); |
| dir->name = estrdup9p(name); |
| dir->muid = estrdup9p("muid"); |
| mode |= q.type<<24; |
| if(mode & DMDIR) |
| mode |= 0755; |
| dir->mode = mode; |
| dir->uid = estrdup9p(uid); |
| dir->gid = estrdup9p("smugfs"); |
| dir->qid = q; |
| dir->length = length; |
| } |
| if(freename) |
| free(name); |
| } |
| |
| static char* |
| xwalk1(Fid *fid, char *name, Qid *qid) |
| { |
| int dotdot, i; |
| vlong id; |
| char *key; |
| SmugFid *sf; |
| char *x; |
| Upload *u; |
| |
| dotdot = strcmp(name, "..") == 0; |
| sf = fid->aux; |
| switch(sf->type){ |
| default: |
| NotFound: |
| return "file not found"; |
| |
| case Qroot: |
| if(dotdot) |
| break; |
| if(strcmp(name, "ctl") == 0){ |
| sf->type = Qctl; |
| break; |
| } |
| if(strcmp(name, "uploads") == 0){ |
| sf->type = Quploads; |
| break; |
| } |
| if(strcmp(name, "rpclog") == 0){ |
| sf->type = Qrpclog; |
| break; |
| } |
| if((i = nickindex(name)) >= 0){ |
| sf->nickid = i; |
| sf->type = Qnick; |
| break; |
| } |
| goto NotFound; |
| |
| case Qnick: |
| if(dotdot){ |
| sf->type = Qroot; |
| sf->nickid = 0; |
| break; |
| } |
| if(strcmp(name, "ctl") == 0){ |
| sf->type = Qnickctl; |
| break; |
| } |
| if(strcmp(name, "albums") == 0){ |
| sf->category = -1; |
| sf->type = Qalbums; |
| break; |
| } |
| if((id = getcategoryid(sf, name)) >= 0){ |
| sf->category = id; |
| sf->type = Qcategory; |
| break; |
| } |
| goto NotFound; |
| |
| case Qalbums: |
| case Qcategory: |
| if(dotdot){ |
| sf->category = 0; |
| sf->type = Qnick; |
| break; |
| } |
| if(strcmp(name, "ctl") == 0){ |
| sf->type++; |
| break; |
| } |
| if((id = getalbumid(sf, name, &key)) >= 0){ |
| sf->album = id; |
| sf->albumkey = key; |
| sf->type = Qalbum; |
| break; |
| } |
| goto NotFound; |
| |
| case Qalbum: |
| if(dotdot){ |
| free(sf->albumkey); |
| sf->albumkey = nil; |
| sf->album = 0; |
| if(sf->category == -1) |
| sf->type = Qalbums; |
| else |
| sf->type = Qcategory; |
| break; |
| } |
| if(strcmp(name, "ctl") == 0){ |
| sf->type = Qalbumctl; |
| break; |
| } |
| if(strcmp(name, "settings") == 0){ |
| sf->type = Qalbumsettings; |
| break; |
| } |
| if((id = getimageid(sf, name, &key)) >= 0){ |
| sf->image = id; |
| sf->imagekey = key; |
| sf->type = Qimage; |
| break; |
| } |
| if((u = getuploadname(sf, name)) != nil){ |
| sf->upload = u; |
| sf->type = Quploadfile; |
| break; |
| } |
| goto NotFound; |
| |
| case Qimage: |
| if(dotdot){ |
| free(sf->imagekey); |
| sf->imagekey = nil; |
| sf->image = 0; |
| sf->type = Qalbum; |
| break; |
| } |
| if(strcmp(name, "ctl") == 0){ |
| sf->type = Qimagectl; |
| break; |
| } |
| if(strcmp(name, "url") == 0){ |
| sf->type = Qimageurl; |
| break; |
| } |
| if(strcmp(name, "settings") == 0){ |
| sf->type = Qimagesettings; |
| break; |
| } |
| if(strcmp(name, "exif") == 0){ |
| sf->type = Qimageexif; |
| break; |
| } |
| x = imagename(sf); |
| if(x && strcmp(name, x) == 0){ |
| free(x); |
| sf->type = Qimagefile; |
| break; |
| } |
| free(x); |
| goto NotFound; |
| } |
| dostat(sf, qid, nil); |
| fid->qid = *qid; |
| return nil; |
| } |
| |
| static int |
| dodirgen(int i, Dir *d, void *v) |
| { |
| SmugFid *sf, xsf; |
| char *key; |
| vlong id; |
| Upload *u; |
| |
| sf = v; |
| xsf = *sf; |
| if(i-- == 0){ |
| xsf.type++; // ctl in every directory |
| dostat(&xsf, nil, d); |
| return 0; |
| } |
| |
| switch(sf->type){ |
| default: |
| return -1; |
| |
| case Qroot: |
| if(i-- == 0){ |
| xsf.type = Qrpclog; |
| dostat(&xsf, nil, d); |
| return 0; |
| } |
| if(i < 0 || i >= nnick) |
| return -1; |
| xsf.type = Qnick; |
| xsf.nickid = i; |
| dostat(&xsf, nil, d); |
| return 0; |
| |
| case Qnick: |
| if(i-- == 0){ |
| xsf.type = Qalbums; |
| dostat(&xsf, nil, d); |
| return 0; |
| } |
| if((id = getcategoryindex(sf, i)) < 0) |
| return -1; |
| xsf.type = Qcategory; |
| xsf.category = id; |
| dostat(&xsf, nil, d); |
| return 0; |
| |
| case Qalbums: |
| case Qcategory: |
| if((id = getalbumindex(sf, i, &key)) < 0) |
| return -1; |
| xsf.type = Qalbum; |
| xsf.album = id; |
| xsf.albumkey = key; |
| dostat(&xsf, nil, d); |
| free(key); |
| return 0; |
| |
| case Qalbum: |
| if(i-- == 0){ |
| xsf.type = Qalbumsettings; |
| dostat(&xsf, nil, d); |
| return 0; |
| } |
| if((u = getuploadindex(sf, &i)) != nil){ |
| xsf.upload = u; |
| xsf.type = Quploadfile; |
| dostat(&xsf, nil, d); |
| closeupload(u); |
| return 0; |
| } |
| if((id = getimageindex(sf, i, &key)) < 0) |
| return -1; |
| xsf.type = Qimage; |
| xsf.image = id; |
| xsf.imagekey = key; |
| dostat(&xsf, nil, d); |
| free(key); |
| return 0; |
| |
| case Qimage: |
| if(i-- == 0){ |
| xsf.type = Qimagefile; |
| dostat(&xsf, nil, d); |
| return 0; |
| } |
| if(i-- == 0){ |
| xsf.type = Qimageexif; |
| dostat(&xsf, nil, d); |
| return 0; |
| } |
| if(i-- == 0){ |
| xsf.type = Qimagesettings; |
| dostat(&xsf, nil, d); |
| return 0; |
| } |
| if(i-- == 0){ |
| xsf.type = Qimageurl; |
| dostat(&xsf, nil, d); |
| return 0; |
| } |
| return -1; |
| } |
| } |
| |
| static void |
| xstat(Req *r) |
| { |
| dostat(r->fid->aux, nil, &r->d); |
| respond(r, nil); |
| } |
| |
| static void |
| xwstat(Req *r) |
| { |
| SmugFid *sf; |
| Json *v; |
| char *s; |
| char strid[50]; |
| |
| sf = r->fid->aux; |
| if(r->d.uid[0] || r->d.gid[0] || r->d.muid[0] || ~r->d.mode != 0 |
| || ~r->d.atime != 0 || ~r->d.mtime != 0 || ~r->d.length != 0){ |
| respond(r, "invalid wstat"); |
| return; |
| } |
| if(r->d.name[0]){ |
| switch(sf->type){ |
| default: |
| respond(r, "invalid wstat"); |
| return; |
| // TODO: rename category |
| case Qalbum: |
| snprint(strid, sizeof strid, "%lld", sf->album); |
| v = ncsmug("smugmug.albums.changeSettings", |
| "AlbumID", strid, "Title", r->d.name, nil); |
| if(v == nil) |
| responderrstr(r); |
| else |
| respond(r, nil); |
| s = smprint("&AlbumID=%lld&", sf->album); |
| jcacheflush(s); |
| free(s); |
| jcacheflush("smugmug.albums.get&"); |
| return; |
| } |
| } |
| respond(r, "invalid wstat"); |
| } |
| |
| static void |
| xattach(Req *r) |
| { |
| SmugFid *sf; |
| |
| sf = emalloc(sizeof *sf); |
| r->fid->aux = sf; |
| sf->type = Qroot; |
| dostat(sf, &r->ofcall.qid, nil); |
| r->fid->qid = r->ofcall.qid; |
| respond(r, nil); |
| } |
| |
| void |
| xopen(Req *r) |
| { |
| SmugFid *sf; |
| |
| if((r->ifcall.mode&~OTRUNC) > 2){ |
| respond(r, "permission denied"); |
| return; |
| } |
| |
| sf = r->fid->aux; |
| switch(sf->type){ |
| case Qctl: |
| case Qnickctl: |
| case Qalbumsctl: |
| case Qcategoryctl: |
| case Qalbumctl: |
| case Qimagectl: |
| case Qalbumsettings: |
| case Qimagesettings: |
| break; |
| |
| case Quploadfile: |
| if(r->ifcall.mode != OREAD){ |
| lock(&sf->upload->lk); |
| if(sf->upload->ready){ |
| unlock(&sf->upload->lk); |
| respond(r, "permission denied"); |
| return; |
| } |
| sf->upwriter = 1; |
| sf->upload->nwriters++; |
| unlock(&sf->upload->lk); |
| } |
| break; |
| |
| default: |
| if(r->ifcall.mode != OREAD){ |
| respond(r, "permission denied"); |
| return; |
| } |
| break; |
| } |
| |
| r->ofcall.qid = r->fid->qid; |
| respond(r, nil); |
| } |
| |
| void |
| xcreate(Req *r) |
| { |
| SmugFid *sf; |
| Json *v; |
| vlong id; |
| char strid[50], *key; |
| Upload *u; |
| |
| sf = r->fid->aux; |
| switch(sf->type){ |
| case Qnick: |
| // Create new category. |
| if(!(r->ifcall.perm&DMDIR)) |
| break; |
| v = ncsmug("smugmug.categories.create", |
| "Name", r->ifcall.name, nil); |
| if(v == nil){ |
| responderrstr(r); |
| return; |
| } |
| id = jint(jwalk(v, "Category/id")); |
| if(id < 0){ |
| fprint(2, "Create category: %J\n", v); |
| jclose(v); |
| responderrstr(r); |
| return; |
| } |
| sf->type = Qcategory; |
| sf->category = id; |
| jcacheflush("method=smugmug.users.getTree&"); |
| jcacheflush("method=smugmug.categories.get&"); |
| dostat(sf, &r->ofcall.qid, nil); |
| respond(r, nil); |
| return; |
| |
| case Qcategory: |
| // Create new album. |
| if(!(r->ifcall.perm&DMDIR)) |
| break; |
| snprint(strid, sizeof strid, "%lld", sf->category); |
| // Start with most restrictive settings. |
| v = ncsmug("smugmug.albums.create", |
| "Title", r->ifcall.name, |
| "CategoryID", strid, |
| "Public", "0", |
| "WorldSearchable", "0", |
| "SmugSearchable", "0", |
| nil); |
| if(v == nil){ |
| responderrstr(r); |
| return; |
| } |
| id = jint(jwalk(v, "Album/id")); |
| key = jstring(jwalk(v, "Album/Key")); |
| if(id < 0 || key == nil){ |
| fprint(2, "Create album: %J\n", v); |
| jclose(v); |
| responderrstr(r); |
| return; |
| } |
| sf->type = Qalbum; |
| sf->album = id; |
| sf->albumkey = estrdup(key); |
| jclose(v); |
| jcacheflush("method=smugmug.users.getTree&"); |
| dostat(sf, &r->ofcall.qid, nil); |
| respond(r, nil); |
| return; |
| |
| case Qalbum: |
| // Upload image to album. |
| if(r->ifcall.perm&DMDIR) |
| break; |
| u = newupload(sf, r->ifcall.name); |
| if(u == nil){ |
| responderrstr(r); |
| return; |
| } |
| sf->upload = u; |
| sf->upwriter = 1; |
| sf->type = Quploadfile; |
| dostat(sf, &r->ofcall.qid, nil); |
| respond(r, nil); |
| return; |
| } |
| respond(r, "permission denied"); |
| } |
| |
| static int |
| writetofd(Req *r, int fd) |
| { |
| int total, n; |
| |
| total = 0; |
| while(total < r->ifcall.count){ |
| n = pwrite(fd, (char*)r->ifcall.data+total, r->ifcall.count-total, r->ifcall.offset+total); |
| if(n <= 0) |
| return -1; |
| total += n; |
| } |
| r->ofcall.count = r->ifcall.count; |
| return 0; |
| } |
| |
| static void |
| readfromfd(Req *r, int fd) |
| { |
| int n; |
| n = pread(fd, r->ofcall.data, r->ifcall.count, r->ifcall.offset); |
| if(n < 0) |
| n = 0; |
| r->ofcall.count = n; |
| } |
| |
| void |
| xread(Req *r) |
| { |
| SmugFid *sf; |
| char *data; |
| int fd; |
| HTTPHeader hdr; |
| char *url; |
| |
| sf = r->fid->aux; |
| r->ofcall.count = 0; |
| switch(sf->type){ |
| default: |
| respond(r, "not implemented"); |
| return; |
| case Qroot: |
| case Qnick: |
| case Qalbums: |
| case Qcategory: |
| case Qalbum: |
| case Qimage: |
| dirread9p(r, dodirgen, sf); |
| break; |
| case Qrpclog: |
| rpclogread(r); |
| return; |
| case Qctl: |
| case Qnickctl: |
| case Qalbumsctl: |
| case Qcategoryctl: |
| case Qalbumctl: |
| case Qimagectl: |
| case Qimageurl: |
| case Qimageexif: |
| case Quploads: |
| case Qimagesettings: |
| case Qalbumsettings: |
| data = readctl(sf); |
| readstr(r, data); |
| free(data); |
| break; |
| case Qimagefile: |
| url = imageurl(sf); |
| if(url == nil || (fd = download(url, &hdr)) < 0){ |
| free(url); |
| responderrstr(r); |
| return; |
| } |
| readfromfd(r, fd); |
| free(url); |
| close(fd); |
| break; |
| case Quploadfile: |
| if(sf->upload) |
| readfromfd(r, sf->upload->fd); |
| break; |
| } |
| respond(r, nil); |
| } |
| |
| void |
| xwrite(Req *r) |
| { |
| int sync; |
| char *s, *t, *p; |
| Json *v; |
| char strid[50]; |
| SmugFid *sf; |
| |
| sf = r->fid->aux; |
| r->ofcall.count = r->ifcall.count; |
| sync = (r->ifcall.count==4 && memcmp(r->ifcall.data, "sync", 4) == 0); |
| switch(sf->type){ |
| case Qctl: |
| if(sync){ |
| jcacheflush(nil); |
| respond(r, nil); |
| return; |
| } |
| break; |
| case Qnickctl: |
| if(sync){ |
| s = smprint("&NickName=%s&", nickname(sf->nickid)); |
| jcacheflush(s); |
| free(s); |
| respond(r, nil); |
| return; |
| } |
| break; |
| case Qalbumsctl: |
| case Qcategoryctl: |
| jcacheflush("smugmug.categories.get"); |
| break; |
| case Qalbumctl: |
| if(sync){ |
| s = smprint("&AlbumID=%lld&", sf->album); |
| jcacheflush(s); |
| free(s); |
| respond(r, nil); |
| return; |
| } |
| break; |
| case Qimagectl: |
| if(sync){ |
| s = smprint("&ImageID=%lld&", sf->image); |
| jcacheflush(s); |
| free(s); |
| respond(r, nil); |
| return; |
| } |
| break; |
| case Quploadfile: |
| if(sf->upload){ |
| if(writetofd(r, sf->upload->fd) < 0){ |
| responderrstr(r); |
| return; |
| } |
| respond(r, nil); |
| return; |
| } |
| break; |
| case Qimagesettings: |
| case Qalbumsettings: |
| s = (char*)r->ifcall.data; // lib9p nul-terminated it |
| t = strpbrk(s, " \r\t\n"); |
| if(t == nil) |
| t = ""; |
| else{ |
| *t++ = 0; |
| while(*t == ' ' || *t == '\r' || *t == '\t' || *t == '\n') |
| t++; |
| } |
| p = strchr(t, '\n'); |
| if(p && p[1] == 0) |
| *p = 0; |
| else if(p){ |
| respond(r, "newline in argument"); |
| return; |
| } |
| if(sf->type == Qalbumsettings) |
| goto Albumsettings; |
| snprint(strid, sizeof strid, "%lld", sf->image); |
| v = ncsmug("smugmug.images.changeSettings", |
| "ImageID", strid, |
| s, t, nil); |
| if(v == nil) |
| responderrstr(r); |
| else |
| respond(r, nil); |
| s = smprint("&ImageID=%lld&", sf->image); |
| jcacheflush(s); |
| free(s); |
| return; |
| Albumsettings: |
| snprint(strid, sizeof strid, "%lld", sf->album); |
| v = ncsmug("smugmug.albums.changeSettings", |
| "AlbumID", strid, s, t, nil); |
| if(v == nil) |
| responderrstr(r); |
| else |
| respond(r, nil); |
| s = smprint("&AlbumID=%lld&", sf->album); |
| jcacheflush(s); |
| free(s); |
| return; |
| } |
| respond(r, "invalid control message"); |
| return; |
| } |
| |
| void |
| xremove(Req *r) |
| { |
| char id[100]; |
| SmugFid *sf; |
| Json *v; |
| |
| sf = r->fid->aux; |
| switch(sf->type){ |
| default: |
| respond(r, "permission denied"); |
| return; |
| case Qcategoryctl: |
| case Qalbumctl: |
| case Qalbumsettings: |
| case Qimagectl: |
| case Qimagesettings: |
| case Qimageexif: |
| case Qimageurl: |
| case Qimagefile: |
| /* ignore remove request, but no error, so rm -r works */ |
| /* you can pretend they get removed and immediately grow back! */ |
| respond(r, nil); |
| return; |
| case Qcategory: |
| v = getalbums(sf); |
| if(v && v->len > 0){ |
| respond(r, "directory not empty"); |
| return; |
| } |
| snprint(id, sizeof id, "%lld", sf->category); |
| v = ncsmug("smugmug.categories.delete", |
| "CategoryID", id, nil); |
| if(v == nil) |
| responderrstr(r); |
| else{ |
| jclose(v); |
| jcacheflush("smugmug.users.getTree"); |
| jcacheflush("smugmug.categories.get"); |
| respond(r, nil); |
| } |
| return; |
| case Qalbum: |
| v = getimages(sf, sf->album, sf->albumkey); |
| if(v && v->len > 0){ |
| respond(r, "directory not empty"); |
| return; |
| } |
| snprint(id, sizeof id, "%lld", sf->album); |
| v = ncsmug("smugmug.albums.delete", |
| "AlbumID", id, nil); |
| if(v == nil) |
| responderrstr(r); |
| else{ |
| jclose(v); |
| jcacheflush("smugmug.users.getTree"); |
| jcacheflush("smugmug.categories.get"); |
| jcacheflush("smugmug.albums.get"); |
| respond(r, nil); |
| } |
| return; |
| |
| case Qimage: |
| snprint(id, sizeof id, "%lld", sf->image); |
| v = ncsmug("smugmug.images.delete", |
| "ImageID", id, nil); |
| if(v == nil) |
| responderrstr(r); |
| else{ |
| jclose(v); |
| snprint(id, sizeof id, "ImageID=%lld&", sf->image); |
| jcacheflush(id); |
| jcacheflush("smugmug.images.get&"); |
| respond(r, nil); |
| } |
| return; |
| } |
| } |
| |
| void |
| xflush(Req *r) |
| { |
| rpclogflush(r->oldreq); |
| respond(r, nil); |
| } |
| |
| Srv xsrv; |
| |
| void |
| xinit(void) |
| { |
| xsrv.attach = xattach; |
| xsrv.open = xopen; |
| xsrv.create = xcreate; |
| xsrv.read = xread; |
| xsrv.stat = xstat; |
| xsrv.walk1 = xwalk1; |
| xsrv.clone = xclone; |
| xsrv.destroyfid = xdestroyfid; |
| xsrv.remove = xremove; |
| xsrv.write = xwrite; |
| xsrv.flush = xflush; |
| xsrv.wstat = xwstat; |
| } |