diff --git a/utils.c b/utils.c index 50e2191..740f545 100644 --- a/utils.c +++ b/utils.c @@ -1,5 +1,8 @@ +#include + #include #include +#include #include #include #include @@ -115,3 +118,15 @@ print_file(FILE *fd) datasent += fwrite(buffer, 1, nread, stdout); return datasent; } + +void +getsubexp(const char *str, regmatch_t m, char *dst) +{ + size_t len = 0; + + if (m.rm_eo > m.rm_so) { /* skip empty substring */ + len = m.rm_eo - m.rm_so; + memcpy(dst, str + m.rm_so, len); + dst[len] = '\0'; + } +} diff --git a/utils.h b/utils.h index 982a3e0..72d64e8 100644 --- a/utils.h +++ b/utils.h @@ -1,3 +1,4 @@ +void getsubexp(const char *, regmatch_t, char *); void echdir (const char *); void epledge(const char *, const char *); void eunveil(const char *, const char *); diff --git a/vger.c b/vger.c index d02f420..04b3c12 100644 --- a/vger.c +++ b/vger.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -503,3 +504,54 @@ get_query(char *path, char *query, size_t querysiz) } return query; } + +void +split_request(const char *request, char *hostname, char *path, char *query) +{ + regex_t greg = {0}; /* compiled gemini regex */ + regmatch_t *match = {0}; /* matches founds */ + size_t nmatch = 4; /* number of substrings to look for */ + char buf[BUFSIZ] = {'\0'}; /* to handle error messages */ + int ret = 0; + + const char *gemini_regex = "^gemini://+([^/|^\?]*)/*([^\?]*)[\?]?(.*)$"; + /* + * ^gemini://+ : in case of gemini:/// + * 1: hostname + * ([^/|^\?]*) : + * catch everything, stop when / or ? is found + * then skip multiple / + * 2: path + * ([^\?]*) : + * catch everything and stop at ? if any + * 3 : query + * [\?]?(.*)$: + * catch everything after ? if any + */ + + if ((ret = regcomp(&greg, gemini_regex, REG_EXTENDED)) != 0) { + regerror(ret, &greg, buf, sizeof(buf)); + regfree(&greg); + status(50, "Internal server error"); + stop(EXIT_FAILURE, "%s", buf); + } + + if ((ret = regexec(&greg, request, nmatch, match, 0)) != 0) { + regerror(ret, &greg, buf, sizeof(buf)); + regfree(&greg); + status(59, "Malformed request"); + stop(EXIT_FAILURE, "Malformed request, error:%s", buf); + } + + /* one may want to check the return of getsubexp + * and change memcpy to strlcpy + * to make sure we didn't try to copy too long + * and that string isn't trunkated. + * It is unlikely to happen since dest string are as long as request + */ + getsubexp(request, match[1], hostname); + getsubexp(request, match[2], path); + getsubexp(request, match[3], query); + + regfree(&greg); +} diff --git a/vger.h b/vger.h index 0585e4a..7a9f5db 100644 --- a/vger.h +++ b/vger.h @@ -31,6 +31,7 @@ char * get_query(char *, char *, size_t); void status(const int, const char *, ...); void strip_trailing_slash(char *); int uridecode (char *); +void split_request(const char *, char *, char *, char *); void stop(const int, const char *, ...); #endif // vger_h_INCLUDED