File Coverage

deps/libgit2/src/signature.c
Criterion Covered Total %
statement 130 167 77.8
branch 65 138 47.1
condition n/a
subroutine n/a
pod n/a
total 195 305 63.9


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