Download raw body.
got-notify-http: use a UNIX timestamp for the date
This was suggested by Lucas some time ago. I think it makes more sense and is less error-prone to use a UNIX timestamp rather than a formatted string for the date (javascript Date.toJSON() notwithstanding.) The upside is that got-notify-email has to do some light parsing of the content of the mail to pretty-print the timestamp. There is also the assumption that a time_t is at least 64 bit wide, and I don't know how to portably work around it. strptime(%s) has timezone issues and I don't know other ways to turn a stringified timestamp into a time_t. Suggestions are welcome! diff /home/op/w/got commit - 533c404ec30ef30690b8c41481cbdbbeeb8e2a5c path + /home/op/w/got blob - a346ab5981dac6f616103dfdb281f5c2785c60a2 file + gotd/gotd.conf.5 --- gotd/gotd.conf.5 +++ gotd/gotd.conf.5 @@ -402,14 +402,7 @@ Has the same fields as the .Dv committer but may be unset. .It Dv date -String representation of the date as -.Xr strftime 3 -.Sq %G-%m-%d -if -.Dv short -is set or -.Sq %a %b %e %X %Y UTC -otherwise. +Number, representing the number of seconds since the Epoch. .It Dv short_message The first line of the commit message. This field is always set. @@ -474,7 +467,7 @@ field for the .Dv commit notification but with all the field guaranteed to be set. .It Dv date -The tag date. +Number, representing the number of seconds since the Epoch. .It Dv object The object being tagged. It contains the fields blob - a0ff1393c1aa0d3ff4f44bd88d6ef8579db72717 file + gotd/libexec/got-notify-email/got-notify-email.c --- gotd/libexec/got-notify-email/got-notify-email.c +++ gotd/libexec/got-notify-email/got-notify-email.c @@ -17,12 +17,14 @@ #include <sys/types.h> #include <sys/socket.h> +#include <ctype.h> #include <errno.h> #include <poll.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> +#include <stdint.h> #include <syslog.h> #include <getopt.h> #include <err.h> @@ -213,6 +215,39 @@ get_datestr(time_t *time, char *datebuf) return s; } +static const struct got_error * +print_date(int s, char *date, int shortfmt) +{ + const struct got_error *error; + struct tm tm; + char *t, datebuf[26]; + const char *errstr; + time_t ts; + + date[strcspn(date, " \n")] = '\0'; + + ts = strtonum(date, INT64_MIN, INT64_MAX, &errstr); + if (errstr) + return got_error_set_errno(EINVAL, errstr); + if (gmtime_r(&ts, &tm) == NULL) + return got_error_set_errno(EINVAL, "gmtime_r"); + + if (!shortfmt) { + t = asctime_r(&tm, datebuf); + if (t == NULL) + return got_error_set_errno(EINVAL, "invalid timestamp"); + t[strcspn(t, "\n")] = '\0'; + error = got_poll_write_full(s, t, strlen(t)); + if (error) + return error; + return got_poll_write_full(s, " UTC\n", 5); + } + + if (strftime(datebuf, sizeof(datebuf), "%G-%m-%d ", &tm) == 0) + return got_error_set_errno(EINVAL, "invalid timestamp"); + return got_poll_write_full(s, datebuf, strlen(datebuf)); +} + static void send_email(int s, const char *myfromaddr, const char *fromaddr, const char *recipient, const char *replytoaddr, @@ -223,6 +258,7 @@ send_email(int s, const char *myfromaddr, const char * size_t linesize = 0; ssize_t linelen; time_t now; + int firstline = 1, shortfmt = 0; char datebuf[26]; char *datestr; @@ -270,11 +306,39 @@ send_email(int s, const char *myfromaddr, const char * fatalx("could not send body delimiter"); while ((linelen = getline(&line, &linesize, stdin)) != -1) { + if (firstline && isdigit((unsigned char)line[0])) + shortfmt = 1; + firstline = 0; + if (line[0] == '.') { /* dot stuffing */ error = got_poll_write_full(s, ".", 1); if (error) fatalx("write: %s", error->msg); } + + if (shortfmt) { + char *t; + t = strchr(line, ' '); + if (t != NULL) { + *t++ = '\0'; + error = print_date(s, line, shortfmt); + if (error) + fatalx("write: %s", error->msg); + error = got_poll_write_full(s, t, strlen(t)); + continue; + } + } + + if (!shortfmt && !strncmp(line, "date: ", 6)) { + error = got_poll_write_full(s, line, 6); + if (error) + fatalx("write: %s", error->msg); + error = print_date(s, line + 6, shortfmt); + if (error) + fatalx("write: %s", error->msg); + continue; + } + error = got_poll_write_full(s, line, linelen); if (error) fatalx("write: %s", error->msg); blob - 60f89ca0e57362d2951dc69102b742825ac30059 file + gotd/libexec/got-notify-http/got-notify-http.c --- gotd/libexec/got-notify-http/got-notify-http.c +++ gotd/libexec/got-notify-http/got-notify-http.c @@ -29,6 +29,7 @@ #include <stdlib.h> #include <string.h> #include <syslog.h> +#include <time.h> #include <unistd.h> #include "got_opentemp.h" @@ -153,6 +154,12 @@ json_field(FILE *fp, const char *key, const char *val, } static void +json_date(FILE *fp, const char *key, const char *date, int comma) +{ + fprintf(fp, "\"%s\":%s%s", key, date, comma ? "," : ""); +} + +static void json_author(FILE *fp, const char *type, char *address, int comma) { char *gt, *lt, *at, *email, *endname; @@ -247,7 +254,7 @@ jsonify_commit_short(FILE *fp, char *line, const char json_field(fp, "repo", repo, 1); json_field(fp, "id", id, 1); json_author(fp, "committer", author, 1); - json_field(fp, "date", date, 1); + json_date(fp, "date", date, 1); json_field(fp, "short_message", message, 0); fprintf(fp, "}"); @@ -331,7 +338,7 @@ jsonify_commit(FILE *fp, const char *repo, char **line /* optional */ if (!strncmp(l, "date: ", 6)) { l += 6; - json_field(fp, "date", l, 1); + json_date(fp, "date", l, 1); phase = P_PARENT; break; } @@ -598,7 +605,7 @@ jsonify_tag(FILE *fp, const char *repo, char **line, s /* optional */ if (!strncmp(l, "date: ", 6)) { l += 6; - json_field(fp, "date", l, 1); + json_date(fp, "date", l, 1); phase = P_OBJECT; break; } blob - abc10999b714c055c44b01f7d380e97962a222f8 file + gotd/notify.c --- gotd/notify.c +++ gotd/notify.c @@ -242,7 +242,7 @@ notify_email(struct gotd_notification_target *target, argv[i++] = "-h"; argv[i++] = target->conf.email.hostname; } - + if (target->conf.email.port) { argv[i++] = "-p"; argv[i++] = target->conf.email.port; blob - 787504ad4f6f0817c648d3f24b5d75d65da66010 file + gotd/repo_write.c --- gotd/repo_write.c +++ gotd/repo_write.c @@ -1629,24 +1629,6 @@ receive_pack_idx(struct imsg *imsg, struct gotd_imsgev return NULL; } -static char * -get_datestr(time_t *time, char *datebuf) -{ - struct tm mytm, *tm; - char *p, *s; - - tm = gmtime_r(time, &mytm); - if (tm == NULL) - return NULL; - s = asctime_r(tm, datebuf); - if (s == NULL) - return NULL; - p = strchr(s, '\n'); - if (p) - *p = '\0'; - return s; -} - static const struct got_error * notify_removed_ref(const char *refname, uint8_t *sha1, struct gotd_imsgev *iev, int fd) @@ -1688,8 +1670,6 @@ print_commit_oneline(struct got_commit_object *commit, char *id_str = NULL, *logmsg0 = NULL; char *s, *nl; char *committer = NULL, *author = NULL; - char datebuf[12]; /* YYYY-MM-DD + SPACE + NUL */ - struct tm tm; time_t committer_time; err = got_object_id_str(&id_str, id); @@ -1697,14 +1677,6 @@ print_commit_oneline(struct got_commit_object *commit, return err; committer_time = got_object_commit_get_committer_time(commit); - if (gmtime_r(&committer_time, &tm) == NULL) { - err = got_error_from_errno("gmtime_r"); - goto done; - } - if (strftime(datebuf, sizeof(datebuf), "%G-%m-%d ", &tm) == 0) { - err = got_error(GOT_ERR_NO_SPACE); - goto done; - } err = got_object_commit_get_logmsg(&logmsg0, commit); if (err) @@ -1726,16 +1698,12 @@ print_commit_oneline(struct got_commit_object *commit, err = got_error_from_errno("strdup"); goto done; } - dprintf(fd, "%s%.7s %.8s %s\n", datebuf, id_str, - format_author(author), s); + dprintf(fd, "%lld %.7s %.8s %s\n", (long long)committer_time, + id_str, format_author(author), s); } else { committer = strdup(got_object_commit_get_committer(commit)); - if (committer == NULL) { - err = got_error_from_errno("strdup"); - goto done; - } - dprintf(fd, "%s%.7s %.8s %s\n", datebuf, id_str, - format_author(committer), s); + dprintf(fd, "%lld %.7s %.8s %s\n", (long long)committer_time, + id_str, format_author(committer), s); } if (fsync(fd) == -1 && err == NULL) @@ -1775,8 +1743,7 @@ print_commit(struct got_commit_object *commit, struct struct got_diffstat_cb_arg *diffstat, int fd) { const struct got_error *err = NULL; - char *id_str, *datestr, *logmsg0, *logmsg, *line; - char datebuf[26]; + char *id_str, *logmsg0, *logmsg, *line; time_t committer_time; const char *author, *committer; @@ -1793,9 +1760,7 @@ print_commit(struct got_commit_object *commit, struct if (strcmp(author, committer) != 0) dprintf(fd, "via: %s\n", committer); committer_time = got_object_commit_get_committer_time(commit); - datestr = get_datestr(&committer_time, datebuf); - if (datestr) - dprintf(fd, "date: %s UTC\n", datestr); + dprintf(fd, "date: %lld\n", (long long)committer_time); if (got_object_commit_get_nparents(commit) > 1) { const struct got_object_id_queue *parent_ids; struct got_object_qid *qid; @@ -2004,8 +1969,7 @@ print_tag(struct got_object_id *id, const struct got_error *err = NULL; struct got_tag_object *tag = NULL; const char *tagger = NULL; - char *id_str = NULL, *tagmsg0 = NULL, *tagmsg, *line, *datestr; - char datebuf[26]; + char *id_str = NULL, *tagmsg0 = NULL, *tagmsg, *line; time_t tagger_time; err = got_object_open_as_tag(&tag, repo, id); @@ -2021,9 +1985,7 @@ print_tag(struct got_object_id *id, dprintf(fd, "tag %s\n", refname); dprintf(fd, "from: %s\n", tagger); - datestr = get_datestr(&tagger_time, datebuf); - if (datestr) - dprintf(fd, "date: %s UTC\n", datestr); + dprintf(fd, "date: %lld\n", (long long)tagger_time); switch (got_object_tag_get_object_type(tag)) { case GOT_OBJ_TYPE_BLOB: blob - 18726da0df985a0e71d65fb2408032c6677ffe00 file + regress/gotd/http_notification.sh --- regress/gotd/http_notification.sh +++ regress/gotd/http_notification.sh @@ -57,8 +57,6 @@ test_file_changed() { wait %1 # wait for the http "server" - d=`date -u -r $author_time +"%a %b %e %X %Y UTC"` - touch "$testroot/stdout.expected" ed -s "$testroot/stdout.expected" <<-EOF a @@ -79,7 +77,7 @@ test_file_changed() { "mail":"$GIT_AUTHOR_EMAIL", "user":"$GOT_AUTHOR_11" }, - "date":"$d", + "date":$author_time, "short_message":"make changes", "message":"make changes\n", "diffstat":{ @@ -150,8 +148,6 @@ test_bad_utf8() { wait %1 # wait for the http "server" - d=`date -u -r $author_time +"%a %b %e %X %Y UTC"` - touch "$testroot/stdout.expected" ed -s "$testroot/stdout.expected" <<-EOF a @@ -172,7 +168,7 @@ test_bad_utf8() { "mail":"$GIT_AUTHOR_EMAIL", "user":"$GOT_AUTHOR_11" }, - "date":"$d", + "date":$author_time, "short_message":"make\uFFFD\uFFFDchanges", "message":"make\uFFFD\uFFFDchanges\n", "diffstat":{ @@ -228,8 +224,7 @@ test_many_commits_not_summarized() { (cd $testroot/wt && got commit -m 'make changes' > /dev/null) local commit_id=`git_show_head $testroot/repo-clone` local author_time=`git_show_author_time $testroot/repo-clone` - d=`date -u -r $author_time +"%a %b %e %X %Y UTC"` - set -- "$@" "$commit_id $d" + set -- "$@" "$commit_id $author_time" done timeout 5 ./http-server -a $AUTH -p "$GOTD_TEST_HTTP_PORT" \ @@ -273,7 +268,7 @@ test_many_commits_not_summarized() { "mail":"$GIT_AUTHOR_EMAIL", "user":"$GOT_AUTHOR_11" }, - "date":"$commit_time", + "date":$commit_time, "short_message":"make changes", "message":"make changes\n", "diffstat":{ @@ -333,8 +328,7 @@ test_many_commits_summarized() { local commit_id=`git_show_head $testroot/repo-clone` local short_commit_id=`trim_obj_id 33 $commit_id` local author_time=`git_show_author_time $testroot/repo-clone` - d=`date -u -r $author_time +"%G-%m-%d"` - set -- "$@" "$short_commit_id $d" + set -- "$@" "$short_commit_id $author_time" done timeout 5 ./http-server -a $AUTH -p "$GOTD_TEST_HTTP_PORT" \ @@ -355,7 +349,7 @@ test_many_commits_summarized() { for i in `seq 1 51`; do s=`pop_idx $i "$@"` commit_id=$(echo $s | cut -d' ' -f1) - commit_time=$(echo "$s" | sed -e "s/^$commit_id //g") + commit_time=$(echo "$s" | cut -d' ' -f2) echo "$comma" comma=',' @@ -369,7 +363,7 @@ test_many_commits_summarized() { "committer":{ "user":"$GOT_AUTHOR_8" }, - "date":"$commit_time", + "date":$commit_time, "short_message":"make changes" } EOF @@ -430,8 +424,6 @@ test_branch_created() { wait %1 # wait for the http "server" - d=`date -u -r $author_time +"%a %b %e %X %Y UTC"` - # in the future it should contain something like this too # { # "type":"new-branch", @@ -460,7 +452,7 @@ test_branch_created() { "mail":"$GIT_AUTHOR_EMAIL", "user":"$GOT_AUTHOR_11" }, - "date":"$d", + "date":$author_time, "short_message":"newbranch", "message":"newbranch\n", "diffstat":{ @@ -572,8 +564,6 @@ test_tag_created() { wait %1 # wait for the http "server" - d=`date -u -r $tagger_time +"%a %b %e %X %Y UTC"` - touch "$testroot/stdout.expected" ed -s "$testroot/stdout.expected" <<-EOF a @@ -587,7 +577,7 @@ test_tag_created() { "mail":"$GIT_AUTHOR_EMAIL", "user":"$GOT_AUTHOR_11" }, - "date":"$d", + "date":$tagger_time, "object":{ "type":"commit", "id":"$commit_id" @@ -650,8 +640,6 @@ test_tag_changed() { wait %1 # wait for the http "server" - d=`date -u -r $tagger_time +"%a %b %e %X %Y UTC"` - # XXX: at the moment this is exactly the same as the "new tag" # notification @@ -668,7 +656,7 @@ test_tag_changed() { "mail":"$GIT_AUTHOR_EMAIL", "user":"$GOT_AUTHOR_11" }, - "date":"$d", + "date":$tagger_time, "object":{ "type":"commit", "id":"$commit_id"
got-notify-http: use a UNIX timestamp for the date