We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add Unit and Integration Tests
If you want to contribute this would be a good place to start. Lots of people are suggesting pretty substantial changes to the codebase and tests would make sure those changes don't unintentionally break things.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `modules/deaddrop/files/deaddrop/journalist.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 import os, datetime, uuid
3 import web
4 import config, crypto, store, version
5 
6 urls = (
7   '/', 'index',
8   '/reply/', 'reply',
9   '/([A-Z1-7]+)/', 'col',
10   '/([A-Z1-7]+)/([0-9]+\.[0-9]+(?:_msg|_doc|)\.gpg)', 'doc' 
11 )
12 
13 render = web.template.render(config.JOURNALIST_TEMPLATES_DIR, base='base', 
14     globals={'version':version.__version__})
15 
16 class index:
17     def GET(self):
18         dirs = os.listdir(config.STORE_DIR)
19         cols = []
20         for d in dirs:
21             if not os.listdir(store.path(d)): continue
22             cols.append(web.storage(name=d, codename=crypto.displayid(d), date=
23               str(datetime.datetime.fromtimestamp(
24                 os.stat(store.path(d)).st_mtime
25               )).split('.')[0]
26             ))
27         cols.sort(lambda x,y: cmp(x.date, y.date), reverse=True)
28 
29         web.header('Cache-Control', 'no-cache, no-store, must-revalidate')
30         web.header('Pragma', 'no-cache')
31         web.header('Expires', '-1')
32         return render.index(cols)
33 
34 class col:
35     def GET(self, sid):
36         fns = os.listdir(store.path(sid))
37         docs = []
38         for f in fns:
39             docs.append(web.storage(
40               name=f, 
41               date=str(datetime.datetime.fromtimestamp(os.stat(store.path(sid, f)).st_mtime))
42             ))
43         docs.sort(lambda x,y: cmp(x.date, y.date))
44         
45         haskey = bool(crypto.getkey(sid))
46 
47         web.header('Cache-Control', 'no-cache, no-store, must-revalidate')
48         web.header('Pragma', 'no-cache')
49         web.header('Expires', '-1')
50         return render.col(docs, sid, haskey, codename=crypto.displayid(sid))
51  
52 class doc:
53     def GET(self, sid, fn):
54         web.header('Content-Disposition', 'attachment; filename="' + 
55           crypto.displayid(sid).replace(' ', '_') + '_' + fn + '"')
56 
57         web.header('Cache-Control', 'no-cache, no-store, must-revalidate')
58         web.header('Pragma', 'no-cache')
59         web.header('Expires', '-1')
60         return file(store.path(sid, fn)).read()
61 
62 class reply:
63     def GET(self):
64         raise web.seeother('/')
65     
66     def POST(self):
67         i = web.input('sid', 'msg')
68         crypto.encrypt(crypto.getkey(i.sid), i.msg, output=
69           store.path(i.sid, 'reply-%.2f.gpg' % (uuid.uuid4().int, ))
70         )
71 
72         web.header('Cache-Control', 'no-cache, no-store, must-revalidate')
73         web.header('Pragma', 'no-cache')
74         web.header('Expires', '-1')
75         return render.reply(i.sid)
76         
77 
78 web.config.debug = False
79 app = web.application(urls, locals())
80 application = app.wsgifunc()
81 
82 if __name__ == "__main__":
83     app.run()
84
```

Path: `modules/deaddrop/files/deaddrop/crypto.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 import os
3 import bcrypt, subprocess, threading
4 from Crypto.Random import random
5 import random as badrandom
6 import gnupg
7 import config
8 import store
9 from base64 import b32encode
10 
11 # to fix gpg error #78 on production
12 os.environ['USERNAME'] = 'www-data'
13 
14 GPG_KEY_TYPE = "RSA"
15 if 'DEADDROPENV' in os.environ and os.environ['DEADDROPENV'] == 'test':
16     # Use small keys to speed up tests (and try to cheat and avoid issues with
17     # async key generation)
18     GPG_KEY_LENGTH = "1024"
19 else:
20     GPG_KEY_LENGTH = "4096"
21 
22 DEFAULT_WORDS_IN_RANDOM_ID = 8
23 
24 class CryptoException(Exception): pass
25 
26 def clean(s, also=''):
27     """
28     >>> clean("Hello, world!")
29     Traceback (most recent call last):
30       ...
31     CryptoException: invalid input
32     >>> clean("Helloworld")
33     'Helloworld'
34     """
35     ok = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
36     for c in s:
37         if c not in ok and c not in also: raise CryptoException("invalid input")
38     return s
39 
40 words = file(config.WORD_LIST).read().split('\n')
41 def genrandomid(words_in_random_id = DEFAULT_WORDS_IN_RANDOM_ID):
42     return ' '.join(random.choice(words) for x in range(words_in_random_id))
43 
44 def displayid(n, words_in_random_id = DEFAULT_WORDS_IN_RANDOM_ID):
45     badrandom_value = badrandom.WichmannHill()
46     badrandom_value.seed(n)
47     return ' '.join(badrandom_value.choice(words) for x in range(words_in_random_id))
48 
49 def shash(s):
50     """
51     >>> shash('Hello, world!')
52     'EQZGCJBRGISGOTC2NZVWG6LILJBHEV3CINNEWSCLLFTUWZLFHBTS6WLCHFHTOLRSGQXUQLRQHFMXKOKKOQ4WQ6SXGZXDAS3Z'
53     """
54     return b32encode(bcrypt.hashpw(s, config.BCRYPT_SALT))
55 
56 GPG_BINARY = 'gpg2'
57 try:
58     p = subprocess.Popen([GPG_BINARY, '--version'], stdout=subprocess.PIPE)
59 except OSError:
60     GPG_BINARY = 'gpg'
61     p = subprocess.Popen([GPG_BINARY, '--version'], stdout=subprocess.PIPE)
62 
63 assert p.stdout.readline().split()[-1].split('.')[0] == '2', "upgrade GPG to 2.0"
64 gpg = gnupg.GPG(gpgbinary=GPG_BINARY, gnupghome=config.GPG_KEY_DIR)
65 
66 def genkeypair(name, secret):
67     """
68     >>> if not gpg.list_keys(shash('randomid')):
69     ...     genkeypair(shash('randomid'), 'randomid').type
70     ... else:
71     ...     u'P'
72     u'P'
73     """
74     name, secret = clean(name), clean(secret, ' ')
75     return gpg.gen_key(gpg.gen_key_input(
76       key_type=GPG_KEY_TYPE, key_length=GPG_KEY_LENGTH,
77       passphrase=secret,
78       name_email="%s@deaddrop.example.com" % name
79     ))
80 
81 def getkey(name):
82     for key in gpg.list_keys():
83         for uid in key['uids']:
84             if ' <%s@' % name in uid: return key['fingerprint']
85     return None
86 
87 def _shquote(s):
88     return "\\'".join("'" + p + "'" for p in s.split("'"))
89 _gpghacklock = threading.Lock()
90 
91 def encrypt(fp, s, output=None, fn=None):
92     r"""
93     >>> key = genkeypair('randomid', 'randomid')
94     >>> encrypt('randomid', "Goodbye, cruel world!")[:45]
95     '-----BEGIN PGP MESSAGE-----\nVersion: GnuPG v2'
96     """
97     if output:
98         store.verify(output)
99     fp = fp.replace(' ', '')
100     if isinstance(s, unicode):
101         s = s.encode('utf8')
102     if isinstance(s, str):
103         out = gpg.encrypt(s, [fp], output=output, always_trust=True)
104     else:
105         if fn:
106             with _gpghacklock:
107                 oldname = gpg.gpgbinary
108                 gpg.gpgbinary += ' --set-filename ' + _shquote(fn)
109                 out = gpg.encrypt_file(s, [fp], output=output, always_trust=True)
110                 gpg.gpgbinary = oldname
111         else:
112             out = gpg.encrypt_file(s, [fp], output=output, always_trust=True)
113     if out.ok:
114         return out.data
115     else:
116         raise CryptoException(out.stderr)
117 
118 def decrypt(name, secret, s):
119     """
120     >>> key = genkeypair('randomid', 'randomid')
121     >>> decrypt('randomid', 'randomid',
122     ...   encrypt('randomid', 'Goodbye, cruel world!')
123     ... )
124     'Goodbye, cruel world!'
125     """
126     return gpg.decrypt(s, passphrase=secret).data
127 
128 def secureunlink(fn):
129     store.verify(fn)
130     return subprocess.check_call(['srm', fn])
131 
132 # crash if we don't have srm:
133 try:
134     subprocess.check_call(['srm'], stdout=subprocess.PIPE)
135 except subprocess.CalledProcessError:
136     pass
137 
138 if __name__ == "__main__":
139     import doctest
140     doctest.testmod()
141
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/modules/deaddrop/files/deaddrop/crypto.py b/modules/deaddrop/files/deaddrop/crypto.py
--- a/modules/deaddrop/files/deaddrop/crypto.py
+++ b/modules/deaddrop/files/deaddrop/crypto.py
@@ -34,7 +34,8 @@
     """
     ok = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
     for c in s:
-        if c not in ok and c not in also: raise CryptoException("invalid input")
+        if c not in ok and c not in also:
+            raise CryptoException("invalid input: %s" % s)
     return s
 
 words = file(config.WORD_LIST).read().split('\n')
diff --git a/modules/deaddrop/files/deaddrop/journalist.py b/modules/deaddrop/files/deaddrop/journalist.py
--- a/modules/deaddrop/files/deaddrop/journalist.py
+++ b/modules/deaddrop/files/deaddrop/journalist.py
@@ -51,6 +51,7 @@
  
 class doc:
     def GET(self, sid, fn):
+        web.header('Content-Type', 'application/octet-stream')
         web.header('Content-Disposition', 'attachment; filename="' + 
           crypto.displayid(sid).replace(' ', '_') + '_' + fn + '"')

{"golden_diff": "diff --git a/modules/deaddrop/files/deaddrop/crypto.py b/modules/deaddrop/files/deaddrop/crypto.py\n--- a/modules/deaddrop/files/deaddrop/crypto.py\n+++ b/modules/deaddrop/files/deaddrop/crypto.py\n@@ -34,7 +34,8 @@\n     \"\"\"\n     ok = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n     for c in s:\n-        if c not in ok and c not in also: raise CryptoException(\"invalid input\")\n+        if c not in ok and c not in also:\n+            raise CryptoException(\"invalid input: %s\" % s)\n     return s\n \n words = file(config.WORD_LIST).read().split('\\n')\ndiff --git a/modules/deaddrop/files/deaddrop/journalist.py b/modules/deaddrop/files/deaddrop/journalist.py\n--- a/modules/deaddrop/files/deaddrop/journalist.py\n+++ b/modules/deaddrop/files/deaddrop/journalist.py\n@@ -51,6 +51,7 @@\n  \n class doc:\n     def GET(self, sid, fn):\n+        web.header('Content-Type', 'application/octet-stream')\n         web.header('Content-Disposition', 'attachment; filename=\"' + \n           crypto.displayid(sid).replace(' ', '_') + '_' + fn + '\"')\n", "issue": "Add Unit and Integration Tests\nIf you want to contribute this would be a good place to start. Lots of people are suggesting pretty substantial changes to the codebase and tests would make sure those changes don't unintentionally break things.\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nimport os, datetime, uuid\nimport web\nimport config, crypto, store, version\n\nurls = (\n  '/', 'index',\n  '/reply/', 'reply',\n  '/([A-Z1-7]+)/', 'col',\n  '/([A-Z1-7]+)/([0-9]+\\.[0-9]+(?:_msg|_doc|)\\.gpg)', 'doc' \n)\n\nrender = web.template.render(config.JOURNALIST_TEMPLATES_DIR, base='base', \n    globals={'version':version.__version__})\n\nclass index:\n    def GET(self):\n        dirs = os.listdir(config.STORE_DIR)\n        cols = []\n        for d in dirs:\n            if not os.listdir(store.path(d)): continue\n            cols.append(web.storage(name=d, codename=crypto.displayid(d), date=\n              str(datetime.datetime.fromtimestamp(\n                os.stat(store.path(d)).st_mtime\n              )).split('.')[0]\n            ))\n        cols.sort(lambda x,y: cmp(x.date, y.date), reverse=True)\n\n        web.header('Cache-Control', 'no-cache, no-store, must-revalidate')\n        web.header('Pragma', 'no-cache')\n        web.header('Expires', '-1')\n        return render.index(cols)\n\nclass col:\n    def GET(self, sid):\n        fns = os.listdir(store.path(sid))\n        docs = []\n        for f in fns:\n            docs.append(web.storage(\n              name=f, \n              date=str(datetime.datetime.fromtimestamp(os.stat(store.path(sid, f)).st_mtime))\n            ))\n        docs.sort(lambda x,y: cmp(x.date, y.date))\n        \n        haskey = bool(crypto.getkey(sid))\n\n        web.header('Cache-Control', 'no-cache, no-store, must-revalidate')\n        web.header('Pragma', 'no-cache')\n        web.header('Expires', '-1')\n        return render.col(docs, sid, haskey, codename=crypto.displayid(sid))\n \nclass doc:\n    def GET(self, sid, fn):\n        web.header('Content-Disposition', 'attachment; filename=\"' + \n          crypto.displayid(sid).replace(' ', '_') + '_' + fn + '\"')\n\n        web.header('Cache-Control', 'no-cache, no-store, must-revalidate')\n        web.header('Pragma', 'no-cache')\n        web.header('Expires', '-1')\n        return file(store.path(sid, fn)).read()\n\nclass reply:\n    def GET(self):\n        raise web.seeother('/')\n    \n    def POST(self):\n        i = web.input('sid', 'msg')\n        crypto.encrypt(crypto.getkey(i.sid), i.msg, output=\n          store.path(i.sid, 'reply-%.2f.gpg' % (uuid.uuid4().int, ))\n        )\n\n        web.header('Cache-Control', 'no-cache, no-store, must-revalidate')\n        web.header('Pragma', 'no-cache')\n        web.header('Expires', '-1')\n        return render.reply(i.sid)\n        \n\nweb.config.debug = False\napp = web.application(urls, locals())\napplication = app.wsgifunc()\n\nif __name__ == \"__main__\":\n    app.run()\n", "path": "modules/deaddrop/files/deaddrop/journalist.py"}, {"content": "# -*- coding: utf-8 -*-\nimport os\nimport bcrypt, subprocess, threading\nfrom Crypto.Random import random\nimport random as badrandom\nimport gnupg\nimport config\nimport store\nfrom base64 import b32encode\n\n# to fix gpg error #78 on production\nos.environ['USERNAME'] = 'www-data'\n\nGPG_KEY_TYPE = \"RSA\"\nif 'DEADDROPENV' in os.environ and os.environ['DEADDROPENV'] == 'test':\n    # Use small keys to speed up tests (and try to cheat and avoid issues with\n    # async key generation)\n    GPG_KEY_LENGTH = \"1024\"\nelse:\n    GPG_KEY_LENGTH = \"4096\"\n\nDEFAULT_WORDS_IN_RANDOM_ID = 8\n\nclass CryptoException(Exception): pass\n\ndef clean(s, also=''):\n    \"\"\"\n    >>> clean(\"Hello, world!\")\n    Traceback (most recent call last):\n      ...\n    CryptoException: invalid input\n    >>> clean(\"Helloworld\")\n    'Helloworld'\n    \"\"\"\n    ok = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n    for c in s:\n        if c not in ok and c not in also: raise CryptoException(\"invalid input\")\n    return s\n\nwords = file(config.WORD_LIST).read().split('\\n')\ndef genrandomid(words_in_random_id = DEFAULT_WORDS_IN_RANDOM_ID):\n    return ' '.join(random.choice(words) for x in range(words_in_random_id))\n\ndef displayid(n, words_in_random_id = DEFAULT_WORDS_IN_RANDOM_ID):\n    badrandom_value = badrandom.WichmannHill()\n    badrandom_value.seed(n)\n    return ' '.join(badrandom_value.choice(words) for x in range(words_in_random_id))\n\ndef shash(s):\n    \"\"\"\n    >>> shash('Hello, world!')\n    'EQZGCJBRGISGOTC2NZVWG6LILJBHEV3CINNEWSCLLFTUWZLFHBTS6WLCHFHTOLRSGQXUQLRQHFMXKOKKOQ4WQ6SXGZXDAS3Z'\n    \"\"\"\n    return b32encode(bcrypt.hashpw(s, config.BCRYPT_SALT))\n\nGPG_BINARY = 'gpg2'\ntry:\n    p = subprocess.Popen([GPG_BINARY, '--version'], stdout=subprocess.PIPE)\nexcept OSError:\n    GPG_BINARY = 'gpg'\n    p = subprocess.Popen([GPG_BINARY, '--version'], stdout=subprocess.PIPE)\n\nassert p.stdout.readline().split()[-1].split('.')[0] == '2', \"upgrade GPG to 2.0\"\ngpg = gnupg.GPG(gpgbinary=GPG_BINARY, gnupghome=config.GPG_KEY_DIR)\n\ndef genkeypair(name, secret):\n    \"\"\"\n    >>> if not gpg.list_keys(shash('randomid')):\n    ...     genkeypair(shash('randomid'), 'randomid').type\n    ... else:\n    ...     u'P'\n    u'P'\n    \"\"\"\n    name, secret = clean(name), clean(secret, ' ')\n    return gpg.gen_key(gpg.gen_key_input(\n      key_type=GPG_KEY_TYPE, key_length=GPG_KEY_LENGTH,\n      passphrase=secret,\n      name_email=\"%s@deaddrop.example.com\" % name\n    ))\n\ndef getkey(name):\n    for key in gpg.list_keys():\n        for uid in key['uids']:\n            if ' <%s@' % name in uid: return key['fingerprint']\n    return None\n\ndef _shquote(s):\n    return \"\\\\'\".join(\"'\" + p + \"'\" for p in s.split(\"'\"))\n_gpghacklock = threading.Lock()\n\ndef encrypt(fp, s, output=None, fn=None):\n    r\"\"\"\n    >>> key = genkeypair('randomid', 'randomid')\n    >>> encrypt('randomid', \"Goodbye, cruel world!\")[:45]\n    '-----BEGIN PGP MESSAGE-----\\nVersion: GnuPG v2'\n    \"\"\"\n    if output:\n        store.verify(output)\n    fp = fp.replace(' ', '')\n    if isinstance(s, unicode):\n        s = s.encode('utf8')\n    if isinstance(s, str):\n        out = gpg.encrypt(s, [fp], output=output, always_trust=True)\n    else:\n        if fn:\n            with _gpghacklock:\n                oldname = gpg.gpgbinary\n                gpg.gpgbinary += ' --set-filename ' + _shquote(fn)\n                out = gpg.encrypt_file(s, [fp], output=output, always_trust=True)\n                gpg.gpgbinary = oldname\n        else:\n            out = gpg.encrypt_file(s, [fp], output=output, always_trust=True)\n    if out.ok:\n        return out.data\n    else:\n        raise CryptoException(out.stderr)\n\ndef decrypt(name, secret, s):\n    \"\"\"\n    >>> key = genkeypair('randomid', 'randomid')\n    >>> decrypt('randomid', 'randomid',\n    ...   encrypt('randomid', 'Goodbye, cruel world!')\n    ... )\n    'Goodbye, cruel world!'\n    \"\"\"\n    return gpg.decrypt(s, passphrase=secret).data\n\ndef secureunlink(fn):\n    store.verify(fn)\n    return subprocess.check_call(['srm', fn])\n\n# crash if we don't have srm:\ntry:\n    subprocess.check_call(['srm'], stdout=subprocess.PIPE)\nexcept subprocess.CalledProcessError:\n    pass\n\nif __name__ == \"__main__\":\n    import doctest\n    doctest.testmod()\n", "path": "modules/deaddrop/files/deaddrop/crypto.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nimport os, datetime, uuid\nimport web\nimport config, crypto, store, version\n\nurls = (\n  '/', 'index',\n  '/reply/', 'reply',\n  '/([A-Z1-7]+)/', 'col',\n  '/([A-Z1-7]+)/([0-9]+\\.[0-9]+(?:_msg|_doc|)\\.gpg)', 'doc' \n)\n\nrender = web.template.render(config.JOURNALIST_TEMPLATES_DIR, base='base', \n    globals={'version':version.__version__})\n\nclass index:\n    def GET(self):\n        dirs = os.listdir(config.STORE_DIR)\n        cols = []\n        for d in dirs:\n            if not os.listdir(store.path(d)): continue\n            cols.append(web.storage(name=d, codename=crypto.displayid(d), date=\n              str(datetime.datetime.fromtimestamp(\n                os.stat(store.path(d)).st_mtime\n              )).split('.')[0]\n            ))\n        cols.sort(lambda x,y: cmp(x.date, y.date), reverse=True)\n\n        web.header('Cache-Control', 'no-cache, no-store, must-revalidate')\n        web.header('Pragma', 'no-cache')\n        web.header('Expires', '-1')\n        return render.index(cols)\n\nclass col:\n    def GET(self, sid):\n        fns = os.listdir(store.path(sid))\n        docs = []\n        for f in fns:\n            docs.append(web.storage(\n              name=f, \n              date=str(datetime.datetime.fromtimestamp(os.stat(store.path(sid, f)).st_mtime))\n            ))\n        docs.sort(lambda x,y: cmp(x.date, y.date))\n        \n        haskey = bool(crypto.getkey(sid))\n\n        web.header('Cache-Control', 'no-cache, no-store, must-revalidate')\n        web.header('Pragma', 'no-cache')\n        web.header('Expires', '-1')\n        return render.col(docs, sid, haskey, codename=crypto.displayid(sid))\n \nclass doc:\n    def GET(self, sid, fn):\n        web.header('Content-Type', 'application/octet-stream')\n        web.header('Content-Disposition', 'attachment; filename=\"' + \n          crypto.displayid(sid).replace(' ', '_') + '_' + fn + '\"')\n\n        web.header('Cache-Control', 'no-cache, no-store, must-revalidate')\n        web.header('Pragma', 'no-cache')\n        web.header('Expires', '-1')\n        return file(store.path(sid, fn)).read()\n\nclass reply:\n    def GET(self):\n        raise web.seeother('/')\n    \n    def POST(self):\n        i = web.input('sid', 'msg')\n        crypto.encrypt(crypto.getkey(i.sid), i.msg, output=\n          store.path(i.sid, 'reply-%.2f.gpg' % (uuid.uuid4().int, ))\n        )\n\n        web.header('Cache-Control', 'no-cache, no-store, must-revalidate')\n        web.header('Pragma', 'no-cache')\n        web.header('Expires', '-1')\n        return render.reply(i.sid)\n        \n\nweb.config.debug = False\napp = web.application(urls, locals())\napplication = app.wsgifunc()\n\nif __name__ == \"__main__\":\n    app.run()\n", "path": "modules/deaddrop/files/deaddrop/journalist.py"}, {"content": "# -*- coding: utf-8 -*-\nimport os\nimport bcrypt, subprocess, threading\nfrom Crypto.Random import random\nimport random as badrandom\nimport gnupg\nimport config\nimport store\nfrom base64 import b32encode\n\n# to fix gpg error #78 on production\nos.environ['USERNAME'] = 'www-data'\n\nGPG_KEY_TYPE = \"RSA\"\nif 'DEADDROPENV' in os.environ and os.environ['DEADDROPENV'] == 'test':\n    # Use small keys to speed up tests (and try to cheat and avoid issues with\n    # async key generation)\n    GPG_KEY_LENGTH = \"1024\"\nelse:\n    GPG_KEY_LENGTH = \"4096\"\n\nDEFAULT_WORDS_IN_RANDOM_ID = 8\n\nclass CryptoException(Exception): pass\n\ndef clean(s, also=''):\n    \"\"\"\n    >>> clean(\"Hello, world!\")\n    Traceback (most recent call last):\n      ...\n    CryptoException: invalid input\n    >>> clean(\"Helloworld\")\n    'Helloworld'\n    \"\"\"\n    ok = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n    for c in s:\n        if c not in ok and c not in also:\n            raise CryptoException(\"invalid input: %s\" % s)\n    return s\n\nwords = file(config.WORD_LIST).read().split('\\n')\ndef genrandomid(words_in_random_id = DEFAULT_WORDS_IN_RANDOM_ID):\n    return ' '.join(random.choice(words) for x in range(words_in_random_id))\n\ndef displayid(n, words_in_random_id = DEFAULT_WORDS_IN_RANDOM_ID):\n    badrandom_value = badrandom.WichmannHill()\n    badrandom_value.seed(n)\n    return ' '.join(badrandom_value.choice(words) for x in range(words_in_random_id))\n\ndef shash(s):\n    \"\"\"\n    >>> shash('Hello, world!')\n    'EQZGCJBRGISGOTC2NZVWG6LILJBHEV3CINNEWSCLLFTUWZLFHBTS6WLCHFHTOLRSGQXUQLRQHFMXKOKKOQ4WQ6SXGZXDAS3Z'\n    \"\"\"\n    return b32encode(bcrypt.hashpw(s, config.BCRYPT_SALT))\n\nGPG_BINARY = 'gpg2'\ntry:\n    p = subprocess.Popen([GPG_BINARY, '--version'], stdout=subprocess.PIPE)\nexcept OSError:\n    GPG_BINARY = 'gpg'\n    p = subprocess.Popen([GPG_BINARY, '--version'], stdout=subprocess.PIPE)\n\nassert p.stdout.readline().split()[-1].split('.')[0] == '2', \"upgrade GPG to 2.0\"\ngpg = gnupg.GPG(gpgbinary=GPG_BINARY, gnupghome=config.GPG_KEY_DIR)\n\ndef genkeypair(name, secret):\n    \"\"\"\n    >>> if not gpg.list_keys(shash('randomid')):\n    ...     genkeypair(shash('randomid'), 'randomid').type\n    ... else:\n    ...     u'P'\n    u'P'\n    \"\"\"\n    name, secret = clean(name), clean(secret, ' ')\n    return gpg.gen_key(gpg.gen_key_input(\n      key_type=GPG_KEY_TYPE, key_length=GPG_KEY_LENGTH,\n      passphrase=secret,\n      name_email=\"%s@deaddrop.example.com\" % name\n    ))\n\ndef getkey(name):\n    for key in gpg.list_keys():\n        for uid in key['uids']:\n            if ' <%s@' % name in uid: return key['fingerprint']\n    return None\n\ndef _shquote(s):\n    return \"\\\\'\".join(\"'\" + p + \"'\" for p in s.split(\"'\"))\n_gpghacklock = threading.Lock()\n\ndef encrypt(fp, s, output=None, fn=None):\n    r\"\"\"\n    >>> key = genkeypair('randomid', 'randomid')\n    >>> encrypt('randomid', \"Goodbye, cruel world!\")[:45]\n    '-----BEGIN PGP MESSAGE-----\\nVersion: GnuPG v2'\n    \"\"\"\n    if output:\n        store.verify(output)\n    fp = fp.replace(' ', '')\n    if isinstance(s, unicode):\n        s = s.encode('utf8')\n    if isinstance(s, str):\n        out = gpg.encrypt(s, [fp], output=output, always_trust=True)\n    else:\n        if fn:\n            with _gpghacklock:\n                oldname = gpg.gpgbinary\n                gpg.gpgbinary += ' --set-filename ' + _shquote(fn)\n                out = gpg.encrypt_file(s, [fp], output=output, always_trust=True)\n                gpg.gpgbinary = oldname\n        else:\n            out = gpg.encrypt_file(s, [fp], output=output, always_trust=True)\n    if out.ok:\n        return out.data\n    else:\n        raise CryptoException(out.stderr)\n\ndef decrypt(name, secret, s):\n    \"\"\"\n    >>> key = genkeypair('randomid', 'randomid')\n    >>> decrypt('randomid', 'randomid',\n    ...   encrypt('randomid', 'Goodbye, cruel world!')\n    ... )\n    'Goodbye, cruel world!'\n    \"\"\"\n    return gpg.decrypt(s, passphrase=secret).data\n\ndef secureunlink(fn):\n    store.verify(fn)\n    return subprocess.check_call(['srm', fn])\n\n# crash if we don't have srm:\ntry:\n    subprocess.check_call(['srm'], stdout=subprocess.PIPE)\nexcept subprocess.CalledProcessError:\n    pass\n\nif __name__ == \"__main__\":\n    import doctest\n    doctest.testmod()\n", "path": "modules/deaddrop/files/deaddrop/crypto.py"}]}