Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

744 lines
23 KiB

/* header.c - handle display of header window
*
* HISTORY:
* 04-Feb-87 danl Bug fix: missing top/bottom line if scroll by 1
* 07-Feb-87 danl Bug fix: duplicate display of header, regencont
* sets oldTop, oldBold
* 09-Mar-87 danl GetSender, allow for missing From
* 22-Mar-87 danl GetDate: bullet proof for malformed date
* 09-Apr-1987 mz Use whiteskip/whitescan
* 11-Apr-1987 mz Use PASCAL/INTERNAL
* 30-Apr-87 danl GenerateFlags: use intermediates for rgdoc[i]
* 19-May-87 danl REGENCONT: use GOTOIDOCALL instead of GOTOIDOC
* 20-Jun-87 danl GetSender: add pIgnFrom
* 01-Jul-87 danl test idoc, inote against -1 not ERROR
* 04-Aug-87 danl Bug fix: REGENCONT, test for i <= inoteLast
* 21-Aug-1987 mz Change references from MAXARG to MAXLINELEN
* 24-Aug-1987 mz Add new string constant for "ALL"
* 09-Sep-1987 mz Fix empty folder screen repainting problem
* 09-Sep-87 danl min to minutes
* 11-Sep-87 danl CREATE: alloc inoteMax +2 nodes
* 12-Oct-1989 leefi v1.10.73, hdrLine() returns(NULL) rather than
* returning(<nothing>), if error occurs
*
*/
#define INCL_DOSINFOSEG
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <wzport.h>
#include <tools.h>
#include "dh.h"
#include "zm.h"
struct flagBit {
CHAR flagStr [ 2 ]; /* chars to display for set flag */
UINT mask; /* bit mask for flag */
};
struct flagBit flagSet [] = {
{ " U", F_UNREAD },
{ " D", F_DELETED },
{ " F", F_FLAGGED },
{ " M", F_MOVED },
{ 0, 0 }
};
/*
** MapIdocInote - map an idoc to an inote
**
** return ERROR if not found or empty mailbox
*/
INOTE PASCAL INTERNAL MapIdocInote ( INT idoc )
{
INT inote;
if ( idoc != -1 && idocLast != -1 ) {
for ( inote = 0; inote <= inoteLast; inote++ )
if ( mpInoteIdoc [ inote ] == idoc )
return inote;
}
return ERROR;
}
/* WhereIdoc - return the line of the window the passed message # falls on.
*
* arguments:
* height height of window which messages will be displayed in
* idoc idoc in question
*
* return value:
* line # message falls on line <line #> of headers window
* ERROR (-1) message is not on screen
*
*/
INT PASCAL INTERNAL WhereIdoc ( INT height, IDOC idoc )
{
register INT end = min ( inoteTop + height, inoteLast + 1 );
register INT i;
for ( i = inoteTop; ( idoc != mpInoteIdoc [ i ] ) && ( i < end ); i++)
;
if ( i >= end )
return ERROR;
else
return i - inoteTop;
}
/* GetSender - return some indication of who sent the message
*
* GetSender is used to generate a sender string from a mail header. It
* starts by trying to find the FROM line. If there is none or if the FROM
* line indicates that we sent it (compared wuth the MAILNAME environment
* variable) then we will try to use the TO: line as the displayed text.
* If no TO: line is present, we us the text NOT-MAILED.
*
* pHdr pointer to header. DO not change it.
* pIgnFrom string, ignore from line if pIgnFrom is sender and use To:
*
* returns pointer to malloced string
*/
PSTR PASCAL INTERNAL GetSender (PSTR pHdr, PSTR pIgnFrom)
{
PSTR pFrom = FindTag ("From", pHdr);
PSTR pTo = FindTag ("To:", pHdr);
PSTR q = NULL;
PSTR p = NULL;
CHAR c;
CHAR line[MAXLINELEN];
if ( !pFrom && !pTo )
return ZMMakeStr ("NOT-MAILED");
if ( pFrom ) {
pFrom = NextToken (pFrom);
p = strbscan (pFrom, " \t\r\n");
c = *p;
*p = '\0';
}
if ( pFrom && _strcmpi (pFrom, pIgnFrom) ) {
/*
** skip over routing char in long names so that we display
** the part after rightmost !, e.g.
** uw-beaver!us-june!randyn becomes !randyn
*/
while ( *(q = strbscan (pFrom+1, "!") ) )
pFrom = q;
pFrom = ZMMakeStr (pFrom);
*p = c;
return pFrom;
} else {
if ( pFrom )
*p = c;
strcpy (line, "To: ");
p = pTo;
if ( pTo ) {
pTo = whiteskip ( pTo + 3 );
p = strbscan (pTo, strCRLF);
if (p - pTo > MAXLINELEN - 5)
p = pTo + MAXLINELEN - 5;
memmove ( line + 4, pTo, p - pTo);
}
line[4 + p - pTo] = '\0';
return ZMMakeStr (line);
}
}
/* GetDate - return some indication of when a message was sent
*
* GetDate scans the header for some timestamp. For now, we look for a
* Date: line. We then compress the found time into a date.
*
* pHdr pointer to header. DO not change it.
*
* returns pointer to malloced string
*/
PSTR PASCAL INTERNAL GetDate (PSTR pHdr)
{
PSTR pDate = FindTag ("From", pHdr);
PSTR pRtn = strEMPTY;
PSTR pEnd = NULL;
PSTR p = NULL;
PSTR q = NULL;
CHAR c;
CHAR line[MAXLINELEN];
CHAR month[20];
CHAR day[20];
CHAR hr[20], minutes[20];
INT date, year, sec;
if ( pDate ) {
/* skip over "from: " and the sender's name */
q = pDate = NextToken ( NextToken (pDate) );
c = *(pEnd = strbscan (pDate, strCRLF) );
*pEnd = '\0';
/*
** Make sure individual tokens < 20 char so sscanf doesn't blow stack
*/
while ( *(p = q) ) {
if ( ( q = NextToken ( p ) ) - p > 20 )
goto rtn;
}
if ( sscanf (pDate, " %s %s %d %2s:%2s:%d %d", day, month, &date,
hr, minutes, &sec, &year) == 7) {
month[3] = '\0';
sprintf (line, "%s %2ld %s:%s", month, (LONG)date, hr, minutes);
pRtn = line;
}
*pEnd = c;
}
rtn:
return ZMMakeStr ( pRtn );
}
/* GetSubject - return the subject line
*
* GetSubject looks for the Subject: line and returns a malloced copy of
* it. If none is present, we return an empty string.
*
* pHdr pointer to header. DO not change it.
* pPrefix pointer to text to be prefixed to header, e.g. "Re: "
*
* returns pointer to malloced string
*/
PSTR PASCAL INTERNAL GetSubject (PSTR pHdr, PSTR pPrefix)
{
PSTR pSubj = FindTag ("Subject:", pHdr);
PSTR p = NULL;
PSTR q = NULL;
CHAR ch;
if ( pSubj != NULL)
{
pSubj = strbskip ( strbscan ( pSubj, ":" ), ": \t" );
p = strbscan ( pSubj, strCRLF);
ch = *p; /* Subject may not be last line of pHdr */
*p = '\0';
q = ZMalloc ( strlen ( pPrefix ) + strlen ( pSubj ) + 1 );
*q = '\0';
if ( !strpre ( pPrefix, pSubj ) )
strcpy ( q, pPrefix );
strcat ( q, pSubj );
*p = ch;
}
else /* subject tag not found - start with empty string */
{
q = ZMalloc ( strlen (pPrefix ) + 1 );
*q = '\0';
strcpy (q, pPrefix);
}
return q;
}
/* hdrInfoFromIdoc - return pointer to the header in memory from a mail item #
*
* msg 0-based number of the mail message to return the header
* ppHdr pointer to char pointer for header
* pBodyLen pointer to body len
*/
VOID PASCAL INTERNAL hdrInfoFromIdoc (IDOC idoc, PSTR *ppHdr, PLONG pBodyLen)
{
Dhandle dMsg;
if (ppHdr != NULL)
*ppHdr = '\0';
dMsg = getdoc (fhMailBox, DOC_SPEC, IDOCTODOC (idoc));
if (dMsg != ERROR) {
if (ppHdr != NULL) {
if ((*ppHdr = gethdr (dMsg)) == NULL)
assert (FALSE);
if (*ppHdr == (PSTR) ERROR)
goto docError;
}
if (pBodyLen != NULL) {
*pBodyLen = getbdylen (dMsg);
if ( *pBodyLen == ERROR )
goto docError;
}
if ( (putdoc (dMsg)) == ERROR )
goto docError;
}
else {
docError:
if (ppHdr != NULL) {
*ppHdr = ZMMakeStr ( "Subject: Discarded message" );
}
if (pBodyLen != NULL) {
*pBodyLen = 0;
}
}
}
/* GenerateFlags - find the mail-flags section of the mailbox and process them
* into an internal form. Make sure that the header field of the message
* points to the in-core header.
*
* The flags on messages are in the form:
*
* Mail-Flags:xxxxxxxxxxxxxx
*
* where the x's are either the single character flag names or are dots.
* The internal form is merely a bitmask that is easily tested. Any
* mask that does not have the F_FLAGSVALID bit set means that either the
* message has no flags or that the message has not been seen previously
*
* idoc 0-based message number
*/
VOID PASCAL INTERNAL GenerateFlags (INT idoc)
{
PSTR p1 = NULL;
PSTR hdr = NULL;
LONG len;
INT i;
if (!TESTFLAG (rgDoc[idoc].flag, F_FLAGSVALID)) {
assert (rgDoc[idoc].hdr == NULL);
hdrInfoFromIdoc (idoc, &hdr, &len);
rgDoc[idoc].hdr = hdr;
rgDoc[idoc].len = len;
i = 0;
if ((p1 = FindTag (MAILFLAG, rgDoc[idoc].hdr)) != NULL)
sscanf (strbscan (p1, ":") + 1, " %x", &i);
rgDoc[idoc].flag = i | F_FLAGSVALID;
} else
assert (rgDoc[idoc].hdr != NULL);
}
/* ChangeHeaderFlag - change flags in the local header structure, update the
* mailbox folder, and update the screen.
*
* idoc 0-based message number that's to be updated.
* setFlag flag to be set in the message header
* resetFlag flag to be reset in the message header
*/
VOID PASCAL INTERNAL ChangeHeaderFlag (INT idoc, FLAG setFlag, FLAG resetFlag)
{
PSTR pHdr = NULL;
PSTR p = NULL;
PSTR p1 = NULL;
Dhandle dMsg;
UINT oldFlag;
GenerateFlags (idoc);
if ( fReadOnlyCur )
return;
oldFlag = rgDoc[idoc].flag;
rgDoc[idoc].flag = (oldFlag | setFlag) & ~resetFlag;
if (rgDoc[idoc].flag != oldFlag) {
SETFLAG ( rgDoc[idoc].flag, F_LOCKED );
dMsg = getdoc (fhMailBox, DOC_SPEC, IDOCTODOC (idoc));
assert (dMsg != ERROR);
pHdr = rgDoc[idoc].hdr;
p = FindTag (MAILFLAG, pHdr);
if (p != NULL) {
/* remove the mail-flag line */
p1 = strbscan (p, strCRLF);
if (*p1 != '\0')
p1++;
Move ((LPSTR) p1, (LPSTR) p, strlen (p1) + 1);
}
/* reallocate a new block that has enough room for a legitimate
* mail flag line
*/
p1 = ZMalloc (strlen (pHdr) + 1 + L_MAILFLAG);
assert (p1 != NULL);
strcpy (p1, pHdr);
ZMfree (pHdr);
sprintf (p1 + strlen (p1), F_MAILFLAG, rgDoc[idoc].flag &
~F_FLAGSVALID & ~F_LOCKED);
if (puthdr (dMsg, p1))
assert (FALSE);
if (putdoc (dMsg))
assert (FALSE);
rgDoc[idoc].hdr = p1;
hdrLine ( hHeaders, idoc );
SendMessage ( hHeaders, PAINT, WhereIdoc ( TWINHEIGHT ( hHeaders ),
idoc ) );
RSETFLAG ( rgDoc[idoc].flag, F_LOCKED );
}
return;
}
/* hdrLine - generate a header line for the given message number, and place
* it in hWnd->pcontent. note that hdrLine does not update the screen!
*
* The format of a header line is:
*
* FLAGS ### FROM/TO SUBJECT [LEN]
*
* hWnd window to place line in
* if hWnd = NULL return line as an alloc'd string
* if hWnd != NULL put result in hWnd->pContent and rtn NULL
* idoc idoc number
*/
PSTR PASCAL INTERNAL hdrLine (HW hWnd, INT idoc)
{
INT w;
INT h;
CHAR line[MAXLINELEN];
PSTR p = NULL;
PSTR pTmp = NULL;
CHAR length[13];
PSTR pLine = NULL;
INT i, j;
w = TWINWIDTH ( hWnd ? hWnd : hHeaders );
h = TWINHEIGHT ( hWnd ? hWnd : hHeaders );
if ( hWnd && ( i = WhereIdoc ( h, idoc ) ) == ERROR )
return NULL;
else
{
GenerateFlags (idoc);
/* generate displayed flags */
pLine = line;
for (j = 0; flagSet[j].mask != 0; j++)
*pLine++ = flagSet[j].flagStr[(rgDoc[idoc].flag & flagSet[j].mask) != 0];
/* add on message number */
sprintf (pLine, "%4d ", idoc + 1);
p = GetSender (rgDoc[idoc].hdr, DefMailName );
if (strlen (p) > FROMLEN) {
p [ FROMLEN ] = '\0';
if ( whitescan ( p ) != strend ( p ) ) {
pTmp = &( p [ FROMLEN - 1 ] );
while ( ( *pTmp == ' ' ) || ( *pTmp == '\t' ) )
pTmp--;
*( pTmp + 0 ) = '+';
*( pTmp + 1 ) = '\0';
}
}
sprintf (strend (pLine), FROMSTR, p);
ZMfree (p);
p = GetDate (rgDoc[idoc].hdr);
if (strlen (p) > DATELEN)
p[DATELEN] = '\0';
sprintf (strend (pLine), DATESTR, p);
ZMfree (p);
strcat (pLine, strBLANK);
p = GetSubject (rgDoc[idoc].hdr, strEMPTY);
p [ min ( strlen ( p ), 80 ) ] = '\0';
sprintf (strend (pLine), "%s", p);
ZMfree (p);
SpacePad ( line, w );
line [ w ] = '\0';
if ( rgDoc[idoc].len >= cShowSize ) {
sprintf (length, " [%ld]", rgDoc[idoc].len);
strcpy (&line[ w - strlen(length) ], length);
}
}
if ( hWnd ) {
Move ( ( LPSTR ) line, ( LPSTR ) ( hWnd->pContent + ( i *
w * sizeof ( CHAR ) ) ), w );
return NULL;
}
else
return ZMMakeStr ( line );
}
/* GenHdrTitle - generate the header title
*
* Generate the header window title based on the mailbox name and the
* current selection of headers
*
* hWnd handle to window to set the title for.
*/
VOID PASCAL INTERNAL GenHdrTitle (HW hWnd)
{
CHAR line[MAXLINELEN];
PSTR pCh = NULL;
PSTR pRO = NULL;
pRO = ( fReadOnlyCur ? "(RO) " : strEMPTY );
sprintf (line, "%sFile: %s Headers: %s", pRO, mboxName, headerText);
if ( strlen ( line ) > (size_t)TWINWIDTH ( hWnd ) ) {
pCh = line + TWINWIDTH ( hWnd ) - 3;
while ( ( *pCh == ' ' ) || ( *pCh == '\t' ) )
pCh--;
*( pCh + 0 ) = '.';
*( pCh + 1 ) = '.';
*( pCh + 2 ) = '.';
*( pCh + 3 ) = '\0';
}
SetWindowText (hWnd, line);
}
/* hdrProc - manage all messages and display for the headers window
*
* The header window displays the headers of all currectly selected messages.
*
* hWnd handle of window receiving message
* command command in message
* data data peculiar to the command
*/
VOID PASCAL INTERNAL hdrProc (HW hWnd, INT command, WDATA data)
{
struct vectorType *pVec = NULL;
PSTR pTmp = NULL;
IDOC idocT;
INOTE inoteT;
INT width = TWINWIDTH ( hWnd );
INT height = TWINHEIGHT ( hWnd );
INT winSize = width *height *sizeof ( CHAR );
INT oldBold = inoteBold;
INT oldTop = inoteTop;
INT i;
INT attr;
switch (command) {
case KEY:
/* IMPORTANT: if there are no messages in the current */
/* mailfolder, or it is undefined, DO NOT call hdrProc */
/* with a KEY command, ZM will bomb! */
switch (data) {
case CTRL_T:
case HOME:
inoteTop = inoteBold = 0;
break;
case CTRL_B:
case END:
inoteBold = inoteLast;
inoteTop = max ( 0, ( inoteBold + 1 ) - height );
break;
case CTRL_K:
case PGUP:
inoteBold = inoteTop = max ( 0, inoteTop - height );
break;
case CTRL_L:
case PGDN:
if ( inoteTop + height <= inoteLast )
inoteBold = inoteTop += height;
else
inoteBold = inoteLast;
break;
case CTRL_P:
case UP:
inoteBold = max ( 0, inoteBold - 1 );
if ( inoteBold < inoteTop )
inoteTop = max ( 0, inoteTop - 1 );
break;
case CTRL_N:
case DOWN:
inoteBold = min ( inoteLast, inoteBold + 1 );
if ( inoteBold - inoteTop >= height )
inoteTop = min ( inoteLast, inoteTop + 1 );
break;
default:
( *hWnd->keyProc ) ( hWnd, data );
break;
}
break;
case GOTOIDOCALL:
/*
** make sure message # data is in the header window
** (data is folder idoc)
** if msg is not in current header list, select the following
** msg. Worst case is data beyond end of current headers list, then
** we use inoteLast.
**
** GOTOIDOCALL - will calc a new inoteTop such that inoteBold will
** be in window and, if possible, so will "last" doc in the
** current headers list. The "last" doc is inote 0 if chron
** is no and inoteLast if chron is yes.
*/
if ( idocLast != -1 ) {
/*
** for perf reasons, duplicate MapIdocInote because we
** wish to bail out as soon as we know no match possible
*/
for ( i = 0; i < inoteLast; i++) {
idocT = mpInoteIdoc [ i ];
if ( (INT)data == idocT ||
( DefMOChron && (INT)data > idocT ) ||
(!DefMOChron && (INT)data < idocT ) )
break;
}
inoteBold = i;
/*
** chron == yes => DefMOChron == FALSE
** chron == no => DefMOChron == TRUE
*/
inoteTop = max ( 0,
( DefMOChron
? inoteBold - (height - 1)
: min ( inoteBold, inoteLast - (height - 1) ) ) );
}
break;
case REGENIDOC:
/*
** REGEN content and redisplay the single line for idoc
*/
ZMfree ( rgDoc[data].hdr );
rgDoc[data].hdr = NULL;
RSETFLAG (rgDoc[data].flag, F_FLAGSVALID);
SendMessage ( hWnd, REGENCONT, data + 1 );
SendMessage ( hWnd, PAINT, WhereIdoc ( height, data ) );
break;
case REGENCONT :
/*
** grab message #data, and put it in window's content region
** data is idoc + 1
** if data == 0, redo all lines in header window
*/
if ( data != 0 )
hdrLine ( hWnd, data - 1 );
else
{
Fill ( ( LPSTR ) hWnd->pContent, ' ', winSize );
for ( i = inoteTop; i <= inoteLast && i < inoteTop + height; i++)
SendMessage ( hWnd, REGENCONT, mpInoteIdoc [ i ] + 1 );
if ( inoteBold - inoteTop >= height )
SendMessage ( hWnd, GOTOIDOCALL, mpInoteIdoc [ inoteBold ] );
/*
** since we just regen'd all content, no need to do the
** scrolling etc at the end of this case statement
*/
oldBold = inoteBold;
oldTop = inoteTop;
}
break;
case PAINT:
/* Paint the data-th line of the header window on the screen */
/* if there is something in the current mailfolder */
if ( ((INT)data < 0 ) || ( (INT)data >= height ))
break;
else {
attr = ((INT)data + inoteTop == inoteBold && idocLast != -1) ? DefBold : DefNorm;
WzTextOut (hWnd, 0, data, hWnd->pContent + data * width, width, attr );
}
break;
case CREATE:
/*
** if inoteMax < 0 then empty folder, i.e. inoteMax == -1
** in this case we allocate inoteMax + 2 == 1 nodes so that we
** at least get something allocated from ZMalloc and don't get
** a NULL back, too many things depend on mpInoteIdoc not being NULL
*/
mpInoteIdoc = ( PINT ) ZMalloc ( ( inoteMax + 2 ) * sizeof ( *mpInoteIdoc ) );
if ( DefMOChron ) {
i = inoteMax;
for ( inoteLast = 0; inoteLast <= inoteMax; inoteLast++)
mpInoteIdoc [ i-- ] = inoteLast;
}
else
for ( inoteLast = 0; inoteLast <= inoteMax; inoteLast++)
mpInoteIdoc [ inoteLast ] = inoteLast;
inoteLast--;
inoteTop = inoteBold = 0;
oldTop = oldBold = 0;
hWnd->pContent = PoolAlloc ( winSize );
hWnd->contSize = winSize;
SendMessage ( hWnd, REGENCONT, 0 );
GenHdrTitle ( hWnd );
break;
case CLOSE:
ZMfree ( mpInoteIdoc );
defWndProc (hWnd, command, data);
break;
case RECREATE:
/*
** A "fast" way to "close" "create" the headers window when its
** content has been changed.
** Preserves the current headerText and inoteBold if possible
*/
/*
** Setup mpInoteIdoc for "all" -- see note above on allocation of +2
*/
ZMfree ( mpInoteIdoc );
mpInoteIdoc = ( PINT ) ZMalloc ( ( inoteMax + 2 ) * sizeof ( *mpInoteIdoc ) );
if ( DefMOChron ) {
i = inoteMax;
for ( inoteLast = 0; inoteLast <= inoteMax; inoteLast++) {
mpInoteIdoc [ i-- ] = inoteLast;
}
} else
{
for ( inoteLast = 0; inoteLast <= inoteMax; inoteLast++)
mpInoteIdoc [ inoteLast ] = inoteLast;
}
inoteLast--;
/*
** if headers not equal to "all" then generate the appropriate
** subset. If can't generate subset, then revert to all, e.g.
** the old headers was "deleted" and we just finished an expunge
*/
if ( strcmpis (strAll, headerText ) ) {
pTmp = headerText;
if ( MsgList ( &pVec, &pTmp, FALSE ) ) {
inoteLast = pVec->count - 1;
for (i = 0; i <= inoteLast; i++)
mpInoteIdoc[i] = (INT) pVec->elem[i];
ZMfree ( pVec );
}
else {
SendMessage ( hCommand, DISPLAY, "Resetting headers to \"all\"" );
inoteTop = inoteBold = 0;
strcpy ( headerText, strAll);
GenHdrTitle (hHeaders);
}
}
SendMessage ( hWnd, REGENCONT, 0 );
DrawWindow ( hHeaders, TRUE );
break;
default:
break;
}
/* inoteTop has moved, so decide how to redraw the header window, */
/* if the window was only move 1 line, scroll it and draw the */
/* new line, otherwise redraw the whole window. */
if ( ( inoteTop == oldTop - 1 ) || ( inoteTop == oldTop + 1 ) ) {
inoteT = ( inoteTop < oldTop ? inoteTop : inoteTop + height - 1 );
ScrollWindow ( hWnd, 1, inoteTop - oldTop );
SendMessage ( hWnd, REGENCONT, mpInoteIdoc [ inoteT ] + 1 );
SendMessage ( hWnd, PAINT, inoteT - inoteTop );
} else if ( inoteTop != oldTop ) {
Fill ( ( LPSTR ) hHeaders->pContent, ' ', winSize );
SendMessage ( hWnd, REGENCONT, 0 );
DrawWindow ( hWnd, FALSE );
}
if ( inoteBold != oldBold ) {
SendMessage (hWnd, PAINT, oldBold - inoteTop);
SendMessage (hWnd, PAINT, inoteBold - inoteTop);
}
return;
}