]>
Commit | Line | Data |
---|---|---|
58519018 VM |
1 | /* |
2 | * This file is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License, version 2, | |
4 | * as published by the Free Software Foundation. | |
5 | * | |
6 | * In addition to the permissions in the GNU General Public License, | |
7 | * the authors give you unlimited permission to link the compiled | |
8 | * version of this file into combinations with other programs, | |
9 | * and to distribute those combinations without any restriction | |
10 | * coming from the use of this file. (The General Public License | |
11 | * restrictions do apply in other respects; for example, they cover | |
12 | * modification of the file, and distribution when not linked into | |
13 | * a combined executable.) | |
14 | * | |
15 | * This file is distributed in the hope that it will be useful, but | |
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | * General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; see the file COPYING. If not, write to | |
22 | * the Free Software Foundation, 51 Franklin Street, Fifth Floor, | |
23 | * Boston, MA 02110-1301, USA. | |
24 | */ | |
25 | ||
26 | #include "common.h" | |
638c2ca4 | 27 | #include "signature.h" |
58519018 | 28 | #include "repository.h" |
44908fe7 | 29 | #include "git2/common.h" |
58519018 | 30 | |
638c2ca4 | 31 | void git_signature_free(git_signature *sig) |
58519018 | 32 | { |
638c2ca4 | 33 | if (sig == NULL) |
58519018 VM |
34 | return; |
35 | ||
638c2ca4 VM |
36 | free(sig->name); |
37 | free(sig->email); | |
38 | free(sig); | |
58519018 VM |
39 | } |
40 | ||
56d8ca26 | 41 | git_signature *git_signature_new(const char *name, const char *email, git_time_t time, int offset) |
58519018 | 42 | { |
638c2ca4 | 43 | git_signature *p = NULL; |
58519018 | 44 | |
638c2ca4 | 45 | if ((p = git__malloc(sizeof(git_signature))) == NULL) |
58519018 VM |
46 | goto cleanup; |
47 | ||
48 | p->name = git__strdup(name); | |
49 | p->email = git__strdup(email); | |
638c2ca4 VM |
50 | p->when.time = time; |
51 | p->when.offset = offset; | |
58519018 VM |
52 | |
53 | if (p->name == NULL || p->email == NULL) | |
54 | goto cleanup; | |
55 | ||
56 | return p; | |
57 | ||
58 | cleanup: | |
638c2ca4 | 59 | git_signature_free(p); |
58519018 VM |
60 | return NULL; |
61 | } | |
62 | ||
638c2ca4 | 63 | git_signature *git_signature_dup(const git_signature *sig) |
58519018 | 64 | { |
638c2ca4 | 65 | return git_signature_new(sig->name, sig->email, sig->when.time, sig->when.offset); |
58519018 VM |
66 | } |
67 | ||
8416c9ad | 68 | git_signature *git_signature_now(const char *name, const char *email) |
9e9e6ae1 CMN |
69 | { |
70 | time_t now; | |
53b7560b | 71 | time_t offset; |
14eb94ee | 72 | struct tm *utc_tm, *local_tm; |
9e9e6ae1 | 73 | |
14eb94ee VM |
74 | #ifndef GIT_WIN32 |
75 | struct tm _utc, _local; | |
76 | #endif | |
9e9e6ae1 | 77 | |
14eb94ee | 78 | time(&now); |
9e9e6ae1 | 79 | |
14eb94ee VM |
80 | /** |
81 | * On Win32, `gmtime_r` doesn't exist but | |
82 | * `gmtime` is threadsafe, so we can use that | |
83 | */ | |
84 | #ifdef GIT_WIN32 | |
85 | utc_tm = gmtime(&now); | |
86 | local_tm = localtime(&now); | |
87 | #else | |
88 | utc_tm = gmtime_r(&now, &_utc); | |
89 | local_tm = localtime_r(&now, &_local); | |
90 | #endif | |
91 | ||
92 | offset = mktime(local_tm) - mktime(utc_tm); | |
9e9e6ae1 | 93 | offset /= 60; |
14eb94ee | 94 | |
9e9e6ae1 | 95 | /* mktime takes care of setting tm_isdst correctly */ |
14eb94ee | 96 | if (local_tm->tm_isdst) |
9e9e6ae1 CMN |
97 | offset += 60; |
98 | ||
53b7560b | 99 | return git_signature_new(name, email, now, (int)offset); |
9e9e6ae1 | 100 | } |
58519018 | 101 | |
c6e65aca | 102 | static int parse_timezone_offset(const char *buffer, long *offset_out) |
13710f1e | 103 | { |
c6e65aca | 104 | long offset, dec_offset; |
13710f1e | 105 | int mins, hours; |
106 | ||
c6e65aca VM |
107 | const char *offset_start; |
108 | const char *offset_end; | |
13710f1e | 109 | |
fbfc7580 | 110 | //we are sure that *buffer == ' ' |
13710f1e | 111 | offset_start = buffer + 1; |
112 | ||
638c2ca4 | 113 | if (*offset_start == '\n') { |
fee065a0 | 114 | *offset_out = 0; |
115 | return GIT_SUCCESS; | |
116 | } | |
117 | ||
13710f1e | 118 | if (offset_start[0] != '-' && offset_start[0] != '+') |
5de24ec7 | 119 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It doesn't start with '+' or '-'"); |
13710f1e | 120 | |
fbfc7580 | 121 | if (offset_start[1] < '0' || offset_start[1] > '9') |
fea400f8 | 122 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset."); |
fbfc7580 | 123 | |
c6e65aca | 124 | if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS) |
5de24ec7 | 125 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It isn't a number"); |
13710f1e | 126 | |
127 | if (offset_end - offset_start != 5) | |
5de24ec7 | 128 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Invalid length"); |
13710f1e | 129 | |
fbfc7580 DG |
130 | if (dec_offset > 1400) |
131 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Value too large"); | |
132 | ||
13710f1e | 133 | hours = dec_offset / 100; |
134 | mins = dec_offset % 100; | |
135 | ||
638c2ca4 | 136 | if (hours > 14) // see http://www.worldtimezone.com/faq.html |
fbfc7580 | 137 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large"); |
13710f1e | 138 | |
638c2ca4 | 139 | if (mins > 59) |
5de24ec7 | 140 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Minute value too large"); |
13710f1e | 141 | |
142 | offset = (hours * 60) + mins; | |
143 | ||
144 | if (offset_start[0] == '-') | |
13710f1e | 145 | offset *= -1; |
13710f1e | 146 | |
147 | *offset_out = offset; | |
148 | ||
149 | return GIT_SUCCESS; | |
150 | } | |
151 | ||
152 | ||
720d5472 | 153 | int git_signature__parse(git_signature *sig, const char **buffer_out, |
58519018 VM |
154 | const char *buffer_end, const char *header) |
155 | { | |
156 | const size_t header_len = strlen(header); | |
157 | ||
158 | int name_length, email_length; | |
720d5472 VM |
159 | const char *buffer = *buffer_out; |
160 | const char *line_end, *name_end, *email_end; | |
c6e65aca | 161 | long offset = 0, time; |
58519018 | 162 | |
638c2ca4 | 163 | memset(sig, 0x0, sizeof(git_signature)); |
58519018 VM |
164 | |
165 | line_end = memchr(buffer, '\n', buffer_end - buffer); | |
166 | if (!line_end) | |
5de24ec7 | 167 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. No newline found");; |
58519018 VM |
168 | |
169 | if (buffer + (header_len + 1) > line_end) | |
5de24ec7 | 170 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short"); |
58519018 VM |
171 | |
172 | if (memcmp(buffer, header, header_len) != 0) | |
5de24ec7 | 173 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Expected prefix '%s' doesn't match actual", header); |
58519018 VM |
174 | |
175 | buffer += header_len; | |
176 | ||
177 | /* Parse name */ | |
fbfc7580 | 178 | if ((name_end = strstr(buffer, " <")) == NULL) |
5de24ec7 | 179 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail start"); |
58519018 | 180 | |
fbfc7580 | 181 | name_length = name_end - buffer; |
fbfc7580 | 182 | |
638c2ca4 | 183 | sig->name = git__malloc(name_length + 1); |
076141a1 CMN |
184 | if (sig->name == NULL) |
185 | return GIT_ENOMEM; | |
186 | ||
638c2ca4 VM |
187 | memcpy(sig->name, buffer, name_length); |
188 | sig->name[name_length] = 0; | |
fbfc7580 | 189 | buffer = name_end + 2; |
58519018 VM |
190 | |
191 | if (buffer >= line_end) | |
5de24ec7 | 192 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly"); |
58519018 VM |
193 | |
194 | /* Parse email */ | |
fbfc7580 | 195 | if ((email_end = strstr(buffer, "> ")) == NULL) |
5de24ec7 | 196 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail end"); |
58519018 VM |
197 | |
198 | email_length = email_end - buffer; | |
638c2ca4 | 199 | sig->email = git__malloc(email_length + 1); |
076141a1 CMN |
200 | if (sig->name == NULL) |
201 | return GIT_ENOMEM; | |
202 | ||
638c2ca4 VM |
203 | memcpy(sig->email, buffer, email_length); |
204 | sig->email[email_length] = 0; | |
fbfc7580 | 205 | buffer = email_end + 2; |
58519018 VM |
206 | |
207 | if (buffer >= line_end) | |
5de24ec7 | 208 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly"); |
58519018 | 209 | |
fbfc7580 | 210 | /* verify email */ |
e053c911 | 211 | if (strpbrk(sig->email, "><\n") != NULL) |
fbfc7580 DG |
212 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Malformed e-mail"); |
213 | ||
c6e65aca | 214 | if (git__strtol32(&time, buffer, &buffer, 10) < GIT_SUCCESS) |
5de24ec7 | 215 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Timestamp isn't a number"); |
58519018 | 216 | |
c6e65aca VM |
217 | sig->when.time = (time_t)time; |
218 | ||
638c2ca4 | 219 | if (parse_timezone_offset(buffer, &offset) < GIT_SUCCESS) |
00556496 | 220 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Could not parse timezone offset"); |
13710f1e | 221 | |
638c2ca4 | 222 | sig->when.offset = offset; |
13710f1e | 223 | |
58519018 | 224 | *buffer_out = (line_end + 1); |
6f02c3ba | 225 | return GIT_SUCCESS; |
58519018 VM |
226 | } |
227 | ||
72a3fe42 | 228 | int git_signature__write(char **signature, const char *header, const git_signature *sig) |
58519018 | 229 | { |
13710f1e | 230 | int offset, hours, mins; |
72a3fe42 VM |
231 | char sig_buffer[2048]; |
232 | int sig_buffer_len; | |
233 | char sign; | |
13710f1e | 234 | |
638c2ca4 VM |
235 | offset = sig->when.offset; |
236 | sign = (sig->when.offset < 0) ? '-' : '+'; | |
13710f1e | 237 | |
238 | if (offset < 0) | |
239 | offset = -offset; | |
240 | ||
241 | hours = offset / 60; | |
242 | mins = offset % 60; | |
243 | ||
72a3fe42 VM |
244 | sig_buffer_len = snprintf(sig_buffer, sizeof(sig_buffer), |
245 | "%s %s <%s> %u %c%02d%02d\n", | |
246 | header, sig->name, sig->email, | |
247 | (unsigned)sig->when.time, sign, hours, mins); | |
248 | ||
249 | if (sig_buffer_len < 0 || (size_t)sig_buffer_len > sizeof(sig_buffer)) | |
250 | return GIT_ENOMEM; | |
251 | ||
252 | *signature = git__strdup(sig_buffer); | |
253 | return sig_buffer_len; | |
58519018 VM |
254 | } |
255 | ||
256 |