/*
    slforum version 1.x - Simple chat room usable via telnet.
    Copyright (C) 1998-2007 Jeremy D. Monin, Dan Chin, Matt Lochansky
	(forum@shadowlands.org).
    Based on jdkchat version 1.3, J.D.Koftinoff Software, Ltd. (GPL'd)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#include "world.h"
#include "slfprefs.h"
#include "slforumutil.h"

/* set in main() */
extern char *wtmpfname;

/* #define DBG(a) printf a */
#define DBG(a) 

char telnet_init[] = 
{ 
	IAC, DONT, TELOPT_LINEMODE, 
	IAC, WILL, TELOPT_SGA,
/* ifdef SLF_ALLOW_HIGHBITS
	IAC, WILL, TELOPT_BINARY,
	IAC, DO,   TELOPT_BINARY,
	endif */
	0 
};

char telnet_local_on[] =
{
	IAC, WONT, TELOPT_ECHO,
	0
};

char telnet_local_off[] =
{
	IAC, WILL, TELOPT_ECHO,
	0
};

char telnet_double_iac[] = { IAC, IAC, 0 };



/* write, printf-style, a formatted buffer to socket fd;
 * up to 255 bytes are printed, # bytes printed is returned.
 * jer 980704.1420
 */
int lpprintf (int fd, const char *fmt, ...) {
  /* from vsprintf manpage example */
  char buf[256];
  va_list ap;
  va_start (ap, fmt);
  vsnprintf(buf, 255, fmt, ap);  buf[255] = '\0';
  va_end (ap);
  return lpwrite (fd, buf, strlen(buf));
}


/* write first sz bytes of a buffer to socket fd */
int lpwrite( int fd, const char *buf, int sz )
{
	const char *b= buf;
	int todo=sz;
	int done=0;
	
	while( todo>0 )
	{
		int cnt=write( fd, b, todo );
		
		if( cnt<0 && errno!=EINTR && errno!=EAGAIN)
		{
			done=-1;	 /* error! */
			break;
		}
		
		if( cnt>0 )
		{
			todo-=cnt;
			done+=cnt;
			b+=cnt;
		}		
	}
	
	if (done == sz)
	    return sz;
	else
	    return -1;
}

int in_lpcat = 0;  /* jer added 981023.0320, so slforummain's SIGALRM handler
		    * can tell we're in lpcat.
		    */

/* write a short file (up to 2047 bytes) to socket fd (jer);
 * on error, returns -1 and sets perror.  if fd = -1,
 * returns 0.
 */
int lpcat( int fd, const char *fname )
{
  FILE *diskf;
  int done;

  if (fd == -1)
    return 0;
  diskf = slf_safe_fopen ("lpcatted file", fname, "r");
  if (diskf == NULL)
    return -1;

  /* jer 981023.0320.... since we have evidence we may be
   * hanging in here (at strchr()), set up so we exit this
   * routine, no matter what, after 5 seconds... this
   * kludges around a potentially infinite loop.
   */
  in_lpcat = 1;
  /* alarm(5); -- 20020514 - no longer necessary */

  {
  char bf[2048];
  char *b;
  int sz;

  int todo;
  int si;  /* size to write; 0 for all */
  int cnt; /* how many bytes actually wrote */
  char *cp, *cp2;
  char crlf[2]; /* the missing half of a full crlf pair. */

  bf[2047] = '\0';
  sz = fread (bf, 1, 2047, diskf );
  b  = bf;
  fclose (diskf);

  todo=sz;
  done=0;


  if (todo == 0)
    lpwrite ( fd, "\r\nlpcat: todo is 0\r\n", 20);

  crlf[0] = ' ';  crlf[1] = '\0';
  while( (todo>0) && in_lpcat )
    {
      /* search for \r without \n or vice versa */
      si = 0; cp = (char *) 4;  /* 4 makes it non-null for start condition */
      while ((si == 0) && (cp != NULL) && in_lpcat ) {
	cp = strchr (b, (int)'\n');
	if (cp != NULL) {
	  cp2 = strchr (b, (int)'\r');
	  if (cp2 == NULL) {
	    /* found \n without \r */
	    si = (cp - b) + 1;
	    crlf[0] = '\r';
	  } else {
	    /* both found, we can terminate that search (20070128.1830) */
	    cp = NULL;
	  }
	} else {
	  /* found no \n */
	  cp = strchr (b, (int)'\r');
	  if (cp != NULL) {
	    /* found \r without \n */
	    si = (cp - b) + 1;
	    crlf[0] = '\n';
	  }	 /* else neither found */
	}
      }  /* end while */
      
      /* now strip out ctrl chars (19990623.2335)
       * Make 'em blanks instead.
       * This code is elsewhere too; grep for the timestamp.
       */
      cp = b;
      {
	int ticker;
	for (ticker = (si == 0 ? sz : si); ticker > 0; ++cp, --ticker) {
	  register int cv = (*cp);
	  if ( ((cv < 32) || ( (cv > 128 ) && (cv < (128+32))) || (cv==IAC) )
	       && (cv != '\n') && (cv != '\r') )
	    *cp = ' ';
	}
      }


      if (si == 0)
	cnt=write( fd, b, sz );  /* everything */
      else {
	cnt=write( fd, b, si );  /* a portion */
	write (fd, crlf, 1 );    /* the missing half of crlf pair */
      }
      /* 981023.0320: commented out check gainst EINTR */

      if( cnt<0 /* && errno!=EINTR */ && errno!=EAGAIN)
	{
	  done=-1;	 /* error! */
	  break;
	}
		
      if( cnt>0 )
	{
	  todo-=cnt;
	  done+=cnt;
	  b+=cnt;
	}		
    }
  }
  in_lpcat = 0;	/* jer 981023.0320 */
  return done;
}


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

/** returns NULL if file does not exist,
 *  or if it's a symlink, directory, or block device.
 *  'desc' should be what it is, such as "log file" or "banner".
 *
 * (jer 19990605.1700)
 */
FILE *slf_safe_fopen (const char *desc,
		      const char *fname, const char *mode) {
  struct stat st;
  const char *reason = NULL;

  if (-1 == lstat (fname, &st)) {
    /* file not found; fopen will deal appropriately.
     * if the mode is set right, for example, a new file is created.
     */
  } else {
    if (S_ISDIR(st.st_mode)) 
    {  errno = EISDIR;  reason = "a directory";  }
    else if (S_ISLNK(st.st_mode))
    {  errno = EPERM;   reason = "a symlink"; } 
    else if (S_ISBLK(st.st_mode))
    {
#ifdef ENOSTR
	    errno = ENOSTR;
#else
	    errno = EPERM;  /* os x has no ENOSTR */
#endif
	    reason = "a block device";
    }
  }

  if (reason != NULL) {
/*     fprintf (stderr,
	     "%s '%s' is %s, not opening it\n", desc, fname, reason);
*/
    return NULL;
  } else
    return fopen (fname, mode);
}



/* jer 20000816.0930 */

static const int datediff_dom[] =
	{ 31, 28, 31, 30, 31, 30,    /* jan-jun */
	  31, 31, 30, 31, 30, 31 };  /* jul-dec */

/** returns 1 on error. (couldn't allocate memory, etc.) 
 *  On success, fills in the following fields of ddif:
 *  tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec.
 *  The contents of each of these fields is the number of
 *  units elapsed between fromtime and tomtime (tm_year is
 *  the number of years, tm_mon is the number of additional
 *  months, etc.)
 */
int datediff(time_t fromtime, time_t totime, struct tm *ddif)
{
    int daystoadd;
    struct tm fromt;

    {
	/* gmtime points to a static area, but we need to call it twice... */
	struct tm *ft;
	ft = gmtime(&fromtime);
	if (ft == NULL)
	    return 1;
	memcpy(&fromt, ft, sizeof(struct tm));

	ft = gmtime(&totime);
	if (ft == NULL)
	    return 1;
	memcpy(ddif, ft, sizeof(struct tm));
    }

    /* fromt is now the from-time, ddif is the to-time. */
    
    /* we might need to add days, if to-time's day-of-month
     * is less than from-time's day-of-month:
     */
    if ((ddif->tm_mday > fromt.tm_mday)
	|| ((ddif->tm_mday == fromt.tm_mday)
	    && (ddif->tm_mon > fromt.tm_mon)))
    {
	daystoadd = 0;		/* no need to add days */
    } else {
	daystoadd = ddif->tm_mon - 1; /* jan==0 */
	if (daystoadd < 0)
	    daystoadd = 11;

	if (( daystoadd != 1 )	/* not feb */
	    || ((ddif->tm_year % 4) != 0)  /* not leap year */
	    || (((ddif->tm_year % 100) == 0)
		&& ((ddif->tm_year % 400) != 0)))
	    daystoadd = datediff_dom[daystoadd];   /* standard lookup */
	else
	    daystoadd = 29;	/* leap year */
    }

    /* now subtract things: */
    ddif->tm_year -= fromt.tm_year;
    ddif->tm_mon  -= fromt.tm_mon;
    ddif->tm_mday -= fromt.tm_mday;
    ddif->tm_hour -= fromt.tm_hour;
    ddif->tm_min  -= fromt.tm_min;
    ddif->tm_sec  -= fromt.tm_sec;

    /* now go through and adjust for when a fromt value is > the totime val */

#define tadjustfor(adjfld,biggerfld,adjadd)	\
    if (ddif->adjfld < 0)			\
    {						\
	--(ddif->biggerfld);			\
	(ddif->adjfld) += adjadd;		\
    }
    /*         printf("added %d units on line %d\n", adjadd, __LINE__);	*/

    tadjustfor(tm_sec,  tm_min,  60);
    tadjustfor(tm_min,  tm_hour, 60);
    tadjustfor(tm_hour, tm_mday, 24);
    tadjustfor(tm_mday, tm_mon,  daystoadd);
    tadjustfor(tm_mon,  tm_year, 12);

#undef tadjustfor

    return 0;  /* success */
}  /* end datediff */


/** Write time to a buffer.
 *  "incldate" 0=time; 1=time,date; 2=time,date,year.
 */
void _writetime (char *buf, size_t bufsize, time_t whicht, int incldate)
{
    struct tm *nowtime;
    buf[bufsize-1] = '\0';
    nowtime = localtime (&whicht);
    if (nowtime == NULL)
    {
	strncpy (buf, "??:??", bufsize);
    } else {
	switch (incldate)
	{
	case 0:
	    strftime (buf, bufsize, "%H:%M", nowtime);           break;
	case 1:
	    strftime (buf, bufsize, "%d %b %H:%M", nowtime);     break;
	case 2:
	    strftime (buf, bufsize, "%d %b %H:%M %Y", nowtime);  break;
	}
    }
}  /* end _writetime */


/* end slforumutil.c */

