Skip to content

Commit

Permalink
Initial source commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Etherwvlf committed Apr 22, 2018
0 parents commit f5a2c97
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 0 deletions.
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Copyright 2018 Chlorine, Inc.
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
Description
============

Teardrop is a memory dwelling webchat running over Tor.
The server creates a temporary URL which can be accessed through Tor browser.
No information is stored on disk.

- Runs on Tor network.
- No JS, can be used with NoScript
- Nothing touches storage, everything happens in memory
- No configuration
- No need for a client
- Chats are deleted every 3 minutes
- Randomized usernames - decreasing chance of username reuse
- New service URL upon each server restart

Requirements
=======

flask, stem

Install
=======

Install Tor

Activate some virtual environment and install:

`apt-get install python-virtualenv`

`virtualenv sandboxme`

`source sandboxme/bin/activate`

`pip install git+https://github.com/etherwvlf/teardrop.git`


Usage
=====

Start Tor or Tor Browser, make Control Port open and listening on its default port.

`teardrop`

Share the service URL with clients to open in Tor Browser.

Bundle
=====

Create Windows bundle:

`pyinstaller -F --icon=raindrop.ico --upx-dir=../upx394w teardrop.py`

Create Linux bundle:

`pyinstaller -F --upx-dir=/upx394w teardrop.py`
Binary file added raindrop.ico
Binary file not shown.
21 changes: 21 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os
from setuptools import setup, find_packages

setup(
name = 'teardrop',
version = '0.5.2',
description = 'Teardrop',
long_description = 'Teardrop - Memory Dwelling Webchat',
url = '',
license = 'Chlorine, Inc.',
author = '',
author_email = '',
packages = [''],
include_package_data = True,
package_data = {'': ['templates/*.html']},
install_requires = ["flask", "stem"],
classifiers = [ 'Development Status :: 7 - Beta',
'Programming Language :: Python'],
entry_points = { 'console_scripts':
[ 'teardrop = teardrop:main']}
)
174 changes: 174 additions & 0 deletions teardrop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
from flask import Flask
from flask import render_template
from flask import request, session, url_for, redirect, abort
import traceback
import sys
import string
from stem.control import Controller
from hashlib import sha224
import random
import datetime
from stem import SocketError
import textwrap
app = Flask(__name__)
import logging
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)


chatters = []
global chatlines
chatlines = []

def id_generator(size=6,
chars=string.ascii_uppercase + string.digits +
string.ascii_lowercase):

return ''.join(random.choice(chars) for i in range(size))


app.secret_key = id_generator(size=64)


def check_older_than(chat_dic, secs_to_live = 180):
now = datetime.datetime.now()
timestamp = chat_dic["timestamp"]
diff = now - timestamp
secs = diff.total_seconds()

if secs >= secs_to_live:
return True

return False


def process_chat(chat_dic):

chats = []
max_chat_len = 69
if len(chat_dic["msg"]) > max_chat_len:

for message in textwrap.wrap(chat_dic["msg"], width = max_chat_len):
partial_chat = {}
partial_chat["msg"] = message.strip()
partial_chat["timestamp"] = datetime.datetime.now()
partial_chat["username"] = session["_id"]
chats.append(partial_chat)

else:
chats = [chat_dic]

return chats


# Remove headers
@app.after_request
def remove_headers(response):
response.headers["Server"] = ""
response.headers["Date"] = ""
return response


# Empty Index page to avoid Flask fingerprinting
@app.route('/', methods=["GET"])
def index():
return ('', 200)


@app.route('/<string:url_addition>', methods=["GET"])
def drop(url_addition):

if url_addition != app.config["path"]:
return ('', 404)

if "_id" not in session:
session["_id"] = id_generator()
chatters.append(session["_id"])

if request.method == "GET":
full_path = app.config["hostname"] + "/" + app.config["path"]
return render_template("drop.html",
hostname=app.config["hostname"],
path=app.config["path"])


@app.route('/<string:url_addition>/rooms', methods=["GET", "POST"])
def chat_messages(url_addition):

global chatlines
more_chats = False
if url_addition != app.config["path"]:
return ('', 404)

to_delete = []
c = 0
for chatline_dic in chatlines:
if check_older_than(chatline_dic):
to_delete.append(c)

c += 1

for _del in to_delete:
chatlines.pop(_del)

if request.method == "POST":

if request.form["dropdata"].strip():

chat = {}
chat["msg"] = request.form["dropdata"].strip()
chat["timestamp"] = datetime.datetime.now()
chat["username"] = session["_id"]
chats = process_chat(chat)
chatlines = chatlines + chats
chatlines = chatlines[-13:]
more_chats = True

return redirect(app.config["path"], code=302)

return render_template("rooms.html",
chatlines=chatlines, num_people = len(chatters))

def main():

try:
controller = Controller.from_port()
except SocketError:
sys.stderr.write(' * Tor proxy or Control Port not running. Start the Tor Browser or Tor daemon and ensure the ControlPort is open.\n')
sys.exit(1)


print(' * Connecting to tor')
with controller:
controller.authenticate()

# Redirect port 80 to 5000(where Flask runs).
print(' * Creating teardrop service...')
result = controller.create_ephemeral_hidden_service({80: 5000}, await_publication = True)

print(" * New teardrop service started.URL: %s.onion" % result.service_id)
###result = controller.create_hidden_service(hidden_service_dir, 80, target_port = 5000)

if not result:
print(" * Something went wrong, shutting down")
###controller.remove_hidden_service(hidden_service_dir)
###shutil.rmtree(hidden_service_dir)

if result.service_id:
app.config["hostname"] = result.service_id
app.config["path"] = id_generator(size = 64)
app.config["full_path"] = app.config["hostname"] + ".onion" + "/" + app.config["path"]
print(' * Press ctrl+c to quit')
print(" * Service address: %s" % app.config["full_path"])
else:
print(" * Unable to determine service's hostname")

try:
app.run(debug=False, threaded = True)
finally:

print(" * Shutting down service")
controller.remove_ephemeral_hidden_service(result.service_id)

if __name__ == "__main__":
main()
25 changes: 25 additions & 0 deletions templates/drop.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<title>Teardrop</title>
<style>
*{
padding-top:3px;
font-family: Lucida Sans Typewriter,Lucida Console,monaco,Bitstream Vera Sans Mono,monospace;
font-size:22px;
background-color:black;
color:lime;
}
</style>

<div id="welcome" style="text-align:right;">Server: {{ hostname }}<br />
</div>

<div style="overflow: auto;text-align:center;">
<iframe src="/{{path}}/rooms" name="chatframe" style="width:90vw; height:90vh;" frameBorder=0></iframe>
</div>

<div style="position: fixed; bottom: 5px;">
<form id="deadform" action="/{{ path }}/rooms" method="POST">
<input type="text" placeholder="Type message and press enter..." name="dropdata" style="width:90vw; height:40px;border:none;" autofocus required>
<input type="submit" value="Send" style="margin-top:5px; float:right; display:none;">
</form>
</div>
20 changes: 20 additions & 0 deletions templates/rooms.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<html>
<meta http-equiv="refresh" content="1" >
<style>
*{
padding-top:3px;
font-family: Lucida Sans Typewriter,Lucida Console,monaco,Bitstream Vera Sans Mono,monospace;
font-size:22px;
}
</style>

<div style="float:right;font-size:14px;color:lime;font-weight:bold;">Entities: {{ num_people }}</div>
<table>
{% for message_dic in chatlines %}
<tr>
<td name="msg" class="msg" style="color:red;"><b>{{ message_dic["username"] }}</b>: {{ message_dic["msg"] }}</td>
</tr>
{% endfor %}
</table>

</html>

0 comments on commit f5a2c97

Please sign in to comment.