summaryrefslogtreecommitdiffstats
path: root/module/network
diff options
context:
space:
mode:
Diffstat (limited to 'module/network')
-rw-r--r--module/network/Browser.py18
-rw-r--r--module/network/Bucket.py45
-rw-r--r--module/network/CookieJar.py66
-rw-r--r--module/network/HTTPChunk.py53
-rw-r--r--module/network/HTTPDownload.py66
-rw-r--r--module/network/HTTPRequest.py104
-rw-r--r--module/network/JsEngine.py256
-rw-r--r--module/network/RequestFactory.py45
-rw-r--r--module/network/XDCCRequest.py84
-rw-r--r--module/network/__init__.py2
10 files changed, 455 insertions, 284 deletions
diff --git a/module/network/Browser.py b/module/network/Browser.py
index d68a23687..89341ff25 100644
--- a/module/network/Browser.py
+++ b/module/network/Browser.py
@@ -1,10 +1,9 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
from logging import getLogger
-from HTTPRequest import HTTPRequest
-from HTTPDownload import HTTPDownload
+from pyload.network.HTTPRequest import HTTPRequest
+from pyload.network.HTTPDownload import HTTPDownload
class Browser(object):
@@ -131,16 +130,3 @@ class Browser(object):
del self.dl
if hasattr(self, "cj"):
del self.cj
-
-if __name__ == "__main__":
- browser = Browser()#proxies={"socks5": "localhost:5000"})
- ip = "http://www.whatismyip.com/automation/n09230945.asp"
- #browser.getPage("http://google.com/search?q=bar")
- #browser.getPage("https://encrypted.google.com/")
- #print browser.getPage(ip)
- #print browser.getRedirectLocation("http://google.com/")
- #browser.getPage("https://encrypted.google.com/")
- #browser.getPage("http://google.com/search?q=bar")
-
- browser.httpDownload("http://speedtest.netcologne.de/test_10mb.bin", "test_10mb.bin")
-
diff --git a/module/network/Bucket.py b/module/network/Bucket.py
index 69da277ae..408a1e240 100644
--- a/module/network/Bucket.py
+++ b/module/network/Bucket.py
@@ -1,45 +1,37 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
from time import time
from threading import Lock
-class Bucket:
+MIN_RATE = 10240 #: 10kb minimum rate
+
+
+class Bucket(object):
+
def __init__(self):
- self.rate = 0
- self.tokens = 0
+ self.rate = 0 # bytes per second, maximum targeted throughput
+ self.tokens = 0
self.timestamp = time()
- self.lock = Lock()
+ self.lock = Lock()
+
def __nonzero__(self):
- return False if self.rate < 10240 else True
+ return False if self.rate < MIN_RATE else True
+
def setRate(self, rate):
self.lock.acquire()
self.rate = int(rate)
self.lock.release()
+
def consumed(self, amount):
- """ return time the process have to sleep, after consumed specified amount """
- if self.rate < 10240: return 0 #min. 10kb, may become unresponsive otherwise
- self.lock.acquire()
+ """ return the time the process has to sleep, after it consumed a specified amount """
+ if self.rate < MIN_RATE:
+ return 0 #: May become unresponsive otherwise
+ self.lock.acquire()
self.calc_tokens()
self.tokens -= amount
@@ -47,15 +39,14 @@ class Bucket:
time = -self.tokens/float(self.rate)
else:
time = 0
-
self.lock.release()
return time
+
def calc_tokens(self):
if self.tokens < self.rate:
now = time()
delta = self.rate * (now - self.timestamp)
self.tokens = min(self.rate, self.tokens + delta)
self.timestamp = now
-
diff --git a/module/network/CookieJar.py b/module/network/CookieJar.py
index c05812334..a2b302776 100644
--- a/module/network/CookieJar.py
+++ b/module/network/CookieJar.py
@@ -1,50 +1,42 @@
# -*- coding: utf-8 -*-
+# @author: RaNaN
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
+import Cookie
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
+from datetime import datetime, timedelta
+from time import time
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
- @author: mkaay, RaNaN
-"""
+# monkey patch for 32 bit systems
+def _getdate(future=0, weekdayname=Cookie._weekdayname, monthname=Cookie._monthname):
+ dt = datetime.now() + timedelta(seconds=int(future))
+ return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \
+ (weekdayname[dt.weekday()], dt.day, monthname[dt.month], dt.year, dt.hour, dt.minute, dt.second)
+
+Cookie._getdate = _getdate
-from time import time
-class CookieJar():
- def __init__(self, pluginname, account=None):
- self.cookies = {}
- self.plugin = pluginname
- self.account = account
+class CookieJar(Cookie.SimpleCookie):
- def addCookies(self, clist):
- for c in clist:
- name = c.split("\t")[5]
- self.cookies[name] = c
+ def getCookie(self, name):
+ return self[name].value
- def getCookies(self):
- return self.cookies.values()
+ def setCookie(self, domain, name, value, path="/", exp=None, secure="FALSE"):
+ self[name] = value
+ self[name]["domain"] = domain
+ self[name]["path"] = path
- def parseCookie(self, name):
- if name in self.cookies:
- return self.cookies[name].split("\t")[6]
+ # Value of expires should be integer if possible
+ # otherwise the cookie won't be used
+ if not exp:
+ expires = time() + 3600 * 24 * 180
else:
- return None
-
- def getCookie(self, name):
- return self.parseCookie(name)
+ try:
+ expires = int(exp)
+ except ValueError:
+ expires = exp
- def setCookie(self, domain, name, value, path="/", exp=time()+3600*24*180):
- s = ".%s TRUE %s FALSE %s %s %s" % (domain, path, exp, name, value)
- self.cookies[name] = s
+ self[name]["expires"] = expires
- def clear(self):
- self.cookies = {}
+ if secure == "TRUE":
+ self[name]["secure"] = secure
diff --git a/module/network/HTTPChunk.py b/module/network/HTTPChunk.py
index b637aef32..41752b940 100644
--- a/module/network/HTTPChunk.py
+++ b/module/network/HTTPChunk.py
@@ -1,36 +1,22 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
+
from os import remove, stat, fsync
from os.path import exists
from time import sleep
from re import search
-from module.utils import fs_encode
+from pyload.utils import fs_encode
import codecs
import pycurl
+import urllib
-from HTTPRequest import HTTPRequest
+from pyload.network.HTTPRequest import HTTPRequest
class WrongFormat(Exception):
pass
-class ChunkInfo():
+class ChunkInfo(object):
def __init__(self, name):
self.name = unicode(name)
self.size = 0
@@ -208,7 +194,7 @@ class HTTPChunk(HTTPRequest):
# as first chunk, we will parse the headers
if not self.range and self.header.endswith("\r\n\r\n"):
self.parseHeader()
- elif not self.range and buf.startswith("150") and "data connection" in buf: #ftp file size parsing
+ elif not self.range and buf.startswith("150") and "data connection" in buf.lower(): #: ftp file size parsing
size = search(r"(\d+) bytes", buf)
if size:
self.p.size = int(size.group(1))
@@ -253,13 +239,23 @@ class HTTPChunk(HTTPRequest):
"""parse data from recieved header"""
for orgline in self.decodeResponse(self.header).splitlines():
line = orgline.strip().lower()
+
if line.startswith("accept-ranges") and "bytes" in line:
self.p.chunkSupport = True
- if line.startswith("content-disposition") and "filename=" in line:
- name = orgline.partition("filename=")[2]
- name = name.replace('"', "").replace("'", "").replace(";", "").strip()
+ if line.startswith("content-disposition") and ("filename=" in line or "filename*=" in line):
+ if "filename*=" in line:
+ # extended version according to RFC 6266 and RFC 5987.
+ encoding = line.partition("*=")[2].partition("''")[0]
+ name = orgline.partition("''")[2]
+ name = urllib.unquote(str(name)).decode(charEnc(encoding))
+ else:
+ # basic version, US-ASCII only
+ name = orgline.partition("filename=")[2]
+
+ name = name.replace('"', "").replace("'", "").replace(";", "").replace("/", "_").strip()
self.p.nameDisposition = name
+
self.log.debug("Content-Disposition: %s" % name)
if not self.resume and line.startswith("content-length"):
@@ -269,7 +265,7 @@ class HTTPChunk(HTTPRequest):
def stop(self):
"""The download will not proceed after next call of writeBody"""
- self.range = [0,0]
+ self.range = [0, 0]
self.size = 0
def resetRange(self):
@@ -290,4 +286,9 @@ class HTTPChunk(HTTPRequest):
""" closes everything, unusable after this """
if self.fp: self.fp.close()
self.c.close()
- if hasattr(self, "p"): del self.p \ No newline at end of file
+ if hasattr(self, "p"): del self.p
+
+
+def charEnc(enc):
+ return {'utf-8' : "utf_8",
+ 'iso-8859-1': "latin_1"}.get(enc, "unknown")
diff --git a/module/network/HTTPDownload.py b/module/network/HTTPDownload.py
index fe8075539..3b2bf26ca 100644
--- a/module/network/HTTPDownload.py
+++ b/module/network/HTTPDownload.py
@@ -1,21 +1,5 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
from os import remove, fsync
from os.path import dirname
@@ -25,17 +9,18 @@ from logging import getLogger
import pycurl
-from HTTPChunk import ChunkInfo, HTTPChunk
-from HTTPRequest import BadHeader
+from pyload.network.HTTPChunk import ChunkInfo, HTTPChunk
+from pyload.network.HTTPRequest import BadHeader
-from module.plugins.Plugin import Abort
-from module.utils import save_join, fs_encode
+from pyload.plugin.Plugin import Abort
+from pyload.utils import safe_join, fs_encode
-class HTTPDownload():
+
+class HTTPDownload(object):
""" loads a url http + ftp """
def __init__(self, url, filename, get={}, post={}, referer=None, cj=None, bucket=None,
- options={}, progressNotify=None, disposition=False):
+ options={}, progress=None, disposition=False):
self.url = url
self.filename = filename #complete file destination, not only name
self.get = get
@@ -63,7 +48,7 @@ class HTTPDownload():
except IOError:
self.info = ChunkInfo(filename)
- self.chunkSupport = None
+ self.chunkSupport = True
self.m = pycurl.CurlMulti()
#needed for speed calculation
@@ -71,7 +56,7 @@ class HTTPDownload():
self.speeds = []
self.lastSpeeds = [0, 0]
- self.progressNotify = progressNotify
+ self.progress = progress
@property
def speed(self):
@@ -114,7 +99,7 @@ class HTTPDownload():
fo.close()
if self.nameDisposition and self.disposition:
- self.filename = save_join(dirname(self.filename), self.nameDisposition)
+ self.filename = safe_join(dirname(self.filename), self.nameDisposition)
move(init, fs_encode(self.filename))
self.info.remove() #remove info file
@@ -130,7 +115,7 @@ class HTTPDownload():
except pycurl.error, e:
#code 33 - no resume
code = e.args[0]
- if code == 33:
+ if resume is True and code == 33:
# try again without resume
self.log.debug("Errno 33 -> Restart without resume")
@@ -151,6 +136,7 @@ class HTTPDownload():
if not resume:
self.info.clear()
self.info.addChunk("%s.chunk0" % self.filename, (0, 0)) #create an initial entry
+ self.info.save()
self.chunks = []
@@ -164,8 +150,8 @@ class HTTPDownload():
chunksDone = set() # list of curl handles that are finished
chunksCreated = False
done = False
- if self.info.getCount() > 1: # This is a resume, if we were chunked originally assume still can
- self.chunkSupport = True
+ if self.info.getCount() is 0: # This is a resume, if we were chunked originally assume still can
+ self.chunkSupport = False
while 1:
#need to create chunks
@@ -284,9 +270,9 @@ class HTTPDownload():
self.updateProgress()
if self.abort:
- raise Abort()
+ raise Abort
- #sleep(0.003) #supress busy waiting - limits dl speed to (1 / x) * buffersize
+ #sleep(0.003) #supress busy waiting - limits dl speed to (1 / x) * buffersize
self.m.select(1)
for chunk in self.chunks:
@@ -295,8 +281,8 @@ class HTTPDownload():
self._copyChunks()
def updateProgress(self):
- if self.progressNotify:
- self.progressNotify(self.percent)
+ if self.progress:
+ self.progress(self.percent)
def findChunk(self, handle):
""" linear search to find a chunk (should be ok since chunk size is usually low) """
@@ -324,17 +310,3 @@ class HTTPDownload():
del self.cj
if hasattr(self, "info"):
del self.info
-
-if __name__ == "__main__":
- url = "http://speedtest.netcologne.de/test_100mb.bin"
-
- from Bucket import Bucket
-
- bucket = Bucket()
- bucket.setRate(200 * 1024)
- bucket = None
-
- print "starting"
-
- dwnld = HTTPDownload(url, "test_100mb.bin", bucket=bucket)
- dwnld.download(chunks=3, resume=True)
diff --git a/module/network/HTTPRequest.py b/module/network/HTTPRequest.py
index 4747d937f..eac03a365 100644
--- a/module/network/HTTPRequest.py
+++ b/module/network/HTTPRequest.py
@@ -1,21 +1,7 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# @author: RaNaN
+
+from __future__ import with_statement
import pycurl
@@ -25,15 +11,17 @@ from httplib import responses
from logging import getLogger
from cStringIO import StringIO
-from module.plugins.Plugin import Abort
+from pyload.plugin.Plugin import Abort, Fail
+
+from pyload.utils import encode
+
def myquote(url):
- return quote(url.encode('utf_8') if isinstance(url, unicode) else url, safe="%/:=&?~#+!$,;'@()*[]")
-
+ return quote(encode(url), safe="%/:=&?~#+!$,;'@()*[]")
+
def myurlencode(data):
data = dict(data)
- return urlencode(dict((x.encode('utf_8') if isinstance(x, unicode) else x, \
- y.encode('utf_8') if isinstance(y, unicode) else y ) for x, y in data.iteritems()))
+ return urlencode(dict((encode(x), encode(y)) for x, y in data.iteritems()))
bad_headers = range(400, 404) + range(405, 418) + range(500, 506)
@@ -44,7 +32,7 @@ class BadHeader(Exception):
self.content = content
-class HTTPRequest():
+class HTTPRequest(object):
def __init__(self, cookies=None, options=None):
self.c = pycurl.Curl()
self.rep = StringIO()
@@ -79,8 +67,9 @@ class HTTPRequest():
if hasattr(pycurl, "AUTOREFERER"):
self.c.setopt(pycurl.AUTOREFERER, 1)
self.c.setopt(pycurl.SSL_VERIFYPEER, 0)
- self.c.setopt(pycurl.LOW_SPEED_TIME, 30)
+ self.c.setopt(pycurl.LOW_SPEED_TIME, 60)
self.c.setopt(pycurl.LOW_SPEED_LIMIT, 5)
+ self.c.setopt(pycurl.USE_SSL, pycurl.CURLUSESSL_TRY)
#self.c.setopt(pycurl.VERBOSE, 1)
@@ -89,8 +78,8 @@ class HTTPRequest():
if pycurl.version_info()[7]:
self.c.setopt(pycurl.ENCODING, "gzip, deflate")
self.c.setopt(pycurl.HTTPHEADER, ["Accept: */*",
- "Accept-Language: en-US,en",
- "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7",
+ "Accept-Language: en-US, en",
+ "Accept-Charset: ISO-8859-1, utf-8;q=0.7,*;q=0.7",
"Connection: keep-alive",
"Keep-Alive: 300",
"Expect:"])
@@ -167,7 +156,7 @@ class HTTPRequest():
self.c.setopt(pycurl.POSTFIELDS, post)
else:
- post = [(x, y.encode('utf8') if type(y) == unicode else y ) for x, y in post.iteritems()]
+ post = [(x, encode(y)) for x, y in post.iteritems()]
self.c.setopt(pycurl.HTTPPOST, post)
else:
self.c.setopt(pycurl.POST, 0)
@@ -181,7 +170,7 @@ class HTTPRequest():
self.getCookies()
- def load(self, url, get={}, post={}, referer=True, cookies=True, just_header=False, multipart=False, decode=False):
+ def load(self, url, get={}, post={}, referer=True, cookies=True, just_header=False, multipart=False, decode=False, follow_location=True, save_cookies=True):
""" load and returns a given page """
self.setRequestContext(url, get, post, referer, cookies, multipart)
@@ -190,24 +179,32 @@ class HTTPRequest():
self.c.setopt(pycurl.HTTPHEADER, self.headers)
- if just_header:
+ if post:
+ self.c.setopt(pycurl.POST, 1)
+ else:
+ self.c.setopt(pycurl.HTTPGET, 1)
+
+ if not follow_location:
self.c.setopt(pycurl.FOLLOWLOCATION, 0)
+
+ if just_header:
self.c.setopt(pycurl.NOBODY, 1)
- self.c.perform()
- rep = self.header
+ self.c.perform()
+ rep = self.header if just_header else self.getResponse()
+
+ if not follow_location:
self.c.setopt(pycurl.FOLLOWLOCATION, 1)
- self.c.setopt(pycurl.NOBODY, 0)
- else:
- self.c.perform()
- rep = self.getResponse()
+ if just_header:
+ self.c.setopt(pycurl.NOBODY, 0)
self.c.setopt(pycurl.POSTFIELDS, "")
self.lastEffectiveURL = self.c.getinfo(pycurl.EFFECTIVE_URL)
self.code = self.verifyHeader()
- self.addCookies()
+ if save_cookies:
+ self.addCookies()
if decode:
rep = self.decodeResponse(rep)
@@ -228,11 +225,13 @@ class HTTPRequest():
def getResponse(self):
""" retrieve response from string io """
- if self.rep is None: return ""
- value = self.rep.getvalue()
- self.rep.close()
- self.rep = StringIO()
- return value
+ if self.rep is None:
+ return ""
+ else:
+ value = self.rep.getvalue()
+ self.rep.close()
+ self.rep = StringIO()
+ return value
def decodeResponse(self, rep):
""" decode with correct encoding, relies on header """
@@ -255,7 +254,7 @@ class HTTPRequest():
#self.log.debug("Decoded %s" % encoding )
if lookup(encoding).name == 'utf-8' and rep.startswith(BOM_UTF8):
encoding = 'utf-8-sig'
-
+
decoder = getincrementaldecoder(encoding)("replace")
rep = decoder.decode(rep, True)
@@ -263,6 +262,7 @@ class HTTPRequest():
except LookupError:
self.log.debug("No Decoder foung for %s" % encoding)
+
except Exception:
self.log.debug("Error when decoding string from %s." % encoding)
@@ -272,13 +272,15 @@ class HTTPRequest():
""" writes response """
if self.rep.tell() > 1000000 or self.abort:
rep = self.getResponse()
- if self.abort: raise Abort()
- f = open("response.dump", "wb")
- f.write(rep)
- f.close()
- raise Exception("Loaded Url exceeded limit")
- self.rep.write(buf)
+ if self.abort:
+ raise Abort
+
+ with open("response.dump", "wb") as f:
+ f.write(rep)
+ raise Fail("Loaded url exceeded size limit")
+ else:
+ self.rep.write(buf)
def writeHeader(self, buf):
""" writes header """
@@ -298,9 +300,3 @@ class HTTPRequest():
if hasattr(self, "c"):
self.c.close()
del self.c
-
-if __name__ == "__main__":
- url = "http://pyload.org"
- c = HTTPRequest()
- print c.load(url)
-
diff --git a/module/network/JsEngine.py b/module/network/JsEngine.py
new file mode 100644
index 000000000..2e98fa37d
--- /dev/null
+++ b/module/network/JsEngine.py
@@ -0,0 +1,256 @@
+# -*- coding: utf-8 -*-
+# @author: vuolter
+
+import subprocess
+import sys
+
+from os import path
+from urllib import quote
+
+from pyload.utils import encode, decode, uniqify
+
+
+class JsEngine(object):
+ """ JS Engine superclass """
+
+ def __init__(self, core, engine=None):
+ self.core = core
+ self.engine = None #: Engine Instance
+
+ if not engine:
+ engine = self.core.config.get("general", "jsengine")
+
+ if engine != "auto" and self.set(engine) is False:
+ engine = "auto"
+ self.core.log.warning("JS Engine set to \"auto\" for safely")
+
+ if engine == "auto":
+ for E in self.find():
+ if self.set(E) is True:
+ break
+ else:
+ self.core.log.error("No JS Engine available")
+
+
+ @classmethod
+ def find(cls):
+ """ Check if there is any engine available """
+ return [E for E in ENGINES if E.find()]
+
+
+ def get(self, engine=None):
+ """ Convert engine name (string) to relative JSE class (AbstractEngine extended) """
+ if not engine:
+ JSE = self.engine
+
+ elif isinstance(engine, basestring):
+ engine_name = engine.lower()
+ for E in ENGINES:
+ if E.__name == engine_name: #: doesn't check if E(NGINE) is available, just convert string to class
+ JSE = E
+ break
+ else:
+ raise ValueError("JSE")
+
+ elif issubclass(engine, AbstractEngine):
+ JSE = engine
+
+ else:
+ raise TypeError("engine")
+
+ return JSE
+
+
+ def set(self, engine):
+ """ Set engine name (string) or JSE class (AbstractEngine extended) as default engine """
+ if isinstance(engine, basestring):
+ return self.set(self.get(engine))
+
+ elif issubclass(engine, AbstractEngine) and engine.find():
+ self.engine = engine
+ return True
+
+ else:
+ return False
+
+
+ def eval(self, script, engine=None): #: engine can be a jse name """string""" or an AbstractEngine """class"""
+ JSE = self.get(engine)
+
+ if not JSE:
+ raise TypeError("engine")
+
+ script = encode(script)
+
+ out, err = JSE.eval(script)
+
+ results = [out]
+
+ if self.core.config.get("general", "debug"):
+ if err:
+ self.core.log.debug(JSE.__name + ":", err)
+
+ engines = self.find()
+ engines.remove(JSE)
+ for E in engines:
+ out, err = E.eval(script)
+ res = err or out
+ self.core.log.debug(E.__name + ":", res)
+ results.append(res)
+
+ if len(results) > 1 and len(uniqify(results)) > 1:
+ self.core.log.warning("JS output of two or more engines mismatch")
+
+ return results[0]
+
+
+class AbstractEngine(object):
+ """ JSE base class """
+
+ __name = ""
+
+
+ def __init__(self):
+ self.setup()
+ self.available = self.find()
+
+
+ def setup(self):
+ pass
+
+
+ @classmethod
+ def find(cls):
+ """ Check if the engine is available """
+ try:
+ __import__(cls.__name)
+ except Exception:
+ try:
+ out, err = cls().eval("print(23+19)")
+ except Exception:
+ res = False
+ else:
+ res = out == "42"
+ else:
+ res = True
+
+ return res
+
+
+ def _eval(args):
+ if not self.available:
+ return None, "JS Engine \"%s\" not found" % self.__name
+
+ try:
+ p = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ bufsize=-1)
+ return map(lambda x: x.strip(), p.communicate())
+ except Exception, e:
+ return None, e
+
+
+ def eval(script):
+ raise NotImplementedError
+
+
+class Pyv8Engine(AbstractEngine):
+
+ __name = "pyv8"
+
+
+ def eval(self, script):
+ if not self.available:
+ return None, "JS Engine \"%s\" not found" % self.__name
+
+ try:
+ rt = PyV8.JSContext()
+ rt.enter()
+ res = rt.eval(script), None #@TODO: parse stderr
+ except Exception, e:
+ res = None, e
+
+ return res
+
+
+class CommonEngine(AbstractEngine):
+
+ __name = "js"
+
+
+ def setup(self):
+ subprocess.Popen(["js", "-v"], bufsize=-1).communicate()
+
+
+ def eval(self, script):
+ script = "print(eval(unescape('%s')))" % quote(script)
+ args = ["js", "-e", script]
+ return self._eval(args)
+
+
+class NodeEngine(AbstractEngine):
+
+ __name = "nodejs"
+
+
+ def setup(self):
+ subprocess.Popen(["node", "-v"], bufsize=-1).communicate()
+
+
+ def eval(self, script):
+ script = "console.log(eval(unescape('%s')))" % quote(script)
+ args = ["node", "-e", script]
+ return self._eval(args)
+
+
+class RhinoEngine(AbstractEngine):
+
+ __name = "rhino"
+
+
+ def setup(self):
+ jspath = [
+ "/usr/share/java*/js.jar",
+ "js.jar",
+ path.join(pypath, "js.jar")
+ ]
+ for p in jspath:
+ if path.exists(p):
+ self.path = p
+ break
+ else:
+ self.path = ""
+
+
+ def eval(self, script):
+ script = "print(eval(unescape('%s')))" % quote(script)
+ args = ["java", "-cp", self.path, "org.mozilla.javascript.tools.shell.Main", "-e", script]
+ res = decode(self._eval(args))
+ try:
+ return res.encode("ISO-8859-1")
+ finally:
+ return res
+
+
+class JscEngine(AbstractEngine):
+
+ __name = "javascriptcore"
+
+
+ def setup(self):
+ jspath = "/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc"
+ self.path = jspath if path.exists(jspath) else ""
+
+
+ def eval(self, script):
+ script = "print(eval(unescape('%s')))" % quote(script)
+ args = [self.path, "-e", script]
+ return self._eval(args)
+
+
+#@NOTE: Priority ordered
+ENGINES = [CommonEngine, Pyv8Engine, NodeEngine, RhinoEngine]
+
+if sys.platform == "darwin":
+ ENGINES.insert(JscEngine)
diff --git a/module/network/RequestFactory.py b/module/network/RequestFactory.py
index 5b1528281..579eafea7 100644
--- a/module/network/RequestFactory.py
+++ b/module/network/RequestFactory.py
@@ -1,32 +1,16 @@
# -*- coding: utf-8 -*-
-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: mkaay, RaNaN
-"""
+# @author: RaNaN, mkaay
from threading import Lock
-from Browser import Browser
-from Bucket import Bucket
-from HTTPRequest import HTTPRequest
-from CookieJar import CookieJar
+from pyload.network.Browser import Browser
+from pyload.network.Bucket import Bucket
+from pyload.network.HTTPRequest import HTTPRequest
+from pyload.network.CookieJar import CookieJar
-from XDCCRequest import XDCCRequest
+from pyload.network.XDCCRequest import XDCCRequest
-class RequestFactory():
+class RequestFactory(object):
def __init__(self, core):
self.lock = Lock()
self.core = core
@@ -62,12 +46,23 @@ class RequestFactory():
def getURL(self, *args, **kwargs):
""" see HTTPRequest for argument list """
- h = HTTPRequest(None, self.getOptions())
+ cj = None
+
+ if 'cookies' in kwargs:
+ if isinstance(kwargs['cookies'], CookieJar):
+ cj = kwargs['cookies']
+ elif isinstance(kwargs['cookies'], list):
+ cj = CookieJar(None)
+ for cookie in kwargs['cookies']:
+ if isinstance(cookie, tuple) and len(cookie) == 3:
+ cj.setCookie(*cookie)
+
+ h = HTTPRequest(cj, self.getOptions())
try:
rep = h.load(*args, **kwargs)
finally:
h.close()
-
+
return rep
def getCookieJar(self, pluginName, account=None):
diff --git a/module/network/XDCCRequest.py b/module/network/XDCCRequest.py
index f03798c17..c49f418c4 100644
--- a/module/network/XDCCRequest.py
+++ b/module/network/XDCCRequest.py
@@ -1,21 +1,5 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: jeix
-"""
+# @author: jeix
import socket
import re
@@ -28,22 +12,21 @@ from time import time
import struct
from select import select
-from module.plugins.Plugin import Abort
+from pyload.plugin.Plugin import Abort
-class XDCCRequest():
+class XDCCRequest(object):
def __init__(self, timeout=30, proxies={}):
-
+
self.proxies = proxies
self.timeout = timeout
-
+
self.filesize = 0
self.recv = 0
self.speed = 0
-
+
self.abort = False
-
def createSocket(self):
# proxytype = None
# proxy = None
@@ -60,78 +43,77 @@ class XDCCRequest():
# else:
# sock = socket.socket()
# return sock
-
+
return socket.socket()
-
- def download(self, ip, port, filename, irc, progressNotify=None):
+
+ def download(self, ip, port, filename, irc, progress=None):
ircbuffer = ""
lastUpdate = time()
cumRecvLen = 0
-
+
dccsock = self.createSocket()
-
+
dccsock.settimeout(self.timeout)
dccsock.connect((ip, port))
-
+
if exists(filename):
i = 0
nameParts = filename.rpartition(".")
while True:
newfilename = "%s-%d%s%s" % (nameParts[0], i, nameParts[1], nameParts[2])
i += 1
-
+
if not exists(newfilename):
filename = newfilename
break
-
+
fh = open(filename, "wb")
-
+
# recv loop for dcc socket
while True:
if self.abort:
dccsock.close()
fh.close()
remove(filename)
- raise Abort()
-
+ raise Abort
+
self._keepAlive(irc, ircbuffer)
-
+
data = dccsock.recv(4096)
dataLen = len(data)
self.recv += dataLen
-
+
cumRecvLen += dataLen
-
+
now = time()
timespan = now - lastUpdate
- if timespan > 1:
+ if timespan > 1:
self.speed = cumRecvLen / timespan
cumRecvLen = 0
lastUpdate = now
-
- if progressNotify:
- progressNotify(self.percent)
-
-
+
+ if progress:
+ progress(self.percent)
+
if not data:
break
-
+
fh.write(data)
-
+
# acknowledge data by sending number of recceived bytes
dccsock.send(struct.pack('!I', self.recv))
-
+
dccsock.close()
fh.close()
-
+
return filename
-
- def _keepAlive(self, sock, readbuffer):
+
+ def _keepAlive(self, sock, *readbuffer):
fdset = select([sock], [], [], 0)
if sock not in fdset[0]:
return
-
+
readbuffer += sock.recv(1024)
temp = readbuffer.split("\n")
readbuffer = temp.pop()
@@ -144,7 +126,7 @@ class XDCCRequest():
def abortDownloads(self):
self.abort = True
-
+
@property
def size(self):
return self.filesize
diff --git a/module/network/__init__.py b/module/network/__init__.py
index 8b1378917..40a96afc6 100644
--- a/module/network/__init__.py
+++ b/module/network/__init__.py
@@ -1 +1 @@
-
+# -*- coding: utf-8 -*-