1 /**********************************************************************
2  * perditionb_mysql.c                                     December 1999
3  * Horms                                             horms@verge.net.au
4  *
5  * Access a MySQL database
6  *
7  * Adapted from code contributed by:
8  * Frederic Delchambre                                     October 1999
9  * N.T.S. / Freegates                                dedel@freegates.be
10  *                                             http://www.freegates.be/
11  *                                                   http://www.nts.be/
12  * perdition
13  * Mail retrieval proxy server, MySQL support
14  * Copyright (C) 1999-2005  Horms and Frederic Delchambre
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License as
18  * published by the Free Software Foundation; either version 2 of the
19  * License, or (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful, but
22  * WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software Foundation,
28  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
29  *
30  **********************************************************************/
31 
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 
36 #include "perditiondb_mysql.h"
37 #include "unused.h"
38 
39 #ifdef DMALLOC
40 #include <dmalloc.h>
41 #endif
42 
43 
44 static vanessa_dynamic_array_t *a=NULL;
45 static vanessa_dynamic_array_t *dbhosts_array=NULL;
46 static size_t dbhosts_count = 0;
47 static char *dbhosts         = PERDITIONDB_MYSQL_DEFAULT_DBHOSTS;
48 static char *dbname          = PERDITIONDB_MYSQL_DEFAULT_DBNAME;
49 static unsigned int dbport   = PERDITIONDB_MYSQL_DEFAULT_DBPORT;
50 static char *dbtable         = PERDITIONDB_MYSQL_DEFAULT_DBTABLE;
51 static char *dbuser          = PERDITIONDB_MYSQL_DEFAULT_DBUSER;
52 static char *dbpwd           = PERDITIONDB_MYSQL_DEFAULT_DBPWD;
53 static char *db_user_col     = PERDITIONDB_MYSQL_DEFAULT_DBUSERCOL;
54 static char *db_srv_col      = PERDITIONDB_MYSQL_DEFAULT_DBSRVCOL;
55 static char *db_port_col     = PERDITIONDB_MYSQL_DEFAULT_DBPORTCOL;
56 
57 
58 /**********************************************************************
59  * perditiondb_mysql_log
60  * Show an error message with mysql errors
61  * pre: msg_str: message to prepent to message
62  *      db: mysql database that error is for
63  * post: msg_str is logged to VANESSA_LOGGER_DEBUG with mysql error appended
64  * return: none
65  **********************************************************************/
66 
67 #define perditiondb_mysql_log(msg_str, db) \
68    VANESSA_LOGGER_DEBUG_UNSAFE("%s: %s", msg_str, mysql_error(db))
69 
70 
71 /**********************************************************************
72  * dbserver_fini
73  * Free static vanessa_dynamic_array_t a if it has been initialised
74  * pre: none
75  * post: static vanessa_dynamic_array_t a and its contents are freed
76  * return:  0 on success
77  *         -1 on db access error
78  *            This inclides file, connection and other data access
79  *            errors. It does not cover memory allocation problems.
80  *         -2 if key cannot be found in map
81  *         -3 on other error
82  **********************************************************************/
83 
dbserver_fini(void)84 int dbserver_fini(void){
85    if(a!=NULL){
86      vanessa_dynamic_array_destroy(a);
87      a=NULL;
88    }
89    return(0);
90 }
91 
92 
93 /**********************************************************************
94  * dbserver_init
95  * Parse options string.
96  * pre: options_str: Options string. String is of the form
97  * [dbhost1[,dbhost2[,...]][:port[:dbname[:dbtable[:dbuser[:dbpwd[:dbsrvcol[:dbusercol[:dbportcol]]]]]]]]]
98  * post: Options string is parsed if not null into
99  *       static vanessa_dynamic_array_t a and
100  *       static char *dbhosts, *dbname, *dbtable, *dbuser, *dbpwd are
101  *       set to pointers insiside a or defaults as neccesary.
102  * return:  0 on success
103  *         -1 on db access error
104  *            This inclides file, connection and other data access
105  *            errors. It does not cover memory allocation problems.
106  *         -2 if key cannot be found in map
107  *         -3 on other error
108  **********************************************************************/
109 
dbserver_init(char * options_str)110 int dbserver_init(char *options_str){
111    int count;
112    char *tmp_str;
113 
114    if(options_str==NULL || a!=NULL){
115      return(0);
116    }
117 
118    if((tmp_str=strdup(options_str))==NULL) {
119      VANESSA_LOGGER_DEBUG_ERRNO("strdup");
120      a=NULL;
121      return(-1);
122    }
123 
124    if((a=vanessa_dynamic_array_split_str(
125      tmp_str,
126      PERDITIONDB_MYSQL_FIELD_DELIMITER
127    ))==NULL){
128      VANESSA_LOGGER_DEBUG("vanessa_dynamic_array_split_str");
129      a=NULL;
130      free(tmp_str);
131      return(-1);
132    }
133 
134    count=vanessa_dynamic_array_get_count(a);
135    if(count>PERDITIONDB_MYSQL_DBHOSTS){
136      dbhosts=vanessa_dynamic_array_get_element(a, PERDITIONDB_MYSQL_DBHOSTS);
137      if ((dbhosts_array = vanessa_dynamic_array_split_str(dbhosts,
138 			  PERDITIONDB_MYSQL_HOSTS_DELIMITER)) == NULL) {
139        VANESSA_LOGGER_DEBUG("vanessa_dynamic_array_split_str");
140        a=NULL;
141        free(tmp_str);
142        return(-1);
143      }
144      dbhosts_count = vanessa_dynamic_array_get_count(dbhosts_array);
145    }
146    if(count>PERDITIONDB_MYSQL_DBNAME){
147      dbname=vanessa_dynamic_array_get_element(a, PERDITIONDB_MYSQL_DBNAME);
148    }
149    if(count>PERDITIONDB_MYSQL_DBPORT){
150      dbport=atoi(vanessa_dynamic_array_get_element(a,
151 	   PERDITIONDB_MYSQL_DBPORT));
152    }
153    if(count>PERDITIONDB_MYSQL_DBTABLE){
154      dbtable=vanessa_dynamic_array_get_element(a, PERDITIONDB_MYSQL_DBTABLE);
155    }
156    if(count>PERDITIONDB_MYSQL_DBUSER){
157      dbuser=vanessa_dynamic_array_get_element(a, PERDITIONDB_MYSQL_DBUSER);
158    }
159    if(count>PERDITIONDB_MYSQL_DBPWD){
160      dbpwd=vanessa_dynamic_array_get_element(a, PERDITIONDB_MYSQL_DBPWD);
161    }
162    if(count>PERDITIONDB_MYSQL_DBSRVCOL){
163      db_srv_col=vanessa_dynamic_array_get_element(a,
164 	 PERDITIONDB_MYSQL_DBSRVCOL
165      );
166    }
167    if(count>PERDITIONDB_MYSQL_DBUSERCOL){
168      db_user_col=vanessa_dynamic_array_get_element(a,
169 	 PERDITIONDB_MYSQL_DBUSERCOL
170      );
171    }
172    if(count>PERDITIONDB_MYSQL_DBPORTCOL){
173      db_port_col=vanessa_dynamic_array_get_element(
174 	 a,
175 	 PERDITIONDB_MYSQL_DBPORTCOL
176      );
177    }
178 
179    free(tmp_str);
180 
181    return(0);
182 }
183 
184 
185 /**********************************************************************
186  * dbserver_get
187  * Read the server information for a given key from the MySQL db
188  * specified in the options string. If fields are missing
189  * from the options string, or it is NULL then defaults are
190  * used as necessary.
191  * pre: key_str:     Key as a null terminated string
192  *      options_str: Options string. The usage of this is
193  *                   implementation dependant.
194  *      str_return:  Value is returned here
195  *      len_return:  Length of value is returned here
196  * post: The str_key is looked up and the corresponding value is
197  *       returned in str_return and len_return.
198  * return:  0 on success
199  *         -1 on db access error
200  *            This inclides file, connection and other data access
201  *            errors. It does not cover memory allocation problems.
202  *         -2 if key cannot be found in map
203  *         -3 on other error
204  * Note: The string returned in str_return should be of the
205  * form <servername>[:<port>].
206  * E.g.: localhost:110
207  *       localhost
208  **********************************************************************/
209 
dbserver_get(const char * key_str,const char * UNUSED (options_str),char ** str_return,size_t * len_return)210 int dbserver_get(
211   const char   *key_str,
212   const char   *UNUSED(options_str),
213   char   **str_return,
214   size_t *len_return
215 ){
216    MYSQL db;
217    MYSQL *rc;
218    MYSQL_RES *res;
219    MYSQL_ROW row;
220    char sqlstr[PERDITIONDB_MYSQL_QUERY_LENGTH];
221    char key_str_escaped[2*PERDITIONDB_MYSQL_QUERY_LENGTH+1];
222    size_t servername_len;
223    size_t hcnt = 0;
224 
225    if (!mysql_init(&db)) {
226      perditiondb_mysql_log("mysql_init", &db);
227      vanessa_dynamic_array_destroy(a);
228      return(-1);
229    }
230 
231    rc = NULL;
232    while (hcnt < dbhosts_count
233         && !(rc=mysql_real_connect(&db,
234                         vanessa_dynamic_array_get_element(dbhosts_array, hcnt),
235                         dbuser,dbpwd,dbname,dbport,NULL,0))) {
236      perditiondb_mysql_log("mysql_connect", &db);
237      hcnt++;
238    }
239    if(!rc){
240      perditiondb_mysql_log("mysql_connect", &db);
241      mysql_close(&db);
242      return(-1);
243    }
244 
245    mysql_real_escape_string(&db,key_str_escaped,key_str,strlen(key_str));
246 
247    if (db_port_col && db_port_col[0]) {
248      if(snprintf(
249        sqlstr,
250        PERDITIONDB_MYSQL_QUERY_LENGTH,
251        "select %s,%s,%s from %s where %s='%s';",
252        db_user_col,
253        db_srv_col,
254        db_port_col,
255        dbtable,
256        db_user_col,
257        key_str_escaped
258      )<0){
259        VANESSA_LOGGER_DEBUG("query truncated, aborting");
260        return(-3);
261      }
262    }
263    else {
264      if(snprintf(
265        sqlstr,
266        PERDITIONDB_MYSQL_QUERY_LENGTH,
267        "select %s,%s from %s where %s='%s';",
268        db_user_col, db_srv_col,
269        dbtable, db_user_col,
270        key_str_escaped
271      )<0){
272        VANESSA_LOGGER_DEBUG("query truncated, aborting");
273        return(-3);
274      }
275    }
276 
277    if (mysql_query(&db, sqlstr)) {
278      perditiondb_mysql_log("mysql_query", &db);
279      mysql_close(&db);
280      return(-1);
281    }
282 
283    res = mysql_store_result(&db);
284    if(!res){
285      perditiondb_mysql_log("mysql_store_result", &db);
286      mysql_close(&db);
287      return(-3);
288    }
289 
290    if(mysql_num_rows(res) == 0){
291      mysql_free_result(res);
292      mysql_close(&db);
293      return(-2);
294    }
295 
296    if((row=mysql_fetch_row(res))==NULL){
297      perditiondb_mysql_log("mysql_fetch_row", &db);
298      mysql_close(&db);
299      return(-3);
300    }
301 
302    if(row[1]==NULL || row[1][0]=='\0'){
303      VANESSA_LOGGER_DEBUG("row[1] is empty");
304      mysql_free_result(res);
305      mysql_close(&db);
306      return(-3);
307    }
308    servername_len=*len_return=1+strlen(row[1]);
309 
310    if (db_port_col && db_port_col[0]){
311      if(row[2]!=NULL && row[2][0]!='\0'){
312        *len_return+=1+strlen(row[2]);
313      }
314    }
315 
316    if((*str_return=(char *)malloc(*len_return))==NULL){
317      VANESSA_LOGGER_DEBUG_ERRNO("malloc");
318      mysql_free_result(res);
319      mysql_close(&db);
320      return(-3);
321    }
322 
323    strcpy(*str_return,row[1]);
324    if (db_port_col && db_port_col[0]){
325      if(row[2]!=NULL && row[2][0]!='\0'){
326        *((*str_return)+servername_len-1)=PERDITIONDB_MYSQL_FIELD_DELIMITER;
327        strcpy((*str_return)+servername_len,row[2]);
328      }
329    }
330 
331    mysql_free_result(res);
332    mysql_close(&db);
333    return(0);
334 }
335