552 lines
14 KiB
C
552 lines
14 KiB
C
#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
|