1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include <apr_strings.h>
21 #include <apr_portable.h>
22 #include <apr_file_info.h>
23 #include <apr_fnmatch.h>
24 #include <apr_tables.h>
30 /**************************************************************************************************/
33 apr_status_t md_util_pool_do(md_util_action *cb, void *baton, apr_pool_t *p)
36 apr_status_t rv = apr_pool_create(&ptemp, p);
37 if (APR_SUCCESS == rv) {
38 rv = cb(baton, p, ptemp);
40 apr_pool_destroy(ptemp);
45 static apr_status_t pool_vado(md_util_vaction *cb, void *baton, apr_pool_t *p, va_list ap)
50 rv = apr_pool_create(&ptemp, p);
51 if (APR_SUCCESS == rv) {
52 rv = cb(baton, p, ptemp, ap);
53 apr_pool_destroy(ptemp);
58 apr_status_t md_util_pool_vdo(md_util_vaction *cb, void *baton, apr_pool_t *p, ...)
64 rv = pool_vado(cb, baton, p, ap);
69 /**************************************************************************************************/
72 char *md_util_str_tolower(char *s)
76 *s = (char)apr_tolower(*s);
82 int md_array_str_index(const apr_array_header_t *array, const char *s,
83 int start, int case_sensitive)
88 for (i = start; i < array->nelts; i++) {
89 const char *p = APR_ARRAY_IDX(array, i, const char *);
90 if ((case_sensitive && !strcmp(p, s))
91 || (!case_sensitive && !apr_strnatcasecmp(p, s))) {
100 int md_array_str_eq(const struct apr_array_header_t *a1,
101 const struct apr_array_header_t *a2, int case_sensitive)
106 if (a1 == a2) return 1;
108 if (a1->nelts != a2->nelts) return 0;
109 for (i = 0; i < a1->nelts; ++i) {
110 s1 = APR_ARRAY_IDX(a1, i, const char *);
111 s2 = APR_ARRAY_IDX(a2, i, const char *);
112 if ((case_sensitive && strcmp(s1, s2))
113 || (!case_sensitive && apr_strnatcasecmp(s1, s2))) {
120 apr_array_header_t *md_array_str_clone(apr_pool_t *p, apr_array_header_t *src)
122 apr_array_header_t *dest = apr_array_make(p, src->nelts, sizeof(const char*));
125 for (i = 0; i < src->nelts; i++) {
126 const char *s = APR_ARRAY_IDX(src, i, const char *);
127 APR_ARRAY_PUSH(dest, const char *) = apr_pstrdup(p, s);
133 struct apr_array_header_t *md_array_str_compact(apr_pool_t *p, struct apr_array_header_t *src,
136 apr_array_header_t *dest = apr_array_make(p, src->nelts, sizeof(const char*));
140 for (i = 0; i < src->nelts; ++i) {
141 s = APR_ARRAY_IDX(src, i, const char *);
142 if (md_array_str_index(dest, s, 0, case_sensitive) < 0) {
143 APR_ARRAY_PUSH(dest, char *) = md_util_str_tolower(apr_pstrdup(p, s));
150 apr_array_header_t *md_array_str_remove(apr_pool_t *p, apr_array_header_t *src,
151 const char *exclude, int case_sensitive)
153 apr_array_header_t *dest = apr_array_make(p, src->nelts, sizeof(const char*));
156 for (i = 0; i < src->nelts; i++) {
157 const char *s = APR_ARRAY_IDX(src, i, const char *);
159 || (case_sensitive && strcmp(exclude, s))
160 || (!case_sensitive && apr_strnatcasecmp(exclude, s))) {
161 APR_ARRAY_PUSH(dest, const char *) = apr_pstrdup(p, s);
168 int md_array_str_add_missing(apr_array_header_t *dest, apr_array_header_t *src, int case_sensitive)
171 for (i = 0; i < src->nelts; i++) {
172 const char *s = APR_ARRAY_IDX(src, i, const char *);
173 if (md_array_str_index(dest, s, 0, case_sensitive) < 0) {
174 APR_ARRAY_PUSH(dest, const char *) = s;
181 /**************************************************************************************************/
182 /* file system related */
184 apr_status_t md_util_fopen(FILE **pf, const char *fn, const char *mode)
186 *pf = fopen(fn, mode);
194 apr_status_t md_util_fcreatex(apr_file_t **pf, const char *fn,
195 apr_fileperms_t perms, apr_pool_t *p)
197 return apr_file_open(pf, fn, (APR_FOPEN_WRITE|APR_FOPEN_CREATE|APR_FOPEN_EXCL),
201 apr_status_t md_util_is_dir(const char *path, apr_pool_t *pool)
204 apr_status_t rv = apr_stat(&info, path, APR_FINFO_TYPE, pool);
205 if (rv == APR_SUCCESS) {
206 rv = (info.filetype == APR_DIR)? APR_SUCCESS : APR_EINVAL;
211 apr_status_t md_util_is_file(const char *path, apr_pool_t *pool)
214 apr_status_t rv = apr_stat(&info, path, APR_FINFO_TYPE, pool);
215 if (rv == APR_SUCCESS) {
216 rv = (info.filetype == APR_REG)? APR_SUCCESS : APR_EINVAL;
221 apr_status_t md_util_path_merge(const char **ppath, apr_pool_t *p, ...)
223 const char *segment, *path;
225 apr_status_t rv = APR_SUCCESS;
228 path = va_arg(ap, char *);
229 while (path && APR_SUCCESS == rv && (segment = va_arg(ap, char *))) {
230 rv = apr_filepath_merge((char **)&path, path, segment, APR_FILEPATH_SECUREROOT , p);
234 *ppath = (APR_SUCCESS == rv)? (path? path : "") : NULL;
238 apr_status_t md_util_freplace(const char *fpath, apr_fileperms_t perms, apr_pool_t *p,
239 md_util_file_cb *write_cb, void *baton)
241 apr_status_t rv = APR_EEXIST;
246 tmp = apr_psprintf(p, "%s.tmp", fpath);
249 while (i < max && APR_EEXIST == (rv = md_util_fcreatex(&f, tmp, perms, p))) {
251 apr_sleep(apr_time_msec(50));
254 && APR_SUCCESS == (rv = apr_file_remove(tmp, p))
260 if (APR_SUCCESS == rv) {
261 rv = write_cb(baton, f, p);
264 if (APR_SUCCESS == rv) {
265 rv = apr_file_rename(tmp, fpath, p);
266 if (APR_SUCCESS != rv) {
267 apr_file_remove(tmp, p);
274 /**************************************************************************************************/
277 apr_status_t md_text_fread8k(const char **ptext, apr_pool_t *p, const char *fpath)
281 char buffer[8 * 1024];
284 if (APR_SUCCESS == (rv = apr_file_open(&f, fpath, APR_FOPEN_READ, 0, p))) {
285 apr_size_t blen = sizeof(buffer)/sizeof(buffer[0]) - 1;
286 rv = apr_file_read_full(f, buffer, blen, &blen);
287 if (APR_SUCCESS == rv || APR_STATUS_IS_EOF(rv)) {
288 *ptext = apr_pstrndup(p, buffer, blen);
296 static apr_status_t write_text(void *baton, struct apr_file_t *f, apr_pool_t *p)
298 const char *text = baton;
299 apr_size_t len = strlen(text);
302 return apr_file_write_full(f, text, len, &len);
305 apr_status_t md_text_fcreatex(const char *fpath, apr_fileperms_t perms,
306 apr_pool_t *p, const char *text)
311 rv = md_util_fcreatex(&f, fpath, perms, p);
312 if (APR_SUCCESS == rv) {
313 rv = write_text((void*)text, f, p);
319 apr_status_t md_text_freplace(const char *fpath, apr_fileperms_t perms,
320 apr_pool_t *p, const char *text)
322 return md_util_freplace(fpath, perms, p, write_text, (void*)text);
327 apr_array_header_t *patterns;
333 static apr_status_t rm_recursive(const char *fpath, apr_pool_t *p, int max_level)
339 if (APR_SUCCESS != (rv = apr_stat(&info, fpath, (APR_FINFO_TYPE|APR_FINFO_LINK), p))) {
343 if (info.filetype == APR_DIR) {
347 if (APR_SUCCESS == (rv = apr_dir_open(&d, fpath, p))) {
349 while (APR_SUCCESS == rv &&
350 APR_SUCCESS == (rv = apr_dir_read(&info, APR_FINFO_TYPE, d))) {
351 if (!strcmp(".", info.name) || !strcmp("..", info.name)) {
355 rv = md_util_path_merge(&npath, p, fpath, info.name, NULL);
356 if (APR_SUCCESS == rv) {
357 rv = rm_recursive(npath, p, max_level - 1);
361 if (APR_STATUS_IS_ENOENT(rv)) {
366 if (APR_SUCCESS == rv) {
367 rv = apr_dir_remove(fpath, p);
371 rv = apr_file_remove(fpath, p);
376 static apr_status_t prm_recursive(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
378 int max_level = va_arg(ap, int);
381 return rm_recursive(baton, ptemp, max_level);
384 apr_status_t md_util_rm_recursive(const char *fpath, apr_pool_t *p, int max_level)
386 return md_util_pool_vdo(prm_recursive, (void*)fpath, p, max_level, NULL);
389 static apr_status_t match_and_do(md_util_fwalk_t *ctx, const char *path, int depth,
390 apr_pool_t *p, apr_pool_t *ptemp)
392 apr_status_t rv = APR_SUCCESS;
393 const char *pattern, *npath;
396 int ndepth = depth + 1;
397 apr_int32_t wanted = (APR_FINFO_TYPE);
399 if (depth >= ctx->patterns->nelts) {
402 pattern = APR_ARRAY_IDX(ctx->patterns, depth, const char *);
404 rv = apr_dir_open(&d, path, ptemp);
405 if (APR_SUCCESS != rv) {
409 while (APR_SUCCESS == (rv = apr_dir_read(&finfo, wanted, d))) {
410 if (!strcmp(".", finfo.name) || !strcmp("..", finfo.name)) {
413 if (APR_SUCCESS == apr_fnmatch(pattern, finfo.name, 0)) {
414 if (ndepth < ctx->patterns->nelts) {
415 if (APR_DIR == finfo.filetype) {
416 /* deeper and deeper, irgendwo in der tiefe leuchtet ein licht */
417 rv = md_util_path_merge(&npath, ptemp, path, finfo.name, NULL);
418 if (APR_SUCCESS == rv) {
419 rv = match_and_do(ctx, npath, ndepth, p, ptemp);
424 rv = ctx->cb(ctx->baton, p, ptemp, path, finfo.name, finfo.filetype);
427 if (APR_SUCCESS != rv) {
432 if (APR_STATUS_IS_ENOENT(rv)) {
440 static apr_status_t files_do_start(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
442 md_util_fwalk_t *ctx = baton;
445 ctx->patterns = apr_array_make(ptemp, 5, sizeof(const char*));
447 segment = va_arg(ap, char *);
449 APR_ARRAY_PUSH(ctx->patterns, const char *) = segment;
450 segment = va_arg(ap, char *);
453 return match_and_do(ctx, ctx->path, 0, p, ptemp);
456 apr_status_t md_util_files_do(md_util_fdo_cb *cb, void *baton, apr_pool_t *p,
457 const char *path, ...)
463 memset(&ctx, 0, sizeof(ctx));
465 ctx.follow_links = 1;
470 rv = pool_vado(files_do_start, &ctx, p, ap);
476 static apr_status_t tree_do(void *baton, apr_pool_t *p, apr_pool_t *ptemp, const char *path)
478 md_util_fwalk_t *ctx = baton;
480 apr_status_t rv = APR_SUCCESS;
481 const char *name, *fpath;
482 apr_filetype_e ftype;
484 apr_int32_t wanted = APR_FINFO_TYPE;
487 if (APR_SUCCESS == (rv = apr_dir_open(&d, path, ptemp))) {
488 while (APR_SUCCESS == (rv = apr_dir_read(&finfo, wanted, d))) {
490 if (!strcmp(".", name) || !strcmp("..", name)) {
495 ftype = finfo.filetype;
497 if (APR_LNK == ftype && ctx->follow_links) {
498 rv = md_util_path_merge(&fpath, ptemp, path, name, NULL);
499 if (APR_SUCCESS == rv) {
500 rv = apr_stat(&finfo, ctx->path, wanted, ptemp);
504 if (APR_DIR == finfo.filetype) {
506 rv = md_util_path_merge(&fpath, ptemp, path, name, NULL);
508 if (APR_SUCCESS == rv) {
509 rv = tree_do(ctx, p, ptemp, fpath);
510 md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, ptemp, "dir cb(%s/%s)",
512 rv = ctx->cb(ctx->baton, p, ptemp, path, name, ftype);
516 md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, ptemp, "file cb(%s/%s)",
518 rv = ctx->cb(ctx->baton, p, ptemp, path, name, finfo.filetype);
524 if (APR_STATUS_IS_ENOENT(rv)) {
531 static apr_status_t tree_start_do(void *baton, apr_pool_t *p, apr_pool_t *ptemp)
533 md_util_fwalk_t *ctx = baton;
536 apr_int32_t wanted = ctx->follow_links? APR_FINFO_TYPE : (APR_FINFO_TYPE|APR_FINFO_LINK);
538 rv = apr_stat(&info, ctx->path, wanted, ptemp);
539 if (rv == APR_SUCCESS) {
540 switch (info.filetype) {
542 rv = tree_do(ctx, p, ptemp, ctx->path);
551 apr_status_t md_util_tree_do(md_util_fdo_cb *cb, void *baton, apr_pool_t *p,
552 const char *path, int follow_links)
557 memset(&ctx, 0, sizeof(ctx));
559 ctx.follow_links = follow_links;
563 rv = md_util_pool_do(tree_start_do, &ctx, p);
568 static apr_status_t rm_cb(void *baton, apr_pool_t *p, apr_pool_t *ptemp,
569 const char *path, const char *name, apr_filetype_e ftype)
576 rv = md_util_path_merge(&fpath, ptemp, path, name, NULL);
577 if (APR_SUCCESS == rv) {
578 if (APR_DIR == ftype) {
579 rv = apr_dir_remove(fpath, ptemp);
582 rv = apr_file_remove(fpath, ptemp);
588 apr_status_t md_util_ftree_remove(const char *path, apr_pool_t *p)
590 apr_status_t rv = md_util_tree_do(rm_cb, NULL, p, path, 0);
591 if (APR_SUCCESS == rv) {
592 rv = apr_dir_remove(path, p);
597 /* DNS name checks ********************************************************************************/
599 int md_util_is_dns_name(apr_pool_t *p, const char *hostname, int need_fqdn)
602 const char *cp = hostname;
605 /* Since we use the names in certificates, we need pure ASCII domain names
606 * and IDN need to be converted to unicode. */
607 while ((c = *cp++)) {
611 md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, p, "dns name with ..: %s",
620 if (!apr_isalnum(c)) {
621 md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, p, "dns invalid char %c: %s",
630 if (last == '.') { /* DNS names may end with '.' */
633 if (need_fqdn && dots <= 0) { /* do not accept just top level domains */
634 md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, p, "not a FQDN: %s", hostname);
637 return 1; /* empty string not allowed */
640 const char *md_util_schemify(apr_pool_t *p, const char *s, const char *def_scheme)
645 /* could be an url scheme, leave unchanged */
648 else if (!apr_isalnum(*cp)) {
653 return apr_psprintf(p, "%s:%s", def_scheme, s);
656 static apr_status_t uri_check(apr_uri_t *uri_parsed, apr_pool_t *p,
657 const char *uri, const char **perr)
659 const char *s, *err = NULL;
662 if (APR_SUCCESS != (rv = apr_uri_parse(p, uri, uri_parsed))) {
665 else if (uri_parsed->scheme) {
666 if (strlen(uri_parsed->scheme) + 1 >= strlen(uri)) {
667 err = "missing uri identifier";
669 else if (!strncmp("http", uri_parsed->scheme, 4)) {
670 if (!uri_parsed->hostname) {
671 err = "missing hostname";
673 else if (!md_util_is_dns_name(p, uri_parsed->hostname, 0)) {
674 err = "invalid hostname";
676 if (uri_parsed->port_str
677 && (!apr_isdigit(uri_parsed->port_str[0])
678 || uri_parsed->port == 0
679 || uri_parsed->port > 65353)) {
680 err = "invalid port";
683 else if (!strcmp("mailto", uri_parsed->scheme)) {
684 s = strchr(uri, '@');
688 else if (strchr(s+1, '@')) {
691 else if (s == uri + strlen(uri_parsed->scheme) + 1) {
692 err = "missing local part";
694 else if (s == (uri + strlen(uri)-1)) {
695 err = "missing hostname";
697 else if (strstr(uri, "..")) {
698 err = "double period";
702 if (strchr(uri, ' ') || strchr(uri, '\t') ) {
703 err = "whitespace in uri";
713 apr_status_t md_util_abs_uri_check(apr_pool_t *p, const char *uri, const char **perr)
715 apr_uri_t uri_parsed;
718 if (APR_SUCCESS == (rv = uri_check(&uri_parsed, p, uri, perr))) {
719 if (!uri_parsed.scheme) {
720 *perr = "missing uri scheme";
727 apr_status_t md_util_abs_http_uri_check(apr_pool_t *p, const char *uri, const char **perr)
729 apr_uri_t uri_parsed;
732 if (APR_SUCCESS == (rv = uri_check(&uri_parsed, p, uri, perr))) {
733 if (!uri_parsed.scheme) {
734 *perr = "missing uri scheme";
737 if (apr_strnatcasecmp("http", uri_parsed.scheme)
738 && apr_strnatcasecmp("https", uri_parsed.scheme)) {
739 *perr = "uri scheme must be http or https";
746 /* try and retry for a while **********************************************************************/
748 apr_status_t md_util_try(md_util_try_fn *fn, void *baton, int ignore_errs,
749 apr_interval_time_t timeout, apr_interval_time_t start_delay,
750 apr_interval_time_t max_delay, int backoff)
753 apr_time_t now = apr_time_now();
754 apr_time_t giveup = now + timeout;
755 apr_interval_time_t nap_duration = start_delay? start_delay : apr_time_from_msec(100);
756 apr_interval_time_t nap_max = max_delay? max_delay : apr_time_from_sec(10);
757 apr_interval_time_t left;
761 if (APR_SUCCESS == (rv = fn(baton, i++))) {
764 else if (!APR_STATUS_IS_EAGAIN(rv) && !ignore_errs) {
768 now = apr_time_now();
775 if (nap_duration > left) {
778 if (nap_duration > nap_max) {
779 nap_duration = nap_max;
782 apr_sleep(nap_duration);
790 /* execute process ********************************************************************************/
792 apr_status_t md_util_exec(apr_pool_t *p, const char *cmd, const char * const *argv,
796 apr_procattr_t *procattr;
801 if (!(proc = apr_pcalloc(p, sizeof(*proc)))) {
804 if ( APR_SUCCESS == (rv = apr_procattr_create(&procattr, p))
805 && APR_SUCCESS == (rv = apr_procattr_io_set(procattr, APR_NO_FILE,
806 APR_NO_PIPE, APR_NO_PIPE))
807 && APR_SUCCESS == (rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM))
808 && APR_SUCCESS == (rv = apr_proc_create(proc, cmd, argv, NULL, procattr, p))
809 && APR_CHILD_DONE == (rv = apr_proc_wait(proc, exit_code, &ewhy, APR_WAIT))) {
810 /* let's not dwell on exit stati, but core should signal something's bad */
811 if (*exit_code > 127 || APR_PROC_SIGNAL_CORE == ewhy) {
812 return APR_EINCOMPLETE;
820 /* date/time encoding *****************************************************************************/
822 const char *md_print_duration(apr_pool_t *p, apr_interval_time_t duration)
824 int secs = (int)(apr_time_sec(duration) % MD_SECS_PER_DAY);
825 return apr_psprintf(p, "%2d:%02d:%02d hours",
826 (int)secs/MD_SECS_PER_HOUR, (int)(secs%(MD_SECS_PER_HOUR))/60,
831 /* base64 url encoding ****************************************************************************/
833 #define N6 (unsigned int)-1
835 static const unsigned int BASE64URL_UINT6[] = {
836 /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
837 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 0 */
838 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 1 */
839 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, 62, N6, N6, /* 2 */
840 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, N6, N6, N6, N6, N6, N6, /* 3 */
841 N6, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 4 */
842 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, N6, N6, N6, N6, 63, /* 5 */
843 N6, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 6 */
844 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, N6, N6, N6, N6, N6, /* 7 */
845 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 8 */
846 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 9 */
847 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* a */
848 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* b */
849 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* c */
850 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* d */
851 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* e */
852 N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6 /* f */
854 static const unsigned char BASE64URL_CHARS[] = {
855 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 0 - 9 */
856 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 10 - 19 */
857 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', /* 20 - 29 */
858 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 30 - 39 */
859 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', /* 40 - 49 */
860 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', /* 50 - 59 */
861 '8', '9', '-', '_', ' ', ' ', ' ', ' ', ' ', ' ', /* 60 - 69 */
864 #define BASE64URL_CHAR(x) BASE64URL_CHARS[ (unsigned int)(x) & 0x3fu ]
866 apr_size_t md_util_base64url_decode(const char **decoded, const char *encoded,
869 const unsigned char *e = (const unsigned char *)encoded;
870 const unsigned char *p = e;
873 long len, mlen, remain, i;
875 while (*p && BASE64URL_UINT6[ *p ] != N6) {
880 *decoded = apr_pcalloc(pool, (apr_size_t)len + 1);
883 d = (unsigned char*)*decoded;
884 for (; i < mlen; i += 4) {
885 n = ((BASE64URL_UINT6[ e[i+0] ] << 18) +
886 (BASE64URL_UINT6[ e[i+1] ] << 12) +
887 (BASE64URL_UINT6[ e[i+2] ] << 6) +
888 (BASE64URL_UINT6[ e[i+3] ]));
889 *d++ = (unsigned char)(n >> 16);
890 *d++ = (unsigned char)(n >> 8 & 0xffu);
891 *d++ = (unsigned char)(n & 0xffu);
896 n = ((BASE64URL_UINT6[ e[mlen+0] ] << 18) +
897 (BASE64URL_UINT6[ e[mlen+1] ] << 12));
898 *d++ = (unsigned char)(n >> 16);
902 n = ((BASE64URL_UINT6[ e[mlen+0] ] << 18) +
903 (BASE64URL_UINT6[ e[mlen+1] ] << 12) +
904 (BASE64URL_UINT6[ e[mlen+2] ] << 6));
905 *d++ = (unsigned char)(n >> 16);
906 *d++ = (unsigned char)(n >> 8 & 0xffu);
909 default: /* do nothing */
912 return (apr_size_t)(mlen/4*3 + remain);
915 const char *md_util_base64url_encode(const char *data, apr_size_t dlen, apr_pool_t *pool)
917 int i, len = (int)dlen;
918 apr_size_t slen = ((dlen+2)/3)*4 + 1; /* 0 terminated */
919 const unsigned char *udata = (const unsigned char*)data;
920 unsigned char *enc, *p = apr_pcalloc(pool, slen);
923 for (i = 0; i < len-2; i+= 3) {
924 *p++ = BASE64URL_CHAR( (udata[i] >> 2) );
925 *p++ = BASE64URL_CHAR( (udata[i] << 4) + (udata[i+1] >> 4) );
926 *p++ = BASE64URL_CHAR( (udata[i+1] << 2) + (udata[i+2] >> 6) );
927 *p++ = BASE64URL_CHAR( (udata[i+2]) );
931 *p++ = BASE64URL_CHAR( (udata[i] >> 2) );
932 if (i == (len - 1)) {
933 *p++ = BASE64URL_CHARS[ ((unsigned int)udata[i] << 4) & 0x3fu ];
936 *p++ = BASE64URL_CHAR( (udata[i] << 4) + (udata[i+1] >> 4) );
937 *p++ = BASE64URL_CHAR( (udata[i+1] << 2) );
944 /*******************************************************************************
945 * link header handling
946 ******************************************************************************/
952 apr_size_t link_start;
960 static int attr_char(char c)
977 return apr_isalnum(c);
981 static int ptoken_char(char c)
1013 return apr_isalnum(c);
1017 static int skip_ws(link_ctx *ctx)
1020 while (ctx->i < ctx->slen
1021 && (((c = ctx->s[ctx->i]) == ' ') || (c == '\t'))) {
1024 return (ctx->i < ctx->slen);
1027 static int skip_nonws(link_ctx *ctx)
1030 while (ctx->i < ctx->slen
1031 && (((c = ctx->s[ctx->i]) != ' ') && (c != '\t'))) {
1034 return (ctx->i < ctx->slen);
1037 static unsigned int find_chr(link_ctx *ctx, char c, apr_size_t *pidx)
1040 for (j = ctx->i; j < ctx->slen; ++j) {
1041 if (ctx->s[j] == c) {
1049 static int read_chr(link_ctx *ctx, char c)
1051 if (ctx->i < ctx->slen && ctx->s[ctx->i] == c) {
1058 static int skip_qstring(link_ctx *ctx)
1060 if (skip_ws(ctx) && read_chr(ctx, '\"')) {
1062 if (find_chr(ctx, '\"', &end)) {
1070 static int skip_ptoken(link_ctx *ctx)
1074 for (i = ctx->i; i < ctx->slen && ptoken_char(ctx->s[i]); ++i) {
1086 static int read_link(link_ctx *ctx)
1088 ctx->link_start = ctx->link_len = 0;
1089 if (skip_ws(ctx) && read_chr(ctx, '<')) {
1091 if (find_chr(ctx, '>', &end)) {
1092 ctx->link_start = ctx->i;
1093 ctx->link_len = end - ctx->link_start;
1101 static int skip_pname(link_ctx *ctx)
1105 for (i = ctx->i; i < ctx->slen && attr_char(ctx->s[i]); ++i) {
1116 static int skip_pvalue(link_ctx *ctx)
1118 if (skip_ws(ctx) && read_chr(ctx, '=')) {
1119 ctx->pv_start = ctx->i;
1120 if (skip_qstring(ctx) || skip_ptoken(ctx)) {
1121 ctx->pv_len = ctx->i - ctx->pv_start;
1128 static int skip_param(link_ctx *ctx)
1130 if (skip_ws(ctx) && read_chr(ctx, ';')) {
1131 ctx->pn_start = ctx->i;
1133 if (skip_pname(ctx)) {
1134 ctx->pn_len = ctx->i - ctx->pn_start;
1136 skip_pvalue(ctx); /* value is optional */
1143 static int pv_contains(link_ctx *ctx, const char *s)
1145 apr_size_t pvstart = ctx->pv_start;
1146 apr_size_t pvlen = ctx->pv_len;
1148 if (ctx->s[pvstart] == '\"' && pvlen > 1) {
1153 apr_size_t slen = strlen(s);
1157 memset(&pvctx, 0, sizeof(pvctx));
1158 pvctx.s = ctx->s + pvstart;
1161 for (i = 0; i < pvctx.slen; i = pvctx.i) {
1163 if ((pvctx.i - i) == slen && !strncmp(s, pvctx.s + i, slen)) {
1172 /* RFC 5988 <https://tools.ietf.org/html/rfc5988#section-6.2.1>
1173 Link = "Link" ":" #link-value
1174 link-value = "<" URI-Reference ">" *( ";" link-param )
1175 link-param = ( ( "rel" "=" relation-types )
1176 | ( "anchor" "=" <"> URI-Reference <"> )
1177 | ( "rev" "=" relation-types )
1178 | ( "hreflang" "=" Language-Tag )
1179 | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) )
1180 | ( "title" "=" quoted-string )
1181 | ( "title*" "=" ext-value )
1182 | ( "type" "=" ( media-type | quoted-mt ) )
1183 | ( link-extension ) )
1184 link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] )
1185 | ( ext-name-star "=" ext-value )
1186 ext-name-star = parmname "*" ; reserved for RFC2231-profiled
1187 ; extensions. Whitespace NOT
1188 ; allowed in between.
1189 ptoken = 1*ptokenchar
1190 ptokenchar = "!" | "#" | "$" | "%" | "&" | "'" | "("
1191 | ")" | "*" | "+" | "-" | "." | "/" | DIGIT
1192 | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA
1193 | "[" | "]" | "^" | "_" | "`" | "{" | "|"
1195 media-type = type-name "/" subtype-name
1196 quoted-mt = <"> media-type <">
1197 relation-types = relation-type
1198 | <"> relation-type *( 1*SP relation-type ) <">
1199 relation-type = reg-rel-type | ext-rel-type
1200 reg-rel-type = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
1203 and from <https://tools.ietf.org/html/rfc5987>
1204 parmname = 1*attr-char
1205 attr-char = ALPHA / DIGIT
1206 / "!" / "#" / "$" / "&" / "+" / "-" / "."
1207 / "^" / "_" / "`" / "|" / "~"
1212 const char *relation;
1216 static int find_url(void *baton, const char *key, const char *value)
1218 find_ctx *outer = baton;
1220 if (!apr_strnatcasecmp("link", key)) {
1223 memset(&ctx, 0, sizeof(ctx));
1225 ctx.slen = strlen(value);
1227 while (read_link(&ctx)) {
1228 while (skip_param(&ctx)) {
1229 if (ctx.pn_len == 3 && !strncmp("rel", ctx.s + ctx.pn_start, 3)
1230 && pv_contains(&ctx, outer->relation)) {
1231 /* this is the link relation we are looking for */
1232 outer->url = apr_pstrndup(outer->pool, ctx.s + ctx.link_start, ctx.link_len);
1241 const char *md_link_find_relation(const apr_table_t *headers,
1242 apr_pool_t *pool, const char *relation)
1246 memset(&ctx, 0, sizeof(ctx));
1248 ctx.relation = relation;
1250 apr_table_do(find_url, &ctx, headers, NULL);