/* (PD) 2001 The Bitzi Corporation
 * Please see file COPYING or http://bitzi.com/publicdomain 
 * for more info.
 *
 * $Id: main.c,v 1.3 2001/04/12 21:01:16 robert Exp $
 */
/*------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "bc_version.h"
#include "bitcollider.h"
#include "tigertree.h"
#include "sha1.h"
#include "mp3.h"
#include "id3.h"
#include "plugin_man.h"
#ifndef _WIN32
  #include "browser.h"
#else
  #include <windows.h>
  #include <shellapi.h>
#endif

/*------------------------------------------------------------------------- */
b_bool hashCheckPassed = false;

/*------------------------------------------------------------------------- */

#define BUFFER_LEN           4096
#define FIRST_N_HEX          20
#define FIRST_N_HEX_SIZE     (FIRST_N_HEX * sizeof(char) * 2)
#define MAX_ATTR_STRING_LEN  1024
#define DEFAULT_NUM_ATTRS    16
#define GROW_NUM_ATTRS       16
#define SHA_HEXSIZE          ((SHA_DIGESTSIZE * 2 * sizeof(char)) + 1)
#define TIGER_HEXSIZE        ((TIGERSIZE * 2 * sizeof(char)) + 1)
#define ONEK_SIZE            1025
#define EMPTY_SHA            "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709"
#define ONE_SHA              "356A192B7913B04C54574D18C28D46E6395428AB"
#define ONEK_SHA             "1009DE2EF6183BD6D8C08F1ED8DF4847E71F7ACB"
#define EMPTY_TIGER          "3293AC630C13F0245F92BBB1766E16167A4E58492DDE73F3"
#define ONE_TIGER            "1D573194A056EB3200F9D302900C843C3D41AB4ED06C03DF"
#define ONEK_TIGER           "D1593128F306DD1C2831459205F14CCB843E675F021F2EA2"

#ifdef _WIN32
#define DIR_SEP                     '\\'
#else
#define DIR_SEP                     '/'
#endif
#define DB printf("%s:%d\n", __FILE__, __LINE__);

/*------------------------------------------------------------------------- */
#define ERROR_FILENOTFOUND   "File not found or permission denied."
#define ERROR_MALLOCFAILED   "Failed to allocate memory."
#define ERROR_LAUNCHBROWSER  "Cannot launch web browser."
#define ERROR_TEMPFILEERR    "Cannot create a temorary file for the " \
                             "bitprint submission."
#define ERROR_HASHCHECK      "The hash functions compiled into this version " \
                             "of the bitcollider utility are faulty!!!"
#define WARNING_NOTMP3       "This is not an MP3 file. " \
                             "Skipping mp3 information."

/*------------------------------------------------------------------------- */

b_bool calculate_hashes(BitcolliderSubmission *submission,
                        FILE                  *source,
                        unsigned char         *sha1, 
                        unsigned char         *tigerTree,
                        mp3_info              *mp3Info,
                        PluginMethods         *methods,
                        Attribute            **attrList);
b_bool generate_first_n_hex(BitcolliderSubmission *submission,
                            FILE          *source, 
                            int            n, 
                            unsigned char *bitprint);
b_bool get_bitprint_data(BitcolliderSubmission *submission,
                         const char            *fileName, 
                         unsigned char         *sha1, 
                         unsigned char         *tigerTree,
                         unsigned char         *firstHex,
                         mp3_info              *mp3Info,
                         PluginMethods         *methods,
                         Attribute            **attrList);
void   convert_to_multiple_submission(BitcolliderSubmission *submission);
b_bool hash_sanity_check(void);
b_bool check_tigertree_hash(const char *result, unsigned char *data, int len);
b_bool check_sha1_hash(const char *result, unsigned char *data, int len);
void   set_error(BitcolliderSubmission *sub, const char *newError);
void   set_warning(BitcolliderSubmission *sub, const char *newError);
/*------------------------------------------------------------------------- */

Bitcollider *bitcollider_init(b_bool printDebugInfo)
{
    Bitcollider *bc;
#ifdef WIN32
    HKEY         hKey;
    DWORD        type;
#endif
    char         path[MAX_PATH], *ptr;
    int          total = 0;
    
    bc = init_plugins();

#ifndef WIN32
    /* Load the plugins from the build dir first if ./plugins exists */
    if (printDebugInfo)
        printf("Loading plugins from ./plugins:\n"); 
        
    total = load_plugins(bc, "./plugins", printDebugInfo);

    ptr = getenv("HOME");
    if (ptr)
    {
        sprintf(path, "%s/.bitcollider/plugins", ptr);
        if (printDebugInfo)
            printf("Loading plugins from %s:\n", path); 
        total += load_plugins(bc, path, printDebugInfo);
    }

    ptr = PREFIX"/lib/bitcollider/plugins";
#else
    
    path[0] = 0;
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Bitzi", 0, 
        KEY_READ, &hKey) != ERROR_SUCCESS)
    {
        /* No registry key set. Figure out where our plugin dir is and
           set that key to the registry. */
        set_plugin_dir(path);
        /*printf("No reg key set. path: '%s'\n", path);*/
    }
    else
    {
        int   ret;
        DWORD size = MAX_PATH;

        ret = RegQueryValueEx(hKey, "PluginDir", NULL, &type, 
                              path, &size);
        RegCloseKey(hKey);

        if (ret == ERROR_FILE_NOT_FOUND)
            set_plugin_dir(path);
        else
        if (ret != ERROR_SUCCESS)
            return bc;

        /*printf("Reg key set. Using path: '%s'\n", path); */
    }
    ptr = path;
#endif

    if (ptr)
    {
        if (printDebugInfo)
            printf("Loading plugins from %s:\n", ptr); 
        total += load_plugins(bc, ptr, printDebugInfo);
    }
    if (printDebugInfo)
        printf("Loaded %d plugins total.\n\n", total);
    
    return bc;
}

void bitcollider_shutdown(Bitcollider *bc)
{
    unload_plugins(bc);
    shutdown_plugins(bc);
}

BitcolliderSubmission *create_submission(Bitcollider *bc)
{
    BitcolliderSubmission *submission;

    submission = (BitcolliderSubmission *)malloc(sizeof(BitcolliderSubmission));
    if (submission == NULL)
       return NULL;

    memset(submission, 0, sizeof(BitcolliderSubmission));
    submission->bc = bc;

    if (!hashCheckPassed && hash_sanity_check())
    {
        set_error(submission, ERROR_HASHCHECK);
    }
    else
        hashCheckPassed = true;

    return submission;
}

b_bool analyze_file(BitcolliderSubmission *submission, 
                    const char            *fileName,
                    b_bool                 matchingExtsOnly)
{
    char                   shaDigest[SHA_HEXSIZE];
    char                   tigerTreeDigest[TIGER_HEXSIZE];
    char                   firstNHex[FIRST_N_HEX_SIZE + 1];
    char                   temp[MAX_ATTR_STRING_LEN], *ext;
    const char            *baseFileName;
    mp3_info              *mp3Info = NULL;
    PluginMethods         *methods = NULL;
    b_bool                 mp3Check = false;
    Attribute             *attrList = NULL, *attr;
    const char            *err;

    if (submission->bc->error)
    {
        free(submission->bc->error);
        submission->bc->error = NULL;
    }
    if (submission->bc->warning)
    {
        free(submission->bc->warning);
        submission->bc->warning = NULL;
    }

    if (!hashCheckPassed)
    {
        set_error(submission, ERROR_HASHCHECK);
        return false;
    }

    if (submission->fileName)
    {
       free(submission->fileName);
       submission->fileName = NULL;
    }

    if (submission->bc->exitNow)
    {
       return false;
    }

    submission->fileName = strdup(fileName);
    baseFileName = strrchr(fileName, DIR_SEP);
    if (baseFileName)
       baseFileName++;
    else
       baseFileName = fileName;

    ext = strrchr(baseFileName, '.');
    if (!submission->checkAsExt && ext && strcasecmp(ext, ".mp3") == 0)
    {
       mp3Check = true;
    }

    /* Load the plugin methods for this file */
    ext = (submission->checkAsExt) ? submission->checkAsExt : ext;
    if (ext)
       methods = get_plugin(submission->bc, ext);

    /* If we're only supposed to work on files with known extensions,
       and we don't know this extension, bail. */
    if (matchingExtsOnly && methods == NULL && mp3Check == false)
    {
       if (submission->bc->progressCallback && !submission->bc->preview)
           submission->bc->progressCallback(0, submission->fileName, 
                                            "skipped.");
       return false;
    }

    /* If we're in preview mode, return now */
    if (submission->bc->preview)
       return true;

    if (mp3Check)
       mp3Info = malloc(sizeof(mp3_info));

    if (!get_bitprint_data(submission, fileName, shaDigest, tigerTreeDigest, 
                           firstNHex, mp3Info, methods, &attrList))
    {
       if (mp3Info)
          free(mp3Info);

       return false;
    }

    /* If this is the first bit print, add a header to the attrs */
    if (submission->numBitprints == 0)
    {
       get_agent_string(temp);
       add_attribute(submission, "head.agent", temp);

       sprintf(temp, "S%s", BC_SUBMITSPECVER);
       add_attribute(submission, "head.version", temp);
    }

    /* If this is the second bitprint, convert the single submission
       to a multiple submission */
    if (submission->numBitprints == 1)
       convert_to_multiple_submission(submission);

    sprintf(temp, "%s%s", shaDigest, tigerTreeDigest);
    add_attribute(submission, "bitprint", temp);

    sprintf(temp, "%lu", submission->fileSize);
    add_attribute(submission, "tag.file.length", temp);
    add_attribute(submission, "tag.file.first20", firstNHex);
    add_attribute(submission, "tag.filename.filename", baseFileName);

    /* Check to make sure that we carried out the mp3 check, and
       make sure that an audioSha was generated. If not, then the
       mp3 routines deemed that this was not a valid mp3 file
       and we should skip the mp3 tag generation */
    if (mp3Check && mp3Info->audioSha[0] == 0)
    {
       set_warning(submission, WARNING_NOTMP3);
       mp3Check = 0;
    }
    else
    if (mp3Check)
    {
       char audioShaDigest[SHA_HEXSIZE];
       ID3Info *info;

       convert_to_hex(mp3Info->audioSha, SHA_DIGESTSIZE, audioShaDigest);
       sprintf(temp, "%d", mp3Info->duration);
       add_attribute(submission, "tag.mp3.duration", temp);
       if (mp3Info->bitrate == 0)
       {
          sprintf(temp, "%d", mp3Info->avgBitrate);
          add_attribute(submission, "tag.mp3.bitrate", temp);
          add_attribute(submission, "tag.mp3.vbr", "y");
       }
       else
       {
          sprintf(temp, "%d", mp3Info->bitrate);
          add_attribute(submission, "tag.mp3.bitrate", temp);
       }
       sprintf(temp, "%d", mp3Info->samplerate);
       add_attribute(submission, "tag.mp3.samplerate", temp);
       add_attribute(submission, "tag.mp3.stereo", 
                                  mp3Info->stereo ? "y" : "n");
       add_attribute(submission, "tag.mp3.audio_sha1", audioShaDigest);
       free(mp3Info);

       info = read_ID3_tag(fileName);
       if (info)
       {
           if (info->encoder)
               add_attribute(submission, "tag.mp3.encoder", info->encoder);
           if (info->title)
               add_attribute(submission, "tag.audiotrack.title", info->title);
           if (info->artist)
               add_attribute(submission, "tag.audiotrack.artist", info->artist);
           if (info->album)
               add_attribute(submission, "tag.audiotrack.album", info->album);
           if (info->tracknumber)
               add_attribute(submission, "tag.audiotrack.tracknumber", 
                             info->tracknumber);
           if (info->genre && atoi(info->genre) >= 0)
               add_attribute(submission, "tag.id3genre.genre", info->genre);
           if (info->year)
               add_attribute(submission, "tag.audiotrack.year", info->year);
           delete_ID3_tag(info);
       }
    }

    /* If a plugin was selected, but no memory analyze functions were
       provided, call the plugin's file analyze methods */
    if (methods && methods->mem_analyze_init == NULL && !submission->bc->exitNow)
       attrList = methods->file_analyze(fileName);

    if (attrList)
    {
       for(attr = attrList; attr->key; attr++)
          add_attribute(submission, attr->key, attr->value);

        methods->free_attributes(attrList);
    }

    /* If we selected a plugin, but the no attributes were returned,
       then check for an error from the plugin */
    if (methods && !attrList)
    {
       /* An error from a plugin should be considered a warning
          since its not fatal to the execution of the bitcollider */
       err = methods->get_error();
       if (err)
           set_warning(submission, err);
    }

    if (submission->bc->progressCallback && 
        !submission->bc->preview && 
        !submission->bc->exitNow)
       submission->bc->progressCallback(100, NULL, "ok.");

    submission->numBitprints++;

    return true;
}

void set_auto_submit(BitcolliderSubmission *tag,
                     b_bool                 autoSubmit)
{
    tag->autoSubmit = autoSubmit;
}

void set_check_as(BitcolliderSubmission *tag,
                  const char            *extension)
{
    if (tag->checkAsExt)
       free(tag->checkAsExt);

    tag->checkAsExt = strdup(extension);
}

void set_progress_callback(Bitcollider *bc,
                           void (*callback)(int, const char *, const char *))
{
    bc->progressCallback = callback;
}

void set_preview(Bitcollider *bc, b_bool preview)
{
    bc->preview = preview;
}

void set_exit(Bitcollider *bc, b_bool exitNow)
{
    if (bc->progressCallback) 
       bc->progressCallback(-2, NULL, "operation cancelled.");
    bc->exitNow = exitNow;
}

int get_num_bitprints(BitcolliderSubmission *sub)
{
    return sub->numBitprints;
}

char *get_error(Bitcollider *bc)
{
    return bc->error;
}

void set_error(BitcolliderSubmission *sub, const char *newError)
{
    if (sub->bc->error)
        free(sub->bc->error);
    sub->bc->error = strdup(newError);
}

char *get_warning(Bitcollider *bc)
{
    return bc->warning;
}

void set_warning(BitcolliderSubmission *sub, const char *newWarning)
{
    if (sub->bc->warning)
        free(sub->bc->warning);
    sub->bc->warning = strdup(newWarning);
}

void delete_submission(BitcolliderSubmission *submission)
{
    int i;

    for(i = 0; i < submission->numItems; i++)
    {
        free(submission->attrList[i]->key);
        free(submission->attrList[i]->value);
        free(submission->attrList[i]);
    }

    if (submission->fileName)
       free(submission->fileName);
    free(submission->attrList);
    free(submission);
}

void convert_to_multiple_submission(BitcolliderSubmission *submission)
{
    int   i;
    char *temp;

    for(i = 0; i < submission->numItems; i++)
    {
        if (strncmp("head.version", submission->attrList[i]->key, 12) == 0)
        {
            submission->attrList[i]->value[0] = 'M';
            continue; 
        }
        if (strncmp("head.", submission->attrList[i]->key, 5) == 0)
            continue; 

        temp = malloc(strlen(submission->attrList[i]->key) + 3);
        sprintf(temp, "0.%s", submission->attrList[i]->key);
        free(submission->attrList[i]->key);
        submission->attrList[i]->key = temp;
    }
}

void add_attribute(BitcolliderSubmission *submission, 
                   const char *key,
                   const char *value)
{
    int   i;
    char *temp = NULL;

    /* Check to see if the attr list already exists */
    if (submission->attrList == NULL)
    {
        submission->attrList = (Attribute **)malloc(sizeof(Attribute *) * 
                                                DEFAULT_NUM_ATTRS);
        memset(submission->attrList, 0, sizeof(submission->attrList));
        submission->numItems = 0;
        submission->numAllocated = DEFAULT_NUM_ATTRS;
    }

    /* Do we have enough space for another attr? If not, grow the array */
    if (submission->numItems == submission->numAllocated)
    {
        submission->numAllocated += GROW_NUM_ATTRS;
        submission->attrList = (Attribute **)realloc(submission->attrList,
                                                 sizeof(Attribute *) * 
                                                 submission->numAllocated);
        memset(&submission->attrList[submission->numItems], 
               0, sizeof(Attribute *) * GROW_NUM_ATTRS);
    }

    /* If this is a multiple tag submission, prepend the sequence number
       to the key */
    if (submission->numBitprints > 0)
    {
        temp = malloc(strlen(key) + 16);
        sprintf(temp, "%d.%s", submission->numBitprints, key);
        key = temp;
    }

    /* Check to make sure we don't already have this attr */
    for(i = 0; i < submission->numItems; i++)
    {
        if (strcmp(key, submission->attrList[i]->key) == 0)
            return; 
    }

    submission->attrList[submission->numItems] = 
                (Attribute *)malloc(sizeof(Attribute));
    submission->attrList[submission->numItems]->key = strdup(key);
    submission->attrList[submission->numItems]->value = strdup(value);
    submission->numItems++;

    if (temp)
       free(temp);
}

const char *get_attribute(BitcolliderSubmission *submission, 
                          const char *key)
{
    int i;

    for(i = 0; i < submission->numItems; i++)
    {
       if (strcmp(submission->attrList[i]->key, key) == 0)
           return submission->attrList[i]->value;
    }

    return NULL;
}

b_bool get_bitprint_data(BitcolliderSubmission *submission,
                         const char            *fileName, 
                         unsigned char         *sha1, 
                         unsigned char         *tigerTree,
                         unsigned char         *firstHex,
                         mp3_info              *mp3Info,
                         PluginMethods         *methods,
                         Attribute            **attrList)     
{
    FILE  *source;
    b_bool   ret;

    source = fopen(fileName, "rb");
    if (source == NULL)
    {
       set_error(submission, ERROR_FILENOTFOUND);
       return false;
    }

    fseek(source, 0, SEEK_END);
    submission->fileSize = ftell(source);
    fseek(source, 0, SEEK_SET);

    ret = calculate_hashes(submission, source, sha1, tigerTree,  
                           mp3Info, methods, attrList);
    if (ret)
        ret = generate_first_n_hex(submission, source, FIRST_N_HEX, firstHex);

    fclose(source);
    return ret;
}

b_bool calculate_hashes(BitcolliderSubmission *submission,
                        FILE                  *source,
                        unsigned char         *sha1, 
                        unsigned char         *tigerTree,
                        mp3_info              *mcontext,
                        PluginMethods         *methods,
                        Attribute            **attrList)     
{
    TT_CONTEXT      tcontext;
    SHA_INFO        scontext;
    unsigned char  *buffer;
    int             bytes;
    unsigned char   tigerHash[TIGERSIZE];
    unsigned char   shaHash[SHA_DIGESTSIZE];
    b_bool          ret = true;
    Context         context = NULL;

    tt_init(&tcontext);
    sha_init(&scontext);
    if (mcontext)
       mp3_init(mcontext);
    if (methods && methods->mem_analyze_init)
       context = methods->mem_analyze_init();

    buffer = (unsigned char*)malloc(BUFFER_LEN);
    if (buffer == NULL)
    {
       set_error(submission, ERROR_MALLOCFAILED);
       return false;
    }
   
    submission->percentComplete = 0;
    if (submission->bc->progressCallback && !submission->bc->preview)
        submission->bc->progressCallback(0, submission->fileName, NULL);

    fseek(source, 0, SEEK_SET);
    for(;;)
    {
        if (submission->bc->exitNow)
           return false; 

        bytes = fread(buffer, 1, BUFFER_LEN, source);
        if (bytes <= 0)
        {
           ret = feof(source);
           break;
        }

        tt_update(&tcontext, buffer, bytes);
        sha_update(&scontext, buffer, bytes);
        if (mcontext)
           mp3_update(mcontext, buffer, bytes);
        if (methods && methods->mem_analyze_update)
           methods->mem_analyze_update(context, buffer, bytes);

        if (submission->bc->progressCallback && !submission->bc->preview)
        {
           int          percentComplete;

           percentComplete = ((ftell(source) * 100) / submission->fileSize);
           if (percentComplete != submission->percentComplete)
           {
               submission->bc->progressCallback(percentComplete, NULL, NULL);
               submission->percentComplete = percentComplete;
           }
        }
    }
    submission->percentComplete = 100;

    free(buffer);

    tt_digest(&tcontext, tigerHash);
    sha_final(shaHash, &scontext);
    if (mcontext)
        mp3_final(mcontext);
    if (methods && methods->mem_analyze_final)
        *attrList = methods->mem_analyze_final(context);

    convert_to_hex(tigerHash, TIGERSIZE, tigerTree);
    convert_to_hex(shaHash, SHA_DIGESTSIZE, sha1);

    return ret;
}

b_bool generate_first_n_hex(BitcolliderSubmission *submission,
                            FILE                  *source, 
                            int                    n, 
                            unsigned char         *bits)
{
    unsigned char *buffer;

    buffer = (unsigned char*)malloc(n);
    if (buffer == NULL)
    {
       set_error(submission, ERROR_MALLOCFAILED);
       return false;
    }
   
    fseek(source, 0, SEEK_SET);
    n = fread(buffer, sizeof(unsigned char), n, source);
    if (n < 0)
    {
        free(buffer);
        return false;
    }

    bits[0] = 0;
    convert_to_hex(buffer, n, bits);

    free(buffer);

    return true;
}

void convert_to_hex(const unsigned char *buffer, int size, char *hexBuffer)
{
    int i;

    for(i = 0; i < size; i++)
    {
        sprintf(hexBuffer + (i * sizeof(char) * 2), "%02X", buffer[i] & 0xFF);
    }
}

b_bool submit_submission(BitcolliderSubmission *submission, 
                         const char *url, 
                         BrowserEnum browser)
{
    FILE *output;
    int   i;
    char  tempFile[MAX_PATH];
    b_bool  ret;

    if (submission->numBitprints == 0)
    {
        set_error(submission, "The submission contained no bitprints.");
        return false;
    }

#ifdef _WIN32
	 GetTempPath(MAX_PATH, tempFile);
    strcat(tempFile, "bitprint.htm");
#else
    strcpy(tempFile, "/tmp/bitprint.html");
#endif
    output = fopen(tempFile, "wb");
    if (output == NULL)
    {
       set_error(submission, ERROR_TEMPFILEERR);
       return false;
    }

    fprintf(output, "<HTML>\n<HEAD>\n<TITLE>\n");
    if (submission->numBitprints == 1)
       fprintf(output, "Bitprint Submission %s\n", submission->fileName);
    else
       fprintf(output, "Multiple [%d] Bitprint Submission\n", submission->numBitprints);
    fprintf(output, "</TITLE>\n</HEAD>\n");
    if (submission->autoSubmit)
       fprintf(output, "<BODY onLoad=\"document.forms[0].submit()\">\n");
    else
       fprintf(output, "<BODY>\n");
    fprintf(output, "<FORM method=post action=\"%s\">\n", 
                     (url == NULL) ? SUBMIT_URL : url);
    fprintf(output, "<PRE>\n");
   
    for(i = 0; i < submission->numItems; i++)
    {
        fprintf(output, "%s=<INPUT TYPE=\"hidden\" ",
                submission->attrList[i]->key);
        fprintf(output, "NAME=\"%s\" VALUE=\"%s\">%s\n",
                submission->attrList[i]->key,
                submission->attrList[i]->value,
                submission->attrList[i]->value);
    }
    fprintf(output, "<INPUT TYPE=\"submit\" NAME=\"Submit\" VALUE=\"Submit\">\n");
    fprintf(output, "</PRE>\n</FORM>\n</BODY>\n</HTML>\n");
    fclose(output);

#ifdef _WIN32
    {
        char url[MAX_PATH], *colon;


        colon = strchr(tempFile, ':');
        if (*colon)
           *colon = '|';
        sprintf(url, "file://%s", tempFile);

        //Have Windows launch the default HTML viewer on the tempfile
        ret = (int)ShellExecute(NULL,"open",url,NULL,NULL,SW_SHOWNORMAL);
        ret = ret > 32;
    }
#else
    ret = launch_browser(tempFile, browser);
#endif
    if (!ret)
       set_error(submission, ERROR_LAUNCHBROWSER);

    return ret;
}

void print_submission(BitcolliderSubmission *submission)
{
    int i;

    printf("\n");
    for(i = 0; i < submission->numItems; i++)
    {
        printf("%s=%s\n", 
                submission->attrList[i]->key,
                submission->attrList[i]->value);
    }
}

b_bool hash_sanity_check(void)
{
    b_bool         ret;
    unsigned char *data; 


    ret = check_tigertree_hash(EMPTY_TIGER, "", 0);
    ret += check_sha1_hash(EMPTY_SHA, "", 0);
    ret += check_tigertree_hash(ONE_TIGER, "1", 1);
    ret += check_sha1_hash(ONE_SHA, "1", 1);

    data = malloc(ONEK_SIZE);
    memset(data, 'a', ONEK_SIZE);
    ret += check_tigertree_hash(ONEK_TIGER, data, ONEK_SIZE);
    ret += check_sha1_hash(ONEK_SHA, data, ONEK_SIZE);
    free(data);

    return ret;
}

/* NOTE: This function returns true if it failed the check! */
b_bool check_tigertree_hash(const char *result, unsigned char *data, int len)
{
    TT_CONTEXT      tcontext;
    unsigned char   tigerTreeHash[TIGERSIZE];
    char            tigerTreeDigest[TIGER_HEXSIZE];

    tt_init(&tcontext);
    tt_update(&tcontext, data, len);
    tt_digest(&tcontext, tigerTreeHash);
    convert_to_hex(tigerTreeHash, TIGERSIZE, tigerTreeDigest);

    if (strcmp(tigerTreeDigest, result))
    {
        fprintf(stderr, "        tigertree: '%s' [%d]\n", 
                tigerTreeDigest, len);
        fprintf(stderr, "correct tigertree: '%s' [%d]\n", 
                result, len);
        return true;
    }
    return false;
}

/* NOTE: This function returns true if it failed the check! */
b_bool check_sha1_hash(const char *result, unsigned char *data, int len)
{
    SHA_INFO        scontext;
    unsigned char   shaHash[SHA_DIGESTSIZE];
    char            shaDigest[SHA_HEXSIZE];

    sha_init(&scontext);
    sha_update(&scontext, data, len);
    sha_final(shaHash, &scontext);
    convert_to_hex(shaHash, SHA_DIGESTSIZE, shaDigest);

    if (strcmp(shaDigest, result))
    {
        fprintf(stderr, "              sha: '%s' [%d]\n", shaDigest, len);
        fprintf(stderr, "      correct sha: '%s' [%d]\n", result, len);
        return true;
    }
    return false;
}

void get_agent_string(char *agentString)
{
    sprintf(agentString, "%s/%s (%s)", BC_AGENTNAME, BC_VERSION, 
                                       BC_AGENTBUILD);
}

#ifdef _WIN32
/* We get to write this function becuase win95 does not 
   support this function! Thanks uncle Bill!!
*/
void getLongPathName(const char *shortName, int len, char *longName)
{
    WIN32_FIND_DATA  FindFileData;
    HANDLE           findHandle;
    char             drive[10], path[MAX_PATH];

    findHandle = FindFirstFile(shortName, &FindFileData);
    if (findHandle != INVALID_HANDLE_VALUE)
    {
        _splitpath(shortName, drive, path, NULL, NULL);
        sprintf(longName, "%s%s%s", drive, path, FindFileData.cFileName);
        FindClose(findHandle);
    }
    else
    {   /* Hmmm. I guess we're screwed. */
        longName[0] = 0;
    }
}
#endif
