Mysql Storage Provider for Bitlbee. main repository http://github.com/shiplu/bitlbee
/********************************************************************\ | |
* BitlBee -- An IRC to other IM-networks gateway * | |
* * | |
* Copyright 2002-2006 Wilmer van der Gaast and others * | |
\********************************************************************/ | |
/* | |
* Storage backend that uses an MySQL. | |
* Sample schema can be found on /doc/schema_mysql.sql | |
*/ | |
/* | |
This program 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 2 of the License, or | |
(at your option) any later version. | |
This program 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 with | |
the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; | |
if not, write to the Free Software Foundation, Inc., 59 Temple Place, | |
Suite 330, Boston, MA 02111-1307 USA | |
*/ | |
#define BITLBEE_CORE | |
#include "bitlbee.h" | |
#include "protocols/bee.h" | |
#include "base64.h" | |
#include "arc.h" | |
#include "sha1.h" | |
#include <mysql/mysql.h> | |
#include <mysql.h> | |
#include <glib/gstdio.h> | |
#include <glib.h> | |
#if GLIB_CHECK_VERSION(2,8,0) | |
#include <glib/gstdio.h> | |
#else | |
/* GLib < 2.8.0 doesn't have g_access, so just use the system access(). */ | |
#include <unistd.h> | |
#define g_access access | |
#endif | |
typedef enum | |
{ | |
MYSQL_PASS_CHECK_ONLY = -1, | |
MYSQL_PASS_UNKNOWN = 0, | |
MYSQL_PASS_WRONG, | |
MYSQL_PASS_OK | |
} mysql_pass_st; | |
/* To make it easier later when extending the format: */ | |
#define MYSQL_FORMAT_VERSION 1 | |
/* Function prototypes. Declared here so I dont have to scroll the whole file */ | |
static void append_mysql_escaped_param(GString *query, GString *buffer, GString *param); | |
static void mysql_storage_init( void ); | |
static void mysql_storage_deinit( void ); | |
static storage_status_t mysql_storage_load( irc_t *irc, const char *password ); | |
static storage_status_t mysql_storage_check_pass( const char *my_nick, const char *password ); | |
static storage_status_t mysql_storage_save( irc_t *irc, int overwrite ); | |
static storage_status_t mysql_storage_remove( const char *nick, const char *password ); | |
/* Utility/Helper functions */ | |
/** Function prototypes. Declared here so I dont have to scroll the whole file */ | |
static void mysql_storage_save_nick(gpointer key, gpointer value, gpointer data); | |
static void append_mysql_escaped_param(GString *query, GString *buffer, GString *param); | |
static char* ret_mysql_esc_str(MYSQL *mysql, GString *buffer, GString *param); | |
static storage_status_t save_kv_pair(GString *q, GString *buf, char *table_name, | |
char *fk_column_name, char *key_name, char *value_name, | |
long fk_column_value, char *key, char *value); | |
static int send_query(MYSQL *mysql, const char *query, unsigned long len); | |
static GList * mysql_multiple_rows(MYSQL *mysql_handle, char* query); | |
static void mysql_free_multiple_rows(GList *table); | |
static void mysql_free_single_row(gpointer data); | |
static GSList* mysql_copy_single_row(MYSQL_RES *result); | |
static long storage_get_user_id(GString *q, GString *buf, GString *nick); | |
typedef struct database_object_t { | |
MYSQL *mysql; | |
GString *query_string; | |
GString *string_buffer; | |
gpointer data; | |
}database_object; | |
MYSQL *mysql = NULL; | |
/** | |
* A wrapper for mysql query. | |
*/ | |
static int send_query(MYSQL *mysql, const char *query, unsigned long len){ | |
int return_value = mysql_real_query(mysql, query, len); | |
unsigned int m_errno = mysql_errno(mysql); | |
char *info = mysql_info(mysql); | |
fprintf(stderr, "QUERY\t%s\n", query); | |
fprintf(stderr, "\tLength: %03lu Errno: %u %s\n", len, m_errno, ((info==NULL)? "":info)); | |
if(m_errno!=0) | |
fprintf(stderr, "\e[31mERROR\t%s\e[0m\n", mysql_error(mysql)); | |
return return_value; | |
} | |
/** | |
* Escapes param and then append to query. Uses buffer for temporary storage; | |
*/ | |
static void append_mysql_escaped_param(GString *query, GString *buffer, GString *param){ | |
g_string_set_size(buffer, param->len*2+1); | |
mysql_real_escape_string(mysql, buffer->str, param->str, param->len); | |
g_string_append(query, buffer->str); | |
} | |
static char* ret_mysql_esc_str(MYSQL *mysql, GString *buffer, GString *param){ | |
/// re-initializing the buffer. | |
g_string_set_size(buffer, param->len*2+1); | |
g_string_printf(buffer,"%s", ""); | |
mysql_real_escape_string(mysql, buffer->str, param->str, param->len); | |
return buffer->str; | |
} | |
static void free_g_str_list(int num, ...){ | |
va_list arguments; | |
GString *gs=NULL; | |
va_start ( arguments, num ); | |
int i; | |
for(i=0; i<num; i++){ | |
gs = va_arg(arguments, GString*); | |
g_string_free(gs, TRUE); | |
} | |
va_end ( arguments ); // Cleans up the list | |
} | |
/** | |
* @param data as GSList with to element. first one is key. second one is value. | |
* @param user_data The address of the set linked lists head element. | |
*/ | |
static void mysql_storage_load_settings(gpointer data, gpointer user_data){ | |
GSList *row = data; | |
/// save this set settings to irc user settings | |
set_setstr(((set_t **)user_data), ((GString *)(row->data))->str, ((GString *)(row->next->data))->str); | |
} | |
static void mysql_storage_load_channels(gpointer data, gpointer user_data){ | |
char *name, *type; | |
long int channel_id=0, user_id = 0; | |
irc_t **ptr_irc= user_data; | |
irc_t *irc = *ptr_irc; | |
irc_channel_t *channel = NULL; | |
GSList *row = data; | |
GString *qry= NULL; | |
GList *m_rows= NULL; | |
/// get all the fields | |
channel_id = atol(((GString *)row->data)->str); | |
row = row->next; | |
user_id = atol(((GString *)row->data)->str); | |
row = row->next; | |
name = g_strdup(((GString *)row->data)->str); | |
row = row->next; | |
type = g_strdup(((GString *)row->data)->str); | |
fprintf(stderr, "\tCurrent Channel: channel_id=%ld, user_id=%ld, name=%s, type=%s\n", channel_id, user_id, name, type); | |
if( !name || !type ){ | |
fprintf(stderr, "\e[31mERROR\tMissing values for channels. channel: %s type: %s\e[0m\n", name, type); | |
g_free(name); | |
g_free(type); | |
return; | |
} | |
/// 4.2 Create/find a channel and assign | |
channel = irc_channel_by_name( irc, name ); | |
if(!channel){ | |
fprintf(stderr, "\tNo IRC channel found. Creating one with name%s\n", name); | |
channel = irc_channel_new( irc, name ); | |
} | |
if(channel){ | |
/// dont know why "type" is hardcoded here. | |
/// I just followed storage_xml.c | |
set_setstr( &channel->set, "type", type ); | |
}else{ | |
g_free(name); | |
g_free(type); | |
fprintf(stderr, "\e[31mERROR\tLast channel creation was not successfull\e[0m\n"); | |
return; | |
} | |
/// 4.3 get all the chanel setting and update 1 by 1 | |
qry = g_string_new("SELECT name, value from channel_settings where channel="); | |
g_string_append_printf(qry, "'%ld'", channel_id); | |
m_rows = mysql_multiple_rows(mysql, qry->str); | |
/// 3.3 Set all the user account settings to irc account struct 1 by 1 | |
g_list_foreach(m_rows, mysql_storage_load_settings, &channel->set); | |
mysql_free_multiple_rows(m_rows); | |
g_free(name); | |
g_free(type); | |
g_string_free(qry, TRUE); | |
} | |
static void mysql_storage_load_account_buddies(gpointer data, gpointer user_data){ | |
account_t **acc = user_data; | |
account_t *account = *acc; | |
GSList *row = data; | |
/// there are two rows. So 2 iteration on fields | |
char *handle = g_strdup(((GString *)(row->data))->str); | |
char *nick = g_strdup(((GString *)(row->next->data))->str); | |
if(account && handle && nick ){ | |
nick_set_raw(account, handle, nick ); | |
}else{ | |
fprintf(stderr, "\e[31mERROR\tMissing values for account buddy\e[0m\n"); | |
} | |
} | |
static void mysql_storage_load_account_settings(gpointer data, gpointer user_data){ | |
account_t **acc = user_data; | |
account_t *account = *acc; | |
GSList *row = data; | |
/// there are two rows. So 2 iteration on fields | |
char *name = g_strdup(((GString *)(row->data))->str); | |
char *value = g_strdup(((GString *)(row->next->data))->str); | |
if(account){ | |
set_t *s = set_find(&account->set, name); | |
if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) ){ | |
g_free(name); | |
/// not sure whether bellow statement will be needed. | |
//name = NULL; | |
return; | |
} | |
} | |
set_setstr(&account->set, name, (char*) value ); | |
g_free(name); | |
/// not sure whether bellow statement will be needed. | |
//name = NULL; | |
} | |
static void mysql_storage_load_accounts(gpointer data, gpointer user_data){ | |
GSList *row = data; | |
GList *m_rows = NULL; | |
account_t *acc = NULL; | |
GString *qry = NULL; | |
irc_t ** ptr_irc = user_data; | |
irc_t *irc = *ptr_irc; | |
char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag; | |
// int pass_len; | |
long int account_id = 0/*, user_id=0*/; | |
struct prpl *prpl = NULL; | |
/** | |
* This is the sequence how data is read | |
* +-------------+ | |
* | id | | |
* | user | | |
* | protocol | | |
* | handle | | |
* | password | | |
* | autoconnect | | |
* | tag | | |
* | server | | |
* +-------------+ | |
*/ | |
account_id = atol(((GString *)row->data)->str); | |
row = row->next; | |
//user_id = atol(((GString *)row->data)->str); | |
row = row->next; | |
protocol = ((GString *)row->data)->str; | |
row = row->next; | |
handle = ((GString *)row->data)->str; | |
row = row->next; | |
password = ((GString *)row->data)->str; | |
row = row->next; | |
autoconnect = ((GString *)row->data)->str; | |
row = row->next; | |
tag = ((GString *)row->data)->str; | |
row = row->next; | |
server = ((GString *)row->data)->str; | |
if( protocol ) | |
prpl = find_protocol( protocol ); | |
if( !handle || !password|| !protocol ) | |
fprintf(stderr, "\e[31mERROR\tMissing values for account\e[0m\n"); | |
else if( !prpl ) | |
fprintf(stderr, "\e[31mERROR\tUnknown protocol: %s\e[0m\n", protocol ); | |
else{ | |
acc = account_add(irc->b, prpl, handle, password ); | |
if( server ) | |
set_setstr( &acc->set, "server", server ); | |
if( autoconnect ) | |
set_setstr( &acc->set, "auto_connect", autoconnect ); | |
if( tag ) | |
set_setstr( &acc->set, "tag", tag ); | |
} | |
g_free( password ); | |
/// 3.2 Get all the settings 1 by 1 | |
qry = g_string_new("SELECT name, value from account_settings where account="); | |
g_string_append_printf(qry, "'%ld'", account_id); | |
m_rows = mysql_multiple_rows(mysql, qry->str); | |
/// 3.3 Set all the user account settings to irc account struct 1 by 1 | |
g_list_foreach(m_rows, mysql_storage_load_account_settings, &acc); | |
mysql_free_multiple_rows(m_rows); | |
/// 3.4 Read all the renamed buddy | |
g_string_printf(qry, "SELECT handle, nick from account_buddies where account='%ld'", account_id); | |
m_rows = mysql_multiple_rows(mysql, qry->str); | |
/// 3.5 Set all the renamed budy to irc account struct 1 by 1 | |
g_list_foreach(m_rows, mysql_storage_load_account_buddies, &acc); | |
mysql_free_multiple_rows(m_rows); | |
g_string_free(qry, TRUE); | |
} | |
static void mysql_free_multiple_rows(GList *table){ | |
g_list_free_full(table, mysql_free_single_row); | |
} | |
/** | |
* Execute the query and returns all the result in a tabular format | |
* You must free this table by caling mysql_free_multiple_rows | |
*/ | |
static GList * mysql_multiple_rows(MYSQL *mysql_handle, char* query){ | |
my_ulonglong num_rows=0; | |
MYSQL_RES *result = NULL; | |
GSList *single_row = NULL; | |
GList *rows = NULL; | |
GString *q = g_string_new(query); | |
int query_status = send_query(mysql_handle, q->str, q->len); | |
if(query_status!=0){ | |
g_string_free(q, TRUE); | |
return NULL; | |
} | |
result = mysql_store_result(mysql_handle); | |
if(result==NULL){ | |
/// it was not a query that returns statemnet (e.g. INSERT, DELETE) | |
g_string_free(q, TRUE); | |
return NULL; | |
} | |
num_rows = mysql_num_rows(result); | |
fprintf(stderr, "\t%Ld row%s found\n", num_rows, ((num_rows==1)? "": "s")); | |
if(num_rows>0){ | |
int i=0; | |
for(i=0;i<num_rows; i++){ | |
single_row = mysql_copy_single_row(result); | |
rows = g_list_prepend(rows, single_row); | |
} | |
/// As prepending list actually reverses it. We reverse it agian | |
/// to get the correct order. | |
rows = g_list_reverse(rows); | |
}else{ | |
mysql_free_result(result); | |
g_string_free(q, TRUE); | |
return NULL; | |
} | |
/// clean up | |
g_string_free(q, TRUE); | |
mysql_free_result(result); | |
return rows; | |
} | |
static void msyql_free_single_row_field(gpointer data){ | |
g_string_free(data, TRUE); | |
} | |
static void mysql_free_single_row(gpointer data){ | |
g_slist_free_full(data, msyql_free_single_row_field); | |
} | |
static GSList* mysql_copy_single_row(MYSQL_RES *result) { | |
GSList* single_row=NULL; | |
unsigned int num_fields = mysql_num_fields(result); | |
int i=0; | |
MYSQL_ROW row=mysql_fetch_row(result); | |
for(i=num_fields-1; i>=0; i--) { | |
GString *f = g_string_new(row[i]); | |
single_row = g_slist_prepend(single_row, f); | |
} | |
return single_row; | |
} | |
/** | |
* Excutes the passed query and returs the the first row as an array of | |
* strings. You must free this array by calling mysql_free_single_row() | |
*/ | |
static GSList * mysql_single_row(MYSQL *mysql_handle, char* query){ | |
GList * rows = mysql_multiple_rows(mysql_handle, query); | |
GSList *first_row = NULL; | |
/// keep only the first row | |
first_row = rows->data; | |
/// clear the list but the elements | |
g_list_free(rows); | |
/// return back the row | |
return first_row; | |
} | |
///@todo this function must be refactored along with set_settings_flag function. | |
static storage_status_t set_channel_settings(MYSQL *mysql, set_t *settings, char * table_name, char *key_column_name, long key_column_id){ | |
GString *q = g_string_new(""); | |
GString *buf = g_string_new(""); | |
set_t *set; | |
for( set = settings; set; set = set->next ) { | |
if( set->value && (strcmp( set->key, "type" ) != 0)) { | |
if(save_kv_pair(q, buf, table_name, key_column_name, | |
"name", "value", key_column_id, set->key, set->value)!=STORAGE_OK){ | |
g_string_free(q, TRUE); | |
g_string_free(buf, TRUE); | |
return STORAGE_OTHER_ERROR; | |
} | |
} | |
} | |
return STORAGE_OK; | |
} | |
static storage_status_t set_settings_flag(MYSQL *mysql, set_t *settings, char * table_name, char *key_column_name, long key_column_id, set_flags_t flag){ | |
GString *q = g_string_new(""); | |
GString *buf = g_string_new(""); | |
set_t *set; | |
for( set = settings; set; set = set->next ) { | |
if( set->value && !( set->flags & flag) ) { | |
if(save_kv_pair(q, buf, table_name, key_column_name, | |
"name", "value", key_column_id, set->key, set->value)!=STORAGE_OK){ | |
g_string_free(q, TRUE); | |
g_string_free(buf, TRUE); | |
return STORAGE_OTHER_ERROR; | |
} | |
} | |
} | |
return STORAGE_OK; | |
} | |
static void mysql_storage_init( void ) { | |
mysql = mysql_init(NULL); | |
if (mysql == NULL) { | |
fprintf(stderr, "\e[31mERROR\tCan not initialize MySQL. Configuration won't be saved.\e[0m\n"); | |
} | |
if (!mysql_real_connect(mysql, global.conf->dbhost, global.conf->dbuser, global.conf->dbpass, NULL, global.conf->dbport, NULL, 0)) { | |
fprintf(stderr, "\e[31mERROR\t%s\nConfiguration won't be saved.\e[0m\n", mysql_error(mysql)); | |
} | |
if (mysql_select_db(mysql, global.conf->dbname)) { | |
fprintf(stderr, "\e[31mERROR\t%s\nConfiguration won't be saved.\e[0m\n", mysql_error(mysql)); | |
} | |
} | |
static storage_status_t mysql_storage_load( irc_t *irc, const char *password ) { | |
// GSList *row = NULL; | |
GList *m_rows = NULL; | |
GString *qry = g_string_new(""); | |
GString *buf = g_string_new(""); | |
GString *nick = g_string_new(irc->user->nick); | |
long user_id =0; | |
/// 1. Check if we have such user | |
storage_status_t ss= mysql_storage_check_pass(irc->user->nick, password); | |
/// if not found or something else we abort | |
if(ss!=STORAGE_OK){ | |
free_g_str_list(3, buf, qry, nick); | |
return ss; | |
} | |
/// 2. Read the user settings now | |
/// 2.1 Get the user id. | |
user_id = storage_get_user_id(qry, buf, nick); | |
/// 2.2 Get all the settings 1 by 1 | |
g_string_printf(qry, "SELECT name, value from user_settings where user='%ld'", user_id); | |
m_rows = mysql_multiple_rows(mysql, qry->str); | |
/// 2.3 Set all the user settings to irc struct 1 by 1 | |
g_list_foreach(m_rows, mysql_storage_load_settings, &irc->b->set); | |
mysql_free_multiple_rows(m_rows); | |
/// 3. Get all the account current user have | |
g_string_printf(qry, "SELECT id, user, protocol, handle, pass" | |
"word, autoconnect, tag, server " /// <--- The space at the end is necessary | |
"FROM `accounts` WHERE `user` = '%ld'", user_id); | |
m_rows = mysql_multiple_rows(mysql, qry->str); | |
if(g_list_length(m_rows)>0){ | |
/// 3.1. set all the accounts to this user | |
/// This funciton also handles | |
/// Setting all account settings and all the buddy list | |
g_list_foreach(m_rows, mysql_storage_load_accounts, &irc); | |
mysql_free_multiple_rows(m_rows); | |
}else{ | |
fprintf(stderr, "\e[31m\tUser has no account!\e[0m"); | |
} | |
/// 4. Get all the channels current user have | |
g_string_printf(qry, "SELECT id, user, name, type " | |
"FROM `channels` WHERE `user` = '%ld'", user_id); | |
m_rows = mysql_multiple_rows(mysql, qry->str); | |
if(g_list_length(m_rows)>0){ | |
/// 4.1 set all the channels info to this user | |
g_list_foreach(m_rows, mysql_storage_load_channels, &irc); | |
mysql_free_multiple_rows(m_rows); | |
}else{ | |
fprintf(stderr, "\e[31m\tUser has no channel!\e[0m"); | |
} | |
/// end clean up | |
free_g_str_list(3, buf, qry, nick); | |
return STORAGE_OK; | |
} | |
static storage_status_t mysql_storage_check_pass( const char *nick, const char *password ) { | |
/// mysql variables | |
MYSQL_RES *result; | |
int ret_query=0; | |
unsigned int field_count; | |
my_ulonglong num_rows=0; | |
/// return variable | |
storage_status_t st = STORAGE_OTHER_ERROR; | |
/// GString wrapper for string params | |
GString *g_nick = g_string_new(nick); | |
GString *g_password = g_string_new(password); | |
/// buffer to build query | |
GString *to = g_string_new(""); | |
/// query string | |
GString *query = g_string_new(""); | |
/// building query | |
g_string_append(query, "SELECT id, nick, password, sha1('"); | |
append_mysql_escaped_param(query, to, g_password); | |
g_string_append(query, "') from users where nick='"); | |
append_mysql_escaped_param(query, to, g_nick); | |
g_string_append(query, "' limit 1"); | |
/// executing query | |
ret_query = send_query(mysql,query->str,query->len); | |
field_count = mysql_field_count(mysql); | |
/// process query | |
if(ret_query==0){ | |
/// query is successfull | |
if(field_count == 4){ | |
result = mysql_store_result(mysql); | |
num_rows = mysql_num_rows(result); | |
if(num_rows==1){ | |
MYSQL_ROW row; | |
row = mysql_fetch_row(result); | |
if(g_ascii_strncasecmp(row[2], row[3], strlen(row[2]))==0){ | |
st = STORAGE_OK; | |
}else{ | |
st = STORAGE_INVALID_PASSWORD; | |
} | |
}else{ | |
st = STORAGE_NO_SUCH_USER; | |
} | |
/// clear up the result | |
mysql_free_result(result); | |
}else{ | |
st = STORAGE_NO_SUCH_USER; | |
} | |
}else{ | |
/// query is failed | |
st = STORAGE_OTHER_ERROR; | |
} | |
/// free up GStrings used | |
g_string_free(to, TRUE); | |
g_string_free(g_nick, TRUE); | |
g_string_free(g_password, TRUE); | |
g_string_free(query, TRUE); | |
return st; | |
} | |
static long storage_get_user_id(GString *q, GString *buf, GString *nick) { | |
long int user_id; | |
GSList *user_row = NULL; | |
g_string_printf(q, "SELECT id from users where nick='"); | |
append_mysql_escaped_param(q, buf, nick); | |
g_string_append(q,"'"); | |
user_row = mysql_single_row(mysql, q->str); | |
user_id = atol(((GString *)user_row->data)->str); | |
g_slist_free(user_row); | |
return user_id; | |
} | |
static storage_status_t mysql_storage_save( irc_t *irc, int overwrite ) { | |
/// static variables | |
my_ulonglong num_rows=0; | |
long user_id = 0; | |
account_t *acc; | |
storage_status_t settings_status; | |
/// dynamic variables. Needs clean up function called | |
// GSList *user_row = NULL; | |
// GString *user_id_str = NULL; | |
GString *nick = g_string_new(irc->user->nick); | |
GString *pass = g_string_new(irc->password); | |
GString *buf = g_string_new(""); | |
GString *q = g_string_new("INSERT INTO users (nick, password) values ('"); | |
/// 1. Save the user name | |
append_mysql_escaped_param(q, buf, nick); | |
g_string_append(q, "',sha1('"); | |
append_mysql_escaped_param(q, buf, pass); | |
g_string_append(q, "')) ON DUPLICATE KEY UPDATE password = sha1('"); | |
append_mysql_escaped_param(q, buf, pass); | |
g_string_append(q, "')"); | |
send_query(mysql, q->str, q->len); | |
g_string_printf(q, "%s", ""); | |
num_rows = mysql_affected_rows(mysql); | |
if(num_rows<0 || num_rows>2){ | |
fprintf(stderr, "User neither added, updated, unchanged\n"); | |
if(mysql_errno(mysql)!=0){ | |
fprintf(stderr, "\e[31mERROR\t%s\e[0m\n", mysql_error(mysql)); | |
} | |
g_string_free(nick, TRUE); | |
g_string_free(pass, TRUE); | |
g_string_free(buf, TRUE); | |
g_string_free(q, TRUE); | |
return STORAGE_OTHER_ERROR; | |
} | |
/// 2. Save the user settings | |
/// 2.1 Get the user id | |
user_id= storage_get_user_id(q, buf,nick); | |
/// 2.2 Set all the settings 1 by 1 | |
settings_status = set_settings_flag(mysql, irc->b->set,"user_settings", "user", user_id, SET_NOSAVE); | |
if(settings_status!=STORAGE_OK){ | |
/// something bad happened! | |
g_string_free(nick, TRUE); | |
g_string_free(pass, TRUE); | |
g_string_free(buf, TRUE); | |
g_string_free(q, TRUE); | |
return settings_status; | |
} | |
/// 3. Set all the user accounts | |
for( acc = irc->b->accounts; acc; acc = acc->next ){ | |
//acc->prpl->name, acc->user, pass_b64, acc->auto_connect, acc->tag | |
/// 3.1 add user accounts | |
GString *acc_protocol = g_string_new(acc->prpl->name); | |
GString *acc_handle = g_string_new(acc->user); | |
GString *acc_password = g_string_new(acc->pass); | |
GString *acc_tag = g_string_new(acc->tag); | |
gboolean server_exists = (acc->server && acc->server[0]); | |
GString *acc_server = NULL; | |
GString *account_id_str = NULL; | |
GSList *account_row = NULL; | |
if(server_exists){ | |
acc_server = g_string_new(acc->server); | |
g_string_printf(q, "INSERT INTO accounts (user, protocol, handle, password, autoconnect, tag, server) values (%ld, '", user_id); | |
} | |
else | |
g_string_printf(q, "INSERT INTO accounts (user, protocol, handle, password, autoconnect, tag) values (%ld, '", user_id); | |
append_mysql_escaped_param(q, buf, acc_protocol); | |
g_string_append(q,"', '"); | |
append_mysql_escaped_param(q, buf, acc_handle); | |
g_string_append(q,"', '"); | |
append_mysql_escaped_param(q, buf, acc_password); | |
g_string_append_printf(q, "', '%d', '", acc->auto_connect); | |
append_mysql_escaped_param(q, buf, acc_tag); | |
if(server_exists){ | |
g_string_append(q,"', '"); | |
append_mysql_escaped_param(q, buf, acc_server); | |
} | |
g_string_append(q,"') on duplicate key UPDATE password='"); | |
append_mysql_escaped_param(q, buf, acc_password); | |
g_string_append_printf(q,"', autoconnect='%d', tag='", acc->auto_connect); | |
append_mysql_escaped_param(q, buf, acc_tag); | |
if(server_exists){ | |
g_string_append(q,"', server='"); | |
append_mysql_escaped_param(q, buf, acc_server); | |
} | |
g_string_append(q,"'"); | |
if(server_exists){ | |
g_string_free(acc_server, TRUE); | |
} | |
send_query(mysql, q->str, q->len); | |
num_rows = mysql_affected_rows(mysql); | |
if(num_rows<0 || num_rows>2){ | |
/// something went wrong. | |
g_string_free(acc_handle, TRUE); | |
g_string_free(acc_password, TRUE); | |
g_string_free(acc_protocol, TRUE); | |
g_string_free(acc_tag, TRUE); | |
g_string_free(account_id_str, TRUE); | |
g_string_free(nick, TRUE); | |
g_string_free(pass, TRUE); | |
g_string_free(buf, TRUE); | |
g_string_free(q, TRUE); | |
return STORAGE_OTHER_ERROR; | |
} | |
/// 3.2 add user account settings | |
g_string_printf(q, "select id from accounts where user='%ld' and protocol='", user_id); | |
append_mysql_escaped_param(q, buf, acc_protocol); | |
g_string_append(q, "' and handle='"); | |
append_mysql_escaped_param(q, buf, acc_handle); | |
g_string_append(q, "'"); | |
account_row = mysql_single_row(mysql, q->str); | |
account_id_str = (GString *)account_row->data; | |
settings_status = set_settings_flag(mysql, irc->b->set, "account_settings", "account", atol(account_id_str->str), ACC_SET_NOSAVE); | |
if(settings_status!=STORAGE_OK){ | |
/// something bad happened! | |
free_g_str_list(8, acc_handle, acc_password, acc_protocol, acc_tag, account_id_str, nick, pass, buf, q); | |
mysql_free_single_row(account_row); | |
return settings_status; | |
} | |
/// 3.3 adding all the renamed buddies. | |
/// buddies you have renamed to good nicks. | |
{ | |
database_object dbo; | |
dbo.mysql = mysql; | |
dbo.query_string = q; | |
dbo.string_buffer = buf; | |
dbo.data = (gpointer)(account_id_str->str); | |
g_hash_table_foreach(acc->nicks, mysql_storage_save_nick, &dbo); | |
} | |
/// Testing if my new g_string free funciton works | |
free_g_str_list(5, acc_handle, acc_password, acc_protocol, acc_tag, account_id_str); | |
} | |
/// 4. Set all the channels | |
{ | |
GSList* l; | |
GSList* channel_row = NULL; | |
GString* channel_id_str = NULL; | |
GString *ch_name = g_string_new(""); | |
GString *ch_type = g_string_new(""); | |
for(l = irc->channels; l; l = l->next ) | |
{ | |
irc_channel_t *ic = l->data; | |
g_string_printf(ch_name, "%s", ic->name); | |
//g_string_printf(ch_type, "set_getstr(&ic->set, \"type\")"); | |
g_string_printf(ch_type, "%s", set_getstr(&ic->set, "type")); | |
if( ic->flags & IRC_CHANNEL_TEMP ) | |
continue; | |
/// 4.1 save channel | |
save_kv_pair(q, buf, "channels", "user", "name", "type", user_id, ch_name->str, ch_type->str); | |
/// fetch channel id | |
g_string_printf(q, "select id from channels where user='%ld' and name='", user_id); | |
append_mysql_escaped_param(q, buf, ch_name); | |
g_string_append(q, "' and type='"); | |
append_mysql_escaped_param(q, buf, ch_type); | |
g_string_append(q, "'"); | |
channel_row = mysql_single_row(mysql, q->str); | |
channel_id_str = (GString*)(channel_row->data); | |
/// 4.2 save channel settings | |
/// the last condition here is a bit geeky. | |
/// its like this becase of refactoring the code. | |
set_channel_settings(mysql, ic->set, "channel_settings", "channel", atol(channel_id_str->str)); | |
} | |
free_g_str_list(2, ch_name, ch_type); | |
mysql_free_single_row(channel_row); | |
} | |
g_string_free(nick, TRUE); | |
g_string_free(pass, TRUE); | |
g_string_free(buf, TRUE); | |
g_string_free(q, TRUE); | |
/// all went good till now. | |
return STORAGE_OK; | |
} | |
static storage_status_t mysql_storage_remove( const char *nick, const char *password ) { | |
int query_status =0; | |
/// GString wrapper for string params | |
GString *g_nick = g_string_new(nick); | |
GString *g_password = g_string_new(password); | |
/// Query and buffer | |
GString *buffer=g_string_new(""), *query = g_string_new("DELETE FROM users where nick='"); | |
append_mysql_escaped_param(query, buffer, g_nick); | |
g_string_append(query,"' and password=sha1('"); | |
append_mysql_escaped_param(query, buffer, g_password); | |
g_string_append(query, "')"); | |
query_status = send_query(mysql, query->str, query->len); | |
/// Free glib objects | |
g_string_free(g_nick, TRUE); | |
g_string_free(g_password, TRUE); | |
g_string_free(query, TRUE); | |
g_string_free(buffer, TRUE); | |
if(query_status==0){ | |
if(mysql_affected_rows(mysql) == 1){ | |
return STORAGE_OK; | |
}else{ | |
/// @TODO should we check if password is wrong and return STORAGE_INVALID_PASSWORD? | |
return STORAGE_NO_SUCH_USER; | |
} | |
}else{ | |
fprintf(stderr, "\e[31mERROR\t%s\e[0m\n", mysql_error(mysql)); | |
return STORAGE_OTHER_ERROR; | |
} | |
} | |
static void mysql_storage_deinit( void ) { | |
if(mysql!=NULL) { | |
mysql_close(mysql); | |
} | |
} | |
/** | |
* If a table has key-value options associated with it in on-to-many | |
* relation this function helps to save those options. | |
* @param q query buffer | |
* @param buf buffer to use for building query. | |
* @param table_name name of the child table | |
* @param fk_column_name foreign key column name mapped to parent | |
* @param key_name name of the key column in key-value | |
* @param value_name name of the value column in key-value | |
* @param fk_column_value value to set for fk_column_name | |
* @param key value to set for key_name | |
* @param value value to set for value_name | |
*/ | |
static storage_status_t save_kv_pair(GString *q, GString *buf, char *table_name, | |
char *fk_column_name, char *key_name, char *value_name, | |
long fk_column_value, char *key, char *value){ | |
GString *v = g_string_new(""); | |
GString *k = g_string_new(""); | |
// building query. | |
g_string_printf(k, "%s", key); | |
g_string_printf(v, "%s", value); | |
g_string_printf(q,"insert into `%s` (`%s`, `%s`, `%s`) values (%ld, '", table_name, | |
fk_column_name, key_name, value_name, fk_column_value); | |
append_mysql_escaped_param(q, buf, k); | |
g_string_append(q,"', '"); | |
append_mysql_escaped_param(q, buf, v); | |
g_string_append_printf(q, "') on duplicate key update `%s`='", value_name); | |
append_mysql_escaped_param(q, buf, v); | |
g_string_append(q,"'"); | |
// executing query | |
send_query(mysql,q->str,q->len); | |
// releasing memory | |
g_string_free(v, TRUE); | |
g_string_free(k, TRUE); | |
if(mysql_errno(mysql)!=0) { | |
/// something bad happened! | |
return STORAGE_OTHER_ERROR; | |
}else{ | |
return STORAGE_OK; | |
} | |
} | |
static void mysql_storage_save_nick(gpointer key, gpointer value, gpointer data){ | |
GString *query = ((database_object *)data)->query_string; | |
GString *buffer = ((database_object *)data)->string_buffer; | |
char * account_id = (char *)(((database_object *)data)->data); | |
/*storage_status_t buddy_kv_stat = */save_kv_pair(query, buffer, "account_buddies", "account", "handle", "nick", | |
atol(account_id), (char *)key, (char *)value); | |
} | |
storage_t storage_mysql = { | |
.name = "mysql", | |
.init = mysql_storage_init, | |
.check_pass = mysql_storage_check_pass, | |
.remove = mysql_storage_remove, | |
.load = mysql_storage_load, | |
.save = mysql_storage_save, | |
.deinit = mysql_storage_deinit | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment