Let the frontend deal with bad requests for posting to member resources
[cascardo/atompub.git] / frontend / cgi / cgi.c
1 /*
2  *  Copyright (C) 2007  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19
20 #include <atompub/atom.h>
21
22 #include <glib.h>
23
24 #define _GNU_SOURCE /* for asprintf */
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <ctype.h>
30
31 static int
32 uri_is_absolute (char *uri)
33 {
34   return (*uri != '/');
35 }
36
37 static char *
38 getbaseurl (void)
39 {
40   char *uri = NULL;
41   char *request_uri = getenv ("REQUEST_URI");
42   char *path = getenv ("PATH_INFO");
43   char *host = getenv ("HTTP_HOST");
44   char *server = getenv ("SERVER_NAME");
45   char *sport = getenv ("SERVER_PORT");
46   int port;
47   if (sport)
48     {
49       port = strtol (sport, NULL, 0);
50     }
51   else
52     {
53       port = 0;
54     }
55   if (request_uri == NULL)
56     return NULL;
57   if (uri_is_absolute (request_uri))
58     {
59       uri = strdup (request_uri);
60     }
61   else if (host)
62     {
63       asprintf (&uri, "http://%s%s", host, request_uri);
64     }
65   else if (server && port != 0 && port != 80)
66     {
67       asprintf (&uri, "http://%s:%d%s", server, port, request_uri);
68     }
69   else if (server)
70     {
71       asprintf (&uri, "http://%s%s", server, request_uri);
72     }
73   if (path && uri)
74     {
75       size_t pathl = strlen (path);
76       size_t uril = strlen (uri);
77       if (!strncmp (uri + uril - pathl, path, pathl))
78         {
79           *(uri + uril - pathl) = 0;
80         }
81     }
82   return uri;
83 }
84
85 static char *
86 getslug (void)
87 {
88   char *origslug;
89   char *slug;
90   char *s;
91   origslug = getenv ("HTTP_SLUG");
92   if (origslug == NULL)
93     return NULL;
94   slug = strdup (origslug);
95   for (s = slug; *s; s++)
96     if (!isalnum (*s))
97       *s = '_';
98   return slug;
99 }
100
101 static void
102 cgi_request_content_set (AtomCtx *ctx, AtomRequest *request)
103 {
104   GIOChannel *channel;
105   GError *error = NULL;
106   gchar *data;
107   gsize len;
108   channel = g_io_channel_unix_new (0);
109   if (g_io_channel_read_to_end (channel, &data, &len, &error) !=
110       G_IO_STATUS_NORMAL)
111     {
112       AtomError *aerr = atom_error_new_from_gerror (error);
113       g_io_channel_unref (channel);
114       atom_error_set (ctx, aerr);
115       g_error_free (error);
116       return;
117     }
118   atom_request_content_set (request, data, len);
119   g_io_channel_unref (channel);
120   g_free (data);
121 }
122
123 static AtomRequest *
124 cgi_get_request (AtomCtx *ctx)
125 {
126   AtomError *error;
127   char *method = getenv ("REQUEST_METHOD");
128   char *path = getenv ("PATH_INFO");
129   AtomRequest *request;
130   if (method == NULL)
131     {
132       error = atom_error_new ();
133       atom_error_code_set (error, 400);
134       atom_error_message_set (error, "Bad Request");
135       atom_error_set (ctx, error);
136       return NULL;
137     }
138   if (path == NULL || *path == '\0')
139     {
140       if (!strcmp (method, "POST"))
141         {
142           error = atom_error_new ();
143           atom_error_code_set (error, 400);
144           atom_error_message_set (error, "Bad Request");
145           atom_error_set (ctx, error);
146           return NULL;
147         }
148       path = "/";
149     }
150   if (!strcmp (method, "GET"))
151     {
152       /* Remove the leading slash before mapping */
153       return atom_request_new (ATOM_REQUEST_GET, path + 1);
154     }
155   else if (!strcmp (method, "POST"))
156     {
157       char *slug = getslug ();
158       char *reqname;
159       if (slug)
160         reqname = slug;
161       else
162         reqname = strdup (path + 1);
163       request = atom_request_new (ATOM_REQUEST_POST, reqname);
164       free (reqname);
165       cgi_request_content_set (ctx, request);
166       if (atom_error_get (ctx) != NULL)
167         {
168           atom_request_delete (request);
169           return NULL;
170         }
171       return request;
172     }
173   error = atom_error_new ();
174   atom_error_code_set (error, 501);
175   atom_error_message_set (error, "Not Implemented");
176   atom_error_set (ctx, error);
177   return NULL;
178 }
179
180 static void
181 cgi_handle_error (AtomCtx *ctx)
182 {
183   AtomError *error;
184   if ((error = atom_error_get (ctx)) != NULL)
185     {
186       int code = atom_error_code (error);
187       char *message = atom_error_message (error);
188       fprintf (stdout, "Status: %d %s\n\n%s\n", code, message, message);
189     }
190   else
191     {
192       fprintf (stdout, "Status: 500 Server error\n\nServer error\n");
193     }
194 }
195
196 static void
197 cgi_write_header (void)
198 {
199   char *header;
200   header = "Content-type: application/atom+xml\n\n";
201   write (1, header, strlen (header));
202 }
203
204 static void
205 cgi_handle_entry (AtomCtx *ctx, AtomEntry *entry)
206 {
207   char * str;
208   size_t len;
209   cgi_write_header ();
210   atom_entry_string (entry, &str, &len);
211   atom_entry_delete (entry);
212   write (1, str, len);
213   g_free (str);
214 }
215
216 static void
217 cgi_handle_feed (AtomCtx *ctx, AtomFeed *feed)
218 {
219   char * str;
220   size_t len;
221   cgi_write_header ();
222   atom_feed_string (feed, &str, &len);
223   atom_feed_delete (feed);
224   write (1, str, len);
225   g_free (str);
226 }
227
228 static void
229 cgi_handle_publish (AtomCtx *ctx, AtomEntry *entry)
230 {
231   char * str;
232   size_t len;
233   char *req;
234   req = atom_id_to_backend (ctx, atom_entry_id (entry));
235   fprintf (stdout, "Status: 201 Created\n");
236   fprintf (stdout, "Location: %s%s\n", getbaseurl (), req);
237   cgi_write_header ();
238   atom_entry_string (entry, &str, &len);
239   atom_entry_delete (entry);
240   write (1, str, len);
241   g_free (str);
242 }
243
244 static int
245 cgi_is_feed (AtomCtx *ctx, char *req)
246 {
247   return (req == NULL || *req == '\0');
248 }
249
250 AtomFrontend *
251 cgi_frontend (void)
252 {
253   AtomFrontend *frontend;
254   frontend = atom_frontend_new ();
255   atom_frontend_map_entries_set (frontend, atom_map_frontend_requests);
256   atom_frontend_is_feed_set (frontend, cgi_is_feed);
257   atom_frontend_get_request_set (frontend, cgi_get_request);
258   atom_frontend_handle_error_set (frontend, cgi_handle_error);
259   atom_frontend_handle_entry_set (frontend, cgi_handle_entry);
260   atom_frontend_handle_feed_set (frontend, cgi_handle_feed);
261   atom_frontend_handle_publish_set (frontend, cgi_handle_publish);
262   return frontend;
263 }