uri.c (3320B)
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+httpc@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #include "httpc.h"
- #include <assert.h>
- #include <errno.h>
- #include <ctype.h> // isalnum, iscntrl
- #include <string.h> // strlen, strerror, strdup
- #include <stdlib.h> // free
- // rfc1945.html#section-3.2
- static inline int
- is_reserved(int c)
- {
- return c == ';' || c == '/' || c == '?' || c == ':' || c == '@' || c == '&' | c == '=' | c == '+';
- }
- static inline int
- is_extra(int c)
- {
- return c == '!' || c == '*' || c == '\'' || c == '(' || c == ')' || c == ',';
- }
- static inline int
- is_safe(int c)
- {
- return c == '$' || c == '-' || c == '_' || c == '.';
- }
- static inline int
- is_unsafe(int c)
- {
- return iscntrl(c) || c == ' ' || c == '#' || c == '%' || c == '<' || c == '>';
- }
- static int
- is_national(int c)
- {
- return !isalnum(c) && !is_reserved(c) && !is_extra(c) && !is_safe(c) && !is_unsafe(c);
- }
- static int
- is_unreserved(int c)
- {
- return isalnum(c) || is_safe(c) || is_extra(c) || is_national(c);
- }
- void
- decode_uri_finish(struct URI *uri)
- {
- if(uri == NULL) return;
- free(uri->scheme);
- free(uri->host);
- if(uri->port != NULL) free(uri->port);
- }
- // Caller must call decode_uri_finish
- struct URI *
- decode_uri(const char *ori, char **err)
- {
- static struct URI uri = {
- .scheme = NULL,
- .host = NULL,
- .port = NULL,
- .path = NULL,
- .fragment = NULL
- };
- if(ori == NULL)
- {
- *err = "Null pointer passed";
- return NULL;
- }
- errno = 0;
- char *arg = strdup(ori);
- if(arg == NULL)
- {
- *err = strerror(errno);
- return NULL;
- }
- int i = 0;
- int len = strlen(arg);
- // scheme
- for(;i<len;i++)
- {
- char c = arg[i];
- if(isalnum(c) || c == '+' || c == '-' || c == '.') continue;
- if(c != ':')
- {
- static char error[] = "Invalid char in scheme: _";
- error[sizeof(error)-2] = c;
- *err = error;
- free(arg);
- return NULL;
- }
- arg[i++] = '\0';
- uri.scheme = arg;
- break;
- }
- if(arg[i] != '/' || arg[i+1] != '/')
- {
- *err = "Missing :// after scheme";
- free(arg);
- return NULL;
- } else i+=2;
- // IPv6 address
- if(arg[i] == '[')
- {
- char *addr = arg+i+1;
- for(;i<len && arg[i] != ']';i++);
- if(arg[i] != ']')
- {
- *err = "Unmatched [ in host";
- free(arg);
- return NULL;
- };
- arg[i] = '\0';
- i++;
- uri.host = strdup(addr);
- if(uri.host == NULL)
- {
- *err = "host = strdup(...) failed";
- free(arg);
- return NULL;
- }
- }
- else
- {
- char *addr = arg+i;
- size_t addr_len = 0;
- // hostname or IPv4 address
- for(;i<len;i++,addr_len++)
- {
- char c = arg[i];
- if(c == '/' || c == ':') break;
- }
- uri.host = strndup(addr, addr_len);
- if(uri.host == NULL)
- {
- *err = "host = strdup(...) failed";
- free(arg);
- return NULL;
- }
- }
- // port (optional)
- if(arg[i] == ':')
- {
- arg[i++] = '\0';
- char *port = arg+i;
- size_t port_len = 0;
- for(;i<len;i++,port_len++)
- {
- char c = arg[i];
- if(c == '/') break;
- if(!isdigit(c))
- {
- static char error[] = "Invalid char in port: _";
- error[sizeof(error)-2] = c;
- *err = error;
- free(arg);
- return NULL;
- }
- }
- uri.port = strndup(port, port_len);
- if(uri.port == NULL)
- {
- *err = "port = strdup(...) failed";
- free(arg);
- return NULL;
- }
- }
- // FIXME: Check for invalid characters
- uri.path = arg+i;
- return &uri;
- }