Repository: pimutils/khal

Issue:
importing same ics twice duplicates entries
I created a test entry on my Google calendar  which I sent to my email. Importing the ICS twice duplicates the entry. Each successive call will add an entry in the calendar

Importing it the first time:
```
➜ khal list today 14:00 1h
No events

~ via 🐍 v3.9.5 
➜ khal import test-meeting-20210509.ics                         
2021-05-19 14:00-2021-05-19 14:30 Test Meeting :: -::~:~::~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~::~:~::-
Please do not edit this section of the description.

This event has a video call.
Join: https://meet.google.com/zmo-jnsn-tcy
(AT) +43 1 22781820 PIN: 279845557#
View more phone numbers: https://tel.meet/zmo-jnsn-tcy?pin=7897278919590&hs=7

View your event at https://calendar.google.com/calendar/event?action=VIEW&eid=N241cjhpamZjdmpmYWZoMm45NGJ1YjhraWEgbWF4LmhvZmVyQG1haWxib3gub3Jn&tok=MjEjbWF4LmhvZmVyQGN5YmVydGVjLmF0NGVlMGZhYWI3MDgzYTZkZGE2OTIwYzk5ZDJjNWM5Y2E4NDEwMjkwYw&ctz=Europe%2FBerlin&hl=en&es=1.
-::~:~::~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~::~:~::-
Do you want to import this event into `Y2FsOi8vMC8zMQ`? [y/N]: y

~ via 🐍 v3.9.5 
➜ khal list today 14:00 1h             
Today, 2021-05-19
14:00-14:30 Test Meeting :: -::~:~::~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~::~:~::-
Please do not edit this section of the description.

This event has a video call.
Join: https://meet.google.com/zmo-jnsn-tcy
(AT) +43 1 22781820 PIN: 279845557#
View more phone numbers: https://tel.meet/zmo-jnsn-tcy?pin=7897278919590&hs=7

View your event at https://calendar.google.com/calendar/event?action=VIEW&eid=N241cjhpamZjdmpmYWZoMm45NGJ1YjhraWEgbWF4LmhvZmVyQG1haWxib3gub3Jn&tok=MjEjbWF4LmhvZmVyQGN5YmVydGVjLmF0NGVlMGZhYWI3MDgzYTZkZGE2OTIwYzk5ZDJjNWM5Y2E4NDEwMjkwYw&ctz=Europe%2FBerlin&hl=en&es=1.
-::~:~::~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~::~:~::-
```

Now the import a 2nd time:
```
➜ khal import test-meeting-20210509.ics
2021-05-19 14:00-2021-05-19 14:30 Test Meeting :: -::~:~::~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~::~:~::-
Please do not edit this section of the description.

This event has a video call.
Join: https://meet.google.com/zmo-jnsn-tcy
(AT) +43 1 22781820 PIN: 279845557#
View more phone numbers: https://tel.meet/zmo-jnsn-tcy?pin=7897278919590&hs=7

View your event at https://calendar.google.com/calendar/event?action=VIEW&eid=N241cjhpamZjdmpmYWZoMm45NGJ1YjhraWEgbWF4LmhvZmVyQG1haWxib3gub3Jn&tok=MjEjbWF4LmhvZmVyQGN5YmVydGVjLmF0NGVlMGZhYWI3MDgzYTZkZGE2OTIwYzk5ZDJjNWM5Y2E4NDEwMjkwYw&ctz=Europe%2FBerlin&hl=en&es=1.
-::~:~::~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~::~:~::-
Do you want to import this event into `Y2FsOi8vMC8zMQ`? [y/N]: y

~ via 🐍 v3.9.5 took 4s 
➜ khal list today 14:00 1h
Today, 2021-05-19
14:00-14:30 Test Meeting :: -::~:~::~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~::~:~::-
Please do not edit this section of the description.

This event has a video call.
Join: https://meet.google.com/zmo-jnsn-tcy
(AT) +43 1 22781820 PIN: 279845557#
View more phone numbers: https://tel.meet/zmo-jnsn-tcy?pin=7897278919590&hs=7

View your event at https://calendar.google.com/calendar/event?action=VIEW&eid=N241cjhpamZjdmpmYWZoMm45NGJ1YjhraWEgbWF4LmhvZmVyQG1haWxib3gub3Jn&tok=MjEjbWF4LmhvZmVyQGN5YmVydGVjLmF0NGVlMGZhYWI3MDgzYTZkZGE2OTIwYzk5ZDJjNWM5Y2E4NDEwMjkwYw&ctz=Europe%2FBerlin&hl=en&es=1.
-::~:~::~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~::~:~::-
14:00-14:30 Test Meeting :: -::~:~::~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~::~:~::-
Please do not edit this section of the description.

This event has a video call.
Join: https://meet.google.com/zmo-jnsn-tcy
(AT) +43 1 22781820 PIN: 279845557#
View more phone numbers: https://tel.meet/zmo-jnsn-tcy?pin=7897278919590&hs=7

View your event at https://calendar.google.com/calendar/event?action=VIEW&eid=N241cjhpamZjdmpmYWZoMm45NGJ1YjhraWEgbWF4LmhvZmVyQG1haWxib3gub3Jn&tok=MjEjbWF4LmhvZmVyQGN5YmVydGVjLmF0NGVlMGZhYWI3MDgzYTZkZGE2OTIwYzk5ZDJjNWM5Y2E4NDEwMjkwYw&ctz=Europe%2FBerlin&hl=en&es=1.
-::~:~::~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~:~::~:~::-
```
There is no question if I want to override the existing one.

Environment:
* Python 3.9.5
* khal, version 0.10.3 - installed in an own clean virtualenv


Discussion:

Patch:
diff --git a/khal/khalendar/vdir.py b/khal/khalendar/vdir.py
index 1a6ce26..af89a30 100644
--- a/khal/khalendar/vdir.py
+++ b/khal/khalendar/vdir.py
@@ -6,6 +6,7 @@ vdirsyncer.
 import errno
 import os
 import uuid
+from hashlib import sha1
 
 from typing import Optional  # noqa
 
@@ -43,7 +44,7 @@ def to_bytes(x, encoding='ascii'):
 
 SAFE_UID_CHARS = ('abcdefghijklmnopqrstuvwxyz'
                   'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-                  '0123456789_.-+')
+                  '0123456789_.-+@')
 
 
 def _href_safe(uid, safe=SAFE_UID_CHARS):
@@ -51,8 +52,10 @@ def _href_safe(uid, safe=SAFE_UID_CHARS):
 
 
 def _generate_href(uid=None, safe=SAFE_UID_CHARS):
-    if not uid or not _href_safe(uid, safe):
+    if not uid:
         return to_unicode(uuid.uuid4().hex)
+    elif not _href_safe(uid, safe):
+        return to_unicode(sha1(uid.encode()).hexdigest())
     else:
         return uid
 


Test patch:
diff --git a/tests/vdir_test.py b/tests/vdir_test.py
index 1006bbf..9339f1c 100644
--- a/tests/vdir_test.py
+++ b/tests/vdir_test.py
@@ -57,7 +57,6 @@ def test_etag_sync(tmpdir):
     file_.write('foo')
     file_.close()
     os.sync()
-
     old_etag = vdir.get_etag_from_file(fpath)
 
     file_ = open(fpath, 'w')
@@ -86,3 +85,19 @@ def test_etag_sleep(tmpdir, sleep_time):
     new_etag = vdir.get_etag_from_file(fpath)
 
     assert old_etag != new_etag
+
+
+def test_get_href_from_uid():
+    # Test UID with unsafe characters
+    uid = "V042MJ8B3SJNFXQOJL6P53OFMHJE8Z3VZWÈÉ@pimutils.org"
+    first_href = vdir._generate_href(uid)
+    second_href = vdir._generate_href(uid)
+    assert first_href == second_href
+
+    # test UID with safe characters
+    uid = "V042MJ8B3SJNFXQOJL6P53OFMHJE8Z3VZWOU@pimutils.org"
+    href = vdir._generate_href(uid)
+    assert href == uid
+
+    href = vdir._generate_href()
+    assert href is not None