| 1 |
/* |
/* |
| 2 |
* HTTP Header Lint |
* HTTP Header Lint |
| 3 |
* Licensed under the same license as Curl |
* Licensed under the MIT License |
| 4 |
* http://curl.haxx.se/docs/copyright.html |
* http://www.opensource.org/licenses/mit-license |
| 5 |
* Copyright 2003 James Bursa <bursa@users.sourceforge.net> |
* Copyright 2004 James Bursa <bursa@users.sourceforge.net> |
| 6 |
*/ |
*/ |
| 7 |
|
|
| 8 |
/* |
/* |
| 37 |
char error_buffer[CURL_ERROR_SIZE]; |
char error_buffer[CURL_ERROR_SIZE]; |
| 38 |
regex_t re_status_line, re_token, re_token_value, re_content_type, re_ugly, |
regex_t re_status_line, re_token, re_token_value, re_content_type, re_ugly, |
| 39 |
re_absolute_uri, re_etag, re_server, re_transfer_coding, re_upgrade, |
re_absolute_uri, re_etag, re_server, re_transfer_coding, re_upgrade, |
| 40 |
re_rfc1123, re_rfc1036, re_asctime; |
re_rfc1123, re_rfc1036, re_asctime, re_cookie_nameval, re_cookie_expires; |
| 41 |
|
|
| 42 |
|
|
| 43 |
void init(void); |
void init(void); |
| 81 |
void header_upgrade(const char *s); |
void header_upgrade(const char *s); |
| 82 |
void header_vary(const char *s); |
void header_vary(const char *s); |
| 83 |
void header_via(const char *s); |
void header_via(const char *s); |
| 84 |
|
void header_set_cookie(const char *s); |
| 85 |
void die(const char *error); |
void die(const char *error); |
| 86 |
void warning(const char *message); |
void warning(const char *message); |
| 87 |
void error(const char *message); |
void error(const char *message); |
| 115 |
{ "Pragma", header_pragma, 0, 0 }, |
{ "Pragma", header_pragma, 0, 0 }, |
| 116 |
{ "Retry-After", header_retry_after, 0, 0 }, |
{ "Retry-After", header_retry_after, 0, 0 }, |
| 117 |
{ "Server", header_server, 0, 0 }, |
{ "Server", header_server, 0, 0 }, |
| 118 |
|
{ "Set-Cookie", header_set_cookie, 0, 0 }, |
| 119 |
{ "Trailer", header_trailer, 0, 0 }, |
{ "Trailer", header_trailer, 0, 0 }, |
| 120 |
{ "Transfer-Encoding", header_transfer_encoding, 0, 0 }, |
{ "Transfer-Encoding", header_transfer_encoding, 0, 0 }, |
| 121 |
{ "Upgrade", header_upgrade, 0, 0 }, |
{ "Upgrade", header_upgrade, 0, 0 }, |
| 179 |
"^HTTP/([0-9]+)[.]([0-9]+) ([0-9][0-9][0-9]) ([\t -~€-ÿ]*)$", |
"^HTTP/([0-9]+)[.]([0-9]+) ([0-9][0-9][0-9]) ([\t -~€-ÿ]*)$", |
| 180 |
REG_EXTENDED); |
REG_EXTENDED); |
| 181 |
regcomp_wrapper(&re_token, |
regcomp_wrapper(&re_token, |
| 182 |
"^([-0-9a-zA-Z_.]+)", |
"^([-0-9a-zA-Z_.!]+)", |
| 183 |
REG_EXTENDED); |
REG_EXTENDED); |
| 184 |
regcomp_wrapper(&re_token_value, |
regcomp_wrapper(&re_token_value, |
| 185 |
"^([-0-9a-zA-Z_.]+)(=([-0-9a-zA-Z_.]+|\"([^\"]|[\\].)*\"))?", |
"^([-0-9a-zA-Z_.!]+)(=([-0-9a-zA-Z_.!]+|\"([^\"]|[\\].)*\"))?", |
| 186 |
REG_EXTENDED); |
REG_EXTENDED); |
| 187 |
regcomp_wrapper(&re_content_type, |
regcomp_wrapper(&re_content_type, |
| 188 |
"^([-0-9a-zA-Z_.]+)/([-0-9a-zA-Z_.]+)[ \t]*" |
"^([-0-9a-zA-Z_.]+)/([-0-9a-zA-Z_.]+)[ \t]*" |
| 196 |
"^(W/[ \t]*)?\"([^\"]|[\\].)*\"$", |
"^(W/[ \t]*)?\"([^\"]|[\\].)*\"$", |
| 197 |
REG_EXTENDED); |
REG_EXTENDED); |
| 198 |
regcomp_wrapper(&re_server, |
regcomp_wrapper(&re_server, |
| 199 |
"^((([-0-9a-zA-Z_.]+(/[-0-9a-zA-Z_.]+)?)|(\\(.*\\)))[ \t]*)+$", |
"^((([-0-9a-zA-Z_.!]+(/[-0-9a-zA-Z_.]+)?)|(\\(.*\\)))[ \t]*)+$", |
| 200 |
REG_EXTENDED); |
REG_EXTENDED); |
| 201 |
regcomp_wrapper(&re_transfer_coding, |
regcomp_wrapper(&re_transfer_coding, |
| 202 |
"^([-0-9a-zA-Z_.]+)[ \t]*" |
"^([-0-9a-zA-Z_.]+)[ \t]*" |
| 207 |
"^([-0-9a-zA-Z_.](/[-0-9a-zA-Z_.])?)+$", |
"^([-0-9a-zA-Z_.](/[-0-9a-zA-Z_.])?)+$", |
| 208 |
REG_EXTENDED); |
REG_EXTENDED); |
| 209 |
regcomp_wrapper(&re_ugly, |
regcomp_wrapper(&re_ugly, |
| 210 |
"^[a-zA-Z0-9]+://[^/]+[/a-zA-Z0-9-_]*$", |
"^[a-zA-Z0-9]+://[^/]+[-/a-zA-Z0-9_]*$", |
| 211 |
REG_EXTENDED); |
REG_EXTENDED); |
| 212 |
regcomp_wrapper(&re_rfc1123, |
regcomp_wrapper(&re_rfc1123, |
| 213 |
"^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), ([0123][0-9]) " |
"^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), ([0123][0-9]) " |
| 224 |
"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([ 12][0-9]) " |
"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([ 12][0-9]) " |
| 225 |
"([012][0-9]):([0-5][0-9]):([0-5][0-9]) ([0-9]{4})$", |
"([012][0-9]):([0-5][0-9]):([0-5][0-9]) ([0-9]{4})$", |
| 226 |
REG_EXTENDED); |
REG_EXTENDED); |
| 227 |
|
regcomp_wrapper(&re_cookie_nameval, |
| 228 |
|
"^[^;, ]+=[^;, ]*$", |
| 229 |
|
REG_EXTENDED); |
| 230 |
|
regcomp_wrapper(&re_cookie_expires, |
| 231 |
|
"^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), ([0123][0-9])-" |
| 232 |
|
"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-([0-9]{4}) " |
| 233 |
|
"([012][0-9]):([0-5][0-9]):([0-5][0-9]) GMT$", |
| 234 |
|
REG_EXTENDED); |
| 235 |
} |
} |
| 236 |
|
|
| 237 |
|
|
| 408 |
if (header) { |
if (header) { |
| 409 |
header->count++; |
header->count++; |
| 410 |
header->handler(value); |
header->handler(value); |
| 411 |
} else |
} else if ((name[0] == 'X' || name[0] == 'x') && name[1] == '-') { |
| 412 |
|
lookup("xheader"); |
| 413 |
|
} else { |
| 414 |
lookup("nonstandard"); |
lookup("nonstandard"); |
| 415 |
|
} |
| 416 |
} |
} |
| 417 |
|
|
| 418 |
|
|
| 830 |
time0 = time(0); |
time0 = time(0); |
| 831 |
if (!parse_date(s, &tm)) |
if (!parse_date(s, &tm)) |
| 832 |
return; |
return; |
| 833 |
time1 = mktime(&tm); |
time1 = mktime_from_utc(&tm); |
| 834 |
|
|
| 835 |
diff = difftime(time1, time0); |
diff = difftime(time1, time0); |
| 836 |
if (10 < diff) |
if (10 < diff) |
| 951 |
lookup("via"); |
lookup("via"); |
| 952 |
} |
} |
| 953 |
|
|
| 954 |
|
/* http://wp.netscape.com/newsref/std/cookie_spec.html */ |
| 955 |
|
void header_set_cookie(const char *s) |
| 956 |
|
{ |
| 957 |
|
bool ok = true; |
| 958 |
|
int r; |
| 959 |
|
const char *semi = strchr(s, ';'); |
| 960 |
|
const char *s2; |
| 961 |
|
struct tm tm; |
| 962 |
|
double diff; |
| 963 |
|
time_t time0, time1; |
| 964 |
|
regmatch_t pmatch[20]; |
| 965 |
|
|
| 966 |
|
if (semi) |
| 967 |
|
s2 = strndup(s, semi - s); |
| 968 |
|
else |
| 969 |
|
s2 = s; |
| 970 |
|
|
| 971 |
|
r = regexec(&re_cookie_nameval, s2, 0, 0, 0); |
| 972 |
|
if (r) { |
| 973 |
|
lookup("cookiebadnameval"); |
| 974 |
|
ok = false; |
| 975 |
|
} |
| 976 |
|
|
| 977 |
|
if (!semi) |
| 978 |
|
return; |
| 979 |
|
|
| 980 |
|
s = skip_lws(semi + 1); |
| 981 |
|
|
| 982 |
|
while (*s) { |
| 983 |
|
semi = strchr(s, ';'); |
| 984 |
|
if (semi) |
| 985 |
|
s2 = strndup(s, semi - s); |
| 986 |
|
else |
| 987 |
|
s2 = s; |
| 988 |
|
|
| 989 |
|
if (strncmp(s2, "expires=", 8) == 0) { |
| 990 |
|
s2 += 8; |
| 991 |
|
r = regexec(&re_cookie_expires, s2, 20, pmatch, 0); |
| 992 |
|
if (r == 0) { |
| 993 |
|
tm.tm_mday = atoi(s2 + pmatch[2].rm_so); |
| 994 |
|
tm.tm_mon = month(s2 + pmatch[3].rm_so); |
| 995 |
|
tm.tm_year = atoi(s2 + pmatch[4].rm_so) - 1900; |
| 996 |
|
tm.tm_hour = atoi(s2 + pmatch[5].rm_so); |
| 997 |
|
tm.tm_min = atoi(s2 + pmatch[6].rm_so); |
| 998 |
|
tm.tm_sec = atoi(s2 + pmatch[7].rm_so); |
| 999 |
|
|
| 1000 |
|
time0 = time(0); |
| 1001 |
|
time1 = mktime_from_utc(&tm); |
| 1002 |
|
|
| 1003 |
|
diff = difftime(time0, time1); |
| 1004 |
|
if (10 < diff) { |
| 1005 |
|
lookup("cookiepastdate"); |
| 1006 |
|
ok = false; |
| 1007 |
|
} |
| 1008 |
|
} else { |
| 1009 |
|
lookup("cookiebaddate"); |
| 1010 |
|
ok = false; |
| 1011 |
|
} |
| 1012 |
|
} else if (strncmp(s2, "domain=", 7) == 0) { |
| 1013 |
|
} else if (strncmp(s2, "path=", 5) == 0) { |
| 1014 |
|
if (s2[5] != '/') { |
| 1015 |
|
lookup("cookiebadpath"); |
| 1016 |
|
ok = false; |
| 1017 |
|
} |
| 1018 |
|
} else if (strcmp(s, "secure") == 0) { |
| 1019 |
|
} else { |
| 1020 |
|
printf(" Set-Cookie field '%s':\n", s2); |
| 1021 |
|
lookup("cookieunknownfield"); |
| 1022 |
|
ok = false; |
| 1023 |
|
} |
| 1024 |
|
|
| 1025 |
|
if (semi) |
| 1026 |
|
s = skip_lws(semi + 1); |
| 1027 |
|
else |
| 1028 |
|
break; |
| 1029 |
|
} |
| 1030 |
|
|
| 1031 |
|
if (ok) |
| 1032 |
|
lookup("ok"); |
| 1033 |
|
} |
| 1034 |
|
|
| 1035 |
|
|
| 1036 |
/** |
/** |
| 1037 |
* Print an error message and exit. |
* Print an error message and exit. |
| 1136 |
"of header names, or \"*\"." }, |
"of header names, or \"*\"." }, |
| 1137 |
{ "contentrange", "Warning: The Content-Range header should not be returned " |
{ "contentrange", "Warning: The Content-Range header should not be returned " |
| 1138 |
"by the server for this request." }, |
"by the server for this request." }, |
| 1139 |
|
{ "cookiebaddate", "Error: The expires date must be in the form " |
| 1140 |
|
"\"Wdy, DD-Mon-YYYY HH:MM:SS GMT\"." }, |
| 1141 |
|
{ "cookiebadnameval", "Error: A Set-Cookie header must start with " |
| 1142 |
|
"name=value, each excluding semi-colon, comma and " |
| 1143 |
|
"white space." }, |
| 1144 |
|
{ "cookiebadpath", "Error: The path does not start with \"/\"." }, |
| 1145 |
|
{ "cookiepastdate", "Warning: The expires date is in the past. The cookie " |
| 1146 |
|
"will be deleted by browsers." }, |
| 1147 |
|
{ "cookieunknownfield", "Warning: This is not a standard Set-Cookie " |
| 1148 |
|
"field." }, |
| 1149 |
{ "futurehttp", "Warning: I only understand HTTP/1.1. Check for a newer " |
{ "futurehttp", "Warning: I only understand HTTP/1.1. Check for a newer " |
| 1150 |
"version of this tool." }, |
"version of this tool." }, |
| 1151 |
{ "futurelastmod", "Error: The specified Last-Modified date-time is in " |
{ "futurelastmod", "Error: The specified Last-Modified date-time is in " |
| 1190 |
{ "via", "This header was added by a proxy, cache or gateway." }, |
{ "via", "This header was added by a proxy, cache or gateway." }, |
| 1191 |
{ "wrongdate", "Warning: The server date-time differs from this system's " |
{ "wrongdate", "Warning: The server date-time differs from this system's " |
| 1192 |
"date-time by more than 10 seconds. Check that both the " |
"date-time by more than 10 seconds. Check that both the " |
| 1193 |
"system clocks are correct." } |
"system clocks are correct." }, |
| 1194 |
|
{ "xheader", "This is an extension header. I don't know how to check it." } |
| 1195 |
}; |
}; |
| 1196 |
|
|
| 1197 |
|
|