import os, logging, urllib2, urllib, re, time
import gevent, msgpack
import zmq.green as zmq
from Config import config
from FileRequest import FileRequest
from Site import SiteManager
class FileServer:
def __init__(self):
self.ip = config.fileserver_ip
self.port = config.fileserver_port
self.log = logging.getLogger(__name__)
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 = SiteManager.list()
# Handle request to fileserver
def handleRequest(self, msg):
if "params" in msg:
self.log.debug("FileRequest: %s %s %s" % (msg["cmd"], msg["params"].get("site"), msg["params"].get("inner_path")))
else:
self.log.debug("FileRequest: %s" % msg["cmd"])
req = FileRequest(self)
req.route(msg["cmd"], msg.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"] == True:
return True # Port already opened
if config.upnpc: # If we have upnpc util, try to use it to puch port on our router
self.log.info("Try to open port using upnpc...")
try:
exit = os.system("%s -e ZeroNet -r %s tcp" % (config.upnpc, self.port))
if exit == 0:
upnpc_success = True
else:
upnpc_success = False
except Exception, err:
self.log.error("Upnpc run error: %s" % err)
upnpc_success = False
if upnpc_success and self.testOpenport(port)["result"] == 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
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" % 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
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
# Check sites integrity
def checkSites(self):
if self.port_opened == 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
# Announce sites every 10 min
def announceSites(self):
while 1:
time.sleep(10*60) # Announce sites every 10 min
for address, site in self.sites.items():
if site.settings["serving"]:
site.announce() # Announce site to tracker
time.sleep(2) # Prevent too quick request
# Bind and start serving sites
def start(self, check_sites = True):
self.log = logging.getLogger(__name__)
if config.debug:
# Auto reload FileRequest on change
from Debug import DebugReloader
DebugReloader(self.reload)
self.context = zmq.Context()
socket = self.context.socket(zmq.REP)
self.socket = socket
self.socket.setsockopt(zmq.RCVTIMEO, 5000) # Wait for data receive
self.log.info("Binding to tcp://%s:%s" % (self.ip, self.port))
try:
self.socket.bind('tcp://%s:%s' % (self.ip, self.port))
except Exception, err:
self.log.error("Can't bind, FileServer must be running already")
return
if check_sites: # Open port, Update sites, Check files integrity
gevent.spawn(self.checkSites)
gevent.spawn(self.announceSites)
while True:
try:
ret = {}
req = msgpack.unpackb(socket.recv())
self.handleRequest(req)
except Exception, err:
self.log.error(err)
self.socket.send(msgpack.packb({"error": "%s" % err}, use_bin_type=True))
if config.debug: # Raise exception
import sys
sys.excepthook(*sys.exc_info())