]> git.proxmox.com Git - libgit2.git/blob - src/signature.c
Merge pull request #223 from carlosmn/valgrind
[libgit2.git] / src / signature.c
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"
27 #include "signature.h"
28 #include "repository.h"
29 #include "git2/common.h"
30
31 void git_signature_free(git_signature *sig)
32 {
33 if (sig == NULL)
34 return;
35
36 free(sig->name);
37 free(sig->email);
38 free(sig);
39 }
40
41 git_signature *git_signature_new(const char *name, const char *email, git_time_t time, int offset)
42 {
43 git_signature *p = NULL;
44
45 if ((p = git__malloc(sizeof(git_signature))) == NULL)
46 goto cleanup;
47
48 p->name = git__strdup(name);
49 p->email = git__strdup(email);
50 p->when.time = time;
51 p->when.offset = offset;
52
53 if (p->name == NULL || p->email == NULL)
54 goto cleanup;
55
56 return p;
57
58 cleanup:
59 git_signature_free(p);
60 return NULL;
61 }
62
63 git_signature *git_signature_dup(const git_signature *sig)
64 {
65 return git_signature_new(sig->name, sig->email, sig->when.time, sig->when.offset);
66 }
67
68 git_signature *git_signature_now(const char *name, const char *email)
69 {
70 time_t now;
71 time_t offset;
72 struct tm *utc_tm, *local_tm;
73
74 #ifndef GIT_WIN32
75 struct tm _utc, _local;
76 #endif
77
78 time(&now);
79
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);
93 offset /= 60;
94
95 /* mktime takes care of setting tm_isdst correctly */
96 if (local_tm->tm_isdst)
97 offset += 60;
98
99 return git_signature_new(name, email, now, (int)offset);
100 }
101
102 static int parse_timezone_offset(const char *buffer, long *offset_out)
103 {
104 long offset, dec_offset;
105 int mins, hours;
106
107 const char *offset_start;
108 const char *offset_end;
109
110 offset_start = buffer + 1;
111
112 if (*offset_start == '\n') {
113 *offset_out = 0;
114 return GIT_SUCCESS;
115 }
116
117 if (offset_start[0] != '-' && offset_start[0] != '+')
118 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It doesn't start with '+' or '-'");
119
120 if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS)
121 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It isn't a number");
122
123 if (offset_end - offset_start != 5)
124 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Invalid length");
125
126 hours = dec_offset / 100;
127 mins = dec_offset % 100;
128
129 if (hours > 14) // see http://www.worldtimezone.com/faq.html
130 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large");;
131
132 if (mins > 59)
133 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Minute value too large");
134
135 offset = (hours * 60) + mins;
136
137 if (offset_start[0] == '-')
138 offset *= -1;
139
140 *offset_out = offset;
141
142 return GIT_SUCCESS;
143 }
144
145
146 int git_signature__parse(git_signature *sig, const char **buffer_out,
147 const char *buffer_end, const char *header)
148 {
149 const size_t header_len = strlen(header);
150
151 int name_length, email_length;
152 const char *buffer = *buffer_out;
153 const char *line_end, *name_end, *email_end;
154 long offset = 0, time;
155
156 memset(sig, 0x0, sizeof(git_signature));
157
158 line_end = memchr(buffer, '\n', buffer_end - buffer);
159 if (!line_end)
160 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. No newline found");;
161
162 if (buffer + (header_len + 1) > line_end)
163 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short");
164
165 if (memcmp(buffer, header, header_len) != 0)
166 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Expected prefix '%s' doesn't match actual", header);
167
168 buffer += header_len;
169
170 /* Parse name */
171 if ((name_end = memchr(buffer, '<', buffer_end - buffer)) == NULL)
172 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail start");
173
174 name_length = name_end - buffer - 1;
175 sig->name = git__malloc(name_length + 1);
176 if (sig->name == NULL)
177 return GIT_ENOMEM;
178
179 memcpy(sig->name, buffer, name_length);
180 sig->name[name_length] = 0;
181 buffer = name_end + 1;
182
183 if (buffer >= line_end)
184 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly");
185
186 /* Parse email */
187 if ((email_end = memchr(buffer, '>', buffer_end - buffer)) == NULL)
188 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail end");
189
190 email_length = email_end - buffer;
191 sig->email = git__malloc(email_length + 1);
192 if (sig->name == NULL)
193 return GIT_ENOMEM;
194
195 memcpy(sig->email, buffer, email_length);
196 sig->email[email_length] = 0;
197 buffer = email_end + 1;
198
199 if (buffer >= line_end)
200 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly");
201
202 if (git__strtol32(&time, buffer, &buffer, 10) < GIT_SUCCESS)
203 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Timestamp isn't a number");
204
205 sig->when.time = (time_t)time;
206
207 if (parse_timezone_offset(buffer, &offset) < GIT_SUCCESS)
208 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Could not parse timezone offset");
209
210 sig->when.offset = offset;
211
212 *buffer_out = (line_end + 1);
213 return GIT_SUCCESS;
214 }
215
216 int git_signature__write(char **signature, const char *header, const git_signature *sig)
217 {
218 int offset, hours, mins;
219 char sig_buffer[2048];
220 int sig_buffer_len;
221 char sign;
222
223 offset = sig->when.offset;
224 sign = (sig->when.offset < 0) ? '-' : '+';
225
226 if (offset < 0)
227 offset = -offset;
228
229 hours = offset / 60;
230 mins = offset % 60;
231
232 sig_buffer_len = snprintf(sig_buffer, sizeof(sig_buffer),
233 "%s %s <%s> %u %c%02d%02d\n",
234 header, sig->name, sig->email,
235 (unsigned)sig->when.time, sign, hours, mins);
236
237 if (sig_buffer_len < 0 || (size_t)sig_buffer_len > sizeof(sig_buffer))
238 return GIT_ENOMEM;
239
240 *signature = git__strdup(sig_buffer);
241 return sig_buffer_len;
242 }
243
244