Commit 659509fa 659509faf4d8dc0379ea3fed0f7b5c9ce998f885 by Sergey Poznyakoff

Essentially rewritten.

mailbox_open_default() supports the following naming schemes:

	   %           --> system mailbox for the real uid
           %user       --> system mailbox for the given user
           ~/file      --> /home/user/file
           ~user/file  --> /home/user/file
           +file       --> /home/user/Mail/file
           =file       --> /home/user/Mail/file
1 parent 6575dfdb
...@@ -34,38 +34,53 @@ ...@@ -34,38 +34,53 @@
34 #include <mailutils/mutil.h> 34 #include <mailutils/mutil.h>
35 #include <mailutils/error.h> 35 #include <mailutils/error.h>
36 36
37 char *mu_path_maildir = MU_PATH_MAILDIR;
38
37 /* Is this a security risk? */ 39 /* Is this a security risk? */
38 #define USE_ENVIRON 1 40 #define USE_ENVIRON 1
39 41
40 static char * tilde_expansion __P ((const char *)); 42 static int
41 static char * plus_equal_expansion __P ((const char *)); 43 split_shortcut (const char *file, const char pfx[], char **user, char **rest)
42 static char * get_cwd __P ((void));
43 static char * get_full_path __P ((const char *));
44 static const char * get_homedir __P ((const char *));
45
46 /* Do + and = expansion to ~/Mail, if necessary. */
47 static char *
48 plus_equal_expansion (const char *file)
49 { 44 {
50 char *p = NULL; 45 *user = NULL;
51 if (file && (*file == '+' || *file == '=')) 46 *rest = NULL;
47
48 if (!strchr (pfx, file[0]))
49 return 0;
50
51 if (*++file == 0)
52 return 0;
53 else
52 { 54 {
53 char *folder; 55 char *p = strchr (file, '/');
54 /* Skip '+' or '='. */ 56 int len;
55 file++; 57 if (p)
56 folder = tilde_expansion ("~/Mail"); 58 len = p - file + 1;
57 if (folder) 59 else
58 { 60 len = strlen (file) + 1;
59 p = malloc (strlen (folder) + 1 + strlen (file) + 1); 61
60 if (p) 62 *user = calloc (1, len);
61 sprintf(p, "%s/%s", folder, file); 63 if (!*user)
62 free (folder); 64 return ENOMEM;
63 } 65
66 memcpy (*user, file, len);
67 (*user)[len-1] = 0;
68 file += len-1;
69 if (file[0] == '/')
70 file++;
64 } 71 }
65 72
66 if (!p) 73 if (file[0])
67 p = strdup (file); 74 {
68 return p; 75 *rest = strdup (file);
76 if (!*rest)
77 {
78 free (*user);
79 return ENOMEM;
80 }
81 }
82
83 return 0;
69 } 84 }
70 85
71 static const char * 86 static const char *
...@@ -77,7 +92,7 @@ get_homedir (const char *user) ...@@ -77,7 +92,7 @@ get_homedir (const char *user)
77 { 92 {
78 pw = mu_getpwnam (user); 93 pw = mu_getpwnam (user);
79 if (pw) 94 if (pw)
80 homedir = pw->pw_dir; 95 homedir = pw->pw_dir;
81 } 96 }
82 else 97 else
83 { 98 {
...@@ -85,164 +100,163 @@ get_homedir (const char *user) ...@@ -85,164 +100,163 @@ get_homedir (const char *user)
85 /* NOTE: Should we honor ${HOME}? */ 100 /* NOTE: Should we honor ${HOME}? */
86 homedir = getenv ("HOME"); 101 homedir = getenv ("HOME");
87 if (homedir == NULL) 102 if (homedir == NULL)
88 { 103 {
89 pw = mu_getpwuid (getuid ()); 104 pw = mu_getpwuid (getuid ());
90 if (pw) 105 if (pw)
91 homedir = pw->pw_dir; 106 homedir = pw->pw_dir;
92 } 107 }
93 #else 108 #else
94 pw = mu_getpwuid (getuid ()); 109 pw = mu_getpwuid (getuid ());
95 if (pw) 110 if (pw)
96 homedir = pw->pw_dir; 111 homedir = pw->pw_dir;
97 #endif 112 #endif
98 } 113 }
99 return homedir; 114 return homedir;
100 } 115 }
101 116
102 /* Do ~ , if necessary. We do not use $HOME. */ 117 static int
103 static char * 118 user_mailbox_name (const char *user, char **mailbox_name)
104 tilde_expansion (const char *file)
105 { 119 {
106 char *p = NULL; 120 #ifdef USE_ENVIRON
107 const char *homedir = NULL; 121 if (!user)
108 const char delim = '/'; /* Not portable. */ 122 user = (getenv ("LOGNAME")) ? getenv ("LOGNAME") : getenv ("USER");
109 123 #endif
110 if (file && *file == '~') 124 if (user == NULL)
111 { 125 {
112 /* Skip the tilde. */ 126 struct passwd *pw;
113 file++; 127 pw = mu_getpwuid (getuid ());
114 128 if (pw)
115 /* This means we have ~ or ~/something. */ 129 user = pw->pw_name;
116 if (*file == delim || *file == '\0')
117 {
118 homedir = get_homedir (NULL);
119 if (homedir)
120 {
121 p = calloc (strlen (homedir) + strlen (file) + 1, 1);
122 if (p)
123 {
124 strcpy (p, homedir);
125 strcat (p, file);
126 }
127 }
128 }
129 /* Means we have ~user or ~user/something. */
130 else 130 else
131 { 131 {
132 const char *s = file; 132 mu_error ("Who am I ?\n");
133 133 return EINVAL;
134 /* Move to the first delim. */
135 while (*s && *s != delim) s++;
136
137 /* Get the username homedir. */
138 {
139 char *name;
140 name = calloc (s - file + 1, 1);
141 if (name)
142 {
143 memcpy (name, file, s - file);
144 name [s - file] = '\0';
145 }
146 homedir = get_homedir (name);
147 free (name);
148 }
149
150 if (homedir)
151 {
152 p = calloc (strlen (homedir) + strlen (s) + 1, 1);
153 if (p)
154 {
155 strcpy (p, homedir);
156 strcat (p, s);
157 }
158 }
159 } 134 }
160 } 135 }
161 136 *mailbox_name = malloc (strlen (user) + strlen (mu_path_maildir) + 2);
162 if (!p) 137 if (*mailbox_name == NULL)
163 p = strdup (file); 138 return ENOMEM;
164 return p; 139 sprintf (*mailbox_name, "%s%s", mu_path_maildir, user);
140 return 0;
165 } 141 }
166 142
167 static char * 143 #define MPREFIX "Mail"
168 get_cwd ()
169 {
170 char *ret;
171 unsigned path_max;
172 char buf[128];
173
174 errno = 0;
175 ret = getcwd (buf, sizeof (buf));
176 if (ret != NULL)
177 return strdup (buf);
178 144
179 if (errno != ERANGE) 145 static int
180 return NULL; 146 plus_expand (const char *file, char **buf)
181 147 {
182 path_max = 128; 148 char *user = NULL;
183 path_max += 2; /* The getcwd docs say to do this. */ 149 char *path = NULL;
184 150 const char *home;
185 for (;;) 151 int status, len;
152
153 if ((status = split_shortcut (file, "+=", &user, &path)))
154 return status;
155
156 if (!path)
186 { 157 {
187 char *cwd = (char *) malloc (path_max); 158 free (user);
159 return ENOENT;
160 }
161
162 home = get_homedir (user);
163 if (!home)
164 {
165 free (user);
166 free (path);
167 return ENOENT;
168 }
188 169
189 errno = 0; 170 len = strlen (home) + sizeof (MPREFIX) + strlen (path) + 3;
190 ret = getcwd (cwd, path_max); 171 *buf = malloc (len);
191 if (ret != NULL) 172 sprintf (*buf, "%s/%s/%s", home, MPREFIX, path);
192 return ret; 173 (*buf)[len-1] = 0;
193 if (errno != ERANGE) 174 free (user);
194 { 175 free (path);
195 int save_errno = errno; 176 return 0;
196 free (cwd); 177 }
197 errno = save_errno;
198 return NULL;
199 }
200 178
201 free (cwd); 179 /* Do ~ , if necessary. We do not use $HOME. */
180 static int
181 tilde_expand (const char *file, char **buf)
182 {
183 char *user = NULL;
184 char *path = NULL;
185 const char *home;
186 int status;
187 int len;
188
189 if ((status = split_shortcut (file, "~", &user, &path)))
190 return status;
191
192 if (!user)
193 return ENOENT;
194 if (!path)
195 {
196 free (user);
197 return ENOENT;
198 }
199
200 home = get_homedir (user);
201 if (!home)
202 {
203 free (user);
204 free (path);
205 return ENOENT;
206 }
202 207
203 path_max += path_max / 16; 208 free (user); /* not needed anymore */
204 path_max += 32; 209
210 len = strlen (home) + strlen (path) + 2;
211 *buf = malloc (len);
212 if (*buf)
213 {
214 sprintf (*buf, "%s/%s", home, path);
215 (*buf)[len-1] = 0;
205 } 216 }
206 /* oops? */ 217
207 return NULL; 218 free (path);
219 free (user);
220 return *buf ? 0 : ENOMEM;
208 } 221 }
209 222
210 static char * 223 static int
211 get_full_path (const char *file) 224 percent_expand (const char *file, char **mbox)
212 { 225 {
213 char *p = NULL; 226 char *user = NULL;
227 char *path = NULL;
228 int status;
229
230 if ((status = split_shortcut (file, "%", &user, &path)))
231 return status;
214 232
215 if (!file) 233 if (path)
216 p = get_cwd ();
217 else if (*file != '/')
218 { 234 {
219 char *cwd = get_cwd (); 235 free (user);
220 if (cwd) 236 free (path);
221 { 237 return ENOENT;
222 p = calloc (strlen (cwd) + 1 + strlen (file) + 1, 1);
223 if (p)
224 sprintf (p, "%s/%s", cwd, file);
225 free (cwd);
226 }
227 } 238 }
228 239
229 if (!p) 240 status = user_mailbox_name (user, mbox);
230 p = strdup (file); 241 free (user);
231 return p; 242 return status;
232 } 243 }
233 244
234 /* We are trying to be smart about the location of the mail. 245 /* We are trying to be smart about the location of the mail.
235 mailbox_create() is not doing this. 246 mailbox_create() is not doing this.
236 ~/file --> /home/user/file 247 % --> system mailbox for the real uid
237 ~user/file --> /home/user/file 248 %user --> system mailbox for the given user
238 +file --> /home/user/Mail/file 249 ~/file --> /home/user/file
239 =file --> /home/user/Mail/file 250 ~user/file --> /home/user/file
251 +file --> /home/user/Mail/file
252 =file --> /home/user/Mail/file
240 */ 253 */
241 int 254 int
242 mailbox_create_default (mailbox_t *pmbox, const char *mail) 255 mailbox_create_default (mailbox_t *pmbox, const char *mail)
243 { 256 {
244 char *mbox = NULL; 257 char *mbox = NULL;
245 int status; 258 char *tmp_mbox = NULL;
259 int status = 0;
246 260
247 /* Sanity. */ 261 /* Sanity. */
248 if (pmbox == NULL) 262 if (pmbox == NULL)
...@@ -251,52 +265,44 @@ mailbox_create_default (mailbox_t *pmbox, const char *mail) ...@@ -251,52 +265,44 @@ mailbox_create_default (mailbox_t *pmbox, const char *mail)
251 /* Other utilities may not understand GNU mailutils url namespace, so 265 /* Other utilities may not understand GNU mailutils url namespace, so
252 use FOLDER instead, to not confuse others by using MAIL. */ 266 use FOLDER instead, to not confuse others by using MAIL. */
253 if (mail == NULL || *mail == '\0') 267 if (mail == NULL || *mail == '\0')
254 mail = getenv ("FOLDER"); 268 {
269 mail = getenv ("FOLDER");
255 270
256 /* Fallback to wellknown environment. */ 271 /* Fallback to wellknown environment. */
257 if (mail == NULL) 272 if (!mail)
258 mail = getenv ("MAIL"); 273 mail = getenv ("MAIL");
259 274
260 /* FIXME: This is weak, it would be better to check 275 if (!mail)
261 for all the known schemes to detect presence of URLs. */ 276 {
262 if (mail && *mail && strchr (mail, ':') == NULL) 277 if (status = user_mailbox_name (NULL, &tmp_mbox))
263 { 278 return status;
264 char *mail0; 279 mail = tmp_mbox;
265 char *mail2; 280 }
266
267 mail0 = tilde_expansion (mail);
268 mail2 = plus_equal_expansion (mail0);
269 free (mail0);
270 mbox = get_full_path (mail2);
271 free (mail2);
272 } 281 }
273 else if (mail)
274 mbox = strdup (mail);
275 282
276 /* Search the spooldir. */ 283 switch (mail[0])
277 if (mbox == NULL)
278 { 284 {
279 const char *user = NULL; 285 case '%':
280 #ifdef USE_ENVIRON 286 status = percent_expand (mail, &mbox);
281 user = (getenv ("LOGNAME")) ? getenv ("LOGNAME") : getenv ("USER"); 287 break;
282 #endif 288 case '~':
283 if (user == NULL) 289 status = tilde_expand (mail, &mbox);
284 { 290 break;
285 struct passwd *pw; 291 case '+':
286 pw = mu_getpwuid (getuid ()); 292 case '=':
287 if (pw) 293 status = plus_expand (mail, &mbox);
288 user = pw->pw_name; 294 break;
289 else 295 default:
290 { 296 mbox = strdup (mail);
291 mu_error ("Who am I ?\n"); 297 break;
292 return EINVAL;
293 }
294 }
295 mbox = malloc (strlen (user) + strlen (MU_PATH_MAILDIR) + 2);
296 if (mbox == NULL)
297 return ENOMEM;
298 sprintf (mbox, "%s%s", MU_PATH_MAILDIR, user);
299 } 298 }
299
300 if (tmp_mbox)
301 free (tmp_mbox);
302
303 if (status)
304 return status;
305
300 status = mailbox_create (pmbox, mbox); 306 status = mailbox_create (pmbox, mbox);
301 free (mbox); 307 free (mbox);
302 return status; 308 return status;
......