blob: f23a7f85b5a28a1f6227ea9674d4acf9adf6f11e [file] [log] [blame]
Jari Aalto726f6381996-08-26 18:22:31 +00001/* mailcheck.c -- The check is in the mail... */
2
Jari Aalto7117c2d2002-07-17 14:10:11 +00003/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
Jari Aalto726f6381996-08-26 18:22:31 +00004
5This file is part of GNU Bash, the Bourne Again SHell.
6
7Bash is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
Jari Aaltobb706242000-03-17 21:46:59 +00009Software Foundation; either version 2, or (at your option) any later
Jari Aalto726f6381996-08-26 18:22:31 +000010version.
11
12Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with Bash; see the file COPYING. If not, write to the Free Software
Jari Aaltobb706242000-03-17 21:46:59 +000019Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
Jari Aalto726f6381996-08-26 18:22:31 +000020
Jari Aaltoccc6cda1996-12-23 17:02:34 +000021#include "config.h"
22
Jari Aalto726f6381996-08-26 18:22:31 +000023#include <stdio.h>
24#include "bashtypes.h"
25#include "posixstat.h"
Jari Aaltocce855b1998-04-17 19:52:44 +000026#ifndef _MINIX
27# include <sys/param.h>
28#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +000029#if defined (HAVE_UNISTD_H)
30# include <unistd.h>
31#endif
Jari Aaltof73dda02001-11-13 17:56:06 +000032#include "posixtime.h"
Jari Aalto726f6381996-08-26 18:22:31 +000033#include "bashansi.h"
Jari Aaltob80f6442004-07-27 13:29:18 +000034#include "bashintl.h"
Jari Aaltoccc6cda1996-12-23 17:02:34 +000035
Jari Aalto726f6381996-08-26 18:22:31 +000036#include "shell.h"
Jari Aalto726f6381996-08-26 18:22:31 +000037#include "execute_cmd.h"
Jari Aaltoccc6cda1996-12-23 17:02:34 +000038#include "mailcheck.h"
Jari Aalto726f6381996-08-26 18:22:31 +000039#include <tilde/tilde.h>
40
Jari Aaltof73dda02001-11-13 17:56:06 +000041extern int mailstat __P((const char *, struct stat *));
Jari Aalto726f6381996-08-26 18:22:31 +000042
43typedef struct {
44 char *name;
Jari Aaltoccc6cda1996-12-23 17:02:34 +000045 char *msg;
Jari Aalto726f6381996-08-26 18:22:31 +000046 time_t access_time;
47 time_t mod_time;
Jari Aaltocce855b1998-04-17 19:52:44 +000048 off_t file_size;
Jari Aalto726f6381996-08-26 18:22:31 +000049} FILEINFO;
50
51/* The list of remembered mail files. */
Jari Aaltoccc6cda1996-12-23 17:02:34 +000052static FILEINFO **mailfiles = (FILEINFO **)NULL;
Jari Aalto726f6381996-08-26 18:22:31 +000053
54/* Number of mail files that we have. */
Jari Aaltoccc6cda1996-12-23 17:02:34 +000055static int mailfiles_count;
Jari Aalto726f6381996-08-26 18:22:31 +000056
57/* The last known time that mail was checked. */
Jari Aaltof73dda02001-11-13 17:56:06 +000058static time_t last_time_mail_checked;
Jari Aaltoccc6cda1996-12-23 17:02:34 +000059
60/* Non-zero means warn if a mail file has been read since last checked. */
61int mail_warning;
Jari Aalto726f6381996-08-26 18:22:31 +000062
Jari Aaltof73dda02001-11-13 17:56:06 +000063static int find_mail_file __P((char *));
64static void update_mail_file __P((int));
65static int add_mail_file __P((char *, char *));
66
67static int file_mod_date_changed __P((int));
68static int file_access_date_changed __P((int));
69static int file_has_grown __P((int));
70
71static char *parse_mailpath_spec __P((char *));
72
Jari Aalto726f6381996-08-26 18:22:31 +000073/* Returns non-zero if it is time to check mail. */
74int
75time_to_check_mail ()
76{
Jari Aaltoccc6cda1996-12-23 17:02:34 +000077 char *temp;
78 time_t now;
Jari Aalto7117c2d2002-07-17 14:10:11 +000079 intmax_t seconds;
Jari Aaltoccc6cda1996-12-23 17:02:34 +000080
81 temp = get_string_value ("MAILCHECK");
Jari Aalto726f6381996-08-26 18:22:31 +000082
83 /* Negative number, or non-numbers (such as empty string) cause no
84 checking to take place. */
Jari Aaltof73dda02001-11-13 17:56:06 +000085 if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0)
Jari Aalto726f6381996-08-26 18:22:31 +000086 return (0);
87
Jari Aaltoccc6cda1996-12-23 17:02:34 +000088 now = NOW;
Jari Aalto726f6381996-08-26 18:22:31 +000089 /* Time to check if MAILCHECK is explicitly set to zero, or if enough
90 time has passed since the last check. */
Jari Aaltoccc6cda1996-12-23 17:02:34 +000091 return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
Jari Aalto726f6381996-08-26 18:22:31 +000092}
93
94/* Okay, we have checked the mail. Perhaps I should make this function
95 go away. */
96void
97reset_mail_timer ()
98{
99 last_time_mail_checked = NOW;
100}
101
102/* Locate a file in the list. Return index of
103 entry, or -1 if not found. */
104static int
105find_mail_file (file)
106 char *file;
107{
108 register int i;
109
110 for (i = 0; i < mailfiles_count; i++)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000111 if (STREQ (mailfiles[i]->name, file))
Jari Aalto726f6381996-08-26 18:22:31 +0000112 return i;
113
114 return -1;
115}
116
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000117#define RESET_MAIL_FILE(i) \
118 do \
119 { \
120 mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
Jari Aaltocce855b1998-04-17 19:52:44 +0000121 mailfiles[i]->file_size = 0; \
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000122 } \
123 while (0)
124
125static void
126update_mail_file (i)
127 int i;
128{
129 char *file;
130 struct stat finfo;
131
132 file = mailfiles[i]->name;
Jari Aaltof73dda02001-11-13 17:56:06 +0000133 if (mailstat (file, &finfo) == 0)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000134 {
135 mailfiles[i]->access_time = finfo.st_atime;
136 mailfiles[i]->mod_time = finfo.st_mtime;
137 mailfiles[i]->file_size = finfo.st_size;
138 }
139 else
140 RESET_MAIL_FILE (i);
141}
142
Jari Aalto726f6381996-08-26 18:22:31 +0000143/* Add this file to the list of remembered files and return its index
144 in the list of mail files. */
145static int
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000146add_mail_file (file, msg)
147 char *file, *msg;
Jari Aalto726f6381996-08-26 18:22:31 +0000148{
149 struct stat finfo;
150 char *filename;
151 int i;
152
153 filename = full_pathname (file);
Jari Aaltoe8ce7751997-09-22 20:22:27 +0000154 i = find_mail_file (filename);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000155 if (i >= 0)
Jari Aalto726f6381996-08-26 18:22:31 +0000156 {
Jari Aaltof73dda02001-11-13 17:56:06 +0000157 if (mailstat (filename, &finfo) == 0)
Jari Aalto726f6381996-08-26 18:22:31 +0000158 {
159 mailfiles[i]->mod_time = finfo.st_mtime;
160 mailfiles[i]->access_time = finfo.st_atime;
Jari Aaltocce855b1998-04-17 19:52:44 +0000161 mailfiles[i]->file_size = finfo.st_size;
Jari Aalto726f6381996-08-26 18:22:31 +0000162 }
163 free (filename);
164 return i;
165 }
166
167 i = mailfiles_count++;
168 mailfiles = (FILEINFO **)xrealloc
169 (mailfiles, mailfiles_count * sizeof (FILEINFO *));
170
171 mailfiles[i] = (FILEINFO *)xmalloc (sizeof (FILEINFO));
172 mailfiles[i]->name = filename;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000173 mailfiles[i]->msg = msg ? savestring (msg) : (char *)NULL;
174 update_mail_file (i);
Jari Aalto726f6381996-08-26 18:22:31 +0000175 return i;
176}
177
178/* Reset the existing mail files access and modification times to zero. */
179void
180reset_mail_files ()
181{
182 register int i;
183
184 for (i = 0; i < mailfiles_count; i++)
185 {
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000186 RESET_MAIL_FILE (i);
Jari Aalto726f6381996-08-26 18:22:31 +0000187 }
188}
189
190/* Free the information that we have about the remembered mail files. */
191void
192free_mail_files ()
193{
194 register int i;
195
196 for (i = 0; i < mailfiles_count; i++)
197 {
198 free (mailfiles[i]->name);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000199 FREE (mailfiles[i]->msg);
Jari Aalto726f6381996-08-26 18:22:31 +0000200 free (mailfiles[i]);
201 }
202
203 if (mailfiles)
204 free (mailfiles);
205
206 mailfiles_count = 0;
207 mailfiles = (FILEINFO **)NULL;
208}
209
210/* Return non-zero if FILE's mod date has changed and it has not been
211 accessed since modified. */
212static int
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000213file_mod_date_changed (i)
214 int i;
Jari Aalto726f6381996-08-26 18:22:31 +0000215{
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000216 time_t mtime;
Jari Aalto726f6381996-08-26 18:22:31 +0000217 struct stat finfo;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000218 char *file;
Jari Aalto726f6381996-08-26 18:22:31 +0000219
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000220 file = mailfiles[i]->name;
221 mtime = mailfiles[i]->mod_time;
Jari Aalto726f6381996-08-26 18:22:31 +0000222
Jari Aaltof73dda02001-11-13 17:56:06 +0000223 if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000224 return (mtime != finfo.st_mtime);
Jari Aalto726f6381996-08-26 18:22:31 +0000225
226 return (0);
227}
228
229/* Return non-zero if FILE's access date has changed. */
230static int
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000231file_access_date_changed (i)
232 int i;
Jari Aalto726f6381996-08-26 18:22:31 +0000233{
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000234 time_t atime;
Jari Aalto726f6381996-08-26 18:22:31 +0000235 struct stat finfo;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000236 char *file;
Jari Aalto726f6381996-08-26 18:22:31 +0000237
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000238 file = mailfiles[i]->name;
239 atime = mailfiles[i]->access_time;
Jari Aalto726f6381996-08-26 18:22:31 +0000240
Jari Aaltof73dda02001-11-13 17:56:06 +0000241 if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000242 return (atime != finfo.st_atime);
Jari Aalto726f6381996-08-26 18:22:31 +0000243
244 return (0);
245}
246
247/* Return non-zero if FILE's size has increased. */
248static int
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000249file_has_grown (i)
250 int i;
Jari Aalto726f6381996-08-26 18:22:31 +0000251{
Jari Aaltocce855b1998-04-17 19:52:44 +0000252 off_t size;
Jari Aalto726f6381996-08-26 18:22:31 +0000253 struct stat finfo;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000254 char *file;
Jari Aalto726f6381996-08-26 18:22:31 +0000255
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000256 file = mailfiles[i]->name;
257 size = mailfiles[i]->file_size;
Jari Aalto726f6381996-08-26 18:22:31 +0000258
Jari Aaltof73dda02001-11-13 17:56:06 +0000259 return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size));
Jari Aalto726f6381996-08-26 18:22:31 +0000260}
261
Jari Aalto726f6381996-08-26 18:22:31 +0000262/* Take an element from $MAILPATH and return the portion from
263 the first unquoted `?' or `%' to the end of the string. This is the
264 message to be printed when the file contents change. */
265static char *
266parse_mailpath_spec (str)
267 char *str;
268{
269 char *s;
270 int pass_next;
271
272 for (s = str, pass_next = 0; s && *s; s++)
273 {
274 if (pass_next)
275 {
276 pass_next = 0;
277 continue;
278 }
279 if (*s == '\\')
280 {
281 pass_next++;
282 continue;
283 }
284 if (*s == '?' || *s == '%')
Jari Aalto28ef6c32001-04-06 19:14:31 +0000285 return s;
Jari Aalto726f6381996-08-26 18:22:31 +0000286 }
287 return ((char *)NULL);
288}
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000289
290char *
291make_default_mailpath ()
292{
Jari Aaltob80f6442004-07-27 13:29:18 +0000293#if defined (DEFAULT_MAIL_DIRECTORY)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000294 char *mp;
295
Jari Aaltocce855b1998-04-17 19:52:44 +0000296 get_current_user_info ();
Jari Aaltof73dda02001-11-13 17:56:06 +0000297 mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000298 strcpy (mp, DEFAULT_MAIL_DIRECTORY);
Jari Aaltod166f041997-06-05 14:59:13 +0000299 mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
300 strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000301 return (mp);
Jari Aaltob80f6442004-07-27 13:29:18 +0000302#else
303 return ((char *)NULL);
304#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000305}
306
Jari Aalto726f6381996-08-26 18:22:31 +0000307/* Remember the dates of the files specified by MAILPATH, or if there is
308 no MAILPATH, by the file specified in MAIL. If neither exists, use a
309 default value, which we randomly concoct from using Unix. */
310void
311remember_mail_dates ()
312{
313 char *mailpaths;
314 char *mailfile, *mp;
315 int i = 0;
316
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000317 mailpaths = get_string_value ("MAILPATH");
318
319 /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
320 if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
321 {
322 add_mail_file (mailpaths, (char *)NULL);
323 return;
324 }
325
326 if (mailpaths == 0)
327 {
328 mailpaths = make_default_mailpath ();
Jari Aaltob80f6442004-07-27 13:29:18 +0000329 if (mailpaths)
330 {
331 add_mail_file (mailpaths, (char *)NULL);
332 free (mailpaths);
333 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000334 return;
335 }
336
Jari Aalto726f6381996-08-26 18:22:31 +0000337 while (mailfile = extract_colon_unit (mailpaths, &i))
338 {
339 mp = parse_mailpath_spec (mailfile);
340 if (mp && *mp)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000341 *mp++ = '\0';
342 add_mail_file (mailfile, mp);
Jari Aalto726f6381996-08-26 18:22:31 +0000343 free (mailfile);
344 }
Jari Aalto726f6381996-08-26 18:22:31 +0000345}
346
347/* check_mail () is useful for more than just checking mail. Since it has
348 the paranoids dream ability of telling you when someone has read your
349 mail, it can just as easily be used to tell you when someones .profile
350 file has been read, thus letting one know when someone else has logged
351 in. Pretty good, huh? */
352
353/* Check for mail in some files. If the modification date of any
354 of the files in MAILPATH has changed since we last did a
355 remember_mail_dates () then mention that the user has mail.
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000356 Special hack: If the variable MAIL_WARNING is non-zero and the
Jari Aalto726f6381996-08-26 18:22:31 +0000357 mail file has been accessed since the last time we remembered, then
358 the message "The mail in <mailfile> has been read" is printed. */
359void
360check_mail ()
361{
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000362 char *current_mail_file, *message;
363 int i, use_user_notification;
364 char *dollar_underscore, *temp;
Jari Aalto726f6381996-08-26 18:22:31 +0000365
366 dollar_underscore = get_string_value ("_");
Jari Aalto726f6381996-08-26 18:22:31 +0000367 if (dollar_underscore)
368 dollar_underscore = savestring (dollar_underscore);
369
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000370 for (i = 0; i < mailfiles_count; i++)
Jari Aalto726f6381996-08-26 18:22:31 +0000371 {
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000372 current_mail_file = mailfiles[i]->name;
Jari Aalto726f6381996-08-26 18:22:31 +0000373
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000374 if (*current_mail_file == '\0')
375 continue;
376
377 if (file_mod_date_changed (i))
Jari Aalto726f6381996-08-26 18:22:31 +0000378 {
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000379 int file_is_bigger;
Jari Aalto726f6381996-08-26 18:22:31 +0000380
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000381 use_user_notification = mailfiles[i]->msg != (char *)NULL;
Jari Aaltob80f6442004-07-27 13:29:18 +0000382 message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_");
Jari Aalto726f6381996-08-26 18:22:31 +0000383
Jari Aalto726f6381996-08-26 18:22:31 +0000384 bind_variable ("_", current_mail_file);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000385
Jari Aalto726f6381996-08-26 18:22:31 +0000386#define atime mailfiles[i]->access_time
387#define mtime mailfiles[i]->mod_time
388
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000389 /* Have to compute this before the call to update_mail_file, which
Jari Aalto726f6381996-08-26 18:22:31 +0000390 resets all the information. */
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000391 file_is_bigger = file_has_grown (i);
Jari Aalto726f6381996-08-26 18:22:31 +0000392
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000393 update_mail_file (i);
Jari Aalto726f6381996-08-26 18:22:31 +0000394
395 /* If the user has just run a program which manipulates the
396 mail file, then don't bother explaining that the mail
397 file has been manipulated. Since some systems don't change
398 the access time to be equal to the modification time when
399 the mail in the file is manipulated, check the size also. If
400 the file has not grown, continue. */
Jari Aaltob80f6442004-07-27 13:29:18 +0000401 if ((atime >= mtime) && !file_is_bigger)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000402 continue;
Jari Aalto726f6381996-08-26 18:22:31 +0000403
404 /* If the mod time is later than the access time and the file
405 has grown, note the fact that this is *new* mail. */
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000406 if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
Jari Aaltob80f6442004-07-27 13:29:18 +0000407 message = _("You have new mail in $_");
Jari Aalto726f6381996-08-26 18:22:31 +0000408#undef atime
409#undef mtime
410
Jari Aaltof73dda02001-11-13 17:56:06 +0000411 if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES))
Jari Aalto726f6381996-08-26 18:22:31 +0000412 {
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000413 puts (temp);
414 free (temp);
Jari Aalto726f6381996-08-26 18:22:31 +0000415 }
416 else
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000417 putchar ('\n');
Jari Aalto726f6381996-08-26 18:22:31 +0000418 }
419
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000420 if (mail_warning && file_access_date_changed (i))
Jari Aalto726f6381996-08-26 18:22:31 +0000421 {
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000422 update_mail_file (i);
Jari Aaltob80f6442004-07-27 13:29:18 +0000423 printf (_("The mail in %s has been read\n"), current_mail_file);
Jari Aalto726f6381996-08-26 18:22:31 +0000424 }
Jari Aalto726f6381996-08-26 18:22:31 +0000425 }
Jari Aalto726f6381996-08-26 18:22:31 +0000426
427 if (dollar_underscore)
428 {
429 bind_variable ("_", dollar_underscore);
430 free (dollar_underscore);
431 }
432 else
433 unbind_variable ("_");
434}