Browse code

Added support to limit number of calls per customer/profile

- added function cnxcc_set_max_channels() per customer/profile
- added function cnxcc_get_channel_count() per customer/profile
- added function cnxcc_terminate_all() to terminate calls per customer/profile
- added select @cnxcc.channels["customer/profile"].count
- added modified version of kamailio-cnxcc.cfg to reflect changes and examples

Carlos Ruiz Diaz authored on 20/06/2013 21:34:49
Showing 9 changed files
... ...
@@ -1,8 +1,25 @@
1 1
 /*
2
- * cnxcc_check.c
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
5
+ *                    ConexionGroup (www.conexiongroup.com)
6
+ *
7
+ * This file is part of Kamailio, a free SIP server.
8
+ *
9
+ * Kamailio is free software; you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation; either version 2 of the License, or
12
+ * (at your option) any later version
13
+ *
14
+ * Kamailio is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with this program; if not, write to the Free Software
21
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
3 22
  *
4
- *  Created on: Dec 10, 2012
5
- *      Author: carlos
6 23
  */
7 24
 
8 25
 #include <stdio.h>
... ...
@@ -1,8 +1,25 @@
1 1
 /*
2
- * cnxcc_check.h
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
5
+ *                    ConexionGroup (www.conexiongroup.com)
6
+ *
7
+ * This file is part of Kamailio, a free SIP server.
8
+ *
9
+ * Kamailio is free software; you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation; either version 2 of the License, or
12
+ * (at your option) any later version
13
+ *
14
+ * Kamailio is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with this program; if not, write to the Free Software
21
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
3 22
  *
4
- *  Created on: Dec 10, 2012
5
- *      Author: carlos
6 23
  */
7 24
 
8 25
 #ifndef CNXCC_CHECK_H_
... ...
@@ -42,7 +42,6 @@
42 42
 #include "../../locking.h"
43 43
 #include "../../lock_ops.h"
44 44
 #include "../../str_hash.h"
45
-//#include "../../timer.h"
46 45
 #include "../../timer_proc.h"
47 46
 #include "../../modules/tm/tm_load.h"
48 47
 #include "../../parser/parse_from.h"
... ...
@@ -52,7 +51,6 @@
52 51
 #include "../../parser/contact/parse_contact.h"
53 52
 #include "../../parser/contact/contact.h"
54 53
 #include "../../parser/parse_rr.h"
55
-//#include "../../lib/kcore/parser_helpers.h"
56 54
 #include "../../mod_fix.h"
57 55
 #include "../dialog/dlg_load.h"
58 56
 #include "../dialog/dlg_hash.h"
... ...
@@ -66,11 +64,12 @@
66 64
 #include "cnxcc_sip_msg_faker.h"
67 65
 #include "cnxcc_check.h"
68 66
 #include "cnxcc_rpc.h"
67
+#include "cnxcc_select.h"
69 68
 
70 69
 MODULE_VERSION
71 70
 
72 71
 #define HT_SIZE						229
73
-#define MODULE_NAME					"CNXCC"
72
+#define MODULE_NAME					"cnxcc"
74 73
 #define NUMBER_OF_TIMERS			2
75 74
 
76 75
 #define TRUE						1
... ...
@@ -106,6 +105,10 @@ static int pv_get_calls(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
106 105
  */
107 106
 static int set_max_time(struct sip_msg* msg, char* number, char* str2);
108 107
 static int set_max_credit(struct sip_msg* msg, char *str_pv_client, char *str_pv_credit, char *str_pv_cps, char *str_pv_inip, char *str_pv_finp);
108
+static int set_max_channels(struct sip_msg* msg, char* str_pv_client, char* str_pv_max_chan);
109
+static int get_channel_count(struct sip_msg* msg, char* str_pv_client, char* str_pv_max_chan);
110
+static int terminate_all(struct sip_msg* msg, char* str_pv_client);
111
+
109 112
 static void start_billing(str *callid, str tags[2]);
110 113
 static void setup_billing(str *callid, unsigned int h_entry, unsigned int h_id);
111 114
 static void stop_billing(str *callid);
... ...
@@ -140,6 +143,10 @@ static cmd_export_t cmds[] =
140 143
 {
141 144
 	{"cnxcc_set_max_time",   (cmd_function) set_max_time, 2, fixup_pvar_pvar, fixup_free_pvar_pvar, ANY_ROUTE},
142 145
 	{"cnxcc_set_max_credit",   (cmd_function) set_max_credit, 5, fixup_par, NULL, ANY_ROUTE},
146
+	{"cnxcc_set_max_channels",   (cmd_function) set_max_channels, 2, fixup_pvar_pvar, NULL, ANY_ROUTE},
147
+	{"cnxcc_get_channel_count",   (cmd_function) get_channel_count, 2, fixup_pvar_pvar, NULL, ANY_ROUTE},
148
+	{"cnxcc_terminate_all",   (cmd_function) terminate_all, 1, fixup_pvar_null, NULL, ANY_ROUTE},
149
+
143 150
 	{0,0,0,0,0,0}
144 151
 };
145 152
 
... ...
@@ -176,10 +183,19 @@ rpc_export_t ul_rpc[] =
176 183
     {0, 0, 0, 0}
177 184
 };
178 185
 
186
+/* selects declaration */
187
+select_row_t sel_declaration[] = {
188
+        { NULL, SEL_PARAM_STR, STR_STATIC_INIT("cnxcc"), sel_root, SEL_PARAM_EXPECTED},
189
+        { sel_root, SEL_PARAM_STR, STR_STATIC_INIT("channels"), sel_channels, SEL_PARAM_EXPECTED|CONSUME_NEXT_STR|FIXUP_CALL},
190
+        { sel_channels, SEL_PARAM_STR, STR_STATIC_INIT("count"), sel_channels_count, 0},
191
+
192
+        { NULL, SEL_PARAM_STR, STR_NULL, NULL, 0}
193
+};
194
+
179 195
 /** module exports */
180 196
 struct module_exports exports =
181 197
 {
182
-	"cnxcc",
198
+	MODULE_NAME,
183 199
 	DEFAULT_DLFLAGS, 	/* dlopen flags */
184 200
 	cmds,
185 201
 	params,
... ...
@@ -217,7 +233,7 @@ static int fixup_par(void** param, int param_no)
217 233
 
218 234
 static int mod_init(void)
219 235
 {
220
-	LM_ALERT("Loading " MODULE_NAME " module\n");
236
+	LM_INFO("Loading " MODULE_NAME " module\n");
221 237
 
222 238
 	_data.cs_route_number = route_get(&event_rt, "cnxcc:call-shutdown");
223 239
 
... ...
@@ -240,6 +256,8 @@ static int mod_init(void)
240 256
 	_data.time.call_data_by_cid 		= shm_malloc(sizeof(struct str_hash_table));
241 257
 	_data.money.credit_data_by_client	= shm_malloc(sizeof(struct str_hash_table));
242 258
 	_data.money.call_data_by_cid 		= shm_malloc(sizeof(struct str_hash_table));
259
+	_data.channel.credit_data_by_client	= shm_malloc(sizeof(struct str_hash_table));
260
+	_data.channel.call_data_by_cid 		= shm_malloc(sizeof(struct str_hash_table));
243 261
 
244 262
 	_data.stats							= (stats_t *) shm_malloc(sizeof(stats_t));
245 263
 
... ...
@@ -265,9 +283,16 @@ static int mod_init(void)
265 283
 	if (init_hashtable(_data.money.call_data_by_cid) != 0)
266 284
 		return -1;
267 285
 
286
+	if (init_hashtable(_data.channel.credit_data_by_client) != 0)
287
+		return -1;
288
+
289
+	if (init_hashtable(_data.channel.call_data_by_cid) != 0)
290
+		return -1;
291
+
268 292
 	lock_init(&_data.lock);
269 293
 	lock_init(&_data.time.lock);
270 294
 	lock_init(&_data.money.lock);
295
+	lock_init(&_data.channel.lock);
271 296
 
272 297
 	register_mi_cmd(mi_credit_control_stats, "cnxcc_stats", NULL, NULL, 0);
273 298
 
... ...
@@ -291,6 +316,8 @@ static int mod_init(void)
291 316
 
292 317
 	_dlgbinds.register_dlgcb(NULL, DLGCB_CREATED, dialog_created_callback, NULL, NULL);
293 318
 
319
+	 register_select_table(sel_declaration);
320
+
294 321
 	return 0;
295 322
 }
296 323
 
... ...
@@ -397,6 +424,7 @@ int try_get_credit_data_entry(str *client_id, credit_data_t **credit_data)
397 424
 	hash_tables_t *hts				= NULL;
398 425
 	*credit_data					= NULL;
399 426
 
427
+	/* by money */
400 428
 	hts					= &_data.money;
401 429
 	lock_get(&hts->lock);
402 430
 
... ...
@@ -411,10 +439,26 @@ int try_get_credit_data_entry(str *client_id, credit_data_t **credit_data)
411 439
 
412 440
 	lock_release(&hts->lock);
413 441
 
442
+	/* by time */
414 443
 	hts					= &_data.time;
415 444
 	lock_get(&hts->lock);
416 445
 
417
-	cd_entry			= str_hash_get(hts->call_data_by_cid, client_id->s, client_id->len);
446
+	cd_entry			= str_hash_get(hts->credit_data_by_client, client_id->s, client_id->len);
447
+
448
+	if (cd_entry != NULL)
449
+	{
450
+		*credit_data	= cd_entry->u.p;
451
+		lock_release(&hts->lock);
452
+		return 0;
453
+	}
454
+
455
+	lock_release(&hts->lock);
456
+
457
+	/* by channel */
458
+	hts					= &_data.channel;
459
+	lock_get(&hts->lock);
460
+
461
+	cd_entry			= str_hash_get(hts->credit_data_by_client, client_id->s, client_id->len);
418 462
 
419 463
 	if (cd_entry != NULL)
420 464
 	{
... ...
@@ -434,6 +478,7 @@ int try_get_call_entry(str *callid, call_t **call, hash_tables_t **hts)
434 478
 
435 479
 	*call					= NULL;
436 480
 
481
+	/* by money */
437 482
 	*hts					= &_data.money;
438 483
 	lock_get(&(*hts)->lock);
439 484
 
... ...
@@ -448,6 +493,7 @@ int try_get_call_entry(str *callid, call_t **call, hash_tables_t **hts)
448 493
 
449 494
 	lock_release(&(*hts)->lock);
450 495
 
496
+	/* by time */
451 497
 	*hts				= &_data.time;
452 498
 	lock_get(&(*hts)->lock);
453 499
 
... ...
@@ -462,6 +508,21 @@ int try_get_call_entry(str *callid, call_t **call, hash_tables_t **hts)
462 508
 
463 509
 	lock_release(&(*hts)->lock);
464 510
 
511
+	/* by channel */
512
+	*hts				= &_data.channel;
513
+	lock_get(&(*hts)->lock);
514
+
515
+	call_entry			= str_hash_get((*hts)->call_data_by_cid, callid->s, callid->len);
516
+
517
+	if (call_entry != NULL)
518
+	{
519
+		*call	= call_entry->u.p;
520
+		lock_release(&(*hts)->lock);
521
+		return 0;
522
+	}
523
+
524
+	lock_release(&(*hts)->lock);
525
+
465 526
 	return -1;
466 527
 }
467 528
 
... ...
@@ -800,8 +861,13 @@ static void free_call(call_t *call)
800 861
 
801 862
 		if (e == NULL)
802 863
 		{
803
-			LM_ERR("Call [%.*s] not found. Couldn't be able to free it from hashtable", call->sip_data.callid.len, call->sip_data.callid.s);
804
-			return;
864
+			e			= str_hash_get(_data.channel.call_data_by_cid, call->sip_data.callid.s, call->sip_data.callid.len);
865
+
866
+			if (e == NULL)
867
+			{
868
+				LM_ERR("Call [%.*s] not found. Couldn't be able to free it from hashtable", call->sip_data.callid.len, call->sip_data.callid.s);
869
+				return;
870
+			}
805 871
 		}
806 872
 	}
807 873
 
... ...
@@ -841,10 +907,30 @@ static int shm_str_hash_alloc(struct str_hash_table *ht, int size)
841 907
 
842 908
 static credit_data_t *get_or_create_credit_data_entry(str *client_id, credit_type_t type)
843 909
 {
844
-	struct str_hash_table *ht	= type == CREDIT_MONEY ? _data.money.credit_data_by_client : _data.time.credit_data_by_client;
845
-	gen_lock_t *lock			= type == CREDIT_MONEY ? &_data.money.lock : &_data.time.lock;
910
+	struct str_hash_table *ht	= NULL;
911
+	gen_lock_t *lock			= NULL;
846 912
 	struct str_hash_entry *e	= NULL;
847 913
 
914
+	switch(type)
915
+	{
916
+	case CREDIT_MONEY:
917
+		ht		= _data.money.credit_data_by_client;
918
+		lock	=  &_data.money.lock;
919
+		break;
920
+	case CREDIT_TIME:
921
+		ht		= _data.time.credit_data_by_client;
922
+		lock	=  &_data.time.lock;
923
+		break;
924
+	case CREDIT_CHANNEL:
925
+		ht		= _data.channel.credit_data_by_client;
926
+		lock	=  &_data.channel.lock;
927
+		break;
928
+	default:
929
+		LM_ERR("Something went terribly wrong");
930
+		return NULL;
931
+	}
932
+
933
+
848 934
 	lock_get(lock);
849 935
 	e							= str_hash_get(ht, client_id->s, client_id->len);
850 936
 	lock_release(lock);
... ...
@@ -1121,11 +1207,95 @@ error:
1121 1207
 	return NULL;
1122 1208
 }
1123 1209
 
1210
+static call_t *alloc_new_call_by_channel(credit_data_t *credit_data, struct sip_msg *msg, int max_chan)
1211
+{
1212
+	call_t *call		= NULL;
1213
+
1214
+	lock_get(&credit_data->lock);
1215
+
1216
+	if (credit_data->call_list == NULL)
1217
+	{
1218
+		LM_ERR("Credit data call list is NULL\n");
1219
+		goto error;
1220
+	}
1221
+
1222
+	call 				= shm_malloc(sizeof(call_t));
1223
+	if (call == NULL)
1224
+	{
1225
+		LM_ERR("No shared memory left\n");
1226
+		goto error;
1227
+	}
1228
+
1229
+	if ( (!msg->callid && parse_headers(msg, HDR_CALLID_F, 0) != 0) ||
1230
+		   shm_str_dup(&call->sip_data.callid, &msg->callid->body) != 0 )
1231
+	{
1232
+		LM_ERR("Error processing CALLID hdr\n");
1233
+		goto error;
1234
+	}
1235
+
1236
+	call->sip_data.to_tag.s		= NULL;
1237
+	call->sip_data.to_tag.len 	= 0;
1238
+	call->sip_data.from_tag.s	= NULL;
1239
+	call->sip_data.from_tag.len = 0;
1240
+
1241
+	call->consumed_amount		= 0;
1242
+	call->confirmed				= FALSE;
1243
+	call->max_amount			= max_chan;
1244
+
1245
+	/*
1246
+	 * Reference the client_id from the root of the list
1247
+	 */
1248
+	call->client_id.s			= credit_data->call_list->client_id.s;
1249
+	call->client_id.len			= credit_data->call_list->client_id.len;
1250
+
1251
+	/*
1252
+	 * Insert the newly created call to the list of calls
1253
+	 */
1254
+	clist_insert(credit_data->call_list, call, next, prev);
1255
+
1256
+	lock_init(&call->lock);
1257
+
1258
+	/*
1259
+	 * Increase the number of calls for this client. This call is not yet confirmed.
1260
+	 */
1261
+	credit_data->number_of_calls++;
1262
+
1263
+	lock_release(&credit_data->lock);
1264
+
1265
+	LM_DBG("New call allocated for client [%.*s]\n", call->client_id.len, call->client_id.s);
1266
+
1267
+
1268
+	return call;
1269
+
1270
+error:
1271
+	lock_release(&credit_data->lock);
1272
+	return NULL;
1273
+}
1274
+
1124 1275
 static int add_call_by_cid(str *cid, call_t *call, credit_type_t type)
1125 1276
 {
1277
+	struct str_hash_table *ht	= NULL;
1278
+	gen_lock_t *lock			= NULL;
1126 1279
 	struct str_hash_entry *e	= NULL;
1127
-	struct str_hash_table *ht	= type == CREDIT_MONEY ? _data.money.call_data_by_cid : _data.time.call_data_by_cid;
1128
-	gen_lock_t *lock			= type == CREDIT_MONEY ? &_data.money.lock : &_data.time.lock;
1280
+
1281
+	switch(type)
1282
+	{
1283
+	case CREDIT_MONEY:
1284
+		ht		= _data.money.call_data_by_cid;
1285
+		lock	=  &_data.money.lock;
1286
+		break;
1287
+	case CREDIT_TIME:
1288
+		ht		= _data.time.call_data_by_cid;
1289
+		lock	=  &_data.time.lock;
1290
+		break;
1291
+	case CREDIT_CHANNEL:
1292
+		ht		= _data.channel.call_data_by_cid;
1293
+		lock	=  &_data.channel.lock;
1294
+		break;
1295
+	default:
1296
+		LM_ERR("Something went terribly wrong");
1297
+		return -1;
1298
+	}
1129 1299
 
1130 1300
 	e	= str_hash_get(ht, cid->s, cid->len);
1131 1301
 
... ...
@@ -1326,13 +1496,184 @@ static int set_max_credit(struct sip_msg* msg,
1326 1496
 	}
1327 1497
 	else
1328 1498
 	{
1329
-		LM_ALERT("MSG was not a request\n");
1499
+		LM_ALERT("MSG was not an INVITE\n");
1330 1500
 		return -1;
1331 1501
 	}
1332 1502
 
1333 1503
 	return 1;
1334 1504
 }
1335 1505
 
1506
+static int terminate_all(struct sip_msg* msg, char* str_pv_client)
1507
+{
1508
+	credit_data_t *credit_data 	= NULL;
1509
+	pv_spec_t *client_id_spec	= (pv_spec_t *) str_pv_client;
1510
+
1511
+	pv_value_t client_id_val;
1512
+
1513
+	if (pv_get_spec_value(msg, client_id_spec, &client_id_val) != 0)
1514
+	{
1515
+		LM_ERR("[%.*s]: can't get client_id pvar value\n", msg->callid->body.len, msg->callid->body.s);
1516
+		return -1;
1517
+	}
1518
+
1519
+	if (client_id_val.rs.len == 0 || client_id_val.rs.s == NULL)
1520
+	{
1521
+		LM_ERR("[%.*s]: client ID cannot be null\n", msg->callid->body.len, msg->callid->body.s);
1522
+		return -1;
1523
+	}
1524
+
1525
+	if (try_get_credit_data_entry(&client_id_val.rs, &credit_data) != 0)
1526
+	{
1527
+		LM_DBG("[%.*s] not found\n", msg->callid->body.len, msg->callid->body.s);
1528
+		return -1;
1529
+	}
1530
+
1531
+	terminate_all_calls(credit_data);
1532
+
1533
+	return 1;
1534
+}
1535
+
1536
+static int get_channel_count(struct sip_msg* msg, char* str_pv_client, char* str_pv_chan_count)
1537
+{
1538
+	credit_data_t *credit_data 	= NULL;
1539
+	pv_spec_t *chan_count_spec	= (pv_spec_t *) str_pv_chan_count,
1540
+			  *client_id_spec	= (pv_spec_t *) str_pv_client;
1541
+
1542
+	pv_value_t chan_count_val, client_id_val;
1543
+	int value					= -1;
1544
+
1545
+	if (pv_get_spec_value(msg, client_id_spec, &client_id_val) != 0)
1546
+	{
1547
+		LM_ERR("[%.*s]: can't get client_id pvar value\n", msg->callid->body.len, msg->callid->body.s);
1548
+		return -1;
1549
+	}
1550
+
1551
+	if (client_id_val.rs.len == 0 || client_id_val.rs.s == NULL)
1552
+	{
1553
+		LM_ERR("[%.*s]: client ID cannot be null\n", msg->callid->body.len, msg->callid->body.s);
1554
+		return -1;
1555
+	}
1556
+
1557
+	if (try_get_credit_data_entry(&client_id_val.rs, &credit_data) == 0)
1558
+		value	= credit_data->number_of_calls;
1559
+	else
1560
+		LM_ALERT("[%.*s] not found\n", msg->callid->body.len, msg->callid->body.s);
1561
+
1562
+	if (!pv_is_w(chan_count_spec))
1563
+	{
1564
+		LM_ERR("pvar is not writable");
1565
+		return -1;
1566
+	}
1567
+
1568
+	memset(&chan_count_val, 0, sizeof(chan_count_val));
1569
+
1570
+	chan_count_val.flags 	= PV_VAL_STR;
1571
+
1572
+	if (value > 0)
1573
+		chan_count_val.rs.s 	= int2str(value, &chan_count_val.rs.len);
1574
+	else
1575
+	{
1576
+		char buff[2]			= { '-', '1' };
1577
+		chan_count_val.rs.s 	= buff;
1578
+		chan_count_val.rs.len	= 2;
1579
+	}
1580
+
1581
+	if (pv_set_spec_value(msg, chan_count_spec, 0, &chan_count_val) != 0)
1582
+	{
1583
+		LM_ERR("Error writing value to pvar");
1584
+		return -1;
1585
+	}
1586
+
1587
+	return 1;
1588
+}
1589
+
1590
+static int set_max_channels(struct sip_msg* msg, char* str_pv_client, char* str_pv_max_chan)
1591
+{
1592
+	credit_data_t *credit_data 	= NULL;
1593
+	call_t *call				= NULL;
1594
+	pv_spec_t *max_chan_spec	= (pv_spec_t *) str_pv_max_chan,
1595
+			  *client_id_spec	= (pv_spec_t *) str_pv_client;
1596
+	pv_value_t max_chan_val, client_id_val;
1597
+	int max_chan				= 0;
1598
+
1599
+	set_ctrl_flag(msg);
1600
+
1601
+	if (parse_headers(msg, HDR_CALLID_F, 0) != 0)
1602
+	{
1603
+		LM_ERR("Error parsing Call-ID");
1604
+		return -1;
1605
+	}
1606
+
1607
+	if (msg->first_line.type == SIP_REQUEST && msg->first_line.u.request.method_value == METHOD_INVITE)
1608
+	{
1609
+		if (has_to_tag(msg))
1610
+		{
1611
+			LM_ERR("INVITE is a reINVITE\n");
1612
+			return -1;
1613
+		}
1614
+
1615
+		if (pv_get_spec_value(msg, max_chan_spec, &max_chan_val) != 0)
1616
+		{
1617
+			LM_ERR("Can't get max_chan pvar value\n");
1618
+			return -1;
1619
+		}
1620
+		max_chan	= max_chan_val.ri;
1621
+
1622
+		if (max_chan <= 0)
1623
+		{
1624
+			LM_ERR("[%.*s] MAX_CHAN cannot be less than or equal to zero: %d\n", msg->callid->body.len, msg->callid->body.s, max_chan);
1625
+			return -1;
1626
+		}
1627
+
1628
+		if (pv_get_spec_value(msg, client_id_spec, &client_id_val) != 0)
1629
+		{
1630
+			LM_ERR("[%.*s]: can't get client_id pvar value\n", msg->callid->body.len, msg->callid->body.s);
1631
+			return -1;
1632
+		}
1633
+
1634
+		if (client_id_val.rs.len == 0 || client_id_val.rs.s == NULL)
1635
+		{
1636
+			LM_ERR("[%.*s]: client ID cannot be null\n", msg->callid->body.len, msg->callid->body.s);
1637
+			return -1;
1638
+		}
1639
+
1640
+		LM_DBG("Setting up new call for client [%.*s], max-chan[%d], call-id[%.*s]\n", client_id_val.rs.len, client_id_val.rs.s,
1641
+																		max_chan,
1642
+																		msg->callid->body.len, msg->callid->body.s);
1643
+
1644
+		if ((credit_data = get_or_create_credit_data_entry(&client_id_val.rs, CREDIT_CHANNEL)) == NULL)
1645
+		{
1646
+			LM_ERR("Error retrieving credit data from shared memory for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
1647
+			return -1;
1648
+		}
1649
+
1650
+		if (credit_data->number_of_calls + 1 > max_chan)
1651
+			return -2; // you have, between calls being setup plus those established, more than you maximum quota
1652
+
1653
+		if (credit_data->concurrent_calls + 1 > max_chan)
1654
+			return -3; // you have the max amount of established calls already
1655
+
1656
+		if ((call = alloc_new_call_by_channel(credit_data, msg, max_chan)) == NULL)
1657
+		{
1658
+			LM_ERR("Unable to allocate new call for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
1659
+			return -1;
1660
+		}
1661
+
1662
+		if (add_call_by_cid(&call->sip_data.callid, call, CREDIT_CHANNEL) != 0)
1663
+		{
1664
+			LM_ERR("Unable to allocate new cid_by_client for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
1665
+			return -1;
1666
+		}
1667
+
1668
+		return 1;
1669
+	}
1670
+	else
1671
+	{
1672
+		LM_ALERT("MSG was not an INVITE\n");
1673
+		return -1;
1674
+	}
1675
+}
1676
+
1336 1677
 static int set_max_time(struct sip_msg* msg, char* str_pv_client, char* str_pv_maxsecs)
1337 1678
 {
1338 1679
 	credit_data_t *credit_data 	= NULL;
... ...
@@ -1407,7 +1748,7 @@ static int set_max_time(struct sip_msg* msg, char* str_pv_client, char* str_pv_m
1407 1748
 	}
1408 1749
 	else
1409 1750
 	{
1410
-		LM_ALERT("MSG was not a request\n");
1751
+		LM_ALERT("MSG was not an INVITE\n");
1411 1752
 		return -1;
1412 1753
 	}
1413 1754
 
... ...
@@ -24,7 +24,6 @@
24 24
 #ifndef _CNXCC_MOD_H
25 25
 #define _CNXCC_MOD_H
26 26
 
27
-// 3,778
28 27
 #include "../../locking.h"
29 28
 #include "../../str_hash.h"
30 29
 #include "../../parser/parse_rr.h"
... ...
@@ -48,7 +47,8 @@ typedef enum cnxpvtypes
48 47
 typedef enum credit_type
49 48
 {
50 49
 	CREDIT_TIME,
51
-	CREDIT_MONEY
50
+	CREDIT_MONEY,
51
+	CREDIT_CHANNEL
52 52
 } credit_type_t;
53 53
 
54 54
 typedef struct hash_tables
... ...
@@ -65,6 +65,7 @@ typedef struct data
65 65
 
66 66
 	hash_tables_t time;
67 67
 	hash_tables_t money;
68
+	hash_tables_t channel;
68 69
 
69 70
 	/*struct str_hash_table *credit_data_by_client;
70 71
 	struct str_hash_table *call_data_by_cid;*/
... ...
@@ -82,7 +83,6 @@ typedef struct data
82 83
 	flag_t ctrl_flag;
83 84
 
84 85
 	int check_period;
85
-	int number_of_timers;
86 86
 
87 87
 } data_t;
88 88
 
... ...
@@ -1,8 +1,25 @@
1 1
 /*
2
- * cnxcc_rpc.c
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
5
+ *                    ConexionGroup (www.conexiongroup.com)
6
+ *
7
+ * This file is part of Kamailio, a free SIP server.
8
+ *
9
+ * Kamailio is free software; you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation; either version 2 of the License, or
12
+ * (at your option) any later version
13
+ *
14
+ * Kamailio is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with this program; if not, write to the Free Software
21
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
3 22
  *
4
- *  Created on: Dec 6, 2012
5
- *      Author: carlos
6 23
  */
7 24
 
8 25
 #include <stdio.h>
... ...
@@ -1,8 +1,25 @@
1 1
 /*
2
- * cnxcc_rpc.h
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
5
+ *                    ConexionGroup (www.conexiongroup.com)
6
+ *
7
+ * This file is part of Kamailio, a free SIP server.
8
+ *
9
+ * Kamailio is free software; you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation; either version 2 of the License, or
12
+ * (at your option) any later version
13
+ *
14
+ * Kamailio is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with this program; if not, write to the Free Software
21
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
3 22
  *
4
- *  Created on: Dec 6, 2012
5
- *      Author: carlos
6 23
  */
7 24
 
8 25
 #ifndef CNXCC_RPC_H_
9 26
new file mode 100644
... ...
@@ -0,0 +1,67 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
5
+ *                    ConexionGroup (www.conexiongroup.com)
6
+ *
7
+ * This file is part of Kamailio, a free SIP server.
8
+ *
9
+ * Kamailio is free software; you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation; either version 2 of the License, or
12
+ * (at your option) any later version
13
+ *
14
+ * Kamailio is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with this program; if not, write to the Free Software
21
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
+ *
23
+ */
24
+
25
+#include "../../select.h"
26
+#include "../../select_buf.h"
27
+
28
+#include "cnxcc_mod.h"
29
+
30
+extern data_t _data;
31
+
32
+int sel_root(str* res, select_t* s, struct sip_msg* msg)  /* dummy */
33
+{
34
+	return 0;
35
+}
36
+
37
+int sel_channels(str* res, select_t* s, struct sip_msg* msg)
38
+{
39
+	LM_DBG("sel_channels");
40
+
41
+	return 0;
42
+}
43
+
44
+int sel_channels_count(str* res, select_t* s, struct sip_msg* msg)
45
+{
46
+	LM_DBG("sel_channels_count for [%.*s]",  s->params[2].v.s.len, s->params[2].v.s.s);
47
+
48
+	credit_data_t *credit_data	= NULL;
49
+	int value					= 0;
50
+
51
+	if (s->params[2].v.s.len <= 0)
52
+	{
53
+		LM_ERR("Client must be specified");
54
+		return -1;
55
+	}
56
+
57
+	if (try_get_credit_data_entry(&s->params[2].v.s, &credit_data) >= 0)
58
+		value = credit_data->number_of_calls;
59
+	else
60
+		LM_DBG("Client [%.*s] not found", s->params[2].v.s.len, s->params[2].v.s.s);
61
+
62
+	res->s 	= int2str(value, &res->len);
63
+
64
+	return 0;
65
+}
66
+
67
+
0 68
new file mode 100644
... ...
@@ -0,0 +1,32 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
5
+ *                    ConexionGroup (www.conexiongroup.com)
6
+ *
7
+ * This file is part of Kamailio, a free SIP server.
8
+ *
9
+ * Kamailio is free software; you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation; either version 2 of the License, or
12
+ * (at your option) any later version
13
+ *
14
+ * Kamailio is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with this program; if not, write to the Free Software
21
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
+ *
23
+ */
24
+
25
+#ifndef CNXCC_SELECT_H_
26
+#define CNXCC_SELECT_H_
27
+
28
+int sel_root(str* res, select_t* s, struct sip_msg* msg);
29
+int sel_channels(str* res, select_t* s, struct sip_msg* msg);
30
+int sel_channels_count(str* res, select_t* s, struct sip_msg* msg);
31
+
32
+#endif /* CNXCC_SELECT_H_ */
... ...
@@ -1,4 +1,9 @@
1 1
 #!KAMAILIO
2
+
3
+##!define CNXCC_TIME
4
+##!define CNXCC_MONEY
5
+#!define CNXCC_CHANNEL
6
+
2 7
 #
3 8
 # Kamailio (OpenSER) SIP Server v3.2 - default configuration script
4 9
 #     - web: http://www.kamailio.org
... ...
@@ -129,9 +134,10 @@
129 134
 
130 135
 ####### Global Parameters #########
131 136
 
137
+
132 138
 #!ifdef WITH_DEBUG
133 139
 debug=4
134
-log_stderror=yes
140
+log_stderror=no
135 141
 #!else
136 142
 debug=2
137 143
 log_stderror=no
... ...
@@ -204,7 +210,7 @@ voicemail.srv_port = "5060" desc "VoiceMail Port"
204 210
 #!ifdef WITH_SRCPATH
205 211
 mpath="modules_k:modules"
206 212
 #!else
207
-mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib/kamailio/modules/"
213
+mpath="/usr/local/lib64/kamailio/modules_k/:/usr/local/lib64/kamailio/modules/"
208 214
 #!endif
209 215
 
210 216
 #!ifdef WITH_MYSQL
... ...
@@ -274,7 +280,7 @@ loadmodule "xmlrpc.so"
274 280
 #!endif
275 281
 
276 282
 #!ifdef WITH_DEBUG
277
-loadmodule "debugger.so"
283
+#loadmodule "debugger.so"
278 284
 #!endif
279 285
 
280 286
 # ----------------- setting module-specific parameters ---------------
... ...
@@ -437,7 +443,7 @@ modparam("xmlrpc", "route", "XMLRPC");
437 443
 
438 444
 #!ifdef WITH_DEBUG
439 445
 # ----- debugger params -----
440
-modparam("debugger", "cfgtrace", 1)
446
+#modparam("debugger", "cfgtrace", 1)
441 447
 #!endif
442 448
 
443 449
 #!define DLG_FLAG 28
... ...
@@ -449,9 +455,15 @@ modparam("dialog", "default_timeout", 3600)
449 455
 modparam("dialog", "db_mode", 0)
450 456
 modparam("dialog", "dlg_flag", DLG_FLAG)
451 457
 
458
+#!ifdef CNXCC_CHANNEL
459
+loadmodule "rtimer.so";
460
+modparam("rtimer", "timer", "name=ta;interval=1;mode=1;")
461
+modparam("rtimer", "exec", "timer=ta;route=SHOW_CHANNEL_COUNT")
462
+#!endif
463
+
452 464
 loadmodule "cnxcc.so"
453 465
 modparam("cnxcc", "dlg_flag", CC_FLAG)
454
-modparam("cnxcc", "credit_check_period", 1)
466
+modparam("cnxcc", "credit_check_period", 1) #check every 1 second
455 467
 
456 468
 ####### Routing Logic ########
457 469
 
... ...
@@ -502,6 +514,7 @@ request_route {
502 514
 	# dispatch requests to foreign domains
503 515
 	route(SIPOUT);
504 516
 
517
+	
505 518
 	### requests for my local domains
506 519
 
507 520
 	# handle presence related requests
... ...
@@ -522,9 +535,12 @@ request_route {
522 535
 
523 536
 	# user location service
524 537
 	route(LOCATION);
525
-
526
-	route(CNXCC);
527 538
 	
539
+	if (is_method("INVITE")) {
540
+		route(CNXCC);
541
+	}
542
+
543
+        	
528 544
 	route(RELAY);
529 545
 }
530 546
 
... ...
@@ -536,14 +552,18 @@ route[CNXCC]
536 552
 	# 
537 553
 	# This hardcoded values are just for illustrative purposes
538 554
 	#
555
+	
556
+	$var(client)		= "customer1";
557
+
558
+#!ifdef CNXCC_MONEY
559
+	xlog("L_INFO", "Setting up money based credit control");
539 560
 
540
-	$var(client)		= "test-client-0-123-01";
541
-	$var(credit) 		= "50";
542
-	$var(cost_per_sec) 	= "0.5";
543
-	$var(i_pulse)		= "30";
544
-	$var(f_pulse)		= "6";
561
+	$var(credit) 		= "10";	# 10$ of credit
562
+	$var(cost_per_sec) 	= "1";  # 1$ per sec
563
+	$var(i_pulse)		= "1";  # 1$ to establish the call
564
+	$var(f_pulse)		= "1";  # 1$ per second
545 565
 
546
-	# if only one call is established, that call should last 1m, 36s
566
+	# if only one call is established, that call should last 9 seconds.
547 567
 
548 568
 	if (!cnxcc_set_max_credit("$var(client)",
549 569
 				  "$var(credit)", 
... ...
@@ -551,7 +571,53 @@ route[CNXCC]
551 571
 				  "$var(i_pulse)", 
552 572
 				  "$var(f_pulse)")) {
553 573
 		xlog("Error setting up credit control");
574
+		return;
554 575
 	}
576
+#!endif
577
+
578
+#!ifdef CNXCC_CHANNEL	
579
+	xlog("L_INFO", "Setting up channel based credit control");
580
+
581
+	$var(max_chan)	= 2;
582
+	$var(retcode)	= cnxcc_set_max_channels("$var(client)", "$var(max_chan)");
583
+
584
+	if ($var(retcode) == -1) {
585
+		xlog("Error setting up credit control");
586
+		return;
587
+	}
588
+
589
+        $var(count)     = -1;
590
+
591
+        if (!cnxcc_get_channel_count("$var(client)", "$var(count)")) {
592
+                xlog("Error getting customer's channel count");
593
+        }
594
+
595
+        xlog("L_INFO", "CNXCC ROUTE: $var(client) has $var(count) call(s)");
596
+
597
+	if ($var(retcode) < -1) {
598
+		xlog("Too many channels for customer");
599
+		sl_send_reply(403, "Forbidden");
600
+
601
+		if (!cnxcc_terminate_all("$var(client)")) {
602
+			xlog("Error terminating customer's calls");
603
+		}
604
+
605
+		exit;
606
+	}
607
+#!endif
608
+
609
+#!ifdef CNXCC_TIME
610
+	xlog("L_INFO", "Setting up time based credit control");
611
+	
612
+	$var(max_time)	= 10;
613
+	
614
+	if (!cnxcc_set_max_time("$var(client)",
615
+                                  "$var(max_time)")) {
616
+                xlog("Error setting up credit control");
617
+                return;
618
+        }
619
+#!endif
620
+
555 621
 }
556 622
 
557 623
 event_route[cnxcc:call-shutdown]
... ...
@@ -561,6 +627,14 @@ event_route[cnxcc:call-shutdown]
561 627
 	# perform some kind of notification, database update, email sending, etc
562 628
 }
563 629
 
630
+#!ifdef CNXCC_CHANNEL
631
+route[SHOW_CHANNEL_COUNT]
632
+{
633
+	$var(count) = @cnxcc.channels["customer1"].count;
634
+	xlog("L_INFO", "customer1 has $var(count) call(s)");
635
+}
636
+#!endif
637
+
564 638
 route[RELAY] {
565 639
 
566 640
 	# enable additional event routes for forwarded requests
... ...
@@ -981,3 +1055,8 @@ failure_route[MANAGE_FAILURE] {
981 1055
 	}
982 1056
 #!endif
983 1057
 }
1058
+
1059
+event_route[dialog:failed]
1060
+{
1061
+	xlog("dialog failed");
1062
+}