31from optparse
import OptionParser
40 from PIL
import ImageFont
41 from PIL
import ImageDraw
42 from PIL
import ImageEnhance
43 from PIL
import ImageOps
44 from PIL
import ImageMath
46 sys.exit(
"This script requires the Python Imaging Library - http://www.pythonware.com/products/pil/")
65 for i
in range(t, b+1):
77 """Generate a captcha image"""
86 edge = max(dim[0], dim[1]) + 2*min(dim[0], dim[1])
87 im =
Image.new(
'RGB', (edge, edge), bgcolor)
91 d.text((x/2-dim[0]/2, y/2-dim[1]/2), text, font=font, fill=fgcolor)
106 bord = min(dim[0], dim[1])/4
107 im =
im.crop((bbox[0]-bord, bbox[1]-bord, bbox[2]+bord, bbox[3]+bord))
114 for x
in range(nsize[0]):
115 for y
in range(nsize[1]):
117 gradient = 70 * x / nsize[0]
118 data[x, y] = r + gradient
122 im =
ImageMath.eval(
'convert(convert(a, "L") / 3 + b, "RGB")', a=im, b=noise)
131 """Generate a subdirectory path out of the first _levels_
132 characters of _hash_, and ensure the directories exist
135 for i
in range(0, levels):
146def try_pick_word(words, blacklist, verbose, nwords, min_length, max_length):
147 if words
is not None:
155 max_length = max_length
if max_length > 0
else 10
160 print(
"word is %s" % word)
162 if len(word) < min_length:
164 print(
"skipping word pair '%s' because it has fewer than %d characters" % (word, min_length))
167 if max_length > 0
and len(word) > max_length:
169 print(
"skipping word pair '%s' because it has more than %d characters" % (word, max_length))
174 print(
"skipping word pair '%s' because it contains non-alphabetic characters" % word)
177 for naughty
in blacklist:
180 print(
"skipping word pair '%s' because it contains blacklisted word '%s'" % (word, naughty))
184def pick_word(words, blacklist, verbose, nwords, min_length, max_length):
185 for x
in range(1000):
186 word =
try_pick_word(words, blacklist, verbose, nwords, min_length, max_length)
189 sys.exit(
"Unable to find valid word combinations")
200 blacklist = object[2]
205 for i
in range(count):
209 md5hash =
hashlib.md5((key+salt+word+key+salt).encode(
'utf-8')).hexdigest()[:16]
210 filename =
"image_%s_%s.png" % (salt, md5hash)
218if __name__ ==
'__main__':
219 """This grabs random words from the dictionary 'words' (one
220 word per line) and generates a captcha image for each one,
221 with a keyed salted hash of the correct answer in the filename.
223 To check a reply, hash it in the same way with the same salt and
224 secret key, then compare with the hash value given.
227 parser = OptionParser()
229 parser.add_option(
"--random", help=
"Use random charcters instead of a wordlist", action=
"store_true")
230 parser.add_option(
"--key", help=
"The passphrase set as $wgCaptchaSecret (required)", metavar=
"KEY")
231 parser.add_option(
"--output", help=
"The directory to put the images in - $wgCaptchaDirectory (required)", metavar=
"DIR")
232 parser.add_option(
"--font", help=
"The font to use (required)", metavar=
"FONT.ttf")
233 parser.add_option(
"--font-size", help=
"The font size (default 40)", metavar=
"N", type=
'int', default=40)
234 parser.add_option(
"--count", help=
"The maximum number of images to make (default 20)", metavar=
"N", type=
'int', default=20)
235 parser.add_option(
"--blacklist", help=
"A blacklist of words that should not be used", metavar=
"FILE", default=
os.path.join(script_dir,
"blacklist"))
236 parser.add_option(
"--fill", help=
"Fill the output directory to contain N files, overrides count, cannot be used with --dirs", metavar=
"N", type=
'int')
237 parser.add_option(
"--dirs", help=
"Put the images into subdirectories N levels deep - $wgCaptchaDirectoryLevels", metavar=
"N", type=
'int')
238 parser.add_option(
"--verbose",
"-v", help=
"Show debugging information", action=
'store_true')
239 parser.add_option(
"--number-words", help=
"Number of words from the wordlist which make a captcha challenge (default 2)", type=
'int', default=2)
240 parser.add_option(
"--min-length", help=
"Minimum length for a captcha challenge", type=
'int', default=1)
241 parser.add_option(
"--max-length", help=
"Maximum length for a captcha challenge", type=
'int', default=-1)
242 parser.add_option(
"--threads", help=
"Maximum number of threads to be used to generate captchas.", type=
'int', default=1)
251 sys.exit(
"Need to specify a wordlist")
259 sys.exit(
"Need to specify an output directory")
263 sys.exit(
"Need to specify the location of a font")
274 count = max(0, fill - len(
os.listdir(output)))
279 words = [x
for x
in words
280 if len(x)
in (4,5)
and x[0] !=
"f"
281 and x[0] != x[1]
and x[-1] != x[-2]]
284 sys.exit(
"No need to generate CAPTCHA images.")
290 chunks = (count / threads)
294 print(
"Generating %s CAPTCHA images separated in %s image(s) per chunk run by %s threads..." % (count, chunks, threads))
295 for i
in range(0, threads):
296 data.append([chunks, words, blacklist, opts, font, fontsize])
298 p.map(run_in_thread, data)
and that you know you can do these things To protect your we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights These restrictions translate to certain responsibilities for you if you distribute copies of the or if you modify it For if you distribute copies of such a whether gratis or for a you must give the recipients all the rights that you have You must make sure that receive or can get the source code And you must show them these terms so they know their rights We protect your rights with two and(2) offer you this license which gives you legal permission to copy
while(( $__line=Maintenance::readconsole()) !==false) print
try_pick_word(words, blacklist, verbose, nwords, min_length, max_length)
gen_captcha(text, fontname, fontsize, file_name)
pick_word(words, blacklist, verbose, nwords, min_length, max_length)
wobbly_copy(src, wob, col, scale, ang)
gen_subdir(basedir, md5hash, levels)