logo

httpc

barebones HTTP client, intended for simple usage like downloading filesgit clone https://hacktivis.me/git/httpc.git

uri.c (3320B)


  1. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+httpc@hacktivis.me>
  2. // SPDX-License-Identifier: MPL-2.0
  3. #define _POSIX_C_SOURCE 200809L
  4. #include "httpc.h"
  5. #include <assert.h>
  6. #include <errno.h>
  7. #include <ctype.h> // isalnum, iscntrl
  8. #include <string.h> // strlen, strerror, strdup
  9. #include <stdlib.h> // free
  10. // rfc1945.html#section-3.2
  11. static inline int
  12. is_reserved(int c)
  13. {
  14. return c == ';' || c == '/' || c == '?' || c == ':' || c == '@' || c == '&' | c == '=' | c == '+';
  15. }
  16. static inline int
  17. is_extra(int c)
  18. {
  19. return c == '!' || c == '*' || c == '\'' || c == '(' || c == ')' || c == ',';
  20. }
  21. static inline int
  22. is_safe(int c)
  23. {
  24. return c == '$' || c == '-' || c == '_' || c == '.';
  25. }
  26. static inline int
  27. is_unsafe(int c)
  28. {
  29. return iscntrl(c) || c == ' ' || c == '#' || c == '%' || c == '<' || c == '>';
  30. }
  31. static int
  32. is_national(int c)
  33. {
  34. return !isalnum(c) && !is_reserved(c) && !is_extra(c) && !is_safe(c) && !is_unsafe(c);
  35. }
  36. static int
  37. is_unreserved(int c)
  38. {
  39. return isalnum(c) || is_safe(c) || is_extra(c) || is_national(c);
  40. }
  41. void
  42. decode_uri_finish(struct URI *uri)
  43. {
  44. if(uri == NULL) return;
  45. free(uri->scheme);
  46. free(uri->host);
  47. if(uri->port != NULL) free(uri->port);
  48. }
  49. // Caller must call decode_uri_finish
  50. struct URI *
  51. decode_uri(const char *ori, char **err)
  52. {
  53. static struct URI uri = {
  54. .scheme = NULL,
  55. .host = NULL,
  56. .port = NULL,
  57. .path = NULL,
  58. .fragment = NULL
  59. };
  60. if(ori == NULL)
  61. {
  62. *err = "Null pointer passed";
  63. return NULL;
  64. }
  65. errno = 0;
  66. char *arg = strdup(ori);
  67. if(arg == NULL)
  68. {
  69. *err = strerror(errno);
  70. return NULL;
  71. }
  72. int i = 0;
  73. int len = strlen(arg);
  74. // scheme
  75. for(;i<len;i++)
  76. {
  77. char c = arg[i];
  78. if(isalnum(c) || c == '+' || c == '-' || c == '.') continue;
  79. if(c != ':')
  80. {
  81. static char error[] = "Invalid char in scheme: _";
  82. error[sizeof(error)-2] = c;
  83. *err = error;
  84. free(arg);
  85. return NULL;
  86. }
  87. arg[i++] = '\0';
  88. uri.scheme = arg;
  89. break;
  90. }
  91. if(arg[i] != '/' || arg[i+1] != '/')
  92. {
  93. *err = "Missing :// after scheme";
  94. free(arg);
  95. return NULL;
  96. } else i+=2;
  97. // IPv6 address
  98. if(arg[i] == '[')
  99. {
  100. char *addr = arg+i+1;
  101. for(;i<len && arg[i] != ']';i++);
  102. if(arg[i] != ']')
  103. {
  104. *err = "Unmatched [ in host";
  105. free(arg);
  106. return NULL;
  107. };
  108. arg[i] = '\0';
  109. i++;
  110. uri.host = strdup(addr);
  111. if(uri.host == NULL)
  112. {
  113. *err = "host = strdup(...) failed";
  114. free(arg);
  115. return NULL;
  116. }
  117. }
  118. else
  119. {
  120. char *addr = arg+i;
  121. size_t addr_len = 0;
  122. // hostname or IPv4 address
  123. for(;i<len;i++,addr_len++)
  124. {
  125. char c = arg[i];
  126. if(c == '/' || c == ':') break;
  127. }
  128. uri.host = strndup(addr, addr_len);
  129. if(uri.host == NULL)
  130. {
  131. *err = "host = strdup(...) failed";
  132. free(arg);
  133. return NULL;
  134. }
  135. }
  136. // port (optional)
  137. if(arg[i] == ':')
  138. {
  139. arg[i++] = '\0';
  140. char *port = arg+i;
  141. size_t port_len = 0;
  142. for(;i<len;i++,port_len++)
  143. {
  144. char c = arg[i];
  145. if(c == '/') break;
  146. if(!isdigit(c))
  147. {
  148. static char error[] = "Invalid char in port: _";
  149. error[sizeof(error)-2] = c;
  150. *err = error;
  151. free(arg);
  152. return NULL;
  153. }
  154. }
  155. uri.port = strndup(port, port_len);
  156. if(uri.port == NULL)
  157. {
  158. *err = "port = strdup(...) failed";
  159. free(arg);
  160. return NULL;
  161. }
  162. }
  163. // FIXME: Check for invalid characters
  164. uri.path = arg+i;
  165. return &uri;
  166. }