summaryrefslogtreecommitdiffabout
path: root/ui-shared.c
authorEric Wong <normalperson@yhbt.net>2008-09-01 06:30:33 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-09-01 23:26:37 (UTC)
commit9c931b1e6e68f8dc891a5653035c3d70038ae3c0 (patch) (unidiff)
treea5cd6d953eb66ad77eb4e5b088e2022973fc4f61 /ui-shared.c
parent687fb6d833d25144c86d1841fe0eca611f1eaeb1 (diff)
downloadcgit-9c931b1e6e68f8dc891a5653035c3d70038ae3c0.zip
cgit-9c931b1e6e68f8dc891a5653035c3d70038ae3c0.tar.gz
cgit-9c931b1e6e68f8dc891a5653035c3d70038ae3c0.tar.bz2
use Host: header to generate cgit_hosturl
I run an instance of lighttpd for cgit behind nginx (nginx doesn't execute CGI). So the port (SERVER_PORT=33333) that lighttpd runs on sends to cgit is different from the standard port 80 that public clients connect to (via nginx). This was causing the Atom feed URL to show the private port number that lighttpd was running on. Since the HTTP/1.1 "Host" header includes the port number if running on a non-standard port, it allows non-client-facing HTTP servers to transparently generate public URLs that clients can see. So use the "Host" header if it is available and fall back to SERVER_NAME/SERVER_PORT for some clients that don't set HTTP_HOST. Signed-off-by: Eric Wong <normalperson@yhbt.net>
Diffstat (limited to 'ui-shared.c') (more/less context) (ignore whitespace changes)
-rw-r--r--ui-shared.c19
1 files changed, 12 insertions, 7 deletions
diff --git a/ui-shared.c b/ui-shared.c
index 4818e70..c23bc75 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,560 +1,565 @@
1/* ui-shared.c: common web output functions 1/* ui-shared.c: common web output functions
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "cmd.h" 10#include "cmd.h"
11#include "html.h" 11#include "html.h"
12 12
13const char cgit_doctype[] = 13const char cgit_doctype[] =
14"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" 14"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
15" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 15" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
16 16
17static char *http_date(time_t t) 17static char *http_date(time_t t)
18{ 18{
19 static char day[][4] = 19 static char day[][4] =
20 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 20 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
21 static char month[][4] = 21 static char month[][4] =
22 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 22 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
23 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; 23 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"};
24 struct tm *tm = gmtime(&t); 24 struct tm *tm = gmtime(&t);
25 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], 25 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
26 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, 26 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year,
27 tm->tm_hour, tm->tm_min, tm->tm_sec); 27 tm->tm_hour, tm->tm_min, tm->tm_sec);
28} 28}
29 29
30void cgit_print_error(char *msg) 30void cgit_print_error(char *msg)
31{ 31{
32 html("<div class='error'>"); 32 html("<div class='error'>");
33 html_txt(msg); 33 html_txt(msg);
34 html("</div>\n"); 34 html("</div>\n");
35} 35}
36 36
37char *cgit_hosturl() 37char *cgit_hosturl()
38{ 38{
39 char *host, *port; 39 char *host, *port;
40 40
41 host = getenv("SERVER_NAME"); 41 host = getenv("HTTP_HOST");
42 if (!host) 42 if (host) {
43 return NULL;
44 port = getenv("SERVER_PORT");
45 if (port && atoi(port) != 80)
46 host = xstrdup(fmt("%s:%d", host, atoi(port)));
47 else
48 host = xstrdup(host); 43 host = xstrdup(host);
44 } else {
45 host = getenv("SERVER_NAME");
46 if (!host)
47 return NULL;
48 port = getenv("SERVER_PORT");
49 if (port && atoi(port) != 80)
50 host = xstrdup(fmt("%s:%d", host, atoi(port)));
51 else
52 host = xstrdup(host);
53 }
49 return host; 54 return host;
50} 55}
51 56
52char *cgit_rooturl() 57char *cgit_rooturl()
53{ 58{
54 if (ctx.cfg.virtual_root) 59 if (ctx.cfg.virtual_root)
55 return fmt("%s/", ctx.cfg.virtual_root); 60 return fmt("%s/", ctx.cfg.virtual_root);
56 else 61 else
57 return ctx.cfg.script_name; 62 return ctx.cfg.script_name;
58} 63}
59 64
60char *cgit_repourl(const char *reponame) 65char *cgit_repourl(const char *reponame)
61{ 66{
62 if (ctx.cfg.virtual_root) { 67 if (ctx.cfg.virtual_root) {
63 return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); 68 return fmt("%s/%s/", ctx.cfg.virtual_root, reponame);
64 } else { 69 } else {
65 return fmt("?r=%s", reponame); 70 return fmt("?r=%s", reponame);
66 } 71 }
67} 72}
68 73
69char *cgit_fileurl(const char *reponame, const char *pagename, 74char *cgit_fileurl(const char *reponame, const char *pagename,
70 const char *filename, const char *query) 75 const char *filename, const char *query)
71{ 76{
72 char *tmp; 77 char *tmp;
73 char *delim; 78 char *delim;
74 79
75 if (ctx.cfg.virtual_root) { 80 if (ctx.cfg.virtual_root) {
76 tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, 81 tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame,
77 pagename, (filename ? filename:"")); 82 pagename, (filename ? filename:""));
78 delim = "?"; 83 delim = "?";
79 } else { 84 } else {
80 tmp = fmt("?url=%s/%s/%s", reponame, pagename, 85 tmp = fmt("?url=%s/%s/%s", reponame, pagename,
81 (filename ? filename : "")); 86 (filename ? filename : ""));
82 delim = "&"; 87 delim = "&";
83 } 88 }
84 if (query) 89 if (query)
85 tmp = fmt("%s%s%s", tmp, delim, query); 90 tmp = fmt("%s%s%s", tmp, delim, query);
86 return tmp; 91 return tmp;
87} 92}
88 93
89char *cgit_pageurl(const char *reponame, const char *pagename, 94char *cgit_pageurl(const char *reponame, const char *pagename,
90 const char *query) 95 const char *query)
91{ 96{
92 return cgit_fileurl(reponame,pagename,0,query); 97 return cgit_fileurl(reponame,pagename,0,query);
93} 98}
94 99
95const char *cgit_repobasename(const char *reponame) 100const char *cgit_repobasename(const char *reponame)
96{ 101{
97 /* I assume we don't need to store more than one repo basename */ 102 /* I assume we don't need to store more than one repo basename */
98 static char rvbuf[1024]; 103 static char rvbuf[1024];
99 int p; 104 int p;
100 const char *rv; 105 const char *rv;
101 strncpy(rvbuf,reponame,sizeof(rvbuf)); 106 strncpy(rvbuf,reponame,sizeof(rvbuf));
102 if(rvbuf[sizeof(rvbuf)-1]) 107 if(rvbuf[sizeof(rvbuf)-1])
103 die("cgit_repobasename: truncated repository name '%s'", reponame); 108 die("cgit_repobasename: truncated repository name '%s'", reponame);
104 p = strlen(rvbuf)-1; 109 p = strlen(rvbuf)-1;
105 /* strip trailing slashes */ 110 /* strip trailing slashes */
106 while(p && rvbuf[p]=='/') rvbuf[p--]=0; 111 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
107 /* strip trailing .git */ 112 /* strip trailing .git */
108 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { 113 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
109 p -= 3; rvbuf[p--] = 0; 114 p -= 3; rvbuf[p--] = 0;
110 } 115 }
111 /* strip more trailing slashes if any */ 116 /* strip more trailing slashes if any */
112 while( p && rvbuf[p]=='/') rvbuf[p--]=0; 117 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
113 /* find last slash in the remaining string */ 118 /* find last slash in the remaining string */
114 rv = strrchr(rvbuf,'/'); 119 rv = strrchr(rvbuf,'/');
115 if(rv) 120 if(rv)
116 return ++rv; 121 return ++rv;
117 return rvbuf; 122 return rvbuf;
118} 123}
119 124
120char *cgit_currurl() 125char *cgit_currurl()
121{ 126{
122 if (!ctx.cfg.virtual_root) 127 if (!ctx.cfg.virtual_root)
123 return ctx.cfg.script_name; 128 return ctx.cfg.script_name;
124 else if (ctx.qry.page) 129 else if (ctx.qry.page)
125 return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page); 130 return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page);
126 else if (ctx.qry.repo) 131 else if (ctx.qry.repo)
127 return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo); 132 return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo);
128 else 133 else
129 return fmt("%s/", ctx.cfg.virtual_root); 134 return fmt("%s/", ctx.cfg.virtual_root);
130} 135}
131 136
132static void site_url(char *page, char *search, int ofs) 137static void site_url(char *page, char *search, int ofs)
133{ 138{
134 char *delim = "?"; 139 char *delim = "?";
135 140
136 if (ctx.cfg.virtual_root) { 141 if (ctx.cfg.virtual_root) {
137 html_attr(ctx.cfg.virtual_root); 142 html_attr(ctx.cfg.virtual_root);
138 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') 143 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
139 html("/"); 144 html("/");
140 } else 145 } else
141 html(ctx.cfg.script_name); 146 html(ctx.cfg.script_name);
142 147
143 if (page) { 148 if (page) {
144 htmlf("?p=%s", page); 149 htmlf("?p=%s", page);
145 delim = "&"; 150 delim = "&";
146 } 151 }
147 if (search) { 152 if (search) {
148 html(delim); 153 html(delim);
149 html("q="); 154 html("q=");
150 html_attr(search); 155 html_attr(search);
151 delim = "&"; 156 delim = "&";
152 } 157 }
153 if (ofs) { 158 if (ofs) {
154 html(delim); 159 html(delim);
155 htmlf("ofs=%d", ofs); 160 htmlf("ofs=%d", ofs);
156 } 161 }
157} 162}
158 163
159static void site_link(char *page, char *name, char *title, char *class, 164static void site_link(char *page, char *name, char *title, char *class,
160 char *search, int ofs) 165 char *search, int ofs)
161{ 166{
162 html("<a"); 167 html("<a");
163 if (title) { 168 if (title) {
164 html(" title='"); 169 html(" title='");
165 html_attr(title); 170 html_attr(title);
166 html("'"); 171 html("'");
167 } 172 }
168 if (class) { 173 if (class) {
169 html(" class='"); 174 html(" class='");
170 html_attr(class); 175 html_attr(class);
171 html("'"); 176 html("'");
172 } 177 }
173 html(" href='"); 178 html(" href='");
174 site_url(page, search, ofs); 179 site_url(page, search, ofs);
175 html("'>"); 180 html("'>");
176 html_txt(name); 181 html_txt(name);
177 html("</a>"); 182 html("</a>");
178} 183}
179 184
180void cgit_index_link(char *name, char *title, char *class, char *pattern, 185void cgit_index_link(char *name, char *title, char *class, char *pattern,
181 int ofs) 186 int ofs)
182{ 187{
183 site_link(NULL, name, title, class, pattern, ofs); 188 site_link(NULL, name, title, class, pattern, ofs);
184} 189}
185 190
186static char *repolink(char *title, char *class, char *page, char *head, 191static char *repolink(char *title, char *class, char *page, char *head,
187 char *path) 192 char *path)
188{ 193{
189 char *delim = "?"; 194 char *delim = "?";
190 195
191 html("<a"); 196 html("<a");
192 if (title) { 197 if (title) {
193 html(" title='"); 198 html(" title='");
194 html_attr(title); 199 html_attr(title);
195 html("'"); 200 html("'");
196 } 201 }
197 if (class) { 202 if (class) {
198 html(" class='"); 203 html(" class='");
199 html_attr(class); 204 html_attr(class);
200 html("'"); 205 html("'");
201 } 206 }
202 html(" href='"); 207 html(" href='");
203 if (ctx.cfg.virtual_root) { 208 if (ctx.cfg.virtual_root) {
204 html_attr(ctx.cfg.virtual_root); 209 html_attr(ctx.cfg.virtual_root);
205 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') 210 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
206 html("/"); 211 html("/");
207 html_attr(ctx.repo->url); 212 html_attr(ctx.repo->url);
208 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') 213 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
209 html("/"); 214 html("/");
210 if (page) { 215 if (page) {
211 html(page); 216 html(page);
212 html("/"); 217 html("/");
213 if (path) 218 if (path)
214 html_attr(path); 219 html_attr(path);
215 } 220 }
216 } else { 221 } else {
217 html(ctx.cfg.script_name); 222 html(ctx.cfg.script_name);
218 html("?url="); 223 html("?url=");
219 html_attr(ctx.repo->url); 224 html_attr(ctx.repo->url);
220 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') 225 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
221 html("/"); 226 html("/");
222 if (page) { 227 if (page) {
223 html(page); 228 html(page);
224 html("/"); 229 html("/");
225 if (path) 230 if (path)
226 html_attr(path); 231 html_attr(path);
227 } 232 }
228 delim = "&amp;"; 233 delim = "&amp;";
229 } 234 }
230 if (head && strcmp(head, ctx.repo->defbranch)) { 235 if (head && strcmp(head, ctx.repo->defbranch)) {
231 html(delim); 236 html(delim);
232 html("h="); 237 html("h=");
233 html_attr(head); 238 html_attr(head);
234 delim = "&amp;"; 239 delim = "&amp;";
235 } 240 }
236 return fmt("%s", delim); 241 return fmt("%s", delim);
237} 242}
238 243
239static void reporevlink(char *page, char *name, char *title, char *class, 244static void reporevlink(char *page, char *name, char *title, char *class,
240 char *head, char *rev, char *path) 245 char *head, char *rev, char *path)
241{ 246{
242 char *delim; 247 char *delim;
243 248
244 delim = repolink(title, class, page, head, path); 249 delim = repolink(title, class, page, head, path);
245 if (rev && strcmp(rev, ctx.qry.head)) { 250 if (rev && strcmp(rev, ctx.qry.head)) {
246 html(delim); 251 html(delim);
247 html("id="); 252 html("id=");
248 html_attr(rev); 253 html_attr(rev);
249 } 254 }
250 html("'>"); 255 html("'>");
251 html_txt(name); 256 html_txt(name);
252 html("</a>"); 257 html("</a>");
253} 258}
254 259
255void cgit_tree_link(char *name, char *title, char *class, char *head, 260void cgit_tree_link(char *name, char *title, char *class, char *head,
256 char *rev, char *path) 261 char *rev, char *path)
257{ 262{
258 reporevlink("tree", name, title, class, head, rev, path); 263 reporevlink("tree", name, title, class, head, rev, path);
259} 264}
260 265
261void cgit_plain_link(char *name, char *title, char *class, char *head, 266void cgit_plain_link(char *name, char *title, char *class, char *head,
262 char *rev, char *path) 267 char *rev, char *path)
263{ 268{
264 reporevlink("plain", name, title, class, head, rev, path); 269 reporevlink("plain", name, title, class, head, rev, path);
265} 270}
266 271
267void cgit_log_link(char *name, char *title, char *class, char *head, 272void cgit_log_link(char *name, char *title, char *class, char *head,
268 char *rev, char *path, int ofs, char *grep, char *pattern) 273 char *rev, char *path, int ofs, char *grep, char *pattern)
269{ 274{
270 char *delim; 275 char *delim;
271 276
272 delim = repolink(title, class, "log", head, path); 277 delim = repolink(title, class, "log", head, path);
273 if (rev && strcmp(rev, ctx.qry.head)) { 278 if (rev && strcmp(rev, ctx.qry.head)) {
274 html(delim); 279 html(delim);
275 html("id="); 280 html("id=");
276 html_attr(rev); 281 html_attr(rev);
277 delim = "&"; 282 delim = "&";
278 } 283 }
279 if (grep && pattern) { 284 if (grep && pattern) {
280 html(delim); 285 html(delim);
281 html("qt="); 286 html("qt=");
282 html_attr(grep); 287 html_attr(grep);
283 delim = "&"; 288 delim = "&";
284 html(delim); 289 html(delim);
285 html("q="); 290 html("q=");
286 html_attr(pattern); 291 html_attr(pattern);
287 } 292 }
288 if (ofs > 0) { 293 if (ofs > 0) {
289 html(delim); 294 html(delim);
290 html("ofs="); 295 html("ofs=");
291 htmlf("%d", ofs); 296 htmlf("%d", ofs);
292 } 297 }
293 html("'>"); 298 html("'>");
294 html_txt(name); 299 html_txt(name);
295 html("</a>"); 300 html("</a>");
296} 301}
297 302
298void cgit_commit_link(char *name, char *title, char *class, char *head, 303void cgit_commit_link(char *name, char *title, char *class, char *head,
299 char *rev) 304 char *rev)
300{ 305{
301 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { 306 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
302 name[ctx.cfg.max_msg_len] = '\0'; 307 name[ctx.cfg.max_msg_len] = '\0';
303 name[ctx.cfg.max_msg_len - 1] = '.'; 308 name[ctx.cfg.max_msg_len - 1] = '.';
304 name[ctx.cfg.max_msg_len - 2] = '.'; 309 name[ctx.cfg.max_msg_len - 2] = '.';
305 name[ctx.cfg.max_msg_len - 3] = '.'; 310 name[ctx.cfg.max_msg_len - 3] = '.';
306 } 311 }
307 reporevlink("commit", name, title, class, head, rev, NULL); 312 reporevlink("commit", name, title, class, head, rev, NULL);
308} 313}
309 314
310void cgit_refs_link(char *name, char *title, char *class, char *head, 315void cgit_refs_link(char *name, char *title, char *class, char *head,
311 char *rev, char *path) 316 char *rev, char *path)
312{ 317{
313 reporevlink("refs", name, title, class, head, rev, path); 318 reporevlink("refs", name, title, class, head, rev, path);
314} 319}
315 320
316void cgit_snapshot_link(char *name, char *title, char *class, char *head, 321void cgit_snapshot_link(char *name, char *title, char *class, char *head,
317 char *rev, char *archivename) 322 char *rev, char *archivename)
318{ 323{
319 reporevlink("snapshot", name, title, class, head, rev, archivename); 324 reporevlink("snapshot", name, title, class, head, rev, archivename);
320} 325}
321 326
322void cgit_diff_link(char *name, char *title, char *class, char *head, 327void cgit_diff_link(char *name, char *title, char *class, char *head,
323 char *new_rev, char *old_rev, char *path) 328 char *new_rev, char *old_rev, char *path)
324{ 329{
325 char *delim; 330 char *delim;
326 331
327 delim = repolink(title, class, "diff", head, path); 332 delim = repolink(title, class, "diff", head, path);
328 if (new_rev && strcmp(new_rev, ctx.qry.head)) { 333 if (new_rev && strcmp(new_rev, ctx.qry.head)) {
329 html(delim); 334 html(delim);
330 html("id="); 335 html("id=");
331 html_attr(new_rev); 336 html_attr(new_rev);
332 delim = "&amp;"; 337 delim = "&amp;";
333 } 338 }
334 if (old_rev) { 339 if (old_rev) {
335 html(delim); 340 html(delim);
336 html("id2="); 341 html("id2=");
337 html_attr(old_rev); 342 html_attr(old_rev);
338 } 343 }
339 html("'>"); 344 html("'>");
340 html_txt(name); 345 html_txt(name);
341 html("</a>"); 346 html("</a>");
342} 347}
343 348
344void cgit_patch_link(char *name, char *title, char *class, char *head, 349void cgit_patch_link(char *name, char *title, char *class, char *head,
345 char *rev) 350 char *rev)
346{ 351{
347 reporevlink("patch", name, title, class, head, rev, NULL); 352 reporevlink("patch", name, title, class, head, rev, NULL);
348} 353}
349 354
350void cgit_object_link(struct object *obj) 355void cgit_object_link(struct object *obj)
351{ 356{
352 char *page, *arg, *url; 357 char *page, *arg, *url;
353 358
354 if (obj->type == OBJ_COMMIT) { 359 if (obj->type == OBJ_COMMIT) {
355 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, 360 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
356 ctx.qry.head, sha1_to_hex(obj->sha1)); 361 ctx.qry.head, sha1_to_hex(obj->sha1));
357 return; 362 return;
358 } else if (obj->type == OBJ_TREE) { 363 } else if (obj->type == OBJ_TREE) {
359 page = "tree"; 364 page = "tree";
360 arg = "id"; 365 arg = "id";
361 } else if (obj->type == OBJ_TAG) { 366 } else if (obj->type == OBJ_TAG) {
362 page = "tag"; 367 page = "tag";
363 arg = "id"; 368 arg = "id";
364 } else { 369 } else {
365 page = "blob"; 370 page = "blob";
366 arg = "id"; 371 arg = "id";
367 } 372 }
368 373
369 url = cgit_pageurl(ctx.qry.repo, page, 374 url = cgit_pageurl(ctx.qry.repo, page,
370 fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); 375 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
371 html_link_open(url, NULL, NULL); 376 html_link_open(url, NULL, NULL);
372 htmlf("%s %s", typename(obj->type), 377 htmlf("%s %s", typename(obj->type),
373 sha1_to_hex(obj->sha1)); 378 sha1_to_hex(obj->sha1));
374 html_link_close(); 379 html_link_close();
375} 380}
376 381
377void cgit_print_date(time_t secs, char *format, int local_time) 382void cgit_print_date(time_t secs, char *format, int local_time)
378{ 383{
379 char buf[64]; 384 char buf[64];
380 struct tm *time; 385 struct tm *time;
381 386
382 if (!secs) 387 if (!secs)
383 return; 388 return;
384 if(local_time) 389 if(local_time)
385 time = localtime(&secs); 390 time = localtime(&secs);
386 else 391 else
387 time = gmtime(&secs); 392 time = gmtime(&secs);
388 strftime(buf, sizeof(buf)-1, format, time); 393 strftime(buf, sizeof(buf)-1, format, time);
389 html_txt(buf); 394 html_txt(buf);
390} 395}
391 396
392void cgit_print_age(time_t t, time_t max_relative, char *format) 397void cgit_print_age(time_t t, time_t max_relative, char *format)
393{ 398{
394 time_t now, secs; 399 time_t now, secs;
395 400
396 if (!t) 401 if (!t)
397 return; 402 return;
398 time(&now); 403 time(&now);
399 secs = now - t; 404 secs = now - t;
400 405
401 if (secs > max_relative && max_relative >= 0) { 406 if (secs > max_relative && max_relative >= 0) {
402 cgit_print_date(t, format, ctx.cfg.local_time); 407 cgit_print_date(t, format, ctx.cfg.local_time);
403 return; 408 return;
404 } 409 }
405 410
406 if (secs < TM_HOUR * 2) { 411 if (secs < TM_HOUR * 2) {
407 htmlf("<span class='age-mins'>%.0f min.</span>", 412 htmlf("<span class='age-mins'>%.0f min.</span>",
408 secs * 1.0 / TM_MIN); 413 secs * 1.0 / TM_MIN);
409 return; 414 return;
410 } 415 }
411 if (secs < TM_DAY * 2) { 416 if (secs < TM_DAY * 2) {
412 htmlf("<span class='age-hours'>%.0f hours</span>", 417 htmlf("<span class='age-hours'>%.0f hours</span>",
413 secs * 1.0 / TM_HOUR); 418 secs * 1.0 / TM_HOUR);
414 return; 419 return;
415 } 420 }
416 if (secs < TM_WEEK * 2) { 421 if (secs < TM_WEEK * 2) {
417 htmlf("<span class='age-days'>%.0f days</span>", 422 htmlf("<span class='age-days'>%.0f days</span>",
418 secs * 1.0 / TM_DAY); 423 secs * 1.0 / TM_DAY);
419 return; 424 return;
420 } 425 }
421 if (secs < TM_MONTH * 2) { 426 if (secs < TM_MONTH * 2) {
422 htmlf("<span class='age-weeks'>%.0f weeks</span>", 427 htmlf("<span class='age-weeks'>%.0f weeks</span>",
423 secs * 1.0 / TM_WEEK); 428 secs * 1.0 / TM_WEEK);
424 return; 429 return;
425 } 430 }
426 if (secs < TM_YEAR * 2) { 431 if (secs < TM_YEAR * 2) {
427 htmlf("<span class='age-months'>%.0f months</span>", 432 htmlf("<span class='age-months'>%.0f months</span>",
428 secs * 1.0 / TM_MONTH); 433 secs * 1.0 / TM_MONTH);
429 return; 434 return;
430 } 435 }
431 htmlf("<span class='age-years'>%.0f years</span>", 436 htmlf("<span class='age-years'>%.0f years</span>",
432 secs * 1.0 / TM_YEAR); 437 secs * 1.0 / TM_YEAR);
433} 438}
434 439
435void cgit_print_http_headers(struct cgit_context *ctx) 440void cgit_print_http_headers(struct cgit_context *ctx)
436{ 441{
437 if (ctx->page.mimetype && ctx->page.charset) 442 if (ctx->page.mimetype && ctx->page.charset)
438 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, 443 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
439 ctx->page.charset); 444 ctx->page.charset);
440 else if (ctx->page.mimetype) 445 else if (ctx->page.mimetype)
441 htmlf("Content-Type: %s\n", ctx->page.mimetype); 446 htmlf("Content-Type: %s\n", ctx->page.mimetype);
442 if (ctx->page.size) 447 if (ctx->page.size)
443 htmlf("Content-Length: %ld\n", ctx->page.size); 448 htmlf("Content-Length: %ld\n", ctx->page.size);
444 if (ctx->page.filename) 449 if (ctx->page.filename)
445 htmlf("Content-Disposition: inline; filename=\"%s\"\n", 450 htmlf("Content-Disposition: inline; filename=\"%s\"\n",
446 ctx->page.filename); 451 ctx->page.filename);
447 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); 452 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
448 htmlf("Expires: %s\n", http_date(ctx->page.expires)); 453 htmlf("Expires: %s\n", http_date(ctx->page.expires));
449 html("\n"); 454 html("\n");
450} 455}
451 456
452void cgit_print_docstart(struct cgit_context *ctx) 457void cgit_print_docstart(struct cgit_context *ctx)
453{ 458{
454 char *host = cgit_hosturl(); 459 char *host = cgit_hosturl();
455 html(cgit_doctype); 460 html(cgit_doctype);
456 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); 461 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
457 html("<head>\n"); 462 html("<head>\n");
458 html("<title>"); 463 html("<title>");
459 html_txt(ctx->page.title); 464 html_txt(ctx->page.title);
460 html("</title>\n"); 465 html("</title>\n");
461 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 466 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
462 if (ctx->cfg.robots && *ctx->cfg.robots) 467 if (ctx->cfg.robots && *ctx->cfg.robots)
463 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); 468 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
464 html("<link rel='stylesheet' type='text/css' href='"); 469 html("<link rel='stylesheet' type='text/css' href='");
465 html_attr(ctx->cfg.css); 470 html_attr(ctx->cfg.css);
466 html("'/>\n"); 471 html("'/>\n");
467 if (ctx->cfg.favicon) { 472 if (ctx->cfg.favicon) {
468 html("<link rel='shortcut icon' href='"); 473 html("<link rel='shortcut icon' href='");
469 html_attr(ctx->cfg.favicon); 474 html_attr(ctx->cfg.favicon);
470 html("'/>\n"); 475 html("'/>\n");
471 } 476 }
472 if (host && ctx->repo) { 477 if (host && ctx->repo) {
473 html("<link rel='alternate' title='Atom feed' href='http://"); 478 html("<link rel='alternate' title='Atom feed' href='http://");
474 html_attr(cgit_hosturl()); 479 html_attr(cgit_hosturl());
475 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path, 480 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path,
476 fmt("h=%s", ctx->qry.head))); 481 fmt("h=%s", ctx->qry.head)));
477 html("' type='application/atom+xml'/>"); 482 html("' type='application/atom+xml'/>");
478 } 483 }
479 html("</head>\n"); 484 html("</head>\n");
480 html("<body>\n"); 485 html("<body>\n");
481} 486}
482 487
483void cgit_print_docend() 488void cgit_print_docend()
484{ 489{
485 html("</div>"); 490 html("</div>");
486 if (ctx.cfg.footer) 491 if (ctx.cfg.footer)
487 html_include(ctx.cfg.footer); 492 html_include(ctx.cfg.footer);
488 else { 493 else {
489 html("<div class='footer'>generated "); 494 html("<div class='footer'>generated ");
490 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time); 495 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time);
491 htmlf(" by cgit %s", cgit_version); 496 htmlf(" by cgit %s", cgit_version);
492 html("</div>\n"); 497 html("</div>\n");
493 } 498 }
494 html("</body>\n</html>\n"); 499 html("</body>\n</html>\n");
495} 500}
496 501
497int print_branch_option(const char *refname, const unsigned char *sha1, 502int print_branch_option(const char *refname, const unsigned char *sha1,
498 int flags, void *cb_data) 503 int flags, void *cb_data)
499{ 504{
500 char *name = (char *)refname; 505 char *name = (char *)refname;
501 html_option(name, name, ctx.qry.head); 506 html_option(name, name, ctx.qry.head);
502 return 0; 507 return 0;
503} 508}
504 509
505int print_archive_ref(const char *refname, const unsigned char *sha1, 510int print_archive_ref(const char *refname, const unsigned char *sha1,
506 int flags, void *cb_data) 511 int flags, void *cb_data)
507{ 512{
508 struct tag *tag; 513 struct tag *tag;
509 struct taginfo *info; 514 struct taginfo *info;
510 struct object *obj; 515 struct object *obj;
511 char buf[256], *url; 516 char buf[256], *url;
512 unsigned char fileid[20]; 517 unsigned char fileid[20];
513 int *header = (int *)cb_data; 518 int *header = (int *)cb_data;
514 519
515 if (prefixcmp(refname, "refs/archives")) 520 if (prefixcmp(refname, "refs/archives"))
516 return 0; 521 return 0;
517 strncpy(buf, refname+14, sizeof(buf)); 522 strncpy(buf, refname+14, sizeof(buf));
518 obj = parse_object(sha1); 523 obj = parse_object(sha1);
519 if (!obj) 524 if (!obj)
520 return 1; 525 return 1;
521 if (obj->type == OBJ_TAG) { 526 if (obj->type == OBJ_TAG) {
522 tag = lookup_tag(sha1); 527 tag = lookup_tag(sha1);
523 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 528 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
524 return 0; 529 return 0;
525 hashcpy(fileid, tag->tagged->sha1); 530 hashcpy(fileid, tag->tagged->sha1);
526 } else if (obj->type != OBJ_BLOB) { 531 } else if (obj->type != OBJ_BLOB) {
527 return 0; 532 return 0;
528 } else { 533 } else {
529 hashcpy(fileid, sha1); 534 hashcpy(fileid, sha1);
530 } 535 }
531 if (!*header) { 536 if (!*header) {
532 html("<h1>download</h1>\n"); 537 html("<h1>download</h1>\n");
533 *header = 1; 538 *header = 1;
534 } 539 }
535 url = cgit_pageurl(ctx.qry.repo, "blob", 540 url = cgit_pageurl(ctx.qry.repo, "blob",
536 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 541 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
537 buf)); 542 buf));
538 html_link_open(url, NULL, "menu"); 543 html_link_open(url, NULL, "menu");
539 html_txt(strlpart(buf, 20)); 544 html_txt(strlpart(buf, 20));
540 html_link_close(); 545 html_link_close();
541 return 0; 546 return 0;
542} 547}
543 548
544void add_hidden_formfields(int incl_head, int incl_search, char *page) 549void add_hidden_formfields(int incl_head, int incl_search, char *page)
545{ 550{
546 char *url; 551 char *url;
547 552
548 if (!ctx.cfg.virtual_root) { 553 if (!ctx.cfg.virtual_root) {
549 url = fmt("%s/%s", ctx.qry.repo, page); 554 url = fmt("%s/%s", ctx.qry.repo, page);
550 if (ctx.qry.path) 555 if (ctx.qry.path)
551 url = fmt("%s/%s", url, ctx.qry.path); 556 url = fmt("%s/%s", url, ctx.qry.path);
552 html_hidden("url", url); 557 html_hidden("url", url);
553 } 558 }
554 559
555 if (incl_head && ctx.qry.head && ctx.repo->defbranch && 560 if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
556 strcmp(ctx.qry.head, ctx.repo->defbranch)) 561 strcmp(ctx.qry.head, ctx.repo->defbranch))
557 html_hidden("h", ctx.qry.head); 562 html_hidden("h", ctx.qry.head);
558 563
559 if (ctx.qry.sha1) 564 if (ctx.qry.sha1)
560 html_hidden("id", ctx.qry.sha1); 565 html_hidden("id", ctx.qry.sha1);