/*****************************************************************************
 *
 * $Id$
 * Purpose ...............: MBSE BBS Daemon
 *
 *****************************************************************************
 * Copyright (C) 1997-2007
 *   
 * Michiel Broek		FIDO:		2:280/2802
 * Beekmansbos 10
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * MB BBS is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MB BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "../config.h"
#include "../lib/mbselib.h"
#include "taskstat.h"
#include "taskregs.h"
#include "taskdisk.h"
#include "taskinfo.h"
#include "taskutil.h"
#include "taskchat.h"
#include "taskcomm.h"
#include "taskibc.h"


extern int			oserr;		/* Copy of Unix error	*/
extern int			sock;		/* Server socket	*/
extern struct sockaddr_un	from;		/* From socket address	*/
extern int			fromlen;	/* From address length	*/
extern int			logtrans;	/* Log transactions	*/



/************************************************************************
 *
 *  Logging procedures.
 */

int userlog(char *);
int userlog(char *param)
{
    char	*prname, *prpid, *grade, *msg;
    static char	lfn[PATH_MAX], token[14];
    int		rc;

    lfn[0] = '\0';
    strncpy(token, strtok(param, ","), 14);
    strncpy(token, strtok(NULL, ","), 14);
    snprintf(lfn, PATH_MAX, "%s/log/%s", getenv("MBSE_ROOT"), token);
    prname = strtok(NULL, ",");
    prpid  = strtok(NULL, ",");
    grade  = strtok(NULL, ",");
    msg    = xstrcpy(cldecode(strtok(NULL, ";")));
    rc = ulog(lfn, grade, prname, prpid, msg);
    free(msg);
    return rc;
}



/*
 * Process command received from the client.
 */
char *exe_cmd(char *);
char *exe_cmd(char *in)
{
    static char	obuf[SS_BUFSIZE];
    static char	ibuf[SS_BUFSIZE];
    static char	cmd[4];
    static char	token[SS_BUFSIZE];
    static char	ebuf[19];
    static char	*cnt, var1[16];
    int		result;
    char	*buf;

    strncpy(ibuf, in, SS_BUFSIZE);
    strncpy(cmd, ibuf, 4);
    strncpy(ebuf, "200:1,Syntax error;", 19);

    /*
     * Split the commandline after the colon so we can give the
     * options directly to the actual functions. Also set a default
     * and most used answer.
     */
    strncpy(token, &ibuf[5], SS_BUFSIZE);
    strncpy(obuf, "100:0;", SS_BUFSIZE);


    /*
     * The A(counting) commands.
     *
     *  AINI:5,pid,tty,user,program,city;
     *  100:1,linenr;
     *  200:1,Syntax Error;
     */
    if (strncmp(cmd, "AINI", 4) == 0) {
	if ((result = reg_newcon(token)) != -1) {
	    snprintf(obuf, SS_BUFSIZE, "100:1,%d;", result);
	    return obuf;
	} else {
	    stat_inc_serr();
	    return ebuf;
	}
    }

    /*
     *  ACLO:1,pid;
     *  107:0;
     *  200:1,Syntax Error;
     */
    if (strncmp(cmd ,"ACLO", 4) == 0) {
	if (reg_closecon(token) == 0) {
	    strcpy(obuf, "107:0;");
	    return obuf;
	} else {
	    stat_inc_serr();
	    return ebuf;
	}
    }

    /*
     *  ADOI:2,pid,doing;
     *  100:0;
     *  200:1,Syntax Error;
     */
    if (strncmp(cmd, "ADOI", 4) == 0) {
	if (reg_doing(token) == 0)
	    return obuf;
	else {
	    stat_inc_serr();
	    return ebuf;
	}
    }

    /*
     *  ATCP:1,pid;
     *  100:0;
     *  200:1,Syntax Error;
     */
    if (strncmp(cmd, "ATCP", 4) == 0) {
	if (reg_ip(token) == 0)
	    return obuf;
	else {
	    stat_inc_serr();
	    return ebuf;
	}
    }

    /*
     *  ATTY:2,pid,tty;
     *  100:0;
     *  200:1,Syntax Error;
     */
    if (strncmp(cmd, "ATTY", 4) == 0) {
	if (reg_tty(token) == 0)
	    return obuf;
	else {
	    stat_inc_serr();
	    return ebuf;
	}
    }

    /*
     *  ALOG:5,file,program,pid,grade,text;
     *  100:0;
     *  201:1,errno;
     */
    if (strncmp(cmd, "ALOG", 4) == 0) {
	if (userlog(token) != 0) 
	    snprintf(obuf, SS_BUFSIZE, "201:1,%d;", oserr);
	return obuf;
    }

    /*
     *  AUSR:3,pid,user,city;
     *  100:0;
     *  200:1,Syntax Error;
     */
    if (strncmp(cmd, "AUSR", 4) == 0) {
	if (reg_user(token) == 0) 
	    return obuf;
	else {
	    stat_inc_serr();
	    return ebuf;
	}
    }

    /*
     *  ADIS:2,pid,flag; (set Do Not Disturb).
     *  100:0;
     *  200:1,Syntax Error;
     */
    if (strncmp(cmd, "ADIS", 4) == 0) {
	if (reg_silent(token) == 0)
	    return obuf;
	else {
	    stat_inc_serr();
	    return ebuf;
	}
    }

    /*
     *  ATIM:2,pid,seconds;
     *  100:0;
     *  200:1,Syntax Error;
     */
    if (strncmp(cmd, "ATIM", 4) == 0) {
	if (reg_timer(TRUE, token) == 0)
	    return obuf;
	else {
	    stat_inc_serr();
	    return ebuf;
	}
    }

    /*
     *  ADEF:1,pid;
     *  100:0;
     */
    if (strncmp(cmd, "ADEF", 4) == 0) {
	if (reg_timer(FALSE, token) == 0)
	    return obuf;
	else {
	    stat_inc_serr();
	    return ebuf;
	}
    }

    /*
     *  Check for personal message
     *
     *  CIPM:1,pid;  (Is personal message present)
     *  100:2,fromname,message;
     *  100:0;
     */
    if (strncmp(cmd, "CIPM", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	reg_ipm_r(token, buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     * CSPM:3,fromuser,touser,text; (Send personal message).
     * 100:1,n;  n: 1=donotdisturb 2=buffer full 3=error
     * 100:0;
     */
    if (strncmp(cmd, "CSPM", 4) == 0) {
	if ((result = reg_spm(token)))
	    snprintf(obuf, SS_BUFSIZE, "100:1,%d;", result);
	return obuf;
    }

    /*
     * CSYS:2,pid,1;    Sysop available for chat (from mbmon)
     * CSYS:2,pid,0;    Sysop goes away (from mbmon)
     * 100:0;		Allways Ok.
     */
    if (strncmp(cmd, "CSYS", 4) == 0) {
	reg_sysop(token);
	return obuf;
    }

    /*
     * CPAG:2,pid,reason;   Page sysop for a chat
     * 100:1,n;		    1=busy, 2=sysop not available, 3=error
     * 100:0;		    Ok
     */
    if (strncmp(cmd, "CPAG", 4) == 0) {
	if ((result = reg_page(token))) {
	    snprintf(obuf, SS_BUFSIZE, "100:1,%d;", result);
	}
	return obuf;
    }

    /*
     * CCAN:1,pid;	    Cancel sysop page
     * 100:0;		    Always Ok
     */
    if (strncmp(cmd, "CCAN", 4) == 0) {
	reg_cancel(token);
	return obuf;
    }

    /*
     * Check for sysop page (from mbmon)
     *
     * CCKP:0;
     * 100:3,pid,1,reason;  Page is active
     * 100:3,pid,0,reason;  Page is canceled, but user still online
     * 100:0;		    No page active
     */
    if (strncmp(cmd, "CCKP", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	reg_checkpage_r(token, buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     * Check for sysop in chatmode for forced sysop chat
     *
     * CISC:1,pid;
     * 100:1,1;		    Yes (and drop into chatmode)
     * 100:1,0;		    No
     */
    if (strncmp(cmd, "CISC", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	chat_checksysop_r(token, buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     * Connect to chatserver
     *
     * CCON:4,pid,username,unixname,n;	Connect to chatserver with username, n=1 user is the sysop
     * 100:1,error;		If error
     * 100:0;			Ok
     */
    if (strncmp(cmd, "CCON", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
        chat_connect_r(token, buf);
        snprintf(obuf, SS_BUFSIZE, "%s", buf);
        free(buf);
        return obuf;
    }
    
    /*
     * Close chat session
     *
     * CCLO:1,pid;	    Leave chatserver
     * 100:1,error;	    Error
     * 100:0;		    Ok
     */
    if (strncmp(cmd, "CCLO", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	chat_close_r(token, buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     * Put message on server
     *
     * CPUT:2,pid,message;  Put message on server
     * 100:2,0,error;	    Error, not fatal and continue chat
     * 100:2,1,error;	    Error, fatal and disconnect
     * 100:0;		    Ok
     */
    if (strncmp(cmd, "CPUT", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	chat_put_r(token, buf);
	strncpy(obuf, buf, SS_BUFSIZE);
	free(buf);
	return obuf;
    }

    /*
     * Get message from server
     *
     * CGET:1,pid;	    Get message from server
     * 100:2,0,message;	    If message present
     * 100:2,1,error;	    Error and disconnect
     * 100:0;		    No message
     */
    if (strncmp(cmd, "CGET", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	chat_get_r(token, buf);
	strncpy(obuf, buf, SS_BUFSIZE);
	free(buf);
	return obuf;
    }

    /*
     * The D(isk) commands.
     */

    /*
     *  DRES:0;             Reset and reread disk tables.
     *  100:0;		    Always Ok.
     */
    if (strncmp(cmd, "DRES", 4) == 0) {
	return disk_reset();
    }
    
    /*
     *  DSPC:1,n;           Check free space in MBytes
     *  100:2,0,n;          No, n = lowest size in MBytes
     *  100:2,1,n;          Yes, n = lowest size in MBytes
     *  100:1,2;            Unknown
     *  100:1,3;            Error
     */
    if (strncmp(cmd, "DSPC", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	disk_check_r(token, buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }
    
    /*
     *  DGFS:0;		    Get filesystem status.
     *  100:n,data1,..,data10;
     */
    if (strncmp(cmd, "DGFS", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	disk_getfs_r(buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }
    
    /*
     * The G(lobal) commands.
     *
     *  GNOP:1,pid;
     *  100:0;
     */
    if (strncmp(cmd ,"GNOP", 4) == 0) {
	reg_nop(token);
	return obuf;
    }

    /*
     *  GPNG:n,data;
     *  100:n,data;
     */
    if (strncmp(cmd, "GPNG", 4) == 0) {
	snprintf(obuf, SS_BUFSIZE, "100:%s", token);
	return obuf;
    }

    /*
     *  GVER:0;
     *  100:1,Version ...;
     */
    if (strncmp(cmd, "GVER", 4) == 0) {
	snprintf(obuf, SS_BUFSIZE, "100:1,Version %s;", VERSION);
	return obuf;
    }

    /*
     *  GSTA:0;
     *  100:19,start,laststart,daily,startups,clients,tot_clients,tot_peak,tot_syntax,tot_comerr,
     *         today_clients,today_peak,today_syntax,today_comerr,!BBSopen,ZMH,internet,Processing,Load,sequence;
     *  201:1,16;
     */
    if (strncmp(cmd, "GSTA", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	stat_status_r(buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     *  GMON:1,n;  n=1 First time
     *  100:7,pid,tty,user,program,city,isdoing,starttime;
     *  100:0;
     */
    if (strncmp(cmd, "GMON", 4) == 0) {
	cnt = strtok(token, ",");
	strcpy(var1, strtok(NULL, ";"));
	buf = calloc(SS_BUFSIZE, sizeof(char));
	get_reginfo_r(atoi(var1), buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     *  GDST:0;		Obsolete!
     *  100:n,data1,..,data10;
     */
//    if (strncmp(cmd, "GDST", 4) == 0) {
//        buf = calloc(SS_BUFSIZE, sizeof(char));
//	disk_getfs_r(buf);
//	snprintf(obuf, SS_BUFSIZE, "%s", buf);
//	free(buf);
//	return obuf;
//    }

    /*
     *  GSYS:0;
     *  100:7,calls,pots_calls,isdn_calls,network_calls,local_calls,startdate,last_caller;
     *  201:1,16;
     */
    if (strncmp(cmd, "GSYS", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	get_sysinfo_r(buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     *  GLCC:0;
     *  100:1,n;
     */
    if (strncmp(cmd, "GLCC", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	get_lastcallercount_r(buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     *  GLCR:1,recno;
     *  100:9,user,location,level,device,time,mins,calls,speed,actions;
     *  201:1,16;
     */
    if (strncmp(cmd, "GLCR", 4) == 0) {
        cnt = strtok(token, ",");
        strcpy(var1, strtok(NULL, ";"));
	buf = calloc(SS_BUFSIZE, sizeof(char));
	get_lastcallerrec_r(atoi(var1), buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }


    /*
     * The (S)tatus commands.
     *
     *  SBBS:0;
     *  100:2,n,status message;
     */
    if (strncmp(cmd, "SBBS", 4) == 0) {
	switch(stat_bbs_stat()) {
	    case 0: snprintf(obuf, SS_BUFSIZE, "100:2,0,The system is open for use;");
		    break;
	    case 1: snprintf(obuf, SS_BUFSIZE, "100:2,1,The system is closed right now!;");
		    break;
	    case 2: snprintf(obuf, SS_BUFSIZE, "100:2,2,The system is closed for Zone Mail Hour!;");
		    break;
	}
	return obuf;
    }

    /*
     *  SOPE:0;
     *  100:0;
     */
    if (strncmp(cmd, "SOPE", 4) == 0) {
	stat_set_open(1);
	return obuf;
    }

    /*
     *  SCLO:0;
     *  100:0;
     */
    if (strncmp(cmd, "SCLO", 4) == 0) {
	stat_set_open(0);
	return obuf;
    }

    /*
     *  SFRE:0;
     *  100:1,Running utilities: n  Active users: n;
     *  100:0;
     *  201:1,16;
     */
    if (strncmp(cmd, "SFRE", 4) == 0) {
	buf = calloc(SS_BUFSIZE, sizeof(char));
	reg_fre_r(buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     *  SSEQ:0;
     *  100:1,number;
     *  200:1,16;
     */
    if (strncmp(cmd, "SSEQ", 4) == 0) {
        buf = calloc(SS_BUFSIZE, sizeof(char));
	getseq_r(buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     *  SEST:1,semafore;   Get status of semafore
     *  100:1,n;           1 = set, 0 = not set
     *  200:1,16;
     */
    if (strncmp(cmd, "SEST", 4) == 0) {
        buf = calloc(SS_BUFSIZE, sizeof(char));
	sem_status_r(token, buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     *  SECR:1,semafore;   Set semafore
     *  100:0;
     *  200:1,16;
     */
    if (strncmp(cmd, "SECR", 4) == 0) {
        buf = calloc(SS_BUFSIZE, sizeof(char));
	sem_create_r(token, buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }

    /*
     *  SERM:1,semafore;   Remove semafore
     *  100:0;
     *  200:1,16;
     */
    if (strncmp(cmd, "SERM", 4) == 0) {
        buf = calloc(SS_BUFSIZE, sizeof(char));
	sem_remove_r(token, buf);
	snprintf(obuf, SS_BUFSIZE, "%s", buf);
	free(buf);
	return obuf;
    }


    /*
     * If we got this far, there must be an error.
     */
    stat_inc_serr();
    Syslog('!', "Comm systax error: \"%s:%s\"", cmd, printable(token, 0));
    return ebuf;
}



void do_cmd(char *cmd)
{
    char    buf[SS_BUFSIZE];
    int	    slen, tries = 0;

    if (logtrans)
	Syslog('-', "< %s", cmd);
    snprintf(buf, SS_BUFSIZE, "%s", exe_cmd(cmd));
    if (logtrans)
	Syslog('-', "> %s", buf);

    for (;;) {
	slen = sendto(sock, buf, strlen(buf), 0, (struct sockaddr *)&from, fromlen);
	if (slen == -1)
	    Syslog('?', "$do_cmd(): sendto error %d %s", tries, from.sun_path);
	else if (slen != strlen(buf))
	    Syslog('?', "do_cmd(): send %d of %d bytes, try=%d", slen, strlen(buf), tries);
	else
	    return;
	tries++;
	if (tries == 3)
	    return;
	sleep(1);
    }
}