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