Repository: caronc/apprise

Issue:
'Unparseable E-Mail URL' error for hostnames containing underscore
With underscore:
```bash
$ docker run --rm caronc/apprise:latest /usr/local/bin/apprise --body='hello world' \
  'mailto://smtp_service:25/?from=foo@localhost&to=bar@localhost'
2020-11-22 16:33:16,297 - ERROR - Unparseable E-Mail URL mailto://smtp_service:25/?from=foo@localhost&to=bar@localhost
2020-11-22 16:33:16,297 - ERROR - You must specify at least one server URL or populated configuration file.
```

Without underscore:
```bash
$ docker run --rm caronc/apprise:latest /usr/local/bin/apprise --body='hello world' \
  'mailto://smtpservice:25/?from=foo@localhost&to=bar@localhost'
2020-11-22 16:34:12,541 - INFO - Notifying 1 service(s) asynchronously.
```

Underscores in hostnames are completely legal, see https://stackoverflow.com/a/2183140/5116073

Comments:

Patch:
diff --git a/.gitignore b/.gitignore
index 11f190a..0315c5b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,3 +67,8 @@ target/
 
 #PyCharm
 .idea
+
+#PyDev (Eclipse)
+.project
+.pydevproject
+.settings
diff --git a/apprise/utils.py b/apprise/utils.py
index 21f2c49..4b4833b 100644
--- a/apprise/utils.py
+++ b/apprise/utils.py
@@ -120,9 +120,9 @@ GET_EMAIL_RE = re.compile(
     r'(?P<email>(?P<userid>[a-z0-9$%=_~-]+'
     r'(?:\.[a-z0-9$%+=_~-]+)'
     r'*)@(?P<domain>('
-    r'(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+'
-    r'[a-z0-9](?:[a-z0-9-]*[a-z0-9]))|'
-    r'[a-z0-9][a-z0-9-]{5,})))'
+    r'(?:[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?\.)+'
+    r'[a-z0-9](?:[a-z0-9_-]*[a-z0-9]))|'
+    r'[a-z0-9][a-z0-9_-]{5,})))'
     r'\s*>?', re.IGNORECASE)
 
 # Regular expression used to extract a phone number
@@ -232,9 +232,12 @@ def is_hostname(hostname, ipv4=True, ipv6=True):
     # - Hostnames can ony be comprised of alpha-numeric characters and the
     #   hyphen (-) character.
     # - Hostnames can not start with the hyphen (-) character.
+    # - as a workaround for https://github.com/docker/compose/issues/229 to
+    #   being able to address services in other stacks, we also allow
+    #   underscores in hostnames
     # - labels can not exceed 63 characters
     allowed = re.compile(
-        r'(?!-)[a-z0-9][a-z0-9-]{1,62}(?<!-)$',
+        r'^[a-z0-9][a-z0-9_-]{1,62}(?<!-)$',
         re.IGNORECASE,
     )
 


Test patch:
diff --git a/test/test_utils.py b/test/test_utils.py
index b187da0..37c2ba7 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -532,6 +532,8 @@ def test_is_hostname():
     assert utils.is_hostname('yahoo.ca.') == 'yahoo.ca'
     assert utils.is_hostname('valid-dashes-in-host.ca') == \
         'valid-dashes-in-host.ca'
+    assert utils.is_hostname('valid-underscores_in_host.ca') == \
+        'valid-underscores_in_host.ca'
 
     # Invalid Hostnames
     assert utils.is_hostname('-hostname.that.starts.with.a.dash') is False
@@ -539,7 +541,6 @@ def test_is_hostname():
     assert utils.is_hostname('    spaces   ') is False
     assert utils.is_hostname('       ') is False
     assert utils.is_hostname('') is False
-    assert utils.is_hostname('valid-underscores_in_host.ca') is False
 
     # Valid IPv4 Addresses
     assert utils.is_hostname('127.0.0.1') == '127.0.0.1'
@@ -625,6 +626,14 @@ def test_is_email():
     assert 'test' == results['user']
     assert '' == results['label']
 
+    results = utils.is_email('test@my-valid_host.com')
+    assert '' == results['name']
+    assert 'test@my-valid_host.com' == results['email']
+    assert 'test@my-valid_host.com' == results['full_email']
+    assert 'my-valid_host.com' == results['domain']
+    assert 'test' == results['user']
+    assert '' == results['label']
+
     results = utils.is_email('tag+test@gmail.com')
     assert '' == results['name']
     assert 'test@gmail.com' == results['email']