]> git.proxmox.com Git - libgit2.git/blame - src/signature.c
New upstream version 1.4.3+dfsg.1
[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
638c2ca4 8#include "signature.h"
eae0bfdc 9
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
e579e0f7
MB
26static int signature_parse_error(const char *msg)
27{
28 git_error_set(GIT_ERROR_INVALID, "failed to parse signature - %s", msg);
29 return GIT_EINVALID;
30}
31
4aa7de15
RB
32static int signature_error(const char *msg)
33{
ac3d33df 34 git_error_set(GIT_ERROR_INVALID, "failed to parse signature - %s", msg);
4aa7de15
RB
35 return -1;
36}
37
c51880ee 38static bool contains_angle_brackets(const char *input)
a01acc47 39{
c51880ee 40 return strchr(input, '<') != NULL || strchr(input, '>') != NULL;
a01acc47 41}
42
307c4a2b
VM
43static bool is_crud(unsigned char c)
44{
45 return c <= 32 ||
46 c == '.' ||
47 c == ',' ||
48 c == ':' ||
49 c == ';' ||
50 c == '<' ||
51 c == '>' ||
52 c == '"' ||
53 c == '\\' ||
54 c == '\'';
55}
56
c51880ee 57static char *extract_trimmed(const char *ptr, size_t len)
8aedf1d5 58{
307c4a2b 59 while (len && is_crud((unsigned char)ptr[0])) {
c51880ee
VM
60 ptr++; len--;
61 }
8aedf1d5 62
307c4a2b 63 while (len && is_crud((unsigned char)ptr[len - 1])) {
c51880ee
VM
64 len--;
65 }
66
67 return git__substrdup(ptr, len);
8aedf1d5 68}
69
63396a39 70int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)
58519018 71{
638c2ca4 72 git_signature *p = NULL;
58519018 73
c25aa7cd
PP
74 GIT_ASSERT_ARG(name);
75 GIT_ASSERT_ARG(email);
a01acc47 76
63396a39
MS
77 *sig_out = NULL;
78
c51880ee
VM
79 if (contains_angle_brackets(name) ||
80 contains_angle_brackets(email)) {
81 return signature_error(
82 "Neither `name` nor `email` should contain angle brackets chars.");
83 }
84
4aa7de15 85 p = git__calloc(1, sizeof(git_signature));
ac3d33df 86 GIT_ERROR_CHECK_ALLOC(p);
a01acc47 87
c51880ee 88 p->name = extract_trimmed(name, strlen(name));
ac3d33df 89 GIT_ERROR_CHECK_ALLOC(p->name);
c51880ee 90 p->email = extract_trimmed(email, strlen(email));
ac3d33df 91 GIT_ERROR_CHECK_ALLOC(p->email);
f0ab7372 92
76e3c43f 93 if (p->name[0] == '\0' || p->email[0] == '\0') {
8aedf1d5 94 git_signature_free(p);
76e3c43f 95 return signature_error("Signature cannot have an empty name or email");
8aedf1d5 96 }
ce23330f 97
a01acc47 98 p->when.time = time;
99 p->when.offset = offset;
eae0bfdc 100 p->when.sign = (offset < 0) ? '-' : '+';
58519018 101
63396a39 102 *sig_out = p;
4aa7de15 103 return 0;
58519018
VM
104}
105
29be3a6d 106int git_signature_dup(git_signature **dest, const git_signature *source)
58519018 107{
29be3a6d 108 git_signature *signature;
f0ab7372 109
29be3a6d
AS
110 if (source == NULL)
111 return 0;
112
113 signature = git__calloc(1, sizeof(git_signature));
ac3d33df 114 GIT_ERROR_CHECK_ALLOC(signature);
29be3a6d
AS
115
116 signature->name = git__strdup(source->name);
ac3d33df 117 GIT_ERROR_CHECK_ALLOC(signature->name);
b20c40a8 118
29be3a6d 119 signature->email = git__strdup(source->email);
ac3d33df 120 GIT_ERROR_CHECK_ALLOC(signature->email);
f0ab7372 121
29be3a6d
AS
122 signature->when.time = source->when.time;
123 signature->when.offset = source->when.offset;
eae0bfdc 124 signature->when.sign = source->when.sign;
f0ab7372 125
29be3a6d
AS
126 *dest = signature;
127
128 return 0;
58519018
VM
129}
130
bdeb8772
CMN
131int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool)
132{
133 git_signature *signature;
134
135 if (source == NULL)
136 return 0;
137
138 signature = git_pool_mallocz(pool, sizeof(git_signature));
ac3d33df 139 GIT_ERROR_CHECK_ALLOC(signature);
bdeb8772
CMN
140
141 signature->name = git_pool_strdup(pool, source->name);
ac3d33df 142 GIT_ERROR_CHECK_ALLOC(signature->name);
bdeb8772
CMN
143
144 signature->email = git_pool_strdup(pool, source->email);
ac3d33df 145 GIT_ERROR_CHECK_ALLOC(signature->email);
bdeb8772
CMN
146
147 signature->when.time = source->when.time;
148 signature->when.offset = source->when.offset;
eae0bfdc 149 signature->when.sign = source->when.sign;
bdeb8772
CMN
150
151 *dest = signature;
152
153 return 0;
154}
155
63396a39 156int git_signature_now(git_signature **sig_out, const char *name, const char *email)
9e9e6ae1
CMN
157{
158 time_t now;
53b7560b 159 time_t offset;
d03d309b 160 struct tm *utc_tm;
63396a39 161 git_signature *sig;
d03d309b 162 struct tm _utc;
9e9e6ae1 163
63396a39
MS
164 *sig_out = NULL;
165
d03d309b
CMN
166 /*
167 * Get the current time as seconds since the epoch and
168 * transform that into a tm struct containing the time at
169 * UTC. Give that to mktime which considers it a local time
170 * (tm_isdst = -1 asks it to take DST into account) and gives
171 * us that time as seconds since the epoch. The difference
172 * between its return value and 'now' is our offset to UTC.
173 */
14eb94ee 174 time(&now);
9ecf860d 175 utc_tm = p_gmtime_r(&now, &_utc);
d03d309b 176 utc_tm->tm_isdst = -1;
b97c169e 177 offset = (time_t)difftime(now, mktime(utc_tm));
9e9e6ae1 178 offset /= 60;
14eb94ee 179
4aa7de15
RB
180 if (git_signature_new(&sig, name, email, now, (int)offset) < 0)
181 return -1;
63396a39
MS
182
183 *sig_out = sig;
184
4aa7de15
RB
185 return 0;
186}
187
ce23330f
RB
188int git_signature_default(git_signature **out, git_repository *repo)
189{
190 int error;
ac99d86b 191 git_config *cfg;
ce23330f
RB
192 const char *user_name, *user_email;
193
ac99d86b 194 if ((error = git_repository_config_snapshot(&cfg, repo)) < 0)
ce23330f
RB
195 return error;
196
197 if (!(error = git_config_get_string(&user_name, cfg, "user.name")) &&
198 !(error = git_config_get_string(&user_email, cfg, "user.email")))
199 error = git_signature_now(out, user_name, user_email);
200
201 git_config_free(cfg);
202 return error;
203}
204
720d5472 205int git_signature__parse(git_signature *sig, const char **buffer_out,
7757be33 206 const char *buffer_end, const char *header, char ender)
58519018 207{
720d5472 208 const char *buffer = *buffer_out;
c51880ee 209 const char *email_start, *email_end;
58519018 210
de70aea6 211 memset(sig, 0, sizeof(git_signature));
58519018 212
d383c39b
ET
213 if (ender &&
214 (buffer_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
e579e0f7 215 return signature_parse_error("no newline given");
58519018 216
8b2c913a
MS
217 if (header) {
218 const size_t header_len = strlen(header);
58519018 219
41051e3f 220 if (buffer + header_len >= buffer_end || memcmp(buffer, header, header_len) != 0)
e579e0f7 221 return signature_parse_error("expected prefix doesn't match actual");
58519018 222
8b2c913a
MS
223 buffer += header_len;
224 }
58519018 225
41051e3f
VM
226 email_start = git__memrchr(buffer, '<', buffer_end - buffer);
227 email_end = git__memrchr(buffer, '>', buffer_end - buffer);
fbfc7580 228
c51880ee 229 if (!email_start || !email_end || email_end <= email_start)
e579e0f7 230 return signature_parse_error("malformed e-mail");
076141a1 231
c51880ee 232 email_start += 1;
cf80993a 233 sig->name = extract_trimmed(buffer, email_start - buffer - 1);
c51880ee 234 sig->email = extract_trimmed(email_start, email_end - email_start);
58519018 235
c51880ee
VM
236 /* Do we even have a time at the end of the signature? */
237 if (email_end + 2 < buffer_end) {
238 const char *time_start = email_end + 2;
239 const char *time_end;
58519018 240
6c7cee42
RD
241 if (git__strntol64(&sig->when.time, time_start,
242 buffer_end - time_start, &time_end, 10) < 0) {
34c13106
ET
243 git__free(sig->name);
244 git__free(sig->email);
eae0bfdc 245 sig->name = sig->email = NULL;
e579e0f7 246 return signature_parse_error("invalid Unix timestamp");
34c13106 247 }
c6e65aca 248
cf80993a 249 /* do we have a timezone? */
c51880ee
VM
250 if (time_end + 1 < buffer_end) {
251 int offset, hours, mins;
252 const char *tz_start, *tz_end;
253
254 tz_start = time_end + 1;
255
1fed6b07 256 if ((tz_start[0] != '-' && tz_start[0] != '+') ||
6c7cee42 257 git__strntol32(&offset, tz_start + 1,
ac3d33df 258 buffer_end - tz_start - 1, &tz_end, 10) < 0) {
6c7cee42 259 /* malformed timezone, just assume it's zero */
dc33b3d7
SG
260 offset = 0;
261 }
c51880ee
VM
262
263 hours = offset / 100;
264 mins = offset % 100;
932d1baf 265
c51880ee
VM
266 /*
267 * only store timezone if it's not overflowing;
268 * see http://www.worldtimezone.com/faq.html
269 */
23c9ff86 270 if (hours <= 14 && mins <= 59) {
c51880ee 271 sig->when.offset = (hours * 60) + mins;
eae0bfdc 272 sig->when.sign = tz_start[0];
c51880ee
VM
273 if (tz_start[0] == '-')
274 sig->when.offset = -sig->when.offset;
275 }
276 }
42a1b5e1 277 }
13710f1e 278
c51880ee 279 *buffer_out = buffer_end + 1;
4aa7de15 280 return 0;
58519018
VM
281}
282
d383c39b
ET
283int git_signature_from_buffer(git_signature **out, const char *buf)
284{
285 git_signature *sig;
286 const char *buf_end;
287 int error;
288
c25aa7cd
PP
289 GIT_ASSERT_ARG(out);
290 GIT_ASSERT_ARG(buf);
d383c39b
ET
291
292 *out = NULL;
293
294 sig = git__calloc(1, sizeof(git_signature));
ac3d33df 295 GIT_ERROR_CHECK_ALLOC(sig);
d383c39b
ET
296
297 buf_end = buf + strlen(buf);
298 error = git_signature__parse(sig, &buf, buf_end, NULL, '\0');
299
300 if (error)
301 git__free(sig);
302 else
303 *out = sig;
304
305 return error;
306}
307
e579e0f7 308void git_signature__writebuf(git_str *buf, const char *header, const git_signature *sig)
afeecf4f
VM
309{
310 int offset, hours, mins;
311 char sign;
312
313 offset = sig->when.offset;
eae0bfdc 314 sign = (sig->when.offset < 0 || sig->when.sign == '-') ? '-' : '+';
afeecf4f
VM
315
316 if (offset < 0)
317 offset = -offset;
318
319 hours = offset / 60;
320 mins = offset % 60;
321
e579e0f7 322 git_str_printf(buf, "%s%s <%s> %u %c%02d%02d\n",
afeecf4f
VM
323 header ? header : "", sig->name, sig->email,
324 (unsigned)sig->when.time, sign, hours, mins);
325}
58519018 326
a35a9890
ET
327bool git_signature__equal(const git_signature *one, const git_signature *two)
328{
c25aa7cd
PP
329 GIT_ASSERT_ARG(one);
330 GIT_ASSERT_ARG(two);
a35a9890
ET
331
332 return
333 git__strcmp(one->name, two->name) == 0 &&
334 git__strcmp(one->email, two->email) == 0 &&
335 one->when.time == two->when.time &&
eae0bfdc
PP
336 one->when.offset == two->when.offset &&
337 one->when.sign == two->when.sign;
a35a9890
ET
338}
339