Add Slug header support to CGI frontend
[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     path = "/";
140   if (!strcmp (method, "GET"))
141     {
142       /* Remove the leading slash before mapping */
143       return atom_request_new (ATOM_REQUEST_GET, path + 1);
144     }
145   else if (!strcmp (method, "POST"))
146     {
147       char *slug = getslug ();
148       char *reqname;
149       if (slug)
150         reqname = slug;
151       else
152         reqname = strdup (path + 1);
153       request = atom_request_new (ATOM_REQUEST_POST, reqname);
154       free (reqname);
155       cgi_request_content_set (ctx, request);
156       if (atom_error_get (ctx) != NULL)
157         {
158           atom_request_delete (request);
159           return NULL;
160         }
161       return request;
162     }
163   error = atom_error_new ();
164   atom_error_code_set (error, 501);
165   atom_error_message_set (error, "Not Implemented");
166   atom_error_set (ctx, error);
167   return NULL;
168 }
169
170 static void
171 cgi_handle_error (AtomCtx *ctx)
172 {
173   AtomError *error;
174   if ((error = atom_error_get (ctx)) != NULL)
175     {
176       int code = atom_error_code (error);
177       char *message = atom_error_message (error);
178       fprintf (stdout, "Status: %d %s\n\n%s\n", code, message, message);
179     }
180   else
181     {
182       fprintf (stdout, "Status: 500 Server error\n\nServer error\n");
183     }
184 }
185
186 static void
187 cgi_write_header (void)
188 {
189   char *header;
190   header = "Content-type: application/atom+xml\n\n";
191   write (1, header, strlen (header));
192 }
193
194 static void
195 cgi_handle_entry (AtomCtx *ctx, AtomEntry *entry)
196 {
197   char * str;
198   size_t len;
199   cgi_write_header ();
200   atom_entry_string (entry, &str, &len);
201   atom_entry_delete (entry);
202   write (1, str, len);
203   g_free (str);
204 }
205
206 static void
207 cgi_handle_feed (AtomCtx *ctx, AtomFeed *feed)
208 {
209   char * str;
210   size_t len;
211   cgi_write_header ();
212   atom_feed_string (feed, &str, &len);
213   atom_feed_delete (feed);
214   write (1, str, len);
215   g_free (str);
216 }
217
218 static void
219 cgi_handle_publish (AtomCtx *ctx, AtomEntry *entry)
220 {
221   char * str;
222   size_t len;
223   char *req;
224   req = atom_id_to_backend (ctx, atom_entry_id (entry));
225   fprintf (stdout, "Status: 201 Created\n");
226   fprintf (stdout, "Location: %s%s\n", getbaseurl (), req);
227   cgi_write_header ();
228   atom_entry_string (entry, &str, &len);
229   atom_entry_delete (entry);
230   write (1, str, len);
231   g_free (str);
232 }
233
234 static int
235 cgi_is_feed (AtomCtx *ctx, char *req)
236 {
237   return (req == NULL || *req == '\0');
238 }
239
240 AtomFrontend *
241 cgi_frontend (void)
242 {
243   AtomFrontend *frontend;
244   frontend = atom_frontend_new ();
245   atom_frontend_map_entries_set (frontend, atom_map_frontend_requests);
246   atom_frontend_is_feed_set (frontend, cgi_is_feed);
247   atom_frontend_get_request_set (frontend, cgi_get_request);
248   atom_frontend_handle_error_set (frontend, cgi_handle_error);
249   atom_frontend_handle_entry_set (frontend, cgi_handle_entry);
250   atom_frontend_handle_feed_set (frontend, cgi_handle_feed);
251   atom_frontend_handle_publish_set (frontend, cgi_handle_publish);
252   return frontend;
253 }