Make random MSISDN assignment optional

Previously if subscriber was automatically created it got assigned
random MSISDN number. Make it optional (defaulting to previous behavior)
by adding following:

* new optional no-extension argument for subscriber-create-on-demand vty
  command
* db unit tests
* vty test

Note: using the db made with new code might result in subscribers with
empty extension. Such subscribers cannot be deleted using old
code. Make sure not to mix db versions or manually fix it by editing
sqlite with external program.

Fixes: OS#1658
Change-Id: Ibbc2e88e4722b08854ebc631485f19ed56443cbb
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index 04f2cc6..b0e8764 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -195,6 +195,10 @@
 	if (net->authorized_reg_str)
 		vty_out(vty, ", authorized regexp: %s", net->authorized_reg_str);
 	vty_out(vty, "%s", VTY_NEWLINE);
+	vty_out(vty, "  Auto create subscriber: %s%s",
+		net->auto_create_subscr ? "yes" : "no", VTY_NEWLINE);
+	vty_out(vty, "  Auto assign extension: %s%s",
+		net->auto_assign_exten ? "yes" : "no", VTY_NEWLINE);
 	vty_out(vty, "  Location updating reject cause: %u%s",
 		net->reject_cause, VTY_NEWLINE);
 	vty_out(vty, "  Encryption: A5/%u%s", net->a5_encryption,
diff --git a/openbsc/src/libbsc/net_init.c b/openbsc/src/libbsc/net_init.c
index 4636d57..0e99097 100644
--- a/openbsc/src/libbsc/net_init.c
+++ b/openbsc/src/libbsc/net_init.c
@@ -21,10 +21,13 @@
 #include <openbsc/osmo_msc_data.h>
 #include <openbsc/gsm_subscriber.h>
 
+#include <stdbool.h>
+
 struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_code,
 				     int (*mncc_recv)(struct gsm_network *, struct msgb *))
 {
 	struct gsm_network *net;
+	const char *default_regexp = "*";
 
 	net = talloc_zero(tall_bsc_ctx, struct gsm_network);
 	if (!net)
@@ -42,13 +45,18 @@
 		return NULL;
 	}
 
+	if (gsm_parse_reg(net, &net->authorized_regexp, &net->authorized_reg_str, 1,
+			  &default_regexp) != 0)
+		return NULL;
+
 	/* Init back pointer */
 	net->bsc_data->auto_off_timeout = -1;
 	net->bsc_data->network = net;
 	INIT_LLIST_HEAD(&net->bsc_data->mscs);
 
 	net->subscr_group->net = net;
-	net->subscr_creation_mode = GSM_SUBSCR_CREAT_W_RAND_EXT;
+	net->auto_create_subscr = true;
+	net->auto_assign_exten = true;
 
 	net->country_code = country_code;
 	net->network_code = network_code;
diff --git a/openbsc/src/libmsc/ctrl_commands.c b/openbsc/src/libmsc/ctrl_commands.c
index a02db36..79e136d 100644
--- a/openbsc/src/libmsc/ctrl_commands.c
+++ b/openbsc/src/libmsc/ctrl_commands.c
@@ -25,6 +25,8 @@
 #include <openbsc/db.h>
 #include <openbsc/debug.h>
 
+#include <stdbool.h>
+
 static bool alg_supported(const char *alg)
 {
 	/*
@@ -96,9 +98,7 @@
 
 	subscr = subscr_get_by_imsi(net->subscr_group, imsi);
 	if (!subscr)
-		subscr = subscr_create_subscriber(net->subscr_group, imsi,
-						  net->ext_min,
-						  net->ext_max);
+		subscr = subscr_create_subscriber(net->subscr_group, imsi);
 	if (!subscr)
 		goto fail;
 
diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c
index b367139..68eba3e 100644
--- a/openbsc/src/libmsc/db.c
+++ b/openbsc/src/libmsc/db.c
@@ -23,6 +23,7 @@
 #include <inttypes.h>
 #include <libgen.h>
 #include <stdio.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -519,7 +520,7 @@
 }
 
 struct gsm_subscriber *db_create_subscriber(const char *imsi, uint64_t smin,
-					    uint64_t smax)
+					    uint64_t smax, bool alloc_exten)
 {
 	dbi_result result;
 	struct gsm_subscriber *subscr;
@@ -551,7 +552,8 @@
 	strncpy(subscr->imsi, imsi, sizeof(subscr->imsi)-1);
 	dbi_result_free(result);
 	LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
-	db_subscriber_alloc_exten(subscr, smin, smax);
+	if (alloc_exten)
+		db_subscriber_alloc_exten(subscr, smin, smax);
 	return subscr;
 }
 
@@ -956,8 +958,11 @@
 
 	dbi_conn_quote_string_copy(conn, 
 				   subscriber->name, &q_name);
-	dbi_conn_quote_string_copy(conn, 
-				   subscriber->extension, &q_extension);
+	if (subscriber->extension[0] != '\0')
+		dbi_conn_quote_string_copy(conn,
+					   subscriber->extension, &q_extension);
+	else
+		q_extension = strdup("NULL");
 	
 	if (subscriber->tmsi != GSM_RESERVED_TMSI) {
 		sprintf(tmsi, "%u", subscriber->tmsi);
@@ -1062,15 +1067,17 @@
 	}
 	dbi_result_free(result);
 
-	result = dbi_conn_queryf(conn,
-			"DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
-			subscr->extension, subscr->extension);
-	if (!result) {
-		LOGP(DDB, LOGL_ERROR,
-			"Failed to delete SMS for %llu\n", subscr->id);
-		return -1;
+	if (subscr->extension[0] != '\0') {
+		result = dbi_conn_queryf(conn,
+			    "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
+					 subscr->extension, subscr->extension);
+		if (!result) {
+			LOGP(DDB, LOGL_ERROR,
+			     "Failed to delete SMS for %llu\n", subscr->id);
+			return -1;
+		}
+		dbi_result_free(result);
 	}
-	dbi_result_free(result);
 
 	result = dbi_conn_queryf(conn,
 			"DELETE FROM VLR WHERE subscriber_id=%llu",
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index 6704497..1b02efe 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -530,15 +530,13 @@
 static struct gsm_subscriber *subscr_create(const struct gsm_network *net,
 					    const char *imsi)
 {
-	if (net->subscr_creation_mode == GSM_SUBSCR_DONT_CREATE)
+	if (!net->auto_create_subscr)
 		return NULL;
 
-	if (net->subscr_creation_mode & GSM_SUBSCR_CREAT_W_REGEXP)
-		if (!subscr_regexp_check(net, imsi))
-			return NULL;
+	if (!subscr_regexp_check(net, imsi))
+		return NULL;
 
-	return subscr_create_subscriber(net->subscr_group, imsi, net->ext_min,
-					net->ext_max);
+	return subscr_create_subscriber(net->subscr_group, imsi);
 }
 
 /* Parse Chapter 9.2.11 Identity Response */
diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c
index 1dc2cc2..08198c7 100644
--- a/openbsc/src/libmsc/gsm_subscriber.c
+++ b/openbsc/src/libmsc/gsm_subscriber.c
@@ -26,6 +26,7 @@
 #include <string.h>
 #include <assert.h>
 #include <time.h>
+#include <stdbool.h>
 
 #include <osmocom/core/talloc.h>
 
@@ -203,10 +204,12 @@
 }
 
 struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp,
-						const char *imsi, uint64_t smin,
-						uint64_t smax)
+						const char *imsi)
 {
-	struct gsm_subscriber *subscr = db_create_subscriber(imsi, smin, smax);
+	struct gsm_subscriber *subscr = db_create_subscriber(imsi,
+							     sgrp->net->ext_min,
+							     sgrp->net->ext_max,
+							     sgrp->net->auto_assign_exten);
 	if (subscr)
 		subscr->group = sgrp;
 	return subscr;
diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c
index a035bf9..74da1d7 100644
--- a/openbsc/src/libmsc/vty_interface_layer3.c
+++ b/openbsc/src/libmsc/vty_interface_layer3.c
@@ -242,9 +242,7 @@
 	if (subscr)
 		db_sync_subscriber(subscr);
 	else {
-		subscr = subscr_create_subscriber(gsmnet->subscr_group, argv[0],
-						  gsmnet->ext_min,
-						  gsmnet->ext_max);
+		subscr = subscr_create_subscriber(gsmnet->subscr_group, argv[0]);
 
 		if (!subscr) {
 			vty_out(vty, "%% No subscriber created for IMSI %s%s",
@@ -1044,6 +1042,8 @@
 {
 	struct gsm_network *gsmnet = gsmnet_from_vty(vty);
 	uint64_t mi = atoi(argv[0]), ma = atoi(argv[1]);
+	gsmnet->auto_create_subscr = true;
+	gsmnet->auto_assign_exten = true;
 	if (mi >= ma) {
 		vty_out(vty, "Incorrect range: %s >= %s, expected MIN < MAX%s",
 			argv[0], argv[1], VTY_NEWLINE);
@@ -1055,15 +1055,13 @@
 }
 
 DEFUN(cfg_nitb_subscr_create, cfg_nitb_subscr_create_cmd,
-      "subscriber-create-on-demand [regexp]",
+      "subscriber-create-on-demand [no-extension]",
       "Make a new record when a subscriber is first seen.\n"
-      "Create subscribers only if IMSI matches the regexp specified in "
-      "authorized-regexp command\n")
+      "Do not automatically assign extension to created subscribers\n")
 {
 	struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-	gsmnet->subscr_creation_mode = GSM_SUBSCR_CREAT_W_RAND_EXT;
-	if (argc)
-		gsmnet->subscr_creation_mode |= GSM_SUBSCR_CREAT_W_REGEXP;
+	gsmnet->auto_create_subscr = true;
+	gsmnet->auto_assign_exten = argc ? false : true;
 	return CMD_SUCCESS;
 }
 
@@ -1072,7 +1070,7 @@
       NO_STR "Make a new record when a subscriber is first seen.\n")
 {
 	struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-	gsmnet->subscr_creation_mode = GSM_SUBSCR_DONT_CREATE;
+	gsmnet->auto_create_subscr = false;
 	return CMD_SUCCESS;
 }
 
@@ -1097,12 +1095,15 @@
 static int config_write_nitb(struct vty *vty)
 {
 	struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-	enum gsm_subscr_creation_mode scm = gsmnet->subscr_creation_mode;
-	const char *reg = (scm & GSM_SUBSCR_CREAT_W_REGEXP) ? " regexp" : "",
-		*pref = scm ? "" : "no ";
+
 	vty_out(vty, "nitb%s", VTY_NEWLINE);
-	vty_out(vty, " %ssubscriber-create-on-demand%s%s",
-		pref, reg, VTY_NEWLINE);
+	if (!gsmnet->auto_create_subscr)
+		vty_out(vty, " no subscriber-create-on-demand%s", VTY_NEWLINE);
+	else
+		vty_out(vty, " subscriber-create-on-demand%s%s",
+			gsmnet->auto_assign_exten ? "" : " no-extension",
+			VTY_NEWLINE);
+
 	if (gsmnet->ext_min != GSM_MIN_EXTEN || gsmnet->ext_max != GSM_MAX_EXTEN)
 		vty_out(vty, " subscriber-create-on-demand random %"PRIu64" %"
 			PRIu64"%s", gsmnet->ext_min, gsmnet->ext_max,