chiark / gitweb /
Commit Debian 3.0 (quilt) metadata
[bash.git] / mailcheck.c
1 /* mailcheck.c -- The check is in the mail... */
2
3 /* Copyright (C) 1987-2009 Free Software Foundation, Inc.
4
5    This file is part of GNU Bash, the Bourne Again SHell.
6
7    Bash is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    Bash 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 General Public License
18    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include "bashtypes.h"
25 #include "posixstat.h"
26 #if defined (HAVE_SYS_PARAM_H)
27 #  include <sys/param.h>
28 #endif
29 #if defined (HAVE_UNISTD_H)
30 #  include <unistd.h>
31 #endif
32 #include "posixtime.h"
33 #include "bashansi.h"
34 #include "bashintl.h"
35
36 #include "shell.h"
37 #include "execute_cmd.h"
38 #include "mailcheck.h"
39 #include <tilde/tilde.h>
40
41 /* Values for flags word in struct _fileinfo */
42 #define MBOX_INITIALIZED        0x01
43
44 extern time_t shell_start_time;
45
46 extern int mailstat __P((const char *, struct stat *));
47
48 typedef struct _fileinfo {
49   char *name;
50   char *msg;
51   time_t access_time;
52   time_t mod_time;
53   off_t file_size;
54   int flags;
55 } FILEINFO;
56
57 /* The list of remembered mail files. */
58 static FILEINFO **mailfiles = (FILEINFO **)NULL;
59
60 /* Number of mail files that we have. */
61 static int mailfiles_count;
62
63 /* The last known time that mail was checked. */
64 static time_t last_time_mail_checked = 0;
65
66 /* Non-zero means warn if a mail file has been read since last checked. */
67 int mail_warning;
68
69 static int find_mail_file __P((char *));
70 static void init_mail_file __P((int));
71 static void update_mail_file __P((int));
72 static int add_mail_file __P((char *, char *));
73
74 static FILEINFO *alloc_mail_file __P((char *, char *));
75 static void dispose_mail_file __P((FILEINFO *));
76
77 static int file_mod_date_changed __P((int));
78 static int file_access_date_changed __P((int));
79 static int file_has_grown __P((int));
80
81 static char *parse_mailpath_spec __P((char *));
82
83 /* Returns non-zero if it is time to check mail. */
84 int
85 time_to_check_mail ()
86 {
87   char *temp;
88   time_t now;
89   intmax_t seconds;
90
91   temp = get_string_value ("MAILCHECK");
92
93   /* Negative number, or non-numbers (such as empty string) cause no
94      checking to take place. */
95   if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0)
96     return (0);
97
98   now = NOW;
99   /* Time to check if MAILCHECK is explicitly set to zero, or if enough
100      time has passed since the last check. */
101   return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
102 }
103
104 /* Okay, we have checked the mail.  Perhaps I should make this function
105    go away. */
106 void
107 reset_mail_timer ()
108 {
109   last_time_mail_checked = NOW;
110 }
111
112 /* Locate a file in the list.  Return index of
113    entry, or -1 if not found. */
114 static int
115 find_mail_file (file)
116      char *file;
117 {
118   register int i;
119
120   for (i = 0; i < mailfiles_count; i++)
121     if (STREQ (mailfiles[i]->name, file))
122       return i;
123
124   return -1;
125 }
126
127 #define RESET_MAIL_FILE(i) \
128   do \
129     { \
130       mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
131       mailfiles[i]->file_size = 0; \
132       mailfiles[i]->flags = 0; \
133     } \
134   while (0)
135
136 #define UPDATE_MAIL_FILE(i, finfo) \
137   do \
138     { \
139       mailfiles[i]->access_time = finfo.st_atime; \
140       mailfiles[i]->mod_time = finfo.st_mtime; \
141       mailfiles[i]->file_size = finfo.st_size; \
142       mailfiles[i]->flags |= MBOX_INITIALIZED; \
143     } \
144   while (0)
145
146 static void
147 init_mail_file (i)
148      int i;
149 {
150   mailfiles[i]->access_time = mailfiles[i]->mod_time = last_time_mail_checked ? last_time_mail_checked : shell_start_time;
151   mailfiles[i]->file_size = 0;
152   mailfiles[i]->flags = 0;
153 }
154
155 static void
156 update_mail_file (i)
157      int i;
158 {
159   char *file;
160   struct stat finfo;
161
162   file = mailfiles[i]->name;
163   if (mailstat (file, &finfo) == 0)
164     UPDATE_MAIL_FILE (i, finfo);
165   else
166     RESET_MAIL_FILE (i);
167 }
168
169 /* Add this file to the list of remembered files and return its index
170    in the list of mail files. */
171 static int
172 add_mail_file (file, msg)
173      char *file, *msg;
174 {
175   struct stat finfo;
176   char *filename;
177   int i;
178
179   filename = full_pathname (file);
180   i = find_mail_file (filename);
181   if (i >= 0)
182     {
183       if (mailstat (filename, &finfo) == 0)
184         UPDATE_MAIL_FILE (i, finfo);
185
186       free (filename);
187       return i;
188     }
189
190   i = mailfiles_count++;
191   mailfiles = (FILEINFO **)xrealloc
192                 (mailfiles, mailfiles_count * sizeof (FILEINFO *));
193
194   mailfiles[i] = alloc_mail_file (filename, msg);
195   init_mail_file (i);
196
197   return i;
198 }
199
200 /* Reset the existing mail files access and modification times to zero. */
201 void
202 reset_mail_files ()
203 {
204   register int i;
205
206   for (i = 0; i < mailfiles_count; i++)
207     RESET_MAIL_FILE (i);
208 }
209
210 static FILEINFO *
211 alloc_mail_file (filename, msg)
212      char *filename, *msg;
213 {
214   FILEINFO *mf;
215
216   mf = (FILEINFO *)xmalloc (sizeof (FILEINFO));
217   mf->name = filename;
218   mf->msg = msg ? savestring (msg) : (char *)NULL;
219   mf->flags = 0;
220
221   return mf;
222 }
223
224 static void
225 dispose_mail_file (mf)
226      FILEINFO *mf;
227 {
228   free (mf->name);
229   FREE (mf->msg);
230   free (mf);
231 }
232
233 /* Free the information that we have about the remembered mail files. */
234 void
235 free_mail_files ()
236 {
237   register int i;
238
239   for (i = 0; i < mailfiles_count; i++)
240     dispose_mail_file (mailfiles[i]);
241
242   if (mailfiles)
243     free (mailfiles);
244
245   mailfiles_count = 0;
246   mailfiles = (FILEINFO **)NULL;
247 }
248
249 void
250 init_mail_dates ()
251 {
252   if (mailfiles == 0)
253     remember_mail_dates ();
254 }
255
256 /* Return non-zero if FILE's mod date has changed and it has not been
257    accessed since modified.  If the size has dropped to zero, reset
258    the cached mail file info. */
259 static int
260 file_mod_date_changed (i)
261      int i;
262 {
263   time_t mtime;
264   struct stat finfo;
265   char *file;
266
267   file = mailfiles[i]->name;
268   mtime = mailfiles[i]->mod_time;
269
270   if (mailstat (file, &finfo) != 0)
271     return (0);
272
273   if (finfo.st_size > 0)
274     return (mtime < finfo.st_mtime);
275
276   if (finfo.st_size == 0 && mailfiles[i]->file_size > 0)
277     UPDATE_MAIL_FILE (i, finfo);
278
279   return (0);
280 }
281
282 /* Return non-zero if FILE's access date has changed. */
283 static int
284 file_access_date_changed (i)
285      int i;
286 {
287   time_t atime;
288   struct stat finfo;
289   char *file;
290
291   file = mailfiles[i]->name;
292   atime = mailfiles[i]->access_time;
293
294   if (mailstat (file, &finfo) != 0)
295     return (0);
296
297   if (finfo.st_size > 0)
298     return (atime < finfo.st_atime);
299
300   return (0);
301 }
302
303 /* Return non-zero if FILE's size has increased. */
304 static int
305 file_has_grown (i)
306      int i;
307 {
308   off_t size;
309   struct stat finfo;
310   char *file;
311
312   file = mailfiles[i]->name;
313   size = mailfiles[i]->file_size;
314
315   return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size));
316 }
317
318 /* Take an element from $MAILPATH and return the portion from
319    the first unquoted `?' or `%' to the end of the string.  This is the
320    message to be printed when the file contents change. */
321 static char *
322 parse_mailpath_spec (str)
323      char *str;
324 {
325   char *s;
326   int pass_next;
327
328   for (s = str, pass_next = 0; s && *s; s++)
329     {
330       if (pass_next)
331         {
332           pass_next = 0;
333           continue;
334         }
335       if (*s == '\\')
336         {
337           pass_next++;
338           continue;
339         }
340       if (*s == '?' || *s == '%')
341         return s;
342     }
343   return ((char *)NULL);
344 }
345
346 char *
347 make_default_mailpath ()
348 {
349 #if defined (DEFAULT_MAIL_DIRECTORY)
350   char *mp;
351
352   get_current_user_info ();
353   mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
354   strcpy (mp, DEFAULT_MAIL_DIRECTORY);
355   mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
356   strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
357   return (mp);
358 #else
359   return ((char *)NULL);
360 #endif
361 }
362
363 /* Remember the dates of the files specified by MAILPATH, or if there is
364    no MAILPATH, by the file specified in MAIL.  If neither exists, use a
365    default value, which we randomly concoct from using Unix. */
366
367 void
368 remember_mail_dates ()
369 {
370   char *mailpaths;
371   char *mailfile, *mp;
372   int i = 0;
373
374   mailpaths = get_string_value ("MAILPATH");
375
376   /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
377   if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
378     {
379       add_mail_file (mailpaths, (char *)NULL);
380       return;
381     }
382
383   if (mailpaths == 0)
384     {
385       mailpaths = make_default_mailpath ();
386       if (mailpaths)
387         {
388           add_mail_file (mailpaths, (char *)NULL);
389           free (mailpaths);
390         }
391       return;
392     }
393
394   while (mailfile = extract_colon_unit (mailpaths, &i))
395     {
396       mp = parse_mailpath_spec (mailfile);
397       if (mp && *mp)
398         *mp++ = '\0';
399       add_mail_file (mailfile, mp);
400       free (mailfile);
401     }
402 }
403
404 /* check_mail () is useful for more than just checking mail.  Since it has
405    the paranoids dream ability of telling you when someone has read your
406    mail, it can just as easily be used to tell you when someones .profile
407    file has been read, thus letting one know when someone else has logged
408    in.  Pretty good, huh? */
409
410 /* Check for mail in some files.  If the modification date of any
411    of the files in MAILPATH has changed since we last did a
412    remember_mail_dates () then mention that the user has mail.
413    Special hack:  If the variable MAIL_WARNING is non-zero and the
414    mail file has been accessed since the last time we remembered, then
415    the message "The mail in <mailfile> has been read" is printed. */
416 void
417 check_mail ()
418 {
419   char *current_mail_file, *message;
420   int i, use_user_notification;
421   char *dollar_underscore, *temp;
422
423   dollar_underscore = get_string_value ("_");
424   if (dollar_underscore)
425     dollar_underscore = savestring (dollar_underscore);
426
427   for (i = 0; i < mailfiles_count; i++)
428     {
429       current_mail_file = mailfiles[i]->name;
430
431       if (*current_mail_file == '\0')
432         continue;
433
434       if (file_mod_date_changed (i))
435         {
436           int file_is_bigger;
437
438           use_user_notification = mailfiles[i]->msg != (char *)NULL;
439           message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_");
440
441           bind_variable ("_", current_mail_file, 0);
442
443 #define atime mailfiles[i]->access_time
444 #define mtime mailfiles[i]->mod_time
445
446           /* Have to compute this before the call to update_mail_file, which
447              resets all the information. */
448           file_is_bigger = file_has_grown (i);
449
450           update_mail_file (i);
451
452           /* If the user has just run a program which manipulates the
453              mail file, then don't bother explaining that the mail
454              file has been manipulated.  Since some systems don't change
455              the access time to be equal to the modification time when
456              the mail in the file is manipulated, check the size also.  If
457              the file has not grown, continue. */
458           if ((atime >= mtime) && !file_is_bigger)
459             continue;
460
461           /* If the mod time is later than the access time and the file
462              has grown, note the fact that this is *new* mail. */
463           if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
464             message = _("You have new mail in $_");
465 #undef atime
466 #undef mtime
467
468           if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES))
469             {
470               puts (temp);
471               free (temp);
472             }
473           else
474             putchar ('\n');
475         }
476
477       if (mail_warning && file_access_date_changed (i))
478         {
479           update_mail_file (i);
480           printf (_("The mail in %s has been read\n"), current_mail_file);
481         }
482     }
483
484   if (dollar_underscore)
485     {
486       bind_variable ("_", dollar_underscore, 0);
487       free (dollar_underscore);
488     }
489   else
490     unbind_variable ("_");
491 }