#include <fcntl.h>
#include "tlse.h"
-#define MARK fprintf(stderr, "%s %s:%d\n", __FILE__, __func__, __LINE__)
struct tls_uri {
char *scheme;
char *host;
char *port;
char *path;
+ char *encoded_path;
char *query;
+ char *encoded_query;
char *fragment;
};
+
int tls_parse_uri(char *, struct tls_uri *);
void tls_free_uri(struct tls_uri *);
}
int match = (memcmp(certhash, fp, 64) == 0);
+ if (!match) {
+ fprintf(stderr, "host %s certificate changed\n", host);
+ fprintf(stderr, "was %.64s\n", fp);
+ fprintf(stderr, "now %.64s\n", certhash);
+ }
close(trustdb);
tls_buffer_free(&tbuf);
tls_buffer_append(hdr, "\r\n", 2);
}
+char *pathlast(char *path) {
+ char *last = 0;
+ size_t len = 0;
+
+ if (path == 0) {
+ return 0;
+ }
+
+ while (*path == '/') {
+ path++;
+ }
+
+ do {
+ last = path;
+ len = 0;
+ while (*path && *path != '/') {
+ path++;
+ len++;
+ }
+ while (*path == '/') {
+ path++;
+ }
+ } while (*path);
+
+ if (len == 0) {
+ return 0;
+ }
+
+ return strndup(last, len);
+}
+
+static time_t file_mtime(char *path) {
+ struct stat st;
+ int rv;
+
+ rv = stat(path, &st);
+ if (rv == -1) {
+ if (errno == ENOENT) {
+ return 0;
+ }
+ perror("stat failed:");
+ return -1;
+ }
+
+ return st.st_mtime;
+}
+
int main(int ac, char *av[]) {
int sockfd, port = -1, rv;
ssize_t ret;
#endif
char *req_file = 0;
char *host = 0;
- struct tls_uri uri;
+ struct tls_uri uri = { 0 };
char *outfile = 0;
int raw = 0, head = 0;
- int out = 1;
+ int out = 1; /* output file descriptor */
int use_tls = 0;
struct io io = { {0}, 0, -1, 0, 0, 0, 0, 0 };
struct TLSContext *clientssl = 0;
struct tls_buffer request;
char lmtime[80];
char *eoh = 0;
+ char *user_agent = 0;
size_t total = 0;
size_t header_len;
char *url = 0;
int redirs = 0, redirlimit = 50, printstatus = 0;
- int verifypolicy = 1;
+ int verifypolicy = 1, calcoutfile = 0, ifnewer = 0;
ltc_mp = tfm_desc;
- while ((option = getopt(ac, av, "o:rIfz:#R:SkK")) != -1) {
+ while ((option = getopt(ac, av, "o:OrIfz:np#R:SkKU:")) != -1) {
switch (option) {
case 'o': outfile = optarg; break;
+ case 'O': calcoutfile = 1; break;
case 'S': printstatus = 1; head = 1; break;
case 'k': verifypolicy = 0; break;
case 'K': verifypolicy = 2; break;
+ case 'U': user_agent = optarg; break;
case 'I': head = 1;
case 'r': raw = 1; break;
case 'f': failsilent = 1; break;
case 'z': lmfile = optarg; break;
+ case 'n': ifnewer = 1; break;
case 'R': redirlimit = strtol(optarg, 0, 10); break;
+ case 'p':
case '#': progressbar = 1; break;
default:
exit(EXIT_FAILURE);
exit(EXIT_FAILURE);
}
+ url = strdup(av[optind]);
+ if (!url) {
+ exit(EXIT_FAILURE);
+ }
+
io.last_modified = 0;
- if (lmfile) {
- struct stat st;
- int rv;
- struct tm *mtime;
- time_t ts;
- rv = stat(lmfile, &st);
- if (rv == -1) {
- perror("stat failed:");
+ if (calcoutfile && !outfile) {
+ tls_parse_uri(url, &uri);
+ outfile = pathlast(uri.path);
+ /* outfile leaks memory here, so if this
+ * were turned into a library function,
+ * we'd need to track it
+ */
+ if (!outfile) {
+ fprintf(stderr, "unable to determine outfile\n");
exit(EXIT_FAILURE);
}
- ts = st.st_mtime;
- io.last_modified = ts;
- mtime = gmtime(&ts);
- strftime(lmtime, sizeof lmtime, "%a, %d %b %Y %H:%M:%S GMT", mtime);
}
- url = strdup(av[optind]);
- if (!url) {
- exit(EXIT_FAILURE);
+ if (ifnewer && outfile && !lmfile) {
+ lmfile = outfile;
}
- if (outfile) {
- out = open(outfile, O_WRONLY|O_CREAT, 0600);
- if (out == -1) {
- perror("can't open output file:");
+ if (lmfile) {
+ struct tm *mtime;
+ time_t ts;
+
+ ts = file_mtime(lmfile);
+
+ if (ts == -1) {
exit(EXIT_FAILURE);
+ } else if (ts != 0) {
+ io.last_modified = ts;
+ mtime = gmtime(&ts);
+ strftime(lmtime, sizeof lmtime, "%a, %d %b %Y %H:%M:%S GMT", mtime);
+ } else {
+ lmfile = 0;
}
}
tls_free_uri(&uri);
io.response.len = 0;
request.len = 0;
+ eoh = 0;
tls_parse_uri(url, &uri);
host = uri.host;
} else {
tls_buffer_append(&request, "GET ", 4);
}
- tls_buffer_append(&request, uri.path, strlen(uri.path));
+ tls_buffer_append(&request, uri.encoded_path, strlen(uri.encoded_path));
+ if (uri.encoded_query) {
+ tls_buffer_append(&request, "?", 1);
+ tls_buffer_append(&request, uri.encoded_query, strlen(uri.encoded_query));
+ }
tls_buffer_append(&request, " HTTP/1.1\r\n", 11);
append_header(&request, "Host", host);
+ if (user_agent) {
+ append_header(&request, "User-Agent", user_agent);
+ }
+ append_header(&request, "Accept", "*/*");
append_header(&request, "Connection", "close");
if (lmfile) {
append_header(&request, "If-Modified-Since", lmtime);
io.socket = sockfd;
+ eoh = 0;
do {
if (io.response.len >= 4) {
eoh = strstr(io.response.buffer, "\r\n\r\n");
} while (!eoh);
if (!eoh) {
- /* never got (complet) header */
- fprintf(stderr, "incomplete response to %s\n", av[optind]);
+ /* never got (complete) header */
+ fprintf(stderr, "incomplete response (ret = %zd) to %s\n", ret, url);
+ fprintf(stderr, "have:\n");
+ fwrite(io.response.buffer, io.response.len, 1, stderr);
exit(EXIT_FAILURE);
}
parse_header(&io);
switch (io.status_code) {
+ case 304:
+ progressbar = 0;
+ break;
case 301:
case 302:
case 303:
case 307:
free(url);
url = strdup(io.redirect);
+ close(io.socket);
continue;
break;
}
}
}
+ if (outfile) {
+ out = open(outfile, O_WRONLY|O_CREAT, 0600);
+ if (out == -1) {
+ perror("can't open output file:");
+ exit(EXIT_FAILURE);
+ }
+ }
+
do {
write(out, io.response.buffer, io.response.len);
ret = io.response.len;
pdots(50, '.', total, total+ret,
io.content_length);
} else {
- int old = total / 1000000;
- int new = (total+ret)/1000000;
- while (old < new) {
- putc('.',stderr);
- }
+ putc('\r', stderr);
+ fprintf(stderr, "%zu", total+ret);
}
total += ret;
}
if (ret < 0) {
fprintf(stderr, "%s read error %zd\n", uri.scheme, ret);
}
- /* futimens(out, ...) */
+ struct timespec ts[2];
+ ts[0].tv_sec = 0; ts[0].tv_nsec = UTIME_OMIT;
+ ts[1].tv_sec = io.last_modified;
+ ts[1].tv_nsec = 0;
+
+ futimens(out, ts);
close(out);
tls_buffer_free(&io.response);
break;
putc('\n',stderr);
}
- return io.status_code == 200 ? 0 : EXIT_FAILURE;
+ return io.status_code < 400 ? 0 : EXIT_FAILURE;
}