Repository: zulip/zulip

Issue:
Support `tel:` and `sms:` schemes for Markdown links
When using the Markdown format, I can embed links in my messages using Markdown syntax, like so:

```
If you want to email me, [click here](mailto:username@example.com).

If you want to see our site, [click here](https://example.com).
```

These links are then invoked in the relevant operating-system-specified handler for their relevant URL schemes. This is great, and I like it a lot!

However, today I tried to send a message that contained my phone number, and wanted someone clicking it to invoke their operating-system-specified handler for the `tel` and `sms` schemes, which should prompt the user to start a call or compose a text message respectively. I wrote the following Markdown:

```
Hey, I just met you, and this is crazy,
but here's my number, so [call me](tel:+14155551234), [maybe](sms:+14155551234).
```

Immediately after sending the message, the relevant pieces of text showed as links (I assume with the appropriate schemes), but shortly after, they fell back to showing my unformatted Markdown.

I verified that this appears in both the current Zulip Desktop and the in-browser version. I did not check the mobile application.


Discussion:
Hello @zulip/server-markdown members, this issue was labeled with the "area: markdown" label, so you may want to check it out!

<!-- areaLabelAddition -->
Thanks for the feedback!  This seems like a reasonable feature to have.

Resolving patch:
diff --git a/zerver/lib/markdown/__init__.py b/zerver/lib/markdown/__init__.py
--- a/zerver/lib/markdown/__init__.py
+++ b/zerver/lib/markdown/__init__.py
@@ -1570,7 +1570,7 @@ def sanitize_url(url: str) -> Optional[str]:
     if not scheme:
         return sanitize_url("http://" + url)
 
-    locless_schemes = ["mailto", "news", "file", "bitcoin"]
+    locless_schemes = ["mailto", "news", "file", "bitcoin", "sms", "tel"]
     if netloc == "" and scheme not in locless_schemes:
         # This fails regardless of anything else.
         # Return immediately to save additional processing
@@ -1580,7 +1580,7 @@ def sanitize_url(url: str) -> Optional[str]:
     # appears to have a netloc.  Additionally there are plenty of other
     # schemes that do weird things like launch external programs.  To be
     # on the safe side, we whitelist the scheme.
-    if scheme not in ("http", "https", "ftp", "mailto", "file", "bitcoin"):
+    if scheme not in ("http", "https", "ftp", "mailto", "file", "bitcoin", "sms", "tel"):
         return None
 
     # Upstream code scans path, parameters, and query for colon characters


Test patch:
diff --git a/zerver/tests/fixtures/markdown_test_cases.json b/zerver/tests/fixtures/markdown_test_cases.json
--- a/zerver/tests/fixtures/markdown_test_cases.json
+++ b/zerver/tests/fixtures/markdown_test_cases.json
@@ -948,6 +948,11 @@
       "input": "<h1>*<h1>[<h2>Static types in Python</h2>](https://blog.zulip.com/2016/10/13/static-types-in-python-oh-mypy)</h1>*</h1>",
       "expected_output": "<p>&lt;h1&gt;<em>&lt;h1&gt;<a href=\"https://blog.zulip.com/2016/10/13/static-types-in-python-oh-mypy\">&lt;h2&gt;Static types in Python&lt;/h2&gt;</a>&lt;/h1&gt;</em>&lt;/h1&gt;</p>",
       "marked_expected_output": "<p>&lt;h1&gt;<em>&lt;h1&gt;<a href=\"https://blog.zulip.com/2016/10/13/static-types-in-python-oh-mypy\">&lt;h2&gt;Static types in Python&lt;/h2&gt;</a>&lt;/h1&gt;</em>&lt;/h1&gt;\n\n</p>"
+    },
+    {
+        "name": "telephone_sms_link",
+        "input": "[call me](tel:+14155551234) [or maybe not](sms:+14155551234)",
+        "expected_output": "<p><a href=\"tel:+14155551234\">call me</a> <a href=\"sms:+14155551234\">or maybe not</a></p>"
     }
   ],
   "linkify_tests": [