static char rcsid[] = "@(#)$Id: from.c,v 1.55 2006/05/07 08:35:31 hurtta Exp $";
/******************************************************************************
* The Elm (ME+) Mail System - $Revision: 1.55 $ $State: Exp $
*
* Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI>
* (was hurtta+elm@ozone.FMI.FI)
******************************************************************************
* The Elm Mail System
*
* Copyright (c) 1988-1992 USENET Community Trust
* Copyright (c) 1986,1987 Dave Taylor
*****************************************************************************/
/** print out whom each message is from in the pending folder or specified
one, including a subject line if available.
**/
#include "elmutil.h"
#include "mboxlib.h"
#include "s_from.h"
#include "s_elm.h"
DEBUG_VAR(Debug,__FILE__,"util");
static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str)
char *str;
{
return (unsigned char *)str;
}
#ifdef PWDINSYS
# include <sys/pwd.h>
#else
# include <pwd.h>
#endif
#include <sys/stat.h>
#define LINEFEED (char) 10
#define metachar(c) (c == '=' || c == '+' || c == '%')
/* ancient wisdom */
#ifndef TRUE
# define TRUE 1
# define FALSE 0
#endif
/* for explain(), positive and negative */
#define POS 1
#define NEG 0
/* defines for selecting messages by Status: */
#define NEW_MSG 0x1
#define OLD_MSG 0x2
#define READ_MSG 0x4
#define UNKNOWN 0x8
#define ALL_MSGS 0xf
/* exit statuses */
#define EXIT_SELECTED 0 /* Selected messages present */
#define EXIT_MAIL 1 /* Mail present, but no selected messages */
#define EXIT_NO_MAIL 2 /* No messages at all */
#define EXIT_ERROR 3 /* Error */
int number = FALSE, /* should we number the messages?? */
veryquiet = FALSE,/* should we be print any output at all? */
quiet = FALSE, /* only print mail/no mail and/or summary */
selct = FALSE, /* select these types of messages */
tidy = FALSE, /* tidy output with long 'from's */
summarize = FALSE,/* print a summary of how many messages of each type */
verbose = FALSE; /* and should we prepend a header? */
char infile[SLEN]; /* current file name */
static char * explain P_((
int selection,
int how_to_say));
static void usage P_((char *prog));
static void print_help P_((void));
static char * whos_mail P_((char *filename,
char *realname));
static void read_headers P_((struct folder_info *folder,
struct read_folder_state * read_state_ptr,
int user_mailbox,
int *total_msgs,
int *selected,
char *realname));
int main P_((int argc, char *argv[]));
int main(argc, argv)
int argc;
char *argv[];
{
char *cp;
int multiple_files = FALSE, output_files = FALSE;
int no_files, c;
int total_msgs = 0, selected_msgs = 0;
char realname[SLEN];
int df = give_dt_enumerate_as_int(&def_folder_status);
extern int optind;
extern char *optarg;
#if DEBUG
init_debugfile("FRM");
#endif
locale_init();
user_init();
strfcpy(realname,username,sizeof realname);
init_mboxlib();
init_defaults();
read_rc_file(0);
/*
* check the first character of the command basename to
* use as the selection criterion.
*/
cp = argv[0] + strlen(argv[0]) - 1;
while (cp != argv[0] && cp[-1] != '/')
cp--;
switch (*cp) {
case 'n': selct |= NEW_MSG; break;
case 'u':
case 'o': selct |= OLD_MSG; break;
case 'r': selct |= READ_MSG; break;
}
while ((c = getopt(argc, argv, "hnQqSs:tvd:X:")) != EOF)
switch (c) {
case 'd' :
#if DEBUG
set_debugging(optarg);
#else
lib_error(CATGETS(elm_msg_cat, FromSet, FromArgsIngoringDebug,
"Warning: system created without debugging enabled - request ignored\n"));
#endif
break;
case 'n': number++; break;
case 'Q': veryquiet++; break;
case 'q': quiet++; break;
case 'S': summarize++; break;
case 't': tidy++; break;
case 'v': verbose++; break;
case 's': if (optarg[1] == '\0') {
switch (*optarg) {
case 'n':
case 'N': selct |= NEW_MSG; break;
case 'o':
case 'O':
case 'u':
case 'U': selct |= OLD_MSG; break;
case 'r':
case 'R': selct |= READ_MSG; break;
default: usage(argv[0]);
exit(EXIT_ERROR);
}
} else if (istrcmp(optarg,"new") == 0)
selct |= NEW_MSG;
else if (istrcmp(optarg,"old") == 0)
selct |= OLD_MSG;
else if (istrcmp(optarg,"unread") == 0)
selct |= OLD_MSG;
else if (istrcmp(optarg,"read") == 0)
selct |= READ_MSG;
else {
usage(argv[0]);
exit(EXIT_ERROR);
}
break;
case 'h': print_help();
exit(EXIT_ERROR);
case 'X' :
#ifdef REMOTE_MBX
if (!set_transaction_file(optarg))
exit(1);
#endif
break;
case '?': usage(argv[0]);
lib_error(CATGETS(elm_msg_cat,
FromSet,FromForMoreInfo,
"For more information, type \"%s -h\"\n"),
argv[0]);
exit(EXIT_ERROR);
}
if (quiet && verbose) {
lib_error(CATGETS(elm_msg_cat,FromSet,FromNoQuietVerbose,
"Can't have quiet *and* verbose!\n"));
exit(EXIT_ERROR);
}
if (veryquiet) {
if (freopen("/dev/null", "w", stdout) == NULL) {
lib_error(CATGETS(elm_msg_cat,FromSet,FromCantOpenDevNull,
"Can't open /dev/null for \"very quiet\" mode.\n"));
exit(EXIT_ERROR);
}
}
elm_sfprintf(version_buff, sizeof version_buff,
FRM("%s PL%s"), VERSION, PATCHLEVEL);
user_init();
#ifdef DEBUG
{
int d = panic_dprint("\n\
======================================================\n\
Debug output of the FRM program (version %s).\n",
version_buff);
if (d >= 50) {
panic_dprint("WARNING: Edit manually out sensitive information from that file!\n");
lib_error(FRM("WARNING: Debug file may include passwords -- edit it!"));
#if POLL_METHOD
wait_for_timeout(5+sleepmsg);
#else
sleep(5+sleepmsg);
#endif
}
}
#endif
/* default is all messages */
if (selct == 0 || selct == (NEW_MSG|OLD_MSG|READ_MSG))
selct = ALL_MSGS;
infile[0] = '\0';
if (no_files = (optind == argc)) { /* assignment intentional */
char * default_val = give_dt_estr_as_str(&defaultfile_e,"incoming-mailbox");
if (!default_val)
exit(1);
strfcpy(infile, default_val, sizeof infile);
optind -= 1; /* ensure one pass through loop */
}
multiple_files = (argc - optind > 1);
for ( ; optind < argc; optind++) {
struct folder_info *folder = NULL;
struct read_folder_state * read_state_ptr = NULL;
CONST char *msg;
enum sessionlock_mode m;
/* copy next argument into infile */
if (multiple_files) {
strfcpy(infile, argv[optind], sizeof infile);
printf("%s%s: \n", output_files++ > 0 ? "\n":"", infile);
}
else if (infile[0] == '\0')
strfcpy(infile, argv[optind], sizeof infile);
if (metachar(infile[0])) {
SIGDPRINT(Debug,4, (&Debug,
"%s => ",infile));
if (expand(infile, sizeof infile) == 0) {
lib_error(CATGETS(elm_msg_cat,
FromSet,FromCouldntExpandFilename,
"%s: couldn't expand filename %s!\n"),
argv[0], infile);
exit(EXIT_ERROR);
}
SIGDPRINT(Debug,4, (&Debug,
"%s (expanded)\n",infile));
} else if (NULL == strpbrk(infile,"/:@") &&
access(infile,ACCESS_EXISTS) == -1 &&
!no_files) {
SIGDPRINT(Debug,4, (&Debug,
"%s => ",infile));
/* only try mailhome if file not found */
strfcpy(infile,mailhome,sizeof infile);
strfcat(infile,argv[optind],sizeof infile);
SIGDPRINT(Debug,4, (&Debug,
"%s (as user)\n",infile));
if (access(infile,ACCESS_EXISTS) == -1) {
lib_error(CATGETS(elm_msg_cat,
FromSet,FromCouldntOpenFolderOrMailbox,
"Couldn't open folder \"%s\" or mailbox \"%s\".\n"),
argv[optind], infile);
continue; /* let's try the next file */
}
} else {
SIGDPRINT(Debug,4, (&Debug,
"%s (no expansion)\n",infile));
}
if (strncmp(infile, mailhome, strlen(mailhome)) == 0) {
/*
* if this is a mailbox, use the identity of the mailbox owner.
* this affects the "To" processing.
*/
strfcpy(username, infile+strlen(mailhome), sizeof username);
/*
* then get full username
*/
if((cp = get_full_name(username)) != NULL)
strfcpy(full_username, cp,sizeof full_username);
else
strfcpy(full_username, username, sizeof full_username);
} else {
/* reset values */
user_init();
}
/* check if this is a mailbox or not, and attempt to open it */
folder = enter_new_folder(infile);
if (!folder)
continue;
m = SESSIONLOCK_NONE;
if ((selct == NEW_MSG) &&
FOLDER_FILE == get_folder_mode(folder) &&
FOLDER_STATUS_NEW != df) {
lib_error(CATGETS(elm_msg_cat,
FromSet,FromNotMailbox,
"WARNING: %s is folder, not mailbox!\n Delivery of new mail to folder when it is open may corrupt it.\n"),
infile);
m = SESSIONLOCK_NONE_CHECKNEW;
}
if (!sessionlock_folder(folder,m)) {
CONST char * X = folder_type(folder);
lib_error(CATGETS(elm_msg_cat,FromSet,FromCouldntOpenX,
"Couldn't open %s \"%s\".\n"), X,infile);
goto clean;
}
if (!prepare_read_folder(folder,PREPARE_NOLOCK,&read_state_ptr)) {
goto clean;
}
if ((msg = is_forwarded_folder(folder,read_state_ptr)) != NULL) {
printf(catgets(elm_msg_cat, ElmSet, ElmMailBeingForwardTo,
"Mail being forwarded to %s"),
msg);
putchar('\n');
} else {
int user_mailbox =
strncmp(infile, mailhome, strlen(mailhome)) == 0;
read_headers(folder,read_state_ptr,
user_mailbox,
&total_msgs, &selected_msgs,realname);
/*
* we know what to say; now we have to figure out *how*
* to say it!
*/
/* no messages at all? */
if (total_msgs == 0) {
if (user_mailbox)
elm_fprintf(stdout,
CATGETS(elm_msg_cat,FromSet,FromStringNoMail,
"%s no mail.\n"),
whos_mail(infile, realname));
else
if (!summarize) {
if (get_folder_mode(folder) & FOLDER_MBOX)
elm_fprintf(stdout,
CATGETS(elm_msg_cat,FromSet,
FromNoMesgInMailbox,
"No messages in that mailbox!\n"));
else
elm_fprintf(stdout,
CATGETS(elm_msg_cat,FromSet,
FromNoMesgInFolder,
"No messages in that folder!\n"));
}
}
else
/* no selected messages then? */
if (selected_msgs == 0) {
if (user_mailbox)
printf(catgets(elm_msg_cat,FromSet,FromNoExplainMail,
"%s no %s mail.\n"),
whos_mail(infile, realname),
explain(selct,NEG));
else
if (!summarize) {
if (get_folder_mode(folder) & FOLDER_MBOX)
printf(catgets(elm_msg_cat,
FromSet,FromNoExplainMessagesMbox,
"No %s messages in that mailbox.\n"),
explain(selct,NEG));
else
printf(catgets(elm_msg_cat,
FromSet,FromNoExplainMessages,
"No %s messages in that folder.\n"),
explain(selct,NEG));
}
}
else
/* there's mail, but we just want a one-liner */
if (quiet && !summarize) {
if (user_mailbox)
printf(catgets(elm_msg_cat,
FromSet,FromStringStringMail,
"%s %s mail.\n"),
whos_mail(infile,realname),
explain(selct,POS));
else
printf(catgets(elm_msg_cat,FromSet,
FromThereAreMesg,
"There are %s messages in that folder.\n"),
explain(selct,POS));
}
}
end_read_folder(folder,&read_state_ptr,0);
clean:
/* FREE folder */
leave_old_folder(&folder,CLOSE_NORMAL);
} /* for each arg */
/*
* return "shell true" (0) if there are selected messages;
* 1 if there are messages, but no selected messages;
* 2 if there are no messages at all.
*/
if (selected_msgs > 0)
exit(EXIT_SELECTED);
else if (total_msgs > 0)
exit(EXIT_MAIL);
else
exit(EXIT_NO_MAIL);
/*NOTREACHED*/
}
static int parse_header_routine P_((struct folder_info *folder,
READ_STATE read_state_ptr,
struct header_rec *entry,
header_list_ptr parsed_headers ));
static int parse_header_routine(folder,read_state_ptr,entry,parsed_headers)
struct folder_info *folder;
READ_STATE read_state_ptr;
struct header_rec *entry;
header_list_ptr parsed_headers;
{
header_list_ptr tmphdr;
/* copy_envelope_folder() may have set entry->content_length */
/* copy_envelope_folder() have set entry->header_charset */
if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
"From"))) {
if (entry->from)
free_addr_items(entry->from);
entry->from =
break_down_address(tmphdr->body,
!(entry->status & NOHDRENCODING),
entry->header_charset);
}
if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
"To"))) {
if (entry->to)
free_addr_items(entry->to);
entry->to =
break_down_address(tmphdr->body,
!(entry->status & NOHDRENCODING),
entry->header_charset);
}
if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
"Cc"))) {
if (entry->cc)
free_addr_items(entry->cc);
entry->cc =
break_down_address(tmphdr->body,
!(entry->status & NOHDRENCODING),
entry->header_charset);
}
if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
"Status"))) {
strfcpy(entry->mailx_status, tmphdr->body, WLEN);
}
if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
"Subject"))) {
if (entry->subject)
free_string(&(entry->subject));
entry->subject =
hdr_to_string(HDR_TEXT,
tmphdr->body,
entry->header_charset,
!(entry->status & NOHDRENCODING));
} else if (!entry->subject)
entry->subject = new_string(display_charset);
return 1;
}
static int parse_body_routine P_((struct folder_info *folder,
READ_STATE read_state_ptr,
struct header_rec *entry,
header_list_ptr parsed_headers,
struct counter_data *counter
));
static int parse_body_routine(folder,read_state_ptr,entry,parsed_headers,
counter)
struct folder_info *folder;
READ_STATE read_state_ptr;
struct header_rec *entry;
header_list_ptr parsed_headers;
struct counter_data *counter;
{
char *buffer;
int len;
long content_remaining = -1L;
content_remaining = entry->content_length;
reset_body:
while (copy_body_folder(folder,read_state_ptr,
&buffer,&len,&content_remaining)) {
if (buffer)
free(buffer);
}
if (!copy_envelope_end_folder(folder,read_state_ptr)) {
if (entry->content_length >= 0L) {
entry->content_length = -1L;
if (copy_envelope_reset_body(folder,read_state_ptr,
&content_remaining))
goto reset_body;
}
SIGDPRINT(Debug,10, (&Debug,
"-- Message parsing FAILED.\n"));
return 0;
}
SIGDPRINT(Debug,10, (&Debug,
"-- Message parsed.\n"));
entry->body_parsed = 1;
return 1;
}
static void read_headers(folder,read_state_ptr,
user_mailbox, total_msgs, selected, realname)
struct folder_info *folder;
struct read_folder_state * read_state_ptr;
int user_mailbox;
int *total_msgs;
int *selected;
char *realname;
{
/** Read the headers, output as found. User-Mailbox is to guarantee
that we get a reasonably sensible message from the '-v' option
**/
struct header_rec hdr;
register int count = 0, selected_msgs = 0;
int i,status;
int summary[ALL_MSGS];
bzero((void *)&hdr, sizeof hdr);
hdr.mbx_info = NULL;
hdr.binary = 0;
for (i=0; i<ALL_MSGS; i++)
summary[i] = 0;
while(copy_envelope_folder(folder,read_state_ptr,
&hdr,
parse_header_routine,parse_body_routine,
NULL) > 0) {
status = 0;
DPRINT(Debug,4,(&Debug,"Message status %0x (%s):",
hdr.status,hdr.mailx_status));
if (index(hdr.mailx_status, 'R') != NULL) {
hdr.status &= ~(NEW | UNREAD);
DPRINT(Debug,4,(&Debug," -NEW -UNREAD"));
}
else if (index(hdr.mailx_status,'O') != NULL) {
hdr.status &= ~NEW;
hdr.status |= UNREAD;
DPRINT(Debug,4,(&Debug," -NEW +UNREAD"));
}
if (index(hdr.mailx_status, 'r') != NULL) {
hdr.status |= REPLIED;
DPRINT(Debug,4,(&Debug," +REPLIED"));
}
DPRINT(Debug,4,(&Debug," "));
/* NOTE: In frm terms OLD is same as UNREAD !! */
if (hdr.status & NEW) {
status |= NEW_MSG;
DPRINT(Debug,4,(&Debug," New"));
} else if (hdr.status & UNREAD) {
status |= OLD_MSG;
DPRINT(Debug,4,(&Debug," Old (= unread)"));
}
if (! (hdr.status & UNREAD)) {
status |= READ_MSG;
DPRINT(Debug,4,(&Debug," Read"));
}
DPRINT(Debug,4,(&Debug," = %s\n",explain(status,POS)));
count++;
summary[status]++;
if ((status & selct) != 0) {
/* what a mess! */
if (verbose && selected_msgs == 0) {
if (user_mailbox) {
if (selct == ALL_MSGS)
printf(catgets(elm_msg_cat,FromSet,FromFollowingMesg,
"%s the following mail messages:\n"),
whos_mail(infile, realname));
else
printf(catgets(elm_msg_cat,FromSet,FromStringStringMail,
"%s %s mail.\n"), whos_mail(infile, realname),
explain(selct,POS));
}
else
printf(catgets(elm_msg_cat,
FromSet,FromFolderContainsFollowing,
"Folder contains the following %s messages:\n"),
explain(selct,POS));
}
selected_msgs++;
if (! quiet) {
int lenWho = 0;
struct string * who = NULL;
int used_to_line = DisplayAddress(&hdr,&who);
if (used_to_line) {
struct string * buffer = new_string2(display_charset,
s2us("To "));
struct string * temp = cat_strings(buffer,who,1);
free_string(&who);
who = temp;
free_string(&buffer);
}
/***
* Print subject on next line if the Who part blows
* the alignment
***/
if (tidy)
lenWho = string_len(who);
else
lenWho = 0; /* forces op on same line */
if (number)
elm_fprintf(stdout,
FRM("%3d: %-20S%c%*s%S\n"),
count, who,
lenWho > 20 ? '\n' : ' ',
lenWho > 20 ? 27 : 1, "",
hdr.subject);
else
elm_fprintf(stdout,
FRM("%-20S%c%*s%S\n"),
who,
lenWho > 20 ? '\n' : ' ',
lenWho > 20 ? 22 : 1, "",
hdr.subject);
}
}
free_rec_mbx_info(&hdr);
if (hdr.header_error)
free_header_errors(& (hdr.header_error));
bzero((void *)&hdr, sizeof hdr); /* memory leak ... */
hdr.binary = 0;
}
*selected = selected_msgs;
*total_msgs = count;
/* print a message type summary */
if (summarize) {
int output=FALSE, unknown = 0;
if (user_mailbox)
printf("%s ", whos_mail(infile, realname));
else
elm_fprintf(stdout,CATGETS(elm_msg_cat,FromSet,FromFolderContains,
"Folder contains "));
for (i=0; i<ALL_MSGS; i++) {
if (summary[i] > 0) {
if (output)
printf(", ");
switch (i) {
case NEW_MSG:
case OLD_MSG:
case READ_MSG:
printf("%d %s ",summary[i], explain(i,POS));
if (summary[i] == 1)
printf("%s",catgets(elm_msg_cat,
FromSet,FromMessage,"message"));
else
printf("%s",catgets(elm_msg_cat,
FromSet,FromMessagePlural,"messages"));
output = TRUE;
break;
default:
unknown += summary[i];
}
}
}
if (unknown)
{
printf("%d ",unknown);
if (unknown == 1)
printf("%s",catgets(elm_msg_cat,
FromSet,FromMessage,"message"));
else
printf("%s",catgets(elm_msg_cat,
FromSet,FromMessagePlural,"messages"));
printf("%s "," of unknown status");
output = TRUE;
}
if (output)
printf(".\n");
else
elm_fprintf(stdout,
CATGETS(elm_msg_cat,FromSet,FromNoMessages,
"no messages.\n"));
}
}
/*
* Return an appropriate string as to whom this mailbox belongs.
*/
char * whos_mail(filename,
realname)
char *filename;
char *realname;
{
static char whos_who[SLEN];
char *mailname;
if (strncmp(filename, mailhome, strlen(mailhome)) == 0) {
mailname = filename + strlen(mailhome);
if (*mailname == '/')
mailname++;
if (strcmp(mailname, realname) == 0)
strfcpy(whos_who,catgets(elm_msg_cat,
FromSet,FromYouHave,"You have"),
sizeof whos_who);
else {
strfcpy(whos_who, mailname, sizeof whos_who);
strfcat(whos_who,catgets(elm_msg_cat,FromSet,FromHas, " has"),
sizeof whos_who);
}
}
else
/* punt... */
strfcpy(whos_who,catgets(elm_msg_cat,
FromSet,FromYouHave,"You have"),
sizeof whos_who);
return whos_who;
}
static void usage(prog)
char *prog;
{
printf(catgets(elm_msg_cat,FromSet,FromUsage,
"Usage: %s [-n] [-v] [-t] [-s {new|old|read}] [filename | username] ...\n"),
prog);
}
static void print_help()
{
elm_fprintf(stdout,
CATGETS(elm_msg_cat,FromSet,FromHelpTitle,
"frm -- list from and subject lines of messages in mailbox or folder\n"));
usage("frm");
elm_fprintf(stdout,
CATGETS(elm_msg_cat,FromSet,FromHelpText,
"\noption summary:\n\
-h\tprint this help message.\n\
-n\tdisplay the message number of each message printed.\n\
-Q\tvery quiet -- no output is produced. This option allows shell\n\
\tscripts to check frm's return status without having output.\n\
-q\tquiet -- only print summaries for each mailbox or folder.\n\
-S\tsummarize the number of messages in each mailbox or folder.\n\
-s status only -- select messages with the specified status.\n\
\t'status' is one of \"new\", \"old\", \"unread\" (same as \"old\"),\n\
\tor \"read\". Only the first letter need be specified.\n\
-t\ttry to align subjects even if 'from' text is long.\n\
-v\tprint a verbose header.\n"));
}
/* explanation of messages visible after selection */
/* usage: "... has the following%s messages ...", explain(selct,POS) */
static char *
explain(selection, how_to_say)
int selection;
int how_to_say;
{
switch (selection) {
case NEW_MSG:
return catgets(elm_msg_cat,FromSet,FromNew,"new");
case OLD_MSG:
return catgets(elm_msg_cat,FromSet,FromUnread,"unread");
case READ_MSG:
return catgets(elm_msg_cat,FromSet,FromRead,"read");
case (NEW_MSG|OLD_MSG):
if (how_to_say == POS)
return catgets(elm_msg_cat,FromSet,FromNewAndUnread,
"new and unread");
else
return catgets(elm_msg_cat,FromSet,FromNewOrUnread,
"new or unread");
case (NEW_MSG|READ_MSG):
if (how_to_say == POS)
return catgets(elm_msg_cat,FromSet,FromNewAndRead,
"new and read");
else
return catgets(elm_msg_cat,FromSet,FromNewOrRead,
"new or read");
case (READ_MSG|OLD_MSG):
if (how_to_say == POS)
return catgets(elm_msg_cat,FromSet,FromReadAndUnread,
"read and unread");
else
return catgets(elm_msg_cat,FromSet,FromReadOrUnread,
"read or unread");
case ALL_MSGS:
return "";
default:
return catgets(elm_msg_cat,FromSet,FromUnknown,"unknown");
}
}
/*
* Local Variables:
* mode:c
* c-basic-offset:4
* buffer-file-coding-system: iso-8859-1
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1