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