]>
Commit | Line | Data |
---|---|---|
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 | 14 | void 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 |
26 | static 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 | 32 | static bool contains_angle_brackets(const char *input) |
a01acc47 | 33 | { |
c51880ee | 34 | return strchr(input, '<') != NULL || strchr(input, '>') != NULL; |
a01acc47 | 35 | } |
36 | ||
c51880ee | 37 | static 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 | 50 | int 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 | 85 | int 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 | 109 | int 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 |
141 | int 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 | 161 | int 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 |
231 | void 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 |