File Coverage

deps/libgit2/src/util/net.c
Criterion Covered Total %
statement 0 417 0.0
branch 0 336 0.0
condition n/a
subroutine n/a
pod n/a
total 0 753 0.0


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
4             * This file is part of libgit2, distributed under the GNU GPL v2 with
5             * a Linking Exception. For full terms see the included COPYING file.
6             */
7              
8             #include "net.h"
9              
10             #include
11              
12             #include "posix.h"
13             #include "str.h"
14             #include "http_parser.h"
15             #include "runtime.h"
16              
17             #define DEFAULT_PORT_HTTP "80"
18             #define DEFAULT_PORT_HTTPS "443"
19             #define DEFAULT_PORT_GIT "9418"
20             #define DEFAULT_PORT_SSH "22"
21              
22 0           bool git_net_str_is_url(const char *str)
23             {
24             const char *c;
25              
26 0 0         for (c = str; *c; c++) {
27 0 0         if (*c == ':' && *(c+1) == '/' && *(c+2) == '/')
    0          
    0          
28 0           return true;
29              
30 0 0         if ((*c < 'a' || *c > 'z') &&
    0          
    0          
31 0 0         (*c < 'A' || *c > 'Z') &&
    0          
32 0 0         (*c < '0' || *c > '9') &&
    0          
33 0 0         (*c != '+' && *c != '-' && *c != '.'))
    0          
34 0           break;
35             }
36              
37 0           return false;
38             }
39              
40 0           static const char *default_port_for_scheme(const char *scheme)
41             {
42 0 0         if (strcmp(scheme, "http") == 0)
43 0           return DEFAULT_PORT_HTTP;
44 0 0         else if (strcmp(scheme, "https") == 0)
45 0           return DEFAULT_PORT_HTTPS;
46 0 0         else if (strcmp(scheme, "git") == 0)
47 0           return DEFAULT_PORT_GIT;
48 0 0         else if (strcmp(scheme, "ssh") == 0 ||
    0          
49 0 0         strcmp(scheme, "ssh+git") == 0 ||
50 0           strcmp(scheme, "git+ssh") == 0)
51 0           return DEFAULT_PORT_SSH;
52              
53 0           return NULL;
54             }
55              
56 0           int git_net_url_dup(git_net_url *out, git_net_url *in)
57             {
58 0 0         if (in->scheme) {
59 0           out->scheme = git__strdup(in->scheme);
60 0 0         GIT_ERROR_CHECK_ALLOC(out->scheme);
61             }
62              
63 0 0         if (in->host) {
64 0           out->host = git__strdup(in->host);
65 0 0         GIT_ERROR_CHECK_ALLOC(out->host);
66             }
67              
68 0 0         if (in->port) {
69 0           out->port = git__strdup(in->port);
70 0 0         GIT_ERROR_CHECK_ALLOC(out->port);
71             }
72              
73 0 0         if (in->path) {
74 0           out->path = git__strdup(in->path);
75 0 0         GIT_ERROR_CHECK_ALLOC(out->path);
76             }
77              
78 0 0         if (in->query) {
79 0           out->query = git__strdup(in->query);
80 0 0         GIT_ERROR_CHECK_ALLOC(out->query);
81             }
82              
83 0 0         if (in->username) {
84 0           out->username = git__strdup(in->username);
85 0 0         GIT_ERROR_CHECK_ALLOC(out->username);
86             }
87              
88 0 0         if (in->password) {
89 0           out->password = git__strdup(in->password);
90 0 0         GIT_ERROR_CHECK_ALLOC(out->password);
91             }
92              
93 0           return 0;
94             }
95              
96 0           int git_net_url_parse(git_net_url *url, const char *given)
97             {
98 0           struct http_parser_url u = {0};
99             bool has_scheme, has_host, has_port, has_path, has_query, has_userinfo;
100 0           git_str scheme = GIT_STR_INIT,
101 0           host = GIT_STR_INIT,
102 0           port = GIT_STR_INIT,
103 0           path = GIT_STR_INIT,
104 0           username = GIT_STR_INIT,
105 0           password = GIT_STR_INIT,
106 0           query = GIT_STR_INIT;
107 0           int error = GIT_EINVALIDSPEC;
108              
109 0 0         if (http_parser_parse_url(given, strlen(given), false, &u)) {
110 0           git_error_set(GIT_ERROR_NET, "malformed URL '%s'", given);
111 0           goto done;
112             }
113              
114 0           has_scheme = !!(u.field_set & (1 << UF_SCHEMA));
115 0           has_host = !!(u.field_set & (1 << UF_HOST));
116 0           has_port = !!(u.field_set & (1 << UF_PORT));
117 0           has_path = !!(u.field_set & (1 << UF_PATH));
118 0           has_query = !!(u.field_set & (1 << UF_QUERY));
119 0           has_userinfo = !!(u.field_set & (1 << UF_USERINFO));
120              
121 0 0         if (has_scheme) {
122 0           const char *url_scheme = given + u.field_data[UF_SCHEMA].off;
123 0           size_t url_scheme_len = u.field_data[UF_SCHEMA].len;
124 0           git_str_put(&scheme, url_scheme, url_scheme_len);
125 0           git__strntolower(scheme.ptr, scheme.size);
126             } else {
127 0           git_error_set(GIT_ERROR_NET, "malformed URL '%s'", given);
128 0           goto done;
129             }
130              
131 0 0         if (has_host) {
132 0           const char *url_host = given + u.field_data[UF_HOST].off;
133 0           size_t url_host_len = u.field_data[UF_HOST].len;
134 0           git_str_decode_percent(&host, url_host, url_host_len);
135             }
136              
137 0 0         if (has_port) {
138 0           const char *url_port = given + u.field_data[UF_PORT].off;
139 0           size_t url_port_len = u.field_data[UF_PORT].len;
140 0           git_str_put(&port, url_port, url_port_len);
141             } else {
142 0           const char *default_port = default_port_for_scheme(scheme.ptr);
143              
144 0 0         if (default_port == NULL) {
145 0           git_error_set(GIT_ERROR_NET, "unknown scheme for URL '%s'", given);
146 0           goto done;
147             }
148              
149 0           git_str_puts(&port, default_port);
150             }
151              
152 0 0         if (has_path) {
153 0           const char *url_path = given + u.field_data[UF_PATH].off;
154 0           size_t url_path_len = u.field_data[UF_PATH].len;
155 0           git_str_put(&path, url_path, url_path_len);
156             } else {
157 0           git_str_puts(&path, "/");
158             }
159              
160 0 0         if (has_query) {
161 0           const char *url_query = given + u.field_data[UF_QUERY].off;
162 0           size_t url_query_len = u.field_data[UF_QUERY].len;
163 0           git_str_decode_percent(&query, url_query, url_query_len);
164             }
165              
166 0 0         if (has_userinfo) {
167 0           const char *url_userinfo = given + u.field_data[UF_USERINFO].off;
168 0           size_t url_userinfo_len = u.field_data[UF_USERINFO].len;
169 0           const char *colon = memchr(url_userinfo, ':', url_userinfo_len);
170              
171 0 0         if (colon) {
172 0           const char *url_username = url_userinfo;
173 0           size_t url_username_len = colon - url_userinfo;
174 0           const char *url_password = colon + 1;
175 0           size_t url_password_len = url_userinfo_len - (url_username_len + 1);
176              
177 0           git_str_decode_percent(&username, url_username, url_username_len);
178 0           git_str_decode_percent(&password, url_password, url_password_len);
179             } else {
180 0           git_str_decode_percent(&username, url_userinfo, url_userinfo_len);
181             }
182             }
183              
184 0           if (git_str_oom(&scheme) ||
185 0 0         git_str_oom(&host) ||
186 0 0         git_str_oom(&port) ||
187 0 0         git_str_oom(&path) ||
188 0 0         git_str_oom(&query) ||
189 0 0         git_str_oom(&username) ||
190 0           git_str_oom(&password))
191 0           return -1;
192              
193 0           url->scheme = git_str_detach(&scheme);
194 0           url->host = git_str_detach(&host);
195 0           url->port = git_str_detach(&port);
196 0           url->path = git_str_detach(&path);
197 0           url->query = git_str_detach(&query);
198 0           url->username = git_str_detach(&username);
199 0           url->password = git_str_detach(&password);
200              
201 0           error = 0;
202              
203             done:
204 0           git_str_dispose(&scheme);
205 0           git_str_dispose(&host);
206 0           git_str_dispose(&port);
207 0           git_str_dispose(&path);
208 0           git_str_dispose(&query);
209 0           git_str_dispose(&username);
210 0           git_str_dispose(&password);
211 0           return error;
212             }
213              
214 0           static int scp_invalid(const char *message)
215             {
216 0           git_error_set(GIT_ERROR_NET, "invalid scp-style path: %s", message);
217 0           return GIT_EINVALIDSPEC;
218             }
219              
220 0           static bool is_ipv6(const char *str)
221             {
222             const char *c;
223 0           size_t colons = 0;
224              
225 0 0         if (*str++ != '[')
226 0           return false;
227              
228 0 0         for (c = str; *c; c++) {
229 0 0         if (*c == ':')
230 0           colons++;
231              
232 0 0         if (*c == ']')
233 0           return (colons > 1);
234              
235 0 0         if (*c != ':' &&
    0          
236 0 0         (*c < '0' || *c > '9') &&
    0          
237 0 0         (*c < 'a' || *c > 'f') &&
    0          
238 0 0         (*c < 'A' || *c > 'F'))
239 0           return false;
240             }
241              
242 0           return false;
243             }
244              
245 0           static bool has_at(const char *str)
246             {
247             const char *c;
248              
249 0 0         for (c = str; *c; c++) {
250 0 0         if (*c == '@')
251 0           return true;
252              
253 0 0         if (*c == ':')
254 0           break;
255             }
256              
257 0           return false;
258             }
259              
260 0           int git_net_url_parse_scp(git_net_url *url, const char *given)
261             {
262 0           const char *default_port = default_port_for_scheme("ssh");
263 0           const char *c, *user, *host, *port, *path = NULL;
264 0           size_t user_len = 0, host_len = 0, port_len = 0;
265 0           unsigned short bracket = 0;
266              
267             enum {
268             NONE,
269             USER,
270             HOST_START, HOST, HOST_END,
271             IPV6, IPV6_END,
272             PORT_START, PORT, PORT_END,
273             PATH_START
274 0           } state = NONE;
275              
276 0           memset(url, 0, sizeof(git_net_url));
277              
278 0 0         for (c = given; *c && !path; c++) {
    0          
279 0           switch (state) {
280             case NONE:
281 0           switch (*c) {
282             case '@':
283 0           return scp_invalid("unexpected '@'");
284             case ':':
285 0           return scp_invalid("unexpected ':'");
286             case '[':
287 0 0         if (is_ipv6(c)) {
288 0           state = IPV6;
289 0           host = c;
290 0 0         } else if (bracket++ > 1) {
291 0           return scp_invalid("unexpected '['");
292             }
293 0           break;
294             default:
295 0 0         if (has_at(c)) {
296 0           state = USER;
297 0           user = c;
298             } else {
299 0           state = HOST;
300 0           host = c;
301             }
302 0           break;
303             }
304 0           break;
305              
306             case USER:
307 0 0         if (*c == '@') {
308 0           user_len = (c - user);
309 0           state = HOST_START;
310             }
311 0           break;
312              
313             case HOST_START:
314 0 0         state = (*c == '[') ? IPV6 : HOST;
315 0           host = c;
316 0           break;
317              
318             case HOST:
319 0 0         if (*c == ':') {
320 0           host_len = (c - host);
321 0 0         state = bracket ? PORT_START : PATH_START;
322 0 0         } else if (*c == ']') {
323 0 0         if (bracket-- == 0)
324 0           return scp_invalid("unexpected ']'");
325              
326 0           host_len = (c - host);
327 0           state = HOST_END;
328             }
329 0           break;
330              
331             case HOST_END:
332 0 0         if (*c != ':')
333 0           return scp_invalid("unexpected character after hostname");
334 0           state = PATH_START;
335 0           break;
336              
337             case IPV6:
338 0 0         if (*c == ']')
339 0           state = IPV6_END;
340 0           break;
341              
342             case IPV6_END:
343 0 0         if (*c != ':')
344 0           return scp_invalid("unexpected character after ipv6 address");
345              
346 0           host_len = (c - host);
347 0 0         state = bracket ? PORT_START : PATH_START;
348 0           break;
349              
350             case PORT_START:
351 0           port = c;
352 0           state = PORT;
353 0           break;
354              
355             case PORT:
356 0 0         if (*c == ']') {
357 0 0         if (bracket-- == 0)
358 0           return scp_invalid("unexpected ']'");
359              
360 0           port_len = c - port;
361 0           state = PORT_END;
362             }
363 0           break;
364              
365             case PORT_END:
366 0 0         if (*c != ':')
367 0           return scp_invalid("unexpected character after ipv6 address");
368              
369 0           state = PATH_START;
370 0           break;
371              
372             case PATH_START:
373 0           path = c;
374 0           break;
375              
376             default:
377             GIT_ASSERT("unhandled state");
378             }
379             }
380              
381 0 0         if (!path)
382 0           return scp_invalid("path is required");
383              
384 0 0         GIT_ERROR_CHECK_ALLOC(url->scheme = git__strdup("ssh"));
385              
386 0 0         if (user_len)
387 0 0         GIT_ERROR_CHECK_ALLOC(url->username = git__strndup(user, user_len));
388              
389 0 0         GIT_ASSERT(host_len);
390 0 0         GIT_ERROR_CHECK_ALLOC(url->host = git__strndup(host, host_len));
391              
392 0 0         if (port_len)
393 0 0         GIT_ERROR_CHECK_ALLOC(url->port = git__strndup(port, port_len));
394             else
395 0 0         GIT_ERROR_CHECK_ALLOC(url->port = git__strdup(default_port));
396              
397 0 0         GIT_ASSERT(path);
398 0 0         GIT_ERROR_CHECK_ALLOC(url->path = git__strdup(path));
399              
400 0           return 0;
401             }
402              
403 0           int git_net_url_joinpath(
404             git_net_url *out,
405             git_net_url *one,
406             const char *two)
407             {
408 0           git_str path = GIT_STR_INIT;
409             const char *query;
410             size_t one_len, two_len;
411              
412 0           git_net_url_dispose(out);
413              
414 0 0         if ((query = strchr(two, '?')) != NULL) {
415 0           two_len = query - two;
416              
417 0 0         if (*(++query) != '\0') {
418 0           out->query = git__strdup(query);
419 0 0         GIT_ERROR_CHECK_ALLOC(out->query);
420             }
421             } else {
422 0           two_len = strlen(two);
423             }
424              
425             /* Strip all trailing `/`s from the first path */
426 0 0         one_len = one->path ? strlen(one->path) : 0;
427 0 0         while (one_len && one->path[one_len - 1] == '/')
    0          
428 0           one_len--;
429              
430             /* Strip all leading `/`s from the second path */
431 0 0         while (*two == '/') {
432 0           two++;
433 0           two_len--;
434             }
435              
436 0           git_str_put(&path, one->path, one_len);
437 0           git_str_putc(&path, '/');
438 0           git_str_put(&path, two, two_len);
439              
440 0 0         if (git_str_oom(&path))
441 0           return -1;
442              
443 0           out->path = git_str_detach(&path);
444              
445 0 0         if (one->scheme) {
446 0           out->scheme = git__strdup(one->scheme);
447 0 0         GIT_ERROR_CHECK_ALLOC(out->scheme);
448             }
449              
450 0 0         if (one->host) {
451 0           out->host = git__strdup(one->host);
452 0 0         GIT_ERROR_CHECK_ALLOC(out->host);
453             }
454              
455 0 0         if (one->port) {
456 0           out->port = git__strdup(one->port);
457 0 0         GIT_ERROR_CHECK_ALLOC(out->port);
458             }
459              
460 0 0         if (one->username) {
461 0           out->username = git__strdup(one->username);
462 0 0         GIT_ERROR_CHECK_ALLOC(out->username);
463             }
464              
465 0 0         if (one->password) {
466 0           out->password = git__strdup(one->password);
467 0 0         GIT_ERROR_CHECK_ALLOC(out->password);
468             }
469              
470 0           return 0;
471             }
472              
473             /*
474             * Some servers strip the query parameters from the Location header
475             * when sending a redirect. Others leave it in place.
476             * Check for both, starting with the stripped case first,
477             * since it appears to be more common.
478             */
479 0           static void remove_service_suffix(
480             git_net_url *url,
481             const char *service_suffix)
482             {
483 0           const char *service_query = strchr(service_suffix, '?');
484 0           size_t full_suffix_len = strlen(service_suffix);
485 0           size_t suffix_len = service_query ?
486 0 0         (size_t)(service_query - service_suffix) : full_suffix_len;
487 0           size_t path_len = strlen(url->path);
488 0           ssize_t truncate = -1;
489              
490             /*
491             * Check for a redirect without query parameters,
492             * like "/newloc/info/refs"'
493             */
494 0 0         if (suffix_len && path_len >= suffix_len) {
    0          
495 0           size_t suffix_offset = path_len - suffix_len;
496              
497 0 0         if (git__strncmp(url->path + suffix_offset, service_suffix, suffix_len) == 0 &&
    0          
498 0 0         (!service_query || git__strcmp(url->query, service_query + 1) == 0)) {
499 0           truncate = suffix_offset;
500             }
501             }
502              
503             /*
504             * If we haven't already found where to truncate to remove the
505             * suffix, check for a redirect with query parameters, like
506             * "/newloc/info/refs?service=git-upload-pack"
507             */
508 0 0         if (truncate < 0 && git__suffixcmp(url->path, service_suffix) == 0)
    0          
509 0           truncate = path_len - full_suffix_len;
510              
511             /* Ensure we leave a minimum of '/' as the path */
512 0 0         if (truncate == 0)
513 0           truncate++;
514              
515 0 0         if (truncate > 0) {
516 0           url->path[truncate] = '\0';
517              
518 0           git__free(url->query);
519 0           url->query = NULL;
520             }
521 0           }
522              
523 0           int git_net_url_apply_redirect(
524             git_net_url *url,
525             const char *redirect_location,
526             bool allow_offsite,
527             const char *service_suffix)
528             {
529 0           git_net_url tmp = GIT_NET_URL_INIT;
530 0           int error = 0;
531              
532 0 0         GIT_ASSERT(url);
533 0 0         GIT_ASSERT(redirect_location);
534              
535 0 0         if (redirect_location[0] == '/') {
536 0           git__free(url->path);
537              
538 0 0         if ((url->path = git__strdup(redirect_location)) == NULL) {
539 0           error = -1;
540 0           goto done;
541             }
542             } else {
543 0           git_net_url *original = url;
544              
545 0 0         if ((error = git_net_url_parse(&tmp, redirect_location)) < 0)
546 0           goto done;
547              
548             /* Validate that this is a legal redirection */
549              
550 0 0         if (original->scheme &&
    0          
551 0 0         strcmp(original->scheme, tmp.scheme) != 0 &&
552 0           strcmp(tmp.scheme, "https") != 0) {
553 0           git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
554             original->scheme, tmp.scheme);
555              
556 0           error = -1;
557 0           goto done;
558             }
559              
560 0 0         if (original->host &&
    0          
561 0 0         !allow_offsite &&
562 0           git__strcasecmp(original->host, tmp.host) != 0) {
563 0           git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
564             original->host, tmp.host);
565              
566 0           error = -1;
567 0           goto done;
568             }
569              
570 0           git_net_url_swap(url, &tmp);
571             }
572              
573             /* Remove the service suffix if it was given to us */
574 0 0         if (service_suffix)
575 0           remove_service_suffix(url, service_suffix);
576              
577             done:
578 0           git_net_url_dispose(&tmp);
579 0           return error;
580             }
581              
582 0           bool git_net_url_valid(git_net_url *url)
583             {
584 0 0         return (url->host && url->port && url->path);
    0          
    0          
585             }
586              
587 0           bool git_net_url_is_default_port(git_net_url *url)
588             {
589             const char *default_port;
590              
591 0 0         if ((default_port = default_port_for_scheme(url->scheme)) != NULL)
592 0           return (strcmp(url->port, default_port) == 0);
593             else
594 0           return false;
595             }
596              
597 0           bool git_net_url_is_ipv6(git_net_url *url)
598             {
599 0           return (strchr(url->host, ':') != NULL);
600             }
601              
602 0           void git_net_url_swap(git_net_url *a, git_net_url *b)
603             {
604 0           git_net_url tmp = GIT_NET_URL_INIT;
605              
606 0           memcpy(&tmp, a, sizeof(git_net_url));
607 0           memcpy(a, b, sizeof(git_net_url));
608 0           memcpy(b, &tmp, sizeof(git_net_url));
609 0           }
610              
611 0           int git_net_url_fmt(git_str *buf, git_net_url *url)
612             {
613 0 0         GIT_ASSERT_ARG(url);
614 0 0         GIT_ASSERT_ARG(url->scheme);
615 0 0         GIT_ASSERT_ARG(url->host);
616              
617 0           git_str_puts(buf, url->scheme);
618 0           git_str_puts(buf, "://");
619              
620 0 0         if (url->username) {
621 0           git_str_puts(buf, url->username);
622              
623 0 0         if (url->password) {
624 0           git_str_puts(buf, ":");
625 0           git_str_puts(buf, url->password);
626             }
627              
628 0           git_str_putc(buf, '@');
629             }
630              
631 0           git_str_puts(buf, url->host);
632              
633 0 0         if (url->port && !git_net_url_is_default_port(url)) {
    0          
634 0           git_str_putc(buf, ':');
635 0           git_str_puts(buf, url->port);
636             }
637              
638 0 0         git_str_puts(buf, url->path ? url->path : "/");
639              
640 0 0         if (url->query) {
641 0           git_str_putc(buf, '?');
642 0           git_str_puts(buf, url->query);
643             }
644              
645 0 0         return git_str_oom(buf) ? -1 : 0;
646             }
647              
648 0           int git_net_url_fmt_path(git_str *buf, git_net_url *url)
649             {
650 0 0         git_str_puts(buf, url->path ? url->path : "/");
651              
652 0 0         if (url->query) {
653 0           git_str_putc(buf, '?');
654 0           git_str_puts(buf, url->query);
655             }
656              
657 0 0         return git_str_oom(buf) ? -1 : 0;
658             }
659              
660 0           static bool matches_pattern(
661             git_net_url *url,
662             const char *pattern,
663             size_t pattern_len)
664             {
665 0           const char *domain, *port = NULL, *colon;
666 0           size_t host_len, domain_len, port_len = 0, wildcard = 0;
667              
668 0           GIT_UNUSED(url);
669 0           GIT_UNUSED(pattern);
670              
671 0 0         if (!pattern_len)
672 0           return false;
673 0 0         else if (pattern_len == 1 && pattern[0] == '*')
    0          
674 0           return true;
675 0 0         else if (pattern_len > 1 && pattern[0] == '*' && pattern[1] == '.')
    0          
    0          
676 0           wildcard = 2;
677 0 0         else if (pattern[0] == '.')
678 0           wildcard = 1;
679              
680 0           domain = pattern + wildcard;
681 0           domain_len = pattern_len - wildcard;
682              
683 0 0         if ((colon = memchr(domain, ':', domain_len)) != NULL) {
684 0           domain_len = colon - domain;
685 0           port = colon + 1;
686 0           port_len = pattern_len - wildcard - domain_len - 1;
687             }
688              
689             /* A pattern's port *must* match if it's specified */
690 0 0         if (port_len && git__strlcmp(url->port, port, port_len) != 0)
    0          
691 0           return false;
692              
693             /* No wildcard? Host must match exactly. */
694 0 0         if (!wildcard)
695 0           return !git__strlcmp(url->host, domain, domain_len);
696              
697             /* Wildcard: ensure there's (at least) a suffix match */
698 0 0         if ((host_len = strlen(url->host)) < domain_len ||
    0          
699 0           memcmp(url->host + (host_len - domain_len), domain, domain_len))
700 0           return false;
701              
702             /* The pattern is *.domain and the host is simply domain */
703 0 0         if (host_len == domain_len)
704 0           return true;
705              
706             /* The pattern is *.domain and the host is foo.domain */
707 0           return (url->host[host_len - domain_len - 1] == '.');
708             }
709              
710 0           bool git_net_url_matches_pattern(git_net_url *url, const char *pattern)
711             {
712 0           return matches_pattern(url, pattern, strlen(pattern));
713             }
714              
715 0           bool git_net_url_matches_pattern_list(
716             git_net_url *url,
717             const char *pattern_list)
718             {
719             const char *pattern, *pattern_end, *sep;
720              
721 0 0         for (pattern = pattern_list;
722 0 0         pattern && *pattern;
723 0 0         pattern = sep ? sep + 1 : NULL) {
724 0           sep = strchr(pattern, ',');
725 0 0         pattern_end = sep ? sep : strchr(pattern, '\0');
726              
727 0 0         if (matches_pattern(url, pattern, (pattern_end - pattern)))
728 0           return true;
729             }
730              
731 0           return false;
732             }
733              
734 0           void git_net_url_dispose(git_net_url *url)
735             {
736 0 0         if (url->username)
737 0           git__memzero(url->username, strlen(url->username));
738              
739 0 0         if (url->password)
740 0           git__memzero(url->password, strlen(url->password));
741              
742 0           git__free(url->scheme); url->scheme = NULL;
743 0           git__free(url->host); url->host = NULL;
744 0           git__free(url->port); url->port = NULL;
745 0           git__free(url->path); url->path = NULL;
746 0           git__free(url->query); url->query = NULL;
747 0           git__free(url->username); url->username = NULL;
748 0           git__free(url->password); url->password = NULL;
749 0           }