summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2009-01-11 20:23:04 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2009-01-11 20:23:04 (UTC)
commiteb14609dc46461728a065c0a243b338fc32fd762 (patch) (unidiff)
treef00563342db8859f46ac8141fdaa5d4e17eb940e
parent720b6ece90900df9f836a45d8e7f1cd56f62400a (diff)
downloadcgit-eb14609dc46461728a065c0a243b338fc32fd762.zip
cgit-eb14609dc46461728a065c0a243b338fc32fd762.tar.gz
cgit-eb14609dc46461728a065c0a243b338fc32fd762.tar.bz2
Avoid SEGFAULT on invalid requests
When an unknown page is requested, either on the querystring or via PATH_INFO, we end up with a null-referencing cgit_cmd. This null- pointer is then used as argument to the hc() function (which decides what tab to render as 'active'), but this function failed to check if a valid cmd was specified and a SEGFAULT would occur. This patch fixes the issue by introducing a 'fallback-cmd' which specifies what tab to render as 'active' when no valid cmd is requested. While at it, we now also keep track of the active repository even if an invalid cmd was requested since we want to show the error message about the invalid request in the correct context. Noticed-by: Robin Redeker <elmex@ta-sa.org> Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c1
-rw-r--r--ui-shared.c7
2 files changed, 6 insertions, 2 deletions
diff --git a/cgit.c b/cgit.c
index c82587b..6e5215e 100644
--- a/cgit.c
+++ b/cgit.c
@@ -164,257 +164,256 @@ static void prepare_context(struct cgit_context *ctx)
164 ctx->cfg.nocache = 0; 164 ctx->cfg.nocache = 0;
165 ctx->cfg.cache_size = 0; 165 ctx->cfg.cache_size = 0;
166 ctx->cfg.cache_dynamic_ttl = 5; 166 ctx->cfg.cache_dynamic_ttl = 5;
167 ctx->cfg.cache_max_create_time = 5; 167 ctx->cfg.cache_max_create_time = 5;
168 ctx->cfg.cache_repo_ttl = 5; 168 ctx->cfg.cache_repo_ttl = 5;
169 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 169 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
170 ctx->cfg.cache_root_ttl = 5; 170 ctx->cfg.cache_root_ttl = 5;
171 ctx->cfg.cache_static_ttl = -1; 171 ctx->cfg.cache_static_ttl = -1;
172 ctx->cfg.css = "/cgit.css"; 172 ctx->cfg.css = "/cgit.css";
173 ctx->cfg.logo = "/git-logo.png"; 173 ctx->cfg.logo = "/git-logo.png";
174 ctx->cfg.local_time = 0; 174 ctx->cfg.local_time = 0;
175 ctx->cfg.max_repo_count = 50; 175 ctx->cfg.max_repo_count = 50;
176 ctx->cfg.max_commit_count = 50; 176 ctx->cfg.max_commit_count = 50;
177 ctx->cfg.max_lock_attempts = 5; 177 ctx->cfg.max_lock_attempts = 5;
178 ctx->cfg.max_msg_len = 80; 178 ctx->cfg.max_msg_len = 80;
179 ctx->cfg.max_repodesc_len = 80; 179 ctx->cfg.max_repodesc_len = 80;
180 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 180 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
181 ctx->cfg.renamelimit = -1; 181 ctx->cfg.renamelimit = -1;
182 ctx->cfg.robots = "index, nofollow"; 182 ctx->cfg.robots = "index, nofollow";
183 ctx->cfg.root_title = "Git repository browser"; 183 ctx->cfg.root_title = "Git repository browser";
184 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 184 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
185 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 185 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
186 ctx->cfg.summary_branches = 10; 186 ctx->cfg.summary_branches = 10;
187 ctx->cfg.summary_log = 10; 187 ctx->cfg.summary_log = 10;
188 ctx->cfg.summary_tags = 10; 188 ctx->cfg.summary_tags = 10;
189 ctx->page.mimetype = "text/html"; 189 ctx->page.mimetype = "text/html";
190 ctx->page.charset = PAGE_ENCODING; 190 ctx->page.charset = PAGE_ENCODING;
191 ctx->page.filename = NULL; 191 ctx->page.filename = NULL;
192 ctx->page.size = 0; 192 ctx->page.size = 0;
193 ctx->page.modified = time(NULL); 193 ctx->page.modified = time(NULL);
194 ctx->page.expires = ctx->page.modified; 194 ctx->page.expires = ctx->page.modified;
195} 195}
196 196
197struct refmatch { 197struct refmatch {
198 char *req_ref; 198 char *req_ref;
199 char *first_ref; 199 char *first_ref;
200 int match; 200 int match;
201}; 201};
202 202
203int find_current_ref(const char *refname, const unsigned char *sha1, 203int find_current_ref(const char *refname, const unsigned char *sha1,
204 int flags, void *cb_data) 204 int flags, void *cb_data)
205{ 205{
206 struct refmatch *info; 206 struct refmatch *info;
207 207
208 info = (struct refmatch *)cb_data; 208 info = (struct refmatch *)cb_data;
209 if (!strcmp(refname, info->req_ref)) 209 if (!strcmp(refname, info->req_ref))
210 info->match = 1; 210 info->match = 1;
211 if (!info->first_ref) 211 if (!info->first_ref)
212 info->first_ref = xstrdup(refname); 212 info->first_ref = xstrdup(refname);
213 return info->match; 213 return info->match;
214} 214}
215 215
216char *find_default_branch(struct cgit_repo *repo) 216char *find_default_branch(struct cgit_repo *repo)
217{ 217{
218 struct refmatch info; 218 struct refmatch info;
219 char *ref; 219 char *ref;
220 220
221 info.req_ref = repo->defbranch; 221 info.req_ref = repo->defbranch;
222 info.first_ref = NULL; 222 info.first_ref = NULL;
223 info.match = 0; 223 info.match = 0;
224 for_each_branch_ref(find_current_ref, &info); 224 for_each_branch_ref(find_current_ref, &info);
225 if (info.match) 225 if (info.match)
226 ref = info.req_ref; 226 ref = info.req_ref;
227 else 227 else
228 ref = info.first_ref; 228 ref = info.first_ref;
229 if (ref) 229 if (ref)
230 ref = xstrdup(ref); 230 ref = xstrdup(ref);
231 return ref; 231 return ref;
232} 232}
233 233
234static int prepare_repo_cmd(struct cgit_context *ctx) 234static int prepare_repo_cmd(struct cgit_context *ctx)
235{ 235{
236 char *tmp; 236 char *tmp;
237 unsigned char sha1[20]; 237 unsigned char sha1[20];
238 int nongit = 0; 238 int nongit = 0;
239 239
240 setenv("GIT_DIR", ctx->repo->path, 1); 240 setenv("GIT_DIR", ctx->repo->path, 1);
241 setup_git_directory_gently(&nongit); 241 setup_git_directory_gently(&nongit);
242 if (nongit) { 242 if (nongit) {
243 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 243 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
244 "config error"); 244 "config error");
245 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 245 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
246 ctx->repo = NULL; 246 ctx->repo = NULL;
247 cgit_print_http_headers(ctx); 247 cgit_print_http_headers(ctx);
248 cgit_print_docstart(ctx); 248 cgit_print_docstart(ctx);
249 cgit_print_pageheader(ctx); 249 cgit_print_pageheader(ctx);
250 cgit_print_error(tmp); 250 cgit_print_error(tmp);
251 cgit_print_docend(); 251 cgit_print_docend();
252 return 1; 252 return 1;
253 } 253 }
254 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 254 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
255 255
256 if (!ctx->qry.head) { 256 if (!ctx->qry.head) {
257 ctx->qry.nohead = 1; 257 ctx->qry.nohead = 1;
258 ctx->qry.head = find_default_branch(ctx->repo); 258 ctx->qry.head = find_default_branch(ctx->repo);
259 ctx->repo->defbranch = ctx->qry.head; 259 ctx->repo->defbranch = ctx->qry.head;
260 } 260 }
261 261
262 if (!ctx->qry.head) { 262 if (!ctx->qry.head) {
263 cgit_print_http_headers(ctx); 263 cgit_print_http_headers(ctx);
264 cgit_print_docstart(ctx); 264 cgit_print_docstart(ctx);
265 cgit_print_pageheader(ctx); 265 cgit_print_pageheader(ctx);
266 cgit_print_error("Repository seems to be empty"); 266 cgit_print_error("Repository seems to be empty");
267 cgit_print_docend(); 267 cgit_print_docend();
268 return 1; 268 return 1;
269 } 269 }
270 270
271 if (get_sha1(ctx->qry.head, sha1)) { 271 if (get_sha1(ctx->qry.head, sha1)) {
272 tmp = xstrdup(ctx->qry.head); 272 tmp = xstrdup(ctx->qry.head);
273 ctx->qry.head = ctx->repo->defbranch; 273 ctx->qry.head = ctx->repo->defbranch;
274 cgit_print_http_headers(ctx); 274 cgit_print_http_headers(ctx);
275 cgit_print_docstart(ctx); 275 cgit_print_docstart(ctx);
276 cgit_print_pageheader(ctx); 276 cgit_print_pageheader(ctx);
277 cgit_print_error(fmt("Invalid branch: %s", tmp)); 277 cgit_print_error(fmt("Invalid branch: %s", tmp));
278 cgit_print_docend(); 278 cgit_print_docend();
279 return 1; 279 return 1;
280 } 280 }
281 return 0; 281 return 0;
282} 282}
283 283
284static void process_request(void *cbdata) 284static void process_request(void *cbdata)
285{ 285{
286 struct cgit_context *ctx = cbdata; 286 struct cgit_context *ctx = cbdata;
287 struct cgit_cmd *cmd; 287 struct cgit_cmd *cmd;
288 288
289 cmd = cgit_get_cmd(ctx); 289 cmd = cgit_get_cmd(ctx);
290 if (!cmd) { 290 if (!cmd) {
291 ctx->page.title = "cgit error"; 291 ctx->page.title = "cgit error";
292 ctx->repo = NULL;
293 cgit_print_http_headers(ctx); 292 cgit_print_http_headers(ctx);
294 cgit_print_docstart(ctx); 293 cgit_print_docstart(ctx);
295 cgit_print_pageheader(ctx); 294 cgit_print_pageheader(ctx);
296 cgit_print_error("Invalid request"); 295 cgit_print_error("Invalid request");
297 cgit_print_docend(); 296 cgit_print_docend();
298 return; 297 return;
299 } 298 }
300 299
301 if (cmd->want_repo && !ctx->repo) { 300 if (cmd->want_repo && !ctx->repo) {
302 cgit_print_http_headers(ctx); 301 cgit_print_http_headers(ctx);
303 cgit_print_docstart(ctx); 302 cgit_print_docstart(ctx);
304 cgit_print_pageheader(ctx); 303 cgit_print_pageheader(ctx);
305 cgit_print_error(fmt("No repository selected")); 304 cgit_print_error(fmt("No repository selected"));
306 cgit_print_docend(); 305 cgit_print_docend();
307 return; 306 return;
308 } 307 }
309 308
310 if (ctx->repo && prepare_repo_cmd(ctx)) 309 if (ctx->repo && prepare_repo_cmd(ctx))
311 return; 310 return;
312 311
313 if (cmd->want_layout) { 312 if (cmd->want_layout) {
314 cgit_print_http_headers(ctx); 313 cgit_print_http_headers(ctx);
315 cgit_print_docstart(ctx); 314 cgit_print_docstart(ctx);
316 cgit_print_pageheader(ctx); 315 cgit_print_pageheader(ctx);
317 } 316 }
318 317
319 cmd->fn(ctx); 318 cmd->fn(ctx);
320 319
321 if (cmd->want_layout) 320 if (cmd->want_layout)
322 cgit_print_docend(); 321 cgit_print_docend();
323} 322}
324 323
325int cmp_repos(const void *a, const void *b) 324int cmp_repos(const void *a, const void *b)
326{ 325{
327 const struct cgit_repo *ra = a, *rb = b; 326 const struct cgit_repo *ra = a, *rb = b;
328 return strcmp(ra->url, rb->url); 327 return strcmp(ra->url, rb->url);
329} 328}
330 329
331void print_repo(struct cgit_repo *repo) 330void print_repo(struct cgit_repo *repo)
332{ 331{
333 printf("repo.url=%s\n", repo->url); 332 printf("repo.url=%s\n", repo->url);
334 printf("repo.name=%s\n", repo->name); 333 printf("repo.name=%s\n", repo->name);
335 printf("repo.path=%s\n", repo->path); 334 printf("repo.path=%s\n", repo->path);
336 if (repo->owner) 335 if (repo->owner)
337 printf("repo.owner=%s\n", repo->owner); 336 printf("repo.owner=%s\n", repo->owner);
338 if (repo->desc) 337 if (repo->desc)
339 printf("repo.desc=%s\n", repo->desc); 338 printf("repo.desc=%s\n", repo->desc);
340 if (repo->readme) 339 if (repo->readme)
341 printf("repo.readme=%s\n", repo->readme); 340 printf("repo.readme=%s\n", repo->readme);
342 printf("\n"); 341 printf("\n");
343} 342}
344 343
345void print_repolist(struct cgit_repolist *list) 344void print_repolist(struct cgit_repolist *list)
346{ 345{
347 int i; 346 int i;
348 347
349 for(i = 0; i < list->count; i++) 348 for(i = 0; i < list->count; i++)
350 print_repo(&list->repos[i]); 349 print_repo(&list->repos[i]);
351} 350}
352 351
353 352
354static void cgit_parse_args(int argc, const char **argv) 353static void cgit_parse_args(int argc, const char **argv)
355{ 354{
356 int i; 355 int i;
357 int scan = 0; 356 int scan = 0;
358 357
359 for (i = 1; i < argc; i++) { 358 for (i = 1; i < argc; i++) {
360 if (!strncmp(argv[i], "--cache=", 8)) { 359 if (!strncmp(argv[i], "--cache=", 8)) {
361 ctx.cfg.cache_root = xstrdup(argv[i]+8); 360 ctx.cfg.cache_root = xstrdup(argv[i]+8);
362 } 361 }
363 if (!strcmp(argv[i], "--nocache")) { 362 if (!strcmp(argv[i], "--nocache")) {
364 ctx.cfg.nocache = 1; 363 ctx.cfg.nocache = 1;
365 } 364 }
366 if (!strncmp(argv[i], "--query=", 8)) { 365 if (!strncmp(argv[i], "--query=", 8)) {
367 ctx.qry.raw = xstrdup(argv[i]+8); 366 ctx.qry.raw = xstrdup(argv[i]+8);
368 } 367 }
369 if (!strncmp(argv[i], "--repo=", 7)) { 368 if (!strncmp(argv[i], "--repo=", 7)) {
370 ctx.qry.repo = xstrdup(argv[i]+7); 369 ctx.qry.repo = xstrdup(argv[i]+7);
371 } 370 }
372 if (!strncmp(argv[i], "--page=", 7)) { 371 if (!strncmp(argv[i], "--page=", 7)) {
373 ctx.qry.page = xstrdup(argv[i]+7); 372 ctx.qry.page = xstrdup(argv[i]+7);
374 } 373 }
375 if (!strncmp(argv[i], "--head=", 7)) { 374 if (!strncmp(argv[i], "--head=", 7)) {
376 ctx.qry.head = xstrdup(argv[i]+7); 375 ctx.qry.head = xstrdup(argv[i]+7);
377 ctx.qry.has_symref = 1; 376 ctx.qry.has_symref = 1;
378 } 377 }
379 if (!strncmp(argv[i], "--sha1=", 7)) { 378 if (!strncmp(argv[i], "--sha1=", 7)) {
380 ctx.qry.sha1 = xstrdup(argv[i]+7); 379 ctx.qry.sha1 = xstrdup(argv[i]+7);
381 ctx.qry.has_sha1 = 1; 380 ctx.qry.has_sha1 = 1;
382 } 381 }
383 if (!strncmp(argv[i], "--ofs=", 6)) { 382 if (!strncmp(argv[i], "--ofs=", 6)) {
384 ctx.qry.ofs = atoi(argv[i]+6); 383 ctx.qry.ofs = atoi(argv[i]+6);
385 } 384 }
386 if (!strncmp(argv[i], "--scan-tree=", 12)) { 385 if (!strncmp(argv[i], "--scan-tree=", 12)) {
387 scan++; 386 scan++;
388 scan_tree(argv[i] + 12); 387 scan_tree(argv[i] + 12);
389 } 388 }
390 } 389 }
391 if (scan) { 390 if (scan) {
392 qsort(cgit_repolist.repos, cgit_repolist.count, 391 qsort(cgit_repolist.repos, cgit_repolist.count,
393 sizeof(struct cgit_repo), cmp_repos); 392 sizeof(struct cgit_repo), cmp_repos);
394 print_repolist(&cgit_repolist); 393 print_repolist(&cgit_repolist);
395 exit(0); 394 exit(0);
396 } 395 }
397} 396}
398 397
399static int calc_ttl() 398static int calc_ttl()
400{ 399{
401 if (!ctx.repo) 400 if (!ctx.repo)
402 return ctx.cfg.cache_root_ttl; 401 return ctx.cfg.cache_root_ttl;
403 402
404 if (!ctx.qry.page) 403 if (!ctx.qry.page)
405 return ctx.cfg.cache_repo_ttl; 404 return ctx.cfg.cache_repo_ttl;
406 405
407 if (ctx.qry.has_symref) 406 if (ctx.qry.has_symref)
408 return ctx.cfg.cache_dynamic_ttl; 407 return ctx.cfg.cache_dynamic_ttl;
409 408
410 if (ctx.qry.has_sha1) 409 if (ctx.qry.has_sha1)
411 return ctx.cfg.cache_static_ttl; 410 return ctx.cfg.cache_static_ttl;
412 411
413 return ctx.cfg.cache_repo_ttl; 412 return ctx.cfg.cache_repo_ttl;
414} 413}
415 414
416int main(int argc, const char **argv) 415int main(int argc, const char **argv)
417{ 416{
418 const char *cgit_config_env = getenv("CGIT_CONFIG"); 417 const char *cgit_config_env = getenv("CGIT_CONFIG");
419 const char *path; 418 const char *path;
420 char *qry; 419 char *qry;
diff --git a/ui-shared.c b/ui-shared.c
index 224e5f3..76cd00d 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -452,263 +452,268 @@ void cgit_print_http_headers(struct cgit_context *ctx)
452 if (ctx->page.filename) 452 if (ctx->page.filename)
453 htmlf("Content-Disposition: inline; filename=\"%s\"\n", 453 htmlf("Content-Disposition: inline; filename=\"%s\"\n",
454 ctx->page.filename); 454 ctx->page.filename);
455 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); 455 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
456 htmlf("Expires: %s\n", http_date(ctx->page.expires)); 456 htmlf("Expires: %s\n", http_date(ctx->page.expires));
457 html("\n"); 457 html("\n");
458} 458}
459 459
460void cgit_print_docstart(struct cgit_context *ctx) 460void cgit_print_docstart(struct cgit_context *ctx)
461{ 461{
462 char *host = cgit_hosturl(); 462 char *host = cgit_hosturl();
463 html(cgit_doctype); 463 html(cgit_doctype);
464 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); 464 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
465 html("<head>\n"); 465 html("<head>\n");
466 html("<title>"); 466 html("<title>");
467 html_txt(ctx->page.title); 467 html_txt(ctx->page.title);
468 html("</title>\n"); 468 html("</title>\n");
469 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 469 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
470 if (ctx->cfg.robots && *ctx->cfg.robots) 470 if (ctx->cfg.robots && *ctx->cfg.robots)
471 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); 471 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
472 html("<link rel='stylesheet' type='text/css' href='"); 472 html("<link rel='stylesheet' type='text/css' href='");
473 html_attr(ctx->cfg.css); 473 html_attr(ctx->cfg.css);
474 html("'/>\n"); 474 html("'/>\n");
475 if (ctx->cfg.favicon) { 475 if (ctx->cfg.favicon) {
476 html("<link rel='shortcut icon' href='"); 476 html("<link rel='shortcut icon' href='");
477 html_attr(ctx->cfg.favicon); 477 html_attr(ctx->cfg.favicon);
478 html("'/>\n"); 478 html("'/>\n");
479 } 479 }
480 if (host && ctx->repo) { 480 if (host && ctx->repo) {
481 html("<link rel='alternate' title='Atom feed' href='http://"); 481 html("<link rel='alternate' title='Atom feed' href='http://");
482 html_attr(cgit_hosturl()); 482 html_attr(cgit_hosturl());
483 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path, 483 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path,
484 fmt("h=%s", ctx->qry.head))); 484 fmt("h=%s", ctx->qry.head)));
485 html("' type='application/atom+xml'/>"); 485 html("' type='application/atom+xml'/>");
486 } 486 }
487 html("</head>\n"); 487 html("</head>\n");
488 html("<body>\n"); 488 html("<body>\n");
489} 489}
490 490
491void cgit_print_docend() 491void cgit_print_docend()
492{ 492{
493 html("</div>"); 493 html("</div>");
494 if (ctx.cfg.footer) 494 if (ctx.cfg.footer)
495 html_include(ctx.cfg.footer); 495 html_include(ctx.cfg.footer);
496 else { 496 else {
497 htmlf("<div class='footer'>generated by cgit %s at ", 497 htmlf("<div class='footer'>generated by cgit %s at ",
498 cgit_version); 498 cgit_version);
499 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time); 499 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time);
500 html("</div>\n"); 500 html("</div>\n");
501 } 501 }
502 html("</body>\n</html>\n"); 502 html("</body>\n</html>\n");
503} 503}
504 504
505int print_branch_option(const char *refname, const unsigned char *sha1, 505int print_branch_option(const char *refname, const unsigned char *sha1,
506 int flags, void *cb_data) 506 int flags, void *cb_data)
507{ 507{
508 char *name = (char *)refname; 508 char *name = (char *)refname;
509 html_option(name, name, ctx.qry.head); 509 html_option(name, name, ctx.qry.head);
510 return 0; 510 return 0;
511} 511}
512 512
513int print_archive_ref(const char *refname, const unsigned char *sha1, 513int print_archive_ref(const char *refname, const unsigned char *sha1,
514 int flags, void *cb_data) 514 int flags, void *cb_data)
515{ 515{
516 struct tag *tag; 516 struct tag *tag;
517 struct taginfo *info; 517 struct taginfo *info;
518 struct object *obj; 518 struct object *obj;
519 char buf[256], *url; 519 char buf[256], *url;
520 unsigned char fileid[20]; 520 unsigned char fileid[20];
521 int *header = (int *)cb_data; 521 int *header = (int *)cb_data;
522 522
523 if (prefixcmp(refname, "refs/archives")) 523 if (prefixcmp(refname, "refs/archives"))
524 return 0; 524 return 0;
525 strncpy(buf, refname+14, sizeof(buf)); 525 strncpy(buf, refname+14, sizeof(buf));
526 obj = parse_object(sha1); 526 obj = parse_object(sha1);
527 if (!obj) 527 if (!obj)
528 return 1; 528 return 1;
529 if (obj->type == OBJ_TAG) { 529 if (obj->type == OBJ_TAG) {
530 tag = lookup_tag(sha1); 530 tag = lookup_tag(sha1);
531 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 531 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
532 return 0; 532 return 0;
533 hashcpy(fileid, tag->tagged->sha1); 533 hashcpy(fileid, tag->tagged->sha1);
534 } else if (obj->type != OBJ_BLOB) { 534 } else if (obj->type != OBJ_BLOB) {
535 return 0; 535 return 0;
536 } else { 536 } else {
537 hashcpy(fileid, sha1); 537 hashcpy(fileid, sha1);
538 } 538 }
539 if (!*header) { 539 if (!*header) {
540 html("<h1>download</h1>\n"); 540 html("<h1>download</h1>\n");
541 *header = 1; 541 *header = 1;
542 } 542 }
543 url = cgit_pageurl(ctx.qry.repo, "blob", 543 url = cgit_pageurl(ctx.qry.repo, "blob",
544 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 544 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
545 buf)); 545 buf));
546 html_link_open(url, NULL, "menu"); 546 html_link_open(url, NULL, "menu");
547 html_txt(strlpart(buf, 20)); 547 html_txt(strlpart(buf, 20));
548 html_link_close(); 548 html_link_close();
549 return 0; 549 return 0;
550} 550}
551 551
552void add_hidden_formfields(int incl_head, int incl_search, char *page) 552void add_hidden_formfields(int incl_head, int incl_search, char *page)
553{ 553{
554 char *url; 554 char *url;
555 555
556 if (!ctx.cfg.virtual_root) { 556 if (!ctx.cfg.virtual_root) {
557 url = fmt("%s/%s", ctx.qry.repo, page); 557 url = fmt("%s/%s", ctx.qry.repo, page);
558 if (ctx.qry.path) 558 if (ctx.qry.path)
559 url = fmt("%s/%s", url, ctx.qry.path); 559 url = fmt("%s/%s", url, ctx.qry.path);
560 html_hidden("url", url); 560 html_hidden("url", url);
561 } 561 }
562 562
563 if (incl_head && ctx.qry.head && ctx.repo->defbranch && 563 if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
564 strcmp(ctx.qry.head, ctx.repo->defbranch)) 564 strcmp(ctx.qry.head, ctx.repo->defbranch))
565 html_hidden("h", ctx.qry.head); 565 html_hidden("h", ctx.qry.head);
566 566
567 if (ctx.qry.sha1) 567 if (ctx.qry.sha1)
568 html_hidden("id", ctx.qry.sha1); 568 html_hidden("id", ctx.qry.sha1);
569 if (ctx.qry.sha2) 569 if (ctx.qry.sha2)
570 html_hidden("id2", ctx.qry.sha2); 570 html_hidden("id2", ctx.qry.sha2);
571 571
572 if (incl_search) { 572 if (incl_search) {
573 if (ctx.qry.grep) 573 if (ctx.qry.grep)
574 html_hidden("qt", ctx.qry.grep); 574 html_hidden("qt", ctx.qry.grep);
575 if (ctx.qry.search) 575 if (ctx.qry.search)
576 html_hidden("q", ctx.qry.search); 576 html_hidden("q", ctx.qry.search);
577 } 577 }
578} 578}
579 579
580const char *fallback_cmd = "repolist";
581
580char *hc(struct cgit_cmd *cmd, const char *page) 582char *hc(struct cgit_cmd *cmd, const char *page)
581{ 583{
582 return (strcmp(cmd->name, page) ? NULL : "active"); 584 return (strcmp(cmd ? cmd->name : fallback_cmd, page) ? NULL : "active");
583} 585}
584 586
585void cgit_print_pageheader(struct cgit_context *ctx) 587void cgit_print_pageheader(struct cgit_context *ctx)
586{ 588{
587 struct cgit_cmd *cmd = cgit_get_cmd(ctx); 589 struct cgit_cmd *cmd = cgit_get_cmd(ctx);
588 590
591 if (!cmd && ctx->repo)
592 fallback_cmd = "summary";
593
589 html("<table id='header'>\n"); 594 html("<table id='header'>\n");
590 html("<tr>\n"); 595 html("<tr>\n");
591 html("<td class='logo' rowspan='2'><a href='"); 596 html("<td class='logo' rowspan='2'><a href='");
592 if (ctx->cfg.logo_link) 597 if (ctx->cfg.logo_link)
593 html_attr(ctx->cfg.logo_link); 598 html_attr(ctx->cfg.logo_link);
594 else 599 else
595 html_attr(cgit_rooturl()); 600 html_attr(cgit_rooturl());
596 html("'><img src='"); 601 html("'><img src='");
597 html_attr(ctx->cfg.logo); 602 html_attr(ctx->cfg.logo);
598 html("' alt='cgit logo'/></a></td>\n"); 603 html("' alt='cgit logo'/></a></td>\n");
599 604
600 html("<td class='main'>"); 605 html("<td class='main'>");
601 if (ctx->repo) { 606 if (ctx->repo) {
602 cgit_index_link("index", NULL, NULL, NULL, 0); 607 cgit_index_link("index", NULL, NULL, NULL, 0);
603 html(" : "); 608 html(" : ");
604 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL); 609 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL);
605 html("</td><td class='form'>"); 610 html("</td><td class='form'>");
606 html("<form method='get' action=''>\n"); 611 html("<form method='get' action=''>\n");
607 add_hidden_formfields(0, 1, ctx->qry.page); 612 add_hidden_formfields(0, 1, ctx->qry.page);
608 html("<select name='h' onchange='this.form.submit();'>\n"); 613 html("<select name='h' onchange='this.form.submit();'>\n");
609 for_each_branch_ref(print_branch_option, ctx->qry.head); 614 for_each_branch_ref(print_branch_option, ctx->qry.head);
610 html("</select> "); 615 html("</select> ");
611 html("<input type='submit' name='' value='switch'/>"); 616 html("<input type='submit' name='' value='switch'/>");
612 html("</form>"); 617 html("</form>");
613 } else 618 } else
614 html_txt(ctx->cfg.root_title); 619 html_txt(ctx->cfg.root_title);
615 html("</td></tr>\n"); 620 html("</td></tr>\n");
616 621
617 html("<tr><td class='sub'>"); 622 html("<tr><td class='sub'>");
618 if (ctx->repo) { 623 if (ctx->repo) {
619 html_txt(ctx->repo->desc); 624 html_txt(ctx->repo->desc);
620 html("</td><td class='sub right'>"); 625 html("</td><td class='sub right'>");
621 html_txt(ctx->repo->owner); 626 html_txt(ctx->repo->owner);
622 } else { 627 } else {
623 if (ctx->cfg.root_desc) 628 if (ctx->cfg.root_desc)
624 html_txt(ctx->cfg.root_desc); 629 html_txt(ctx->cfg.root_desc);
625 else if (ctx->cfg.index_info) 630 else if (ctx->cfg.index_info)
626 html_include(ctx->cfg.index_info); 631 html_include(ctx->cfg.index_info);
627 } 632 }
628 html("</td></tr></table>\n"); 633 html("</td></tr></table>\n");
629 634
630 html("<table class='tabs'><tr><td>\n"); 635 html("<table class='tabs'><tr><td>\n");
631 if (ctx->repo) { 636 if (ctx->repo) {
632 cgit_summary_link("summary", NULL, hc(cmd, "summary"), 637 cgit_summary_link("summary", NULL, hc(cmd, "summary"),
633 ctx->qry.head); 638 ctx->qry.head);
634 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head, 639 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head,
635 ctx->qry.sha1, NULL); 640 ctx->qry.sha1, NULL);
636 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head, 641 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head,
637 NULL, NULL, 0, NULL, NULL); 642 NULL, NULL, 0, NULL, NULL);
638 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head, 643 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head,
639 ctx->qry.sha1, NULL); 644 ctx->qry.sha1, NULL);
640 cgit_commit_link("commit", NULL, hc(cmd, "commit"), 645 cgit_commit_link("commit", NULL, hc(cmd, "commit"),
641 ctx->qry.head, ctx->qry.sha1); 646 ctx->qry.head, ctx->qry.sha1);
642 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head, 647 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head,
643 ctx->qry.sha1, ctx->qry.sha2, NULL); 648 ctx->qry.sha1, ctx->qry.sha2, NULL);
644 if (ctx->repo->readme) 649 if (ctx->repo->readme)
645 reporevlink("about", "about", NULL, 650 reporevlink("about", "about", NULL,
646 hc(cmd, "about"), ctx->qry.head, NULL, 651 hc(cmd, "about"), ctx->qry.head, NULL,
647 NULL); 652 NULL);
648 html("</td><td class='form'>"); 653 html("</td><td class='form'>");
649 html("<form class='right' method='get' action='"); 654 html("<form class='right' method='get' action='");
650 if (ctx->cfg.virtual_root) 655 if (ctx->cfg.virtual_root)
651 html_url_path(cgit_fileurl(ctx->qry.repo, "log", 656 html_url_path(cgit_fileurl(ctx->qry.repo, "log",
652 ctx->qry.path, NULL)); 657 ctx->qry.path, NULL));
653 html("'>\n"); 658 html("'>\n");
654 add_hidden_formfields(1, 0, "log"); 659 add_hidden_formfields(1, 0, "log");
655 html("<select name='qt'>\n"); 660 html("<select name='qt'>\n");
656 html_option("grep", "log msg", ctx->qry.grep); 661 html_option("grep", "log msg", ctx->qry.grep);
657 html_option("author", "author", ctx->qry.grep); 662 html_option("author", "author", ctx->qry.grep);
658 html_option("committer", "committer", ctx->qry.grep); 663 html_option("committer", "committer", ctx->qry.grep);
659 html("</select>\n"); 664 html("</select>\n");
660 html("<input class='txt' type='text' size='10' name='q' value='"); 665 html("<input class='txt' type='text' size='10' name='q' value='");
661 html_attr(ctx->qry.search); 666 html_attr(ctx->qry.search);
662 html("'/>\n"); 667 html("'/>\n");
663 html("<input type='submit' value='search'/>\n"); 668 html("<input type='submit' value='search'/>\n");
664 html("</form>\n"); 669 html("</form>\n");
665 } else { 670 } else {
666 site_link(NULL, "index", NULL, hc(cmd, "repolist"), NULL, 0); 671 site_link(NULL, "index", NULL, hc(cmd, "repolist"), NULL, 0);
667 if (ctx->cfg.root_readme) 672 if (ctx->cfg.root_readme)
668 site_link("about", "about", NULL, hc(cmd, "about"), 673 site_link("about", "about", NULL, hc(cmd, "about"),
669 NULL, 0); 674 NULL, 0);
670 html("</td><td class='form'>"); 675 html("</td><td class='form'>");
671 html("<form method='get' action='"); 676 html("<form method='get' action='");
672 html_attr(cgit_rooturl()); 677 html_attr(cgit_rooturl());
673 html("'>\n"); 678 html("'>\n");
674 html("<input type='text' name='q' size='10' value='"); 679 html("<input type='text' name='q' size='10' value='");
675 html_attr(ctx->qry.search); 680 html_attr(ctx->qry.search);
676 html("'/>\n"); 681 html("'/>\n");
677 html("<input type='submit' value='search'/>\n"); 682 html("<input type='submit' value='search'/>\n");
678 html("</form>"); 683 html("</form>");
679 } 684 }
680 html("</td></tr></table>\n"); 685 html("</td></tr></table>\n");
681 html("<div class='content'>"); 686 html("<div class='content'>");
682} 687}
683 688
684void cgit_print_filemode(unsigned short mode) 689void cgit_print_filemode(unsigned short mode)
685{ 690{
686 if (S_ISDIR(mode)) 691 if (S_ISDIR(mode))
687 html("d"); 692 html("d");
688 else if (S_ISLNK(mode)) 693 else if (S_ISLNK(mode))
689 html("l"); 694 html("l");
690 else if (S_ISGITLINK(mode)) 695 else if (S_ISGITLINK(mode))
691 html("m"); 696 html("m");
692 else 697 else
693 html("-"); 698 html("-");
694 html_fileperm(mode >> 6); 699 html_fileperm(mode >> 6);
695 html_fileperm(mode >> 3); 700 html_fileperm(mode >> 3);
696 html_fileperm(mode); 701 html_fileperm(mode);
697} 702}
698 703
699void cgit_print_snapshot_links(const char *repo, const char *head, 704void cgit_print_snapshot_links(const char *repo, const char *head,
700 const char *hex, int snapshots) 705 const char *hex, int snapshots)
701{ 706{
702 const struct cgit_snapshot_format* f; 707 const struct cgit_snapshot_format* f;
703 char *filename; 708 char *filename;
704 709
705 for (f = cgit_snapshot_formats; f->suffix; f++) { 710 for (f = cgit_snapshot_formats; f->suffix; f++) {
706 if (!(snapshots & f->bit)) 711 if (!(snapshots & f->bit))
707 continue; 712 continue;
708 filename = fmt("%s-%s%s", cgit_repobasename(repo), hex, 713 filename = fmt("%s-%s%s", cgit_repobasename(repo), hex,
709 f->suffix); 714 f->suffix);
710 cgit_snapshot_link(filename, NULL, NULL, (char *)head, 715 cgit_snapshot_link(filename, NULL, NULL, (char *)head,
711 (char *)hex, filename); 716 (char *)hex, filename);
712 html("<br/>"); 717 html("<br/>");
713 } 718 }
714} 719}