/* --- BEGIN COPYRIGHT BLOCK ---
 * This program 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; version 2 of the License.
 * 
 * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * Copyright (C) 2007 Red Hat, Inc.
 * All rights reserved.
 * --- END COPYRIGHT BLOCK ---
 */

#include <pistool.h>

/* returns 0 for success, -1 for failure (EOF encountered) */
static int
UpdateRNG( void )
{
    char           *randbuf;
    int             fd;
    int             i;
    int             count;
    int             c;
    int             rv = 0;
#ifdef XP_UNIX
    cc_t            orig_cc_min;
    cc_t            orig_cc_time;
    tcflag_t        orig_lflag;
    struct termios  tio;
#endif

#define FPS PR_fprintf( PR_STDOUT, 
    FPS "\n");
    FPS "A random seed must be generated that will be used in the\n");
    FPS "creation of your key.  One of the easiest ways to create a\n");
    FPS "random seed is to use the timing of keystrokes on a keyboard.\n");
    FPS "\n");
    FPS "To begin, type keys on the keyboard until this progress meter\n");
    FPS "is full.  DO NOT USE THE AUTOREPEAT FUNCTION ON YOUR KEYBOARD!\n");
    FPS "\n");
    FPS "\n");
    FPS "Continue typing until the progress meter is full:\n\n");
    FPS "|                                                            |\r|");

    /* turn off echo on stdin & return on 1 char instead of NL */
    fd = fileno( stdin );

#if defined( XP_UNIX ) && !defined( VMS )
    tcgetattr( fd, &tio );
    orig_lflag       = tio.c_lflag;
    orig_cc_min      = tio.c_cc[VMIN];
    orig_cc_time     = tio.c_cc[VTIME];
    tio.c_lflag     &= ~ECHO;
    tio.c_lflag     &= ~ICANON;
    tio.c_cc[VMIN]   = 1;
    tio.c_cc[VTIME]  = 0;
    tcsetattr( fd, TCSAFLUSH, &tio );
#endif

    /* Get random noise from keyboard strokes */
    randbuf = ( char * ) PORT_Alloc( RAND_BUF_LENGTH );
    count = 0;
    while( count < NUM_KEYSTROKES+1 ) {
#ifdef VMS
        c = GENERIC_GETCHAR_NOECHO();
#elif XP_UNIX
        c = getc( stdin );
#else
        c = getch();
#endif
        if( c == EOF ) {
            rv = -1;
            break;
        }

        PK11_RandomUpdate(
        /* data            */  randbuf,
        /* length in bytes */  sizeof( randbuf ) );

        if( c != randbuf[0] ) {
            randbuf[0] = c;

            FPS "\r|");

            for( i = 0 ;
                 i < count / ( NUM_KEYSTROKES / RAND_BUF_LENGTH ) ;
                 i++ ) {
                FPS "*");
            }

            if( count % ( NUM_KEYSTROKES / RAND_BUF_LENGTH ) == 1 ) {
                FPS "/");
            }

            count++;
        }
    }

    free( randbuf ); 

    FPS "\n\n");
    FPS "Finished.\n");

    TKS_TypeProceedToContinue();

    FPS "\n");

#undef FPS

#if defined( XP_UNIX ) && !defined( VMS )
    /* set back termio the way it was */
    tio.c_lflag     = orig_lflag;
    tio.c_cc[VMIN]  = orig_cc_min;
    tio.c_cc[VTIME] = orig_cc_time;
    tcsetattr( fd, TCSAFLUSH, &tio );
#endif

    return rv;
}


void
TKS_FileForRNG( char *noise )
{
    char        buf[2048];
    PRFileDesc *fd;
    PRInt32     count;

    fd = PR_OpenFile( noise, PR_RDONLY, 0666 );
    if( !fd ) {
        return;
    }

    do {
        count = PR_Read( fd, buf, sizeof( buf ) );
        if (count > 0) {
            PK11_RandomUpdate( 
            /* data            */  buf,
            /* length in bytes */  count );
        }
    } while( count > 0 );

    PR_Close( fd );
}


SECStatus
TKS_SeedRNG( char *noise )
{
    /* Clear the screen */
    TKS_ClearScreen();

    /* Seed the RNG */
    if( noise ) {
        TKS_FileForRNG( noise );
    } else {
        int rv = UpdateRNG();
        if( rv ) {
            PORT_SetError( PR_END_OF_FILE_ERROR );
            return SECFailure;
        }
    }

    return SECSuccess;
}

