import logging import urllib2 import re import time import gevent from Config import config from FileRequest import FileRequest from Site import SiteManager from Debug import Debug from Connection import ConnectionServer from util import UpnpPunch class FileServer(ConnectionServer): def __init__(self, ip=config.fileserver_ip, port=config.fileserver_port): ConnectionServer.__init__(self, ip, port, self.handleRequest) if config.ip_external: # Ip external definied in arguments self.port_opened = True SiteManager.peer_blacklist.append((config.ip_external, self.port)) # Add myself to peer blacklist else: self.port_opened = None # Is file server opened on router self.sites = {} # Handle request to fileserver def handleRequest(self, connection, message): if "params" in message: self.log.debug( "FileRequest: %s %s %s %s" % (str(connection), message["cmd"], message["params"].get("site"), message["params"].get("inner_path")) ) else: self.log.debug("FileRequest: %s %s" % (str(connection), message["cmd"])) req = FileRequest(self, connection) req.route(message["cmd"], message.get("req_id"), message.get("params")) # Reload the FileRequest class to prevent restarts in debug mode def reload(self): global FileRequest import imp FileRequest = imp.load_source("FileRequest", "src/File/FileRequest.py").FileRequest # Try to open the port using upnp def openport(self, port=None, check=True): if not port: port = self.port if self.port_opened: return True # Port already opened if check: # Check first if its already opened if self.testOpenport(port)["result"] is True: return True # Port already opened self.log.info("Trying to open port using UpnpPunch...") try: upnp_punch = UpnpPunch.open_port(self.port, 'ZeroNet') upnp_punch = True except Exception, err: self.log.error("UpnpPunch run error: %s" % Debug.formatException(err)) upnp_punch = False if upnp_punch and self.testOpenport(port)["result"] is True: return True self.log.info("Upnp mapping failed :( Please forward port %s on your router to your ipaddress" % port) return False # Test if the port is open def testOpenport(self, port=None): time.sleep(1) # Wait for port open if not port: port = self.port back = self.testOpenportPortchecker(port) if back["result"] is True: # Successful port check return back else: # Alternative port checker return self.testOpenportCanyouseeme(port) def testOpenportPortchecker(self, port=None): self.log.info("Checking port %s using portchecker.co..." % port) try: data = urllib2.urlopen("http://portchecker.co/check", "port=%s" % port, timeout=20.0).read() message = re.match('.*
(.*?)
', data, re.DOTALL).group(1) message = re.sub("<.*?>", "", message.replace("
", " ").replace(" ", " ").strip()) # Strip http tags except Exception, err: message = "Error: %s" % Debug.formatException(err) data = "" if "closed" in message or "Error" in message: self.log.info("[BAD :(] Port closed: %s" % message) if port == self.port: self.port_opened = False # Self port, update port_opened status match = re.match(".*targetIP.*?value=\"(.*?)\"", data, re.DOTALL) # Try find my external ip in message if match: # Found my ip in message config.ip_external = match.group(1) SiteManager.peer_blacklist.append((config.ip_external, self.port)) # Add myself to peer blacklist else: config.ip_external = False return {"result": False, "message": message} else: self.log.info("[OK :)] Port open: %s" % message) if port == self.port: # Self port, update port_opened status self.port_opened = True match = re.match(".*targetIP.*?value=\"(.*?)\"", data, re.DOTALL) # Try find my external ip in message if match: # Found my ip in message config.ip_external = match.group(1) SiteManager.peer_blacklist.append((config.ip_external, self.port)) # Add myself to peer blacklist else: config.ip_external = False return {"result": True, "message": message} def testOpenportCanyouseeme(self, port=None): self.log.info("Checking port %s using canyouseeme.org..." % port) try: data = urllib2.urlopen("http://www.canyouseeme.org/", "port=%s" % port, timeout=20.0).read() message = re.match('.*

(.*?)

', data, re.DOTALL).group(1) message = re.sub("<.*?>", "", message.replace("
", " ").replace(" ", " ")) # Strip http tags except Exception, err: message = "Error: %s" % Debug.formatException(err) if "Error" in message: self.log.info("[BAD :(] Port closed: %s" % message) if port == self.port: self.port_opened = False # Self port, update port_opened status match = re.match(".*?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)", message) # Try find my external ip in message if match: # Found my ip in message config.ip_external = match.group(1) SiteManager.peer_blacklist.append((config.ip_external, self.port)) # Add myself to peer blacklist else: config.ip_external = False return {"result": False, "message": message} else: self.log.info("[OK :)] Port open: %s" % message) if port == self.port: # Self port, update port_opened status self.port_opened = True match = re.match(".*?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)", message) # Try find my external ip in message if match: # Found my ip in message config.ip_external = match.group(1) SiteManager.peer_blacklist.append((config.ip_external, self.port)) # Add myself to peer blacklist else: config.ip_external = False return {"result": True, "message": message} # Set external ip without testing def setIpExternal(self, ip_external): logging.info("Setting external ip without testing: %s..." % ip_external) config.ip_external = ip_external self.port_opened = True # Check site file integrity def checkSite(self, site): if site.settings["serving"]: site.announce() # Announce site to tracker site.update() # Update site's content.json and download changed files if self.port_opened is False: # In passive mode keep 5 active peer connection to get the updates site.needConnections() # Check sites integrity def checkSites(self): if self.port_opened is None: # Test and open port if not tested yet self.openport() self.log.debug("Checking sites integrity..") for address, site in self.sites.items(): # Check sites integrity gevent.spawn(self.checkSite, site) # Check in new thread time.sleep(2) # Prevent too quick request site = None # Announce sites every 20 min def announceSites(self): import gc first_announce = True # First start while 1: # Sites healthcare if config.trackers_file: config.loadTrackersFile() for address, site in self.sites.items(): if site.settings["serving"]: 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: site.bad_files[inner_path] = 0 # Retry failed files if site.bad_files: site.retryBadFiles() site.cleanupPeers() # In passive mode keep 5 active peer connection to get the updates if self.port_opened is False: site.needConnections() time.sleep(2) # Prevent too quick request 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 if config.trackers_file: config.loadTrackersFile() 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() while 1: time.sleep(30) if time.time() - last_time > 60: # If taken more than 60 second then the computer was in sleep mode self.log.info( "Wakeup detected: time wrap from %s to %s (%s sleep seconds), acting like startup..." % (last_time, time.time(), time.time() - last_time) ) self.port_opened = None # Check if we still has the open port on router self.checkSites() last_time = time.time() # Bind and start serving sites def start(self, check_sites=True): self.sites = SiteManager.site_manager.list() self.log = logging.getLogger("FileServer") if config.debug: # Auto reload FileRequest on change from Debug import DebugReloader DebugReloader(self.reload) if check_sites: # Open port, Update sites, Check files integrity gevent.spawn(self.checkSites) thread_announce_sites = gevent.spawn(self.announceSites) thread_wakeup_watcher = gevent.spawn(self.wakeupWatcher) ConnectionServer.start(self) # thread_wakeup_watcher.kill(exception=Debug.Notify("Stopping FileServer")) # thread_announce_sites.kill(exception=Debug.Notify("Stopping FileServer")) self.log.debug("Stopped.")