*** raddb/dictionary.orig	Mon Nov  8 15:28:15 1999
--- raddb/dictionary	Mon Nov  8 15:29:30 1999
***************
*** 121,126 ****
--- 121,134 ----
  ATTRIBUTE	Login-Time		1042	string
  
  #
+ #	Experimental Attributes for SQL Accounting
+ #
+ ATTRIBUTE	LocalTime		1100	integer
+ ATTRIBUTE	GMTime			1101	integer
+ ATTRIBUTE	SQL-LocalTime		1102	string
+ ATTRIBUTE	SQL-GMTime		1103	string
+ 
+ #
  #	Non-Protocol Attributes
  #	These attributes are used internally by the server
  #
*** raddb/sqlconf.orig	Mon Nov  8 15:28:09 1999
--- raddb/sqlconf	Mon Nov  8 15:45:15 1999
***************
*** 0 ****
--- 1,92 ----
+ #
+ # sqlconf
+ #
+ # This file controls how radiusd connects to the SQL server, and what
+ # attributes get logged.  There isn't any hard and fast rule as to how
+ # to lay out your database, but please read the README.sql file in the
+ # doc directory to see a sample schema that should work well to get
+ # you started (and will work with the definitions below).
+ #
+ 
+ # Do we want to do SQL accounting?
+ SQL-Accounting				yes
+ 
+ # Accounting Database type, host, port, user, pass, etc...
+ SQL-Acct-Type				MySQL
+ SQL-Acct-Host				localhost
+ SQL-Acct-User				radius
+ SQL-Acct-Passwd				foobar
+ SQL-Acct-Database			radius
+ SQL-Acct-Table				radius_log
+ SQL-Acct-Insert-Cmd			REPLACE
+ 
+ # Are we interested in Stop records?
+ SQL-Log-Stop-Records			yes
+ 
+ # How about Start records?
+ SQL-Log-Start-Records			no
+ 
+ # And those pesky "alive" records?
+ SQL-Log-Alive-Records			no
+ 
+ # What about unknown record types?
+ SQL-Log-Unknown-Records			no
+ 
+ # Do you want to log accounting requests with blank usernames and caller-ids?
+ # (not yet implemented)
+ #SQL-Log-Blank-Username			yes
+ 
+ 
+ # LocalTime, GMTime, SQL-LocalTime, and SQL-GMTime are special cases.
+ # They are generated internally by the sql accounting code.  It is
+ # reccomended that you use GMTime if you have POPS in multiple time
+ # zones or plan to deploy pops in multiple time zones.
+ #
+ # LocalTime and GMTime are UNIX time values (seconds since epoch)
+ #
+ # For the SQL time fields, it is reccomended to let your SQL server
+ # generate the timestamps itself, but you can have radiusd do that for
+ # you.  Additionally, if you use these fields, you will need to have
+ # these four attributes in your dictionary.  The value is not important.
+ #
+ # ATTRIBUTE       LocalTime               1100    integer
+ # ATTRIBUTE       GMTime                  1101    integer
+ # ATTRIBUTE       SQL-LocalTime           1102    string
+ # ATTRIBUTE       SQL-GMTime              1103    string
+ 
+ #LocalTime			event_time
+ GMTime				event_time
+ #SQL-LocalTime			sql_event_time
+ #SQL-GMTime			sql_event_time
+ 
+ 
+ # And here, the fun begins.  Something nobody else has done yet
+ # (At least none that I could find :)
+ #
+ # Make a map of your accounting attributes that you want to capture,
+ # and associate them with the appropriate fields in your SQL database.
+ #
+ # Any attributes listed here that are not in the accounting record will
+ # be ignored.
+ #
+ # Likewise, any attribute in the accounting record that is not mapped
+ # here will also be ignored.
+ #
+ # In other words, we're only going to pay attention to those attributes
+ # that both exist here and in the actual accounting request.  This allows
+ # us to get rid of useless or unwanted information.
+ #
+ 
+ Acct-Session-Id			session_id
+ User-Name			user_name
+ Caller-ID			caller_id
+ NAS-IP-Address			nas_ip
+ Acct-Session-Time		session_time
+ #Connect-Info			connect_info
+ Ascend-Disconnect-Cause		ascend_disconnect_cause
+ Ascend-Connect-Progress		ascend_connect_progress
+ Ascend-Data-Rate		connect_rate
+ Ascend-Xmit-Rate		download_rate
+ Ascend-First-Dest		first_dest
+ Client-Port-DNIS		dnis
+ Framed-Address			framed_address
*** doc/README.sql.orig	Mon Nov  8 15:45:30 1999
--- doc/README.sql	Mon Nov  8 16:01:16 1999
***************
*** 0 ****
--- 1,148 ----
+ This is the README file for the SQL hacks
+ Author:  Troy Settle <st@i-plus.net>
+ 
+ 
+ CONTENTS
+ --------
+ 1. COMMENTS
+ 2. CREDITS
+ 3. FEATURES
+ 4. RELEASE NOTES
+ 5. INSTALLATION
+ 6. SQL Schema
+ 
+ 
+ COMMENTS
+ --------
+ 
+ To start, I want to tell you straight up that I'm not a programmer of any
+ real talent, and my skills at documentation suck even more.  Please read
+ this text as best you can, and ask questions as needed.
+ 
+ In 1997, we purchaced our first network access server, an Ascend Max 4004.  
+ At the time I didn't know any better, so I installed Ascend's radiusd.  
+ It worked well enough.  As time went on, I found a patch to allow it to
+ log accounting information directly into a MySQL database.  I implemented
+ it, and it's been in production for nearly 2 years now.
+ 
+ Several months ago, I went on a search for a better solution. I found
+ radiusd-cistron.  It did everything I needed, and then some.  There were
+ even patches for it to use MySQL as a backend for accounting.  Problem was
+ that the existing patches were not very friendly to Ascend's accounting
+ informatin, and putting a bandaid on them wasn't a desirable awnswer.  So
+ after a couple months of procrastination, I finally sat down and started
+ cranking out some code.  My first real experience in C for about 4 years.
+ 
+ Initially, this was going to be for internal use only.  But after laying
+ out the framework, I realized that this was going to turn out to be one of
+ the better radius-SQL implementations out there, so I decided to go ahead
+ and release it.
+ 
+ 
+ CREDITS
+ -------
+ 
+ I modeled some routines after the some of the code in the other SQL
+ patches availiable, but I don't think my code is similar enough to warrent
+ credit.  If you feel that I'm using a signifigant chunk of your code and
+ desire proper credit, please let me know and I'll be glad to stick your
+ name in there.
+ 
+ 
+ FEATURES
+ ---------------
+ 
+ Currently, the only feature I've implemented, is SQL accounting. So far,
+ it's working beautifully as an accounting server for ~500 ports.
+ 
+ At this point, there's only a few items that I'd like to eventually add:
+ 
+ 	Spooling in the event of a SQL breakdown.
+ 	SQL Authentication
+ 	SQL radutmp
+ 
+ There may be other features that can be added.  If you come up with one,
+ be sure to let me know.
+ 
+ 
+ RELEASE NOTES
+ -------------
+ 
+ To build, you will need to edit the Makefile for your platform. The one
+ for BSD works on FreeBSD 3.2-STABLE.  Use it as a model for writing the 
+ Makefile for your platform.  When you get it working, please be sure to 
+ email me your Makefile for inclusion into future patches.
+ 
+ Currently, there is only support for MySQL.  Adding support for other SQL
+ servers should be trivial.  If you do hack in additional functionality,
+ please send me a diff so that I can incorporate it into my patch.
+ 
+ If you run into any problems building this, please let me know what the
+ problem is, and what you had to do to make it work.  I'll be straight up
+ and honest about this, I'm not an experienced developer, and will not be
+ able to offer much help at this point.
+ 
+ There is one problem with an "assignment from incompatible pointer type"
+ that I've not yet been able to figure out.  The code does work as expected
+ though.  At this time, I believe it's safe to ignore these warnings.
+ 
+ 
+ INSTALLATION
+ ------------
+ 
+ In order to use this update, you will need to replace your existing
+ dictionary file with the one included with this distribution.  You will
+ also need to copy and edit the sqlconf file out of the raddb directory.  
+ When editing the sqlconf file, please be careful as to how you spell
+ things.  As with everything else unix, it will not be forgiving on
+ syntactical errors.
+ 
+ 
+ SQL SCHEMA
+ ----------
+ 
+ 
+ This table suits the needs for my company.  You may need to track different
+ accounting information.  If this is so, adjust the schema here to suit your
+ needs, then edit raddb/sqlconf to match.
+ 
+ 
+ #
+ # Table structure for table 'detail'
+ #
+ CREATE TABLE detail (
+   record_id int(11) DEFAULT '0' NOT NULL auto_increment,
+   sql_event_time datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
+   event_time int(11) DEFAULT '0' NOT NULL,
+   session_id char(32) DEFAULT '' NOT NULL,
+   user_name char(32) DEFAULT '' NOT NULL,
+   caller_id char(20) DEFAULT '' NOT NULL,
+   nas_ip char(16) DEFAULT '' NOT NULL,
+   session_time int(11) DEFAULT '0' NOT NULL,
+   connect_info char(32) DEFAULT '' NOT NULL,
+   ascend_disconnect_cause smallint(6) DEFAULT '0' NOT NULL,
+   ascend_connect_progress smallint(6) DEFAULT '0' NOT NULL,
+   connect_rate int(11) DEFAULT '0' NOT NULL,
+   download_rate int(11) DEFAULT '0' NOT NULL,
+   first_dest char(16) DEFAULT '' NOT NULL,
+   dnis char(10) DEFAULT '' NOT NULL,
+   framed_address char(16) DEFAULT '' NOT NULL,
+   PRIMARY KEY (record_id),
+   UNIQUE foo (session_id,nas_ip),
+   KEY BYCLID (caller_id,event_time,sql_event_time),
+   KEY BYUSER (user_name,event_time,sql_event_time),
+   KEY BYADDR (framed_address,event_time,sql_event_time)
+ );
+ 
+ 
+ 
+ 
+ I'm not sure what else needs to be said for the installation.  If you have
+ any issues let me know, and I'll do what I can to help you out.
+ 
+ 
+ Thanks,
+ 
+ Troy Settle <st@i-plus.net>
+ iPlus Internet Services
+ 
*** src/acct.c.orig	Mon Nov  8 15:26:18 1999
--- src/acct.c	Mon Nov  8 12:32:36 1999
***************
*** 655,660 ****
--- 655,663 ----
  				fputs("\t", outfd);
  				fprint_attr_val(outfd, pair);
  				fputs("\n", outfd);
+ #ifdef USE_SQL
+ 				sql_build_record(pair);
+ #endif /* USE_SQL */
  			}
  			pair = pair->next;
  		}
***************
*** 679,684 ****
--- 682,691 ----
  		fputs("\n", outfd);
  		fclose(outfd);
  	}
+ 
+ #ifdef USE_SQL
+ 				sql_save_record(pair);
+ #endif /* USE_SQL */
  
  	return ret;
  }
*** src/radiusd.c.orig	Mon Nov  8 15:26:41 1999
--- src/radiusd.c	Mon Nov  8 16:44:33 1999
***************
*** 131,136 ****
--- 131,144 ----
  	if (res == 0 && read_config_files() != 0)
  		res = -1;
  
+ #ifdef USE_SQL
+ 	/* Read the SQL config file at startup only until we get it fixed */
+ 	if(!reload) {
+ 		if (res == 0 && sql_rad_init() != 0)
+ 			res = -1;
+ 	}
+ #endif
+ 
  	if (res != 0) {
  		if (pid == radius_pid) {
  			log(L_ERR|L_CONS,
*** src/radiusd.h.orig	Mon Nov  8 15:26:35 1999
--- src/radiusd.h	Mon Nov  8 12:03:16 1999
***************
*** 279,281 ****
--- 279,297 ----
  /* timestr.c */
  int		timestr_match(char *, time_t);
  
+ /* sql.c */
+ typedef struct sql_pair {
+ 	int			type;
+ 	char			name[64];
+ 	int			attribute;
+ 	char			field[32];
+ 	char			value[128];
+ 	struct sql_attr		*next;
+ } SQL_PAIR;
+ 
+ int	sql_rad_init();
+ void	sql_pairadd(SQL_PAIR **, SQL_PAIR *);
+ void	sql_pairfree(SQL_PAIR *);
+ void	sql_save_record();
+ void	sql_build_record(VALUE_PAIR *);
+ 
*** src/sql.c.orig	Mon Nov  8 15:24:09 1999
--- src/sql.c	Mon Nov  8 15:24:05 1999
***************
*** 0 ****
--- 1,536 ----
+ #ifdef USE_SQL
+ 
+ #include	<stdio.h>
+ #include	<stdlib.h>
+ #include	<time.h>
+ #include	<ctype.h>
+ #include	<strings.h>
+ 
+ #ifdef USE_MYSQL
+ # include	<mysql/mysql.h>
+ #endif
+ 
+ #include	"radiusd.h"
+ 
+ static	char *getfirstword(char *, char *);
+ static	SQL_PAIR *sql_paircopy(SQL_PAIR *);
+ static	void sql_safeprint(char *, int, char *, int);
+ 
+ 
+ /*
+  *	Global Variables
+  */
+ 
+ int	sql_acct_type;
+ 
+ #define SQL_TYPE_MYSQL 1
+ 
+ int	sql_accounting;
+ char	sql_acct_host[64];
+ char	sql_acct_user[16];
+ char	sql_acct_pass[16];
+ char	sql_acct_database[16];
+ char	sql_acct_table[16];
+ char	sql_acct_insert_cmd[128];
+ 
+ int	sql_log_stop_records;
+ int	sql_log_start_records;
+ int	sql_log_alive_records;
+ int	sql_log_unknown_records;
+ int	sql_log_blank_username;
+ 
+ SQL_PAIR *sql_attrs = NULL;
+ 
+ /*
+  *	Write the accounting record out
+  */
+ void sql_save_record()
+ {
+ 	SQL_PAIR *tp;
+ 	int	saverecord = 1;
+ 	char	buffer[128];
+ 	char	query[8196];
+ 	char	fields[2048];
+ 	char	values[2048];
+ 	time_t	t;
+ #ifdef USE_MYSQL
+ 	MYSQL sqlfd;
+ #endif
+ 
+ 	memset(query,0,sizeof(query));
+ 	memset(fields,0,sizeof(fields));
+ 	memset(values,0,sizeof(values));
+ 
+ 	time(&t);
+ 
+ 	/* log(L_INFO,"SQL_SAVE: Saving record - maybe..."); */
+ 
+ 	for(tp = sql_attrs; tp ; tp = tp->next)
+ 	{
+ 		if(strcasecmp("LocalTime",tp->name) == 0)
+ 		{
+ 			strftime(buffer, sizeof(buffer), "%s", localtime(&t));
+ 			sprintf(values, "%s,'%s'", values, buffer);
+ 			sprintf(fields, "%s,%s", fields, tp->field);
+ 		}
+ 		else
+ 		if(strcasecmp("GMTime",tp->name) == 0)
+ 		{
+ 			strftime(buffer, sizeof(buffer), "%s", gmtime(&t));
+ 			sprintf(values, "%s,'%s'", values, buffer);
+ 			sprintf(fields, "%s,%s", fields, tp->field);
+ 		}
+ 		else
+ 		if(strcasecmp("SQL-LocalTime",tp->name) == 0)
+ 		{
+ 			strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localtime(&t));
+ 			sprintf(values, "%s,'%s'", values, buffer);
+ 			sprintf(fields, "%s,%s", fields, tp->field);
+ 		}
+ 		else
+ 		if(strcasecmp("SQL-GMTime",tp->name) == 0)
+ 		{
+ 			strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", gmtime(&t));
+ 			sprintf(values, "%s,'%s'", values, buffer);
+ 			sprintf(fields, "%s,%s", fields, tp->field);
+ 		}
+ 		else
+ 		{
+ 			
+ 			switch(tp->attribute)
+ 			{
+ 				case PW_ACCT_STATUS_TYPE:
+ 					switch(atoi(tp->value))
+ 					{
+ 						case PW_STATUS_STOP:
+ 							saverecord = sql_log_stop_records;
+ 							break;
+ 						case PW_STATUS_START:
+ 							saverecord = sql_log_start_records;
+ 							break;
+ 						case PW_STATUS_ALIVE:
+ 							saverecord = sql_log_alive_records;
+ 							break;
+ 						default:
+ 							saverecord = sql_log_unknown_records;
+ 							break;
+ 					}
+ 					break;
+ 				case PW_USER_NAME:
+ 					if (strlen(tp->value) == 0)
+ 						saverecord = sql_log_blank_username;
+ 					break;
+ 				default:
+ 					break;
+ 			}
+ 			sprintf(values, "%s,'%s'", values, tp->value);
+ 			strcpy(tp->value, "");
+ 			sprintf(fields, "%s,%s", fields, tp->field);
+ 		}
+ 	}
+ 	sprintf(query, "%s INTO %s (%s) VALUES (%s)",
+ 		sql_acct_insert_cmd,
+ 		sql_acct_table,
+ 		&fields[1],
+ 		&values[1]);
+ 
+ 	if(saverecord != 0)
+ 	{
+ 		/* log(L_INFO,"SQL_save: Query: %s",query); */
+ 		switch(sql_acct_type) {
+ 			#ifdef USE_MYSQL
+ 			case SQL_TYPE_MYSQL:
+ 				mysql_init(&sqlfd);
+ 				if(!(mysql_real_connect(&sqlfd,sql_acct_host,sql_acct_user,sql_acct_pass,sql_acct_database,0,NULL,0)))
+ 				{
+ 					/* MySQL server not responding...			*/
+ 					log(L_ERR,"SQL_save: MySQL server (%s@%s/%s) not responding: %s",
+ 						sql_acct_user, sql_acct_host, sql_acct_database, mysql_error(&sqlfd));
+ 				}
+ 				else
+ 				{
+ 					mysql_real_query(&sqlfd,query,sizeof(query));
+ 					mysql_close(&sqlfd);
+ 				}
+ 				break;
+ 			#endif /* USE_MYSQL */
+ 
+ 			default:
+ 				log(L_ERR,"SQL: Acct module activated, but no valid SQL server type selected");
+ 				break;
+ 		}
+ 	}
+ }
+ 
+ /*
+  *	Build the accounting record for SQL insertion
+  */
+ void sql_build_record(VALUE_PAIR *pair)
+ {
+ 	SQL_PAIR *tp;
+ 	char buffer[1024];
+ 	char *a = NULL;
+ 
+ 	if(!sql_accounting) return;
+ 	
+ 	/*	Are we interested in this pair?
+ 	 *	If so, print the value in to buffer
+ 	 *	then copy buffer to tp->value
+ 	*/
+ 	for (tp = sql_attrs ; tp ; tp = tp->next)
+ 	{
+ 		if (tp->attribute == pair->attribute)
+ 		{
+ 			/* Found one, add it to the list		*/
+ 			switch(pair->type)
+ 			{
+ 				case PW_TYPE_STRING:
+ 					if (pair->attribute == PW_NAS_PORT_ID)
+ 						a = pair->strvalue;
+ 					else
+ 					{
+ 						sql_safeprint(pair->strvalue, pair->length,
+ 							buffer, sizeof(buffer));
+ 						a = buffer;
+ 					}
+ 					break;
+ 
+ 			        case PW_TYPE_IPADDR:
+ 					ipaddr2str(buffer, pair->lvalue);
+ 					a = buffer;
+ 					break;
+ 
+ 				case PW_TYPE_INTEGER:
+ 					sprintf(buffer, "%ld", pair->lvalue);
+ 					a = buffer;
+ 					break;
+ 
+ 				default:
+ 					a = "UNKNOWN";
+ 					break;
+ 			}
+ 			strncpy(tp->value, a, sizeof(tp->value));
+ 		}
+ 	}
+ }
+ 
+ 
+ /*
+  *	Initialize the SQL stuffs
+  */
+ int sql_rad_init()
+ {
+ 	FILE *sqlfd;
+ 	char buffer[1024];
+ 	char sqlfile[256];
+ 	char cname[256];
+ 	char cval[256];
+ 	char *ptr, *ptr2;
+ 	int  line=0;
+ 	DICT_ATTR *dentry = NULL;
+ 	SQL_PAIR *tp;
+ 
+ 	sql_acct_type = SQL_TYPE_MYSQL;
+ 	strcpy(sql_acct_host,"localhost");
+ 	strcpy(sql_acct_user,"radius");
+ 	strcpy(sql_acct_pass,"");
+ 	strcpy(sql_acct_database,"radius");
+ 	strcpy(sql_acct_table,"radacct");
+ 	strcpy(sql_acct_insert_cmd,"REPLACE");
+ 
+ 	sql_accounting=0;
+ 
+ 	sql_log_start_records=0;
+ 	sql_log_stop_records=0;	
+ 	sql_log_alive_records=0;
+ 	sql_log_unknown_records=0;
+ 	sql_log_blank_username=0;
+ 
+ 
+ 	sprintf(sqlfile, "%s/%s", radius_dir, "sqlconf");
+ 
+ 	if((sqlfd = fopen(sqlfile,"r")) == (FILE *)NULL)
+ 
+ 	{
+ 		log(L_ERR,"could not open file %s for reading",sqlfile);
+ 		return(-1);
+ 	}
+ 
+ 	while(fgets(buffer, sizeof(buffer), sqlfd) != (char *)NULL) {
+ 		line++;
+ 
+ 		if(buffer[0] == 35 || buffer[0] == '\0' || buffer[0] == '\n') {
+ 			continue;
+ 		}
+ 		/* Look for configuration variables */
+ 
+ 		/* get the first field from the config, return pointer to rest */
+ 		ptr = getfirstword(buffer, cname);
+ 
+ 		if (*ptr)
+ 		{
+ 			/* Trim off trailing whitespace */
+ 			ptr2 = &ptr[strlen(ptr) -1];
+ 			while(*ptr2 == ' ' || *ptr2 == '\n' || *ptr2 == '\t')
+ 			{
+ 				ptr2--;
+ 				if (ptr2 == ptr) break;
+ 			}
+ 			ptr2++; *ptr2='\0';
+ 			strcpy(cval,ptr);
+ 		}
+ 		else
+ 		{
+ 			log(L_ERR,"SQL_init: Invalid paramater count on line %d of %s\n",line,buffer);
+ 			continue;
+ 		}
+ 
+ 		/*
+ 		 * We now have the name and value of the config var
+ 		 * There as got to be a better way to do this
+ 		 */
+ 
+ 		/* first the true/false, yes/no type stuff */
+ 		if ((strcasecmp(cname,"SQL-Accounting") == 0) && (strcasecmp(cval,"yes") == 0))
+ 			sql_accounting = 1;
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Log-Stop-Records") == 0) && (strcasecmp(cval,"yes") == 0))
+ 			sql_log_stop_records = 1;
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Log-Start-Records") == 0) && (strcasecmp(cval,"yes") == 0))
+ 			sql_log_start_records = 1;
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Log-Alive-Records") == 0) && (strcasecmp(cval,"yes") == 0))
+ 			sql_log_alive_records = 1;
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Log-Unknown-Records") == 0) && (strcasecmp(cval,"yes") == 0))
+ 			sql_log_unknown_records = 1;
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Log-Blank-Username") == 0) && (strcasecmp(cval,"yes") == 0))
+ 			sql_log_blank_username = 1;
+ 
+ 		/* Now the funner stuff */
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Acct-Type") == 0) && *cval)
+ 		{
+ 			if (strcasecmp(cval,"MySQL") == 0)
+ 				sql_acct_type = SQL_TYPE_MYSQL;
+ 		}
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Acct-Host") == 0) && *cval)
+ 			strcpy(sql_acct_host,cval);
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Acct-User") == 0) && *cval)
+ 			strcpy(sql_acct_user,cval);
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Acct-Passwd") == 0) && *cval)
+ 			strcpy(sql_acct_pass,cval);
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Acct-Database") == 0) && *cval)
+ 			strcpy(sql_acct_database,cval);
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Acct-Table") == 0) && *cval)
+ 			strcpy(sql_acct_table,cval);
+ 		else
+ 		if ((strcasecmp(cname,"SQL-Acct-Insert-Cmd") == 0) && *cval)
+ 			strcpy(sql_acct_insert_cmd,cval);
+ 
+ 
+ 
+ 		/* see if this is a valid Attrib from the dictionary */
+ 		else
+ 		if((dentry = dict_attrfind(cname)) != (DICT_ATTR *)NULL)
+ 		{
+ 			if((tp = (SQL_PAIR *)malloc(sizeof(SQL_PAIR))) == (SQL_PAIR *)NULL)
+ 			{
+ 				log(L_ERR|L_CONS, "SQL_init: out of memory");
+ 				return (-1);
+ 			}
+ 			strcpy(tp->name,dentry->name);			/* Attr Name */
+ 			strcpy(tp->field,cval);				/* Field Name */
+ 			tp->type = dentry->type;			/* Attr Type */
+ 			tp->attribute = dentry->value;			/* Attr Value */
+ 			strcpy(tp->value, "");
+ 			sql_pairadd(&sql_attrs,sql_paircopy(tp));	/* Add Item to List */
+ 			sql_pairfree(tp);
+ 		}
+ 	}
+ 	fclose(sqlfd);
+ 	if(sql_accounting)
+ 		log(L_INFO,"SQL_INIT: Acct Module Using %s@%s:%s.%s",
+ 			sql_acct_user,
+ 			sql_acct_host,
+ 			sql_acct_database,
+ 			sql_acct_table);
+ 	return(0);
+ }
+ 
+ 
+ /*
+  *	Allocate for a new SQL_PAIR
+  */
+ 
+ static SQL_PAIR *sql_paircopy(SQL_PAIR *from)
+ {
+         SQL_PAIR *vp = NULL;
+         SQL_PAIR *last = NULL;
+         SQL_PAIR *i, *t;
+ 
+         for(i = from; i; i = i->next) {
+                 if ((t = malloc(sizeof(SQL_PAIR))) == NULL)
+                         continue;
+                 memcpy(t, i, sizeof(SQL_PAIR));
+                 t->next = NULL;
+                 if (last)
+                         last->next = t;
+                 else
+                         vp = t;
+                 last = t;
+         }
+ 
+         return vp;
+ }
+ 
+ 
+ /*
+  *	Add a pair to the end of a SQL_PAIR chain
+  */
+ void sql_pairadd(SQL_PAIR **first, SQL_PAIR *new)
+ {
+ 	SQL_PAIR *i;
+ 
+ 	new->next = NULL;
+ 	if (*first == NULL) {
+ 		*first = new;
+ 		return;
+ 	}
+ 	for(i = *first; i->next; i = i->next)
+ 		;
+ 	i->next = new;
+ }
+ 
+ 
+ /*
+  *	Free up memory used by an SQL_PAIR list
+  */
+ void sql_pairfree(SQL_PAIR *pair)
+ {
+ 	SQL_PAIR      *next;
+ 
+ 	while(pair != NULL) {
+ 		next = pair->next;
+ 		free(pair);
+ 		pair = next;
+ 	}
+ }
+ 
+ 
+ /*
+  *	Convert a string to something printable.
+  *	The output string has to be _at least_ 4x the size
+  *	of the input string!
+  *
+  *	Borrowed from radiusd-cistron 1.6alpha2
+  *	will return when I re-write this as a module for 1.6
+  */
+ static void sql_safeprint(char *in, int inlen, char *out, int outlen)
+ {
+ 	unsigned char	*str = (unsigned char *)in;
+ 	int		done = 0;
+ 	int		sp = 0;
+ 
+ 	if (inlen < 0) inlen = strlen(str);
+ 
+ 	while (inlen-- > 0 && (done + 3) < outlen) {
+ 		/*
+ 		 *	Hack: never print trailing zero.
+ 		 *	Some clients send strings with an off-by-one
+ 		 *	length (confused with strings in C).
+ 		 */
+ 		if (inlen == 0 && *str == 0)
+ 			break;
+ 
+ 		sp = 0;
+ 
+ 		switch (*str) {
+ 			case '\\':
+ 				sp = '\\';
+ 				break;
+ 			case '\r':
+ 				sp = 'r';
+ 				break;
+ 			case '\n':
+ 				sp = 'n';
+ 				break;
+ 			case '\t':
+ 				sp = 't';
+ 				break;
+ 			default:
+ 				if (*str < 32 || (*str >= 128 && *str <= 160)){
+ 					sprintf(out, "\\%03o", *str);
+ 					done += 4;
+ 					out  += 4;
+ 				} else {
+ 					*out++ = *str;
+ 					done++;
+ 				}
+ 		}
+ 		if (sp) {
+ 			*out++ = '\\';
+ 			*out++ = sp;
+ 			done += 2;
+ 		}
+ 		str++;
+ 	}
+ 	*out = 0;
+ }
+ 
+ /*
+  *	Copy the first word from the buffer, and return 
+  *	a pointer to the first non-space charachter after that.
+  *
+  *	We allow "" and \ to include spaces in the word
+  *	(hold over from when this was getusername)
+  */
+ static char *getfirstword(char *buffer, char *entry)
+ {
+ 	char *ptr;
+ 	char *to;
+ 	int spc_seen = 0;
+ 
+ 	ptr = buffer;
+ 	to = entry;
+ 
+ 	while (*ptr && !spc_seen) {
+ 		switch (*ptr) {
+ 			case '"':
+ 				ptr++;
+ 				while (*ptr && *ptr != '"')
+ 					*to++ = *ptr++;
+ 				if (*ptr) ptr++;
+ 				break;
+ 			case '\\':
+ 				ptr++;
+ 				*to++ = *ptr;
+ 				if (*ptr) ptr++;
+ 				break;
+ 			case ' ':
+ 			case '\t':
+ 			case '\n':
+ 				spc_seen = 1;
+ 				break;
+ 			default:
+ 				*to++ = *ptr++;
+ 				break;
+ 		}
+ 	}
+ 	*to = 0;
+ 
+ 	while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
+ 		ptr++;
+ 
+ 	return ptr;
+ }
+ 
+ 
+ #endif /* USE_SQL */
*** src/Make.inc.orig	Mon Nov  8 15:25:46 1999
--- src/Make.inc	Mon Nov  8 13:15:26 1999
***************
*** 4,18 ****
  #
  #
  
! SERVER_OBJS    = radiusd.o dict.o files.o util.o md5.o attrprint.o \
  			acct.o radius.o pam.o log.o version.o proxy.o \
  			exec.o auth.o timestr.o cache.o
  SERVERDBM_OBJS = radiusddbm.o dict.o filesdbm.o util.o md5.o attrprint.o \
  			acct.o radius.o pam.o log.o versiondbm.o proxy.o \
! 			exec.o auth.o timestr.o cache.o
  SERVER_SRCS    = radiusd.c dict.c files.c util.c md5.c attrprint.c acct.c \
  			radius.c pam.c log.c version.c proxy.c \
! 			exec.c auth.c timestr.c cache.c
  INCLUDES       = radius.h conf.h
  
  all:	radiusd radwho radzap raduse radtest
--- 4,18 ----
  #
  #
  
! SERVER_OBJS    = radiusd.o dict.o files.o util.o sql.o md5.o attrprint.o \
  			acct.o radius.o pam.o log.o version.o proxy.o \
  			exec.o auth.o timestr.o cache.o
  SERVERDBM_OBJS = radiusddbm.o dict.o filesdbm.o util.o md5.o attrprint.o \
  			acct.o radius.o pam.o log.o versiondbm.o proxy.o \
! 			exec.o auth.o timestr.o cache.o sql.o
  SERVER_SRCS    = radiusd.c dict.c files.c util.c md5.c attrprint.c acct.c \
  			radius.c pam.c log.c version.c proxy.c \
! 			exec.c auth.c timestr.c cache.c sql.c
  INCLUDES       = radius.h conf.h
  
  all:	radiusd radwho radzap raduse radtest
***************
*** 20,39 ****
  dbm:	radiusd.dbm builddbm
  
  radiusd: $(SERVER_OBJS)
! 	$(CC) $(LDFLAGS) -o radiusd $(SERVER_OBJS) $(LIBS) $(LCRYPT) $(PAMLIB)
  
  radiusd.dbm: $(SERVERDBM_OBJS)
  	$(CC) $(LDFLAGS) -o radiusd.dbm $(SERVERDBM_OBJS) $(LIBS) $(LCRYPT) \
! 			 $(DBMLIB) $(PAMLIB)
  
  radiusd.o: radiusd.c $(INCLUDES)
! 	$(CC) $(CFLAGS) -c radiusd.c
  
  radiusddbm.o: radiusd.c $(INCLUDES)
! 	$(CC) $(CFLAGS) $(DBM) -c radiusd.c -o radiusddbm.o
  
  acct.o: acct.c $(INCLUDES)
! 	$(CC) $(CFLAGS) -c acct.c
  
  attrprint.o: attrprint.c $(INCLUDES)
  	$(CC) $(CFLAGS) -c attrprint.c
--- 20,40 ----
  dbm:	radiusd.dbm builddbm
  
  radiusd: $(SERVER_OBJS)
! 	$(CC) $(LDFLAGS) -o radiusd $(SERVER_OBJS) $(LIBS) $(LCRYPT) $(PAMLIB) \
! 		$(SQLLIB) $(SQLLIBDIR)
  
  radiusd.dbm: $(SERVERDBM_OBJS)
  	$(CC) $(LDFLAGS) -o radiusd.dbm $(SERVERDBM_OBJS) $(LIBS) $(LCRYPT) \
! 			 $(DBMLIB) $(PAMLIB) $(SQLLIB) $(SQLLIBDIR)
  
  radiusd.o: radiusd.c $(INCLUDES)
! 	$(CC) $(CFLAGS) $(SQL) -c radiusd.c
  
  radiusddbm.o: radiusd.c $(INCLUDES)
! 	$(CC) $(CFLAGS) $(DBM) $(SQL) -c radiusd.c -o radiusddbm.o
  
  acct.o: acct.c $(INCLUDES)
! 	$(CC) $(CFLAGS) $(SQL) -c acct.c
  
  attrprint.o: attrprint.c $(INCLUDES)
  	$(CC) $(CFLAGS) -c attrprint.c
***************
*** 58,63 ****
--- 59,67 ----
  
  cache.o:  cache.c $(INCLUDES)
  	$(CC) $(CFLAGS) -c cache.c 
+ 
+ sql.o:  sql.c $(INCLUDES)
+ 	$(CC) $(CFLAGS) $(SQL) -c sql.c
  
  proxy.o:  proxy.c $(INCLUDES)
  	$(CC) $(CFLAGS) -c proxy.c
*** src/Makefile.BSD.orig	Sat Sep 18 18:10:41 1999
--- src/Makefile.BSD	Mon Nov  8 12:05:49 1999
***************
*** 4,10 ****
  #
  
  CC	= gcc
! CFLAGS	= -Wall -g -DNOSHADOW
  LDFLAGS	= # -s #tatic
  LIBS	=
  LCRYPT	= -lcrypt
--- 4,10 ----
  #
  
  CC	= gcc
! CFLAGS	= -Wall -g -DNOSHADOW -I/usr/local/include
  LDFLAGS	= # -s #tatic
  LIBS	=
  LCRYPT	= -lcrypt
***************
*** 17,21 ****
--- 17,25 ----
  
  BINDIR  = /usr/local/bin
  SBINDIR = /usr/local/sbin
+ 
+ SQL             = -DUSE_SQL -DUSE_MYSQL
+ SQLLIB          = -lmysqlclient
+ SQLLIBDIR       = -L/usr/local/lib/mysql
  
  .include "Make.inc"
*** src/Makefile.lnx.orig	Mon Nov  8 16:04:38 1999
--- src/Makefile.lnx	Mon Nov  8 16:07:59 1999
***************
*** 28,31 ****
--- 28,35 ----
  BINDIR  = /usr/local/bin
  SBINDIR = /usr/local/sbin
  
+ SQL             = -DUSE_SQL -DUSE_MYSQL
+ SQLLIB          = -lmysqlclient
+ SQLLIBDIR       = -L/usr/lib/mysql
+ 
  include Make.inc
