Blame


1 18824b58 2008-08-03 rsc #include "a.h"
2 18824b58 2008-08-03 rsc
3 18824b58 2008-08-03 rsc // JSON request/reply cache.
4 18824b58 2008-08-03 rsc
5 18824b58 2008-08-03 rsc int chattyhttp;
6 18824b58 2008-08-03 rsc
7 18824b58 2008-08-03 rsc typedef struct JEntry JEntry;
8 18824b58 2008-08-03 rsc struct JEntry
9 18824b58 2008-08-03 rsc {
10 18824b58 2008-08-03 rsc CEntry ce;
11 18824b58 2008-08-03 rsc Json *reply;
12 18824b58 2008-08-03 rsc };
13 18824b58 2008-08-03 rsc
14 18824b58 2008-08-03 rsc static Cache *jsoncache;
15 18824b58 2008-08-03 rsc
16 18824b58 2008-08-03 rsc static void
17 18824b58 2008-08-03 rsc jfree(CEntry *ce)
18 18824b58 2008-08-03 rsc {
19 18824b58 2008-08-03 rsc JEntry *j;
20 18824b58 2008-08-03 rsc
21 18824b58 2008-08-03 rsc j = (JEntry*)ce;
22 18824b58 2008-08-03 rsc jclose(j->reply);
23 18824b58 2008-08-03 rsc }
24 18824b58 2008-08-03 rsc
25 18824b58 2008-08-03 rsc static JEntry*
26 18824b58 2008-08-03 rsc jcachelookup(char *request)
27 18824b58 2008-08-03 rsc {
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);
31 18824b58 2008-08-03 rsc }
32 18824b58 2008-08-03 rsc
33 18824b58 2008-08-03 rsc void
34 18824b58 2008-08-03 rsc jcacheflush(char *substr)
35 18824b58 2008-08-03 rsc {
36 18824b58 2008-08-03 rsc if(jsoncache == nil)
37 18824b58 2008-08-03 rsc return;
38 18824b58 2008-08-03 rsc cacheflush(jsoncache, substr);
39 18824b58 2008-08-03 rsc }
40 18824b58 2008-08-03 rsc
41 18824b58 2008-08-03 rsc
42 18824b58 2008-08-03 rsc // JSON RPC over HTTP
43 18824b58 2008-08-03 rsc
44 18824b58 2008-08-03 rsc static char*
45 18824b58 2008-08-03 rsc makehttprequest(char *host, char *path, char *postdata)
46 18824b58 2008-08-03 rsc {
47 18824b58 2008-08-03 rsc Fmt fmt;
48 18824b58 2008-08-03 rsc
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);
58 18824b58 2008-08-03 rsc }
59 18824b58 2008-08-03 rsc
60 18824b58 2008-08-03 rsc static char*
61 18824b58 2008-08-03 rsc makerequest(char *method, char *name1, va_list arg)
62 18824b58 2008-08-03 rsc {
63 18824b58 2008-08-03 rsc char *p, *key, *val;
64 18824b58 2008-08-03 rsc Fmt fmt;
65 18824b58 2008-08-03 rsc
66 18824b58 2008-08-03 rsc fmtstrinit(&fmt);
67 18824b58 2008-08-03 rsc fmtprint(&fmt, "&");
68 18824b58 2008-08-03 rsc p = name1;
69 18824b58 2008-08-03 rsc while(p != nil){
70 18824b58 2008-08-03 rsc key = p;
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*);
76 18824b58 2008-08-03 rsc }
77 18824b58 2008-08-03 rsc // TODO: These are SmugMug-specific, probably.
78 18824b58 2008-08-03 rsc fmtprint(&fmt, "method=%s&", method);
79 18824b58 2008-08-03 rsc if(sessid)
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);
83 18824b58 2008-08-03 rsc }
84 18824b58 2008-08-03 rsc
85 18824b58 2008-08-03 rsc static char*
86 18824b58 2008-08-03 rsc dojsonhttp(Protocol *proto, char *host, char *request, int rfd, vlong rlength)
87 18824b58 2008-08-03 rsc {
88 18824b58 2008-08-03 rsc char *data;
89 18824b58 2008-08-03 rsc HTTPHeader hdr;
90 18824b58 2008-08-03 rsc
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");
94 18824b58 2008-08-03 rsc return nil;
95 18824b58 2008-08-03 rsc }
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);
101 18824b58 2008-08-03 rsc return nil;
102 18824b58 2008-08-03 rsc }
103 18824b58 2008-08-03 rsc if(hdr.contentlength == 0){
104 18824b58 2008-08-03 rsc werrstr("no content");
105 18824b58 2008-08-03 rsc return nil;
106 18824b58 2008-08-03 rsc }
107 18824b58 2008-08-03 rsc return data;
108 18824b58 2008-08-03 rsc }
109 18824b58 2008-08-03 rsc
110 18824b58 2008-08-03 rsc Json*
111 18824b58 2008-08-03 rsc jsonrpc(Protocol *proto, char *host, char *path, char *method, char *name1, va_list arg, int usecache)
112 18824b58 2008-08-03 rsc {
113 18824b58 2008-08-03 rsc char *httpreq, *request, *reply;
114 18824b58 2008-08-03 rsc JEntry *je;
115 18824b58 2008-08-03 rsc Json *jv, *jstat, *jmsg;
116 18824b58 2008-08-03 rsc
117 18824b58 2008-08-03 rsc request = makerequest(method, name1, arg);
118 18824b58 2008-08-03 rsc
119 18824b58 2008-08-03 rsc je = nil;
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);
125 18824b58 2008-08-03 rsc }
126 18824b58 2008-08-03 rsc }
127 18824b58 2008-08-03 rsc
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);
131 18824b58 2008-08-03 rsc
132 18824b58 2008-08-03 rsc if((reply = dojsonhttp(proto, host, httpreq, -1, 0)) == nil){
133 18824b58 2008-08-03 rsc free(httpreq);
134 18824b58 2008-08-03 rsc return nil;
135 18824b58 2008-08-03 rsc }
136 18824b58 2008-08-03 rsc free(httpreq);
137 18824b58 2008-08-03 rsc
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);
142 18824b58 2008-08-03 rsc return nil;
143 18824b58 2008-08-03 rsc }
144 18824b58 2008-08-03 rsc
145 18824b58 2008-08-03 rsc if(jstrcmp((jstat = jlookup(jv, "stat")), "ok") == 0){
146 18824b58 2008-08-03 rsc if(je)
147 18824b58 2008-08-03 rsc je->reply = jincref(jv);
148 18824b58 2008-08-03 rsc return jv;
149 18824b58 2008-08-03 rsc }
150 18824b58 2008-08-03 rsc
151 18824b58 2008-08-03 rsc if(jstrcmp(jstat, "fail") == 0){
152 18824b58 2008-08-03 rsc jmsg = jlookup(jv, "message");
153 18824b58 2008-08-03 rsc if(jmsg){
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){
158 18824b58 2008-08-03 rsc jclose(jv);
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);
163 18824b58 2008-08-03 rsc return jv;
164 18824b58 2008-08-03 rsc }
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);
169 18824b58 2008-08-03 rsc jclose(jv);
170 18824b58 2008-08-03 rsc return nil;
171 18824b58 2008-08-03 rsc }
172 18824b58 2008-08-03 rsc rpclog("%s: json status: %J", method, jstat);
173 18824b58 2008-08-03 rsc jclose(jv);
174 18824b58 2008-08-03 rsc return nil;
175 18824b58 2008-08-03 rsc }
176 18824b58 2008-08-03 rsc
177 18824b58 2008-08-03 rsc rpclog("%s: json stat=%J", method, jstat);
178 18824b58 2008-08-03 rsc jclose(jv);
179 18824b58 2008-08-03 rsc return nil;
180 18824b58 2008-08-03 rsc }
181 18824b58 2008-08-03 rsc
182 18824b58 2008-08-03 rsc Json*
183 18824b58 2008-08-03 rsc ncsmug(char *method, char *name1, ...)
184 18824b58 2008-08-03 rsc {
185 18824b58 2008-08-03 rsc Json *jv;
186 18824b58 2008-08-03 rsc va_list arg;
187 18824b58 2008-08-03 rsc
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);
193 18824b58 2008-08-03 rsc return jv;
194 18824b58 2008-08-03 rsc }
195 18824b58 2008-08-03 rsc
196 18824b58 2008-08-03 rsc Json*
197 18824b58 2008-08-03 rsc smug(char *method, char *name1, ...)
198 18824b58 2008-08-03 rsc {
199 18824b58 2008-08-03 rsc Json *jv;
200 18824b58 2008-08-03 rsc va_list arg;
201 18824b58 2008-08-03 rsc
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);
205 18824b58 2008-08-03 rsc return jv;
206 18824b58 2008-08-03 rsc }
207 18824b58 2008-08-03 rsc
208 18824b58 2008-08-03 rsc Json*
209 18824b58 2008-08-03 rsc jsonupload(Protocol *proto, char *host, char *req, int rfd, vlong rlength)
210 18824b58 2008-08-03 rsc {
211 18824b58 2008-08-03 rsc Json *jv, *jstat, *jmsg;
212 18824b58 2008-08-03 rsc char *reply;
213 18824b58 2008-08-03 rsc
214 18824b58 2008-08-03 rsc if((reply = dojsonhttp(proto, host, req, rfd, rlength)) == nil)
215 18824b58 2008-08-03 rsc return nil;
216 18824b58 2008-08-03 rsc
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");
221 18824b58 2008-08-03 rsc return nil;
222 18824b58 2008-08-03 rsc }
223 18824b58 2008-08-03 rsc
224 18824b58 2008-08-03 rsc if(jstrcmp((jstat = jlookup(jv, "stat")), "ok") == 0)
225 18824b58 2008-08-03 rsc return jv;
226 18824b58 2008-08-03 rsc
227 18824b58 2008-08-03 rsc if(jstrcmp(jstat, "fail") == 0){
228 18824b58 2008-08-03 rsc jmsg = jlookup(jv, "message");
229 18824b58 2008-08-03 rsc if(jmsg){
230 18824b58 2008-08-03 rsc fprint(2, "upload: %J\n", jmsg);
231 18824b58 2008-08-03 rsc werrstr("%J", jmsg);
232 18824b58 2008-08-03 rsc jclose(jv);
233 18824b58 2008-08-03 rsc return nil;
234 18824b58 2008-08-03 rsc }
235 18824b58 2008-08-03 rsc fprint(2, "upload: json status: %J\n", jstat);
236 18824b58 2008-08-03 rsc jclose(jv);
237 18824b58 2008-08-03 rsc return nil;
238 18824b58 2008-08-03 rsc }
239 18824b58 2008-08-03 rsc
240 18824b58 2008-08-03 rsc fprint(2, "upload: %J\n", jv);
241 18824b58 2008-08-03 rsc jclose(jv);
242 18824b58 2008-08-03 rsc return nil;
243 18824b58 2008-08-03 rsc }
244 18824b58 2008-08-03 rsc