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

  1. /******************************************************************************
  2. CMI.C        Channel management interface (CMI).
  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 is an API of function calls that do all of the low-level work
  20. involved in managing channels.  The CMI handles such tasks as maintaining the
  21. channel index (CHANIDX.TCH), tracking which users are in each channel, and
  22. moving users from one channel to another.  Although the CMI is not particularly
  23. complicated, it is very delicate and should not be modified until you have a
  24. thorough understanding of how it works.  See CHANNELS.TXT for full details.
  25. CMI return codes are defined in TOP.H and explained in CHANNELS.TXT.
  26. ******************************************************************************/
  27.  
  28. #include "top.h"
  29.  
  30. /* The entire CMI relies on a couple of global variables.
  31.    cmibuf - Data for the user's CURRENT channel, loaded from CHANIDX.TCH.
  32.    cmirec - Record number inside CHANIDX.TCH that refers to the user's
  33.             CURRENT channel.
  34. */
  35.  
  36. /* cmi_join() - Joins a channel.
  37.    Parameters:  chnum - Channel number to join.
  38.    Returns:  CMI return code.
  39. */
  40. char cmi_join(unsigned long chnum)
  41.     {
  42.     XINT res; /* Result code. */
  43.  
  44.     /* Load channel data for the new channel. */
  45.     res = cmi_load(chnum);
  46.     if (res == CMI_OKAY)
  47.         {
  48.         /* Make sure user is allowed to join. */
  49.         res = cmi_check();
  50.         if (res == CMI_OKAY)
  51.             {
  52.             /* Add the user to the channel and save. */
  53.             cmi_adduser();
  54.             res = cmi_save();
  55.             }
  56.         else
  57.             {
  58.             /* Resave the channel, which unlocks its data. */
  59.             cmi_save();
  60.             }
  61.         return res;
  62.         }
  63.     /* Only abort if a serious error occurred. */
  64.     if (res != CMIERR_NOTFOUND)
  65.         {
  66.         return res;
  67.         }
  68.  
  69.     /* If the user is trying to join another user's personal channel and
  70.        the channel data is not found, then the other user has not yet
  71.        entered his or her channel, which means logically that the user
  72.        cannot have been invited.  Sysops are exempt from requiring
  73.        invitations. */
  74.     if (chnum >= 4000000000UL && chnum <= 4000999999UL &&
  75.         (XINT) (chnum - 4000000000UL) != od_control.od_node &&
  76.         user.security < cfg.sysopsec)
  77.         {
  78.         return CMIERR_NOINV;
  79.         }
  80.  
  81.     /* Create new data for this channel. */
  82.     res = cmi_make(chnum);
  83.     if (res == CMI_OKAY)
  84.         {
  85.         /* Add the user to the channel and save. */
  86.         cmi_adduser();
  87.         res = cmi_save();
  88.         }
  89.  
  90.     return res;
  91.     }
  92.  
  93. /* cmi_unjoin() - Unjoins (leaves) a channel.
  94.    Parameters:  chnum - Channel number to unjoin.
  95.    Returns:  CMI return code.
  96. */
  97. char cmi_unjoin(unsigned long chnum)
  98.     {
  99.     XINT res; /* Result code. */
  100.  
  101.     /* Load the channel data. */
  102.     res = cmi_load(chnum);
  103.     if (res != CMI_OKAY)
  104.         {
  105.         return res;
  106.         }
  107.     /* Subtract (remove) the user and save. */
  108.     cmi_subuser();
  109.     res = cmi_save();
  110.  
  111.     return res;
  112.     }
  113.  
  114. /* cmi_adduser() - Adds one user to the current channel.
  115.    Parameters:  None.
  116.    Returns:  Nothing.
  117. */
  118. void cmi_adduser(void)
  119.     {
  120.  
  121.     /* Add one to the current user count. */
  122.     cmibuf.usercount++;
  123.  
  124.     }
  125.  
  126. /* cmi_subuser() - Subtracts one user from the current channel.
  127.    Parameters:  None.
  128.    Returns:  Nothing.
  129. */
  130. void cmi_subuser(void)
  131.     {
  132.  
  133.     /* Subtract the user. */
  134.     if (--cmibuf.usercount == 0)
  135.         {
  136.         /* If this user was the last, flag the channel as deleted. */
  137.         cmibuf.type = CHAN_DELETED;
  138.         }
  139.  
  140.     }
  141.  
  142. /* cmi_load() - Loads channel data from CHANIDX.TCH.
  143.    Parameters:  chnum - Channel number to load data for.
  144.    Returns:  CMI return code.
  145.    Notes:  Always call this function to load channel data.  Never call
  146.            cmi_loadraw() directly.  This function locks the data then retreives
  147.            it.  Upon success, the data is NOT UNLOCKED because it is assumed it
  148.            needs to be modified immediately.  To unlock the data, call
  149.            cmi_save() (preferred) or cmi_unlock().
  150. */
  151. char cmi_load(unsigned long chnum)
  152. {
  153.     XINT d, res; /* Counter, result code. */
  154.  
  155.     /* Scan each record in CHANIDX.TCH. */
  156.     for (d = 0; d < filelength(chidxfil) / sizeof(chan_idx_typ); d++)
  157.         {
  158.         /* Lock the data. */
  159.         res = cmi_lock(d);
  160.         if (res != CMI_OKAY)
  161.             {
  162.             return res;
  163.             }
  164.         /* Load the raw data into the current channel data buffer. */
  165.         res = cmi_loadraw(d);
  166.         if (res != CMI_OKAY)
  167.             {
  168.             return res;
  169.             }
  170.         /* Check for a match and that the channel is not "deleted". */
  171.         if (cmibuf.channum == chnum && cmibuf.type != CHAN_DELETED)
  172.             {
  173.             /* Match found, flag the current record and return okay. */
  174.             cmirec = d;
  175.             return CMI_OKAY;
  176.             }
  177.         /* This wasn't a match so the data is unlocked. */
  178.         res = cmi_unlock(d);
  179.         if (res != CMI_OKAY)
  180.             {
  181.             return res;
  182.             }
  183.         }
  184.  
  185.     return CMIERR_NOTFOUND;
  186.     }
  187.  
  188. /* cmi_save() - Saves channel data to CHANIDX.TCH.
  189.    Parameters:  None.
  190.    Returns:  CMI return code.
  191.    Notes:  cmirec must be set to the record the data is to be saved to.
  192.            This is typically done with a cmi_load() call.  This function
  193.            will NOT LOCK THE DATA!  Again, it is assumed cmi_load() already
  194.            did this.  NEVER CALL THIS FUNCTION WITHOUT LOCKING THE DATA
  195.            FIRST!
  196. */
  197. char cmi_save(void)
  198.     {
  199.     XINT res; /* Result code. */
  200.  
  201.     /* Save the raw data to CHANIDX.TCH. */
  202.     res = cmi_saveraw(cmirec);
  203.     if (res != CMI_OKAY)
  204.         {
  205.         return res;
  206.         }
  207.     /* Unlock the data. */
  208.     res = cmi_unlock(cmirec);
  209.     if (res != CMI_OKAY)
  210.         {
  211.         return res;
  212.         }
  213.  
  214.     return CMI_OKAY;
  215.     }
  216.  
  217. /* cmi_loadraw() - Loads raw CMI data from CHANIDX.TCH.
  218.    Parameters:  rec - Record number of the data to load.
  219.    Returns:  CMI error code.
  220. */
  221. char cmi_loadraw(XINT rec)
  222.     {
  223.  
  224.     // Should use errchecking here!
  225.  
  226.     /* Load the raw data. */
  227.     lseek(chidxfil, (long) rec * sizeof(chan_idx_typ), SEEK_SET);
  228.     read(chidxfil, &cmibuf, sizeof(chan_idx_typ));
  229.  
  230.     return CMI_OKAY;
  231.     }
  232.  
  233. /* cmi_saveraw() - Saves raw CMI data to CHANIDX.TCH.
  234.    Parameters:  Record number of the data to save.
  235.    Returns:  CMI error code.
  236. */
  237. char cmi_saveraw(XINT rec)
  238.     {
  239.  
  240.     // Should use errchecking here!
  241.  
  242.     /* Save the raw data. */
  243.     lseek(chidxfil, (long) rec * sizeof(chan_idx_typ), SEEK_SET);
  244.     write(chidxfil, &cmibuf, sizeof(chan_idx_typ));
  245.  
  246.     return CMI_OKAY;
  247.     }
  248.  
  249. /* cmi_make() - Creates new channel data inside CHANIDX.TOP.
  250.    Parameters:  chnum - Channel number being created.
  251.    Returns:  CMI error code.
  252.    Notes:  Like cmi_load(), the data is NOT UNLOCKED after it is created!
  253.            This function is intended to work just like cmi_load() except
  254.            that it creates the channel too.  In fact, it could have easily
  255.            been called from cmi_load() but it was decided to let the calling
  256.            function do this in case creation of a new channel was
  257.            undesirable.
  258. */
  259. char cmi_make(unsigned long chnum)
  260.     {
  261.     XINT d, e, res; /* Counter, counter, result code. */
  262.     chan_idx_typ chantmp; /* Temporary data buffer. */
  263.  
  264.     /* Prepare new data. */
  265.     chantmp.channum = chnum;
  266.     /* Normal channels and conferences are "open". */
  267.     if (chnum <= 3999999999UL || chnum >= 4001000000UL)
  268.         {
  269.         chantmp.type = CHAN_OPEN;
  270.         }
  271.     /* Personal channels are "closed". */
  272.     if (chnum >= 4000000000UL && chnum <= 4000999999UL)
  273.         {
  274.         chantmp.type = CHAN_CLOSED;
  275.         }
  276.     chantmp.usercount = 0;
  277.     chantmp.topic[0] = '\0';
  278.     chantmp.modnode = od_control.od_node;
  279.     /* Insert the forced topic for this channel if one is defined. */
  280.     if ((res = findchannelrec(chnum)) > -1)
  281.         {
  282.         if (chan[res].topic[0] != '\0')
  283.             {
  284.             strcpy(chantmp.topic, chan[res].topic);
  285.             }
  286.         }
  287.     /* Clear all special nodes. */
  288.     for (e = 0; e < MAXCHANSPECNODES; e++)
  289.         {
  290.         chantmp.specnode[e] = -1;
  291.         }
  292.  
  293.     /* Search for a blank (deleted) channel data record. */
  294.     for (d = 0; d < filelength(chidxfil) / sizeof(chan_idx_typ); d++)
  295.         {
  296.         /* Lock the data. */
  297.         res = cmi_lock(d);
  298.         if (res != CMI_OKAY)
  299.             {
  300.             return res;
  301.             }
  302.         /* Load the data. */
  303.         res = cmi_loadraw(d);
  304.         if (res != CMI_OKAY)
  305.             {
  306.             return res;
  307.             }
  308.         /* One condition being true should never occur (it should be both
  309.            or neither), but an OR is used in case something screwed up
  310.            somewhere else. */
  311.         if (cmibuf.type == CHAN_DELETED || cmibuf.usercount < 1)
  312.             {
  313.             /* Deleted record has been found.  Flag it. */
  314.             cmirec = d;
  315.             /* Copy the data to the current channel buffer. */
  316.             memcpy(&cmibuf, &chantmp, sizeof(chan_idx_typ));
  317.             /* Save the new data. */
  318.             res = cmi_saveraw(cmirec);
  319.             if (res == CMI_OKAY)
  320.                 {
  321.                 return CMI_OKAY;
  322.                 }
  323.             }
  324.         /* Unlock the channel data. */
  325.         res = cmi_unlock(d);
  326.         if (res != CMI_OKAY)
  327.             {
  328.             return res;
  329.             }
  330.         }
  331.  
  332.     /* If TOP gets to this point it means there is no blank record free so
  333.        we have to create a new one. */
  334.  
  335.     /* d will now equal the total number of records in the file, thus the
  336.        number of the new record. */
  337.     cmirec = d;
  338.     /* Copy the data to the current channel buffer. */
  339.     memcpy(&cmibuf, &chantmp, sizeof(chan_idx_typ));
  340.     /* Lock the new location. */
  341.     res = cmi_lock(cmirec);
  342.     if (res != CMI_OKAY)
  343.         {
  344.         return res;
  345.         }
  346.     /* Save the new data. */
  347.     res = cmi_saveraw(cmirec);
  348.  
  349.     return res;
  350.     }
  351.  
  352. /* cmi_lock() - Locks channel data in CHANIDX.TOP
  353.    Parameters:  rec - Record number to lock.
  354.    Returns:  CMI error code.
  355. */
  356. char cmi_lock(XINT rec)
  357.     {
  358.     XINT res; /* Result code. */
  359.  
  360.     /* Lock the data. */
  361.     res = rec_locking(REC_LOCK, chidxfil, (long) rec * sizeof(chan_idx_typ),
  362.                       sizeof(chan_idx_typ));
  363.     if (res)
  364.         {
  365.         return CMIERR_FILEIO;
  366.         }
  367.  
  368.     return CMI_OKAY;
  369.     }
  370.  
  371. /* cmi_unlock() - Unlocks channel data in CHANIDX.TOP
  372.    Parameters:  rec - Record number to unlock.
  373.    Returns:  CMI error code.
  374. */
  375. char cmi_unlock(XINT rec)
  376.     {
  377.     XINT res; /* Result code. */
  378.  
  379.     /* Unlock the data. */
  380.     res = rec_locking(REC_UNLOCK, chidxfil, (long) rec * sizeof(chan_idx_typ),
  381.                       sizeof(chan_idx_typ));
  382.     if (res)
  383.         {
  384.         return CMIERR_FILEIO;
  385.         }
  386.  
  387.     return CMI_OKAY;
  388.     }
  389.  
  390. /* cmi_check() - Checks if the user can join the current channel.
  391.    Parameters:  None.
  392.    Returns: CMI error code.
  393.    Notes:  The current channel data must be in cmibuf.
  394. */
  395. char cmi_check(void)
  396.     {
  397.     XINT d; /* Counter. */
  398.  
  399.     /* No checking needs to be done for sysops or if the current channel
  400.        is the user's personal channel. */
  401.     if (user.security >= cfg.sysopsec ||
  402.         cmibuf.channum ==
  403.             ((unsigned long) od_control.od_node + 4000000000UL))
  404.         {
  405.         return CMI_OKAY;
  406.         }
  407.  
  408.     /* Iterate through all special nodes. */
  409.     for (d = 0; d < MAXCHANSPECNODES; d++)
  410.         {
  411.         if (cmibuf.type == CHAN_OPEN &&
  412.             cmibuf.specnode[d] == od_control.od_node)
  413.             {
  414.             /* In open channels, if the user exists as a special node it
  415.                means the user has been banned from the channel. */
  416.             return CMIERR_BANNED;
  417.             }
  418.         if (cmibuf.type == CHAN_CLOSED &&
  419.             cmibuf.specnode[d] == od_control.od_node)
  420.             {
  421.             /* In closed channels, if the user exists as a special node it
  422.                means they have been invited and thus may join the channel. */
  423.             return CMI_OKAY;
  424.             }
  425.         }
  426.  
  427.     /* If we've made it to this point, the user has either not been banned
  428.        or not been invited, depending on the channel type. */
  429.     return (cmibuf.type == CHAN_OPEN ? CMI_OKAY : CMIERR_NOINV);
  430.     }
  431.  
  432. /* cmi_setspec() - Sets a special node in the channel data.
  433.    Parameters:  node - Node number to set the status for.
  434.                 status - TRUE to add the node to the special nodes list,
  435.                          FALSE to remove the node.
  436.    Returns:  CMI error code.
  437.    Notes:  See the discussion of special nodes in CHANNELS.TXT.
  438. */
  439. char cmi_setspec(XINT node, char status)
  440.     {
  441.     XINT d; /* Counter. */
  442.  
  443.     if (status)
  444.         {
  445.         /* If the node is to be added to the list, the list is checked to
  446.            be sure the user isn't already on it, to avoid wasting specnode
  447.            slots. */
  448.         for (d = 0; d < MAXCHANSPECNODES; d++)
  449.             {
  450.             if (cmibuf.specnode[d] == node)
  451.                 {
  452.                 /* The node was found so no changes need to be made. */
  453.                 return CMI_OKAY;
  454.                 }
  455.             }
  456.         }
  457.  
  458.     /* Loop through each special node. */
  459.     for (d = 0; d < MAXCHANSPECNODES; d++)
  460.         {
  461.         if (status)
  462.             {
  463.             /* If the node is being added, find the first free slot. */
  464.             if (cmibuf.specnode[d] == -1)
  465.                 {
  466.                 /* Found a free slot, insert the node into it. */
  467.                 cmibuf.specnode[d] = node;
  468.                 return CMI_OKAY;
  469.                 }
  470.             }
  471.         else
  472.             {
  473.             /* If the node is being removed, find it in the list. */
  474.             if (cmibuf.specnode[d] == node)
  475.                 {
  476.                 /* Found the node, clear its slot. */
  477.                 cmibuf.specnode[d] = -1;
  478.                 return CMI_OKAY;
  479.                 }
  480.             }
  481.         }
  482.  
  483.     /* At this point, either the special nodes list was full (if the node
  484.        was to be added) or the node was not found (if the node was being
  485.        deleted) so return an error code. */
  486.     return (status ? CMIERR_FULL : CMIERR_NOTFOUND);
  487.     }
  488.  
  489. /* cmi_busy() - Engages "busy" mode.
  490.    Parameters:  None.
  491.    Returns:  CMI error code.
  492.    Notes:  This function saves the current channel, then joins the
  493.            designated busy channel (usually 0xFFFFFFFF).  It should be
  494.            called before the user enters an editor or some other involving
  495.            feature (like Sysop Chat).  Manually joining the busy channel
  496.            (i.e. without using this function) is not recommended!
  497. */
  498. char cmi_busy(void)
  499.     {
  500.     XINT res; /* Result code. */
  501.  
  502.     /* Unjoin the current channel. */
  503.     res = cmi_unjoin(curchannel);
  504.     if (res != CMI_OKAY)
  505.         {
  506.         return res;
  507.         }
  508.     /* Remember the current channel. */
  509.     cmiprevchan = curchannel;
  510.  
  511.     /* Join the busy channel. */
  512.     res = cmi_join(BUSYCHANNEL);
  513.     if (res != CMI_OKAY)
  514.         {
  515.         return res;
  516.         }
  517.  
  518.     /* Update the NODEIDX to show the busy channel. */
  519.     node->channel = BUSYCHANNEL;
  520.     save_node_data(od_control.od_node, node);
  521.  
  522.     /* Set the new channel to the busy channel. */
  523.     curchannel = BUSYCHANNEL;
  524.  
  525.     return CMI_OKAY;
  526.     }
  527.  
  528. /* cmi_unbusy() - Disengages "busy" mode.
  529.    Parameters:  None.
  530.    Returns:  CMI error code.
  531.    Notes:  This function processes any leftover messages on the busy
  532.            channel, then remembers the original channel and joins it.  NEVER
  533.            CALL THIS FUNCTION WITHOUT CALLING cmi_busy() FIRST!  Manually
  534.            unbusying the user is not recommended.
  535. */
  536. char cmi_unbusy(void)
  537.     {
  538.     XINT res; /* Result code. */
  539.  
  540.     /* Process leftover messages on the busy channel. */
  541.     process_messages(FALSE);
  542.  
  543.     /* Unjoin the busychannel. */
  544.     res = cmi_unjoin(BUSYCHANNEL);
  545.     if (res != CMI_OKAY)
  546.         {
  547.         return res;
  548.         }
  549.  
  550.     /* Restore the original channel. */
  551.     curchannel = cmiprevchan;
  552.  
  553.     /* Update the NODEIDX with the original channel. */
  554.     node->channel = curchannel;
  555.     save_node_data(od_control.od_node, node);
  556.  
  557.     /* Rejoin the original channel. */
  558.     res = cmi_join(curchannel);
  559.     if (res != CMI_OKAY)
  560.         {
  561.         return res;
  562.         }
  563.  
  564.     return CMI_OKAY;
  565.     }
  566.