increased MAXLANGUAGES definition
[NeonServV5.git] / src / mysqlConn.c
1 /* mysqlConn.c - NeonServ v5.6
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #include "mysqlConn.h"
19 #include "ConfigParser.h"
20 #include "tools.h"
21 #include "log.h"
22 #define DATABASE_VERSION "20"
23
24 static void show_mysql_error();
25
26 struct mysql_conn_struct {
27     unsigned int tid;
28     MYSQL *mysql_conn;
29     struct used_result *used_results;
30     struct escaped_string *escaped_strings;
31     struct mysql_conn_struct *next;
32 };
33
34 struct used_result {
35     MYSQL_RES *result;
36     struct used_result *next;
37 };
38
39 struct escaped_string {
40     char *string;
41     struct escaped_string *next;
42 };
43
44 struct mysql_conn_struct *get_mysql_conn_struct();
45
46 struct mysql_conn_struct *mysql_conns = NULL;
47 static int mysql_serverport;
48 static char *mysql_host, *mysql_user, *mysql_pass, *mysql_base;
49
50 #ifdef HAVE_THREADS
51 static pthread_mutex_t synchronized;
52 #endif
53
54 static void check_mysql() {
55     MYSQL *mysql_conn = get_mysql_conn();
56     int errid;
57     if((errid = mysql_ping(mysql_conn))) {
58         if(mysql_errno(mysql_conn) == CR_SERVER_GONE_ERROR) {
59             if(!mysql_real_connect(mysql_conn, mysql_host, mysql_user, mysql_pass, mysql_base, mysql_serverport, NULL, 0)) {
60                 show_mysql_error();
61             }
62         } else {
63             //mysql error
64             show_mysql_error();
65         }
66     }
67 }
68
69 MYSQL_RES *mysql_use() {
70     struct mysql_conn_struct *mysql_conn = get_mysql_conn_struct();
71     MYSQL_RES *res = mysql_store_result(mysql_conn->mysql_conn);
72     struct used_result *result = malloc(sizeof(*result));
73     if (!result) {
74         mysql_free_result(res);
75         return NULL;
76     }
77     result->result = res;
78     result->next = mysql_conn->used_results;
79     mysql_conn->used_results = result;
80     return res;
81 }
82
83 void mysql_free() {
84     struct mysql_conn_struct *mysql_conn = get_mysql_conn_struct();
85     if(!mysql_conn) return;
86     struct used_result *result, *next_result;
87     for(result = mysql_conn->used_results; result; result = next_result) {
88         next_result = result->next;
89         mysql_free_result(result->result);
90         free(result);
91     }
92     mysql_conn->used_results = NULL;
93     struct escaped_string *escaped, *next_escaped;
94     for(escaped = mysql_conn->escaped_strings; escaped; escaped = next_escaped) {
95         next_escaped = escaped->next;
96         free(escaped->string);
97         free(escaped);
98     }
99     mysql_conn->escaped_strings = NULL;
100 }
101
102 int reload_mysql() {
103     char *new_mysql_host = get_string_field("MySQL.host");
104     char *new_mysql_user = get_string_field("MySQL.user");
105     char *new_mysql_pass = get_string_field("MySQL.pass");
106     char *new_mysql_base = get_string_field("MySQL.base");
107     if(!(new_mysql_host && new_mysql_user && new_mysql_pass && new_mysql_base))
108         return 0;
109     
110     //replace login data
111     if(mysql_host)
112         free(mysql_host);
113     mysql_host = strdup(new_mysql_host);
114     
115     if(mysql_user)
116         free(mysql_user);
117     mysql_user = strdup(new_mysql_user);
118     
119     if(mysql_pass)
120         free(mysql_pass);
121     mysql_pass = strdup(new_mysql_pass);
122     
123     if(mysql_base)
124         free(mysql_base);
125     mysql_base = strdup(new_mysql_base);
126     
127     mysql_serverport = get_int_field("MySQL.port");
128     if(!mysql_serverport)
129         mysql_serverport = 3306;
130     return 1;
131 }
132
133 void init_mysql() {
134     THREAD_MUTEX_INIT(synchronized);
135     
136     MYSQL *mysql_conn = get_mysql_conn();
137     
138     //check database version...
139     int version = 0;
140     if(!mysql_query(mysql_conn, "SELECT `database_version` FROM `version`")) {
141         MYSQL_RES *res = mysql_use();
142         MYSQL_ROW row;
143         if((row = mysql_fetch_row(res))) {
144             version = atoi(row[0]);
145         }
146     }
147     if(!version) {
148         //CREATE DATABASE
149         FILE *f = fopen("database.sql", "r");
150         mysql_set_server_option(mysql_conn, MYSQL_OPTION_MULTI_STATEMENTS_ON);
151         if (f) {
152             char line[512];
153             char query_buffer[8192];
154             int query_buffer_pos = 0;
155             while (fgets(line, sizeof(line), f)) {
156                 query_buffer_pos += sprintf(query_buffer + query_buffer_pos, " %s", line);
157                 if(line[(strlen(line) - 2)] == ';') {
158                     if(mysql_query(mysql_conn, query_buffer))
159                         show_mysql_error();
160                     query_buffer_pos = 0;
161                 }
162             }
163             fclose(f);
164         } else
165             printf_log("main", LOG_ERROR | LOG_MYSQL, "File not found: database.sql");
166         f = fopen("database.defaults.sql", "r");
167         if (f) {
168             char line[4096];
169             char query_buffer[131072];
170             int query_buffer_pos = 0;
171             while (fgets(line, sizeof(line), f)) {
172                 query_buffer_pos += sprintf(query_buffer + query_buffer_pos, " %s", line);
173                 if(line[(strlen(line) - 2)] == ';') {
174                     if(mysql_query(mysql_conn, query_buffer))
175                         show_mysql_error();
176                     query_buffer_pos = 0;
177                 }
178             }
179             fclose(f);
180         } else
181             printf_log("main", LOG_ERROR | LOG_MYSQL, "File not found: database.defaults.sql");
182         do { 
183             MYSQL_RES *res = mysql_store_result(mysql_conn); 
184             mysql_free_result(res); 
185         } while(!mysql_next_result(mysql_conn));
186         mysql_set_server_option(mysql_conn, MYSQL_OPTION_MULTI_STATEMENTS_OFF);
187         mysql_query(mysql_conn, "INSERT INTO `version` (`database_version`) VALUES ('" DATABASE_VERSION "')");
188     }
189     else if(version < atoi(DATABASE_VERSION)) {
190         //UPDATE DATABASE
191         FILE *f = fopen("database.upgrade.sql", "r");
192         mysql_set_server_option(mysql_conn, MYSQL_OPTION_MULTI_STATEMENTS_ON);
193         if (f) {
194             char line[512];
195             char query_buffer[8192];
196             int query_buffer_pos = 0, use_querys = 0;
197             sprintf(query_buffer, "-- version: %d", version);
198             while (fgets(line, sizeof(line), f)) {
199                 if(use_querys) {
200                     query_buffer_pos += sprintf(query_buffer + query_buffer_pos, " %s", line);
201                     if(line[strlen(line) - 1] == ';') {
202                         mysql_query(mysql_conn, query_buffer);
203                         query_buffer_pos = 0;
204                     }
205                 } else if(!stricmplen(query_buffer, line, strlen(query_buffer))) {
206                     use_querys = 1;
207                 }
208             }
209             if(query_buffer_pos) {
210                 if(mysql_query(mysql_conn, query_buffer))
211                     show_mysql_error();
212             }
213             fclose(f);
214         } else
215             printf_log("main", LOG_ERROR | LOG_MYSQL, "File not found: database.upgrade.sql");
216         do { 
217             MYSQL_RES *res = mysql_store_result(mysql_conn); 
218             mysql_free_result(res); 
219         } while(!mysql_next_result(mysql_conn));
220         mysql_set_server_option(mysql_conn, MYSQL_OPTION_MULTI_STATEMENTS_OFF);
221         mysql_query(mysql_conn, "UPDATE `version` SET `database_version` = '" DATABASE_VERSION "'");
222     }
223 }
224
225 void free_mysql() {
226     struct mysql_conn_struct *mysql_conn, *next;
227     for(mysql_conn = mysql_conns; mysql_conn; mysql_conn = next) {
228         next = mysql_conn->next;
229         mysql_close(mysql_conn->mysql_conn);
230         free(mysql_conn);
231     }
232     mysql_conns = NULL;
233 }
234
235 static void show_mysql_error() {
236     MYSQL *mysql_conn = get_mysql_conn();
237     //show mysql_error()
238     printf_log("main", LOG_ERROR | LOG_MYSQL, "Error: %s\n", mysql_error(mysql_conn));
239 }
240
241 void printf_mysql_query(const char *text, ...) {
242     MYSQL *mysql_conn = get_mysql_conn();
243     va_list arg_list;
244     char queryBuf[MYSQLMAXLEN];
245     int pos;
246     queryBuf[0] = '\0';
247     va_start(arg_list, text);
248     pos = vsnprintf(queryBuf, MYSQLMAXLEN - 2, text, arg_list);
249     va_end(arg_list);
250     if (pos < 0 || pos > (MYSQLMAXLEN - 2)) pos = MYSQLMAXLEN - 2;
251     queryBuf[pos] = '\0';
252     printf_log("main", LOG_MYSQL, "%s\n", queryBuf);
253     if(mysql_query(mysql_conn, queryBuf)) {
254         check_mysql();
255         if(mysql_query(mysql_conn, queryBuf)) {
256             show_mysql_error();
257         }
258     }
259 }
260
261 void printf_long_mysql_query(int len, const char *text, ...) {
262     MYSQL *mysql_conn = get_mysql_conn();
263     va_list arg_list;
264     char queryBuf[len];
265     int pos;
266     queryBuf[0] = '\0';
267     va_start(arg_list, text);
268     pos = vsnprintf(queryBuf, len - 2, text, arg_list);
269     va_end(arg_list);
270     if (pos < 0 || pos > (len - 2)) pos = len - 2;
271     queryBuf[pos] = '\0';
272     printf_log("main", LOG_MYSQL, "%s\n", queryBuf);
273     if(mysql_query(mysql_conn, queryBuf)) {
274         check_mysql();
275         if(mysql_query(mysql_conn, queryBuf)) {
276             show_mysql_error();
277         }
278     }
279 }
280
281 char* escape_string(const char *str) {
282     struct mysql_conn_struct *mysql_conn = get_mysql_conn_struct();
283     struct escaped_string *escapedstr = malloc(sizeof(*escapedstr));
284     if (!escapedstr) {
285         return NULL;
286     }
287     char escaped[strlen(str)*2+1];
288     mysql_real_escape_string(mysql_conn->mysql_conn, escaped, str, strlen(str));
289     escapedstr->string = strdup(escaped);
290     escapedstr->next = mysql_conn->escaped_strings;
291     mysql_conn->escaped_strings = escapedstr;
292     return escapedstr->string;
293 }
294
295 struct mysql_conn_struct *get_mysql_conn_struct() {
296     SYNCHRONIZE(synchronized);
297     struct mysql_conn_struct *mysql_conn;
298     unsigned int tid;
299     #ifdef HAVE_THREADS
300     tid = (unsigned int) pthread_self_tid();
301     #else
302     tid = 1;
303     #endif
304     for(mysql_conn = mysql_conns; mysql_conn; mysql_conn = mysql_conn->next) {
305         if(mysql_conn->tid == tid) {
306             DESYNCHRONIZE(synchronized);
307             return mysql_conn;
308         }
309     }
310     mysql_conn = malloc(sizeof(*mysql_conn));
311     mysql_conn->mysql_conn = mysql_init(NULL);
312     mysql_conn->tid = tid;
313     mysql_conn->used_results = NULL;
314     mysql_conn->escaped_strings = NULL;
315     mysql_conn->next = mysql_conns;
316     mysql_conns = mysql_conn;
317     if (!mysql_real_connect(mysql_conn->mysql_conn, mysql_host, mysql_user, mysql_pass, mysql_base, mysql_serverport, NULL, 0)) {
318         //error
319         show_mysql_error();
320     }
321     DESYNCHRONIZE(synchronized);
322     return mysql_conn;
323 }
324
325 MYSQL *get_mysql_conn() {
326     struct mysql_conn_struct *mysql_conn = get_mysql_conn_struct();
327     return mysql_conn->mysql_conn;
328 }