Improved quoting the fullname.
[mmh] / sbr / mts.c
1 /*
2 ** mts.c -- definitions for the mail transport system
3 **
4 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9 #include <h/mh.h>   /* for snprintf() */
10 #include <h/nmh.h>
11 #include <h/utils.h>
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <h/mts.h>
15 #include <pwd.h>
16 #include <netdb.h>
17
18 #ifdef HAVE_SYS_UTSNAME_H
19 # include <sys/utsname.h>
20 #endif
21
22 #define NOTOK  (-1)
23 #define OK     0
24
25 /*
26 ** static prototypes
27 */
28 static void getuserinfo(void);
29
30 /* Cache the username and fullname of the user */
31 static char username[BUFSIZ];
32 static char fullname[BUFSIZ];
33
34
35 /*
36 ** Get the fully qualified name of the local host.
37 */
38 char *
39 LocalName(void)
40 {
41         static char buffer[BUFSIZ] = "";
42         struct addrinfo hints, *res;
43 #ifdef HAVE_UNAME
44         struct utsname name;
45 #endif
46
47         /* check if we have cached the local name */
48         if (buffer[0])
49                 return buffer;
50
51         memset(buffer, 0, sizeof(buffer));
52 #ifdef HAVE_UNAME
53         /* first get our local name */
54         uname(&name);
55         strncpy(buffer, name.nodename, sizeof(buffer) - 1);
56 #else
57         /* first get our local name */
58         gethostname(buffer, sizeof(buffer) - 1);
59 #endif
60         /* now fully qualify our name */
61
62         memset(&hints, 0, sizeof(hints));
63         hints.ai_flags = AI_CANONNAME;
64         hints.ai_family = PF_UNSPEC;
65         if (getaddrinfo(buffer, NULL, &hints, &res) == 0) {
66                 strncpy(buffer, res->ai_canonname, sizeof(buffer) - 1);
67                 freeaddrinfo(res);
68         }
69
70         return buffer;
71 }
72
73
74 /*
75 ** This is only for UUCP mail.  It gets the hostname
76 ** as part of the UUCP "domain".
77 */
78 char *
79 SystemName(void)
80 {
81         static char buffer[BUFSIZ] = "";
82
83 #ifdef HAVE_UNAME
84         struct utsname name;
85 #endif
86
87         /* check if we have cached the system name */
88         if (buffer[0])
89                 return buffer;
90
91 #ifdef HAVE_UNAME
92         uname(&name);
93         strncpy(buffer, name.nodename, sizeof(buffer));
94 #else
95         gethostname(buffer, sizeof(buffer));
96 #endif
97
98         return buffer;
99 }
100
101
102 /*
103 ** Get the username of current user
104 */
105 char *
106 getusername(void)
107 {
108         if (username[0] == '\0')
109                 getuserinfo();
110
111         return username;
112 }
113
114
115 /*
116 ** Get full name of current user (typically from GECOS
117 ** field of password file).
118 */
119 char *
120 getfullname(void)
121 {
122         if (username[0] == '\0')
123                 getuserinfo();
124
125         return fullname;
126 }
127
128
129 /*
130 ** Find the user's username and full name, and cache them.
131 */
132 static void
133 getuserinfo(void)
134 {
135         unsigned char *cp;
136         char *np;
137         struct passwd *pw;
138         int needquotes = 0;
139         char tmp[BUFSIZ];
140         char *tp;
141
142         if (!(pw = getpwuid(getuid())) || !pw->pw_name || !*pw->pw_name) {
143                 strncpy(username, "unknown", sizeof(username));
144                 snprintf(fullname, sizeof(fullname),
145                                 "The Unknown User-ID (%d)", (int)getuid());
146                 return;
147         }
148
149         np = pw->pw_gecos;
150
151         /*
152         ** Get the user's real name from the GECOS field.  Stop once
153         ** we hit a ',', which some OSes use to separate other 'finger'
154         ** information in the GECOS field, like phone number.
155         */
156         for (cp = tmp; *np != '\0' && *np != ',';) {
157 #ifndef BSD42
158                 *cp++ = *np++;
159 #else /* BSD42 */
160                 /*
161                 ** On BSD(-derived) systems, the system utilities that
162                 ** deal with the GECOS field (finger, mail, sendmail,
163                 ** etc.) translate any '&' character in it to the login name,
164                 ** with the first letter capitalized.  So, for instance,
165                 ** fingering a user "bob" with the GECOS field "& Jones"
166                 ** would reveal him to be "In real life: Bob Jones".
167                 ** Surprisingly, though, the OS doesn't do the translation
168                 ** for you, so we have to do it manually here.
169                 */
170                 if (*np == '&') {  /* blech! */
171                         strcpy(cp, pw->pw_name);
172                         *cp = toupper(*cp);
173                         while (*cp)
174                                 cp++;
175                         np++;
176                 } else {
177                         *cp++ = *np++;
178                 }
179 #endif /* BSD42 */
180         }
181         *cp = '\0';
182         strncpy(username, pw->pw_name, sizeof(username));
183
184         /*
185         ** The $SIGNATURE environment variable overrides the GECOS field's
186         ** idea of your real name.
187         */
188         if ((cp = getenv("SIGNATURE")) && *cp)
189                 strncpy(tmp, cp, sizeof(tmp));
190
191         /* quote the fullname as needed */
192         needquotes = 0;
193         for (tp=tmp; *tp; tp++) {
194                 switch (*tp) {
195                 case '(': case ')': case '<': case '>': case '[': case ']':
196                 case ':': case ';': case '@': case '\\': case ',': case '.':
197                 case '"':  /* cf. RFC 5322 */
198                         break;  /* ... the switch */
199                 default:
200                         continue;  /* ... the loop */
201                 }
202                 /* we've found a special char */
203                 needquotes = 1;
204                 break;
205         }
206         cp=fullname;
207         if (needquotes) {
208                 *cp++ = '"';
209         }
210         for (tp=tmp; *tp; *cp++=*tp++) {
211                 if (*tp == '"') {
212                         *cp++ = '\\';  /* prepend backslash */
213                 }
214         }
215         if (needquotes) {
216                 *cp++ = '"';
217         }
218         *cp = '\0';
219
220         return;
221 }