You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

http_util.c 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*-
  2. * Copyright 2019 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "libutil/http_util.h"
  17. #include "libutil/printf.h"
  18. #include "libutil/util.h"
  19. static const gchar *http_week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  20. static const gchar *http_month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  21. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  22. glong
  23. rspamd_http_date_format (gchar *buf, gsize len, time_t time)
  24. {
  25. struct tm tms;
  26. rspamd_gmtime (time, &tms);
  27. return rspamd_snprintf (buf, len, "%s, %02d %s %4d %02d:%02d:%02d GMT",
  28. http_week[tms.tm_wday], tms.tm_mday,
  29. http_month[tms.tm_mon], tms.tm_year + 1900,
  30. tms.tm_hour, tms.tm_min, tms.tm_sec);
  31. }
  32. void
  33. rspamd_http_normalize_path_inplace (gchar *path, guint len, guint *nlen)
  34. {
  35. const gchar *p, *end, *slash = NULL, *dot = NULL;
  36. gchar *o;
  37. enum {
  38. st_normal = 0,
  39. st_got_dot,
  40. st_got_dot_dot,
  41. st_got_slash,
  42. st_got_slash_slash,
  43. } state = st_normal;
  44. p = path;
  45. end = path + len;
  46. o = path;
  47. while (p < end) {
  48. switch (state) {
  49. case st_normal:
  50. if (G_UNLIKELY (*p == '/')) {
  51. state = st_got_slash;
  52. slash = p;
  53. }
  54. else if (G_UNLIKELY (*p == '.')) {
  55. state = st_got_dot;
  56. dot = p;
  57. }
  58. else {
  59. *o++ = *p;
  60. }
  61. p ++;
  62. break;
  63. case st_got_slash:
  64. if (G_UNLIKELY (*p == '/')) {
  65. /* Ignore double slash */
  66. *o++ = *p;
  67. state = st_got_slash_slash;
  68. }
  69. else if (G_UNLIKELY (*p == '.')) {
  70. dot = p;
  71. state = st_got_dot;
  72. }
  73. else {
  74. *o++ = '/';
  75. *o++ = *p;
  76. slash = NULL;
  77. dot = NULL;
  78. state = st_normal;
  79. }
  80. p ++;
  81. break;
  82. case st_got_slash_slash:
  83. if (G_LIKELY (*p != '/')) {
  84. slash = p - 1;
  85. dot = NULL;
  86. state = st_normal;
  87. continue;
  88. }
  89. p ++;
  90. break;
  91. case st_got_dot:
  92. if (G_UNLIKELY (*p == '/')) {
  93. /* Remove any /./ or ./ paths */
  94. if (((o > path && *(o - 1) != '/') || (o == path)) && slash) {
  95. /* Preserve one slash */
  96. *o++ = '/';
  97. }
  98. slash = p;
  99. dot = NULL;
  100. /* Ignore last slash */
  101. state = st_normal;
  102. }
  103. else if (*p == '.') {
  104. /* Double dot character */
  105. state = st_got_dot_dot;
  106. }
  107. else {
  108. /* We have something like .some or /.some */
  109. if (dot && p > dot) {
  110. if (slash == dot - 1 && (o > path && *(o - 1) != '/')) {
  111. /* /.blah */
  112. memmove (o, slash, p - slash);
  113. o += p - slash;
  114. }
  115. else {
  116. memmove (o, dot, p - dot);
  117. o += p - dot;
  118. }
  119. }
  120. slash = NULL;
  121. dot = NULL;
  122. state = st_normal;
  123. continue;
  124. }
  125. p ++;
  126. break;
  127. case st_got_dot_dot:
  128. if (*p == '/') {
  129. /* We have something like /../ or ../ */
  130. if (slash) {
  131. /* We need to remove the last component from o if it is there */
  132. if (o > path + 2 && *(o - 1) == '/') {
  133. slash = rspamd_memrchr (path, '/', o - path - 2);
  134. }
  135. else if (o > path + 1) {
  136. slash = rspamd_memrchr (path, '/', o - path - 1);
  137. }
  138. else {
  139. slash = NULL;
  140. }
  141. if (slash) {
  142. o = (gchar *)slash;
  143. }
  144. /* Otherwise we keep these dots */
  145. slash = p;
  146. state = st_got_slash;
  147. }
  148. else {
  149. /* We have something like bla../, so we need to copy it as is */
  150. if (o > path && dot && p > dot) {
  151. memmove (o, dot, p - dot);
  152. o += p - dot;
  153. }
  154. slash = NULL;
  155. dot = NULL;
  156. state = st_normal;
  157. continue;
  158. }
  159. }
  160. else {
  161. /* We have something like ..bla or ... */
  162. if (slash) {
  163. *o ++ = '/';
  164. }
  165. if (dot && p > dot) {
  166. memmove (o, dot, p - dot);
  167. o += p - dot;
  168. }
  169. slash = NULL;
  170. dot = NULL;
  171. state = st_normal;
  172. continue;
  173. }
  174. p ++;
  175. break;
  176. }
  177. }
  178. /* Leftover */
  179. switch (state) {
  180. case st_got_dot_dot:
  181. /* Trailing .. */
  182. if (slash) {
  183. /* We need to remove the last component from o if it is there */
  184. if (o > path + 2 && *(o - 1) == '/') {
  185. slash = rspamd_memrchr (path, '/', o - path - 2);
  186. }
  187. else if (o > path + 1) {
  188. slash = rspamd_memrchr (path, '/', o - path - 1);
  189. }
  190. else {
  191. if (o == path) {
  192. /* Corner case */
  193. *o++ = '/';
  194. }
  195. slash = NULL;
  196. }
  197. if (slash) {
  198. /* Remove last / */
  199. o = (gchar *)slash;
  200. }
  201. }
  202. else {
  203. /* Corner case */
  204. if (o == path) {
  205. *o++ = '/';
  206. }
  207. else {
  208. if (dot && p > dot) {
  209. memmove (o, dot, p - dot);
  210. o += p - dot;
  211. }
  212. }
  213. }
  214. break;
  215. case st_got_slash:
  216. *o++ = '/';
  217. break;
  218. default:
  219. if (o > path + 1 && *(o - 1) == '/') {
  220. o --;
  221. }
  222. break;
  223. }
  224. if (nlen) {
  225. *nlen = (o - path);
  226. }
  227. }