diff --git a/plugins/Sidebar/SidebarPlugin.py b/plugins/Sidebar/SidebarPlugin.py index d3490cd4..1724a078 100644 --- a/plugins/Sidebar/SidebarPlugin.py +++ b/plugins/Sidebar/SidebarPlugin.py @@ -376,7 +376,7 @@ class UiWebsocketPlugin(object): else: loc = geodb.get(peer.ip) loc_cache[peer.ip] = loc - if not loc: + if not loc or "location" not in loc: continue # Create position array diff --git a/src/Config.py b/src/Config.py index fe0079e9..e850a8a8 100644 --- a/src/Config.py +++ b/src/Config.py @@ -8,7 +8,7 @@ class Config(object): def __init__(self, argv): self.version = "0.3.2" - self.rev = 378 + self.rev = 390 self.argv = argv self.action = None self.createParser() @@ -29,6 +29,14 @@ class Config(object): # Create command line arguments def createArguments(self): + trackers = [ + "udp://open.demonii.com:1337", + "udp://tracker.leechers-paradise.org:6969", + "udp://9.rarbg.com:2710", + "http://tracker.aletorrenty.pl:2710/announce", + "http://retracker.telecom.kz/announce", + "http://torrent.gresille.org/announce" + ] # Platform specific if sys.platform.startswith("win"): coffeescript = "type %s | tools\\coffee\\coffee.cmd" @@ -122,7 +130,8 @@ class Config(object): self.parser.add_argument('--fileserver_port', help='FileServer bind port', default=15441, type=int, metavar='port') self.parser.add_argument('--disable_udp', help='Disable UDP connections', action='store_true') self.parser.add_argument('--proxy', help='Socks proxy address', metavar='ip:port') - self.parser.add_argument('--ip_external', help='External ip (tested on start if None)', metavar='ip') + self.parser.add_argument('--ip_external', help='Set reported external ip (tested on start if None)', metavar='ip') + self.parser.add_argument('--trackers', help='Bootstraping torrent trackers', default=trackers, metavar='protocol://address', nargs='*') self.parser.add_argument('--use_openssl', help='Use OpenSSL liblary for speedup', type='bool', choices=[True, False], default=use_openssl) self.parser.add_argument('--disable_encryption', help='Disable connection encryption', action='store_true') @@ -238,7 +247,8 @@ class Config(object): if section != "global": # If not global prefix key with section key = section + "_" + key if val: - argv.insert(1, val) + for line in val.strip().split("\n"): # Allow multi-line values + argv.insert(1, line) argv.insert(1, "--%s" % key) return argv diff --git a/src/File/FileServer.py b/src/File/FileServer.py index 30c6e9da..0f59a43d 100644 --- a/src/File/FileServer.py +++ b/src/File/FileServer.py @@ -170,11 +170,15 @@ class FileServer(ConnectionServer): # Announce sites every 20 min def announceSites(self): import gc + first_announce = True # First start while 1: - time.sleep(20 * 60) # Announce sites every 20 min + # Sites healthcare for address, site in self.sites.items(): if site.settings["serving"]: - site.announce() # Announce site to tracker + if first_announce: # Announce to all trackers on startup + site.announce() + else: # If not first run only use PEX + site.announcePex() # Reset bad file retry counter for inner_path in site.bad_files: @@ -195,6 +199,20 @@ class FileServer(ConnectionServer): site = None gc.collect() # Implicit grabage collection + # Find new peers + for tracker_i in range(len(config.trackers)): + time.sleep(60 * 20 / len(config.trackers)) # Query all trackers one-by-one in 20 minutes evenly distributed + + for address, site in self.sites.items(): + site.announce(num = 1, pex = False) + time.sleep(2) + + first_announce = False + + + + + # Detects if computer back from wakeup def wakeupWatcher(self): last_time = time.time() diff --git a/src/Site/Site.py b/src/Site/Site.py index cdee33a0..6152e0d8 100644 --- a/src/Site/Site.py +++ b/src/Site/Site.py @@ -38,6 +38,7 @@ class Site: self.peers = {} # Key: ip:port, Value: Peer.Peer self.peer_blacklist = SiteManager.peer_blacklist # Ignore this peers (eg. myself) self.last_announce = 0 # Last announce time to tracker + self.last_tracker_id = random.randint(0, 10) # Last announced tracker id self.worker_manager = WorkerManager(self) # Handle site download from other peers self.bad_files = {} # SHA check failed files, need to redownload {"inner.content": 1} (key: file, value: failed accept) self.content_updated = None # Content.js update time @@ -510,12 +511,13 @@ class Site: # Gather peers from tracker # Return: Complete time or False on error - def announceTracker(self, protocol, ip, port, fileserver_port, address_hash, my_peer_id): + def announceTracker(self, protocol, address, fileserver_port, address_hash, my_peer_id): s = time.time() if protocol == "udp": # Udp tracker if config.disable_udp: return False # No udp supported - tracker = UdpTrackerClient(ip, port) + ip, port = address.split(":") + tracker = UdpTrackerClient(ip, int(port)) tracker.peer_port = fileserver_port try: tracker.connect() @@ -535,7 +537,7 @@ class Site: } req = None try: - url = "http://" + ip + "?" + urllib.urlencode(params) + url = "http://" + address + "?" + urllib.urlencode(params) # Load url with gevent.Timeout(10, False): # Make sure of timeout req = urllib2.urlopen(url, timeout=8) @@ -577,10 +579,17 @@ class Site: return time.time() - s # Add myself and get other peers from tracker - def announce(self, force=False): + def announce(self, force=False, num=5, pex=True): if time.time() < self.last_announce + 30 and not force: return # No reannouncing within 30 secs self.last_announce = time.time() + + trackers = config.trackers + if num == 1: # Only announce on one tracker, increment the queried tracker id + self.last_tracker_id += 1 + self.last_tracker_id = self.last_tracker_id % len(trackers) + trackers = [trackers[self.last_tracker_id]] # We only going to use this one + errors = [] slow = [] address_hash = hashlib.sha1(self.address).hexdigest() # Site address hash @@ -595,39 +604,42 @@ class Site: announced = 0 threads = [] - for protocol, ip, port in SiteManager.TRACKERS: # Start announce threads - thread = gevent.spawn(self.announceTracker, protocol, ip, port, fileserver_port, address_hash, my_peer_id) + for tracker in trackers: # Start announce threads + protocol, address = tracker.split("://") + thread = gevent.spawn(self.announceTracker, protocol, address, fileserver_port, address_hash, my_peer_id) threads.append(thread) - thread.ip = ip + thread.address = address thread.protocol = protocol + if len(threads) > num: break # Announce limit gevent.joinall(threads) # Wait for announce finish for thread in threads: if thread.value: if thread.value > 1: - slow.append("%.2fs %s://%s" % (thread.value, thread.protocol, thread.ip)) + slow.append("%.2fs %s://%s" % (thread.value, thread.protocol, thread.address)) announced += 1 else: - errors.append("%s://%s" % (thread.protocol, thread.ip)) + errors.append("%s://%s" % (thread.protocol, thread.address)) # Save peers num self.settings["peers"] = len(self.peers) self.saveSettings() - if len(errors) < len(SiteManager.TRACKERS): # Less errors than total tracker nums + if len(errors) < min(num, len(trackers)): # Less errors than total tracker nums self.log.debug( "Announced port %s to %s trackers in %.3fs, errors: %s, slow: %s" % (fileserver_port, announced, time.time() - s, errors, slow) ) else: - self.log.error("Announced to %s trackers in %.3fs, failed" % (announced, time.time() - s)) + self.log.error("Announce to %s trackers in %.3fs, failed" % (announced, time.time() - s)) - if not [peer for peer in self.peers.values() if peer.connection and peer.connection.connected]: - # If no connected peer yet then wait for connections - gevent.spawn_later(3, self.announcePex, need_num=10) # Spawn 3 secs later - else: # Else announce immediately - self.announcePex() + if pex: + if not [peer for peer in self.peers.values() if peer.connection and peer.connection.connected]: + # If no connected peer yet then wait for connections + gevent.spawn_later(3, self.announcePex, need_num=10) # Spawn 3 secs later + else: # Else announce immediately + self.announcePex() # Keep connections to get the updates (required for passive clients) def needConnections(self, num=3): diff --git a/src/Site/SiteManager.py b/src/Site/SiteManager.py index 1b61a5e3..18f9d278 100644 --- a/src/Site/SiteManager.py +++ b/src/Site/SiteManager.py @@ -6,26 +6,6 @@ import os from Plugin import PluginManager from Config import config -TRACKERS = [ - ("udp", "open.demonii.com", 1337), - # ("udp", "sugoi.pomf.se", 2710), - # ("udp", "tracker.coppersurfer.tk", 80), - ("udp", "tracker.leechers-paradise.org", 6969), - ("udp", "9.rarbg.com", 2710), - # ("udp", "www.eddie4.nl", 6969), - # ("udp", "trackr.sytes.net", 80), - # ("udp", "tracker4.piratux.com", 6969) - # ("http", "exodus.desync.com:80/announce", None), Off - ("http", "tracker.aletorrenty.pl:2710/announce", None), - # ("http", "torrent.gresille.org/announce", None), # Slow - # ("http", "announce.torrentsmd.com:6969/announce", None), # Off - # ("http", "i.bandito.org/announce", None), # Off - ("http", "retracker.telecom.kz/announce", None), - ("http", "torrent.gresille.org/announce", None), - -] - - @PluginManager.acceptPlugins class SiteManager(object): diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index 52f6a42a..a7aa844d 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -4,6 +4,8 @@ import os import mimetypes import json import cgi +import string +import random from Config import config from Site import SiteManager @@ -69,7 +71,7 @@ class UiRequest(object): return self.actionConsole() # Site media wrapper else: - if self.get.get("wrapper") == "False": + if self.get.get("wrapper_nonce"): return self.actionSiteMedia("/media" + path) # Only serve html files with frame else: body = self.actionWrapper(path) @@ -202,7 +204,6 @@ class UiRequest(object): else: # Bad url return False - def renderWrapper(self, site, path, inner_path, title, extra_headers): file_inner_path = inner_path if not file_inner_path: @@ -219,10 +220,12 @@ class UiRequest(object): body_style = "" meta_tags = "" + wrapper_nonce = self.getWrapperNonce() + if self.env.get("QUERY_STRING"): - query_string = "?" + self.env["QUERY_STRING"] + "&wrapper=False" + query_string = "?%s&wrapper_nonce=%s" % (self.env["QUERY_STRING"], wrapper_nonce) else: - query_string = "?wrapper=False" + query_string = "?wrapper_nonce=%s" % wrapper_nonce if self.isProxyRequest(): # Its a remote proxy request if self.env["REMOTE_ADDR"] == "127.0.0.1": # Local client, the server address also should be 127.0.0.1 @@ -260,6 +263,13 @@ class UiRequest(object): homepage=homepage ) + # Create a new wrapper nonce that allows to get one html file without the wrapper + def getWrapperNonce(self): + wrapper_nonce = ''.join( + random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(24) + ) + self.server.wrapper_nonces.append(wrapper_nonce) + return wrapper_nonce # Returns if media request allowed from that referer def isMediaRequestAllowed(self, site_address, referer): @@ -276,6 +286,14 @@ class UiRequest(object): match = re.match("/media/(?P
[A-Za-z0-9\._-]+)/(?P%s+ + """ % (title, message, json.dumps(details, indent=4)) # - Reload for eaiser developing - diff --git a/src/Ui/UiServer.py b/src/Ui/UiServer.py index 280304d6..797a6355 100644 --- a/src/Ui/UiServer.py +++ b/src/Ui/UiServer.py @@ -55,6 +55,7 @@ class UiServer: self.port = config.ui_port if self.ip == "*": self.ip = "" # Bind all + self.wrapper_nonces = [] self.sites = SiteManager.site_manager.list() self.log = logging.getLogger(__name__) diff --git a/src/Ui/media/all.css b/src/Ui/media/all.css index 29b470d8..a649c605 100644 --- a/src/Ui/media/all.css +++ b/src/Ui/media/all.css @@ -35,7 +35,7 @@ a { color: black } display: block; width: 80px; height: 80px; -webkit-transition: background-color 0.2s, box-shadow 0.5s; -moz-transition: background-color 0.2s, box-shadow 0.5s; -o-transition: background-color 0.2s, box-shadow 0.5s; -ms-transition: background-color 0.2s, box-shadow 0.5s; transition: background-color 0.2s, box-shadow 0.5s ; -webkit-transform: scale(0.6); -moz-transform: scale(0.6); -o-transform: scale(0.6); -ms-transform: scale(0.6); transform: scale(0.6) ; margin-left: -20px; margin-top: -20px; /* 2x size to prevent blur on anim */ /*box-shadow: inset 105px 260px 0px -200px rgba(0,0,0,0.1);*/ /* -webkit-box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1); -moz-box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1); -o-box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1); -ms-box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1); box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1) ; */ } -.fixbutton-text { pointer-events: none; position: absolute; z-index: 999; width: 40px; backface-visibility: hidden; -webkit-perspective: 1000px; -moz-perspective: 1000px; -o-perspective: 1000px; -ms-perspective: 1000px; perspective: 1000px ; line-height: 0px; padding-top: 20px } +.fixbutton-text { pointer-events: none; position: absolute; z-index: 999; width: 40px; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden ; -webkit-perspective: 1000px; -moz-perspective: 1000px; -o-perspective: 1000px; -ms-perspective: 1000px; perspective: 1000px ; line-height: 0px; padding-top: 20px } .fixbutton-burger { pointer-events: none; position: absolute; z-index: 999; width: 40px; opacity: 0; left: -20px; font-size: 40px; line-height: 0px; font-family: Verdana, sans-serif; margin-top: 17px } .fixbutton-bg:hover { background-color: #AF3BFF } .fixbutton-bg:active { background-color: #9E2FEA; top: 1px; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none } @@ -45,7 +45,7 @@ a { color: black } .notifications { position: absolute; top: 0px; right: 80px; display: inline-block; z-index: 999; white-space: nowrap } .notification { - position: relative; float: right; clear: both; margin: 10px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; overflow: hidden; backface-visibility: hidden; -webkit-perspective: 1000px; -moz-perspective: 1000px; -o-perspective: 1000px; -ms-perspective: 1000px; perspective: 1000px ; padding-bottom: 5px; + position: relative; float: right; clear: both; margin: 10px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; overflow: hidden; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden ; -webkit-perspective: 1000px; -moz-perspective: 1000px; -o-perspective: 1000px; -ms-perspective: 1000px; perspective: 1000px ; padding-bottom: 5px; color: #4F4F4F; font-family: 'Lucida Grande', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; /*border: 1px solid rgba(210, 206, 205, 0.2)*/ } .notification-icon { @@ -112,7 +112,7 @@ a { color: black } .flipper-container { width: 40px; height: 40px; position: absolute; top: 0%; left: 50%; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); -o-transform: translate3d(-50%, -50%, 0); -ms-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0) ; -webkit-perspective: 1200; -moz-perspective: 1200; -o-perspective: 1200; -ms-perspective: 1200; perspective: 1200 ; opacity: 0 } .flipper { position: relative; display: block; height: inherit; width: inherit; -webkit-animation: flip 1.2s infinite ease-in-out; -moz-animation: flip 1.2s infinite ease-in-out; -o-animation: flip 1.2s infinite ease-in-out; -ms-animation: flip 1.2s infinite ease-in-out; animation: flip 1.2s infinite ease-in-out ; -webkit-transform-style: preserve-3d; } .flipper .front, .flipper .back { - position: absolute; top: 0; left: 0; backface-visibility: hidden; /*transform-style: preserve-3d;*/ display: block; + position: absolute; top: 0; left: 0; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden ; /*transform-style: preserve-3d;*/ display: block; background-color: #d50000; height: 100%; width: 100%; /*outline: 1px solid transparent; /* FF AA fix */ } .flipper .back { background-color: white; z-index: 800; -webkit-transform: rotateY(-180deg) ; -moz-transform: rotateY(-180deg) ; -o-transform: rotateY(-180deg) ; -ms-transform: rotateY(-180deg) ; transform: rotateY(-180deg) } diff --git a/src/Ui/template/wrapper.html b/src/Ui/template/wrapper.html index b2046d3e..049fdc2b 100644 --- a/src/Ui/template/wrapper.html +++ b/src/Ui/template/wrapper.html @@ -15,6 +15,7 @@ // If we are inside iframe escape from it if (window.self !== window.top) window.open(window.location.toString(), "_top"); if (window.self !== window.top) window.stop(); +if (window.self !== window.top && document.execCommand) document.execCommand("Stop", false) // Dont allow site to load in a popup if (window.opener) document.write("Opener not allowed") @@ -51,10 +52,13 @@ if (window.opener && window.stop) window.stop() - +