429 lines
12 KiB
C
429 lines
12 KiB
C
/*
|
|
* Copyright (c) 2014 Håvard Pettersson <haavard.pettersson@gmail.com>
|
|
*
|
|
* This file is part of Tox-WeeChat.
|
|
*
|
|
* Tox-WeeChat is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Tox-WeeChat is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Tox-WeeChat. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <weechat/weechat-plugin.h>
|
|
#include <tox/tox.h>
|
|
#include <sqlite3.h>
|
|
|
|
#include "twc.h"
|
|
#include "twc-list.h"
|
|
#include "twc-profile.h"
|
|
#include "twc-friend-request.h"
|
|
|
|
#include "twc-sqlite.h"
|
|
|
|
sqlite3 *twc_sqlite_db = NULL;
|
|
struct t_twc_list *twc_sqlite_statements = NULL;
|
|
|
|
// TODO: move to config
|
|
#define TWC_SQLITE_PATH "%h/tox/data.db"
|
|
|
|
#define TWC_SQLITE_DEBUG_RC(rc, expected_rc) \
|
|
if (rc != expected_rc) \
|
|
weechat_printf(NULL, \
|
|
"%s%s: SQLite error in %s: error code %d", \
|
|
weechat_prefix("error"), weechat_plugin->name, \
|
|
__func__, rc); \
|
|
|
|
/**
|
|
* Create or reset an SQLite statement.
|
|
*/
|
|
#define TWC_SQLITE_STMT(statement, statement_str) \
|
|
static sqlite3_stmt *statement = NULL; \
|
|
if (!statement) \
|
|
{ \
|
|
int rc = sqlite3_prepare_v2(twc_sqlite_db, \
|
|
statement_str, \
|
|
strlen(statement_str) + 1, \
|
|
&statement, \
|
|
NULL); \
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_OK); \
|
|
if (rc != SQLITE_OK) \
|
|
statement = NULL; \
|
|
else \
|
|
twc_list_item_new_data_add(twc_sqlite_statements, statement); \
|
|
} \
|
|
else \
|
|
{ \
|
|
sqlite3_reset(statement); \
|
|
}
|
|
|
|
/**
|
|
* Return the full path to our SQLite database file. Must be freed.
|
|
*/
|
|
char *
|
|
twc_sqlite_db_path()
|
|
{
|
|
const char *weechat_dir = weechat_info_get("weechat_dir", NULL);
|
|
return weechat_string_replace(TWC_SQLITE_PATH, "%h", weechat_dir);
|
|
}
|
|
|
|
/**
|
|
* Initialize profile table. Return 0 on success, -1 on errorh
|
|
*/
|
|
int
|
|
twc_sqlite_init_profiles()
|
|
{
|
|
TWC_SQLITE_STMT(statement,
|
|
"CREATE TABLE IF NOT EXISTS profiles ("
|
|
"id INTEGER PRIMARY KEY,"
|
|
"tox_id BLOB UNIQUE NOT NULL"
|
|
")");
|
|
|
|
int rc = sqlite3_step(statement);
|
|
if (rc != SQLITE_DONE)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Initialize friend request table. Return 0 on success, -1 on error.
|
|
*/
|
|
int
|
|
twc_sqlite_init_friend_requests()
|
|
{
|
|
TWC_SQLITE_STMT(statement,
|
|
"CREATE TABLE IF NOT EXISTS friend_requests ("
|
|
"id INTEGER PRIMARY KEY,"
|
|
"tox_id BLOB NOT NULL,"
|
|
"message TEXT,"
|
|
"profile_id INTEGER NOT NULL,"
|
|
"FOREIGN KEY(profile_id) REFERENCES profiles(id) ON DELETE CASCADE,"
|
|
"UNIQUE(tox_id, profile_id)"
|
|
")");
|
|
int rc = sqlite3_step(statement);
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_DONE)
|
|
if (rc != SQLITE_DONE)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Add a profile, if it does not exist.
|
|
*/
|
|
int
|
|
twc_sqlite_add_profile(struct t_twc_profile *profile)
|
|
{
|
|
TWC_SQLITE_STMT(statement,
|
|
"INSERT OR IGNORE INTO profiles (tox_id)"
|
|
"VALUES (?)");
|
|
|
|
uint8_t tox_id[TOX_FRIEND_ADDRESS_SIZE];
|
|
tox_get_address(profile->tox, tox_id);
|
|
sqlite3_bind_blob(statement, 1,
|
|
tox_id, TOX_CLIENT_ID_SIZE,
|
|
NULL);
|
|
|
|
int rc = sqlite3_step(statement);
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_DONE)
|
|
if (rc != SQLITE_DONE)
|
|
{
|
|
weechat_printf(NULL, "sqlite error in %s: %d", __func__, rc);
|
|
return -1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get the rowid of a profile. Returns true on success, false on error (e.g.
|
|
* not found).
|
|
*/
|
|
bool
|
|
twc_sqlite_profile_id(struct t_twc_profile *profile, int64_t *out)
|
|
{
|
|
if (!(profile->tox))
|
|
return false;
|
|
|
|
TWC_SQLITE_STMT(statement,
|
|
"SELECT id FROM profiles WHERE tox_id == ?");
|
|
|
|
uint8_t tox_id[TOX_FRIEND_ADDRESS_SIZE];
|
|
tox_get_address(profile->tox, tox_id);
|
|
sqlite3_bind_blob(statement, 1,
|
|
tox_id, TOX_CLIENT_ID_SIZE,
|
|
NULL);
|
|
|
|
int rc = sqlite3_step(statement);
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_ROW)
|
|
if (rc != SQLITE_ROW)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
*out = sqlite3_column_int(statement, 0);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a profile from persistent storage.
|
|
*/
|
|
int
|
|
twc_sqlite_delete_profile(struct t_twc_profile *profile)
|
|
{
|
|
int64_t profile_id;
|
|
if (!twc_sqlite_profile_id(profile, &profile_id))
|
|
{
|
|
weechat_printf(NULL, "missing profile!");
|
|
return -1;
|
|
}
|
|
|
|
TWC_SQLITE_STMT(statement,
|
|
"DELETE FROM profiles "
|
|
"WHERE id == ?");
|
|
sqlite3_bind_int(statement, 1,
|
|
profile_id);
|
|
|
|
int rc = sqlite3_step(statement);
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_DONE)
|
|
if (rc == SQLITE_DONE)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Add a friend request. Return ID on success, -1 on error.
|
|
*/
|
|
int
|
|
twc_sqlite_add_friend_request(struct t_twc_profile *profile,
|
|
struct t_twc_friend_request *friend_request)
|
|
{
|
|
int64_t profile_id;
|
|
if (!twc_sqlite_profile_id(profile, &profile_id))
|
|
{
|
|
weechat_printf(NULL, "missing profile!");
|
|
return -1;
|
|
}
|
|
|
|
TWC_SQLITE_STMT(statement,
|
|
"INSERT OR REPLACE INTO friend_requests (tox_id, message, profile_id)"
|
|
"VALUES (?, ?, ?)");
|
|
sqlite3_bind_blob(statement, 1,
|
|
friend_request->tox_id, TOX_CLIENT_ID_SIZE,
|
|
NULL);
|
|
sqlite3_bind_text(statement, 2,
|
|
friend_request->message, strlen(friend_request->message) + 1,
|
|
NULL);
|
|
sqlite3_bind_int(statement, 3,
|
|
profile_id);
|
|
|
|
int rc = sqlite3_step(statement);
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_DONE)
|
|
if (rc != SQLITE_DONE)
|
|
return -1;
|
|
else
|
|
return sqlite3_last_insert_rowid(twc_sqlite_db);
|
|
}
|
|
|
|
/**
|
|
* Return the number of friend requests for a profile.
|
|
*/
|
|
int
|
|
twc_sqlite_friend_request_count(struct t_twc_profile *profile)
|
|
{
|
|
int64_t profile_id;
|
|
if (!twc_sqlite_profile_id(profile, &profile_id))
|
|
{
|
|
weechat_printf(NULL, "missing profile!");
|
|
return -1;
|
|
}
|
|
|
|
TWC_SQLITE_STMT(statement,
|
|
"SELECT COUNT(*) FROM friend_requests WHERE profile_id == ?");
|
|
sqlite3_bind_int(statement, 1,
|
|
profile_id);
|
|
|
|
int rc = sqlite3_step(statement);
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_ROW)
|
|
if (rc != SQLITE_ROW)
|
|
return 0;
|
|
else
|
|
return sqlite3_column_int(statement, 0);
|
|
}
|
|
|
|
/**
|
|
* Convert a row from an SQLite statement to a friend request object.
|
|
*/
|
|
struct t_twc_friend_request *
|
|
twc_sqlite_friend_request_row(sqlite3_stmt *statement,
|
|
struct t_twc_profile *profile)
|
|
{
|
|
struct t_twc_friend_request *request = malloc(sizeof(struct t_twc_friend_request));
|
|
request->request_id = sqlite3_column_int(statement, 0);
|
|
memcpy(request->tox_id,
|
|
sqlite3_column_blob(statement, 1),
|
|
TOX_CLIENT_ID_SIZE);
|
|
request->message = strdup((const char *)sqlite3_column_text(statement, 2));
|
|
request->profile = profile;
|
|
|
|
return request;
|
|
}
|
|
|
|
/**
|
|
* Return a list of all friend requests for the given profile.
|
|
*/
|
|
struct t_twc_list *
|
|
twc_sqlite_friend_requests(struct t_twc_profile *profile)
|
|
{
|
|
int64_t profile_id;
|
|
if (!twc_sqlite_profile_id(profile, &profile_id))
|
|
{
|
|
weechat_printf(NULL, "missing profile!");
|
|
return NULL;
|
|
}
|
|
|
|
TWC_SQLITE_STMT(statement,
|
|
"SELECT id, tox_id, message "
|
|
"FROM friend_requests "
|
|
"WHERE profile_id == ?");
|
|
sqlite3_bind_int(statement, 1,
|
|
profile_id);
|
|
|
|
struct t_twc_list *friend_requests = twc_list_new();
|
|
|
|
int rc;
|
|
while ((rc = sqlite3_step(statement)) == SQLITE_ROW)
|
|
{
|
|
struct t_twc_friend_request *request =
|
|
twc_sqlite_friend_request_row(statement, profile);
|
|
twc_list_item_new_data_add(friend_requests, request);
|
|
}
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_DONE)
|
|
|
|
return friend_requests;
|
|
}
|
|
|
|
struct t_twc_friend_request *
|
|
twc_sqlite_friend_request_with_id(struct t_twc_profile *profile,
|
|
int64_t id)
|
|
{
|
|
int64_t profile_id;
|
|
if (!twc_sqlite_profile_id(profile, &profile_id))
|
|
{
|
|
weechat_printf(NULL, "missing profile!");
|
|
return NULL;
|
|
}
|
|
|
|
TWC_SQLITE_STMT(statement,
|
|
"SELECT id, tox_id, message "
|
|
"FROM friend_requests "
|
|
"WHERE id == ? AND profile_id == ?");
|
|
sqlite3_bind_int(statement, 1, id);
|
|
sqlite3_bind_int(statement, 2, profile_id);
|
|
|
|
struct t_twc_friend_request *request;
|
|
int rc = sqlite3_step(statement);
|
|
if (rc == SQLITE_ROW)
|
|
{
|
|
request = twc_sqlite_friend_request_row(statement, profile);
|
|
return request;
|
|
}
|
|
else
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_DONE)
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
twc_sqlite_delete_friend_request_with_id(struct t_twc_profile *profile,
|
|
int64_t id)
|
|
{
|
|
int64_t profile_id;
|
|
if (!twc_sqlite_profile_id(profile, &profile_id))
|
|
{
|
|
weechat_printf(NULL, "missing profile!");
|
|
return -1;
|
|
}
|
|
|
|
TWC_SQLITE_STMT(statement,
|
|
"DELETE FROM friend_requests "
|
|
"WHERE id == ? AND profile_id == ?");
|
|
sqlite3_bind_int(statement, 1,
|
|
id);
|
|
sqlite3_bind_int(statement, 2,
|
|
profile_id);
|
|
|
|
int rc = sqlite3_step(statement);
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_DONE)
|
|
if (rc == SQLITE_DONE)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Initialize connection to SQLite database and create tables if necessary.
|
|
* Returns 0 on success, -1 on failure.
|
|
*/
|
|
int
|
|
twc_sqlite_init()
|
|
{
|
|
char *path = twc_sqlite_db_path();
|
|
|
|
int rc = sqlite3_open(path, &twc_sqlite_db);
|
|
free(path);
|
|
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_OK)
|
|
if (rc != SQLITE_OK)
|
|
{
|
|
weechat_printf(NULL,
|
|
"%s: could not open database: %s\n",
|
|
weechat_plugin->name,
|
|
sqlite3_errmsg(twc_sqlite_db));
|
|
sqlite3_close(twc_sqlite_db);
|
|
twc_sqlite_db = NULL;
|
|
|
|
return -1;
|
|
}
|
|
|
|
// statement list (so we can finalize later)
|
|
twc_sqlite_statements = twc_list_new();
|
|
|
|
// initialize tables
|
|
if (twc_sqlite_init_profiles() != 0 ||
|
|
twc_sqlite_init_friend_requests() != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Close connection to SQLite database.
|
|
*/
|
|
void
|
|
twc_sqlite_end()
|
|
{
|
|
size_t index;
|
|
struct t_twc_list_item *item;
|
|
twc_list_foreach(twc_sqlite_statements, index, item)
|
|
sqlite3_finalize(item->data);
|
|
|
|
int rc = sqlite3_close(twc_sqlite_db);
|
|
TWC_SQLITE_DEBUG_RC(rc, SQLITE_OK)
|
|
}
|
|
|