ident.con commit format-patch: do not use bogus email addresses in message ids (59f9b8a)
   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
  10static struct strbuf git_default_name = STRBUF_INIT;
  11static struct strbuf git_default_email = STRBUF_INIT;
  12static char git_default_date[50];
  13int user_ident_explicitly_given;
  14
  15#ifdef NO_GECOS_IN_PWENT
  16#define get_gecos(ignored) "&"
  17#else
  18#define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
  19#endif
  20
  21static void copy_gecos(const struct passwd *w, struct strbuf *name)
  22{
  23        char *src;
  24
  25        /* Traditionally GECOS field had office phone numbers etc, separated
  26         * with commas.  Also & stands for capitalized form of the login name.
  27         */
  28
  29        for (src = get_gecos(w); *src && *src != ','; src++) {
  30                int ch = *src;
  31                if (ch != '&')
  32                        strbuf_addch(name, ch);
  33                else {
  34                        /* Sorry, Mr. McDonald... */
  35                        strbuf_addch(name, toupper(*w->pw_name));
  36                        strbuf_addstr(name, w->pw_name + 1);
  37                }
  38        }
  39}
  40
  41static int add_mailname_host(struct strbuf *buf)
  42{
  43        FILE *mailname;
  44
  45        mailname = fopen("/etc/mailname", "r");
  46        if (!mailname) {
  47                if (errno != ENOENT)
  48                        warning("cannot open /etc/mailname: %s",
  49                                strerror(errno));
  50                return -1;
  51        }
  52        if (strbuf_getline(buf, mailname, '\n') == EOF) {
  53                if (ferror(mailname))
  54                        warning("cannot read /etc/mailname: %s",
  55                                strerror(errno));
  56                fclose(mailname);
  57                return -1;
  58        }
  59        /* success! */
  60        fclose(mailname);
  61        return 0;
  62}
  63
  64static void add_domainname(struct strbuf *out)
  65{
  66        char buf[1024];
  67        struct hostent *he;
  68
  69        if (gethostname(buf, sizeof(buf))) {
  70                warning("cannot get host name: %s", strerror(errno));
  71                strbuf_addstr(out, "(none)");
  72                return;
  73        }
  74        if (strchr(buf, '.'))
  75                strbuf_addstr(out, buf);
  76        else if ((he = gethostbyname(buf)) && strchr(he->h_name, '.'))
  77                strbuf_addstr(out, he->h_name);
  78        else
  79                strbuf_addf(out, "%s.(none)", buf);
  80}
  81
  82static void copy_email(const struct passwd *pw, struct strbuf *email)
  83{
  84        /*
  85         * Make up a fake email address
  86         * (name + '@' + hostname [+ '.' + domainname])
  87         */
  88        strbuf_addstr(email, pw->pw_name);
  89        strbuf_addch(email, '@');
  90
  91        if (!add_mailname_host(email))
  92                return; /* read from "/etc/mailname" (Debian) */
  93        add_domainname(email);
  94}
  95
  96const char *ident_default_name(void)
  97{
  98        if (!git_default_name.len) {
  99                copy_gecos(xgetpwuid_self(), &git_default_name);
 100                strbuf_trim(&git_default_name);
 101        }
 102        return git_default_name.buf;
 103}
 104
 105const char *ident_default_email(void)
 106{
 107        if (!git_default_email.len) {
 108                const char *email = getenv("EMAIL");
 109
 110                if (email && email[0]) {
 111                        strbuf_addstr(&git_default_email, email);
 112                        user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
 113                } else
 114                        copy_email(xgetpwuid_self(), &git_default_email);
 115                strbuf_trim(&git_default_email);
 116        }
 117        return git_default_email.buf;
 118}
 119
 120const char *ident_default_date(void)
 121{
 122        if (!git_default_date[0])
 123                datestamp(git_default_date, sizeof(git_default_date));
 124        return git_default_date;
 125}
 126
 127static int crud(unsigned char c)
 128{
 129        return  c <= 32  ||
 130                c == '.' ||
 131                c == ',' ||
 132                c == ':' ||
 133                c == ';' ||
 134                c == '<' ||
 135                c == '>' ||
 136                c == '"' ||
 137                c == '\\' ||
 138                c == '\'';
 139}
 140
 141/*
 142 * Copy over a string to the destination, but avoid special
 143 * characters ('\n', '<' and '>') and remove crud at the end
 144 */
 145static void strbuf_addstr_without_crud(struct strbuf *sb, const char *src)
 146{
 147        size_t i, len;
 148        unsigned char c;
 149
 150        /* Remove crud from the beginning.. */
 151        while ((c = *src) != 0) {
 152                if (!crud(c))
 153                        break;
 154                src++;
 155        }
 156
 157        /* Remove crud from the end.. */
 158        len = strlen(src);
 159        while (len > 0) {
 160                c = src[len-1];
 161                if (!crud(c))
 162                        break;
 163                --len;
 164        }
 165
 166        /*
 167         * Copy the rest to the buffer, but avoid the special
 168         * characters '\n' '<' and '>' that act as delimiters on
 169         * an identification line. We can only remove crud, never add it,
 170         * so 'len' is our maximum.
 171         */
 172        strbuf_grow(sb, len);
 173        for (i = 0; i < len; i++) {
 174                c = *src++;
 175                switch (c) {
 176                case '\n': case '<': case '>':
 177                        continue;
 178                }
 179                sb->buf[sb->len++] = c;
 180        }
 181        sb->buf[sb->len] = '\0';
 182}
 183
 184/*
 185 * Reverse of fmt_ident(); given an ident line, split the fields
 186 * to allow the caller to parse it.
 187 * Signal a success by returning 0, but date/tz fields of the result
 188 * can still be NULL if the input line only has the name/email part
 189 * (e.g. reading from a reflog entry).
 190 */
 191int split_ident_line(struct ident_split *split, const char *line, int len)
 192{
 193        const char *cp;
 194        size_t span;
 195        int status = -1;
 196
 197        memset(split, 0, sizeof(*split));
 198
 199        split->name_begin = line;
 200        for (cp = line; *cp && cp < line + len; cp++)
 201                if (*cp == '<') {
 202                        split->mail_begin = cp + 1;
 203                        break;
 204                }
 205        if (!split->mail_begin)
 206                return status;
 207
 208        for (cp = split->mail_begin - 2; line < cp; cp--)
 209                if (!isspace(*cp)) {
 210                        split->name_end = cp + 1;
 211                        break;
 212                }
 213        if (!split->name_end)
 214                return status;
 215
 216        for (cp = split->mail_begin; cp < line + len; cp++)
 217                if (*cp == '>') {
 218                        split->mail_end = cp;
 219                        break;
 220                }
 221        if (!split->mail_end)
 222                return status;
 223
 224        for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++)
 225                ;
 226        if (line + len <= cp)
 227                goto person_only;
 228        split->date_begin = cp;
 229        span = strspn(cp, "0123456789");
 230        if (!span)
 231                goto person_only;
 232        split->date_end = split->date_begin + span;
 233        for (cp = split->date_end; cp < line + len && isspace(*cp); cp++)
 234                ;
 235        if (line + len <= cp || (*cp != '+' && *cp != '-'))
 236                goto person_only;
 237        split->tz_begin = cp;
 238        span = strspn(cp + 1, "0123456789");
 239        if (!span)
 240                goto person_only;
 241        split->tz_end = split->tz_begin + 1 + span;
 242        return 0;
 243
 244person_only:
 245        split->date_begin = NULL;
 246        split->date_end = NULL;
 247        split->tz_begin = NULL;
 248        split->tz_end = NULL;
 249        return 0;
 250}
 251
 252static const char *env_hint =
 253"\n"
 254"*** Please tell me who you are.\n"
 255"\n"
 256"Run\n"
 257"\n"
 258"  git config --global user.email \"you@example.com\"\n"
 259"  git config --global user.name \"Your Name\"\n"
 260"\n"
 261"to set your account\'s default identity.\n"
 262"Omit --global to set the identity only in this repository.\n"
 263"\n";
 264
 265const char *fmt_ident(const char *name, const char *email,
 266                      const char *date_str, int flag)
 267{
 268        static struct strbuf ident = STRBUF_INIT;
 269        char date[50];
 270        int strict = (flag & IDENT_STRICT);
 271        int want_date = !(flag & IDENT_NO_DATE);
 272        int want_name = !(flag & IDENT_NO_NAME);
 273
 274        if (want_name && !name)
 275                name = ident_default_name();
 276        if (!email)
 277                email = ident_default_email();
 278
 279        if (want_name && !*name) {
 280                struct passwd *pw;
 281
 282                if (strict) {
 283                        if (name == git_default_name.buf)
 284                                fputs(env_hint, stderr);
 285                        die("empty ident name (for <%s>) not allowed", email);
 286                }
 287                pw = xgetpwuid_self();
 288                name = pw->pw_name;
 289        }
 290
 291        if (strict && email == git_default_email.buf &&
 292            strstr(email, "(none)")) {
 293                fputs(env_hint, stderr);
 294                die("unable to auto-detect email address (got '%s')", email);
 295        }
 296
 297        if (want_date) {
 298                if (date_str && date_str[0]) {
 299                        if (parse_date(date_str, date, sizeof(date)) < 0)
 300                                die("invalid date format: %s", date_str);
 301                }
 302                else
 303                        strcpy(date, ident_default_date());
 304        }
 305
 306        strbuf_reset(&ident);
 307        if (want_name) {
 308                strbuf_addstr_without_crud(&ident, name);
 309                strbuf_addstr(&ident, " <");
 310        }
 311        strbuf_addstr_without_crud(&ident, email);
 312        if (want_name)
 313                        strbuf_addch(&ident, '>');
 314        if (want_date) {
 315                strbuf_addch(&ident, ' ');
 316                strbuf_addstr_without_crud(&ident, date);
 317        }
 318        return ident.buf;
 319}
 320
 321const char *fmt_name(const char *name, const char *email)
 322{
 323        return fmt_ident(name, email, NULL, IDENT_STRICT | IDENT_NO_DATE);
 324}
 325
 326const char *git_author_info(int flag)
 327{
 328        return fmt_ident(getenv("GIT_AUTHOR_NAME"),
 329                         getenv("GIT_AUTHOR_EMAIL"),
 330                         getenv("GIT_AUTHOR_DATE"),
 331                         flag);
 332}
 333
 334const char *git_committer_info(int flag)
 335{
 336        if (getenv("GIT_COMMITTER_NAME"))
 337                user_ident_explicitly_given |= IDENT_NAME_GIVEN;
 338        if (getenv("GIT_COMMITTER_EMAIL"))
 339                user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
 340        return fmt_ident(getenv("GIT_COMMITTER_NAME"),
 341                         getenv("GIT_COMMITTER_EMAIL"),
 342                         getenv("GIT_COMMITTER_DATE"),
 343                         flag);
 344}
 345
 346int user_ident_sufficiently_given(void)
 347{
 348#ifndef WINDOWS
 349        return (user_ident_explicitly_given & IDENT_MAIL_GIVEN);
 350#else
 351        return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
 352#endif
 353}
 354
 355int git_ident_config(const char *var, const char *value, void *data)
 356{
 357        if (!strcmp(var, "user.name")) {
 358                if (!value)
 359                        return config_error_nonbool(var);
 360                strbuf_reset(&git_default_name);
 361                strbuf_addstr(&git_default_name, value);
 362                user_ident_explicitly_given |= IDENT_NAME_GIVEN;
 363                return 0;
 364        }
 365
 366        if (!strcmp(var, "user.email")) {
 367                if (!value)
 368                        return config_error_nonbool(var);
 369                strbuf_reset(&git_default_email);
 370                strbuf_addstr(&git_default_email, value);
 371                user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
 372                return 0;
 373        }
 374
 375        return 0;
 376}