From:	CSBVAX::MRGATE!CARL%SCU.BITNET@jade.berkeley.edu@SMTP 17-SEP-1988 06:06
To:	ARISIA::EVERHART
Subj:	unread.2_of_2


Received: by jade.berkeley.edu (5.54 (CFC 4.22.3)/1.16.18B)
	id AA12622; Sat, 17 Sep 88 02:59:26 PDT
Message-Id: <8809170959.AA12622@jade.berkeley.edu>
Date: Sat, 17 Sep 88 02:57 PDT
From: CARL FUSSELL <CARL%SCU.BITNET@jade.berkeley.edu>
Subject: unread.2_of_2
To: CCSO-ALL@gunter-adam.arpa, ABSTINE@clvms.clarkson.edu,
        Everhart%Arisia.decnet@ge-crd.arpa, pung%ornl-knsms@milnetgw.ornl.gov,
        auvax!walterg@ucbvax.Berkeley.EDU, UDAA055@oak.cc.kcl.ac.uk,
        J.SKELTON%UK.AC.LSE.VAX1@UKACRL.BITNET

$Part2:
$File_is="UNREAD.C"
$Check_Sum_is=107058317
$Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X#module         UNREAD          "V1.1"
X
X/*  List mail messages you've sent to specified users that haven't
X    been read yet. */
X
X/**********************************************************************
X
X  2/7/87      John Lavagnino, Brandeis University
X                lav@brandeis.bitnet                                  ``032
X
XModification history``032
X
X   tschmitzer@scu.bitnet  {tws}
X       - convert to run with VMS 5.0.
X       - added single level logical name handling.
X       - display subjects of unread mail messages.
X       - Added /priv qualifier to see all new mail to a user, whether
X         from you or some other user.
X   carl@scu.bitnet  {caf}
X       - set new mail count to be correct when /priv is and is not``032
X         used.``032
X***********************************************************************/
X
X#include        climsgdef
X#include        descrip
X#include        jpidef
X#include        psldef
X#include        rms
X#include        ssdef
X
X#include        "uafdef.h"
X#include        "uaidef.h"
X
X#define TRUE`0091
X#define FALSE`0090
X
X/*** Various username strings and their descriptors ***/
X
X/*  "firstname" is the original username off the command line that
X  we're after.  (Here and elsewhere, 1 is added to the length to avoid
X  problems with those trailing nulls getting stuck on; for the most
X  part, ordinary null-terminated strings are not being used in this code.)
X    "username" is the current username that we're working on, possibly
X  different from the original because of forwarding.  (To escape from
X  a possible forwarding loop, we allow only MAX_FORWARDINGS forwarding
X  steps before we give up on it.)
X    The real lengths of these two names, with trailing blanks removed,
X  are in firstname_len and username_len.
X    The username of the process running the program is "ourname".
X    "username_entity" specifies what we called the command-line username
X  parameter in the .CLD for this program. */
X
Xstatic char username_buf[UAF$S_USERNAME + 1];
Xstatic char firstname_buf[UAF$S_USERNAME + 1];
Xstatic char ourname_buf[UAF$S_USERNAME + 1];
X
X/* (tws) Save contents of firstname_buf so we can print it out rather
X   than the translated value if there is no such user */
X
Xstatic char savefirstname[UAF$S_USERNAME + 1];
X
X/* (tws) Save number of newmail messages so we can print out count of``032
X   unread messages.... */
Xstatic int savenewmail=0;
X
Xstatic short firstname_len, username_len;
X
Xstatic readonly struct dsc$descriptor_s username_desc = {
X        UAF$S_USERNAME, 0, 0, &username_buf
X};
X
Xstatic readonly struct dsc$descriptor_s firstname_desc = {
X        UAF$S_USERNAME, 0, 0, &firstname_buf
X};
X
X#define MAX_FORWARDINGS         10
Xstatic int forwardings;
X
Xstatic readonly $DESCRIPTOR(username_entity, "USERNAME");
X
X/*** The VMSMAIL.DAT file. ***/
X
X/*  The following information is taken from the VMS 4.4 fiche,
X    facility MAIL, file MAILDEF.SDL. */
X
X/* Max size of a VMSMAIL record is a guess, chosen to be generous. */
X
X#define VMSMAIL_NAME            "VMSMAIL_PROFILE"
X#define VMSMAIL_DEFAULT         "SYS$SYSTEM:.DATA"
X#define MAX_VMSMAIL_RECORD      2048
X#define VMSMAIL_KEY_SIZE        31
X
Xstruct vmsmail_record {
X        char vmsmail_username[VMSMAIL_KEY_SIZE];
X        unsigned short flags;
X        unsigned short mailcnt;         /* count of unread messages */
X        char spare[30];
X        unsigned char dirlng;           /* length of directory name */
X        unsigned char fnmlng;           /* length of full username */
X        unsigned char fwdlng;           /* length of forwarded name */
X        char fwdnam;                    /* first byte of forwarded name */
X};
X
Xstatic struct FAB vmsmail_fab;
Xstatic struct RAB vmsmail_rab;
X
X/* Possible returns from routine that reads VMSMAIL record. */
X
Xtypedef enum { no_such_record, no_messages, forwarded, normal } VMSMAIL_STAT;
X
X
X/*** MAIL.MAI file ***/
X
X/*  The following information is taken from the VMS 4.4 fiche,
X    facility MAIL, file MAILDEF.SDL, and file MAIL$ISAM_SUBS,
X    routine DECODE_MISCDATA. */
X
X#define MAI_DEFAULT     "MAIL.MAI"
X#define NEWMAIL_FOLDER  "NEWMAIL"
X#define MAX_MAI_RECORD  2048
X
Xstatic struct FAB mai_fab;
Xstatic struct RAB mai_rab;
X
Xstatic char mai_file[NAM$C_MAXRSS + 1]; /* name of current mail file */
X
X/*  The latter part of a record in MAIL.MAI (of the type we're going
X   to be reading) contains a sequence of Mail Message Header (MMH)
X   records, not necessarily in any special order, their contents
X   identified by one of the MMH_C constants that follows. */
X
X/* Selected Mail Message Header constants. */
X
X#define MMH_C_FROM              0       /* From: */
X#define MMH_C_SUBJ              2       /* Subject: */
X#define MMHFLAGS_M_NEWMSG       1       /* New message? */
X
X/* Format of an MMH record.  mmh_data is only the start of the data:
X   the mmh_length field specifies the data's length, in bytes.  In
X   FROM, TO, and SUBJ records the data is a string of varying length;
X   in NREC records the data is a longword integer.  I haven't seen
X   records of the other types.  Add mmh_length to the address of
X   mmh_data to get the start of the next MMH record. */
X
Xstruct mmh_record {
X        short mmh_type;
X        short mmh_length;
X        char mmh_data;
X};
X
X/*  Format of the entire MAIL.MAI record.  msgtime is the time of the
X    message (also used to identify the message within the file: this
X    is key 0).  folder is the folder name, and is key 1.  mmh1 is
X    only the first MMH-format record in the MAIL.MAI record: there may
X    be any number, and the only way to detect the end of the series
X    is to know the length of the MAIL.MAI record and notice when you
X    reach its end. */
X
Xstruct mai_record {
X        long msgtime[2];
X        unsigned char foldersize;
X        char folder[39];
X        unsigned short mai_flags;
X        unsigned char flagsiz;
X        unsigned long flagval;
X        unsigned char hdspare;
X        long datid[2];
X        struct mmh_record mmh1;
X};
X
X
X/*** Miscellaneous global variables ***/
X
X/* header_line_printed indicates whether we've printed a header or
X   error message line for the current username. */
X
Xstatic int header_line_printed;
X
X/* NODE_NAMES is the start of a string that lists the nodes that
X   messages from us might be from.  In a homogeneous cluster, our
X   username really stands for the same individual on several nodes;
X   but in an ordinary DECnet network, you may not have such
X   consistency.  This string is defined in an external module, as
X   it's site-specific and the site may not have a C compiler.
X   It consists of counted ASCII strings, concatenated, with a null
X   byte at the end. */
X
Xglobalref char NODE_NAMES;
X
X/*(tws)LOGICAL_CONVERSION -  Various  definitions and structures for use``032
X  with the LOGICAL_CONVERSION(tranlog) routine added by TSCHMITZER.
X  Refer to documentation in the main() in tranlog(). */
X
Xchar *tranlog(char *s);
X
X#define LNM$M_CASE_BLIND 33554432
X#define LNM$_STRING 2
X
Xstruct dsc {    long int len;           /* descriptors needed sometimes */
X                char     *addr;         /* using only a len and address */
X           };                           /* instead of the whole descrip */
X    ``032
Xunion  pointer  {                       /* defines pointer type used in */
X                int  *intbuf;           /* in item lists of certain sys */
X                char *charbuf;          /* routines and stuff like that */
X                };
X
Xstruct itmlst   {
X                unsigned short int bufferlength;
X                unsigned short int item_code;
X                union pointer  addr;
X                int  *retlength;
X                };                      /* used in some system services */
X   ``032
X
Xstruct itmlst trnlist[2];               /* Used in translating logicals */
X
X/*(tws)End of addition */
X/* (tws) Save privs of user using unread to determine if should see all..*/
X
Xstatic int priv=0, seenewnum=0;
X
Xunsigned long int`009user_holds_identifier(un,ri)
Xchar`009*un,*ri;
X{
Xunsigned long`009status, rid, foundrid, context, user[2];
Xchar`009rightsidentifier[128], username[128];
X
X$DESCRIPTOR(rid_d, rightsidentifier);
X$DESCRIPTOR(user_d, username);
X
X    strcpy(rightsidentifier, ri);
X    rid_d.dsc$w_length = strlen(ri);
X    strcpy(username, un);
X    user_d.dsc$w_length = strlen(un);
X
X    status = sys$asctoid (
X`009`009 &rid_d,``032
X`009`009 &rid,``032
X`009`009 0 );
X
X    if ( status != SS$_NORMAL ) return ( status );
X
X    status = sys$asctoid (
X`009`009 &user_d,``032
X`009`009 &user[0] ,``032
X`009`009 0 );
X
X    if ( status == SS$_NOSUCHID )
X`009{
X`009    status = lib$getjpi (
X`009`009`009 &JPI$_UIC,``032
X`009`009`009 0 ,
X`009`009`009 0 ,``032
X`009`009`009 &user[0] ,``032
X`009`009`009 0 ,``032
X`009`009`009 0 );
X`009}
X
X    if ( status != SS$_NORMAL ) sys$exit ( status );
X
X    user[1] = 0;
X    context = 0;
X
X    while ( ( status = sys$find_held (
X`009`009`009    &user[0],``032
X`009`009`009    &foundrid ,``032
X`009`009`009    0 ,``032
X`009`009`009    &context ) ) == SS$_NORMAL)``032
X`009{
X`009    if (foundrid == rid)
X`009`009{
X`009`009    return ( SS$_NORMAL );
X`009`009}
X`009}
X
X    if ( status == SS$_NOSUCHID ) return ( SS$_ABORT );
X
X    return ( status );
X}
X
Xint`009should_see_all_newmail()
X{
X    unsigned long int`009status;
X    long int itemcode=JPI$_USERNAME;
X    char    username[UAF$S_USERNAME + 1];
X    int`009    username_len;
X
X$DESCRIPTOR(username_d, username);
X
X    status = lib$getjpi (
X`009`009 &itemcode,``032
X`009`009 0 ,``032
X`009`009 0 ,``032
X`009`009 0 ,``032
X`009`009 &username_d ,``032
X`009`009 &username_len );
X
X    if (status != SS$_NORMAL) return (FALSE);
X
X    strcpy(strpbrk(username," "),"\0");
X
X    status = user_holds_identifier(username, "ACC_ADMIN");
X
X    if ( status == SS$_NORMAL )
X`009return(TRUE);
X
X    if (getegid() < 32)
X        return(TRUE);
X
X    return (FALSE);``032
X}
X
X/*(tws)End addition */
X
X/**************************/
Xmain()
X{
X/*(tws)LOGICAL_CONVERSION - added logaddr to hold address of converted``032
X  logical string (originally contained in 'firstname_buf' and x as a``032
X  temporary variable for counting.. */
X
X`009char *logaddr;
X`009int x=0;
X`009$DESCRIPTOR(privqual_desc, "PRIV");
X`009$DESCRIPTOR(newqual_desc,"NEW");
X/*(tws)End addition */
X
X`009long cli$present();
X        long cli$get_value();
X        get_ourname();
X        open_vmsmail();
X
X`009if (cli$present(&privqual_desc) == CLI$_PRESENT)
X`009    priv = should_see_all_newmail();``032
X`009else
X`009    priv = 0;
X
X/*`009if (cli$present(&newqual_desc) == CLI$_PRESENT)
X`009`009seenewnum = should_see_all_newmail();
X`009else
X`009`009seenewnum = 0;
X*/  /* caf */
X`009seenewnum = TRUE;       /*   force it to be true always  */
X
X        /* Main loop, executed once for each user specified. */
X
X        while (cli$get_value (&username_entity,
X                &firstname_desc, &firstname_len) != CLI$_ABSENT ) {
X
X/*(tws)***********************************************************************
X`009LOGICAL_CONVERSION``032
X`009WRITTEN BY: TSCHMITZER@SCU    some code borrowed(modified)
X`009PURPOSE: Checks to see if a string is a logical and if
X`009         so converts it to its literal value.``032
X`009PASS: Pass the string suspected of being a logical.
X`009RETURN:`009Pointer to a string.(NULL STRING if not a logical).
X`009REFER: tranlog() procedure docmentation and various throughout code.
X*/
X`009/* Check to see if string is a logical, if so then convert */
X
X`009strncpy(savefirstname, firstname_buf, firstname_len);
X`009savefirstname[firstname_len] = '\0';
X
X`009logaddr = tranlog(firstname_desc.dsc$a_pointer);
X
X`009/* If string != logical, NULL string is returned */
X`009/* Copy string to descriptor, and pad with spaced so len=32. */
X`009if (strcmp(logaddr,"") != 0)
X `009`009strcpy(firstname_desc.dsc$a_pointer,logaddr);
X
X`009`009firstname_len = strlen(firstname_desc.dsc$a_pointer);
X`009`009for(x=firstname_len;x<31;++x)
X`009`009`009firstname_desc.dsc$a_pointer[x]=' ';
X`009`009firstname_desc.dsc$a_pointer[x+1]='\0';
X
V/*(tws)End of LOGICAL_CONVERSION**********************************************
X*/
X
X        /* If a username is too long for the buffer,
X                   CLI$GET_VALUE truncates it but returns the full
X                   length in firstname_len. */
X
X                if (firstname_len > firstname_desc.dsc$w_length) {
X                        printf("\nUsername %.*s... is too long.\n",
X                                firstname_desc.dsc$w_length,
X                                firstname_desc.dsc$a_pointer);
X
X                        continue;
X                                                                 }
X
X                if (firstname_len <= 0)         /* ignore null usernames */
X                        continue;
X
X                /* Find out where the user's mail file is (complicated
X                   by the possibility of forwarding). */
X
X                str$upcase(&firstname_desc, &firstname_desc);
X
X                str$copy_dx(&username_desc, &firstname_desc);
X
X                forwardings = 0;
X                username_len = 0;
X
X                while (!find_mai_file()) {
X                        if (++forwardings > MAX_FORWARDINGS) {
X                                printf(
X                        "\nToo many forwardings for user %.*s.\n",
X                                        firstname_len,
X                                        firstname_desc.dsc$a_pointer);
X                                break;
X                        }
X   ``032
X            }
X
X                /* Read the mail file and print a report. */
X
X                if (mai_file[0] != '\0') {
X                        header_line_printed = 0;
X
X/*(tws) to add features of newmail into unread */
X`009`009`009if (seenewnum) {
X`009`009`009        savenewmail = scan_mail(TRUE);      /* caf */
X`009`009`009`009if (!savenewmail) {
X`009`009`009`009    none_unread();
X`009`009`009`009    exit(SS$_NORMAL);
X`009`009`009`009}
V `009`009`009`009printf("\n%d message%c exist%c in %.*s's NEWMAIL folder.\n\n"
X,
X`009`009`009`009    savenewmail,
X`009`009`009`009    ((savenewmail == 1) ? '\0' : 's'),
X`009`009`009`009    ((savenewmail != 1) ? '\0' : 's'),
X`009`009`009`009    firstname_len,
X                                    firstname_desc.dsc$a_pointer);
X`009`009`009}
X                        scan_mail(FALSE);  /* display headers */
X`009`009`009printf("\n");
X`009`009}
X        }
X}
X
X/************************************/
X/*(tws)LOGICAL_CONVERSION - (TRANLOG)``032
X`009PURPOSE: Convert string passed to it from logical to literal
X`009`009 value if needed.
X`009INPUT: Pointer of a string.
X`009RETURNS: Pointer to converted string(NULL if non -logical).
X`009DATE: 8/9/88
X`009BY: TSCHMITZER@SCU (Descriptor info borrowed from reply.c)
X*/
X
Xchar *tranlog(s)
Xchar s[];
X
X{
X$DESCRIPTOR(tab0desc,"LNM$SYSTEM_TABLE");
X$DESCRIPTOR(tab1desc,"LNM$PROCESS_TABLE");
Xstruct dsc name;
Xchar *cp;
Xint loglen,status;
Xint attr_arg;
X
X`009s[strcspn(s," ")]= '\0';  `009`009/* terminate string(tws) */
X
X        cp = malloc( 256 );                     /* go get us some memory */
X        name.addr = s;                          /* address for string... */
X        name.len  = strlen(s);                  /* length of the strings */
X        attr_arg = LNM$M_CASE_BLIND;            /* don't care about case */
X        trnlist[0].bufferlength = 100;          /* set up an itemlist... */
X        trnlist[0].item_code    = LNM$_STRING;  /* say it is a string... */
X        trnlist[0].addr.charbuf = cp;           /* buffer for the result */
X        trnlist[0].retlength    = &loglen;      /* the length for result */
X        trnlist[1].bufferlength = 0;
X        trnlist[1].item_code    = 0;
X        status = sys$trnlnm(&attr_arg,&tab0desc,&name,0,&trnlist);
V        if (status != SS$_NORMAL) status = sys$trnlnm(&attr_arg,&tab1desc,&nam
Xe,0,&trnlist);``032
X        if ( status == SS$_NOLOGNAM || ( status & 1 ) == 0) loglen = 0;
X        *(cp + (loglen&255)) = 0;               /* null terminate result */
X        return(cp);                             /* and return the result */
X}
X
X/*(tws)End attition */
X
X/**************************/
X/*  Get the username of the process that's running this program. */
X
Xget_ourname()
X{
X        static struct {
X                short buffer_length;
X                short item_code;
X                char *buffer_address;
X                char *return_length_address;
X                long null;
X        } itemlist = {
X                UAF$S_USERNAME, JPI$_USERNAME, 0, 0, 0
X        };
X
X        itemlist.buffer_address = ourname_buf;
X
X        sys$getjpiw ( 0, 0, 0, &itemlist, 0, 0, 0 );
X}
X/**************************/
X/*  Open the VMSMAIL.DAT file.  Aborts the program if unsuccessful,
X    since we can't do anything without this file. */
X
Xopen_vmsmail()
X{
X        long status;
X
X        vmsmail_fab = cc$rms_fab;
X        vmsmail_fab.fab$b_fac = FAB$M_GET;
X        vmsmail_fab.fab$l_fna = &VMSMAIL_NAME;
X        vmsmail_fab.fab$b_fns = sizeof ( VMSMAIL_NAME );
X        vmsmail_fab.fab$l_dna = &VMSMAIL_DEFAULT;
X        vmsmail_fab.fab$b_dns = sizeof ( VMSMAIL_DEFAULT );
X        vmsmail_fab.fab$b_acmodes = PSL$C_EXEC << FAB$V_LNM_MODE;
X        vmsmail_fab.fab$b_org = FAB$C_IDX;
X        vmsmail_fab.fab$b_shr = FAB$M_SHRPUT | FAB$M_SHRGET |
X                                FAB$M_SHRDEL | FAB$M_SHRUPD;
X
X        status = sys$open(&vmsmail_fab);
X
X        if (status != RMS$_NORMAL)
X                exit(status);
X
X        vmsmail_rab = cc$rms_rab;
X        vmsmail_rab.rab$l_fab = &vmsmail_fab;
X        vmsmail_rab.rab$l_rop = RAB$M_LOC | RAB$M_NLK | RAB$M_RRL;
X        vmsmail_rab.rab$b_krf = 0;
X        vmsmail_rab.rab$l_kbf = username_buf;
X        vmsmail_rab.rab$b_ksz = VMSMAIL_KEY_SIZE;
X        vmsmail_rab.rab$b_rac = RAB$C_KEY;
X        vmsmail_rab.rab$w_usz = MAX_VMSMAIL_RECORD;
X
X        status = sys$connect(&vmsmail_rab);
X
X        if (status != RMS$_NORMAL)
X                exit(status);
X}
X/**************************/
X/*  Given global variable "username", looks for the MAIL.MAI file.
X    Returns 0 if it should be called again to process a forwarded
X    username, 1 if all done for whatever reason. */
X
Xfind_mai_file()
X{
X        char vmsmail_dir[NAM$C_MAXRSS + 1];
X        VMSMAIL_STAT get_vmsmail_data();
X
X        *mai_file = '\0';
X
X        /* The only non-obvious case is "no such record": this is
X           perfectly legal if the user in question has never sent
X           or received mail.
X             The message count in VMSMAIL.DAT can have the wrong value,
X           but the error is usually that it's > 0 when there are no
X           messages to be read.  Thus it's generally safe to return
X           without even bothering to open MAIL.MAI if that count is
X           zero, as we do here. */
X
X        switch (get_vmsmail_data(vmsmail_dir)) {
X                case no_such_record:
X           if (assemble_mai_file_name(vmsmail_dir))
X                                none_unread();
X                        else
X                                no_such_user();
X                        *mai_file = '\0';
X                        return(1);
X                        break;
X                case no_messages:
X                        none_unread();
X                        return(1);
X                        break;
X                case forwarded:
X                        return(0);
X                        break;
X                case normal:
X                        break;
X        }
X
X        if (!assemble_mai_file_name(vmsmail_dir)) {
X                no_such_user();
X                *mai_file = '\0';
X                return(1);
X        }
X
X        return(1);
X}
X/**************************/
X/*  Read the VMSMAIL record for a given username. */
X
XVMSMAIL_STAT get_vmsmail_data(vmsmail_dir)
Xchar *vmsmail_dir;
X{
X        char vmsmail_data[MAX_VMSMAIL_RECORD + 1];
X        struct vmsmail_record *data;
X        long status, new_len;
X
X        *vmsmail_dir = '\0';
X
X        vmsmail_rab.rab$l_ubf = vmsmail_data;
X
X        status = sys$get(&vmsmail_rab);
X
X        if (status == RMS$_RNF)
X                return(no_such_record);
X
X        if (status != RMS$_NORMAL && status != RMS$_OK_RRL)
X                exit(status);
X
X        /* fwdnam in a VMSMAIL record is the start of
X           forwarded_username + full_name + devdir, concatenated;
X           we pick out the pieces we want using the length bytes. */
X
X/*        data = (struct vmsmail_record *)(vmsmail_rab.rab$l_rbf);
X*/
X`009  data = cvtv5profile(vmsmail_rab.rab$l_rbf);
X
X/*(tws) save number of newmail messages for adding newmail to unread */
X`009savenewmail = data->mailcnt;
X        if (data->fwdlng == 0) {                /* no forwarding */
X                if (data->mailcnt == 0)
X                        return(no_messages);
X
X                if (data->dirlng != 0) {
X                        strncpy(vmsmail_dir,
X                                &(data->fwdnam) + data->fnmlng,
X                                data->dirlng);
X                        vmsmail_dir[data->dirlng] = '\0';
X                }
X                return(normal);
X        }
X        else {                  /* user is forwarded */
X                sprintf(username_buf, "%-*.*s", UAF$S_USERNAME,
X                                        data->fwdlng, &(data->fwdnam));
X                username_len = data->fwdlng > UAF$S_USERNAME ?
X                                        UAF$S_USERNAME : data->fwdlng;
X                return(forwarded);
X        }
X}
X/**************************/
X/*  Looks the user up in the UAF and fills in mai_file.  Returns 1
X    if all okay, 0 if the user doesn't exist in the UAF.
X      Note that this is done after checking VMSMAIL.  It's possible
X    for someone to be in VMSMAIL but not the UAF, legally, if the
X    name is simply forwarded on to a real person. */
X
Xint assemble_mai_file_name(vmsmail_dir)
Xchar *vmsmail_dir;
X{
X        char defdev[UAF$S_DEFDEV + 1], defdir[UAF$S_DEFDIR + 1];
X
X        static struct {
X                short   buflen1;
X                short   itmcode1;
X                char    *bufadr1;
X                long    retlenadr1;
X                short   buflen2;
X                short   itmcode2;
X                char    *bufadr2;
X                long    retlenadr2;
X                long    blank;
X        } uai_itemlist = { UAF$S_DEFDEV, UAI$_DEFDEV, 0, 0,
X                UAF$S_DEFDIR, UAI$_DEFDIR, 0, 0, 0 };
X
X        long status, dev_len, dir_len;
X
X
X        uai_itemlist.bufadr1 = defdev;
X        uai_itemlist.bufadr2 = defdir;
X
X        status = sys$getuai(0, 0, &username_desc, &uai_itemlist,
X                                        0, 0, 0);
X
X        if (status == RMS$_RNF)         /* there's no such user */
X                return(0);
X
X        if (status != SS$_NORMAL)
X                exit(status);
X
X        /* Put together the device and directory from the UAF. */
X
X        dev_len = defdev[0];    /* these are counted strings */
X        dir_len = defdir[0];
X        strncpy(mai_file, defdev + 1, dev_len);
X        strncpy(mai_file + dev_len, defdir + 1, dir_len);
X        mai_file[dev_len + dir_len] = '\0';
X
X        /* Add on the subdirectory, if any, specified in VMSMAIL.
X           It's assumed that this is of the form [.MAIL] (braces
X           included): we eliminate the final ] in the mail-file-name
X           so far, and then tack on the subdirectory from VMSMAIL
X           minus the [ at its start.  (We get RMS to tack on the
X           MAIL.MAI part for us.) */
X
X        if (*vmsmail_dir != '\0') {
X                mai_file[strlen(mai_file) - 1] = '\0';
X                strcat(mai_file, vmsmail_dir + 1);
X        }
X
X        return(1);
X}
X/**************************/
X/* Do all the processing for one MAIL.MAI file. */
X
Xint scan_mail(cnt_only)      /* caf - add cnt_only param */
Xint cnt_only;
X{
X        char mai_data[MAX_MAI_RECORD + 1];
X        struct mai_record *mai;
X        long status;
X`009int match_cnt=0;
X
X        if (!open_mail())   /* missing mail file, or no NEWMAIL folder */
X                return(0);
X        mai_rab.rab$l_ubf = mai_data;
X
X        /* Keep reading records until we run out. */
X
X        while (1) {
X                status = sys$get(&mai_rab);
X                if (status == RMS$_OK_LIM || status == RMS$_RNF ||
X                                        status == RMS$_EOF)
X                        break;
X                if (status != RMS$_NORMAL)
X                        exit(status);
X                mai = (struct mai_record *)(mai_rab.rab$l_rbf);
X`009`009if (cnt_only)     /* caf */
X`009`009        match_cnt += analyze_mai_record(mai, mai_rab.rab$w_rsz,
X`009`009`009`009`009`009`009FALSE);
X`009`009else
X`009`009        match_cnt += analyze_mai_record(mai, mai_rab.rab$w_rsz,
X                                                        TRUE);
X`009`009++savenewmail;
X        }
X        sys$close(&mai_fab);
X`009return(match_cnt);
X}
X/**************************/
X/*  Open the mail file.
X
X    Based on information from the VMS 4.4 fiche, facility MAIL,
X    file MAIL$ISAM_SUBS, routine SELECT_MSGS. */
X
Xopen_mail()
X{
X        long status;
X
X        mai_fab = cc$rms_fab;
X        mai_fab.fab$b_fac = FAB$M_GET;
X        mai_fab.fab$l_fna = mai_file;
X        mai_fab.fab$b_fns = strlen ( mai_file );
X        mai_fab.fab$l_dna = &MAI_DEFAULT;
X        mai_fab.fab$b_dns = sizeof ( MAI_DEFAULT );
X        mai_fab.fab$b_acmodes = PSL$C_EXEC << FAB$V_LNM_MODE;
X        mai_fab.fab$b_org = FAB$C_IDX;
X        mai_fab.fab$b_shr = FAB$M_SHRPUT | FAB$M_SHRGET |
X                            FAB$M_SHRDEL | FAB$M_SHRUPD;
X
X        status = sys$open(&mai_fab);
X
X        if (status == RMS$_FNF) {       /* no mail file */
X                lost_mail_file();
X                header_line_printed = 1;
X                return(0);
X        }
X
X        if (status != RMS$_NORMAL)
X                exit(status);
X
X        mai_rab = cc$rms_rab;
X        mai_rab.rab$l_fab = &mai_fab;
X        mai_rab.rab$l_rop = RAB$M_LOC | RAB$M_NLK | RAB$M_LIM | RAB$M_WAT;
X        mai_rab.rab$b_krf = 1;
X        mai_rab.rab$l_kbf = &NEWMAIL_FOLDER;
X        mai_rab.rab$b_ksz = sizeof ( NEWMAIL_FOLDER );
X        mai_rab.rab$b_rac = RAB$C_KEY;
X        mai_rab.rab$w_usz = MAX_MAI_RECORD;
X
X        status = sys$connect(&mai_rab);
X
X        if (status != RMS$_NORMAL) {
X                sys$close(&mai_fab);
X                exit(status);
X        }
X
X        status = sys$find(&mai_rab);
X
X        if (status == RMS$_RNF) {       /* no NEWMAIL folder */
X                sys$close(&mai_fab);
X                return(0);
X        }
X
X        if (status != RMS$_NORMAL) {
X                sys$close(&mai_fab);
X                exit(status);
X        }
X
X        mai_rab.rab$b_rac = RAB$C_SEQ;
X
X        return(1);
X}
X/**************************/
X/*  Analyze one MAIL.MAI record and decide if it's a match. */
X
Xint analyze_mai_record(mai, mai_size, print)    /* caf - add print param & */
Xstruct mai_record *mai;                         /* return value            */
Xint mai_size;
Xint print;
X{
X`009char *fromuser();
X        struct mmh_record *mmh;
X        int matched = 0;
X        char *subject;
X        int subject_length;
X`009char whofrom[255];
X
X/*(tws)PRIVS -- if have privs add check here */
X
X        if ((mai->mai_flags & MMHFLAGS_M_NEWMSG) == 0)
X               return(0);
X        mmh = (struct mmh_record *)(&(mai->mmh1));
X
X        while (mmh < (char *)mai + mai_size) {
X                if (mmh->mmh_type == MMH_C_FROM) {
X`009`009     if ( (test_from( &(mmh->mmh_data), mmh->mmh_length))``032
X`009`009`009   || (priv == 1))
X`009`009`009{
X                           matched = 1;
X`009`009`009   strncpy(whofrom, &(mmh->mmh_data), mmh->mmh_length);
X`009`009`009   whofrom[mmh->mmh_length] = '\0';
X`009`009`009   whofrom[strcspn(whofrom," ")] = '\0';
X`009`009`009} /* if */
X                     else
X                           return(0);
X`009`009} /* if */
X                else if (mmh->mmh_type == MMH_C_SUBJ) {
X                        subject = &(mmh->mmh_data);
X                        subject_length = mmh->mmh_length;
X                } /* if */
X                mmh = (struct mmh_record *)
X                                (&(mmh->mmh_data) + mmh->mmh_length);
X`009} /* while */
X        if (matched && print) {        /* caf */
X                display_mai(&(mai->msgtime[0]), subject,``032
X`009`009    subject_length,
X`009`009    whofrom);
X                return(1);
X        }
X`009else if (matched)
X`009        return(1);
X`009else``032
X                return(0);
X`009`009``032
X}
X
X/**************************/
X/*  Break the "from" string into a node name and username, and test
X    both for a match. */
X
Xint test_from(from, from_length)
Xchar *from;
Xint from_length;
X{
X        int i;
X        int node_size = 0;
X
X        /* Find first space.  The "from" string often includes a
X           "personal name", which is separated from the username
X           by some spaces. */
X
X        for (i = 0; i < from_length; i++)
X                if (from[i] == ' ')
X                        break;
X        from_length = i;
X
X        /* Find :: between node name and username.  We actually seek
X           only a :, since anything that contains a single colon
X           is something weird which we shouldn't match. */
X
X        for (i = 0; i < from_length; i++)
X                if (from[i] == ':')
X                        break;
X
X        /* If we found a node name, test it. */
X
X        if (i < from_length) {
X                if (!test_node(from, i))
X                        return(0);
X                i += 2;         /* add length of :: to length of nodename */
X                from += i;
X                from_length -= i;
X                if (from_length < 0)
X                        return(0);
X        }
X
X        /* Now test the username from the "from" field and see if it's
X           the same as that of the current process. */
X
X
X
X
X        if (from_length > UAF$S_USERNAME)
X                return(0);
X
X        for (i = 0; i < from_length; i++)
X                if (ourname_buf[i] != from[i] )
X                        return(0);
X
X        if (from_length < UAF$S_USERNAME &&
X                ourname_buf[from_length] != ' ')
X                        return(0);
X
X        return(1);
X}
X/**************************/
X/*  Test a node name to see if it's one of those allowed. */
X
Xint test_node(node, node_length)
Xchar *node;
Xint node_length;
X{
X        int i, testnamesize;
X        char *testname;
X
X        testname = &NODE_NAMES;
X
X        while (*testname != 0) {
X                testnamesize = *(testname++);
X                if (testnamesize == node_length) {
X                        for (i = 0; i < node_length; i++)
X                                if (node[i] != testname[i])
X                                        break;
X                        if (i == node_length)
X                                return(1);
X                }
X                testname += testnamesize;
X        }
X
X        return(0);
X}
X/**************************/
X/*  Print date/time and subject of this message, with a nice header
X    line if this is the first message to this user. */
X
Xdisplay_mai(time, subject, subject_length, whofrom)
Xlong *time;             /* pointer to 64-bit time */
Xchar *subject;          /* pointer to subject string */
Xint subject_length;     /* length of subject string */
Xchar *whofrom;`009`009/* who from */
X{
X#define TIME_LEN        23
X
X        static struct dsc$descriptor_s time_desc = {
X                TIME_LEN, 0, 0, 0
X        };
X        char time_buf[TIME_LEN];
X
X        time_desc.dsc$a_pointer = time_buf;
X        sys$asctim(0, &time_desc, time, 0);
X        time_buf[17] = '\0';
X/*
X        if (header_line_printed == 0) {
X                if (forwardings == 0)
X                        printf("\nUnread messages %sto %.*s:\n\n",
X`009`009`009`009(priv == 1) ? "\0" : "from you ",
X                                firstname_len, firstname_desc.dsc$a_pointer);
X                else {
X                        printf(
V                        "\nUnread messages %s to %.*s (forwarded to %.*s):\n\n
X",
X`009`009`009`009(priv == 1) ? "\0" : "from you",
X                                firstname_len, firstname_desc.dsc$a_pointer,
X                                username_len, username_desc.dsc$a_pointer);
X                }
X        }
X*/    /* caf */
X`009if (priv == 1)
X`009    printf("%s %-24.24s %.*s\n", time_buf, whofrom,
X                subject_length > 37 ? 37 : subject_length, subject);
X`009else
X`009    printf("%s %.*s\n", time_buf,``032
X                subject_length > 60 ? 60 : subject_length, subject);
X
X        header_line_printed++;
X}
X/**************************/
X/*  Print messages for various other cases. */
X
Xnone_unread()
X{
X/*(tws)if no newmail folder then display no unread messages */
X
X`009if(savenewmail == 0)
X        printf("\nNo unread messages to %.*s.\n\n",
X                        firstname_len, firstname_desc.dsc$a_pointer);
X}
X
X
Xno_such_user()
X{
X        if (forwardings == 0)
X/*              printf("\nNo such user %.*s.\n",
X                        firstname_len, firstname_desc.dsc$a_pointer);
X*/
X/* (tws) Don't reveal the translation of the logical name string -- it
X   will confuse the users */
X
X`009    `009printf("\nNo such user %s.\n", savefirstname);
X        else
X/*                printf(
X                "\nUsername %.*s is forwarded to nonexistent user %.*s.\n",
X                        firstname_len, firstname_desc.dsc$a_pointer,
X                        username_len, username_desc.dsc$a_pointer);
X*/
X                printf(
X                "\nUsername %s is forwarded to nonexistent user %.*s.\n",
X                        savefirstname,
X                        username_len, username_desc.dsc$a_pointer);
X}
X
X
Xlost_mail_file()
X{
X        printf("\nUnable to open mail file for %.*s.\n",
X                        firstname_len, firstname_desc.dsc$a_pointer);
X}
$GoSub Convert_File
$Exit
