Commit Diff


commit - 4ddc9d7454d1838afd08981593a8b32332f3725f
commit + 9d88153b9f990652b2dd274efb7b561fc1446708
blob - ed6de8db29817b25f285cd13c8395a03bfa2e2aa
blob + 6e68ff331dae7881c9b5fdd240f869c658aedce6
--- gotd/repo_write.c
+++ gotd/repo_write.c
@@ -1627,8 +1627,203 @@ receive_pack_idx(struct imsg *imsg, struct gotd_imsgev
 		return got_error(GOT_ERR_PRIVSEP_NO_FD);
 
 	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 *
+print_commit_or_tag(int fd, struct got_object_id *id)
+{
+	const struct got_error *err;
+	struct got_object_id *commit_id;
+	struct got_commit_object *commit = NULL;
+	struct got_tag_object *tag = NULL;
+	char *commit_id_str = NULL;
+	char *logmsg = NULL, *datestr;
+	const char *author = NULL, *committer = NULL;
+	char datebuf[26];
+	time_t time;
+	int obj_type;
+
+	err = got_object_open_as_commit(&commit, repo_write.repo, id);
+	if (err) {
+		if (err->code != GOT_ERR_OBJ_TYPE)
+			return err;
+
+		err = got_object_open_as_tag(&tag, repo_write.repo, id);
+		if (err)
+			return err;
+
+		dprintf(fd, "Tag: %s\n", got_object_tag_get_name(tag));
+		dprintf(fd, "Tagger: %s\n", got_object_tag_get_tagger(tag));
+
+		obj_type = got_object_tag_get_object_type(tag);
+		if (obj_type != GOT_OBJ_TYPE_COMMIT)
+			goto done;
+
+		commit_id = got_object_tag_get_object_id(tag);
+		err = got_object_open_as_commit(&commit, repo_write.repo,
+		    commit_id);
+		if (err)
+			goto done;
+	} else
+		commit_id = id;
+
+	err = got_object_id_str(&commit_id_str, commit_id);
+	if (err)
+		goto done;
+
+	err = got_object_commit_get_logmsg(&logmsg, commit);
+	if (err)
+		goto done;
+
+	committer = got_object_commit_get_committer(commit);
+	author = got_object_commit_get_author(commit);
+	dprintf(fd, "Commit: %s\n", commit_id_str);
+	dprintf(fd, "Committer: %s\n", committer);
+	datestr = get_datestr(&time, datebuf);
+	dprintf(fd, "Date: %s\n", datestr);
+	if (strcmp(author, committer) != 0)
+		dprintf(fd, "Author: %s\n", author);
+	dprintf(fd, "Log message:\n%s\n", logmsg);
+done:
+	if (commit)
+		got_object_commit_close(commit);
+	if (tag)
+		got_object_tag_close(tag);
+	free(commit_id_str);
+	free(logmsg);
+	return err;
+}
+
+static const struct got_error *
+notify_created_ref(const char *refname, uint8_t *sha1,
+    struct gotd_imsgev *iev, int fd)
+{
+	const struct got_error *err;
+	struct got_object_id id;
+	char *id_str;
+
+	memset(&id, 0, sizeof(id));
+	memcpy(id.sha1, sha1, sizeof(id.sha1));
+
+	err = got_object_id_str(&id_str, &id);
+	if (err)
+		return err;
+
+	dprintf(fd, "Created %s: %s\n", refname, id_str);
+	err = print_commit_or_tag(fd, &id);
+	free(id_str);
+	return err;
 }
 
+static const struct got_error *
+notify_removed_ref(const char *refname, uint8_t *sha1,
+    struct gotd_imsgev *iev, int fd)
+{
+	const struct got_error *err;
+	struct got_object_id id;
+	char *id_str;
+
+	memset(&id, 0, sizeof(id));
+	memcpy(id.sha1, sha1, sizeof(id.sha1));
+
+	err = got_object_id_str(&id_str, &id);
+	if (err)
+		return err;
+
+	dprintf(fd, "Removed %s: %s\n", refname, id_str);
+	free(id_str);
+	return err;
+}
+
+static const struct got_error *
+notify_changed_ref(const char *refname, uint8_t *old_sha1,
+    uint8_t *new_sha1, struct gotd_imsgev *iev, int fd)
+{
+	const struct got_error *err;
+	struct got_object_id old_id, new_id;
+	char *old_id_str = NULL, *new_id_str = NULL;
+
+	memset(&old_id, 0, sizeof(old_id));
+	memcpy(old_id.sha1, old_sha1, sizeof(old_id.sha1));
+	memset(&new_id, 0, sizeof(new_id));
+	memcpy(new_id.sha1, new_sha1, sizeof(new_id.sha1));
+
+	err = got_object_id_str(&old_id_str, &old_id);
+	if (err)
+		return err;
+	err = got_object_id_str(&new_id_str, &new_id);
+	if (err)
+		goto done;
+
+	dprintf(fd, "Changed %s: %s\n", refname, new_id_str);
+	dprintf(fd, "(formerly %s)\n", old_id_str);
+
+	/* TODO: Walk commits and determine the new set... */
+done:
+	free(old_id_str);
+	free(new_id_str);
+	return err;
+}
+
+static const struct got_error *
+render_notification(struct imsg *imsg, struct gotd_imsgev *iev)
+{
+	const struct got_error *err = NULL;
+	struct gotd_imsg_notification_content ireq;
+	size_t datalen;
+	char *refname;
+
+	if (imsg->fd == -1)
+		return got_error(GOT_ERR_PRIVSEP_NO_FD);
+
+	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+	if (datalen < sizeof(ireq))
+		return got_error(GOT_ERR_PRIVSEP_LEN);
+
+	memcpy(&ireq, imsg->data, sizeof(ireq));
+
+	if (datalen != sizeof(ireq) +  ireq.refname_len)
+		return got_error(GOT_ERR_PRIVSEP_LEN);
+
+	refname = strndup(imsg->data + sizeof(ireq), ireq.refname_len);
+	if (refname == NULL)
+		return got_error_from_errno("strndup");
+
+	switch (ireq.action) {
+	case GOTD_NOTIF_ACTION_CREATED:
+		err = notify_created_ref(refname, ireq.new_id, iev, imsg->fd);
+		break;
+	case GOTD_NOTIF_ACTION_REMOVED:
+		err = notify_removed_ref(refname, ireq.old_id, iev, imsg->fd);
+		break;
+	case GOTD_NOTIF_ACTION_CHANGED:
+		err = notify_changed_ref(refname, ireq.old_id, ireq.new_id,
+		    iev, imsg->fd);
+		break;
+	}
+
+	free(refname);
+	return err;
+}
+
 static void
 repo_write_dispatch_session(int fd, short event, void *arg)
 {
@@ -1721,6 +1916,11 @@ repo_write_dispatch_session(int fd, short event, void 
 				log_warnx("update refs: %s", err->msg);
 			}
 			break;
+		case GOTD_IMSG_NOTIFY:
+			err = render_notification(&imsg, iev);
+			if (err)
+				log_warnx("render notification: %s", err->msg);
+			break;
 		default:
 			log_debug("unexpected imsg %d", imsg.hdr.type);
 			break;