/*
    This source code is protected under the GNU General Public License (GPL),
    found at http://www.gnu.org/licenses/gpl.html
*/

#include <stdio.h>
#include <sys/socket.h>
#include "s-pluginapi.h"

#define RL 32  // This plugin will hold 32 records of <{REGHASH} "IP Address">

  static uint32 pwr(uint16 base, uint8 exp);

  typedef struct
{
    uint32 Seed;
    uint32 ipMin;
    uint32 ipMax;
} HostMgr, *HostMgrPtr;

  static uint8 RegHash[] =
{
    4,  //A
    5,  //B
    6,  //C
    7,  //D
    0,  //E
    1,  //F
    2,  //G
    3,  //H
    12, //I
    0,  //J
    0,  //K
    0,  //L
    8,  //M
    9,  //N
    10, //O
    11  //P
};

  static uint8 VldChrs[] =
{
    '{',
    'A',
    'B',
    'C',
    'D',
    'E',
    'F',
    'G',
    'H',
    'I',
    'M',
    'N',
    'O',
    'P',
    '}',
    ' ',
    '\t',
    '\"',
    '0',
    '1',
    '2',
    '3',
    '4',
    '5',
    '6',
    '7',
    '8',
    '9',
    '*',
    '.',
    0
};

  static char *config;
  static uint8 nbrRecs;
  static HostMgr Recs[RL];

  AnyType initializeServerPlugin(PluginID pluginID, int argc, char *argv[])
{
    setPluginUserInfoSize(pluginID, sizeof(char));
    if (argc > 1)
    {
        config = argv[1];
    }
    FormattedLogMessage(NULL, "HostManager", "v1.0 (Limit: %d) (2004-2005) by Paul Taylor (Janus) <janus@thepalacehost.com>", RL);
    scheduleTimerEvent(pluginID, 0, 0);
    return (AnyType) pluginID;
}

  void noteUserCreation(ServerState *state, AnyType pluginDat, ServerUserRec *user)
{
    *((char *)getPluginUserInfo((PluginID) pluginDat, user)) = 0;
}
  /*
    If you ask why I didn't code the user verification system into the ClientMsg
    function, here's why. Verifying this way allows all other plugin security,
    including the server itself to verify a user's information that has been sent
    is valid. Other issues I got into just grew tiresome and boring. There's nothing
    wrong with the way I did this here *wink* // Janus
  */
  void noteUserRoomEntry(ServerState *state, AnyType pluginDat, ServerUserRec *user, ServerRoomRec *room)
{
    char *Checked = (char *)getPluginUserInfo((PluginID) pluginDat, user);
    if (!(*Checked) && user->loggedOn)
    {
        (*Checked)++;
        uint16 i;
        uint32 ipAdr = htonl(user->feIPAddr);
        /*
            Show me da SEED! // Janus
        */
        uint32 Seed = user->crc ^ user->counter ^ 0x9602C9BFL;
        for (i = 0; i < nbrRecs; i++)
        {
            if (Seed == Recs[i].Seed && ipAdr >= Recs[i].ipMin && ipAdr <= Recs[i].ipMax)
            {
                /*
                    Sets wiz/god (op/admin) flags to 3, OR forces these bits to TRUE // Janus
                */
                user->flags |= 3;
                /*
                    Since the client doesn't always use null bytes at the end of a username,
                    also considering strings are considered terminated by a null character,
                    we force the user's name to end with a null byte (if it's length is less than 31)! // Janus
                */
                Checked = user->user.name;
                if (*Checked < 31)
                {
                    user->user.name[*Checked + 1] = 0;
                }
                /*
                    Lets leave a special note that indeed, an Admin was here. // Janus
                */
                FormattedLogMessage(user, "HostManager", "(Reigstered Host Admin)");
                sendMsgUserStatus(toUser(user), user, user->flags, "");
                /*
                    Alert the staff that it's just a friend, not an intruder. // Janus
                */
                PerformPage(user, "Attention on the deck! A Host Admin has come aboard!");
                UserMessage(user, "Welcome Host Admin %0.31s!", ++Checked);
                break;
            }
        }
    }
}

  void handleTimerEvent(ServerState *state, AnyType pluginDat, AnyType timerDat)
{
    uint8 iBfr[64];
    uint8 e, m, i, j, k;
    char *lBfr, *Ptr;
    nbrRecs = 0;
    FILE *STDIN = fopen(config, "r");
    /*
        Required, incase permissions are wrong, trying to read from
        a file that's permissioned off would crash our server! // Janus
    */
    if (STDIN != NULL)
    {
ReadErr:
        while (!feof(STDIN) && nbrRecs < RL)
        {
            m = 0;
            lBfr = iBfr;
            fgets(iBfr, 63, STDIN);
            Recs[nbrRecs].Seed = 0;
            Recs[nbrRecs].ipMin = 0;
            Recs[nbrRecs].ipMax = 0;
            while (*lBfr)
            {
                if (*lBfr == '#')
                {
                    goto ReadErr;
                }
                e = 0;
                for (i = 0; i < strlen(VldChrs); i++)
                {
                    if (*lBfr == VldChrs[i])
                    {
                        e = 1;
                        break;
                    }
                }
                if (e == 0)
                {
                    goto ReadErr;
                }
                switch (m)
                {
                    case 0:
                        /*
                             This section resets the previous input record entry for the SEED. // Janus
                        */
                        if (*lBfr == '{')
                        {
                             m++;
                             j = 0;
                        }
                        break;
                    case 1:
                        /*
                             This section builds the Reg SEED from the Hash in the bless file. // Janus
                        */
                        if (*lBfr == '}')
                        {
                            m++;
                            break;
                        }
                        if (*lBfr >= 'A' && *lBfr <= 'P' && j < 9)
                        {
                            Recs[nbrRecs].Seed += (RegHash[*lBfr - 'A'] * pwr(13, j++)); // 'A' = 65 // Janus
                        } else {
                            goto ReadErr;
                        }
                        break;
                    case 2:
                        /*
                             This section resets the previous input record entry for the ipMin/ipMax. // Janus
                        */
                        if (*lBfr == '\"')
                        {
                            m++;
                            j = 4;
                            Ptr = lBfr + 1;
                        }
                        break;
                    case 3:
                        /*
                            This section builds the ipMin/ipMax ranges [(#/*).(#/*).(#/*).(#/*)]. // Janus
                        */
                        if (!j)
                        {
                            goto ReadErr;
                        }
                        if (*lBfr == '.' || *lBfr == '\"')
                        {
                            /*
                                This part may not be neccessary since atoi() only converts integers. // Janus
                            */
                            if (*lBfr == '.')
                            {
                                *lBfr = 0;
                            }
                            if (*Ptr == '*')
                            {
                                if (Ptr == (lBfr - 1))
                                {
                                    /*
                                        Since this is a range, only increase the upper value. // Janus
                                    */
                                    Recs[nbrRecs].ipMax += (255 * pwr(256, --j));
                                } else {
                                    /*
                                        Somebody didn't use the range (*) character properly. // Janus
                                    */
                                    goto ReadErr;
                                }
                            } else {
                                /*
                                    Since this is a range, this builds up both the upper/lower values. // Janus
                                */
                                k = atoi(Ptr);
                                Recs[nbrRecs].ipMin += (k * pwr(256, --j));
                                Recs[nbrRecs].ipMax += (k * pwr(256, j));
                            }
                            Ptr = lBfr + 1;
                        }
                        if (*lBfr == '\"')
                        {
                            /*
                                All security/format checks passed, we're clear to try another round/line of the file! // Janus
                            */
                            ++nbrRecs;
                            goto ReadErr;
                        }
                        break;
                }
                ++lBfr;
            }
        }
        fclose(STDIN);
    }
    /*
        Reloads the hostmgr.bless file every 2.5 minutes
        (150 seconds) = 1 Swatch Time Beat (Swiss Time) // Janus
    */
    scheduleTimerEvent((PluginID) pluginDat, 0, 150);
}
  /*
      I know <math.h> has a pow() function, but our server
      didn't like using pow(), so, I rigged up my own! // Janus
  */
  static uint32 pwr(uint16 base, uint8 exp)
{
    uint32 i = 1;
    while (exp--)
    {
        i *= base;
    }
    return i;
}

  /*
      I would like to thank OpenText for not butting into palace
      development projects in the works, even if Phalanx does suck.
      I would like to thank my crew, past and present from MPP/PEG,
      Aephix, Aephix Core, Gemini Project, and now ThePalaceHost!
      Specifically Darryl, Richard, Patrick, and Scott. You guys
      are the best and you've always been there in spirit.

      I didn't make the initial plugin SDK so I'm not going to open
      source that, but if you can find a copy, I wish you the best
      of luck, might have to switch the linker as I did from ELF
      (Solaris/Linux) to OSF (Unix).

      Aight! PEACE! // Janus

      PS To Joe (owner of PalaceBox), unlike you, I enjoy sharing knowledge.
  */
