/*******************************************************************************/
/*                                                                             */
/*  Copyright 2004 Pascal Gloor                                                */
/*                                                                             */
/*  Licensed under the Apache License, Version 2.0 (the "License");            */
/*  you may not use this file except in compliance with the License.           */
/*  You may obtain a copy of the License at                                    */
/*                                                                             */
/*     http://www.apache.org/licenses/LICENSE-2.0                              */
/*                                                                             */
/*  Unless required by applicable law or agreed to in writing, software        */
/*  distributed under the License is distributed on an "AS IS" BASIS,          */
/*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/*  See the License for the specific language governing permissions and        */
/*  limitations under the License.                                             */
/*                                                                             */
/*******************************************************************************/


#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>

#include <p_defs.h>
#include <p_config.h>

/* reading configuration file */

int p_config_load(struct config_t *config, struct peer_t *peer, uint32_t mytime)
{
	FILE *fd;
	char line[128];

	/* cleaning 'newallow' */
	{
		int a;
		for(a=0; a<MAX_PEERS; a++)
		{
			peer[a].newallow = 0;
		}
	}

	config->uid = -1;
	config->gid = -1;

	if ( ( fd = fopen(config->file, "r") ) == NULL )
	{
		printf("error: failed to read %s\n",config->file);
		return -1;
	}

	while(fgets(line, sizeof(line), fd))
	{
		char *s = line;
		if ( strncmp(s, "#", 1) == 0 ) { continue; }
		s = strtok(s, " ");

		if ( !strcmp(s,"bgp_router_id"))
		{
			s = strtok(NULL, " ");
			if ( s != NULL && strlen(s) > 0 && strlen(s) <= 16 )
			{
				config->routerid = inet_addr(s);
				#ifdef DEBUG
				printf("DEBUG: config bgp_router_id %s",s);
				#endif
			}
		}
		else if ( !strcmp(s,"local_ip"))
		{
			s = strtok(NULL, " ");
			if ( s != NULL && strlen(s) > 0 && strlen(s) <= 16 )
			{
				config->ip = inet_addr(s);
				#ifdef DEBUG
				printf("DEBUG: config local_ip %s",s);
				#endif
			}
		}
		else if ( !strcmp(s,"user"))
		{
			s = strtok(NULL, " ");
			if ( s != NULL && strlen(s) > 0 && strlen(s) <= 50 )
			{
				struct passwd *mypwd;
				memcpy(s+strlen(s)-1,"\0",1);
				mypwd = getpwnam(s);
				if ( mypwd == NULL )
				{
					config->uid = -1;
					config->gid = -1;
				}
				else
				{
					config->uid = mypwd->pw_uid;
					config->gid = mypwd->pw_gid;
				}
				#ifdef DEBUG
				printf("DEBUG: config user %i/%i (uid/gid) %s\n",config->uid,config->gid,s);
				#endif
			}
		}
		else if ( !strcmp(s,"local_as"))
		{
			s = strtok(NULL, " ");
			if ( s != NULL && strlen(s) > 0 && strlen(s) <=  6 )
			{
				config->as = atoi(s);
				#ifdef DEBUG
				printf("DEBUG: config local_as %s",s);
				#endif
			}
		}
		else if ( !strcmp(s,"local_port"))
		{
			s = strtok(NULL, " ");
			if ( s != NULL && strlen(s) > 0 && strlen(s) <= 6 )
			{
				config->port = atoi(s);
				#ifdef DEBUG
				printf("DEBUG: config local_port %s",s);
				#endif
			}
		}
		else if ( !strcmp(s,"bgp_holdtime"))
		{
			s = strtok(NULL, " ");
			if ( s != NULL && strlen(s) > 0 && strlen(s) <= 4 )
			{
				config->holdtime = atoi(s);
				#ifdef DEBUG
				printf("DEBUG: config bgp_holdtime %s",s);
				#endif
			}
		}
		else if ( !strcmp(s,"neighbor"))
		{
			s = strtok(NULL, " ");
			if ( s != NULL && strlen(s) > 0 && strlen(s) <= 16 )
			{
				uint32_t peer_ip = inet_addr(s);
				#ifdef DEBUG
				printf("DEBUG: config neighbor ip %s(%u) ",s,peer_ip);
				#endif
				s = strtok(NULL, " ");
				if ( s != NULL && strlen(s) > 0 && strlen(s) <= 6 )
				{
					uint16_t peer_as = atoi(s);
					#ifdef DEBUG
					printf("as %s",s);
					#endif
					p_config_add_peer(peer,peer_ip,peer_as,mytime);
				}
				#ifdef DEBUG
				else { printf("\n"); }
				#endif
			}
		}
	}

	fclose(fd);

	/* clearning no more allowed peers */
	{
		int a;
		for(a=0; a<MAX_PEERS; a++)
		{
			if ( peer[a].newallow == 0 )
			{
				peer[a].status = 0;
				peer[a].allow  = 0;
			}
		}
	}

	/* check that all required values are set */

	if ( config->routerid == 0 )
	{
		printf("configuration error: no bgp router id set\n");
		return -1;
	}
	if ( config->as == 0 )
	{
		printf("configuration error: no local AS set\n");
		return -1;
	}

	if ( config->uid == -1 || config->gid == -1 )
	{
		printf("configuration error: could not find user\n");
		return -1;
	}

	return 0;
}

/* add, update of peers */
void p_config_add_peer(struct peer_t *peer, uint32_t ip, uint16_t as, uint32_t mytime)
{
	int a;
	if ( ip == 0 || as == 0 ) { return; }

	for(a = 0; a<MAX_PEERS; a++)
	{
		if ( peer[a].ip == ip && peer[a].allow == 1 )
		{
			if ( peer[a].as != as )
			{
				peer[a].as     = as;
				peer[a].cts    = mytime;
				peer[a].status = 0;
			}
			peer[a].newallow = 1;
			return;
		}
	}

	for(a = 0; a<MAX_PEERS; a++)
	{
		if ( peer[a].allow == 0 )
		{
			peer[a].ip       = ip;
			peer[a].as       = as;
			peer[a].allow    = 1;
			peer[a].newallow = 1;
			peer[a].status   = 0;
			peer[a].sock     = 0;
			peer[a].cts      = mytime;
			return;
		}
	}
}