2018-03-08 08:45:07 -05:00
|
|
|
"""
|
|
|
|
OnionShare | https://onionshare.org/
|
|
|
|
|
2018-04-24 13:07:59 -04:00
|
|
|
Copyright (C) 2014-2018 Micah Lee <micah@micahflee.com>
|
2018-03-08 08:45:07 -05:00
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import contextlib
|
|
|
|
import inspect
|
|
|
|
import io
|
|
|
|
import os
|
|
|
|
import random
|
|
|
|
import re
|
|
|
|
import socket
|
|
|
|
import sys
|
|
|
|
import zipfile
|
2018-04-29 20:51:58 -04:00
|
|
|
import tempfile
|
2018-03-08 08:45:07 -05:00
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
2018-03-13 05:22:26 -04:00
|
|
|
from onionshare.common import Common
|
2018-10-12 19:35:09 -04:00
|
|
|
from onionshare import strings
|
2018-04-29 20:51:58 -04:00
|
|
|
from onionshare.web import Web
|
2018-04-29 21:19:00 -04:00
|
|
|
from onionshare.settings import Settings
|
2018-03-08 08:45:07 -05:00
|
|
|
|
|
|
|
DEFAULT_ZW_FILENAME_REGEX = re.compile(r'^onionshare_[a-z2-7]{6}.zip$')
|
|
|
|
RANDOM_STR_REGEX = re.compile(r'^[a-z2-7]+$')
|
|
|
|
|
2018-04-29 20:51:58 -04:00
|
|
|
|
2018-09-21 14:14:32 -04:00
|
|
|
def web_obj(common_obj, mode, num_files=0):
|
2018-04-29 20:51:58 -04:00
|
|
|
""" Creates a Web object, in either share mode or receive mode, ready for testing """
|
2018-04-29 21:19:00 -04:00
|
|
|
common_obj.load_settings()
|
2018-10-12 19:35:09 -04:00
|
|
|
strings.load_strings(common_obj)
|
2018-09-21 14:14:32 -04:00
|
|
|
web = Web(common_obj, False, mode)
|
2018-04-29 20:51:58 -04:00
|
|
|
web.generate_slug()
|
|
|
|
web.stay_open = True
|
2018-04-29 21:00:10 -04:00
|
|
|
web.running = True
|
|
|
|
|
2018-04-29 20:51:58 -04:00
|
|
|
web.app.testing = True
|
|
|
|
|
|
|
|
# Share mode
|
2018-09-21 14:14:32 -04:00
|
|
|
if mode == 'share':
|
2018-04-29 20:51:58 -04:00
|
|
|
# Add files
|
|
|
|
files = []
|
|
|
|
for i in range(num_files):
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
|
|
|
|
tmp_file.write(b'*' * 1024)
|
|
|
|
files.append(tmp_file.name)
|
2018-09-21 14:14:32 -04:00
|
|
|
web.share_mode.set_file_info(files)
|
2018-04-29 20:51:58 -04:00
|
|
|
# Receive mode
|
|
|
|
else:
|
|
|
|
pass
|
2018-09-14 21:59:36 -04:00
|
|
|
|
2018-04-29 20:51:58 -04:00
|
|
|
return web
|
|
|
|
|
|
|
|
|
|
|
|
class TestWeb:
|
|
|
|
def test_share_mode(self, common_obj):
|
2018-09-21 14:14:32 -04:00
|
|
|
web = web_obj(common_obj, 'share', 3)
|
|
|
|
assert web.mode is 'share'
|
2018-04-29 20:51:58 -04:00
|
|
|
with web.app.test_client() as c:
|
|
|
|
# Load 404 pages
|
|
|
|
res = c.get('/')
|
2018-04-29 21:00:10 -04:00
|
|
|
res.get_data()
|
2018-04-29 20:51:58 -04:00
|
|
|
assert res.status_code == 404
|
|
|
|
|
|
|
|
res = c.get('/invalidslug'.format(web.slug))
|
2018-04-29 21:00:10 -04:00
|
|
|
res.get_data()
|
2018-04-29 20:51:58 -04:00
|
|
|
assert res.status_code == 404
|
|
|
|
|
|
|
|
# Load download page
|
|
|
|
res = c.get('/{}'.format(web.slug))
|
2018-04-29 21:00:10 -04:00
|
|
|
res.get_data()
|
2018-04-29 20:51:58 -04:00
|
|
|
assert res.status_code == 200
|
|
|
|
|
|
|
|
# Download
|
|
|
|
res = c.get('/{}/download'.format(web.slug))
|
2018-04-29 21:00:10 -04:00
|
|
|
res.get_data()
|
2018-04-29 20:51:58 -04:00
|
|
|
assert res.status_code == 200
|
|
|
|
assert res.mimetype == 'application/zip'
|
2018-09-14 21:59:36 -04:00
|
|
|
|
2018-04-29 21:19:00 -04:00
|
|
|
def test_share_mode_close_after_first_download_on(self, common_obj, temp_file_1024):
|
2018-09-21 14:14:32 -04:00
|
|
|
web = web_obj(common_obj, 'share', 3)
|
2018-04-29 21:00:10 -04:00
|
|
|
web.stay_open = False
|
|
|
|
|
|
|
|
assert web.running == True
|
|
|
|
|
|
|
|
with web.app.test_client() as c:
|
|
|
|
# Download the first time
|
|
|
|
res = c.get('/{}/download'.format(web.slug))
|
|
|
|
res.get_data()
|
|
|
|
assert res.status_code == 200
|
|
|
|
assert res.mimetype == 'application/zip'
|
|
|
|
|
|
|
|
assert web.running == False
|
2018-09-14 21:59:36 -04:00
|
|
|
|
2018-04-29 21:19:00 -04:00
|
|
|
def test_share_mode_close_after_first_download_off(self, common_obj, temp_file_1024):
|
2018-09-21 14:14:32 -04:00
|
|
|
web = web_obj(common_obj, 'share', 3)
|
2018-04-29 21:19:00 -04:00
|
|
|
web.stay_open = True
|
|
|
|
|
|
|
|
assert web.running == True
|
|
|
|
|
|
|
|
with web.app.test_client() as c:
|
|
|
|
# Download the first time
|
|
|
|
res = c.get('/{}/download'.format(web.slug))
|
|
|
|
res.get_data()
|
|
|
|
assert res.status_code == 200
|
|
|
|
assert res.mimetype == 'application/zip'
|
|
|
|
assert web.running == True
|
2018-09-14 21:59:36 -04:00
|
|
|
|
2018-04-29 21:19:00 -04:00
|
|
|
def test_receive_mode(self, common_obj):
|
2018-09-21 14:14:32 -04:00
|
|
|
web = web_obj(common_obj, 'receive')
|
|
|
|
assert web.mode is 'receive'
|
2018-04-29 21:19:00 -04:00
|
|
|
|
|
|
|
with web.app.test_client() as c:
|
|
|
|
# Load 404 pages
|
|
|
|
res = c.get('/')
|
|
|
|
res.get_data()
|
|
|
|
assert res.status_code == 404
|
|
|
|
|
|
|
|
res = c.get('/invalidslug'.format(web.slug))
|
|
|
|
res.get_data()
|
|
|
|
assert res.status_code == 404
|
|
|
|
|
|
|
|
# Load upload page
|
|
|
|
res = c.get('/{}'.format(web.slug))
|
|
|
|
res.get_data()
|
|
|
|
assert res.status_code == 200
|
2018-09-14 21:59:36 -04:00
|
|
|
|
2018-04-29 21:19:00 -04:00
|
|
|
def test_receive_mode_allow_receiver_shutdown_on(self, common_obj):
|
2018-09-21 14:14:32 -04:00
|
|
|
web = web_obj(common_obj, 'receive')
|
2018-04-29 21:19:00 -04:00
|
|
|
|
|
|
|
common_obj.settings.set('receive_allow_receiver_shutdown', True)
|
|
|
|
|
|
|
|
assert web.running == True
|
|
|
|
|
|
|
|
with web.app.test_client() as c:
|
|
|
|
# Load close page
|
|
|
|
res = c.post('/{}/close'.format(web.slug))
|
|
|
|
res.get_data()
|
|
|
|
# Should return ok, and server should stop
|
|
|
|
assert res.status_code == 200
|
|
|
|
assert web.running == False
|
2018-09-14 21:59:36 -04:00
|
|
|
|
2018-04-29 21:19:00 -04:00
|
|
|
def test_receive_mode_allow_receiver_shutdown_off(self, common_obj):
|
2018-09-21 14:14:32 -04:00
|
|
|
web = web_obj(common_obj, 'receive')
|
2018-04-29 21:19:00 -04:00
|
|
|
|
|
|
|
common_obj.settings.set('receive_allow_receiver_shutdown', False)
|
|
|
|
|
|
|
|
assert web.running == True
|
|
|
|
|
|
|
|
with web.app.test_client() as c:
|
|
|
|
# Load close page
|
|
|
|
res = c.post('/{}/close'.format(web.slug))
|
|
|
|
res.get_data()
|
|
|
|
# Should redirect to index, and server should still be running
|
|
|
|
assert res.status_code == 302
|
|
|
|
assert web.running == True
|
2018-09-21 14:14:32 -04:00
|
|
|
|
2018-07-22 00:58:14 -04:00
|
|
|
def test_public_mode_on(self, common_obj):
|
2018-09-21 14:14:32 -04:00
|
|
|
web = web_obj(common_obj, 'receive')
|
2018-07-22 00:58:14 -04:00
|
|
|
common_obj.settings.set('public_mode', True)
|
2018-04-29 21:19:00 -04:00
|
|
|
|
|
|
|
with web.app.test_client() as c:
|
2018-09-16 00:18:44 -04:00
|
|
|
# Upload page should be accessible from /
|
2018-04-29 21:19:00 -04:00
|
|
|
res = c.get('/')
|
|
|
|
data1 = res.get_data()
|
|
|
|
assert res.status_code == 200
|
|
|
|
|
2018-09-16 00:18:44 -04:00
|
|
|
# /[slug] should be a 404
|
2018-04-29 21:19:00 -04:00
|
|
|
res = c.get('/{}'.format(web.slug))
|
|
|
|
data2 = res.get_data()
|
2018-09-16 00:18:44 -04:00
|
|
|
assert res.status_code == 404
|
2018-09-21 14:14:32 -04:00
|
|
|
|
2018-07-22 00:58:14 -04:00
|
|
|
def test_public_mode_off(self, common_obj):
|
2018-09-21 14:14:32 -04:00
|
|
|
web = web_obj(common_obj, 'receive')
|
2018-07-22 00:58:14 -04:00
|
|
|
common_obj.settings.set('public_mode', False)
|
2018-04-29 21:19:00 -04:00
|
|
|
|
|
|
|
with web.app.test_client() as c:
|
|
|
|
# / should be a 404
|
|
|
|
res = c.get('/')
|
|
|
|
data1 = res.get_data()
|
|
|
|
assert res.status_code == 404
|
|
|
|
|
2018-09-16 00:18:44 -04:00
|
|
|
# Upload page should be accessible from /[slug]
|
2018-04-29 21:19:00 -04:00
|
|
|
res = c.get('/{}'.format(web.slug))
|
|
|
|
data2 = res.get_data()
|
|
|
|
assert res.status_code == 200
|
2018-04-29 20:51:58 -04:00
|
|
|
|
|
|
|
|
2018-03-08 08:45:07 -05:00
|
|
|
class TestZipWriterDefault:
|
|
|
|
@pytest.mark.parametrize('test_input', (
|
|
|
|
'onionshare_{}.zip'.format(''.join(
|
|
|
|
random.choice('abcdefghijklmnopqrstuvwxyz234567') for _ in range(6)
|
|
|
|
)) for _ in range(50)
|
|
|
|
))
|
|
|
|
def test_default_zw_filename_regex(self, test_input):
|
|
|
|
assert bool(DEFAULT_ZW_FILENAME_REGEX.match(test_input))
|
|
|
|
|
|
|
|
def test_zw_filename(self, default_zw):
|
|
|
|
zw_filename = os.path.basename(default_zw.zip_filename)
|
|
|
|
assert bool(DEFAULT_ZW_FILENAME_REGEX.match(zw_filename))
|
|
|
|
|
|
|
|
def test_zipfile_filename_matches_zipwriter_filename(self, default_zw):
|
|
|
|
assert default_zw.z.filename == default_zw.zip_filename
|
|
|
|
|
|
|
|
def test_zipfile_allow_zip64(self, default_zw):
|
|
|
|
assert default_zw.z._allowZip64 is True
|
|
|
|
|
|
|
|
def test_zipfile_mode(self, default_zw):
|
|
|
|
assert default_zw.z.mode == 'w'
|
|
|
|
|
|
|
|
def test_callback(self, default_zw):
|
|
|
|
assert default_zw.processed_size_callback(None) is None
|
|
|
|
|
|
|
|
def test_add_file(self, default_zw, temp_file_1024_delete):
|
|
|
|
default_zw.add_file(temp_file_1024_delete)
|
|
|
|
zipfile_info = default_zw.z.getinfo(
|
|
|
|
os.path.basename(temp_file_1024_delete))
|
|
|
|
|
|
|
|
assert zipfile_info.compress_type == zipfile.ZIP_DEFLATED
|
|
|
|
assert zipfile_info.file_size == 1024
|
|
|
|
|
|
|
|
def test_add_directory(self, temp_dir_1024_delete, default_zw):
|
|
|
|
previous_size = default_zw._size # size before adding directory
|
|
|
|
default_zw.add_dir(temp_dir_1024_delete)
|
|
|
|
assert default_zw._size == previous_size + 1024
|
|
|
|
|
|
|
|
|
|
|
|
class TestZipWriterCustom:
|
|
|
|
@pytest.mark.parametrize('test_input', (
|
2018-03-13 05:22:26 -04:00
|
|
|
Common.random_string(
|
2018-03-08 08:45:07 -05:00
|
|
|
random.randint(2, 50),
|
|
|
|
random.choice((None, random.randint(2, 50)))
|
|
|
|
) for _ in range(50)
|
|
|
|
))
|
|
|
|
def test_random_string_regex(self, test_input):
|
|
|
|
assert bool(RANDOM_STR_REGEX.match(test_input))
|
|
|
|
|
|
|
|
def test_custom_filename(self, custom_zw):
|
|
|
|
assert bool(RANDOM_STR_REGEX.match(custom_zw.zip_filename))
|
|
|
|
|
|
|
|
def test_custom_callback(self, custom_zw):
|
|
|
|
assert custom_zw.processed_size_callback(None) == 'custom_callback'
|