Implement publish handler in 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
30 static int
31 uri_is_absolute (char *uri)
32 {
33   return (*uri != '/');
34 }
35
36 static char *
37 getbaseurl (void)
38 {
39   char *uri = NULL;
40   char *request_uri = getenv ("REQUEST_URI");
41   char *path = getenv ("PATH_INFO");
42   char *host = getenv ("HTTP_HOST");
43   char *server = getenv ("SERVER_NAME");
44   char *sport = getenv ("SERVER_PORT");
45   int port;
46   if (sport)
47     {
48       port = strtol (sport, NULL, 0);
49     }
50   else
51     {
52       port = 0;
53     }
54   if (request_uri == NULL)
55     return NULL;
56   if (uri_is_absolute (request_uri))
57     {
58       uri = strdup (request_uri);
59     }
60   else if (host)
61     {
62       asprintf (&uri, "http://%s%s", host, request_uri);
63     }
64   else if (server && port != 0 && port != 80)
65     {
66       asprintf (&uri, "http://%s:%d%s", server, port, request_uri);
67     }
68   else if (server)
69     {
70       asprintf (&uri, "http://%s%s", server, request_uri);
71     }
72   if (path && uri)
73     {
74       size_t pathl = strlen (path);
75       size_t uril = strlen (uri);
76       if (!strncmp (uri + uril - pathl, path, pathl))
77         {
78           *(uri + uril - pathl) = 0;
79         }
80     }
81   return uri;
82 }
83
84 static void
85 cgi_request_content_set (AtomCtx *ctx, AtomRequest *request)
86 {
87   GIOChannel *channel;
88   GError *error = NULL;
89   gchar *data;
90   gsize len;
91   channel = g_io_channel_unix_new (0);
92   if (g_io_channel_read_to_end (channel, &data, &len, &error) !=
93       G_IO_STATUS_NORMAL)
94     {
95       AtomError *aerr = atom_error_new_from_gerror (error);
96       g_io_channel_unref (channel);
97       atom_error_set (ctx, aerr);
98       g_error_free (error);
99       return;
100     }
101   atom_request_content_set (request, data, len);
102   g_io_channel_unref (channel);
103   g_free (data);
104 }
105
106 static AtomRequest *
107 cgi_get_request (AtomCtx *ctx)
108 {
109   AtomError *error;
110   char *method = getenv ("REQUEST_METHOD");
111   char *path = getenv ("PATH_INFO");
112   AtomRequest *request;
113   if (method == NULL)
114     {
115       error = atom_error_new ();
116       atom_error_code_set (error, 400);
117       atom_error_message_set (error, "Bad Request");
118       atom_error_set (ctx, error);
119       return NULL;
120     }
121   if (path == NULL || *path == '\0')
122     path = "/";
123   if (!strcmp (method, "GET"))
124     {
125       /* Remove the leading slash before mapping */
126       return atom_request_new (ATOM_REQUEST_GET, path + 1);
127     }
128   else if (!strcmp (method, "POST"))
129     {
130       request = atom_request_new (ATOM_REQUEST_POST, path + 1);
131       cgi_request_content_set (ctx, request);
132       if (atom_error_get (ctx) != NULL)
133         {
134           atom_request_delete (request);
135           return NULL;
136         }
137       return request;
138     }
139   error = atom_error_new ();
140   atom_error_code_set (error, 501);
141   atom_error_message_set (error, "Not Implemented");
142   atom_error_set (ctx, error);
143   return NULL;
144 }
145
146 static void
147 cgi_handle_error (AtomCtx *ctx)
148 {
149   AtomError *error;
150   if ((error = atom_error_get (ctx)) != NULL)
151     {
152       int code = atom_error_code (error);
153       char *message = atom_error_message (error);
154       fprintf (stdout, "Status: %d %s\n\n%s\n", code, message, message);
155     }
156   else
157     {
158       fprintf (stdout, "Status: 500 Server error\n\nServer error\n");
159     }
160 }
161
162 static void
163 cgi_write_header (void)
164 {
165   char *header;
166   header = "Content-type: application/atom+xml\n\n";
167   write (1, header, strlen (header));
168 }
169
170 static void
171 cgi_handle_entry (AtomCtx *ctx, AtomEntry *entry)
172 {
173   char * str;
174   size_t len;
175   cgi_write_header ();
176   atom_entry_string (entry, &str, &len);
177   atom_entry_delete (entry);
178   write (1, str, len);
179   g_free (str);
180 }
181
182 static void
183 cgi_handle_feed (AtomCtx *ctx, AtomFeed *feed)
184 {
185   char * str;
186   size_t len;
187   cgi_write_header ();
188   atom_feed_string (feed, &str, &len);
189   atom_feed_delete (feed);
190   write (1, str, len);
191   g_free (str);
192 }
193
194 static void
195 cgi_handle_publish (AtomCtx *ctx, AtomEntry *entry)
196 {
197   char * str;
198   size_t len;
199   char *req;
200   req = atom_id_to_backend (ctx, atom_entry_id (entry));
201   fprintf (stdout, "Status: 201 Created\n");
202   fprintf (stdout, "Location: %s%s\n", getbaseurl (), req);
203   cgi_write_header ();
204   atom_entry_string (entry, &str, &len);
205   atom_entry_delete (entry);
206   write (1, str, len);
207   g_free (str);
208 }
209
210 static int
211 cgi_is_feed (AtomCtx *ctx, char *req)
212 {
213   return (req == NULL || *req == '\0');
214 }
215
216 AtomFrontend *
217 cgi_frontend (void)
218 {
219   AtomFrontend *frontend;
220   frontend = atom_frontend_new ();
221   atom_frontend_map_entries_set (frontend, atom_map_frontend_requests);
222   atom_frontend_is_feed_set (frontend, cgi_is_feed);
223   atom_frontend_get_request_set (frontend, cgi_get_request);
224   atom_frontend_handle_error_set (frontend, cgi_handle_error);
225   atom_frontend_handle_entry_set (frontend, cgi_handle_entry);
226   atom_frontend_handle_feed_set (frontend, cgi_handle_feed);
227   atom_frontend_handle_publish_set (frontend, cgi_handle_publish);
228   return frontend;
229 }