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