/*
 * <insert one-line description of what the program does>
 * Copyright (c) <2008-2009>, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *

File Name:          DeviceInfo.c

Description:

Environment (opt):
 
Notes (opt):
  =====================================================================
  Revision   Revision History		Author     Date
  =====================================================================
  
  =====================================================================
****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <stdarg.h>
#include <ctype.h>
#include <netinet/in.h>
#include <errno.h>
#include "InfoMgmt/InfoMgmt.h"
#include "TPMCtrl/TPMCtrl.h"
#include "GUI/error.h"


/* Description: Get Local DNS information
 * Module: Device Info
 * Parameter: locdns (local dns info which we need)
 * Return Value: 0 represent SUCCEED, POSITIVE INTEGER represent FAIL. 
*/
static int DeviceInfo_GetLocDns(char *locdns)
{
	int nDnsCount = 0;
	FILE *fp = NULL;
	char *str = locdns;

	char buf[MAXNUM_PER_LINE], ip[16];

	/*parameter validity check*/
	if(locdns == NULL)
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}
	
	*str++ = NEWNICFLAG;
	
	/*open dns config file /etc/resolv.conf*/
	if((fp = fopen(_PATH_RESOLV_CONF, "r")) == NULL)
	{
		WriteLog(INFOMGMT_ERR_NORESOLVCONF);
		return INFOMGMT_ERR_NORESOLVCONF;
	}
	while(fgets(buf, MAXNUM_PER_LINE, fp))
	{
		/*get the string which follows "nameserver "*/
		if(sscanf(buf, "nameserver %16s", ip))
		{
			if(*(str - 1) != NEWNICFLAG)
				*str++ = SPLITFLAG;
			strcpy(str, ip);
			str += strlen(ip);
			nDnsCount++;

			/*could record MAXRECCOUNT nameserverS at most*/
			if(nDnsCount >= MAXRECCOUNT)
			{
				fclose(fp);
				return 0;
			}
		}
	}
	
	*str = '\0';
	
	fclose(fp);
	return 0;
}


/* Description: Translate ip information from HEX STRING to SOCKADDR
 * Module: Device Info
 * Parameter: 	buf (contains the HEX STRING)
 * 		sa (constains sockaddr which is translated from buf)
 * Return Value: 0 represent SUCCEED, POSITIVE INTEGER represent FAIL. 
*/
static int get_sockaddr(char *buf, struct sockaddr *sa)
{
	char *tmp = buf, *bp = NULL;
        unsigned int i = 0;
    	unsigned val = 0;
        struct sockaddr_in *sinp = NULL;

	/*parameter validity check*/
	if(buf == NULL || sa == NULL)
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}
    	sinp = (struct sockaddr_in *) sa;
	sinp->sin_family = AF_INET;
	sinp->sin_port = 0;

    	bp = (char *) &val;
        for (i = 0; i < sizeof(sinp->sin_addr.s_addr); i++) {
		*tmp = toupper(*tmp);
		
		if ((*tmp >= 'A') && (*tmp <= 'F'))
	    		bp[i] |= (int) (*tmp - 'A') + 10;
		else if ((*tmp >= '0') && (*tmp <= '9'))
		    	bp[i] |= (int) (*tmp - '0');
		else
		{
			WriteLog(INFOMGMT_ERR_GW_CHAR);
			return INFOMGMT_ERR_GW_CHAR;
		}
		bp[i] <<= 4;
		tmp++;
		*tmp = toupper(*tmp);

		if ((*tmp >= 'A') && (*tmp <= 'F'))
		    	bp[i] |= (int) (*tmp - 'A') + 10;
		else if ((*tmp >= '0') && (*tmp <= '9'))
		    	bp[i] |= (int) (*tmp - '0');
		else
		{
			WriteLog(INFOMGMT_ERR_GW_CHAR);
		    	return INFOMGMT_ERR_GW_CHAR;
		}

		tmp++;
	}
    
	sinp->sin_addr.s_addr = htonl(val);
        return 0;
}

/* Description: Generate the format string for /proc/net/route file
 * Module: Device Info
 * Parameter: 
 * Return Value: NULL represent FAIL, or format string will be returned. 
*/
static char *procroute_fmt(char *namep, int more, FILE * fd,...)
{
    	char buf[512], fmt_result[512] = "";
	char *title = NULL, *head = NULL, *hdr = NULL;
   	va_list ap;

	/*parameter validity check*/
	if(fd == NULL || namep == NULL)
		return NULL;

    	if (!fgets(buf, (sizeof buf) - 1, fd))
		return NULL;
    	strcat(buf, " ");

    	va_start(ap, fd);
    	title = va_arg(ap, char *);
    	for (hdr = buf; hdr;) 
	{
		while (isspace(*hdr) || *hdr == '|')
	    		hdr++;
		head = hdr;
		hdr = strpbrk(hdr, "| \t\n");
		if (hdr)
	    		*hdr++ = 0;

		if (!strcmp(title, head)) 
		{
	    		strcat(fmt_result, va_arg(ap, char *));
	    		title = va_arg(ap, char *);
	    		if (!title || !head)
				break;
		} 
		else 
		{
	    		strcat(fmt_result, "%*s");	/* XXX */
		}
		strcat(fmt_result, " ");
    	}
    	va_end(ap);

    	if (!more && title) 
	{
		fprintf(stderr, "warning: %s does not contain required field %s\n", namep, title);
		return NULL;
    	}
    	return strdup(fmt_result);
}


/* Description: Get Gateway information
 * Module: Device Info
 * Parameter: gateway (gateway info which we need)
 * Return Value: 0 represent SUCCEED, POSITIVE INTEGER represent FAIL. 
*/
static int DeviceInfo_GetGateWay(char *gateway)
{
	char buf[1024],iface[16], flags[64], gate_addr[128], net_addr[128], mask_addr[128];
	int num, iflags, metric, refcnt, use, mss, window, irtt;
	FILE *fp = fopen(_PATH_PROCNET_ROUTE, "r");
	char *fmt = NULL;
	char *str = NULL;
	int ret;

	/*parameter validity check*/
	if(gateway == NULL)
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}
	
	if (!fp) 
	{
		WriteLog(INFOMGMT_ERR_NOPROCNETROUTE);
	    	return INFOMGMT_ERR_NOPROCNETROUTE;
	}

	/*generate the format string for /proc/net/route/ file*/
	fmt = procroute_fmt(_PATH_PROCNET_ROUTE, 0, fp,
			"Iface", "%16s",
			"Destination", "%128s",
			"Gateway", "%128s",
			"Flags", "%X",
			"RefCnt", "%d",
			"Use", "%d",
			"Metric", "%d",
			"Mask", "%128s",
			"MTU", "%d",
			"Window", "%d",
			"IRTT", "%d",
			NULL);


	if(!fmt)
	{
		fclose(fp);		
		WriteLog(INFOMGMT_ERR_FMT);
		return INFOMGMT_ERR_FMT;
	}

	while(fgets(buf, 1023, fp))
	{
		struct sockaddr snet_gateway;
		/*get the value according to the format*/
		sscanf(buf, fmt, iface, net_addr, gate_addr, &iflags, &refcnt, &use, &metric, mask_addr, &mss, &window, &irtt);

		/*get GATEWAY info*/
		if(iflags &0x2)
		{
			/*translate the gateway info from HEX string to ip fmt*/
			if(ret = get_sockaddr(gate_addr, &snet_gateway))
			{
				fclose(fp);
				return ret;
			}	
			str = (char *)inet_ntoa(((struct sockaddr_in*)(&snet_gateway))->sin_addr);
			*gateway = NEWNICFLAG;
			strcpy(gateway + 1, str);
			fclose(fp);
			return 0;
		}
	}
	fclose(fp);
	WriteLog(INFOMGMT_ERR_NOGW);
	return INFOMGMT_ERR_NOGW;
}


/* Description: Get Local IP Address information
 * Module: Device Info
 * Parameter: ipaddress (local ip info which we need)
 * Return Value: 0 represent SUCCEED, POSITIVE INTEGER represent FAIL. 
*/
static int DeviceInfo_GetIPAddress(char *ipaddress)
{
	struct ifconf ifc;
	struct ifreq buf[16];
	int fd, ifn, nIpCount = 0;
	char *str = ipaddress, *ip = NULL;
	
	/*parameter validity check*/
	if(ipaddress == NULL)
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}

	if((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
	{
		ifc.ifc_len = sizeof(buf);
		ifc.ifc_buf = (caddr_t)buf;

		if(ioctl(fd, SIOCGIFCONF, (char*)&ifc) == 0)
		{
			/*get the number of network interfaces*/
			ifn = ifc.ifc_len/sizeof(struct ifreq);
			if(ifn == 0)
			{
				*str = '\0';
				WriteLog(INFOMGMT_ERR_NOIF);
				return INFOMGMT_ERR_NOIF;
			}
			
			while(ifn-- > 0)
			{

				*str++ = NEWNICFLAG;

				if(ioctl(fd, SIOCGIFADDR, (char*)&buf[ifn]) == 0)
				{
					/*get the ipaddress of current interface*/
					ip = (char *)inet_ntoa(((struct sockaddr_in*)(&buf[ifn].ifr_addr))->sin_addr);
					strcpy(str, ip);
					str += strlen(ip);
					nIpCount++;
					/*could record MAXRECCOUNT ipaddressS at most*/
					if(nIpCount >= MAXRECCOUNT)
						return 0;
	
				}
				else
				{
					WriteLog(INFOMGMT_ERR_IOCTL);
					return INFOMGMT_ERR_IOCTL;
				}

				
			}
		}
		else
		{
			WriteLog(INFOMGMT_ERR_IOCTL);
			return INFOMGMT_ERR_IOCTL;
		}
	}
	else
	{
		WriteLog(INFOMGMT_ERR_SOCKET);
		return INFOMGMT_ERR_SOCKET;
	}
	*str = '\0';
	return 0;
}


/* Description: Get boot tick information
 * Module: Device Info
 * Parameter: boottick (local boottick info which we need)
 * Return Value: 0 represent SUCCEED, POSITIVE INTEGER represent FAIL. 
*/
static int DeviceInfo_GetBootTick(int *boottick)
{
	/*parameter validity check*/
	if(boottick == NULL)
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}

        int ret = 0;
        int provisioned=0;
	
	*boottick=0;
        ret = OtherInfo_Provisioned(&provisioned, "GET");
        if (ret) return ret;

        if (provisioned){

	TPMCtrl tpm;
	if(ret = TPMCtrl_Construct(&tpm))
		return ret;
	if(ret = TPMCtrl_Bootick(&tpm, boottick))
		return ret;	
	}
	
	return 0;
}

/* Description: Get hardware id information
 * Module: Device Info
 * Parameter: hwid (hardware id info which we need)
 * Return Value: 0 represent SUCCEED, POSITIVE INTEGER represent FAIL. 
*/
static int DeviceInfo_GetHardwareID(char *hwid)
{
	/*parameter validity check*/
	if(hwid == NULL)
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}

	int ret = 0;
	int provisioned=0;

	ret = OtherInfo_Provisioned(&provisioned, "GET");
	if (ret) return ret;

	if (provisioned){
		TPMCtrl tpm;
		if(ret = TPMCtrl_Construct(&tpm))
			return ret;
		if(ret = TPMCtrl_HWID(&tpm, hwid))
			return ret;
	}
	else
	{
		//char hwid[15];
		ret = Provision_GetMacAddress(hwid);
		if (ret) return ret;
	}
	return 0;
}


/* Description: Get student id information
 * Module: Device Info
 * Parameter: stid (student id info which we need)
 * Return Value: 0 represent SUCCEED, POSITIVE INTEGER represent FAIL. 
*/
static int DeviceInfo_GetStudentID(char *stid)
{
	/*parameter validity check*/
	if(stid == NULL)
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}
	return OtherInfo_StudentID(stid, "GET");	
}

/* Description: Get Proxy information
 * Module: Device Info
 * Parameter: proxy (local proxy info which we need)
 * Return Value: 0 represent SUCCEED, POSITIVE INTEGER represent FAIL. 
*/
static int DeviceInfo_GetProxy(char *proxy)
{	
	/*parameter validity check*/
	if(proxy == NULL)
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}
	return ProxyInfo_Construct(proxy);
}


/* Description: StudentID Operation
 * Module: Device Info
 * Parameter: pstid (the StudentID Value), cmd (operation command)
 * Return Value: 0 represent SUCCEED, POSITIVE INTEGER represent FAIL.
 */

int DeviceInfo_StudentID(char *stid, char *cmd)
{
	/*parameter validity check*/
	if(stid == NULL || cmd == NULL)
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}
	if(!strcmp(cmd, "GET"))
		return OtherInfo_StudentID(stid, "GET");
	else if(!strcmp(cmd, "SET"))
		return OtherInfo_StudentID(stid, "SET");
	WriteLog(INFOMGMT_ERR_INVALIDCMD);
	return INFOMGMT_ERR_INVALIDCMD;
}

/*Start add by Roc Zhang 2007-9-28*/
/* Description: Get Host name information
 * Module: Device Info
 * Parameter: hostname ( host name info which we need)
 * Parameter: buf_len  ( the  buffer length )
 * Return Value: 0 represent SUCCEED, other  represent FAIL. 
*/
static int DeviceInfo_GetHostname(char *hostname, int buf_len)
{
	/*parameter validity check*/
	if(hostname == NULL || buf_len <= 0 )
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}

	/*success*/
	if ( 0 == gethostname( hostname, buf_len) )
		return 0;
	
	WritestrLog(MODULE_INFOMGMT,"get hostname error, use ip address instead");
	
	DeviceInfo_GetIPAddress(hostname);
	return errno;
}
/* End add*/

/* Description: Get Device Information
 * Module: Device Info
 * Parameter: pDevInfo (device info which we need)
 * Return Value: 0 represent SUCCEED, POSITIVE INTEGER represent FAIL. 
*/
int DeviceInfo_Construct(DeviceInfo *pDevInfo)
{
	int ret = 0, err = 0;
	/*parameter validity check*/
	if(pDevInfo == NULL)
	{
		WriteLog(INFOMGMT_ERR_PARAM);
		return INFOMGMT_ERR_PARAM;
	}
	memset(pDevInfo, 0, sizeof(DeviceInfo));
	if(ret = DeviceInfo_GetIPAddress(pDevInfo->IPAddress))
	{
#ifdef DEBUG
printf("DeviceInfoErrno1: %d\n", ret);
#endif
		err = ret;
	}

	if(ret = DeviceInfo_GetLocDns(pDevInfo->LocDns))
	{
#ifdef DEBUG
printf("DeviceInfoErrno2: %d\n", ret);
#endif
		err = ret;
	}

	if(ret = DeviceInfo_GetProxy(pDevInfo->Proxy))
	{
#ifdef DEBUG
	printf("DeviceInfoErrno3: %d\n", ret);
#endif
	//	err = ret;
	}

	if(ret = DeviceInfo_GetStudentID(pDevInfo->StudentID))
	{
#ifdef DEBUG
printf("DeviceInfoErrno4: %d\n", ret);
#endif
		err = ret;
	}

	if(ret = DeviceInfo_GetGateWay(pDevInfo->GateWay))
	{
#ifdef DEBUG			
printf("DeviceInfoErrno5: %d\n", ret);
#endif
		err = ret;
	}

	if(ret = DeviceInfo_GetHardwareID(pDevInfo->HardwareID))
	{
#ifdef DEBUG
printf("DeviceInfoErrno6: %d\n", ret);
#endif
		return ret;
	}

	if(ret = DeviceInfo_GetBootTick(&(pDevInfo->BootTick)))
	{
#ifdef DEBUG		
printf("DeviceInfoErrno7: %d\n", ret);
#endif
		return ret;
	}

	/*Start add by Roc Zhang 2007-9-28*/
	if( ret = DeviceInfo_GetHostname( pDevInfo->HostName, 200) )
	{
#ifdef DEBUG		
printf("DeviceInfoErrno8: %d\n", ret);
#endif
		return ret;
	}

	if (strlen(pDevInfo->HostName)<1)
	{
		strcpy(pDevInfo->HostName,pDevInfo->IPAddress);
	}
	/* end add*/

	if(err)
	{
		WriteLog(INFOMGMT_ERR_DEVICEINFO);
		return INFOMGMT_ERR_DEVICEINFO;
	}
	else
	{
		return 0;
	}
}
