Line data Source code
1 : /* mbox-util.c - Mail address helper functions
2 : * Copyright (C) 1998-2010 Free Software Foundation, Inc.
3 : * Copyright (C) 1998-2015 Werner Koch
4 : *
5 : * This file is part of GnuPG.
6 : *
7 : * This file is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU Lesser General Public License as
9 : * published by the Free Software Foundation; either version 2.1 of
10 : * the License, or (at your option) any later version.
11 : *
12 : * This file is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU Lesser General Public License
18 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : /* NB: This code has been taken from GnuPG. Please keep it in sync
22 : * with GnuPG. */
23 :
24 : #if HAVE_CONFIG_H
25 : # include <config.h>
26 : #endif
27 :
28 : #include <stdio.h>
29 : #include <stdlib.h>
30 : #include <string.h>
31 : #include <unistd.h>
32 : #include <errno.h>
33 :
34 : #include "mbox-util.h"
35 :
36 : /* Lowercase all ASCII characters in STRING. */
37 : static char *
38 247 : ascii_strlwr (char *string)
39 : {
40 : char *p;
41 :
42 4365 : for (p = string; *p; p++ )
43 4118 : if (!(*p & ~0x7f) && *p >= 'A' && *p <= 'Z')
44 0 : *p |= 0x20;
45 :
46 247 : return string;
47 : }
48 :
49 :
50 : static int
51 235 : string_count_chr (const char *string, int c)
52 : {
53 : int count;
54 :
55 4158 : for (count=0; *string; string++ )
56 3923 : if ( *string == c )
57 235 : count++;
58 235 : return count;
59 : }
60 :
61 : static int
62 12 : mem_count_chr (const void *buffer, int c, size_t length)
63 : {
64 12 : const char *s = buffer;
65 : int count;
66 :
67 207 : for (count=0; length; length--, s++)
68 195 : if (*s == c)
69 12 : count++;
70 12 : return count;
71 : }
72 :
73 :
74 : /* This is a case-sensitive version of our memistr. I wonder why no
75 : standard function memstr exists but I better do not use the name
76 : memstr to avoid future conflicts. */
77 : static const char *
78 12 : my_memstr (const void *buffer, size_t buflen, const char *sub)
79 : {
80 12 : const unsigned char *buf = buffer;
81 12 : const unsigned char *t = (const unsigned char *)buf;
82 12 : const unsigned char *s = (const unsigned char *)sub;
83 12 : size_t n = buflen;
84 :
85 207 : for ( ; n ; t++, n-- )
86 : {
87 195 : if (*t == *s)
88 : {
89 12 : for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
90 : ;
91 12 : if (!*s)
92 0 : return (const char*)buf;
93 12 : t = (const unsigned char *)buf;
94 12 : s = (const unsigned char *)sub ;
95 12 : n = buflen;
96 : }
97 : }
98 12 : return NULL;
99 : }
100 :
101 :
102 :
103 : static int
104 235 : string_has_ctrl_or_space (const char *string)
105 : {
106 4158 : for (; *string; string++ )
107 3923 : if (!(*string & 0x80) && *string <= 0x20)
108 0 : return 1;
109 235 : return 0;
110 : }
111 :
112 :
113 : /* Return true if STRING has two consecutive '.' after an '@'
114 : sign. */
115 : static int
116 235 : has_dotdot_after_at (const char *string)
117 : {
118 235 : string = strchr (string, '@');
119 235 : if (!string)
120 0 : return 0; /* No at-sign. */
121 235 : string++;
122 235 : return !!strstr (string, "..");
123 : }
124 :
125 :
126 : /* Check whether BUFFER has characters not valid in an RFC-822
127 : address. LENGTH gives the length of BUFFER.
128 :
129 : To cope with OpenPGP we ignore non-ascii characters so that for
130 : example umlauts are legal in an email address. An OpenPGP user ID
131 : must be utf-8 encoded but there is no strict requirement for
132 : RFC-822. Thus to avoid IDNA encoding we put the address verbatim
133 : as utf-8 into the user ID under the assumption that mail programs
134 : handle IDNA at a lower level and take OpenPGP user IDs as utf-8.
135 : Note that we can't do an utf-8 encoding checking here because in
136 : keygen.c this function is called with the native encoding and
137 : native to utf-8 encoding is only done later. */
138 : static int
139 117 : has_invalid_email_chars (const void *buffer, size_t length)
140 : {
141 117 : const unsigned char *s = buffer;
142 117 : int at_seen=0;
143 117 : const char *valid_chars=
144 : "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
145 :
146 935 : for ( ; length && *s; length--, s++ )
147 : {
148 923 : if ((*s & 0x80))
149 0 : continue; /* We only care about ASCII. */
150 923 : if (*s == '@')
151 12 : at_seen=1;
152 1025 : else if (!at_seen && !(strchr (valid_chars, *s)
153 114 : || strchr ("!#$%&'*+/=?^`{|}~", *s)))
154 105 : return 1;
155 806 : else if (at_seen && !strchr (valid_chars, *s))
156 0 : return 1;
157 : }
158 12 : return 0;
159 : }
160 :
161 :
162 : /* Same as is_valid_mailbox (see below) but operates on non-nul
163 : terminated buffer. */
164 : static int
165 117 : is_valid_mailbox_mem (const void *name_arg, size_t namelen)
166 : {
167 117 : const char *name = name_arg;
168 :
169 129 : return !( !name
170 117 : || !namelen
171 117 : || has_invalid_email_chars (name, namelen)
172 12 : || mem_count_chr (name, '@', namelen) != 1
173 12 : || *name == '@'
174 12 : || name[namelen-1] == '@'
175 12 : || name[namelen-1] == '.'
176 12 : || my_memstr (name, namelen, ".."));
177 : }
178 :
179 :
180 : /* Check whether NAME represents a valid mailbox according to
181 : RFC822. Returns true if so. */
182 : int
183 117 : _gpgme_is_valid_mailbox (const char *name)
184 : {
185 117 : return name? is_valid_mailbox_mem (name, strlen (name)) : 0;
186 : }
187 :
188 :
189 : /* Return the mailbox (local-part@domain) form a standard user id.
190 : All plain ASCII characters in the result are converted to
191 : lowercase. Caller must free the result. Returns NULL if no valid
192 : mailbox was found (or we are out of memory). */
193 : char *
194 352 : _gpgme_mailbox_from_userid (const char *userid)
195 : {
196 : const char *s, *s_end;
197 : size_t len;
198 352 : char *result = NULL;
199 :
200 352 : s = strchr (userid, '<');
201 352 : if (s)
202 : {
203 : /* Seems to be a standard user id. */
204 235 : s++;
205 235 : s_end = strchr (s, '>');
206 235 : if (s_end && s_end > s)
207 : {
208 235 : len = s_end - s;
209 235 : result = malloc (len + 1);
210 235 : if (!result)
211 0 : return NULL; /* Ooops - out of core. */
212 235 : strncpy (result, s, len);
213 235 : result[len] = 0;
214 : /* Apply some basic checks on the address. We do not use
215 : is_valid_mailbox because those checks are too strict. */
216 470 : if (string_count_chr (result, '@') != 1 /* Need exactly one '@. */
217 235 : || *result == '@' /* local-part missing. */
218 235 : || result[len-1] == '@' /* domain missing. */
219 235 : || result[len-1] == '.' /* ends with a dot. */
220 235 : || string_has_ctrl_or_space (result)
221 235 : || has_dotdot_after_at (result))
222 : {
223 0 : free (result);
224 0 : result = NULL;
225 0 : errno = EINVAL;
226 : }
227 : }
228 : else
229 0 : errno = EINVAL;
230 : }
231 117 : else if (_gpgme_is_valid_mailbox (userid))
232 : {
233 : /* The entire user id is a mailbox. Return that one. Note that
234 : this fallback method has some restrictions on the valid
235 : syntax of the mailbox. However, those who want weird
236 : addresses should know about it and use the regular <...>
237 : syntax. */
238 12 : result = strdup (userid);
239 : }
240 : else
241 105 : errno = EINVAL;
242 :
243 352 : return result? ascii_strlwr (result): NULL;
244 : }
245 :
246 :
247 : /* /\* Check whether UID is a valid standard user id of the form */
248 : /* "Heinrich Heine <heinrichh@duesseldorf.de>" */
249 : /* and return true if this is the case. *\/ */
250 : /* int */
251 : /* is_valid_user_id (const char *uid) */
252 : /* { */
253 : /* if (!uid || !*uid) */
254 : /* return 0; */
255 :
256 : /* return 1; */
257 : /* } */
|