Merge commit 'har/master'
diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h
index 67436df..0713593 100644
--- a/openbsc/include/openbsc/db.h
+++ b/openbsc/include/openbsc/db.h
@@ -48,4 +48,9 @@
 struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id);
 struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr);
 int db_sms_mark_sent(struct gsm_sms *sms);
+
+/* APDU blob storage */
+int db_apdu_blob_store(struct gsm_subscriber *subscr, 
+			u_int8_t apdu_id_flags, u_int8_t len,
+			u_int8_t *apdu);
 #endif /* _DB_H */
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index 1ca1ccd..161364c 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -705,6 +705,8 @@
 int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi);
 
 int gsm48_send_rr_release(struct gsm_lchan *lchan);
+int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
+			   u_int8_t apdu_len, u_int8_t *apdu);
 
 int bsc_upqueue(struct gsm_network *net);
 
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index cbfeeaa..448de06 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -10,7 +10,7 @@
 		gsm_04_11.c telnet_interface.c subchan_demux.c \
 		trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
 		input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c talloc_ctx.c \
-		transaction.c rtp_proxy.c bsc_rll.c token_auth.c
+		transaction.c rtp_proxy.c bsc_rll.c token_auth.c rrlp.c
 
 libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
 
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 5f84513..9945aa9 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -930,6 +930,8 @@
 		(struct gsm48_system_information_type_3*)&si3;
 	struct gsm48_system_information_type_4 *type_4 =
 		(struct gsm48_system_information_type_4*)&si4;
+	struct gsm48_system_information_type_5 *type_5 =
+		(struct gsm48_system_information_type_5*)&si5;
 	struct gsm48_system_information_type_6 *type_6 =
 		(struct gsm48_system_information_type_6*)&si6;
 	struct gsm48_loc_area_id lai;
@@ -969,6 +971,17 @@
 		type_3->rach_control.cell_bar = 0;
 		type_4->rach_control.cell_bar = 0;
 	}
+
+	/* FIXME: This is just for HAR */
+	if (bts->c0->arfcn == 121) {
+		/* this is setting pin 124 */
+		type_2->bcch_frequency_list[0] = 0x08;
+		type_5->bcch_frequency_list[0] = 0x08;
+	} else if (bts->c0->arfcn == 124) {
+		/* this is setting pin 121 */
+		type_2->bcch_frequency_list[0] = 0x01;
+		type_5->bcch_frequency_list[0] = 0x01;
+	}
 }
 
 
@@ -1219,6 +1232,7 @@
 	tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
 	talloc_ctx_init();
 	on_dso_load_token();
+	on_dso_load_rrlp();
 
 	/* parse options */
 	handle_options(argc, argv);
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 0704bca..45e950b 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -24,6 +24,7 @@
 #include <openbsc/gsm_04_11.h>
 #include <openbsc/db.h>
 #include <openbsc/talloc.h>
+#include <openbsc/debug.h>
 
 #include <libgen.h>
 #include <stdio.h>
@@ -88,6 +89,7 @@
 		"sent TIMESTAMP, "
 		"sender_id INTEGER NOT NULL, "
 		"receiver_id INTEGER NOT NULL, "
+		"deliver_attempts INTEGER NOT NULL DEFAULT 0, "
 		/* data directly copied/derived from SMS */
 		"valid_until TIMESTAMP, "
 		"reply_path_req INTEGER NOT NULL, "
@@ -108,6 +110,13 @@
 		"subscriber_id NUMERIC UNIQUE NOT NULL, "
 		"last_bts NUMERIC NOT NULL "
 		")",
+	"CREATE TABLE IF NOT EXISTS ApduBlobs ("
+		"id INTEGER PRIMARY KEY AUTOINCREMENT, "
+		"created TIMESTAMP NOT NULL, "
+		"apdu_id_flags INTEGER NOT NULL, "
+		"subscriber_id INTEGER NOT NULL, "
+		"apdu BLOB "
+		")",
 };
 
 void db_error_func(dbi_conn conn, void* data) {
@@ -252,6 +261,53 @@
 	return subscr;
 }
 
+static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
+{
+	dbi_result result;
+	char *string;
+	unsigned int cm1;
+	const unsigned char *cm2, *cm3;
+	struct gsm_equipment *equip = &subscr->equipment;
+
+	result = dbi_conn_queryf(conn,
+				"SELECT equipment.* FROM equipment,equipmentwatch "
+				"WHERE equipmentwatch.equipment_id=equipment.id "
+				"AND equipmentwatch.subscriber_id = %llu "
+				"ORDER BY updated DESC", subscr->id);
+	if (!result)
+		return -EIO;
+
+	if (!dbi_result_next_row(result)) {
+		dbi_result_free(result);
+		return -ENOENT;
+	}
+
+	equip->id = dbi_result_get_ulonglong(result, "id");
+
+	string = dbi_result_get_string(result, "imei");
+	if (string)
+		strncpy(equip->imei, string, sizeof(equip->imei));
+
+	cm1 = dbi_result_get_uint(result, "classmark1") & 0xff;
+	equip->classmark1 = *((struct gsm48_classmark1 *) &cm1);
+
+	equip->classmark2_len = dbi_result_get_field_length(result, "classmark2");
+	cm2 = dbi_result_get_binary(result, "classmark2");
+	if (equip->classmark2_len > sizeof(equip->classmark2))
+		equip->classmark2_len = sizeof(equip->classmark2);
+	memcpy(equip->classmark2, cm2, equip->classmark2_len);
+
+	equip->classmark3_len = dbi_result_get_field_length(result, "classmark3");
+	cm3 = dbi_result_get_binary(result, "classmark3");
+	if (equip->classmark3_len > sizeof(equip->classmark3))
+		equip->classmark3_len = sizeof(equip->classmark3);
+	memcpy(equip->classmark3, cm3, equip->classmark3_len);
+
+	dbi_result_free(result);
+
+	return 0;
+}
+#define BASE_QUERY "SELECT * FROM Subscriber "
 struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
 					 enum gsm_subscriber_field field,
 					 const char *id)
@@ -265,7 +321,7 @@
 	case GSM_SUBSCRIBER_IMSI:
 		dbi_conn_quote_string_copy(conn, id, &quoted);
 		result = dbi_conn_queryf(conn,
-			"SELECT * FROM Subscriber "
+			BASE_QUERY
 			"WHERE imsi = %s ",
 			quoted
 		);
@@ -274,7 +330,7 @@
 	case GSM_SUBSCRIBER_TMSI:
 		dbi_conn_quote_string_copy(conn, id, &quoted);
 		result = dbi_conn_queryf(conn,
-			"SELECT * FROM Subscriber "
+			BASE_QUERY
 			"WHERE tmsi = %s ",
 			quoted
 		);
@@ -283,7 +339,7 @@
 	case GSM_SUBSCRIBER_EXTENSION:
 		dbi_conn_quote_string_copy(conn, id, &quoted);
 		result = dbi_conn_queryf(conn,
-			"SELECT * FROM Subscriber "
+			BASE_QUERY
 			"WHERE extension = %s ",
 			quoted
 		);
@@ -292,7 +348,7 @@
 	case GSM_SUBSCRIBER_ID:
 		dbi_conn_quote_string_copy(conn, id, &quoted);
 		result = dbi_conn_queryf(conn,
-			"SELECT * FROM Subscriber "
+			BASE_QUERY
 			"WHERE id = %s ", quoted);
 		free(quoted);
 		break;
@@ -336,6 +392,9 @@
 		subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension,
 		subscr->lac, subscr->authorized);
 	dbi_result_free(result);
+
+	get_equipment_by_subscr(subscr);
+
 	return subscr;
 }
 
@@ -378,6 +437,16 @@
 	dbi_result result;
 	unsigned char *cm2, *cm3;
 
+	printf("DB: Sync Equipment IMEI=%s, classmark1=%02x",
+		equip->imei, equip->classmark1);
+	if (equip->classmark2_len)
+ 		printf(", classmark2=%s",
+			hexdump(equip->classmark2, equip->classmark2_len));
+	if (equip->classmark3_len)
+		printf(", classmark3=%s",
+			hexdump(equip->classmark3, equip->classmark3_len));
+	printf("\n");
+
 	dbi_conn_quote_binary_copy(conn, equip->classmark2,
 				   equip->classmark2_len, &cm2);
 	dbi_conn_quote_binary_copy(conn, equip->classmark3,
@@ -688,8 +757,10 @@
 	struct gsm_sms *sms;
 
 	result = dbi_conn_queryf(conn,
-		"SELECT * FROM SMS "
-		"WHERE id >= %llu AND sent is NULL ORDER BY id",
+		"SELECT * FROM SMS,Subscriber "
+		"WHERE sms.id >= %llu AND sms.sent is NULL "
+			"AND subscriber.lac > 0 "
+		"ORDER BY id",
 		min_id);
 	if (!result)
 		return NULL;
@@ -713,8 +784,10 @@
 	struct gsm_sms *sms;
 
 	result = dbi_conn_queryf(conn,
-		"SELECT * FROM SMS "
-		"WHERE receiver_id = %llu AND sent is NULL ORDER BY id",
+		"SELECT * FROM SMS,Subscriber "
+		"WHERE sms.receiver_id = %llu AND sms.sent is NULL "
+			"AND subscriber.lac > 0 "
+		"ORDER BY id",
 		subscr->id);
 	if (!result)
 		return NULL;
@@ -748,3 +821,45 @@
 	dbi_result_free(result);
 	return 0;
 }
+
+/* increase the number of attempted deliveries */
+int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
+{
+	dbi_result result;
+
+	result = dbi_conn_queryf(conn,
+		"UPDATE SMS "
+		"SET deliver_attempts = deliver_attempts + 1 "
+		"WHERE id = %llu", sms->id);
+	if (!result) {
+		printf("DB: Failed to inc deliver attempts for SMS %llu.\n", sms->id);
+		return 1;
+	}
+
+	dbi_result_free(result);
+	return 0;
+}
+
+int db_apdu_blob_store(struct gsm_subscriber *subscr, 
+			u_int8_t apdu_id_flags, u_int8_t len,
+			u_int8_t *apdu)
+{
+	dbi_result result;
+	char *q_apdu;
+
+	dbi_conn_quote_binary_copy(conn, apdu, len, &q_apdu);
+
+	result = dbi_conn_queryf(conn,
+		"INSERT INTO ApduBlobs "
+		"(created,subscriber_id,apdu_id_flags,apdu) VALUES "
+		"(datetime('now'),%llu,%u,%s)",
+		subscr->id, apdu_id_flags, q_apdu);
+
+	free(q_apdu);
+
+	if (!result)
+		return -EIO;
+
+	dbi_result_free(result);
+	return 0;
+}
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index e5ab49d..46b4479 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1165,8 +1165,10 @@
 	case GSM_MI_TYPE_IMEI:
 	case GSM_MI_TYPE_IMEISV:
 		/* update subscribe <-> IMEI mapping */
-		if (lchan->subscr)
+		if (lchan->subscr) {
 			db_subscriber_assoc_imei(lchan->subscr, mi_string);
+			db_sync_equipment(&lchan->subscr->equipment);
+		}
 		if (lchan->loc_operation)
 			lchan->loc_operation->waiting_for_imei = 0;
 		break;
@@ -1289,6 +1291,7 @@
 	}
 
 	lchan->subscr = subscr;
+	lchan->subscr->equipment.classmark1 = lu->classmark1;
 
 	/* check if we can let the subscriber into our network immediately
 	 * or if we need to wait for identity responses. */
@@ -1614,6 +1617,10 @@
 				GSM_SUBSCRIBER_UPDATE_DETACHED);
 		DEBUGP(DMM, "Subscriber: %s\n",
 		       subscr->name ? subscr->name : subscr->imsi);
+
+		subscr->equipment.classmark1 = idi->classmark1;
+		db_sync_equipment(&subscr->equipment);
+
 		subscr_put(subscr);
 	} else
 		DEBUGP(DMM, "Unknown Subscriber ?!?\n");
@@ -1825,6 +1832,24 @@
 	return 0;
 }
 
+static int gsm48_rx_rr_app_info(struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	u_int8_t apdu_id_flags;
+	u_int8_t apdu_len;
+	u_int8_t *apdu_data;
+
+	apdu_id_flags = gh->data[0];
+	apdu_len = gh->data[1];
+	apdu_data = gh->data+2;
+	
+	DEBUGP(DNM, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s",
+		apdu_id_flags, apdu_len, hexdump(apdu_data, apdu_len));
+
+	return db_apdu_blob_store(msg->lchan->subscr, apdu_id_flags, apdu_len, apdu_data);
+}
+
+
 /* Receive a GSM 04.08 Radio Resource (RR) message */
 static int gsm0408_rcv_rr(struct msgb *msg)
 {
@@ -1854,6 +1879,9 @@
 	case GSM48_MT_RR_MEAS_REP:
 		rc = gsm48_rx_rr_meas_rep(msg);
 		break;
+	case GSM48_MT_RR_APP_INFO:
+		rc = gsm48_rx_rr_app_info(msg);
+		break;
 	default:
 		fprintf(stderr, "Unimplemented GSM 04.08 RR msg type 0x%02x\n",
 			gh->msg_type);
@@ -1888,6 +1916,27 @@
 	return rsl_deact_sacch(lchan);
 }
 
+int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
+			   u_int8_t apdu_len, u_int8_t *apdu)
+{
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh;
+
+	msg->lchan = lchan;
+	
+	DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n",
+		apdu_id, apdu_len);
+	
+	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len);
+	gh->proto_discr = GSM48_PDISC_RR;
+	gh->msg_type = GSM48_MT_RR_APP_INFO;
+	gh->data[0] = apdu_id;
+	gh->data[1] = apdu_len;
+	memcpy(gh->data+2, apdu, apdu_len);
+
+	return gsm48_sendmsg(msg, NULL);
+}
+
 /* Call Control */
 
 /* The entire call control code is written in accordance with Figure 7.10c
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 277a321..85fd6b6 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -295,6 +295,9 @@
 	}
 	/* dispatch a signal to tell higher level about it */
 	dispatch_signal(SS_SMS, S_SMS_SUBMITTED, gsms);
+	/* try delivering the SMS right now */
+	//gsm411_send_sms_subscr(gsms->receiver, gsms);
+
 	return 0;
 }
 
@@ -675,14 +678,19 @@
 
 	if (!trans->sms.is_mt) {
 		DEBUGP(DSMS, "RX RP-ERR on a MO transfer ?\n");
+#if 0
 		return gsm411_send_rp_error(trans, rph->msg_ref,
 					    GSM411_RP_CAUSE_MSG_INCOMP_STATE);
+#endif
 	}
 
 	if (!sms) {
 		DEBUGP(DSMS, "RX RP-ERR, but no sms in transaction?!?\n");
+		return -EINVAL;
+#if 0
 		return gsm411_send_rp_error(trans, rph->msg_ref,
 					    GSM411_RP_CAUSE_PROTOCOL_ERR);
+#endif
 	}
 
 	if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) {
diff --git a/openbsc/src/rrlp.c b/openbsc/src/rrlp.c
new file mode 100644
index 0000000..61bb202
--- /dev/null
+++ b/openbsc/src/rrlp.c
@@ -0,0 +1,73 @@
+
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * 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 Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <sys/types.h>
+
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/signal.h>
+#include <openbsc/gsm_subscriber.h>
+
+/* RRLP MS based position request */
+static const u_int8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 };
+
+static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
+			 void *handler_data, void *signal_data)
+{
+	struct gsm_subscriber *subscr;
+	struct gsm_lchan *lchan;
+
+	switch (signal) {
+	case S_SUBSCR_ATTACHED:
+		/* A subscriber has attached. */
+		subscr = signal_data;
+		lchan = lchan_for_subscr(subscr);
+		if (!lchan)
+			break;
+		gsm48_send_rr_app_info(lchan, 0x00, sizeof(ms_based_pos_req),
+					ms_based_pos_req);
+		break;
+	}
+	return 0;
+}
+
+static int paging_sig_cb(unsigned int subsys, unsigned int signal,
+			 void *handler_data, void *signal_data)
+{
+	struct paging_signal_data *psig_data = signal_data;
+
+	switch (signal) {
+	case S_PAGING_COMPLETED:
+		/* A subscriber has attached. */
+		gsm48_send_rr_app_info(psig_data->lchan, 0x00, 
+					sizeof(ms_based_pos_req),
+					ms_based_pos_req);
+		break;
+	}
+	return 0;
+}
+
+void on_dso_load_rrlp(void)
+{
+	register_signal_handler(SS_SUBSCR, subscr_sig_cb, NULL);
+	register_signal_handler(SS_PAGING, paging_sig_cb, NULL);
+}