/*
 * libkarma/util.c
 *
 * Copyright (c) Frank Zschockelt <libkarma@freakysoft.de> 2004
 *
 * char *simple_itoa was borrowed from boa [http://www.boa.org]
 *
 * You may distribute and modify this program under the terms of 
 * the GNU GPL, version 2 or later.
 * 
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#ifdef __linux__
#include <endian.h>
#endif
#ifdef __APPLE__
#  define __BIG_ENDIAN __BIG_ENDIAN__
#  if __LITTLE_ENDIAN__ 
#    define __BYTE_ORDER __LITTLE_ENDIAN__
#  else
#    define __BYTE_ORDER __BIG_ENDIAN
#  endif
#endif
#ifdef __NetBSD__
#include <sys/endian.h>
#define __BYTE_ORDER _BYTE_ORDER
#define __BIG_ENDIAN _BIG_ENDIAN
#endif

#include "lkarma.h"
#include "md5.h"
#include "util.h"
#include "properties.h"


/*copied from the boa webserver*/
char *simple_itoa(unsigned int i)
{
    /* 21 digits plus null terminator, good for 64-bit or smaller ints
     * for bigger ints, use a bigger buffer!
     *
     * 4294967295 is, incidentally, MAX_UINT (on 32bit systems at this time)
     * and is 10 bytes long
     */
    static char local[22];
    char *p = &local[21];
    *p-- = '\0';
    do{
        *p-- = '0' + i%10;
        i/=10;
    }while(i>0);
    return p+1;
}

/* returns a string for $HOME/.openrio/pearl-$serial/$*fname */
char * lk_path_string(char * file)
{
    int len;
    char * str, * home;
    home=strdup(getenv("HOME"));
    /*24=strlen("/.openrio/pearl-0123456/")*/
    len=strlen(home)+24+strlen(file)+1;
    str=malloc(len);
    snprintf(str, len, "%s/.openrio/pearl-%d/%s", home, serial, file);
                                                       /*cut the leading zeros*/
    return str;
}

/* Recursively create directories in a path. The last element is assumed to
   be a filename, and simply ignored. Code based on create_bittorrent_path() */
int mk_path(const char *pathname)
{
    int pos, ret;
    char *path;
    unsigned char separator;
    
    path=strdup(pathname);
    if (!*path) return -1;
    
    for (pos = 1; path[pos]; pos++) {
        separator = path[pos];
        if (separator != '/')
            continue;
        path[pos] = 0;
        ret = mkdir(path, 0700); /*S_IREAD | S_IWRITE | S_IEXEC);*/
        path[pos] = separator;
        if (ret < 0 && errno != EEXIST){
            free(path);
            lk_errors_set(E_MKDIR);
            return -1;
        }
    }
    free(path);
    return 0;
}

/*
( ((x) & 0xff000000) >> 24) 
| ((x) & 0x00ff0000) >>  8) 
| ((x) & 0x0000ff00) <<  8) 
| ((x) & 0x000000ff) << 24)
*/
uint32_t lk_htorl(uint32_t hostlong)
{
#if __BYTE_ORDER==__BIG_ENDIAN
    return (hostlong>>24) | 
        ((hostlong&0xff0000)>>8) | 
        ((hostlong&0xff00)<<8) | 
        (hostlong<<24);
#else
    return hostlong;
#endif
}

uint32_t lk_rtohl(uint32_t hostlong)
{
    return lk_htorl(hostlong);
}

/*
( (((x) & 0xff00000000000000ull) >> 56)
| (((x) & 0x00ff000000000000ull) >> 40)                             
| (((x) & 0x0000ff0000000000ull) >> 24)                                
| (((x) & 0x000000ff00000000ull) >> 8)                                 
| (((x) & 0x00000000ff000000ull) << 8)                                
| (((x) & 0x0000000000ff0000ull) << 24)                              
| (((x) & 0x000000000000ff00ull) << 40)                              
| (((x) & 0x00000000000000ffull) << 56))
*/
uint64_t lk_htorll(uint64_t hostlong)
{
#if __BYTE_ORDER==__BIG_ENDIAN
    return (hostlong>>56) | 
        ((hostlong&0xff000000000000ULL)>>40) |
        ((hostlong&0xff0000000000ULL)>>24) |
        ((hostlong&0xff00000000ULL)>>8) |
        ((hostlong&0xff000000ULL)<<8) |
        ((hostlong&0xff0000ULL)<<24) |
        ((hostlong&0xff00ULL)<<40) |
         (hostlong<<56);
#else
    return hostlong;
#endif
}
        
uint64_t lk_rtohll(uint64_t hostlong)
{
    return lk_htorll(hostlong);
}

/*
     {thanks Cliff L. Biffle...}
     computes the RID of a file
     rid algorithm pseudocode:
     if(length of file <= 64k) 
         return MD5(file);
     else
         A = MD5(first 64k);
         B = MD5(last 64k);
         C = MD5(64k starting at (length - 64k)/2);
         return A ^ B ^ C; 
     
     oggs,flacs: offset=0 length=length of file
     mp3s: offset=after the tags length=length of file -128
*/
#define RID_BLOCKS 65536
unsigned char * lk_generate_rid(int file, int offset, int length)
{
    static unsigned char rid_output[33];
    int i;
    uint32_t size;
    unsigned char cache[RID_BLOCKS];
    unsigned char md5output[16], b_md5[16], c_md5[16];
    unsigned char *ret;
    md5_context ctx;
    
    if(lseek(file, offset, SEEK_SET)==-1) return NULL;
    size=read(file, &cache, RID_BLOCKS);
    
    md5_starts(&ctx);
    if(length-offset < RID_BLOCKS){
        md5_update(&ctx, (uint8_t *)&cache, size);
        md5_finish(&ctx, md5output);
    }else{
        md5_update(&ctx, cache, RID_BLOCKS);
        md5_finish(&ctx, md5output);
        lseek(file, length-RID_BLOCKS, SEEK_SET);
        read(file, &cache, RID_BLOCKS);
        md5_starts(&ctx);
        md5_update(&ctx, cache, RID_BLOCKS);
        md5_finish(&ctx, b_md5);
        lseek(file, (offset + length - RID_BLOCKS)/2, SEEK_SET);
        size=read(file, &cache, RID_BLOCKS);
        md5_starts(&ctx);
        md5_update(&ctx, cache, size);
        md5_finish(&ctx, c_md5);
        for(i=0; i < 16; i++)
            md5output[i]=md5output[i]^b_md5[i]^c_md5[i];
    }
    for(i=0; i<16;i++){
        if((md5output[i]>>4)<10) rid_output[i*2]=(md5output[i]>>4)+'0';
        else rid_output[i*2]=(md5output[i]>>4)+87;
        if((md5output[i]%16)<10)
             rid_output[i*2+1]=(md5output[i]%16)+'0';
        else rid_output[i*2+1]=(md5output[i]%16)+87;
    }
    rid_output[32]='\0';
    ret=&rid_output[0];
    return ret;
}

int lk_is_karma_mountpoint(const char *mountpoint)
{
    char *str = NULL;
    int len, ret;
    struct stat st;
    len = strlen(mountpoint);
    str = malloc(len+strlen(RK_SMALLDB)+1);
    if (!str)
        return -1;
    memcpy(str, mountpoint, len);
    memcpy(str+len, RK_SMALLDB, strlen(RK_SMALLDB)+1);
    ret = stat(str, &st);
    if (ret != 0 || S_ISREG(st.st_mode) == 0) {
        lk_errors_set(E_NOSMALLDB);
        goto err;
    }
    memcpy(str+len, "/fids0", strlen("/fids0")+1);
    ret = stat(str, &st);
    if (ret != 0 || S_ISDIR(st.st_mode) == 0) {
        lk_errors_set(E_NOMOUNT);
        goto err;
    }
    free(str);
    return 0;
  err:
    if (str)
        free(str);
    return -1;
}
