File backend support for Captchas.
[toast/cookiecaptcha.git] / maintenance / GenerateFancyCaptchas.php
1 <?php
2 /**
3  * Generate captchas using a python script and copy them into storage.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  * @author Aaron Schulz
22  * @ingroup Maintenance
23  */
24 if ( getenv( 'MW_INSTALL_PATH' ) ) {
25         $IP = getenv( 'MW_INSTALL_PATH' );
26 } else {
27         $IP = dirname(__FILE__).'/../../..';
28 }
29
30 require_once( "$IP/maintenance/Maintenance.php" );
31
32 /**
33  * Maintenance script to change the password of a given user.
34  *
35  * @ingroup Maintenance
36  */
37 class GenerateFancyCaptchas extends Maintenance {
38         public function __construct() {
39                 parent::__construct();
40                 // See captcha.py for argument usage
41                 $this->addOption( "wordlist", 'A list of words', true, true );
42                 $this->addOption( "font", "The font to use", true, true );
43                 $this->addOption( "font-size", "The font size ", false, true );
44                 $this->addOption( "blacklist", "A blacklist of words that should not be used", false, true );
45                 $this->addOption( "fill", "Fill the captcha container to N files", true, true );
46                 $this->addOption( "verbose", "Show debugging information" );
47                 $this->mDescription = "Generate new captchas and move them into storage";
48         }
49
50         public function execute() {
51                 global $wgCaptchaSecret, $wgCaptchaDirectoryLevels;
52
53                 $instance = ConfirmEditHooks::getInstance();
54                 if ( !( $instance instanceof FancyCaptcha ) ) {
55                         $this->error( "\$wgCaptchaClass is not FancyCaptcha.\n", 1 );
56                 }
57                 $backend = $instance->getBackend();
58
59                 $countAct = $instance->estimateCaptchaCount();
60                 $this->output( "Estimated number of captchas is $countAct.\n" );
61
62                 $countGen = (int)$this->getOption( 'fill' ) - $countAct;
63                 if ( $countGen <= 0 ) {
64                         $this->output( "No need to generate anymore captchas.\n" );
65                         return;
66                 }
67
68                 $tmpDir = wfTempDir() . '/mw-fancycaptcha-' . time() . '-' . wfRandomString( 6 );
69                 if ( !wfMkdirParents( $tmpDir ) ) {
70                         $this->error( "Could not create temp directory.\n", 1 );
71                 }
72
73                 $e = null; // exception
74                 try {
75                         $cmd = sprintf( "python %s --key %s --output %s --count %s --dirs %s",
76                                 wfEscapeShellArg( __DIR__ . '/../captcha.py' ),
77                                 wfEscapeShellArg( $wgCaptchaSecret ),
78                                 wfEscapeShellArg( $tmpDir ),
79                                 wfEscapeShellArg( $countGen ),
80                                 wfEscapeShellArg( $wgCaptchaDirectoryLevels )
81                         );
82                         foreach ( array( 'wordlist', 'font', 'font-size', 'blacklist', 'verbose' ) as $par ) {
83                                 if ( $this->hasOption( $par ) ) {
84                                         $cmd .= " --$par " . wfEscapeShellArg( $this->getOption( $par ) );
85                                 }
86                         }
87
88                         $this->output( "Generating $countGen new captchas...\n" );
89                         $retVal = 1;
90                         wfShellExec( $cmd, $retVal );
91                         if ( $retVal != 0 ) {
92                                 wfRecursiveRemoveDir( $tmpDir );
93                                 $this->error( "Could not run generation script.\n", 1 );
94                         }
95
96                         $flags = FilesystemIterator::SKIP_DOTS;
97                         $iter = new RecursiveIteratorIterator(
98                                 new RecursiveDirectoryIterator( $tmpDir, $flags ),
99                                 RecursiveIteratorIterator::CHILD_FIRST // include dirs
100                         );
101
102                         $this->output( "Copying the new captchas to storage...\n" );
103                         foreach ( $iter as $fileInfo ) {
104                                 if ( !$fileInfo->isFile() ) {
105                                         continue;
106                                 }
107                                 list( $salt, $hash ) = $instance->hashFromImageName( $fileInfo->getBasename() );
108                                 $status = $backend->quickStore( array(
109                                         'src' => $fileInfo->getPathname(),
110                                         'dst' => $instance->imagePath( $salt, $hash )
111                                 ) );
112                                 if ( !$status->isOK() ) {
113                                         $this->error( "Could not save file '{$fileInfo->getPathname()}'.\n" );
114                                 }
115                         }
116                 } catch ( Exception $e ) {
117                         wfRecursiveRemoveDir( $tmpDir );
118                         throw $e;
119                 }
120
121                 $this->output( "Removing temporary files...\n" );
122                 wfRecursiveRemoveDir( $tmpDir );
123                 $this->output( "Done.\n" );
124         }
125 }
126
127 $maintClass = "GenerateFancyCaptchas";
128 require_once( RUN_MAINTENANCE_IF_MAIN );