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 52
 #include "../../parser/contact/parse_contact.h"
53 53
 #include "../../parser/contact/contact.h"
54 54
 #include "../../parser/parse_rr.h"
55
-//#include "../../lib/kcore/parser_helpers.h"
56 55
 #include "../../mod_fix.h"
57 56
 #include "../dialog/dlg_load.h"
58 57
 #include "../dialog/dlg_hash.h"
... ...
@@ -66,11 +64,12 @@
66 66
 #include "cnxcc_sip_msg_faker.h"
67 67
 #include "cnxcc_check.h"
68 68
 #include "cnxcc_rpc.h"
69
+#include "cnxcc_select.h"
69 70
 
70 71
 MODULE_VERSION
71 72
 
72 73
 #define HT_SIZE						229
73
-#define MODULE_NAME					"CNXCC"
74
+#define MODULE_NAME					"cnxcc"
74 75
 #define NUMBER_OF_TIMERS			2
75 76
 
76 77
 #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 106
  */
107 107
 static int set_max_time(struct sip_msg* msg, char* number, char* str2);
108 108
 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);
109
+static int set_max_channels(struct sip_msg* msg, char* str_pv_client, char* str_pv_max_chan);
110
+static int get_channel_count(struct sip_msg* msg, char* str_pv_client, char* str_pv_max_chan);
111
+static int terminate_all(struct sip_msg* msg, char* str_pv_client);
112
+
109 113
 static void start_billing(str *callid, str tags[2]);
110 114
 static void setup_billing(str *callid, unsigned int h_entry, unsigned int h_id);
111 115
 static void stop_billing(str *callid);
... ...
@@ -140,6 +143,10 @@ static cmd_export_t cmds[] =
140 140
 {
141 141
 	{"cnxcc_set_max_time",   (cmd_function) set_max_time, 2, fixup_pvar_pvar, fixup_free_pvar_pvar, ANY_ROUTE},
142 142
 	{"cnxcc_set_max_credit",   (cmd_function) set_max_credit, 5, fixup_par, NULL, ANY_ROUTE},
143
+	{"cnxcc_set_max_channels",   (cmd_function) set_max_channels, 2, fixup_pvar_pvar, NULL, ANY_ROUTE},
144
+	{"cnxcc_get_channel_count",   (cmd_function) get_channel_count, 2, fixup_pvar_pvar, NULL, ANY_ROUTE},
145
+	{"cnxcc_terminate_all",   (cmd_function) terminate_all, 1, fixup_pvar_null, NULL, ANY_ROUTE},
146
+
143 147
 	{0,0,0,0,0,0}
144 148
 };
145 149
 
... ...
@@ -176,10 +183,19 @@ rpc_export_t ul_rpc[] =
176 176
     {0, 0, 0, 0}
177 177
 };
178 178
 
179
+/* selects declaration */
180
+select_row_t sel_declaration[] = {
181
+        { NULL, SEL_PARAM_STR, STR_STATIC_INIT("cnxcc"), sel_root, SEL_PARAM_EXPECTED},
182
+        { sel_root, SEL_PARAM_STR, STR_STATIC_INIT("channels"), sel_channels, SEL_PARAM_EXPECTED|CONSUME_NEXT_STR|FIXUP_CALL},
183
+        { sel_channels, SEL_PARAM_STR, STR_STATIC_INIT("count"), sel_channels_count, 0},
184
+
185
+        { NULL, SEL_PARAM_STR, STR_NULL, NULL, 0}
186
+};
187
+
179 188
 /** module exports */
180 189
 struct module_exports exports =
181 190
 {
182
-	"cnxcc",
191
+	MODULE_NAME,
183 192
 	DEFAULT_DLFLAGS, 	/* dlopen flags */
184 193
 	cmds,
185 194
 	params,
... ...
@@ -217,7 +233,7 @@ static int fixup_par(void** param, int param_no)
217 217
 
218 218
 static int mod_init(void)
219 219
 {
220
-	LM_ALERT("Loading " MODULE_NAME " module\n");
220
+	LM_INFO("Loading " MODULE_NAME " module\n");
221 221
 
222 222
 	_data.cs_route_number = route_get(&event_rt, "cnxcc:call-shutdown");
223 223
 
... ...
@@ -240,6 +256,8 @@ static int mod_init(void)
240 240
 	_data.time.call_data_by_cid 		= shm_malloc(sizeof(struct str_hash_table));
241 241
 	_data.money.credit_data_by_client	= shm_malloc(sizeof(struct str_hash_table));
242 242
 	_data.money.call_data_by_cid 		= shm_malloc(sizeof(struct str_hash_table));
243
+	_data.channel.credit_data_by_client	= shm_malloc(sizeof(struct str_hash_table));
244
+	_data.channel.call_data_by_cid 		= shm_malloc(sizeof(struct str_hash_table));
243 245
 
244 246
 	_data.stats							= (stats_t *) shm_malloc(sizeof(stats_t));
245 247
 
... ...
@@ -265,9 +283,16 @@ static int mod_init(void)
265 265
 	if (init_hashtable(_data.money.call_data_by_cid) != 0)
266 266
 		return -1;
267 267
 
268
+	if (init_hashtable(_data.channel.credit_data_by_client) != 0)
269
+		return -1;
270
+
271
+	if (init_hashtable(_data.channel.call_data_by_cid) != 0)
272
+		return -1;
273
+
268 274
 	lock_init(&_data.lock);
269 275
 	lock_init(&_data.time.lock);
270 276
 	lock_init(&_data.money.lock);
277
+	lock_init(&_data.channel.lock);
271 278
 
272 279
 	register_mi_cmd(mi_credit_control_stats, "cnxcc_stats", NULL, NULL, 0);
273 280
 
... ...
@@ -291,6 +316,8 @@ static int mod_init(void)
291 291
 
292 292
 	_dlgbinds.register_dlgcb(NULL, DLGCB_CREATED, dialog_created_callback, NULL, NULL);
293 293
 
294
+	 register_select_table(sel_declaration);
295
+
294 296
 	return 0;
295 297
 }
296 298
 
... ...
@@ -397,6 +424,7 @@ int try_get_credit_data_entry(str *client_id, credit_data_t **credit_data)
397 397
 	hash_tables_t *hts				= NULL;
398 398
 	*credit_data					= NULL;
399 399
 
400
+	/* by money */
400 401
 	hts					= &_data.money;
401 402
 	lock_get(&hts->lock);
402 403
 
... ...
@@ -411,10 +439,26 @@ int try_get_credit_data_entry(str *client_id, credit_data_t **credit_data)
411 411
 
412 412
 	lock_release(&hts->lock);
413 413
 
414
+	/* by time */
414 415
 	hts					= &_data.time;
415 416
 	lock_get(&hts->lock);
416 417
 
417
-	cd_entry			= str_hash_get(hts->call_data_by_cid, client_id->s, client_id->len);
418
+	cd_entry			= str_hash_get(hts->credit_data_by_client, client_id->s, client_id->len);
419
+
420
+	if (cd_entry != NULL)
421
+	{
422
+		*credit_data	= cd_entry->u.p;
423
+		lock_release(&hts->lock);
424
+		return 0;
425
+	}
426
+
427
+	lock_release(&hts->lock);
428
+
429
+	/* by channel */
430
+	hts					= &_data.channel;
431
+	lock_get(&hts->lock);
432
+
433
+	cd_entry			= str_hash_get(hts->credit_data_by_client, client_id->s, client_id->len);
418 434
 
419 435
 	if (cd_entry != NULL)
420 436
 	{
... ...
@@ -434,6 +478,7 @@ int try_get_call_entry(str *callid, call_t **call, hash_tables_t **hts)
434 434
 
435 435
 	*call					= NULL;
436 436
 
437
+	/* by money */
437 438
 	*hts					= &_data.money;
438 439
 	lock_get(&(*hts)->lock);
439 440
 
... ...
@@ -448,6 +493,7 @@ int try_get_call_entry(str *callid, call_t **call, hash_tables_t **hts)
448 448
 
449 449
 	lock_release(&(*hts)->lock);
450 450
 
451
+	/* by time */
451 452
 	*hts				= &_data.time;
452 453
 	lock_get(&(*hts)->lock);
453 454
 
... ...
@@ -462,6 +508,21 @@ int try_get_call_entry(str *callid, call_t **call, hash_tables_t **hts)
462 462
 
463 463
 	lock_release(&(*hts)->lock);
464 464
 
465
+	/* by channel */
466
+	*hts				= &_data.channel;
467
+	lock_get(&(*hts)->lock);
468
+
469
+	call_entry			= str_hash_get((*hts)->call_data_by_cid, callid->s, callid->len);
470
+
471
+	if (call_entry != NULL)
472
+	{
473
+		*call	= call_entry->u.p;
474
+		lock_release(&(*hts)->lock);
475
+		return 0;
476
+	}
477
+
478
+	lock_release(&(*hts)->lock);
479
+
465 480
 	return -1;
466 481
 }
467 482
 
... ...
@@ -800,8 +861,13 @@ static void free_call(call_t *call)
800 800
 
801 801
 		if (e == NULL)
802 802
 		{
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;
803
+			e			= str_hash_get(_data.channel.call_data_by_cid, call->sip_data.callid.s, call->sip_data.callid.len);
804
+
805
+			if (e == NULL)
806
+			{
807
+				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);
808
+				return;
809
+			}
805 810
 		}
806 811
 	}
807 812
 
... ...
@@ -841,10 +907,30 @@ static int shm_str_hash_alloc(struct str_hash_table *ht, int size)
841 841
 
842 842
 static credit_data_t *get_or_create_credit_data_entry(str *client_id, credit_type_t type)
843 843
 {
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;
844
+	struct str_hash_table *ht	= NULL;
845
+	gen_lock_t *lock			= NULL;
846 846
 	struct str_hash_entry *e	= NULL;
847 847
 
848
+	switch(type)
849
+	{
850
+	case CREDIT_MONEY:
851
+		ht		= _data.money.credit_data_by_client;
852
+		lock	=  &_data.money.lock;
853
+		break;
854
+	case CREDIT_TIME:
855
+		ht		= _data.time.credit_data_by_client;
856
+		lock	=  &_data.time.lock;
857
+		break;
858
+	case CREDIT_CHANNEL:
859
+		ht		= _data.channel.credit_data_by_client;
860
+		lock	=  &_data.channel.lock;
861
+		break;
862
+	default:
863
+		LM_ERR("Something went terribly wrong");
864
+		return NULL;
865
+	}
866
+
867
+
848 868
 	lock_get(lock);
849 869
 	e							= str_hash_get(ht, client_id->s, client_id->len);
850 870
 	lock_release(lock);
... ...
@@ -1121,11 +1207,95 @@ error:
1121 1121
 	return NULL;
1122 1122
 }
1123 1123
 
1124
+static call_t *alloc_new_call_by_channel(credit_data_t *credit_data, struct sip_msg *msg, int max_chan)
1125
+{
1126
+	call_t *call		= NULL;
1127
+
1128
+	lock_get(&credit_data->lock);
1129
+
1130
+	if (credit_data->call_list == NULL)
1131
+	{
1132
+		LM_ERR("Credit data call list is NULL\n");
1133
+		goto error;
1134
+	}
1135
+
1136
+	call 				= shm_malloc(sizeof(call_t));
1137
+	if (call == NULL)
1138
+	{
1139
+		LM_ERR("No shared memory left\n");
1140
+		goto error;
1141
+	}
1142
+
1143
+	if ( (!msg->callid && parse_headers(msg, HDR_CALLID_F, 0) != 0) ||
1144
+		   shm_str_dup(&call->sip_data.callid, &msg->callid->body) != 0 )
1145
+	{
1146
+		LM_ERR("Error processing CALLID hdr\n");
1147
+		goto error;
1148
+	}
1149
+
1150
+	call->sip_data.to_tag.s		= NULL;
1151
+	call->sip_data.to_tag.len 	= 0;
1152
+	call->sip_data.from_tag.s	= NULL;
1153
+	call->sip_data.from_tag.len = 0;
1154
+
1155
+	call->consumed_amount		= 0;
1156
+	call->confirmed				= FALSE;
1157
+	call->max_amount			= max_chan;
1158
+
1159
+	/*
1160
+	 * Reference the client_id from the root of the list
1161
+	 */
1162
+	call->client_id.s			= credit_data->call_list->client_id.s;
1163
+	call->client_id.len			= credit_data->call_list->client_id.len;
1164
+
1165
+	/*
1166
+	 * Insert the newly created call to the list of calls
1167
+	 */
1168
+	clist_insert(credit_data->call_list, call, next, prev);
1169
+
1170
+	lock_init(&call->lock);
1171
+
1172
+	/*
1173
+	 * Increase the number of calls for this client. This call is not yet confirmed.
1174
+	 */
1175
+	credit_data->number_of_calls++;
1176
+
1177
+	lock_release(&credit_data->lock);
1178
+
1179
+	LM_DBG("New call allocated for client [%.*s]\n", call->client_id.len, call->client_id.s);
1180
+
1181
+
1182
+	return call;
1183
+
1184
+error:
1185
+	lock_release(&credit_data->lock);
1186
+	return NULL;
1187
+}
1188
+
1124 1189
 static int add_call_by_cid(str *cid, call_t *call, credit_type_t type)
1125 1190
 {
1191
+	struct str_hash_table *ht	= NULL;
1192
+	gen_lock_t *lock			= NULL;
1126 1193
 	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;
1194
+
1195
+	switch(type)
1196
+	{
1197
+	case CREDIT_MONEY:
1198
+		ht		= _data.money.call_data_by_cid;
1199
+		lock	=  &_data.money.lock;
1200
+		break;
1201
+	case CREDIT_TIME:
1202
+		ht		= _data.time.call_data_by_cid;
1203
+		lock	=  &_data.time.lock;
1204
+		break;
1205
+	case CREDIT_CHANNEL:
1206
+		ht		= _data.channel.call_data_by_cid;
1207
+		lock	=  &_data.channel.lock;
1208
+		break;
1209
+	default:
1210
+		LM_ERR("Something went terribly wrong");
1211
+		return -1;
1212
+	}
1129 1213
 
1130 1214
 	e	= str_hash_get(ht, cid->s, cid->len);
1131 1215
 
... ...
@@ -1326,13 +1496,184 @@ static int set_max_credit(struct sip_msg* msg,
1326 1326
 	}
1327 1327
 	else
1328 1328
 	{
1329
-		LM_ALERT("MSG was not a request\n");
1329
+		LM_ALERT("MSG was not an INVITE\n");
1330 1330
 		return -1;
1331 1331
 	}
1332 1332
 
1333 1333
 	return 1;
1334 1334
 }
1335 1335
 
1336
+static int terminate_all(struct sip_msg* msg, char* str_pv_client)
1337
+{
1338
+	credit_data_t *credit_data 	= NULL;
1339
+	pv_spec_t *client_id_spec	= (pv_spec_t *) str_pv_client;
1340
+
1341
+	pv_value_t client_id_val;
1342
+
1343
+	if (pv_get_spec_value(msg, client_id_spec, &client_id_val) != 0)
1344
+	{
1345
+		LM_ERR("[%.*s]: can't get client_id pvar value\n", msg->callid->body.len, msg->callid->body.s);
1346
+		return -1;
1347
+	}
1348
+
1349
+	if (client_id_val.rs.len == 0 || client_id_val.rs.s == NULL)
1350
+	{
1351
+		LM_ERR("[%.*s]: client ID cannot be null\n", msg->callid->body.len, msg->callid->body.s);
1352
+		return -1;
1353
+	}
1354
+
1355
+	if (try_get_credit_data_entry(&client_id_val.rs, &credit_data) != 0)
1356
+	{
1357
+		LM_DBG("[%.*s] not found\n", msg->callid->body.len, msg->callid->body.s);
1358
+		return -1;
1359
+	}
1360
+
1361
+	terminate_all_calls(credit_data);
1362
+
1363
+	return 1;
1364
+}
1365
+
1366
+static int get_channel_count(struct sip_msg* msg, char* str_pv_client, char* str_pv_chan_count)
1367
+{
1368
+	credit_data_t *credit_data 	= NULL;
1369
+	pv_spec_t *chan_count_spec	= (pv_spec_t *) str_pv_chan_count,
1370
+			  *client_id_spec	= (pv_spec_t *) str_pv_client;
1371
+
1372
+	pv_value_t chan_count_val, client_id_val;
1373
+	int value					= -1;
1374
+
1375
+	if (pv_get_spec_value(msg, client_id_spec, &client_id_val) != 0)
1376
+	{
1377
+		LM_ERR("[%.*s]: can't get client_id pvar value\n", msg->callid->body.len, msg->callid->body.s);
1378
+		return -1;
1379
+	}
1380
+
1381
+	if (client_id_val.rs.len == 0 || client_id_val.rs.s == NULL)
1382
+	{
1383
+		LM_ERR("[%.*s]: client ID cannot be null\n", msg->callid->body.len, msg->callid->body.s);
1384
+		return -1;
1385
+	}
1386
+
1387
+	if (try_get_credit_data_entry(&client_id_val.rs, &credit_data) == 0)
1388
+		value	= credit_data->number_of_calls;
1389
+	else
1390
+		LM_ALERT("[%.*s] not found\n", msg->callid->body.len, msg->callid->body.s);
1391
+
1392
+	if (!pv_is_w(chan_count_spec))
1393
+	{
1394
+		LM_ERR("pvar is not writable");
1395
+		return -1;
1396
+	}
1397
+
1398
+	memset(&chan_count_val, 0, sizeof(chan_count_val));
1399
+
1400
+	chan_count_val.flags 	= PV_VAL_STR;
1401
+
1402
+	if (value > 0)
1403
+		chan_count_val.rs.s 	= int2str(value, &chan_count_val.rs.len);
1404
+	else
1405
+	{
1406
+		char buff[2]			= { '-', '1' };
1407
+		chan_count_val.rs.s 	= buff;
1408
+		chan_count_val.rs.len	= 2;
1409
+	}
1410
+
1411
+	if (pv_set_spec_value(msg, chan_count_spec, 0, &chan_count_val) != 0)
1412
+	{
1413
+		LM_ERR("Error writing value to pvar");
1414
+		return -1;
1415
+	}
1416
+
1417
+	return 1;
1418
+}
1419
+
1420
+static int set_max_channels(struct sip_msg* msg, char* str_pv_client, char* str_pv_max_chan)
1421
+{
1422
+	credit_data_t *credit_data 	= NULL;
1423
+	call_t *call				= NULL;
1424
+	pv_spec_t *max_chan_spec	= (pv_spec_t *) str_pv_max_chan,
1425
+			  *client_id_spec	= (pv_spec_t *) str_pv_client;
1426
+	pv_value_t max_chan_val, client_id_val;
1427
+	int max_chan				= 0;
1428
+
1429
+	set_ctrl_flag(msg);
1430
+
1431
+	if (parse_headers(msg, HDR_CALLID_F, 0) != 0)
1432
+	{
1433
+		LM_ERR("Error parsing Call-ID");
1434
+		return -1;
1435
+	}
1436
+
1437
+	if (msg->first_line.type == SIP_REQUEST && msg->first_line.u.request.method_value == METHOD_INVITE)
1438
+	{
1439
+		if (has_to_tag(msg))
1440
+		{
1441
+			LM_ERR("INVITE is a reINVITE\n");
1442
+			return -1;
1443
+		}
1444
+
1445
+		if (pv_get_spec_value(msg, max_chan_spec, &max_chan_val) != 0)
1446
+		{
1447
+			LM_ERR("Can't get max_chan pvar value\n");
1448
+			return -1;
1449
+		}
1450
+		max_chan	= max_chan_val.ri;
1451
+
1452
+		if (max_chan <= 0)
1453
+		{
1454
+			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);
1455
+			return -1;
1456
+		}
1457
+
1458
+		if (pv_get_spec_value(msg, client_id_spec, &client_id_val) != 0)
1459
+		{
1460
+			LM_ERR("[%.*s]: can't get client_id pvar value\n", msg->callid->body.len, msg->callid->body.s);
1461
+			return -1;
1462
+		}
1463
+
1464
+		if (client_id_val.rs.len == 0 || client_id_val.rs.s == NULL)
1465
+		{
1466
+			LM_ERR("[%.*s]: client ID cannot be null\n", msg->callid->body.len, msg->callid->body.s);
1467
+			return -1;
1468
+		}
1469
+
1470
+		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,
1471
+																		max_chan,
1472
+																		msg->callid->body.len, msg->callid->body.s);
1473
+
1474
+		if ((credit_data = get_or_create_credit_data_entry(&client_id_val.rs, CREDIT_CHANNEL)) == NULL)
1475
+		{
1476
+			LM_ERR("Error retrieving credit data from shared memory for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
1477
+			return -1;
1478
+		}
1479
+
1480
+		if (credit_data->number_of_calls + 1 > max_chan)
1481
+			return -2; // you have, between calls being setup plus those established, more than you maximum quota
1482
+
1483
+		if (credit_data->concurrent_calls + 1 > max_chan)
1484
+			return -3; // you have the max amount of established calls already
1485
+
1486
+		if ((call = alloc_new_call_by_channel(credit_data, msg, max_chan)) == NULL)
1487
+		{
1488
+			LM_ERR("Unable to allocate new call for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
1489
+			return -1;
1490
+		}
1491
+
1492
+		if (add_call_by_cid(&call->sip_data.callid, call, CREDIT_CHANNEL) != 0)
1493
+		{
1494
+			LM_ERR("Unable to allocate new cid_by_client for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
1495
+			return -1;
1496
+		}
1497
+
1498
+		return 1;
1499
+	}
1500
+	else
1501
+	{
1502
+		LM_ALERT("MSG was not an INVITE\n");
1503
+		return -1;
1504
+	}
1505
+}
1506
+
1336 1507
 static int set_max_time(struct sip_msg* msg, char* str_pv_client, char* str_pv_maxsecs)
1337 1508
 {
1338 1509
 	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 1407
 	}
1408 1408
 	else
1409 1409
 	{
1410
-		LM_ALERT("MSG was not a request\n");
1410
+		LM_ALERT("MSG was not an INVITE\n");
1411 1411
 		return -1;
1412 1412
 	}
1413 1413
 
... ...
@@ -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 48
 typedef enum credit_type
49 49
 {
50 50
 	CREDIT_TIME,
51
-	CREDIT_MONEY
51
+	CREDIT_MONEY,
52
+	CREDIT_CHANNEL
52 53
 } credit_type_t;
53 54
 
54 55
 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 82
 	flag_t ctrl_flag;
83 83
 
84 84
 	int check_period;
85
-	int number_of_timers;
86 85
 
87 86
 } data_t;
88 87
 
... ...
@@ -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 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
4
+ *                    ConexionGroup (www.conexiongroup.com)
5
+ *
6
+ * This file is part of Kamailio, a free SIP server.
7
+ *
8
+ * Kamailio is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * Kamailio is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
+ *
22
+ */
23
+
24
+#include "../../select.h"
25
+#include "../../select_buf.h"
26
+
27
+#include "cnxcc_mod.h"
28
+
29
+extern data_t _data;
30
+
31
+int sel_root(str* res, select_t* s, struct sip_msg* msg)  /* dummy */
32
+{
33
+	return 0;
34
+}
35
+
36
+int sel_channels(str* res, select_t* s, struct sip_msg* msg)
37
+{
38
+	LM_DBG("sel_channels");
39
+
40
+	return 0;
41
+}
42
+
43
+int sel_channels_count(str* res, select_t* s, struct sip_msg* msg)
44
+{
45
+	LM_DBG("sel_channels_count for [%.*s]",  s->params[2].v.s.len, s->params[2].v.s.s);
46
+
47
+	credit_data_t *credit_data	= NULL;
48
+	int value					= 0;
49
+
50
+	if (s->params[2].v.s.len <= 0)
51
+	{
52
+		LM_ERR("Client must be specified");
53
+		return -1;
54
+	}
55
+
56
+	if (try_get_credit_data_entry(&s->params[2].v.s, &credit_data) >= 0)
57
+		value = credit_data->number_of_calls;
58
+	else
59
+		LM_DBG("Client [%.*s] not found", s->params[2].v.s.len, s->params[2].v.s.s);
60
+
61
+	res->s 	= int2str(value, &res->len);
62
+
63
+	return 0;
64
+}
65
+
66
+
0 67
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
4
+ *                    ConexionGroup (www.conexiongroup.com)
5
+ *
6
+ * This file is part of Kamailio, a free SIP server.
7
+ *
8
+ * Kamailio is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * Kamailio is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
+ *
22
+ */
23
+
24
+#ifndef CNXCC_SELECT_H_
25
+#define CNXCC_SELECT_H_
26
+
27
+int sel_root(str* res, select_t* s, struct sip_msg* msg);
28
+int sel_channels(str* res, select_t* s, struct sip_msg* msg);
29
+int sel_channels_count(str* res, select_t* s, struct sip_msg* msg);
30
+
31
+#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 129
 
130 130
 ####### Global Parameters #########
131 131
 
132
+
132 133
 #!ifdef WITH_DEBUG
133 134
 debug=4
134
-log_stderror=yes
135
+log_stderror=no
135 136
 #!else
136 137
 debug=2
137 138
 log_stderror=no
... ...
@@ -204,7 +210,7 @@ voicemail.srv_port = "5060" desc "VoiceMail Port"
204 204
 #!ifdef WITH_SRCPATH
205 205
 mpath="modules_k:modules"
206 206
 #!else
207
-mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib/kamailio/modules/"
207
+mpath="/usr/local/lib64/kamailio/modules_k/:/usr/local/lib64/kamailio/modules/"
208 208
 #!endif
209 209
 
210 210
 #!ifdef WITH_MYSQL
... ...
@@ -274,7 +280,7 @@ loadmodule "xmlrpc.so"
274 274
 #!endif
275 275
 
276 276
 #!ifdef WITH_DEBUG
277
-loadmodule "debugger.so"
277
+#loadmodule "debugger.so"
278 278
 #!endif
279 279
 
280 280
 # ----------------- setting module-specific parameters ---------------
... ...
@@ -437,7 +443,7 @@ modparam("xmlrpc", "route", "XMLRPC");
437 437
 
438 438
 #!ifdef WITH_DEBUG
439 439
 # ----- debugger params -----
440
-modparam("debugger", "cfgtrace", 1)
440
+#modparam("debugger", "cfgtrace", 1)
441 441
 #!endif
442 442
 
443 443
 #!define DLG_FLAG 28
... ...
@@ -449,9 +455,15 @@ modparam("dialog", "default_timeout", 3600)
449 449
 modparam("dialog", "db_mode", 0)
450 450
 modparam("dialog", "dlg_flag", DLG_FLAG)
451 451
 
452
+#!ifdef CNXCC_CHANNEL
453
+loadmodule "rtimer.so";
454
+modparam("rtimer", "timer", "name=ta;interval=1;mode=1;")
455
+modparam("rtimer", "exec", "timer=ta;route=SHOW_CHANNEL_COUNT")
456
+#!endif
457
+
452 458
 loadmodule "cnxcc.so"
453 459
 modparam("cnxcc", "dlg_flag", CC_FLAG)
454
-modparam("cnxcc", "credit_check_period", 1)
460
+modparam("cnxcc", "credit_check_period", 1) #check every 1 second
455 461
 
456 462
 ####### Routing Logic ########
457 463
 
... ...
@@ -502,6 +514,7 @@ request_route {
502 502
 	# dispatch requests to foreign domains
503 503
 	route(SIPOUT);
504 504
 
505
+	
505 506
 	### requests for my local domains
506 507
 
507 508
 	# handle presence related requests
... ...
@@ -522,9 +535,12 @@ request_route {
522 522
 
523 523
 	# user location service
524 524
 	route(LOCATION);
525
-
526
-	route(CNXCC);
527 525
 	
526
+	if (is_method("INVITE")) {
527
+		route(CNXCC);
528
+	}
529
+
530
+        	
528 531
 	route(RELAY);
529 532
 }
530 533
 
... ...
@@ -536,14 +552,18 @@ route[CNXCC]
536 536
 	# 
537 537
 	# This hardcoded values are just for illustrative purposes
538 538
 	#
539
+	
540
+	$var(client)		= "customer1";
541
+
542
+#!ifdef CNXCC_MONEY
543
+	xlog("L_INFO", "Setting up money based credit control");
539 544
 
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";
545
+	$var(credit) 		= "10";	# 10$ of credit
546
+	$var(cost_per_sec) 	= "1";  # 1$ per sec
547
+	$var(i_pulse)		= "1";  # 1$ to establish the call
548
+	$var(f_pulse)		= "1";  # 1$ per second
545 549
 
546
-	# if only one call is established, that call should last 1m, 36s
550
+	# if only one call is established, that call should last 9 seconds.
547 551
 
548 552
 	if (!cnxcc_set_max_credit("$var(client)",
549 553
 				  "$var(credit)", 
... ...
@@ -551,7 +571,53 @@ route[CNXCC]
551 551
 				  "$var(i_pulse)", 
552 552
 				  "$var(f_pulse)")) {
553 553
 		xlog("Error setting up credit control");
554
+		return;
554 555
 	}
556
+#!endif
557
+
558
+#!ifdef CNXCC_CHANNEL	
559
+	xlog("L_INFO", "Setting up channel based credit control");
560
+
561
+	$var(max_chan)	= 2;
562
+	$var(retcode)	= cnxcc_set_max_channels("$var(client)", "$var(max_chan)");
563
+
564
+	if ($var(retcode) == -1) {
565
+		xlog("Error setting up credit control");
566
+		return;
567
+	}
568
+
569
+        $var(count)     = -1;
570
+
571
+        if (!cnxcc_get_channel_count("$var(client)", "$var(count)")) {
572
+                xlog("Error getting customer's channel count");
573
+        }
574
+
575
+        xlog("L_INFO", "CNXCC ROUTE: $var(client) has $var(count) call(s)");
576
+
577
+	if ($var(retcode) < -1) {
578
+		xlog("Too many channels for customer");
579
+		sl_send_reply(403, "Forbidden");
580
+
581
+		if (!cnxcc_terminate_all("$var(client)")) {
582
+			xlog("Error terminating customer's calls");
583
+		}
584
+
585
+		exit;
586
+	}
587
+#!endif
588
+
589
+#!ifdef CNXCC_TIME
590
+	xlog("L_INFO", "Setting up time based credit control");
591
+	
592
+	$var(max_time)	= 10;
593
+	
594
+	if (!cnxcc_set_max_time("$var(client)",
595
+                                  "$var(max_time)")) {
596
+                xlog("Error setting up credit control");
597
+                return;
598
+        }
599
+#!endif
600
+
555 601
 }
556 602
 
557 603
 event_route[cnxcc:call-shutdown]
... ...
@@ -561,6 +627,14 @@ event_route[cnxcc:call-shutdown]
561 561
 	# perform some kind of notification, database update, email sending, etc
562 562
 }
563 563
 
564
+#!ifdef CNXCC_CHANNEL
565
+route[SHOW_CHANNEL_COUNT]
566
+{
567
+	$var(count) = @cnxcc.channels["customer1"].count;
568
+	xlog("L_INFO", "customer1 has $var(count) call(s)");
569
+}
570
+#!endif
571
+
564 572
 route[RELAY] {
565 573
 
566 574
 	# enable additional event routes for forwarded requests
... ...
@@ -981,3 +1055,8 @@ failure_route[MANAGE_FAILURE] {
981 981
 	}
982 982
 #!endif
983 983
 }
984
+
985
+event_route[dialog:failed]
986
+{
987
+	xlog("dialog failed");
988
+}