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

/* this file does the authentication stuff */

/* FIX - finish linux mods and the pri stuff for sun */

/* initialize stuff.. like SYSLOGFILE */
void initStream()
{
   int fd;
   int res, stop = 0;

#  ifdef SUN
   struct stat stats;
   struct strioctl str;
#  endif
   
   struct sockaddr_un sunx;

   initSpool();
   memset(sunx.sun_path, 0, sizeof(sunx.sun_path));

   debug("(in initStream) initializing server for streaming..\n\n");

#  ifdef BSD
   sunx.sun_len = SUN_LEN(&sunx);
   sunx.sun_family = AF_LOCAL;
#  else
   sunx.sun_family = AF_UNIX;
#  endif

   (void)strncpy(sunx.sun_path, SYSLOGFILE, sizeof(sunx.sun_path));
   
# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(0);
#  else
   res = seteuid(0);
#  endif

   if (res == ERROR) 
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif

#  ifdef SUN
   res = lstat(SYSLOGFILE, &stats);
   if (res == ERROR)
   {
      error("error with lstat'ing %s: %s\n\n", SYSLOGFILE, strerror(errno));
      quit(ERROR);
   }

   if (!S_ISLNK(stats.st_mode))
   {
      error("/dev/log is [possibly] setup wrong.. "
            "linking to %s\n\n", SUNLOGFILE);

      (void)unlink(SYSLOGFILE);

      res = access(SUNLOGFILE, F_OK);
      if (res == ERROR)
      {
         error("error access'ing %s: %s\n", SUNLOGFILE, strerror(errno));
         quit(ERROR);
      }

      res = link(SUNLOGFILE, SYSLOGFILE);
      if (res == ERROR)
      {
         error("error link'ing %s to %s: %s\n", SUNLOGFILE, SYSLOGFILE,
               strerror(errno));

         quit(ERROR);
      }
   }

#  else
   (void)unlink(SYSLOGFILE);
#  endif

   errno = errors = 0;

#  ifdef SUN
   fd = open(SYSLOGFILE, O_RDONLY);
#  elif BSD
   fd = socket(AF_LOCAL, SOCK_DGRAM, 0);
#  else
   fd = socket(AF_UNIX, SOCK_STREAM, 0);
#  endif

   if ((fd == ERROR) && (errno > 0))
   {
#     ifndef SUN
      error("error with socket(): %s\n\n", strerror(errno));
#     else
      error("error opening %s: %s\n\n", SYSLOGFILE, strerror(errno));
      quit(ERROR);
#     endif

      stop = 1;
   }

#  ifdef SUN
   str.ic_cmd = I_CONSLOG;
   str.ic_timout = 0;
   str.ic_len = 0;
   str.ic_dp = NULL;

   if (ioctl(fd, I_STR, &str) < 0)
   {
      error("error registering to log conmsgs: %s\n\n", strerror(errno));
      quit(ERROR);
   }

   /* FIX - open pid file */
   /* do lock here (sun = fnctl with F_SETLK */

#  else
   (void)setsockopts(fd, 0);

   /* FIX - open pid file */

#  ifdef BSD
   /* do lock here (bsd = flock()) */
#  else
   /* in linux it just checks if the file exists.. */
#  endif

   if (stop != 1)
   {
#     ifdef BSD
      res = bind(fd, (struct sockaddr *) &sunx, SUN_LEN(&sunx));
#     else
      res = bind(fd, (struct sockaddr *) &sunx, 
                 sizeof(sunx.sun_family) + strlen(sunx.sun_path));
#     endif

      if (res == ERROR)
      {
         error("error with bind():\n%s\n\n", strerror(errno));
         stop = 1;
      }
   }
#  endif

   if (stop != 1)
   {
      res = chmod(SYSLOGFILE, 0666);
      if (res == ERROR)
      {
         error("error with chmod(): %s\n\n", strerror(errno));
         stop = 1;
      }

#    if !defined(SUN) && !defined(BSD)
      if (pwd != NULL)
      {
#        ifdef _POSIX_SAVED_IDS
         res = setuid(pwd->pw_uid);
#        else
         res = seteuid(pwd->pw_uid);
#        endif

         if (res == ERROR) 
         {
            error("error setting [e]uid: %s\n\n", strerror(errno));
            quit(ERROR);
         }
      }
#    endif

#     ifndef SUN
      res = listen(fd, 5);
      if (res == ERROR)
      {
         error("error with listen(): %s\n\n", strerror(errno));
         stop = 1;
      }
#     endif
   }

   if (stop == 1)
   {
      error("error while setting up %s...\n\n", SYSLOGFILE);
      quit(ERROR);
   }
    
# if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif

      if (res == ERROR) 
      {
         error("error setting [e]uid: %s\n\n", strerror(errno));
         quit(ERROR);
      }
   }
# endif

   funix = fd;

   klogfd = open(KLOGFILE, O_RDONLY);
   if (klogfd == ERROR) 
      error("error opening klogfd.. continuing anyway\n\n");
}


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


/* read data from SYSLOGFILE.. and send it to server */
void doStreaming()
{
   int res;
   char *res1;

   int begin = 0, end = 0;

#  if !defined(BSD) && !defined(SUN)
   int fd;
#  endif

#  ifndef SUN
   int count = 0;
#  endif

   int nospool = ERROR;

   struct timeval tm;
   struct tm *tv;
   time_t value;
   
#  ifndef SUN
   char newmsg[MAXLOGSIZE];
   char logmsg[MAXLOGSIZE];
#  endif

   char readbuf[MAXREADSIZE];
   char spoolfile[MAXFNAMESIZE];  

   char newbuf[MAXLOGSIZE];
   char testbuf[MAXLOGSIZE];

#  ifndef SUN
   char *logptr, *dataptr, *newptr;
#  endif

#  ifdef BSD
   u_char cmsg[MAXREADSIZE/4];

   struct iovec iov;
   struct msghdr msghdr;

#  elif SUN
   char *bufptr;
   int flags = 0;
#  endif

   errno = 0;
   FD_ZERO(&readfds);

   memset(spoolfile, 0, sizeof(spoolfile));

   if (spoolFile == NULL)
      (void)sprintf(spoolfile, "%s/spool/%s", SRSdir, LOGFILE);

   else (void)snprintf(spoolfile, sizeof(spoolFile)-1, "%s", spoolFile);

   if (spoolfd != NULL) (void)fclose(spoolfd);

# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(0);
#  else
   res = seteuid(0);
#  endif

   if (res == ERROR) 
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif

   if (nospool != 1)
   {
      spoolfd = fopen(spoolfile, "rb");
      if (spoolfd == NULL)
      {
         error("(in doStreaming) error opening: %s: %s\n\n", spoolfile, 
               strerror(errno));

         nospool = 1;
      }
   }

# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(pwd->pw_uid);
#  else
   res = seteuid(pwd->pw_uid);
#  endif

   if (res == ERROR) 
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif

   sendSysConf();

#  ifdef NOSSL
   startPings(); /* using this will SSL breaks stuff */
#  endif

   debug("now telling server we're streaming..\n");
   streaming = 1, send_data("START STREAM\n");

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

   if (nospool != 1)
   {
      debug("now checking for spooled data...\n");

      while(1)
      {
         errno = 0;
         memset(readbuf, 0, sizeof(readbuf));

         res1 = fgets(readbuf, sizeof(readbuf)-1, spoolfd);

         if ((res1 == NULL) && (errno == EINTR)) continue;
         else if (res1 != NULL)
         {
            if (strncmp(readbuf, "STREAM", 6) == 0)
            {
               /* there is data in the spool file.. so we were spooling */

               if (didspool != 1)
               {
                  debug("possible spooled data...\n");
                  debug("streaming this (if any) before real-time data\n\n");

                  didspool = 1;
               }

               if (((readbuf[0] != '\0') && (readbuf[0] != '\n')) &&
                   (isprint((int)readbuf[1]) != 0)) send_data(readbuf);
            }
         }

         else break;
      }

      spooling = didspool = 0;

      debug("spooled data (if any) was sent...\n");
      debug("now streaming live logs\n\n");
   }

   else nospool = 0;

# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(0);
#  else
   res = seteuid(0);
#  endif

   if (res == ERROR) 
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif

   if (spoolfd != NULL) (void)fclose(spoolfd);
   (void)unlink(spoolfile);

# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(pwd->pw_uid);
#  else
   res = seteuid(pwd->pw_uid);
#  endif

   if (res == ERROR) 
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif

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

   debug("sending \"BEGIN LOGS\" marker...\n");

   value = time ((time_t *)NULL);
   tv = localtime (&value);

   sprintf(testbuf, "STREAM : ! --- BEGIN LOGS FOR CLIENT ID %s at "
           "%d:%d:%d (%d.%d.%d) ---\n", ID, tv->tm_hour, tv->tm_min,
           tv->tm_sec, (tv->tm_mon + 1), tv->tm_mday, tv->tm_year);

   send_data (testbuf);

   debug("now waiting for system logs to stream...\n\n");

#  ifdef BSD
   iov.iov_base = readbuf;
   iov.iov_len = sizeof(readbuf)-1;
#  endif

   while(1)
   {
#     ifdef SUN
      struct log_ctl hdr;
      struct strbuf ctl, dat;
#     endif

      memset(readbuf, 0, sizeof(readbuf));

      FD_SET(funix, &readfds);
      FD_SET(sockfd, &readfds);
      if (klogfd > 0) FD_SET(klogfd, &readfds);

#     if !defined(SUN) && !defined(BSD)
      for (nfds = 0; nfds < FD_SETSIZE-2; nfds++)
         if (FD_ISSET(nfds, &unixm)) FD_SET(nfds, &readfds);
#     endif

      tm.tv_sec = MAXTIMEOUT * 2, tm.tv_usec = 0;

      nfds = 0, errno = 0;
      nfds = select(FD_SETSIZE, (fd_set *) &readfds, (fd_set *) NULL,
                   (fd_set *) NULL, &tm);
                
      /* won't happen.. we don't have a timeout */
      if (nfds == 0)
      {
         debug("no select activity... restarting in loop\n");
         debug("sending a ping (to check for sigpipe) to server..\n");

         FD_CLR(funix, &readfds);
         FD_CLR(sockfd, &readfds);
         if (klogfd > 0) FD_CLR(klogfd, &readfds);

         send_data("PING CHECK\n");
         continue;
      }
    
      else if (nfds == ERROR)
      {
         if (errno == EINTR) continue;

         error("error with select(): %s\n\n", strerror(errno));
         quit(ERROR);
      }

      /* ------------------------------------- */
    
      /* handle data from the server */
      if (FD_ISSET(sockfd, &readfds)) 
      {
         debug("new data from server\n");

         memset(readbuf, 0, sizeof(readbuf));
         recv_data(readbuf, sizeof(readbuf));

         FD_CLR(sockfd, &readfds);
      }

      /* handle new data on /dev/klog */
      if ((klogfd > 0) && (FD_ISSET(klogfd, &readfds)))
      {
         memset(readbuf, 0, sizeof(readbuf));

         res = read(klogfd, readbuf, sizeof(readbuf)-1);
         if (res == ERROR)
         {
            error("error reading klogfd.. removing\n\n");

            FD_CLR(klogfd, &readfds);
            (void)close(klogfd), klogfd = ERROR;

            continue;
         }

         else if (((readbuf[0] != '\0') && (readbuf[0] != '\n')) &&
                  (isprint((int)readbuf[1]) != 0))
         {
            /* FIX - do it (add the kern def pri.. "kernel: " and */
            /*     - doLogging it.. then sending it to server     */

            debug("new klog msg = %s\n", readbuf);
         }

         FD_CLR(klogfd, &readfds);
      }

#     if !defined(SUN) && !defined(BSD)
      for (fd = 0; fd < FD_SETSIZE; fd++)
         if ((FD_ISSET(fd, &readfds)) && (FD_ISSET(fd, &unixm)))

#     else
      if (FD_ISSET(funix, &readfds)) 
#     endif

      {
         memset(readbuf, 0, sizeof(readbuf));

#        ifdef BSD
         memset(cmsg, 0, sizeof(cmsg));
         memset(&msghdr, 0, sizeof(msghdr));

         msghdr.msg_iov = &iov;
         msghdr.msg_iovlen = 1;
         msghdr.msg_flags = 0;
         msghdr.msg_control = (caddr_t)cmsg;
         msghdr.msg_controllen = sizeof(cmsg);

         /* FIX - do something with credentials.. */

         res = recvmsg(funix, &msghdr, 0);
         if (res == ERROR)
         {
            error("error with recvmsg from %s: %s\n\n", SYSLOGFILE,
                  strerror(errno));

            quit(ERROR);
         }

#        elif SUN
         dat.buf = readbuf;
         dat.maxlen = sizeof(readbuf) - 1;

         ctl.buf = (caddr_t)&hdr;
         ctl.maxlen = sizeof(struct log_ctl);

         while(1)
         {
            errno = 0;

            res = getmsg(funix, &ctl, &dat, &flags);
            if (res != MOREDATA)
               if ((res != ERROR) || ((res == ERROR) && (errno != EINTR)))
                  break;

            bufptr = &dat.buf[dat.len], *bufptr = '\0';
            while ((*bufptr != '\n') && (bufptr != readbuf)) bufptr -= 1;

            if (bufptr != readbuf) *bufptr = '\0';

            if (((readbuf[0] != '\0') && (readbuf[0] != '\n')) &&
                (isprint((int)readbuf[1]) != 0))
            {
               send_data("STREAM : %s%c", readbuf,
                         (readbuf[strlen(readbuf)-1] != '\n' ? '\n' : '\0'));

               doLogging(readbuf);
            }

            if (readbuf != bufptr) 
            {
               strcpy(readbuf, bufptr); 
               dat.maxlen = sizeof(readbuf) - strlen(readbuf) - 1;
               dat.buf = &readbuf[strlen(readbuf)];
            }

            else
            {
               dat.maxlen = sizeof(readbuf) - 1;
               dat.buf = readbuf;
            }
         }

         if ((res == 0) && (dat.len > 0))
         {
            dat.buf[dat.len] = '\0';

            if (((readbuf[0] != '\0') && (readbuf[0] != '\n')) &&
                (isprint((int)readbuf[1]) != 0))
            {
               send_data("STREAM : %s%c", readbuf,
                         (readbuf[strlen(readbuf)-1] != '\n' ? '\n' : '\0'));
 
              doLogging(readbuf);
            }
         }

         else if ((res == ERROR) && (errno != EINTR))
         {
            error("error in getmsg: %s\n\n", strerror(errno));

            (void)close(funix);
            funix = ERROR;

            quit(ERROR);
         }

#        else
         while(1)
         {
            errno = 0;

            memset(readbuf, 0, sizeof(readbuf));
            res = read(fd, readbuf, sizeof(readbuf)-1);

            if ((res <= 0) && (errno > 0))
            {
               if (errno == EINTR) continue;

               error("error with read(): %s\n", strerror(errno));
               (void)close(fd), quit(ERROR);
            }
 
           else break;
         }

         FD_CLR(fd, &readfds);
         FD_CLR(fd, &unixm);
#        endif

#        ifndef SUN
         if (((readbuf[0] != '\0') && (readbuf[0] != '\n')) &&
             (isprint((int)readbuf[1]) != 0))
         {
            memset(newmsg, 0, sizeof(newmsg));
            memset(logmsg, 0, sizeof(logmsg));

            send_data("STREAM : %s%c", readbuf,
                      (readbuf[strlen(readbuf)-1] != '\n' ? '\n' : '\0'));

            doLogging(readbuf);

            logptr = logmsg;
            dataptr = readbuf, dataptr += 20; 

            count = 0;
            while ((*dataptr) && (isprint((int)*dataptr) != 0) &&
                   (count < (int)sizeof(logmsg)))
            {
               *logptr++ = *dataptr++;
               count++;
            }

            if (isprint((int)prevlogmsg[0]) == 0) 
            {
               (void)strncpy(prevlogmsg, logmsg, sizeof(prevlogmsg)-1);
               prevlogtime = time(NULL);

               doLogging(readbuf);
            }

            else if (strncmp(logmsg, prevlogmsg, strlen(prevlogmsg)) == 0)
            {
               if ((time(NULL) - prevlogtime) >= REPTIME)
               {
                  char repmsg[MAXLOGSIZE - 20];

                  memset(repmsg, 0, sizeof(repmsg));

                  if (repcount == 1)
                     sprintf(repmsg, " last message repeated %d time\n",
                             repcount);

                  else
                     sprintf(repmsg, " last message repeated %d times\n",
                             repcount);

                  repcount = 0;
                  prevlogtime = time(NULL);

                  count = 0;
                  newptr = newmsg, dataptr = readbuf;

                  while ((*dataptr) && (isprint((int)*dataptr) != 0) &&
                         (count < (int)sizeof(newmsg)) && (count < 19))
                  {
                     *newptr++ = *dataptr++;
                     count++;
                  }

                  strncat(newmsg, repmsg, 
                          sizeof(newmsg) - strlen(newmsg) - 1);

                  debug("newmsg = %s\n", newmsg);
                  doLogging(newmsg);
               }

               else
               {
                  repcount++;
                  debug("last message repeated...\n\n");
               }
            }

            else
            {
               prevlogtime = time(NULL);
               (void)strncpy(prevlogmsg, logmsg, sizeof(prevlogmsg)-1);
               doLogging(readbuf);
            }
         }
#        endif
      }

      value = time(NULL);
      tv = localtime (&value);

      if (begin == 1 && tv->tm_hour != BEGIN_HOUR)
      {
        debug ("-- RESET BEGIN MARKER --\n");
        begin = 0;
      }

      if (end == 1 && tv->tm_hour != END_HOUR)
      {
        debug ("-- RESET END MARKER --\n");
        end = 0;
      }

      if (tv->tm_hour == BEGIN_HOUR && begin != 1)
      {
         begin = 1;
         memset(newbuf, 0, sizeof(newbuf));

         sprintf(newbuf, "STREAM : ! --- BEGIN LOGS FOR CLIENT ID %s "
                 "at %d:%d:%d (%d.%d.%d) ---\n", ID, tv->tm_hour, tv->tm_min,
                 tv->tm_sec, (tv->tm_mon + 1), tv->tm_mday, tv->tm_year);

         send_data(newbuf);

         debug("--- BEGIN LOGS FOR CLIENT ID %s "
               "at %d:%d:%d (%d.%d.%d) ---\n", ID, tv->tm_hour, tv->tm_min,
               tv->tm_sec, (tv->tm_mon + 1), tv->tm_mday, tv->tm_year);
      }

      if (tv->tm_hour == END_HOUR && tv->tm_min == END_MINUTE &&
          tv->tm_sec >= END_SECOND && end != 1)
      {
         end = 1;
         memset(newbuf, 0, sizeof(newbuf));

         sprintf(newbuf, "STREAM : ! --- END LOGS FOR CLIENT ID %s "
                 "at %d:%d:%d (%d.%d.%d) ---\n", ID, tv->tm_hour,
                 tv->tm_min, tv->tm_sec, (tv->tm_mon + 1), tv->tm_mday,
                 tv->tm_year);

         send_data(newbuf);

         debug("--- END LOGS FOR CLIENT ID %s "
               "at %d:%d:%d (%d.%d.%d) ---\n", ID, tv->tm_hour,
               tv->tm_min, tv->tm_sec, (tv->tm_mon + 1), tv->tm_mday,
               tv->tm_year);
      }

#     if !defined(SUN) && !defined(BSD)
      if (FD_ISSET(funix, &readfds))
      {
         /* accept a new connection */
         /* debug("got new unix connect\n"); */

         fd = accept(funix, (struct sockaddr *)NULL, 0);
         if (fd >= 0)
         {
            FD_SET(fd, &unixm);
            FD_SET(fd, &readfds);
         }

         else error("error with accept(): %s\n", strerror(errno));
      }
#     endif

   }
}
