1 /*
2  * ident.c
3  *
4  * create git identifier lines of the form "name <email> date"
5  *
6  * Copyright (C) 2005 Linus Torvalds
7  */
8 #include "cache.h"
9 #include "config.h"
10 
11 static struct strbuf git_default_name = STRBUF_INIT;
12 static struct strbuf git_default_email = STRBUF_INIT;
13 static struct strbuf git_default_date = STRBUF_INIT;
14 static struct strbuf git_author_name = STRBUF_INIT;
15 static struct strbuf git_author_email = STRBUF_INIT;
16 static struct strbuf git_committer_name = STRBUF_INIT;
17 static struct strbuf git_committer_email = STRBUF_INIT;
18 static int default_email_is_bogus;
19 static int default_name_is_bogus;
20 
21 static int ident_use_config_only;
22 
23 #define IDENT_NAME_GIVEN 01
24 #define IDENT_MAIL_GIVEN 02
25 #define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
26 static int committer_ident_explicitly_given;
27 static int author_ident_explicitly_given;
28 static int ident_config_given;
29 
30 #ifdef NO_GECOS_IN_PWENT
31 #define get_gecos(ignored) "&"
32 #else
33 #define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
34 #endif
35 
xgetpwuid_self(int * is_bogus)36 static struct passwd *xgetpwuid_self(int *is_bogus)
37 {
38 	struct passwd *pw;
39 
40 	errno = 0;
41 	pw = getpwuid(getuid());
42 	if (!pw) {
43 		static struct passwd fallback;
44 		fallback.pw_name = "unknown";
45 #ifndef NO_GECOS_IN_PWENT
46 		fallback.pw_gecos = "Unknown";
47 #endif
48 		pw = &fallback;
49 		if (is_bogus)
50 			*is_bogus = 1;
51 	}
52 	return pw;
53 }
54 
copy_gecos(const struct passwd * w,struct strbuf * name)55 static void copy_gecos(const struct passwd *w, struct strbuf *name)
56 {
57 	char *src;
58 
59 	/* Traditionally GECOS field had office phone numbers etc, separated
60 	 * with commas.  Also & stands for capitalized form of the login name.
61 	 */
62 
63 	for (src = get_gecos(w); *src && *src != ','; src++) {
64 		int ch = *src;
65 		if (ch != '&')
66 			strbuf_addch(name, ch);
67 		else {
68 			/* Sorry, Mr. McDonald... */
69 			strbuf_addch(name, toupper(*w->pw_name));
70 			strbuf_addstr(name, w->pw_name + 1);
71 		}
72 	}
73 }
74 
add_mailname_host(struct strbuf * buf)75 static int add_mailname_host(struct strbuf *buf)
76 {
77 	FILE *mailname;
78 	struct strbuf mailnamebuf = STRBUF_INIT;
79 
80 	mailname = fopen_or_warn("/etc/mailname", "r");
81 	if (!mailname)
82 		return -1;
83 
84 	if (strbuf_getline(&mailnamebuf, mailname) == EOF) {
85 		if (ferror(mailname))
86 			warning_errno("cannot read /etc/mailname");
87 		strbuf_release(&mailnamebuf);
88 		fclose(mailname);
89 		return -1;
90 	}
91 	/* success! */
92 	strbuf_addbuf(buf, &mailnamebuf);
93 	strbuf_release(&mailnamebuf);
94 	fclose(mailname);
95 	return 0;
96 }
97 
canonical_name(const char * host,struct strbuf * out)98 static int canonical_name(const char *host, struct strbuf *out)
99 {
100 	int status = -1;
101 
102 #ifndef NO_IPV6
103 	struct addrinfo hints, *ai;
104 	memset (&hints, '\0', sizeof (hints));
105 	hints.ai_flags = AI_CANONNAME;
106 	if (!getaddrinfo(host, NULL, &hints, &ai)) {
107 		if (ai && ai->ai_canonname && strchr(ai->ai_canonname, '.')) {
108 			strbuf_addstr(out, ai->ai_canonname);
109 			status = 0;
110 		}
111 		freeaddrinfo(ai);
112 	}
113 #else
114 	struct hostent *he = gethostbyname(host);
115 	if (he && strchr(he->h_name, '.')) {
116 		strbuf_addstr(out, he->h_name);
117 		status = 0;
118 	}
119 #endif /* NO_IPV6 */
120 
121 	return status;
122 }
123 
add_domainname(struct strbuf * out,int * is_bogus)124 static void add_domainname(struct strbuf *out, int *is_bogus)
125 {
126 	char buf[HOST_NAME_MAX + 1];
127 
128 	if (xgethostname(buf, sizeof(buf))) {
129 		warning_errno("cannot get host name");
130 		strbuf_addstr(out, "(none)");
131 		*is_bogus = 1;
132 		return;
133 	}
134 	if (strchr(buf, '.'))
135 		strbuf_addstr(out, buf);
136 	else if (canonical_name(buf, out) < 0) {
137 		strbuf_addf(out, "%s.(none)", buf);
138 		*is_bogus = 1;
139 	}
140 }
141 
copy_email(const struct passwd * pw,struct strbuf * email,int * is_bogus)142 static void copy_email(const struct passwd *pw, struct strbuf *email,
143 		       int *is_bogus)
144 {
145 	/*
146 	 * Make up a fake email address
147 	 * (name + '@' + hostname [+ '.' + domainname])
148 	 */
149 	strbuf_addstr(email, pw->pw_name);
150 	strbuf_addch(email, '@');
151 
152 	if (!add_mailname_host(email))
153 		return;	/* read from "/etc/mailname" (Debian) */
154 	add_domainname(email, is_bogus);
155 }
156 
ident_default_name(void)157 const char *ident_default_name(void)
158 {
159 	if (!(ident_config_given & IDENT_NAME_GIVEN) && !git_default_name.len) {
160 		copy_gecos(xgetpwuid_self(&default_name_is_bogus), &git_default_name);
161 		strbuf_trim(&git_default_name);
162 	}
163 	return git_default_name.buf;
164 }
165 
ident_default_email(void)166 const char *ident_default_email(void)
167 {
168 	if (!(ident_config_given & IDENT_MAIL_GIVEN) && !git_default_email.len) {
169 		const char *email = getenv("EMAIL");
170 
171 		if (email && email[0]) {
172 			strbuf_addstr(&git_default_email, email);
173 			committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
174 			author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
175 		} else if ((email = query_user_email()) && email[0]) {
176 			strbuf_addstr(&git_default_email, email);
177 			free((char *)email);
178 		} else
179 			copy_email(xgetpwuid_self(&default_email_is_bogus),
180 				   &git_default_email, &default_email_is_bogus);
181 		strbuf_trim(&git_default_email);
182 	}
183 	return git_default_email.buf;
184 }
185 
ident_default_date(void)186 static const char *ident_default_date(void)
187 {
188 	if (!git_default_date.len)
189 		datestamp(&git_default_date);
190 	return git_default_date.buf;
191 }
192 
reset_ident_date(void)193 void reset_ident_date(void)
194 {
195 	strbuf_reset(&git_default_date);
196 }
197 
crud(unsigned char c)198 static int crud(unsigned char c)
199 {
200 	return  c <= 32  ||
201 		c == '.' ||
202 		c == ',' ||
203 		c == ':' ||
204 		c == ';' ||
205 		c == '<' ||
206 		c == '>' ||
207 		c == '"' ||
208 		c == '\\' ||
209 		c == '\'';
210 }
211 
has_non_crud(const char * str)212 static int has_non_crud(const char *str)
213 {
214 	for (; *str; str++) {
215 		if (!crud(*str))
216 			return 1;
217 	}
218 	return 0;
219 }
220 
221 /*
222  * Copy over a string to the destination, but avoid special
223  * characters ('\n', '<' and '>') and remove crud at the end
224  */
strbuf_addstr_without_crud(struct strbuf * sb,const char * src)225 static void strbuf_addstr_without_crud(struct strbuf *sb, const char *src)
226 {
227 	size_t i, len;
228 	unsigned char c;
229 
230 	/* Remove crud from the beginning.. */
231 	while ((c = *src) != 0) {
232 		if (!crud(c))
233 			break;
234 		src++;
235 	}
236 
237 	/* Remove crud from the end.. */
238 	len = strlen(src);
239 	while (len > 0) {
240 		c = src[len-1];
241 		if (!crud(c))
242 			break;
243 		--len;
244 	}
245 
246 	/*
247 	 * Copy the rest to the buffer, but avoid the special
248 	 * characters '\n' '<' and '>' that act as delimiters on
249 	 * an identification line. We can only remove crud, never add it,
250 	 * so 'len' is our maximum.
251 	 */
252 	strbuf_grow(sb, len);
253 	for (i = 0; i < len; i++) {
254 		c = *src++;
255 		switch (c) {
256 		case '\n': case '<': case '>':
257 			continue;
258 		}
259 		sb->buf[sb->len++] = c;
260 	}
261 	sb->buf[sb->len] = '\0';
262 }
263 
264 /*
265  * Reverse of fmt_ident(); given an ident line, split the fields
266  * to allow the caller to parse it.
267  * Signal a success by returning 0, but date/tz fields of the result
268  * can still be NULL if the input line only has the name/email part
269  * (e.g. reading from a reflog entry).
270  */
split_ident_line(struct ident_split * split,const char * line,int len)271 int split_ident_line(struct ident_split *split, const char *line, int len)
272 {
273 	const char *cp;
274 	size_t span;
275 	int status = -1;
276 
277 	memset(split, 0, sizeof(*split));
278 
279 	split->name_begin = line;
280 	for (cp = line; *cp && cp < line + len; cp++)
281 		if (*cp == '<') {
282 			split->mail_begin = cp + 1;
283 			break;
284 		}
285 	if (!split->mail_begin)
286 		return status;
287 
288 	for (cp = split->mail_begin - 2; line <= cp; cp--)
289 		if (!isspace(*cp)) {
290 			split->name_end = cp + 1;
291 			break;
292 		}
293 	if (!split->name_end) {
294 		/* no human readable name */
295 		split->name_end = split->name_begin;
296 	}
297 
298 	for (cp = split->mail_begin; cp < line + len; cp++)
299 		if (*cp == '>') {
300 			split->mail_end = cp;
301 			break;
302 		}
303 	if (!split->mail_end)
304 		return status;
305 
306 	/*
307 	 * Look from the end-of-line to find the trailing ">" of the mail
308 	 * address, even though we should already know it as split->mail_end.
309 	 * This can help in cases of broken idents with an extra ">" somewhere
310 	 * in the email address.  Note that we are assuming the timestamp will
311 	 * never have a ">" in it.
312 	 *
313 	 * Note that we will always find some ">" before going off the front of
314 	 * the string, because will always hit the split->mail_end closing
315 	 * bracket.
316 	 */
317 	for (cp = line + len - 1; *cp != '>'; cp--)
318 		;
319 
320 	for (cp = cp + 1; cp < line + len && isspace(*cp); cp++)
321 		;
322 	if (line + len <= cp)
323 		goto person_only;
324 	split->date_begin = cp;
325 	span = strspn(cp, "0123456789");
326 	if (!span)
327 		goto person_only;
328 	split->date_end = split->date_begin + span;
329 	for (cp = split->date_end; cp < line + len && isspace(*cp); cp++)
330 		;
331 	if (line + len <= cp || (*cp != '+' && *cp != '-'))
332 		goto person_only;
333 	split->tz_begin = cp;
334 	span = strspn(cp + 1, "0123456789");
335 	if (!span)
336 		goto person_only;
337 	split->tz_end = split->tz_begin + 1 + span;
338 	return 0;
339 
340 person_only:
341 	split->date_begin = NULL;
342 	split->date_end = NULL;
343 	split->tz_begin = NULL;
344 	split->tz_end = NULL;
345 	return 0;
346 }
347 
348 
ident_env_hint(enum want_ident whose_ident)349 static void ident_env_hint(enum want_ident whose_ident)
350 {
351 	switch (whose_ident) {
352 	case WANT_AUTHOR_IDENT:
353 		fputs(_("Author identity unknown\n"), stderr);
354 		break;
355 	case WANT_COMMITTER_IDENT:
356 		fputs(_("Committer identity unknown\n"), stderr);
357 		break;
358 	default:
359 		break;
360 	}
361 
362 	fputs(_("\n"
363 		"*** Please tell me who you are.\n"
364 		"\n"
365 		"Run\n"
366 		"\n"
367 		"  git config --global user.email \"you@example.com\"\n"
368 		"  git config --global user.name \"Your Name\"\n"
369 		"\n"
370 		"to set your account\'s default identity.\n"
371 		"Omit --global to set the identity only in this repository.\n"
372 		"\n"), stderr);
373 }
374 
fmt_ident(const char * name,const char * email,enum want_ident whose_ident,const char * date_str,int flag)375 const char *fmt_ident(const char *name, const char *email,
376 		      enum want_ident whose_ident, const char *date_str, int flag)
377 {
378 	static int index;
379 	static struct strbuf ident_pool[2] = { STRBUF_INIT, STRBUF_INIT };
380 	int strict = (flag & IDENT_STRICT);
381 	int want_date = !(flag & IDENT_NO_DATE);
382 	int want_name = !(flag & IDENT_NO_NAME);
383 
384 	struct strbuf *ident = &ident_pool[index];
385 	index = (index + 1) % ARRAY_SIZE(ident_pool);
386 
387 	if (!email) {
388 		if (whose_ident == WANT_AUTHOR_IDENT && git_author_email.len)
389 			email = git_author_email.buf;
390 		else if (whose_ident == WANT_COMMITTER_IDENT && git_committer_email.len)
391 			email = git_committer_email.buf;
392 	}
393 	if (!email) {
394 		if (strict && ident_use_config_only
395 		    && !(ident_config_given & IDENT_MAIL_GIVEN)) {
396 			ident_env_hint(whose_ident);
397 			die(_("no email was given and auto-detection is disabled"));
398 		}
399 		email = ident_default_email();
400 		if (strict && default_email_is_bogus) {
401 			ident_env_hint(whose_ident);
402 			die(_("unable to auto-detect email address (got '%s')"), email);
403 		}
404 	}
405 
406 	if (want_name) {
407 		int using_default = 0;
408 		if (!name) {
409 			if (whose_ident == WANT_AUTHOR_IDENT && git_author_name.len)
410 				name = git_author_name.buf;
411 			else if (whose_ident == WANT_COMMITTER_IDENT &&
412 					git_committer_name.len)
413 				name = git_committer_name.buf;
414 		}
415 		if (!name) {
416 			if (strict && ident_use_config_only
417 			    && !(ident_config_given & IDENT_NAME_GIVEN)) {
418 				ident_env_hint(whose_ident);
419 				die(_("no name was given and auto-detection is disabled"));
420 			}
421 			name = ident_default_name();
422 			using_default = 1;
423 			if (strict && default_name_is_bogus) {
424 				ident_env_hint(whose_ident);
425 				die(_("unable to auto-detect name (got '%s')"), name);
426 			}
427 		}
428 		if (!*name) {
429 			struct passwd *pw;
430 			if (strict) {
431 				if (using_default)
432 					ident_env_hint(whose_ident);
433 				die(_("empty ident name (for <%s>) not allowed"), email);
434 			}
435 			pw = xgetpwuid_self(NULL);
436 			name = pw->pw_name;
437 		}
438 		if (strict && !has_non_crud(name))
439 			die(_("name consists only of disallowed characters: %s"), name);
440 	}
441 
442 	strbuf_reset(ident);
443 	if (want_name) {
444 		strbuf_addstr_without_crud(ident, name);
445 		strbuf_addstr(ident, " <");
446 	}
447 	strbuf_addstr_without_crud(ident, email);
448 	if (want_name)
449 		strbuf_addch(ident, '>');
450 	if (want_date) {
451 		strbuf_addch(ident, ' ');
452 		if (date_str && date_str[0]) {
453 			if (parse_date(date_str, ident) < 0)
454 				die(_("invalid date format: %s"), date_str);
455 		}
456 		else
457 			strbuf_addstr(ident, ident_default_date());
458 	}
459 
460 	return ident->buf;
461 }
462 
fmt_name(enum want_ident whose_ident)463 const char *fmt_name(enum want_ident whose_ident)
464 {
465 	char *name = NULL;
466 	char *email = NULL;
467 
468 	switch (whose_ident) {
469 	case WANT_BLANK_IDENT:
470 		break;
471 	case WANT_AUTHOR_IDENT:
472 		name = getenv("GIT_AUTHOR_NAME");
473 		email = getenv("GIT_AUTHOR_EMAIL");
474 		break;
475 	case WANT_COMMITTER_IDENT:
476 		name = getenv("GIT_COMMITTER_NAME");
477 		email = getenv("GIT_COMMITTER_EMAIL");
478 		break;
479 	}
480 	return fmt_ident(name, email, whose_ident, NULL,
481 			IDENT_STRICT | IDENT_NO_DATE);
482 }
483 
git_author_info(int flag)484 const char *git_author_info(int flag)
485 {
486 	if (getenv("GIT_AUTHOR_NAME"))
487 		author_ident_explicitly_given |= IDENT_NAME_GIVEN;
488 	if (getenv("GIT_AUTHOR_EMAIL"))
489 		author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
490 	return fmt_ident(getenv("GIT_AUTHOR_NAME"),
491 			 getenv("GIT_AUTHOR_EMAIL"),
492 			 WANT_AUTHOR_IDENT,
493 			 getenv("GIT_AUTHOR_DATE"),
494 			 flag);
495 }
496 
git_committer_info(int flag)497 const char *git_committer_info(int flag)
498 {
499 	if (getenv("GIT_COMMITTER_NAME"))
500 		committer_ident_explicitly_given |= IDENT_NAME_GIVEN;
501 	if (getenv("GIT_COMMITTER_EMAIL"))
502 		committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
503 	return fmt_ident(getenv("GIT_COMMITTER_NAME"),
504 			 getenv("GIT_COMMITTER_EMAIL"),
505 			 WANT_COMMITTER_IDENT,
506 			 getenv("GIT_COMMITTER_DATE"),
507 			 flag);
508 }
509 
ident_is_sufficient(int user_ident_explicitly_given)510 static int ident_is_sufficient(int user_ident_explicitly_given)
511 {
512 #ifndef WINDOWS
513 	return (user_ident_explicitly_given & IDENT_MAIL_GIVEN);
514 #else
515 	return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
516 #endif
517 }
518 
committer_ident_sufficiently_given(void)519 int committer_ident_sufficiently_given(void)
520 {
521 	return ident_is_sufficient(committer_ident_explicitly_given);
522 }
523 
author_ident_sufficiently_given(void)524 int author_ident_sufficiently_given(void)
525 {
526 	return ident_is_sufficient(author_ident_explicitly_given);
527 }
528 
set_ident(const char * var,const char * value)529 static int set_ident(const char *var, const char *value)
530 {
531 	if (!strcmp(var, "author.name")) {
532 		if (!value)
533 			return config_error_nonbool(var);
534 		strbuf_reset(&git_author_name);
535 		strbuf_addstr(&git_author_name, value);
536 		author_ident_explicitly_given |= IDENT_NAME_GIVEN;
537 		ident_config_given |= IDENT_NAME_GIVEN;
538 		return 0;
539 	}
540 
541 	if (!strcmp(var, "author.email")) {
542 		if (!value)
543 			return config_error_nonbool(var);
544 		strbuf_reset(&git_author_email);
545 		strbuf_addstr(&git_author_email, value);
546 		author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
547 		ident_config_given |= IDENT_MAIL_GIVEN;
548 		return 0;
549 	}
550 
551 	if (!strcmp(var, "committer.name")) {
552 		if (!value)
553 			return config_error_nonbool(var);
554 		strbuf_reset(&git_committer_name);
555 		strbuf_addstr(&git_committer_name, value);
556 		committer_ident_explicitly_given |= IDENT_NAME_GIVEN;
557 		ident_config_given |= IDENT_NAME_GIVEN;
558 		return 0;
559 	}
560 
561 	if (!strcmp(var, "committer.email")) {
562 		if (!value)
563 			return config_error_nonbool(var);
564 		strbuf_reset(&git_committer_email);
565 		strbuf_addstr(&git_committer_email, value);
566 		committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
567 		ident_config_given |= IDENT_MAIL_GIVEN;
568 		return 0;
569 	}
570 
571 	if (!strcmp(var, "user.name")) {
572 		if (!value)
573 			return config_error_nonbool(var);
574 		strbuf_reset(&git_default_name);
575 		strbuf_addstr(&git_default_name, value);
576 		committer_ident_explicitly_given |= IDENT_NAME_GIVEN;
577 		author_ident_explicitly_given |= IDENT_NAME_GIVEN;
578 		ident_config_given |= IDENT_NAME_GIVEN;
579 		return 0;
580 	}
581 
582 	if (!strcmp(var, "user.email")) {
583 		if (!value)
584 			return config_error_nonbool(var);
585 		strbuf_reset(&git_default_email);
586 		strbuf_addstr(&git_default_email, value);
587 		committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
588 		author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
589 		ident_config_given |= IDENT_MAIL_GIVEN;
590 		return 0;
591 	}
592 
593 	return 0;
594 }
595 
git_ident_config(const char * var,const char * value,void * data)596 int git_ident_config(const char *var, const char *value, void *data)
597 {
598 	if (!strcmp(var, "user.useconfigonly")) {
599 		ident_use_config_only = git_config_bool(var, value);
600 		return 0;
601 	}
602 
603 	return set_ident(var, value);
604 }
605 
set_env_if(const char * key,const char * value,int * given,int bit)606 static void set_env_if(const char *key, const char *value, int *given, int bit)
607 {
608 	if ((*given & bit) || getenv(key))
609 		return; /* nothing to do */
610 	setenv(key, value, 0);
611 	*given |= bit;
612 }
613 
prepare_fallback_ident(const char * name,const char * email)614 void prepare_fallback_ident(const char *name, const char *email)
615 {
616 	set_env_if("GIT_AUTHOR_NAME", name,
617 		   &author_ident_explicitly_given, IDENT_NAME_GIVEN);
618 	set_env_if("GIT_AUTHOR_EMAIL", email,
619 		   &author_ident_explicitly_given, IDENT_MAIL_GIVEN);
620 	set_env_if("GIT_COMMITTER_NAME", name,
621 		   &committer_ident_explicitly_given, IDENT_NAME_GIVEN);
622 	set_env_if("GIT_COMMITTER_EMAIL", email,
623 		   &committer_ident_explicitly_given, IDENT_MAIL_GIVEN);
624 }
625 
buf_cmp(const char * a_begin,const char * a_end,const char * b_begin,const char * b_end)626 static int buf_cmp(const char *a_begin, const char *a_end,
627 		   const char *b_begin, const char *b_end)
628 {
629 	int a_len = a_end - a_begin;
630 	int b_len = b_end - b_begin;
631 	int min = a_len < b_len ? a_len : b_len;
632 	int cmp;
633 
634 	cmp = memcmp(a_begin, b_begin, min);
635 	if (cmp)
636 		return cmp;
637 
638 	return a_len - b_len;
639 }
640 
ident_cmp(const struct ident_split * a,const struct ident_split * b)641 int ident_cmp(const struct ident_split *a,
642 	      const struct ident_split *b)
643 {
644 	int cmp;
645 
646 	cmp = buf_cmp(a->mail_begin, a->mail_end,
647 		      b->mail_begin, b->mail_end);
648 	if (cmp)
649 		return cmp;
650 
651 	return buf_cmp(a->name_begin, a->name_end,
652 		       b->name_begin, b->name_end);
653 }
654