/**************************************************
OpengateM - MAC address authentication system 
 module for misc routines

Copyright (C) 2011 Opengate Project Team
Written by Yoshiaki Watanabe

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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Email: watanaby@is.saga-u.ac.jp
**************************************************/

#include "opengatemmng.h"


/*************************************************/
/* formated write                                */
/* fd : file descriptor                          */
/* fmt : format to write                         */
/* ... : terms to write                          */
/* usage is same as fprintf                      */
/*************************************************/
void Writefmt(int fd, const char *fmt, ...){
  char	buff[BUFFMAXLN];
  va_list     ap;
  int nchar, nwrt;

  va_start(ap, fmt);
  vsnprintf(buff, BUFFMAXLN, fmt, ap);	
  va_end(ap);

  nchar=strlen(buff);
  nwrt=write(fd, buff, nchar);

  return;
}

/***********************************************/
/* SSL version of WriteFmt                     */
/***********************************************/
void WritefmtSSL(SSL *fd, const char *fmt, ...){
  char	buff[BUFFMAXLN];
  va_list     ap;
  int nchar, nwrt;

  va_start(ap, fmt);
  vsnprintf(buff, BUFFMAXLN, fmt, ap);	
  va_end(ap);

  nchar=strlen(buff);
  nwrt=SSL_write(fd, buff, nchar);

  return;
}


/******************************************************/
/* Read one line                                      */
/*   fd: file descriptor                              */
/*   vptr: input buffer pointer                       */
/*   maxlen: buffer length                            */
/*                                                    */
/* read the string terminated with EOL or EOF         */
/* # this function assumes two EOL chars [CR LF]      */ 
/*  CRLF is not stored into buffer and is skipped     */
/* [abcdCRLFefghCRLF] => store[abcd],remain[efghCRLF] */
/*                                                    */
/* return value                                       */
/*    plus value means the count of read-in chars     */
/*    value 0 means NULL line (no-chars & CRLF)       */
/*    value -1 means error (errno is set)             */
/*    value -2 means EOF (no-chars & EOF)             */ 
/******************************************************/
ssize_t readln(int fd, void *vptr, size_t maxlen){
  ssize_t	n, rc;
  char	*ptr,c;

  ptr=vptr;

  /* pre read */
  rc = read(fd, &c, 1);
  if(rc==0) return(-2); /* EOF */
  if(rc<0) return(-1);  /* ERR */

  /* get char loop */
  n=0;
  while(n < maxlen-1) {
    if ( rc == 1) {      /* get some char */
      if (iscntrl(c)){         /* get control char (means EOL) */
	rc = read(fd, &c, 1);  /* skip second EOL char */
	break;
      }
      *ptr++ = c;
      n++;
    }else if (rc == 0) { /* EOF (but some chars are read already) */
      break;
    } else {             /* ERR */
      return(-1);
    }
    rc = read(fd, &c, 1);
  }
  /* null terminate string */  
  *ptr++ = 0;
  return(n);
}

/***********************************************/
/* SSL version of readIn                       */
/***********************************************/
ssize_t readlnSSL(SSL *fd, void *vptr, size_t maxlen){
  ssize_t	n, rc;
  char	*ptr,c;

  ptr=vptr;

  /* pre read */
  rc = SSL_read(fd, &c, 1);
  if(rc==0) return(-2); /* EOF */
  if(rc<0) return(-1);  /* ERR */

  /* get char loop */
  n=0;
  while(n < maxlen-1) {
    if ( rc == 1) {      /* get some char */
      if (iscntrl(c)){      /* get control char (means EOL) */
	  rc = SSL_read(fd, &c, 1); /* skip second EOL char */
	  break;
      }
      *ptr++ = c;
      n++;
    }else if (rc == 0) { /* EOF (but some char are read already */
      break;
    } else {             /* ERR */
      return(-1);
    }
    rc = SSL_read(fd, &c, 1);
  }
  /* null terminate string */  
  *ptr++ = 0;
  return(n);
}

/******************************/
/* lock function using fcntl  */
/******************************/
int lock(int fd){
  struct flock lck;
  
  lck.l_type=F_WRLCK;
  lck.l_whence=SEEK_SET;
  lck.l_start=0;
  lck.l_len=0;
  return fcntl(fd, F_SETLKW, &lck);
}
  
/********************************/
/* unlock function using fcntl  */
/********************************/
int unlock(int fd){
  struct flock lck;

  lck.l_type=F_UNLCK;
  lck.l_whence=SEEK_SET;
  lck.l_start=0;
  lck.l_len=0;
  return fcntl(fd, F_SETLK, &lck);
}

/**********************************************/
/* return true, if the pointer value is NULL  */
/* or pointer points to null(length=0) string */
/**********************************************/
int isNull(const char *pStr){
  if(pStr==NULL) return TRUE;
  if(*pStr=='\0') return TRUE;
  return FALSE;
}

/**************************************************/
/* popen with argument list                       */
/* rootPriv: if 1, run command as root user       */
/* type : open type "r" or "w"                    */
/* path : path of command executing fork/exec     */
/* ... : command arguments. last must be (char*)0 */
/*  If you use [user entered string] in arguments */
/*  sanitize the string to prevent hacking.       */
/**************************************************/
FILE *Popenl(int rootPriv, const char *type, const char *path, ...){
  char	commandLine[BUFFMAXLN];
  va_list     ap;
  char *pStr;
  FILE *file;

  /* insert command path */
  strlcpy(commandLine, path, BUFFMAXLN);

  /* insert command arguments */
  va_start(ap, path);
  
  while((pStr=va_arg(ap, char *))!=(char *)0){
    strcat(commandLine, " ");
    strlcat(commandLine, pStr, BUFFMAXLN);
  }
  free(pStr);
  va_end(ap);

  /* if desired, add root privilege */
  if(rootPriv==1){
    if(seteuid(0)!=0){
      err_msg("ERR at %s#%d: cannot add root privilege ",
	      __FILE__,__LINE__);
    } 
  }

  /* open the pipe to the program  */
  if(debug>1) err_msg("DEBUG:=>popen(%s, %s)", commandLine, type);
  file=popen(commandLine, type);
  if(debug>1) err_msg("DEBUG:(%x)<=popen( )",file);  

  /* remove root privilege */
  seteuid(getuid()); 

  return file;
}


/**************************************************/
/* system with argument list                      */
/* rootPriv: if 1, run command as root user       */
/* path : command path to fork/exec               */
/* ... : command arguments. last must be (char*)0 */
/*  If you use [user entered string] in arguments */
/*  sanitize the string to prevent hacking.       */
/**************************************************/
int Systeml(int rootPriv, const char *path, ...){
  char	commandLine[BUFFMAXLN];
  va_list     ap;
  char *pStr;
  int ret;

  /* insert command path */
  strlcpy(commandLine, path, BUFFMAXLN);

  /* insert command arguments */
  va_start(ap, path);
  
  while((pStr=va_arg(ap, char *))!=(char *)0){
    strcat(commandLine, " ");
    strlcat(commandLine, pStr, BUFFMAXLN);
  }
  free(pStr);
  va_end(ap);

  /* if desired, add root privilege */
  if(rootPriv==1){
    if(seteuid(0)!=0){
      err_msg("ERR at %s#%d: cannot add root privilege ",
	      __FILE__,__LINE__);
    } 
  }

  /* execute shell  */
  if(debug>1) err_msg("DEBUG:=>system(%s)", commandLine);
  ret=system(commandLine);
  if(debug>1) err_msg("DEBUG:<=system()");

  /* remove root privilege */
  seteuid(getuid()); 
  return ret;
}

/*************************************************/
/* calc MD5 and return in hex form               */
/*  str: (input)plain text to convert            */
/*  hexdigest: (output) converted hex string     */
/*      prepare buff more or equal to 33chars    */
/*  len: (input) length of hexdigest buffer      */
/*************************************************/
char *md5hex(char *hexdigest, int len, char *str){
  char unsigned digest[16];
  char hexcode[16]="0123456789abcdef";
  int i;
  
  /* if not enough buffer, exit */
  if(len<33){
    *hexdigest='\0';
    return hexdigest;
  }

  /* calc MD5 digest */
  MD5((unsigned char*)str, strlen(str), digest);

  /* convert to HEX string */
  for(i=0;i<16;i++){
    hexdigest[2*i]=hexcode[digest[i]/16];
    hexdigest[2*i+1]=hexcode[digest[i]%16];
  }
  hexdigest[2*16]='\0';

  return hexdigest;
}

/*******************************************/
/* create random session cookie            */
/* as md5hex(processId.TimeNow)            */
/*******************************************/
void createCookie(char *cookie)
{
  char str[BUFFMAXLN];

  /* make Http-cookie from pid&time */
  snprintf(str, BUFFMAXLN, "%d%ld", getpid(),time(NULL));
  md5hex(cookie, SIDMAXLN, str);
}

/********************************************/
/* get port number defined in /etc/services */
/* return as string form                    */
/********************************************/
char *getServicePortStr(char *servName){
  struct servent *pServEnt;
  static char portStr[WORDMAXLN];

  /* get service info from service name */
  pServEnt = getservbyname(servName, NULL);

  if(pServEnt==NULL){
    err_msg("ERR at %s#%d: cannot find /etc/services entry for %s",
	    __FILE__,__LINE__,servName);
    return "";
  }

  /* convert service port number to string form */
  snprintf(portStr, sizeof(portStr),"%d",ntohs(pServEnt->s_port));

  return portStr;
}

/**************************************************************/
/* get env value from string including multiple env-var       */
/*                                                            */
/* if string(env) = "ab cd-ef-gh ijk"                         */
/*  repert the search of env-var having non-null value        */
/*  getenv("ab"),getenv("cd_ef_gh"),getenv("ijk")             */
/* if pre=TRUE convert '-' to '_' in env-var before search    */
/* if post=TRUE convert ' '|'@' to '_' in get-str after search*/
/**************************************************************/
char* getenvEx(char* env, int pre, int post){
  char work[BUFFMAXLN];
  char* envValue="";
  char* p=NULL;
  char* thisVar=NULL;
  char* nextVar=NULL;
  int found=FALSE;

  /* copy string to work area */
  strlcpy(work, env, BUFFMAXLN);

  /* repeat from the head to the tail of the work area */
  thisVar=nextVar=work;
  while(!isNull(thisVar)){

    /* skip preceeding spaces in string, and points the head of vairable */
    for(p=thisVar; *p==' '; p++);
    thisVar=p;

    /* search space (=the end of this variable) */
    for(p=thisVar; (*p!=' ' && *p!='\0'); p++);

    /* points the next variable */
    if(*p=='\0') nextVar=p; /* end of string */
    else{                   /* some string follows */
      *p='\0';                /* set termination char of this variable */
      nextVar=p+1;            /* set the next search point */
    }

    /* if PRE is true, replace '-' in string with '_' */
    if(pre){
      for(p=thisVar; *p!='\0'; p++){
	if(*p=='-') *p='_';
      }
    }

    /* exeute getenv. if success, exit loop */
    envValue = getenv(thisVar);
    if(!isNull(envValue)){
      found=TRUE;
      break;
    }

    /* try next variable */
    thisVar=nextVar;
  }

  /* getting no data */
  if(!found) return NULL;

  /* if POST is true, convert ' ' and '@' to '_' */
  if(post){
    for(p=envValue; *p!='\0'; p++){
      if(*p==' ') *p='_';
      if(*p=='@') *p='_';
    }
  }

  return envValue;
}

/*********************************************************/
/* TCP Connection routine                                 */
/*  from UNIX NETWORK PROGRAMMING,Vol.1,Second Edition,   */
/*      By W. Richard Stevens, Published By Prentice Hall */
/*       ftp://ftp.kohala.com/pub/rstevens/unpv12e.tar.gz */
/**********************************************************/
int tcp_connect(const char *host, const char *serv){
	int				sockfd, n;
	struct addrinfo	hints, *res, *ressave;

	bzero(&hints, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0){
		err_msg("tcp_connect error for %s, %s: %s",
				 host, serv, gai_strerror(n));
		return -1;
	}
	ressave = res;

	do {
		sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
		if (sockfd < 0)
			continue;	/* ignore this one */

		if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
			break;		/* success */

		Close(sockfd);	/* ignore this one */
	} while ( (res = res->ai_next) != NULL);

	if (res == NULL){	/* errno set from final connect() */
		err_msg("tcp_connect error for %s, %s", host, serv);
		return -1;
	}

	freeaddrinfo(ressave);

	return(sockfd);
}

/****************************************/
/****************************************/
void CreateCookie(char *cookie){
  if(debug>1) err_msg("DEBUG:=>createCookie( )");
  createCookie(cookie);
  if(debug>1) err_msg("DEBUG:<=createCookie(%s)", cookie);  
}

char *GetServicePortStr(char *servName){
  char *ret;
  if(debug>1) err_msg("DEBUG:=>getServicePortStr(%s)", servName);
  ret = getServicePortStr(servName);
  if(debug>1) err_msg("DEBUG:(%s)<=getServicePortStr( )", ret);  
  return ret;
}

int Pclose(FILE *stream){
  int ret;

  if(debug>1) err_msg("DEBUG:=>pclose( )");
  ret = pclose(stream);
  if(debug>1) err_msg("DEBUG:<=pclose( )");  

  return ret;
}

int Lock(int fd){
  int ret;

  if(debug>1) err_msg("DEBUG:=>lock( )");
  ret=lock(fd);
  if(debug>1) err_msg("DEBUG:(%d)<=lock( )",ret);

  return ret;
}

int Unlock(int fd){
  int ret;

  if(debug>1) err_msg("DEBUG:=>unlock( )");
  ret=unlock(fd);
  if(debug>1) err_msg("DEBUG:(%d)<=unlock( )",ret);

  return ret;
}

int Open(const char *pathname, int oflag, mode_t mode){
	int		fd;

	if ( (fd = open(pathname, oflag, mode)) == -1)
		err_msg("open error for %s", pathname);
	return(fd);
}

int Close(int fd){
  int ret;

  /*if( (ret=close(fd)) == -1)
   *  err_msg("close error");
   */

  ret=close(fd);

  return ret;
}

pid_t Fork(void){
	pid_t	pid;

	if ( (pid = fork()) == -1)
		err_msg("fork error");
	return(pid);
}

int Pipe(int *fds){
  int ret;
	if ((ret=pipe(fds)) < 0)
		err_msg("pipe error");

	return ret;
}

void *Malloc(size_t size){
	void	*ptr;

	if ( (ptr = malloc(size)) == NULL)
		err_msg("malloc error");
	return(ptr);
}

int Tcp_connect(const char *host, const char *serv){
	return(tcp_connect(host, serv));
}

