Created
July 18, 2012 16:10
-
-
Save shiplu/3137199 to your computer and use it in GitHub Desktop.
Mysql Storage Provider for Bitlbee. main repository http://github.com/shiplu/bitlbee
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************************************\ | |
* 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