Merge branch 'zecke/mgcp-dtmf'
diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h
index d618f3c..bb21e68 100644
--- a/openbsc/include/openbsc/mgcp.h
+++ b/openbsc/include/openbsc/mgcp.h
@@ -1,8 +1,8 @@
 /* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 
 /*
- * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2011 by On-Waves
+ * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2012 by On-Waves
  * All Rights Reserved
  *
  * This program is free software; you can redistribute it and/or modify
@@ -83,6 +83,7 @@
 typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
 typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
 typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
+typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone, const char *data);
 
 #define PORT_ALLOC_STATIC	0
 #define PORT_ALLOC_DYNAMIC	1
@@ -148,6 +149,7 @@
 	mgcp_policy policy_cb;
 	mgcp_reset reset_cb;
 	mgcp_realloc realloc_cb;
+	mgcp_rqnt rqnt_cb;
 	void *data;
 
 	uint32_t last_call_id;
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c
index 4b0222f..54af15b 100644
--- a/openbsc/src/libmgcp/mgcp_protocol.c
+++ b/openbsc/src/libmgcp/mgcp_protocol.c
@@ -2,8 +2,8 @@
 /* The protocol implementation */
 
 /*
- * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2011 by On-Waves
+ * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2012 by On-Waves
  * All Rights Reserved
  *
  * This program is free software; you can redistribute it and/or modify
@@ -822,6 +822,15 @@
 	return NULL;
 }
 
+static char extract_tone(const char *line)
+{
+	const char *str = strstr(line, "D/");
+	if (!str)
+		return CHAR_MAX;
+
+	return str[2];
+}
+
 /*
  * This can request like DTMF detection and forward, fax detection... it
  * can also request when the notification should be send and such. We don't
@@ -831,18 +840,45 @@
 {
 	const char *trans_id;
 	struct mgcp_endpoint *endp;
-	int found;
-	char *data = strtok((char *) msg->l3h, "\r\n");
+	int found, res = 0;
+	char *line, *save;
+	char tone = 0;
 
-	found = mgcp_analyze_header(cfg, data, &trans_id, &endp);
-	if (found != 0)
-		return create_err_response(400, "RQNT", trans_id);
+	for_each_line((char *) msg->l3h, line, save) {
+		/* skip first line */
+		if ((char *) msg->l3h == line) {
+			found = mgcp_analyze_header(cfg, line, &trans_id,
+						&endp);
+			if (found != 0)
+				return create_err_response(400, "RQNT",
+						trans_id);
 
-	if (!endp->allocated) {
-		LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp));
-		return create_err_response(400, "RQNT", trans_id);
+			if (!endp->allocated) {
+				LOGP(DMGCP, LOGL_ERROR,
+					"Endpoint is not used. 0x%x\n",
+					ENDPOINT_NUMBER(endp));
+				return create_err_response(400, "RQNT",
+						trans_id);
+			}
+		}
+
+		switch (line[0]) {
+		case 'S':
+			tone = extract_tone(line);
+			break;
+		}
 	}
-	return create_ok_response(200, "RQNT", trans_id);
+
+	/* we didn't see a signal request with a tone */
+	if (tone == CHAR_MAX)
+		return create_ok_response(200, "RQNT", trans_id);
+
+	if (cfg->rqnt_cb)
+		res = cfg->rqnt_cb(endp, tone, (const char *) msg->l3h);
+
+	return res == 0 ?
+		create_ok_response(200, "RQNT", trans_id) :
+		create_err_response(res, "RQNT", trans_id);
 }
 
 struct mgcp_config *mgcp_config_alloc(void)
diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c
index 9b65666..74f5efe 100644
--- a/openbsc/tests/mgcp/mgcp_test.c
+++ b/openbsc/tests/mgcp/mgcp_test.c
@@ -74,6 +74,15 @@
 #define DLCX_RET "250 7 OK\r\n"			\
 		 "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n"
 
+#define RQNT	 "RQNT 186908780 1@mgw MGCP 1.0\r\n"	\
+		 "X: B244F267488\r\n"			\
+		 "S: D/9\r\n"
+
+#define RQNT2	 "RQNT 186908780 1@mgw MGCP 1.0\r\n"	\
+		 "X: ADD4F26746F\r\n"			\
+		 "R: D/[0-9#*](N), G/ft, fxr/t38\r\n"
+
+#define RQNT_RET "200 186908780 OK\r\n"
 
 struct mgcp_test {
 	const char *name;
@@ -91,6 +100,8 @@
 	{ "SHORT2", SHORT2, SHORT2_RET },
 	{ "SHORT3", SHORT3, SHORT2_RET },
 	{ "SHORT4", SHORT4, SHORT2_RET },
+	{ "RQNT1", RQNT, RQNT_RET },
+	{ "RQNT2", RQNT2, RQNT_RET },
 	{ "DLCX", DLCX, DLCX_RET },
 };
 
@@ -137,6 +148,52 @@
 	talloc_free(cfg);
 }
 
+static int rqnt_cb(struct mgcp_endpoint *endp, char _tone, const char *data)
+{
+	ptrdiff_t tone = _tone;
+	endp->cfg->data = (void *) tone;
+	return 0;
+}
+
+static void test_rqnt_cb(void)
+{
+	struct mgcp_config *cfg;
+	struct msgb *inp, *msg;
+
+	cfg = mgcp_config_alloc();
+	cfg->rqnt_cb = rqnt_cb;
+
+	cfg->trunk.number_endpoints = 64;
+	mgcp_endpoints_allocate(&cfg->trunk);
+
+	mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1));
+
+	inp = create_msg(CRCX);
+	msgb_free(mgcp_handle_message(cfg, inp));
+	msgb_free(inp);
+
+	/* send the RQNT and check for the CB */
+	inp = create_msg(RQNT);
+	msg = mgcp_handle_message(cfg, inp);
+	if (strncmp((const char *) msg->l2h, "200", 3) != 0) {
+		printf("FAILED: message is not 200. '%s'\n", msg->l2h);
+		abort();
+	}
+
+	if (cfg->data != (void *) '9') {
+		printf("FAILED: callback not called: %p\n", cfg->data);
+		abort();
+	}
+
+	msgb_free(msg);
+	msgb_free(inp);
+
+	inp = create_msg(DLCX);
+	msgb_free(mgcp_handle_message(cfg, inp));
+	msgb_free(inp);
+	talloc_free(cfg);
+}
+
 struct pl_test {
 	int		cycles;
 	uint16_t	base_seq;
@@ -195,6 +252,7 @@
 
 	test_messages();
 	test_packet_loss_calc();
+	test_rqnt_cb();
 
 	printf("Done\n");
 	return EXIT_SUCCESS;
diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok
index e61c0bc..1143a04 100644
--- a/openbsc/tests/mgcp/mgcp_test.ok
+++ b/openbsc/tests/mgcp/mgcp_test.ok
@@ -7,6 +7,8 @@
 Testing SHORT2
 Testing SHORT3
 Testing SHORT4
+Testing RQNT1
+Testing RQNT2
 Testing DLCX
 Testing packet loss calculation.
 Done