Update release notes to 2.7
[gitweb.git] / ident.c
diff --git a/ident.c b/ident.c
index b29f81f83a68258b354c59df9bde40c254060ed9..daf7e1ea8370e5b6a60c5a33a1591ef939655664 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -9,7 +9,9 @@
 
 static struct strbuf git_default_name = STRBUF_INIT;
 static struct strbuf git_default_email = STRBUF_INIT;
-static char git_default_date[50];
+static struct strbuf git_default_date = STRBUF_INIT;
+static int default_email_is_bogus;
+static int default_name_is_bogus;
 
 #define IDENT_NAME_GIVEN 01
 #define IDENT_MAIL_GIVEN 02
@@ -23,6 +25,25 @@ static int author_ident_explicitly_given;
 #define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
 #endif
 
+static struct passwd *xgetpwuid_self(int *is_bogus)
+{
+       struct passwd *pw;
+
+       errno = 0;
+       pw = getpwuid(getuid());
+       if (!pw) {
+               static struct passwd fallback;
+               fallback.pw_name = "unknown";
+#ifndef NO_GECOS_IN_PWENT
+               fallback.pw_gecos = "Unknown";
+#endif
+               pw = &fallback;
+               if (is_bogus)
+                       *is_bogus = 1;
+       }
+       return pw;
+}
+
 static void copy_gecos(const struct passwd *w, struct strbuf *name)
 {
        char *src;
@@ -70,25 +91,52 @@ static int add_mailname_host(struct strbuf *buf)
        return 0;
 }
 
-static void add_domainname(struct strbuf *out)
+static int canonical_name(const char *host, struct strbuf *out)
+{
+       int status = -1;
+
+#ifndef NO_IPV6
+       struct addrinfo hints, *ai;
+       memset (&hints, '\0', sizeof (hints));
+       hints.ai_flags = AI_CANONNAME;
+       if (!getaddrinfo(host, NULL, &hints, &ai)) {
+               if (ai && strchr(ai->ai_canonname, '.')) {
+                       strbuf_addstr(out, ai->ai_canonname);
+                       status = 0;
+               }
+               freeaddrinfo(ai);
+       }
+#else
+       struct hostent *he = gethostbyname(host);
+       if (he && strchr(he->h_name, '.')) {
+               strbuf_addstr(out, he->h_name);
+               status = 0;
+       }
+#endif /* NO_IPV6 */
+
+       return status;
+}
+
+static void add_domainname(struct strbuf *out, int *is_bogus)
 {
        char buf[1024];
-       struct hostent *he;
 
        if (gethostname(buf, sizeof(buf))) {
                warning("cannot get host name: %s", strerror(errno));
                strbuf_addstr(out, "(none)");
+               *is_bogus = 1;
                return;
        }
        if (strchr(buf, '.'))
                strbuf_addstr(out, buf);
-       else if ((he = gethostbyname(buf)) && strchr(he->h_name, '.'))
-               strbuf_addstr(out, he->h_name);
-       else
+       else if (canonical_name(buf, out) < 0) {
                strbuf_addf(out, "%s.(none)", buf);
+               *is_bogus = 1;
+       }
 }
 
-static void copy_email(const struct passwd *pw, struct strbuf *email)
+static void copy_email(const struct passwd *pw, struct strbuf *email,
+                      int *is_bogus)
 {
        /*
         * Make up a fake email address
@@ -99,13 +147,13 @@ static void copy_email(const struct passwd *pw, struct strbuf *email)
 
        if (!add_mailname_host(email))
                return; /* read from "/etc/mailname" (Debian) */
-       add_domainname(email);
+       add_domainname(email, is_bogus);
 }
 
-static const char *ident_default_name(void)
+const char *ident_default_name(void)
 {
        if (!git_default_name.len) {
-               copy_gecos(xgetpwuid_self(), &git_default_name);
+               copy_gecos(xgetpwuid_self(&default_name_is_bogus), &git_default_name);
                strbuf_trim(&git_default_name);
        }
        return git_default_name.buf;
@@ -121,7 +169,8 @@ const char *ident_default_email(void)
                        committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
                        author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
                } else
-                       copy_email(xgetpwuid_self(), &git_default_email);
+                       copy_email(xgetpwuid_self(&default_email_is_bogus),
+                                  &git_default_email, &default_email_is_bogus);
                strbuf_trim(&git_default_email);
        }
        return git_default_email.buf;
@@ -129,9 +178,9 @@ const char *ident_default_email(void)
 
 static const char *ident_default_date(void)
 {
-       if (!git_default_date[0])
-               datestamp(git_default_date, sizeof(git_default_date));
-       return git_default_date;
+       if (!git_default_date.len)
+               datestamp(&git_default_date);
+       return git_default_date.buf;
 }
 
 static int crud(unsigned char c)
@@ -233,7 +282,21 @@ int split_ident_line(struct ident_split *split, const char *line, int len)
        if (!split->mail_end)
                return status;
 
-       for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++)
+       /*
+        * Look from the end-of-line to find the trailing ">" of the mail
+        * address, even though we should already know it as split->mail_end.
+        * This can help in cases of broken idents with an extra ">" somewhere
+        * in the email address.  Note that we are assuming the timestamp will
+        * never have a ">" in it.
+        *
+        * Note that we will always find some ">" before going off the front of
+        * the string, because will always hit the split->mail_end closing
+        * bracket.
+        */
+       for (cp = line + len - 1; *cp != '>'; cp--)
+               ;
+
+       for (cp = cp + 1; cp < line + len && isspace(*cp); cp++)
                ;
        if (line + len <= cp)
                goto person_only;
@@ -278,7 +341,6 @@ const char *fmt_ident(const char *name, const char *email,
                      const char *date_str, int flag)
 {
        static struct strbuf ident = STRBUF_INIT;
-       char date[50];
        int strict = (flag & IDENT_STRICT);
        int want_date = !(flag & IDENT_NO_DATE);
        int want_name = !(flag & IDENT_NO_NAME);
@@ -296,23 +358,19 @@ const char *fmt_ident(const char *name, const char *email,
                                fputs(env_hint, stderr);
                        die("empty ident name (for <%s>) not allowed", email);
                }
-               pw = xgetpwuid_self();
+               pw = xgetpwuid_self(NULL);
                name = pw->pw_name;
        }
 
-       if (strict && email == git_default_email.buf &&
-           strstr(email, "(none)")) {
+       if (want_name && strict &&
+           name == git_default_name.buf && default_name_is_bogus) {
                fputs(env_hint, stderr);
-               die("unable to auto-detect email address (got '%s')", email);
+               die("unable to auto-detect name (got '%s')", name);
        }
 
-       if (want_date) {
-               if (date_str && date_str[0]) {
-                       if (parse_date(date_str, date, sizeof(date)) < 0)
-                               die("invalid date format: %s", date_str);
-               }
-               else
-                       strcpy(date, ident_default_date());
+       if (strict && email == git_default_email.buf && default_email_is_bogus) {
+               fputs(env_hint, stderr);
+               die("unable to auto-detect email address (got '%s')", email);
        }
 
        strbuf_reset(&ident);
@@ -325,8 +383,14 @@ const char *fmt_ident(const char *name, const char *email,
                        strbuf_addch(&ident, '>');
        if (want_date) {
                strbuf_addch(&ident, ' ');
-               strbuf_addstr_without_crud(&ident, date);
+               if (date_str && date_str[0]) {
+                       if (parse_date(date_str, &ident) < 0)
+                               die("invalid date format: %s", date_str);
+               }
+               else
+                       strbuf_addstr(&ident, ident_default_date());
        }
+
        return ident.buf;
 }