]> git.proxmox.com Git - libgit2.git/blame - src/signature.c
config: share the strmap on snapshot
[libgit2.git] / src / signature.c
CommitLineData
58519018 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
58519018 3 *
bb742ede
VM
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
58519018
VM
6 */
7
8#include "common.h"
638c2ca4 9#include "signature.h"
58519018 10#include "repository.h"
44908fe7 11#include "git2/common.h"
114f5a6c 12#include "posix.h"
58519018 13
638c2ca4 14void git_signature_free(git_signature *sig)
58519018 15{
638c2ca4 16 if (sig == NULL)
58519018
VM
17 return;
18
3286c408 19 git__free(sig->name);
97769280 20 sig->name = NULL;
3286c408 21 git__free(sig->email);
97769280 22 sig->email = NULL;
3286c408 23 git__free(sig);
58519018
VM
24}
25
4aa7de15
RB
26static int signature_error(const char *msg)
27{
c51880ee 28 giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg);
4aa7de15
RB
29 return -1;
30}
31
c51880ee 32static bool contains_angle_brackets(const char *input)
a01acc47 33{
c51880ee 34 return strchr(input, '<') != NULL || strchr(input, '>') != NULL;
a01acc47 35}
36
c51880ee 37static char *extract_trimmed(const char *ptr, size_t len)
8aedf1d5 38{
24ec6999 39 while (len && git__isspace(ptr[0])) {
c51880ee
VM
40 ptr++; len--;
41 }
8aedf1d5 42
24ec6999 43 while (len && git__isspace(ptr[len - 1])) {
c51880ee
VM
44 len--;
45 }
46
47 return git__substrdup(ptr, len);
8aedf1d5 48}
49
63396a39 50int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)
58519018 51{
638c2ca4 52 git_signature *p = NULL;
58519018 53
a01acc47 54 assert(name && email);
55
63396a39
MS
56 *sig_out = NULL;
57
c51880ee
VM
58 if (contains_angle_brackets(name) ||
59 contains_angle_brackets(email)) {
60 return signature_error(
61 "Neither `name` nor `email` should contain angle brackets chars.");
62 }
63
4aa7de15
RB
64 p = git__calloc(1, sizeof(git_signature));
65 GITERR_CHECK_ALLOC(p);
a01acc47 66
c51880ee
VM
67 p->name = extract_trimmed(name, strlen(name));
68 p->email = extract_trimmed(email, strlen(email));
69
f0ab7372
VM
70 if (p->name == NULL || p->email == NULL)
71 return -1; /* oom */
72
73 if (p->name[0] == '\0') {
8aedf1d5 74 git_signature_free(p);
f0ab7372 75 return signature_error("Signature cannot have an empty name");
8aedf1d5 76 }
ce23330f 77
a01acc47 78 p->when.time = time;
79 p->when.offset = offset;
58519018 80
63396a39 81 *sig_out = p;
4aa7de15 82 return 0;
58519018
VM
83}
84
29be3a6d 85int git_signature_dup(git_signature **dest, const git_signature *source)
58519018 86{
29be3a6d 87 git_signature *signature;
f0ab7372 88
29be3a6d
AS
89 if (source == NULL)
90 return 0;
91
92 signature = git__calloc(1, sizeof(git_signature));
93 GITERR_CHECK_ALLOC(signature);
94
95 signature->name = git__strdup(source->name);
96 GITERR_CHECK_ALLOC(signature->name);
b20c40a8 97
29be3a6d
AS
98 signature->email = git__strdup(source->email);
99 GITERR_CHECK_ALLOC(signature->email);
f0ab7372 100
29be3a6d
AS
101 signature->when.time = source->when.time;
102 signature->when.offset = source->when.offset;
f0ab7372 103
29be3a6d
AS
104 *dest = signature;
105
106 return 0;
58519018
VM
107}
108
63396a39 109int git_signature_now(git_signature **sig_out, const char *name, const char *email)
9e9e6ae1
CMN
110{
111 time_t now;
53b7560b 112 time_t offset;
d03d309b 113 struct tm *utc_tm;
63396a39 114 git_signature *sig;
d03d309b 115 struct tm _utc;
9e9e6ae1 116
63396a39
MS
117 *sig_out = NULL;
118
d03d309b
CMN
119 /*
120 * Get the current time as seconds since the epoch and
121 * transform that into a tm struct containing the time at
122 * UTC. Give that to mktime which considers it a local time
123 * (tm_isdst = -1 asks it to take DST into account) and gives
124 * us that time as seconds since the epoch. The difference
125 * between its return value and 'now' is our offset to UTC.
126 */
14eb94ee 127 time(&now);
9ecf860d 128 utc_tm = p_gmtime_r(&now, &_utc);
d03d309b 129 utc_tm->tm_isdst = -1;
b97c169e 130 offset = (time_t)difftime(now, mktime(utc_tm));
9e9e6ae1 131 offset /= 60;
14eb94ee 132
4aa7de15
RB
133 if (git_signature_new(&sig, name, email, now, (int)offset) < 0)
134 return -1;
63396a39
MS
135
136 *sig_out = sig;
137
4aa7de15
RB
138 return 0;
139}
140
ce23330f
RB
141int git_signature_default(git_signature **out, git_repository *repo)
142{
143 int error;
29c4cb09 144 git_config *cfg, *repo_cfg;
ce23330f
RB
145 const char *user_name, *user_email;
146
29c4cb09
CMN
147 if ((error = git_repository_config__weakptr(&repo_cfg, repo)) < 0)
148 return error;
149
150 if ((error = git_config_snapshot(&cfg, repo_cfg)) < 0)
ce23330f
RB
151 return error;
152
153 if (!(error = git_config_get_string(&user_name, cfg, "user.name")) &&
154 !(error = git_config_get_string(&user_email, cfg, "user.email")))
155 error = git_signature_now(out, user_name, user_email);
156
157 git_config_free(cfg);
158 return error;
159}
160
720d5472 161int git_signature__parse(git_signature *sig, const char **buffer_out,
7757be33 162 const char *buffer_end, const char *header, char ender)
58519018 163{
720d5472 164 const char *buffer = *buffer_out;
c51880ee 165 const char *email_start, *email_end;
58519018 166
de70aea6 167 memset(sig, 0, sizeof(git_signature));
58519018 168
c51880ee 169 if ((buffer_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
4aa7de15 170 return signature_error("no newline given");
58519018 171
8b2c913a
MS
172 if (header) {
173 const size_t header_len = strlen(header);
58519018 174
41051e3f 175 if (buffer + header_len >= buffer_end || memcmp(buffer, header, header_len) != 0)
4aa7de15 176 return signature_error("expected prefix doesn't match actual");
58519018 177
8b2c913a
MS
178 buffer += header_len;
179 }
58519018 180
41051e3f
VM
181 email_start = git__memrchr(buffer, '<', buffer_end - buffer);
182 email_end = git__memrchr(buffer, '>', buffer_end - buffer);
fbfc7580 183
c51880ee 184 if (!email_start || !email_end || email_end <= email_start)
4aa7de15 185 return signature_error("malformed e-mail");
076141a1 186
c51880ee 187 email_start += 1;
cf80993a 188 sig->name = extract_trimmed(buffer, email_start - buffer - 1);
c51880ee 189 sig->email = extract_trimmed(email_start, email_end - email_start);
58519018 190
c51880ee
VM
191 /* Do we even have a time at the end of the signature? */
192 if (email_end + 2 < buffer_end) {
193 const char *time_start = email_end + 2;
194 const char *time_end;
58519018 195
c51880ee
VM
196 if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0)
197 return signature_error("invalid Unix timestamp");
c6e65aca 198
cf80993a 199 /* do we have a timezone? */
c51880ee
VM
200 if (time_end + 1 < buffer_end) {
201 int offset, hours, mins;
202 const char *tz_start, *tz_end;
203
204 tz_start = time_end + 1;
205
1fed6b07 206 if ((tz_start[0] != '-' && tz_start[0] != '+') ||
dc33b3d7
SG
207 git__strtol32(&offset, tz_start + 1, &tz_end, 10) < 0) {
208 //malformed timezone, just assume it's zero
209 offset = 0;
210 }
c51880ee
VM
211
212 hours = offset / 100;
213 mins = offset % 100;
932d1baf 214
c51880ee
VM
215 /*
216 * only store timezone if it's not overflowing;
217 * see http://www.worldtimezone.com/faq.html
218 */
219 if (hours < 14 && mins < 59) {
220 sig->when.offset = (hours * 60) + mins;
221 if (tz_start[0] == '-')
222 sig->when.offset = -sig->when.offset;
223 }
224 }
42a1b5e1 225 }
13710f1e 226
c51880ee 227 *buffer_out = buffer_end + 1;
4aa7de15 228 return 0;
58519018
VM
229}
230
afeecf4f
VM
231void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig)
232{
233 int offset, hours, mins;
234 char sign;
235
80c29fe9
RB
236 assert(buf && sig);
237
afeecf4f
VM
238 offset = sig->when.offset;
239 sign = (sig->when.offset < 0) ? '-' : '+';
240
241 if (offset < 0)
242 offset = -offset;
243
244 hours = offset / 60;
245 mins = offset % 60;
246
247 git_buf_printf(buf, "%s%s <%s> %u %c%02d%02d\n",
248 header ? header : "", sig->name, sig->email,
249 (unsigned)sig->when.time, sign, hours, mins);
250}
58519018 251