1 18824b58 2008-08-03 rsc #include "a.h"
3 18824b58 2008-08-03 rsc // JSON request/reply cache.
5 18824b58 2008-08-03 rsc int chattyhttp;
7 18824b58 2008-08-03 rsc typedef struct JEntry JEntry;
14 18824b58 2008-08-03 rsc static Cache *jsoncache;
17 18824b58 2008-08-03 rsc jfree(CEntry *ce)
21 18824b58 2008-08-03 rsc j = (JEntry*)ce;
22 18824b58 2008-08-03 rsc jclose(j->reply);
25 18824b58 2008-08-03 rsc static JEntry*
26 18824b58 2008-08-03 rsc jcachelookup(char *request)
28 18824b58 2008-08-03 rsc if(jsoncache == nil)
29 18824b58 2008-08-03 rsc jsoncache = newcache(sizeof(JEntry), 1000, jfree);
30 18824b58 2008-08-03 rsc return (JEntry*)cachelookup(jsoncache, request, 1);
34 18824b58 2008-08-03 rsc jcacheflush(char *substr)
36 18824b58 2008-08-03 rsc if(jsoncache == nil)
38 18824b58 2008-08-03 rsc cacheflush(jsoncache, substr);
42 18824b58 2008-08-03 rsc // JSON RPC over HTTP
45 18824b58 2008-08-03 rsc makehttprequest(char *host, char *path, char *postdata)
49 18824b58 2008-08-03 rsc fmtstrinit(&fmt);
50 18824b58 2008-08-03 rsc fmtprint(&fmt, "POST %s HTTP/1.0\r\n", path);
51 18824b58 2008-08-03 rsc fmtprint(&fmt, "Host: %s\r\n", host);
52 18824b58 2008-08-03 rsc fmtprint(&fmt, "User-Agent: " USER_AGENT "\r\n");
53 18824b58 2008-08-03 rsc fmtprint(&fmt, "Content-Type: application/x-www-form-urlencoded\r\n");
54 18824b58 2008-08-03 rsc fmtprint(&fmt, "Content-Length: %d\r\n", strlen(postdata));
55 18824b58 2008-08-03 rsc fmtprint(&fmt, "\r\n");
56 18824b58 2008-08-03 rsc fmtprint(&fmt, "%s", postdata);
57 18824b58 2008-08-03 rsc return fmtstrflush(&fmt);
61 18824b58 2008-08-03 rsc makerequest(char *method, char *name1, va_list arg)
63 18824b58 2008-08-03 rsc char *p, *key, *val;
66 18824b58 2008-08-03 rsc fmtstrinit(&fmt);
67 18824b58 2008-08-03 rsc fmtprint(&fmt, "&");
69 18824b58 2008-08-03 rsc while(p != nil){
71 18824b58 2008-08-03 rsc val = va_arg(arg, char*);
72 18824b58 2008-08-03 rsc if(val == nil)
73 18824b58 2008-08-03 rsc sysfatal("jsonrpc: nil value");
74 18824b58 2008-08-03 rsc fmtprint(&fmt, "%U=%U&", key, val);
75 18824b58 2008-08-03 rsc p = va_arg(arg, char*);
77 18824b58 2008-08-03 rsc // TODO: These are SmugMug-specific, probably.
78 18824b58 2008-08-03 rsc fmtprint(&fmt, "method=%s&", method);
80 18824b58 2008-08-03 rsc fmtprint(&fmt, "SessionID=%s&", sessid);
81 18824b58 2008-08-03 rsc fmtprint(&fmt, "APIKey=%s", APIKEY);
82 18824b58 2008-08-03 rsc return fmtstrflush(&fmt);
86 18824b58 2008-08-03 rsc dojsonhttp(Protocol *proto, char *host, char *request, int rfd, vlong rlength)
89 18824b58 2008-08-03 rsc HTTPHeader hdr;
91 18824b58 2008-08-03 rsc data = httpreq(proto, host, request, &hdr, rfd, rlength);
92 18824b58 2008-08-03 rsc if(data == nil){
93 18824b58 2008-08-03 rsc fprint(2, "httpreq: %r\n");
96 18824b58 2008-08-03 rsc if(strcmp(hdr.contenttype, "application/json") != 0 &&
97 18824b58 2008-08-03 rsc (strcmp(hdr.contenttype, "text/html; charset=utf-8") != 0 || data[0] != '{')){ // upload.smugmug.com, sigh
98 18824b58 2008-08-03 rsc werrstr("bad content type: %s", hdr.contenttype);
99 18824b58 2008-08-03 rsc fprint(2, "Content-Type: %s\n", hdr.contenttype);
100 18824b58 2008-08-03 rsc write(2, data, hdr.contentlength);
103 18824b58 2008-08-03 rsc if(hdr.contentlength == 0){
104 18824b58 2008-08-03 rsc werrstr("no content");
107 18824b58 2008-08-03 rsc return data;
111 18824b58 2008-08-03 rsc jsonrpc(Protocol *proto, char *host, char *path, char *method, char *name1, va_list arg, int usecache)
113 18824b58 2008-08-03 rsc char *httpreq, *request, *reply;
115 18824b58 2008-08-03 rsc Json *jv, *jstat, *jmsg;
117 18824b58 2008-08-03 rsc request = makerequest(method, name1, arg);
120 18824b58 2008-08-03 rsc if(usecache){
121 18824b58 2008-08-03 rsc je = jcachelookup(request);
122 18824b58 2008-08-03 rsc if(je->reply){
123 18824b58 2008-08-03 rsc free(request);
124 18824b58 2008-08-03 rsc return jincref(je->reply);
128 18824b58 2008-08-03 rsc rpclog("%T %s", request);
129 18824b58 2008-08-03 rsc httpreq = makehttprequest(host, path, request);
130 18824b58 2008-08-03 rsc free(request);
132 18824b58 2008-08-03 rsc if((reply = dojsonhttp(proto, host, httpreq, -1, 0)) == nil){
133 18824b58 2008-08-03 rsc free(httpreq);
136 18824b58 2008-08-03 rsc free(httpreq);
138 18824b58 2008-08-03 rsc jv = parsejson(reply);
139 18824b58 2008-08-03 rsc free(reply);
140 18824b58 2008-08-03 rsc if(jv == nil){
141 18824b58 2008-08-03 rsc rpclog("%s: error parsing JSON reply: %r", method);
145 18824b58 2008-08-03 rsc if(jstrcmp((jstat = jlookup(jv, "stat")), "ok") == 0){
147 18824b58 2008-08-03 rsc je->reply = jincref(jv);
151 18824b58 2008-08-03 rsc if(jstrcmp(jstat, "fail") == 0){
152 18824b58 2008-08-03 rsc jmsg = jlookup(jv, "message");
154 18824b58 2008-08-03 rsc // If there are no images, that's not an error!
155 18824b58 2008-08-03 rsc // (But SmugMug says it is.)
156 18824b58 2008-08-03 rsc if(strcmp(method, "smugmug.images.get") == 0 &&
157 18824b58 2008-08-03 rsc jstrcmp(jmsg, "empty set - no images found") == 0){
159 18824b58 2008-08-03 rsc jv = parsejson("{\"stat\":\"ok\", \"Images\":[]}");
160 18824b58 2008-08-03 rsc if(jv == nil)
161 18824b58 2008-08-03 rsc sysfatal("parsejson: %r");
162 18824b58 2008-08-03 rsc je->reply = jincref(jv);
165 18824b58 2008-08-03 rsc if(printerrors)
166 18824b58 2008-08-03 rsc fprint(2, "%s: %J\n", method, jv);
167 18824b58 2008-08-03 rsc rpclog("%s: %J", method, jmsg);
168 18824b58 2008-08-03 rsc werrstr("%J", jmsg);
172 18824b58 2008-08-03 rsc rpclog("%s: json status: %J", method, jstat);
177 18824b58 2008-08-03 rsc rpclog("%s: json stat=%J", method, jstat);
183 18824b58 2008-08-03 rsc ncsmug(char *method, char *name1, ...)
186 18824b58 2008-08-03 rsc va_list arg;
188 18824b58 2008-08-03 rsc va_start(arg, name1);
189 18824b58 2008-08-03 rsc // TODO: Could use https only for login.
190 18824b58 2008-08-03 rsc jv = jsonrpc(&https, HOST, PATH, method, name1, arg, 0);
191 18824b58 2008-08-03 rsc va_end(arg);
192 18824b58 2008-08-03 rsc rpclog("reply: %J", jv);
197 18824b58 2008-08-03 rsc smug(char *method, char *name1, ...)
200 18824b58 2008-08-03 rsc va_list arg;
202 18824b58 2008-08-03 rsc va_start(arg, name1);
203 18824b58 2008-08-03 rsc jv = jsonrpc(&http, HOST, PATH, method, name1, arg, 1);
204 18824b58 2008-08-03 rsc va_end(arg);
209 18824b58 2008-08-03 rsc jsonupload(Protocol *proto, char *host, char *req, int rfd, vlong rlength)
211 18824b58 2008-08-03 rsc Json *jv, *jstat, *jmsg;
212 18824b58 2008-08-03 rsc char *reply;
214 18824b58 2008-08-03 rsc if((reply = dojsonhttp(proto, host, req, rfd, rlength)) == nil)
217 18824b58 2008-08-03 rsc jv = parsejson(reply);
218 18824b58 2008-08-03 rsc free(reply);
219 18824b58 2008-08-03 rsc if(jv == nil){
220 18824b58 2008-08-03 rsc fprint(2, "upload: error parsing JSON reply\n");
224 18824b58 2008-08-03 rsc if(jstrcmp((jstat = jlookup(jv, "stat")), "ok") == 0)
227 18824b58 2008-08-03 rsc if(jstrcmp(jstat, "fail") == 0){
228 18824b58 2008-08-03 rsc jmsg = jlookup(jv, "message");
230 18824b58 2008-08-03 rsc fprint(2, "upload: %J\n", jmsg);
231 18824b58 2008-08-03 rsc werrstr("%J", jmsg);
235 18824b58 2008-08-03 rsc fprint(2, "upload: json status: %J\n", jstat);
240 18824b58 2008-08-03 rsc fprint(2, "upload: %J\n", jv);