Factored the 'dwim' code out
[webgit] / src / webgit.c
1 /*
2     cehtehs git web frontend
3
4   Copyright (C)
5     2007, 2008,         Christian Thaeter <ct@pipapo.org>
6
7   This program is free software: you can redistribute it and/or modify
8   it under the terms of the GNU Affero General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU Affero General Public License for more details.
16
17   You should have received a copy of the GNU Affero General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22 #include "webgit.h"
23
24 #include "login.h"
25 #include "actions.h"
26 #include "query.h"
27 #include "options.h"
28 #include "rxpd_client.h"
29
30 #include "git/git-compat-util.h"
31
32 #include <cwa.h>
33
34 #include <stdio.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <time.h>
38 #include <setjmp.h>
39
40 Html error_log;
41 jmp_buf err_jmp;
42
43 static void
44 webgit_err_vargs (const char *err, va_list params)
45 {
46   html_list_append (error_log, html (html_fmt_vargs (err, params, html_print_escaped), "<br />"));
47   longjmp (err_jmp, 0);
48 }
49
50
51 void
52 webgit_err (const char *err, ...)
53 {
54   va_list args;
55   va_start (args, err);
56   webgit_err_vargs (err, args);
57   va_end (args);
58 }
59
60
61 static void
62 webgit_warn_vargs (const char *err, va_list params)
63 {
64   html_list_append (error_log, html (html_fmt_vargs (err, params, html_print_escaped), "<br />"));
65 }
66
67
68 void
69 webgit_warn (const char *err, ...)
70 {
71   va_list args;
72   va_start (args, err);
73   webgit_warn_vargs (err, args);
74   va_end (args);
75 }
76
77
78 static void
79 webgit_setup (void)
80 {
81   set_die_routine (webgit_err_vargs);
82   set_error_routine (webgit_err_vargs);
83   set_warn_routine (webgit_warn_vargs);
84 }
85
86
87 char*
88 webgit_buffer_provide (size_t size)
89 {
90   static char* buffers[32];
91   static size_t sizes[32];
92   static unsigned idx;
93
94   idx = (idx + 1) & 0x1f;
95
96   if (sizes[idx] < size)
97     {
98       free (buffers[idx]);
99       sizes[idx] = (size+32) & ~0x1f;
100       buffers[idx] = cwa_malloc (sizes[idx]);
101     }
102   return buffers[idx];
103 }
104
105
106 char*
107 webgit_webskinpath (struct webgit_query* query, const char* element)
108 {
109   char* buffer = webgit_buffer_provide (strlen (query->webskindir) + strlen (query->skin) + strlen (element) + 3);
110   sprintf (buffer, "%s/%s/%s", query->webskindir, query->skin, element);
111   return buffer;
112 }
113
114
115 char*
116 webgit_skinpath (struct webgit_query* query, const char* element)
117 {
118   char* buffer = webgit_buffer_provide (strlen (query->skindir) + strlen (query->skin) + strlen (element) + 3);
119   sprintf (buffer, "%s/%s/%s", query->skindir, query->skin, element);
120   return buffer;
121 }
122
123
124 Html
125 webgit_email_link (Html name, Html email)
126 {
127   return html (
128                html_tag("a",
129                         html_attr ("href",
130                                    html ("mailto:", email))
131                         ),
132                name
133                );
134 }
135
136
137 char*
138 webgit_mimetype (const char* name)
139 {
140   char buf[256];
141
142   if (!name)
143     return NULL;
144
145   char* ext = strrchr (name, '.');
146   if (!ext)
147     return NULL;
148   ++ext;
149   if (!*ext)
150     return NULL;
151
152   FILE* mime_types = fopen ("/etc/mime.types", "r");
153   if (!mime_types)
154     return NULL;
155
156   while (fgets (buf, 256, mime_types))
157     {
158       if (buf[0] == '#' || !strtok (buf, " \t\n"))
159         continue;
160
161       char* suffix;
162       while ((suffix = strtok (NULL, " \t\n")))
163         if (!strcmp (suffix, ext))
164           {
165             fclose (mime_types);
166             return cwa_strndup (buf, SIZE_MAX);
167           }
168     }
169   return NULL;
170 }
171
172
173 int
174 main (int argc, char**argv)
175 {
176   Html page;
177   struct webgit_query query;
178
179   setreuid (geteuid (), -1);
180   setregid (getegid (), -1);
181
182   webgit_setup();
183
184   error_log = html_list();
185
186   clock_t timestat = clock();
187
188   webgit_query_init (&query);
189
190   /* set some defaults if not already set */
191   setenv ("REQUEST_METHOD", "GET", 0);
192   setenv ("SCRIPT_NAME", argv[0], 0);
193   setenv ("WEBGIT_CONFIG", WEBGIT_CONFIG, 0);
194
195   /* we can only longjmp out of libgit's error handling */
196   if (setjmp (err_jmp))
197     goto error;
198
199   if (webgit_commandline_dispatch (argc, argv, &query))
200     goto error;
201
202   // read config
203   if (webgit_configfile_dispatch (getenv("WEBGIT_CONFIG"), &query))
204     goto error;
205
206
207   // parse request/query What to show?
208   query.request = cgi_new ();
209
210   cgi_run_query (query.request, webgit_param_dispatch, &query);
211
212   if (query.count < 0)
213     query.count = query.count_def;
214
215   /* dwim, select/complete actions and parameters */
216   webgit_action_dwim (&query);
217
218   /* initial rxpd check, just a 'global' access tuple */
219   if (query.rxpd)
220     {
221       RxpdClient rxpd = rxpd_client_new (query.rxpd, "webgit/");  /*TODO config rxpd prefix*/
222       rxpd_client_cmd (rxpd, "CHECK", "access");
223
224       if (query.request->user_agent && strchr (query.request->user_agent, '\n'))
225         webgit_err ("I dont like you");
226
227       rxpd_client_query (rxpd,
228                          "HOST=%s:USER-AGENT=%s:ACTION=%s:REPO=%s:HEAD=%s\n\n",
229                          query.request->user_addr?query.request->user_addr:"",
230                          query.request->user_agent?query.request->user_agent:"",
231                          query.action,
232                          query.repo?query.repo:"",
233                          query.head?query.head:""
234                          );
235
236       if (rxpd_client_pending (rxpd, 1000) < 1)
237         webgit_err ("rxpd check failed");
238
239       if (!!strncmp (rxpd_client_recieve (rxpd), "allow:", 6))
240         webgit_err ("access denied HOST=%s:USER-AGENT=%s:ACTION=%s:REPO=%s:HEAD=%s\n\n",
241                     query.request->user_addr?query.request->user_addr:"",
242                     query.request->user_agent?query.request->user_agent:"",
243                     query.action,
244                     query.repo?query.repo:"",
245                     query.head?query.head:""
246                     );
247
248       rxpd_client_free (rxpd);
249     }
250
251   // query to cache-line
252
253   // check if in cache
254
255
256   // Cookie prep
257
258   time_t expire = query.now + query.cookie_expire;
259   char expire_buf[32];
260   strftime (expire_buf, 32, "%a, %d %b %Y %T %z", gmtime (&expire));
261
262   webgit_login_bakecookie (&query);
263
264   // generate page
265   Html content = webgit_action_dispatch (&query);
266
267   if (query.content_type && !strcmp (query.content_type, "application/xhtml+xml"))
268     {
269       /* generate html page */
270
271       page = html(
272                   html_httpheader(
273                                   html_httpfield (
274                                                   "Content-type",
275                                                   query.content_type,
276                                                   html_attr("charset", "UTF-8")
277                                                   ),
278                                   query.login_cookie ?
279                                   html_httpfield (
280                                                   "Set-Cookie",
281                                                   html_attr ("login", html_str_printfn (query.login_cookie,
282                                                                                         html_print_urlencoded)),
283                                                   html_attr ("path", query.request->script_name),
284                                                   html_attr ("expires", expire_buf)
285                                                   // domain ...
286                                                   ) : html ()
287                                   //html_httpfield("Last-Modified", cgit_page.modified),
288                                   //html_httpfield("Expires", cgit_page.expires)
289                                   ),
290                   html_document(
291                                 "-//W3C//DTD XHTML 1.0 Transitional//EN",
292                                 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd",
293                                 html_content(
294                                              /*head*/
295                                              html(
296                                                   html(html_tag("title"), "webgit"),
297                                                   html_nl(),
298                                                   html_meta("generator", "webgit"),
299                                                   html_link_rel("stylesheet", "text/css",
300                                                                 webgit_webskinpath (&query, "css/webgit.css")
301                                                                 ),
302                                                   html_link_rel("icon", "image/png",
303                                                                 webgit_webskinpath (&query, "icons/favicon.png")
304                                                                 )
305 #ifdef ENABLE_CODEPRESS
306                                                   , html ( /*TODO, only if action == object && object == blob*/
307                                                           html_tag ("script",
308                                                                     html_attr ("src", "/codepress/codepress.js"),
309                                                                     html_attr ("type", "text/javascript")
310                                                                     )
311                                                           )
312 #endif
313
314 #ifdef ENABLE_SORTTABLE
315                                                     , html (
316                                                           html_tag ("script",
317                                                                     html_attr ("src",
318                                                                                webgit_webskinpath (&query, "js/sorttable.js")
319                                                                                ),
320                                                                     html_attr ("type", "text/javascript")
321                                                                     )
322                                                             )
323 #endif
324
325                                                   ),
326                                              /*body*/
327                                              html (
328                                                    html (
329                                                          html_tag("div",
330                                                                   html_attr("id", "container")
331                                                                   ),
332                                                          /*
333                                                            TODO header.inc should be configurable and may not exist
334                                                             The idea is to have a project header and footer available
335                                                             as plain html so that someone can install webgit
336                                                             and quickly add their project logo, menu back to the main
337                                                             project website and any other notices they may wish to add
338                                                          */
339                                                          html (
340                                                                html_tag("div",
341                                                                         html_attr("id", "header")),
342                                                                html_include (webgit_skinpath (&query, "inc/header.inc"))
343                                                                ),
344                                                          content,
345                                                          html(
346                                                               html_tag("div",
347                                                                        html_attr("id", "errors")
348                                                                        ),
349                                                               error_log
350                                                               ),
351                                                          // See TODO header.inc above
352                                                          html (
353                                                                html_tag("div",
354                                                                         html_attr("id", "footer")),
355                                                                html_include (webgit_skinpath (&query, "inc/footer.inc"))
356                                                                )
357
358                                                          )
359                                                    ),
360                                              html_attr ("xmlns", "http://www.w3.org/1999/xhtml"),
361                                              html_attr ("xml:lang", "en"),
362                                              html_attr ("lang", "en")
363                                              )
364                                 )
365                   );
366
367       timestat = clock() - timestat;
368       html_list_append (error_log, html (
369                                          html_tag("div", html_attr ("id", "timestat")),
370                                          html_fmt ("processing took %g secs\n", timestat/(float)CLOCKS_PER_SEC)
371                                          )
372                         );
373
374     }
375   else
376     {
377       /* diffrent content type, raw content */
378       html_free (error_log);
379
380       page = html(
381                   html_httpheader (
382                                    html_httpfield ("Content-type",
383                                                    query.content_type ?
384                                                    query.content_type
385                                                    : "application/octet-stream")
386                                    ),
387                   content
388                   );
389     }
390
391   html_fprint (stdout, page);
392
393   html_free (page);
394
395   webgit_query_destroy (&query);
396
397   return 0;
398
399  error:
400   page = html(
401               html_httpheader(
402                               html_httpfield(
403                                              "Content-type",
404                                              "application/xhtml+xml",
405                                              html_attr("charset", "UTF-8")
406                                              )                              ),
407               html_document(
408                             "-//W3C//DTD XHTML 1.0 Transitional//EN",
409                             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd",
410                             html_content(
411                                          /*head*/
412                                          html(
413                                               html(html_tag("title"), "ERROR: webgit"),
414                                               html_nl(),
415                                               html_meta("generator", "webgit")
416                                               ),
417                                          /*body*/
418                                          html(
419                                               html_tag("div"),
420                                               error_log
421                                               ),
422                                          html_attr("xmlns", "http://www.w3.org/1999/xhtml"),
423                                          html_attr("xml:lang", "en"),
424                                          html_attr("lang", "en")
425                                          )
426                             )
427               );
428
429   html_fprint (stdout, page);
430   html_free (page);
431   return 10;
432 }
433
434
435 /*
436 //      Local Variables:
437 //      mode: C
438 //      c-file-style: "gnu"
439 //      indent-tabs-mode: nil
440 //      End:
441 */