igopher-validator.c - gopher-validator - Simple gopher menu validator. Err bitreich.org 70 hgit clone git://bitreich.org/gopher-validator git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/gopher-validator URL:git://bitreich.org/gopher-validator git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/gopher-validator bitreich.org 70 1Log /scm/gopher-validator/log.gph bitreich.org 70 1Files /scm/gopher-validator/files.gph bitreich.org 70 1Refs /scm/gopher-validator/refs.gph bitreich.org 70 1Tags /scm/gopher-validator/tag bitreich.org 70 1README /scm/gopher-validator/file/README.gph bitreich.org 70 1LICENSE /scm/gopher-validator/file/LICENSE.gph bitreich.org 70 i--- Err bitreich.org 70 igopher-validator.c (12543B) Err bitreich.org 70 i--- Err bitreich.org 70 i 1 #include Err bitreich.org 70 i 2 #include Err bitreich.org 70 i 3 #include Err bitreich.org 70 i 4 Err bitreich.org 70 i 5 #include Err bitreich.org 70 i 6 #include Err bitreich.org 70 i 7 #include Err bitreich.org 70 i 8 #include Err bitreich.org 70 i 9 #include Err bitreich.org 70 i 10 #include Err bitreich.org 70 i 11 #include Err bitreich.org 70 i 12 #include Err bitreich.org 70 i 13 #include Err bitreich.org 70 i 14 #include Err bitreich.org 70 i 15 #include Err bitreich.org 70 i 16 #include Err bitreich.org 70 i 17 Err bitreich.org 70 i 18 #define MAX_RESPONSETIMEOUT 10 /* timeout in seconds */ Err bitreich.org 70 i 19 Err bitreich.org 70 i 20 #ifndef __OpenBSD__ Err bitreich.org 70 i 21 #define pledge(a,b) 0 Err bitreich.org 70 i 22 #endif Err bitreich.org 70 i 23 Err bitreich.org 70 i 24 struct uri { Err bitreich.org 70 i 25 char host[256]; Err bitreich.org 70 i 26 char port[8]; Err bitreich.org 70 i 27 char path[1024]; Err bitreich.org 70 i 28 }; Err bitreich.org 70 i 29 Err bitreich.org 70 i 30 struct visited { Err bitreich.org 70 i 31 int _type; Err bitreich.org 70 i 32 char username[1024]; Err bitreich.org 70 i 33 char path[1024]; Err bitreich.org 70 i 34 char host[256]; Err bitreich.org 70 i 35 char port[8]; Err bitreich.org 70 i 36 }; Err bitreich.org 70 i 37 Err bitreich.org 70 i 38 /* check valid types with extension in path */ Err bitreich.org 70 i 39 struct gophertype { Err bitreich.org 70 i 40 const char *ext; /* filename extension */ Err bitreich.org 70 i 41 const char *allow; /* allowed types for this extension */ Err bitreich.org 70 i 42 }; Err bitreich.org 70 i 43 Err bitreich.org 70 i 44 /* must be sorted alphabetically by extension */ Err bitreich.org 70 i 45 struct gophertype types[] = { Err bitreich.org 70 i 46 { .ext = "asc", "0" }, Err bitreich.org 70 i 47 { .ext = "avi", "9" }, Err bitreich.org 70 i 48 { .ext = "bz2", "9" }, Err bitreich.org 70 i 49 { .ext = "c", "0" }, Err bitreich.org 70 i 50 { .ext = "dcgi", "17" }, Err bitreich.org 70 i 51 { .ext = "doc", "9" }, Err bitreich.org 70 i 52 { .ext = "exe", "9" }, Err bitreich.org 70 i 53 { .ext = "gif", "gI" }, Err bitreich.org 70 i 54 { .ext = "go", "0" }, Err bitreich.org 70 i 55 { .ext = "gph", "1" }, Err bitreich.org 70 i 56 { .ext = "gz", "9" }, Err bitreich.org 70 i 57 { .ext = "h", "0" }, Err bitreich.org 70 i 58 { .ext = "htm", "0h" }, Err bitreich.org 70 i 59 { .ext = "html", "0h" }, Err bitreich.org 70 i 60 { .ext = "iso", "9" }, Err bitreich.org 70 i 61 { .ext = "jpeg", "I" }, Err bitreich.org 70 i 62 { .ext = "jpg", "I" }, Err bitreich.org 70 i 63 { .ext = "json", "0" }, Err bitreich.org 70 i 64 { .ext = "lzma", "9" }, Err bitreich.org 70 i 65 { .ext = "m3u", "0" }, Err bitreich.org 70 i 66 { .ext = "md", "0" }, Err bitreich.org 70 i 67 { .ext = "md5", "0" }, Err bitreich.org 70 i 68 { .ext = "md5sum", "0" }, Err bitreich.org 70 i 69 { .ext = "mkv", "9" }, Err bitreich.org 70 i 70 { .ext = "mp3", "9" }, Err bitreich.org 70 i 71 { .ext = "mp4", "9" }, Err bitreich.org 70 i 72 { .ext = "ogg", "9" }, Err bitreich.org 70 i 73 { .ext = "ogv", "9" }, Err bitreich.org 70 i 74 { .ext = "pdf", "9" }, Err bitreich.org 70 i 75 { .ext = "png", "I" }, Err bitreich.org 70 i 76 { .ext = "rss", "0" }, Err bitreich.org 70 i 77 { .ext = "sh", "0" }, Err bitreich.org 70 i 78 { .ext = "sha1", "0" }, Err bitreich.org 70 i 79 { .ext = "sha1sum", "0" }, Err bitreich.org 70 i 80 { .ext = "sha256", "0" }, Err bitreich.org 70 i 81 { .ext = "sha256sum", "0" }, Err bitreich.org 70 i 82 { .ext = "sha512", "0" }, Err bitreich.org 70 i 83 { .ext = "sha512sum", "0" }, Err bitreich.org 70 i 84 { .ext = "srt", "0" }, Err bitreich.org 70 i 85 { .ext = "tgz", "9" }, Err bitreich.org 70 i 86 { .ext = "txt", "0" }, Err bitreich.org 70 i 87 { .ext = "wav", "9" }, Err bitreich.org 70 i 88 { .ext = "xml", "0" }, Err bitreich.org 70 i 89 { .ext = "xz", "9" }, Err bitreich.org 70 i 90 }; Err bitreich.org 70 i 91 Err bitreich.org 70 i 92 int exitcode = 0; Err bitreich.org 70 i 93 FILE *errfp, *outfp; Err bitreich.org 70 i 94 Err bitreich.org 70 i 95 void Err bitreich.org 70 i 96 die(const char *fmt, ...) Err bitreich.org 70 i 97 { Err bitreich.org 70 i 98 va_list ap; Err bitreich.org 70 i 99 Err bitreich.org 70 i 100 fputs("fatal: ", errfp); Err bitreich.org 70 i 101 Err bitreich.org 70 i 102 va_start(ap, fmt); Err bitreich.org 70 i 103 vfprintf(errfp, fmt, ap); Err bitreich.org 70 i 104 va_end(ap); Err bitreich.org 70 i 105 Err bitreich.org 70 i 106 exit(2); Err bitreich.org 70 i 107 } Err bitreich.org 70 i 108 Err bitreich.org 70 i 109 void Err bitreich.org 70 i 110 error(const char *fmt, ...) Err bitreich.org 70 i 111 { Err bitreich.org 70 i 112 va_list ap; Err bitreich.org 70 i 113 Err bitreich.org 70 i 114 fputs("error: ", outfp); Err bitreich.org 70 i 115 Err bitreich.org 70 i 116 va_start(ap, fmt); Err bitreich.org 70 i 117 vfprintf(outfp, fmt, ap); Err bitreich.org 70 i 118 va_end(ap); Err bitreich.org 70 i 119 Err bitreich.org 70 i 120 exitcode = 1; Err bitreich.org 70 i 121 } Err bitreich.org 70 i 122 Err bitreich.org 70 i 123 void Err bitreich.org 70 i 124 warning(const char *fmt, ...) Err bitreich.org 70 i 125 { Err bitreich.org 70 i 126 va_list ap; Err bitreich.org 70 i 127 Err bitreich.org 70 i 128 fputs("warning: ", outfp); Err bitreich.org 70 i 129 Err bitreich.org 70 i 130 va_start(ap, fmt); Err bitreich.org 70 i 131 vfprintf(outfp, fmt, ap); Err bitreich.org 70 i 132 va_end(ap); Err bitreich.org 70 i 133 } Err bitreich.org 70 i 134 Err bitreich.org 70 i 135 int Err bitreich.org 70 i 136 gophertypecmp(const void *v1, const void *v2) Err bitreich.org 70 i 137 { Err bitreich.org 70 i 138 return strcasecmp(((struct gophertype *)v1)->ext, Err bitreich.org 70 i 139 ((struct gophertype *)v2)->ext); Err bitreich.org 70 i 140 } Err bitreich.org 70 i 141 Err bitreich.org 70 i 142 int Err bitreich.org 70 i 143 isvalidhost(const char *s) Err bitreich.org 70 i 144 { Err bitreich.org 70 i 145 int colons; Err bitreich.org 70 i 146 Err bitreich.org 70 i 147 /* IPv6 */ Err bitreich.org 70 i 148 if (*s == '[') { Err bitreich.org 70 i 149 colons = 0; Err bitreich.org 70 i 150 s++; Err bitreich.org 70 i 151 for (; *s; s++) { Err bitreich.org 70 i 152 if (*s == ':') Err bitreich.org 70 i 153 colons++; Err bitreich.org 70 i 154 else if (*s == ']') Err bitreich.org 70 i 155 break; Err bitreich.org 70 i 156 else if (isxdigit((unsigned char)*s) || *s == '.') Err bitreich.org 70 i 157 ; Err bitreich.org 70 i 158 else Err bitreich.org 70 i 159 return 0; Err bitreich.org 70 i 160 } Err bitreich.org 70 i 161 if (colons < 2 || *s != ']') Err bitreich.org 70 i 162 return 0; Err bitreich.org 70 i 163 } else { Err bitreich.org 70 i 164 if (!*s) Err bitreich.org 70 i 165 return 0; Err bitreich.org 70 i 166 for (; *s; s++) { Err bitreich.org 70 i 167 if (!isalpha((unsigned char)*s) && Err bitreich.org 70 i 168 !isdigit((unsigned char)*s) && Err bitreich.org 70 i 169 *s != '-' && *s != '.') Err bitreich.org 70 i 170 return 0; Err bitreich.org 70 i 171 } Err bitreich.org 70 i 172 } Err bitreich.org 70 i 173 Err bitreich.org 70 i 174 return 1; Err bitreich.org 70 i 175 } Err bitreich.org 70 i 176 Err bitreich.org 70 i 177 int Err bitreich.org 70 i 178 edial(const char *host, const char *port) Err bitreich.org 70 i 179 { Err bitreich.org 70 i 180 struct addrinfo hints, *res, *res0; Err bitreich.org 70 i 181 int error, save_errno, s; Err bitreich.org 70 i 182 const char *cause = NULL; Err bitreich.org 70 i 183 struct timeval timeout; Err bitreich.org 70 i 184 Err bitreich.org 70 i 185 memset(&hints, 0, sizeof(hints)); Err bitreich.org 70 i 186 hints.ai_family = AF_UNSPEC; Err bitreich.org 70 i 187 hints.ai_socktype = SOCK_STREAM; Err bitreich.org 70 i 188 hints.ai_flags = AI_NUMERICSERV; /* numeric port only */ Err bitreich.org 70 i 189 if ((error = getaddrinfo(host, port, &hints, &res0))) Err bitreich.org 70 i 190 die("%s: %s: %s:%s\n", __func__, gai_strerror(error), host, port); Err bitreich.org 70 i 191 s = -1; Err bitreich.org 70 i 192 for (res = res0; res; res = res->ai_next) { Err bitreich.org 70 i 193 s = socket(res->ai_family, res->ai_socktype, Err bitreich.org 70 i 194 res->ai_protocol); Err bitreich.org 70 i 195 if (s == -1) { Err bitreich.org 70 i 196 cause = "socket"; Err bitreich.org 70 i 197 continue; Err bitreich.org 70 i 198 } Err bitreich.org 70 i 199 Err bitreich.org 70 i 200 timeout.tv_sec = MAX_RESPONSETIMEOUT; Err bitreich.org 70 i 201 timeout.tv_usec = 0; Err bitreich.org 70 i 202 if (setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == -1) Err bitreich.org 70 i 203 die("%s: setsockopt: %s\n", __func__, strerror(errno)); Err bitreich.org 70 i 204 Err bitreich.org 70 i 205 timeout.tv_sec = MAX_RESPONSETIMEOUT; Err bitreich.org 70 i 206 timeout.tv_usec = 0; Err bitreich.org 70 i 207 if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) Err bitreich.org 70 i 208 die("%s: setsockopt: %s\n", __func__, strerror(errno)); Err bitreich.org 70 i 209 Err bitreich.org 70 i 210 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { Err bitreich.org 70 i 211 cause = "connect"; Err bitreich.org 70 i 212 save_errno = errno; Err bitreich.org 70 i 213 close(s); Err bitreich.org 70 i 214 errno = save_errno; Err bitreich.org 70 i 215 s = -1; Err bitreich.org 70 i 216 continue; Err bitreich.org 70 i 217 } Err bitreich.org 70 i 218 break; Err bitreich.org 70 i 219 } Err bitreich.org 70 i 220 if (s == -1) Err bitreich.org 70 i 221 die("%s: %s: %s:%s\n", __func__, cause, host, port); Err bitreich.org 70 i 222 freeaddrinfo(res0); Err bitreich.org 70 i 223 Err bitreich.org 70 i 224 return s; Err bitreich.org 70 i 225 } Err bitreich.org 70 i 226 Err bitreich.org 70 i 227 void Err bitreich.org 70 i 228 checkdir(FILE *fp) Err bitreich.org 70 i 229 { Err bitreich.org 70 i 230 struct gophertype gt, *rgt; Err bitreich.org 70 i 231 struct visited v; Err bitreich.org 70 i 232 char line[1024], *end, *s; Err bitreich.org 70 i 233 size_t linenr; Err bitreich.org 70 i 234 ssize_t n; Err bitreich.org 70 i 235 long long l; Err bitreich.org 70 i 236 int i, r, len, hasdotend = 0, c, primarytype = 0, wc, col; Err bitreich.org 70 i 237 wchar_t w; Err bitreich.org 70 i 238 Err bitreich.org 70 i 239 if (pledge("stdio", NULL) == -1) Err bitreich.org 70 i 240 die("pledge: %s\n", strerror(errno)); Err bitreich.org 70 i 241 Err bitreich.org 70 i 242 for (linenr = 1; fgets(line, sizeof(line), fp); linenr++) { Err bitreich.org 70 i 243 n = strcspn(line, "\n"); Err bitreich.org 70 i 244 if (line[n] != '\n') Err bitreich.org 70 i 245 die("%zu: line too long\n", linenr); /* fatal */ Err bitreich.org 70 i 246 if (n && line[n] == '\n') Err bitreich.org 70 i 247 line[n] = '\0'; Err bitreich.org 70 i 248 if (n && line[n - 1] == '\r') Err bitreich.org 70 i 249 line[--n] = '\0'; Err bitreich.org 70 i 250 else Err bitreich.org 70 i 251 error("%zu: invalid line-ending, not CRLF (\\r\\n)\n", linenr); Err bitreich.org 70 i 252 if (n == 1 && line[0] == '.') { Err bitreich.org 70 i 253 hasdotend = 1; Err bitreich.org 70 i 254 break; Err bitreich.org 70 i 255 } Err bitreich.org 70 i 256 Err bitreich.org 70 i 257 memset(&v, 0, sizeof(v)); Err bitreich.org 70 i 258 Err bitreich.org 70 i 259 v._type = line[0]; Err bitreich.org 70 i 260 Err bitreich.org 70 i 261 /* "username" */ Err bitreich.org 70 i 262 i = 1; Err bitreich.org 70 i 263 len = strcspn(line + i, "\t"); Err bitreich.org 70 i 264 if (len + 1 < sizeof(v.username)) { Err bitreich.org 70 i 265 memcpy(v.username, line + i, len); Err bitreich.org 70 i 266 v.username[len] = '\0'; Err bitreich.org 70 i 267 } else { Err bitreich.org 70 i 268 error("%zu: username field too long\n", linenr); Err bitreich.org 70 i 269 continue; Err bitreich.org 70 i 270 } Err bitreich.org 70 i 271 if (line[i + len] == '\t') { Err bitreich.org 70 i 272 i += len + 1; Err bitreich.org 70 i 273 } else { Err bitreich.org 70 i 274 error("%zu: invalid line / field count\n", linenr); Err bitreich.org 70 i 275 continue; Err bitreich.org 70 i 276 } Err bitreich.org 70 i 277 Err bitreich.org 70 i 278 /* selector / path */ Err bitreich.org 70 i 279 len = strcspn(line + i, "\t"); Err bitreich.org 70 i 280 if (len + 1 < sizeof(v.path)) { Err bitreich.org 70 i 281 memcpy(v.path, line + i, len); Err bitreich.org 70 i 282 v.path[len] = '\0'; Err bitreich.org 70 i 283 } else { Err bitreich.org 70 i 284 error("%zu: path field too long\n", linenr); Err bitreich.org 70 i 285 continue; Err bitreich.org 70 i 286 } Err bitreich.org 70 i 287 if (line[i + len] == '\t') { Err bitreich.org 70 i 288 i += len + 1; Err bitreich.org 70 i 289 } else { Err bitreich.org 70 i 290 error("%zu: invalid line / field count\n", linenr); Err bitreich.org 70 i 291 continue; Err bitreich.org 70 i 292 } Err bitreich.org 70 i 293 Err bitreich.org 70 i 294 /* host */ Err bitreich.org 70 i 295 len = strcspn(line + i, "\t"); Err bitreich.org 70 i 296 if (len + 1 < sizeof(v.host)) { Err bitreich.org 70 i 297 memcpy(v.host, line + i, len); Err bitreich.org 70 i 298 v.host[len] = '\0'; Err bitreich.org 70 i 299 } else { Err bitreich.org 70 i 300 error("%zu: host field too long\n", linenr); Err bitreich.org 70 i 301 continue; Err bitreich.org 70 i 302 } Err bitreich.org 70 i 303 if (line[i + len] == '\t') { Err bitreich.org 70 i 304 i += len + 1; Err bitreich.org 70 i 305 } else { Err bitreich.org 70 i 306 error("%zu: invalid line / field count\n", linenr); Err bitreich.org 70 i 307 continue; Err bitreich.org 70 i 308 } Err bitreich.org 70 i 309 Err bitreich.org 70 i 310 /* port */ Err bitreich.org 70 i 311 len = strcspn(line + i, "\t"); Err bitreich.org 70 i 312 if (len + 1 < sizeof(v.port)) { Err bitreich.org 70 i 313 memcpy(v.port, line + i, len); Err bitreich.org 70 i 314 v.port[len] = '\0'; Err bitreich.org 70 i 315 } else { Err bitreich.org 70 i 316 error("%zu: port field too long\n", linenr); Err bitreich.org 70 i 317 continue; Err bitreich.org 70 i 318 } Err bitreich.org 70 i 319 Err bitreich.org 70 i 320 /* check non-standard types */ Err bitreich.org 70 i 321 c = v._type; Err bitreich.org 70 i 322 if (v._type == '+' && !primarytype) Err bitreich.org 70 i 323 error("%zu: mirror type used, but no previous type set\n", linenr); Err bitreich.org 70 i 324 if (v._type != '+') Err bitreich.org 70 i 325 primarytype = v._type; Err bitreich.org 70 i 326 Err bitreich.org 70 i 327 if (!(isdigit(c) || c == 'g' || c == 'I' || c == 'T' || c == '+')) { Err bitreich.org 70 i 328 /* common-used */ Err bitreich.org 70 i 329 if (c == 'i' || c == 'h') { Err bitreich.org 70 i 330 #if 0 Err bitreich.org 70 i 331 warning("%zu: non-standard, but common-used type: %c\n", Err bitreich.org 70 i 332 linenr, c); Err bitreich.org 70 i 333 #endif Err bitreich.org 70 i 334 } else { Err bitreich.org 70 i 335 /* 3.8: "Characters '0' through 'Z' are reserved. Local Err bitreich.org 70 i 336 experiments should use other characters. Err bitreich.org 70 i 337 Machine-specific extensions are not encouraged." */ Err bitreich.org 70 i 338 if (c >= '0' && c <= 'Z') Err bitreich.org 70 i 339 error("%zu: unknown / non-standard type: %c\n", Err bitreich.org 70 i 340 linenr, c); Err bitreich.org 70 i 341 } Err bitreich.org 70 i 342 } Err bitreich.org 70 i 343 Err bitreich.org 70 i 344 /* check type with file extension, unless it is the HTML 'h' Err bitreich.org 70 i 345 type with a "URL:" prefix */ Err bitreich.org 70 i 346 if ((s = strrchr(v.path, '.')) && !strchr(s, '/') && Err bitreich.org 70 i 347 !(primarytype == 'h' && !strncmp(v.path, "URL:", sizeof("URL:") - 1))) { Err bitreich.org 70 i 348 gt.ext = ++s; Err bitreich.org 70 i 349 if (!(rgt = bsearch(>, &types, sizeof(types) / sizeof(types[0]), Err bitreich.org 70 i 350 sizeof(types[0]), &gophertypecmp))) Err bitreich.org 70 i 351 continue; Err bitreich.org 70 i 352 Err bitreich.org 70 i 353 if (!strchr(rgt->allow, primarytype)) Err bitreich.org 70 i 354 warning("%zu: invalid type '%c' for extension '%s', valid types: '%s'\n", Err bitreich.org 70 i 355 linenr, primarytype, rgt->ext, rgt->allow); Err bitreich.org 70 i 356 } Err bitreich.org 70 i 357 Err bitreich.org 70 i 358 if (!isvalidhost(v.host)) Err bitreich.org 70 i 359 error("%zu: invalid host: %s\n", linenr, v.host); Err bitreich.org 70 i 360 Err bitreich.org 70 i 361 /* check port, must be numeric and in range, port 0 is allowed: Err bitreich.org 70 i 362 "Appendix: Err bitreich.org 70 i 363 Note: Port corresponds the the TCP Port Number, its value should Err bitreich.org 70 i 364 be in the range [0..65535]; port 70 is officially assigned Err bitreich.org 70 i 365 to gopher." */ Err bitreich.org 70 i 366 Err bitreich.org 70 i 367 errno = 0; Err bitreich.org 70 i 368 l = strtoll(v.port, &end, 10); Err bitreich.org 70 i 369 if (errno || v.port == end || *end || l < 0 || l > 65535) { Err bitreich.org 70 i 370 error("%zu: invalid port: %s\n", linenr, v.port); Err bitreich.org 70 i 371 } else { Err bitreich.org 70 i 372 #if 0 Err bitreich.org 70 i 373 if (l != 70) Err bitreich.org 70 i 374 warning("%zu: non-standard gopher port: %lld, not 70\n", Err bitreich.org 70 i 375 linenr, l); Err bitreich.org 70 i 376 #endif Err bitreich.org 70 i 377 } Err bitreich.org 70 i 378 Err bitreich.org 70 i 379 /* RFC "Notes": "The Selector string should be no longer than Err bitreich.org 70 i 380 255 characters." */ Err bitreich.org 70 i 381 if ((len = strlen(v.path)) > 255) Err bitreich.org 70 i 382 error("%zu: selector should not be longer than 255 characters: %d bytes\n", Err bitreich.org 70 i 383 linenr, len); Err bitreich.org 70 i 384 Err bitreich.org 70 i 385 /* decode UTF-8 (text-encoding is ASCII/Latin1 in the RFC, but Err bitreich.org 70 i 386 Latin1 sucks, recommend UTF-8 instead. Err bitreich.org 70 i 387 Check column length as recommended as described in the RFC Err bitreich.org 70 i 388 in section 3.9. */ Err bitreich.org 70 i 389 s = v.username; Err bitreich.org 70 i 390 len = strlen(s); Err bitreich.org 70 i 391 col = 0; Err bitreich.org 70 i 392 for (i = 0; i < len; i += r) { Err bitreich.org 70 i 393 r = mbtowc(&w, &s[i], len - i < 4 ? len - i : 4); Err bitreich.org 70 i 394 if (r == 0) Err bitreich.org 70 i 395 break; Err bitreich.org 70 i 396 if (r == -1) { Err bitreich.org 70 i 397 mbtowc(NULL, NULL, 0); /* reset state */ Err bitreich.org 70 i 398 warning("%zu:%d: username: first invalid byte, not UTF-8\n", Err bitreich.org 70 i 399 linenr, i + 1); Err bitreich.org 70 i 400 break; Err bitreich.org 70 i 401 } Err bitreich.org 70 i 402 if ((wc = wcwidth(w)) == -1) Err bitreich.org 70 i 403 wc = 1; Err bitreich.org 70 i 404 col += (size_t)wc; Err bitreich.org 70 i 405 Err bitreich.org 70 i 406 /* RFC "Notes": "It is *highly* recommended that the Err bitreich.org 70 i 407 User_Name field contain only printable characters". */ Err bitreich.org 70 i 408 if (!iswprint(w)) { Err bitreich.org 70 i 409 error("%zu:%d: first non-printable character in username field\n", Err bitreich.org 70 i 410 linenr, i + 1); Err bitreich.org 70 i 411 break; Err bitreich.org 70 i 412 } Err bitreich.org 70 i 413 } Err bitreich.org 70 i 414 #if 0 Err bitreich.org 70 i 415 /* instead of 70 check 79 */ Err bitreich.org 70 i 416 if (col > 79) Err bitreich.org 70 i 417 warning("%zu: username column length is > 79 (%d), see section 3.9 of the RFC\n", Err bitreich.org 70 i 418 linenr, col); Err bitreich.org 70 i 419 #endif Err bitreich.org 70 i 420 Err bitreich.org 70 i 421 if (!strcmp(v.path, "..") || strstr(v.path, "../")) Err bitreich.org 70 i 422 warning("%zu: found ../ in path: don't use relative paths\n", linenr); Err bitreich.org 70 i 423 } Err bitreich.org 70 i 424 if (ferror(fp)) Err bitreich.org 70 i 425 die("fgets: %s\n", strerror(errno)); Err bitreich.org 70 i 426 Err bitreich.org 70 i 427 if (!hasdotend) Err bitreich.org 70 i 428 error("no .\\r\\n end\n"); Err bitreich.org 70 i 429 } Err bitreich.org 70 i 430 Err bitreich.org 70 i 431 void Err bitreich.org 70 i 432 checkremote(const char *host, const char *port, const char *path, const char *param) Err bitreich.org 70 i 433 { Err bitreich.org 70 i 434 FILE *fp; Err bitreich.org 70 i 435 int fd, r; Err bitreich.org 70 i 436 Err bitreich.org 70 i 437 fd = edial(host, port); Err bitreich.org 70 i 438 Err bitreich.org 70 i 439 if (param[0]) Err bitreich.org 70 i 440 r = dprintf(fd, "%s\t%s\r\n", path, param); Err bitreich.org 70 i 441 else Err bitreich.org 70 i 442 r = dprintf(fd, "%s\r\n", path); Err bitreich.org 70 i 443 if (r == -1) Err bitreich.org 70 i 444 die("write: %s\n", strerror(errno)); Err bitreich.org 70 i 445 Err bitreich.org 70 i 446 if (!(fp = fdopen(fd, "rb+"))) Err bitreich.org 70 i 447 die("fdopen: %s\n", strerror(errno)); Err bitreich.org 70 i 448 checkdir(fp); Err bitreich.org 70 i 449 fclose(fp); Err bitreich.org 70 i 450 } Err bitreich.org 70 i 451 Err bitreich.org 70 i 452 int Err bitreich.org 70 i 453 parseuri(const char *str, struct uri *u) Err bitreich.org 70 i 454 { Err bitreich.org 70 i 455 const char *s, *e; Err bitreich.org 70 i 456 Err bitreich.org 70 i 457 memset(u, 0, sizeof(struct uri)); Err bitreich.org 70 i 458 Err bitreich.org 70 i 459 s = str; Err bitreich.org 70 i 460 Err bitreich.org 70 i 461 /* IPv6 */ Err bitreich.org 70 i 462 if (*s == '[') { Err bitreich.org 70 i 463 s++; Err bitreich.org 70 i 464 e = strchr(s, ']'); Err bitreich.org 70 i 465 if (!e || e - s + 1 >= sizeof(u->host)) Err bitreich.org 70 i 466 return 0; Err bitreich.org 70 i 467 memcpy(u->host, s, e - s); Err bitreich.org 70 i 468 u->host[e - s] = '\0'; Err bitreich.org 70 i 469 e++; Err bitreich.org 70 i 470 } else { Err bitreich.org 70 i 471 e = &s[strcspn(s, ":/")]; Err bitreich.org 70 i 472 if (e - s + 1 >= sizeof(u->host)) Err bitreich.org 70 i 473 return 0; Err bitreich.org 70 i 474 memcpy(u->host, s, e - s); Err bitreich.org 70 i 475 u->host[e - s] = '\0'; Err bitreich.org 70 i 476 } Err bitreich.org 70 i 477 Err bitreich.org 70 i 478 if (*e == ':') { Err bitreich.org 70 i 479 s = e + 1; Err bitreich.org 70 i 480 e = &s[strcspn(s, "/")]; Err bitreich.org 70 i 481 Err bitreich.org 70 i 482 if (e - s + 1 >= sizeof(u->port)) Err bitreich.org 70 i 483 return 0; Err bitreich.org 70 i 484 memcpy(u->port, s, e - s); Err bitreich.org 70 i 485 u->port[e - s] = '\0'; Err bitreich.org 70 i 486 } Err bitreich.org 70 i 487 if (*e && *e != '/') Err bitreich.org 70 i 488 return 0; /* invalid path */ Err bitreich.org 70 i 489 Err bitreich.org 70 i 490 s = e; Err bitreich.org 70 i 491 e = s + strlen(s); Err bitreich.org 70 i 492 Err bitreich.org 70 i 493 if (e - s + 1 >= sizeof(u->path)) Err bitreich.org 70 i 494 return 0; Err bitreich.org 70 i 495 memcpy(u->path, s, e - s); Err bitreich.org 70 i 496 u->path[e - s] = '\0'; Err bitreich.org 70 i 497 Err bitreich.org 70 i 498 return 1; Err bitreich.org 70 i 499 } Err bitreich.org 70 i 500 Err bitreich.org 70 i 501 int Err bitreich.org 70 i 502 main(int argc, char **argv) Err bitreich.org 70 i 503 { Err bitreich.org 70 i 504 struct uri u; Err bitreich.org 70 i 505 const char *path, *uri = "", *param = "", *s; Err bitreich.org 70 i 506 int _type = '1'; Err bitreich.org 70 i 507 Err bitreich.org 70 i 508 setlocale(LC_CTYPE, ""); Err bitreich.org 70 i 509 Err bitreich.org 70 i 510 outfp = stdout; Err bitreich.org 70 i 511 errfp = stderr; Err bitreich.org 70 i 512 Err bitreich.org 70 i 513 /* CGI-mode or stand-alone */ Err bitreich.org 70 i 514 if ((s = getenv("QUERY_STRING"))) { Err bitreich.org 70 i 515 uri = s; Err bitreich.org 70 i 516 param = ""; Err bitreich.org 70 i 517 errfp = stdout; /* output errors to stdout also in CGI mode */ Err bitreich.org 70 i 518 } else { Err bitreich.org 70 i 519 switch (argc) { Err bitreich.org 70 i 520 case 3: Err bitreich.org 70 i 521 param = argv[2]; Err bitreich.org 70 i 522 case 2: Err bitreich.org 70 i 523 uri = argv[1]; Err bitreich.org 70 i 524 break; Err bitreich.org 70 i 525 case 1: Err bitreich.org 70 i 526 checkdir(stdin); Err bitreich.org 70 i 527 return exitcode; Err bitreich.org 70 i 528 default: Err bitreich.org 70 i 529 fprintf(errfp, "usage: %s [uri] [param]\n", argv[0]); Err bitreich.org 70 i 530 return 1; Err bitreich.org 70 i 531 } Err bitreich.org 70 i 532 } Err bitreich.org 70 i 533 Err bitreich.org 70 i 534 if (pledge("stdio inet dns", NULL) == -1) Err bitreich.org 70 i 535 die("pledge: %s\n", strerror(errno)); Err bitreich.org 70 i 536 Err bitreich.org 70 i 537 if (!strncmp(uri, "gopher://", sizeof("gopher://") - 1)) Err bitreich.org 70 i 538 uri += sizeof("gopher://") - 1; Err bitreich.org 70 i 539 Err bitreich.org 70 i 540 if (!parseuri(uri, &u)) Err bitreich.org 70 i 541 die("Invalid uri\n"); Err bitreich.org 70 i 542 if (u.host[0] == '\0') Err bitreich.org 70 i 543 die("Invalid hostname\n"); Err bitreich.org 70 i 544 Err bitreich.org 70 i 545 if (u.path[0] == '\0') Err bitreich.org 70 i 546 memcpy(u.path, "/", 2); Err bitreich.org 70 i 547 if (u.port[0] == '\0') Err bitreich.org 70 i 548 memcpy(u.port, "70", 3); Err bitreich.org 70 i 549 Err bitreich.org 70 i 550 path = u.path; Err bitreich.org 70 i 551 if (path[0] == '/') { Err bitreich.org 70 i 552 path++; Err bitreich.org 70 i 553 if (*path) { Err bitreich.org 70 i 554 _type = *path; Err bitreich.org 70 i 555 path++; Err bitreich.org 70 i 556 } Err bitreich.org 70 i 557 } else { Err bitreich.org 70 i 558 path = ""; Err bitreich.org 70 i 559 } Err bitreich.org 70 i 560 Err bitreich.org 70 i 561 switch (_type) { Err bitreich.org 70 i 562 case '1': Err bitreich.org 70 i 563 case '7': Err bitreich.org 70 i 564 break; /* handled below */ Err bitreich.org 70 i 565 default: /* these types are not validated */ Err bitreich.org 70 i 566 fprintf(errfp, "only types 1 (dir) and 7 (search) are validated\n"); Err bitreich.org 70 i 567 return 1; Err bitreich.org 70 i 568 } Err bitreich.org 70 i 569 Err bitreich.org 70 i 570 if (_type != '7') Err bitreich.org 70 i 571 param = ""; Err bitreich.org 70 i 572 Err bitreich.org 70 i 573 checkremote(u.host, u.port, path, param); Err bitreich.org 70 i 574 Err bitreich.org 70 i 575 return exitcode; Err bitreich.org 70 i 576 } Err bitreich.org 70 .