SMPP: Implement transaction mode for SUBMIT-SM

WARNING: if the ESME disconnects, osmo_esme gets freed, and
sms->smpp.esme might point to invalid/unallocated memory!
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index dea4119..c6a40e3 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -271,12 +271,29 @@
 	struct ctrl_handle *ctrl;
 };
 
+struct osmo_esme;
+
+enum gsm_sms_source_id {
+	SMS_SOURCE_UNKNOWN = 0,
+	SMS_SOURCE_MS,		/* received from MS */
+	SMS_SOURCE_VTY,		/* received from VTY */
+	SMS_SOURCE_SMPP,	/* received via SMPP */
+};
+
 #define SMS_HDR_SIZE	128
 #define SMS_TEXT_SIZE	256
 struct gsm_sms {
 	unsigned long long id;
 	struct gsm_subscriber *sender;
 	struct gsm_subscriber *receiver;
+	enum gsm_sms_source_id source;
+
+	struct {
+		struct osmo_esme *esme;
+		uint32_t sequence_nr;
+		int transaction_mode;
+		char msg_id[16];
+	} smpp;
 
 	unsigned long validity_minutes;
 	uint8_t reply_path_req;
diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c
index cd3ab3a..32dac61 100644
--- a/openbsc/src/libmsc/smpp_openbsc.c
+++ b/openbsc/src/libmsc/smpp_openbsc.c
@@ -38,6 +38,8 @@
 #include <openbsc/debug.h>
 #include <openbsc/db.h>
 #include <openbsc/gsm_04_11.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/signal.h>
 
 #include "smpp_smsc.h"
 
@@ -111,6 +113,8 @@
 	}
 
 	sms = sms_alloc();
+	sms->source = SMS_SOURCE_SMPP;
+	sms->smpp.sequence_nr = submit->sequence_number;
 	sms->receiver = subscr_get(dest);
 	strncpy(sms->dest_addr, dest->extension, sizeof(sms->dest_addr)-1);
 	sms->sender = subscr_get_by_id(net, 1);
@@ -163,6 +167,7 @@
 		submit_r->command_status = rc;
 		return 0;
 	}
+	sms->smpp.esme = esme;
 	/* FIXME: TP-PID */
 
 	switch (submit->esm_class & 3) {
@@ -182,13 +187,54 @@
 		rc = 0;
 		break;
 	case 2: /* forward (i.e. transaction) mode */
-		/* FIXME */
+		sms->smpp.transaction_mode = 1;
+		gsm411_send_sms_subscr(sms->receiver, sms);
 		rc = 1; /* don't send any response yet */
 		break;
 	}
 	return rc;
 }
 
+static int smpp_sms_cb(unsigned int subsys, unsigned int signal,
+			void *handler_data, void *signal_data)
+{
+	struct gsm_network *network = handler_data;
+	struct sms_signal_data *sig_sms = signal_data;
+	struct gsm_sms *sms = sig_sms->sms;
+	int rc = 0;
+
+	if (!sms)
+		return 0;
+
+	if (sms->source != SMS_SOURCE_SMPP)
+		return 0;
+
+	switch (signal) {
+	case S_SMS_UNKNOWN_ERROR:
+		if (sms->smpp.transaction_mode) {
+			/* Send back the SUBMIT-SM response with apropriate error */
+			LOGP(DSMS, LOGL_INFO, "SMPP SUBMIT-SM: Error\n");
+			rc = smpp_tx_submit_r(sms->smpp.esme,
+					      sms->smpp.sequence_nr,
+					      ESME_RDELIVERYFAILURE,
+					      sms->smpp.msg_id);
+		}
+		break;
+	case S_SMS_DELIVERED:
+		/* SMS layer tells us the delivery has been completed */
+		if (sms->smpp.transaction_mode) {
+			/* Send back the SUBMIT-SM response */
+			LOGP(DSMS, LOGL_INFO, "SMPP SUBMIT-SM: Success\n");
+			rc = smpp_tx_submit_r(sms->smpp.esme,
+					      sms->smpp.sequence_nr,
+					      ESME_ROK, sms->smpp.msg_id);
+		}
+		break;
+	}
+
+	return rc;
+}
+
 
 int smpp_openbsc_init(struct gsm_network *net, uint16_t port)
 {
@@ -201,6 +247,8 @@
 	if (rc < 0)
 		talloc_free(smsc);
 
+	osmo_signal_register_handler(SS_SMS, smpp_sms_cb, net);
+
 	return rc;
 }
 
diff --git a/openbsc/src/libmsc/smpp_smsc.c b/openbsc/src/libmsc/smpp_smsc.c
index ff6b1f9..dd6a2f9 100644
--- a/openbsc/src/libmsc/smpp_smsc.c
+++ b/openbsc/src/libmsc/smpp_smsc.c
@@ -46,6 +46,17 @@
 	ESME_BIND_TX = 0x02,
 };
 
+static struct osmo_esme *
+esme_by_system_id(const struct smsc *smsc, char *system_id)
+{
+	struct osmo_esme *e;
+
+	llist_for_each_entry(e, &smsc->esme_list, list) {
+		if (!strcmp(e->system_id, system_id))
+			return e;
+	}
+	return NULL;
+}
 
 
 #define INIT_RESP(type, resp, req) 		{ \
@@ -280,6 +291,21 @@
 	return PACK_AND_SEND(esme, &enq_r);
 }
 
+int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr,
+		     uint32_t command_status, char *msg_id)
+{
+	struct submit_sm_resp_t submit_r;
+
+	memset(&submit_r, 0, sizeof(submit_r));
+	submit_r.command_length	= 0;
+	submit_r.command_id	= SUBMIT_SM_RESP;
+	submit_r.command_status	= command_status;
+	submit_r.sequence_number= sequence_nr;
+	snprintf(submit_r.message_id, sizeof(submit_r.message_id), "%s", msg_id);
+
+	return PACK_AND_SEND(esme, &submit_r);
+}
+
 static int smpp_handle_submit(struct osmo_esme *esme, struct msgb *msg)
 {
 	struct submit_sm_t submit;
diff --git a/openbsc/src/libmsc/smpp_smsc.h b/openbsc/src/libmsc/smpp_smsc.h
index 500fbc3..8cdd2e7 100644
--- a/openbsc/src/libmsc/smpp_smsc.h
+++ b/openbsc/src/libmsc/smpp_smsc.h
@@ -46,6 +46,9 @@
 
 int smpp_smsc_init(struct smsc *smsc, uint16_t port);
 
+int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr,
+		     uint32_t command_status, char *msg_id);
+
 int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit,
 			struct submit_sm_resp_t *submit_r);