summaryrefslogtreecommitdiff
path: root/src/libgit2/signature.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libgit2/signature.c')
-rw-r--r--src/libgit2/signature.c339
1 files changed, 339 insertions, 0 deletions
diff --git a/src/libgit2/signature.c b/src/libgit2/signature.c
new file mode 100644
index 000000000..5d6ab572c
--- /dev/null
+++ b/src/libgit2/signature.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "signature.h"
+
+#include "repository.h"
+#include "git2/common.h"
+#include "posix.h"
+
+void git_signature_free(git_signature *sig)
+{
+ if (sig == NULL)
+ return;
+
+ git__free(sig->name);
+ sig->name = NULL;
+ git__free(sig->email);
+ sig->email = NULL;
+ git__free(sig);
+}
+
+static int signature_parse_error(const char *msg)
+{
+ git_error_set(GIT_ERROR_INVALID, "failed to parse signature - %s", msg);
+ return GIT_EINVALID;
+}
+
+static int signature_error(const char *msg)
+{
+ git_error_set(GIT_ERROR_INVALID, "failed to parse signature - %s", msg);
+ return -1;
+}
+
+static bool contains_angle_brackets(const char *input)
+{
+ return strchr(input, '<') != NULL || strchr(input, '>') != NULL;
+}
+
+static bool is_crud(unsigned char c)
+{
+ return c <= 32 ||
+ c == '.' ||
+ c == ',' ||
+ c == ':' ||
+ c == ';' ||
+ c == '<' ||
+ c == '>' ||
+ c == '"' ||
+ c == '\\' ||
+ c == '\'';
+}
+
+static char *extract_trimmed(const char *ptr, size_t len)
+{
+ while (len && is_crud((unsigned char)ptr[0])) {
+ ptr++; len--;
+ }
+
+ while (len && is_crud((unsigned char)ptr[len - 1])) {
+ len--;
+ }
+
+ return git__substrdup(ptr, len);
+}
+
+int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)
+{
+ git_signature *p = NULL;
+
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(email);
+
+ *sig_out = NULL;
+
+ if (contains_angle_brackets(name) ||
+ contains_angle_brackets(email)) {
+ return signature_error(
+ "Neither `name` nor `email` should contain angle brackets chars.");
+ }
+
+ p = git__calloc(1, sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(p);
+
+ p->name = extract_trimmed(name, strlen(name));
+ GIT_ERROR_CHECK_ALLOC(p->name);
+ p->email = extract_trimmed(email, strlen(email));
+ GIT_ERROR_CHECK_ALLOC(p->email);
+
+ if (p->name[0] == '\0' || p->email[0] == '\0') {
+ git_signature_free(p);
+ return signature_error("Signature cannot have an empty name or email");
+ }
+
+ p->when.time = time;
+ p->when.offset = offset;
+ p->when.sign = (offset < 0) ? '-' : '+';
+
+ *sig_out = p;
+ return 0;
+}
+
+int git_signature_dup(git_signature **dest, const git_signature *source)
+{
+ git_signature *signature;
+
+ if (source == NULL)
+ return 0;
+
+ signature = git__calloc(1, sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(signature);
+
+ signature->name = git__strdup(source->name);
+ GIT_ERROR_CHECK_ALLOC(signature->name);
+
+ signature->email = git__strdup(source->email);
+ GIT_ERROR_CHECK_ALLOC(signature->email);
+
+ signature->when.time = source->when.time;
+ signature->when.offset = source->when.offset;
+ signature->when.sign = source->when.sign;
+
+ *dest = signature;
+
+ return 0;
+}
+
+int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool)
+{
+ git_signature *signature;
+
+ if (source == NULL)
+ return 0;
+
+ signature = git_pool_mallocz(pool, sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(signature);
+
+ signature->name = git_pool_strdup(pool, source->name);
+ GIT_ERROR_CHECK_ALLOC(signature->name);
+
+ signature->email = git_pool_strdup(pool, source->email);
+ GIT_ERROR_CHECK_ALLOC(signature->email);
+
+ signature->when.time = source->when.time;
+ signature->when.offset = source->when.offset;
+ signature->when.sign = source->when.sign;
+
+ *dest = signature;
+
+ return 0;
+}
+
+int git_signature_now(git_signature **sig_out, const char *name, const char *email)
+{
+ time_t now;
+ time_t offset;
+ struct tm *utc_tm;
+ git_signature *sig;
+ struct tm _utc;
+
+ *sig_out = NULL;
+
+ /*
+ * Get the current time as seconds since the epoch and
+ * transform that into a tm struct containing the time at
+ * UTC. Give that to mktime which considers it a local time
+ * (tm_isdst = -1 asks it to take DST into account) and gives
+ * us that time as seconds since the epoch. The difference
+ * between its return value and 'now' is our offset to UTC.
+ */
+ time(&now);
+ utc_tm = p_gmtime_r(&now, &_utc);
+ utc_tm->tm_isdst = -1;
+ offset = (time_t)difftime(now, mktime(utc_tm));
+ offset /= 60;
+
+ if (git_signature_new(&sig, name, email, now, (int)offset) < 0)
+ return -1;
+
+ *sig_out = sig;
+
+ return 0;
+}
+
+int git_signature_default(git_signature **out, git_repository *repo)
+{
+ int error;
+ git_config *cfg;
+ const char *user_name, *user_email;
+
+ if ((error = git_repository_config_snapshot(&cfg, repo)) < 0)
+ return error;
+
+ if (!(error = git_config_get_string(&user_name, cfg, "user.name")) &&
+ !(error = git_config_get_string(&user_email, cfg, "user.email")))
+ error = git_signature_now(out, user_name, user_email);
+
+ git_config_free(cfg);
+ return error;
+}
+
+int git_signature__parse(git_signature *sig, const char **buffer_out,
+ const char *buffer_end, const char *header, char ender)
+{
+ const char *buffer = *buffer_out;
+ const char *email_start, *email_end;
+
+ memset(sig, 0, sizeof(git_signature));
+
+ if (ender &&
+ (buffer_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
+ return signature_parse_error("no newline given");
+
+ if (header) {
+ const size_t header_len = strlen(header);
+
+ if (buffer + header_len >= buffer_end || memcmp(buffer, header, header_len) != 0)
+ return signature_parse_error("expected prefix doesn't match actual");
+
+ buffer += header_len;
+ }
+
+ email_start = git__memrchr(buffer, '<', buffer_end - buffer);
+ email_end = git__memrchr(buffer, '>', buffer_end - buffer);
+
+ if (!email_start || !email_end || email_end <= email_start)
+ return signature_parse_error("malformed e-mail");
+
+ email_start += 1;
+ sig->name = extract_trimmed(buffer, email_start - buffer - 1);
+ sig->email = extract_trimmed(email_start, email_end - email_start);
+
+ /* Do we even have a time at the end of the signature? */
+ if (email_end + 2 < buffer_end) {
+ const char *time_start = email_end + 2;
+ const char *time_end;
+
+ if (git__strntol64(&sig->when.time, time_start,
+ buffer_end - time_start, &time_end, 10) < 0) {
+ git__free(sig->name);
+ git__free(sig->email);
+ sig->name = sig->email = NULL;
+ return signature_parse_error("invalid Unix timestamp");
+ }
+
+ /* do we have a timezone? */
+ if (time_end + 1 < buffer_end) {
+ int offset, hours, mins;
+ const char *tz_start, *tz_end;
+
+ tz_start = time_end + 1;
+
+ if ((tz_start[0] != '-' && tz_start[0] != '+') ||
+ git__strntol32(&offset, tz_start + 1,
+ buffer_end - tz_start - 1, &tz_end, 10) < 0) {
+ /* malformed timezone, just assume it's zero */
+ offset = 0;
+ }
+
+ hours = offset / 100;
+ mins = offset % 100;
+
+ /*
+ * only store timezone if it's not overflowing;
+ * see http://www.worldtimezone.com/faq.html
+ */
+ if (hours <= 14 && mins <= 59) {
+ sig->when.offset = (hours * 60) + mins;
+ sig->when.sign = tz_start[0];
+ if (tz_start[0] == '-')
+ sig->when.offset = -sig->when.offset;
+ }
+ }
+ }
+
+ *buffer_out = buffer_end + 1;
+ return 0;
+}
+
+int git_signature_from_buffer(git_signature **out, const char *buf)
+{
+ git_signature *sig;
+ const char *buf_end;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(buf);
+
+ *out = NULL;
+
+ sig = git__calloc(1, sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(sig);
+
+ buf_end = buf + strlen(buf);
+ error = git_signature__parse(sig, &buf, buf_end, NULL, '\0');
+
+ if (error)
+ git__free(sig);
+ else
+ *out = sig;
+
+ return error;
+}
+
+void git_signature__writebuf(git_str *buf, const char *header, const git_signature *sig)
+{
+ int offset, hours, mins;
+ char sign;
+
+ offset = sig->when.offset;
+ sign = (sig->when.offset < 0 || sig->when.sign == '-') ? '-' : '+';
+
+ if (offset < 0)
+ offset = -offset;
+
+ hours = offset / 60;
+ mins = offset % 60;
+
+ git_str_printf(buf, "%s%s <%s> %u %c%02d%02d\n",
+ header ? header : "", sig->name, sig->email,
+ (unsigned)sig->when.time, sign, hours, mins);
+}
+
+bool git_signature__equal(const git_signature *one, const git_signature *two)
+{
+ GIT_ASSERT_ARG(one);
+ GIT_ASSERT_ARG(two);
+
+ return
+ git__strcmp(one->name, two->name) == 0 &&
+ git__strcmp(one->email, two->email) == 0 &&
+ one->when.time == two->when.time &&
+ one->when.offset == two->when.offset &&
+ one->when.sign == two->when.sign;
+}
+