Calctime.C

  /* Program CalcTime
	This program reads a Radius authentication file and writes to STDOUT
	a list of users and their cumulative login times.


	Additionally, it will perform the following options:
	-i <path/file> input file.  Mandatory!
	-o <path/file> output file.  If absent, output to STDOUT
	-f Create user files and append to each user file each login
		 and logout.
	-m <text> Extract records from source stream only if the text (usually month)
		is found in the master records.
	-q <text> Force End-of-File when it sees this pattern in a master record.


	Created by Michael G. Gordon, February 1997.
*/


#undef __cplusplus




#define STDOUT 1
#define STDIN 0
#define STDERR 2


#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>




#define MAXPORTS 160
#define MAXUSERS 1500
#define LINESIZE 256		/* size of text lines for this program */
#define MAXUSERIDLENGTH 16
#define TRUE  1
#define FALSE 0
#define BUFFSIZE   (16U*2048U)  /* input or output buffer size */




#define long int	// convert 16 bit dos specs to Linux




typedef enum {NOFLUSH, FLUSHAFTER, FLUSH};
typedef enum {NORECORDTYPE, START, STOP} Recordtypes;
typedef char userstring[MAXUSERIDLENGTH];


// DOS end-of-line
// char crlf[] = {13,10,0};
// UNIX end-of-line
char crlf[] = {10,0};




unsigned long portsused[MAXPORTS]; /* Here accumulate port logins */




// The users cumulative data array and pointers.
struct {
	char user[MAXUSERIDLENGTH];
	int  logins;				// 0=not logged in.  1=logged in, 2= logged in on two ports, etc.
	long totalseconds;
	time_t logontime;			// Initial logon; hold until logout with no concurrent logins.
	time_t startconcurrent;	// time of concurrent login.
	long totalconcurrent;	// cumulative concurrency
	} oneuser, allusers[MAXUSERS];
int	numusers;	/* variables for finding users in the array */


userstring ports[MAXPORTS];	// array to track who is on what port
int curport, numports;




char statusbyte;
char eoffound;
char db_flag;				// if set, means to export for DB import (tab delimited)
char db_text[63];   		// file path to store it.
int db_handle;				// file handle for the database export file.
char f_flag;				// means to make individual user data files
char o_flag;				// means the output filename is specified
char h_flag;  				// means make histogram data
char s_flag;				// means include START records.  
char m_flag;  				// means to extract only for a defined [string], usually month.
char m_text[33];
char d_flag;  			// Means to delete records where datestring contains [d_text]
char d_text[33];
char q_flag;
char q_text[33];
char *months[14]={"???","Jan","Feb","Mar","Apr","May","Jun","Jul",
					  "Aug","Sep","Oct","Nov","Dec","???"};


// Histogram stuff
char histogramfilename[80];
int histogramhandle;
long sessiontimebins[40]; // Counts of sessions having n duration, where n varies from .1 hour to 7 days exponentially
long sessionhourbins[24]; // average session length ENDING this hour.


// analysis file variables
char ipaddress[16];  // from
char ipfirst[16];    // from Ascend-First-Dest = 207.201.65.10
int  stopcode;       // disconnection reason
long baudrate;			//connection speed
int  portid;			// Port number for login, logout (validation check)
int  presessiontime; 
long bytes2user;
long bytesfmuser;
char datestring[LINESIZE];	// The text of the date/time
char timestring[LINESIZE]; // The text of the time (after fixup: hh:mm:ss)










int  inhandle;            /* which one are we using      */
char filepath[128];
char outfilepath[128];
unsigned int bytesin;     /* bytes in inbuffer           */
unsigned int bytesout;    /* bytes in out buffer         */
unsigned buffersize;      /* for search buffer and index */
unsigned long totalbytesread;










char onetab[] = {9,0};
char * inbuffer;
char * outbuffer;
char * outpointer;
char * lid;
char * nextbyte;
char morein;	// flag that indicates NOT end of file
unsigned long bytecounter;


union {
  char * c;
  unsigned int *w;
  long * l;
  } parser;


typedef struct {
	int	codeindex;
	char  *codetext;
	} codelisttype;


codelisttype codelist[] = {
 {0,"No Reason"},
 {1,"Not Applicable"},
 {2,"Unknown"},
 {3,"Call Disconnected"},
 {4,"CLID Authentication Failed"},
 {5,"CLID Radius Timeout"},
 {10,"Modem never detected DCD"},
 {11,"DCD detected but went inactive"},
 {12,"Modem result codes parse fail"},
 {20,"TermSrv - user quit"},
 {21,"TermSrv - idle timeout"},
 {22,"TermSrv - exit Telnet"},
 {23,"TermSrv - no IP Addr"},
 {24,"TermSrv - exit Raw TCP"},
 {25,"TermSrv - login failed"},
 {26,"TermSrv - Raw TCP disabled"},
 {27,"TermSrv - CTRL-C during login"},
 {28,"TermSrv - Destroyed"},
 {29,"TermSrv - user closed VirtConn"},
 {30,"TermSrv - VirtConn destroyed"},
 {31,"TermSrv - exit Rlogin"},
 {32,"TermSrv - bad Rlogin option"},
 {33,"TermSrv - not enough resources"},
 {35,"MPP - no NULL msg timeout"},
 {40,"PPP - LCP Timeout"},
 {41,"PPP - LCP Negotiation failed"},
 {42,"PPP - PAP Auth failed"},
 {43,"PPP - CHAP Auth failed"},
 {44,"PPP - Remote Auth failed"},
 {45,"PPP - Receive Terminate Req"},
 {46,"PPP - Receive Close Event"},
 {47,"PPP - No NCP's were open"},
 {48,"PPP - MP bundle unknown"},
 {49,"PPP - LCP close MP add fail"},
 {50,"Session Table Full"},
 {51,"Out of resources"},
 {52,"Invalid IP Address"},
 {53,"Hostname resolution failed"},
 {54,"Bad/missing port number"},
 {60,"Host Reset"},
 {61,"Connection refused"},
 {62,"Connection timeout"},
 {63,"Connection Closed"},
 {64,"Network unreachable"},
 {65,"Host unreachable"},
 {66,"Network admin unreachable"},
 {67,"Host admin unreachable"},
 {68,"Port unreachable"},
 {100,"Session Timeout"},
 {101,"Invalid incoming user"},
 {102,"Disconnect due to callback"},
 {120,"Protocol disabled/unsupported"},
 {150,"Disconnect requested by RADIUS"},
 {151,"Disconnect by Local Admin"},
 {160,"V110 timeout/sync retry exceed"},
 {170,"PPP Auth Timeout exceeded"},
 {180,"User executed Do..Hangup"},
 {185,"Remote End Hung Up"},
 {190,"Resource has been Quiesced"},
 {195,"MAX Call duration reached"}
 };


int numcodes = sizeof(codelist) / sizeof(codelist[0]);




char * strupr(char * instring)
{
int i,alength;
	alength = strlen(instring);
	if (alength > 120) 
		return instring;   // something went wrong.
	for (i=0;i<alength;i++)
		instring[i] = toupper(instring[i]);
	return instring;
}






void comment(char * astring)
{
	// write a comment to STDERR


	write(STDERR,astring,strlen(astring));
	write(STDERR,crlf,strlen(crlf));
}








char * reason(int mystopcode)
{
int i;
//	printf("%d Number of codes.  Requested code: %d\n",numcodes,stopcode);
//	sleep(1);
	for (i=0;i<numcodes;i++)
	{
		if (codelist[i].codeindex == mystopcode)
		{
			return(codelist[i].codetext);
		}
	}
	return(codelist[0].codetext);
}






void fileproblem(char * description, char * afilename)
{
char textout[128];
	sprintf(textout,"%s [%s] error number %d = %s \n", description, afilename, errno, sys_errlist[errno]) ;
	write(2,textout,strlen(textout)); // write to STD ERR
}




char * makefilename(char * username)
{
static char afilename[MAXUSERIDLENGTH+16];
int i;
char ch;
	memset(afilename,0,sizeof(afilename));
	strcpy(afilename,"users/");


	for (i=0;i<MAXUSERIDLENGTH && i<strlen(username);i++)
	{
		ch=username[i];
		if (isalnum(ch))
			afilename[i+6]=ch;
		else
			afilename[i+6]='_';
	}
	return afilename;
}










char getabyte(void)
{
char ch;
	if (inhandle==0) 	// yes, we need to open the file.
	{
		inhandle=open(filepath,O_RDONLY,S_IREAD);
		if (inhandle <0)
		{
			fileproblem("Problem opening input file:",filepath);
			exit(1);
		}
	}
	if ((bytesin==0) && (!eoffound))
	{
		bytesin=(unsigned int) read(inhandle,inbuffer,buffersize);


		// Check for error and exit if so.
		if ((bytesin & 0xffff) == 0xffff) // error
		{
			fileproblem("Problem reading input file:",filepath);
			close(inhandle);
			exit(1);
		}
		lid = inbuffer + bytesin;	// calculate a lid, or stopper, or fence, address.
		nextbyte = inbuffer;			// reset the read pointer
		totalbytesread += bytesin;
		printf("requested: %u  loaded: %u  Total: %lu \n",buffersize,bytesin,totalbytesread);


		// Check for end of file.  This is detected by retrieving fewer bytes than asked for.
		// In the case of the previous read obtaining exactly the number of bytes that exist,
		// the next read will obtain zero bytes, and the one after that, an error.
		if (bytesin == buffersize)
			morein=TRUE;
		else
		{
			morein=FALSE;
		}
	}


	ch = *nextbyte;
	nextbyte++;
	bytesin --;


	if ((bytesin==0) && (morein==0)) eoffound=TRUE;	// signal end of file.


	// At exit, the data byte returned is GOOD if eoffound is false, or if
	// it just turned true (ie, don't ENTER this function with eoffound = true)


	// keep count globally of bytes issued.  If you want the byte address
	// of the byte that is about to be issued, capture and keep a copy of
	// bytecounter BEFORE calling this function.
	bytecounter++;
	return(ch);
}


char newgetabyte(void)
{
char ch;
	if (inhandle==0) 	// yes, we need to open the file.
	{
		inhandle=open(filepath,O_RDONLY,S_IREAD);
		if (inhandle <0)
		{
			fileproblem("Problem opening input file:",filepath);
			exit(1);
		}
	}
	if ((bytesin==0) && (!eoffound))
	{
		memset(inbuffer,0,buffersize);
		bytesin=(unsigned int) read(inhandle,inbuffer,buffersize);
		printf("requested: %ud  loaded: %ud   \n",buffersize,bytesin);
		// Check for error and exit if so.
		if ((bytesin & 0xffff) == 0xffff) // error
		{
			bytesin=0;
			eoffound=TRUE;


		}
		lid = inbuffer + bytesin;	// calculate a lid, or stopper, or fence, address.
		nextbyte = inbuffer;			// reset the read pointer


/*		Curiously, the read requests are not being fulfilled completely; so the
		anticipation of more data is not working!  We'll just have to read data
		until it complains.
*/
	}


	ch = *nextbyte;
	nextbyte++;
	bytesin --;


//	if ((bytesin==0) && (morein==0)) eoffound=TRUE;	// signal end of file.


	// At exit, the data byte returned is GOOD if eoffound is false, or if
	// it just turned true (ie, don't ENTER this function with eoffound = true)


	// keep count globally of bytes issued.  If you want the byte address
	// of the byte that is about to be issued, capture and keep a copy of
	// bytecounter BEFORE calling this function.
	bytecounter++;
	return(ch);
}


char * getaline(void)
{
int onepointer;
static char oneline[LINESIZE];
char ch;
char endline;
	memset(oneline,0,sizeof(oneline));
	onepointer=0;
	endline=FALSE;
	do
	{
		if (!eoffound)
		{
			ch = getabyte();
			if ((ch==10) || (ch==13))
			{
				if (onepointer > 0) endline = TRUE;
			}
			else
			{
				oneline[onepointer++] = ch;
				if (onepointer >= (LINESIZE-1)) onepointer = LINESIZE-2;
			}
		}
// getaline exits when it finds an END LINE character and is
// not a null-line, or, if end-of-file is reached.
	} while ((!eoffound) && (!endline));
	return (oneline);
}






int writetouser(int userhandle,char * filename, char * astring)
{
// Write string to open file identified by userhandle
int	alength;
int	result;
	alength=strlen(astring);
	result=write(userhandle,astring,alength);
	if ((result != alength) || (result == -1))
		fileproblem("Problem with writetouser on file ",filename);
}




int openuserfile(char * afilename)
{
//  Common user file opener.  If file does not exist, create it and fill it with
//  some header information.  Several functions may choose to record something to
//  The user files and they may, or may not, exist.


int userhandle;
char isproblem;
char outline[128];
char textout[128];


	isproblem=FALSE;
	userhandle=0;
	


	// Sloppy, but for now, here is where we put the customer blocks:
	if (strcmp("users/broyer",afilename)==0) return (0);  // requested by email to not receive this message.
	if (strcmp("users/jadams",afilename)==0) return (0);  // requested by email to not receive this message.
	if (strcmp("users/spiricon",afilename)==0) return (0);  // requested by email to not receive this message.


	memset(outline,0,sizeof(outline));
	// First, test for existence of file.
	userhandle=open(afilename,O_RDONLY,0660);
	if (userhandle >0)
	{
		// It exists.  REopen with Append
		close(userhandle);
		userhandle=0;
		userhandle=open(afilename,O_APPEND|O_WRONLY,0666);
	//	sprintf(textout,"%s %d Opened for Append",afilename,userhandle);
	//	comment(textout);
		if (userhandle < 0)
		{
			fileproblem("Cannot append to existing file",afilename);
			return (0);
		}
	}
	else
	{
			userhandle=0;
			isproblem=TRUE;
			fileproblem("Problem opening a user file:",afilename);
			comment(">>>Trying to create it...");


			userhandle=creat(afilename,0666);
			if (userhandle <0) // probably doesn't exist
			{
				fileproblem("Problem creating a user file:",afilename);
				return (0);
			}
			else
			{
				writetouser(userhandle,afilename,"Dear Digital Planet Customer,\r\n");
				writetouser(userhandle,afilename,"   Ths is your monthly utilization report.  It is not a bill.\r\n");
				writetouser(userhandle,afilename,"It simply shows when you logged onto the internet, and got off.\r\n");
				writetouser(userhandle,afilename,"    Procedures for password changing, a more detailed explanation\r\n");
				writetouser(userhandle,afilename,"of this report, and other help may be found on our web server:\r\n\r\n");
				writetouser(userhandle,afilename,"http://www.digitalpla.net/support\r\n\r\n");


			}
		}
		if (isproblem)
		{
			comment("Fixed!");
			
		}
	return (userhandle);
}


void recordastomp(char * username, int portid)
{
int userhandle;
int byteswritten;
long seekresult;
char afilename[MAXUSERIDLENGTH+16];
char outline[128];


	if (f_flag ==0) return;	// don't record a stomp if user files are turned off.


	strcpy(afilename,makefilename(username));


	userhandle=openuserfile(afilename);


	if (f_flag && (userhandle >2))
	{
		sprintf(outline,"%s stomped; port %d in use by another user at %s\r\n",username,portid,datestring);
		seekresult=lseek(userhandle,0,SEEK_END);
		if (seekresult<0)
			fileproblem("(recordastomp)Problem seeking end-of-file on a user file:",afilename);
		else
		{
			byteswritten=write(userhandle,outline,strlen(outline));
			if (byteswritten != strlen(outline))
				fileproblem("(recordastomp)Problem writing to a user file:",afilename);
		}
		close(userhandle);
		userhandle=0;
	} // end of "if f_flag" to write individual user data files.
}


int getuser(char * username)
{
char founduser;
int testuser;
// searches for username in the user data array.  Returns with index number.
// If user name not found, then it is added, and then returns with index number.
	founduser = FALSE;
	for (testuser=0;testuser<numusers;testuser++)
	{
		if (strcmp(allusers[testuser].user,username)==0)
		{
			founduser = TRUE;
			break;
		}
	}
	if (!founduser)
	{
		// name is not in the array.  Just add it here.
		strncpy(allusers[testuser].user,username,MAXUSERIDLENGTH-1);
		if (numusers < MAXUSERS) numusers++;
	}
	return(testuser);
}




void storedb(char * username, unsigned long seconds, Recordtypes recordtype)
{
// warning, uses some globals.
// Prepares data for subsequent import into a database (tab delimited)


char outline[1000];
int byteswritten;
	if ((recordtype == START) && s_flag)
	{
			sprintf(outline,"START\t%s\t%d\t%s %s\t%8.3f\t%s\t%s\t%s\t%ld\t%ld\t%ld\t%d\n",
				username,
				portid,
				datestring,
				timestring,
				((double)0.0),
				ipaddress,
				ipfirst,
				reason(stopcode),
				baudrate,
				bytes2user,
				bytesfmuser,
				presessiontime


			);
		byteswritten=write(db_handle,outline,strlen(outline));
	}


		if (recordtype == STOP)
		{
			sprintf(outline,"STOP\t%s\t%d\t%s %s\t%8.3f\t%s\t%s\t%s\t%ld\t%ld\t%ld\t%d\n",
				username,
				portid,
				datestring,
				timestring,
				((double)seconds/3600.0),
				ipaddress,
				ipfirst,
				reason(stopcode),
				baudrate,
				bytes2user,
				bytesfmuser,
				presessiontime
				);
//			printf("%s\n",outline);
			byteswritten=write(db_handle,outline,strlen(outline));
			if (byteswritten < 0)
			{
				fileproblem("Problem writing to database file",db_text);
				exit (1);
			}


//			printf("%d \n",byteswritten);
//			sleep(1);
		}
}










void storedata(char * username, char * filename,
	unsigned long seconds, time_t recordtime, Recordtypes recordtype)
{
	// Input:  ONE record to be stored.
	// Output: Stores the record on a disk file
	// Assumption: openfile will open user file, create and
	// initilize if needed, and leave in append mode.


char outline[LINESIZE];
char temps[60];
int curuser,altuser;	// Pointers within alluser array.
long concurrenttime;	// Session concurrency computed from first concurrent login time.
long seekresult;
time_t checktime;
time_t timecharged;
int byteswritten;
int userhandle;


	memset (outline,0,sizeof(outline));
	checktime=timecharged=0;
	concurrenttime =0;


	curuser=getuser(username);		// Get or add user to array and remember where it is.


	// open file here for any type that needs it.
	if ((f_flag) && ((recordtype==START) || (recordtype == STOP)) && (strlen(username)>0))
	{
		userhandle=openuserfile(filename);
		if (userhandle <=2)
			fileproblem("Open customer file failed",filename);
		
	}


	if (userhandle <=0) userhandle=0;


	if (recordtype == STOP)
	{
				concurrenttime = 0;
				ports[portid][0]=0;


				// Serious problems occasionally with the Radius timestamps.  Use
				// The lesser of Radius timestamp or my own calculation.
				checktime=recordtime - allusers[curuser].logontime;
				if (((checktime > 0) && (checktime < seconds)) || (seconds == 0))
					timecharged = checktime;
				else
					timecharged += seconds;  // use lesser.


				allusers[curuser].totalseconds += timecharged;  // use lesser.


				// the above uses the Radius time-stamp time.




				// Log the user out.
				if (allusers[curuser].logins >0) allusers[curuser].logins--;


				// Now let us compute the concurrency time, if any.
				// Any remaining logins?
				if (allusers[curuser].logins>0)
				{
					// We have concurrency!
					if ((allusers[curuser].startconcurrent) && (recordtime > 0))
							concurrenttime = recordtime - allusers[curuser].startconcurrent;
					// This is session concurrency.  Add to accumulator and reset
					// startconcurrent to present record time if logins is SILL >1
					// or to zero if logins is 1 (end concurrent period).
					allusers[curuser].totalconcurrent += concurrenttime;
					if (allusers[curuser].logins >1)
						allusers[curuser].startconcurrent = recordtime;
					else
						allusers[curuser].startconcurrent = 0; // stop concurrency
				}


				if (f_flag && (userhandle >2))
				{


							sprintf(outline,"  %s port %-3d STOP at %s, used %8.3f hours. Code: %s ",
								username,portid,datestring,((double)timecharged/3600.0),reason(stopcode));


							if (concurrenttime)
							{
								strcat(outline,"Concurrent:  ");
								sprintf(temps,"%8.3f",((double)concurrenttime/3600.0));
								strcat(outline,temps);
							}
							strncat(outline,crlf,strlen(crlf));


							//seekresult=lseek(userhandle,0,SEEK_END);
							//if (seekresult<0)
							//{
							//	printf("User handle: %d ",userhandle);
							//	fileproblem("Problem seeking end-of-file on a user file:",filename);
							//}
							//else
							//{
								byteswritten=write(userhandle,outline,strlen(outline));
								if (byteswritten != strlen(outline))
									fileproblem("Problem writing to a user file:",filename);
							//}
				} // end of "if f_flag" to write individual user data files.
	}




	// If the type of entry is "Start" we now have enough to write
	// the individual user entry file, if enabled.
	if (recordtype == START)
	{
				// Am I already on this port?  (that is, did I get cut off and am
				// I logging right back in?)  IF so, it does not count toward concurrency.
				if (ports[portid][0]!=0)
				{
					// OK, someone is already on this port.  Who?  Register a "stomped"
					// message and count login if not present user; but if present user,
					// and still marked logged in, then leave it just marked logged in.
					if (strcmp(ports[portid],username)!=0)
					{
						// User was stomped.
						printf("User %s stomped by user %s on port %d on %s\n",ports[portid],username,portid,datestring);
						allusers[curuser].logins++;	// add a login for THIS user
						recordastomp(ports[portid],portid);
						// We'd better fix up the info for the user that got stomped.
						altuser=getuser(ports[portid]);		// get array index for the stomped user
						// Next, reduce the stomped user's login count.
						if (allusers[altuser].logins > 0) allusers[altuser].logins--;
						// Next, if the result of the reduction makes it zero, clean out
						// Any residual memory of concurrency.
						if (allusers[altuser].logins == 0)
						{
							allusers[altuser].logontime = 0;
							allusers[altuser].startconcurrent = 0;
						}


					}
					else
					{
						// must be same user stomping himself.  Obviously cannot be concurrent.
						if (allusers[curuser].logins==0)
						{
							allusers[curuser].logins++;	// add a login if not marked logged in
						}
					}
				}
				else
				{
					// Free port, add a login to this user
					allusers[curuser].logins++;	// change state to logged in.
				}


				// In all cases, store the current user of this port.
				memset(ports[portid],0,sizeof(ports[0]));
				strncpy(ports[portid],username,sizeof(ports[0])-1);


				if (allusers[curuser].logins >1)	// do we have concurrency?
				{
					// Yes, we have concurrent login, remember the time it started.
					if (recordtime)
						allusers[curuser].startconcurrent = recordtime;
				}
				else
				{
					// First (nonconcurrent) logon.
					if (recordtime)
						allusers[curuser].logontime = recordtime;
				}


				if (f_flag && (userhandle >2))
				{
							// username start/stop port time date-time-text [Concurrency data]
							sprintf(outline,"%s port %-3d START  at %s.  ",
								username,portid,datestring);
//							strcpy(outline,username);
//							strcat(outline,onetab);
//							strcat(outline,"Start");
//							strcat(outline,onetab);
//							itoa(portid,temps,10);
//							strcat(outline,temps);
//							strcat(outline,onetab);
//							strcat(outline,datestring);


							if (allusers[curuser].logins > 1)
							{
								strcat(outline,onetab);
								strcat(outline,"Concurrent (Multi-port) Login detected");
							}
							strncat(outline,crlf,strlen(crlf));
							//seekresult=lseek(userhandle,0,SEEK_END);
							//if (seekresult<0)
							//	fileproblem("Problem seeking end-of-file on a user file:",filename);
							//else
							//{
								byteswritten=write(userhandle,outline,strlen(outline));
								if (byteswritten != strlen(outline))
									fileproblem("Problem writing to a user file:",filename);
							//}
				} // end of if-f_flag
	
	} // end of if-recordtype=START
	if (userhandle>0) 
	{
		close(userhandle);
		userhandle=0;
	}
}


void storetotals(void)
{
char outline[LINESIZE];
char filename[MAXUSERIDLENGTH+16];
int curuser;	// Pointers within alluser array.
long seekresult;
int byteswritten;
int userhandle;


	for (curuser=0;curuser<numusers;curuser++)
	{
		memset (outline,0,sizeof(outline));
		memset (filename,0,sizeof(filename));
		// open file here for any type that needs it.
		strncpy(filename,makefilename(allusers[curuser].user),sizeof(filename)-1);
		userhandle=openuserfile(filename);
		if (userhandle <=2) userhandle=0;


		if (userhandle)
		{
//			 seekresult=lseek(userhandle,0,SEEK_END);
//			 if (seekresult<0)
//					fileproblem("Problem seeking end-of-file on a user file:",filename);
//			 else
//			 {
				sprintf(outline,"\r\nTotal Hours Used by %s: %4.3lf and concurrent usage of %4.3lf\r\n",
					allusers[curuser].user,
					(double)((double)allusers[curuser].totalseconds/3600.0),
					(double)((double)allusers[curuser].totalconcurrent/3600.0));
					byteswritten=write(userhandle,outline,strlen(outline));
					if (byteswritten != strlen(outline))
						fileproblem("Problem writing to a user file:",filename);
//			 }
			 close(userhandle);
			userhandle=0;
		}
	}
}






void parsemessages(void)
{
/* description of the expected input (Radius Authorization Log) file




Wed Jul 23 18:17:27 1997
		  User-Name = "eyestorm"
		  NAS-Identifier = 208.23.23.150
		  NAS-Port = 20208
		  Acct-Status-Type = Stop
		  Acct-Delay-Time = 0
		  Acct-Session-Id = "238007122"
		  Acct-Authentic = RADIUS
		  Acct-Session-Time = 1757
		  Acct-Input-Octets = 30896
		  Acct-Output-Octets = 715465
		  Acct-Input-Packets = 1353
		  Acct-Output-Packets = 3658
		  Ascend-Disconnect-Cause = 11
		  Ascend-Connect-Progress = 65
		  Ascend-Data-Rate = 24000
		  Ascend-PreSession-Time = 25
		  Ascend-Pre-Input-Octets = 276
		  Ascend-Pre-Output-Octets = 289
		  Ascend-Pre-Input-Packets = 11
		  Ascend-Pre-Output-Packets = 10
		  Ascend-First-Dest = 207.201.65.10
		  Framed-Protocol = PPP
		  Framed-Address = 208.23.23.175
Wed Jul 23 18:18:26 1997
		  User-Name = "deanb"
		  NAS-Identifier = 208.23.23.150
		  NAS-Port = 20202
		  Acct-Status-Type = Start
		  Acct-Delay-Time = 0
		  Acct-Session-Id = "238007148"
		  Acct-Authentic = RADIUS
		  Framed-Protocol = PPP
		  Framed-Address = 208.23.23.153






-------------------------------------------------------------------- */
int i;
char ch;
char * beginuserid;
char * enduserid;
char * x;				// generic char pointer
int namesize;
char oneline[LINESIZE];


// Current text record accumulators
char username[LINESIZE];	// The username
char filename[MAXUSERIDLENGTH+6];	// A fixed-up username with .USR suffixed
char temps[40];
unsigned long seconds;					// time used converted to binary
time_t recordtime;			// the binary time of this text record.
Recordtypes recordtype;		// Tokenized record type
int result;






// Time stuff
struct tm time_check;


char atoken[4];


	memset (oneline,0,sizeof(oneline));
	memset (atoken,0,sizeof(atoken));
	memset (username,0,sizeof(username));
	memset (datestring,0,sizeof(datestring));
	portid=0;
	seconds=0;
	recordtime=0;
	stopcode=0;
	memset(ipaddress,0,sizeof(ipaddress));
	memset(ipfirst,0,sizeof(ipfirst));
	recordtype=NORECORDTYPE;


	do
	{
		if (eoffound) break;


		strcpy(oneline,getaline());
		// What kind of line is it?
		strncpy(atoken,oneline,3);  // copy just first three bytes.
		if ((strcmp(atoken,"Mon")==0) ||
				 (strcmp(atoken,"Tue")==0) ||
				 (strcmp(atoken,"Wed")==0) ||
				 (strcmp(atoken,"Thu")==0) ||
				 (strcmp(atoken,"Fri")==0) ||
				 (strcmp(atoken,"Sat")==0) ||
				 (strcmp(atoken,"Sun")==0))
		{ // we have a master record line.  Parsing of previous record is complete,
			// so do something with held data.  If no data held, this is the first
			// record, or prior record was not proper in some way.  Use "Datestring"
			// for existence flag.


			if (datestring[0])
			{	if (db_flag) storedb(username,seconds,recordtype);
				storedata(username,filename,seconds,recordtime,recordtype);
			}


			recordtype = NORECORDTYPE;
			memset(ipaddress,0,sizeof(ipaddress));
			memset(ipfirst,0,sizeof(ipfirst));
			memset (atoken,0,sizeof(atoken));
			memset (username,0,sizeof(username));
			memset (datestring,0,sizeof(datestring)); // also serves as record parse flag
			stopcode = 0;
			baudrate = 0;
			presessiontime = 0;
			bytes2user = 0;
			bytesfmuser = 0;
			portid=0;
			seconds=0;
			recordtime=0;
			recordtype=NORECORDTYPE;


			// The following is the quitter.
			if (q_flag)
			{
				if (strstr(oneline,q_text))
				{
					eoffound = TRUE;
					return; // leave this function immediately.
				}
			}


			if (m_flag)    // This is the selector
			{
				if (strstr(oneline,m_text))
					strncpy(datestring,&oneline[4],sizeof(datestring)-1);
			}
			else
				strncpy(datestring,&oneline[4],sizeof(datestring)-1);




			if (d_flag)    // Delete this record, reset logged in users
			{
				if (strstr(oneline,d_text))
				{
					memset(datestring,0,sizeof(datestring)); // make sure it doesn't write
					memset(ports,0,sizeof(ports));
					for (i=0;i<numusers;i++)
					{
						allusers[i].logins=0;
						allusers[i].logontime=0;
						allusers[i].startconcurrent=0;
					}
				}
			}




			// OK, shall we continue?  Absent datestring means no.
			if (strlen(datestring)==0) continue;
			// convert datestring into binary seconds.
			// int sscanf(const char *buffer, const char *format[, address, ...]);
			// First, space out the colons so sscanf can grab the tokens
			for (i=0;i<strlen(oneline);i++)
				if (oneline[i]==':') oneline[i]=' ';


			// now let sscanf grab and store date tokens. "Mon Nov 18 16 50 45 1996 "
			memset(&time_check,0,sizeof(time_check));
			recordtime=0;
			result=sscanf(&oneline[4],"%3s %d %d %d %d %d",temps,&time_check.tm_mday,
				&time_check.tm_hour,&time_check.tm_min,&time_check.tm_sec,&time_check.tm_year);
			if (result != 6)
			{
				write(STDERR,"Problem in sscanf()",strlen("Problem in sscanf()"));
				write(STDERR,crlf,strlen(crlf));
				recordtime=0;
			}
			else
			{
				// Now convert the tokens to a binary time and store it in the temporary record.
				if (strcmp(temps,"Jan")==0) time_check.tm_mon = 0;
				else if (strcmp(temps,"Feb")==0) time_check.tm_mon = 1;
				else if (strcmp(temps,"Mar")==0) time_check.tm_mon = 2;
				else if (strcmp(temps,"Apr")==0) time_check.tm_mon = 3;
				else if (strcmp(temps,"May")==0) time_check.tm_mon = 4;
				else if (strcmp(temps,"Jun")==0) time_check.tm_mon = 5;
				else if (strcmp(temps,"Jul")==0) time_check.tm_mon = 6;
				else if (strcmp(temps,"Aug")==0) time_check.tm_mon = 7;
				else if (strcmp(temps,"Sep")==0) time_check.tm_mon = 8;
				else if (strcmp(temps,"Oct")==0) time_check.tm_mon = 9;
				else if (strcmp(temps,"Nov")==0) time_check.tm_mon = 10;
				else if (strcmp(temps,"Dec")==0) time_check.tm_mon = 11;




				// Now reconstruct date string for export.
				sprintf(datestring,"%02d/%02d/%4d",time_check.tm_mon+1,time_check.tm_mday,time_check.tm_year);
				sprintf(timestring,"%d:%02d:%02d",time_check.tm_hour,time_check.tm_min,time_check.tm_sec);


				time_check.tm_year -= 1900;


				recordtime = mktime(&time_check);
				if (recordtime == (time_t)(-1))
				{
					comment("Problem in mktime()");
					recordtime=0;
				}
			}
		}
		else // perhaps we have a detail line?
		{
			if (((atoken[0]==' ') || (atoken[0]=='\t')) && (datestring[0] != 0)) // we have a detail line!
			{
				// what KIND of detail line do we have?
				if (strstr(oneline,"Acct-Status-Type ="))
				{
					if (strstr(&oneline[11],"Start"))
						recordtype = START;
					else if (strstr(&oneline[11],"Stop"))
						recordtype = STOP;
				}


				else if (x=strstr(oneline,"User-Name = "))
				{
					beginuserid = x + 13;
					if (enduserid=strchr(beginuserid,'\"'))
					{
						// no suffix, just don't include the trailing quote.
						namesize=enduserid-beginuserid;
						if (namesize > MAXUSERIDLENGTH) namesize=MAXUSERIDLENGTH;
						if (namesize < 0) namesize=1;
						strncpy(username,beginuserid,namesize);
					}


					// Here create the filename for further usage.
					strcpy(filename,makefilename(username));
				}




				else if (strstr(oneline,"Acct-Session-Time ="))
				{
					seconds = atol(&oneline[21]);
				}


//===========
				else if (strstr(oneline,"Framed-Address = "))
				{
					strncpy(ipaddress,&oneline[18],sizeof(ipaddress)-1);
				}
				else if (strstr(oneline,"Ascend-First-Dest ="))
				{
					strncpy(ipfirst,&oneline[21],sizeof(ipfirst)-1);
				}
				else if (strstr(oneline,"Ascend-Data-Rate ="))
				{
					baudrate = atol(&oneline[20]);
				}
				else if (strstr(oneline,"Ascend-Disconnect-Cause ="))
				{
					stopcode = atoi(&oneline[27]);
				}
				else if (strstr(oneline,"Acct-Output-Octets = "))
				{
					bytes2user = atol(&oneline[22]);
				}
				else if (strstr(oneline,"Acct-Input-Octets = "))
				{
					bytesfmuser = atol(&oneline[21]);
				}


				else if (strstr(oneline,"Ascend-PreSession-Time = "))
				{
					presessiontime = atoi(&oneline[26]);
				}










//===========
				else if (strstr(oneline,"NAS-Port ="))
				{
					portid = 0;
					portid = atoi(&oneline[12]);
					if ((portid >20100) && (portid <= 20124))  // T1 Line 1
						portid = portid - 20100;    // convert to ports 1-24
					else
					if ((portid >20200) && (portid <= 20224))  // T1 Line 2
						portid = portid - 20200 + 24;  // convert to ports 25-48
					else
					if ((portid >20300) && (portid <= 20324))  // T1 Line 2
						portid = portid - 20300 + 48;  // convert to ports 49-72
					else
					if ((portid >20400) && (portid <= 20424))  // T1 Line 2
						portid = portid - 20400 + 72;  // convert to ports 73-96


					if (portid >= MAXPORTS) portid = 0;
				}
			} // end of "if we found a detail line"
		} // end of "else not a master record"


	} while (!eoffound);
	// Might be the last record stuck in the buffers.  Check variables and
	// if they constitute a full record, write it.
	if (datestring[0] && username[0] && (recordtype==START || recordtype==STOP))
	{   if (db_flag) storedb(username,seconds,recordtype);
		 storedata(username,filename,seconds,recordtime,recordtype);
	}
}






void oops(void)
{
		comment("Usage: calctime -i inputpath/name [-f] [-s] [-m <matchstring>] [-d <matchstring>] ");
		comment("-o        Output File.  If absent, STDOUT is assumed.");
		comment("-f        Create and append to user files (*.usr) time records for each.");
		comment("-s        Include START records (helps with concurrency problems) ");
		comment("-h <file> Create Histogram file  ");
		comment("-m <text> Match master records, extract when dateline contains this string.");
		comment("-q <text> Quit processing auth file when it sees this text on dateline. ");
		comment("-d <text> Delete records containing this string (date) in dateline. ");
		comment("          (Resets remembered logins)  ");
		comment("-db <file> DataBase Export, tab delimited, ");
		comment("STDOUT    Output is to Standard Output. ");
		exit(1);
}












int main(int argc, char * argv[])
{
int i;
int outhandle;
int curuser;
int byteswritten;
char outline[256];
	buffersize=BUFFSIZE;
	inbuffer=(char *) malloc(buffersize);
	if (inbuffer==NULL)
	{
		puts("Not enough memory for input buffer");
		printf("Amount asked for %u bytes \n",buffersize);
		exit(1);
	}
	memset(inbuffer,0,buffersize);


	outbuffer=(char *) malloc(buffersize);
	if (outbuffer==NULL)
	{  puts("Not enough memory for output buffer");
	  printf("Amount asked for %u bytes \n",buffersize);
	  exit(1);
	}
	memset(outbuffer,0,buffersize);


	bytesout=0;
	outpointer=outbuffer;


	m_flag=s_flag=f_flag=q_flag=o_flag=d_flag=db_flag=FALSE;


	memset(m_text,0,sizeof(m_text));
	memset(d_text,0,sizeof(d_text));
	memset(q_text,0,sizeof(q_text));
	memset(filepath,0,sizeof(filepath));
	memset(outfilepath,0,sizeof(outfilepath));
	memset(allusers,0,sizeof(allusers));
	numusers=0;
	memset(ports,0,sizeof(ports));
	numports=curport=0;


	// make a directory for the user files
	mkdir("users",0770);




	for (i=0;i<argc;i++)
	{
		if (strcmp(strupr(argv[i]),"-F")==0)
			f_flag = TRUE;


		else if (strcmp(strupr(argv[i]),"-S")==0)
		{
			s_flag = TRUE;
		}
		else if (strcmp(strupr(argv[i]),"-DB")==0)
		{
			db_flag = TRUE;
			strncpy(db_text,argv[i+1],sizeof(db_text)-1);
			i++;
		}
		else if (strcmp(strupr(argv[i]),"-M")==0)
		{
			m_flag = TRUE;
			strncpy(m_text,argv[i+1],sizeof(m_text)-1);
			i++;
		}
		else if (strcmp(strupr(argv[i]),"-H")==0)
		{
			h_flag = TRUE;
			strncpy(histogramfilename,argv[i+1],sizeof(histogramfilename)-1);
			i++;
		}
		else if (strcmp(strupr(argv[i]),"-D")==0)
		{
			d_flag = TRUE;
			strncpy(d_text,argv[i+1],sizeof(d_text)-1);
			printf("Deleting days: %s\n",d_text);
			i++;
		}
		else if (strcmp(strupr(argv[i]),"-Q")==0)
		{
			q_flag = TRUE;
			strncpy(q_text,argv[i+1],sizeof(q_text)-1);
			i++;
		}
		else if (strcmp(strupr(argv[i]),"-I")==0)
		{
			strncpy(filepath,argv[i+1],sizeof(filepath)-1);
			i++;
		}
		else if (strcmp(strupr(argv[i]),"-O")==0)
		{
			o_flag=TRUE;
			strncpy(outfilepath,argv[i+1],sizeof(outfilepath)-1);
			i++;
		}
	}


	if (strlen(filepath)==0)
		oops();


	if (m_flag && (strlen(m_text)==0))
		oops();


	if (d_flag && (strlen(d_text)==0))
		oops();


	if (db_flag && (strlen(db_text)==0))
		oops();


	if (q_flag && (strlen(q_text)==0))
		oops();


	if (o_flag)
	{
		// Test writeability before wasting a lot of time.
		outhandle=open(outfilepath,O_WRONLY|O_CREAT,0660);
		if (outhandle<=0)
		{
			fileproblem("Problem creating the output file:",outfilepath);
			write(STDERR,crlf,strlen(crlf));
			exit(1);
		}
		close(outhandle);
		outhandle=0;
	}


	if (db_flag)
	{
		db_handle=open(db_text,O_WRONLY|O_TRUNC|O_CREAT,0660);
		if (db_handle<=0)
		{
			fileproblem("Problem creating the database file:",db_text);
			write(STDERR,crlf,strlen(crlf));
			exit(1);
		}
	}
	// Output file is open if flag is true.




	parsemessages();


	if (db_handle) close(db_handle);




	// Now write out the array
	for (curuser=0;curuser<numusers;curuser++)
	{
		printf("%s\t%6.3lf\t%6.3lf\n",allusers[curuser].user,
		(double)(allusers[curuser].totalseconds/3600.0),
		(double)(allusers[curuser].totalconcurrent/3600.0));
	}


	// if f-flag, write out the total time to each customer file
	if (f_flag) storetotals();


	if (o_flag)
	{
//		outhandle=open(outfilepath,O_TRUNC|O_CREAT,0660);
		outhandle=open(outfilepath,O_WRONLY|O_TRUNC|O_CREAT);
		if (outhandle<=0)
		{
			fileproblem("Problem creating the output file:",outfilepath);
			write(STDERR,crlf,strlen(crlf));
			exit(1);
		}
		else
		{
			for (curuser=0;curuser<numusers;curuser++)
			{
				sprintf(outline,"%s\t%6.3lf\t%6.3lf\r\n",
					allusers[curuser].user,
					(double)(allusers[curuser].totalseconds/3600.0),
					(double)(allusers[curuser].totalconcurrent/3600.0));


				byteswritten=write(outhandle,outline,strlen(outline));
				if (byteswritten < 0)
				{
					fileproblem("Problem writing the results file:",outfilepath);
					exit(1);
				}
			}
			close(outhandle);
		}
	}
	return (0);
}