]>
git.proxmox.com Git - libgit2.git/blob - src/signature.c
2 * Copyright (C) 2009-2012 the libgit2 contributors
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.
10 #include "repository.h"
11 #include "git2/common.h"
13 void git_signature_free(git_signature
*sig
)
20 git__free(sig
->email
);
25 static const char *skip_leading_spaces(const char *buffer
, const char *buffer_end
)
27 while (*buffer
== ' ' && buffer
< buffer_end
)
33 static const char *skip_trailing_spaces(const char *buffer_start
, const char *buffer_end
)
35 while (*buffer_end
== ' ' && buffer_end
> buffer_start
)
41 static int process_trimming(const char *input
, char **storage
, const char *input_end
, int fail_when_empty
)
43 const char *left
, *right
;
44 int trimmed_input_length
;
46 left
= skip_leading_spaces(input
, input_end
);
47 right
= skip_trailing_spaces(input
, input_end
- 1);
51 return git__throw(GIT_EINVALIDARGS
, "Failed to trim. Input is either empty or only contains spaces");
56 trimmed_input_length
= right
- left
+ 1;
58 *storage
= git__malloc(trimmed_input_length
+ 1);
62 memcpy(*storage
, left
, trimmed_input_length
);
63 (*storage
)[trimmed_input_length
] = 0;
68 int git_signature_new(git_signature
**sig_out
, const char *name
, const char *email
, git_time_t time
, int offset
)
71 git_signature
*p
= NULL
;
73 assert(name
&& email
);
77 if ((p
= git__malloc(sizeof(git_signature
))) == NULL
) {
82 memset(p
, 0x0, sizeof(git_signature
));
84 error
= process_trimming(name
, &p
->name
, name
+ strlen(name
), 1);
85 if (error
< GIT_SUCCESS
) {
86 git__rethrow(GIT_EINVALIDARGS
, "Failed to create signature. 'name' argument is invalid");
90 error
= process_trimming(email
, &p
->email
, email
+ strlen(email
), 1);
91 if (error
< GIT_SUCCESS
) {
92 git__rethrow(GIT_EINVALIDARGS
, "Failed to create signature. 'email' argument is invalid");
97 p
->when
.offset
= offset
;
104 git_signature_free(p
);
108 git_signature
*git_signature_dup(const git_signature
*sig
)
111 if (git_signature_new(&new, sig
->name
, sig
->email
, sig
->when
.time
, sig
->when
.offset
) < GIT_SUCCESS
)
116 int git_signature_now(git_signature
**sig_out
, const char *name
, const char *email
)
121 struct tm
*utc_tm
, *local_tm
;
125 struct tm _utc
, _local
;
133 * On Win32, `gmtime_r` doesn't exist but
134 * `gmtime` is threadsafe, so we can use that
137 utc_tm
= gmtime(&now
);
138 local_tm
= localtime(&now
);
140 utc_tm
= gmtime_r(&now
, &_utc
);
141 local_tm
= localtime_r(&now
, &_local
);
144 offset
= mktime(local_tm
) - mktime(utc_tm
);
147 /* mktime takes care of setting tm_isdst correctly */
148 if (local_tm
->tm_isdst
)
151 if ((error
= git_signature_new(&sig
, name
, email
, now
, (int)offset
)) < GIT_SUCCESS
)
159 static int parse_timezone_offset(const char *buffer
, int *offset_out
)
162 int mins
, hours
, offset
;
164 const char *offset_start
;
165 const char *offset_end
;
167 offset_start
= buffer
;
169 if (*offset_start
== '\n') {
174 if (offset_start
[0] != '-' && offset_start
[0] != '+')
175 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. It doesn't start with '+' or '-'");
177 if (offset_start
[1] < '0' || offset_start
[1] > '9')
178 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset.");
180 if (git__strtol32(&dec_offset
, offset_start
+ 1, &offset_end
, 10) < GIT_SUCCESS
)
181 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. It isn't a number");
183 if (offset_end
- offset_start
!= 5)
184 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. Invalid length");
186 if (dec_offset
> 1400)
187 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. Value too large");
189 hours
= dec_offset
/ 100;
190 mins
= dec_offset
% 100;
192 if (hours
> 14) // see http://www.worldtimezone.com/faq.html
193 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. Hour value too large");
196 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse TZ offset. Minute value too large");
198 offset
= (hours
* 60) + mins
;
200 if (offset_start
[0] == '-')
203 *offset_out
= offset
;
208 static int process_next_token(const char **buffer_out
, char **storage
,
209 const char *token_end
, const char *right_boundary
)
211 int error
= process_trimming(*buffer_out
, storage
, token_end
, 0);
212 if (error
< GIT_SUCCESS
)
215 *buffer_out
= token_end
+ 1;
217 if (*buffer_out
> right_boundary
)
218 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Signature too short");
223 static const char *scan_for_previous_token(const char *buffer
, const char *left_boundary
)
227 if (buffer
<= left_boundary
)
230 start
= skip_trailing_spaces(left_boundary
, buffer
);
232 /* Search for previous occurence of space */
233 while (start
[-1] != ' ' && start
> left_boundary
)
239 static int parse_time(git_time_t
*time_out
, const char *buffer
)
244 if (*buffer
== '+' || *buffer
== '-')
245 return git__throw(GIT_ERROR
, "Failed while parsing time. '%s' rather look like a timezone offset.", buffer
);
247 error
= git__strtol32(&time
, buffer
, &buffer
, 10);
249 if (error
< GIT_SUCCESS
)
252 *time_out
= (git_time_t
)time
;
257 int git_signature__parse(git_signature
*sig
, const char **buffer_out
,
258 const char *buffer_end
, const char *header
, char ender
)
260 const char *buffer
= *buffer_out
;
261 const char *line_end
, *name_end
, *email_end
, *tz_start
, *time_start
;
262 int error
= GIT_SUCCESS
;
264 memset(sig
, 0x0, sizeof(git_signature
));
266 if ((line_end
= memchr(buffer
, ender
, buffer_end
- buffer
)) == NULL
)
267 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. No newline given");
270 const size_t header_len
= strlen(header
);
272 if (memcmp(buffer
, header
, header_len
) != 0)
273 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Expected prefix '%s' doesn't match actual", header
);
275 buffer
+= header_len
;
278 if (buffer
> line_end
)
279 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Signature too short");
281 if ((name_end
= strchr(buffer
, '<')) == NULL
)
282 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Cannot find '<' in signature");
284 if ((email_end
= strchr(name_end
, '>')) == NULL
)
285 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Cannot find '>' in signature");
287 if (email_end
< name_end
)
288 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse signature. Malformed e-mail");
290 error
= process_next_token(&buffer
, &sig
->name
, name_end
, line_end
);
291 if (error
< GIT_SUCCESS
)
294 error
= process_next_token(&buffer
, &sig
->email
, email_end
, line_end
);
295 if (error
< GIT_SUCCESS
)
298 tz_start
= scan_for_previous_token(line_end
- 1, buffer
);
300 if (tz_start
== NULL
)
301 goto clean_exit
; /* No timezone nor date */
303 time_start
= scan_for_previous_token(tz_start
- 1, buffer
);
304 if (time_start
== NULL
|| parse_time(&sig
->when
.time
, time_start
) < GIT_SUCCESS
) {
305 /* The tz_start might point at the time */
306 parse_time(&sig
->when
.time
, tz_start
);
310 if (parse_timezone_offset(tz_start
, &sig
->when
.offset
) < GIT_SUCCESS
) {
311 sig
->when
.time
= 0; /* Bogus timezone, we reset the time */
315 *buffer_out
= line_end
+ 1;
319 void git_signature__writebuf(git_buf
*buf
, const char *header
, const git_signature
*sig
)
321 int offset
, hours
, mins
;
324 offset
= sig
->when
.offset
;
325 sign
= (sig
->when
.offset
< 0) ? '-' : '+';
333 git_buf_printf(buf
, "%s%s <%s> %u %c%02d%02d\n",
334 header
? header
: "", sig
->name
, sig
->email
,
335 (unsigned)sig
->when
.time
, sign
, hours
, mins
);