Add subscription marking to keep track of things you've read
[blerg.git] / cgi / cgi_blerg.c
1 /* Blerg is (C) 2011 The Dominion of Awesome, and is distributed under a
2  * BSD-style license.  Please see the COPYING file for details.
3  */
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <cgi-util.h>
8 #include <yajl/yajl_gen.h>
9 #include "database.h"
10 #include "tags.h"
11 #include "auth.h"
12 #include "subscription.h"
13 #include "canned_responses.h"
14 #include "app.h"
15 #include "config.h"
16
17 yajl_gen_config yajl_c = { 0, 0 };
18
19 int check_auth(const char *username) {
20         if (username == NULL || username[0] == 0) {
21                 respond_403();
22                 return 0;
23         }
24
25         const char *given_token = cgi_getcookie("auth");
26         if (!auth_check_token(username, given_token)) {
27                 respond_403();
28                 return 0;
29         }
30         return 1;
31 }
32
33
34 void respond_for_range(struct blerg *b, uint64_t from, uint64_t to) {
35         const unsigned char *ybuf;
36         unsigned int len;
37         uint64_t i;
38         yajl_gen g;
39         uint64_t record_count = blerg_get_record_count(b);
40
41         printf("Content-type: application/json\r\n\r\n");
42
43         if (from > to || from >= record_count || to >= record_count || to - from > 99) {
44                 respond_JSON_Failure();
45                 return;
46         }
47
48         g = yajl_gen_alloc(&yajl_c, NULL);
49         yajl_gen_array_open(g);
50
51         for (i = to; i != from - 1; i--) {
52                 json_generate_one_record(g, NULL, b, i, 0);
53                 yajl_gen_get_buf(g, &ybuf, &len);
54                 fwrite(ybuf, len, 1, stdout);
55                 yajl_gen_clear(g);
56         }
57
58         yajl_gen_array_close(g);
59         yajl_gen_get_buf(g, &ybuf, &len);
60         fwrite(ybuf, len, 1, stdout);
61         yajl_gen_free(g);
62 }
63
64 void respond_blergref_list(struct blergref * results, int i) {
65         const unsigned char *ybuf;
66         unsigned int len;
67         struct blerg *b;
68         yajl_gen g;
69
70         i--;
71
72         printf("Content-type: application/json\r\n\r\n");
73         g = yajl_gen_alloc(&yajl_c, NULL);
74
75         yajl_gen_array_open(g);
76
77         while (i >= 0) {
78                 b = blerg_open(results[i].author);
79                 if (b != NULL) {
80                         json_generate_one_record(g, results[i].author, b, results[i].record, 0);
81                         blerg_close(b);
82                 }
83                 yajl_gen_get_buf(g, &ybuf, &len);
84                 fwrite(ybuf, len, 1, stdout);
85                 yajl_gen_clear(g);
86
87                 i--;
88         }
89
90         yajl_gen_array_close(g);
91         yajl_gen_get_buf(g, &ybuf, &len);
92         fwrite(ybuf, len, 1, stdout);
93         yajl_gen_free(g);
94 }
95
96 int main(int argc, char *argv[]) {
97         char *path;
98         char *request_method;
99         int ret, len;
100         struct url_info info;
101         char *data;
102
103         if (cgi_init() != CGIERR_NONE)
104                 exit(0);
105
106         path = getenv("PATH_INFO");
107         if (path == NULL) {
108                 respond_404();
109                 exit(0);
110         }
111         request_method = getenv("REQUEST_METHOD");
112         if (request_method == NULL) {
113                 fprintf(stderr, "Request method is null!?\n");
114                 exit(0);
115         }
116
117         if (strncmp(path, "/get", 4) == 0 && strlen(path) > 4) {
118                 if (strncmp(request_method, "GET", 4) != 0) {
119                         respond_405();
120                         exit(0);
121                 }
122
123                 if (path[4] != '/') {
124                         respond_404();
125                         exit(0);
126                 }
127
128                 ret = parse_url_info(path + 5, &info);
129                 if ((ret & URL_INFO_NAME) == 0) {
130                         respond_404();
131                         exit(0);
132                 }
133
134                 if (!blerg_exists(info.name)) {
135                         respond_404();
136                         exit(0);
137                 }
138
139                 struct blerg *b = blerg_open(info.name);
140
141                 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
142                         respond_for_range(b, info.record, info.record_to);
143                 } else if (ret & URL_INFO_RECORD) {
144                         ret = blerg_fetch(b, info.record, &data, &len);
145
146                         if (ret == 0) {
147                                 respond_404();
148                                 exit(0);
149                         }
150                         respond_simple_data(data, len);
151                 } else {
152                         uint64_t record_count, from, to;
153                         record_count = blerg_get_record_count(b);
154                         if (record_count == 0) {
155                                 respond_simple_data("[]", 2);
156                         } else {
157                                 to = record_count - 1;
158                                 from = (record_count > 50 ? to - 49 : 0);
159                                 respond_for_range(b, from, to);
160                         }
161                 }
162
163                 blerg_close(b);
164         } else if (strncmp(path, "/tag", 4) == 0 && strlen(path) > 4) {
165                 if (strcmp(request_method, "GET") != 0) {
166                         respond_405();
167                         exit(0);
168                 }
169
170                 if (path[4] != '/') {
171                         respond_404();
172                         exit(0);
173                 }
174
175                 ret = parse_url_info(path + 5, &info);
176                 if ((ret & URL_INFO_NAME) == 0) {
177                         respond_404();
178                         exit(0);
179                 }
180
181                 if (info.name[0] == 'H')
182                         info.name[0] = '#';
183                 if (!tag_exists(info.name)) {
184                         respond_404();
185                         exit(0);
186                 }
187
188                 int recs = 50;
189                 struct blergref *taglist = tag_list(info.name, 0, &recs, -1);
190
191                 if (recs == 0) {
192                         respond_simple_data("[]", 2);
193                 } else {
194                         respond_blergref_list(taglist, recs);
195                 }
196         } else if (strncmp(path, "/put", 4) == 0) {
197                 if (strcmp(request_method, "POST") != 0) {
198                         respond_405();
199                         exit(0);
200                 }
201
202                 const char *username = cgi_getentrystr("username");
203                 if (!check_auth(username))
204                         exit(0);
205
206                 if (path[4] == '/') {
207                         respond_404();
208                         exit(0);
209                 }
210
211                 const char *data = cgi_getentrystr("data");
212                 if (data == NULL || data[0] == 0) {
213                         respond_JSON_Failure();
214                         exit(0);
215                 }
216
217                 struct blerg *b = blerg_open(username);
218                 if (b == NULL) {
219                         respond_JSON_Failure();
220                         exit(0);
221                 }
222                 ret = blerg_store(b, data, strlen(data));
223                 blerg_close(b);
224                 if (ret == -1) {
225                         respond_JSON_Failure();
226                         exit(0);
227                 }
228
229                 respond_JSON_Success();
230         } else if (strncmp(path, "/info", 5) == 0) {
231                 if (strcmp(request_method, "GET") != 0) {
232                         respond_405();
233                         exit(0);
234                 }
235
236                 if (path[5] != '/') {
237                         respond_404();
238                         exit(0);
239                 }
240
241                 ret = parse_url_info(path + 6, &info);
242                 if ((ret & URL_INFO_NAME) == 0) {
243                         respond_404();
244                         exit(0);
245                 }
246
247                 if (!blerg_exists(info.name)) {
248                         respond_404();
249                         exit(0);
250                 }
251
252                 struct blerg *b = blerg_open(info.name);
253                 uint64_t record_count = blerg_get_record_count(b);
254                 blerg_close(b);
255
256                 char number[21];
257                 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
258                 yajl_gen_map_open(g);
259                 yajl_gen_string(g, "record_count", 12);
260                 snprintf(number, 21, "%llu", record_count);
261                 yajl_gen_string(g, number, strlen(number));
262                 yajl_gen_map_close(g);
263
264                 const unsigned char *ybuf;
265                 yajl_gen_get_buf(g, &ybuf, &len);
266
267                 printf("Content-type: application/json\r\n");
268                 printf("Content-length: %d\r\n\r\n", len);
269                 fwrite(ybuf, len, 1, stdout);
270
271                 yajl_gen_free(g);
272         } else if (strncmp(path, "/create", 8) == 0) {
273                 if (strcmp(request_method, "POST") != 0) {
274                         respond_405();
275                         exit(0);
276                 }
277
278                 const char *username = cgi_getentrystr("username");
279                 const char *password = cgi_getentrystr("password");
280                 if (username == NULL || username[0] == 0 ||
281                     password == NULL || password[0] == 0) {
282                         respond_JSON_Failure();
283                         exit(0);
284                 }
285
286                 if (blerg_exists(username)) {
287                         respond_JSON_Failure();
288                         exit(0);
289                 }
290
291                 struct blerg *b = blerg_open(username);
292                 blerg_close(b);
293                 auth_set_password(username, password);
294                 
295                 respond_JSON_Success();
296         } else if (strncmp(path, "/login", 7) == 0) {
297                 if (strcmp(request_method, "POST") != 0) {
298                         respond_405();
299                         exit(0);
300                 }
301
302                 const char *username = cgi_getentrystr("username");
303                 const char *password = cgi_getentrystr("password");
304                 if (username == NULL || username[0] == 0 ||
305                     password == NULL || password[0] == 0) {
306                         respond_JSON_Failure();
307                         exit(0);
308                 }
309
310                 char *token = auth_login(username, password);
311                 if (token == NULL) {
312                         respond_JSON_Failure();
313                         exit(0);
314                 }
315
316                 printf("Set-Cookie: auth=%s\r\n", token);
317                 free(token);
318
319                 respond_JSON_Success();
320         } else if (strncmp(path, "/logout", 8) == 0) {
321                 if (strcmp(request_method, "POST") != 0) {
322                         respond_405();
323                         exit(0);
324                 }
325
326                 const char *username = cgi_getentrystr("username");
327                 if (!check_auth(username))
328                         exit(0);
329
330                 const char *given_token = cgi_getcookie("auth");
331                 auth_logout(username, given_token);
332                 respond_JSON_Success();
333         } else if (strncmp(path, "/subscribe", 10) == 0 || strncmp(path, "/unsubscribe", 12) == 0) {
334                 const char *username = cgi_getentrystr("username");
335                 if (!check_auth(username))
336                         exit(0);
337
338                 if (path[1] == 'u') {
339                         if (path[12] != '/') {
340                                 respond_404();
341                                 exit(0);
342                         }
343
344                         ret = parse_url_info(path + 13, &info);
345                         if ((ret & URL_INFO_NAME) == 0) {
346                                 respond_404();
347                                 exit(0);
348                         }
349
350                         subscription_remove(username, info.name);
351                 } else {
352                         if (path[10] != '/') {
353                                 respond_404();
354                                 exit(0);
355                         }
356
357                         ret = parse_url_info(path + 11, &info);
358                         if ((ret & URL_INFO_NAME) == 0) {
359                                 respond_404();
360                                 exit(0);
361                         }
362
363                         subscription_add(username, info.name);
364                 }
365                 respond_JSON_Success();
366         } else if (strncmp(path, "/feed", 6) == 0) {
367                 const char *username = cgi_getentrystr("username");
368                 if (!check_auth(username))
369                         exit(0);
370
371                 int recs = 50;
372                 struct blergref *feedlist = subscription_list(username, 0, &recs, -1);
373
374                 if (recs == 0) {
375                         respond_simple_data("[]", 2);
376                 } else {
377                         respond_blergref_list(feedlist, recs);
378                 }
379         } else if (strncmp(path, "/feedinfo", 9) == 0) {
380                 const char *username = cgi_getentrystr("username");
381                 if (!check_auth(username))
382                         exit(0);
383
384                 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
385                 yajl_gen_map_open(g);
386                 ret = parse_url_info(path + 10, &info);
387                 if ((ret & URL_INFO_NAME) == 0) {
388                         struct blerg *b = blerg_open(username);
389                         uint64_t subscription_mark = blerg_get_subscription_mark(b);
390                         blerg_close(b);
391
392                         yajl_gen_string(g, "new", 3);
393                         yajl_gen_integer(g, subscription_count_items(username) - subscription_mark);
394                 } else {
395                         yajl_gen_string(g, "subscribed", 10);
396                         yajl_gen_bool(g, is_subscribed(username, info.name));
397
398                 }
399                 yajl_gen_map_close(g);
400
401                 const unsigned char *ybuf;
402                 yajl_gen_get_buf(g, &ybuf, &len);
403
404                 printf("Content-type: application/json\r\n");
405                 printf("Content-length: %d\r\n\r\n", len);
406                 fwrite(ybuf, len, 1, stdout);
407
408                 yajl_gen_free(g);
409         } else {
410                 respond_404();
411                 exit(0);
412         }
413
414         cgi_quit();
415 }