]>
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 signature_error(const char *msg
)
43 giterr_set(GITERR_INVALID
, "Failed to parse signature - %s", msg
);
47 static int process_trimming(const char *input
, char **storage
, const char *input_end
, int fail_when_empty
)
49 const char *left
, *right
;
50 int trimmed_input_length
;
54 left
= skip_leading_spaces(input
, input_end
);
55 right
= skip_trailing_spaces(input
, input_end
- 1);
59 return signature_error("input is either empty of contains only spaces");
64 trimmed_input_length
= right
- left
+ 1;
66 *storage
= git__malloc(trimmed_input_length
+ 1);
67 GITERR_CHECK_ALLOC(*storage
);
69 memcpy(*storage
, left
, trimmed_input_length
);
70 (*storage
)[trimmed_input_length
] = 0;
75 int git_signature_new(git_signature
**sig_out
, const char *name
, const char *email
, git_time_t time
, int offset
)
78 git_signature
*p
= NULL
;
80 assert(name
&& email
);
84 p
= git__calloc(1, sizeof(git_signature
));
85 GITERR_CHECK_ALLOC(p
);
87 if ((error
= process_trimming(name
, &p
->name
, name
+ strlen(name
), 1)) < 0 ||
88 (error
= process_trimming(email
, &p
->email
, email
+ strlen(email
), 1)) < 0)
90 git_signature_free(p
);
95 p
->when
.offset
= offset
;
102 git_signature
*git_signature_dup(const git_signature
*sig
)
105 if (git_signature_new(&new, sig
->name
, sig
->email
, sig
->when
.time
, sig
->when
.offset
) < 0)
110 int git_signature_now(git_signature
**sig_out
, const char *name
, const char *email
)
114 struct tm
*utc_tm
, *local_tm
;
118 struct tm _utc
, _local
;
126 * On Win32, `gmtime_r` doesn't exist but
127 * `gmtime` is threadsafe, so we can use that
130 utc_tm
= gmtime(&now
);
131 local_tm
= localtime(&now
);
133 utc_tm
= gmtime_r(&now
, &_utc
);
134 local_tm
= localtime_r(&now
, &_local
);
137 offset
= mktime(local_tm
) - mktime(utc_tm
);
140 /* mktime takes care of setting tm_isdst correctly */
141 if (local_tm
->tm_isdst
)
144 if (git_signature_new(&sig
, name
, email
, now
, (int)offset
) < 0)
152 static int timezone_error(const char *msg
)
154 giterr_set(GITERR_INVALID
, "Failed to parse TZ offset - %s", msg
);
158 static int parse_timezone_offset(const char *buffer
, int *offset_out
)
161 int mins
, hours
, offset
;
163 const char *offset_start
;
164 const char *offset_end
;
166 offset_start
= buffer
;
168 if (*offset_start
== '\n') {
173 if (offset_start
[0] != '-' && offset_start
[0] != '+')
174 return timezone_error("does not start with '+' or '-'");
176 if (offset_start
[1] < '0' || offset_start
[1] > '9')
177 return timezone_error("expected initial digit");
179 if (git__strtol32(&dec_offset
, offset_start
+ 1, &offset_end
, 10) < GIT_SUCCESS
)
180 return timezone_error("not a valid number");
182 if (offset_end
- offset_start
!= 5)
183 return timezone_error("invalid length");
185 if (dec_offset
> 1400)
186 return timezone_error("value too large");
188 hours
= dec_offset
/ 100;
189 mins
= dec_offset
% 100;
191 if (hours
> 14) // see http://www.worldtimezone.com/faq.html
192 return timezone_error("hour value too large");
195 return timezone_error("minutes value too large");
197 offset
= (hours
* 60) + mins
;
199 if (offset_start
[0] == '-')
202 *offset_out
= offset
;
207 static int process_next_token(const char **buffer_out
, char **storage
,
208 const char *token_end
, const char *right_boundary
)
210 int error
= process_trimming(*buffer_out
, storage
, token_end
, 0);
214 *buffer_out
= token_end
+ 1;
216 if (*buffer_out
> right_boundary
)
217 return signature_error("signature is too short");
222 static const char *scan_for_previous_token(const char *buffer
, const char *left_boundary
)
226 if (buffer
<= left_boundary
)
229 start
= skip_trailing_spaces(left_boundary
, buffer
);
231 /* Search for previous occurence of space */
232 while (start
[-1] != ' ' && start
> left_boundary
)
238 static int parse_time(git_time_t
*time_out
, const char *buffer
)
243 if (*buffer
== '+' || *buffer
== '-') {
244 giterr_set(GITERR_INVALID
, "Failed while parsing time. '%s' actually looks like a timezone offset.", buffer
);
248 error
= git__strtol32(&time
, buffer
, &buffer
, 10);
251 *time_out
= (git_time_t
)time
;
256 int git_signature__parse(git_signature
*sig
, const char **buffer_out
,
257 const char *buffer_end
, const char *header
, char ender
)
259 const char *buffer
= *buffer_out
;
260 const char *line_end
, *name_end
, *email_end
, *tz_start
, *time_start
;
261 int error
= GIT_SUCCESS
;
263 memset(sig
, 0x0, sizeof(git_signature
));
265 if ((line_end
= memchr(buffer
, ender
, buffer_end
- buffer
)) == NULL
)
266 return signature_error("no newline given");
269 const size_t header_len
= strlen(header
);
271 if (memcmp(buffer
, header
, header_len
) != 0)
272 return signature_error("expected prefix doesn't match actual");
274 buffer
+= header_len
;
277 if (buffer
> line_end
)
278 return signature_error("signature too short");
280 if ((name_end
= strchr(buffer
, '<')) == NULL
)
281 return signature_error("character '<' not allowed in signature");
283 if ((email_end
= strchr(name_end
, '>')) == NULL
)
284 return signature_error("character '>' not allowed in signature");
286 if (email_end
< name_end
)
287 return signature_error("malformed e-mail");
289 error
= process_next_token(&buffer
, &sig
->name
, name_end
, line_end
);
293 error
= process_next_token(&buffer
, &sig
->email
, email_end
, line_end
);
297 tz_start
= scan_for_previous_token(line_end
- 1, buffer
);
299 if (tz_start
== NULL
)
300 goto clean_exit
; /* No timezone nor date */
302 time_start
= scan_for_previous_token(tz_start
- 1, buffer
);
303 if (time_start
== NULL
|| parse_time(&sig
->when
.time
, time_start
) < 0) {
304 /* The tz_start might point at the time */
305 parse_time(&sig
->when
.time
, tz_start
);
309 if (parse_timezone_offset(tz_start
, &sig
->when
.offset
) < 0) {
310 sig
->when
.time
= 0; /* Bogus timezone, we reset the time */
314 *buffer_out
= line_end
+ 1;
318 void git_signature__writebuf(git_buf
*buf
, const char *header
, const git_signature
*sig
)
320 int offset
, hours
, mins
;
323 offset
= sig
->when
.offset
;
324 sign
= (sig
->when
.offset
< 0) ? '-' : '+';
332 git_buf_printf(buf
, "%s%s <%s> %u %c%02d%02d\n",
333 header
? header
: "", sig
->name
, sig
->email
,
334 (unsigned)sig
->when
.time
, sign
, hours
, mins
);