#include "headers.h" /* has all important stuff */

/* this file has various functions to get/process data from client */

/* create directories for clients */
void createDirs(int client)
{
   int res;
   char curClientID[8], curSubID[8];

   memset(curClientID, 0, sizeof(curClientID));
   memset(curSubID, 0, sizeof(curSubID));
   
   errno = 0;

   /* -----------  FOR SRS DIRECTORY ------- */

   debug("creating %s\n%c", SRSDIR, (client == 0 ? '\n' : '\0'));

   res = mkdir(SRSDIR, (0766 & ~077));
   if (res == ERROR)
      if (errno != EEXIST)
      {
         error("error making %s: %s\n\n", SRSDIR, strerror(errno));
         quit(ERROR);
      }

   errno = 0;
   res = chdir(SRSDIR);
   if (res == ERROR)
   {
      error("error chdir()'ing to %s: %s\n\n", SRSDIR, strerror(errno));
      quit(ERROR);
   }

   /* --------------------- FOR CERTS DIR ------------------------- */

   res = mkdir("certs", (0766 & ~077));
   if (res == ERROR)
      if (errno != EEXIST)
      {
         error("error making %s/%s: %s\n\n", SRSDIR, CLIENTDIRS,
               strerror(errno));

         quit(ERROR);
      }

   if (client == 0) return; /* no client.. so don't create client dirs */

   /* ---------- FOR CLIENTS DIRECTORY -------- */
   errno = 0;

   debug("creating %s/%s\n", SRSDIR, CLIENTDIRS);

   res = mkdir(CLIENTDIRS, (0766 & ~077));
   if (res == ERROR)
      if (errno != EEXIST)
      {
         error("error making %s/%s: %s\n\n", SRSDIR, CLIENTDIRS,
               strerror(errno));

         quit(ERROR);
      }

   errno = 0;
   res = chdir(CLIENTDIRS);
   if (res == ERROR)
   {
      error("error chdir()'ing to %s/%s: %s\n\n", SRSDIR, CLIENTDIRS,
            strerror(errno));

      quit(ERROR);
   }


   /* FOR CURRENT ID ------------------ */


   (void)sprintf(curClientID, "%04d", (clients[curClient]).ID);

   debug("creating %s/%s/%s\n", SRSDIR, CLIENTDIRS, curClientID);

   errno = 0;

   res = mkdir(curClientID, (0766 & ~077)); 
   if (res == ERROR)
      if (errno != EEXIST) 
      {
         error("error creating %s/%s/%s: %s\n\n", SRSDIR, CLIENTDIRS,
               curClientID, strerror(errno));

         quit(ERROR);
      }

   errno = 0;
   res = chdir(curClientID);
   if (res == ERROR)
   {
      error("error chdir()'ing %s/%s/%s: %s\n\n", SRSDIR, CLIENTDIRS,
            curClientID, strerror(errno));

      quit(ERROR);
   }

   
/* FOR SUB ID ------------------- */

   if (debugging == 1)
   {
      (void)putchar('\n');
      (void)write(dblogfd, "\n", 1);
   }

   debug("(in createDirs) numSubIDs = %d\n", clients[curClient].numSubIDs);

   (void)sprintf(curSubID, "%d", (clients[curClient]).numSubIDs);

   debug("creating %s/%s/%s/%s\n\n", SRSDIR, CLIENTDIRS, curClientID,
         curSubID);

   errno = 0;
   res = mkdir(curSubID, (0766 & ~077));
   if (res == ERROR)
      if (errno != EEXIST)
      {
         error("error creating %s/%s/%s/%s: %s\n\n", 
               SRSDIR, CLIENTDIRS, curClientID, curSubID,
               strerror(errno));

         quit(ERROR);

      }

   errno = 0;
   res = chdir(curSubID);
   if (res == ERROR)
      if (errno != EEXIST) 
      {
         error("error chdir()'ing to %s/%s/%s/%s: %s\n\n", SRSDIR,
               CLIENTDIRS, curClientID, curSubID, strerror(errno));

         quit(ERROR);
      }

   if ((client != 1) && debugging == 1)
   {
      (void)putchar('\n');
      (void)write(dblogfd, "\n", 1);
   }
}


/* ----------------------- */


/* make sure our parent pid is pidstats[curPid-1].pid */
void checkValid()
{
   int res = 0;

   /* we have to kill our parent now... */
   if ((clients[curClient].pidstats[curPid].ppid !=
        clients[curClient].pidstats[curPid-1].pid) ||
       (clients[curClient].pidstats[curPid].ppid != getppid()))
   {
      error("my parent pid [pid %d] is not pidstats[%d].pid (pid %d)!\n\n", 
            getppid(), curPid-1, clients[curClient].pidstats[curPid-1].pid);

      res = seteuid(0);
      if (res == ERROR)
      {
         error("error with seteuid: %s\n\n", strerror(errno));
         quit(ERROR);
      }

      if (clients[curClient].pidstats[curPid].ppid != getpid())
      {
         res = kill(clients[curClient].pidstats[curPid].ppid, SIGTERM);

         if (res == ERROR) 
            error("error killing pid %d: %s\n\n", 
                  clients[curClient].pidstats[curPid].ppid, strerror(errno));
      }

      if (clients[curClient].pidstats[curPid-1].ppid != getpid())
      {
         res = kill(clients[curClient].pidstats[curPid-1].pid, SIGTERM);

         if (res == ERROR)
            error("error killing pid %d: %s\n\n",
                  clients[curClient].pidstats[curPid-1].pid, strerror(errno));
      }

      res = kill(getppid(), SIGTERM);

      if (res == ERROR)
         error("error killing pid %d: %s\n\n", getppid(), strerror(errno));

      res = seteuid(pwd->pw_uid);
      if (res == ERROR)
      {
         error("error with seteuid: %s\n\n", strerror(errno));
         quit(ERROR);
      }

      quit(ERROR);
   }
}


/* ----------------------- */


/* check to see if we're over the max sub-ID limit */
int checkMaxSubIDs()
{
   if (clients[curClient].numSubIDs > MAXSUBIDS)
   {
      signal(SIGPIPE, SIG_IGN);

      error("too many connections from client ID %04\n\n",
            clients[curClient].ID);

      /* report error to client */
      send_data((clients[curClient]).sockfd, "ERROR: %s\n", MAXSUBIDERROR);

      (void)sleep(MAXPAUSE); /* give them time to handle this */


      clients[curClient].numSubIDs--;
      close(clients[curClient].sockfd);

      return ERROR;
   }

   return 0;
}


/* ----------------------- */


/* parse and setup args */
void doArgs(int argc, char **argv)
{
   int opt;

   if (argc > 1)
   { 
      /* -h == help, -d == debugging enabled,     */
      /* -e == error file, -s == server list file */
      /* -p == port to bind locally               */

      while ((opt = getopt(argc, argv, "hde:s:p:")) != ERROR)
      {
         int  testfd1;
         FILE *testfd; /* test if user-defined files work */

         switch(opt)
         {
            case 'h':
               usage(argv[0]);
               exit(SUCCESS);

            case 'd':
               debugging = 1;
               debug("debugging enabled\n");

               break;

            case 'e':
               errorFile = optarg;
               debug("using %s to log errors\n", errorFile);

               while(1)
               {
                  testfd1 = open(errorFile, O_CREAT | O_WRONLY | O_APPEND, 
                                 0600);

                  if (testfd1 == ERROR) 
                  {
                     if (errno == EINTR) continue;

                     (void)fprintf(stderr, "Unable to open %s: %s\n\n",
                                   errorFile, strerror(errno));

                     exit(ERROR);
                  }

                  else break;
               }

               (void)close(testfd1);
               break;

            case 's':
               servListFile = optarg;
               debug("using %s to get the server list\n", servListFile);

               while(1)
               {
                  testfd = fopen(servListFile, "r");
                  if (testfd == NULL)
                  {
                     if (errno == EINTR) continue;

                     (void)fprintf(stderr, "Unable to open %s: %s\n\n",
                                   servListFile, strerror(errno));

                     exit(ERROR);
                  }

                  else break;                                 
                  (void)chmod(servListFile, S_IREAD | S_IWRITE);
                  (void)fclose(testfd);
               }

               break;

            case 'p':
               locPort = atoi(optarg);
               break;

            case '?':
               (void)fputc('\n', stderr), usage(argv[0]);
               fprintf(stderr, "continuing anyway..\n\n");

               break;

            default:
               fprintf(stderr, "getopt() returned %d.. exiting\n", opt);
               exit(ERROR);

         }
      }
   }
}


/* ------------------------------- */


/* get "OKAY" from the server */
void getOkay()
{
   char readbuf[MAXREADSIZE];

   while(1)
   {
      memset(readbuf, 0, sizeof(readbuf));
      recv_data(clients[curClient].sockfd, readbuf, sizeof(readbuf)-1,
                (child == 1 ? 1 : 0));

      if ((errors == 1) || (timeout == 1))
      {
         errors = 1;

         send_data(clients[curClient].sockfd, "ERROR: %s\n", CMDACKERROR);
         longjmp(newconn, 1);
      }

      if (strncmp(readbuf, "OKAY", 4) == 0)
      {
         if (silent != 1) debug("got OKAY\n");
         return;
      }

      else
      {
         error("OKAY not received... retrying\n\n");
         continue;
      }
   }
}


/* ------------------------------- */


/* put client's name in argv[] */
void makeHostArgs(int argc, char **argv)
{
   register int i;

   if (argc == 2)
      (void)sprintf(argv[1], "%s %04d %d", clients[curClient].hname,
                    clients[curClient].ID, clients[curClient].numSubIDs);

   else if (argc > 2)
   {
      for (i = 0; argv[i] != NULL; i++)
          memset(argv[i], 0, strlen(argv[i]));

      (void)sprintf(argv[1], "%s %04d %d", clients[curClient].hname,
                    clients[curClient].ID, clients[curClient].numSubIDs);
   }
}


/* ------------------------- */


/* daemonize into background */
void daemonize()
{
   int res;
   res = fork();

   if (res == ERROR)
   {
      error("error forking into the background: %s\n\n",
            strerror(errno));

      quit(ERROR);
   }

   else if (res == 0) 
   {
      int res1;

      setsid();

      res1 = fork();
      if (res1 == ERROR)
      {
         error("error with fork(): %s\n\n", strerror(errno));
         quit(ERROR);
      }

      else if (res1 == 0)
      {
         nullfd = open("/dev/null", O_RDONLY);
         if (nullfd == ERROR)
         {
            error("error opening /dev/null: %s\n\n", strerror(errno));
            quit(ERROR);
         }

         (void)close(STDIN), (void)dup(nullfd);
         (void)close(STDOUT), (void)dup(errlogfd);
         (void)close(STDERR), (void)dup(errlogfd);
      }

      else (void)exit(SUCCESS);
   }

   else (void)exit(SUCCESS);

   umask(077);
   mainpid = getpid();
}


/* ---------------------------------------- */


/* export int for cmd len.. up to  65,535 bytes long) */
char *exportInt(int value)
{
   static char out[2];

   out[0] = value, out[1] = value >> 8;
   return out;
}


/* ---------------------------------------- */


/* import int for cmd len.. up to  65,535 bytes long) */
int importInt(char *value)
{
   u_char *in = (u_char *)value;
   return ((int)in[0]) | ((int)in[1] << 8);
}


/* ---------------------------------------- */


/* copy a log msg structure to a new one (to handle users) */
void copyLogStruct(int srcstruct, int dststruct)
{
   clients[curClient].logs[dststruct].facility =
		clients[curClient].logs[srcstruct].facility;

   clients[curClient].logs[dststruct].priority.single = 
		clients[curClient].logs[srcstruct].priority.single;

   clients[curClient].logs[dststruct].priority.exclude = 
		clients[curClient].logs[srcstruct].priority.exclude;

   clients[curClient].logs[dststruct].priority.ignpri = 
		clients[curClient].logs[srcstruct].priority.ignpri;

   clients[curClient].logs[dststruct].priority.priority = 
		clients[curClient].logs[srcstruct].priority.priority;

   clients[curClient].logs[dststruct].nsync = 
		clients[curClient].logs[srcstruct].nsync;
}


/* ---------------------------------------- */


/* get uid of SRS user */
void getSRSuser()
{
   int count;
   FILE *userfd;

   char *res;
   char *userptr, *dataptr;

   char buf[MAXREADSIZE/4];
   char srsuser[MAXUSERNAME+1];

   memset(srsuser, 0, sizeof(srsuser));

   userfd = fopen(USERFILE, "r");
   if (userfd == NULL)
   {
      error("error opening %s: %s..\n"
            "please run install.sh and read SRS.doc\n\n", 
            USERFILE, strerror(errno));

      quit(ERROR);
   }

   while(1)
   {
      memset(buf, 0, sizeof(buf));
      res = fgets(buf, sizeof(buf)-1, userfd);

      if (res == NULL)
      {
         if (errno == EINTR) continue;
         else if ((!feof(userfd)) && (errno > 0))
         {
            error("error reading from %s: %s\n\n", USERFILE, strerror(errno));
            quit(ERROR);
         }

         else
         {
            (void)fclose(userfd);
            break;
         }
      }

      debug("(in getSRSuser) parsing line: %s%c", buf, 
            (strchr(buf, '\n') == NULL ? '\n' : '\0'));

      if (isprint(buf[0]) == 0) continue;
      else
      {
         if (buf[0] == '#') continue;
         else
         {
            if (strchr(buf, '#') != NULL) (*(strchr(buf, '#'))) = '\0';
            
            count = 0, dataptr = buf, userptr = srsuser;
            while ((*dataptr) && (isprint(*dataptr) != 0) &&
                   (count < sizeof(srsuser)))
            {
               *userptr++ = *dataptr++;
               count++;
            }

            pwd = getpwnam(srsuser);

            if ((pwd != NULL) && (pwd->pw_uid > 0)) break;
            else
            {
               if (pwd != NULL)
                  error("the SRS user should not be root.. aborting\n\n");

               else error("error with getpwnam(%s): %s\n\n", srsuser,
                          strerror(errno));

               quit(ERROR);
            }
         }
      }
   }

   (void)fclose(userfd);
}
