Overview

Packages

  • CONTENIDO
  • Core
    • Authentication
    • Backend
    • Cache
    • CEC
    • Chain
    • ContentType
    • Database
    • Debug
    • Exception
    • Frontend
      • Search
      • URI
      • Util
    • GenericDB
      • Model
    • GUI
      • HTML
    • I18N
    • LayoutHandler
    • Log
    • Security
    • Session
    • Util
    • Validation
    • Versioning
    • XML
  • Module
    • ContentSitemapHtml
    • ContentSitemapXml
    • ContentUserForum
    • NavigationTop
    • ScriptCookieDirective
  • mpAutoloaderClassMap
  • None
  • PHP
  • Plugin
    • ContentAllocation
    • CronjobOverview
    • FormAssistant
    • FrontendLogic
    • FrontendUsers
    • Linkchecker
    • ModRewrite
    • Newsletter
    • Repository
      • FrontendNavigation
      • KeywordDensity
    • SmartyWrapper
    • UrlShortener
    • UserForum
    • Workflow
  • PluginManager
  • Setup
    • Form
    • GUI
    • Helper
      • Environment
      • Filesystem
      • MySQL
      • PHP
    • UpgradeJob

Classes

  • cAjaxRequest
  • cAutoload
  • cBackend
  • cEffectiveSetting
  • cGuiScrollListAlltranslations
  • cHTMLValidator
  • cMailer
  • cModuleFileTranslation
  • cModuleHandler
  • cModuleSearch
  • cModuleSynchronizer
  • cModuleTemplateHandler
  • CodeMirror
  • cPasswordRequest
  • cPermission
  • cRegistry
  • cSystemPurge
  • cSystemtest
  • cTinymce4Configuration
  • cTinyMCE4Editor
  • cTinyMCEEditor
  • cWYSIWYGEditor
  • FrontendList
  • HtmlParser
  • TODOBackendList
  • TreeItem
  • UploadList
  • UploadSearchResultList

Functions

  • addArtspec
  • addSortImages
  • backToMainArea
  • buildArticleSelect
  • buildCategorySelect
  • buildCategorySelectRights
  • buildHeapTable
  • buildStackString
  • buildTree
  • buildUserOrGroupPermsFromRequest
  • callPluginStore
  • cApiCatGetLevelNode
  • cApiImageCheckCachedImageValidity
  • cApiImageCheckImageEditingPosibility
  • cApiImageCheckImageEditingPossibility
  • cApiImageGetCacheFileName
  • cApiImageGetTargetDimensions
  • cApiImageIsAnimGif
  • cApiImgScale
  • cApiImgScaleGetMD5CacheFile
  • cApiImgScaleHQ
  • cApiImgScaleImageMagick
  • cApiImgScaleLQ
  • cApiIsImageMagickAvailable
  • cApiStrCleanURLCharacters
  • cApiStrNormalizeLineEndings
  • cApiStrRecodeString
  • cApiStrReplaceDiacritics
  • cApiStrTrimAfterWord
  • cApiStrTrimHard
  • cApiStrTrimSentence
  • cDeprecated
  • cDie
  • cError
  • checkLangInClients
  • checkPathInformation
  • cInclude
  • compareUrlStrings
  • conChangeTemplateForCat
  • conCopyArticle
  • conCopyArtLang
  • conCopyContainerConf
  • conCopyContent
  • conCopyMetaTags
  • conCopyTemplateConfiguration
  • conCreateLocationString
  • conDeeperCategoriesArray
  • conDeleteart
  • conEditArt
  • conEditFirstTime
  • conFetchCategoryTree
  • conFlagOnOffline
  • conGenerateCode
  • conGenerateCodeForAllArts
  • conGenerateCodeForAllArtsInCategory
  • conGenerateCodeForAllartsUsingLayout
  • conGenerateCodeForAllartsUsingMod
  • conGenerateCodeForAllArtsUsingTemplate
  • conGenerateCodeForArtInAllCategories
  • conGenerateCodeForClient
  • conGenerateKeywords
  • conGetAvailableMetaTagTypes
  • conGetCategoryArticleId
  • conGetCategoryAssignments
  • conGetContainerConfiguration
  • conGetContentFromArticle
  • conGetHtmlTranslationTable
  • conGetMetaValue
  • conGetTemplateConfigurationIdForArticle
  • conGetTemplateConfigurationIdForCategory
  • conGetTopmostCat
  • conGetUniqueArticleUrlname
  • conGetUsedModules
  • conHtmlentities
  • conHtmlEntityDecode
  • conHtmlSpecialChars
  • conIsArticleUrlnameUnique
  • conIsLocked
  • conLock
  • conLockBulkEditing
  • conMakeArticleIndex
  • conMakeCatOnline
  • conMakeInlineScript
  • conMakeOnline
  • conMakeOnlineBulkEditing
  • conMakePublic
  • conMakeStart
  • conMoveArticles
  • conPhp54Check
  • conRemoveOldCategoryArticle
  • conSaveContentEntry
  • conSetCodeFlag
  • conSetCodeFlagBulkEditing
  • conSetMetaValue
  • conSetStartArticle
  • consoleLog
  • conSyncArticle
  • copyRightsForElement
  • createBulkEditingFunction
  • createRandomName
  • createRightsForElement
  • cWarning
  • dbGetColumns
  • dbGetIndexes
  • dbGetPrimaryKeyName
  • dbTableExists
  • dbUpgradeTable
  • defineIfNotDefined
  • deleteArtspec
  • deleteRightsForElement
  • deleteSystemProperty
  • displayDatetime
  • emptyLogFile
  • endAndLogTiming
  • extractNumber
  • generateDisplayFilePath
  • generateJs
  • getAllClientsAndLanguages
  • getArtLang
  • getArtspec
  • getAvailableContentTypes
  • getCanonicalDay
  • getCanonicalMonth
  • getDirectorySize
  • getEffectiveSetting
  • getEffectiveSettingsByType
  • getEncodingByLanguage
  • getFileInformation
  • getFileType
  • getGroupOrUserName
  • getIDForArea
  • getJsHelpContext
  • getLanguageNamesByClient
  • getLanguagesByClient
  • getmicrotime
  • getNamedFrame
  • getParentAreaId
  • getRightsList
  • getSearchResults
  • getStrExpandCollapseButton
  • getSystemProperties
  • getSystemPropertiesByType
  • getSystemProperty
  • getTemplateSelect
  • getUplExpandCollapseButton
  • htmldecode
  • htmlentities_iso88592
  • humanReadableSize
  • includePlugins
  • insertEmptyStrRow
  • ipMatch
  • isAlphanumeric
  • isArchive
  • isArtInMultipleUse
  • isFunctionDisabled
  • isGroup
  • isIPv4
  • isRunningFromWeb
  • isStartArticle
  • isUtf8
  • isValidMail
  • langActivateDeactivateLanguage
  • langDeleteLanguage
  • langEditLanguage
  • langGetTextDirection
  • langNewLanguage
  • langRenameLanguage
  • layDeleteLayout
  • layEditLayout
  • machineReadableSize
  • mailLogBulkEditingFunctions
  • mailLogDecodeAddresses
  • markSubMenuItem
  • mask
  • modDeleteModule
  • modEditModule
  • phpInfoToHtml
  • plugin_include
  • prCreateURLNameLocationString
  • prDeleteCacheFileContent
  • prGetCacheFileContent
  • prResolvePathViaCategoryNames
  • prResolvePathViaURLNames
  • prWriteCacheFileContent
  • recursiveCopy
  • removeFileInformation
  • renderBackendBreadcrumb
  • renderLabel
  • renderSelectProperty
  • renderTextProperty
  • saveGroupRights
  • saveRights
  • scanDirectory
  • scanPlugins
  • sendEncodingHeader
  • set_magic_quotes_gpc
  • setArtspecDefault
  • setArtspecOnline
  • setSystemProperty
  • showTree
  • startTiming
  • statCreateLocationString
  • statDisplayTopChooser
  • statDisplayYearlyTopChooser
  • statGetAvailableMonths
  • statGetAvailableYears
  • statResetStatistic
  • statsArchive
  • statsDisplayInfo
  • statsOverviewAll
  • statsOverviewTop
  • statsOverviewTopYear
  • statsOverviewYear
  • strAssignTemplate
  • strBuildSqlValues
  • strCheckAlias
  • strCheckTreeForErrors
  • strCopyCategory
  • strCopyTree
  • strDeeperCategoriesArray
  • strDeleteCategory
  • strHasArticles
  • strHasStartArticle
  • strMakePublic
  • strMakeVisible
  • strMoveCatTargetallowed
  • strMoveDownCategory
  • strMoveSubtree
  • strMoveUpCategory
  • strNewCategory
  • strNewTree
  • strNextBackwards
  • strNextDeeper
  • strNextDeeperAll
  • strNextPost
  • strOrderedPostTreeList
  • strRemakeTreeTable
  • strRenameCategory
  • strRenameCategoryAlias
  • strSortPrePost
  • strSyncCategory
  • systemHavePerm
  • tplAutoFillModules
  • tplBrowseLayoutForContainers
  • tplcfgDuplicate
  • tplDeleteTemplate
  • tplDuplicateTemplate
  • tplEditTemplate
  • tplGetContainerDefault
  • tplGetContainerMode
  • tplGetContainerName
  • tplGetContainerNumbersInLayout
  • tplGetContainerTypes
  • tplGetInUsedData
  • tplIsTemplateInUse
  • tplPreparseLayout
  • tplProcessSendContainerConfiguration
  • updateClientCache
  • updateFileInformation
  • uplCreateFriendlyName
  • uplDirectoryListRecursive
  • uplGetDirectoriesToExclude
  • uplGetFileExtension
  • uplGetFileIcon
  • uplGetFileTypeDescription
  • uplGetThumbnail
  • uplHasFiles
  • uplHasSubdirs
  • uplmkdir
  • uplRecursiveDBDirectoryList
  • uplRecursiveDirectoryList
  • uplRenameDirectory
  • uplSearch
  • uplSyncDirectory
  • uplSyncDirectoryDBFS
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: 
  3: /**
  4:  * This file contains the the password request class.
  5:  *
  6:  * @package    Core
  7:  * @subpackage Backend
  8:  * @author     Timo Trautmann
  9:  * @copyright  four for business AG <www.4fb.de>
 10:  * @license    http://www.contenido.org/license/LIZENZ.txt
 11:  * @link       http://www.4fb.de
 12:  * @link       http://www.contenido.org
 13:  */
 14: 
 15: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
 16: 
 17: /**
 18:  * Class for handling passwort recovery of backend users.
 19:  * If a user has set his e-mail address, this class generates a new password for user and submits to his e-mail address.
 20:  * Submitting a new Password is only possible every 30 minutes. Mailsender, Mailsendername and Mailserver are set into
 21:  * system properties. There it is also possible to deactivate this feature.
 22:  *
 23:  * @package    Core
 24:  * @subpackage Backend
 25:  */
 26: class cPasswordRequest
 27: {
 28:     /**
 29:      * The CONTENIDO database object
 30:      *
 31:      * @var cDb
 32:      */
 33:     protected $_db;
 34: 
 35:     /**
 36:      * The CONTENIDO configuration array
 37:      *
 38:      * @var array
 39:      */
 40:     protected $_cfg;
 41: 
 42:     /**
 43:      * The CONTENIDO template object
 44:      *
 45:      * @var cTemplate
 46:      */
 47:     protected $_tpl;
 48: 
 49:     /**
 50:      * Username of user which requests password
 51:      *
 52:      * @var string
 53:      */
 54:     protected $_username;
 55: 
 56:     /**
 57:      * E-mail address of user which requests password
 58:      *
 59:      * @var string
 60:      */
 61:     protected $_email;
 62: 
 63:     /**
 64:      * Time in minutes after which user is allowed to request a new password
 65:      *
 66:      * @var int
 67:      */
 68:     protected $_reloadTime;
 69: 
 70:     /**
 71:      * Length of validation token, which is generated automatically
 72:      *
 73:      * @var int
 74:      */
 75:     protected $_tokenLength;
 76: 
 77:     /**
 78:      * Defines if passwort request is enabled or disabled.
 79:      * Default: This feature is enabled
 80:      *
 81:      * @var bool
 82:      */
 83:     protected $_isEnabled;
 84: 
 85:     /**
 86:      * E-mail address of the sender
 87:      *
 88:      * @var string
 89:      */
 90:     protected $_sendermail;
 91: 
 92:     /**
 93:      * Name of the sender
 94:      *
 95:      * @var string
 96:      */
 97:     protected $_sendername;
 98: 
 99:     /**
100:      * Constructor to create an instance of this class.
101:      *
102:      * @param cDb   $db
103:      *         CONTENIDO database object
104:      * @param array $cfg
105:      *         The CONTENIDO configuration array
106:      *
107:      * @throws cDbException
108:      * @throws cException
109:      */
110:     public function __construct($db, $cfg)
111:     {
112:         // generate new db object, if it does not exist
113:         if (!is_object($db)) {
114:             $this->_db = cRegistry::getDb();
115:         } else {
116:             $this->_db = $db;
117:         }
118: 
119:         // init class variables
120:         $this->_cfg      = $cfg;
121:         $this->_tpl      = new cTemplate();
122:         $this->_username = '';
123:         $this->_email    = '';
124: 
125:         // set reload to 4 hours (60*4 minutes)
126:         $this->_reloadTime = 240;
127: 
128:         // set token length to 14 chars
129:         $this->_tokenLength = 14;
130: 
131:         // get systemproperty, which definies if password request is enabled
132:         // (true) or disabled (false) : default to enabled
133:         $sEnable = getSystemProperty('pw_request', 'enable');
134:         if ($sEnable == 'false') {
135:             $this->_isEnabled = false;
136:         } else {
137:             $this->_isEnabled = true;
138:         }
139: 
140:         // get systemproperty for senders mail and validate mail address, if not
141:         // set use standard sender
142:         $sendermail = getSystemProperty('system', 'mail_sender');
143:         if (preg_match("/^.+@.+\.([A-Za-z0-9\-_]{1,20})$/", $sendermail)) {
144:             $this->_sendermail = $sendermail;
145:         } else {
146:             $this->_sendermail = 'info@contenido.org';
147:         }
148: 
149:         // get systemproperty for senders name, if not set use CONTENIDO Backend
150:         $sendername = getSystemProperty('system', 'mail_sender_name');
151:         if ($sendername != '') {
152:             $this->_sendername = $sendername;
153:         } else {
154:             $this->_sendername = 'CONTENIDO Backend';
155:         }
156: 
157:         // show form if password reset is wished
158:         // if feature is not enabled, do nothing
159:         if (true === $this->_isEnabled) {
160:             // check if confirmation link from mail used
161:             if (isset($_GET['pw_reset']) && '' !== $_GET['pw_reset']) {
162:                 // check if requests found
163:                 $aRequests = $this->_getCurrentRequests();
164:                 if (count($aRequests) > 0) {
165:                     // check if form with username and new password was filled out
166:                     if (false === isset($_POST['user_name'])
167:                         || false === isset($_POST['user_pw'])
168:                         || false === isset($_POST['user_pw_repeat'])
169:                     ) {
170:                         // show form to set new password
171:                         $this->_renderNewPwForm();
172:                     } else {
173:                         // do validation checks then set new password for user in database
174:                         $this->_handleResetPw();
175:                     }
176:                 }
177:             }
178:         }
179:     }
180: 
181:     /**
182:      * Function displays form for password request, if
183:      * password is submitted this function also starts the
184:      * passwort reset request and sending process
185:      *
186:      * @param bool $return [optional]
187:      *                     Return or print template
188:      *
189:      * @return string
190:      *         rendered HTML code
191:      *
192:      * @throws cDbException
193:      * @throws cException
194:      * @throws cInvalidArgumentException
195:      */
196:     public function renderForm($return = false)
197:     {
198:         // if feature is not enabled, do nothing
199:         if (!$this->_isEnabled) {
200:             return '';
201:         }
202: 
203:         // if form is sumbitted call function handleNewPassword() and set
204:         // submitted username to class variable $sUsername
205:         if (isset($_POST['action']) && $_POST['action'] == 'request_pw') {
206:             // avoid SQL-Injection, first check if submitted vars are escaped
207:             // automatically
208:             $this->_username = $_POST['request_username'];
209: 
210:             $message = $this->_handleNewPassword();
211: 
212:             // if form is submitted, show corresponding password request layer
213:             $this->_tpl->set('s', 'JS_CALL', 'showRequestLayer();');
214:         } else {
215:             $message = '';
216: 
217:             // by default request layer is invisible so do nothing
218:             $this->_tpl->set('s', 'JS_CALL', '');
219:         }
220: 
221:         // generate new form
222:         $form = new cHTMLForm('request_pw', 'index.php', 'post');
223: 
224:         // CON-2772 generate username safe to display
225:         $safeUsername = stripslashes($this->_username);
226:         $safeUsername = conHtmlentities($safeUsername);
227: 
228:         // generate input for username
229:         $inputUsername = new cHTMLTextbox('request_username', $safeUsername, '', '', 'request_username');
230:         $inputUsername->setStyle('width:215px;');
231: 
232:         // set request action and current language
233:         $form->setVar('action', 'request_pw');
234:         $form->setVar('belang', $GLOBALS['belang']);
235: 
236:         // generate submitbutton and fill the form
237:         $form->setContent(
238:             '<input class="password_request_input" type="image" src="images/submit.gif" alt="' . i18n('Submit')
239:             . '" title="' . i18n('Submit') . '">' . $inputUsername->render()
240:         );
241:         $this->_tpl->set('s', 'FORM', $form->render());
242:         $this->_tpl->set('s', 'MESSAGE', $message);
243:         $this->_tpl->set('s', 'LABEL', i18n('Please enter your login') . ':');
244: 
245:         // if handleNewPassword() returns a message, display it
246:         return $this->_tpl->generate(
247:             $this->_cfg['path']['contenido'] . $this->_cfg['path']['templates']
248:             . $this->_cfg['templates']['request_password'],
249:             $return
250:         );
251:     }
252: 
253:     /**
254:      * Function to display form to set new password for user.
255:      *
256:      * @throws cDbException
257:      * @throws cException
258:      * @throws cInvalidArgumentException
259:      */
260:     protected function _renderNewPwForm()
261:     {
262:         if (isset($_POST['action']) && $_POST['action'] == 'reset_pw') {
263:             $this->_username = $_POST['request_username'];
264: 
265:             $message = $this->_handleNewPassword();
266:             
267:             // do not show password reset form
268:             $this->_tpl->set('s', 'JS_CALL', '');
269:         } else {
270:             // show password reset form using JavaScript
271:             $this->_tpl->set('s', 'JS_CALL', 'showResetLayer();');
272:         }
273: 
274:         $msg = i18n('You may now set a new password');
275:         $this->_tpl->set('s', 'RESET_LABEL', $msg);
276: 
277:         // insert form with username, password and password repeat fields
278:         $form =
279:             new cHTMLForm('reset_form', htmlentities(cRegistry::getBackendUrl()) . '?pw_reset=' . $_GET['pw_reset']);
280: 
281:         $userNameLbl = new cHTMLDiv(new cHTMLLabel(i18n('User name') . ': ', 'user_name'));
282:         $userNameBox = new cHTMLTextbox('user_name');
283:         $userNameBox->removeAttribute('size');
284:         $userNameBox = new cHTMLDiv($userNameBox);
285: 
286:         $userPwLbl = new cHTMLLabel(i18n('New password') . ': ', 'user_pw');
287:         $userPwBox = new cHTMLTextbox('user_pw');
288:         $userPwBox->setAttribute('type', 'password');
289:         $userPwBox->removeAttribute('size');
290:         $userPwBox = new cHTMLDiv($userPwBox);
291: 
292:         $userPwRepeatLbl = new cHTMLLabel(i18n('Confirm new password'), 'user_pw_repeat');
293:         $userPwRepeatBox = new cHTMLTextbox('user_pw_repeat');
294:         $userPwRepeatBox->setAttribute('type', 'password');
295:         $userPwRepeatBox->removeAttribute('size');
296: 
297:         $sendBtn = new cHTMLButton('submit');
298:         $sendBtn->setAttribute('type', 'image');
299:         $sendBtn->setAttribute('src', 'images/submit.gif');
300:         $sendBtn->setAttribute('alt', i18n('Submit'));
301:         $sendBtn->setAttribute('title', i18n('Submit'));
302:         $sendBtn->setAttribute('class', 'send_btn');
303:         $lastFormRow = new cHTMLDiv($userPwRepeatLbl . $userPwRepeatBox . $sendBtn);
304:         $lastFormRow->setAttribute('class', 'last_row');
305: 
306:         $sendBtn->removeAttribute('value');
307:         $form->setContent([$userNameLbl, $userNameBox, $userPwLbl, $userPwBox, $lastFormRow]);
308: 
309:         $this->_tpl->set('s', 'RESET_MESSAGE', '');
310: 
311:         $this->_tpl->set('s', 'RESET_FORM', $form->render());
312:     }
313: 
314:     /**
315:      * Getter function to obtain an array of all current user password reset requests
316:      *
317:      * @return array
318:      *
319:      * @throws cDbException
320:      * @throws cException
321:      */
322:     protected function _getCurrentRequests()
323:     {
324:         $oApiUserPasswordRequest = new cApiUserPasswordRequestCollection();
325: 
326:         return $oApiUserPasswordRequest->fetchCurrentRequests();
327:     }
328: 
329:     /**
330:      * Function checks password request for errors and sends a mail using
331:      * _submitMail() in case of valid requests
332:      *
333:      * @return string
334:      *
335:      * @throws cDbException
336:      * @throws cException
337:      * @throws cInvalidArgumentException
338:      */
339:     protected function _handleNewPassword()
340:     {
341:         // notification message, which is returned to caller
342:         $message = '';
343: 
344:         // check if requested username exists, also get email and timestamp when
345:         // user last requests a new password (last_pw_request)
346:         $sql = "SELECT
347:                     username, email
348:                 FROM
349:                     " . $this->_cfg['tab']['user'] . "
350:                 WHERE
351:                     username = '" . $this->_db->escape($this->_username) . "'
352:                     AND (valid_from <= NOW() OR valid_from = '0000-00-00 00:00:00' OR valid_from IS NULL)
353:                     AND (valid_to >= NOW() OR valid_to = '0000-00-00 00:00:00' OR valid_to IS NULL)";
354:         $this->_db->query($sql);
355: 
356:         if ($this->_db->nextRecord() && md5($this->_username) == md5($this->_db->f('username'))) {
357:             // by default user is allowed to request new password
358:             $isAllowed = true;
359: 
360:             // we need latest password request for timelimit comparison
361:             $lastPwRequest = '0000-00-00 00:00:00';
362: 
363:             // check if user has already used max amount of reset requests
364:             $oApiUser = new cApiUser();
365:             // try to load user by name
366:             // this should always work because username in database already confirmed
367:             if (false === $oApiUser->loadBy('username', $this->_username)) {
368:                 $isAllowed = false;
369:                 $message   = i18n('New password was submitted to your e-mail address.');
370:             } else {
371:                 $oApiPasswordRequestCol = new cApiUserPasswordRequestCollection();
372:                 $requests               = $oApiPasswordRequestCol->fetchAvailableRequests();
373: 
374:                 // do maintainance for all user password requests
375:                 foreach ($requests as $oApiUserPasswordRequest) {
376:                     // get time of password reset request
377:                     $reqTime = $oApiUserPasswordRequest->get('request');
378: 
379:                     // if $reqTime is newer than $lastPwRequest then use this as new last password request time
380:                     if (strtotime($lastPwRequest) < strtotime($reqTime)
381:                         && $this->_db->f($oApiUser->getPrimaryKeyName()) === $oApiUser->get(
382:                             $oApiUser->getPrimaryKeyName()
383:                         )
384:                     ) {
385:                         $lastPwRequest = $reqTime;
386:                     }
387: 
388:                     // check if password request is too old and considered outdated
389:                     // by default 1 day old requests are outdated
390:                     if (false === ($outdatedStr = getEffectiveSetting('pw_request', 'outdated_threshold', false))
391:                         || '' === $outdatedStr
392:                     ) {
393:                         $outdatedStr = '-1 day';
394:                     }
395:                     // convert times to DateTime objects for comparison
396:                     // force all data to be compared using UTC timezone
397:                     $outdated = new DateTime('now', new DateTimeZone('UTC'));
398:                     $outdated->modify($outdatedStr);
399:                     $expiration = new DateTime($oApiUserPasswordRequest->get('expiration'), new DateTimeZone('UTC'));
400:                     if (false === $oApiUserPasswordRequest->get('expiration')
401:                         || '' === $oApiUserPasswordRequest->get('expiration')
402:                         || $expiration < $outdated
403:                     ) {
404:                         // delete password request as it is considered outdated
405:                         $oApiPasswordRequestCol->delete(
406:                             $oApiUserPasswordRequest->get($oApiUserPasswordRequest->getPrimaryKeyName())
407:                         );
408:                     }
409:                 }
410: 
411:                 // get all password reset requests related to entered username in form
412:                 $uid      = $oApiUser->get($oApiUser->getPrimaryKeyName());
413:                 $requests = $oApiPasswordRequestCol->fetchAvailableRequests($uid);
414: 
415:                 // get amount of max password reset requests
416:                 if (false === ($resetThreshold = getEffectiveSetting('pw_request', 'reset_threshold', false))
417:                     || '' === $resetThreshold
418:                 ) {
419:                     // use 4 as default value
420:                     $resetThreshold = 4;
421:                 }
422: 
423:                 // check if there are more than allowed number of password requests for user
424:                 if (count($requests) > $resetThreshold) {
425:                     $isAllowed = false;
426:                     $message   =
427:                         i18n('Too many password reset requests. You may wait before requesting a new password.');
428:                 }
429:                 unset($requests);
430:             }
431: 
432:             // store users mail address to class variable
433:             $this->_email = $this->_db->f('email');
434: 
435:             // check if there is a correct last request date
436:             if (preg_match('/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/', $lastPwRequest, $aMatches)) {
437:                 $lastRequest =
438:                     mktime($aMatches[4], $aMatches[5], $aMatches[6], $aMatches[2], $aMatches[3], $aMatches[1]);
439: 
440:                 // check if this last request is longer ago than timelimit.
441:                 if ((time() - $lastRequest) < (60 * $this->_reloadTime)) {
442:                     // user is not allowed to request new password, he has to wait
443:                     $isAllowed = false;
444:                     $message   = sprintf(i18n('Password requests are allowed every %s minutes.'), $this->_reloadTime);
445:                 }
446:             }
447: 
448:             $this->_username = stripslashes($this->_username);
449: 
450:             // check if syntax of users mail address is correct and there is no
451:             // standard mail address like admin_kunde@IhreSite.de or
452:             // sysadmin@IhreSite.de
453:             if ((!preg_match("/^.+@.+\.([A-Za-z0-9\-_]{1,20})$/", $this->_email)
454:                     || $this->_email == 'sysadmin@IhreSite.de'
455:                     || $this->_email == 'admin_kunde@IhreSite.de')
456:                 && $isAllowed
457:             ) {
458:                 $isAllowed = false;
459:                 // $sMessage = i18n('The requested user has no valid e-mail
460:                 // address. Submitting new password is not possible. Please
461:                 // contact your system- administrator for further support.');
462:                 $message = i18n('No matching data found. Please contact your system administrator.');
463:             }
464: 
465:             // if there are no errors, call function _generateToken(), else wait
466:             // a while, then return error message
467:             if ($isAllowed) {
468:                 // generate a new token
469:                 $token = $this->_generateToken();
470: 
471:                 // how long should the password reset request be valid?
472:                 // use 4 hours as expiration time
473:                 $expiration = new DateTime('+4 hour', new DateTimeZone('UTC'));
474: 
475:                 if (false == $token || false === $this->_safePwResetRequest($token, $expiration)
476:                     || false === $this->_submitMail($token)
477:                 ) {
478:                     $message = i18n('An unknown problem occurred. Please contact your system administrator.');
479:                 } else {
480:                     $message = i18n('New password was submitted to your e-mail address.');
481:                 }
482:             } else {
483:                 sleep(5);
484:             }
485:         } else {
486:             // sleep a while, then return error message
487:             // $sMessage = i18n('This user does not exist.');
488:             $message = i18n('No matching data found. Please contact your system administrator.');
489:             sleep(5);
490:         }
491: 
492:         return $message;
493:     }
494: 
495:     /**
496:      * Function checks password reset request for errors and sets a new password in case there is no error
497:      *
498:      * @throws cDbException
499:      * @throws cException
500:      * @throws cInvalidArgumentException
501:      */
502:     protected function _handleResetPw()
503:     {
504:         $this->_tpl->set('s', 'JS_CALL', 'showResetLayer();');
505:         $username = (string)$_POST['user_name'];
506:         $pw       = (string)$_POST['user_pw'];
507:         $pwRepeat = (string)$_POST['user_pw_repeat'];
508: 
509:         if (0 === cString::getStringLength($username)) {
510:             $this->_tpl->set('s', 'RESET_MESSAGE', i18n('Username can\'t be empty'));
511:             $this->_tpl->set('s', 'RESET_LABEL', '');
512:             $this->_renderNewPwForm();
513: 
514:             return;
515:         }
516:         if ($pw !== $pwRepeat) {
517:             $this->_tpl->set('s', 'RESET_MESSAGE', i18n('Passwords don\'t match'));
518:             $this->_tpl->set('s', 'RESET_LABEL', '');
519:             $this->_renderNewPwForm();
520: 
521:             return;
522:         }
523:         if ((string)$_POST['user_pw'] === (string)$_GET['pw_reset']) {
524:             $this->_tpl->set('s', 'RESET_MESSAGE', i18n('You may not use the confirmation token as password'));
525:             $this->_tpl->set('s', 'RESET_LABEL', '');
526:             $this->_renderNewPwForm();
527: 
528:             return;
529:         }
530: 
531:         // pass data to cApiUser class
532:         $oApiUser = new cApiUser();
533:         $oApiUser->loadUserByUsername($username);
534:         // check if user exists
535:         if (false === $oApiUser->isLoaded()) {
536:             // present same message as if it worked
537:             // so we do not give information whether a user exists
538:             $this->_tpl->set('s', 'RESET_MESSAGE', i18n('New password has been set.'));
539:             $this->_tpl->set('s', 'RESET_LABEL', '');
540:             $this->_tpl->set('s', 'RESET_FORM', '');
541: 
542:             return;
543:         }
544: 
545:         $oPasswordRequest = new cApiUserPasswordRequestCollection();
546:         // check if username matches validation token
547:         // user alice must not be able to set password for a different user bob
548: 
549:         // get available requests for all users
550:         if (null === ($requests = $this->_getCurrentRequests())) {
551:             // no password requests found but do not tell user
552:             $this->_tpl->set('s', 'RESET_MESSAGE', i18n('New password has been set.'));
553:             $this->_tpl->set('s', 'RESET_LABEL', '');
554:             $this->_tpl->set('s', 'RESET_FORM', '');
555: 
556:             return;
557:         }
558: 
559:         // check if passed get parameter matches request for one user
560:         $validUser = false;
561:         foreach ($requests as $request) {
562:             // match validation token against database password reset entry
563:             if ($request->get('validation_token') === $_GET['pw_reset']) {
564:                 // we found the used token
565:                 if ($oApiUser->get($oApiUser->getPrimaryKeyName()) === $request->get($oApiUser->getPrimaryKeyName())) {
566:                     // user entered in form matches user related to validation token
567:                     $validUser = true;
568:                 }
569:             }
570:         }
571:         if (false === $validUser) {
572:             // no password requests found for this user
573:             // but let the user think it could set password for different user
574:             $this->_tpl->set('s', 'RESET_MESSAGE', i18n('New password has been set.'));
575:             $this->_tpl->set('s', 'RESET_LABEL', '');
576:             $this->_tpl->set('s', 'RESET_FORM', '');
577: 
578:             return;
579:         }
580: 
581:         // try to set password
582:         $res = $oApiUser->setPassword($pw);
583: 
584:         // check if password was accepted by cApiUser class
585:         if (cApiUser::PASS_OK !== $res) {
586:             // password not accepted, present error message from cApiUser class to end user
587:             $msg = cApiUser::getErrorString($res);
588:             $this->_tpl->set('s', 'RESET_MESSAGE', $msg);
589:             $this->_tpl->set('s', 'RESET_LABEL', '');
590:             $this->_renderNewPwForm();
591: 
592:             return;
593:         }
594: 
595:         // check if new password can be saved for user
596:         if (false !== $oApiUser->store()) {
597:             $this->_tpl->set('s', 'RESET_LABEL', '');
598:             $this->_tpl->set('s', 'RESET_FORM', '');
599:             // remove all password requests for this user from database
600:             $oPasswordRequest->deleteByUserId($oApiUser->get($oApiUser->getPrimaryKeyName()));
601:             $msg = i18n('New password has been set.');
602:         } else {
603:             // password could not be saved
604:             $msg = i18n('An unknown problem occurred. Please contact your system administrator.');
605:         }
606: 
607:         // display message in form
608:         $this->_tpl->set('s', 'RESET_MESSAGE', $msg);
609:     }
610: 
611:     /**
612:      * Save request into db for future validity check
613:      *
614:      * @param string   $token
615:      *         Token used to check for validity at user confirmation part
616:      * @param DateTime $expiration
617:      *
618:      * @return bool
619:      *         whether password request could be safed successfully
620:      *
621:      * @throws cDbException
622:      * @throws cException
623:      * @throws cInvalidArgumentException
624:      */
625:     protected function _safePwResetRequest($token, DateTime $expiration)
626:     {
627:         $oUserPwRequestCol = new cApiUserPasswordRequestCollection();
628:         $oUserPwRequest    = $oUserPwRequestCol->createNewItem();
629: 
630:         // set request data
631:         $requestTime = new DateTime('now', new DateTimeZone('UTC'));
632:         $oApiUser    = new cApiUser();
633:         $oApiUser->loadBy('username', $this->_username);
634: 
635:         $oUserPwRequest->set($oApiUser->getPrimaryKeyName(), $oApiUser->get($oApiUser->getPrimaryKeyName()));
636:         $oUserPwRequest->set('request', $requestTime->format('Y-m-d H:i:s'));
637:         $oUserPwRequest->set('expiration', $expiration->format('Y-m-d H:i:s'));
638:         $oUserPwRequest->set('validation_token', $token);
639: 
640:         // save request data
641:         return $oUserPwRequest->store();
642:     }
643: 
644:     /**
645:      * Function submits new password to users mail address
646:      *
647:      * @param string $token
648:      *         The token used to authorise password change
649:      *
650:      * @return bool true if successful
651:      * @throws cException
652:      */
653:     protected function _submitMail($token)
654:     {
655:         $cfg = cRegistry::getConfig();
656: 
657:         $token = (string)$token;
658: 
659:         // get translation for mailbody and insert username and new password
660:         $msg      = i18n(
661:             "Dear CONTENIDO-User %s,\n\nA request to change your password for Content Management System CONTENIDO was made. "
662:         );
663:         $msg      .= i18n("Use the following URL to confirm the password change:\n\n");
664:         $msg      .= cRegistry::getBackendUrl() . '?pw_reset=';
665:         $msg      .= i18n("%s\n\nBest regards\n\nYour CONTENIDO sysadmin");
666:         $mailBody = sprintf($msg, $this->_username, $token);
667: 
668:         $from = [$this->_sendermail => $this->_sendername];
669: 
670:         // Decoding and encoding for charsets (without UTF-8)
671:         if ($cfg['php_settings']['default_charset'] != 'UTF-8') {
672:             $subject = utf8_encode(
673:                 conHtmlEntityDecode(
674:                     stripslashes(i18n('Your new password for CONTENIDO Backend')),
675:                     '',
676:                     $cfg['php_settings']['default_charset']
677:                 )
678:             );
679:             $body    = utf8_encode(conHtmlEntityDecode($mailBody, '', $cfg['php_settings']['default_charset']));
680:         } else {
681:             $subject = conHtmlEntityDecode(stripslashes(i18n('Your new password for CONTENIDO Backend')));
682:             $body    = conHtmlEntityDecode($mailBody);
683:         }
684: 
685:         try {
686:             $mailer = new cMailer();
687:             $mailer->sendMail($from, $this->_email, $subject, $body);
688: 
689:             return true;
690:         } catch (cDbException $e) {
691:             return false;
692:         } catch (cInvalidArgumentException $e) {
693:             return false;
694:         } catch (cException $e) {
695:             return false;
696:         }
697:     }
698: 
699:     /**
700:      * Function generates new token
701:      *
702:      * @return string
703:      *         The new token
704:      */
705:     protected function _generateToken()
706:     {
707:         // possible chars which were used in password
708:         $chars = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjkmnopqrstuvwxyz123456789";
709: 
710:         $password = "";
711: 
712:         // for each character of token choose one from $sChars randomly
713:         for ($i = 0; $i < $this->_tokenLength; $i++) {
714:             $password .= $chars[rand(0, cString::getStringLength($chars))];
715:         }
716: 
717:         return $password;
718:     }
719: }
720: 
CMS CONTENIDO 4.10.0 API documentation generated by ApiGen 2.8.0