]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.151 2017/03/15 13:49:50 schwarze Exp $ */
3 * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@usta.de>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
36 #include "mandoc_aux.h"
43 #include "mansearch.h"
47 * A query as passed to the search function.
50 char *manpath
; /* desired manual directory */
51 char *arch
; /* architecture */
52 char *sec
; /* manual section */
53 char *query
; /* unparsed query expression */
54 int equal
; /* match whole names, not substrings */
59 char **p
; /* array of available manpaths */
60 size_t psz
; /* number of available manpaths */
61 int isquery
; /* QUERY_STRING used, not PATH_INFO */
69 static void html_print(const char *);
70 static void html_putchar(char);
71 static int http_decode(char *);
72 static void parse_manpath_conf(struct req
*);
73 static void parse_path_info(struct req
*req
, const char *path
);
74 static void parse_query_string(struct req
*, const char *);
75 static void pg_error_badrequest(const char *);
76 static void pg_error_internal(void);
77 static void pg_index(const struct req
*);
78 static void pg_noresult(const struct req
*, const char *);
79 static void pg_redirect(const struct req
*, const char *);
80 static void pg_search(const struct req
*);
81 static void pg_searchres(const struct req
*,
82 struct manpage
*, size_t);
83 static void pg_show(struct req
*, const char *);
84 static void resp_begin_html(int, const char *, const char *);
85 static void resp_begin_http(int, const char *);
86 static void resp_catman(const struct req
*, const char *);
87 static void resp_copy(const char *);
88 static void resp_end_html(void);
89 static void resp_format(const struct req
*, const char *);
90 static void resp_searchform(const struct req
*, enum focus
);
91 static void resp_show(const struct req
*, const char *);
92 static void set_query_attr(char **, char **);
93 static int validate_filename(const char *);
94 static int validate_manpath(const struct req
*, const char *);
95 static int validate_urifrag(const char *);
97 static const char *scriptname
= SCRIPT_NAME
;
99 static const int sec_prios
[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
100 static const char *const sec_numbers
[] = {
101 "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
103 static const char *const sec_names
[] = {
105 "1 - General Commands",
107 "3 - Library Functions",
109 "4 - Device Drivers",
112 "7 - Miscellaneous Information",
113 "8 - System Manager\'s Manual",
114 "9 - Kernel Developer\'s Manual"
116 static const int sec_MAX
= sizeof(sec_names
) / sizeof(char *);
118 static const char *const arch_names
[] = {
119 "amd64", "alpha", "armv7", "arm64",
120 "hppa", "i386", "landisk",
121 "loongson", "luna88k", "macppc", "mips64",
122 "octeon", "sgi", "socppc", "sparc64",
123 "amiga", "arc", "armish", "arm32",
124 "atari", "aviion", "beagle", "cats",
126 "ia64", "mac68k", "mvme68k", "mvme88k",
127 "mvmeppc", "palm", "pc532", "pegasos",
128 "pmax", "powerpc", "solbourne", "sparc",
129 "sun3", "vax", "wgrisc", "x68k",
132 static const int arch_MAX
= sizeof(arch_names
) / sizeof(char *);
135 * Print a character, escaping HTML along the way.
136 * This will pass non-ASCII straight to output: be warned!
156 putchar((unsigned char)c
);
162 * Call through to html_putchar().
163 * Accepts NULL strings.
166 html_print(const char *p
)
176 * Transfer the responsibility for the allocated string *val
177 * to the query structure.
180 set_query_attr(char **attr
, char **val
)
193 * Parse the QUERY_STRING for key-value pairs
194 * and store the values into the query structure.
197 parse_query_string(struct req
*req
, const char *qs
)
203 req
->q
.manpath
= NULL
;
210 while (*qs
!= '\0') {
214 keysz
= strcspn(qs
, "=;&");
215 key
= mandoc_strndup(qs
, keysz
);
220 /* Parse one value. */
222 valsz
= strcspn(++qs
, ";&");
223 val
= mandoc_strndup(qs
, valsz
);
226 /* Decode and catch encoding errors. */
228 if ( ! (http_decode(key
) && http_decode(val
)))
231 /* Handle key-value pairs. */
233 if ( ! strcmp(key
, "query"))
234 set_query_attr(&req
->q
.query
, &val
);
236 else if ( ! strcmp(key
, "apropos"))
237 req
->q
.equal
= !strcmp(val
, "0");
239 else if ( ! strcmp(key
, "manpath")) {
241 if ( ! strncmp(val
, "OpenBSD ", 8)) {
247 set_query_attr(&req
->q
.manpath
, &val
);
250 else if ( ! (strcmp(key
, "sec")
252 && strcmp(key
, "sektion")
255 if ( ! strcmp(val
, "0"))
257 set_query_attr(&req
->q
.sec
, &val
);
260 else if ( ! strcmp(key
, "arch")) {
261 if ( ! strcmp(val
, "default"))
263 set_query_attr(&req
->q
.arch
, &val
);
267 * The key must be freed in any case.
268 * The val may have been handed over to the query
269 * structure, in which case it is now NULL.
283 * HTTP-decode a string. The standard explanation is that this turns
284 * "%4e+foo" into "n foo" in the regular way. This is done in-place
285 * over the allocated string.
297 for ( ; '\0' != *p
; p
++, q
++) {
299 if ('\0' == (hex
[0] = *(p
+ 1)))
301 if ('\0' == (hex
[1] = *(p
+ 2)))
303 if (1 != sscanf(hex
, "%x", &c
))
311 *q
= '+' == *p
? ' ' : *p
;
319 resp_begin_http(int code
, const char *msg
)
323 printf("Status: %d %s\r\n", code
, msg
);
325 printf("Content-Type: text/html; charset=utf-8\r\n"
326 "Cache-Control: no-cache\r\n"
327 "Pragma: no-cache\r\n"
334 resp_copy(const char *filename
)
340 if ((fd
= open(filename
, O_RDONLY
)) != -1) {
342 while ((sz
= read(fd
, buf
, sizeof(buf
))) > 0)
343 write(STDOUT_FILENO
, buf
, sz
);
349 resp_begin_html(int code
, const char *msg
, const char *file
)
353 resp_begin_http(code
, msg
);
355 printf("<!DOCTYPE html>\n"
358 " <meta charset=\"UTF-8\"/>\n"
359 " <link rel=\"stylesheet\" href=\"%s/mandoc.css\""
360 " type=\"text/css\" media=\"all\">\n"
364 if ((cp
= strrchr(file
, '/')) != NULL
)
366 if ((cp
= strrchr(file
, '.')) != NULL
) {
367 printf("%.*s(%s) - ", (int)(cp
- file
), file
, cp
+ 1);
369 printf("%s - ", file
);
371 printf("%s</title>\n"
376 resp_copy(MAN_DIR
"/header.html");
383 resp_copy(MAN_DIR
"/footer.html");
390 resp_searchform(const struct req
*req
, enum focus focus
)
394 printf("<form action=\"/%s\" method=\"get\">\n"
396 " <legend>Manual Page Search Parameters</legend>\n",
399 /* Write query input box. */
401 printf(" <input type=\"text\" name=\"query\" value=\"");
402 if (req
->q
.query
!= NULL
)
403 html_print(req
->q
.query
);
404 printf( "\" size=\"40\"");
405 if (focus
== FOCUS_QUERY
)
406 printf(" autofocus");
409 /* Write submission buttons. */
411 printf( " <button type=\"submit\" name=\"apropos\" value=\"0\">"
413 " <button type=\"submit\" name=\"apropos\" value=\"1\">"
417 /* Write section selector. */
419 puts(" <select name=\"sec\">");
420 for (i
= 0; i
< sec_MAX
; i
++) {
421 printf(" <option value=\"%s\"", sec_numbers
[i
]);
422 if (NULL
!= req
->q
.sec
&&
423 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
424 printf(" selected=\"selected\"");
425 printf(">%s</option>\n", sec_names
[i
]);
429 /* Write architecture selector. */
431 printf( " <select name=\"arch\">\n"
432 " <option value=\"default\"");
433 if (NULL
== req
->q
.arch
)
434 printf(" selected=\"selected\"");
435 puts(">All Architectures</option>");
436 for (i
= 0; i
< arch_MAX
; i
++) {
437 printf(" <option value=\"%s\"", arch_names
[i
]);
438 if (NULL
!= req
->q
.arch
&&
439 0 == strcmp(arch_names
[i
], req
->q
.arch
))
440 printf(" selected=\"selected\"");
441 printf(">%s</option>\n", arch_names
[i
]);
445 /* Write manpath selector. */
448 puts(" <select name=\"manpath\">");
449 for (i
= 0; i
< (int)req
->psz
; i
++) {
451 if (strcmp(req
->q
.manpath
, req
->p
[i
]) == 0)
452 printf("selected=\"selected\" ");
454 html_print(req
->p
[i
]);
456 html_print(req
->p
[i
]);
462 puts(" </fieldset>\n"
467 validate_urifrag(const char *frag
)
470 while ('\0' != *frag
) {
471 if ( ! (isalnum((unsigned char)*frag
) ||
472 '-' == *frag
|| '.' == *frag
||
473 '/' == *frag
|| '_' == *frag
))
481 validate_manpath(const struct req
*req
, const char* manpath
)
485 for (i
= 0; i
< req
->psz
; i
++)
486 if ( ! strcmp(manpath
, req
->p
[i
]))
493 validate_filename(const char *file
)
496 if ('.' == file
[0] && '/' == file
[1])
499 return ! (strstr(file
, "../") || strstr(file
, "/..") ||
500 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3)));
504 pg_index(const struct req
*req
)
507 resp_begin_html(200, NULL
, NULL
);
508 resp_searchform(req
, FOCUS_QUERY
);
510 "This web interface is documented in the\n"
511 "<a class=\"Xr\" href=\"/%s%sman.cgi.8\">man.cgi(8)</a>\n"
513 "<a class=\"Xr\" href=\"/%s%sapropos.1\">apropos(1)</a>\n"
514 "manual explains the query syntax.\n"
516 scriptname
, *scriptname
== '\0' ? "" : "/",
517 scriptname
, *scriptname
== '\0' ? "" : "/");
522 pg_noresult(const struct req
*req
, const char *msg
)
524 resp_begin_html(200, NULL
, NULL
);
525 resp_searchform(req
, FOCUS_QUERY
);
533 pg_error_badrequest(const char *msg
)
536 resp_begin_html(400, "Bad Request", NULL
);
537 puts("<h1>Bad Request</h1>\n"
540 printf("Try again from the\n"
541 "<a href=\"/%s\">main page</a>.\n"
547 pg_error_internal(void)
549 resp_begin_html(500, "Internal Server Error", NULL
);
550 puts("<p>Internal Server Error</p>");
555 pg_redirect(const struct req
*req
, const char *name
)
557 printf("Status: 303 See Other\r\n");
558 printf("Location: http://%s/", HTTP_HOST
);
559 if (*scriptname
!= '\0')
560 printf("%s/", scriptname
);
561 if (strcmp(req
->q
.manpath
, req
->p
[0]))
562 printf("%s/", req
->q
.manpath
);
563 if (req
->q
.arch
!= NULL
)
564 printf("%s/", req
->q
.arch
);
566 if (req
->q
.sec
!= NULL
)
567 printf(".%s", req
->q
.sec
);
568 printf("\r\nContent-Type: text/html; charset=utf-8\r\n\r\n");
572 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
574 char *arch
, *archend
;
577 int archprio
, archpriouse
;
580 for (i
= 0; i
< sz
; i
++) {
581 if (validate_filename(r
[i
].file
))
583 warnx("invalid filename %s in %s database",
584 r
[i
].file
, req
->q
.manpath
);
589 if (req
->isquery
&& sz
== 1) {
591 * If we have just one result, then jump there now
594 printf("Status: 303 See Other\r\n");
595 printf("Location: http://%s/%s%s%s/%s",
596 HTTP_HOST
, scriptname
,
597 *scriptname
== '\0' ? "" : "/",
598 req
->q
.manpath
, r
[0].file
);
600 "Content-Type: text/html; charset=utf-8\r\n"
606 * In man(1) mode, show one of the pages
607 * even if more than one is found.
611 if (req
->q
.equal
|| sz
== 1) {
614 for (i
= 0; i
< sz
; i
++) {
616 sec
+= strcspn(sec
, "123456789");
619 prio
= sec_prios
[sec
[0] - '1'];
622 if (req
->q
.arch
== NULL
) {
624 ((arch
= strchr(sec
+ 1, '/'))
626 ((archend
= strchr(arch
+ 1, '/'))
628 strncmp(arch
, "amd64/",
629 archend
- arch
) ? 2 : 1;
630 if (archprio
< archpriouse
) {
631 archpriouse
= archprio
;
636 if (archprio
> archpriouse
)
644 resp_begin_html(200, NULL
, r
[iuse
].file
);
646 resp_begin_html(200, NULL
, NULL
);
649 req
->q
.equal
|| sz
== 1 ? FOCUS_NONE
: FOCUS_QUERY
);
652 puts("<table class=\"results\">");
653 for (i
= 0; i
< sz
; i
++) {
656 "<a class=\"Xr\" href=\"/");
657 if (*scriptname
!= '\0')
658 printf("%s/", scriptname
);
659 if (strcmp(req
->q
.manpath
, req
->p
[0]))
660 printf("%s/", req
->q
.manpath
);
661 printf("%s\">", r
[i
].file
);
662 html_print(r
[i
].names
);
664 " <td><span class=\"Nd\">");
665 html_print(r
[i
].output
);
666 puts("</span></td>\n"
672 if (req
->q
.equal
|| sz
== 1) {
674 resp_show(req
, r
[iuse
].file
);
681 resp_catman(const struct req
*req
, const char *file
)
690 if ((f
= fopen(file
, "r")) == NULL
) {
691 puts("<p>You specified an invalid manual file.</p>");
695 puts("<div class=\"catman\">\n"
701 while ((len
= getline(&p
, &sz
, f
)) != -1) {
703 for (i
= 0; i
< len
- 1; i
++) {
705 * This means that the catpage is out of state.
706 * Ignore it and keep going (although the
710 if ('\b' == p
[i
] || '\n' == p
[i
])
714 * Print a regular character.
715 * Close out any bold/italic scopes.
716 * If we're in back-space mode, make sure we'll
717 * have something to enter when we backspace.
720 if ('\b' != p
[i
+ 1]) {
728 } else if (i
+ 2 >= len
)
746 * Handle funny behaviour troff-isms.
747 * These grok'd from the original man2html.c.
750 if (('+' == p
[i
] && 'o' == p
[i
+ 2]) ||
751 ('o' == p
[i
] && '+' == p
[i
+ 2]) ||
752 ('|' == p
[i
] && '=' == p
[i
+ 2]) ||
753 ('=' == p
[i
] && '|' == p
[i
+ 2]) ||
754 ('*' == p
[i
] && '=' == p
[i
+ 2]) ||
755 ('=' == p
[i
] && '*' == p
[i
+ 2]) ||
756 ('*' == p
[i
] && '|' == p
[i
+ 2]) ||
757 ('|' == p
[i
] && '*' == p
[i
+ 2])) {
766 } else if (('|' == p
[i
] && '-' == p
[i
+ 2]) ||
767 ('-' == p
[i
] && '|' == p
[i
+ 1]) ||
768 ('+' == p
[i
] && '-' == p
[i
+ 1]) ||
769 ('-' == p
[i
] && '+' == p
[i
+ 1]) ||
770 ('+' == p
[i
] && '|' == p
[i
+ 1]) ||
771 ('|' == p
[i
] && '+' == p
[i
+ 1])) {
795 * Clean up the last character.
796 * We can get to a newline; don't print that.
804 if (i
== len
- 1 && p
[i
] != '\n')
818 resp_format(const struct req
*req
, const char *file
)
820 struct manoutput conf
;
822 struct roff_man
*man
;
827 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
828 puts("<p>You specified an invalid manual file.</p>");
833 mp
= mparse_alloc(MPARSE_SO
| MPARSE_UTF8
| MPARSE_LATIN1
,
834 MANDOCLEVEL_BADARG
, NULL
, req
->q
.manpath
);
835 mparse_readfd(mp
, fd
, file
);
838 memset(&conf
, 0, sizeof(conf
));
840 conf
.style
= mandoc_strdup(CSS_DIR
"/mandoc.css");
841 usepath
= strcmp(req
->q
.manpath
, req
->p
[0]);
842 mandoc_asprintf(&conf
.man
, "/%s%s%%N.%%S",
843 usepath
? req
->q
.manpath
: "", usepath
? "/" : "");
845 mparse_result(mp
, &man
, NULL
);
847 warnx("fatal mandoc error: %s/%s", req
->q
.manpath
, file
);
854 vp
= html_alloc(&conf
);
856 if (man
->macroset
== MACROSET_MDOC
) {
872 resp_show(const struct req
*req
, const char *file
)
875 if ('.' == file
[0] && '/' == file
[1])
879 resp_catman(req
, file
);
881 resp_format(req
, file
);
885 pg_show(struct req
*req
, const char *fullpath
)
890 if ((file
= strchr(fullpath
, '/')) == NULL
) {
892 "You did not specify a page to show.");
895 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
898 if ( ! validate_manpath(req
, manpath
)) {
900 "You specified an invalid manpath.");
906 * Begin by chdir()ing into the manpath.
907 * This way we can pick up the database files, which are
908 * relative to the manpath root.
911 if (chdir(manpath
) == -1) {
912 warn("chdir %s", manpath
);
919 if ( ! validate_filename(file
)) {
921 "You specified an invalid manual file.");
925 resp_begin_html(200, NULL
, file
);
926 resp_searchform(req
, FOCUS_NONE
);
927 resp_show(req
, file
);
932 pg_search(const struct req
*req
)
934 struct mansearch search
;
935 struct manpaths paths
;
938 char *query
, *rp
, *wp
;
943 * Begin by chdir()ing into the root of the manpath.
944 * This way we can pick up the database files, which are
945 * relative to the manpath root.
948 if (chdir(req
->q
.manpath
) == -1) {
949 warn("chdir %s", req
->q
.manpath
);
954 search
.arch
= req
->q
.arch
;
955 search
.sec
= req
->q
.sec
;
956 search
.outkey
= "Nd";
957 search
.argmode
= req
->q
.equal
? ARG_NAME
: ARG_EXPR
;
958 search
.firstmatch
= 1;
961 paths
.paths
= mandoc_malloc(sizeof(char *));
962 paths
.paths
[0] = mandoc_strdup(".");
965 * Break apart at spaces with backslash-escaping.
970 rp
= query
= mandoc_strdup(req
->q
.query
);
972 while (isspace((unsigned char)*rp
))
976 argv
= mandoc_reallocarray(argv
, argc
+ 1, sizeof(char *));
977 argv
[argc
++] = wp
= rp
;
979 if (isspace((unsigned char)*rp
)) {
984 if (rp
[0] == '\\' && rp
[1] != '\0')
997 if (req
->isquery
&& req
->q
.equal
&& argc
== 1)
998 pg_redirect(req
, argv
[0]);
999 else if (mansearch(&search
, &paths
, argc
, argv
, &res
, &ressz
) == 0)
1000 pg_noresult(req
, "You entered an invalid query.");
1001 else if (ressz
== 0)
1002 pg_noresult(req
, "No results found.");
1004 pg_searchres(req
, res
, ressz
);
1007 mansearch_free(res
, ressz
);
1008 free(paths
.paths
[0]);
1016 struct itimerval itimer
;
1018 const char *querystring
;
1023 * The "rpath" pledge could be revoked after mparse_readfd()
1024 * if the file desciptor to "/footer.html" would be opened
1025 * up front, but it's probably not worth the complication
1026 * of the code it would cause: it would require scattering
1027 * pledge() calls in multiple low-level resp_*() functions.
1030 if (pledge("stdio rpath", NULL
) == -1) {
1032 pg_error_internal();
1033 return EXIT_FAILURE
;
1037 /* Poor man's ReDoS mitigation. */
1039 itimer
.it_value
.tv_sec
= 2;
1040 itimer
.it_value
.tv_usec
= 0;
1041 itimer
.it_interval
.tv_sec
= 2;
1042 itimer
.it_interval
.tv_usec
= 0;
1043 if (setitimer(ITIMER_VIRTUAL
, &itimer
, NULL
) == -1) {
1045 pg_error_internal();
1046 return EXIT_FAILURE
;
1050 * First we change directory into the MAN_DIR so that
1051 * subsequent scanning for manpath directories is rooted
1052 * relative to the same position.
1055 if (chdir(MAN_DIR
) == -1) {
1056 warn("MAN_DIR: %s", MAN_DIR
);
1057 pg_error_internal();
1058 return EXIT_FAILURE
;
1061 memset(&req
, 0, sizeof(struct req
));
1063 parse_manpath_conf(&req
);
1065 /* Parse the path info and the query string. */
1067 if ((path
= getenv("PATH_INFO")) == NULL
)
1069 else if (*path
== '/')
1072 if (*path
!= '\0') {
1073 parse_path_info(&req
, path
);
1074 if (req
.q
.manpath
== NULL
|| access(path
, F_OK
) == -1)
1076 } else if ((querystring
= getenv("QUERY_STRING")) != NULL
)
1077 parse_query_string(&req
, querystring
);
1079 /* Validate parsed data and add defaults. */
1081 if (req
.q
.manpath
== NULL
)
1082 req
.q
.manpath
= mandoc_strdup(req
.p
[0]);
1083 else if ( ! validate_manpath(&req
, req
.q
.manpath
)) {
1084 pg_error_badrequest(
1085 "You specified an invalid manpath.");
1086 return EXIT_FAILURE
;
1089 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1090 pg_error_badrequest(
1091 "You specified an invalid architecture.");
1092 return EXIT_FAILURE
;
1095 /* Dispatch to the three different pages. */
1098 pg_show(&req
, path
);
1099 else if (NULL
!= req
.q
.query
)
1104 free(req
.q
.manpath
);
1108 for (i
= 0; i
< (int)req
.psz
; i
++)
1111 return EXIT_SUCCESS
;
1115 * If PATH_INFO is not a file name, translate it to a query.
1118 parse_path_info(struct req
*req
, const char *path
)
1125 req
->q
.manpath
= mandoc_strdup(path
);
1128 /* Mandatory manual page name. */
1129 if ((req
->q
.query
= strrchr(req
->q
.manpath
, '/')) == NULL
) {
1130 req
->q
.query
= req
->q
.manpath
;
1131 req
->q
.manpath
= NULL
;
1133 *req
->q
.query
++ = '\0';
1135 /* Optional trailing section. */
1136 if ((req
->q
.sec
= strrchr(req
->q
.query
, '.')) != NULL
) {
1137 if(isdigit((unsigned char)req
->q
.sec
[1])) {
1138 *req
->q
.sec
++ = '\0';
1139 req
->q
.sec
= mandoc_strdup(req
->q
.sec
);
1144 /* Handle the case of name[.section] only. */
1145 if (req
->q
.manpath
== NULL
)
1147 req
->q
.query
= mandoc_strdup(req
->q
.query
);
1149 /* Split directory components. */
1150 dir
[i
= 0] = req
->q
.manpath
;
1151 while ((dir
[i
+ 1] = strchr(dir
[i
], '/')) != NULL
) {
1153 pg_error_badrequest(
1154 "You specified too many directory components.");
1160 /* Optional manpath. */
1161 if ((i
= validate_manpath(req
, req
->q
.manpath
)) == 0)
1162 req
->q
.manpath
= NULL
;
1163 else if (dir
[1] == NULL
)
1166 /* Optional section. */
1167 if (strncmp(dir
[i
], "man", 3) == 0) {
1169 req
->q
.sec
= mandoc_strdup(dir
[i
++] + 3);
1171 if (dir
[i
] == NULL
) {
1172 if (req
->q
.manpath
== NULL
)
1176 if (dir
[i
+ 1] != NULL
) {
1177 pg_error_badrequest(
1178 "You specified an invalid directory component.");
1182 /* Optional architecture. */
1184 req
->q
.arch
= mandoc_strdup(dir
[i
]);
1185 if (req
->q
.manpath
== NULL
)
1188 req
->q
.arch
= dir
[0];
1192 * Scan for indexable paths.
1195 parse_manpath_conf(struct req
*req
)
1202 if ((fp
= fopen("manpath.conf", "r")) == NULL
) {
1203 warn("%s/manpath.conf", MAN_DIR
);
1204 pg_error_internal();
1211 while ((len
= getline(&dp
, &dpsz
, fp
)) != -1) {
1212 if (dp
[len
- 1] == '\n')
1214 req
->p
= mandoc_realloc(req
->p
,
1215 (req
->psz
+ 1) * sizeof(char *));
1216 if ( ! validate_urifrag(dp
)) {
1217 warnx("%s/manpath.conf contains "
1218 "unsafe path \"%s\"", MAN_DIR
, dp
);
1219 pg_error_internal();
1222 if (strchr(dp
, '/') != NULL
) {
1223 warnx("%s/manpath.conf contains "
1224 "path with slash \"%s\"", MAN_DIR
, dp
);
1225 pg_error_internal();
1228 req
->p
[req
->psz
++] = dp
;
1234 if (req
->p
== NULL
) {
1235 warnx("%s/manpath.conf is empty", MAN_DIR
);
1236 pg_error_internal();