Skip to content

Commit

Permalink
Merge pull request #5 from ogarcia/setup
Browse files Browse the repository at this point in the history
Several improvements and code fix
  • Loading branch information
brechin authored Jun 7, 2017
2 parents 46eaf37 + 6c431ff commit 62bc248
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 96 deletions.
5 changes: 5 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CONTRIBUTORS

Alphabetically-ordered list of the people who contributed to FileTeaSend:

- Óscar García Amor (@ogarcia)
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
FileTeaSend
===========
# FileTeaSend

Easy file transfer using FileTea service

## Installation

```bash
git clone https://github.com/brechin/FileTeaSend.git
virtualenv ./fileteavenv
source ./fileteavenv/bin/activate
cd FileTeaSend
python setup.py install
```

## Run

Simply run `filetea` command with `-h` or `--help` to read the help of
command. It's self explanatory.

## Options and arguments

You can specify FileTea server with `FILETEAURL` environment variable or
with `-l` or `--url` arguments.

The `-v` argument enable the verbose mode, you can repeat it to get more
verbose output.
102 changes: 8 additions & 94 deletions filetea.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,96 +1,10 @@
#!/usr/bin/env python
from __future__ import print_function
import logging
import sys
import os
import json
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Distributed under terms of the GNU GPLv3 license.

import requests
import magic
from fileteasend.app import main

__FILETEA_URL = 'https://filetea.me/'
logging.basicConfig(level=logging.DEBUG)


def get_peer_id(session_obj):
"""Perform handshake with endpoint to get a usable peer-id"""
logger = logging.getLogger('get_peer_id')
handshake_text = '{"mechanisms":["websocket","long-polling"],"url":"/transport/"}'
handshake = session_obj.post('%stransport/handshake' % __FILETEA_URL, data=handshake_text,
headers={'Content-Type': 'text/plain'})
logger.debug(handshake.headers)
logger.debug(handshake.content)
return handshake.json()['peer-id']


def send_file(session_obj, file_name_to_send, token):
"""Send specified file to endpoint"""
logger = logging.getLogger('send_file')
logger.debug('Sending %s to %s' % (file_name_to_send, token))
logger.info('Sending file %s' % file_name_to_send)
with open(file_name_to_send, 'rb') as f:
resp = session_obj.put('%s%s' % (__FILETEA_URL, token), f)
logger.debug(resp)
return resp


def register_file(session_obj, file_to_send):
"""Register file with endpoint, returns url to use to download the file"""
logger = logging.getLogger('register_file')
ms = magic.open(magic.MIME_TYPE)
ms.load()
mime_type = ms.file(file_to_send)
ms.close()
send_url = '%stransport/lp/send?%s' % (__FILETEA_URL, my_uuid)
send_data = 'X{"method":"addFileSources","params":[["%s","%s",%d]],"id":"1"}' % (
os.path.basename(file_to_send), mime_type, os.path.getsize(file_to_send))
logger.info('Sending %s' % send_data)
sent_response = session_obj.post(send_url, data=send_data, headers={'Content-Type': 'text/plain'})
logger.debug(sent_response.headers)
logger.debug(sent_response.text)
send_return_data = sent_response.text
assert send_return_data.startswith('@')
send_return_data = send_return_data[1:]
send_response_json = json.loads(send_return_data)
logger.debug('Decoded JSON: %s' % send_response_json)
file_download_url = '%s%s' % (__FILETEA_URL, send_response_json['result'][0][0])
return file_download_url

if __name__ == '__main__':
base_logger = logging.getLogger('filetea')
session = requests.session()

if len(sys.argv) < 2:
sys.exit('Need to specify file to share')

file_to_send = sys.argv[1]
if not os.path.exists(file_to_send):
sys.exit('File does not exist.')
if not os.path.isfile(file_to_send):
sys.exit('Path is not a file.')

# Get a peer-id registered with endpoint
my_uuid = get_peer_id(session)
# Register specified file with endpoint, which returns the URL to download the file
url = register_file(session, file_to_send)
base_logger.info('URL for file %s: %s' % (file_to_send, url))
print('URL: %s', url)

while True:
# Try recv, might 404
base_logger.info('Waiting to send recv')
recv_url = '%stransport/lp/receive?%s' % (__FILETEA_URL, my_uuid)
recv_response = session.get(recv_url)
base_logger.debug(recv_response.content)

recv_return_data = recv_response.content
if recv_return_data.startswith('w'):
base_logger.debug('Got file transfer request')
recv_return_data = recv_return_data[1:]

r = json.loads(recv_return_data)

if r['method'] == "fileTransferNew":
send_file(session, file_to_send, r['params'][-1])
else:
base_logger.debug('Not a file transfer')
if __name__ == "__main__":
main()
Empty file added fileteasend/__init__.py
Empty file.
172 changes: 172 additions & 0 deletions fileteasend/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Distributed under terms of the GNU GPLv3 license.

from __future__ import print_function
try:
from urllib.parse import urljoin, urlparse
except ImportError:
from urlparse import urljoin, urlparse
import argparse
import json
import logging
import os
import signal
import sys

import magic
import requests

class FileTeaClient(object):
"""Base client class for FileTea access"""
def __init__(self, url=None):
logger = logging.getLogger('filetea.FileTeaClient')
# Set URL to default if not URL is passed
self.url = 'https://filetea.me/' if url is None else url
logger.debug('Using FileTea server {}'.format(self.url))
self.session = requests.session()

def get_peer_id(self):
"""
Perform handshake with endpoint to get a usable peer-id
:return: peer-id
:rtype: string
"""
logger = logging.getLogger('filetea.FileTeaClient.get_peer_id')
handshake_headers = {'Content-Type': 'text/plain'}
handshake_text = '{"mechanisms":["websocket","long-polling"],"url":"/transport/"}'
url = urljoin(self.url, 'transport/handshake')
handshake = self.session.post(url, data=handshake_text, headers=handshake_headers)
logger.debug(handshake.headers)
logger.debug(handshake.content)
return handshake.json()['peer-id']

def register_file(self, peer_id, file_to_send):
"""
Register file with endpoint and returns url to use to download the file
:return: url
:rtype: string
"""
logger = logging.getLogger('filetea.FileTeaClient.register_file')
file_name = os.path.basename(file_to_send)
file_size = os.path.getsize(file_to_send)
if hasattr(magic, 'open'): # use magic-file-extensions
ms = magic.open(magic.MIME_TYPE)
ms.load()
mime_type = ms.file(file_to_send)
ms.close()
else: # use python-magic
mime_type = magic.from_file(file_to_send, mime=True)
send_headers = {'Content-Type': 'text/plain'}
send_url = urljoin(self.url, 'transport/lp/send')
send_data = {
"method": "addFileSources",
"params": [[
file_name,
mime_type,
file_size
]],
"id": "1" }
send_data = 'X{}'.format(json.dumps(send_data))
logger.info('Sending: {}'.format(send_data))
send_response = self.session.post(send_url, data=send_data, params=peer_id, headers=send_headers)
logger.debug(send_response.headers)
logger.debug(send_response.text)
send_response_text = send_response.text
assert send_response_text.startswith('@')
send_response_text = send_response_text[1:]
send_response_json = json.loads(send_response_text)
logger.debug('Decoded JSON response: {}'.format(send_response_json))
file_download_url = urljoin(self.url, send_response_json['result'][0][0])
return file_download_url

def send_file(self, file_to_send, token):
"""
Send specified file to endpoint
"""
logger = logging.getLogger('filetea.FileTeaClient.send_file')
logger.info('Sending file {} to {}'.format(file_to_send, token))
send_url = urljoin(self.url, token)
with open(file_to_send, 'rb') as f:
send_response = self.session.put(send_url, f)
logger.debug(send_response)

def receive_response(self, peer_id, file_to_send):
"""
Wait for server response to send file
"""
logger = logging.getLogger('filetea.FileTeaClient.receive_response')
receive_url = urljoin(self.url, 'transport/lp/receive')
receive_response = self.session.get(receive_url, params=peer_id)
receive_response_text = receive_response.text
logger.debug(receive_response_text)
try:
receive_response_text = receive_response_text[1:]
receive_response_json = json.loads(receive_response_text)
logger.debug('Decoded JSON response: {}'.format(receive_response_json))
if receive_response_json['method'] == 'fileTransferNew':
token = receive_response_json['params'][-1]
logger.debug('Got a file transfer request from token {}'.format(token))
print('File transfer request received from token {}'.format(token))
self.send_file(file_to_send, token)
else:
logger.info('Not a file transfer request')
except Exception as e:
logger.info('Not a file transfer request: {}'.format(e))
pass

def exit(signal, frame):
sys.exit(0)

def run(args):
logger = logging.getLogger('filetea')
# Set FileTea server URL
server_url = args.url if args.url != None else os.getenv('FILETEAURL', 'https://filetea.me/')
if not urlparse(server_url).netloc:
sys.exit('No schema supplied on URL.')

file_tea_client = FileTeaClient(server_url)
# Get a peer-id registered with endpoint
peer_id = file_tea_client.get_peer_id()
# Register specified file with endpoint, which returns the URL to download the file
file_url = file_tea_client.register_file(peer_id, args.file)
logger.info('URL for file {}: {}'.format(args.file, file_url))
print('URL: {}'.format(file_url))
print('Press CTRL+C to stop sharing...')

while True:
# Main loop
logger.info('Waiting for a server response')
file_tea_client.receive_response(peer_id, args.file)

def main():
signal.signal(signal.SIGINT, exit)
parser = argparse.ArgumentParser(description='Easy file transfer using the FileTea service')
parser.add_argument('--url', '-l', help='filetea server url (also FILETEAURL)')
parser.add_argument('--verbose', '-v', action='count', default=0, help='be verbose (vv to increase verbosity)')
parser.add_argument('file', help='file to send')
parser.set_defaults(func=run)
args = parser.parse_args()

# Set loglevel equivalents
log_levels = {
0: logging.ERROR,
1: logging.INFO,
2: logging.DEBUG }

# Maximum loglevel is 2 if user sends more vvv we ignore it
args.verbose = 2 if args.verbose >= 2 else args.verbose
logging.basicConfig(level=log_levels[args.verbose])

# Check if file exists and can be read
if not os.path.exists(args.file):
sys.exit('File does not exist.')
if not os.path.isfile(args.file):
sys.exit('Path is not a file.')
if not os.access(args.file, os.R_OK):
sys.exit('File can not be read.')

args.func(args)
43 changes: 43 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Distributed under terms of the GNU GPLv3 license.

import os
from setuptools import setup

# Utility function to read the README file.
# Used for the long_description. It's nice, because now 1) we have a top level
# README file and 2) it's easier to type in the README file than to put a raw
# string in below ...
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()

setup(
name = "fileteasend",
version = "1.0",
author = "Jason Brechin",
author_email = "[email protected]",
description = ("Easy file transfer using the FileTea service"),
license = "GPLv3",
keywords = "file web transfer easy FileTea",
url = "https://github.com/brechin/FileTeaSend",
packages=['fileteasend'],
long_description=read('README.md'),
entry_points={
'console_scripts': [
'filetea = fileteasend.app:main'
]
},
install_requires = [
"requests>=2.13.0",
"python-magic>=0.4.0"
],
classifiers=[
"Development Status :: 3 - Alpha",
"Topic :: Utilities",
"License :: OSI Approved :: GNU General Public License (GPLv3)",
'Programming Language :: Python :: 3',
],
)

0 comments on commit 62bc248

Please sign in to comment.