File Coverage

deps/libgit2/src/libgit2/signature.c
Criterion Covered Total %
statement 130 172 75.5
branch 63 134 47.0
condition n/a
subroutine n/a
pod n/a
total 193 306 63.0


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