Split scheme detection logic, use where possible.

Avoid parsing URLs in multiple places.
This commit is contained in:
Érico Rolim 2020-09-24 05:05:34 -03:00
parent d95fdcddd6
commit 01eaa937fe
5 changed files with 88 additions and 32 deletions

40
gemi.c
View File

@ -114,9 +114,14 @@ int main(int argc, char **argv)
.debug = debug, .no_strip = no_strip,
.type = GEMINI_CONN};
rv = send_and_receive(&ci);
// free resources
bearssl_free_certs(&btas, num_ta);
close(socket);
bearssl_free_certs(&btas, num_ta);
if (rv == EXIT_FAILURE) {
goto early_out;
}
// generic way of outputting data:
// - if using FILE backend, offset is 0 and nothing happens
@ -130,32 +135,49 @@ int main(int argc, char **argv)
fputs("Input desired link (starts at 0): ", stderr);
int in;
int err = scanf("%d", &in);
if (err != 1) {
if (err == EOF) {
// exit cleanly on EOF
goto early_out;
} else if (err != 1) {
// TODO: ? option to show all found links
fputs("\nBad input!\n", stderr);
rv = EXIT_FAILURE;
goto early_out;
}
fprintf(stderr, "Selected link: %d\n", in);
// if it leaves this part, it's a bad exit
rv = EXIT_FAILURE;
if (in >= 0 && in < n) {
// valid link number
char *new_path = get_gemini_node_by_n(head, in)->path;
fprintf(stderr, "Selected link: %s\n", new_path);
char *new_argv[] = {progpath, "-b", new_path, NULL};
if (strstr(new_path, "gemini://") == NULL) {
// in is a valid link number
char *new_arg = get_gemini_node_by_n(head, in)->path;
fprintf(stderr, "Selected link: %s\n", new_arg);
char *new_argv[] = {progpath, "-b", new_arg, NULL};
int new_portn = get_port_from_link(new_arg);
if (new_portn == NO_INFO_PORT) {
// link is not absolute path
// TODO: path resolution
// TODO: better error msgs
char *new_url = calloc(1, strlen(url) + strlen(new_path) + 1);
// TODO: treat error codes from server -> bad links (lacking trailing /, for example),
// can probably be solved locally with a smarter client
// Perhaps make header parsing a virtual function kind of thing?
char *new_url = calloc(1, strlen(url) + strlen(new_arg) + 1);
if (new_url == NULL) {
perror("calloc()");
return rv;
}
sprintf(new_url, "%s%s", url, new_path);
sprintf(new_url, "%s%s", url, new_arg);
new_argv[2] = new_url;
} else if (new_portn != GEMINI_PORT) {
fputs("\nUnsupported protocol!\n", stderr);
goto early_out;
}
execvp(progpath, new_argv);
} else {
fputs("\nBad number!\n", stderr);
}
}
}

View File

@ -9,7 +9,7 @@ struct gemini_link_node {
};
/* gemini.c */
int get_links_from_gmi(const char*, struct gemini_link_node **);
int get_links_from_gmi(const char *, struct gemini_link_node **);
struct gemini_link_node *get_gemini_node_by_n(struct gemini_link_node *, int);
#endif // __GEMINI_H_

3
purr.h
View File

@ -10,6 +10,8 @@
#define HTTP_PORT 80
#define HTTPS_PORT 443
#define GEMINI_PORT 1965
#define UNKNOWN_PORT -2
#define NO_INFO_PORT -1
#define HEADER_MAX_LEN 8192
@ -54,6 +56,7 @@ int socket_read(void *, uint8_t *, size_t);
int socket_write(void *, const uint8_t *, size_t);
/* urls.c */
int get_port_from_link(const char *);
int clean_up_link(const char *, char **, char **, char **, char **);
int get_encryption_params(char *, uint8_t **, uint8_t **);
int host_connect(const char *, const char *, bool);

13
tests.c
View File

@ -82,6 +82,19 @@ int main()
free(path); path = NULL;
free(port); port = NULL;
dirty = "hello.com";
portn = clean_up_link(dirty, &scheme, &clean, &path, &port);
rv = compare_strings("http://", scheme, "clean_up_link") ? 1 : rv;
rv = compare_strings("hello.com", clean, "clean_up_link") ? 1 : rv;
rv = compare_strings("/", path, "clean_up_link") ? 1 : rv;
rv = compare_strings("80", port, "clean_up_link") ? 1 : rv;
assert(portn == HTTP_PORT);
free(scheme); scheme = NULL;
free(clean); clean = NULL;
free(path); path = NULL;
free(port); port = NULL;
dirty = "https://bsd.ac/paste.html#sieqaqk_73fe_df51";
portn = clean_up_link(dirty, &scheme, &clean, &path, &port);
rv = compare_strings("https://", scheme, "clean_up_link") ? 1 : rv;

62
urls.c
View File

@ -16,6 +16,32 @@ static const char *http_sch = "http://";
static const char *https_sch = "https://";
static const char *gemini_sch = "gemini://";
int get_port_from_link(const char *url)
{
int portn = -1;
const char *scheme_separator = strstr(url, "://");
if (scheme_separator) {
// found protocol specified, otherwise return error
if (scheme_separator - url + 3 > MAX_SHORTY_LEN) {
fputs("get_port_from_link(): scheme is too long!\n", stderr);
return portn;
}
size_t scheme_len = scheme_separator - url + 3;
if (memcmp(url, https_sch, scheme_len) == 0) {
portn = HTTPS_PORT;
} else if (memcmp(url, http_sch, scheme_len) == 0) {
portn = HTTP_PORT;
} else if (memcmp(url, gemini_sch, scheme_len) == 0) {
portn = GEMINI_PORT;
} else {
portn = UNKNOWN_PORT;
fputs("clean_up_link(): unknown protocol!\n", stderr);
}
}
return portn;
}
/*
* This function cleans up the link in dirty, providing each of its parts in the
* buffers pointed to by schemep, cleanp, pathp and portp.
@ -46,32 +72,24 @@ int clean_up_link(const char *dirty, char **schemep, char **cleanp, char **pathp
*portp = port;
// detect protocol, remove protocol prefix
const char *scheme_separator = strstr(dirty, "://");
const char *start_link = NULL;
if (scheme_separator == NULL) {
// no protocol specified, default to HTTP
portn = get_port_from_link(dirty);
bool get_scheme_len = true;
if (portn == UNKNOWN_PORT) {
fputs("clean_up_link(): unknown protocol!\n", stderr);
return portn;
} else if (portn == NO_INFO_PORT || portn == HTTP_PORT) {
// no scheme defined -> default to HTTP
// if no scheme defined -> no need to advance scheme
get_scheme_len = portn != NO_INFO_PORT;
portn = HTTP_PORT;
strcpy(scheme, http_sch);
start_link = dirty;
} else {
if (scheme_separator - dirty + 3 > MAX_SHORTY_LEN) {
fputs("clean_up_link(): scheme is too long!\n", stderr);
return -1;
}
memcpy(scheme, dirty, scheme_separator - dirty + 3);
if (strcmp(scheme, https_sch) == 0) {
portn = HTTPS_PORT;
} else if (strcmp(scheme, http_sch) == 0) {
portn = HTTP_PORT;
} else if (strcmp(scheme, gemini_sch) == 0) {
portn = GEMINI_PORT;
} else {
fputs("clean_up_link(): unknown protocol!\n", stderr);
return -1;
}
start_link = dirty + strlen(scheme);
} else if (portn == HTTPS_PORT) {
strcpy(scheme, https_sch);
} else if (portn == GEMINI_PORT) {
strcpy(scheme, gemini_sch);
}
start_link = dirty + (get_scheme_len ? strlen(scheme) : 0);
// maximum size necessary
// use strncpy for portability