db_postgres: database URL supports IPv6 address as hostname
[sip-router] / src / modules / db_postgres / pg_uri.c
1 /* 
2  * PostgreSQL Database Driver for Kamailio
3  *
4  * Portions Copyright (C) 2001-2003 FhG FOKUS
5  * Copyright (C) 2003 August.Net Services, LLC
6  * Portions Copyright (C) 2005-2008 iptelorg GmbH
7  *
8  * This file is part of Kamailio, a free SIP server.
9  *
10  * Kamailio is free software; you can redistribute it and/or modify it under the
11  * terms of the GNU General Public License as published by the Free Software
12  * Foundation; either version 2 of the License, or (at your option) any later
13  * version
14  *
15  * Kamailio is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc., 59
22  * Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  */
24
25 /** \addtogroup postgres
26  * @{ 
27  */
28
29 /** \file 
30  * The implementation of parser parsing postgres://.. URIs.
31  */
32
33 #include "pg_uri.h"
34
35 #include "../../core/dprint.h"
36 #include "../../core/mem/mem.h"
37 #include "../../core/ut.h"
38 #include "../../lib/srdb2/db_gen.h"
39
40 #include <stdlib.h>
41 #include <string.h>
42
43
44 /** compare s1 & s2  with a function f (which should return 0 if ==);
45  * s1 & s2 can be null
46  * return 0 if match, 1 if not 
47  */
48 #define cmpstr(s1, s2, f) \
49         ((s1) != (s2)) && ((s1) == 0 || (s2) == 0 || (f)((s1), (s2)) != 0)
50
51
52 /** Compare two connection URIs */
53 static unsigned char pg_uri_cmp(db_uri_t *uri1, db_uri_t *uri2)
54 {
55         struct pg_uri *puri1, *puri2;
56
57         if(!uri1 || !uri2)
58                 return 0;
59
60         puri1 = DB_GET_PAYLOAD(uri1);
61         puri2 = DB_GET_PAYLOAD(uri2);
62         if(puri1->port != puri2->port)
63                 return 0;
64
65         if(cmpstr(puri1->username, puri2->username, strcmp))
66                 return 0;
67         if(cmpstr(puri1->password, puri2->password, strcmp))
68                 return 0;
69         if(cmpstr(puri1->host, puri2->host, strcasecmp))
70                 return 0;
71         if(cmpstr(puri1->database, puri2->database, strcmp))
72                 return 0;
73         return 1;
74 }
75
76
77 /** Duplicate a string
78  */
79 static int dupl_string(char **dst, const char *begin, const char *end)
80 {
81         if(*dst)
82                 pkg_free(*dst);
83
84         *dst = pkg_malloc(end - begin + 1);
85         if((*dst) == NULL) {
86                 PKG_MEM_ERROR;
87                 return -1;
88         }
89
90         memcpy(*dst, begin, end - begin);
91         (*dst)[end - begin] = '\0';
92         return 0;
93 }
94
95
96 /** Parses postgres URI of form 
97  * //[username[:password]@]hostname[:port]/database
98  *
99  * Returns 0 if parsing was successful and -1 otherwise
100  */
101 static int parse_postgres_uri(struct pg_uri *res, str *uri)
102 {
103 #define SHORTEST_DB_URL "//a/b"
104 #define SHORTEST_DB_URL_LEN (sizeof(SHORTEST_DB_URL) - 1)
105
106         enum state
107         {
108                 ST_SLASH1,      /* First slash */
109                 ST_SLASH2,      /* Second slash */
110                 ST_USER_HOST, /* Username or hostname */
111                 ST_PASS_PORT, /* Password or port part */
112                 ST_HOST,          /* Hostname part */
113                 ST_HOST6,         /* Hostname part IPv6 */
114                 ST_PORT,          /* Port part */
115                 ST_DB             /* Database part */
116         };
117
118         enum state st;
119         int i, ipv6_flag=0;
120         const char *begin;
121         char *prev_token;
122
123         prev_token = 0;
124
125         if(!res || !uri) {
126                 goto err;
127         }
128
129         if(uri->len < SHORTEST_DB_URL_LEN) {
130                 goto err;
131         }
132
133         st = ST_SLASH1;
134         begin = uri->s;
135
136         for(i = 0; i < uri->len; i++) {
137                 switch(st) {
138                         case ST_SLASH1:
139                                 switch(uri->s[i]) {
140                                         case '/':
141                                                 st = ST_SLASH2;
142                                                 break;
143
144                                         default:
145                                                 goto err;
146                                 }
147                                 break;
148
149                         case ST_SLASH2:
150                                 switch(uri->s[i]) {
151                                         case '/':
152                                                 st = ST_USER_HOST;
153                                                 begin = uri->s + i + 1;
154                                                 break;
155
156                                         default:
157                                                 goto err;
158                                 }
159                                 break;
160
161                         case ST_USER_HOST:
162                                 switch(uri->s[i]) {
163                                         case '@':
164                                                 st = ST_HOST;
165                                                 if(dupl_string(&res->username, begin, uri->s + i) < 0)
166                                                         goto err;
167                                                 begin = uri->s + i + 1;
168                                                 break;
169
170                                         case ':':
171                                                 st = ST_PASS_PORT;
172                                                 if(dupl_string(&prev_token, begin, uri->s + i) < 0)
173                                                         goto err;
174                                                 begin = uri->s + i + 1;
175                                                 break;
176
177                                         case '[':
178                                                 st = ST_HOST6;
179                                                 begin = uri->s + i + 1;
180                                                 break;
181
182                                         case '/':
183                                                 if(memchr(uri->s + i + 1, '/', uri->len - i - 1)
184                                                                 != NULL)
185                                                         break;
186                                                 if(dupl_string(&res->host, begin, uri->s + i) < 0)
187                                                         goto err;
188                                                 if(dupl_string(&res->database, uri->s + i + 1,
189                                                                    uri->s + uri->len)
190                                                                 < 0)
191                                                         goto err;
192                                                 return 0;
193                                 }
194                                 break;
195
196                         case ST_PASS_PORT:
197                                 switch(uri->s[i]) {
198                                         case '@':
199                                                 st = ST_HOST;
200                                                 res->username = prev_token;
201                                                 if(dupl_string(&res->password, begin, uri->s + i) < 0)
202                                                         goto err;
203                                                 begin = uri->s + i + 1;
204                                                 break;
205
206                                         case '/':
207                                                 if(memchr(uri->s + i + 1, '/', uri->len - i - 1)
208                                                                 != NULL)
209                                                         break;
210                                                 res->host = prev_token;
211                                                 res->port = str2s(begin, uri->s + i - begin, 0);
212                                                 if(dupl_string(&res->database, uri->s + i + 1,
213                                                                    uri->s + uri->len)
214                                                                 < 0)
215                                                         goto err;
216                                                 return 0;
217                                 }
218                                 break;
219
220                         case ST_HOST:
221                                 switch(uri->s[i]) {
222                                         case '[':
223                                                 st = ST_HOST6;
224                                                 begin = uri->s + i + 1;
225                                                 break;
226
227                                         case ':':
228                                                 st = ST_PORT;
229                                                 if(dupl_string(&res->host, begin, uri->s + i - ipv6_flag) < 0)
230                                                         goto err;
231                                                 begin = uri->s + i + 1;
232                                                 break;
233
234                                         case '/':
235                                                 if(memchr(uri->s + i + 1, '/', uri->len - i - 1)
236                                                                 != NULL)
237                                                         break;
238                                                 if(dupl_string(&res->host, begin, uri->s + i - ipv6_flag) < 0)
239                                                         goto err;
240                                                 if(dupl_string(&res->database, uri->s + i + 1,
241                                                                    uri->s + uri->len)
242                                                                 < 0)
243                                                         goto err;
244                                                 return 0;
245                                 }
246                                 break;
247
248                         case ST_HOST6:
249                                 switch(uri->s[i]) {
250                                         case ']':
251                                                 ipv6_flag = 1;
252                                                 st = ST_HOST;
253                                                 break;
254                                 }
255                                 break;
256
257                         case ST_PORT:
258                                 switch(uri->s[i]) {
259                                         case '/':
260                                                 res->port = str2s(begin, uri->s + i - begin, 0);
261                                                 if(dupl_string(&res->database, uri->s + i + 1,
262                                                                    uri->s + uri->len)
263                                                                 < 0)
264                                                         goto err;
265                                                 return 0;
266                                 }
267                                 break;
268
269                         case ST_DB:
270                                 break;
271                 }
272         }
273
274         if(st != ST_DB)
275                 goto err;
276         return 0;
277
278 err:
279         if(prev_token) {
280                 if(res==NULL || (res->username!=prev_token && res->host!=prev_token))
281                         pkg_free(prev_token);
282         }
283         if(res == NULL)
284                 return -1;
285         if(res->username) {
286                 pkg_free(res->username);
287                 res->username = NULL;
288         }
289         if(res->password) {
290                 pkg_free(res->password);
291                 res->password = NULL;
292         }
293         if(res->host) {
294                 pkg_free(res->host);
295                 res->host = NULL;
296         }
297         if(res->database) {
298                 pkg_free(res->database);
299                 res->database = NULL;
300         }
301         return -1;
302 }
303
304
305 static void pg_uri_free(db_uri_t *uri, struct pg_uri *payload)
306 {
307         if(payload == NULL)
308                 return;
309         db_drv_free(&payload->drv);
310         if(payload->username)
311                 pkg_free(payload->username);
312         if(payload->password)
313                 pkg_free(payload->password);
314         if(payload->host)
315                 pkg_free(payload->host);
316         if(payload->database)
317                 pkg_free(payload->database);
318         pkg_free(payload);
319 }
320
321
322 int pg_uri(db_uri_t *uri)
323 {
324         struct pg_uri *puri;
325
326         puri = (struct pg_uri *)pkg_malloc(sizeof(struct pg_uri));
327         if(puri == NULL) {
328                 PKG_MEM_ERROR;
329                 goto error;
330         }
331         memset(puri, '\0', sizeof(struct pg_uri));
332         if(db_drv_init(&puri->drv, pg_uri_free) < 0)
333                 goto error;
334         if(parse_postgres_uri(puri, &uri->body) < 0)
335                 goto error;
336
337         DB_SET_PAYLOAD(uri, puri);
338         uri->cmp = pg_uri_cmp;
339         return 0;
340
341 error:
342         if(puri) {
343                 db_drv_free(&puri->drv);
344                 if(puri)
345                         pkg_free(puri);
346         }
347         return -1;
348 }
349
350 /** @} */