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