#if defined(ENABLE_WWW)

#include <string.h>
#include <time.h>
#include <sqlite3.h>
#include <libgen.h>
#include "bbs.h"
#include "../deps/hashids/hashids.h"


extern struct bbs_config conf;
extern struct user_record *gUser;
extern char * aha(char *input);

static char *www_decode(char *clean_url) {
    char *url = (char *)malloc(strlen(clean_url) + 1);
    int i;
    int j = 0;
    unsigned char c;
    if (clean_url == NULL) {
        free(url);
        return NULL;
    }

    for (i=0;i<strlen(clean_url);i++) {
        if (clean_url[i] == '%') {
            c = clean_url[i+1] * 16 + clean_url[i+2];
            url[j++] = (char)c;
            url[j] = '\0';
            i+=2;
        } else {
            url[j++] = clean_url[i];
            url[j] = '\0';
        }
    }

    return url;
}

static char *www_encode(char *url) {
    char *clean_url = (char *)malloc(strlen(url) * 3 + 1);
    int i;
    int j;

    if (url == NULL) {
        free(clean_url);
        return NULL;
    }

    j = 0;
    memset(clean_url, 0, strlen(url) * 3);

    for (i=0;i<strlen(url);i++) {
        if (isalnum(url[i]) || url[i] == '~' || url[i] == '.' || url[i] == '_') {
            sprintf(&clean_url[j], "%c", url[i]);
            j++;
        } else {
            sprintf(&clean_url[j], "%%%02X", url[i]);
            j+=3;
        }
    }

    return clean_url;
}

void www_expire_old_links() {
    char buffer[PATH_MAX];
	sqlite3 *db;
    sqlite3_stmt *res;
    int rc;
    char sql[] = "delete from wwwhash where expiry <= ?";
    char *ret;
    time_t now = time(NULL);

    snprintf(buffer, PATH_MAX, "%s/www_file_hashes.sq3", conf.bbs_path);

 	rc = sqlite3_open(buffer, &db);
	if (rc != SQLITE_OK) {
        dolog("Cannot open database: %s", sqlite3_errmsg(db));
        return;
    }
	sqlite3_busy_timeout(db, 5000);
    rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
    if (rc != SQLITE_OK) {
    	sqlite3_close(db);
		return;
    }
    sqlite3_bind_int(res, 1, now);
    sqlite3_step(res);
    sqlite3_finalize(res);
    sqlite3_close(db);   
}

int www_check_hash_expired(char *hash) {
    char buffer[PATH_MAX];
    sqlite3 *db;
    sqlite3_stmt *res;
    int rc;
    time_t now = time(NULL);
    char sql[] = "select id from wwwhash where hash = ? and expiry > ?";
    snprintf(buffer, PATH_MAX, "%s/www_file_hashes.sq3", conf.bbs_path);
	rc = sqlite3_open(buffer, &db);
	if (rc != SQLITE_OK) {
        return 1;
    }
	sqlite3_busy_timeout(db, 5000);
    rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
    if (rc != SQLITE_OK) {
    	sqlite3_close(db);
		return 0;
    }

    sqlite3_bind_text(res, 1, hash, -1, 0);
    sqlite3_bind_int(res, 2, now);

    if (sqlite3_step(res) == SQLITE_ROW) {
        sqlite3_finalize(res);
	    sqlite3_close(db);
        return 0;
    }
    sqlite3_finalize(res);
	sqlite3_close(db);
    return 1;
}

void www_add_hash_to_db(char *hash, time_t expiry) {
    char buffer[PATH_MAX];
	sqlite3 *db;
    sqlite3_stmt *res;
    int rc;
    char csql[] = "create table if not exists wwwhash (id INTEGER PRIMARY KEY, hash TEXT, expiry INTEGER)";
    char chsql[] = "select id from wwwhash where hash = ?";
    char usql[] = "update wwwhash SET expiry = ? WHERE hash = ?";
    char isql[] = "insert into wwwhash (hash, expiry) values(?, ?)";
    
    char *ret;
    char *err_msg = 0;

    snprintf(buffer, PATH_MAX, "%s/www_file_hashes.sq3", conf.bbs_path);

	rc = sqlite3_open(buffer, &db);
	if (rc != SQLITE_OK) {
        return;
    }
	sqlite3_busy_timeout(db, 5000);   

    rc = sqlite3_exec(db, csql, 0, 0, &err_msg);
    if (rc != SQLITE_OK) {

        dolog("SQL error: %s", err_msg);

        sqlite3_free(err_msg);
        sqlite3_close(db);
        return;
    }

    // first check if hash is in database
    rc = sqlite3_prepare_v2(db, chsql, -1, &res, 0);
    if (rc != SQLITE_OK) {
    	sqlite3_close(db);
		return;
    }
    sqlite3_bind_text(res, 1, hash, -1, 0);
    rc = sqlite3_step(res);
    if (rc == SQLITE_ROW) {
        // if so, update hash
        sqlite3_finalize(res);
        rc = sqlite3_prepare_v2(db, usql, -1, &res, 0);
        if (rc != SQLITE_OK) {
    	    sqlite3_close(db);
		    return;
        }
        sqlite3_bind_int(res, 1, expiry);
        sqlite3_bind_text(res, 2, hash, -1, 0);
        sqlite3_step(res);
        sqlite3_finalize(res);
        sqlite3_close(db);

        return;
    }
    // if not add hash
    sqlite3_finalize(res);
    rc = sqlite3_prepare_v2(db, isql, -1, &res, 0);
    if (rc != SQLITE_OK) {
    	sqlite3_close(db);
		return;
    }
    sqlite3_bind_text(res, 1, hash, -1, 0);
    sqlite3_bind_int(res, 2, expiry);
    sqlite3_step(res);
    sqlite3_finalize(res);
    sqlite3_close(db);
}

char *www_decode_hash(char *hash) {
    unsigned long long numbers[4];
    int dir, sub, fid, uid;
    hashids_t *hashids = hashids_init(conf.bbs_name);
    char buffer[PATH_MAX];
	sqlite3 *db;
    sqlite3_stmt *res;
    int rc;
    char sql[] = "select filename from files where approved = 1 and id = ?";
    char *ret;

    if (www_check_hash_expired(hash)) {
        return NULL;
    }

    if (hashids_decode(hashids, hash, numbers) != 4) {
        hashids_free(hashids);
        return NULL;
    }
    hashids_free(hashids);

    uid = (int)numbers[0];
    dir = (int)numbers[1];
    sub = (int)numbers[2];
    fid = (int)numbers[3];

    if (dir >= conf.file_directory_count || sub >= conf.file_directories[dir]->file_sub_count) {
       return NULL;
    }

    // get filename from database
    snprintf(buffer, PATH_MAX, "%s/%s.sq3", conf.bbs_path, conf.file_directories[dir]->file_subs[sub]->database);
	rc = sqlite3_open(buffer, &db);
	if (rc != SQLITE_OK) {
        return NULL;
    }
	sqlite3_busy_timeout(db, 5000);
    rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
    if (rc != SQLITE_OK) {
    	sqlite3_close(db);
		return NULL;
    }
    sqlite3_bind_int(res, 1, fid);
    if (sqlite3_step(res) == SQLITE_ROW) {
        ret = strdup(sqlite3_column_text(res, 0));
        sqlite3_finalize(res);
	    sqlite3_close(db);

        return ret;
    }
	sqlite3_finalize(res);
	sqlite3_close(db);
    return NULL;    
}

char *www_create_link(int dir, int sub, int fid) {
    char url[PATH_MAX];
    char *ret;
    char *hashid;
    int sizereq;
    time_t expiry;

    hashids_t *hashids = hashids_init(conf.bbs_name);

    sizereq = hashids_estimate_encoded_size_v(hashids, 4, (unsigned long long)gUser->id, (unsigned long long)dir, (unsigned long long)sub, (unsigned long long)fid);

    hashid = (char *)malloc(sizereq + 1);

    memset(hashid, 0, sizereq + 1);

    if (hashids_encode_v(hashids, hashid, 4, (unsigned long long)gUser->id, (unsigned long long)dir, (unsigned long long)sub, (unsigned long long)fid) == 0) {
        hashids_free(hashids);
        free(hashid);
        return NULL;
    }

    hashids_free(hashids);

    snprintf(url, PATH_MAX, "%sfiles/%s", conf.www_url, hashid);

    // add link into hash database
    expiry = time(NULL) + 86400;
    www_add_hash_to_db(hashid, expiry);

    free(hashid);

    ret = strdup(url);

    return ret;
}
char *www_files_display_listing(int dir, int sub) {
    char *page;
    int max_len;
    int len;
    char buffer[4096];
	char *sql = "select id, filename, description, size, dlcount, uploaddate from files where approved=1 ORDER BY filename";
    char *filename;
    char c;
    int size;
    char *aha_out;
    char *description;
	sqlite3 *db;
    sqlite3_stmt *res;
    int rc;
    int i;
    char *clean_url;

  	page = (char *)malloc(4096);
	max_len = 4096;
	len = 0;
	memset(page, 0, 4096);
	
	snprintf(buffer, 4096, "<div class=\"content-header\"><h2>Files: %s - %s</h2></div>\n", conf.file_directories[dir]->name, conf.file_directories[dir]->file_subs[sub]->name);
    if (len + strlen(buffer) > max_len - 1) {
		max_len += 4096;
		page = (char *)realloc(page, max_len);
	}
	strcat(page, buffer);
	len += strlen(buffer); 
    
	snprintf(buffer, 4096, "%s/%s.sq3", conf.bbs_path, conf.file_directories[dir]->file_subs[sub]->database);

	rc = sqlite3_open(buffer, &db);
	if (rc != SQLITE_OK) {
        free(page);
        return NULL;
    }
	sqlite3_busy_timeout(db, 5000);
    rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
    if (rc != SQLITE_OK) {
		sqlite3_close(db);
        free(page);
		return NULL;
    }
	snprintf(buffer, 4096, "<table class=\"fileentry\"><thead><tr><td>Filename</td><td>Size</td><td>Description</td></tr></thead><tbody>\n");
    if (len + strlen(buffer) > max_len - 1) {
		max_len += 4096;
		page = (char *)realloc(page, max_len);
	}
	strcat(page, buffer);
	len += strlen(buffer); 
    
	while (sqlite3_step(res) == SQLITE_ROW) {
        filename = strdup(sqlite3_column_text(res, 1));
        clean_url = www_encode(basename(filename));
        snprintf(buffer, 4096, "<tr><td class=\"filename\"><a href=\"%sfiles/areas/%d/%d/%s\">%s</a></td>", conf.www_url, dir, sub, basename(clean_url), basename(filename));
        free(clean_url);
        free(filename);
        if (len + strlen(buffer) > max_len - 1) {
		    max_len += 4096;
		    page = (char *)realloc(page, max_len);
	    }
	    strcat(page, buffer);
	    len += strlen(buffer); 

        size = sqlite3_column_int(res, 3);

        if (size > 1024 * 1024 * 1024) {
			size = size / 1024 / 1024 / 1024;
			c = 'G';
		} else if (size > 1024 * 1024) {
			size = size / 1024 / 1024;
			c = 'M';
		} else if (size > 1024) {
			size = size / 1024;
			c = 'K';
		} else {
			c = 'b';
		}

        snprintf(buffer, 4096, "<td class=\"filesize\">%d%c</td>", size, c);
        if (len + strlen(buffer) > max_len - 1) {
		    max_len += 4096;
		    page = (char *)realloc(page, max_len);
	    }
	    strcat(page, buffer);
	    len += strlen(buffer); 

        description = strdup((char *)sqlite3_column_text(res, 2));

        for (i=0;i<strlen(description);i++) {
            if (description[i] == '\n') {
                description[i] = '\r';
            }
        }

        snprintf(buffer, 4096, "<td class=\"filedesc\">");
        if (len + strlen(buffer) > max_len - 1) {
		    max_len += 4096;
		    page = (char *)realloc(page, max_len);
	    }
	    strcat(page, buffer);
	    len += strlen(buffer); 

		aha_out = aha(description);

		while (len + strlen(aha_out) > max_len - 1) {
			max_len += 4096;
			page = (char *)realloc(page, max_len);
		}	
		strcat(page, aha_out);
		len += strlen(aha_out);			
		
		free(aha_out);
		free(description);

        snprintf(buffer, 4096, "</td></tr>\n");
        if (len + strlen(buffer) > max_len - 1) {
		    max_len += 4096;
		    page = (char *)realloc(page, max_len);
	    }
	    strcat(page, buffer);
	    len += strlen(buffer); 

	}

    snprintf(buffer, 4096, "</tbody></table>\n");
    if (len + strlen(buffer) > max_len - 1) {
		max_len += 4096;
		page = (char *)realloc(page, max_len);
	}
	strcat(page, buffer);
	len += strlen(buffer); 

	sqlite3_finalize(res);
	sqlite3_close(db);

    return page;
}

char *www_files_areas() {
    char *page;
    int max_len;
    int len;
    char buffer[4096];
    int i;
    int j;

  	page = (char *)malloc(4096);
	max_len = 4096;
	len = 0;
	memset(page, 0, 4096);
	
	sprintf(buffer, "<div class=\"content-header\"><h2>File Directories</h2></div>\n");
    if (len + strlen(buffer) > max_len - 1) {
		max_len += 4096;
		page = (char *)realloc(page, max_len);
	}
	strcat(page, buffer);
	len += strlen(buffer); 

    for (i=0;i<conf.file_directory_count;i++) {
        if (conf.file_directories[i]->display_on_web) {
            sprintf(buffer, "<div class=\"conference-list-item\">%s</div>\n", conf.file_directories[i]->name);
            if (len + strlen(buffer) > max_len - 1) {
		        max_len += 4096;
		        page = (char *)realloc(page, max_len);
	        }
	        strcat(page, buffer);
	        len += strlen(buffer);             
            for (j=0;j<conf.file_directories[i]->file_sub_count;j++) {
                sprintf(buffer, "<div class=\"area-list-item\"><a href=\"%sfiles/areas/%d/%d/\">%s</a></div>\n", conf.www_url, i, j, conf.file_directories[i]->file_subs[j]->name);
                if (len + strlen(buffer) > max_len - 1) {
		            max_len += 4096;
		            page = (char *)realloc(page, max_len);
	            }
	            strcat(page, buffer);
	            len += strlen(buffer); 
            }
        }
    }

    return page;
}

char *www_files_get_from_area(int dir, int sub, char *clean_file) {
    char *sql = "SELECT filename FROM files WHERE approved=1 AND filename LIKE ? ESCAPE \"^\"";
    char *filenamelike;
	sqlite3 *db;
    sqlite3_stmt *res;
    int rc;
    char buffer[PATH_MAX];
    char *ret = NULL;
    int i;
    int extra = 0;
    int j;
    char *file;

    file = www_decode(clean_file);

    for (i=0;i<strlen(file);i++) {
        if (file[i] == '^' || file[i] == '%' || file[i] == '_') {
            extra++;
        }
    }

    filenamelike = (char *)malloc(strlen(file) + 3 + extra);

    i = 0;
    filenamelike[i++] = '%';
    filenamelike[i] = '\0';

    for (j = 0; j < strlen(file); j++) {
        switch(file[j]) {
            case '^':
                filenamelike[i++] = '^';
                filenamelike[i++] = '^';
                filenamelike[i] = '\0';
                break;
            case '_':
                filenamelike[i++] = '^';
                filenamelike[i++] = '_';
                filenamelike[i] = '\0';
                break;
            case '%':
                filenamelike[i++] = '^';
                filenamelike[i++] = '%';
                filenamelike[i] = '\0';
                break;
            default:
                filenamelike[i++] = file[j];
                filenamelike[i] = '\0';
                break;                
        }
    }

 //   sprintf(filenamelike, "%%/%s", file);

    free(file);

	snprintf(buffer, PATH_MAX, "%s/%s.sq3", conf.bbs_path, conf.file_directories[dir]->file_subs[sub]->database);

	rc = sqlite3_open(buffer, &db);
	if (rc != SQLITE_OK) {
        return NULL;
    }
	sqlite3_busy_timeout(db, 5000);
    rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
    if (rc != SQLITE_OK) {
		sqlite3_close(db);
		return NULL;
    }    
    sqlite3_bind_text(res, 1, filenamelike, -1, 0);

    rc = sqlite3_step(res);
    if (rc == SQLITE_ROW) {
        ret = strdup(sqlite3_column_text(res, 0));

    }

    free(filenamelike);
    sqlite3_finalize(res);
    sqlite3_close(db);
    return ret;
}

#endif
