diff --git a/CHANGES b/CHANGES
index f5f6524485b00e7c5510d662ba11c4ac40faa7fc..8c3a25347117ea68690df6905a77e5ed15ac7a0b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -86,6 +86,17 @@ MixMonitor
  * A new function, MIXMONITOR, has been added to allow access to individual
    instances of MixMonitor on a channel.
 
+
+Channel Drivers
+-------------------------
+
+chan_sip
+-------------------------
+ * TEL URI support for inbound INVITE requests has been added. chan_sip will
+   now handle TEL schemes in the Request and From URIs. The phone-context in
+   the Request URI will be stored in the TELPHONECONTEXT channel variable on
+   the inbound channel.
+
 Debugging
 -------------------------
  * Core Show Locks output now includes Thread/LWP ID if the platform
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 68d07a63cb1c549881b398e3ad5dc88045d49e58..24a9370a716005c4a972b750b61ed6316e954607 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -17691,7 +17691,7 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
 
 	uri = ast_strdupa(get_in_brackets(tmp));
 
-	if (parse_uri_legacy_check(uri, "sip:,sips:", &uri, &unused_password, &domain, NULL)) {
+	if (parse_uri_legacy_check(uri, "sip:,sips:,tel:", &uri, &unused_password, &domain, NULL)) {
 		ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", uri);
 		return SIP_GET_DEST_INVALID_URI;
 	}
@@ -17719,7 +17719,7 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
 	ast_copy_string(tmpf, sip_get_header(req, "From"), sizeof(tmpf));
 	if (!ast_strlen_zero(tmpf)) {
 		from = get_in_brackets(tmpf);
-		if (parse_uri_legacy_check(from, "sip:,sips:", &from, NULL, &domain, NULL)) {
+		if (parse_uri_legacy_check(from, "sip:,sips:,tel:", &from, NULL, &domain, NULL)) {
 			ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", from);
 			return SIP_GET_DEST_INVALID_URI;
 		}
@@ -18607,10 +18607,13 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
 
 	if (ast_strlen_zero(p->exten)) {
 		char *t = uri2;
-		if (!strncasecmp(t, "sip:", 4))
-			t+= 4;
-		else if (!strncasecmp(t, "sips:", 5))
+		if (!strncasecmp(t, "sip:", 4)) {
+			t += 4;
+		} else if (!strncasecmp(t, "sips:", 5)) {
 			t += 5;
+		} else if (!strncasecmp(t, "tel:", 4)) {	/* TEL URI INVITE */
+			t += 4;
+		}
 		ast_string_field_set(p, exten, t);
 		t = strchr(p->exten, '@');
 		if (t)
@@ -18625,7 +18628,7 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
 	/* save the URI part of the From header */
 	ast_string_field_set(p, from, of);
 
-	if (parse_uri_legacy_check(of, "sip:,sips:", &name, &unused_password, &domain, NULL)) {
+	if (parse_uri_legacy_check(of, "sip:,sips:,tel:", &name, &unused_password, &domain, NULL)) {
 		ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
 	}
 
diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c
index 7d4445784ae2c8ddbba7fd9f06bd9505f6669563..5c2f2eb1f359954d3d79e6a06c242efa07de8149 100644
--- a/channels/sip/reqresp_parser.c
+++ b/channels/sip/reqresp_parser.c
@@ -45,6 +45,7 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
 	char *endparams = NULL;
 	char *c = NULL;
 	int error = 0;
+	int teluri_scheme = 0;
 
 	/*
 	 * Initialize requested strings - some functions don't care if parse_uri fails
@@ -79,6 +80,7 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
 		for (; !ast_strlen_zero(cur); cur = strsep(&scheme2, ",")) {
 			l = strlen(cur);
 			if (!strncasecmp(uri, cur, l)) {
+				teluri_scheme = !strncasecmp(uri, "tel:", 4);	/* TEL URI */
 				uri += l;
 				break;
 			}
@@ -93,6 +95,42 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
 		/* if we don't want to split around hostport, keep everything as a
 		 * userinfo - cos thats how old parse_uri operated*/
 		userinfo = uri;
+	} else if (teluri_scheme) {
+		/*
+		 * tel: TEL URI INVITE RFC 3966 patch
+		 * See http://www.ietf.org/rfc/rfc3966.txt
+		 *
+		 * Once the full RFC 3966 parsing is implemented,
+		 * the ext= or isub= parameters would be extracted from userinfo.
+		 * When this kind of subaddressing would be implemented, the userinfo must be further parsed.
+		 * Those parameters would be used for ISDN or PSTN local extensions.
+		 *
+		 * Current restrictions:
+		 * We currently consider the ";isub=" or the ";ext=" as part of the userinfo (unparsed).
+		 */
+
+		if ((c = strstr(uri, ";phone-context="))) {
+			/*
+			 * Local number with context or domain.
+			 * ext= or isub= TEL URI parameters should be upfront.
+			 * All other parameters should come after the ";phone-context=" parameter.
+			 * If other parameters would occur before ";phone-context=" they will be ignored.
+			 */
+
+                        *c = '\0';
+                        userinfo = uri;
+                        uri = c + 15;
+			*hostport = uri;
+                } else if ('+' == uri[0]) {
+			/* Global number without context or domain; possibly followed by RFC 3966 and optional other parameters. */
+
+                        userinfo = uri;
+			*hostport = uri;
+		} else {
+			ast_debug(1, "No RFC 3966 global number or context found in '%s'; returning local number anyway\n", uri);
+                        userinfo = uri;		/* Return local number anyway */
+			error = -1;
+		}
 	} else {
 		char *dom = "";
 		if ((c = strchr(uri, '@'))) {
@@ -375,6 +413,51 @@ AST_TEST_DEFINE(sip_parse_uri_full_test)
 		.params.user = ""
 	};
 
+	/* RFC 3966 TEL URI INVITE */
+	struct testdata td11 = {
+		.desc = "tel local number",
+		.uri = "tel:0987654321;phone-context=+32987654321",
+		.user = "0987654321",
+		.pass = "",
+		.hostport = "+32987654321",
+		.headers = "",
+		.residue = "",
+		.params.transport = "",
+		.params.lr = 0,
+		.params.user = ""
+	};
+
+	struct testdata td12 = {
+		.desc = "tel global number",
+		.uri = "tel:+32987654321",
+		.user = "+32987654321",
+		.pass = "",
+		.hostport = "+32987654321",
+		.headers = "",
+		.residue = "",
+		.params.transport = "",
+		.params.lr = 0,
+		.params.user = ""
+	};
+
+	/*
+	 * Once the full RFC 3966 parsing is implemented,
+	 * only the ext= or isub= parameters would be extracted from .user
+	 * Then the ;param=discard would be ignored,
+	 * and the .user would only contain "0987654321"
+	 */
+	struct testdata td13 = {
+		.desc = "tel local number",
+		.uri = "tel:0987654321;ext=1234;param=discard;phone-context=+32987654321;transport=udp;param2=discard2?header=blah&header2=blah2;param3=residue",
+		.user = "0987654321;ext=1234;param=discard",
+		.pass = "",
+		.hostport = "+32987654321",
+		.headers = "header=blah&header2=blah2",
+		.residue = "param3=residue",
+		.params.transport = "udp",
+		.params.lr = 0,
+		.params.user = ""
+	};
 
 	AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
 	AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
@@ -386,7 +469,9 @@ AST_TEST_DEFINE(sip_parse_uri_full_test)
 	AST_LIST_INSERT_TAIL(&testdatalist, &td8, list);
 	AST_LIST_INSERT_TAIL(&testdatalist, &td9, list);
 	AST_LIST_INSERT_TAIL(&testdatalist, &td10, list);
-
+	AST_LIST_INSERT_TAIL(&testdatalist, &td11, list);
+	AST_LIST_INSERT_TAIL(&testdatalist, &td12, list);
+	AST_LIST_INSERT_TAIL(&testdatalist, &td13, list);
 
 	switch (cmd) {
 	case TEST_INIT:
@@ -407,7 +492,7 @@ AST_TEST_DEFINE(sip_parse_uri_full_test)
 		params.lr = 0;
 
 		ast_copy_string(uri,testdataptr->uri,sizeof(uri));
-		if (parse_uri_full(uri, "sip:,sips:", &user,
+		if (parse_uri_full(uri, "sip:,sips:,tel:", &user,
 				   &pass, &hostport,
 				   &params,
 				   &headers,
@@ -460,6 +545,7 @@ AST_TEST_DEFINE(sip_parse_uri_test)
 	char uri9[] = "sip:host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
 	char uri10[] = "host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
 	char uri11[] = "host";
+	char uri12[] = "tel:911";	/* TEL URI Local number without context or global number */
 
 	switch (cmd) {
 	case TEST_INIT:
@@ -588,6 +674,17 @@ AST_TEST_DEFINE(sip_parse_uri_test)
 		res = AST_TEST_FAIL;
 	}
 
+	/* Test 12, simple URI */
+	name = pass = hostport = transport = NULL;
+	if (!parse_uri(uri12, "sip:,sips:,tel:", &name, &pass, &hostport, &transport) ||
+			strcmp(name, "911")      ||	/* We return local number anyway */
+			!ast_strlen_zero(pass)      ||
+			!ast_strlen_zero(hostport)      ||	/* No global number nor context */
+			!ast_strlen_zero(transport)) {
+		ast_test_status_update(test, "Test 12: TEL URI INVITE failed.\n");
+		res = AST_TEST_FAIL;
+	}
+
 	return res;
 }