home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / top2src.zip / PRIVCHAT.C < prev    next >
C/C++ Source or Header  |  2000-07-13  |  13KB  |  403 lines

  1. /******************************************************************************
  2. PRIVCHAT.C   Private chat mode functions.
  3.  
  4.     Copyright 1993 - 2000 Paul J. Sidorsky
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License, version 2, as
  8.     published by the Free Software Foundation.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  
  19. This module implements TOP's private chat mode (CHAT command).  A complete
  20. explanation of the private chat file system is given in PRIVCHAT.TXT.
  21. ******************************************************************************/
  22.  
  23. #include "top.h"
  24.  
  25. /* This module uses several module-wide global variables that are not needed
  26.    in other modules. */
  27.  
  28. /* Function prototypes. */
  29. XINT privchatreadbuf(void);
  30. void privchatputch(XINT pckey);
  31. XINT privchatwritebuf(void);
  32.  
  33. /* Incoming chat file handle, outgoing chat file handle. */
  34. XINT ipchatfil, opchatfil;
  35. /* Incoming chat file name, outgoing chat file name. */
  36. unsigned char *pcinam = NULL, *pconam = NULL;
  37. unsigned char *pcobuf = NULL; /* Outgoing chat buffer. */
  38. XINT pcobufpos; /* Outgoing buffer position. */
  39. char pccmdstr[50]; /* Command string detection buffer. */
  40. char exitflag; /* Flag when chat is exited. */
  41. /* Time of last private chat poll, time of last BBS poll. */
  42. clock_t privlastpoll = 0, privlastbbspoll = 0;
  43. /* File name of exit semaphore file. */
  44. unsigned char exitfilenam[81];
  45.  
  46. /* privatechat() - Enters private chat mode (CHAT command).
  47.    Parameters:  pcnode - Node entering private chat with this one.
  48.    Returns:  Nothing.
  49. */
  50. void privatechat(XINT pcnode)
  51.     {
  52.     XINT key, ttry; /* Keypress holder, file i/o attempt counter. */
  53.  
  54.     /* Private chats are logged for security purposes. */
  55.     od_log_write(top_output(OUT_STRING, getlang("LogEnterPrivChat"),
  56.                  handles[pcnode].string));
  57.  
  58.     /* Initialize variables and buffers. */
  59.     exitflag = 0;
  60.     pcinam = malloc(256);
  61.     pconam = malloc(256);
  62.     /* The private chat buffer gets overallocated by 5 bytes to give some
  63.        breathing room in the event of a file i/o problem when the buffer
  64.        fills up. */
  65.     pcobuf = malloc(cfg.privchatbufsize + 5);
  66.     if (pcinam == NULL || pconam == NULL || pcobuf == NULL)
  67.         {
  68.         dofree(pcinam);
  69.         dofree(pconam);
  70.         dofree(pcobuf);
  71.         top_output(OUT_SCREEN, getlang("PrivChatNoMem"));
  72.         od_log_write(top_output(OUT_STRING, getlang("LogOutOfMemory")));
  73.         return;
  74.         }
  75.  
  76.     /* Delete any existing exit semaphore files for this node and the
  77.        other node. */
  78.     sprintf(pcinam, "%sEPR%05i.TCH", cfg.topworkpath, od_control.od_node);
  79.     sprintf(pconam, "%sEPR%05i.TCH", cfg.topworkpath, pcnode);
  80.     sh_unlink(pcinam);
  81.     sh_unlink(pconam);
  82.  
  83.     /* Open both the incoming chat file (this node) and the outgoing chat
  84.        file (other node). */
  85.     sprintf(pcinam, "%sPRV%05i.TCH", cfg.topworkpath, od_control.od_node);
  86.     sprintf(pconam, "%sPRV%05i.TCH", cfg.topworkpath, pcnode);
  87.     ipchatfil = sh_open(pcinam, O_RDWR | O_CREAT | O_BINARY, SH_DENYNONE,
  88.                         S_IREAD | S_IWRITE);
  89.     opchatfil = sh_open(pconam, O_RDWR | O_CREAT | O_BINARY | O_APPEND,
  90.                         SH_DENYNONE, S_IREAD | S_IWRITE);
  91.     chsize(opchatfil, 1);
  92.     if (ipchatfil == -1 || opchatfil == -1)
  93.         {
  94.         close(ipchatfil);
  95.         close(opchatfil);
  96.         dofree(pcinam);
  97.         dofree(pconam);
  98.         dofree(pcobuf);
  99.         top_output(OUT_SCREEN, getlang("PrivChatCantOpen"),
  100.                    ipchatfil == -1 ? pcinam : pconam);
  101.         od_log_write(top_output(OUT_STRING, getlang("LogPrivChatFileErr")));
  102.         return;
  103.         }
  104.  
  105.     /* Prepare the screen for chat. */
  106.     top_output(OUT_SCREEN, getlang("PrivChatPrefix"));
  107.  
  108.     /* Initialize variables. */
  109.     pcobufpos = 0;
  110.     sprintf(exitfilenam, "%sEPR%05i.TCH", cfg.topworkpath,
  111.             od_control.od_node);
  112.  
  113.     /* Main private chat loop. */
  114.     do
  115.         {
  116.         /* Private chat buffer polling is hardcoded at once every tenth of
  117.            a second. */
  118.         if ((((float) myclock() - (float) privlastpoll) / (float) CLK_TCK) >=
  119.             0.10) // Configurable later!
  120.             {
  121.             privchatreadbuf();
  122.             /* BBS polling is harcoded at once every second. */
  123.             if ((((float) myclock() - (float) privlastbbspoll) / (float) CLK_TCK) >=
  124.                 1.00) // Configurable later!
  125.                 {
  126.                 /* Incoming BBS pages are still displayed in chat mode. */
  127.                 if (cfg.bbstype != BBS_UNKNOWN && bbs_call_processmsgs)
  128.                     {
  129.                     (*bbs_call_processmsgs)();
  130.                     }
  131.                 privlastbbspoll = myclock();
  132.                 }
  133.             privlastpoll = myclock();
  134.             /* Flush the private chat buffer if there is something in it and
  135.                if the exit semaphore file is nonexistant. */
  136.             if (pcobufpos > 0 || !access(exitfilenam, 0))
  137.                 {
  138.                 privchatwritebuf();
  139.                 }
  140.             }
  141.  
  142.         /* Get the next keypress, without waiting for one. */
  143.         key = od_get_key(FALSE);
  144.         if (key)
  145.             {
  146.             /* Place the key in the buffer if one was pressed. */
  147.             privchatputch(key);
  148.             }
  149.         else
  150.             {
  151.             /* Timeslice if no activity. */
  152. #ifndef __OS2__
  153.             od_sleep(0);
  154. #else
  155.             DosSleep(10);
  156. #endif
  157.             }
  158.  
  159.         /* Timeslice again.  This can result in significant lag but it does
  160.            take a lot of pressure off the processor. */
  161. #ifndef __OS2__
  162.         od_sleep(0);
  163. #else
  164.         DosSleep(10);
  165. #endif
  166.         }
  167.     while(!exitflag); /* Loop until an exit is flagged. */
  168.  
  169.     /* Attempt to flush the outgoing buffer one last time. */
  170.     ttry = 0;
  171.     while(!privchatwritebuf() && ttry < cfg.privchatmaxtries) ttry++;
  172.  
  173.     /* Attempt to read the incoming buffer one last time. */
  174.     ttry = 0;
  175.     while(!privchatreadbuf() && ttry < cfg.privchatmaxtries) ttry++;
  176.  
  177.     close(ipchatfil);
  178.     close(opchatfil);
  179.  
  180.  
  181.     /* This node requested the exit. */
  182.     if (exitflag == 1)
  183.         {
  184.         FILE *exitfil = NULL; /* Exit semaphore file stream. */
  185.  
  186.         /* Create a 0-length file to signal the other node that we wish to
  187.            exit private chat. */
  188.         sprintf(outbuf, "%sEPR%05i.TCH", cfg.topworkpath, pcnode);
  189.         exitfil = _fsopen(outbuf, "wb", SH_DENYRW);
  190.         fclose(exitfil);
  191.         }
  192.     /* The other node requested the exit. */
  193.     if (exitflag == 2)
  194.         {
  195.         /* Inform the user of the exit. */
  196.         top_output(OUT_SCREEN, getlang("PrivChatSuffix2"),
  197.                    handles[pcnode].string);
  198.  
  199.         /* Wait until we have access to the exit semaphore file.  There is
  200.            usually a very short period where this node becomes aware of the
  201.            exit file before the other node has closed it. */
  202.         ttry = 0;
  203.         while(access(exitfilenam, 6) && ttry < cfg.privchatmaxtries) ttry++;
  204.  
  205.         /* Delete the file if time didn't elapse.  Deletion is not critical,
  206.            but it does keep things cleaner. */
  207.         if (ttry < cfg.privchatmaxtries)
  208.             {
  209.             sh_unlink(exitfilenam);
  210.             }
  211.  
  212.         /* Delete the chat files as they are only good for one chat
  213.            session. */
  214.         sh_unlink(pcinam);
  215.         sh_unlink(pconam);
  216.         }
  217.  
  218.     /* Free memory. */
  219.     dofree(pcinam);
  220.     dofree(pconam);
  221.     dofree(pcobuf);
  222.  
  223.     /* Clear the chat request nodes. */
  224.     privchatin = -1;
  225.     privchatout = -1;
  226.  
  227.     /* Log the end of the chat. */
  228.     od_log_write(top_output(OUT_STRING, getlang("LogEndPrivChat")));
  229.  
  230.     }
  231.  
  232. /* privchatreadbuf() - Reads incoming characters in private chat mode.
  233.    Parameters:  None.
  234.    Returns:  TRUE on error, FALSE on success.
  235. */
  236. XINT privchatreadbuf(void)
  237.     {
  238.     unsigned char *readbuf = NULL; /* Incoming text buffer. */
  239.     XINT res; /* Result code. */
  240.  
  241.     /* Test if the exit semaphore exists, meaning the other node wants to
  242.        exit private chat. */
  243.     if (!access(exitfilenam, 0))
  244.         {
  245.         exitflag = 2;
  246.         return 1;
  247.         }
  248.  
  249.     /* If the length of the incoming chat file is 0 or 1, there is no new
  250.        text to read. */
  251.     if (filelength(ipchatfil) <= 1L)
  252.         {
  253.         /* Timeslice. */
  254. #ifndef __OS2__
  255.         od_sleep(0);
  256. #else
  257.         DosSleep(10);
  258. #endif
  259.         return 1;
  260.         }
  261.  
  262.     /* The first byte of the file is locked to show the file is in use. */
  263.     res = rec_locking(REC_LOCK, ipchatfil, 0, 1);
  264.     if (res == -1)
  265.         {
  266.         return 0;
  267.         }
  268.  
  269.     /* Allocate a buffer that will hold the entire contents of the file,
  270.        which is normally no more than a few bytes.  Although the first byte
  271.        is not used for anything but locking, we need an extra byte for the
  272.        terminating \0 so it works out nicely to use the file size. */
  273.     readbuf = malloc(filelength(ipchatfil));
  274.     if (readbuf == NULL)
  275.         {
  276.         /* Signal an immediate exit if the allocation fails. */
  277.         top_output(OUT_SCREEN, getlang("PrivChatNoMem"));
  278.         exitflag = 1;
  279.         return 0;
  280.         }
  281.     memset(readbuf, 0, filelength(ipchatfil));
  282.  
  283.     /* Read the incoming text, which starts at byte 1 (the second byte in
  284.        the file). */
  285.     lseek(ipchatfil, 1, SEEK_SET);
  286.     read(ipchatfil, readbuf, filelength(ipchatfil) - 1);
  287.  
  288.     /* Select the other person's text colour. */
  289.     top_output(OUT_SCREEN, getlang("PrivChatCol2"));
  290.  
  291.     /* Display the text, ignoring any codes that may happen to be in the
  292.        text. */
  293.     outproccol = FALSE; outproclang = FALSE;
  294.     top_output(OUT_SCREEN, readbuf);
  295.     outproccol = TRUE; outproclang = TRUE;
  296.  
  297.     /* Truncate the file to one byte, effectively clearing it. */
  298.     chsize(ipchatfil, 1);
  299.  
  300.     /* Unlock the first byte, signifying we are done. */
  301.     res = rec_locking(REC_UNLOCK, ipchatfil, 0, 1);
  302.     dofree(readbuf);
  303.     if (res == -1)
  304.         {
  305.         return 0;
  306.         }
  307.  
  308.     return 1;
  309.     }
  310.  
  311. /* privchatputch() - Places a keypress in the outgoing private chat buffer.
  312.    Parameters:  pckey - Key value to insert.
  313.    Returns:  Nothing.
  314. */
  315. void privchatputch(XINT pckey)
  316.     {
  317.  
  318.     /* Attempt to flush the buffer if it is full. */
  319.     if (pcobufpos >= cfg.privchatbufsize)
  320.         {
  321.         if (!privchatwritebuf())
  322.             {
  323.             /* Abort if the buffer cannot be flushed.  This will likely lead
  324.                to an infinite loop, forcing the user to hang up, unless the
  325.                buffer can be cleared.  This isn't such a bad effect because
  326.                if the buffer is filling up then there are probably bigger
  327.                problems anyhow. */
  328.             top_output(OUT_SCREEN, getlang("PrivChatBufProb"));
  329.             return;
  330.             }
  331.         }
  332.  
  333.     /* Backspace. */
  334.     if (pckey == 8)
  335.         {
  336.         /* Backspaces are sent nondestructive. */
  337.         pcobuf[pcobufpos++] = 8;
  338.         top_output(OUT_SCREEN, "\b");
  339.         }
  340.     /* New line. */
  341.     if (pckey == 13)
  342.         {
  343.         /* A complete CRLF pair is sent. */
  344.         pcobuf[pcobufpos++] = 13;
  345.         pcobuf[pcobufpos++] = 10;
  346.         top_output(OUT_SCREEN, getlang("PrivChatEndLine"));
  347.         }
  348.     /* Escape (exit private chat). */
  349.     if (pckey == 27)
  350.         {
  351.         exitflag = 1;
  352.         top_output(OUT_SCREEN, getlang("PrivChatSuffix"));
  353.         }
  354.     /* Normal key. */
  355.     if (pckey >= 32 && pckey < 255)
  356.         {
  357.         /* Add the key to the buffer. */
  358.         pcobuf[pcobufpos++] = pckey;
  359.  
  360.         /* Select our text colour. */
  361.         top_output(OUT_SCREEN, getlang("PrivChatCol1"));
  362.  
  363.         /* top_output() needs a string. */
  364.         sprintf(outbuf, "%c", pckey);
  365.  
  366.         /* Output the string, ignoring any codes. */
  367.         outproccol = FALSE; outproclang = FALSE;
  368.         top_output(OUT_SCREEN, outbuf);
  369.         outproccol = TRUE; outproclang = TRUE;
  370.         }
  371.  
  372.     }
  373.  
  374. /* privchatwritebuf() - Writes the outgoing private chat buffer to disk.
  375.    Parameters:  None.
  376.    Returns:  TRUE on success, FALSE on error.
  377. */
  378. XINT privchatwritebuf(void)
  379.     {
  380.     XINT res; /* Result code. */
  381.  
  382.     /* Lock the first byte of the file while it is being updated. */
  383.     res = rec_locking(REC_LOCK, opchatfil, 0, 1);
  384.     if (res == -1)
  385.         {
  386.         return 0;
  387.         }
  388.  
  389.     /* Write the entire contents of the buffer. */
  390.     write(opchatfil, pcobuf, pcobufpos);
  391.     pcobufpos = 0;
  392.  
  393.     /* Unlock the first byte. */
  394.     res = rec_locking(REC_UNLOCK, opchatfil, 0, 1);
  395.     if (res == -1)
  396.         {
  397.         return 0;
  398.         }
  399.  
  400.     return 1;
  401.     }
  402.  
  403.