Raw
1 #include "git-compat-util.h"
2 #include "hex-ll.h"
3 #include "strbuf.h"
4 #include "url.h"
5
6 int is_rfc3986_unreserved(char ch)
7 {
8 return isalnum(ch) ||
9 ch == '-' || ch == '_' || ch == '.' || ch == '~';
10 }
11
12 int is_casefolding_rfc3986_unreserved(char c)
13 {
14 return (c >= 'a' && c <= 'z') ||
15 (c >= '0' && c <= '9') ||
16 c == '-' || c == '.' || c == '_' || c == '~';
17 }
18
19 int is_urlschemechar(int first_flag, int ch)
20 {
21 /*
22 * The set of valid URL schemes, as per STD66 (RFC3986) is
23 * '[A-Za-z][A-Za-z0-9+.-]*'. But use slightly looser check
24 * of '[A-Za-z0-9][A-Za-z0-9+.-]*' because earlier version
25 * of check used '[A-Za-z0-9]+' so not to break any remote
26 * helpers.
27 */
28 int alphanumeric, special;
29 alphanumeric = ch > 0 && isalnum(ch);
30 special = ch == '+' || ch == '-' || ch == '.';
31 return alphanumeric || (!first_flag && special);
32 }
33
34 int is_url(const char *url)
35 {
36 /* Is "scheme" part reasonable? */
37 if (!url || !is_urlschemechar(1, *url++))
38 return 0;
39 while (*url && *url != ':') {
40 if (!is_urlschemechar(0, *url++))
41 return 0;
42 }
43 /* We've seen "scheme"; we want colon-slash-slash */
44 return (url[0] == ':' && url[1] == '/' && url[2] == '/');
45 }
46
47 static char *url_decode_internal(const char **query, int len,
48 const char *stop_at, struct strbuf *out,
49 int decode_plus)
50 {
51 const char *q = *query;
52
53 while (len) {
54 unsigned char c = *q;
55
56 if (!c)
57 break;
58 if (stop_at && strchr(stop_at, c)) {
59 q++;
60 len--;
61 break;
62 }
63
64 if (c == '%' && (len < 0 || len >= 3)) {
65 int val = hex2chr(q + 1);
66 if (0 < val) {
67 strbuf_addch(out, val);
68 q += 3;
69 len -= 3;
70 continue;
71 }
72 }
73
74 if (decode_plus && c == '+')
75 strbuf_addch(out, ' ');
76 else
77 strbuf_addch(out, c);
78 q++;
79 len--;
80 }
81 *query = q;
82 return strbuf_detach(out, NULL);
83 }
84
85 char *url_decode(const char *url)
86 {
87 return url_decode_mem(url, strlen(url));
88 }
89
90 char *url_decode_mem(const char *url, int len)
91 {
92 struct strbuf out = STRBUF_INIT;
93 const char *colon = memchr(url, ':', len);
94
95 /* Skip protocol part if present */
96 if (colon && url < colon) {
97 strbuf_add(&out, url, colon - url);
98 len -= colon - url;
99 url = colon;
100 }
101 return url_decode_internal(&url, len, NULL, &out, 0);
102 }
103
104 char *url_percent_decode(const char *encoded)
105 {
106 struct strbuf out = STRBUF_INIT;
107 return url_decode_internal(&encoded, strlen(encoded), NULL, &out, 0);
108 }
109
110 char *url_decode_parameter_name(const char **query)
111 {
112 struct strbuf out = STRBUF_INIT;
113 return url_decode_internal(query, -1, "&=", &out, 1);
114 }
115
116 char *url_decode_parameter_value(const char **query)
117 {
118 struct strbuf out = STRBUF_INIT;
119 return url_decode_internal(query, -1, "&", &out, 1);
120 }
121
122 void end_url_with_slash(struct strbuf *buf, const char *url)
123 {
124 strbuf_addstr(buf, url);
125 strbuf_complete(buf, '/');
126 }
127
128 void str_end_url_with_slash(const char *url, char **dest)
129 {
130 struct strbuf buf = STRBUF_INIT;
131 end_url_with_slash(&buf, url);
132 free(*dest);
133 *dest = strbuf_detach(&buf, NULL);
134 }
135
136 int url_is_local_not_ssh(const char *url)
137 {
138 const char *colon = strchr(url, ':');
139 const char *slash = strchr(url, '/');
140 return !colon || (slash && slash < colon) ||
141 (has_dos_drive_prefix(url) && is_valid_path(url));
142 }
143
144 enum url_scheme url_get_scheme(const char *name)
145 {
146 if (!strcmp(name, "ssh"))
147 return URL_SCHEME_SSH;
148 if (!strcmp(name, "git"))
149 return URL_SCHEME_GIT;
150 if (!strcmp(name, "git+ssh")) /* deprecated - do not use */
151 return URL_SCHEME_SSH;
152 if (!strcmp(name, "ssh+git")) /* deprecated - do not use */
153 return URL_SCHEME_SSH;
154 if (!strcmp(name, "file"))
155 return URL_SCHEME_FILE;
156 return URL_SCHEME_UNKNOWN;
157 }