1<?php 2/* 3 * Gallery - a web based photo album viewer and editor 4 * Copyright (C) 2000-2008 Bharat Mediratta 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or (at 9 * your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. 19 */ 20 21GalleryCoreApi::requireOnce('modules/core/classes/helpers/UserRecoverPasswordHelper_simple.class'); 22 23/** 24 * This controller will handle the recovery of passwords that have been lost or forgotten 25 * by the user. 26 * @package GalleryCore 27 * @subpackage UserInterface 28 * @author Jay Rossiter <cryptographite@users.sf.net> 29 * @version $Revision: 20996 $ 30 */ 31class UserRecoverPasswordController extends GalleryController { 32 /** 33 * ValidationPlugin instances to use when handling this request. Only used by test code. 34 * 35 * @var array (pluginId => ValidationPlugin) $_pluginInstances 36 * @access private 37 */ 38 var $_pluginInstances; 39 40 /** 41 * Tests can use this method to hardwire a specific set of plugin instances to use. 42 * This avoids situations where some of the option instances will do unpredictable 43 * things and derail the tests. 44 * 45 * @param array $pluginInstances of GalleryValidationPlugin 46 */ 47 function setPluginInstances($pluginInstances) { 48 $this->_pluginInstances = $pluginInstances; 49 } 50 51 /** 52 * @see GalleryController::handleRequest 53 */ 54 function handleRequest($form) { 55 global $gallery; 56 57 $status = $error = $results = array(); 58 59 $phpVm = $gallery->getPhpVm(); 60 if (isset($form['action']['recover'])) { 61 $form['userName'] = is_string($form['userName']) ? $form['userName'] : null; 62 if (empty($form['userName'])) { 63 $error[] = 'form[error][userName][missing]'; 64 } 65 66 /* If no errors have been detected, let the validation plugins do their work */ 67 if (empty($error)) { 68 if (isset($this->_pluginInstances)) { 69 $pluginInstances = $this->_pluginInstances; 70 } else { 71 /* Get all the validation plugins */ 72 list ($ret, $pluginInstances) = 73 GalleryCoreApi::getAllFactoryImplementationIds('GalleryValidationPlugin'); 74 if ($ret) { 75 return array($ret, null); 76 } 77 78 foreach (array_keys($pluginInstances) as $pluginId) { 79 list ($ret, $pluginInstances[$pluginId]) = 80 GalleryCoreApi::newFactoryInstanceById('GalleryValidationPlugin', 81 $pluginId); 82 if ($ret) { 83 return array($ret, null); 84 } 85 } 86 } 87 88 /* Let each plugin do its verification */ 89 foreach ($pluginInstances as $plugin) { 90 list ($ret, $pluginErrors, $continue) = $plugin->performValidation($form); 91 if ($ret) { 92 return array($ret, null); 93 } 94 95 $error = array_merge($error, $pluginErrors); 96 if (!$continue) { 97 break; 98 } 99 } 100 } 101 102 /* 103 * Still no errors? Check the DB for a previous request and then 104 * update, reject or add based on the results. 105 */ 106 $shouldSendEmail = false; 107 if (empty($error)) { 108 list ($ret, $user) = GalleryCoreApi::fetchUserByUsername($form['userName']); 109 if ($ret && !($ret->getErrorCode() & ERROR_MISSING_OBJECT)) { 110 return array($ret, null); 111 } 112 113 if (isset($user) && $user->getEmail() != '') { 114 /* Generate a unique auth string based on userName, time of request and IP */ 115 $authString = $this->_generateAuthString(); 116 117 /* Generate the request expiration: Now + 7 Days */ 118 $requestExpires = mktime(date('G'), date('i'), date('s'), 119 date('m'), date('d')+7, date('Y')); 120 121 /* 122 * Check the database to see if a previous request. 123 * If a request exists, check the timestamp to see if a new 124 * request can be generated, or if they will be denied 125 * because the window is too small. 126 */ 127 list ($ret, $lastRequest) = UserRecoverPasswordHelper_simple::getRequestExpires( 128 $user->getUserName(), null); 129 if ($ret) { 130 return array($ret, null); 131 } 132 133 /* 134 * This request was made less than 20 minutes ago. Don't update the auth 135 * string. We'll silently succeed to thwart phishing attempts. 136 */ 137 if (!empty($lastRequest)) { 138 if (($lastRequest - (7 * 24 * 60 * 60) + (20 * 60)) < time()) { 139 $ret = GalleryCoreApi::updateMapEntry( 140 'GalleryRecoverPasswordMap', 141 array('userName' => $user->getUserName()), 142 array('authString' => $authString, 143 'requestExpires' => $requestExpires)); 144 $shouldSendEmail = true; 145 } 146 } else { 147 /* 148 * Add the map entry before sending email to the user - 149 * We don't want to send them mail if the data never gets into the DB 150 */ 151 $ret = GalleryCoreApi::addMapEntry( 152 'GalleryRecoverPasswordMap', 153 array('userName' => $form['userName'], 154 'authString' => $authString, 155 'requestExpires' => $requestExpires)); 156 if ($ret) { 157 return array($ret, null); 158 } 159 $shouldSendEmail = true; 160 } 161 162 if (empty($error) && $shouldSendEmail) { 163 /* Generate baseUrl and recoverUrl for the email template */ 164 $generator =& $gallery->getUrlGenerator(); 165 $baseUrl = $generator->generateUrl(array(), 166 array('forceFullUrl' => true, 'htmlEntities' => false, 167 'forceSessionId' => false)); 168 $recoverUrl = $generator->generateUrl( 169 array('view' => 'core.UserAdmin', 170 'subView' => 'core.UserRecoverPasswordConfirm', 171 'userName' => $user->getUserName(), 172 'authString' => $authString), 173 array('forceFullUrl' => true, 'htmlEntities' => false, 174 'forceSessionId' => false)); 175 176 /* email template data */ 177 $tplData = array('name' => $user->getfullName(), 178 'baseUrl' => $baseUrl, 179 'ip' => GalleryUtilities::getRemoteHostAddress(), 180 'date' => date('r'), 181 'userName' => $user->getUserName(), 182 'recoverUrl' => $recoverUrl, 183 ); 184 185 /* Load core for translation */ 186 list ($ret, $module) = GalleryCoreApi::loadPlugin('module', 'core'); 187 if ($ret) { 188 return array($ret, null); 189 } 190 191 /* Send the user email based on our confirmation template */ 192 $ret = GalleryCoreApi::sendTemplatedEmail( 193 'modules/core/templates/UserRecoverPasswordEmail.tpl', 194 $tplData, '', $user->getEmail(), 195 $module->translate('Password Recovery')); 196 if ($ret) { 197 return array($ret, null); 198 } 199 } 200 201 /* Set the recovered info flag */ 202 $status['requestSent'] = 1; 203 } else { 204 /* Silently succeed; we don't reward phishing attempts */ 205 /* Set the recovered info flag */ 206 $status['requestSent'] = 1; 207 } 208 } 209 } else if (isset($form['action']['cancel'])) { 210 $results['return'] = 1; 211 } 212 213 if (empty($subView)) { 214 $subView = 'core.UserRecoverPassword'; 215 } 216 217 if (empty($error)) { 218 $results['redirect']['view'] = 'core.UserAdmin'; 219 $results['redirect']['subView'] = $subView; 220 } else { 221 $results['delegate']['view'] = 'core.UserAdmin'; 222 $results['delegate']['subView'] = $subView; 223 } 224 225 $results['status'] = $status; 226 $results['error'] = $error; 227 228 return array(null, $results); 229 } 230 231 /** 232 * Generate the authorization string used for login.txt 233 * @access private 234 */ 235 function _generateAuthString() { 236 GalleryCoreApi::requireOnce('lib/joomla/crypt.php'); 237 $j = new JCrypt(); 238 return md5($j->genRandomBytes(32)); 239 } 240} 241 242/** 243 * This view shows information about password recovery 244 */ 245class UserRecoverPasswordView extends GalleryView { 246 247 /** 248 * @see GalleryView::loadTemplate 249 */ 250 function loadTemplate(&$template, &$form) { 251 global $gallery; 252 253 if ($form['formName'] == 'UserRecoverPassword') { 254 if (empty($form['userName'])) { 255 $form['error']['userName']['missing'] = 1; 256 } 257 } else { 258 $form['userName'] = ''; 259 $form['formName'] = 'UserRecoverPassword'; 260 } 261 262 $UserRecoverPassword = array(); 263 264 /* Get all the login plugins */ 265 list ($ret, $allPluginIds) = 266 GalleryCoreApi::getAllFactoryImplementationIds('GalleryValidationPlugin'); 267 if ($ret) { 268 return array($ret, null); 269 } 270 271 /* Let each plugin load its template data */ 272 $UserRecoverPassword['plugins'] = array(); 273 foreach (array_keys($allPluginIds) as $pluginId) { 274 list ($ret, $plugin) = 275 GalleryCoreApi::newFactoryInstanceById('GalleryValidationPlugin', $pluginId); 276 if ($ret) { 277 return array($ret, null); 278 } 279 280 list ($ret, $data['file'], $data['l10Domain']) = $plugin->loadTemplate($form); 281 if ($ret) { 282 return array($ret, null); 283 } 284 285 if (isset($data['file'])) { 286 $UserRecoverPassword['plugins'][] = $data; 287 } 288 } 289 290 $template->setVariable('UserRecoverPassword', $UserRecoverPassword); 291 $template->setVariable('controller', 'core.UserRecoverPassword'); 292 return array(null, array('body' => 'modules/core/templates/UserRecoverPassword.tpl')); 293 } 294} 295?> 296