diff options
author | Russ Cox <rsc@swtch.com> | 2008-08-03 07:42:27 -0700 |
---|---|---|
committer | Russ Cox <rsc@swtch.com> | 2008-08-03 07:42:27 -0700 |
commit | 18824b586835525594cde126fbc90b8281d5af8b (patch) | |
tree | e8b69c05eda4543b6d2f3d30777abe6109b48b7f /src/cmd/smugfs/jsonrpc.c | |
parent | 3d36f4437348227c5bad62587dc12b5fd4a3e95e (diff) | |
download | plan9port-18824b586835525594cde126fbc90b8281d5af8b.tar.gz plan9port-18824b586835525594cde126fbc90b8281d5af8b.tar.bz2 plan9port-18824b586835525594cde126fbc90b8281d5af8b.zip |
smugfs(4): new program
Diffstat (limited to 'src/cmd/smugfs/jsonrpc.c')
-rw-r--r-- | src/cmd/smugfs/jsonrpc.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/src/cmd/smugfs/jsonrpc.c b/src/cmd/smugfs/jsonrpc.c new file mode 100644 index 00000000..92490e77 --- /dev/null +++ b/src/cmd/smugfs/jsonrpc.c @@ -0,0 +1,244 @@ +#include "a.h" + +// JSON request/reply cache. + +int chattyhttp; + +typedef struct JEntry JEntry; +struct JEntry +{ + CEntry ce; + Json *reply; +}; + +static Cache *jsoncache; + +static void +jfree(CEntry *ce) +{ + JEntry *j; + + j = (JEntry*)ce; + jclose(j->reply); +} + +static JEntry* +jcachelookup(char *request) +{ + if(jsoncache == nil) + jsoncache = newcache(sizeof(JEntry), 1000, jfree); + return (JEntry*)cachelookup(jsoncache, request, 1); +} + +void +jcacheflush(char *substr) +{ + if(jsoncache == nil) + return; + cacheflush(jsoncache, substr); +} + + +// JSON RPC over HTTP + +static char* +makehttprequest(char *host, char *path, char *postdata) +{ + Fmt fmt; + + fmtstrinit(&fmt); + fmtprint(&fmt, "POST %s HTTP/1.0\r\n", path); + fmtprint(&fmt, "Host: %s\r\n", host); + fmtprint(&fmt, "User-Agent: " USER_AGENT "\r\n"); + fmtprint(&fmt, "Content-Type: application/x-www-form-urlencoded\r\n"); + fmtprint(&fmt, "Content-Length: %d\r\n", strlen(postdata)); + fmtprint(&fmt, "\r\n"); + fmtprint(&fmt, "%s", postdata); + return fmtstrflush(&fmt); +} + +static char* +makerequest(char *method, char *name1, va_list arg) +{ + char *p, *key, *val; + Fmt fmt; + + fmtstrinit(&fmt); + fmtprint(&fmt, "&"); + p = name1; + while(p != nil){ + key = p; + val = va_arg(arg, char*); + if(val == nil) + sysfatal("jsonrpc: nil value"); + fmtprint(&fmt, "%U=%U&", key, val); + p = va_arg(arg, char*); + } + // TODO: These are SmugMug-specific, probably. + fmtprint(&fmt, "method=%s&", method); + if(sessid) + fmtprint(&fmt, "SessionID=%s&", sessid); + fmtprint(&fmt, "APIKey=%s", APIKEY); + return fmtstrflush(&fmt); +} + +static char* +dojsonhttp(Protocol *proto, char *host, char *request, int rfd, vlong rlength) +{ + char *data; + HTTPHeader hdr; + + data = httpreq(proto, host, request, &hdr, rfd, rlength); + if(data == nil){ + fprint(2, "httpreq: %r\n"); + return nil; + } + if(strcmp(hdr.contenttype, "application/json") != 0 && + (strcmp(hdr.contenttype, "text/html; charset=utf-8") != 0 || data[0] != '{')){ // upload.smugmug.com, sigh + werrstr("bad content type: %s", hdr.contenttype); + fprint(2, "Content-Type: %s\n", hdr.contenttype); + write(2, data, hdr.contentlength); + return nil; + } + if(hdr.contentlength == 0){ + werrstr("no content"); + return nil; + } + return data; +} + +Json* +jsonrpc(Protocol *proto, char *host, char *path, char *method, char *name1, va_list arg, int usecache) +{ + char *httpreq, *request, *reply; + JEntry *je; + Json *jv, *jstat, *jmsg; + + request = makerequest(method, name1, arg); + + je = nil; + if(usecache){ + je = jcachelookup(request); + if(je->reply){ + free(request); + return jincref(je->reply); + } + } + + rpclog("%T %s", request); + httpreq = makehttprequest(host, path, request); + free(request); + + if((reply = dojsonhttp(proto, host, httpreq, -1, 0)) == nil){ + free(httpreq); + return nil; + } + free(httpreq); + + jv = parsejson(reply); + free(reply); + if(jv == nil){ + rpclog("%s: error parsing JSON reply: %r", method); + return nil; + } + + if(jstrcmp((jstat = jlookup(jv, "stat")), "ok") == 0){ + if(je) + je->reply = jincref(jv); + return jv; + } + + if(jstrcmp(jstat, "fail") == 0){ + jmsg = jlookup(jv, "message"); + if(jmsg){ + // If there are no images, that's not an error! + // (But SmugMug says it is.) + if(strcmp(method, "smugmug.images.get") == 0 && + jstrcmp(jmsg, "empty set - no images found") == 0){ + jclose(jv); + jv = parsejson("{\"stat\":\"ok\", \"Images\":[]}"); + if(jv == nil) + sysfatal("parsejson: %r"); + je->reply = jincref(jv); + return jv; + } + if(printerrors) + fprint(2, "%s: %J\n", method, jv); + rpclog("%s: %J", method, jmsg); + werrstr("%J", jmsg); + jclose(jv); + return nil; + } + rpclog("%s: json status: %J", method, jstat); + jclose(jv); + return nil; + } + + rpclog("%s: json stat=%J", method, jstat); + jclose(jv); + return nil; +} + +Json* +ncsmug(char *method, char *name1, ...) +{ + Json *jv; + va_list arg; + + va_start(arg, name1); + // TODO: Could use https only for login. + jv = jsonrpc(&https, HOST, PATH, method, name1, arg, 0); + va_end(arg); + rpclog("reply: %J", jv); + return jv; +} + +Json* +smug(char *method, char *name1, ...) +{ + Json *jv; + va_list arg; + + va_start(arg, name1); + jv = jsonrpc(&http, HOST, PATH, method, name1, arg, 1); + va_end(arg); + return jv; +} + +Json* +jsonupload(Protocol *proto, char *host, char *req, int rfd, vlong rlength) +{ + Json *jv, *jstat, *jmsg; + char *reply; + + if((reply = dojsonhttp(proto, host, req, rfd, rlength)) == nil) + return nil; + + jv = parsejson(reply); + free(reply); + if(jv == nil){ + fprint(2, "upload: error parsing JSON reply\n"); + return nil; + } + + if(jstrcmp((jstat = jlookup(jv, "stat")), "ok") == 0) + return jv; + + if(jstrcmp(jstat, "fail") == 0){ + jmsg = jlookup(jv, "message"); + if(jmsg){ + fprint(2, "upload: %J\n", jmsg); + werrstr("%J", jmsg); + jclose(jv); + return nil; + } + fprint(2, "upload: json status: %J\n", jstat); + jclose(jv); + return nil; + } + + fprint(2, "upload: %J\n", jv); + jclose(jv); + return nil; +} + |