strip added smb:// shares of their user/pass when adding, and instead store that...
[vuplus_xbmc] / lib / cmyth / libcmyth / mysql_query.c
1 /*
2  *  Copyright (C) 2006, Simon Hyde
3  *  http://www.mvpmc.org/
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2.1 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <sys/types.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <cmyth_local.h>
24
25 #define CMYTH_ULONG_STRLEN ((sizeof(long)*3)+1)
26 #define CMYTH_LONG_STRLEN (CMYTH_ULONG_STRLEN+1)
27
28 /**
29  * Hold in-progress query
30  */
31 struct cmyth_mysql_query_s
32 {
33     char * buf;
34     const char * source;
35     const char * source_pos;
36     int buf_size, buf_used, source_len;
37     cmyth_database_t db;
38 };
39
40
41 /**
42  * Internal only! Registered as callback to de-allocate actual buffer if
43  * the query is de-allocated
44  * \param p pointer to the query data structure
45  */
46 static void
47 query_destroy(void *p)
48 {
49     cmyth_mysql_query_t * query = (cmyth_mysql_query_t *)p;
50     if(query->buf != NULL)
51     {
52         ref_release(query->buf);
53         query->buf = NULL;
54         query->buf_size = 0;
55     }
56     if(query->db != NULL)
57     {
58         ref_release(query->db);
59         query->db = NULL;
60     }
61 }
62
63 /**
64  * Allocate a dynamic query string to have parameters added to it
65  * \param db database connection object
66  * \param query_string Query string with ? placemarks for all dynamic
67  *                      parameters, this is NOT copied and must therefore
68  *                      remain valid for the life of the query.
69  */
70 cmyth_mysql_query_t * 
71 cmyth_mysql_query_create(cmyth_database_t db, const char * query_string)
72 {
73     cmyth_mysql_query_t * out;
74     out = ref_alloc(sizeof(*out));
75     if(out != NULL)
76     {
77         ref_set_destroy(out,query_destroy);
78         out->source = out->source_pos = query_string;
79         out->source_len = strlen(out->source);
80         out->buf_size = out->source_len *2;
81         out->buf_used = 0;
82         out->db = ref_hold(db);
83         out->buf = ref_alloc(out->buf_size);
84         if(out->buf == NULL)
85         {
86             ref_release(out);
87             out = NULL;
88         }
89         else
90         {
91             out->buf[0] = '\0';
92         }
93
94     }
95     return out;
96 }
97
98 void
99 cmyth_mysql_query_reset(cmyth_mysql_query_t *query)
100 {
101     query->buf_used = 0;
102     query->source_pos = query->source;
103 }
104
105 static int
106 query_buffer_check_len(cmyth_mysql_query_t *query, int len)
107 {
108     if(len + query->buf_used >= query->buf_size)
109     {
110         /* Increase buffer size by len or out->source_len, whichever
111          * is bigger
112          */
113         if(query->source_len > len) 
114             query->buf_size += query->source_len;
115         else
116             query->buf_size += len;
117         query->buf = ref_realloc(query->buf,query->buf_size);
118         if(query->buf == NULL)
119         {
120             cmyth_mysql_query_reset(query);
121             return -1;
122         }
123     }
124     return 0;
125 }
126
127 static int
128 query_buffer_add(cmyth_mysql_query_t *query, const char *buf,int len)
129 {
130     int ret = query_buffer_check_len(query,len);
131     if(ret < 0)
132         return ret;
133     memcpy(query->buf + query->buf_used,buf,len);
134     query->buf_used +=len;
135     query->buf[query->buf_used] = '\0';
136     return len;
137 }
138
139 static inline int
140 query_buffer_add_str(cmyth_mysql_query_t *query, const char *str)
141 {
142     return query_buffer_add(query,str,strlen(str));
143 }
144
145 static int
146 query_buffer_add_escape_str(cmyth_mysql_query_t *query, const char *str)
147 {
148     int ret;
149     int srclen = strlen(str);
150     MYSQL * mysql;
151     unsigned long destlen;
152     /*According to the mysql C API refrence, there must be sourcelen*2 +1
153      * characters of space in the destination buffer
154      */
155     ret = query_buffer_check_len(query,srclen*2 +1);
156     if(ret < 0)
157         return ret;
158     mysql = cmyth_db_get_connection(query->db);
159     if(mysql == NULL)
160         return -1;
161     destlen = mysql_real_escape_string(mysql, query->buf + query->buf_used,
162                                         str, srclen);
163     query->buf_used += destlen;
164     /* MySQL claims it null terminates, but do so anyway just in case we've
165      * done something stupid
166      */
167     query->buf[query->buf_used] = '\0';
168     return destlen;
169 }
170
171 static int
172 query_begin_next_param(cmyth_mysql_query_t *query)
173 {
174     int len,ret;
175     const char * endpos = strchr(query->source_pos,(int)'?');
176     /*No more parameter insertion points left!*/
177     if(endpos == NULL)
178         return -1;
179     len = endpos - query->source_pos;
180     ret = query_buffer_add(query,query->source_pos,len);
181     query->source_pos = endpos + 1;
182     return ret;
183 }
184
185 static inline int
186 query_buffer_add_long(cmyth_mysql_query_t * query, long param)
187 {
188     char buf[CMYTH_LONG_STRLEN];
189     sprintf(buf,"%ld",param);
190     return query_buffer_add_str(query,buf);
191 }
192
193 static inline int
194 query_buffer_add_ulong(cmyth_mysql_query_t * query, long param)
195 {
196     char buf[CMYTH_ULONG_STRLEN];
197     sprintf(buf,"%lu",param);
198     return query_buffer_add_str(query,buf);
199 }
200
201 /**
202  * Add a long integer parameter
203  * \param query the query object
204  * \param param the integer to add
205  */
206 int
207 cmyth_mysql_query_param_long(cmyth_mysql_query_t * query,long param)
208 {
209     int ret;
210     ret = query_begin_next_param(query);
211     if(ret < 0)
212         return ret;
213     return query_buffer_add_long(query,param);
214 }
215
216 /**
217  * Add an unsigned long integer parameter
218  * \param query the query object
219  * \param param the integer to add
220  */
221 int
222 cmyth_mysql_query_param_ulong(cmyth_mysql_query_t * query,unsigned long param)
223 {
224     int ret;
225     ret = query_begin_next_param(query);
226     if(ret < 0)
227         return ret;
228     return query_buffer_add_ulong(query,param);
229 }
230
231 /**
232  * Add an integer parameter
233  * \param query the query object
234  * \param param the integer to add
235  */
236 int
237 cmyth_mysql_query_param_int(cmyth_mysql_query_t * query,int param)
238 {
239     return cmyth_mysql_query_param_long(query,(long)param);
240 }
241
242 /**
243  * Add an unsigned integer parameter
244  * \param query the query object
245  * \param param the integer to add
246  */
247 int
248 cmyth_mysql_query_param_uint(cmyth_mysql_query_t * query,int param)
249 {
250     return cmyth_mysql_query_param_ulong(query,(unsigned long)param);
251 }
252
253 /**
254  * Add, and convert a unixtime to mysql date/timestamp
255  * \param query the query object
256  * \param param the time to add
257  */
258 int
259 cmyth_mysql_query_param_unixtime(cmyth_mysql_query_t * query, time_t param)
260 {
261     int ret;
262     ret = query_begin_next_param(query);
263     if(ret < 0)
264         return ret;
265     ret = query_buffer_add_str(query,"FROM_UNIXTIME(");
266     if(ret < 0)
267         return ret;
268     ret = query_buffer_add_long(query,(long)param);
269     if(ret < 0)
270         return ret;
271     return query_buffer_add_str(query,")");
272 }
273
274
275 /**
276  * Add (including adding quotes), and escape a string parameter.
277  * \param query the query object
278  * \param param the string to add
279  */
280 int
281 cmyth_mysql_query_param_str(cmyth_mysql_query_t * query, const char *param)
282 {
283     int ret;
284     ret = query_begin_next_param(query);
285     if(ret < 0)
286         return ret;
287     if(param == NULL)
288         return query_buffer_add_str(query,"NULL");
289     ret = query_buffer_add_str(query,"'");
290     if(ret < 0)
291         return ret;
292     ret = query_buffer_add_escape_str(query,param);
293     if(ret < 0)
294         return ret;
295     return query_buffer_add_str(query,"'");
296 }
297
298 /**
299  * Get the completed query string
300  * \return If all fields haven't been filled, or there is some other failure
301  *      this will return NULL, otherwise a string is returned. The returned
302  *      string must be released by the caller using ref_release().
303  */
304 char *
305 cmyth_mysql_query_string(cmyth_mysql_query_t * query)
306 {
307     if(strchr(query->source_pos, (int)'?') != NULL)
308     {
309         return NULL;/*Still more parameters to be added*/
310     }
311     if(query_buffer_add_str(query,query->source_pos) < 0)
312         return NULL;
313     /*Point source_pos to the '\0' at the end of the string so this can
314      * be called multiple times
315      */
316     query->source_pos = query->source + query->source_len;
317     return ref_hold(query->buf);
318 }
319
320
321 MYSQL_RES *
322 cmyth_mysql_query_result(cmyth_mysql_query_t * query)
323 {
324     MYSQL_RES * retval = NULL;
325     int ret;
326     char * query_str;
327     MYSQL *mysql = cmyth_db_get_connection(query->db);
328     if(mysql == NULL)
329         return NULL;
330     query_str = cmyth_mysql_query_string(query);
331     if(query_str == NULL)
332         return NULL;
333     ret = mysql_query(mysql,query_str);
334     ref_release(query_str);
335     if(ret != 0)
336     {
337          cmyth_dbg(CMYTH_DBG_ERROR, "%s: mysql_query(%s) Failed: %s\n",
338                                 __FUNCTION__, query_str, mysql_error(mysql));
339          return NULL;
340     }
341     retval = mysql_store_result(mysql);
342     if(retval == NULL)
343     {
344          cmyth_dbg(CMYTH_DBG_ERROR, "%s: mysql_use_result Failed: %s\n",
345                                 __FUNCTION__, query_str, mysql_error(mysql));
346     }
347     return retval;
348 }
349
350 int
351 cmyth_mysql_query(cmyth_mysql_query_t * query)
352 {
353         int ret;
354         char * query_str;
355         MYSQL *mysql = cmyth_db_get_connection(query->db);
356         if(mysql == NULL)
357                 return -1;
358         query_str = cmyth_mysql_query_string(query);
359         if(query_str == NULL)
360                 return -1;
361         ret = mysql_query(mysql,query_str);
362         ref_release(query_str);
363         if(ret != 0)
364         {
365                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: mysql_query(%s) Failed: %s\n",
366                                 __FUNCTION__, query_str, mysql_error(mysql));
367                 return -1;
368         }
369         return 0;
370 }