~ubuntu-branches/ubuntu/trusty/bash/trusty-security

1.5.1 by Matthias Klose
Import upstream version 4.3~rc1
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
}