Repository: pymedusa/Medusa

Issue:
[py-3.7.0] Config encryption exception
**Describe the bug**
Medusa won't run on Python 3.7 with config password encryption enabled.

**To Reproduce**
1. Enable password encryption
2. Run using Python 3.7 (probably affects 3.6 & 3.5 as well)

**Medusa (please complete the following information):**
- **Python**: 3.7.0
 - **OS:** Windows 7 x64 SP1 (7601)
 - **Branch:** develop
 - **Commit:** 3c4fc5155e989c9268f20120cd3dee7e36d1802d

**Logs:**
<details>
<summary>:book: </summary>

```
Traceback (most recent call last):
  File "C:\Python37\lib\base64.py", line 517, in _input_type_check
    m = memoryview(s)
TypeError: memoryview: a bytes-like object is required, not 'str'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Medusa\medusa\config.py", line 729, in check_setting_str
    my_val = helpers.decrypt(config[cfg_name][item_name], encryption_version)
  File "C:\Medusa\medusa\helpers\__init__.py", line 977, in decrypt
    return encrypt(data, encryption_version, _decrypt=True)
  File "C:\Medusa\medusa\helpers\__init__.py", line 966, in encrypt
    return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(base64.decodestring(data),
  File "C:\Python37\lib\base64.py", line 561, in decodestring
    return decodebytes(s)
  File "C:\Python37\lib\base64.py", line 552, in decodebytes
    _input_type_check(s)
  File "C:\Python37\lib\base64.py", line 520, in _input_type_check
    raise TypeError(msg) from err
TypeError: expected bytes-like object, not str
```



```
During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Python37\lib\base64.py", line 517, in _input_type_check
    m = memoryview(s)
TypeError: memoryview: a bytes-like object is required, not 'str'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Medusa\medusa\config.py", line 735, in check_setting_str
    config[cfg_name][item_name] = helpers.encrypt(my_val, encryption_version)
  File "C:\Medusa\medusa\helpers\__init__.py", line 970, in encrypt
    ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(data, cycle(app.ENCRYPTION_SECRET)))).strip()
  File "C:\Python37\lib\base64.py", line 547, in encodestring
    return encodebytes(s)
  File "C:\Python37\lib\base64.py", line 534, in encodebytes
    _input_type_check(s)
  File "C:\Python37\lib\base64.py", line 520, in _input_type_check
    raise TypeError(msg) from err
TypeError: expected bytes-like object, not str
```


```
During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Python37\lib\base64.py", line 517, in _input_type_check
    m = memoryview(s)
TypeError: memoryview: a bytes-like object is required, not 'str'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Medusa\start.py", line 14, in <module>
    main()
  File "C:\Medusa\medusa\__main__.py", line 2184, in main
    application.start(sys.argv[1:])
  File "C:\Medusa\medusa\__main__.py", line 317, in start
    self.initialize(console_logging=self.console_logging)
  File "C:\Medusa\medusa\__main__.py", line 425, in initialize
    app.GIT_PASSWORD = check_setting_str(app.CFG, 'General', 'git_password', '', censor_log='low')
  File "C:\Medusa\medusa\config.py", line 738, in check_setting_str
    config[cfg_name][item_name] = helpers.encrypt(my_val, encryption_version)
  File "C:\Medusa\medusa\helpers\__init__.py", line 970, in encrypt
    ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(data, cycle(app.ENCRYPTION_SECRET)))).strip()
  File "C:\Python37\lib\base64.py", line 547, in encodestring
    return encodebytes(s)
  File "C:\Python37\lib\base64.py", line 534, in encodebytes
    _input_type_check(s)
  File "C:\Python37\lib\base64.py", line 520, in _input_type_check
    raise TypeError(msg) from err
TypeError: expected bytes-like object, not str
```
</details>


Discussion:
Is this `the sharkykh` reporting? Thx for the bug report bro. 

Resolving patch:
diff --git a/dredd/dredd_hook.py b/dredd/dredd_hook.py
--- a/dredd/dredd_hook.py
+++ b/dredd/dredd_hook.py
@@ -5,6 +5,11 @@
 
 import io
 import json
+import os
+import sys
+
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../ext')))
+
 from collections import Mapping
 
 import dredd_hooks as hooks
diff --git a/medusa/__main__.py b/medusa/__main__.py
--- a/medusa/__main__.py
+++ b/medusa/__main__.py
@@ -952,8 +952,8 @@ def initialize(self, console_logging=True):
             app.GIT_REMOTE_BRANCHES = []
             app.KODI_LIBRARY_CLEAN_PENDING = False
             app.SELECTED_ROOT = check_setting_int(app.CFG, 'GUI', 'selected_root', -1)
-            app.BACKLOG_PERIOD = check_setting_str(app.CFG, 'GUI', 'backlog_period', 'all')
-            app.BACKLOG_STATUS = check_setting_str(app.CFG, 'GUI', 'backlog_status', 'all')
+            app.BACKLOG_PERIOD = check_setting_str(app.CFG, 'General', 'backlog_period', 'all')
+            app.BACKLOG_STATUS = check_setting_str(app.CFG, 'General', 'backlog_status', 'all')
             app.LAYOUT_WIDE = check_setting_bool(app.CFG, 'GUI', 'layout_wide', 0)
             app.SHOW_LIST_ORDER = check_setting_list(app.CFG, 'GUI', 'show_list_order', app.SHOW_LIST_ORDER)
 
diff --git a/medusa/helpers/__init__.py b/medusa/helpers/__init__.py
--- a/medusa/helpers/__init__.py
+++ b/medusa/helpers/__init__.py
@@ -54,7 +54,7 @@
 import requests
 from requests.compat import urlparse
 
-from six import binary_type, string_types, text_type, viewitems
+from six import binary_type, ensure_binary, ensure_text, string_types, text_type, viewitems
 from six.moves import http_client
 
 log = BraceAdapter(logging.getLogger(__name__))
@@ -953,24 +953,19 @@ def check_url(url):
 
 
 def encrypt(data, encryption_version=0, _decrypt=False):
-    # Version 1: Simple XOR encryption (this is not very secure, but works)
-    if encryption_version == 1:
-        if _decrypt:
-            return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(base64.decodestring(data), cycle(unique_key1)))
-        else:
-            return base64.encodestring(
-                ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(data, cycle(unique_key1)))).strip()
-    # Version 2: Simple XOR encryption (this is not very secure, but works)
-    elif encryption_version == 2:
-        if _decrypt:
-            return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(base64.decodestring(data),
-                                                                  cycle(app.ENCRYPTION_SECRET)))
-        else:
-            return base64.encodestring(
-                ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(data, cycle(app.ENCRYPTION_SECRET)))).strip()
     # Version 0: Plain text
-    else:
+    if encryption_version == 0:
         return data
+    else:
+        # Simple XOR encryption, Base64 encoded
+        # Version 1: unique_key1; Version 2: app.ENCRYPTION_SECRET
+        key = unique_key1 if encryption_version == 1 else app.ENCRYPTION_SECRET
+        if _decrypt:
+            data = ensure_text(base64.decodestring(ensure_binary(data)))
+            return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(data, cycle(key)))
+        else:
+            data = ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(data, cycle(key)))
+            return ensure_text(base64.encodestring(ensure_binary(data))).strip()
 
 
 def decrypt(data, encryption_version=0):


Test patch: