#include <rtos.h>
#include <net.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <graph.h>
#include <httpd.h>
#include <time.h>
#include <inifile.h>
#include <syslog.h>
#include <smtpcli.h>
#include <snmp.h>

#include <stdlib.h>

extern char *emailuserid;
extern char *syslogname;
extern DWORD oursysloghost;
extern char *snmptrapname;
extern DWORD snmptraphost;
extern char ftpdpassword[];
extern char *loginuserid;   /* for web */
extern char *loginpassword;

/***************************************************************************
 * general support code                                                    *
 ***************************************************************************/
void replacestr( char *src, char **dest )
{
    char *p;

    /* trim start and end */
    while ( isspace( *src ) )
        src++;

    for ( p = strchr( src, 0 ) ; p != src ; p-- ) {
        if ( !isspace( *p ) ) break;
        *p = 0;
    }

    if ( *dest ) kfree( *dest );
    if ( *src ) *dest = kstrdup( src );
    else *dest = NULL;
}

/***************************************************************************
 * Code to generate graphs                                                 *
 ***************************************************************************/

/*
 * Sample Data Collector
 *
 * This code is mostly in charge of aging the data history
 * every 5 seconds.
 *
 * It is placed in a separate thread so it is easy to read
 */
#define POINTS 20
static int last_n_points[ POINTS ];    /* this is the series over time */
crit_x *collector_cs;

#pragma argsused
void collector( DWORD param )
{
    int i;
    int bytes = 0;

    /* clear history */
    memset( last_n_points, 0, sizeof( last_n_points ));


    /* we will use a critical section so that collector and
     * reporter are not interrupting each other
     */
    collector_cs = cs_alloc();

    do {
        rt_sleep( 500 );    /* collect data for 0.5 seconds */

        /* react to that collected data
         * start by locking it
         */
        cs_enter( collector_cs );

        /* remove the last one from history */
        for ( i = 0 ; i < POINTS - 2 ; ++i )
            last_n_points[ i ] = last_n_points[ i + 1 ];
        /* and include the most recent */
        last_n_points[ POINTS - 2 ] = bytes;

        /* and reset the input to 0 for next round */
        bytes += 1;
        if ( bytes == 5 ) bytes = 0;
        cs_exit( collector_cs );
    } while ( 1 );
}


/* graph code */
crit_x *graph_cs = NULL;
void web_graph( tcp_Socket *s )
{
    graph_x *g;

    kblock();
    if ( graph_cs == NULL )
        graph_cs = cs_alloc();
    kunblock();

    cs_enter( graph_cs );

#define GRWIDTH 200
#define GRHEIGHT 50
    g = gr_alloc( GRWIDTH, GRHEIGHT);
    if ( g != NULL ) {
        gr_background( g, 7 );

        /* put a title on the graph */
        gr_text_at( g , "Bytes per 5 second internval", GRWIDTH/5, GRHEIGHT - 10, 0 );

        /* we need to lock the data for this time */
        cs_enter( collector_cs );

        gr_linegraph( g, POINTS, last_n_points, NULL, NULL, 0, 1 );

        /* we don't need the lock on the data anymore */
        cs_exit( collector_cs );

        sock_mode( s, TCP_MODE_BINARY );
        sock_mode( s, TCP_MODE_BINARY | TCP_MODE_NONAGLE );
        gr_gif( s, g );     /* write the graph out as a GIF file */
        gr_free( g );
    }
    cs_exit( graph_cs );
}

/***************************************************************************
 * Server Side Include Code - answer details from SHTML web pages          *
 ***************************************************************************/

void sh_randvalue( tcp_Socket *s, stringlist *unused )
{
    char buffer[64];

    sprintf( buffer, "%lu", (rand() * 25L) / RAND_MAX );
    sock_write( s, buffer, strlen( buffer ) );
}

#pragma argsused

void sh_testsyslog( tcp_Socket *s, stringlist *unused )
{

    if  (oursysloghost == 0 ) sock_puts( s, "not sent, no syslog host defined");
    else {
        syslog( oursysloghost, LOG_DAEMON, LOG_ERR, "user requested test message");
        sock_puts( s, "sent");
    }
}

#pragma argsused
void sh_testemail( tcp_Socket *s, stringlist *unused )
{
    extern char *emailuserid;
    char buffer[128];

    if  (emailuserid == NULL ) sock_puts( s, "not sent, no email userid defined");
    else {
        smtp_client( 0, "thisbox", emailuserid, "AUTOSEND: TermServer test message",
            "Client requested test Email message be sent");
        sprintf( buffer, "Sent to %s, as long as SMTP.GATEWAY is defined in TCP.CFG", emailuserid );
        sock_puts( s, buffer);
    }
}

#pragma argsused

void sh_header( tcp_Socket *s, stringlist *cookies )
{
    http_dump( s, "web", "header","htm");
}

#pragma argsused

void sh_tail( tcp_Socket *s, stringlist *cookies )
{
    http_dump( s, "web", "tail","htm");
}

#pragma argsused

void sh_uptime( tcp_Socket *s, stringlist *cookies )
{
    char buffer[ 128 ];
    DWORD secs, days, hours;

    /* we can get this from the kernel clock */
    secs = ktime / 1000;
    days = secs / (24*60*60L);
    if ( ktime2 > 0 ) {
        days = (ktime2 * 50) + days;
        sprintf( buffer, "%lu months", days / 30 );
    } else {
        secs = secs - (days * 24*60*60L);
        sprintf( buffer, " %lu days, %lu seconds ", days, secs );
    }
    sock_puts( s, buffer );
}

#pragma argsused
void sh_memfree( tcp_Socket *s, stringlist *cookies )
{
    char buffer[ 128 ];
    sprintf( buffer , " %lu kB ", kcorefree() / 1024 );
    sock_puts( s, buffer );
}
#pragma argsused
void sh_sessions( tcp_Socket *s, stringlist *cookies )
{
    extern WORD inuse[];
    int i, count = 0;
    char buffer[ 128];

    for ( i = 1 ; i < 5 ; ++i ) {
        sprintf( buffer, "TELNET session %u: %s<br>", i, inuse[i] ? "in use" : "available");
        sock_puts( s, buffer );
        count++;
    }
    if ( count == 0 ) sock_puts( s, "no telnet sessions open");
}

#pragma argsused
void sh_smtp( tcp_Socket *s, stringlist *cookies )
{
    sock_puts( s, emailuserid ? emailuserid : "" );
}

#pragma argsused
void sh_syslog( tcp_Socket *s, stringlist *cookies )
{
    sock_puts( s, syslogname ? syslogname : "");
}

#pragma argsused
void sh_ftppass( tcp_Socket *s, stringlist *cookies )
{
    sock_puts( s, ftpdpassword );
}

#pragma argsused
void sh_snmp( tcp_Socket *s, stringlist *cookies )
{
    sock_puts( s, snmptrapname ? snmptrapname : "");
}

void sh_testsnmp( tcp_Socket *s, stringlist *unused )
{
    char *community = "public";
    DWORD agent = 0;
    char *entOID = "1.3.6.1";
    char *fromOID = "1.3.6.1.2.1";
    int genTrapType = SNMP_TRAP_WARMSTART; // generic trap type, 1-6 see RFC 1157
    int specTrapType = 0;   // specific trap type, 0 if using genTrapType
    int type = SNMP_OCTETSTR;
    char *value = "user requested SNMP trap";

    if  (snmptraphost == 0 ) sock_puts( s, "not sent, no SNMP trap host defined");
    else {
        snmp_trap( snmptraphost, community, entOID, fromOID, agent,
            genTrapType, specTrapType,
            type, value, strlen( value ) );
        sock_puts( s, "sent");
    }
}


static ssi_type ssi_list[] =
    {   { "header",   sh_header },
        { "tail",     sh_tail },
        { "uptime",   sh_uptime },
        { "memfree",  sh_memfree },
        { "sessions", sh_sessions },
        { "smtp",     sh_smtp },
        { "trap",     sh_snmp },
        { "syslog",   sh_syslog },
        { "ftppass",  sh_ftppass },
        { "RandValue", sh_randvalue },
        { "testsyslog", sh_testsyslog },
        { "testemail", sh_testemail },
        { "testsnmp", sh_testsnmp },
        NULL };

/***************************************************************************
 * Code to handle user authentication                                      *
 * It works by creating a web cookie with a special ticket.  The ticket    *
 * string is regenerated at each reboot.                                   *
 * odds of a guess are 1/16^63 or one in 2^252                             *
 ***************************************************************************/
char ticket[ 64 ] = "";

void init_ticket( void )
{
    int i;
    /* dream up a ticket */
    randomize();
    for ( i = 0 ; i < sizeof( ticket ) - 1 ; ++i )
        ticket[i] = 'A'+ rand()/(RAND_MAX/16);
    ticket[i] = 0;
}
#pragma startup init_ticket 100

void web_ticket( tcp_Socket *s , char *userid)
{
    char buffer[ 128 ];

    sprintf( buffer, "Set-Cookie: ticket=%s", ticket);
    sock_puts( s, buffer );
    sprintf( buffer, "Set-Cookie: userid=%s", userid);
    sock_puts( s, buffer );
}

/* routine to test if userid is logged in already */
int web_trusted( tcp_Socket *s, stringlist *cookies )
{
    char *p, *q;
    int success = 0;

    if ( strlst_findfirst( cookies, "ticket", NULL, &q ) != NULL )
        if ( *ticket != 0 )
            if ( !strcmp( ticket, q ))
                success = 1;
    if ( success == 0 )
        http_shtml( s, "web", "nologged","sht", ssi_list, cookies );

    return( success );
}

void do_login( tcp_Socket *s, stringlist *cookies )
{
    stringlist *sl;
    char *p, *userid, *password;
    int success = 0;

    /* need to get userid + password */
    sl = cgi_getstrings( s );

    /* look for a valid userid and password */
    if ( NULL != strlst_findfirst( sl, "Userid", NULL, &userid ) ) {
        if (  strlst_findfirst( sl, "Password", NULL, &password ) != NULL ) {

            /* compare these passwords to known ones */
            if ( !strcmp( loginuserid,  userid )) {
                if ( !strcmp( loginpassword, password ))
                    success = 1;
            }
        }
    }

    /* important, free strings !!! */
    cgi_freestrings( sl );

    /* report success or failure to userid */
    if ( success ) {
        web_ticket( s, userid );    /* grant a ticket to allow access to functions */
        http_shtml( s, "web", "logged","sht", ssi_list, cookies );
    } else {
        http_shtml( s, "web", "nologged","sht", ssi_list, cookies );
    }
}
/*
 * web_logout - erase the password cookie and go back to login screen
 */
void do_logout( tcp_Socket *s, stringlist *cookies )
{
    sock_puts( s, "Set-Cookie: ticket=" );
    sock_puts( s, "Set-Cookie: userid=" );
    http_shtml( s, "web", "unlogged","sht", ssi_list, cookies );
}

/***************************************************************************
 * Code to handle changes to system settings                               *
 ***************************************************************************/

void do_set( tcp_Socket *s, stringlist *cookies )
{
    stringlist *sl;
    char *p, *q;

    if ( web_trusted( s, cookies )) {
        /* need to get userid + password */
        sl = cgi_getstrings( s );

        /* replace local contents, and save in config file */
        if ( (p = strlst_findfirst( sl, "smtp", NULL, &q )) != NULL ) {
            replacestr( q, &emailuserid );
            SetIniString( "tcp.cfg","settings","email.notify",q );
        }

        if ( (p = strlst_findfirst( sl, "snmp", NULL, &q )) != NULL ) {
            SetIniString("tcp.cfg","settings","snmptrap.host",q);
            replacestr( q, &snmptrapname );
            snmptraphost = resolve( snmptrapname );
        }

        if ( (p = strlst_findfirst( sl, "syslog", NULL, &q )) != NULL ) {
            SetIniString( "tcp.cfg","settings","syslog.host",q );
            replacestr( q, &syslogname);
            oursysloghost = resolve( syslogname );
        }

        if ( (p = strlst_findfirst( sl, "ftppass", NULL, &q )) != NULL ) {
            SetIniString( "tcp.cfg","settings","ftpd.password",q );
            strncpy( ftpdpassword, q, 64 );
            ftpdpassword[64] = 0;
        }

        /* important, free strings !!! */
        cgi_freestrings( sl );

        http_shtml( s, "web", "settings","sht", ssi_list, cookies );

    }
}

/***************************************************************************
 * Code to handle all web requests                                         *
 ***************************************************************************/

/*
 * - the web server calls this proc for each web request
 * - it is called in the context of *one* of the HTTPD threads,
 *   though which is not known or important
 * - multiple threads may be in the same proc at the same time
 */
#pragma argsused
void user_proc( tcp_Socket *s, char *cmd, char *file, char *ext, stringlist *cookies )
{
    /* prepare output */
    if ( !stricmp( ext, "gif"))  {
        sock_puts( s, "Content-Type: image/gif");
        if ( !stricmp( file, "/graph")) {
            /* short expiry (1 sec) so it re-loads */
            html_datestring( s, "Expires: content=\"%s\"\n\r", 1);
            web_graph( s );
        }
        else {
            sock_puts(s, "");   /* need a blank line */
            http_dump( s, "web", file,ext );
        }
    } else if ( !stricmp( ext, "cgi" )) {
        /* do userid/password validation */
        if ( !stricmp( file, "/dologin" ))
            do_login( s, cookies );
        if ( !stricmp( file, "/dologout" ))
            do_logout( s, cookies );
        if ( !stricmp( file, "/set" ))
            do_set( s, cookies );
    } else {
        /* non gif stuff */
        if ( !stricmp( file, "/" ))
            http_shtml( s, "web", "index","sht", ssi_list, cookies );
        else if ( !stricmp( file, "/login" ))
            http_shtml( s, "web", "login","sht", ssi_list, cookies );

        /* remaining pages only if trusted */
        else if ( web_trusted( s, cookies )) {

            if ( !stricmp(ext,"SHT"))
                http_shtml( s, "web", file, ext, ssi_list, cookies );
            else {
                sock_puts(s, "");       /* need a blank line */
                http_dump( s, "web", file, ext );
            }
        }
    }
}

