home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol150 / 211msg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1984-04-29  |  30.1 KB  |  1,091 lines

  1. /************************************************************************/
  2. /*                msg.c                    */
  3. /*                                    */
  4. /*    Message handling for Citadel bulletin board system        */
  5. /************************************************************************/
  6.  
  7. /************************************************************************/
  8. /*                history                 */
  9. /*                                    */
  10. /* 83Mar03 CrT & SB   Various bug fixes...                */
  11. /* 83Feb27 CrT    Save private mail for sender as well as recipient.    */
  12. /* 83Feb23    Various.  transmitFile() won't drop first char on WC... */
  13. /* 82Dec06 CrT    2.00 release.                        */
  14. /* 82Nov05 CrT    Stream retrieval.  Handles messages longer than MAXTEXT.*/
  15. /* 82Nov04 CrT    Revised disk format implemented.            */
  16. /* 82Nov03 CrT    Individual history begun.  General cleanup.        */
  17. /************************************************************************/
  18.  
  19. #include "b:210ctdl.h"
  20.  
  21. /************************************************************************/
  22. /*                contents                */
  23. /*                                    */
  24. /*    aideMessage()        saves auto message in Aide>        */
  25. /*    dGetWord()        reads a word off disk            */
  26. /*    dPrintf()        printf() that writes to disk        */
  27. /*    fakeFullCase()        converts uppercase message to mixed case*/
  28. /*    findPerson()        load log record for named person    */
  29. /*    flushMsgBuf()        wraps up message-to-disk store        */
  30. /*    getMessage()        load message into RAM            */
  31. /*    getMsgChar()        returns successive chars off disk    */
  32. /*    getMsgStr()        reads a string out of message.buf    */
  33. /*    getWord()        gets one word from message buffer    */
  34. /*    mAbort()        checks for user abort of typeout    */
  35. /*    makeMessage()        menu-level message-entry routine    */
  36. /*    mFormat()        formats a string to modem and console    */
  37. /*    mPeek()         sysop debugging tool--shows ctdlmsg.sys */
  38. /*    mPrintf()        writes a line to modem & console    */
  39. /*    mWCprintf()        special mprintf for WC transfers    */
  40. /*    msgInit()        sets up catChar, catSect etc.        */
  41. /*    noteLogMessage()    enter message into log record        */
  42. /*    noteMessage()        enter message into current room     */
  43. /*    note2Message()        noteMessage() local            */
  44. /*    printMessage()        prints a message on modem & console    */
  45. /*    pullIt()        sysop special message-removal routine    */
  46. /*    putMessage()        write message to disk            */
  47. /*    putMsgChar()        writes successive message chars to disk */
  48. /*    putWord()        writes one word to modem & console    */
  49. /*    showMessages()        menu-level show-roomful-of-messages fn    */
  50. /*    startAt()        setup to read a message off disk    */
  51. /*    unGetMsgChar()        return a char to getMsgChar()        */
  52. /*    zapMsgFile()        initialize ctdlmsg.sys            */
  53. /************************************************************************/
  54.  
  55. /************************************************************************/
  56. /*    aideMessage() saves auto message in Aide>            */
  57. /************************************************************************/
  58. aideMessage(noteDeletedMessage)
  59. char noteDeletedMessage;
  60. {
  61.     int ourRoom;
  62.  
  63.     /* message is already set up in msgBuf.mbtext */
  64.     putRoom(ourRoom=thisRoom, &roomBuf);
  65.     getRoom(AIDEROOM, &roomBuf);
  66.  
  67.     strCpy(msgBuf.mbauth, "Citadel");
  68.     msgBuf.mbto[0] = '\0';
  69.     if (putMessage( /* uploading== */ FALSE))    noteMessage(0, ERROR);
  70.  
  71.     if (noteDeletedMessage)   {
  72.     note2Message(pulledMId, pulledMLoc);
  73.     }
  74.  
  75.     putRoom(AIDEROOM, &roomBuf);
  76.     noteRoom();
  77.     getRoom(ourRoom, &roomBuf);
  78. }
  79.  
  80. /************************************************************************/
  81. /*    dGetWord() fetches one word from current message, off disk    */
  82. /*    returns TRUE if more words follow, else FALSE            */
  83. /************************************************************************/
  84. char dGetWord(dest, lim)
  85. char *dest;
  86. int  lim;
  87. {
  88.     char getMsgChar();
  89.     char c;
  90.  
  91.     --lim;    /* play it safe */
  92.  
  93.     /* pick up any leading blanks: */
  94.     for (c = getMsgChar();   c == ' '  &&  c && lim;   c = getMsgChar()) {
  95.     if (lim) { *dest++ = c;   lim--; }
  96.     }
  97.  
  98.     /* step through word: */
  99.     for (         ;  c != ' ' && c && lim;   c = getMsgChar()) {
  100.     if (lim) { *dest++ = c;   lim--; }
  101.     }
  102.  
  103.     /* trailing blanks: */
  104.     for (         ;   c == ' ' && c && lim;   c = getMsgChar()) {
  105.     if (lim) { *dest++ = c;   lim--; }
  106.     }
  107.  
  108.     if (c)  unGetMsgChar(c);    /* took one too many    */
  109.  
  110.     *dest = '\0';        /* tie off string    */
  111.  
  112.     return  c;
  113. }
  114.  
  115. /************************************************************************/
  116. /*    dPrintf() write from format+args to disk            */
  117. /************************************************************************/
  118. dPrintf(format /* plus an unknown #arguments for format */)
  119. char *format;
  120. #define MAXWORD 256    /* maximum length of a word */
  121. {
  122.     char *s, string[MAXWORD];
  123.  
  124.     string[0]    = 0;
  125.     _spr(string, &format);
  126.  
  127.     s    = string;
  128.     while (*s)    putMsgChar(*s++);
  129. }
  130.  
  131. /************************************************************************/
  132. /*    fakeFullCase() converts a message in uppercase-only to a    */
  133. /*    reasonable mix.  It can't possibly make matters worse...    */
  134. /*    Algorithm: First alphabetic after a period is uppercase, all    */
  135. /*    others are lowercase, excepting pronoun "I" is a special case.    */
  136. /*    We assume an imaginary period preceding the text.        */
  137. /************************************************************************/
  138. fakeFullCase(text)
  139. char *text;
  140. {
  141.     char toLower(), toUpper();
  142.     char *c;
  143.     char lastWasPeriod;
  144.     char state;
  145.  
  146.     for(lastWasPeriod=TRUE, c=text;   *c;  c++) {
  147.     if (
  148.         *c != '.'
  149.         &&
  150.         *c != '?'
  151.         &&
  152.         *c != '!'
  153.     ) {
  154.         if (isAlpha(*c)) {
  155.         if (lastWasPeriod)    *c    = toUpper(*c);
  156.         else            *c    = toLower(*c);
  157.         lastWasPeriod    = FALSE;
  158.         }
  159.     } else {
  160.         lastWasPeriod    = TRUE ;
  161.     }
  162.     }
  163.  
  164.     /* little state machine to search for ' i ': */
  165. #define NUTHIN        0
  166. #define FIRSTBLANK    1
  167. #define BLANKI        2
  168.     for (state=NUTHIN, c=text;    *c;  c++) {
  169.     switch (state) {
  170.     case NUTHIN:
  171.         if (isSpace(*c))    state    = FIRSTBLANK;
  172.         else        state    = NUTHIN    ;
  173.         break;
  174.     case FIRSTBLANK:
  175.         if (*c == 'i')    state    = BLANKI    ;
  176.         else        state    = NUTHIN    ;
  177.         break;
  178.     case BLANKI:
  179.         if (isSpace(*c))    state    = FIRSTBLANK;
  180.         else        state    = NUTHIN    ;
  181.  
  182.         if (!isAlpha(*c))    *(c-1)    = 'I';
  183.         break;
  184.     }
  185.     }
  186. }
  187.  
  188. /************************************************************************/
  189. /*    findPerson() loads log record for named person.         */
  190. /*    RETURNS: ERROR if not found, else log record #            */
  191. /************************************************************************/
  192. int findPerson(name, lBuf)
  193. char            *name;
  194. struct logBuffer    *lBuf;
  195. {
  196.     int  h, i, foundIt, logNo;
  197.  
  198.     h    = hash(name);
  199.     for (foundIt=i=0;  i<MAXLOGTAB && !foundIt;  i++) {
  200.     if (logTab[i].ltnmhash == h) {
  201.         getLog(lBuf, logNo = logTab[i].ltlogSlot);
  202.         if (strCmpU(name, lBuf->lbname) == SAMESTRING) {
  203.         foundIt = TRUE;
  204.         }
  205.     }
  206.     }
  207.     if (!foundIt)    return ERROR;
  208.     else         return logNo;
  209. }
  210.  
  211. /************************************************************************/
  212. /*    flushMsgBuf() wraps up writing a message to disk        */
  213. /************************************************************************/
  214. flushMsgBuf() {
  215.     rseek(msgfl, thisSector, 0);
  216.     crypte(sectBuf, SECTSIZE, 0);
  217.     if (rwrite(msgfl, sectBuf, 1) != 1) {
  218.     printf("?ctdlmsg.sys write fail");
  219.     }
  220.     crypte(sectBuf, SECTSIZE, 0);
  221. }
  222.  
  223. /************************************************************************/
  224. /*    getMessage() reads a message off disk into RAM.         */
  225. /*    a previous call to setUp has specified the message.        */
  226. /************************************************************************/
  227. getMessage() {
  228.     char c;
  229.  
  230.     /* clear msgBuf out */
  231.     msgBuf.mbauth[ 0]    = '\0';
  232.     msgBuf.mbdate[ 0]    = '\0';
  233.     msgBuf.mborig[ 0]    = '\0';
  234.     msgBuf.mboname[0]    = '\0';
  235.     msgBuf.mbroom[ 0]    = '\0';
  236.     msgBuf.mbsrcId[0]    = '\0';
  237.     msgBuf.mbtext[ 0]    = '\0';
  238.     msgBuf.mbto[   0]    = '\0';
  239.  
  240.     do c = getMsgChar(); while (c != 0xFF);    /* find start of msg    */
  241.  
  242.     msgBuf.mbheadChar    = oldChar;        /* record location    */
  243.     msgBuf.mbheadSector = oldSector;
  244.  
  245.     getMsgStr(msgBuf.mbId, NAMESIZE);
  246.  
  247.     do    {
  248.     c = getMsgChar();
  249.     switch (c) {
  250.     case 'A':    getMsgStr(msgBuf.mbauth,  NAMESIZE);    break;
  251.     case 'D':    getMsgStr(msgBuf.mbdate,  NAMESIZE);    break;
  252.     case 'M':    /* just exit -- we'll read off disk */    break;
  253.     case 'N':    getMsgStr(msgBuf.mboname, NAMESIZE);    break;
  254.     case 'O':    getMsgStr(msgBuf.mborig,  NAMESIZE);    break;
  255.     case 'R':    getMsgStr(msgBuf.mbroom,  NAMESIZE);    break;
  256.     case 'S':    getMsgStr(msgBuf.mbsrcId, NAMESIZE);    break;
  257.     case 'T':    getMsgStr(msgBuf.mbto,      NAMESIZE);    break;
  258.     default:
  259.         getMsgStr(msgBuf.mbtext, MAXTEXT);    /* discard unknown field  */
  260.         msgBuf.mbtext[0]    = '\0';
  261.         break;
  262.     }
  263.     } while (c != 'M'  &&  isAlpha(c));
  264. }
  265.  
  266. /************************************************************************/
  267. /*    getMsgChar() returns sequential chars from message on disk    */
  268. /************************************************************************/
  269. char getMsgChar() {
  270.     char visible();
  271.     char toReturn;
  272.     int  mark, val;
  273.  
  274.     if (GMCCache) {    /* someone did an unGetMsgChar() --return it    */
  275.     toReturn= GMCCache;
  276.     GMCCache= '\0';
  277.     return toReturn;
  278.     }
  279.  
  280.     oldChar    = thisChar;
  281.     oldSector    = thisSector;
  282.  
  283.     toReturn    = sectBuf[thisChar];
  284.  
  285. #ifdef XYZZY
  286.     if (debug) putCh(visible(toReturn));
  287. #endif
  288.  
  289.     thisChar    = ++thisChar % SECTSIZE;
  290.     if (thisChar == 0) {
  291.     /* time to read next sector in: */
  292.     thisSector  = ++thisSector % maxMSector;
  293.     rseek(msgfl, thisSector, 0);
  294.     if (rread(msgfl, sectBuf, 1) >= 1000) {
  295.         printf("?nextMsgChar-rread fail");
  296.     }
  297.     crypte(sectBuf, SECTSIZE, 0);
  298.     }
  299.     return(toReturn);
  300. }
  301.  
  302. /************************************************************************/
  303. /*    getMsgStr() reads a string from message.buf            */
  304. /************************************************************************/
  305. getMsgStr(dest, lim)
  306. char *dest;
  307. int  lim;
  308. {
  309.     char c;
  310.  
  311.     while (c = getMsgChar()) {        /* read the complete string    */
  312.     if (lim) {            /* if we have room then     */
  313.         lim--;
  314.         *dest++ = c;        /* copy char to buffer        */
  315.     }
  316.     }
  317.     *dest = '\0';            /* tie string off with null    */
  318. }
  319.  
  320. /************************************************************************/
  321. /*    getWord() fetches one word from current message         */
  322. /************************************************************************/
  323. int getWord(dest, source, offset, lim)
  324. char *dest, *source;
  325. int  lim, offset;
  326. {
  327.     int i, j;
  328.  
  329.     /* skip leading blanks if any */
  330.     for (i=0;  source[offset+i]==' ' && i<lim;    i++);
  331.  
  332.     /* step over word */
  333.     for (;
  334.  
  335.      source[offset+i]   != ' '    &&
  336.      i            <  lim    &&
  337.      source[offset+i]   != 0;
  338.  
  339.      i++
  340.     );
  341.  
  342.     /* pick up any trailing blanks */
  343.     for (;  source[offset+i]==' ' && i<lim;  i++);
  344.  
  345.     /* copy word over */
  346.     for (j=0;  j<i;  j++)  dest[j] = source[offset+j];
  347.     dest[j] = 0;    /* null to tie off string */
  348.  
  349.     return(offset+i);
  350. }
  351.  
  352. /************************************************************************/
  353. /*    mAbort() returns TRUE if the user has aborted typeout        */
  354. /*    Globals modified:    outFlag                 */
  355. /************************************************************************/
  356. char mAbort() {
  357.     char BBSCharReady(), iChar(), toUpper();
  358.     char c, toReturn;
  359.  
  360.     /* Check for abort/pause from user */
  361.     if (!BBSCharReady()) {
  362.     toReturn    = FALSE;
  363.     } else {
  364.     echo    = NEITHER;
  365.     c = toUpper(iChar());
  366.     switch (c) {
  367.     case XOFF:
  368.     case 'P':                    /*    pause:          */
  369.         c = iChar();                /* wait to resume */
  370.         if (
  371.         toLower(c) == 'd'
  372.         &&
  373.         aide
  374.         ) pullMessage = TRUE;
  375.         toReturn      = FALSE;
  376.         break;
  377.     case 'J':                    /* jump paragraph:*/
  378.         outFlag    = OUTPARAGRAPH;
  379.         toReturn    = FALSE;
  380.         break;
  381.     case 'N':                    /* next:          */
  382.         outFlag    = OUTNEXT;
  383.         toReturn    = TRUE;
  384.         break;
  385.     case 'S':                    /* skip:          */
  386.         outFlag    = OUTSKIP;
  387.         toReturn    = TRUE;
  388.         break;
  389.     default:
  390.         toReturn    = FALSE;
  391.         break;
  392.     }
  393.     echo    = BOTH;
  394.     }
  395.     return toReturn;
  396. }
  397.  
  398. /************************************************************************/
  399. /*    makeMessage is menu-level routine to enter a message        */
  400. /*    Return: TRUE if message saved else FALSE            */
  401. /************************************************************************/
  402. makeMessage(uploading)
  403. char uploading;     /* TRUE if message is coming via WC protocol    */
  404. {
  405.     char        putMessage();
  406.     char        allUpper, *pc, toReturn;
  407.     char        toName[NAMESIZE];
  408.     struct logBuffer    lBuf;
  409.     int         logNo;
  410.  
  411.     toReturn = FALSE;
  412.  
  413.     logNo    = ERROR;/* not needed, but it's nice to initialize...    */
  414.  
  415.     if (thisRoom != MAILROOM)  msgBuf.mbto[0] = FALSE;
  416.     else {
  417.     if (!loggedIn) {
  418.         strCpy(msgBuf.mbto, "Sysop");
  419.         mPrintf(" (private mail to 'sysop')\n ");
  420.     } else {
  421.         getString("recipient", msgBuf.mbto, NAMESIZE);
  422.         normalizeString(msgBuf.mbto);
  423.         logNo   = findPerson(msgBuf.mbto, &lBuf);
  424.         if (logNo==ERROR  &&  hash(msgBuf.mbto)!=hash("Sysop")) {
  425.         mPrintf("No '%s' known", msgBuf.mbto);
  426.         return FALSE;
  427.         }
  428.     }
  429.     }
  430.  
  431.     strcpy(msgBuf.mbauth, logBuf.lbname);        /* record author*/
  432.     if (uploading || gettext("message", msgBuf.mbtext, MAXTEXT)) {
  433.     if (!uploading)   {
  434.         for (pc=msgBuf.mbtext, allUpper=TRUE;   *pc && allUpper;  pc++)   {
  435.         if (toUpper(*pc) != *pc)   allUpper = FALSE;
  436.         }
  437.         if (allUpper)   fakeFullCase(msgBuf.mbtext, MAXTEXT);
  438.     }
  439.  
  440.     if (toReturn=putMessage(uploading))   noteMessage(&lBuf, logNo);
  441.     }
  442.     return toReturn;
  443. }
  444.  
  445. /************************************************************************/
  446. /*    mFormat() formats a string to modem and console         */
  447. /************************************************************************/
  448. mFormat(string)
  449. char *string;
  450. #define MAXWORD 256    /* maximum length of a word */
  451. {
  452.     char wordBuf[MAXWORD];
  453.     char mAbort();
  454.     int  i;
  455.  
  456.     for (i=0;  string[i] && (!outFlag || outFlag==OUTPARAGRAPH);  ) {
  457.     i = getWord(wordBuf, string, i, MAXWORD);
  458.     putWord(wordBuf);
  459.     if (mAbort()) return;
  460.     }
  461. }
  462.  
  463. /************************************************************************/
  464. /*    mPeek() dumps a sector in message.buf.    sysop debugging tool    */
  465. /************************************************************************/
  466. mPeek() {
  467.     char visible();
  468.     char peekBuf[SECTSIZE];
  469.     int  col, row, s;
  470.  
  471.     s = getNumber(" sector to dump", 0, maxMSector-1);
  472.     rseek(msgfl, s, 0);
  473.     rread(msgfl, peekBuf, 1);
  474.     for (row=0;  row<2;  row++) {
  475.     mPrintf("\n ");
  476.     for (col=0;  col<64;  col++) {
  477.         oChar(visible(peekBuf[row*64 +col]));
  478.     }
  479.     }
  480. }
  481.  
  482. /************************************************************************/
  483. /*    mPrintf() formats format+args to modem and console        */
  484. /************************************************************************/
  485. mPrintf(format /* plus an unknown #arguments for format */)
  486. char *format;
  487. #define MAXWORD 256    /* maximum length of a word */
  488. {
  489.     char mAbort();
  490.     char string[MAXWORD], wordBuf[MAXWORD];
  491.     int  i;
  492.  
  493.     string[0] = 0;
  494.     _spr(string, &format);
  495.  
  496.     for (i=0;  string[i] && (!outFlag  || outFlag==OUTPARAGRAPH);  ) {
  497.     i = getWord(wordBuf, string, i, MAXWORD);
  498.     putWord(wordBuf);
  499.  
  500.     if (mAbort()) return;
  501.     }
  502. }
  503.  
  504. /************************************************************************/
  505. /*    mWCprintf() formats format+args to sendWCChar()         */
  506. /************************************************************************/
  507. mWCprintf(format /* plus an unknown #arguments for format */)
  508. char *format;
  509. {
  510.     char *s;
  511.     char string[MAXWORD];
  512.  
  513.     string[0] = 0;
  514.     _spr(string, &format);
  515.  
  516.     s    = string;
  517.     do sendWCChar(*s); while (*s++);    /* send terminal null also    */
  518. }
  519.  
  520. /************************************************************************/
  521. /*    msgInit() sets up lowId, highId, catSector and catChar,     */
  522. /*    by scanning over message.buf                    */
  523. /************************************************************************/
  524. msgInit() {
  525.     int  aToI();
  526.     int  firstLo, firstHi, hereLo, hereHi;    /* 32 bits by halves    */
  527.  
  528.     startAt(0, 0);
  529.     getMessage();
  530.  
  531.     /* get the ID# */
  532.     sscanf(msgBuf.mbId, "%d %d", &firstHi, &firstLo);
  533.     printf("message# %d %d\n", firstHi, firstLo);
  534.     newestHi    = firstHi;
  535.     newestLo    = firstLo;
  536.  
  537.     oldestHi    = firstHi;
  538.     oldestLo    = firstLo;
  539.  
  540.     catSector    = thisSector;
  541.     catChar    = thisChar;
  542.  
  543.     for (
  544.     getMessage();
  545.  
  546.     sscanf(msgBuf.mbId, "%d %d", &hereHi, &hereLo),
  547.     !(hereHi == firstHi   &&   hereLo == firstLo);
  548.  
  549.     getMessage()
  550.  
  551.     ) {
  552.     printf("message# %d %d\n", hereHi, hereLo);
  553.  
  554.     /* find highest and lowest message IDs: */
  555.     /* 32-bit "<" by hand: */
  556.     if ((hereHi<oldestHi)  ||  (hereHi==oldestHi && hereLo<oldestLo)) {
  557.         oldestHi    = hereHi;
  558.         oldestLo    = hereLo;
  559.         printf(" oldest=%u %u\n", oldestHi, oldestLo);
  560.     }
  561.     if ((hereHi>newestHi)  ||  (hereHi==newestHi && hereLo>newestLo)) {
  562.         newestHi    = hereHi;
  563.         newestLo    = hereLo;
  564.         printf(" newest=%u %u\n", newestHi, newestLo);
  565.  
  566.         /* read rest of message in and remember where it ends,    */
  567.         /* in case it turns out to be the last message        */
  568.         /* in which case, that's where to start writing next message*/
  569.         while (dGetWord(msgBuf.mbtext, MAXTEXT));
  570.         catSector    = thisSector;
  571.         catChar    = thisChar;
  572.     }
  573.     }
  574. }
  575.  
  576. /************************************************************************/
  577. /*    noteLogMessage() slots message into log record            */
  578. /************************************************************************/
  579. noteLogMessage(lBuf, logNo)
  580. struct logBuffer   *lBuf;
  581. int           logNo;
  582. {
  583.     int i;
  584.  
  585.     /* store into recipient's log record: */
  586.     /* slide message pointers down to make room for this one: */
  587.     for (i=0;  i<MAILSLOTS-1;  i++) {
  588.     (*lBuf).lbslot[i]   = (*lBuf).lbslot[i+1];
  589.     (*lBuf).lbId[  i]   = (*lBuf).lbId[  i+1];
  590.     }
  591.  
  592.     /* slot this message in:    */
  593.     (*lBuf).lbId[MAILSLOTS-1]        = newestLo ;
  594.     (*lBuf).lbslot[MAILSLOTS-1]     = catSector;
  595.  
  596.     putLog(lBuf, logNo);
  597. }
  598.  
  599. /************************************************************************/
  600. /*    noteMessage() slots message into current room            */
  601. /************************************************************************/
  602. noteMessage(lBuf, logNo)
  603. struct logBuffer   *lBuf;
  604. int           logNo;
  605. {
  606.     int i, roomNo;
  607.  
  608.     if (!++newestLo) ++newestHi;    /* 32-bit '++' by hand    */
  609.     logBuf.lbvisit[0]    = newestLo;
  610.  
  611.     if (thisRoom != MAILROOM) {
  612.     note2Message(newestLo, catSector);
  613.  
  614.     /* write it to disk:        */
  615.     putRoom(thisRoom, &roomBuf);
  616.     noteRoom();
  617.     } else {
  618.     if (hash(msgBuf.mbto) != hash("Sysop"))  {
  619.         if (logNo != thisLog)  {
  620.         noteLogMessage(lBuf, logNo);        /* note in recipient    */
  621.         }
  622.         noteLogMessage(&logBuf, thisLog);        /* note in ourself        */
  623.         fillMailRoom();                /* update room also     */
  624.     } else {
  625.        getRoom(AIDEROOM, &roomBuf);
  626.  
  627.        /* enter in Aide> room -- 'sysop' is special */
  628.        note2Message(newestLo, catSector);
  629.  
  630.        /* write it to disk:        */
  631.        putRoom(AIDEROOM, &roomBuf);
  632.        noteRoom();
  633.  
  634.        getRoom(MAILROOM, &roomBuf);
  635.        /* note in ourself if logged in: */
  636.        if (loggedIn)   noteLogMessage(&logBuf, thisLog);
  637.        fillMailRoom();
  638.     }
  639.     }
  640.  
  641.     /* make message official:    */
  642.     catSector    = thisSector;
  643.     catChar    = thisChar;
  644.     setUp(FALSE);
  645. }
  646.  
  647. /************************************************************************/
  648. /*    note2Message() makes slot in current room... called by noteMess */
  649. /************************************************************************/
  650. note2Message(id, loc)
  651. int id, loc;
  652. {
  653.     int i;
  654.  
  655.     /* store into current room: */
  656.     /* slide message pointers down to make room for this one:        */
  657.     for (i=0;  i<MSGSPERRM-1;  i++) {
  658.     roomBuf.vp.msg[i].rbmsgLoc  = roomBuf.vp.msg[i+1].rbmsgLoc;
  659.     roomBuf.vp.msg[i].rbmsgNo   = roomBuf.vp.msg[i+1].rbmsgNo ;
  660.     }
  661.  
  662.     /* slot this message in:        */
  663.     roomBuf.vp.msg[MSGSPERRM-1].rbmsgNo     = id ;
  664.     roomBuf.vp.msg[MSGSPERRM-1].rbmsgLoc    = loc;
  665. }
  666.  
  667.  
  668. /************************************************************************/
  669. /*    printMessage() prints indicated message on modem & console    */
  670. /************************************************************************/
  671. printMessage(loc, id)
  672. int     loc;         /* sector in message.buf         */
  673. unsigned id;         /* unique-for-some-time ID#     */
  674. {
  675.     char dGetWord(), mAbort();
  676.     char c, moreFollows;
  677.     int hereHi, hereLo;
  678.  
  679.     startAt(loc, 0);
  680.  
  681.     do getMessage(); while (
  682.     (
  683.         sscanf(msgBuf.mbId, "%d %d", &hereHi, &hereLo),
  684.         hereLo != id
  685.     ) &&
  686.     thisSector == loc
  687.     );
  688.     if (hereLo != id  &&  !usingWCprotocol) {
  689.     mPrintf("?can't find message");
  690. #ifdef XYZZY
  691.     mPrintf(" loc=%d, id=%u, mbIds=%s, here=%d %d\n",
  692.         loc, id, msgBuf.mbId, &hereHi, &hereLo
  693.     );
  694. #endif
  695.     return;
  696.     }
  697.  
  698.     if (!usingWCprotocol) {
  699.     doCR();
  700.  
  701.     if (msgBuf.mbdate[ 0])        mPrintf(    "   %s ",     msgBuf.mbdate );
  702.     if (msgBuf.mbauth[ 0])        mPrintf(    "from %s",    msgBuf.mbauth );
  703.     if (msgBuf.mboname[0])        mPrintf(    " @%s",       msgBuf.mboname);
  704.     if (
  705.         msgBuf.mbroom[0]
  706.         &&
  707.         strCmp(msgBuf.mbroom, roomBuf.rbname) != SAMESTRING
  708.     ) {
  709.         mPrintf(                " in %s>",    msgBuf.mbroom );
  710.     }
  711.     if (msgBuf.mbto[   0])        mPrintf(    " to %s",     msgBuf.mbto   );
  712.  
  713.     doCR();
  714.  
  715.     do {
  716.         moreFollows     = dGetWord(msgBuf.mbtext, 150);
  717.         putWord(msgBuf.mbtext);
  718.     } while (moreFollows  &&  !mAbort());
  719.     doCR();
  720.  
  721.     } else {
  722.     /* networking dump of message: */
  723.  
  724.     /* fill in local node in origin fields if local message: */
  725.     if (!msgBuf.mborig[ 0])  strcpy(msgBuf.mborig,    nodeId       );
  726.     if (!msgBuf.mboname[0])  strcpy(msgBuf.mboname, nodeName   );
  727.     if (!msgBuf.mbsrcId[0])  strcpy(msgBuf.mbsrcId, msgBuf.mbId);
  728.  
  729.     /* send header fields out: */
  730.     if (msgBuf.mbauth[ 0])    mWCprintf("A%s", msgBuf.mbauth );
  731.     if (msgBuf.mbdate[ 0])    mWCprintf("D%s", msgBuf.mbdate );
  732.     if (msgBuf.mboname[0])    mWCprintf("N%s", msgBuf.mboname);
  733.     if (msgBuf.mborig[ 0])    mWCprintf("O%s", msgBuf.mborig );
  734.     if (msgBuf.mbroom[ 0])    mWCprintf("R%s", msgBuf.mbroom );
  735.     if (msgBuf.mbsrcId[0])    mWCprintf("S%s", msgBuf.mbsrcId);
  736.     if (msgBuf.mbto[   0])    mWCprintf("T%s", msgBuf.mbto   );
  737.  
  738.     /* send message text proper: */
  739.     sendWCChar('M');
  740.     do  {
  741.         c = getMsgChar();
  742.         if (c=='\n')  c='\r';
  743.         sendWCChar(c);
  744.     } while (c);
  745.     }
  746. }
  747.  
  748. /************************************************************************/
  749. /*    pullIt() is a sysop special to remove a message from a room    */
  750. /************************************************************************/
  751. pullIt(m)
  752. int m;
  753. {
  754.     int i, low;
  755.  
  756.     /* confirm that we're removing the right one:    */
  757.     outFlag = OUTOK;
  758.     printMessage(roomBuf.vp.msg[m].rbmsgLoc, roomBuf.vp.msg[m].rbmsgNo);
  759.     if (!getYesNo("pull")) return;
  760.  
  761.     /* record vital statistics for possible insertion elsewhere: */
  762.     pulledMLoc = roomBuf.vp.msg[m].rbmsgLoc;
  763.     pulledMId  = roomBuf.vp.msg[m].rbmsgNo ;
  764.  
  765.     if (thisRoom == AIDEROOM)    return;
  766.  
  767.     /* return emptied slot: */
  768.     for (i=m;  i>0;  i--) {
  769.     roomBuf.vp.msg[i].rbmsgLoc    = roomBuf.vp.msg[i-1].rbmsgLoc;
  770.     roomBuf.vp.msg[i].rbmsgNo    = roomBuf.vp.msg[i-1].rbmsgNo ;
  771.     }
  772.     roomBuf.vp.msg[0].rbmsgNo    = ERROR;   /* mark new slot at end as free */
  773.  
  774.     /* store revised room to disk before we forget...    */
  775.     noteRoom();
  776.     putRoom(thisRoom, &roomBuf);
  777.  
  778.     /* note in Aide>: */
  779.     sPrintf(msgBuf.mbtext, "Following message deleted by %s:", logBuf.lbname);
  780.     aideMessage( /* noteDeletedMessage== */ TRUE);
  781. }
  782.  
  783. /************************************************************************/
  784. /*    putMessage() stores a message to disk                */
  785. /*    Always called before noteMessage() -- newestLo not ++ed yet.    */
  786. /*    Returns: TRUE on successful save, else FALSE            */
  787. /************************************************************************/
  788. char putMessage(uploading)
  789. char uploading;     /* true to get text via WC modem input, not RAM */
  790. {
  791.     char *s, allOk;
  792.     int  putWCChar();
  793.  
  794.     startAt(catSector, catChar);    /* tell putMsgChar where to write    */
  795.     putMsgChar(0xFF);            /* start-of-message         */
  796.  
  797.     /* write message ID */
  798.     dPrintf("%u %u", newestHi, newestLo+1);
  799.     putMsgChar(0);
  800.  
  801.     /* write date:    */
  802.     dPrintf("D%d%s%02d",
  803.     interpret(pGetYear),
  804.     monthTab[interpret(pGetMonth)],
  805.     interpret(pGetDay)
  806.     );
  807.     putMsgChar(0);
  808.  
  809.     /* write room name out:        */
  810.     dPrintf("R%s", roomBuf.rbname);
  811.     putMsgChar(0);
  812.  
  813.     if (loggedIn) {
  814.     /* write author's name out:        */
  815.     dPrintf("A%s", msgBuf.mbauth);
  816.     putMsgChar(0);        /* null to end string   */
  817.     }
  818.  
  819.     if (msgBuf.mbto[0]) {    /* private message -- write addressee    */
  820.     dPrintf("T%s", msgBuf.mbto);
  821.     putMsgChar(0);
  822.     }
  823.  
  824.     /* write message text by hand because it would overrun dPrintf buffer: */
  825.     putMsgChar('M');    /* M-for-message.    */
  826.     if (!uploading) {
  827.     for (s=msgBuf.mbtext;  *s;  s++) putMsgChar(*s);
  828.     allOk    = TRUE;
  829.     } else {
  830.     outFlag = FALSE;    /* setup for putWCChar()    */
  831.     allOk    = readFile(putWCChar);
  832.     }
  833.  
  834.     if (allOk) {
  835.     putMsgChar(0);        /* null to end text     */
  836.  
  837.     flushMsgBuf();
  838.     } else {
  839.     flushMsgBuf();        /* so message count is ok    */
  840.  
  841.     /* erase start-of-message indicator: */
  842.     startAt(catSector, catChar);
  843.     putmsgChar(0);        /* overwrite 0xFF byte    */
  844.     }
  845.  
  846.     return  allOk;
  847. }
  848.  
  849. /************************************************************************/
  850. /*    putMsgChar() writes successive message chars to disk        */
  851. /*    Globals:    thisChar=    thisSector=            */
  852. /*    Returns:    ERROR if problems else TRUE            */
  853. /************************************************************************/
  854. int putMsgChar(c)
  855. char c;
  856. {
  857.     char visible();
  858.     int  toReturn;
  859.  
  860.     toReturn = TRUE;
  861.  
  862. #ifdef XYZZY
  863.     if (debug) putch(visible(c));
  864. #endif
  865.  
  866.     if (sectBuf[thisChar] == 0xFF)  {
  867.     /* obliterating a msg    */
  868.     if (!++oldestLo) ++oldestHi;    /* 32-bit increment by hand    */
  869.     logBuf.lbvisit[(MAXVISIT-1)]    = oldestLo;
  870.     }
  871.  
  872.     sectBuf[thisChar]    = c;
  873.  
  874.     thisChar    = ++thisChar % SECTSIZE;
  875.  
  876.     if (thisChar == 0) {    /* time to write sector out a get next: */
  877.     rseek(msgfl, thisSector, 0);
  878.     crypte(sectBuf, SECTSIZE, 0);
  879.     if (rwrite(msgfl, sectBuf, 1) != 1) {
  880.         printf("?putMsgChar-rwrite fail");
  881.         toReturn    = ERROR;
  882.     }
  883.  
  884.     thisSector    = ++thisSector % maxMSector;
  885.     rseek(msgfl, thisSector, 0);
  886.     if (rread(msgfl, sectBuf, 1) >= 1000) {
  887.         printf("?putMsgChar-rread fail");
  888.         toReturn    = ERROR;
  889.     }
  890.     crypte(sectBuf, SECTSIZE, 0);
  891.     }
  892.     return  toReturn;
  893. }
  894.  
  895. /************************************************************************/
  896. /*    putWord() writes one word to modem & console            */
  897. /************************************************************************/
  898. putWord(st)
  899. char *st;
  900. {
  901.     char *s;
  902.     int  newColumn;
  903.  
  904.     for (newColumn=crtColumn, s=st;  *s; s++)    {
  905.     if (*s != TAB)    ++newColumn;
  906.     else        while (++newColumn % 8);
  907.     }
  908.     if (newColumn > termWidth)     doCR();
  909.  
  910.     for (;  *st;  st++) {
  911.  
  912.     if (*st != TAB) ++crtColumn;
  913.     else        while (++crtColumn % 8);
  914.  
  915.     /* worry about words longer than a line:    */
  916.     if (crtColumn > termWidth) doCR();
  917.  
  918.     if (prevChar!=NEWLINE  ||  (*st > ' '))   oChar(*st);
  919.     else {
  920.         /* end of paragraph: */
  921.         if (outFlag == OUTPARAGRAPH)   {
  922.         outFlag = OUTOK;
  923.         }
  924.         doCR();
  925.         oChar(*st);
  926.     }
  927.     }
  928. }
  929.  
  930. /************************************************************************/
  931. /*    showMessages() is routine to print roomful of msgs        */
  932. /************************************************************************/
  933. showMessages(whichMess, revOrder)
  934. char whichMess, revOrder;
  935. {
  936.     char toUpper(), iChar();
  937.     char c;
  938.     int i;
  939.     int start, finish, increment, msgNo;
  940.     unsigned lowLim, highLim;
  941.  
  942.     setUp(FALSE);
  943.  
  944.     /* Allow for reverse retrieval: */
  945.     if (!revOrder) {
  946.     start        = 0;
  947.     finish        = MSGSPERRM;
  948.     increment   = 1;
  949.     } else {
  950.     start        = (MSGSPERRM -1);
  951.     finish        = -1;
  952.     increment   = -1;
  953.     }
  954.  
  955.     switch (whichMess)     {
  956.     case NEWoNLY:
  957.     lowLim    = logBuf.lbvisit[ logBuf.lbgen[thisRoom] & CALLMASK]+1;
  958.     highLim = newestLo;
  959.     break;
  960.     case OLDaNDnEW:
  961.     lowLim    = oldestLo;
  962.     highLim = newestLo;
  963.     break;
  964.     case OLDoNLY:
  965.     lowLim    = oldestLo;
  966.     highLim = logBuf.lbvisit[ logBuf.lbgen[thisRoom] & CALLMASK];
  967.     break;
  968.     }
  969.  
  970.     /* stuff may have scrolled off system unseen, so: */
  971.     /* was "if (lowLim < oldestLo)...", rigged for wraparound: */
  972.     if (oldestLo-lowLim < 0x8000) {
  973.     lowLim = oldestLo;
  974.     }
  975.     if (!expert && !usingWCprotocol)  {
  976.     mPrintf("\n <J>ump <N>ext <P>ause <S>top");
  977.     }
  978.  
  979.     for (i=start;   i!=finish;     i+=increment) {
  980.     if (outFlag) {
  981.         if        (
  982.              outFlag == OUTNEXT
  983.              ||
  984.              outFlag == OUTPARAGRAPH
  985.         ) outFlag = OUTOK;
  986.         else if (outFlag == OUTSKIP)   {
  987.         echo = BOTH;
  988.         return;
  989.         }
  990.     }
  991.  
  992.     /* "<" comparison with 64K wraparound in mind: */
  993.     msgNo    = roomBuf.vp.msg[i].rbmsgNo;
  994.     if (
  995.         msgNo - lowLim  < 0x8000
  996.         &&
  997.         highLim - msgNo < 0x8000
  998.      ) {
  999.         printMessage(roomBuf.vp.msg[i].rbmsgLoc, msgNo);
  1000.  
  1001.         /*    Pull current message from room if flag set */
  1002.         if (pullMessage) {
  1003.         pullMessage = FALSE;
  1004.         pullIt(i);
  1005.         if (revOrder)    i++;
  1006.         }
  1007.  
  1008.         if (
  1009.         thisRoom  == MAILROOM
  1010.         &&
  1011.         whichMess == NEWoNLY
  1012.         &&
  1013.         getYesNo("respond")
  1014.         ) {
  1015.         if (makeMessage( /* uploading== */ FALSE)) i--;
  1016.         }
  1017.     }
  1018.     }
  1019. }
  1020.  
  1021. /************************************************************************/
  1022. /*    startAt() sets location to begin reading message from        */
  1023. /************************************************************************/
  1024. startAt(sect, byt)
  1025. int sect;
  1026. int byt;
  1027. {
  1028.     GMCCache  = '\0';    /* cache to unGetMsgChar() into */
  1029.  
  1030.     if (sect >= maxMSector) {
  1031.     printf("?startAt s=%d,b=%d", sect, byt);
  1032.     return;
  1033.     }
  1034.     thisChar    = byt;
  1035.     thisSector    = sect;
  1036.  
  1037.     rseek(msgfl, sect, 0);
  1038.     if (rread(msgfl, sectBuf, 1) >= 1000) {
  1039.     printf("?startAt rread fail");
  1040.     }
  1041.     crypte(sectBuf, SECTSIZE, 0);
  1042. }
  1043.  
  1044. /************************************************************************/
  1045. /*    unGetMsgChar() returns (at most one) char to getMsgChar()    */
  1046. /************************************************************************/
  1047. unGetMsgChar(c)
  1048. char c;
  1049. {
  1050.     GMCCache    = c;
  1051. }
  1052.  
  1053. /************************************************************************/
  1054. /*    zapMsgFl() initializes message.buf                */
  1055. /************************************************************************/
  1056. zapMsgFile() {
  1057.     char getCh(), toUpper();
  1058.     int i, sect, val;
  1059.  
  1060.     printf("\nDestroy all current messages? ");
  1061.     if (toUpper(getCh()) != 'Y')   return;
  1062.  
  1063.     /* put null message in first sector... */
  1064.     sectBuf[0]    = 0xFF; /*   \                */
  1065.     sectBuf[1]    =  '0'; /*    \             */
  1066.     sectBuf[2]    =  ' '; /*     > Message ID "0 1"    */
  1067.     sectBuf[3]    =  '1'; /*    /             */
  1068.     sectBuf[4]    = '\0'; /*   /                */
  1069.     sectBuf[5]    =  'M'; /*   \    Null messsage        */
  1070.     sectBuf[6]    = '\0'; /*   /                */
  1071.  
  1072.     for (i=7;  i<SECTSIZE;  i++) sectBuf[i] = 0;
  1073.  
  1074.     rseek(msgfl, 0, 0);
  1075.     crypte(sectBuf, SECTSIZE, 0);    /* encrypt    */
  1076.     if ((val = rwrite(msgfl, sectBuf, 1)) != 1) {
  1077.     printf("zapMsgFil: rwrite failed, %d records!\n", val);
  1078.     }
  1079.  
  1080.     crypte(sectBuf, SECTSIZE, 0);    /* decrypt    */
  1081.     sectBuf[0] = 0;
  1082.     crypte(sectBuf, SECTSIZE, 0);    /* encrypt    */
  1083.     for (sect=1;  sect<maxMSector;  sect++) {
  1084.     rseek(msgfl, sect, 0);
  1085.     if ((val = rwrite(msgfl, sectBuf, 1)) != 1) {
  1086.         printf("zapMsgFil: rwrite failed, wrote %d records!\n", val);
  1087.     }
  1088.     }
  1089.     crypte(sectBuf, SECTSIZE, 0);    /* decrypt    */
  1090. }
  1091.