1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
/* ui-ci.c: redirect to an external CI system
*
* Copyright (C) 2006-2025 cgit Development Team <cgit@lists.zx2c4.com>
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#define USE_THE_REPOSITORY_VARIABLE
#include "cgit.h"
#include "ui-ci.h"
#include "html.h"
#include "ui-shared.h"
/* Memoized outcome of resolving the ci url for this request: -1 while
* unresolved, 0 when there is no ci url for the current ref, and 1 when
* ci_url holds the url to redirect to.
*/
static int ci_resolved = -1;
static char *ci_url;
static int ref_is_tag(const char *ref)
{
struct strbuf fullref = STRBUF_INIT;
struct object_id oid;
int is_tag;
strbuf_addf(&fullref, "refs/tags/%s", ref);
is_tag = !repo_get_oid(the_repository, fullref.buf, &oid);
strbuf_release(&fullref);
return is_tag;
}
/* Append the repository url with any ".git" suffix removed and all
* slashes replaced by dashes, e.g. "sayauz/web.git" becomes
* "sayauz-web".
*/
static void add_repo_slug(struct strbuf *buf, const char *url)
{
size_t len, i;
if (!strip_suffix(url, ".git", &len))
len = strlen(url);
for (i = 0; i < len; i++)
strbuf_addch(buf, url[i] == '/' ? '-' : url[i]);
}
static char *expand_ci_url(const char *template)
{
const char *p, *rest;
struct strbuf buf = STRBUF_INIT;
for (p = template; *p; p++) {
if (*p != '$') {
strbuf_addch(&buf, *p);
continue;
}
if (skip_prefix(p, "$ref", &rest))
strbuf_addstr(&buf, ctx.qry.head);
else if (skip_prefix(p, "$repo", &rest))
strbuf_addstr(&buf, ctx.repo->url);
else if (skip_prefix(p, "$slug", &rest))
add_repo_slug(&buf, ctx.repo->url);
else if (skip_prefix(p, "$$", &rest))
strbuf_addch(&buf, '$');
else {
strbuf_addch(&buf, '$');
continue;
}
p = rest - 1;
}
return strbuf_detach(&buf, NULL);
}
static void resolve_ci_url(void)
{
const char *template;
if (ci_resolved != -1)
return;
ci_resolved = 0;
if (!ctx.repo || !ctx.qry.head || !cgit_have_repository())
return;
if (ref_is_tag(ctx.qry.head))
template = ctx.repo->ci_tag_url;
else
template = ctx.repo->ci_branch_url;
if (!template)
template = ctx.repo->ci_url;
if (!template)
return;
ci_url = expand_ci_url(template);
ci_resolved = 1;
}
int cgit_ci_available(void)
{
resolve_ci_url();
return ci_resolved == 1;
}
void cgit_print_ci(void)
{
resolve_ci_url();
if (ci_resolved != 1) {
cgit_print_error_page(404, "Not found",
"No ci url available for %s",
ctx.qry.head);
return;
}
/* The expanded template is emitted verbatim rather than through
* cgit_redirect(), which percent-encodes characters such as '?',
* '=' and '%' that are meaningful in a ci url. Refuse anything
* which could be used to smuggle in extra response headers.
*/
if (strpbrk(ci_url, "\r\n")) {
cgit_print_error_page(500, "Internal server error",
"Malformed ci url");
return;
}
htmlf("Status: 302 Found\nLocation: %s\n\n", ci_url);
}
|