#include "common.h"
#include "ssh.h"
#include "fingerprint.h"
#include "find.h"
#include "files.h"
#include "memcached.h"
#include "check-hash.h"
#include "command-line-args.h"
#include "filesigning.h"
#include "cgi.h"
#include "otp.h"
#include "output.h"
#include "xdialog.h"

int HashFromListFile(HashratCtx *Ctx)
{
    char *Tempstr=NULL, *HashStr=NULL;
    const char *ptr;
    STREAM *S;
    struct stat Stat;
    int result=FALSE;

    ptr=GetVar(Ctx->Vars, "ItemsListSource");
    if (
        (! StrValid(ptr)) ||
        (strcmp(ptr,"-")==0)
    ) S=STREAMFromFD(0);
    else S=STREAMOpen(ptr,"r");

//if we managed to open the list, return TRUE
    if (S) result=TRUE;

    Tempstr=STREAMReadLine(Tempstr, S);
    while (Tempstr)
    {
        StripTrailingWhitespace(Tempstr);
        if (StatFile(Ctx,Tempstr,&Stat) != -1)
        {
            HashStartTime=GetTime(TIME_MILLISECS);
            if (HashItem(Ctx, Ctx->HashType, Tempstr, &Stat, &HashStr))
            {
                HashratOutputFileInfo(Ctx, Ctx->Out, Tempstr, &Stat, HashStr);
                HashratStoreHash(Ctx, Tempstr, &Stat, HashStr);
            }
            else
            {
                fprintf(stderr,"ERROR: Failed to open file %s\n",Tempstr);
                result=FALSE;
            }
        }
        else
        {
            fprintf(stderr,"ERROR: Failed to open file %s\n",Tempstr);
            result=FALSE;
        }
        Tempstr=STREAMReadLine(Tempstr, S);
    }

    STREAMClose(S);

    Destroy(Tempstr);
    Destroy(HashStr);

    return(result);
}





void HashStdIn(HashratCtx *Ctx)
{
    STREAM *In=NULL;
    char *Tempstr=NULL, *Hash=NULL;

    if (Flags & FLAG_LINEMODE)
    {
        In=STREAMFromFD(0);
        STREAMSetTimeout(In,0);

        if (Flags & FLAG_HIDE_INPUT) Tempstr=TerminalReadText(Tempstr, TERM_HIDETEXT, In);
        else if (Flags & FLAG_STAR_INPUT) Tempstr=TerminalReadText(Tempstr, TERM_SHOWSTARS, In);
        else Tempstr=STREAMReadLine(Tempstr,In);
        while (Tempstr)
        {
            if (! (Flags & FLAG_RAW)) StripTrailingWhitespace(Tempstr);
            Hash=CopyStr(Hash,"");
            ProcessData(&Hash, Ctx, Tempstr, StrLen(Tempstr));
            OutputHash(Ctx, Hash, "");

            if (Flags & FLAG_HIDE_INPUT) Tempstr=TerminalReadText(Tempstr, TERM_HIDETEXT, In);
            else if (Flags & FLAG_STAR_INPUT) Tempstr=TerminalReadText(Tempstr, TERM_SHOWSTARS, In);
            else Tempstr=STREAMReadLine(Tempstr,In);
        }
    }
    else
    {
        HashratHashSingleFile(Ctx, Ctx->HashType, 0, "-", NULL, &Hash);
        OutputHash(Ctx, Hash, "");
    }
    STREAMDestroy(In);

    Destroy(Tempstr);
    Destroy(Hash);
}



int ProcessTargetItems(HashratCtx *Ctx)
{
    struct stat Stat;
    int result=IGNORE, type;
    char *Item=NULL;
    const char *ptr;

    ptr=GetToken(Ctx->Targets, ",", &Item, GETTOKEN_QUOTES);
    while(ptr)
    {
        type=StatFile(Ctx, Item, &Stat);

        if (type > -1) result=ProcessItem(Ctx, Item, &Stat, TRUE);
        else
        {
            if (result==IGNORE) result=0;
            fprintf(stderr,"ERROR: File '%s' not found\n", Item);
        }
        ptr=GetToken(ptr, ",", &Item, GETTOKEN_QUOTES);
    }

    DestroyString(Item);

    return(result);
}



int main(int argc, char *argv[])
{
    char *Tempstr=NULL;
    const char *ptr;
    int i, result=FALSE;
    struct stat Stat;
    HashratCtx *Ctx;

    time(&Now);
    Tempstr=SetStrLen(Tempstr, HOST_NAME_MAX);
    gethostname(Tempstr, HOST_NAME_MAX);
    LocalHost=CopyStr(LocalHost, Tempstr);
    Tempstr=SetStrLen(Tempstr, HOST_NAME_MAX);
    result=getdomainname(Tempstr, HOST_NAME_MAX);
    if (result > -1) Tempstr[result]='\0';
    LocalHost=MCatStr(LocalHost, ".", Tempstr, NULL);

    memset(&Stat,0,sizeof(struct stat)); //to keep valgrind happy

    Ctx=CommandLineParseArgs(argc,argv);
    if (Ctx)
    {
        MemcachedConnect(GetVar(Ctx->Vars, "Memcached:Server"));

        switch (Ctx->Action)
        {
        case ACT_LIST_TYPES:
            Tempstr=HashAvailableTypes(Tempstr);
            strrep(Tempstr, ',', '\n');
            printf("%s\n", Tempstr);
            break;

        case ACT_HASH:
            result=ProcessTargetItems(Ctx);
            //if we didn't find anything on the command-line then read from stdin
            if (result==IGNORE) HashStdIn(Ctx);
            break;

        case ACT_HASHDIR:
            result=ProcessTargetItems(Ctx);
            break;

        case ACT_CHECK_LIST:
            result=CheckHashesFromList(Ctx);
            break;

        case ACT_HASH_LISTFILE:
            HashFromListFile(Ctx);
            break;

        case ACT_CHECK:
        case ACT_FINDMATCHES:
            if (! MatchesLoad(Ctx, 0))
            {
                printf("No hashes supplied on stdin.\n");
                exit(1);
            }
            result=ProcessTargetItems(Ctx);
            if (Ctx->Action==ACT_CHECK) OutputUnmatched(Ctx);
            break;

        case ACT_OTP:
            OTPProcess(Ctx);
            break;

        //Load matches to an mcached server
        case ACT_LOADMATCHES:
            ptr=GetVar(Ctx->Vars, "Memcached:Server");
            if (StrEnd(ptr)) printf("ERROR: No memcached server specified. Use the -memcached <server> command-line argument to specify one\n");
            else MatchesLoad(Ctx, FLAG_MEMCACHED);
            break;

        case ACT_FINDMATCHES_MEMCACHED:
        case ACT_FINDDUPLICATES:
            result=ProcessTargetItems(Ctx);
            break;

        case ACT_CHECK_XATTR:
            result=ProcessTargetItems(Ctx);
            break;

        case ACT_CHECK_MEMCACHED:
            result=ProcessTargetItems(Ctx);
            break;

        case ACT_CGI:
            CGIDisplayPage();
            break;

        case ACT_XDIALOG:
            XDialogFrontend(Ctx);
            break;

        case ACT_PRINTUSAGE:
            CommandLinePrintUsage();
            break;

        case ACT_SIGN:
            Ctx->Encoding = ENCODE_BASE64;
            for (i=1; i < argc; i++)
            {
                if (StrValid(argv[i]))
                {
                    if (StatFile(Ctx, argv[i],&Stat) != -1)
                    {
                        if (S_ISLNK(Stat.st_mode)) fprintf(stderr,"WARN: Not following symbolic link %s\n",argv[i]);
                        else HashratSignFile(argv[i],Ctx);
                    }
                    else fprintf(stderr,"ERROR: File '%s' not found\n",argv[i]);
                }
            }
            break;

        case ACT_CHECKSIGN:
            Ctx->Encoding |= ENCODE_BASE64;
            for (i=1; i < argc; i++)
            {
                if (StrValid(argv[i]))
                {
                    if (StatFile(Ctx, argv[i],&Stat) != -1)
                    {
                        if (S_ISLNK(Stat.st_mode)) fprintf(stderr,"WARN: Not following symbolic link %s\n",argv[i]);
                        else HashratCheckSignedFile(argv[i], Ctx);
                    }
                    else fprintf(stderr,"ERROR: File '%s' not found\n",argv[i]);
                }
            }
            break;
        }

        fflush(NULL);
        if (Ctx->Out) STREAMClose(Ctx->Out);
        if (Ctx->Aux) STREAMClose(Ctx->Aux);

        switch (Ctx->Action)
        {
        case ACT_HASH:
        case ACT_HASHDIR:
        case ACT_HASH_LISTFILE:

        case ACT_FINDMATCHES:
        case ACT_FINDMATCHES_MEMCACHED:
        case ACT_FINDDUPLICATES:
            //result==TRUE for these means 'yes we found something' or 'yes that worked'
            if (result==TRUE) exit(0);
            else exit(1);
            break;

        case ACT_CHECK:
        case ACT_CHECK_LIST:
        case ACT_CHECK_MEMCACHED:
        case ACT_CHECK_XATTR:
            //result==TRUE for these means 'CHECK FAILED'
            if (result==TRUE) exit(1);
            else exit(0);
            break;
        }
    }

    Destroy(Tempstr);

    exit(1);
}
