2 * Copyright (c) 2019, 2020 Tracey Emery <tracey@traceyemery.net>
3 * Copyright (c) 2018, 2019 Stefan Sperling <stsp@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
33 #include <got_error.h>
34 #include <got_object.h>
35 #include <got_reference.h>
36 #include <got_repository.h>
38 #include <got_cancel.h>
39 #include <got_worktree.h>
41 #include <got_commit_graph.h>
42 #include <got_blame.h>
43 #include <got_privsep.h>
44 #include <got_opentemp.h>
52 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
56 TAILQ_HEAD(headers
, gw_header
) gw_headers
;
57 TAILQ_HEAD(dirs
, gw_dir
) gw_dirs
;
58 struct got_repository
*repo
;
59 struct gw_dir
*gw_dir
;
60 struct gotweb_config
*gw_conf
;
61 struct ktemplate
*gw_tmpl
;
62 struct khtmlreq
*gw_html_req
;
64 const struct got_error
*error
;
65 const char *repo_name
;
70 const char *repo_file
;
75 unsigned int repos_total
;
80 TAILQ_ENTRY(gw_header
) entry
;
81 struct got_reflist_head refs
;
85 char *commit_id
; /* id_str1 */
86 char *parent_id
; /* id_str2 */
91 time_t committer_time
;
95 TAILQ_ENTRY(gw_dir
) entry
;
137 static const char *const gw_templs
[TEMPL__MAX
] = {
147 static const struct kvalid gw_keys
[KEY__ZMAX
] = {
148 { kvalid_stringne
, "action" },
149 { kvalid_stringne
, "commit" },
150 { kvalid_stringne
, "file" },
151 { kvalid_stringne
, "folder" },
152 { kvalid_stringne
, "headref" },
153 { kvalid_int
, "page" },
154 { kvalid_stringne
, "path" },
155 { kvalid_stringne
, "prev" },
158 static struct gw_header
*gw_init_header(void);
160 static void gw_free_header(struct gw_header
*);
162 static int gw_template(size_t, void *);
164 static const struct got_error
*gw_error(struct gw_trans
*);
165 static const struct got_error
*gw_init_gw_dir(struct gw_dir
**, const char *);
166 static const struct got_error
*gw_get_repo_description(char **,
167 struct gw_trans
*, char *);
168 static const struct got_error
*gw_get_repo_owner(char **, struct gw_trans
*,
170 static const struct got_error
*gw_get_time_str(char **, time_t, int);
171 static const struct got_error
*gw_get_repo_age(char **, struct gw_trans
*,
172 char *, const char *, int);
173 static const struct got_error
*gw_output_file_blame(struct gw_trans
*,
175 static const struct got_error
*gw_output_blob_buf(struct gw_trans
*,
177 static const struct got_error
*gw_output_repo_tree(struct gw_trans
*,
179 static const struct got_error
*gw_output_diff(struct gw_trans
*,
181 static const struct got_error
*gw_output_repo_tags(struct gw_trans
*,
182 struct gw_header
*, int, int);
183 static const struct got_error
*gw_output_repo_heads(struct gw_trans
*);
184 static const struct got_error
*gw_output_site_link(struct gw_trans
*);
185 static const struct got_error
*gw_get_clone_url(char **, struct gw_trans
*,
187 static const struct got_error
*gw_colordiff_line(struct gw_trans
*, char *);
189 static const struct got_error
*gw_gen_commit_header(struct gw_trans
*, char *,
191 static const struct got_error
*gw_gen_diff_header(struct gw_trans
*, char *,
193 static const struct got_error
*gw_gen_author_header(struct gw_trans
*,
195 static const struct got_error
*gw_gen_age_header(struct gw_trans
*,
197 static const struct got_error
*gw_gen_committer_header(struct gw_trans
*,
199 static const struct got_error
*gw_gen_commit_msg_header(struct gw_trans
*,
201 static const struct got_error
*gw_gen_tree_header(struct gw_trans
*, char *);
202 static const struct got_error
*gw_display_open(struct gw_trans
*, enum khttp
,
204 static const struct got_error
*gw_display_index(struct gw_trans
*);
205 static const struct got_error
*gw_get_header(struct gw_trans
*,
206 struct gw_header
*, int);
207 static const struct got_error
*gw_get_commits(struct gw_trans
*,
208 struct gw_header
*, int,
209 struct got_object_id
*);
210 static const struct got_error
*gw_get_commit(struct gw_trans
*,
212 struct got_commit_object
*,
213 struct got_object_id
*);
214 static const struct got_error
*gw_apply_unveil(const char *);
215 static const struct got_error
*gw_blame_cb(void *, int, int,
216 struct got_object_id
*);
217 static const struct got_error
*gw_load_got_paths(struct gw_trans
*);
218 static const struct got_error
*gw_load_got_path(struct gw_trans
*,
220 static const struct got_error
*gw_parse_querystring(struct gw_trans
*);
221 static const struct got_error
*gw_blame(struct gw_trans
*);
222 static const struct got_error
*gw_blob(struct gw_trans
*);
223 static const struct got_error
*gw_diff(struct gw_trans
*);
224 static const struct got_error
*gw_index(struct gw_trans
*);
225 static const struct got_error
*gw_commits(struct gw_trans
*);
226 static const struct got_error
*gw_briefs(struct gw_trans
*);
227 static const struct got_error
*gw_summary(struct gw_trans
*);
228 static const struct got_error
*gw_tree(struct gw_trans
*);
229 static const struct got_error
*gw_tag(struct gw_trans
*);
230 static const struct got_error
*gw_tags(struct gw_trans
*);
232 struct gw_query_action
{
233 unsigned int func_id
;
234 const char *func_name
;
235 const struct got_error
*(*func_main
)(struct gw_trans
*);
239 enum gw_query_actions
{
253 static struct gw_query_action gw_query_funcs
[] = {
254 { GW_BLAME
, "blame", gw_blame
, "gw_tmpl/blame.tmpl" },
255 { GW_BLOB
, "blob", NULL
, NULL
},
256 { GW_BRIEFS
, "briefs", gw_briefs
, "gw_tmpl/briefs.tmpl" },
257 { GW_COMMITS
, "commits", gw_commits
, "gw_tmpl/commit.tmpl" },
258 { GW_DIFF
, "diff", gw_diff
, "gw_tmpl/diff.tmpl" },
259 { GW_ERR
, "error", gw_error
, "gw_tmpl/err.tmpl" },
260 { GW_INDEX
, "index", gw_index
, "gw_tmpl/index.tmpl" },
261 { GW_SUMMARY
, "summary", gw_summary
, "gw_tmpl/summry.tmpl" },
262 { GW_TAG
, "tag", gw_tag
, "gw_tmpl/tag.tmpl" },
263 { GW_TAGS
, "tags", gw_tags
, "gw_tmpl/tags.tmpl" },
264 { GW_TREE
, "tree", gw_tree
, "gw_tmpl/tree.tmpl" },
268 gw_get_action_name(struct gw_trans
*gw_trans
)
270 return gw_query_funcs
[gw_trans
->action
].func_name
;
273 static const struct got_error
*
274 gw_kcgi_error(enum kcgi_err kerr
)
279 if (kerr
== KCGI_EXIT
|| kerr
== KCGI_HUP
)
280 return got_error(GOT_ERR_CANCELLED
);
282 if (kerr
== KCGI_ENOMEM
)
283 return got_error_set_errno(ENOMEM
,
284 kcgi_strerror(kerr
));
286 if (kerr
== KCGI_ENFILE
)
287 return got_error_set_errno(ENFILE
,
288 kcgi_strerror(kerr
));
290 if (kerr
== KCGI_EAGAIN
)
291 return got_error_set_errno(EAGAIN
,
292 kcgi_strerror(kerr
));
294 if (kerr
== KCGI_FORM
)
295 return got_error_msg(GOT_ERR_IO
,
296 kcgi_strerror(kerr
));
298 return got_error_from_errno(kcgi_strerror(kerr
));
301 static const struct got_error
*
302 gw_apply_unveil(const char *repo_path
)
304 const struct got_error
*err
;
307 if (unveil("gmon.out", "rwc") != 0)
308 return got_error_from_errno2("unveil", "gmon.out");
310 if (repo_path
&& unveil(repo_path
, "r") != 0)
311 return got_error_from_errno2("unveil", repo_path
);
313 if (unveil(GOT_TMPDIR_STR
, "rwc") != 0)
314 return got_error_from_errno2("unveil", GOT_TMPDIR_STR
);
316 err
= got_privsep_unveil_exec_helpers();
320 if (unveil(NULL
, NULL
) != 0)
321 return got_error_from_errno("unveil");
327 isbinary(const uint8_t *buf
, size_t n
)
331 for (i
= 0; i
< n
; i
++)
337 static const struct got_error
*
338 gw_blame(struct gw_trans
*gw_trans
)
340 const struct got_error
*error
= NULL
;
341 struct gw_header
*header
= NULL
;
343 enum kcgi_err kerr
= KCGI_OK
;
346 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
348 return got_error_from_errno("pledge");
350 if ((header
= gw_init_header()) == NULL
)
351 return got_error_from_errno("malloc");
353 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
357 /* check querystring */
358 if (gw_trans
->repo_file
== NULL
) {
359 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
360 "file required in querystring");
363 if (gw_trans
->commit_id
== NULL
) {
364 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
365 "commit required in querystring");
369 error
= gw_get_header(gw_trans
, header
, 1);
372 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
373 "blame_header_wrapper", KATTR__MAX
);
376 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
377 "blame_header", KATTR__MAX
);
380 error
= gw_get_time_str(&age
, header
->committer_time
,
384 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
387 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
390 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
393 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
394 "dotted_line", KATTR__MAX
);
397 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
401 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
402 "blame", KATTR__MAX
);
405 error
= gw_output_file_blame(gw_trans
, header
);
408 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
410 gw_free_header(header
);
411 if (error
== NULL
&& kerr
!= KCGI_OK
)
412 error
= gw_kcgi_error(kerr
);
416 static const struct got_error
*
417 gw_blob(struct gw_trans
*gw_trans
)
419 const struct got_error
*error
= NULL
, *err
= NULL
;
420 struct gw_header
*header
= NULL
;
421 enum kcgi_err kerr
= KCGI_OK
;
424 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
426 return got_error_from_errno("pledge");
428 if ((header
= gw_init_header()) == NULL
)
429 return got_error_from_errno("malloc");
431 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
435 /* check querystring */
436 if (gw_trans
->repo_file
== NULL
) {
437 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
438 "file required in querystring");
441 if (gw_trans
->commit_id
== NULL
) {
442 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
443 "commit required in querystring");
446 error
= gw_get_header(gw_trans
, header
, 1);
450 error
= gw_output_blob_buf(gw_trans
, header
);
454 gw_trans
->mime
= KMIME_TEXT_PLAIN
;
455 err
= gw_display_index(gw_trans
);
460 kerr
= khttp_puts(gw_trans
->gw_req
, error
->msg
);
463 gw_free_header(header
);
464 if (error
== NULL
&& kerr
!= KCGI_OK
)
465 error
= gw_kcgi_error(kerr
);
469 static const struct got_error
*
470 gw_diff(struct gw_trans
*gw_trans
)
472 const struct got_error
*error
= NULL
;
473 struct gw_header
*header
= NULL
;
475 enum kcgi_err kerr
= KCGI_OK
;
478 if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
480 return got_error_from_errno("pledge");
482 if ((header
= gw_init_header()) == NULL
)
483 return got_error_from_errno("malloc");
485 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
489 error
= gw_get_header(gw_trans
, header
, 1);
493 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
494 "diff_header_wrapper", KATTR__MAX
);
497 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
498 "diff_header", KATTR__MAX
);
501 error
= gw_gen_diff_header(gw_trans
, header
->parent_id
,
505 error
= gw_gen_commit_header(gw_trans
, header
->commit_id
,
509 error
= gw_gen_tree_header(gw_trans
, header
->tree_id
);
512 error
= gw_gen_author_header(gw_trans
, header
->author
);
515 error
= gw_gen_committer_header(gw_trans
, header
->author
);
518 error
= gw_get_time_str(&age
, header
->committer_time
,
522 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
525 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
528 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
531 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
532 "dotted_line", KATTR__MAX
);
535 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
539 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
543 error
= gw_output_diff(gw_trans
, header
);
547 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
549 gw_free_header(header
);
551 if (error
== NULL
&& kerr
!= KCGI_OK
)
552 error
= gw_kcgi_error(kerr
);
556 static const struct got_error
*
557 gw_index(struct gw_trans
*gw_trans
)
559 const struct got_error
*error
= NULL
;
560 struct gw_dir
*gw_dir
= NULL
;
561 char *href_next
= NULL
, *href_prev
= NULL
, *href_summary
= NULL
;
562 char *href_briefs
= NULL
, *href_commits
= NULL
, *href_tree
= NULL
;
563 char *href_tags
= NULL
;
564 unsigned int prev_disp
= 0, next_disp
= 1, dir_c
= 0;
565 enum kcgi_err kerr
= KCGI_OK
;
568 if (pledge("stdio rpath proc exec sendfd unveil",
570 error
= got_error_from_errno("pledge");
574 error
= gw_apply_unveil(gw_trans
->gw_conf
->got_repos_path
);
578 error
= gw_load_got_paths(gw_trans
);
582 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
583 "index_header", KATTR__MAX
);
585 return gw_kcgi_error(kerr
);
586 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
587 "index_header_project", KATTR__MAX
);
589 return gw_kcgi_error(kerr
);
590 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Project");
592 return gw_kcgi_error(kerr
);
593 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
595 return gw_kcgi_error(kerr
);
597 if (gw_trans
->gw_conf
->got_show_repo_description
) {
598 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
599 "index_header_description", KATTR__MAX
);
601 return gw_kcgi_error(kerr
);
602 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Description");
604 return gw_kcgi_error(kerr
);
605 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
607 return gw_kcgi_error(kerr
);
610 if (gw_trans
->gw_conf
->got_show_repo_owner
) {
611 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
612 "index_header_owner", KATTR__MAX
);
614 return gw_kcgi_error(kerr
);
615 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Owner");
617 return gw_kcgi_error(kerr
);
618 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
620 return gw_kcgi_error(kerr
);
623 if (gw_trans
->gw_conf
->got_show_repo_age
) {
624 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
625 "index_header_age", KATTR__MAX
);
627 return gw_kcgi_error(kerr
);
628 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Last Change");
630 return gw_kcgi_error(kerr
);
631 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
633 return gw_kcgi_error(kerr
);
636 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
638 return gw_kcgi_error(kerr
);
640 if (TAILQ_EMPTY(&gw_trans
->gw_dirs
)) {
641 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
642 "index_wrapper", KATTR__MAX
);
644 return gw_kcgi_error(kerr
);
645 kerr
= khtml_printf(gw_trans
->gw_html_req
,
646 "No repositories found in %s",
647 gw_trans
->gw_conf
->got_repos_path
);
649 return gw_kcgi_error(kerr
);
650 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
652 return gw_kcgi_error(kerr
);
653 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
654 "dotted_line", KATTR__MAX
);
656 return gw_kcgi_error(kerr
);
657 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
659 return gw_kcgi_error(kerr
);
663 TAILQ_FOREACH(gw_dir
, &gw_trans
->gw_dirs
, entry
)
666 TAILQ_FOREACH(gw_dir
, &gw_trans
->gw_dirs
, entry
) {
667 if (gw_trans
->page
> 0 && (gw_trans
->page
*
668 gw_trans
->gw_conf
->got_max_repos_display
) > prev_disp
) {
675 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
676 "index_wrapper", KATTR__MAX
);
680 href_summary
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
681 gw_dir
->name
, "action", "summary", NULL
);
682 if (href_summary
== NULL
) {
683 error
= got_error_from_errno("khttp_urlpart");
686 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
687 "index_project", KATTR__MAX
);
690 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
691 href_summary
, KATTR__MAX
);
694 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_dir
->name
);
697 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
700 if (gw_trans
->gw_conf
->got_show_repo_description
) {
701 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
702 KATTR_ID
, "index_project_description", KATTR__MAX
);
705 kerr
= khtml_puts(gw_trans
->gw_html_req
,
706 gw_dir
->description
? gw_dir
->description
: "");
709 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
713 if (gw_trans
->gw_conf
->got_show_repo_owner
) {
714 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
715 KATTR_ID
, "index_project_owner", KATTR__MAX
);
718 kerr
= khtml_puts(gw_trans
->gw_html_req
,
719 gw_dir
->owner
? gw_dir
->owner
: "");
722 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
726 if (gw_trans
->gw_conf
->got_show_repo_age
) {
727 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
728 KATTR_ID
, "index_project_age", KATTR__MAX
);
731 kerr
= khtml_puts(gw_trans
->gw_html_req
,
732 gw_dir
->age
? gw_dir
->age
: "");
735 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
740 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
741 "navs_wrapper", KATTR__MAX
);
744 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
749 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
750 href_summary
, KATTR__MAX
);
753 kerr
= khtml_puts(gw_trans
->gw_html_req
, "summary");
756 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
760 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
764 href_briefs
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
765 gw_dir
->name
, "action", "briefs", NULL
);
766 if (href_briefs
== NULL
) {
767 error
= got_error_from_errno("khttp_urlpart");
770 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
771 href_briefs
, KATTR__MAX
);
774 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commit briefs");
777 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
779 error
= gw_kcgi_error(kerr
);
781 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
785 href_commits
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
786 gw_dir
->name
, "action", "commits", NULL
);
787 if (href_commits
== NULL
) {
788 error
= got_error_from_errno("khttp_urlpart");
791 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
792 href_commits
, KATTR__MAX
);
795 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commits");
798 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
802 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
806 href_tags
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
807 gw_dir
->name
, "action", "tags", NULL
);
808 if (href_tags
== NULL
) {
809 error
= got_error_from_errno("khttp_urlpart");
812 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
813 href_tags
, KATTR__MAX
);
816 kerr
= khtml_puts(gw_trans
->gw_html_req
, "tags");
819 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
823 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
827 href_tree
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
828 gw_dir
->name
, "action", "tree", NULL
);
829 if (href_tree
== NULL
) {
830 error
= got_error_from_errno("khttp_urlpart");
833 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
834 href_tree
, KATTR__MAX
);
837 kerr
= khtml_puts(gw_trans
->gw_html_req
, "tree");
841 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 4);
844 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
845 "dotted_line", KATTR__MAX
);
848 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
863 if (gw_trans
->gw_conf
->got_max_repos_display
== 0)
866 if ((next_disp
== gw_trans
->gw_conf
->got_max_repos_display
) ||
867 ((gw_trans
->gw_conf
->got_max_repos_display
> 0) &&
868 (gw_trans
->page
> 0) &&
869 (next_disp
== gw_trans
->gw_conf
->got_max_repos_display
||
870 prev_disp
== gw_trans
->repos_total
))) {
871 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
872 KATTR_ID
, "np_wrapper", KATTR__MAX
);
875 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
876 KATTR_ID
, "nav_prev", KATTR__MAX
);
881 if ((gw_trans
->gw_conf
->got_max_repos_display
> 0) &&
882 (gw_trans
->page
> 0) &&
883 (next_disp
== gw_trans
->gw_conf
->got_max_repos_display
||
884 prev_disp
== gw_trans
->repos_total
)) {
885 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "page",
886 KATTRX_INT
, (int64_t)(gw_trans
->page
- 1), NULL
);
887 if (href_prev
== NULL
) {
888 error
= got_error_from_errno("khttp_urlpartx");
891 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
892 KATTR_HREF
, href_prev
, KATTR__MAX
);
895 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
898 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
903 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
905 return gw_kcgi_error(kerr
);
907 if (gw_trans
->gw_conf
->got_max_repos_display
> 0 &&
908 next_disp
== gw_trans
->gw_conf
->got_max_repos_display
&&
909 dir_c
!= (gw_trans
->page
+ 1) *
910 gw_trans
->gw_conf
->got_max_repos_display
) {
911 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
912 KATTR_ID
, "nav_next", KATTR__MAX
);
915 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "page",
916 KATTRX_INT
, (int64_t)(gw_trans
->page
+ 1), NULL
);
917 if (href_next
== NULL
) {
918 error
= got_error_from_errno("khttp_urlpartx");
921 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
922 KATTR_HREF
, href_next
, KATTR__MAX
);
925 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
928 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
935 if ((gw_trans
->gw_conf
->got_max_repos_display
> 0) &&
936 (gw_trans
->page
> 0) &&
937 (next_disp
== gw_trans
->gw_conf
->got_max_repos_display
||
938 prev_disp
== gw_trans
->repos_total
)) {
939 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
953 if (error
== NULL
&& kerr
!= KCGI_OK
)
954 error
= gw_kcgi_error(kerr
);
958 static const struct got_error
*
959 gw_commits(struct gw_trans
*gw_trans
)
961 const struct got_error
*error
= NULL
;
962 struct gw_header
*header
= NULL
, *n_header
= NULL
;
963 char *age
= NULL
, *href_diff
= NULL
, *href_tree
= NULL
;
964 char *href_prev
= NULL
, *href_next
= NULL
;
965 enum kcgi_err kerr
= KCGI_OK
;
966 int commit_found
= 0;
968 if ((header
= gw_init_header()) == NULL
)
969 return got_error_from_errno("malloc");
972 if (pledge("stdio rpath proc exec sendfd unveil",
974 error
= got_error_from_errno("pledge");
978 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
982 error
= gw_get_header(gw_trans
, header
,
983 gw_trans
->gw_conf
->got_max_commits_display
);
987 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
) {
988 if (commit_found
== 0 && gw_trans
->commit_id
!= NULL
) {
989 if (strcmp(gw_trans
->commit_id
,
990 n_header
->commit_id
) != 0)
995 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
996 "commits_line_wrapper", KATTR__MAX
);
999 error
= gw_gen_commit_header(gw_trans
, n_header
->commit_id
,
1000 n_header
->refs_str
);
1003 error
= gw_gen_author_header(gw_trans
, n_header
->author
);
1006 error
= gw_gen_committer_header(gw_trans
, n_header
->author
);
1009 error
= gw_get_time_str(&age
, n_header
->committer_time
,
1013 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
1016 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1017 if (kerr
!= KCGI_OK
)
1020 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1021 "dotted_line", KATTR__MAX
);
1022 if (kerr
!= KCGI_OK
)
1024 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1025 if (kerr
!= KCGI_OK
)
1028 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1029 "commit", KATTR__MAX
);
1030 if (kerr
!= KCGI_OK
)
1032 kerr
= khttp_puts(gw_trans
->gw_req
, n_header
->commit_msg
);
1033 if (kerr
!= KCGI_OK
)
1035 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1036 if (kerr
!= KCGI_OK
)
1039 href_diff
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1040 gw_trans
->repo_name
, "action", "diff", "commit",
1041 n_header
->commit_id
, NULL
);
1042 if (href_diff
== NULL
) {
1043 error
= got_error_from_errno("khttp_urlpart");
1046 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1047 KATTR_ID
, "navs_wrapper", KATTR__MAX
);
1048 if (kerr
!= KCGI_OK
)
1050 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1051 KATTR_ID
, "navs", KATTR__MAX
);
1052 if (kerr
!= KCGI_OK
)
1054 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1055 KATTR_HREF
, href_diff
, KATTR__MAX
);
1056 if (kerr
!= KCGI_OK
)
1058 kerr
= khtml_puts(gw_trans
->gw_html_req
, "diff");
1059 if (kerr
!= KCGI_OK
)
1061 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1062 if (kerr
!= KCGI_OK
)
1065 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
1066 if (kerr
!= KCGI_OK
)
1069 href_tree
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1070 gw_trans
->repo_name
, "action", "tree", "commit",
1071 n_header
->commit_id
, NULL
);
1072 if (href_tree
== NULL
) {
1073 error
= got_error_from_errno("khttp_urlpart");
1076 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1077 KATTR_HREF
, href_tree
, KATTR__MAX
);
1078 if (kerr
!= KCGI_OK
)
1080 khtml_puts(gw_trans
->gw_html_req
, "tree");
1081 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1082 if (kerr
!= KCGI_OK
)
1084 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1085 if (kerr
!= KCGI_OK
)
1088 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1089 "solid_line", KATTR__MAX
);
1090 if (kerr
!= KCGI_OK
)
1092 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1093 if (kerr
!= KCGI_OK
)
1100 if (gw_trans
->next_id
|| gw_trans
->prev_id
) {
1101 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1102 KATTR_ID
, "np_wrapper", KATTR__MAX
);
1103 if (kerr
!= KCGI_OK
)
1105 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1106 KATTR_ID
, "nav_prev", KATTR__MAX
);
1107 if (kerr
!= KCGI_OK
)
1111 if (gw_trans
->prev_id
) {
1112 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1113 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1114 KATTRX_INT
, (int64_t) (gw_trans
->page
- 1), "action",
1115 KATTRX_STRING
, "commits", "commit", KATTRX_STRING
,
1116 gw_trans
->prev_id
? gw_trans
->prev_id
: "", NULL
);
1117 if (href_prev
== NULL
) {
1118 error
= got_error_from_errno("khttp_urlpartx");
1121 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1122 KATTR_HREF
, href_prev
, KATTR__MAX
);
1123 if (kerr
!= KCGI_OK
)
1125 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
1126 if (kerr
!= KCGI_OK
)
1128 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1129 if (kerr
!= KCGI_OK
)
1133 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1134 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1135 if (kerr
!= KCGI_OK
)
1136 return gw_kcgi_error(kerr
);
1139 if (gw_trans
->next_id
) {
1140 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1141 KATTR_ID
, "nav_next", KATTR__MAX
);
1142 if (kerr
!= KCGI_OK
)
1144 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1145 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1146 KATTRX_INT
, (int64_t) (gw_trans
->page
+ 1), "action",
1147 KATTRX_STRING
, "commits", "commit", KATTRX_STRING
,
1148 gw_trans
->next_id
, NULL
);
1149 if (href_next
== NULL
) {
1150 error
= got_error_from_errno("khttp_urlpartx");
1153 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1154 KATTR_HREF
, href_next
, KATTR__MAX
);
1155 if (kerr
!= KCGI_OK
)
1157 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
1158 if (kerr
!= KCGI_OK
)
1160 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1161 if (kerr
!= KCGI_OK
)
1165 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1166 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1167 if (kerr
!= KCGI_OK
)
1171 gw_free_header(header
);
1172 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
)
1173 gw_free_header(n_header
);
1179 if (error
== NULL
&& kerr
!= KCGI_OK
)
1180 error
= gw_kcgi_error(kerr
);
1184 static const struct got_error
*
1185 gw_briefs(struct gw_trans
*gw_trans
)
1187 const struct got_error
*error
= NULL
;
1188 struct gw_header
*header
= NULL
, *n_header
= NULL
;
1189 char *age
= NULL
, *href_diff
= NULL
, *href_tree
= NULL
;
1190 char *href_prev
= NULL
, *href_next
= NULL
;
1191 char *newline
, *smallerthan
;
1192 enum kcgi_err kerr
= KCGI_OK
;
1193 int commit_found
= 0;
1195 if ((header
= gw_init_header()) == NULL
)
1196 return got_error_from_errno("malloc");
1199 if (pledge("stdio rpath proc exec sendfd unveil",
1201 error
= got_error_from_errno("pledge");
1205 if (gw_trans
->action
!= GW_SUMMARY
) {
1206 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1211 if (gw_trans
->action
== GW_SUMMARY
)
1212 error
= gw_get_header(gw_trans
, header
, D_MAXSLCOMMDISP
);
1214 error
= gw_get_header(gw_trans
, header
,
1215 gw_trans
->gw_conf
->got_max_commits_display
);
1219 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
) {
1220 if (commit_found
== 0 && gw_trans
->commit_id
!= NULL
) {
1221 if (strcmp(gw_trans
->commit_id
,
1222 n_header
->commit_id
) != 0)
1227 error
= gw_get_time_str(&age
, n_header
->committer_time
,
1232 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1233 KATTR_ID
, "briefs_wrapper", KATTR__MAX
);
1234 if (kerr
!= KCGI_OK
)
1237 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1238 KATTR_ID
, "briefs_age", KATTR__MAX
);
1239 if (kerr
!= KCGI_OK
)
1241 kerr
= khtml_puts(gw_trans
->gw_html_req
, age
? age
: "");
1242 if (kerr
!= KCGI_OK
)
1244 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1245 if (kerr
!= KCGI_OK
)
1248 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1249 KATTR_ID
, "briefs_author", KATTR__MAX
);
1250 if (kerr
!= KCGI_OK
)
1252 smallerthan
= strchr(n_header
->author
, '<');
1254 *smallerthan
= '\0';
1255 kerr
= khtml_puts(gw_trans
->gw_html_req
, n_header
->author
);
1256 if (kerr
!= KCGI_OK
)
1258 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1259 if (kerr
!= KCGI_OK
)
1262 href_diff
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1263 gw_trans
->repo_name
, "action", "diff", "commit",
1264 n_header
->commit_id
, NULL
);
1265 if (href_diff
== NULL
) {
1266 error
= got_error_from_errno("khttp_urlpart");
1269 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1270 KATTR_ID
, "briefs_log", KATTR__MAX
);
1271 if (kerr
!= KCGI_OK
)
1273 newline
= strchr(n_header
->commit_msg
, '\n');
1276 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1277 KATTR_HREF
, href_diff
, KATTR__MAX
);
1278 if (kerr
!= KCGI_OK
)
1280 kerr
= khtml_puts(gw_trans
->gw_html_req
, n_header
->commit_msg
);
1281 if (kerr
!= KCGI_OK
)
1283 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1284 if (kerr
!= KCGI_OK
)
1287 if (n_header
->refs_str
) {
1288 kerr
= khtml_puts(gw_trans
->gw_html_req
, " ");
1289 if (kerr
!= KCGI_OK
)
1291 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_SPAN
,
1292 KATTR_ID
, "refs_str", KATTR__MAX
);
1293 if (kerr
!= KCGI_OK
)
1295 kerr
= khtml_printf(gw_trans
->gw_html_req
, "(%s)",
1296 n_header
->refs_str
);
1297 if (kerr
!= KCGI_OK
)
1299 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1300 if (kerr
!= KCGI_OK
)
1304 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1305 if (kerr
!= KCGI_OK
)
1308 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1309 KATTR_ID
, "navs_wrapper", KATTR__MAX
);
1310 if (kerr
!= KCGI_OK
)
1312 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1313 KATTR_ID
, "navs", KATTR__MAX
);
1314 if (kerr
!= KCGI_OK
)
1316 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1317 KATTR_HREF
, href_diff
, KATTR__MAX
);
1318 if (kerr
!= KCGI_OK
)
1320 kerr
= khtml_puts(gw_trans
->gw_html_req
, "diff");
1321 if (kerr
!= KCGI_OK
)
1323 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1324 if (kerr
!= KCGI_OK
)
1327 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
1328 if (kerr
!= KCGI_OK
)
1331 href_tree
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
1332 gw_trans
->repo_name
, "action", "tree", "commit",
1333 n_header
->commit_id
, NULL
);
1334 if (href_tree
== NULL
) {
1335 error
= got_error_from_errno("khttp_urlpart");
1338 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1339 KATTR_HREF
, href_tree
, KATTR__MAX
);
1340 if (kerr
!= KCGI_OK
)
1342 khtml_puts(gw_trans
->gw_html_req
, "tree");
1343 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1344 if (kerr
!= KCGI_OK
)
1346 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1347 if (kerr
!= KCGI_OK
)
1350 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1351 KATTR_ID
, "dotted_line", KATTR__MAX
);
1352 if (kerr
!= KCGI_OK
)
1354 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1355 if (kerr
!= KCGI_OK
)
1366 if (gw_trans
->next_id
|| gw_trans
->prev_id
) {
1367 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1368 KATTR_ID
, "np_wrapper", KATTR__MAX
);
1369 if (kerr
!= KCGI_OK
)
1371 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1372 KATTR_ID
, "nav_prev", KATTR__MAX
);
1373 if (kerr
!= KCGI_OK
)
1377 if (gw_trans
->prev_id
) {
1378 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1379 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1380 KATTRX_INT
, (int64_t) (gw_trans
->page
- 1), "action",
1381 KATTRX_STRING
, "briefs", "commit", KATTRX_STRING
,
1382 gw_trans
->prev_id
? gw_trans
->prev_id
: "", NULL
);
1383 if (href_prev
== NULL
) {
1384 error
= got_error_from_errno("khttp_urlpartx");
1387 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1388 KATTR_HREF
, href_prev
, KATTR__MAX
);
1389 if (kerr
!= KCGI_OK
)
1391 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
1392 if (kerr
!= KCGI_OK
)
1394 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1395 if (kerr
!= KCGI_OK
)
1399 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1400 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1401 if (kerr
!= KCGI_OK
)
1402 return gw_kcgi_error(kerr
);
1405 if (gw_trans
->next_id
) {
1406 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1407 KATTR_ID
, "nav_next", KATTR__MAX
);
1408 if (kerr
!= KCGI_OK
)
1411 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1412 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1413 KATTRX_INT
, (int64_t) (gw_trans
->page
+ 1), "action",
1414 KATTRX_STRING
, "briefs", "commit", KATTRX_STRING
,
1415 gw_trans
->next_id
, NULL
);
1416 if (href_next
== NULL
) {
1417 error
= got_error_from_errno("khttp_urlpartx");
1420 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1421 KATTR_HREF
, href_next
, KATTR__MAX
);
1422 if (kerr
!= KCGI_OK
)
1424 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
1425 if (kerr
!= KCGI_OK
)
1427 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1428 if (kerr
!= KCGI_OK
)
1432 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1433 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1434 if (kerr
!= KCGI_OK
)
1438 gw_free_header(header
);
1439 TAILQ_FOREACH(n_header
, &gw_trans
->gw_headers
, entry
)
1440 gw_free_header(n_header
);
1446 if (error
== NULL
&& kerr
!= KCGI_OK
)
1447 error
= gw_kcgi_error(kerr
);
1451 static const struct got_error
*
1452 gw_summary(struct gw_trans
*gw_trans
)
1454 const struct got_error
*error
= NULL
;
1456 enum kcgi_err kerr
= KCGI_OK
;
1459 if (pledge("stdio rpath proc exec sendfd unveil", NULL
) == -1)
1460 return got_error_from_errno("pledge");
1462 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1466 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1467 "summary_wrapper", KATTR__MAX
);
1468 if (kerr
!= KCGI_OK
)
1469 return gw_kcgi_error(kerr
);
1471 if (gw_trans
->gw_conf
->got_show_repo_description
&&
1472 gw_trans
->gw_dir
->description
!= NULL
&&
1473 (strcmp(gw_trans
->gw_dir
->description
, "") != 0)) {
1474 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1475 KATTR_ID
, "description_title", KATTR__MAX
);
1476 if (kerr
!= KCGI_OK
)
1478 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Description: ");
1479 if (kerr
!= KCGI_OK
)
1481 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1482 if (kerr
!= KCGI_OK
)
1484 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1485 KATTR_ID
, "description", KATTR__MAX
);
1486 if (kerr
!= KCGI_OK
)
1488 kerr
= khtml_puts(gw_trans
->gw_html_req
,
1489 gw_trans
->gw_dir
->description
);
1490 if (kerr
!= KCGI_OK
)
1492 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1493 if (kerr
!= KCGI_OK
)
1497 if (gw_trans
->gw_conf
->got_show_repo_owner
&&
1498 gw_trans
->gw_dir
->owner
!= NULL
&&
1499 (strcmp(gw_trans
->gw_dir
->owner
, "") != 0)) {
1500 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1501 KATTR_ID
, "repo_owner_title", KATTR__MAX
);
1502 if (kerr
!= KCGI_OK
)
1504 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Owner: ");
1505 if (kerr
!= KCGI_OK
)
1507 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1508 if (kerr
!= KCGI_OK
)
1510 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1511 KATTR_ID
, "repo_owner", KATTR__MAX
);
1512 if (kerr
!= KCGI_OK
)
1514 kerr
= khtml_puts(gw_trans
->gw_html_req
,
1515 gw_trans
->gw_dir
->owner
);
1516 if (kerr
!= KCGI_OK
)
1518 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1519 if (kerr
!= KCGI_OK
)
1523 if (gw_trans
->gw_conf
->got_show_repo_age
) {
1524 error
= gw_get_repo_age(&age
, gw_trans
, gw_trans
->gw_dir
->path
,
1529 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1530 KATTR_ID
, "last_change_title", KATTR__MAX
);
1531 if (kerr
!= KCGI_OK
)
1533 kerr
= khtml_puts(gw_trans
->gw_html_req
,
1535 if (kerr
!= KCGI_OK
)
1537 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1538 if (kerr
!= KCGI_OK
)
1540 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1541 KATTR_ID
, "last_change", KATTR__MAX
);
1542 if (kerr
!= KCGI_OK
)
1544 kerr
= khtml_puts(gw_trans
->gw_html_req
, age
);
1545 if (kerr
!= KCGI_OK
)
1547 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1548 if (kerr
!= KCGI_OK
)
1553 if (gw_trans
->gw_conf
->got_show_repo_cloneurl
&&
1554 gw_trans
->gw_dir
->url
!= NULL
&&
1555 (strcmp(gw_trans
->gw_dir
->url
, "") != 0)) {
1556 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1557 KATTR_ID
, "cloneurl_title", KATTR__MAX
);
1558 if (kerr
!= KCGI_OK
)
1560 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Clone URL: ");
1561 if (kerr
!= KCGI_OK
)
1563 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1564 if (kerr
!= KCGI_OK
)
1566 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1567 KATTR_ID
, "cloneurl", KATTR__MAX
);
1568 if (kerr
!= KCGI_OK
)
1570 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_trans
->gw_dir
->url
);
1571 if (kerr
!= KCGI_OK
)
1573 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1574 if (kerr
!= KCGI_OK
)
1578 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1579 if (kerr
!= KCGI_OK
)
1582 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1583 "briefs_title_wrapper", KATTR__MAX
);
1584 if (kerr
!= KCGI_OK
)
1586 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1587 "briefs_title", KATTR__MAX
);
1588 if (kerr
!= KCGI_OK
)
1590 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Commit Briefs");
1591 if (kerr
!= KCGI_OK
)
1593 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1594 if (kerr
!= KCGI_OK
)
1596 error
= gw_briefs(gw_trans
);
1600 error
= gw_tags(gw_trans
);
1604 error
= gw_output_repo_heads(gw_trans
);
1607 if (error
== NULL
&& kerr
!= KCGI_OK
)
1608 error
= gw_kcgi_error(kerr
);
1612 static const struct got_error
*
1613 gw_tree(struct gw_trans
*gw_trans
)
1615 const struct got_error
*error
= NULL
;
1616 struct gw_header
*header
= NULL
;
1617 char *tree
= NULL
, *tree_html
= NULL
, *tree_html_disp
= NULL
;
1619 enum kcgi_err kerr
= KCGI_OK
;
1622 if (pledge("stdio rpath proc exec sendfd unveil", NULL
) == -1)
1623 return got_error_from_errno("pledge");
1625 if ((header
= gw_init_header()) == NULL
)
1626 return got_error_from_errno("malloc");
1628 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1632 error
= gw_get_header(gw_trans
, header
, 1);
1636 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1637 "tree_header_wrapper", KATTR__MAX
);
1638 if (kerr
!= KCGI_OK
)
1640 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1641 "tree_header", KATTR__MAX
);
1642 if (kerr
!= KCGI_OK
)
1644 error
= gw_gen_tree_header(gw_trans
, header
->tree_id
);
1647 error
= gw_get_time_str(&age
, header
->committer_time
,
1651 error
= gw_gen_age_header(gw_trans
, age
?age
: "");
1654 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
1657 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1658 if (kerr
!= KCGI_OK
)
1660 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1661 "dotted_line", KATTR__MAX
);
1662 if (kerr
!= KCGI_OK
)
1664 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1665 if (kerr
!= KCGI_OK
)
1668 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1669 "tree", KATTR__MAX
);
1670 if (kerr
!= KCGI_OK
)
1672 error
= gw_output_repo_tree(gw_trans
, header
);
1676 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1678 gw_free_header(header
);
1679 free(tree_html_disp
);
1683 if (error
== NULL
&& kerr
!= KCGI_OK
)
1684 error
= gw_kcgi_error(kerr
);
1688 static const struct got_error
*
1689 gw_tags(struct gw_trans
*gw_trans
)
1691 const struct got_error
*error
= NULL
;
1692 struct gw_header
*header
= NULL
;
1693 char *href_next
= NULL
, *href_prev
= NULL
;
1694 enum kcgi_err kerr
= KCGI_OK
;
1697 if (pledge("stdio rpath proc exec sendfd unveil", NULL
) == -1)
1698 return got_error_from_errno("pledge");
1700 if ((header
= gw_init_header()) == NULL
)
1701 return got_error_from_errno("malloc");
1703 if (gw_trans
->action
!= GW_SUMMARY
) {
1704 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1709 error
= gw_get_header(gw_trans
, header
, 1);
1713 if (gw_trans
->action
== GW_SUMMARY
) {
1714 gw_trans
->next_id
= NULL
;
1715 error
= gw_output_repo_tags(gw_trans
, header
,
1716 D_MAXSLCOMMDISP
, TAGBRIEF
);
1720 error
= gw_output_repo_tags(gw_trans
, header
,
1721 gw_trans
->gw_conf
->got_max_commits_display
, TAGBRIEF
);
1726 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1727 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1728 KATTR_ID
, "np_wrapper", KATTR__MAX
);
1729 if (kerr
!= KCGI_OK
)
1731 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1732 KATTR_ID
, "nav_prev", KATTR__MAX
);
1733 if (kerr
!= KCGI_OK
)
1737 if (gw_trans
->prev_id
) {
1738 href_prev
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1739 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1740 KATTRX_INT
, (int64_t) (gw_trans
->page
- 1), "action",
1741 KATTRX_STRING
, "tags", "commit", KATTRX_STRING
,
1742 gw_trans
->prev_id
? gw_trans
->prev_id
: "", NULL
);
1743 if (href_prev
== NULL
) {
1744 error
= got_error_from_errno("khttp_urlpartx");
1747 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1748 KATTR_HREF
, href_prev
, KATTR__MAX
);
1749 if (kerr
!= KCGI_OK
)
1751 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Previous");
1752 if (kerr
!= KCGI_OK
)
1754 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1755 if (kerr
!= KCGI_OK
)
1759 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1760 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1761 if (kerr
!= KCGI_OK
)
1762 return gw_kcgi_error(kerr
);
1765 if (gw_trans
->next_id
) {
1766 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
1767 KATTR_ID
, "nav_next", KATTR__MAX
);
1768 if (kerr
!= KCGI_OK
)
1770 href_next
= khttp_urlpartx(NULL
, NULL
, "gotweb", "path",
1771 KATTRX_STRING
, gw_trans
->repo_name
, "page",
1772 KATTRX_INT
, (int64_t) (gw_trans
->page
+ 1), "action",
1773 KATTRX_STRING
, "tags", "commit", KATTRX_STRING
,
1774 gw_trans
->next_id
, NULL
);
1775 if (href_next
== NULL
) {
1776 error
= got_error_from_errno("khttp_urlpartx");
1779 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
1780 KATTR_HREF
, href_next
, KATTR__MAX
);
1781 if (kerr
!= KCGI_OK
)
1783 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Next");
1784 if (kerr
!= KCGI_OK
)
1786 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
1787 if (kerr
!= KCGI_OK
)
1791 if (gw_trans
->next_id
|| gw_trans
->page
> 0) {
1792 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1793 if (kerr
!= KCGI_OK
)
1797 gw_free_header(header
);
1800 if (error
== NULL
&& kerr
!= KCGI_OK
)
1801 error
= gw_kcgi_error(kerr
);
1805 static const struct got_error
*
1806 gw_tag(struct gw_trans
*gw_trans
)
1808 const struct got_error
*error
= NULL
;
1809 struct gw_header
*header
= NULL
;
1810 enum kcgi_err kerr
= KCGI_OK
;
1813 if (pledge("stdio rpath proc exec sendfd unveil", NULL
) == -1)
1814 return got_error_from_errno("pledge");
1816 if ((header
= gw_init_header()) == NULL
)
1817 return got_error_from_errno("malloc");
1819 error
= gw_apply_unveil(gw_trans
->gw_dir
->path
);
1823 if (gw_trans
->commit_id
== NULL
) {
1824 error
= got_error_msg(GOT_ERR_QUERYSTRING
,
1825 "commit required in querystring");
1829 error
= gw_get_header(gw_trans
, header
, 1);
1833 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1834 "tag_header_wrapper", KATTR__MAX
);
1835 if (kerr
!= KCGI_OK
)
1837 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1838 "tag_header", KATTR__MAX
);
1839 if (kerr
!= KCGI_OK
)
1841 error
= gw_gen_commit_header(gw_trans
, header
->commit_id
,
1845 error
= gw_gen_commit_msg_header(gw_trans
, header
->commit_msg
);
1848 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
1849 if (kerr
!= KCGI_OK
)
1851 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1852 "dotted_line", KATTR__MAX
);
1853 if (kerr
!= KCGI_OK
)
1855 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1856 if (kerr
!= KCGI_OK
)
1859 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
1860 "tree", KATTR__MAX
);
1861 if (kerr
!= KCGI_OK
)
1864 error
= gw_output_repo_tags(gw_trans
, header
, 1, TAGFULL
);
1868 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
1870 gw_free_header(header
);
1871 if (error
== NULL
&& kerr
!= KCGI_OK
)
1872 error
= gw_kcgi_error(kerr
);
1876 static const struct got_error
*
1877 gw_load_got_path(struct gw_trans
*gw_trans
, struct gw_dir
*gw_dir
)
1879 const struct got_error
*error
= NULL
;
1884 if (asprintf(&dir_test
, "%s/%s/%s",
1885 gw_trans
->gw_conf
->got_repos_path
, gw_dir
->name
,
1886 GOTWEB_GIT_DIR
) == -1)
1887 return got_error_from_errno("asprintf");
1889 dt
= opendir(dir_test
);
1893 gw_dir
->path
= strdup(dir_test
);
1894 if (gw_dir
->path
== NULL
) {
1896 error
= got_error_from_errno("strdup");
1903 if (asprintf(&dir_test
, "%s/%s/%s",
1904 gw_trans
->gw_conf
->got_repos_path
, gw_dir
->name
,
1905 GOTWEB_GOT_DIR
) == -1) {
1907 error
= got_error_from_errno("asprintf");
1911 dt
= opendir(dir_test
);
1916 error
= got_error(GOT_ERR_NOT_GIT_REPO
);
1920 if (asprintf(&dir_test
, "%s/%s",
1921 gw_trans
->gw_conf
->got_repos_path
, gw_dir
->name
) == -1) {
1922 error
= got_error_from_errno("asprintf");
1927 gw_dir
->path
= strdup(dir_test
);
1928 if (gw_dir
->path
== NULL
) {
1930 error
= got_error_from_errno("strdup");
1934 dt
= opendir(dir_test
);
1936 error
= got_error_path(gw_dir
->name
, GOT_ERR_NOT_GIT_REPO
);
1941 error
= gw_get_repo_description(&gw_dir
->description
, gw_trans
,
1945 error
= gw_get_repo_owner(&gw_dir
->owner
, gw_trans
, gw_dir
->path
);
1948 error
= gw_get_repo_age(&gw_dir
->age
, gw_trans
, gw_dir
->path
,
1952 error
= gw_get_clone_url(&gw_dir
->url
, gw_trans
, gw_dir
->path
);
1956 if (dt
&& closedir(dt
) == -1 && error
== NULL
)
1957 error
= got_error_from_errno("closedir");
1961 static const struct got_error
*
1962 gw_load_got_paths(struct gw_trans
*gw_trans
)
1964 const struct got_error
*error
= NULL
;
1966 struct dirent
**sd_dent
;
1967 struct gw_dir
*gw_dir
;
1969 unsigned int d_cnt
, d_i
;
1971 d
= opendir(gw_trans
->gw_conf
->got_repos_path
);
1973 error
= got_error_from_errno2("opendir",
1974 gw_trans
->gw_conf
->got_repos_path
);
1978 d_cnt
= scandir(gw_trans
->gw_conf
->got_repos_path
, &sd_dent
, NULL
,
1981 error
= got_error_from_errno2("scandir",
1982 gw_trans
->gw_conf
->got_repos_path
);
1986 for (d_i
= 0; d_i
< d_cnt
; d_i
++) {
1987 if (gw_trans
->gw_conf
->got_max_repos
> 0 &&
1988 (d_i
- 2) == gw_trans
->gw_conf
->got_max_repos
)
1989 break; /* account for parent and self */
1991 if (strcmp(sd_dent
[d_i
]->d_name
, ".") == 0 ||
1992 strcmp(sd_dent
[d_i
]->d_name
, "..") == 0)
1995 error
= gw_init_gw_dir(&gw_dir
, sd_dent
[d_i
]->d_name
);
1999 error
= gw_load_got_path(gw_trans
, gw_dir
);
2000 if (error
&& error
->code
== GOT_ERR_NOT_GIT_REPO
) {
2007 if (lstat(gw_dir
->path
, &st
) == 0 && S_ISDIR(st
.st_mode
) &&
2008 !got_path_dir_is_empty(gw_dir
->path
)) {
2009 TAILQ_INSERT_TAIL(&gw_trans
->gw_dirs
, gw_dir
,
2011 gw_trans
->repos_total
++;
2015 if (d
&& closedir(d
) == -1 && error
== NULL
)
2016 error
= got_error_from_errno("closedir");
2020 static const struct got_error
*
2021 gw_parse_querystring(struct gw_trans
*gw_trans
)
2023 const struct got_error
*error
= NULL
;
2025 struct gw_query_action
*action
= NULL
;
2028 if (gw_trans
->gw_req
->fieldnmap
[0]) {
2029 return got_error(GOT_ERR_QUERYSTRING
);
2030 } else if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_PATH
])) {
2031 /* define gw_trans->repo_path */
2032 gw_trans
->repo_name
= p
->parsed
.s
;
2034 if (asprintf(&gw_trans
->repo_path
, "%s/%s",
2035 gw_trans
->gw_conf
->got_repos_path
, p
->parsed
.s
) == -1)
2036 return got_error_from_errno("asprintf");
2038 /* get action and set function */
2039 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_ACTION
])) {
2040 for (i
= 0; i
< nitems(gw_query_funcs
); i
++) {
2041 action
= &gw_query_funcs
[i
];
2042 if (action
->func_name
== NULL
)
2044 if (strcmp(action
->func_name
,
2045 p
->parsed
.s
) == 0) {
2046 gw_trans
->action
= i
;
2051 if (gw_trans
->action
== -1) {
2052 gw_trans
->action
= GW_ERR
;
2053 gw_trans
->error
= got_error_msg(GOT_ERR_QUERYSTRING
,
2054 p
!= NULL
? "bad action in querystring" :
2055 "no action in querystring");
2059 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_COMMIT_ID
])) {
2060 if (asprintf(&gw_trans
->commit_id
, "%s",
2062 return got_error_from_errno("asprintf");
2065 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_FILE
]))
2066 gw_trans
->repo_file
= p
->parsed
.s
;
2068 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_FOLDER
])) {
2069 if (asprintf(&gw_trans
->repo_folder
, "%s",
2071 return got_error_from_errno("asprintf");
2074 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_PREV_ID
])) {
2075 if (asprintf(&gw_trans
->prev_id
, "%s",
2077 return got_error_from_errno("asprintf");
2080 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_HEADREF
]))
2081 gw_trans
->headref
= p
->parsed
.s
;
2083 error
= gw_init_gw_dir(&gw_trans
->gw_dir
, gw_trans
->repo_name
);
2087 gw_trans
->error
= gw_load_got_path(gw_trans
, gw_trans
->gw_dir
);
2089 gw_trans
->action
= GW_INDEX
;
2091 if ((p
= gw_trans
->gw_req
->fieldmap
[KEY_PAGE
]))
2092 gw_trans
->page
= p
->parsed
.i
;
2097 static const struct got_error
*
2098 gw_init_gw_dir(struct gw_dir
**gw_dir
, const char *dir
)
2100 const struct got_error
*error
;
2102 *gw_dir
= malloc(sizeof(**gw_dir
));
2103 if (*gw_dir
== NULL
)
2104 return got_error_from_errno("malloc");
2106 if (asprintf(&(*gw_dir
)->name
, "%s", dir
) == -1) {
2107 error
= got_error_from_errno("asprintf");
2116 static const struct got_error
*
2117 gw_display_open(struct gw_trans
*gw_trans
, enum khttp code
, enum kmime mime
)
2119 enum kcgi_err kerr
= KCGI_OK
;
2121 kerr
= khttp_head(gw_trans
->gw_req
, kresps
[KRESP_ALLOW
], "GET");
2122 if (kerr
!= KCGI_OK
)
2123 return gw_kcgi_error(kerr
);
2124 kerr
= khttp_head(gw_trans
->gw_req
, kresps
[KRESP_STATUS
], "%s",
2126 if (kerr
!= KCGI_OK
)
2127 return gw_kcgi_error(kerr
);
2128 kerr
= khttp_head(gw_trans
->gw_req
, kresps
[KRESP_CONTENT_TYPE
], "%s",
2130 if (kerr
!= KCGI_OK
)
2131 return gw_kcgi_error(kerr
);
2132 kerr
= khttp_head(gw_trans
->gw_req
, "X-Content-Type-Options",
2134 if (kerr
!= KCGI_OK
)
2135 return gw_kcgi_error(kerr
);
2136 kerr
= khttp_head(gw_trans
->gw_req
, "X-Frame-Options", "DENY");
2137 if (kerr
!= KCGI_OK
)
2138 return gw_kcgi_error(kerr
);
2139 kerr
= khttp_head(gw_trans
->gw_req
, "X-XSS-Protection",
2141 if (kerr
!= KCGI_OK
)
2142 return gw_kcgi_error(kerr
);
2144 if (gw_trans
->mime
== KMIME_APP_OCTET_STREAM
) {
2145 kerr
= khttp_head(gw_trans
->gw_req
,
2146 kresps
[KRESP_CONTENT_DISPOSITION
],
2147 "attachment; filename=%s", gw_trans
->repo_file
);
2148 if (kerr
!= KCGI_OK
)
2149 return gw_kcgi_error(kerr
);
2152 kerr
= khttp_body(gw_trans
->gw_req
);
2153 return gw_kcgi_error(kerr
);
2156 static const struct got_error
*
2157 gw_display_index(struct gw_trans
*gw_trans
)
2159 const struct got_error
*error
;
2160 enum kcgi_err kerr
= KCGI_OK
;
2162 /* catch early querystring errors */
2163 if (gw_trans
->error
)
2164 gw_trans
->action
= GW_ERR
;
2166 error
= gw_display_open(gw_trans
, KHTTP_200
, gw_trans
->mime
);
2170 kerr
= khtml_open(gw_trans
->gw_html_req
, gw_trans
->gw_req
, 0);
2171 if (kerr
!= KCGI_OK
)
2172 return gw_kcgi_error(kerr
);
2174 if (gw_trans
->action
!= GW_BLOB
) {
2175 kerr
= khttp_template(gw_trans
->gw_req
, gw_trans
->gw_tmpl
,
2176 gw_query_funcs
[gw_trans
->action
].template);
2177 if (kerr
!= KCGI_OK
) {
2178 khtml_close(gw_trans
->gw_html_req
);
2179 return gw_kcgi_error(kerr
);
2183 return gw_kcgi_error(khtml_close(gw_trans
->gw_html_req
));
2186 static const struct got_error
*
2187 gw_error(struct gw_trans
*gw_trans
)
2189 enum kcgi_err kerr
= KCGI_OK
;
2191 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_trans
->error
->msg
);
2193 return gw_kcgi_error(kerr
);
2197 gw_template(size_t key
, void *arg
)
2199 const struct got_error
*error
= NULL
;
2200 enum kcgi_err kerr
= KCGI_OK
;
2201 struct gw_trans
*gw_trans
= arg
;
2202 char *ati
= NULL
, *fic32
= NULL
, *fic16
= NULL
;
2203 char *swm
= NULL
, *spt
= NULL
, *css
= NULL
, *logo
= NULL
;
2205 if (asprintf(&ati
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2206 "/apple-touch-icon.png") == -1)
2208 if (asprintf(&fic32
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2209 "/favicon-32x32.png") == -1)
2211 if (asprintf(&fic16
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2212 "/favicon-16x16.png") == -1)
2214 if (asprintf(&swm
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2215 "/site.webmanifest") == -1)
2217 if (asprintf(&spt
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2218 "/safari-pinned-tab.svg") == -1)
2220 if (asprintf(&css
, "%s%s", gw_trans
->gw_conf
->got_www_path
,
2221 "/gotweb.css") == -1)
2223 if (asprintf(&logo
, "%s%s%s", gw_trans
->gw_conf
->got_www_path
,
2224 gw_trans
->gw_conf
->got_www_path
? "/" : "",
2225 gw_trans
->gw_conf
->got_logo
) == -1)
2230 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2231 KATTR_NAME
, "viewport",
2232 KATTR_CONTENT
, "initial-scale=.75, user-scalable=yes",
2234 if (kerr
!= KCGI_OK
)
2236 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2237 if (kerr
!= KCGI_OK
)
2239 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2240 KATTR_CHARSET
, "utf-8",
2242 if (kerr
!= KCGI_OK
)
2244 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2245 if (kerr
!= KCGI_OK
)
2247 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2248 KATTR_NAME
, "msapplication-TileColor",
2249 KATTR_CONTENT
, "#da532c", KATTR__MAX
);
2250 if (kerr
!= KCGI_OK
)
2252 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2253 if (kerr
!= KCGI_OK
)
2255 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_META
,
2256 KATTR_NAME
, "theme-color",
2257 KATTR_CONTENT
, "#ffffff", KATTR__MAX
);
2258 if (kerr
!= KCGI_OK
)
2260 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2261 if (kerr
!= KCGI_OK
)
2263 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2264 KATTR_REL
, "apple-touch-icon", KATTR_SIZES
, "180x180",
2265 KATTR_HREF
, ati
, KATTR__MAX
);
2266 if (kerr
!= KCGI_OK
)
2268 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2269 if (kerr
!= KCGI_OK
)
2271 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2272 KATTR_REL
, "icon", KATTR_TYPE
, "image/png", KATTR_SIZES
,
2273 "32x32", KATTR_HREF
, fic32
, KATTR__MAX
);
2274 if (kerr
!= KCGI_OK
)
2276 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2277 if (kerr
!= KCGI_OK
)
2279 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2280 KATTR_REL
, "icon", KATTR_TYPE
, "image/png", KATTR_SIZES
,
2281 "16x16", KATTR_HREF
, fic16
, KATTR__MAX
);
2282 if (kerr
!= KCGI_OK
)
2284 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2285 if (kerr
!= KCGI_OK
)
2287 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2288 KATTR_REL
, "manifest", KATTR_HREF
, swm
,
2290 if (kerr
!= KCGI_OK
)
2292 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2293 if (kerr
!= KCGI_OK
)
2295 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2296 KATTR_REL
, "mask-icon", KATTR_HREF
,
2298 if (kerr
!= KCGI_OK
)
2300 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2301 if (kerr
!= KCGI_OK
)
2303 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_LINK
,
2304 KATTR_REL
, "stylesheet", KATTR_TYPE
, "text/css",
2305 KATTR_HREF
, css
, KATTR__MAX
);
2306 if (kerr
!= KCGI_OK
)
2308 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2309 if (kerr
!= KCGI_OK
)
2313 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2314 KATTR_ID
, "got_link", KATTR__MAX
);
2315 if (kerr
!= KCGI_OK
)
2317 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
2318 KATTR_HREF
, gw_trans
->gw_conf
->got_logo_url
,
2319 KATTR_TARGET
, "_sotd", KATTR__MAX
);
2320 if (kerr
!= KCGI_OK
)
2322 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_IMG
,
2323 KATTR_SRC
, logo
, KATTR__MAX
);
2324 if (kerr
!= KCGI_OK
)
2326 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
2327 if (kerr
!= KCGI_OK
)
2330 case (TEMPL_SITEPATH
):
2331 error
= gw_output_site_link(gw_trans
);
2336 if (gw_trans
->gw_conf
->got_site_name
!= NULL
) {
2337 kerr
= khtml_puts(gw_trans
->gw_html_req
,
2338 gw_trans
->gw_conf
->got_site_name
);
2339 if (kerr
!= KCGI_OK
)
2343 case (TEMPL_SEARCH
):
2345 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
2346 "search", KATTR__MAX
);
2347 if (kerr
!= KCGI_OK
)
2349 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_FORM
,
2350 KATTR_METHOD
, "POST", KATTR__MAX
);
2351 if (kerr
!= KCGI_OK
)
2353 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_INPUT
, KATTR_ID
,
2354 "got-search", KATTR_NAME
, "got-search", KATTR_SIZE
, "15",
2355 KATTR_MAXLENGTH
, "50", KATTR__MAX
);
2356 if (kerr
!= KCGI_OK
)
2358 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_BUTTON
,
2360 if (kerr
!= KCGI_OK
)
2362 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Search");
2363 if (kerr
!= KCGI_OK
)
2365 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 4);
2366 if (kerr
!= KCGI_OK
)
2369 case(TEMPL_SITEOWNER
):
2370 if (gw_trans
->gw_conf
->got_site_owner
!= NULL
&&
2371 gw_trans
->gw_conf
->got_show_site_owner
) {
2372 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2373 KATTR_ID
, "site_owner_wrapper", KATTR__MAX
);
2374 if (kerr
!= KCGI_OK
)
2376 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2377 KATTR_ID
, "site_owner", KATTR__MAX
);
2378 if (kerr
!= KCGI_OK
)
2380 kerr
= khtml_puts(gw_trans
->gw_html_req
,
2381 gw_trans
->gw_conf
->got_site_owner
);
2382 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
2383 if (kerr
!= KCGI_OK
)
2387 case(TEMPL_CONTENT
):
2388 error
= gw_query_funcs
[gw_trans
->action
].func_main(gw_trans
);
2390 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2391 KATTR_ID
, "tmpl_err", KATTR__MAX
);
2392 if (kerr
!= KCGI_OK
)
2394 kerr
= khttp_printf(gw_trans
->gw_req
, "Error: %s",
2396 if (kerr
!= KCGI_OK
)
2398 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2399 if (kerr
!= KCGI_OK
)
2425 static const struct got_error
*
2426 gw_gen_commit_header(struct gw_trans
*gw_trans
, char *str1
, char *str2
)
2428 const struct got_error
*error
= NULL
;
2429 enum kcgi_err kerr
= KCGI_OK
;
2431 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2432 KATTR_ID
, "header_commit_title", KATTR__MAX
);
2433 if (kerr
!= KCGI_OK
)
2435 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Commit: ");
2436 if (kerr
!= KCGI_OK
)
2438 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2439 if (kerr
!= KCGI_OK
)
2441 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2442 KATTR_ID
, "header_commit", KATTR__MAX
);
2443 if (kerr
!= KCGI_OK
)
2445 kerr
= khtml_printf(gw_trans
->gw_html_req
, "%s ", str1
);
2446 if (kerr
!= KCGI_OK
)
2449 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_SPAN
,
2450 KATTR_ID
, "refs_str", KATTR__MAX
);
2451 if (kerr
!= KCGI_OK
)
2453 kerr
= khtml_printf(gw_trans
->gw_html_req
, "(%s)", str2
);
2454 if (kerr
!= KCGI_OK
)
2456 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2457 if (kerr
!= KCGI_OK
)
2460 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2462 if (error
== NULL
&& kerr
!= KCGI_OK
)
2463 error
= gw_kcgi_error(kerr
);
2467 static const struct got_error
*
2468 gw_gen_diff_header(struct gw_trans
*gw_trans
, char *str1
, char *str2
)
2470 const struct got_error
*error
= NULL
;
2471 enum kcgi_err kerr
= KCGI_OK
;
2473 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2474 KATTR_ID
, "header_diff_title", KATTR__MAX
);
2475 if (kerr
!= KCGI_OK
)
2477 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Diff: ");
2478 if (kerr
!= KCGI_OK
)
2480 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2481 if (kerr
!= KCGI_OK
)
2483 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2484 KATTR_ID
, "header_diff", KATTR__MAX
);
2485 if (kerr
!= KCGI_OK
)
2488 kerr
= khtml_puts(gw_trans
->gw_html_req
, str1
);
2489 if (kerr
!= KCGI_OK
)
2492 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_BR
, KATTR__MAX
);
2493 if (kerr
!= KCGI_OK
)
2495 kerr
= khtml_puts(gw_trans
->gw_html_req
, str2
);
2496 if (kerr
!= KCGI_OK
)
2498 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2500 if (error
== NULL
&& kerr
!= KCGI_OK
)
2501 error
= gw_kcgi_error(kerr
);
2505 static const struct got_error
*
2506 gw_gen_age_header(struct gw_trans
*gw_trans
, const char *str
)
2508 const struct got_error
*error
= NULL
;
2509 enum kcgi_err kerr
= KCGI_OK
;
2511 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2512 KATTR_ID
, "header_age_title", KATTR__MAX
);
2513 if (kerr
!= KCGI_OK
)
2515 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Date: ");
2516 if (kerr
!= KCGI_OK
)
2518 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2519 if (kerr
!= KCGI_OK
)
2521 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2522 KATTR_ID
, "header_age", KATTR__MAX
);
2523 if (kerr
!= KCGI_OK
)
2525 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2526 if (kerr
!= KCGI_OK
)
2528 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2530 if (error
== NULL
&& kerr
!= KCGI_OK
)
2531 error
= gw_kcgi_error(kerr
);
2535 static const struct got_error
*
2536 gw_gen_author_header(struct gw_trans
*gw_trans
, const char *str
)
2538 const struct got_error
*error
= NULL
;
2539 enum kcgi_err kerr
= KCGI_OK
;
2541 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2542 KATTR_ID
, "header_author_title", KATTR__MAX
);
2543 if (kerr
!= KCGI_OK
)
2545 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Author: ");
2546 if (kerr
!= KCGI_OK
)
2548 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2549 if (kerr
!= KCGI_OK
)
2551 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2552 KATTR_ID
, "header_author", KATTR__MAX
);
2553 if (kerr
!= KCGI_OK
)
2555 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2556 if (kerr
!= KCGI_OK
)
2558 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2560 if (error
== NULL
&& kerr
!= KCGI_OK
)
2561 error
= gw_kcgi_error(kerr
);
2565 static const struct got_error
*
2566 gw_gen_committer_header(struct gw_trans
*gw_trans
, const char *str
)
2568 const struct got_error
*error
= NULL
;
2569 enum kcgi_err kerr
= KCGI_OK
;
2571 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2572 KATTR_ID
, "header_committer_title", KATTR__MAX
);
2573 if (kerr
!= KCGI_OK
)
2575 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Committer: ");
2576 if (kerr
!= KCGI_OK
)
2578 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2579 if (kerr
!= KCGI_OK
)
2581 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2582 KATTR_ID
, "header_committer", KATTR__MAX
);
2583 if (kerr
!= KCGI_OK
)
2585 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2586 if (kerr
!= KCGI_OK
)
2588 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2590 if (error
== NULL
&& kerr
!= KCGI_OK
)
2591 error
= gw_kcgi_error(kerr
);
2595 static const struct got_error
*
2596 gw_gen_commit_msg_header(struct gw_trans
*gw_trans
, char *str
)
2598 const struct got_error
*error
= NULL
;
2599 enum kcgi_err kerr
= KCGI_OK
;
2601 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2602 KATTR_ID
, "header_commit_msg_title", KATTR__MAX
);
2603 if (kerr
!= KCGI_OK
)
2605 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Message: ");
2606 if (kerr
!= KCGI_OK
)
2608 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2609 if (kerr
!= KCGI_OK
)
2611 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2612 KATTR_ID
, "header_commit_msg", KATTR__MAX
);
2613 if (kerr
!= KCGI_OK
)
2615 kerr
= khttp_puts(gw_trans
->gw_req
, str
);
2616 if (kerr
!= KCGI_OK
)
2618 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2620 if (error
== NULL
&& kerr
!= KCGI_OK
)
2621 error
= gw_kcgi_error(kerr
);
2625 static const struct got_error
*
2626 gw_gen_tree_header(struct gw_trans
*gw_trans
, char *str
)
2628 const struct got_error
*error
= NULL
;
2629 enum kcgi_err kerr
= KCGI_OK
;
2631 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2632 KATTR_ID
, "header_tree_title", KATTR__MAX
);
2633 if (kerr
!= KCGI_OK
)
2635 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tree: ");
2636 if (kerr
!= KCGI_OK
)
2638 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2639 if (kerr
!= KCGI_OK
)
2641 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
2642 KATTR_ID
, "header_tree", KATTR__MAX
);
2643 if (kerr
!= KCGI_OK
)
2645 kerr
= khtml_puts(gw_trans
->gw_html_req
, str
);
2646 if (kerr
!= KCGI_OK
)
2648 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2650 if (error
== NULL
&& kerr
!= KCGI_OK
)
2651 error
= gw_kcgi_error(kerr
);
2655 static const struct got_error
*
2656 gw_get_repo_description(char **description
, struct gw_trans
*gw_trans
,
2659 const struct got_error
*error
= NULL
;
2661 char *d_file
= NULL
;
2665 *description
= NULL
;
2666 if (gw_trans
->gw_conf
->got_show_repo_description
== 0)
2669 if (asprintf(&d_file
, "%s/description", dir
) == -1)
2670 return got_error_from_errno("asprintf");
2672 f
= fopen(d_file
, "r");
2674 if (errno
== ENOENT
|| errno
== EACCES
)
2676 error
= got_error_from_errno2("fopen", d_file
);
2680 if (fseek(f
, 0, SEEK_END
) == -1) {
2681 error
= got_ferror(f
, GOT_ERR_IO
);
2686 error
= got_ferror(f
, GOT_ERR_IO
);
2689 if (fseek(f
, 0, SEEK_SET
) == -1) {
2690 error
= got_ferror(f
, GOT_ERR_IO
);
2693 *description
= calloc(len
+ 1, sizeof(**description
));
2694 if (*description
== NULL
) {
2695 error
= got_error_from_errno("calloc");
2699 n
= fread(*description
, 1, len
, f
);
2700 if (n
== 0 && ferror(f
))
2701 error
= got_ferror(f
, GOT_ERR_IO
);
2703 if (f
!= NULL
&& fclose(f
) == EOF
&& error
== NULL
)
2704 error
= got_error_from_errno("fclose");
2709 static const struct got_error
*
2710 gw_get_time_str(char **repo_age
, time_t committer_time
, int ref_tm
)
2714 char *years
= "years ago", *months
= "months ago";
2715 char *weeks
= "weeks ago", *days
= "days ago", *hours
= "hours ago";
2716 char *minutes
= "minutes ago", *seconds
= "seconds ago";
2717 char *now
= "right now";
2725 diff_time
= time(NULL
) - committer_time
;
2726 if (diff_time
> 60 * 60 * 24 * 365 * 2) {
2727 if (asprintf(repo_age
, "%lld %s",
2728 (diff_time
/ 60 / 60 / 24 / 365), years
) == -1)
2729 return got_error_from_errno("asprintf");
2730 } else if (diff_time
> 60 * 60 * 24 * (365 / 12) * 2) {
2731 if (asprintf(repo_age
, "%lld %s",
2732 (diff_time
/ 60 / 60 / 24 / (365 / 12)),
2734 return got_error_from_errno("asprintf");
2735 } else if (diff_time
> 60 * 60 * 24 * 7 * 2) {
2736 if (asprintf(repo_age
, "%lld %s",
2737 (diff_time
/ 60 / 60 / 24 / 7), weeks
) == -1)
2738 return got_error_from_errno("asprintf");
2739 } else if (diff_time
> 60 * 60 * 24 * 2) {
2740 if (asprintf(repo_age
, "%lld %s",
2741 (diff_time
/ 60 / 60 / 24), days
) == -1)
2742 return got_error_from_errno("asprintf");
2743 } else if (diff_time
> 60 * 60 * 2) {
2744 if (asprintf(repo_age
, "%lld %s",
2745 (diff_time
/ 60 / 60), hours
) == -1)
2746 return got_error_from_errno("asprintf");
2747 } else if (diff_time
> 60 * 2) {
2748 if (asprintf(repo_age
, "%lld %s", (diff_time
/ 60),
2750 return got_error_from_errno("asprintf");
2751 } else if (diff_time
> 2) {
2752 if (asprintf(repo_age
, "%lld %s", diff_time
,
2754 return got_error_from_errno("asprintf");
2756 if (asprintf(repo_age
, "%s", now
) == -1)
2757 return got_error_from_errno("asprintf");
2761 if (gmtime_r(&committer_time
, &tm
) == NULL
)
2762 return got_error_from_errno("gmtime_r");
2764 s
= asctime_r(&tm
, datebuf
);
2766 return got_error_from_errno("asctime_r");
2768 if (asprintf(repo_age
, "%s UTC", datebuf
) == -1)
2769 return got_error_from_errno("asprintf");
2775 static const struct got_error
*
2776 gw_get_repo_age(char **repo_age
, struct gw_trans
*gw_trans
, char *dir
,
2777 const char *refname
, int ref_tm
)
2779 const struct got_error
*error
= NULL
;
2780 struct got_repository
*repo
= NULL
;
2781 struct got_commit_object
*commit
= NULL
;
2782 struct got_reflist_head refs
;
2783 struct got_reflist_entry
*re
;
2784 time_t committer_time
= 0, cmp_time
= 0;
2789 if (gw_trans
->gw_conf
->got_show_repo_age
== 0)
2793 repo
= gw_trans
->repo
;
2795 error
= got_repo_open(&repo
, dir
, NULL
);
2800 error
= got_ref_list(&refs
, repo
, "refs/heads",
2801 got_ref_cmp_by_name
, NULL
);
2806 * Find the youngest branch tip in the repository, or the age of
2807 * the a specific branch tip if a name was provided by the caller.
2809 TAILQ_FOREACH(re
, &refs
, entry
) {
2810 struct got_object_id
*id
= NULL
;
2812 if (refname
&& strcmp(got_ref_get_name(re
->ref
), refname
) != 0)
2815 error
= got_ref_resolve(&id
, repo
, re
->ref
);
2819 error
= got_object_open_as_commit(&commit
, repo
, id
);
2825 got_object_commit_get_committer_time(commit
);
2826 got_object_commit_close(commit
);
2827 if (cmp_time
< committer_time
)
2828 cmp_time
= committer_time
;
2834 if (cmp_time
!= 0) {
2835 committer_time
= cmp_time
;
2836 error
= gw_get_time_str(repo_age
, committer_time
, ref_tm
);
2839 got_ref_list_free(&refs
);
2840 if (gw_trans
->repo
== NULL
) {
2841 const struct got_error
*close_err
= got_repo_close(repo
);
2848 static const struct got_error
*
2849 gw_output_diff(struct gw_trans
*gw_trans
, struct gw_header
*header
)
2851 const struct got_error
*error
;
2853 struct got_object_id
*id1
= NULL
, *id2
= NULL
;
2854 char *label1
= NULL
, *label2
= NULL
, *line
= NULL
;
2856 size_t linesize
= 0;
2858 enum kcgi_err kerr
= KCGI_OK
;
2864 if (header
->parent_id
!= NULL
&&
2865 strncmp(header
->parent_id
, "/dev/null", 9) != 0) {
2866 error
= got_repo_match_object_id(&id1
, &label1
,
2867 header
->parent_id
, GOT_OBJ_TYPE_ANY
,
2868 &header
->refs
, gw_trans
->repo
);
2873 error
= got_repo_match_object_id(&id2
, &label2
,
2874 header
->commit_id
, GOT_OBJ_TYPE_ANY
, &header
->refs
,
2879 error
= got_object_get_type(&obj_type
, gw_trans
->repo
, id2
);
2883 case GOT_OBJ_TYPE_BLOB
:
2884 error
= got_diff_objects_as_blobs(NULL
, NULL
, id1
, id2
,
2885 NULL
, NULL
, 3, 0, 0, gw_trans
->repo
, f
);
2887 case GOT_OBJ_TYPE_TREE
:
2888 error
= got_diff_objects_as_trees(NULL
, NULL
, id1
, id2
,
2889 "", "", 3, 0, 0, gw_trans
->repo
, f
);
2891 case GOT_OBJ_TYPE_COMMIT
:
2892 error
= got_diff_objects_as_commits(NULL
, NULL
, id1
, id2
,
2893 3, 0, 0, gw_trans
->repo
, f
);
2896 error
= got_error(GOT_ERR_OBJ_TYPE
);
2901 if (fseek(f
, 0, SEEK_SET
) == -1) {
2902 error
= got_ferror(f
, GOT_ERR_IO
);
2906 while ((linelen
= getline(&line
, &linesize
, f
)) != -1) {
2907 error
= gw_colordiff_line(gw_trans
, line
);
2910 /* XXX: KHTML_PRETTY breaks this */
2911 kerr
= khtml_puts(gw_trans
->gw_html_req
, line
);
2912 if (kerr
!= KCGI_OK
)
2914 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
2915 if (kerr
!= KCGI_OK
)
2918 if (linelen
== -1 && ferror(f
))
2919 error
= got_error_from_errno("getline");
2921 if (f
&& fclose(f
) == EOF
&& error
== NULL
)
2922 error
= got_error_from_errno("fclose");
2929 if (error
== NULL
&& kerr
!= KCGI_OK
)
2930 error
= gw_kcgi_error(kerr
);
2934 static const struct got_error
*
2935 gw_get_repo_owner(char **owner
, struct gw_trans
*gw_trans
, char *dir
)
2937 const struct got_error
*error
= NULL
, *close_err
;
2938 struct got_repository
*repo
;
2939 const char *gitconfig_owner
;
2943 if (gw_trans
->gw_conf
->got_show_repo_owner
== 0)
2946 error
= got_repo_open(&repo
, dir
, NULL
);
2949 gitconfig_owner
= got_repo_get_gitconfig_owner(repo
);
2950 if (gitconfig_owner
) {
2951 *owner
= strdup(gitconfig_owner
);
2953 error
= got_error_from_errno("strdup");
2955 close_err
= got_repo_close(repo
);
2961 static const struct got_error
*
2962 gw_get_clone_url(char **url
, struct gw_trans
*gw_trans
, char *dir
)
2964 const struct got_error
*error
= NULL
;
2966 char *d_file
= NULL
;
2972 if (asprintf(&d_file
, "%s/cloneurl", dir
) == -1)
2973 return got_error_from_errno("asprintf");
2975 f
= fopen(d_file
, "r");
2977 if (errno
!= ENOENT
&& errno
!= EACCES
)
2978 error
= got_error_from_errno2("fopen", d_file
);
2982 if (fseek(f
, 0, SEEK_END
) == -1) {
2983 error
= got_ferror(f
, GOT_ERR_IO
);
2988 error
= got_ferror(f
, GOT_ERR_IO
);
2991 if (fseek(f
, 0, SEEK_SET
) == -1) {
2992 error
= got_ferror(f
, GOT_ERR_IO
);
2996 *url
= calloc(len
+ 1, sizeof(**url
));
2998 error
= got_error_from_errno("calloc");
3002 n
= fread(*url
, 1, len
, f
);
3003 if (n
== 0 && ferror(f
))
3004 error
= got_ferror(f
, GOT_ERR_IO
);
3006 if (f
&& fclose(f
) == EOF
&& error
== NULL
)
3007 error
= got_error_from_errno("fclose");
3012 static const struct got_error
*
3013 gw_output_repo_tags(struct gw_trans
*gw_trans
, struct gw_header
*header
,
3014 int limit
, int tag_type
)
3016 const struct got_error
*error
= NULL
;
3017 struct got_reflist_head refs
;
3018 struct got_reflist_entry
*re
;
3020 char *id_str
= NULL
, *newline
, *href_commits
= NULL
;
3021 char *tag_commit0
= NULL
, *href_tag
= NULL
, *href_briefs
= NULL
;
3022 struct got_tag_object
*tag
= NULL
;
3023 enum kcgi_err kerr
= KCGI_OK
;
3024 int summary_header_displayed
= 0, chk_next
= 0;
3025 int tag_count
= 0, commit_found
= 0, c_cnt
= 0;
3029 error
= got_ref_list(&refs
, gw_trans
->repo
, "refs/tags",
3030 got_ref_cmp_tags
, gw_trans
->repo
);
3034 TAILQ_FOREACH(re
, &refs
, entry
) {
3035 const char *refname
;
3037 const char *tag_commit
;
3039 struct got_object_id
*id
;
3040 struct got_commit_object
*commit
= NULL
;
3042 refname
= got_ref_get_name(re
->ref
);
3043 if (strncmp(refname
, "refs/tags/", 10) != 0)
3047 error
= got_ref_resolve(&id
, gw_trans
->repo
, re
->ref
);
3051 error
= got_object_open_as_tag(&tag
, gw_trans
->repo
, id
);
3053 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
3057 /* "lightweight" tag */
3058 error
= got_object_open_as_commit(&commit
,
3059 gw_trans
->repo
, id
);
3064 tagger
= got_object_commit_get_committer(commit
);
3066 got_object_commit_get_committer_time(commit
);
3067 error
= got_object_id_str(&id_str
, id
);
3071 tagger
= got_object_tag_get_tagger(tag
);
3072 tagger_time
= got_object_tag_get_tagger_time(tag
);
3073 error
= got_object_id_str(&id_str
,
3074 got_object_tag_get_object_id(tag
));
3079 if (tag_type
== TAGFULL
&& strncmp(id_str
, header
->commit_id
,
3080 strlen(id_str
)) != 0)
3083 if (tag_type
== TAGBRIEF
&& gw_trans
->commit_id
&&
3084 commit_found
== 0 && strncmp(id_str
, gw_trans
->commit_id
,
3085 strlen(id_str
)) != 0)
3093 gw_trans
->next_id
= strdup(id_str
);
3094 if (gw_trans
->next_id
== NULL
)
3095 error
= got_error_from_errno("strdup");
3100 error
= got_object_commit_get_logmsg(&tag_commit0
,
3104 got_object_commit_close(commit
);
3106 tag_commit0
= strdup(got_object_tag_get_message(tag
));
3107 if (tag_commit0
== NULL
) {
3108 error
= got_error_from_errno("strdup");
3113 tag_commit
= tag_commit0
;
3114 while (*tag_commit
== '\n')
3119 newline
= strchr(tag_commit
, '\n');
3123 if (summary_header_displayed
== 0) {
3124 kerr
= khtml_attr(gw_trans
->gw_html_req
,
3125 KELEM_DIV
, KATTR_ID
,
3126 "summary_tags_title_wrapper", KATTR__MAX
);
3127 if (kerr
!= KCGI_OK
)
3129 kerr
= khtml_attr(gw_trans
->gw_html_req
,
3130 KELEM_DIV
, KATTR_ID
,
3131 "summary_tags_title", KATTR__MAX
);
3132 if (kerr
!= KCGI_OK
)
3134 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3136 if (kerr
!= KCGI_OK
)
3138 kerr
= khtml_closeelem(gw_trans
->gw_html_req
,
3140 if (kerr
!= KCGI_OK
)
3142 kerr
= khtml_attr(gw_trans
->gw_html_req
,
3143 KELEM_DIV
, KATTR_ID
,
3144 "summary_tags_content", KATTR__MAX
);
3145 if (kerr
!= KCGI_OK
)
3147 summary_header_displayed
= 1;
3150 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3151 KATTR_ID
, "tag_wrapper", KATTR__MAX
);
3152 if (kerr
!= KCGI_OK
)
3154 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3155 KATTR_ID
, "tag_age", KATTR__MAX
);
3156 if (kerr
!= KCGI_OK
)
3158 error
= gw_get_time_str(&age
, tagger_time
, TM_DIFF
);
3161 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3163 if (kerr
!= KCGI_OK
)
3165 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3166 if (kerr
!= KCGI_OK
)
3168 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3169 KATTR_ID
, "tag", KATTR__MAX
);
3170 if (kerr
!= KCGI_OK
)
3172 kerr
= khtml_puts(gw_trans
->gw_html_req
, refname
);
3173 if (kerr
!= KCGI_OK
)
3175 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3176 if (kerr
!= KCGI_OK
)
3178 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3179 KATTR_ID
, "tag_name", KATTR__MAX
);
3180 if (kerr
!= KCGI_OK
)
3183 href_tag
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
3184 gw_trans
->repo_name
, "action", "tag", "commit",
3186 if (href_tag
== NULL
) {
3187 error
= got_error_from_errno("khttp_urlpart");
3190 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3191 KATTR_HREF
, href_tag
, KATTR__MAX
);
3192 if (kerr
!= KCGI_OK
)
3194 kerr
= khtml_puts(gw_trans
->gw_html_req
, tag_commit
);
3195 if (kerr
!= KCGI_OK
)
3197 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
3198 if (kerr
!= KCGI_OK
)
3201 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3202 KATTR_ID
, "navs_wrapper", KATTR__MAX
);
3203 if (kerr
!= KCGI_OK
)
3205 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3206 KATTR_ID
, "navs", KATTR__MAX
);
3207 if (kerr
!= KCGI_OK
)
3210 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3211 KATTR_HREF
, href_tag
, KATTR__MAX
);
3212 if (kerr
!= KCGI_OK
)
3214 kerr
= khtml_puts(gw_trans
->gw_html_req
, "tag");
3215 if (kerr
!= KCGI_OK
)
3217 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3218 if (kerr
!= KCGI_OK
)
3221 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
3222 if (kerr
!= KCGI_OK
)
3225 href_briefs
= khttp_urlpart(NULL
, NULL
, "gotweb",
3226 "path", gw_trans
->repo_name
, "action", "briefs",
3227 "commit", id_str
, NULL
);
3228 if (href_briefs
== NULL
) {
3229 error
= got_error_from_errno("khttp_urlpart");
3232 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3233 KATTR_HREF
, href_briefs
, KATTR__MAX
);
3234 if (kerr
!= KCGI_OK
)
3236 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3238 if (kerr
!= KCGI_OK
)
3240 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3241 if (kerr
!= KCGI_OK
)
3244 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
3245 if (kerr
!= KCGI_OK
)
3248 href_commits
= khttp_urlpart(NULL
, NULL
, "gotweb",
3249 "path", gw_trans
->repo_name
, "action", "commits",
3250 "commit", id_str
, NULL
);
3251 if (href_commits
== NULL
) {
3252 error
= got_error_from_errno("khttp_urlpart");
3255 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
3256 KATTR_HREF
, href_commits
, KATTR__MAX
);
3257 if (kerr
!= KCGI_OK
)
3259 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commits");
3260 if (kerr
!= KCGI_OK
)
3262 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
3263 if (kerr
!= KCGI_OK
)
3266 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3267 KATTR_ID
, "dotted_line", KATTR__MAX
);
3268 if (kerr
!= KCGI_OK
)
3270 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
3271 if (kerr
!= KCGI_OK
)
3275 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3276 KATTR_ID
, "tag_info_date_title", KATTR__MAX
);
3277 if (kerr
!= KCGI_OK
)
3279 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tag Date:");
3280 if (kerr
!= KCGI_OK
)
3282 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3283 if (kerr
!= KCGI_OK
)
3285 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3286 KATTR_ID
, "tag_info_date", KATTR__MAX
);
3287 if (kerr
!= KCGI_OK
)
3289 error
= gw_get_time_str(&age
, tagger_time
, TM_LONG
);
3292 kerr
= khtml_puts(gw_trans
->gw_html_req
,
3294 if (kerr
!= KCGI_OK
)
3296 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3297 if (kerr
!= KCGI_OK
)
3300 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3301 KATTR_ID
, "tag_info_tagger_title", KATTR__MAX
);
3302 if (kerr
!= KCGI_OK
)
3304 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tagger:");
3305 if (kerr
!= KCGI_OK
)
3307 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3308 if (kerr
!= KCGI_OK
)
3310 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3311 KATTR_ID
, "tag_info_date", KATTR__MAX
);
3312 if (kerr
!= KCGI_OK
)
3314 kerr
= khtml_puts(gw_trans
->gw_html_req
, tagger
);
3315 if (kerr
!= KCGI_OK
)
3317 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3318 if (kerr
!= KCGI_OK
)
3321 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
3322 KATTR_ID
, "tag_info", KATTR__MAX
);
3323 if (kerr
!= KCGI_OK
)
3325 kerr
= khttp_puts(gw_trans
->gw_req
, tag_commit
);
3326 if (kerr
!= KCGI_OK
)
3332 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
3333 if (kerr
!= KCGI_OK
)
3336 if (limit
&& --limit
== 0)
3340 got_object_tag_close(tag
);
3353 href_commits
= NULL
;
3355 if (tag_count
== 0) {
3356 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3357 "summary_tags_title_wrapper", KATTR__MAX
);
3358 if (kerr
!= KCGI_OK
)
3360 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3361 "summary_tags_title", KATTR__MAX
);
3362 if (kerr
!= KCGI_OK
)
3364 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Tags");
3365 if (kerr
!= KCGI_OK
)
3367 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
3368 if (kerr
!= KCGI_OK
)
3370 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3371 "summary_tags_content", KATTR__MAX
);
3372 if (kerr
!= KCGI_OK
)
3374 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3375 "tags_info", KATTR__MAX
);
3376 if (kerr
!= KCGI_OK
)
3378 kerr
= khttp_puts(gw_trans
->gw_req
,
3379 "There are no tags for this repo.");
3380 if (kerr
!= KCGI_OK
)
3382 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
3387 TAILQ_FOREACH_REVERSE(re
, &refs
, got_reflist_head
, entry
) {
3388 const char *refname
;
3391 struct got_object_id
*id
;
3392 struct got_commit_object
*commit
= NULL
;
3394 refname
= got_ref_get_name(re
->ref
);
3395 if (strncmp(refname
, "refs/tags/", 10) != 0)
3399 error
= got_ref_resolve(&id
, gw_trans
->repo
, re
->ref
);
3403 error
= got_object_open_as_tag(&tag
, gw_trans
->repo
, id
);
3405 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
3409 /* "lightweight" tag */
3410 error
= got_object_open_as_commit(&commit
,
3411 gw_trans
->repo
, id
);
3416 tagger
= got_object_commit_get_committer(commit
);
3418 got_object_commit_get_committer_time(commit
);
3419 error
= got_object_id_str(&id_str
, id
);
3423 tagger
= got_object_tag_get_tagger(tag
);
3424 tagger_time
= got_object_tag_get_tagger_time(tag
);
3425 error
= got_object_id_str(&id_str
,
3426 got_object_tag_get_object_id(tag
));
3431 if (tag_type
== TAGFULL
&& strncmp(id_str
, header
->commit_id
,
3432 strlen(id_str
)) != 0)
3435 if (commit_found
== 0 && tag_type
== TAGBRIEF
&&
3436 gw_trans
->commit_id
!= NULL
&&
3437 strncmp(id_str
, gw_trans
->commit_id
, strlen(id_str
)) != 0)
3442 if (gw_trans
->commit_id
!= NULL
&&
3443 strcmp(id_str
, gw_trans
->commit_id
) != 0 &&
3444 (re
== TAILQ_FIRST(&refs
) ||
3445 c_cnt
== gw_trans
->gw_conf
->got_max_commits_display
)) {
3446 gw_trans
->prev_id
= strdup(id_str
);
3447 if (gw_trans
->prev_id
== NULL
) {
3448 error
= got_error_from_errno("strdup");
3457 got_object_tag_close(tag
);
3464 got_ref_list_free(&refs
);
3465 if (error
== NULL
&& kerr
!= KCGI_OK
)
3466 error
= gw_kcgi_error(kerr
);
3471 gw_free_header(struct gw_header
*header
)
3474 free(header
->author
);
3475 free(header
->committer
);
3476 free(header
->refs_str
);
3477 free(header
->commit_id
);
3478 free(header
->parent_id
);
3479 free(header
->tree_id
);
3480 free(header
->commit_msg
);
3483 static struct gw_header
*
3486 struct gw_header
*header
;
3488 header
= malloc(sizeof(*header
));
3492 header
->path
= NULL
;
3493 TAILQ_INIT(&header
->refs
);
3495 header
->refs_str
= NULL
;
3496 header
->commit_id
= NULL
;
3497 header
->committer
= NULL
;
3498 header
->author
= NULL
;
3499 header
->parent_id
= NULL
;
3500 header
->tree_id
= NULL
;
3501 header
->commit_msg
= NULL
;
3506 static const struct got_error
*
3507 gw_get_commits(struct gw_trans
* gw_trans
, struct gw_header
*header
,
3508 int limit
, struct got_object_id
*id
)
3510 const struct got_error
*error
= NULL
;
3511 struct got_commit_graph
*graph
= NULL
;
3512 struct got_commit_object
*commit
= NULL
;
3513 int chk_next
= 0, chk_multi
= 0, c_cnt
= 0, commit_found
= 0;
3514 struct gw_header
*t_header
= NULL
;
3516 error
= got_commit_graph_open(&graph
, header
->path
, 0);
3520 error
= got_commit_graph_iter_start(graph
, id
, gw_trans
->repo
, NULL
,
3526 error
= got_commit_graph_iter_next(&id
, graph
, gw_trans
->repo
,
3529 if (error
->code
== GOT_ERR_ITER_COMPLETED
)
3536 error
= got_object_open_as_commit(&commit
, gw_trans
->repo
, id
);
3539 if (limit
== 1 && chk_multi
== 0 &&
3540 gw_trans
->gw_conf
->got_max_commits_display
!= 1) {
3541 error
= gw_get_commit(gw_trans
, header
, commit
, id
);
3547 struct gw_header
*n_header
= NULL
;
3548 if ((n_header
= gw_init_header()) == NULL
) {
3549 error
= got_error_from_errno("malloc");
3552 TAILQ_INSERT_TAIL(&gw_trans
->gw_headers
, n_header
,
3554 error
= got_ref_list(&n_header
->refs
, gw_trans
->repo
,
3555 NULL
, got_ref_cmp_by_name
, NULL
);
3559 error
= gw_get_commit(gw_trans
, n_header
, commit
, id
);
3562 got_ref_list_free(&n_header
->refs
);
3564 if (gw_trans
->commit_id
!= NULL
) {
3565 if (strcmp(gw_trans
->commit_id
,
3566 n_header
->commit_id
) == 0)
3572 * check for one more commit before breaking,
3573 * so we know whether to navicate through gw_briefs
3574 * gw_commits and gw_summary
3576 if (chk_next
&& (gw_trans
->action
== GW_BRIEFS
||
3577 gw_trans
->action
== GW_COMMITS
||
3578 gw_trans
->action
== GW_SUMMARY
)) {
3579 gw_trans
->next_id
= strdup(n_header
->commit_id
);
3580 if (gw_trans
->next_id
== NULL
)
3581 error
= got_error_from_errno("strdup");
3582 TAILQ_REMOVE(&gw_trans
->gw_headers
, n_header
,
3588 if (commit_found
== 1 && (error
|| (limit
&& --limit
== 0))) {
3595 if (gw_trans
->prev_id
== NULL
&& gw_trans
->commit_id
!= NULL
&&
3596 (gw_trans
->action
== GW_BRIEFS
|| gw_trans
->action
== GW_COMMITS
)) {
3598 TAILQ_FOREACH_REVERSE(t_header
, &gw_trans
->gw_headers
,
3600 if (commit_found
== 0 &&
3601 strcmp(gw_trans
->commit_id
,
3602 t_header
->commit_id
) != 0)
3606 if (gw_trans
->commit_id
!= NULL
&&
3607 strcmp(gw_trans
->commit_id
,
3608 t_header
->commit_id
) != 0 &&
3609 (c_cnt
== gw_trans
->gw_conf
->got_max_commits_display
3611 TAILQ_FIRST(&gw_trans
->gw_headers
))) {
3612 gw_trans
->prev_id
= strdup(t_header
->commit_id
);
3613 if (gw_trans
->prev_id
== NULL
)
3614 error
= got_error_from_errno("strdup");
3622 got_object_commit_close(commit
);
3624 got_commit_graph_close(graph
);
3628 static const struct got_error
*
3629 gw_get_commit(struct gw_trans
*gw_trans
, struct gw_header
*header
,
3630 struct got_commit_object
*commit
, struct got_object_id
*id
)
3632 const struct got_error
*error
= NULL
;
3633 struct got_reflist_entry
*re
;
3634 struct got_object_id
*id2
= NULL
;
3635 struct got_object_qid
*parent_id
;
3636 char *commit_msg
= NULL
, *commit_msg0
;
3639 TAILQ_FOREACH(re
, &header
->refs
, entry
) {
3642 struct got_tag_object
*tag
= NULL
;
3643 struct got_object_id
*ref_id
;
3646 if (got_ref_is_symbolic(re
->ref
))
3649 name
= got_ref_get_name(re
->ref
);
3650 if (strncmp(name
, "refs/", 5) == 0)
3652 if (strncmp(name
, "got/", 4) == 0)
3654 if (strncmp(name
, "heads/", 6) == 0)
3656 if (strncmp(name
, "remotes/", 8) == 0) {
3658 s
= strstr(name
, "/" GOT_REF_HEAD
);
3659 if (s
!= NULL
&& s
[strlen(s
)] == '\0')
3662 error
= got_ref_resolve(&ref_id
, gw_trans
->repo
, re
->ref
);
3665 if (strncmp(name
, "tags/", 5) == 0) {
3666 error
= got_object_open_as_tag(&tag
, gw_trans
->repo
,
3669 if (error
->code
!= GOT_ERR_OBJ_TYPE
) {
3674 * Ref points at something other
3681 cmp
= got_object_id_cmp(tag
?
3682 got_object_tag_get_object_id(tag
) : ref_id
, id
);
3685 got_object_tag_close(tag
);
3688 s
= header
->refs_str
;
3689 if (asprintf(&header
->refs_str
, "%s%s%s", s
? s
: "",
3690 s
? ", " : "", name
) == -1) {
3691 error
= got_error_from_errno("asprintf");
3693 header
->refs_str
= NULL
;
3699 error
= got_object_id_str(&header
->commit_id
, id
);
3703 error
= got_object_id_str(&header
->tree_id
,
3704 got_object_commit_get_tree_id(commit
));
3708 if (gw_trans
->action
== GW_DIFF
) {
3709 parent_id
= STAILQ_FIRST(
3710 got_object_commit_get_parent_ids(commit
));
3711 if (parent_id
!= NULL
) {
3712 id2
= got_object_id_dup(parent_id
->id
);
3714 error
= got_object_id_str(&header
->parent_id
, id2
);
3719 header
->parent_id
= strdup("/dev/null");
3720 if (header
->parent_id
== NULL
) {
3721 error
= got_error_from_errno("strdup");
3727 header
->committer_time
=
3728 got_object_commit_get_committer_time(commit
);
3731 strdup(got_object_commit_get_author(commit
));
3732 if (header
->author
== NULL
) {
3733 error
= got_error_from_errno("strdup");
3737 strdup(got_object_commit_get_committer(commit
));
3738 if (header
->committer
== NULL
) {
3739 error
= got_error_from_errno("strdup");
3742 error
= got_object_commit_get_logmsg(&commit_msg0
, commit
);
3746 commit_msg
= commit_msg0
;
3747 while (*commit_msg
== '\n')
3750 header
->commit_msg
= strdup(commit_msg
);
3751 if (header
->commit_msg
== NULL
)
3752 error
= got_error_from_errno("strdup");
3757 static const struct got_error
*
3758 gw_get_header(struct gw_trans
*gw_trans
, struct gw_header
*header
, int limit
)
3760 const struct got_error
*error
= NULL
;
3761 char *in_repo_path
= NULL
;
3762 struct got_object_id
*id
= NULL
;
3763 struct got_reference
*ref
;
3765 error
= got_repo_open(&gw_trans
->repo
, gw_trans
->repo_path
, NULL
);
3769 if (gw_trans
->commit_id
== NULL
|| gw_trans
->action
== GW_COMMITS
||
3770 gw_trans
->action
== GW_BRIEFS
|| gw_trans
->action
== GW_SUMMARY
||
3771 gw_trans
->action
== GW_TAGS
) {
3772 error
= got_ref_open(&ref
, gw_trans
->repo
,
3773 gw_trans
->headref
, 0);
3777 error
= got_ref_resolve(&id
, gw_trans
->repo
, ref
);
3782 error
= got_ref_open(&ref
, gw_trans
->repo
,
3783 gw_trans
->commit_id
, 0);
3784 if (error
== NULL
) {
3786 error
= got_ref_resolve(&id
, gw_trans
->repo
, ref
);
3790 error
= got_object_get_type(&obj_type
, gw_trans
->repo
,
3794 if (obj_type
== GOT_OBJ_TYPE_TAG
) {
3795 struct got_tag_object
*tag
;
3796 error
= got_object_open_as_tag(&tag
,
3797 gw_trans
->repo
, id
);
3800 if (got_object_tag_get_object_type(tag
) !=
3801 GOT_OBJ_TYPE_COMMIT
) {
3802 got_object_tag_close(tag
);
3803 error
= got_error(GOT_ERR_OBJ_TYPE
);
3807 id
= got_object_id_dup(
3808 got_object_tag_get_object_id(tag
));
3810 error
= got_error_from_errno(
3811 "got_object_id_dup");
3812 got_object_tag_close(tag
);
3815 } else if (obj_type
!= GOT_OBJ_TYPE_COMMIT
) {
3816 error
= got_error(GOT_ERR_OBJ_TYPE
);
3820 error
= got_repo_match_object_id_prefix(&id
,
3821 gw_trans
->commit_id
, GOT_OBJ_TYPE_COMMIT
,
3827 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
,
3828 gw_trans
->repo_path
);
3833 header
->path
= strdup(in_repo_path
);
3834 if (header
->path
== NULL
) {
3835 error
= got_error_from_errno("strdup");
3840 error
= got_ref_list(&header
->refs
, gw_trans
->repo
, NULL
,
3841 got_ref_cmp_by_name
, NULL
);
3845 error
= gw_get_commits(gw_trans
, header
, limit
, id
);
3847 got_ref_list_free(&header
->refs
);
3857 char datebuf
[11]; /* YYYY-MM-DD + NUL */
3860 struct gw_blame_cb_args
{
3861 struct blame_line
*lines
;
3865 off_t
*line_offsets
;
3867 struct got_repository
*repo
;
3868 struct gw_trans
*gw_trans
;
3871 static const struct got_error
*
3872 gw_blame_cb(void *arg
, int nlines
, int lineno
, struct got_object_id
*id
)
3874 const struct got_error
*err
= NULL
;
3875 struct gw_blame_cb_args
*a
= arg
;
3876 struct blame_line
*bline
;
3878 size_t linesize
= 0;
3879 struct got_commit_object
*commit
= NULL
;
3882 time_t committer_time
;
3883 enum kcgi_err kerr
= KCGI_OK
;
3885 if (nlines
!= a
->nlines
||
3886 (lineno
!= -1 && lineno
< 1) || lineno
> a
->nlines
)
3887 return got_error(GOT_ERR_RANGE
);
3890 return NULL
; /* no change in this commit */
3892 /* Annotate this line. */
3893 bline
= &a
->lines
[lineno
- 1];
3894 if (bline
->annotated
)
3896 err
= got_object_id_str(&bline
->id_str
, id
);
3900 err
= got_object_open_as_commit(&commit
, a
->repo
, id
);
3904 bline
->committer
= strdup(got_object_commit_get_committer(commit
));
3905 if (bline
->committer
== NULL
) {
3906 err
= got_error_from_errno("strdup");
3910 committer_time
= got_object_commit_get_committer_time(commit
);
3911 if (gmtime_r(&committer_time
, &tm
) == NULL
)
3912 return got_error_from_errno("gmtime_r");
3913 if (strftime(bline
->datebuf
, sizeof(bline
->datebuf
), "%G-%m-%d",
3915 err
= got_error(GOT_ERR_NO_SPACE
);
3918 bline
->annotated
= 1;
3920 /* Print lines annotated so far. */
3921 bline
= &a
->lines
[a
->lineno_cur
- 1];
3922 if (!bline
->annotated
)
3925 offset
= a
->line_offsets
[a
->lineno_cur
- 1];
3926 if (fseeko(a
->f
, offset
, SEEK_SET
) == -1) {
3927 err
= got_error_from_errno("fseeko");
3931 while (bline
->annotated
) {
3932 char *smallerthan
, *at
, *nl
, *committer
;
3933 char *href_diff
= NULL
;
3936 if (getline(&line
, &linesize
, a
->f
) == -1) {
3938 err
= got_error_from_errno("getline");
3942 committer
= bline
->committer
;
3943 smallerthan
= strchr(committer
, '<');
3944 if (smallerthan
&& smallerthan
[1] != '\0')
3945 committer
= smallerthan
+ 1;
3946 at
= strchr(committer
, '@');
3949 len
= strlen(committer
);
3951 committer
[8] = '\0';
3953 nl
= strchr(line
, '\n');
3957 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3958 "blame_wrapper", KATTR__MAX
);
3959 if (kerr
!= KCGI_OK
)
3961 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3962 "blame_number", KATTR__MAX
);
3963 if (kerr
!= KCGI_OK
)
3965 kerr
= khtml_printf(a
->gw_trans
->gw_html_req
, "%.*d",
3966 a
->nlines_prec
, a
->lineno_cur
);
3967 if (kerr
!= KCGI_OK
)
3969 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
3970 if (kerr
!= KCGI_OK
)
3973 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3974 "blame_hash", KATTR__MAX
);
3975 if (kerr
!= KCGI_OK
)
3978 href_diff
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
3979 a
->gw_trans
->repo_name
, "action", "diff", "commit",
3980 bline
->id_str
, NULL
);
3981 if (href_diff
== NULL
) {
3982 err
= got_error_from_errno("khttp_urlpart");
3985 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_A
,
3986 KATTR_HREF
, href_diff
, KATTR__MAX
);
3987 if (kerr
!= KCGI_OK
)
3989 kerr
= khtml_printf(a
->gw_trans
->gw_html_req
, "%.8s",
3991 if (kerr
!= KCGI_OK
)
3993 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 2);
3994 if (kerr
!= KCGI_OK
)
3997 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
3998 "blame_date", KATTR__MAX
);
3999 if (kerr
!= KCGI_OK
)
4001 kerr
= khtml_puts(a
->gw_trans
->gw_html_req
, bline
->datebuf
);
4002 if (kerr
!= KCGI_OK
)
4004 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4005 if (kerr
!= KCGI_OK
)
4008 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4009 "blame_author", KATTR__MAX
);
4010 if (kerr
!= KCGI_OK
)
4012 kerr
= khtml_puts(a
->gw_trans
->gw_html_req
, committer
);
4013 if (kerr
!= KCGI_OK
)
4015 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4016 if (kerr
!= KCGI_OK
)
4019 kerr
= khtml_attr(a
->gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4020 "blame_code", KATTR__MAX
);
4021 if (kerr
!= KCGI_OK
)
4023 kerr
= khtml_puts(a
->gw_trans
->gw_html_req
, line
);
4024 if (kerr
!= KCGI_OK
)
4026 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4027 if (kerr
!= KCGI_OK
)
4030 kerr
= khtml_closeelem(a
->gw_trans
->gw_html_req
, 1);
4031 if (kerr
!= KCGI_OK
)
4035 bline
= &a
->lines
[a
->lineno_cur
- 1];
4041 got_object_commit_close(commit
);
4043 if (err
== NULL
&& kerr
!= KCGI_OK
)
4044 err
= gw_kcgi_error(kerr
);
4048 static const struct got_error
*
4049 gw_output_file_blame(struct gw_trans
*gw_trans
, struct gw_header
*header
)
4051 const struct got_error
*error
= NULL
;
4052 struct got_object_id
*obj_id
= NULL
;
4053 struct got_object_id
*commit_id
= NULL
;
4054 struct got_blob_object
*blob
= NULL
;
4055 char *path
= NULL
, *in_repo_path
= NULL
;
4056 struct gw_blame_cb_args bca
;
4060 if (asprintf(&path
, "%s%s%s",
4061 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4062 gw_trans
->repo_folder
? "/" : "",
4063 gw_trans
->repo_file
) == -1) {
4064 error
= got_error_from_errno("asprintf");
4068 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
, path
);
4072 error
= got_repo_match_object_id(&commit_id
, NULL
, gw_trans
->commit_id
,
4073 GOT_OBJ_TYPE_COMMIT
, &header
->refs
, gw_trans
->repo
);
4077 error
= got_object_id_by_path(&obj_id
, gw_trans
->repo
, commit_id
,
4082 if (obj_id
== NULL
) {
4083 error
= got_error(GOT_ERR_NO_OBJ
);
4087 error
= got_object_get_type(&obj_type
, gw_trans
->repo
, obj_id
);
4091 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
4092 error
= got_error(GOT_ERR_OBJ_TYPE
);
4096 error
= got_object_open_as_blob(&blob
, gw_trans
->repo
, obj_id
, 8192);
4100 bca
.f
= got_opentemp();
4101 if (bca
.f
== NULL
) {
4102 error
= got_error_from_errno("got_opentemp");
4105 error
= got_object_blob_dump_to_file(&filesize
, &bca
.nlines
,
4106 &bca
.line_offsets
, bca
.f
, blob
);
4107 if (error
|| bca
.nlines
== 0)
4110 /* Don't include \n at EOF in the blame line count. */
4111 if (bca
.line_offsets
[bca
.nlines
- 1] == filesize
)
4114 bca
.lines
= calloc(bca
.nlines
, sizeof(*bca
.lines
));
4115 if (bca
.lines
== NULL
) {
4116 error
= got_error_from_errno("calloc");
4120 bca
.nlines_prec
= 0;
4126 bca
.repo
= gw_trans
->repo
;
4127 bca
.gw_trans
= gw_trans
;
4129 error
= got_blame(in_repo_path
, commit_id
, gw_trans
->repo
, gw_blame_cb
,
4138 free(bca
.line_offsets
);
4139 for (i
= 0; i
< bca
.nlines
; i
++) {
4140 struct blame_line
*bline
= &bca
.lines
[i
];
4141 free(bline
->id_str
);
4142 free(bline
->committer
);
4145 if (bca
.f
&& fclose(bca
.f
) == EOF
&& error
== NULL
)
4146 error
= got_error_from_errno("fclose");
4149 got_object_blob_close(blob
);
4153 static const struct got_error
*
4154 gw_output_blob_buf(struct gw_trans
*gw_trans
, struct gw_header
*header
)
4156 const struct got_error
*error
= NULL
;
4157 struct got_object_id
*obj_id
= NULL
;
4158 struct got_object_id
*commit_id
= NULL
;
4159 struct got_blob_object
*blob
= NULL
;
4160 char *path
= NULL
, *in_repo_path
= NULL
;
4161 int obj_type
, set_mime
= 0;
4164 enum kcgi_err kerr
= KCGI_OK
;
4166 if (asprintf(&path
, "%s%s%s",
4167 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4168 gw_trans
->repo_folder
? "/" : "",
4169 gw_trans
->repo_file
) == -1) {
4170 error
= got_error_from_errno("asprintf");
4174 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
, path
);
4178 error
= got_repo_match_object_id(&commit_id
, NULL
, gw_trans
->commit_id
,
4179 GOT_OBJ_TYPE_COMMIT
, &header
->refs
, gw_trans
->repo
);
4183 error
= got_object_id_by_path(&obj_id
, gw_trans
->repo
, commit_id
,
4188 if (obj_id
== NULL
) {
4189 error
= got_error(GOT_ERR_NO_OBJ
);
4193 error
= got_object_get_type(&obj_type
, gw_trans
->repo
, obj_id
);
4197 if (obj_type
!= GOT_OBJ_TYPE_BLOB
) {
4198 error
= got_error(GOT_ERR_OBJ_TYPE
);
4202 error
= got_object_open_as_blob(&blob
, gw_trans
->repo
, obj_id
, 8192);
4206 hdrlen
= got_object_blob_get_hdrlen(blob
);
4208 error
= got_object_blob_read_block(&len
, blob
);
4211 buf
= got_object_blob_get_read_buf(blob
);
4214 * Skip blob object header first time around,
4215 * which also contains a zero byte.
4218 if (set_mime
== 0) {
4219 if (isbinary(buf
, len
- hdrlen
))
4220 gw_trans
->mime
= KMIME_APP_OCTET_STREAM
;
4222 gw_trans
->mime
= KMIME_TEXT_PLAIN
;
4224 error
= gw_display_index(gw_trans
);
4228 kerr
= khttp_write(gw_trans
->gw_req
, buf
, len
- hdrlen
);
4229 if (kerr
!= KCGI_OK
)
4239 got_object_blob_close(blob
);
4240 if (error
== NULL
&& kerr
!= KCGI_OK
)
4241 error
= gw_kcgi_error(kerr
);
4245 static const struct got_error
*
4246 gw_output_repo_tree(struct gw_trans
*gw_trans
, struct gw_header
*header
)
4248 const struct got_error
*error
= NULL
;
4249 struct got_object_id
*tree_id
= NULL
, *commit_id
= NULL
;
4250 struct got_tree_object
*tree
= NULL
;
4251 char *path
= NULL
, *in_repo_path
= NULL
;
4252 char *id_str
= NULL
;
4253 char *build_folder
= NULL
;
4254 char *href_blob
= NULL
, *href_blame
= NULL
;
4255 const char *class = NULL
;
4256 int nentries
, i
, class_flip
= 0;
4257 enum kcgi_err kerr
= KCGI_OK
;
4259 if (gw_trans
->repo_folder
!= NULL
) {
4260 path
= strdup(gw_trans
->repo_folder
);
4262 error
= got_error_from_errno("strdup");
4266 error
= got_repo_map_path(&in_repo_path
, gw_trans
->repo
,
4267 gw_trans
->repo_path
);
4271 path
= in_repo_path
;
4274 if (gw_trans
->commit_id
== NULL
) {
4275 struct got_reference
*head_ref
;
4276 error
= got_ref_open(&head_ref
, gw_trans
->repo
,
4277 gw_trans
->headref
, 0);
4280 error
= got_ref_resolve(&commit_id
, gw_trans
->repo
, head_ref
);
4283 got_ref_close(head_ref
);
4285 * gw_trans->commit_id was not parsed from the querystring
4286 * we hit this code path from gw_index, where we don't know the
4287 * commit values for the tree link yet, so set
4288 * gw_trans->commit_id here to continue further into the tree
4290 error
= got_object_id_str(&gw_trans
->commit_id
, commit_id
);
4295 error
= got_repo_match_object_id(&commit_id
, NULL
,
4296 gw_trans
->commit_id
, GOT_OBJ_TYPE_COMMIT
, &header
->refs
,
4302 error
= got_object_id_by_path(&tree_id
, gw_trans
->repo
, commit_id
,
4307 error
= got_object_open_as_tree(&tree
, gw_trans
->repo
, tree_id
);
4311 nentries
= got_object_tree_get_nentries(tree
);
4312 for (i
= 0; i
< nentries
; i
++) {
4313 struct got_tree_entry
*te
;
4314 const char *modestr
= "";
4317 te
= got_object_tree_get_entry(tree
, i
);
4319 error
= got_object_id_str(&id_str
, got_tree_entry_get_id(te
));
4323 mode
= got_tree_entry_get_mode(te
);
4324 if (got_object_tree_entry_is_submodule(te
))
4326 else if (S_ISLNK(mode
))
4328 else if (S_ISDIR(mode
))
4330 else if (mode
& S_IXUSR
)
4333 if (class_flip
== 0) {
4334 class = "back_lightgray";
4337 class = "back_white";
4341 if (S_ISDIR(mode
)) {
4342 if (asprintf(&build_folder
, "%s/%s",
4343 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4344 got_tree_entry_get_name(te
)) == -1) {
4345 error
= got_error_from_errno("asprintf");
4349 href_blob
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4350 gw_trans
->repo_name
, "action",
4351 gw_get_action_name(gw_trans
), "commit",
4352 gw_trans
->commit_id
, "folder", build_folder
, NULL
);
4353 if (href_blob
== NULL
) {
4354 error
= got_error_from_errno("khttp_urlpart");
4357 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4358 KATTR_ID
, "tree_wrapper", KATTR__MAX
);
4359 if (kerr
!= KCGI_OK
)
4361 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4362 KATTR_ID
, "tree_line", KATTR_CLASS
, class,
4364 if (kerr
!= KCGI_OK
)
4366 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4367 KATTR_HREF
, href_blob
, KATTR_CLASS
,
4368 "diff_directory", KATTR__MAX
);
4369 if (kerr
!= KCGI_OK
)
4371 kerr
= khtml_printf(gw_trans
->gw_html_req
, "%s%s",
4372 got_tree_entry_get_name(te
), modestr
);
4373 if (kerr
!= KCGI_OK
)
4375 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4376 if (kerr
!= KCGI_OK
)
4378 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4379 KATTR_ID
, "tree_line_blank", KATTR_CLASS
, class,
4381 if (kerr
!= KCGI_OK
)
4383 kerr
= khtml_entity(gw_trans
->gw_html_req
,
4385 if (kerr
!= KCGI_OK
)
4387 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4388 if (kerr
!= KCGI_OK
)
4391 href_blob
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4392 gw_trans
->repo_name
, "action", "blob", "commit",
4393 gw_trans
->commit_id
, "file",
4394 got_tree_entry_get_name(te
), "folder",
4395 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4397 if (href_blob
== NULL
) {
4398 error
= got_error_from_errno("khttp_urlpart");
4401 href_blame
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4402 gw_trans
->repo_name
, "action", "blame", "commit",
4403 gw_trans
->commit_id
, "file",
4404 got_tree_entry_get_name(te
), "folder",
4405 gw_trans
->repo_folder
? gw_trans
->repo_folder
: "",
4407 if (href_blame
== NULL
) {
4408 error
= got_error_from_errno("khttp_urlpart");
4411 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4412 KATTR_ID
, "tree_wrapper", KATTR__MAX
);
4413 if (kerr
!= KCGI_OK
)
4415 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4416 KATTR_ID
, "tree_line", KATTR_CLASS
, class,
4418 if (kerr
!= KCGI_OK
)
4420 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4421 KATTR_HREF
, href_blob
, KATTR__MAX
);
4422 if (kerr
!= KCGI_OK
)
4424 kerr
= khtml_printf(gw_trans
->gw_html_req
, "%s%s",
4425 got_tree_entry_get_name(te
), modestr
);
4426 if (kerr
!= KCGI_OK
)
4428 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4429 if (kerr
!= KCGI_OK
)
4431 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4432 KATTR_ID
, "tree_line_navs", KATTR_CLASS
, class,
4434 if (kerr
!= KCGI_OK
)
4437 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4438 KATTR_HREF
, href_blob
, KATTR__MAX
);
4439 if (kerr
!= KCGI_OK
)
4441 kerr
= khtml_puts(gw_trans
->gw_html_req
, "blob");
4442 if (kerr
!= KCGI_OK
)
4444 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4445 if (kerr
!= KCGI_OK
)
4448 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
4449 if (kerr
!= KCGI_OK
)
4452 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
,
4453 KATTR_HREF
, href_blame
, KATTR__MAX
);
4454 if (kerr
!= KCGI_OK
)
4456 kerr
= khtml_puts(gw_trans
->gw_html_req
, "blame");
4457 if (kerr
!= KCGI_OK
)
4460 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
4461 if (kerr
!= KCGI_OK
)
4469 build_folder
= NULL
;
4473 got_object_tree_close(tree
);
4480 if (error
== NULL
&& kerr
!= KCGI_OK
)
4481 error
= gw_kcgi_error(kerr
);
4485 static const struct got_error
*
4486 gw_output_repo_heads(struct gw_trans
*gw_trans
)
4488 const struct got_error
*error
= NULL
;
4489 struct got_reflist_head refs
;
4490 struct got_reflist_entry
*re
;
4491 char *age
= NULL
, *href_summary
= NULL
, *href_briefs
= NULL
;
4492 char *href_commits
= NULL
;
4493 enum kcgi_err kerr
= KCGI_OK
;
4497 error
= got_ref_list(&refs
, gw_trans
->repo
, "refs/heads",
4498 got_ref_cmp_by_name
, NULL
);
4502 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4503 KATTR_ID
, "summary_heads_title_wrapper", KATTR__MAX
);
4504 if (kerr
!= KCGI_OK
)
4506 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4507 KATTR_ID
, "summary_heads_title", KATTR__MAX
);
4508 if (kerr
!= KCGI_OK
)
4510 kerr
= khtml_puts(gw_trans
->gw_html_req
, "Heads");
4511 if (kerr
!= KCGI_OK
)
4513 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4514 if (kerr
!= KCGI_OK
)
4516 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4517 KATTR_ID
, "summary_heads_content", KATTR__MAX
);
4518 if (kerr
!= KCGI_OK
)
4521 TAILQ_FOREACH(re
, &refs
, entry
) {
4522 const char *refname
;
4524 if (got_ref_is_symbolic(re
->ref
))
4527 refname
= got_ref_get_name(re
->ref
);
4528 if (strncmp(refname
, "refs/heads/", 11) != 0)
4531 error
= gw_get_repo_age(&age
, gw_trans
, gw_trans
->gw_dir
->path
,
4536 if (strncmp(refname
, "refs/heads/", 11) == 0)
4539 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4540 KATTR_ID
, "heads_wrapper", KATTR__MAX
);
4541 if (kerr
!= KCGI_OK
)
4543 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4544 KATTR_ID
, "heads_age", KATTR__MAX
);
4545 if (kerr
!= KCGI_OK
)
4547 kerr
= khtml_puts(gw_trans
->gw_html_req
, age
? age
: "");
4548 if (kerr
!= KCGI_OK
)
4550 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4551 if (kerr
!= KCGI_OK
)
4553 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4554 KATTR_ID
, "heads_space", KATTR__MAX
);
4555 if (kerr
!= KCGI_OK
)
4557 kerr
= khtml_entity(gw_trans
->gw_html_req
, KENTITY_nbsp
);
4558 if (kerr
!= KCGI_OK
)
4560 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4561 if (kerr
!= KCGI_OK
)
4563 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
,
4564 KATTR_ID
, "head", KATTR__MAX
);
4565 if (kerr
!= KCGI_OK
)
4568 href_summary
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4569 gw_trans
->repo_name
, "action", "summary", "headref",
4571 if (href_summary
== NULL
) {
4572 error
= got_error_from_errno("khttp_urlpart");
4575 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4576 href_summary
, KATTR__MAX
);
4577 kerr
= khtml_puts(gw_trans
->gw_html_req
, refname
);
4578 if (kerr
!= KCGI_OK
)
4580 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
4581 if (kerr
!= KCGI_OK
)
4584 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4585 "navs_wrapper", KATTR__MAX
);
4586 if (kerr
!= KCGI_OK
)
4588 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4589 "navs", KATTR__MAX
);
4590 if (kerr
!= KCGI_OK
)
4593 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4594 href_summary
, KATTR__MAX
);
4595 if (kerr
!= KCGI_OK
)
4597 kerr
= khtml_puts(gw_trans
->gw_html_req
, "summary");
4598 if (kerr
!= KCGI_OK
)
4600 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4601 if (kerr
!= KCGI_OK
)
4604 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
4605 if (kerr
!= KCGI_OK
)
4608 href_briefs
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4609 gw_trans
->repo_name
, "action", "briefs", "headref",
4611 if (href_briefs
== NULL
) {
4612 error
= got_error_from_errno("khttp_urlpart");
4615 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4616 href_briefs
, KATTR__MAX
);
4617 if (kerr
!= KCGI_OK
)
4619 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commit briefs");
4620 if (kerr
!= KCGI_OK
)
4622 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4623 if (kerr
!= KCGI_OK
)
4626 kerr
= khtml_puts(gw_trans
->gw_html_req
, " | ");
4627 if (kerr
!= KCGI_OK
)
4630 href_commits
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4631 gw_trans
->repo_name
, "action", "commits", "headref",
4633 if (href_commits
== NULL
) {
4634 error
= got_error_from_errno("khttp_urlpart");
4637 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4638 href_commits
, KATTR__MAX
);
4639 if (kerr
!= KCGI_OK
)
4641 kerr
= khtml_puts(gw_trans
->gw_html_req
, "commits");
4642 if (kerr
!= KCGI_OK
)
4644 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 3);
4645 if (kerr
!= KCGI_OK
)
4648 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4649 "dotted_line", KATTR__MAX
);
4650 if (kerr
!= KCGI_OK
)
4652 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 2);
4653 if (kerr
!= KCGI_OK
)
4656 href_summary
= NULL
;
4660 href_commits
= NULL
;
4663 got_ref_list_free(&refs
);
4670 static const struct got_error
*
4671 gw_output_site_link(struct gw_trans
*gw_trans
)
4673 const struct got_error
*error
= NULL
;
4674 char *href_summary
= NULL
;
4675 enum kcgi_err kerr
= KCGI_OK
;
4677 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4678 "site_link", KATTR__MAX
);
4679 if (kerr
!= KCGI_OK
)
4681 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
, GOTWEB
,
4683 if (kerr
!= KCGI_OK
)
4685 kerr
= khtml_puts(gw_trans
->gw_html_req
,
4686 gw_trans
->gw_conf
->got_site_link
);
4687 if (kerr
!= KCGI_OK
)
4689 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4690 if (kerr
!= KCGI_OK
)
4693 if (gw_trans
->repo_name
!= NULL
) {
4694 kerr
= khtml_puts(gw_trans
->gw_html_req
, " / ");
4695 if (kerr
!= KCGI_OK
)
4698 href_summary
= khttp_urlpart(NULL
, NULL
, "gotweb", "path",
4699 gw_trans
->repo_name
, "action", "summary", NULL
);
4700 if (href_summary
== NULL
) {
4701 error
= got_error_from_errno("khttp_urlpart");
4704 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_A
, KATTR_HREF
,
4705 href_summary
, KATTR__MAX
);
4706 if (kerr
!= KCGI_OK
)
4708 kerr
= khtml_puts(gw_trans
->gw_html_req
, gw_trans
->repo_name
);
4709 if (kerr
!= KCGI_OK
)
4711 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4712 if (kerr
!= KCGI_OK
)
4714 kerr
= khtml_printf(gw_trans
->gw_html_req
, " / %s",
4715 gw_get_action_name(gw_trans
));
4716 if (kerr
!= KCGI_OK
)
4720 kerr
= khtml_closeelem(gw_trans
->gw_html_req
, 1);
4721 if (kerr
!= KCGI_OK
)
4725 if (error
== NULL
&& kerr
!= KCGI_OK
)
4726 error
= gw_kcgi_error(kerr
);
4730 static const struct got_error
*
4731 gw_colordiff_line(struct gw_trans
*gw_trans
, char *buf
)
4733 const struct got_error
*error
= NULL
;
4735 enum kcgi_err kerr
= KCGI_OK
;
4737 if (strncmp(buf
, "-", 1) == 0)
4738 color
= "diff_minus";
4739 else if (strncmp(buf
, "+", 1) == 0)
4740 color
= "diff_plus";
4741 else if (strncmp(buf
, "@@", 2) == 0)
4742 color
= "diff_chunk_header";
4743 else if (strncmp(buf
, "@@", 2) == 0)
4744 color
= "diff_chunk_header";
4745 else if (strncmp(buf
, "commit +", 8) == 0)
4746 color
= "diff_meta";
4747 else if (strncmp(buf
, "commit -", 8) == 0)
4748 color
= "diff_meta";
4749 else if (strncmp(buf
, "blob +", 6) == 0)
4750 color
= "diff_meta";
4751 else if (strncmp(buf
, "blob -", 6) == 0)
4752 color
= "diff_meta";
4753 else if (strncmp(buf
, "file +", 6) == 0)
4754 color
= "diff_meta";
4755 else if (strncmp(buf
, "file -", 6) == 0)
4756 color
= "diff_meta";
4757 else if (strncmp(buf
, "from:", 5) == 0)
4758 color
= "diff_author";
4759 else if (strncmp(buf
, "via:", 4) == 0)
4760 color
= "diff_author";
4761 else if (strncmp(buf
, "date:", 5) == 0)
4762 color
= "diff_date";
4763 kerr
= khtml_attr(gw_trans
->gw_html_req
, KELEM_DIV
, KATTR_ID
,
4764 "diff_line", KATTR_CLASS
, color
? color
: "", KATTR__MAX
);
4765 if (error
== NULL
&& kerr
!= KCGI_OK
)
4766 error
= gw_kcgi_error(kerr
);
4771 main(int argc
, char *argv
[])
4773 const struct got_error
*error
= NULL
, *error2
= NULL
;
4774 struct gw_trans
*gw_trans
;
4775 struct gw_dir
*dir
= NULL
, *tdir
;
4776 const char *page
= "index";
4777 enum kcgi_err kerr
= KCGI_OK
;
4779 if ((gw_trans
= malloc(sizeof(struct gw_trans
))) == NULL
)
4782 if ((gw_trans
->gw_req
= malloc(sizeof(struct kreq
))) == NULL
)
4785 if ((gw_trans
->gw_html_req
= malloc(sizeof(struct khtmlreq
))) == NULL
)
4788 if ((gw_trans
->gw_tmpl
= malloc(sizeof(struct ktemplate
))) == NULL
)
4791 kerr
= khttp_parse(gw_trans
->gw_req
, gw_keys
, KEY__ZMAX
, &page
, 1, 0);
4792 if (kerr
!= KCGI_OK
) {
4793 error
= gw_kcgi_error(kerr
);
4797 TAILQ_INIT(&gw_trans
->gw_dirs
);
4798 TAILQ_INIT(&gw_trans
->gw_headers
);
4800 gw_trans
->action
= -1;
4802 gw_trans
->repos_total
= 0;
4803 gw_trans
->repo_path
= NULL
;
4804 gw_trans
->commit_id
= NULL
;
4805 gw_trans
->next_id
= NULL
;
4806 gw_trans
->prev_id
= NULL
;
4807 gw_trans
->headref
= GOT_REF_HEAD
;
4808 gw_trans
->mime
= KMIME_TEXT_HTML
;
4809 gw_trans
->gw_tmpl
->key
= gw_templs
;
4810 gw_trans
->gw_tmpl
->keysz
= TEMPL__MAX
;
4811 gw_trans
->gw_tmpl
->arg
= gw_trans
;
4812 gw_trans
->gw_tmpl
->cb
= gw_template
;
4814 error
= parse_gotweb_config(&gw_trans
->gw_conf
, GOTWEB_CONF
);
4818 error
= gw_parse_querystring(gw_trans
);
4822 if (gw_trans
->action
== GW_BLOB
)
4823 error
= gw_blob(gw_trans
);
4825 error
= gw_display_index(gw_trans
);
4827 if (gw_trans
->repo
) {
4828 const struct got_error
*close_err
;
4829 close_err
= got_repo_close(gw_trans
->repo
);
4834 gw_trans
->error
= error
;
4835 gw_trans
->action
= GW_ERR
;
4836 error2
= gw_display_open(gw_trans
, KHTTP_200
, gw_trans
->mime
);
4838 goto cleanup
; /* we can't display an error page */
4839 kerr
= khtml_open(gw_trans
->gw_html_req
, gw_trans
->gw_req
, 0);
4840 if (kerr
!= KCGI_OK
)
4841 goto cleanup
; /* we can't display an error page */
4842 kerr
= khttp_template(gw_trans
->gw_req
, gw_trans
->gw_tmpl
,
4843 gw_query_funcs
[gw_trans
->action
].template);
4844 if (kerr
!= KCGI_OK
) {
4845 khtml_close(gw_trans
->gw_html_req
);
4846 goto cleanup
; /* we can't display an error page */
4851 free(gw_trans
->gw_conf
->got_repos_path
);
4852 free(gw_trans
->gw_conf
->got_www_path
);
4853 free(gw_trans
->gw_conf
->got_site_name
);
4854 free(gw_trans
->gw_conf
->got_site_owner
);
4855 free(gw_trans
->gw_conf
->got_site_link
);
4856 free(gw_trans
->gw_conf
->got_logo
);
4857 free(gw_trans
->gw_conf
->got_logo_url
);
4858 free(gw_trans
->gw_conf
);
4859 free(gw_trans
->commit_id
);
4860 free(gw_trans
->next_id
);
4861 free(gw_trans
->prev_id
);
4862 free(gw_trans
->repo_path
);
4863 TAILQ_FOREACH_SAFE(dir
, &gw_trans
->gw_dirs
, entry
, tdir
) {
4865 free(dir
->description
);
4872 khttp_free(gw_trans
->gw_req
);