#include "melange.h"

void util_WriteMsgLog(char *logString);
void util_WriteLog(int logLevel, char *logString);
void util_LogInit();
void util_ReadConfigFile(char *configFileName);
void util_ChatInit(char *configFileName, int quiet);
void util_LoadDefaults();
void util_PrintConfiguration();
void util_printf(char *infoText);
char *util_FitString(char *string);
int util_IndexOf(char *string, int offset);
int util_GetUserId(int sender, char *data);
void util_ResetLog(int mode);
int util_GetBMT();

/*** MANHATTAN:  next function added *****/

void util_AdjustUserCount(int adjustment) {
 static int user_count = 0;     /* server starts with zero users */
 int chat_empty;
 
 chat_empty = (user_count == 0); /* was the chat empty upon entry to this fcn? */
 user_count += adjustment;
 if(user_count < 0) 
      user_count = 0;             /* though this should never happen */
      
 if(user_count == 0)
     unlink(ACTIVEFILE);
 else
     if(chat_empty)
       close(creat(ACTIVEFILE,S_IREAD|S_IWRITE|S_IROTH));
}           

/*** MANHATTAN: next function added - write message to classroom log **/

void util_WriteClassLog(int sender_id, char *sender_name, char *msg, char *group_name)
{
#define MAX_SENDER_LEN 40
#define MAX_PATH 256

 char path[MAX_PATH + 1];
 FILE *fp;
 
 time_t msg_time;       /* time message was sent */
 int deleted = 0;           /* was this message deleted */
 int msg_len;           /* how long is the message? */
 char sender[MAX_SENDER_LEN + 1];       /* must be at least as long as MAX_NAME in manhattan's global.h */
  
  
 /** don't do anything if an empty group name is passed ***/
 if(!strlen(group_name))
   return;
    
 msg_time = time(NULL);
 msg_len=strlen(msg);
 strncpy(sender,sender_name,MAX_SENDER_LEN + 1);
 *(sender + MAX_SENDER_LEN) = '\0';
 
 snprintf(path,MAX_PATH + 1,"../courses/%s/chat_messages.dat",group_name);
 *(path + MAX_PATH) = '\0';

 fp = fopen(path,"a+");
 if(fp)
   {
     fwrite(&msg_time,sizeof(time_t),1,fp);
     fwrite(&deleted,sizeof(int),1,fp);
     fwrite(sender,MAX_SENDER_LEN * sizeof(char),1,fp);
     fwrite(&msg_len,sizeof(int),1,fp);
     fprintf(fp,"%s",msg);
     fclose(fp);
    } 
  else
     logout_unknown_intruder(sender_id);   /* chatutil.c */


#undef MAX_SENDER_LEN

}

int util_GetBMT() {
    time_t t;
    struct tm *gmt;
    float timeGMT;
    
    t=time(NULL);
    gmt=gmtime(&t);
    gmt->tm_hour++;
    if (gmt->tm_hour==24) gmt->tm_hour=0;
    timeGMT=gmt->tm_hour*60+gmt->tm_min;
    return((int)(timeGMT/1.44));
}

void util_WriteLog(int logLevel, char *logString) {
    time_t t;
    char timestring[81];

    if ((logIsActive==YES)&&(logLevel<=mLogLevel)) {
        t=time(NULL);
        strftime (timestring, 80, "%a %m/%d/%Y %I:%M:%S %p",localtime(&t));

	if (logFileHandle!=-1) {
	    if (strlen(logString)>MAX_LOG_STRING)
	        snprintf(msgText, MAX_MSG_TEXT + 1, "%s: -- LOGSTRING BUFFER OVERFLOW --\r\n", timestring);
	    else
	        snprintf(msgText, MAX_MSG_TEXT + 1, "%s: %s", timestring, logString);
	    *(msgText + MAX_MSG_TEXT) = '\0';		
	    write(logFileHandle,msgText,strlen(msgText));
	}
	else logIsActive=NO;
    }
}



void util_WriteMsgLog(char *logString) {
    time_t t;
    struct tm *gmt;
    char myBuffer[500];

    if (mAdmin.msgLog==YES) {
        t=time(NULL);
	gmt=localtime(&t);

	if (msgLogFileHandle!=-1) {
	    if (strlen(logString)>MAX_LOG_STRING)
		snprintf(msgText,MAX_MSG_TEXT + 1, "%d.%d.%d, @%d: -- LOGSTRING BUFFER OVERFLOW --\r\n",
		        gmt->tm_mday, gmt->tm_mon+1, gmt->tm_year+1900, util_GetBMT());
	    else {
		strncpy(infoText,logString, MAX_INFO_TEXT + 1);
		*(infoText + MAX_INFO_TEXT) = '\0';
		strncpy(myBuffer,util_FitString(infoText), 500);
		*(myBuffer + 499) = '\0';
		snprintf(msgText,MAX_MSG_TEXT + 1, "%d.%d.%d, @%d: %s\r\n",
		        gmt->tm_mday, gmt->tm_mon+1, gmt->tm_year+1900, util_GetBMT(),myBuffer);    	
	    }			
	    *(msgText + MAX_MSG_TEXT) = '\0';
	    write(msgLogFileHandle,msgText,strlen(msgText));
	}
	else mAdmin.msgLog=NO;
    }
}

void util_ResetLog(int mode) {
    if (mode==DEFAULT) {
	if ((logFileHandle=open(logFileName,O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP))==-1) 
    	    logIsActive=NO;
	if ((msgLogFileHandle=open(msgLogFileName,O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP))==-1) 
    	    mAdmin.msgLog=NO;
    }
    if (mode==KILL) {
	close(logFileHandle);
	close(msgLogFileHandle);
    }
}

/** sets the global mAdmin structure (see melange.h)with
*** default values.  The default values set here can be
*** over-ridded when the configuration file is read
***
**/
void util_LoadDefaults() {
    int i,ii;

   /* mAdmin is a global variable of type adminFeature
   ** see melange.h 
   **/
    mAdmin.activated=NO;                /* completely disable admin function */
    mAdmin.administrator=SYSTEM;        /* currently, the admin is no known user */
    mAdmin.password[0]=0;               /* there is no admin password */
    mAdmin.allowUserChannels=YES;       /* Allow users to create their own channels */
    mAdmin.allowAnonymousChannel=NO;    /* Don't allow users to switch to the anonymous channel */
    mAdmin.defaultBannTime=BANNTIME;    /* see #define BANNTIME in melange.h */
    mAdmin.sysMsg=NO;                   /* should certain system messages be sent to Java clients that are
                                        ** able to interpret them?
                                        */

    mAdmin.securityType=ALLOW;          /* allow all hosts connect, except those explicitly denied access 
                                        ** via entries in config file
                                        */

    mAdmin.uniqueNames=NO;              /* A person's name within Manhattan's chat does NOT have to be unique */

    mAdmin.changeNames=NO;              /* don't allow users to change their nicknames */

    mAdmin.guestLogin=NO;               /* don't allow people to login as 'guest'.  Actually a moot point because
                                        ** Manhattan completely controls the login process via the 'enter_chat'
                                        ** program.
                                        */
    mAdmin.maxIdleTime=5400;            /* ZERO means never kick out anyone from chat regardless of how long it's
                                        ** been since they've typed a msg.
                                        ** 5400 secs == 90 minutes
                                        **/

    mAdmin.msgLog=NO;                   /** Don't ever record transcripts.  Manhattan mods WILL automatically record
                                        *** transcripts that are privately accessible to a particular class
                                        **/

   /**** QUESTION: mGroup array is not used????   ****/
    for (i=0;i<maxGroups;i++) 
	*(mGroup[i].passwd) = '\0';
	
   /*** QUESTION: is this used, or needed??? ****/	
    for (i=0;i<maxSecurityProfiles;i++) {
	mSecurity[i].hostmask[0]=0;
	mSecurity[i].banntime=0;
    }   
    
    /** Clear out the global mClient[] array
    *** There's nobody logged in.
    **/
    for (i=0;i<maxClients;i++) {
	mClient[i].socket=0;
	strncpy(mClient[i].name,"unknown", MAX_CLIENT_NAME + 1);
	strncpy(mClient[i].host,"unknown", MAX_HOST_NAME + 1);
	mClient[i].channel=0;
	mClient[i].invite=NO;
	mClient[i].online=NO;
	mClient[i].syscap=0x00;
	mClient[i].idle=0;
	mClient[i].group=SYSTEM;
	for (ii=0;ii<maxClients;ii++)
    	    mClient[i].squelch[ii]=NO;
    }


    /** Clear out the mChannel[] array.
    *** No channels have been created
    **/
    for (i=0;i<maxChannels;i++) {
	strncpy(mChannel[i].name,"NONAME", MAX_CHANNEL_NAME + 1);
	mChannel[i].owner=NOBODY;
	mChannel[i].locked=NO;
	mChannel[i].group=SYSTEM;
    }
    
    
   /** Now create the two default system channels
   *** Channel 0 is MAIN
   *** Channel 1 is ANONYM
   ***
   *** QUESTION: should ANONYM be eliminated here, since
   *** we don't want it within Manhattan????
   **/
    
    strncpy(mChannel[0].name,"MAIN", MAX_CHANNEL_NAME + 1); 
    strncpy(mChannel[1].name,"ANONYM", MAX_CHANNEL_NAME + 1); 
    mChannel[0].owner=mChannel[1].owner=SYSTEM;


    /* Set up the log file names */
    logFileName[0]=0;
    msgLogFileName[0]=0;
    mLogLevel=3;       
}




void util_LogInit() {
    char tmpLogString[500];

    if (mLogLevel>0) {
        logIsActive=YES;
	if (logFileName[0]==0) {
		strncpy(logFileName,LOGFILE, MAX_LOGFILE_NAME + 1);
		*(logFileName + MAX_LOGFILE_NAME) = '\0';
	}
	if ((logFileHandle=open(logFileName,O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP))==-1) {
	    printf ("Unable to open logfile %s - Logging disabled.\r\n",logFileName);
	    logIsActive=NO;
	}
	else {
	    snprintf(tmpLogString,500,"Server started on port %d (PID %d).\r\n",mServerPort,getpid());
	    *(tmpLogString + 499) = '\0';
	    util_WriteLog(1,tmpLogString);
	    util_WriteLog(1,VERSION);
	}
    }
    if (mAdmin.msgLog==YES) {
	if (msgLogFileName[0]==0) {
		strncpy(msgLogFileName,MSGLOGFILE, MAX_LOGFILE_NAME + 1);
	}
	if ((msgLogFileHandle=open(msgLogFileName,O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP))==-1) {
	    printf ("Unable to open logfile %s - Logging disabled.\r\n",msgLogFileName);
	    mAdmin.msgLog=NO;
	}
	else {
	    snprintf(tmpLogString,500, "Server started on port %d (PID %d).\r\n",mServerPort,getpid());
	    *(tmpLogString + 499) = '\0';
	    util_WriteLog(1,tmpLogString);
	    util_WriteLog(1,VERSION);
	    util_WriteMsgLog(tmpLogString);
	    util_WriteMsgLog(VERSION);
	}
    }
}



/*** Read the configuration file, parse the lines
**** looking for directives, and use what you find
**** to change the system settings stored in the 
**** mAdmin structure.
****
**** QUESTION: should Manhattan's version use FEWER
**** directives here???
***/

void util_ReadConfigFile(char *configFileName) 
{
 FILE *configFileHandle;
 char tmpString[500];
 char tmpChar;
 long configFileLength;   /* was unsigned long - changed to fix gcc 3.3 warning */
 int i,j;

    if ((configFileHandle=fopen(configFileName,"rb"))!=NULL) 
      {
	fseek(configFileHandle,0,SEEK_END);
        configFileLength=ftell(configFileHandle);
        rewind(configFileHandle);
	    
        while (ftell(configFileHandle)<configFileLength) {
           fscanf (configFileHandle,"%s",tmpString);
	   if (tmpString[0]=='#') {
	       do {
		    fscanf(configFileHandle,"%c",&tmpChar);
	       } while ((tmpChar!='\r')&&(tmpChar!='\n')&&(ftell(configFileHandle)<configFileLength));
	       tmpString[0]=0;	       
	   }
	   if ( ((strcasecmp(tmpString,"ALLOW")==0)&&(mAdmin.securityType==DENY)) ||
	        ((strcasecmp(tmpString,"DENY")==0)&&(mAdmin.securityType==ALLOW)) ) {
	       for (i=0;i<maxSecurityProfiles;i++) {
	           if (mSecurity[i].hostmask[0]==0) {
		       fscanf(configFileHandle,"%s",tmpString);
		       if (strlen(tmpString)<50)
		           strncpy(mSecurity[i].hostmask,tmpString, MAX_HOSTMASK + 1);
		       break;
		   }
	       }
	       tmpString[0]=0;	       
	   }
	   if (strcasecmp(tmpString,"PROFILE")==0) {
	       fscanf(configFileHandle,"%s",tmpString);
	       if (strcasecmp(tmpString,"deny")==0)
	           mAdmin.securityType=DENY;
	       tmpString[0]=0;
	   }
	   if (strcasecmp(tmpString,"IDLETIME")==0)
	       fscanf(configFileHandle,"%d",&mAdmin.maxIdleTime);
	   if (strcasecmp(tmpString,"KICKOUTTIME")==0)
	       fscanf(configFileHandle,"%d",&mAdmin.defaultBannTime);
	   if (strcasecmp(tmpString,"PASSWD")==0) {
	       fscanf(configFileHandle,"%s",tmpString);
	       if (strlen(tmpString)< (MAX_ADMIN_PASSWORD + 1)) {
	           strncpy(mAdmin.password,tmpString, MAX_ADMIN_PASSWORD + 1);
		   if (strcasecmp(tmpString,"changethis")!=0)
		       mAdmin.activated=YES;
	       }
	       tmpString[0]=0;	       
	   }
	   if (strcasecmp(tmpString,"LOGFILE")==0) {
	        fscanf(configFileHandle,"%s",tmpString);
	        if (strlen(tmpString)< (MAX_LOGFILE_NAME + 1))
		    strncpy(logFileName,tmpString, MAX_LOGFILE_NAME + 1);
	        tmpString[0]=0;	       
	   }

/*********************** QUESTION: MSGLOGFILE is disabled -why not fix this througout???
	   if (strcasecmp(tmpString,"MSGLOGFILE")==0) {
	       fscanf(configFileHandle,"%s",tmpString);
	       mAdmin.msgLog=YES;
	       if ((strcasecmp(tmpString,"on")!=0)&&(strlen(tmpString)< (MAX_LOGFILE_NAME + 1))) 
	           strncpy(msgLogFileName,tmpString, MAX_LOGFILE_NAME + 1);
	       tmpString[0]=0;	       
	   }
	   
********************** end comment out ****************/


	   
	   if (strcasecmp(tmpString,"LOGLEVEL")==0) {
	       fscanf(configFileHandle,"%d",&i);
	       if ((i>=0)&&(i<5))
	           mLogLevel=i;
	   }
	   if (strcasecmp(tmpString,"ALLOWCHANNELS")==0) {
	       fscanf(configFileHandle,"%s",tmpString);
	       if (strcasecmp(tmpString,"no")==0) {
	           mAdmin.allowUserChannels=NO;
		   for(i=2;i<maxChannels;i++) {
		       if (mChannel[i].owner!=SYSTEM)
			   mChannel[i].locked=PERMANENT;
		   }
	       }
	       tmpString[0]=0;	       
	   }
	   if (strcasecmp(tmpString,"ANONYMOUS")==0) {
	       fscanf(configFileHandle,"%s",tmpString);
	       	   if (strcasecmp(tmpString,"no")==0) {
		       mAdmin.allowAnonymousChannel=NO;
		       mChannel[1].locked=PERMANENT;
		   }
	       tmpString[0]=0;	       
	   }
	   if (strcasecmp(tmpString,"UNIQUE")==0) {
	       fscanf(configFileHandle,"%s",tmpString);
	       	   if (strcasecmp(tmpString,"yes")==0) 
		       mAdmin.uniqueNames=YES;
	       tmpString[0]=0;	       
	   }




/***************** comment out CHANGENICKS *******
	   if (strcasecmp(tmpString,"CHANGENICKS")==0) {
	       fscanf(configFileHandle,"%s",tmpString);
	       	   if (strcasecmp(tmpString,"no")==0) 
		       mAdmin.changeNames=NO;
	       tmpString[0]=0;	       
	   }
	   
*********************************************/

	   
	   if (strcasecmp(tmpString,"SYSMSG")==0) {
	       fscanf(configFileHandle,"%s",tmpString);
	       	   if (strcasecmp(tmpString,"on")==0)
		       mAdmin.sysMsg=YES;
	       tmpString[0]=0;	       
	   }
	   if (strcasecmp(tmpString,"GUESTLOGIN")==0) {
	       fscanf(configFileHandle,"%s",tmpString);
	       	   if (strcasecmp(tmpString,"no")==0)
		       mAdmin.guestLogin=NO;
	       tmpString[0]=0;	       
	   }
	   if (strcasecmp(tmpString,"SYSCHANNEL")==0) {
	       fscanf(configFileHandle,"%d",&i);
	       fscanf(configFileHandle,"%s",tmpString);
	       if ((strlen(tmpString)>2)&&(strlen(tmpString)<50)&&(tmpString!=NULL))
	           if ((i>1)&&(i<maxChannels))
		       if (mChannel[i].owner==NOBODY) {
		           mChannel[i].owner=SYSTEM;
			   mChannel[i].locked=NO;
			   strncpy(mChannel[i].name,tmpString,MAX_CHANNEL_NAME + 1);
		       }
	       tmpString[0]=0;	       
	   }
	   if (strcasecmp(tmpString,"SETGROUP")==0) {
	       fscanf(configFileHandle,"%d",&i);
	       fscanf(configFileHandle,"%d",&j);
	       if ((mChannel[i].owner!=NOBODY)&&
	          (j>0)&&(j<maxGroups)&&(mGroup[j].passwd[0]!=0))
		      mChannel[i].group=j;
	   }
	   if (strcasecmp(tmpString,"GROUP")==0) {
	       fscanf(configFileHandle,"%d",&i);
	       fscanf(configFileHandle,"%s",tmpString);
	       if ((strlen(tmpString)>0)&&(strlen(tmpString)< (MAX_GROUP_PASSWORD + 1))&&(tmpString!=NULL)&&(i<maxGroups))
	           strncpy(mGroup[i].passwd,tmpString, MAX_GROUP_PASSWORD + 1);
	       tmpString[0]=0;	       
	   }
        }
	fclose(configFileHandle);
   }
}

void util_printf(char *infoText) {
    printf("%s",infoText);
    util_WriteLog(1,infoText);
}

void util_PrintConfiguration() {
    int i;
    char infoText[100];
    
    if (mAdmin.activated==YES)
	util_printf("--> Administrator Features: Enabled\r\n");
    else
    	util_printf("--> Administrator Features: Disabled\r\n");

    if (mAdmin.sysMsg==YES)
	util_printf("--> System Messages: On\r\n");
    else 
	util_printf("--> System Messages: Off\r\n");

    if (mAdmin.allowUserChannels==YES)
	util_printf("--> User Channels: Enabled\r\n");
    else 
	util_printf("--> User Channels: Disabled\r\n");

    if (mAdmin.allowAnonymousChannel==YES)
	util_printf("--> Anonymous Channel: Enabled\r\n");
    else
	util_printf("--> Anonymous Channel: Disabled\r\n");

    if (mAdmin.uniqueNames==YES)
	util_printf("--> Only unique nicknames allowed: Yes\r\n");
    else
	util_printf("--> Only unique nicknames allowed: No\r\n");

    if (mAdmin.guestLogin==YES)
	util_printf("--> Guest login allowed: Yes\r\n");
    else
	util_printf("--> Guest login allowed: No\r\n");

    if (mAdmin.changeNames==YES)
	util_printf("--> Users are allowed to change their nicknames: Yes\r\n");
    else
	util_printf("--> Users are allowed to change their nicknames: No\r\n");

    if (mAdmin.securityType==ALLOW) {
	util_printf("--> Default Security Policy: ALLOW ALL HOSTS\r\n");
	util_printf("--> List of hosts NOT allowed to connect:\r\n");
    }
    else {
    	util_printf("--> Default Security Policy: DENY ALL HOSTS\r\n");
	util_printf("--> List of hosts ALLOWED to connect:\r\n");
    }

    for (i=0;i<maxSecurityProfiles;i++) {
	if (mSecurity[i].hostmask[0]!=0) {
	    snprintf (infoText,MAX_INFO_TEXT + 1, "--> Mask #%d: (%s)\r\n",i,mSecurity[i].hostmask);
	    *(infoText + MAX_INFO_TEXT) = '\0';
	    util_printf(infoText);
	}
    }
    snprintf(infoText,MAX_INFO_TEXT + 1, "--> Default BannTime Setting [sec]: %d\r\n",mAdmin.defaultBannTime);
    *(infoText + MAX_INFO_TEXT) = '\0';
    util_printf(infoText);
    
    snprintf(infoText,MAX_INFO_TEXT + 1, "--> Max Idle Time [sec]: %d\r\n",mAdmin.maxIdleTime);
    *(infoText + MAX_INFO_TEXT) = '\0';
    util_printf(infoText);

    snprintf(infoText,MAX_INFO_TEXT + 1, "--> LogLevel: %d\r\n",mLogLevel);
    *(infoText + MAX_INFO_TEXT) = '\0';
    util_printf(infoText);
    
    snprintf(infoText,MAX_INFO_TEXT + 1, "--> ServerPort: %d\r\n",mServerPort);
    *(infoText + MAX_INFO_TEXT) = '\0';
    util_printf(infoText);
    
    
    if (mAdmin.msgLog==YES)
	snprintf(infoText,MAX_INFO_TEXT + 1, "--> Message Logfile: %s\r\n",msgLogFileName);
    else
    	snprintf(infoText,MAX_INFO_TEXT + 1, "--> Message Logfile: disabled\r\n");
    *(infoText + MAX_INFO_TEXT) = '\0';
    util_printf(infoText);

    printf("--> Logging Activated: %d\r\n",logIsActive);
}







/** Initializes the chat server.
*** Called only on startup from main()
**/
void util_ChatInit(char *configFileName, int quiet) {
    util_LoadDefaults();                  /* set up the mAdmin structure with default values ... */
    util_ReadConfigFile(configFileName);  /* ... read the config file to possibly change those values */
    util_LogInit();                       /* set up the system log file */

    /* 'quiet' is set to '0' via command line switch by Manhattan, so
    **  util_PrintConfiguration() is never called.  QUESTION: should 
    *** util_PrintConfiguration() be changed to write the info to the system
    *** log, so Manhattan admin can see the settings????
    **/
    if(!quiet)                            
         util_PrintConfiguration();
   
   
    mServerStartupTime=time(NULL);   /* set this global variable, which is used to report system uptime */
}





char *util_FitString(char *string) {
    int i;

    for (i=0; i < (int)strlen(string); i++)
	if ((string[i]=='\n')||(string[i]=='\r'))
	    string[i]=0;
    return(string);
}

int util_IndexOf(char *string, int offset) {
    int i;
    
    for (i=offset; i < (int)strlen(string); i++)
	if (string[i]==32) 
	    return(i);
    return(-1);
}

int util_GetUserId(int sender, char *data) {
    int userid=-1;
    int i, found=-1;

    if (((data[0]>64)&&(data[0]<91))||((data[0]>96)&&(data[0]<123))) {
	for (i=0;i<maxClients;i++) {
	    if (strcasecmp(mClient[i].name,data)==0) {
		if (found==-1)
		    found=i;
		else {
		    comm_SendTo(SYSMSG,sender,MSG_MOREUSERS);
		    return(-1);
		}
	    }
	}
    }
    if (found==-1) {
	if ((data[0]>47)&&(data[0]<58))
	    userid=atoi(data);
    } else
	userid=found;
    if ((userid<0)||(userid>maxClients)) {
        if (sender!=SYSTEM)
	    comm_SendTo(SYSMSG,sender,MSG_NOVALIDUSR);
	return(-1);
    }
    return(userid);
}
