src/lib/srdb1/db_id.c
857b95ea
 /* 
  * Copyright (C) 2001-2005 iptel.org
bc4bf265
  * Copyright (C) 2007-2008 1&1 Internet AG
857b95ea
  *
d520eaf5
  * This file is part of Kamailio, a free SIP server.
857b95ea
  *
d520eaf5
  * Kamailio is free software; you can redistribute it and/or modify
857b95ea
  * 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
  *
d520eaf5
  * Kamailio is distributed in the hope that it will be useful,
857b95ea
  * 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 
  * along with this program; if not, write to the Free Software 
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
857b95ea
  */
 
bc4bf265
 /**
7d14b037
  * \file lib/srdb1/db_id.c
a33b9a87
  * \ingroup db1
bc4bf265
  * \brief Functions for parsing a database URL and work with db identifier.
  */
 
6eb22c8d
 #include "db.h"
857b95ea
 #include "db_id.h"
cf83221d
 #include "../../core/dprint.h"
 #include "../../core/mem/mem.h"
2aa614d4
 #include "../../core/resolve.h"
cf83221d
 #include "../../core/pt.h"
 #include "../../core/ut.h"
857b95ea
 #include <stdlib.h>
 #include <string.h>
 
 
25f17988
 /**
857b95ea
  * Duplicate a string
875f5c94
  * \param dst destination
  * \param begin start of the string
  * \param end end of the string
857b95ea
  */
 static int dupl_string(char** dst, const char* begin, const char* end)
 {
 	if (*dst) pkg_free(*dst);
 
 	*dst = pkg_malloc(end - begin + 1);
 	if ((*dst) == NULL) {
3b0c06ef
 		PKG_MEM_ERROR;
857b95ea
 		return -1;
 	}
 
 	memcpy(*dst, begin, end - begin);
 	(*dst)[end - begin] = '\0';
 	return 0;
 }
 
4da5d898
 /**
  * Duplicate a string name (until a params separator found)
  * \param dst destination
  * \param begin start of the string
  * \param end end of the string
  */
 static int dupl_string_name(char** dst, const char* begin, const char* end)
 {
 	char *p;
 	if (*dst) pkg_free(*dst);
 
 	for(p=(char*)begin; p<end; p++) {
 		if(*p=='?') break;
 	}
 	*dst = pkg_malloc(p - begin + 1);
 	if ((*dst) == NULL) {
3b0c06ef
 		PKG_MEM_ERROR;
4da5d898
 		return -1;
 	}
 
 	memcpy(*dst, begin, p - begin);
 	(*dst)[p - begin] = '\0';
 	return 0;
 }
 
857b95ea
 
25f17988
 /**
857b95ea
  * Parse a database URL of form 
  * scheme://[username[:password]@]hostname[:port]/database
  *
875f5c94
  * \param id filled id struct
  * \param url parsed URL
25f17988
  * \return 0 if parsing was successful and -1 otherwise
857b95ea
  */
b17945f7
 static int parse_db_url(struct db_id* id, const str* url)
857b95ea
 {
 #define SHORTEST_DB_URL "s://a/b"
 #define SHORTEST_DB_URL_LEN (sizeof(SHORTEST_DB_URL) - 1)
 
 	enum state {
 		ST_SCHEME,     /* Scheme part */
 		ST_SLASH1,     /* First slash */
 		ST_SLASH2,     /* Second slash */
 		ST_USER_HOST,  /* Username or hostname */
 		ST_PASS_PORT,  /* Password or port part */
 		ST_HOST,       /* Hostname part */
9e29e262
 		ST_HOST6,      /* Hostname part IPv6 */
857b95ea
 		ST_PORT,       /* Port part */
 		ST_DB          /* Database part */
 	};
 
 	enum state st;
9e29e262
 	unsigned int len, i, j, a, foundanother, ipv6_flag=0;
857b95ea
 	const char* begin;
 	char* prev_token;
2aa614d4
 	str sval = STR_NULL;
857b95ea
 
d419f427
 	foundanother = 0;
857b95ea
 	prev_token = 0;
 
25f17988
 	if (!id || !url || !url->s) {
857b95ea
 		goto err;
 	}
d419f427
 
25f17988
 	len = url->len;
857b95ea
 	if (len < SHORTEST_DB_URL_LEN) {
 		goto err;
 	}
d419f427
 
25f17988
 	/* Initialize all attributes to 0 */
857b95ea
 	memset(id, 0, sizeof(struct db_id));
b17945f7
 	st = ST_SCHEME;
25f17988
 	begin = url->s;
857b95ea
 
 	for(i = 0; i < len; i++) {
 		switch(st) {
 		case ST_SCHEME:
25f17988
 			switch(url->s[i]) {
857b95ea
 			case ':':
 				st = ST_SLASH1;
25f17988
 				if (dupl_string(&id->scheme, begin, url->s + i) < 0) goto err;
857b95ea
 				break;
 			}
 			break;
 
 		case ST_SLASH1:
25f17988
 			switch(url->s[i]) {
857b95ea
 			case '/':
 				st = ST_SLASH2;
 				break;
 
 			default:
 				goto err;
 			}
 			break;
 
 		case ST_SLASH2:
25f17988
 			switch(url->s[i]) {
857b95ea
 			case '/':
 				st = ST_USER_HOST;
25f17988
 				begin = url->s + i + 1;
857b95ea
 				break;
d419f427
 
857b95ea
 			default:
 				goto err;
 			}
 			break;
 
 		case ST_USER_HOST:
25f17988
 			switch(url->s[i]) {
857b95ea
 			case '@':
d419f427
 				/* look for another @ to cope with username@domain user id */
 				if (foundanother == 0) {
 					for (j = i + 1; j < url->len; j++) {
 						if (url->s[j] == '@') {
 							foundanother = 1;
 							break;
 						}
 					}
 					if (foundanother == 1) {
 						/* keep the current @ in the username */
 						st = ST_USER_HOST;
 						break;
 					}
 				}
857b95ea
 				st = ST_HOST;
25f17988
 				if (dupl_string(&id->username, begin, url->s + i) < 0) goto err;
 				begin = url->s + i + 1;
857b95ea
 				break;
 
 			case ':':
 				st = ST_PASS_PORT;
25f17988
 				if (dupl_string(&prev_token, begin, url->s + i) < 0) goto err;
 				begin = url->s + i + 1;
857b95ea
 				break;
 
9e29e262
 			case '[':
 				st = ST_HOST6;
 				begin = url->s + i + 1;
 				break;
 
857b95ea
 			case '/':
25f17988
 				if (dupl_string(&id->host, begin, url->s + i) < 0) goto err;
4da5d898
 				if (dupl_string_name(&id->database, url->s + i + 1, url->s + len) < 0) goto err;
857b95ea
 				return 0;
 			}
 			break;
 
 		case ST_PASS_PORT:
25f17988
 			switch(url->s[i]) {
857b95ea
 			case '@':
 				st = ST_HOST;
 				id->username = prev_token;
765c17f8
 				prev_token = 0;
c02e165c
 				a = 0;
 				/* go to last '@' to support when it is part of password */
 				for(j = i+1; j < len; j++) {
 					if(url->s[j]=='@') {
 						a = j;
 					}
 				}
 				if(a!=0) i = a;
25f17988
 				if (dupl_string(&id->password, begin, url->s + i) < 0) goto err;
 				begin = url->s + i + 1;
857b95ea
 				break;
 
 			case '/':
 				id->host = prev_token;
765c17f8
 				prev_token = 0;
25f17988
 				id->port = str2s(begin, url->s + i - begin, 0);
4da5d898
 				if (dupl_string_name(&id->database, url->s + i + 1, url->s + len) < 0) goto err;
857b95ea
 				return 0;
 			}
 			break;
 
 		case ST_HOST:
25f17988
 			switch(url->s[i]) {
9e29e262
 			case '[':
 				st = ST_HOST6;
 				begin = url->s + i + 1;
 				break;
 
857b95ea
 			case ':':
 				st = ST_PORT;
9e29e262
 				if (dupl_string(&id->host, begin, url->s + i - ipv6_flag) < 0) goto err;
25f17988
 				begin = url->s + i + 1;
857b95ea
 				break;
 
 			case '/':
9e29e262
 				if (dupl_string(&id->host, begin, url->s + i - ipv6_flag) < 0) goto err;
4da5d898
 				if (dupl_string_name(&id->database, url->s + i + 1, url->s + len) < 0) goto err;
857b95ea
 				return 0;
 			}
 			break;
 
9e29e262
 		case ST_HOST6:
 			switch(url->s[i]) {
 			case ']':
2aa614d4
 				sval.s = (char*)begin;
 				sval.len = url->s + i - begin;
 				if(str2ip6(&sval)==NULL) {
 					ipv6_flag = 0;
 					begin -= 1;
 				} else {
 					ipv6_flag = 1;
 				}
9e29e262
 				st = ST_HOST;
 				break;
 			}
 			break;
 
857b95ea
 		case ST_PORT:
25f17988
 			switch(url->s[i]) {
857b95ea
 			case '/':
25f17988
 				id->port = str2s(begin, url->s + i - begin, 0);
4da5d898
 				if (dupl_string_name(&id->database, url->s + i + 1, url->s + len) < 0) goto err;
857b95ea
 				return 0;
 			}
 			break;
d419f427
 
857b95ea
 		case ST_DB:
 			break;
 		}
 	}
 
 	if (st != ST_DB) goto err;
 	return 0;
 
  err:
2ded4b2f
 	if (!id) goto end;
857b95ea
 	if (id->scheme) pkg_free(id->scheme);
 	if (id->username) pkg_free(id->username);
 	if (id->password) pkg_free(id->password);
 	if (id->host) pkg_free(id->host);
 	if (id->database) pkg_free(id->database);
765c17f8
 	memset(id, 0, sizeof(struct db_id));
857b95ea
 	if (prev_token) pkg_free(prev_token);
2ded4b2f
  end:
857b95ea
 	return -1;
 }
 
 
875f5c94
 /**
857b95ea
  * Create a new connection identifier
875f5c94
  * \param url database URL
6eb22c8d
  * \param pooling whether or not a pooled connection may be used
875f5c94
  * \return connection identifier, or zero on error
857b95ea
  */
6eb22c8d
 struct db_id* new_db_id(const str* url, db_pooling_t pooling)
857b95ea
 {
2dad0215
 	static int poolid=0;
857b95ea
 	struct db_id* ptr;
 
25f17988
 	if (!url || !url->s) {
53d94439
 		LM_ERR("invalid parameter\n");
857b95ea
 		return 0;
 	}
 
5457bcbc
 	ptr = (struct db_id*)pkg_malloc(sizeof(struct db_id) + url->len + 1);
857b95ea
 	if (!ptr) {
3b0c06ef
 		PKG_MEM_ERROR;
857b95ea
 		goto err;
 	}
5457bcbc
 	memset(ptr, 0, sizeof(struct db_id)+url->len+1);
857b95ea
 
b17945f7
 	if (parse_db_url(ptr, url) < 0) {
25f17988
 		LM_ERR("error while parsing database URL: '%.*s' \n", url->len, url->s);
857b95ea
 		goto err;
 	}
b17945f7
 
6eb22c8d
 	if (pooling == DB_POOLING_NONE) ptr->poolid = ++poolid;
b17945f7
 	else ptr->poolid = 0;
f138cce2
 	ptr->pid = my_pid();
ca92959a
 	ptr->url.s = (char*)ptr + sizeof(struct db_id);
5457bcbc
 	ptr->url.len = url->len;
 	strncpy(ptr->url.s, url->s, url->len);
 	ptr->url.s[url->len] = '\0';
857b95ea
 
 	return ptr;
 
  err:
 	if (ptr) pkg_free(ptr);
 	return 0;
 }
 
 
875f5c94
 /**
857b95ea
  * Compare two connection identifiers
875f5c94
  * \param id1 first identifier
  * \param id2 second identifier
  * \return one if both are equal, zero otherwise
857b95ea
  */
bc4bf265
 unsigned char cmp_db_id(const struct db_id* id1, const struct db_id* id2)
857b95ea
 {
 	if (!id1 || !id2) return 0;
 	if (id1->port != id2->port) return 0;
 
 	if (strcmp(id1->scheme, id2->scheme)) return 0;
c0a18f4f
 	if (id1->username!=0 && id2->username!=0) {
 		if (strcmp(id1->username, id2->username)) return 0;
 	} else {
 		if (id1->username!=0 || id2->username!=0) return 0;
 	}
850c6f95
 	if (id1->password!=0 && id2->password!=0) {
 		if(strcmp(id1->password, id2->password)) return 0;
 	} else {
 		if (id1->password!=0 || id2->password!=0) return 0;
 	}
857b95ea
 	if (strcasecmp(id1->host, id2->host)) return 0;
 	if (strcmp(id1->database, id2->database)) return 0;
f138cce2
 	if(id1->pid!=id2->pid) {
9fb37a55
 		LM_DBG("identical DB URLs, but different DB connection pid [%d/%d]\n",
db384b84
 				id1->pid, id2->pid);
f138cce2
 		return 0;
 	}
2dad0215
 	if(id1->poolid!=id2->poolid) {
 		LM_DBG("identical DB URLs, but different poolids [%d/%d]\n",
 				id1->poolid, id2->poolid);
 		return 0;
 	}
857b95ea
 	return 1;
 }
 
 
875f5c94
 /**
857b95ea
  * Free a connection identifier
875f5c94
  * \param id identifier
857b95ea
  */
 void free_db_id(struct db_id* id)
 {
 	if (!id) return;
 
 	if (id->scheme) pkg_free(id->scheme);
 	if (id->username) pkg_free(id->username);
 	if (id->password) pkg_free(id->password);
 	if (id->host) pkg_free(id->host);
 	if (id->database) pkg_free(id->database);
 	pkg_free(id);
 }