/**************************************************
OpengateM - MAC address authentication system 
 cgi module for reg/update by owner oneself

 The device having Web browser is registered and updated with this program.
 When accessing this cgi, The MAC address of accessed device is detected, 
 and the page for new registration or update of registration is returned.

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"

/***************************************************/
/*  main routine called as cgi from Web server     */
/***************************************************/
int  main(int argc, char **argv)
{
  char language[WORDMAXLN]="";      /* browser prefered language(e.g.:ja,en) */
  char userId[USERMAXLN]="";        /* user id */
  char extraId[USERMAXLN]="";       /* extra id used as user@extra */
  char requestStr[BUFFMAXLN]="";    /* http request string */
  char* progName="";                /* the name of this program in argv[0] */
  char mailDefault[BUFFMAXLN]="";   /* default mail address for receiving warning mail */
  char macAddress[ADDRMAXLN]="";    /* client MAC address */
  char macAddressReq[ADDRMAXLN]=""; /* MAC address in request text */
  char ipAddress[ADDRMAXLN]="";     /* IP address */
  char* dev;                        /* name of network interface */
  char ipAddressInQueue[ADDRMAXLN]="";  /* IP address found in queue */
  char macAddressInQueue[ADDRMAXLN]=""; /* MAC address found in queue */ 
  char deviceName[WORDMAXLN]="";    /* client device name */
  char mailAddress[BUFFMAXLN]="";   /* user's mail address for recieving warning mail */
  int isNatOrRouter=0;              /* flag for nat or router insertion (checked in md) */
  char redirectedUrl[BUFFMAXLN]=""; /* url from which the page is forwarded */
  int modified=FALSE;               /* register or update is done */
  int splash=FALSE;                 /* Splash page only management */

  /* drop root privilege (to prevent dangerous action) */
  seteuid(getuid());

  /* if this is executed in shell with '-v' option, show makedir */
  /* this is prepared to show information about version */
  if(argc>1){
    if(strcmp(argv[1],"-v")==0){
      printf("makedir: %s\n", MAKEDIR);
    }else{
      printf("This is cgi program\n");
      printf("To show version, run this on console with '-v' option\n");
    }
    exit(0);
  }

  /* save program load path to the valiable in proc.c */
  /* and get this program name from it */
  saveLoadPath(argv[0]);
  progName = getProgramName();

  /* prepare configuration file */
  if(OpenConfFile()==-1){
    PutMessageToClient("Check config file by running this cgi on console");
    return 0;
  }
 
  /* start syslog */
  errToSyslog(atoi(GetConfValue("Syslog/Enable")));
  openlog(progName, LOG_PID, atoi(GetConfValue("Syslog/Facility")));

  /* initialize configuration and database */
  InitConf();
  if(!InitMngDb()) return 0;
  if(!InitWorkDb()) return 0;

  /* get language and redirectedUrl */
  /* the two data are acquired from env-var "QUERY_STRING" (HTTP GET) */
  /* (other data are acquired from stdin(HTTP POST), below) */
  GetLangFromQueryString(language);
  GetRedirectedUrlFromQueryString(redirectedUrl);

  /* check client's http-agent, if the agent is not allowed in conf, exit */
  if(!IsAccessedFromAllowableAgent()){
    SetMessage(UnallowableAgent);
    PutDenyToClient(language);
    CloseMngDb();
    return 0;
  }

  /*** Get request from client ***/
  /* copy post data to requestStr */
  GetPostData(requestStr, BUFFMAXLN);

  /*** Check the Splash-only setting ***/
  /* (splash-only can be defined in AuthServer/Protocol in conf. */
  /*  if defined, no authentication is requested and userid="?") */
  splash=IsSplashOnlyManagement();

  /* if not splash-only, exec authentication */
  if(!splash){

    /* if cgi name is not defined in conf file, return error */
    if(isNull(GetConfValue("OwnCgi"))){
      err_msg("ERR at %s#%d: OwnCgi cannot get from conf",__FILE__,__LINE__);
      return 0;
    }
    
    /* get userid and other information from requestStr */
    /* if not get these, send authentication page and exit */
    if(!GetUserId(requestStr, userId, extraId, language, NORMALUSER,
		  GetConfValue("OwnCgi"),mailDefault,redirectedUrl)){
      CloseMngDb();
      return 0;
    }
  }

  /*** Get client MAC address ***/
  /* get the client ip address from environment variable */
  strlcpy(ipAddress, getenv("REMOTE_ADDR"), ADDRMAXLN);

  /* get NIC interface name in conf */
  if(isNull(dev=GetConfValue("Device"))){
    err_msg("ERR at %s#%d: Device cannot get from conf file",__FILE__,__LINE__);
    return 0;
  }

  /* get IP-MAC pair list from ARP/NDP (received from the NIC) */
  /* the pairs are stored in the queue (controlled by queue.c) */
  Initqueue();
  if(strchr(ipAddress,':')==NULL) GetMacAddrListFromArp(dev);
  else GetMacAddrListFromNdp(dev);

  /* search the pair in queue matching to the client ip address */
  /* and set the paired mac address as the client mac address */
  /* (the mac address from request content is not employed) */
  while(Dequeue(macAddressInQueue, ipAddressInQueue)){
    if(strcmp(ipAddress, ipAddressInQueue)==0){
      strlcpy(macAddress, macAddressInQueue, ADDRMAXLN);
      break;
    }
  }

  /* clear the IP-MAC pair list */
  /*** the queue is valid only from above Initqueue() to this Freequeue() */
  Freequeue();

  /* if sent too many requests from one MAC per day, send error and exit */
  if(CountMacModifyPerDayInMngDb("", "", macAddress) 
     > atoi(GetConfValue("MaxMacModifyPerDay"))){
    SetMessage(ModifyCountOver);
    PutDenyToClient(language);
    CloseMngDb();
    return 0;
  }

  /*** Check the acquired MAC ***/
  /* if accessed via nat or router, exit  */
  /* if via nat, the function returns 1. if via router, it returns 2. */
  isNatOrRouter=IsNatSuspectedInWorkDb(macAddress);
  if(isNatOrRouter==NAT || isNatOrRouter==ROUTER){
    SetMessage(SuspectNat);
    PutDenyToClient(language);
    CloseMngDb();
    return 0;
  }

  /*** splash only management ***/
  /* if splash page only, exec reg/up without analyzing request */
  if(splash){
    if(RegOrUpNobodyMacAddr(macAddress)){
      PutMacAddressToServers(macAddress);
      ReturnToRedirectedPage(redirectedUrl, language);
    }else{
      SetMessage(NoService);
      PutDenyToClient(language);
    }
    CloseMngDb();
    return 0;
  }
    
  /*** execute the client request ***/
  /* if requestStr includes update-request, execute update */
  if(AnalyzeUpdateRequestAndExecute(requestStr, userId, extraId)){
    SetMessage(UpdateSuccess);
    modified=TRUE;
  }
  
  /* if requestStr includes registration-request */
  else if(AnalyzeRegisterRequest(requestStr, macAddressReq, 
				 deviceName, mailAddress)){
    
    /* execute registration */
    RegistMacAddrToMngDb(macAddress,deviceName,userId,extraId,mailAddress);
    PutMacModifyLogToMngDb(userId, extraId, macAddress, 'R');
    PutMacAddressToServers(macAddress);
    SetMessage(RegisterSuccess);
    modified=TRUE;
  }

  /*** Send back web page ***/
  /* if registration/updating is done, return to the redirected site */
  if(modified){
    ReturnToRedirectedPage(redirectedUrl, language);
  }
  
  /* if no execution and the MAC is registered, send back update page */
  else if(IsMacAddrFoundInMngDb(macAddress)){
    PutUpdatePageToClient(language, userId, extraId, OWNER, redirectedUrl);
  }
  
  /* the MAC is not-registered */
  /* but allowable registration device count is over the limit */
  /* send back update page with overflow error message */
  else if(CountMacAddrsForUserInMngDb(userId,extraId) 
	  >= atoi(GetConfValue("MaxDevices"))){
    SetMessage(DeviceCountOver);
    PutUpdatePageToClient(language, userId, extraId, OWNER, redirectedUrl);
  }

  /* MAC is not-registered and device count is under the limit */
  else{

    /* if mail address is acquired, restore mail address */
    if(isNull(mailAddress)) strlcpy(mailAddress, mailDefault, BUFFMAXLN);

    /* send back registration page */
    PutRegisterPageToClient(language, macAddress, deviceName, mailAddress, 
			    userId, extraId, OWNER, redirectedUrl);
  }

  /* finalize */
  CloseMngDb();
  return 0;
}

