/*
 * The following code will send a verification code to a player after they set their email with 'setemail'.
 * After verifying their email the MUD can then be used to send other emails (password recovery, immortal note, etc).
 *
 * This was written for AckFUSS 4.4.1 but should port easily to any Diku-based game. Substitute char * fields for string
 * fields and adjust as needed if you are compiling with gcc instead of g++.
 *
 * STEP 1:
 *    Add the defines for do_verify_email() and do_set_email() to your command table/headers like any other command.
 *
 * STEP 2:
 *    Add the following struct to your primary .h file
 *     struct email_data
 *     {
 *         bool verified;
 *         string address;
 *         string confirmation_code;
 *     };
 *    Then declare the following typedef with your other typedefs
 *     typedef struct email_data EMAIL_DATA;
 *    Finally, add it to your char_data or pc_data struct where applicable.
 *    NOTE: Be sure to make proper new/delete memory calls in your character create/delete routines.
 *      EMAIL_DATA *email;
 *
 * STEP 3:
 *    Add fields in your save/load player routines to save all the values of your new email_data struct.
 *
 * STEP 4:
 *    Adjust things to fit your game, such as mudnamecolor, the EMAIL_FILE logging, and monitor_chan notifications.
 *
 * STEP 5:
 *    Ensure your system is setup for command line email. You can test via your shell with the following:
 *     echo "This is the body text." | mail -s "Testing CLI Email" "your@email.com"
 *
 * STEP 6:
 *    Fix anything broken with your server's email daemon if you don't receive your email from step 4 :).
 *
 * Feel free to modify this code to suit your needs, and if you have a great improvement I wouldn't mind if you
 * shared it with me either, if you're so inclined :D. All I ask is to give me credit for the code if you do
 * choose to use it.
 *
 * --Kline (Matt Goff)
 */

DO_FUN(do_verify_email)
{
    if ( IS_NPC( ch ) )
        return;
    if ( ch->pcdata->email->address.empty() )
    {
        ch->send( "You need to provide an email address first with setemail <address>.\r\n" );
        return;
    }
    if ( ch->pcdata->email->verified )
    {
        ch->send( "You have already verified your email. If you wish to update your email, please use setemail <address>.\r\n" );
        return;
    }
    if ( argument[0] == '\0' )
    {
        ch->send( "To verify your email address you must everify <code> using the code you received.\r\n" );
        return;
    }
    if ( !str_cmp( argument, ch->pcdata->email->confirmation_code ) )
    {
       ch->send( "Thank you for verifying your email address.\r\n");
       ch->pcdata->email->confirmation_code.clear();
       ch->pcdata->email->verified = true;
       snprintf( log_buf, MSL, "%s (%s) has verified their email address.", ch->get_name(), ch->pcdata->email->address.c_str() );
       monitor_chan( log_buf, MONITOR_EMAIL );
       return;
    }
    else
    {
        ch->send( "Invalid code. If you did not receive a validation code please reset your email using the setemail command.\r\n" );
        return;
    }

    return;
}

DO_FUN(do_set_email)
{
    char body[MSL], subject[MSL];

    if ( IS_NPC( ch ) )
        return;
    if ( argument[0] == '\0' )
    {
        ch->send( "You didn't provide an email address. Syntax is setemail <email>.\r\n" );
        return;
    }

    ch->pcdata->email->address = argument;
    ch->pcdata->email->confirmation_code.clear();
    ch->pcdata->email->confirmation_code = gen_rand_string(8);
    ch->pcdata->email->verified = false;
    ch->send( "An email has been sent to %s with a confirmation code. Please verify your address by typing everify <code> once your have received the email. If you do not receive an email, set your email again and a new code will be sent.\r\n", argument );

    snprintf( subject, MSL, "%s Email Confirmation Code", mudnamenocolor );
    snprintf( body, MSL, "<html>This email was used by a player of %s. If this was not you, please disregard it.<br><br>Confirmation code: <b>%s</b></html>", mudnamenocolor, ch->pcdata->email->confirmation_code.c_str() );
    send_email( argument, subject, body, false, ch);

    return;
}

bool send_email( const char *address, const char *subject, const char *body, bool validate, CHAR_DATA *ch )
{
    char mailbuf[MSL];
    FILE *fp;

    if( IS_NPC(ch) ) /* Safety check for later --Kline */
        ch = NULL;

    if ( validate && ch != NULL && !ch->pcdata->email->verified )
    {
        snprintf( log_buf, MSL, "Unable to send email to %s (%s); email not verified.", ch->get_name(), ch->pcdata->email->address.c_str() );
        monitor_chan( log_buf, MONITOR_EMAIL );
        return false;
    }

    snprintf( mailbuf, MSL, "echo \"%s\" | mail -a \"Content-type: text/html;\" -s \"%s\" \"%s\"", body, subject, ch == NULL ? address : ch->pcdata->email->address.c_str() );

    /*
     * system() is if() encapsulated to suppress a warning. system() returns different results on different distros,
     * so there is no reliable return value to check against. --Kline
     */
    if ( system( mailbuf ) ) {}

    if( ch == NULL )
        snprintf( log_buf, MSL, "An email was sent to (%s) with subject (%s).", address, subject );
    else
        snprintf( log_buf, MSL, "An email was sent to %s (%s) with subject (%s).", ch->get_name(), ch->pcdata->email->address.c_str(), subject );
    monitor_chan( log_buf, MONITOR_EMAIL );

    if ( ( fp = file_open( EMAIL_FILE, "a" ) ) != NULL )
    {
        fprintf( fp, "%s :: %s\n", current_time_str(), mailbuf );
        file_close( fp );
    }

    return true;
}

const char *gen_rand_string( int length )
{
    int i, r;
    char tmp[2];
    static char output[MSL];
    const char valid[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    srand ( current_time );

    for ( i = 0; i < length; i++ )
    {
        r = rand() % strlen(valid);
        snprintf( tmp, 2, "%c", valid[r] );
        if ( i == 0 )
            snprintf ( output, length+1, "%s", tmp );
        else
            strncat ( output, tmp, length+1 );
    }

    return output;
}


const char *current_time_str( void )
{
    char *strtime;
    static char output[MSL];

    strtime = ctime( &current_time );
    strtime[strlen( strtime ) - 1] = '\0';
    snprintf( output, MSL, "%s", strtime );

    return output;
}