diff --git a/CMakeLists.txt b/CMakeLists.txt
index 08e39e7..411a54f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -43,6 +43,7 @@ add_library(tox MODULE
src/twc-list.c
src/twc-message-queue.c
src/twc-profile.c
+ src/twc-sqlite.c
src/twc-tox-callbacks.c
src/twc-utils.c
)
@@ -50,6 +51,7 @@ add_library(tox MODULE
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -Werror -Wno-unused-parameter")
target_link_libraries(tox toxcore)
+target_link_libraries(tox sqlite3)
# remove lib prefix (libtox.so -> tox.so)
set_target_properties(tox PROPERTIES PREFIX "")
diff --git a/README.md b/README.md
index c846d64..b6f6cc4 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ Installation
------------
> Tox-WeeChat is available in the [AUR][4].
-Tox-WeeChat requires [WeeChat][1] >= 1.0.1, [libjansson][5] >= 2.5 and the latest-ish [libtoxcore][6]. It also requires CMake to be built. Installation is fairly simple; after getting the source, compile and install using CMake:
+Tox-WeeChat requires [WeeChat][1] >= 1.0.1, [SQLite][5] >= 3.6.19 and the latest-ish [libtoxcore][6]. It also requires CMake to be built. Installation is fairly simple; after getting the source, compile and install using CMake:
$ mkdir build && cd build
$ cmake -DHOME_FOLDER_INSTALL=ON ..
@@ -61,6 +61,6 @@ along with Tox-WeeChat. If not, see .
[2]: http://weechat.org
[3]: https://travis-ci.org/haavardp/tox-weechat
[4]: https://aur.archlinux.org/packages/tox-weechat-git
-[5]: http://www.digip.org/jansson
+[5]: http://www.sqlite.org
[6]: https://github.com/irungentoo/toxcore
diff --git a/src/twc-commands.c b/src/twc-commands.c
index ce90122..4b443cd 100644
--- a/src/twc-commands.c
+++ b/src/twc-commands.c
@@ -28,6 +28,7 @@
#include "twc-chat.h"
#include "twc-friend-request.h"
#include "twc-bootstrap.h"
+#include "twc-sqlite.h"
#include "twc-utils.h"
#include "twc-commands.h"
@@ -279,22 +280,22 @@ twc_cmd_friend(void *data, struct t_gui_buffer *buffer,
struct t_twc_friend_request *request;
if (weechat_strcasecmp(argv[2], "all") == 0)
{
- int count = 0;
- while ((request = twc_friend_request_with_index(profile, 0)) != NULL)
+ struct t_twc_list *requests = twc_sqlite_friend_requests(profile);
+ size_t index;
+ struct t_twc_list_item *item;
+ twc_list_foreach(requests, index, item)
{
if (accept)
- twc_friend_request_accept(request);
+ twc_friend_request_accept(item->friend_request);
else
- twc_friend_request_remove(request);
-
- ++count;
+ twc_friend_request_remove(item->friend_request);
}
weechat_printf(profile->buffer,
"%s%s %d friend requests.",
weechat_prefix("network"),
accept ? "Accepted" : "Declined",
- count);
+ index);
return WEECHAT_RC_OK;
}
@@ -312,14 +313,16 @@ twc_cmd_friend(void *data, struct t_gui_buffer *buffer,
char hex_address[TOX_CLIENT_ID_SIZE * 2 + 1];
twc_bin2hex(request->tox_id,
- TOX_CLIENT_ID_SIZE,
- hex_address);
+ TOX_CLIENT_ID_SIZE,
+ hex_address);
if (accept)
twc_friend_request_accept(request);
else
twc_friend_request_remove(request);
+ twc_friend_request_free(request);
+
weechat_printf(profile->buffer,
"%s%s friend request from %s.",
weechat_prefix("network"),
@@ -333,37 +336,34 @@ twc_cmd_friend(void *data, struct t_gui_buffer *buffer,
// /friend requests
else if (argc == 2 && weechat_strcasecmp(argv[1], "requests") == 0)
{
- if (profile->friend_requests == NULL)
- {
- weechat_printf(profile->buffer,
- "%sNo pending friend requests :(",
- weechat_prefix("network"));
- }
- else
- {
- weechat_printf(profile->buffer,
- "%sPending friend requests:",
- weechat_prefix("network"));
+ weechat_printf(profile->buffer,
+ "%sPending friend requests:",
+ weechat_prefix("network"));
- size_t index;
- struct t_twc_list_item *item;
- twc_list_foreach(profile->friend_requests, index, item)
- {
- // TODO: load short form address length from config
- char hex_address[12 + 1];
- twc_bin2hex(item->friend_request->tox_id,
- 6,
- hex_address);
+ struct t_twc_list *friend_requests = twc_sqlite_friend_requests(profile);
- weechat_printf(profile->buffer,
- "%s[%d] Address: %s\n"
- "[%d] Message: %s",
- weechat_prefix("network"),
- index, hex_address,
- index, item->friend_request->message);
- }
+ size_t index;
+ struct t_twc_list_item *item;
+ twc_list_foreach(friend_requests, index, item)
+ {
+ // TODO: load short form address length from config
+ char hex_address[12 + 1];
+ twc_bin2hex(item->friend_request->tox_id,
+ 6,
+ hex_address);
+
+ weechat_printf(profile->buffer,
+ "%s[%d] Address: %s\n"
+ "[%d] Message: %s",
+ weechat_prefix("network"),
+ item->friend_request->request_id,
+ hex_address,
+ item->friend_request->request_id,
+ item->friend_request->message);
}
+ twc_friend_request_free_list(friend_requests);
+
return WEECHAT_RC_OK;
}
@@ -388,7 +388,6 @@ twc_cmd_me(void *data, struct t_gui_buffer *buffer,
return WEECHAT_RC_OK;
}
-
/**
* Command /msg callback.
*/
@@ -704,7 +703,7 @@ twc_commands_init()
"address: internet address of node to bootstrap with\n"
" port: port of the node\n"
" Tox ID: Tox ID of the node",
- NULL, twc_cmd_bootstrap, NULL);
+ "connect", twc_cmd_bootstrap, NULL);
weechat_hook_command("friend",
"manage friends",
@@ -719,7 +718,13 @@ twc_commands_init()
"requests: list friend requests\n"
" accept: accept friend requests\n"
" decline: decline friend requests\n",
- NULL, twc_cmd_friend, NULL);
+ "list"
+ " || add"
+ " || remove"
+ " || requests"
+ " || accept"
+ " || decline",
+ twc_cmd_friend, NULL);
weechat_hook_command("me",
"send an action to the current chat",
diff --git a/src/twc-friend-request.c b/src/twc-friend-request.c
index e25d884..77627b1 100644
--- a/src/twc-friend-request.c
+++ b/src/twc-friend-request.c
@@ -25,6 +25,7 @@
#include "twc.h"
#include "twc-list.h"
#include "twc-profile.h"
+#include "twc-sqlite.h"
#include "twc-utils.h"
#include "twc-friend-request.h"
@@ -39,14 +40,6 @@ twc_friend_request_add(struct t_twc_profile *profile,
const uint8_t *client_id,
const char *message)
{
- struct t_config_option *option =
- profile->options[TWC_PROFILE_OPTION_MAX_FRIEND_REQUESTS];
- unsigned int max_requests = weechat_config_integer(option);
-
- // check for a full friend request list
- if (profile->friend_requests->count >= max_requests)
- return -1;
-
// create a new request
struct t_twc_friend_request *request
= malloc(sizeof(struct t_twc_friend_request));
@@ -58,7 +51,8 @@ twc_friend_request_add(struct t_twc_profile *profile,
memcpy(request->tox_id, client_id, TOX_CLIENT_ID_SIZE);
// add to list
- twc_list_item_new_data_add(profile->friend_requests, request);
+ if (twc_sqlite_add_friend_request(profile, request) == -1)
+ return -2;
return 0;
}
@@ -79,8 +73,8 @@ twc_friend_request_accept(struct t_twc_friend_request *request)
void
twc_friend_request_remove(struct t_twc_friend_request *request)
{
- twc_list_remove_with_data(request->profile->friend_requests, request);
- twc_friend_request_free(request);
+ twc_sqlite_delete_friend_request_with_id(request->profile,
+ request->request_id);
}
/**
@@ -88,13 +82,9 @@ twc_friend_request_remove(struct t_twc_friend_request *request)
*/
struct t_twc_friend_request *
twc_friend_request_with_index(struct t_twc_profile *profile,
- unsigned int index)
+ int64_t index)
{
- struct t_twc_list_item *item = twc_list_get(profile->friend_requests, index);
- if (item)
- return item->friend_request;
- else
- return NULL;
+ return twc_sqlite_friend_request_with_id(profile, index);
}
/**
@@ -108,16 +98,15 @@ twc_friend_request_free(struct t_twc_friend_request *request)
}
/**
- * Free all friend requests from a profile.
+ * Free all friend requests from a list.
*/
void
-twc_friend_request_free_profile(struct t_twc_profile *profile)
+twc_friend_request_free_list(struct t_twc_list *list)
{
struct t_twc_friend_request *request;
-
- while ((request = twc_list_pop(profile->friend_requests)))
+ while ((request = twc_list_pop(list)))
twc_friend_request_free(request);
- free(profile->friend_requests);
+ free(list);
}
diff --git a/src/twc-friend-request.h b/src/twc-friend-request.h
index d381cf6..85e39f7 100644
--- a/src/twc-friend-request.h
+++ b/src/twc-friend-request.h
@@ -22,6 +22,8 @@
#include
+struct t_twc_list;
+
/**
* Represents a friend request with a Tox ID and a message.
*/
@@ -29,6 +31,7 @@ struct t_twc_friend_request
{
struct t_twc_profile *profile;
+ int request_id;
uint8_t tox_id[TOX_CLIENT_ID_SIZE];
char *message;
};
@@ -46,13 +49,13 @@ twc_friend_request_remove(struct t_twc_friend_request *request);
struct t_twc_friend_request *
twc_friend_request_with_index(struct t_twc_profile *profile,
- unsigned int index);
+ int64_t index);
void
twc_friend_request_free(struct t_twc_friend_request *request);
void
-twc_friend_request_free_profile(struct t_twc_profile *profile);
+twc_friend_request_free_list(struct t_twc_list *list);
#endif // TOX_WEECHAT_FRIEND_REQUEST_H
diff --git a/src/twc-profile.c b/src/twc-profile.c
index b9cd614..607b3b0 100644
--- a/src/twc-profile.c
+++ b/src/twc-profile.c
@@ -33,6 +33,7 @@
#include "twc-message-queue.h"
#include "twc-chat.h"
#include "twc-tox-callbacks.h"
+#include "twc-sqlite.h"
#include "twc-utils.h"
#include "twc-profile.h"
@@ -176,7 +177,6 @@ twc_profile_new(const char *name)
profile->tox_online = false;
profile->chats = twc_list_new();
- profile->friend_requests = twc_list_new();
profile->message_queues = weechat_hashtable_new(32,
WEECHAT_HASHTABLE_INTEGER,
WEECHAT_HASHTABLE_POINTER,
@@ -213,15 +213,6 @@ twc_profile_load(struct t_twc_profile *profile)
weechat_prefix("network"), weechat_plugin->name,
profile->name);
- // TODO: this does nothing
- if (profile->friend_requests->count > 0)
- {
- weechat_printf(profile->buffer,
- "%sYou have %d pending friend requests.",
- weechat_prefix("network"),
- profile->friend_requests->count);
- }
-
// create Tox
profile->tox = tox_new(NULL);
if (!(profile->tox))
@@ -233,6 +224,7 @@ twc_profile_load(struct t_twc_profile *profile)
}
// try loading Tox saved data
+ // TODO: this can return -1 even if it does not fail
if (twc_profile_load_data(profile) == -1)
{
// we failed to load - set some defaults
@@ -247,6 +239,18 @@ twc_profile_load(struct t_twc_profile *profile)
(uint8_t *)name, strlen(name));
}
+ // register with sqlite
+ twc_sqlite_add_profile(profile);
+
+ int friend_request_count = twc_sqlite_friend_request_count(profile);
+ if (friend_request_count > 0)
+ {
+ weechat_printf(profile->buffer,
+ "%sYou have %d pending friend requests.",
+ weechat_prefix("network"),
+ friend_request_count);
+ }
+
// bootstrap DHT
// TODO: add count to config
int bootstrap_node_count = 5;
@@ -404,6 +408,7 @@ twc_profile_delete(struct t_twc_profile *profile,
{
char *data_path = twc_profile_expanded_data_path(profile);
+ twc_sqlite_delete_profile(profile);
twc_profile_free(profile);
if (delete_data)
@@ -427,7 +432,6 @@ twc_profile_free(struct t_twc_profile *profile)
}
// free things
- twc_friend_request_free_profile(profile);
twc_chat_free_profile(profile);
twc_message_queue_free_profile(profile);
free(profile->name);
diff --git a/src/twc-profile.h b/src/twc-profile.h
index 4cd5c97..b186817 100644
--- a/src/twc-profile.h
+++ b/src/twc-profile.h
@@ -46,7 +46,6 @@ struct t_twc_profile
struct t_gui_buffer *buffer;
struct t_hook *tox_do_timer;
- struct t_twc_list *friend_requests;
struct t_twc_list *chats;
struct t_hashtable *message_queues;
};
diff --git a/src/twc-sqlite.c b/src/twc-sqlite.c
new file mode 100644
index 0000000..202f666
--- /dev/null
+++ b/src/twc-sqlite.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2014 Håvard Pettersson
+ *
+ * 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 .
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+
+#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 0 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 0;
+}
+
+/**
+ * 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)
+}
+
diff --git a/src/twc-sqlite.h b/src/twc-sqlite.h
new file mode 100644
index 0000000..ab7db32
--- /dev/null
+++ b/src/twc-sqlite.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2014 Håvard Pettersson
+ *
+ * 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 .
+ */
+
+#ifndef TWC_SQLITE_H
+#define TWC_SQLITE_H
+
+#include
+
+struct t_twc_profile;
+struct t_twc_friend_request;
+
+int
+twc_sqlite_init();
+
+int
+twc_sqlite_add_profile(struct t_twc_profile *profile);
+
+int
+twc_sqlite_delete_profile(struct t_twc_profile *profile);
+
+int
+twc_sqlite_add_friend_request(struct t_twc_profile *profile,
+ struct t_twc_friend_request *friend_request);
+
+int
+twc_sqlite_friend_request_count(struct t_twc_profile *profile);
+
+struct t_twc_list *
+twc_sqlite_friend_requests(struct t_twc_profile *profile);
+
+struct t_twc_friend_request *
+twc_sqlite_friend_request_with_id(struct t_twc_profile *profile,
+ int64_t id);
+
+int
+twc_sqlite_delete_friend_request_with_id(struct t_twc_profile *profile,
+ int64_t id);
+
+void
+twc_sqlite_end();
+
+#endif // TWC_SQLITE_H
diff --git a/src/twc.c b/src/twc.c
index 35856d1..b59b2aa 100644
--- a/src/twc.c
+++ b/src/twc.c
@@ -26,6 +26,7 @@
#include "twc-gui.h"
#include "twc-config.h"
#include "twc-completion.h"
+#include "twc-sqlite.h"
#include "twc.h"
@@ -42,6 +43,14 @@ weechat_plugin_init(struct t_weechat_plugin *plugin, int argc, char *argv[])
{
weechat_plugin = plugin;
+ if (twc_sqlite_init() != 0)
+ {
+ weechat_printf(NULL,
+ "%s%s: failed to initialize persistent storage, some "
+ "data will not be saved",
+ weechat_prefix("error"), weechat_plugin->name);
+ }
+
twc_profile_init();
twc_commands_init();
twc_gui_init();
@@ -63,6 +72,8 @@ weechat_plugin_end(struct t_weechat_plugin *plugin)
twc_profile_free_all();
+ twc_sqlite_end();
+
return WEECHAT_RC_OK;
}