summaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
authorGravatar Nitzo <nitzo2001@yahoo.com> 2016-02-27 03:50:03 +0100
committerGravatar Nitzo <nitzo2001@yahoo.com> 2016-02-27 03:50:03 +0100
commit93c0627b1d05212cc4f08d4518309f977dfdff90 (patch)
tree4c6933ee33e114a2dcfeab0b7e086509a0cf0d68 /module
parent[Plugin] Load(): allow referer as string parameter (diff)
downloadpyload-93c0627b1d05212cc4f08d4518309f977dfdff90.tar.xz
[ReCaptcha] First support for NoCaptcha ReCaptcha (Thanks @Arno-Nymous)
Diffstat (limited to 'module')
-rw-r--r--module/plugins/captcha/ReCaptcha.py241
1 files changed, 151 insertions, 90 deletions
diff --git a/module/plugins/captcha/ReCaptcha.py b/module/plugins/captcha/ReCaptcha.py
index 535525761..4b5793b95 100644
--- a/module/plugins/captcha/ReCaptcha.py
+++ b/module/plugins/captcha/ReCaptcha.py
@@ -1,25 +1,36 @@
# -*- coding: utf-8 -*-
-import base64
-import random
+import os
import re
-import time
+import urllib
import urlparse
+from StringIO import StringIO
from module.plugins.internal.CaptchaService import CaptchaService
+try:
+ from PIL import _imaging
+ from PIL import Image
+ from PIL import ImageDraw
+ from PIL import ImageFont
+except ImportError:
+ import _imaging
+ import Image
+ import ImageDraw
+ import ImageFont
+
class ReCaptcha(CaptchaService):
- __name__ = "ReCaptcha"
- __type__ = "captcha"
- __version__ = "0.21"
- __status__ = "testing"
+ __name__ = 'ReCaptcha'
+ __type__ = 'captcha'
+ __version__ = '0.22'
+ __status__ = 'testing'
- __description__ = """ReCaptcha captcha service plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org"),
- ("Walter Purcaro", "vuolter@gmail.com"),
- ("zapp-brannigan", "fuerst.reinje@web.de")]
+ __description__ = 'ReCaptcha captcha service plugin'
+ __license__ = 'GPLv3'
+ __authors__ = [('Walter Purcaro', 'vuolter@gmail.com' ),
+ ('zapp-brannigan', 'fuerst.reinje@web.de'),
+ ('Arno-Nymous' , None )]
KEY_V1_PATTERN = r'(?:recaptcha(?:/api|\.net)/(?:challenge|noscript)\?k=|Recaptcha\.create\s*\(\s*["\'])([\w\-]+)'
@@ -57,7 +68,7 @@ class ReCaptcha(CaptchaService):
challenge = re.search("challenge : '(.+?)',", html).group(1)
server = re.search("server : '(.+?)',", html).group(1)
- except AttributeError:
+ except (AttributeError, IndexError):
self.fail(_("ReCaptcha challenge pattern not found"))
self.log_debug("Challenge: %s" % challenge)
@@ -76,7 +87,7 @@ class ReCaptcha(CaptchaService):
try:
challenge = re.search('\(\'(.+?)\',',html).group(1)
- except AttributeError:
+ except (AttributeError, IndexError):
self.fail(_("ReCaptcha second challenge pattern not found"))
self.log_debug("Second challenge: %s" % challenge)
@@ -108,87 +119,137 @@ class ReCaptcha(CaptchaService):
return vers, language, jsh
- def _prepare_time_and_rpc(self):
- self.pyfile.plugin.load("http://www.google.com/recaptcha/api2/demo")
+ def _prepare_image(self, image, challenge_msg):
+ dummy_text = 'pk'
+ # This is just a string to calculate biggest height of a text, since usually
+ # the letters 'p' and 'k' reach to the lower most respective higher most
+ # points in a text font (see typography) and thus we can hereby calculate
+ # the biggest text height of a given font
+
+ font_name = 'arialbd'
+ s = StringIO()
+ s.write(image)
+ s.seek(0)
+
+ img = Image.open(s)
+ draw = ImageDraw.Draw(img)
+
+ if os.name == 'nt':
+ font = ImageFont.truetype(font_name, 13)
+ else:
+ font = None
+
+ tile_size = {'width': img.size[0] / 3, 'height': img.size[1] / 3}
+ tile_index_size = {'width': draw.textsize('0')[0], 'height': draw.textsize('0')[1]}
+
+ for i in xrange(3):
+ for j in xrange(3):
+ tile_index_pos = {
+ 'x': i * tile_size['width'] + (tile_size['width'] / 2) - (tile_index_size['width'] / 2),
+ 'y': j * tile_size['height']
+ }
+ draw.rectangle(
+ [
+ tile_index_pos['x'],
+ tile_index_pos['y'],
+ tile_index_pos['x'] + tile_index_size['width'],
+ tile_index_pos['y'] + tile_index_size['height']
+ ],
+ fill='white'
+ )
+ index_number = str(j * 3 + i + 1)
+ text_width, text_height = draw.textsize(index_number, font=font)
+ draw.text(
+ (
+ tile_index_pos['x'] + (tile_index_size['width'] / 2) - (text_width / 2),
+ tile_index_pos['y'] + (tile_index_size['height'] / 2) - (text_height / 2)
+ ),
+ index_number,
+ '#000',
+ font=font
+ )
+
+ if os.name == 'nt':
+ font = ImageFont.truetype(font_name, 16)
+
+ message = challenge_msg + '\n(Type image numbers like "258")'
+
+ # the text's real height is twice as big as returned by font.getsize() since we use
+ # a newline character which indeed breaks the text but doesn't count as a second line
+ # in font.getsize().
+ if os.name == 'nt':
+ text_height = draw.multiline_textsize(message, font=font)[1]
+
+ else:
+ lines = message.split('\n')
+ text_height = len(lines) * draw.textsize(dummy_text, font=font)[1]
+
+ margin = 5
+ text_height = text_height + margin * 2 # add some margin on top and bottom of text
+
+ img2 = Image.new('RGB', (img.size[0], img.size[1] + text_height), 'white')
+ img2.paste(img, (0, text_height))
+ draw = ImageDraw.Draw(img2)
- millis = int(round(time.time() * 1000))
+ if os.name == 'nt':
+ draw.text((3, margin), message, fill='black', font=font)
+ else:
+ for i in xrange(len(lines)):
+ draw.text((3, i * draw.textsize(dummy_text)[1] + margin), lines[i], fill='black', font=font)
- self.log_debug("Time: %s" % millis)
+ s.truncate(0)
+ img2.save(s, format='JPEG')
+ img = s.getvalue()
+ s.close()
- rand = random.randint(1, 99999999)
- a = "0.%s" % str(rand * 2147483647)
- rpc = int(100000000 * float(a))
+ return img
- self.log_debug("Rpc-token: %s" % rpc)
- return millis, rpc
+ def _challenge_v2(self, key):
+ fallback_url = 'http://www.google.com/recaptcha/api/fallback?k=' + key
+ html = self.pyfile.plugin.load(fallback_url)
- def _challenge_v2(self, key, parent=None):
- if parent is None:
+ for i in xrange(10):
try:
- parent = urlparse.urljoin("http://", urlparse.urlparse(self.pyfile.url).netloc)
-
- except Exception:
- parent = ""
-
- botguardstring = "!A"
- vers, language, jsh = self._collect_api_info()
- millis, rpc = self._prepare_time_and_rpc()
-
- html = self.pyfile.plugin.load("https://www.google.com/recaptcha/api2/anchor",
- get={'k' : key,
- 'hl' : language,
- 'v' : vers,
- 'usegapi' : "1",
- 'jsh' : "%s#id=IO_%s" % (jsh, millis),
- 'parent' : parent,
- 'pfname' : "",
- 'rpctoken': rpc})
-
- token1 = re.search(r'id="recaptcha-token" value="(.*?)">', html)
- self.log_debug("Token #1: %s" % token1.group(1))
-
- html = self.pyfile.plugin.load("https://www.google.com/recaptcha/api2/frame",
- get={'c' : token1.group(1),
- 'hl' : language,
- 'v' : vers,
- 'bg' : botguardstring,
- 'k' : key,
- 'usegapi': "1",
- 'jsh' : jsh},
- decode="unicode-escape")
-
- token2 = re.search(r'"finput","(.*?)",', html)
- self.log_debug("Token #2: %s" % token2.group(1))
-
- token3 = re.search(r'"rresp","(.*?)",', html)
- self.log_debug("Token #3: %s" % token3.group(1))
-
- millis_captcha_loading = int(round(time.time() * 1000))
- captcha_response = self.decrypt("https://www.google.com/recaptcha/api2/payload",
- get={'c':token3.group(1), 'k':key},
- cookies=True,
- ocr=False,
- timeout=30)
- response = base64.b64encode('{"response":"%s"}' % captcha_response)
-
- self.log_debug("Result: `%s`" % response)
-
- timeToSolve = int(round(time.time() * 1000)) - millis_captcha_loading
- timeToSolveMore = timeToSolve + int(float("0." + str(random.randint(1, 99999999))) * 500)
-
- html = self.pyfile.plugin.load("https://www.google.com/recaptcha/api2/userverify",
- post={'k' : key,
- 'c' : token3.group(1),
- 'response': response,
- 't' : timeToSolve,
- 'ct' : timeToSolveMore,
- 'bg' : botguardstring})
-
- token4 = re.search(r'"uvresp","(.*?)",', html)
- self.log_debug("Token #4: %s" % token4.group(1))
-
- result = token4.group(1)
-
- return result, None
+ challenge = re.search(r'name="c"\s+value=\s*"([^"]+)', html).group(1)
+
+ except (AttributeError, IndexError):
+ self.fail(_("ReCaptcha challenge pattern not found"))
+
+ try:
+ challenge_msg = re.search(r'<label .*?class="fbc-imageselect-message-text">(.*?)</label>', html).group(1)
+
+ except (AttributeError, IndexError):
+ try:
+ challenge_msg = re.search(r'<div .*?class=\"fbc-imageselect-message-error\">(.*?)</div>', html).group(1)
+
+ except (AttributeError, IndexError):
+ self.fail(_("ReCaptcha challenge message not found"))
+
+ challenge_msg = re.sub(r'</?\w+?>', "", challenge_msg, 0 , re.I)
+
+ image_url = urlparse.urljoin('http://www.google.com',
+ re.search(r'"(/recaptcha/api2/payload[^"]+)', html).group(1))
+
+ img = self.pyfile.plugin.load(image_url, ref=fallback_url, decode=False)
+
+ img = self._prepare_image(img, challenge_msg)
+
+ response = self.decrypt_image(img)
+
+ post_str = "c=" + urllib.quote_plus(challenge) +\
+ "".join("&response=%s" % str(int(k) - 1) for k in response if k.isdigit())
+ html = self.pyfile.plugin.load(fallback_url, post=post_str, ref=fallback_url)
+
+ try:
+ result = re.search(r'"this\.select\(\)">(.*?)</textarea>', html).group(1)
+ break
+
+ except (AttributeError, IndexError):
+ pass
+
+ else:
+ self.fail(_("Recaptcha max retries exceeded"))
+
+ return result, challenge