]>
git.proxmox.com Git - libgit2.git/blob - src/signature.c
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.
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.)
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.
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.
27 #include "signature.h"
28 #include "repository.h"
29 #include "git2/common.h"
31 void git_signature_free(git_signature
*sig
)
41 static const char *skip_leading_spaces(const char *buffer
, const char *buffer_end
)
43 while (*buffer
== ' ' && buffer
< buffer_end
)
49 static const char *skip_trailing_spaces(const char *buffer_start
, const char *buffer_end
)
51 while (*buffer_end
== ' ' && buffer_end
> buffer_start
)
57 static int process_trimming(const char *input
, char **storage
, const char *input_end
, int fail_when_empty
)
59 const char *left
, *right
;
60 int trimmed_input_length
;
62 left
= skip_leading_spaces(input
, input_end
);
63 right
= skip_trailing_spaces(input
, input_end
- 1);
67 return git__throw(GIT_EINVALIDARGS
, "Failed to trim. Input is either empty or only contains spaces");
72 trimmed_input_length
= right
- left
+ 1;
74 *storage
= git__malloc(trimmed_input_length
+ 1);
78 memcpy(*storage
, left
, trimmed_input_length
);
79 (*storage
)[trimmed_input_length
] = 0;
84 git_signature
*git_signature_new(const char *name
, const char *email
, git_time_t time
, int offset
)
87 git_signature
*p
= NULL
;
89 assert(name
&& email
);
91 if ((p
= git__malloc(sizeof(git_signature
))) == NULL
)
94 memset(p
, 0x0, sizeof(git_signature
));
96 error
= process_trimming(name
, &p
->name
, name
+ strlen(name
), 1);
97 if (error
< GIT_SUCCESS
) {
98 git__rethrow(GIT_EINVALIDARGS
, "Failed to create signature. 'name' argument is invalid");
102 error
= process_trimming(email
, &p
->email
, email
+ strlen(email
), 1);
103 if (error
< GIT_SUCCESS
) {
104 git__rethrow(GIT_EINVALIDARGS
, "Failed to create signature. 'email' argument is invalid");
109 p
->when
.offset
= offset
;
114 git_signature_free(p
);
118 git_signature
*git_signature_dup(const git_signature
*sig
)
120 return git_signature_new(sig
->name
, sig
->email
, sig
->when
.time
, sig
->when
.offset
);
123 git_signature
*git_signature_now(const char *name
, const char *email
)
127 struct tm
*utc_tm
, *local_tm
;
130 struct tm _utc
, _local
;
136 * On Win32, `gmtime_r` doesn't exist but
137 * `gmtime` is threadsafe, so we can use that
140 utc_tm
= gmtime(&now
);
141 local_tm
= localtime(&now
);
143 utc_tm
= gmtime_r(&now
, &_utc
);
144 local_tm
= localtime_r(&now
, &_local
);
147 offset
= mktime(local_tm
) - mktime(utc_tm
);
150 /* mktime takes care of setting tm_isdst correctly */
151 if (local_tm
->tm_isdst
)
154 return git_signature_new(name
, email
, now
, (int)offset
);
157 static int parse_timezone_offset(const char *buffer
, int *offset_out
)
160 int mins
, hours
, offset
;
162 const char *offset_start
;
163 const char *offset_end
;
165 offset_start
= buffer
;
167 if (*offset_start
== '\n') {
172 if (offset_start
[0] != '-' && offset_start
[0] != '+')
173 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. It doesn't start with '+' or '-'");
175 if (offset_start
[1] < '0' || offset_start
[1] > '9')
176 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset.");
178 if (git__strtol32(&dec_offset
, offset_start
+ 1, &offset_end
, 10) < GIT_SUCCESS
)
179 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. It isn't a number");
181 if (offset_end
- offset_start
!= 5)
182 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. Invalid length");
184 if (dec_offset
> 1400)
185 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. Value too large");
187 hours
= dec_offset
/ 100;
188 mins
= dec_offset
% 100;
190 if (hours
> 14) // see http://www.worldtimezone.com/faq.html
191 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. Hour value too large");
194 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. Minute value too large");
196 offset
= (hours
* 60) + mins
;
198 if (offset_start
[0] == '-')
201 *offset_out
= offset
;
206 int process_next_token(const char **buffer_out
, char **storage
,
207 const char *token_end
, const char *right_boundary
)
209 int error
= process_trimming(*buffer_out
, storage
, token_end
, 0);
210 if (error
< GIT_SUCCESS
)
213 *buffer_out
= token_end
+ 1;
215 if (*buffer_out
> right_boundary
)
216 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Signature too short");
221 const char *scan_for_previous_token(const char *buffer
, const char *left_boundary
)
225 if (buffer
<= left_boundary
)
228 start
= skip_trailing_spaces(left_boundary
, buffer
);
230 /* Search for previous occurence of space */
231 while (start
[-1] != ' ' && start
> left_boundary
)
237 int parse_time(git_time_t
*time_out
, const char *buffer
)
242 if (*buffer
== '+' || *buffer
== '-')
243 return git__throw(GIT_ERROR
, "Failed while parsing time. '%s' rather look like a timezone offset.", buffer
);
245 error
= git__strtol32(&time
, buffer
, &buffer
, 10);
247 if (error
< GIT_SUCCESS
)
250 *time_out
= (git_time_t
)time
;
255 int git_signature__parse(git_signature
*sig
, const char **buffer_out
,
256 const char *buffer_end
, const char *header
, char ender
)
258 const char *buffer
= *buffer_out
;
259 const char *line_end
, *name_end
, *email_end
, *tz_start
, *time_start
;
260 int error
= GIT_SUCCESS
;
262 memset(sig
, 0x0, sizeof(git_signature
));
264 if ((line_end
= memchr(buffer
, ender
, buffer_end
- buffer
)) == NULL
)
265 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. No newline given");
268 const size_t header_len
= strlen(header
);
270 if (memcmp(buffer
, header
, header_len
) != 0)
271 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Expected prefix '%s' doesn't match actual", header
);
273 buffer
+= header_len
;
276 if (buffer
> line_end
)
277 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Signature too short");
279 if ((name_end
= strchr(buffer
, '<')) == NULL
)
280 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Cannot find '<' in signature");
282 if ((email_end
= strchr(buffer
, '>')) == NULL
)
283 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Cannot find '>' in signature");
285 if (email_end
< name_end
)
286 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Malformed e-mail");
288 error
= process_next_token(&buffer
, &sig
->name
, name_end
, line_end
);
289 if (error
< GIT_SUCCESS
)
292 error
= process_next_token(&buffer
, &sig
->email
, email_end
, line_end
);
293 if (error
< GIT_SUCCESS
)
296 tz_start
= scan_for_previous_token(line_end
- 1, buffer
);
298 if (tz_start
== NULL
)
299 goto clean_exit
; /* No timezone nor date */
301 time_start
= scan_for_previous_token(tz_start
- 1, buffer
);
302 if (time_start
== NULL
|| parse_time(&sig
->when
.time
, time_start
) < GIT_SUCCESS
) {
303 /* The tz_start might point at the time */
304 parse_time(&sig
->when
.time
, tz_start
);
308 if (parse_timezone_offset(tz_start
, &sig
->when
.offset
) < GIT_SUCCESS
) {
309 sig
->when
.time
= 0; /* Bogus timezone, we reset the time */
313 *buffer_out
= line_end
+ 1;
317 void git_signature__writebuf(git_buf
*buf
, const char *header
, const git_signature
*sig
)
319 int offset
, hours
, mins
;
322 offset
= sig
->when
.offset
;
323 sign
= (sig
->when
.offset
< 0) ? '-' : '+';
331 git_buf_printf(buf
, "%s%s <%s> %u %c%02d%02d\n",
332 header
? header
: "", sig
->name
, sig
->email
,
333 (unsigned)sig
->when
.time
, sign
, hours
, mins
);