openrat-cms

OpenRat Content Management System
git clone http://git.code.weiherhei.de/openrat-cms.git
Log | Files | Refs | README

commit 087806f7c37df37ff6648f82fadbf018eccbd67c
parent d1cef8daea90ee57894494d28002659e9703aeee
Author: Jan Dankert <develop@jandankert.de>
Date:   Sat, 31 Oct 2020 00:43:29 +0100

New: Support for OpenId Connect; Removed: Support for LDAP.

Diffstat:
Mmodules/cms/action/LoginAction.class.php | 328++++++++++++++++++-------------------------------------------------------------
Mmodules/cms/action/SearchAction.class.php | 2+-
Mmodules/cms/action/StartAction.class.php | 45---------------------------------------------
Mmodules/cms/action/UserAction.class.php | 5-----
Mmodules/cms/action/UserlistAction.class.php | 12+++++-------
Dmodules/cms/auth/LdapAuth.class.php | 86-------------------------------------------------------------------------------
Dmodules/cms/auth/LdapUserDNAuth.class.php | 74--------------------------------------------------------------------------
Dmodules/cms/auth/OpenIdAuth.class.php | 458-------------------------------------------------------------------------------
Mmodules/cms/base/DefaultConfig.class.php | 72++++++++++++++++++++----------------------------------------------------
Mmodules/cms/model/Group.class.php | 24++++++++++++------------
Mmodules/cms/model/User.class.php | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mmodules/cms/ui/action/IndexAction.class.php | 2+-
Mmodules/cms/ui/themes/default/html/views/element/advanced.php | 50+++++++++++++++++++++++++-------------------------
Mmodules/cms/ui/themes/default/html/views/file/info.php | 4++--
Mmodules/cms/ui/themes/default/html/views/file/pub.php | 2+-
Mmodules/cms/ui/themes/default/html/views/folder/advanced.php | 6+++---
Mmodules/cms/ui/themes/default/html/views/folder/content.php | 4++--
Mmodules/cms/ui/themes/default/html/views/folder/create.php | 14+++++++-------
Mmodules/cms/ui/themes/default/html/views/folder/edit.php | 4++--
Mmodules/cms/ui/themes/default/html/views/folder/inherit.php | 2+-
Mmodules/cms/ui/themes/default/html/views/folder/pub.php | 10+++++-----
Mmodules/cms/ui/themes/default/html/views/folder/show.php | 4++--
Mmodules/cms/ui/themes/default/html/views/group/rights.php | 10+++++-----
Mmodules/cms/ui/themes/default/html/views/image/pub.php | 2+-
Mmodules/cms/ui/themes/default/html/views/image/size.php | 2+-
Mmodules/cms/ui/themes/default/html/views/languagelist/show.php | 8++++----
Mmodules/cms/ui/themes/default/html/views/login/login.php | 220+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mmodules/cms/ui/themes/default/html/views/login/login.tpl.src.xml | 104+++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Amodules/cms/ui/themes/default/html/views/login/oidc.php | 2++
Amodules/cms/ui/themes/default/html/views/login/oidc.tpl.src.xml | 4++++
Dmodules/cms/ui/themes/default/html/views/login/openid.php | 113-------------------------------------------------------------------------------
Dmodules/cms/ui/themes/default/html/views/login/openid.tpl.src.xml | 53-----------------------------------------------------
Mmodules/cms/ui/themes/default/html/views/login/password.php | 4++--
Mmodules/cms/ui/themes/default/html/views/login/register.php | 4++--
Mmodules/cms/ui/themes/default/html/views/modellist/show.php | 6+++---
Mmodules/cms/ui/themes/default/html/views/object/aclform.php | 6+++---
Mmodules/cms/ui/themes/default/html/views/object/info.php | 8++++----
Mmodules/cms/ui/themes/default/html/views/object/inherit.php | 2+-
Mmodules/cms/ui/themes/default/html/views/object/rights.php | 14+++++++-------
Mmodules/cms/ui/themes/default/html/views/page/edit.php | 2+-
Mmodules/cms/ui/themes/default/html/views/page/form.php | 14+++++++-------
Mmodules/cms/ui/themes/default/html/views/page/info.php | 4++--
Mmodules/cms/ui/themes/default/html/views/page/pub.php | 2+-
Mmodules/cms/ui/themes/default/html/views/pageelement/advanced.php | 4++--
Mmodules/cms/ui/themes/default/html/views/pageelement/diff.php | 8++++----
Mmodules/cms/ui/themes/default/html/views/pageelement/history.php | 30+++++++++++++++---------------
Mmodules/cms/ui/themes/default/html/views/pageelement/link.php | 8++++----
Mmodules/cms/ui/themes/default/html/views/pageelement/pub.php | 2+-
Mmodules/cms/ui/themes/default/html/views/pageelement/value.php | 38+++++++++++++++++++-------------------
Mmodules/cms/ui/themes/default/html/views/profile/memberships.php | 2+-
Mmodules/cms/ui/themes/default/html/views/profile/pw.php | 4++--
Mmodules/cms/ui/themes/default/html/views/project/edit.php | 2+-
Mmodules/cms/ui/themes/default/html/views/project/history.php | 10+++++-----
Mmodules/cms/ui/themes/default/html/views/project/prop.php | 2+-
Mmodules/cms/ui/themes/default/html/views/start/userprojecttimeline.php | 8++++----
Mmodules/cms/ui/themes/default/html/views/template/edit.php | 2+-
Mmodules/cms/ui/themes/default/html/views/template/listing.php | 2+-
Mmodules/cms/ui/themes/default/html/views/template/pub.php | 2+-
Mmodules/cms/ui/themes/default/html/views/template/srcelement.php | 4++--
Mmodules/cms/ui/themes/default/html/views/templatelist/show.php | 2+-
Mmodules/cms/ui/themes/default/html/views/text/pub.php | 2+-
Mmodules/cms/ui/themes/default/html/views/text/size.php | 2+-
Mmodules/cms/ui/themes/default/html/views/title/show.php | 18+++++++++---------
Mmodules/cms/ui/themes/default/html/views/user/info.php | 2+-
Mmodules/cms/ui/themes/default/html/views/user/prop.php | 9+--------
Mmodules/cms/ui/themes/default/html/views/user/prop.tpl.src.xml | 4----
Mmodules/cms/ui/themes/default/html/views/user/pw.php | 4++--
Mmodules/cms/ui/themes/default/html/views/user/rights.php | 12++++++------
Mmodules/cms/ui/themes/default/html/views/userlist/show.php | 2+-
Mmodules/cms/ui/themes/default/script/openrat.js | 3+++
Mmodules/cms/ui/themes/default/script/openrat.min.js | 2+-
Mmodules/cms/ui/themes/default/script/plugin/jquery-plugin-orLinkify.js | 3+++
Mmodules/cms/update/Update.class.php | 8++++----
Amodules/cms/update/version/DBVersion000022.class.php | 37+++++++++++++++++++++++++++++++++++++
Mmodules/configuration/Config.class.php | 2+-
Mmodules/database/Column.class.php | 25++++++++++++++++++-------
Mmodules/database/DbVersion.class.php | 4++--
Mmodules/database/Statement.class.php | 19++++++++++++++++++-
Mmodules/database/Table.class.php | 11++++++++---
Amodules/openid_connect/OpenIDConnectClient.php | 1735+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/phpseclib/AUTHORS | 6++++++
Amodules/phpseclib/BACKERS.md | 9+++++++++
Amodules/phpseclib/Crypt/RSA.class.php | 3203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/phpseclib/LICENSE | 21+++++++++++++++++++++
Amodules/phpseclib/Math/BigInteger.class.php | 3787+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/phpseclib/README.md | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmodules/template_engine/engine/TemplateEngine.class.php | 4++--
87 files changed, 9534 insertions(+), 1553 deletions(-)

diff --git a/modules/cms/action/LoginAction.class.php b/modules/cms/action/LoginAction.class.php @@ -12,6 +12,9 @@ use cms\model\Group; use configuration\Config; +use Exception; +use http\Env\Request; +use openid_connect\OpenIDConnectClient; use util\FileUtils; use util\Http; use cms\auth\InternalAuth; @@ -164,6 +167,44 @@ class LoginAction extends BaseAction } + public function oidcView() { + + $providerName = $this->request->getRequiredRequestVar('id',RequestParams::FILTER_ALPHANUM); + + $providerConfig = Configuration::subset(['security']['oidc']['provider'][$providerName]); + + $oidc = new OpenIDConnectClient(); + $oidc->setProviderURL ( $providerConfig->get('url' )); + $oidc->setClientID ( $providerConfig->get('client_id' )); + $oidc->setClientSecret( $providerConfig->get('client_secret')); + + try { + $oidc->authenticate(); + $subjectIdentifier = $oidc->requestUserInfo('sub'); + + $user = User::loadWithName( $subjectIdentifier,User::AUTH_TYPE_OIDC,$providerName ); + + if ( ! $user ) { + // Create user + $user = new User(); + $user->name = $subjectIdentifier; + $user->type = User::AUTH_TYPE_OIDC; + $user->issuer = $providerName; + $user->add(); + + } + + Session::setUser( $user ); + + } catch( Exception $e) { + throw new \RuntimeException('OpenId-Connect authentication failed',0,$e); + } + + header( 'Location: ./'); + } + + + /** * Anzeigen der Loginmaske. * @@ -172,8 +213,6 @@ class LoginAction extends BaseAction */ function loginView() { - // Hier nie "304 not modified" setzen, da sonst keine - // Login-Fehlermeldung erscheinen kann. $conf = Configuration::rawConfig(); $sso = $conf['security']['sso']; @@ -183,6 +222,27 @@ class LoginAction extends BaseAction $ssl_user_var = ''; extract( $ssl, EXTR_PREFIX_ALL, 'ssl' ); + $oidcList = []; + + $authenticateConfig = Configuration::subset('authenticate'); + $authenticateEnabled = $authenticateConfig->is('enable',true); + + + + $oidcConfig = Configuration::subset(['security','oidc']); + + if ( $oidcConfig->is('enabled',true) ) { + foreach ( $oidcConfig->subset('provider')->subsets() as $name=>$providerConfig ) { + if ( $providerConfig->is('enabled',true)) { + $oidcList[ $name ] = $providerConfig->get('label',$name ); + } + } + } + + $this->setTemplateVar('enableUserPasswordLogin',$authenticateEnabled); + $this->setTemplateVar('enableOpenIdConnect' ,(boolean)$oidcList ); + $this->setTemplateVar('provider' ,$oidcList ); + if ( $sso['enable'] ) { $authid = $this->getRequestVar( $sso['auth_param_name']); @@ -245,7 +305,7 @@ class LoginAction extends BaseAction $username = $treffer[1]; - $user = User::loadWithName( $username ); + $user = User::loadWithName( $username,User::AUTH_TYPE_INTERNAL ); if ( ! $user->isValid( )) throw new \util\exception\SecurityException('authorization failed: user not found: '.$username); @@ -266,7 +326,7 @@ class LoginAction extends BaseAction if ( empty($username) ) throw new \util\exception\SecurityException( 'no username in client certificate ('.$ssl_user_var.') (or there is no client certificate...?)' ); - $user = User::loadWithName( $username ); + $user = User::loadWithName( $username,User::AUTH_TYPE_INTERNAL ); if ( !$user->isValid() ) throw new \LogicException( 'unknown username: '.$username ); @@ -395,55 +455,6 @@ class LoginAction extends BaseAction } - /** - * Anzeigen der Loginmaske. - * - * Es wird nur die Loginmaske angezeigt. - * Hier nie "304 not modified" setzen, da sonst keine - * Login-Fehlermeldung erscheinen kann - */ - function openidView() - { - $conf = Configuration::rawConfig(); - - foreach( $conf['database'] as $dbname=>$dbconf ) - { - if ( is_array($dbconf) && $dbconf['enabled'] ) - $dbids[$dbname] = array('key' =>$dbname, - 'value'=>Text::maxLength($dbconf['description']), - 'title'=>$dbconf['description'].(isset($dbconf['host'])?' ('.$dbconf['host'].')':'') ); - } - - $openid_provider = array(); - foreach( explode(',',$conf['security']['openid']['provider']['name']) as $provider ) - $openid_provider[$provider] = Configuration::config('security','openid','provider.'.$provider.'.name'); - $this->setTemplateVar('openid_providers',$openid_provider); - $this->setTemplateVar('openid_user_identity', Configuration::config('security','openid','user_identity')); - //$this->setTemplateVar('openid_provider','identity'); - - - if ( empty($dbids) ) - $this->addNotice('', 0, '', 'no_database_configuration', Action::NOTICE_WARN); - - if ( !isset($_COOKIE['or_username']) ) - $this->setTemplateVar('login_name',$_COOKIE['or_username']); - else - $this->setTemplateVar('login_name',$conf['security']['default']['username']); - - $this->setTemplateVar( 'dbids',$dbids ); - - $db = DB::get(); - if ( is_object($db) ) - $this->setTemplateVar('actdbid',$db->id); - else - $this->setTemplateVar('actdbid',$conf['database']['default']); - - $this->setTemplateVar('objectid' ,$this->getRequestVar('objectid' ,RequestParams::FILTER_NUMBER) ); - $this->setTemplateVar('projectid' ,$this->getRequestVar('projectid' ,RequestParams::FILTER_NUMBER) ); - $this->setTemplateVar('modelid' ,$this->getRequestVar('modelid' ,RequestParams::FILTER_NUMBER) ); - $this->setTemplateVar('languageid',$this->getRequestVar('languageid',RequestParams::FILTER_NUMBER) ); - - } @@ -490,171 +501,6 @@ class LoginAction extends BaseAction - - /** - * Open-Id Login, ?berpr?fen der Anmeldung.<br> - * Spezifikation: http://openid.net/specs/openid-authentication-1_1.html<br> - * Kapitel "4.4. check_authentication"<br> - * <br> - * Im 2. Schritt (Mode "id_res") erfolgte ein Redirect vom Open-Id Provider an OpenRat zur?ck.<br> - * Wir befinden uns nun im darauf folgenden Request des Browsers.<br> - * <br> - * Es muss noch beim OpenId-Provider die Best?tigung eingeholt werden, danach ist der - * Benutzer angemeldet.<br> - */ - public function openidloginView() - { - $conf = Configuration::rawConfig(); - $openId = Session::get('openid'); - - if ( !$openId->checkAuthentication() ) - { - throw new \util\exception\SecurityException('OpenId-Login failed' ); - } - - //Html::debug($openId); - - // Anmeldung wurde mit "is_valid:true" best?tigt. - // Der Benutzer ist jetzt eingeloggt. - $username = $openId->getUserFromIdentiy(); - - Logger::debug("OpenId-Login successful for $username"); - - if ( empty($username) ) - { - // Es konnte kein Benutzername ermittelt werden. - throw new \util\exception\SecurityException('no username supplied by openid provider' ); - } - - $user = User::loadWithName( $username ); - - if ( $user->userid <=0) - { - // Benutzer ist (noch) nicht vorhanden. - if ( $conf['security']['openid']['add']) // Anlegen? - { - $user->name = $username; - $user->add(); - - $user->mail = @$openId->info['email']; - $user->fullname = @$openId->info['fullname']; - $user->save(); // Um E-Mail zu speichern (wird bei add() nicht gemacht) - } - else - { - Logger::debug("OpenId-Login failed for $username"); - // Benutzer ist nicht in Benutzertabelle vorhanden (und angelegt werden soll er auch nicht). - throw new \util\exception\SecurityException('user',$username,'LOGIN_OPENID_FAILED','error',array('name'=>$username) ); - } - } - else - { - // Benutzer ist bereits vorhanden. - if ( @$conf['security']['openid']['update_user']) - { - $user->fullname = @$openId->info['fullname']; - $user->mail = @$openId->info['email']; - $user->save(); - } - } - - Logger::info("User login successful: ".$username); - $user->setCurrent(); // Benutzer ist jetzt in der Sitzung. - - $server = Http::getServer(); - Logger::debug("Redirecting to $server"); - header('Location: '.FileUtils::slashify($server) ); - exit(); - } - - - /** - * Login. - */ - function openidPost() - { - $conf = Configuration::rawConfig(); - - Session::setUser(''); - - if ( $conf['login']['nologin'] ) - throw new \util\exception\SecurityException('login disabled'); - - $openid_user = $this->getRequestVar('openid_url' ); - $loginName = $this->getRequestVar('login_name' ,RequestParams::FILTER_ALPHANUM); - $loginPassword = $this->getRequestVar('login_password',RequestParams::FILTER_ALPHANUM); - $newPassword1 = $this->getRequestVar('password1' ,RequestParams::FILTER_ALPHANUM); - $newPassword2 = $this->getRequestVar('password2' ,RequestParams::FILTER_ALPHANUM); - - // Cookie setzen - $this->setCookie('or_username',$loginName ); - - // Login mit Open-Id. - if ( $this->hasRequestVar('openid_provider') && ($this->getRequestVar('openid_provider') != 'identity' || !empty($openid_user)) ) - { - $openId = new OpenId($this->getRequestVar('openid_provider'),$openid_user); - - if ( ! $openId->login() ) - { - $this->addNotice('user', 0, $openid_user, 'LOGIN_OPENID_FAILED', 'error', array('name' => $openid_user), array($openId->error)); - $this->addValidationError('openid_url',''); - $this->callSubAction('showlogin'); - return; - } - - Session::set('openid',$openId); - //$this->redirect( $openId->getRedirectUrl() ); - return; - } - } - - - /** - * Synchronisiert die bisherigen Gruppen des Benutzers mit den Gruppen, die sich aus der Authentifzierung ergeben haben. - * - * @param $user User Benutzerobjekt - * @param $groups array $groups Einfaches Array von Gruppennamen. - */ - private function checkGroups($user, $groups) - { - if ( $groups == null ) - return; - - $oldGroups = $user->getGroups(); - - foreach( $oldGroups as $id=>$name) - { - if ( !in_array($name,$groups) ) - $user->delGroup($id); - } - - foreach( $groups as $name) - { - if ( ! in_array($name,$oldGroups)) - { - try - { - $group = Group::loadWithName( $name ); - $user->addGroup($group->groupid); - } - catch (ObjectNotFoundException $e) - { - // Gruppe fehlt. Anlegen? - if ( Configuration::config('ldap','authorize','auto_add' ) ) - { - // Die Gruppe in der OpenRat-Datenbank hinzufuegen. - $g = new Group(); - $g->name = $group; - $g->add(); // Gruppe hinzufuegen - $user->addGroup($g->groupid); // Und Gruppe dem Benutzer hinzufuegen. - } - - } - } - } - } - - /** * Login. * Zuerst wird die Datenbankverbindung aufgebaut und falls notwendig, aktualisiert. @@ -702,7 +548,7 @@ class LoginAction extends BaseAction else { // Kennwoerter identisch und lang genug. - $user = User::loadWithName($loginName); + $user = User::loadWithName($loginName,User::AUTH_TYPE_INTERNAL); $user->setPassword( $newPassword1,true ); // Das neue gesetzte Kennwort für die weitere Authentifizierung benutzen. @@ -729,7 +575,6 @@ class LoginAction extends BaseAction $loginOk = false; $mustChangePassword = false; $tokenFailed = false; - $groups = null; $lastModule = null; // Jedes Authentifizierungsmodul durchlaufen, bis ein Login erfolgreich ist. @@ -751,9 +596,6 @@ class LoginAction extends BaseAction Logger::info('Login successful for '.$loginName); $lastModule = $module; - if ( isset($auth->groups ) ) - $groups = $auth->groups; - break; // Login erfolgreich, erstes Modul gewinnt. } } @@ -772,7 +614,7 @@ class LoginAction extends BaseAction try { // Benutzer über den Benutzernamen laden. - $user = User::loadWithName($loginName); + $user = User::loadWithName($loginName,User::AUTH_TYPE_INTERNAL,null); $user->loginModuleName = $lastModule; $user->setCurrent(); $user->updateLoginTimestamp(); @@ -847,8 +689,6 @@ class LoginAction extends BaseAction Logger::debug("Login successful for user '$loginName' from IP $ip"); - $this->checkGroups( $user, $groups ); - if ( $this->hasRequestVar('remember') ) { // Cookie setzen @@ -944,41 +784,21 @@ class LoginAction extends BaseAction * die Benutzerinformationen des angemeldeten Benutzers aus dieser * Anwendung auslesen k?nnen. */ - function userinfo() + function userinfoView() { $user = Session::getUser(); + $info = array('username' => $user->name, 'fullname' => $user->fullname, 'mail' => $user->mail, 'telephone' => $user->tel, 'style' => $user->style, - 'admin' => $user->isAdmin?'true':'false', - 'ldap' => $user->ldap_dn, + 'admin' => $user->isAdmin, 'groups' => implode(',',$user->getGroups()), 'description'=> $user->desc ); - // Wenn der HTTP-Parameter "xml" vorhanden ist, dann geben wir die - // Informationen per XML aus. - if ( $this->hasRequestVar('xml') ) - { - header('Content-Type: text/xml'); - echo '<userinfo>'; - foreach( $info as $n=>$i ) - echo '<'.$n.'>'.$i.'</'.$n.'>'."\n"; - echo '</userinfo>'; - - } - - // Sonst normale Textausgabe im INI-Datei-Format. - else - { - header('Content-Type: text/plain'); - foreach( $info as $n=>$i ) - echo $n.'="'.$i."\"\n"; - } - - exit; // Fertig. + $this->setTemplateVar('userinfo',$info); } @@ -1009,7 +829,7 @@ class LoginAction extends BaseAction if ( $conf['security']['guest']['enable'] ) { $username = $conf['security']['guest']['user']; - $user = User::loadWithName($username); + $user = User::loadWithName($username,User::AUTH_TYPE_INTERNAL); if ( $user->userid > 0 ) $user->setCurrent(); else @@ -1168,7 +988,7 @@ class LoginAction extends BaseAction return; } - $user = User::loadWithName( $this->getRequestVar('username') ); + $user = User::loadWithName( $this->getRequestVar('username'),User::AUTH_TYPE_INTERNAL ); if ( $user->isValid() ) { $this->addValidationError('username','USER_ALREADY_IN_DATABASE'); @@ -1225,7 +1045,7 @@ class LoginAction extends BaseAction return; } - $user = User::loadWithName( $this->getRequestVar("username") ); + $user = User::loadWithName( $this->getRequestVar("username"),User::AUTH_TYPE_INTERNAL ); // Html::debug($user); Password::delay(); if ( $user->isValid() ) @@ -1282,7 +1102,7 @@ class LoginAction extends BaseAction return; } - $user = User::loadWithName( $username ); + $user = User::loadWithName( $username,User::AUTH_TYPE_INTERNAL ); if ( !$user->isValid() ) { diff --git a/modules/cms/action/SearchAction.class.php b/modules/cms/action/SearchAction.class.php @@ -175,7 +175,7 @@ class SearchAction extends BaseAction { if ( $this->userIsAdmin() ) { - $user = User::loadWithName($searchText); + $user = User::loadWithName($searchText,User::AUTH_TYPE_INTERNAL); if (is_object($user)) { $userResult = array('url' => '', 'type' => 'user', diff --git a/modules/cms/action/StartAction.class.php b/modules/cms/action/StartAction.class.php @@ -725,51 +725,6 @@ class StartAction extends BaseAction - /** - * Ausgeben von maschinenlesbaren Benutzerinformationen. - * - * Diese Funktion dient dem Single-Signon f�r fremde Anwendungen, welche - * die Benutzerinformationen des angemeldeten Benutzers aus dieser - * Anwendung auslesen k�nnen. - */ - function userinfo() - { - $user = Session::getUser(); - $info = array('username' => $user->name, - 'fullname' => $user->fullname, - 'mail' => $user->mail, - 'telephone' => $user->tel, - 'style' => $user->style, - 'admin' => $user->isAdmin?'true':'false', - 'ldap' => $user->ldap_dn, - 'groups' => implode(',',$user->getGroups()), - 'description'=> $user->desc - ); - - // Wenn der HTTP-Parameter "xml" vorhanden ist, dann geben wir die - // Informationen per XML aus. - if ( $this->hasRequestVar('xml') ) - { - header('Content-Type: text/xml'); - echo '<userinfo>'; - foreach( $info as $n=>$i ) - echo '<'.$n.'>'.$i.'</'.$n.'>'."\n"; - echo '</userinfo>'; - - } - - // Sonst normale Textausgabe im INI-Datei-Format. - else - { - header('Content-Type: text/plain'); - foreach( $info as $n=>$i ) - echo $n.'="'.$i."\"\n"; - } - - exit; // Fertig. - } - - function project() { $user = Session::getUser(); diff --git a/modules/cms/action/UserAction.class.php b/modules/cms/action/UserAction.class.php @@ -82,7 +82,6 @@ class UserAction extends BaseAction $this->user->name = $this->getRequestVar('name' ); $this->user->fullname = $this->getRequestVar('fullname'); $this->user->isAdmin = $this->hasRequestVar('is_admin'); - $this->user->ldap_dn = $this->getRequestVar('ldap_dn' ); $this->user->tel = $this->getRequestVar('tel' ); $this->user->desc = $this->getRequestVar('desc' ); $this->user->language = $this->getRequestVar('language'); @@ -313,10 +312,6 @@ class UserAction extends BaseAction $this->setTemplateVar($varName,$hasGroup); } $this->setTemplateVar('memberships',$gruppenListe); - - $conf = \cms\base\Configuration::rawConfig(); - if ($conf['security']['authorize']['type']=='ldap') - $this->addNotice('user', 0, $this->user->name, 'GROUPS_MAY_CONFLICT_WITH_LDAP', Action::NOTICE_WARN); } diff --git a/modules/cms/action/UserlistAction.class.php b/modules/cms/action/UserlistAction.class.php @@ -50,7 +50,7 @@ class UserlistAction extends BaseAction $list[$user->userid]['id' ] = $user->userid; } $this->setTemplateVar('el',$list); - } + } /** @@ -61,9 +61,8 @@ class UserlistAction extends BaseAction $this->nextSubAction('show'); } - - function addView() + public function addView() { } @@ -75,17 +74,16 @@ class UserlistAction extends BaseAction { $name = $this->request->cleanText($name,RequestParams::FILTER_ALPHANUM); - $user = User::loadWithName($name); + $user = User::loadWithName($name,User::AUTH_TYPE_INTERNAL); if ( !empty($user) ) throw new ValidationException( 'name',Messages::USER_ALREADY_IN_DATABASE); $user = new User(); - $user->add( $name ); + $user->name = $name; + $user->add(); $this->addNoticeFor($user, Messages::ADDED); } - - } \ No newline at end of file diff --git a/modules/cms/auth/LdapAuth.class.php b/modules/cms/auth/LdapAuth.class.php @@ -1,85 +0,0 @@ -<?php - -namespace cms\auth; - -use cms\auth\Auth; -use logger\Logger; -use User; -use util\Ldap; - -class LdapAuth implements Auth -{ - - public function login($username, $password, $token) - { - - // Falls Modul LDAP nicht vorhanden ist können wir gleich beenden. - if (!extension_loaded('ldap')) { - - Logger::warn("LdapAuth: LDAP Login is not possible: LDAP-Extension ist not loaded."); - return false; - } - - $conf = \cms\base\Configuration::rawConfig(); - $db = \cms\base\DB::get(); - $this->mustChangePassword = false; - - // Lesen des Benutzers aus der DB-Tabelle - $sql = $db->sql(<<<SQL -SELECT * FROM {{user}} - WHERE name={name} -SQL - ); - $sql->setString('name', $username); - - $row_user = $sql->getRow(); - $userid = $row_user['id']; - - $ldap = new Ldap(); - $ldap->connect(); - - if (empty($conf['ldap']['dn'])) { - // Der Benutzername wird im LDAP-Verzeichnis gesucht. - // Falls gefunden, wird der DN (=der eindeutige Schl�ssel im Verzeichnis) ermittelt. - $dn = $ldap->searchUser($username); - - if (empty($dn)) { - Logger::debug('User not found in LDAP directory'); - return false; // Kein LDAP-Account gefunden. - } - - Logger::debug('User found: ' . $dn); - } else { - $dn = str_replace('{user}', $username, $conf['ldap']['dn']); - } - - // LDAP-Login versuchen - $ok = $ldap->bind($dn, $password); - - Logger::debug('LDAP bind: ' . ($ok ? 'success' : 'failed')); - - if (!$ok) - return false; - - $sucheAttribut = $conf['ldap']['authorize']['group_name']; - $sucheFilter = str_replace('{dn}', $dn, $conf['ldap']['authorize']['group_filter']); - - $this->groups = $ldap->searchAttribute($sucheFilter, $sucheAttribut); - $user = new User($userid); - - // Html::debug($this->groups,'Gruppen/Ids des Benutzers'); - - // Verbindung zum LDAP-Server brav beenden - $ldap->close(); - - return true; - } - - public function username() - { - return null; - } - -} - -?>- \ No newline at end of file diff --git a/modules/cms/auth/LdapUserDNAuth.class.php b/modules/cms/auth/LdapUserDNAuth.class.php @@ -1,73 +0,0 @@ -<?php - -namespace cms\auth; - -use cms\auth\Auth; -use logger\Logger; -use util\Ldap; - -/** - * Authentifizierung gegen einen LDAP-Server. - * - * @author Jan Dankert - */ -class LdapUserDNAuth implements Auth -{ - /** - * @see Auth::login() - */ - public function login($username, $password, $token) - { - $db = \cms\base\DB::get(); - $this->mustChangePassword = false; - - // Lesen des Benutzers aus der DB-Tabelle - $sql = $db->sql(<<<SQL -SELECT * FROM {{user}} - WHERE name={name} -SQL - ); - $sql->setString('name', $username); - - $row_user = $sql->getRow(); - - if (empty($row_user)) - return false; - - // Benutzername ist bereits in der Datenbank. - $userid = $row_user['id']; - $ldap_dn = $row_user['ldap_dn']; - - if (empty($ldap_dn)) - return false; - - // Falls Modul LDAP nicht vorhanden ist können wir gleich beenden. - if (!extension_loaded('ldap')) { - - Logger::warn("LDAP Login is not possible: LDAP-Extension ist not loaded."); - return false; - } - - - Logger::debug('checking login via ldap'); - $ldap = new Ldap(); - $ldap->connect(); - - // Benutzer ist bereits in Datenbank - // LDAP-Login mit dem bereits vorhandenen DN versuchen - $ok = $ldap->bind($ldap_dn, $password); - - // Verbindung zum LDAP-Server brav beenden - $ldap->close(); - - return $ok; - } - - public function username() - { - return null; - } - -} - -?>- \ No newline at end of file diff --git a/modules/cms/auth/OpenIdAuth.class.php b/modules/cms/auth/OpenIdAuth.class.php @@ -1,457 +0,0 @@ -<?php - -namespace cms\auth; - -use cms\auth\Auth; -use logger\Logger; -use OpenId; -use Parameter; -use unknown; -use util\FileUtils; -use util\Http; - - -/** - * Open-Id Authentisierung gem�� OpenId-Spezifikation 1.0. - * - */ -class OpenIdAuth implements Auth -{ - function username() - { - return null; - } - - - function login($username, $password, $token) - { - return false; - } - - - function redirect() - { - $this->login2(); - return $this->getRedirectUrl(); - } - - - function checkToken() - { - $this->checkAuthentication(); - } - - /** - * Open-Id Server, an den die Authentisierungsanfrage gestellt wird. - * - * @var String - */ - var $server; - - - /** - * Informationen zum Benutzer. - * - * @var Array - */ - var $info; - - /** - * Open-Id Identity. - * - * @var String - */ - var $identity; - - /** - * Fehlermeldung (falls vorhanden). - * - * @var String - */ - var $error; - - /** - * OpenId-Benutzername. - * - * @var String - */ - var $user; - - /** - * OpenId-Provider. - * - * @var String - */ - var $provider; - - - var $supportAX; - var $supportSREG; - var $supportOpenId1_1; - var $supportOpenId2_0; - - - /** - * Neue Open-Id Anfrage. - * - * @param String $user - * @return OpenId - */ - function OpenId($provider = '', $user = '') - { - $this->provider = $provider; - $this->user = $user; - } - - - /** - * Stellt fest, ob der Server vertrauenswuerdig ist. - * - * @return true, wenn vertrauenswuerdig. - */ - function serverOk() - { - $conf = \cms\base\Configuration::rawConfig(); - $servers = $conf['security']['openid']['trusted_server']; - - if (empty($servers)) { - return true; - } else { - $serverList = explode(',', $servers); - - $http = new Http($this->server); - if (!in_array($http->url['host'], $serverList)) { - $this->error = 'Server ' . $this->server . ' is not trusted'; - return false; - } else - return true; - } - - } - - - /** - * Authentisierung Schritt 1.<br> - * Ermitteln der Identity. - * - * @return boolean TRUE, wenn Identity ermittelt wurde. - */ - function login2() - { - if ($this->provider != 'identity') { - $this->user = \cms\base\Configuration::config('security', 'openid', 'provider.' . $this->provider . '.xrds_uri'); - $this->identity = 'http://specs.openid.net/auth/2.0/identifier_select'; - } - $this->supportSREG = \cms\base\Configuration::config('security', 'openid', 'provider.' . $this->provider . '.sreg_1_0'); - $this->supportAX = \cms\base\Configuration::config('security', 'openid', 'provider.' . $this->provider . '.ax_1_0'); - - // Schritt 1: Identity aus Yadis-Dokument laden. - $this->getIdentityFromYadis(); - - // Schritt 2: Fallback auf HTML-Dokument. - if (empty($this->server)) { - $this->getIdentityFromHtmlMetaData(); - } - - // Falls immer noch kein Servername gefunden wurde, dann Abbruch. - if (empty($this->server)) { - if (empty($this->error)) - $this->error = 'Unable to locate OpenId-Server in URL'; - return false; - } - - if (!$this->serverOk()) - return false; // Server nicht vertrauenswuerdig. - - if (empty($this->identity)) - // Falls die Identity bis hierher nicht deligiert wurde... - // Lt. Spezifikation mit Prefix "http://". - $this->identity = 'http://' . $this->user; - - return true; - } - - - /** - * Erzeugt einen HTTP-Redirect auf den OpenId-Provider. - */ - public function getRedirectUrl() - { - $conf = \cms\base\Configuration::rawConfig(); - - $this->handle = md5(microtime() . session_id()); - - $redirHttp = new Http($this->server); - - if ($this->supportOpenId2_0) - $redirHttp->requestParameter['openid.ns'] = 'http://specs.openid.net/auth/2.0'; - - $redirHttp->requestParameter['openid.mode'] = 'checkid_setup'; - $redirHttp->requestParameter['openid.identity'] = $this->identity; - - if ($this->supportOpenId2_0) - $redirHttp->requestParameter['openid.claimed_id'] = $this->identity; - - - // Profilangaben anfordern. E-Mail wird ben�tigt, Name und Sprache sind optional. - - if ($this->supportAX) { - Logger::info("OpenId-Server is using OpenID Attribute Exchange 1.0"); - $redirHttp->requestParameter['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0'; - $redirHttp->requestParameter['openid.ax.mode'] = 'fetch_request'; - $redirHttp->requestParameter['openid.ax.type.email'] = 'http://axschema.org/contact/email'; - $redirHttp->requestParameter['openid.ax.type.username'] = 'http://axschema.org/namePerson/friendly'; - $redirHttp->requestParameter['openid.ax.type.fullname'] = 'http://axschema.org/namePerson'; - $redirHttp->requestParameter['openid.ax.type.language'] = 'http://axschema.org/pref/language'; - $redirHttp->requestParameter['openid.ax.required'] = 'username,email'; - $redirHttp->requestParameter['openid.ax.if_available'] = 'language,fullname'; - } - - if ($this->supportSREG) { - Logger::info("OpenId-Server is using OpenID Simple Registration Extension 1.0"); - $redirHttp->requestParameter['openid.ns.sreg'] = 'http://openid.net/sreg/1.0'; - $redirHttp->requestParameter['openid.sreg.required'] = 'email,nickname'; - $redirHttp->requestParameter['openid.sreg.optional'] = 'fullname,language'; - } - - $trustRoot = @$conf['security']['openid']['trust_root']; - $server = Http::getServer(); - if (empty($trustRoot)) - $trustRoot = $server; - - $redirHttp->requestParameter['openid.trust_root'] = FileUtils::slashify($trustRoot); - $redirHttp->requestParameter['openid.return_to'] = FileUtils::slashify($server) . 'openid.php'; // FIXME url - //$redirHttp->requestParameter['openid.realm' ] = slashify($server).'openid.'.PHP_EXT; - $redirHttp->requestParameter['openid.assoc_handle'] = $this->handle; - - return $redirHttp->getUrl(); - } - - - /** - * Ermittelt OpenId-Server und OpenId-Identity aus Yadis-Dokument.<br> - * - * @return unknown - */ - private function getIdentityFromYadis() - { - $http = new Http($this->user); -// $http->url['host'] = $this->user; - - $http->header[] = 'Accept: application/xrds+xml'; - if (!$http->request()) { - $this->error = 'Unable to get XML delegate information'; - return false; - } - - Logger::debug("OpenId: Found YADIS-document for " . $http->getUrl()); - //die(); - $p = xml_parser_create(); - $ok = xml_parse_into_struct($p, $http->body, $vals, $index); - xml_parser_free($p); - - foreach ($vals as $tag) { - if (strtolower($tag['tag']) == 'type') { - if ($tag['value'] == 'http://openid.net/srv/ax/1.0') - $this->supportAX = true; - - if ($tag['value'] == 'http://openid.net/sreg/1.0') - $this->supportSREG = true; - - if ($tag['value'] == 'http://openid.net/signon/1.1') - $this->supportOpenId1_1 = true; - - if ($tag['value'] == 'http://specs.openid.net/auth/2.0/server') - $this->supportOpenId2_0 = true; - } - - if (strtolower($tag['tag']) == 'uri') { - $this->server = $tag['value']; - } - - if (strtolower($tag['tag']) == 'openid:delegate') { - $this->identity = $tag['value']; - } - } - - if (!$this->supportOpenId1_1 && !$this->supportOpenId2_0) { - $this->error = 'Only OpenId 1.1 and 2.0 is supported but this identity-provider does not seem to support any of these.'; - return false; - } - if (!$this->supportAX && !$this->supportSREG) { - $this->error = 'The identity-provider must support either Attribute-Exchange (AX) oder Simple-Registration (SREG), but it does not seem to support any of these.'; - return false; - } - } - - - /** - * Ermittelt OpenId-Server und OpenId-Identity aus HTML Meta-Tags.<br> - */ - private function getIdentityFromHtmlMetaData() - { - $http = new Http($this->user); -// $http = new Http(); -// $http->url['host'] = $this->user; - $http->header[] = 'Accept: text/html'; - - if (!$http->request()) { - $this->error = 'Unable to get HTML delegate information'; - return false; - } - - $seite = $http->body; - - // Die Meta-Tags mit regulaerem Ausdruck auslesen. - $treffer = array(); - preg_match('/rel="openid.server"\s+href="(\S+)"/', $seite, $treffer); - if (count($treffer) >= 1) { - $this->server = $treffer[1]; - $this->supportOpenId1_1 = true; - } - - $treffer = array(); - preg_match('/rel="openid2.provider"\s+href="(\S+)"/', $seite, $treffer); - if (count($treffer) >= 1) { - $this->supportOpenId2_0 = true; - $this->server = $treffer[1]; - } - - $treffer = array(); - preg_match('/rel="openid.delegate"\s+href="(\S+)"/', $seite, $treffer); - if (count($treffer) >= 1) - $this->identity = $treffer[1]; - } - - - /** - * Ermittelt den Hostnamen aus der Identity. - * - * @return String - */ - public function getUserFromIdentiy() - { - if ($this->provider == 'identity') { - $http = new Http($this->identity); - return $http->url['host']; - } else { - $attribute_name = \cms\base\Configuration::config('security', 'openid', 'provider.' . $this->provider . '.map_attribute'); - return $this->info[$attribute_name]; - } - } - - - /** - * Open-Id Login, �berpr�fen der Anmeldung.<br> - * Spezifikation: http://openid.net/specs/openid-authentication-1_1.html<br> - * Kapitel "4.4. check_authentication"<br> - * <br> - * Im 2. Schritt (Mode "id_res") erfolgte ein Redirect vom Open-Id Provider an OpenRat zur�ck.<br> - * Wir befinden uns nun im darauf folgenden Request des Browsers.<br> - * <br> - * Es muss noch beim OpenId-Provider die Best�tigung eingeholt werden, danach ist der - * Benutzer angemeldet.<br> - */ - public function checkAuthentication() - { - $queryVars = $this->getQueryParamList(); - - if ($queryVars['openid.invalidate_handle'] != $this->handle) { - throw new \util\exception\SecurityException('Association-Handle mismatch.'); - } - - if ($queryVars['openid.mode'] != 'id_res') { - throw new \util\exception\SecurityException('Open-Id: Unknown mode:' . $queryVars['openid.mode']); - } - - if ($this->provider == 'identity' && $queryVars['openid.identity'] != $this->identity) { - throw new \util\exception\SecurityException('Open-Id: Identity mismatch. Wrong identity:' . $queryVars['openid.identity']); - } - - - $params = array(); - - if ($this->supportAX) - // Den Namespace-Prefix für AX (attribute exchange) herausfinden. - // Leider kann das ein anderer Prefix sein, als wir im Request verwendet haben. - foreach ($queryVars as $request_key => $request_value) - if (substr($request_key, 0, 10) == 'openid.ns.' && $request_value == 'http://openid.net/srv/ax/1.0') - $axPrefix = substr($request_key, 10); - - foreach ($queryVars as $request_key => $request_value) { - // Benutzer-Attribute ermitteln. - // Benutzer-Attribute über SREG ermitteln. - if ($this->supportSREG && substr($request_key, 0, 12) == 'openid.sreg.') - $this->info[substr($request_key, 12)] = $request_value; - // Benutzer-Attribute über AX ermitteln. - elseif ($this->supportAX && substr($request_key, 0, 14 + strlen($axPrefix)) == 'openid.' . $axPrefix . '.value.') - $this->info[substr($request_key, 14 + strlen($axPrefix))] = $request_value; - - // Alle OpenId-Parameter in den Check-Authentication-Request übertragen. - if (substr($request_key, 0, 7) == 'openid.') - $params['openid.' . substr($request_key, 7)] = $request_value; - } - $params['openid.mode'] = 'check_authentication'; - - $checkRequest = new Http($this->server); - - $checkRequest->method = 'POST'; // Spezifikation verlangt POST. - $checkRequest->header['Accept'] = 'text/plain'; - $checkRequest->requestParameter = $params; - - if (!$checkRequest->request()) { - // Der HTTP-Request ging in die Hose. - $this->error = $checkRequest->error; - return false; - } - //Html::debug($checkRequest); - - // Analyse der HTTP-Antwort, Parsen des BODYs. - // Die Anmeldung ist best�tigt, wenn im BODY die Zeile "is_valid:true" vorhanden ist. - // Siehe Spezifikation Kapitel 4.4.2 - $result = array(); - foreach (explode("\n", $checkRequest->body) as $line) { - $pair = explode(':', trim($line)); - if (count($pair) == 2) - $result[strtolower($pair[0])] = strtolower($pair[1]); - } - - if (!array_key_exists('is_valid', $result)) { - // Zeile nicht gefunden. - throw new \util\exception\SecurityException('Undefined Open-Id response: "is_valid" expected, but not found'); - } elseif ($result['is_valid'] == 'true') { - // Anmeldung wurde mit "is_valid:true" best�tigt. - return true; - } else { - // Bestaetigung wurde durch den OpenId-Provider abgelehnt. - throw new \util\exception\SecurityException('Server refused login.'); - } - } - - - /** - * Liefert die Query-Parameter aus der aktuellen URL.<br> - * <br> - * PHP hat leider die sehr bescheuerte Angewohnheit, Punkte und Leerzeichen in Request-Variablen - * durch Unterstriche zu ersetzen. Diese Funktion liefert die GET-Parameter ohne diese Ersetzung. - * - * @return Parameter der aktuellen URL - */ - private function getQueryParamList() - { - // Quelle: php.net - $str = $_SERVER['QUERY_STRING']; - $op = array(); - $pairs = explode("&", $str); - foreach ($pairs as $pair) { - list($k, $v) = array_map("urldecode", explode("=", $pair)); - $op[$k] = $v; - } - - return $op; - } - - -} - -?>- \ No newline at end of file diff --git a/modules/cms/base/DefaultConfig.class.php b/modules/cms/base/DefaultConfig.class.php @@ -374,30 +374,6 @@ class DefaultConfig { 'auto_extend' => true, ], ], - 'ldap' => - [ - 'host' => 'localhost', - 'port' => '389', - 'protocol' => '2', - 'dn' => '', - 'search' => - [ - 'anonymous' => true, - 'user' => 'uid=openrat,ou=users,dc=example,dc=com', - 'password' => 'verysecret', - 'basedn' => 'dc=example,dc=com', - 'filter' => '(uid={user})', - 'aliases' => true, - 'timeout' => 30, - 'add' => true, - ], - 'authorize' => - [ - 'group_filter' => '(memberUid={dn})', - 'group_name' => 'cn', - 'auto_add' => true, - ], - ], 'login' => [ 'motd' => '', @@ -589,9 +565,8 @@ class DefaultConfig { [ 'modules' => [ - 0 => 'LdapUserDN', - 1 => 'Database', - 2 => 'Internal', + 'Database', + 'Internal' ], ], 'newuser' => @@ -631,31 +606,24 @@ class DefaultConfig { 'client_cert_dn_env' => 'SSL_CLIENT_S_DN_CN', ], 'openid' => - [ - 'enable' => false, - 'add' => false, - 'logo_url' => 'http://openid.net/login-bg.gif', - 'trust_root' => '0', - 'trusted_server' => '0', - 'update_user' => true, - 'user_identity' => true, - 'provider' => - [ - 'name' => 'google', - 'google' => - [ - 'xrds_uri' => 'http://google.com/accounts/o8/id', - 'map_attribute' => 'email', - 'name' => 'Google', - 'map_internal' => 'mail', - ], - 'yahoo' => - [ - 'xrds_uri' => 'http://??????', - 'map_attribute' => 'usename', - 'map_internal' => 'mail', - ], - ], + [ + // Google has discontinued OpenID support as of July 22nd 2015 + ], + 'oidc' => + [ + // OpenID connect + 'enabled' => true, + 'provider' => [ + 'google' => [ + 'enabled' => false, + 'label' => 'Google', + 'url' => 'https://acounts.google.com', + 'client_id' => 'xyz', + 'client_secret' => 'mysecret' + ] + + ], + 'add'=>true ], 'sso' => [ diff --git a/modules/cms/model/Group.class.php b/modules/cms/model/Group.class.php @@ -81,27 +81,27 @@ class Group extends ModelBase /** * Read a group. * @param $name string name of the group - * @return Group - * @throws \util\exception\ObjectNotFoundException + * @return Group|null */ public static function loadWithName( $name ) { - $sql = Db::sql( 'SELECT * FROM {{group}}'. - ' WHERE name={name}' ); + $sql = Db::sql( <<<SQL + SELECT id FROM {{group}} + WHERE name={name} +SQL + ); $sql->setString('name',$name ); $row = $sql->getRow(); - if ( count($row) > 0 ) - { - $group = new Group( $row['id'] ); + + if ( $row ) { + $group = new Group($row['id']); $group->load(); - + return $group; } - else - { - throw new \util\exception\ObjectNotFoundException( "Group does not exist: ".$name); - } + + return null; } diff --git a/modules/cms/model/User.class.php b/modules/cms/model/User.class.php @@ -18,8 +18,10 @@ namespace cms\model; // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +use cms\base\Configuration; use cms\base\DB as Db; use security\Password; +use util\exception\ObjectNotFoundException; /** @@ -29,12 +31,21 @@ use security\Password; */ class User extends ModelBase { + /** + * Local user database + */ + const AUTH_TYPE_INTERNAL = 1; + + /** + * OpenId Connect + */ + const AUTH_TYPE_OIDC = 2; + var $userid = 0; var $error = ''; var $name = ''; var $fullname = ''; - var $ldap_dn; var $tel; var $mail; var $desc; @@ -60,6 +71,9 @@ class User extends ModelBase var $groups = null; var $loginModuleName = null; + public $issuer = null; + public $type = User::AUTH_TYPE_INTERNAL; + // Konstruktor public function __construct( $userid='' ) { @@ -284,15 +298,25 @@ SQL * Liefert ein neues Benutzerobjekt zur�ck. * * @static - * @param name Benutzername + * @param $name string Benutzername + * @param $authType int authentication type + * @param $issuer string issuer who created this user */ - public static function loadWithName( $name ) + public static function loadWithName( $name,$authType,$issuer = null ) { - // Benutzer �ber Namen suchen - $sql = Db::sql( 'SELECT id FROM {{user}}'. - ' WHERE name={name}' ); - //Html::debug($sql); - $sql->setString( 'name',$name ); + // Search user with name + $sql = Db::sql( <<<SQL + SELECT id FROM {{user}} + WHERE name={name} + AND auth_type={type} + AND ( issuer={issuer} + OR (auth_type=1 AND issuer IS NULL) + ) +SQL + ); + $sql->setString( 'name' ,$name ); + $sql->setString( 'type' ,$authType ); + $sql->setString( 'issuer',$issuer ); $userId = $sql->getOne(); @@ -330,7 +354,6 @@ SQL $this->name = $row['name' ]; $this->style = $row['style' ]; $this->isAdmin = ( $row['is_admin'] == '1'); - $this->ldap_dn = $row['ldap_dn' ]; $this->fullname = $row['fullname']; $this->tel = $row['tel' ]; $this->mail = $row['mail' ]; @@ -401,7 +424,6 @@ SQL UPDATE {{user}} SET name={name}, fullname={fullname}, - ldap_dn ={ldap_dn} , tel ={tel} , descr ={desc} , mail ={mail} , @@ -416,7 +438,6 @@ SQL ); $sql->setString ( 'name' ,$this->name ); $sql->setString ( 'fullname',$this->fullname); - $sql->setString ( 'ldap_dn' ,$this->ldap_dn ); $sql->setString ( 'tel' ,$this->tel ); $sql->setString ( 'desc' ,$this->desc ); $sql->setString ( 'mail' ,$this->mail ); @@ -438,21 +459,24 @@ SQL * * @param String $name Benutzername */ - function add( $name = '' ) + public function add() { - if ( $name != '' ) - $this->name = $name; - - $db = \cms\base\DB::get(); - - $sql = $db->sql('SELECT MAX(id) FROM {{user}}'); + $sql = Db::sql( <<<SQL + SELECT MAX(id) FROM {{user}} +SQL + ); $this->userid = intval($sql->getOne())+1; - $sql = $db->sql('INSERT INTO {{user}}'. - ' (id,name,password_hash,ldap_dn,fullname,tel,mail,descr,style,is_admin,password_salt)'. - " VALUES( {userid},{name},'','','','','','','default',0,'' )" ); - $sql->setInt ('userid',$this->userid); - $sql->setString('name' ,$this->name ); + $sql = Db::sql( <<<SQL + INSERT INTO {{user}} + (id,name,password_hash,fullname,tel,mail,descr,style,is_admin,password_salt,auth_type,issuer) + VALUES( {userid},{name},'','','','','','default',0,'',{type},{issuer} ) +SQL + ); + $sql->setInt ('userid',$this->userid ); + $sql->setString ('name' ,$this->name ); + $sql->setStringOrNull('issuer',$this->issuer ); + $sql->setString ('type' ,$this->type ); // Datenbankbefehl ausfuehren $sql->query(); @@ -465,30 +489,59 @@ SQL /** - * Zu einem neuen Benutzer automatisch Gruppen hinzufuegen. - * Diese Methode wird automatisch in "add()" aufgerufen. + * Enrich a new user with groups. + * + * Called from add() */ - function addNewUserGroups() + protected function addNewUserGroups() { - $conf = \cms\base\Configuration::rawConfig(); - $groupNames = explode(',',@$conf['security']['newuser']['groups']); - - if ( count($groupNames) == 0 ) - return; // Nichts zu tun. - - $db = \cms\base\DB::get(); + $newUserConfig = Configuration::subset( ['security','newuser'] ); + $templateUser = $newUserConfig->get('copy_user'); + + $userToCopy = null; + + if ( is_int($templateUser)) { + $userToCopy = new User( $templateUser ); + try { + $userToCopy->load(); + } catch( ObjectNotFoundException $onfe) { + $userToCopy = null; + } + }elseif ( is_string($templateUser)) { + $userToCopy = User::loadWithName( $templateUser,User::AUTH_TYPE_INTERNAL ); + } - $groupNames = "'".implode("','",$groupNames)."'"; - $sql = $db->sql("SELECT id FROM {{group}} WHERE name IN($groupNames)"); - $groupIds = array_unique( $sql->getCol() ); - - // Wir brauchen hier nicht weiter pr�fen, ob der Benutzer eine Gruppe schon hat, denn - // - passiert dies nur bei der Neuanlage eines Benutzers - // - Enth�lt die Group-Id-Liste eine ID nur 1x. + if ( $userToCopy ) { + foreach( $userToCopy->getGroupIds() as $groupId ) { + $this->addGroup( $groupId ); + } + + $this->fullname = $userToCopy->fullname; + $this->desc = $userToCopy->desc; + $this->style = $userToCopy->style; + $this->mail = $userToCopy->mail; + $this->tel = $userToCopy->tel; + $this->language = $userToCopy->language; + $this->timezone = $userToCopy->timezone; + } + + + foreach ( $newUserConfig->get('groups',[]) as $group) { - // Gruppen diesem Benutzer zuordnen. - foreach( $groupIds as $groupId ) - $this->addGroup( $groupId ); + if ( is_int($group)) { + $groupToAdd = new Group( $group ); + $groupToAdd->load(); + if ( ! $groupToAdd->groupid ) + $groupToAdd = null; + } + elseif ( is_string($group)) { + $groupToAdd = Group::loadWithName($group); + } + + if ( $groupToAdd ) + $this->addGroup( $groupToAdd->groupid ); + } + } @@ -564,7 +617,11 @@ SQL */ public function getProperties() { - return parent::getProperties() + array('id'=>$this->userid,'is_admin'=> $this->isAdmin); + return parent::getProperties() + [ + 'id' => $this->userid, + 'is_admin' => $this->isAdmin, + 'auth_type' => ($this->type==User::AUTH_TYPE_INTERNAL?'local':'remote') + ]; } diff --git a/modules/cms/ui/action/IndexAction.class.php b/modules/cms/ui/action/IndexAction.class.php @@ -353,7 +353,7 @@ class IndexAction extends Action { try { - $user = User::loadWithName( $username ); + $user = User::loadWithName( $username,User::AUTH_TYPE_INTERNAL ); $user->setCurrent(); // Do not update the login timestamp, because this is a readonly request. Logger::info('auto-login for user '.$username); diff --git a/modules/cms/ui/themes/default/html/views/element/advanced.php b/modules/cms/ui/themes/default/html/views/element/advanced.php @@ -1,6 +1,6 @@ <?php /* THIS FILE IS GENERATED from advanced.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <?php $if1=(O::config('security','disable_dynamic_code')); if($if1) { ?> - <?php $if1=(!1); if($if1) { ?> + <?php $if2=(O::config('security','disable_dynamic_code')); if($if2) { ?> + <?php $if3=(!1); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-message warn') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOTICE_CODE_DISABLED').'') ?> </span> @@ -19,12 +19,12 @@ <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml('') ?> </h2> <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($subtype)); if($if1) { ?> + <?php $if4=(isset($subtype)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('ELEMENT_SUBTYPE').'') ?> </h3> <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($subtypes)); if($if1) { ?> + <?php $if6=(isset($subtypes)); if($if6) { ?> <select name="<?php echo O::escapeHtml('subtype') ?>" size="<?php echo O::escapeHtml('1') ?>" class="<?php echo O::escapeHtml('or-input') ?>"><?php echo O::escapeHtml('') ?> <option value="<?php echo O::escapeHtml('') ?>"><?php echo O::escapeHtml(''.@O::lang('LIST_ENTRY_EMPTY').'') ?> </option> @@ -34,13 +34,13 @@ <?php } ?> </select> <?php } ?> - <?php $if1=!(isset($subtypes)); if($if1) { ?> + <?php $if6=!(isset($subtypes)); if($if6) { ?> <input name="<?php echo O::escapeHtml('subtype') ?>" type="<?php echo O::escapeHtml('text') ?>" maxlength="<?php echo O::escapeHtml('256') ?>" value="<?php echo O::escapeHtml(''.@$subtype.'') ?>" class="<?php echo O::escapeHtml('or-input') ?>" /><?php echo O::escapeHtml('') ?> <?php } ?> </div> </section> <?php } ?> - <?php $if1=(isset($with_icon)); if($if1) { ?> + <?php $if4=(isset($with_icon)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -53,7 +53,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($inherit)); if($if1) { ?> + <?php $if4=(isset($inherit)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -66,7 +66,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($all_languages)); if($if1) { ?> + <?php $if4=(isset($all_languages)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -79,7 +79,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($writable)); if($if1) { ?> + <?php $if4=(isset($writable)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -92,7 +92,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($width)); if($if1) { ?> + <?php $if4=(isset($width)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('width').'') ?> </h3> @@ -101,7 +101,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($height)); if($if1) { ?> + <?php $if4=(isset($height)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('height').'') ?> </h3> @@ -110,7 +110,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($dateformat)); if($if1) { ?> + <?php $if4=(isset($dateformat)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_DATEFORMAT').'') ?> </h3> @@ -124,7 +124,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($format)); if($if1) { ?> + <?php $if4=(isset($format)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_FORMAT').'') ?> </h3> @@ -140,7 +140,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($decimals)); if($if1) { ?> + <?php $if4=(isset($decimals)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_DECIMALS').'') ?> </h3> @@ -149,7 +149,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($dec_point)); if($if1) { ?> + <?php $if4=(isset($dec_point)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_DEC_POINT').'') ?> </h3> @@ -158,7 +158,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($thousand_sep)); if($if1) { ?> + <?php $if4=(isset($thousand_sep)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_thousand_sep').'') ?> </h3> @@ -167,7 +167,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($default_text)); if($if1) { ?> + <?php $if4=(isset($default_text)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_default_text').'') ?> </h3> @@ -176,7 +176,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($default_longtext)); if($if1) { ?> + <?php $if4=(isset($default_longtext)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_default_longtext').'') ?> </h3> @@ -186,7 +186,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($parameters)); if($if1) { ?> + <?php $if4=(isset($parameters)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_DYNAMIC_PARAMETERS').'') ?> </h3> @@ -215,7 +215,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($select_items)); if($if1) { ?> + <?php $if4=(isset($select_items)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_select_items').'') ?> </h3> @@ -225,7 +225,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($linkelement)); if($if1) { ?> + <?php $if4=(isset($linkelement)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_LINK').'') ?> </h3> @@ -239,7 +239,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($name)); if($if1) { ?> + <?php $if4=(isset($name)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('ELEMENT_NAME').'') ?> </h3> @@ -253,7 +253,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($folderobjectid)); if($if1) { ?> + <?php $if4=(isset($folderobjectid)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_DEFAULT_FOLDEROBJECT').'') ?> </h3> @@ -267,7 +267,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($default_objectid)); if($if1) { ?> + <?php $if4=(isset($default_objectid)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_DEFAULT_OBJECT').'') ?> </h3> @@ -283,7 +283,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($code)); if($if1) { ?> + <?php $if4=(isset($code)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('EL_PROP_code').'') ?> </h3> diff --git a/modules/cms/ui/themes/default/html/views/file/info.php b/modules/cms/ui/themes/default/html/views/file/info.php @@ -144,7 +144,7 @@ </div> </div> </section> - <?php $if1=(isset($cache_filename)); if($if1) { ?> + <?php $if4=(isset($cache_filename)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -197,7 +197,7 @@ </table> </div> </div> - <?php $if1=(($pages)==FALSE); if($if1) { ?> + <?php $if6=(($pages)==FALSE); if($if6) { ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> </span> <?php } ?> diff --git a/modules/cms/ui/themes/default/html/views/file/pub.php b/modules/cms/ui/themes/default/html/views/file/pub.php @@ -1,5 +1,5 @@ <?php /* THIS FILE IS GENERATED from pub.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <?php $if1=(O::config('security','nopublish')); if($if1) { ?> + <?php $if2=(O::config('security','nopublish')); if($if2) { ?> <div class="<?php echo O::escapeHtml('or-message warn') ?>"><?php echo O::escapeHtml('') ?> <span class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml(''.@O::lang('NOPUBLISH_DESC').'') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/folder/advanced.php b/modules/cms/ui/themes/default/html/views/folder/advanced.php @@ -29,10 +29,10 @@ <?php foreach((array)$object as $list_key=>$list_value) { extract($list_value); ?> <tr class="<?php echo O::escapeHtml('or-data') ?>"><?php echo O::escapeHtml('') ?> <td width="<?php echo O::escapeHtml('1%') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=($writable); if($if1) { ?> + <?php $if7=($writable); if($if7) { ?> <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml(''.@$id.'') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$$id){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> <?php } ?> - <?php $if1=(!writable); if($if1) { ?> + <?php $if7=(!writable); if($if7) { ?> <span><?php echo O::escapeHtml(' ') ?> </span> <?php } ?> @@ -51,7 +51,7 @@ </td> </tr> <?php } ?> - <?php $if1=(($object)==FALSE); if($if1) { ?> + <?php $if4=(($object)==FALSE); if($if4) { ?> <tr><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('2') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> diff --git a/modules/cms/ui/themes/default/html/views/folder/content.php b/modules/cms/ui/themes/default/html/views/folder/content.php @@ -19,7 +19,7 @@ </span> </td> </tr> - <?php $if1=(isset($up_url)); if($if1) { ?> + <?php $if3=(isset($up_url)); if($if3) { ?> <tr class="<?php echo O::escapeHtml('or-data') ?>"><?php echo O::escapeHtml('') ?> <td><?php echo O::escapeHtml('') ?> <img src="<?php echo O::escapeHtml('./modules/cms/ui/themes/default/images/icon_folder.png') ?>" /><?php echo O::escapeHtml('') ?> @@ -47,7 +47,7 @@ </td> </tr> <?php } ?> - <?php $if1=(($object)==FALSE); if($if1) { ?> + <?php $if3=(($object)==FALSE); if($if3) { ?> <tr><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('2') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> diff --git a/modules/cms/ui/themes/default/html/views/folder/create.php b/modules/cms/ui/themes/default/html/views/folder/create.php @@ -1,6 +1,6 @@ <?php /* THIS FILE IS GENERATED from create.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> <div class="<?php echo O::escapeHtml('or-linklist') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=($mayCreateFolder); if($if1) { ?> + <?php $if3=($mayCreateFolder); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-act-clickable or-linklist-line or-round-corners or-hover-effect') ?>"><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('createfolder') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('createfolder') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':null,\'dialogMethod\':\'createfolder\'}') ?>" href="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('menu_createfolder').'') ?> @@ -8,7 +8,7 @@ </a> </div> <?php } ?> - <?php $if1=($mayCreatePage); if($if1) { ?> + <?php $if3=($mayCreatePage); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-act-clickable or-linklist-line or-round-corners or-hover-effect') ?>"><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('createpage') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('createpage') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':null,\'dialogMethod\':\'createpage\'}') ?>" href="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('menu_createpage').'') ?> @@ -16,7 +16,7 @@ </a> </div> <?php } ?> - <?php $if1=($mayCreateFile); if($if1) { ?> + <?php $if3=($mayCreateFile); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-act-clickable or-linklist-line or-round-corners or-hover-effect') ?>"><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('createfile') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('createfile') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':null,\'dialogMethod\':\'createfile\'}') ?>" href="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('menu_createfile').'') ?> @@ -24,7 +24,7 @@ </a> </div> <?php } ?> - <?php $if1=($mayCreateImage); if($if1) { ?> + <?php $if3=($mayCreateImage); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-act-clickable or-linklist-line or-round-corners or-hover-effect') ?>"><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('createimage') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('createimage') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':null,\'dialogMethod\':\'createimage\'}') ?>" href="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('menu_createimage').'') ?> @@ -32,7 +32,7 @@ </a> </div> <?php } ?> - <?php $if1=($mayCreateText); if($if1) { ?> + <?php $if3=($mayCreateText); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-act-clickable or-linklist-line or-round-corners or-hover-effect') ?>"><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('createtext') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('createtext') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':null,\'dialogMethod\':\'createtext\'}') ?>" href="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('menu_createtext').'') ?> @@ -40,7 +40,7 @@ </a> </div> <?php } ?> - <?php $if1=($mayCreateUrl); if($if1) { ?> + <?php $if3=($mayCreateUrl); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-act-clickable or-linklist-line or-round-corners or-hover-effect') ?>"><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('createurl') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('createurl') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':null,\'dialogMethod\':\'createurl\'}') ?>" href="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('menu_createurl').'') ?> @@ -48,7 +48,7 @@ </a> </div> <?php } ?> - <?php $if1=($mayCreateLink); if($if1) { ?> + <?php $if3=($mayCreateLink); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-act-clickable or-linklist-line or-round-corners or-hover-effect') ?>"><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('createlink') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('createlink') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':null,\'dialogMethod\':\'createlink\'}') ?>" href="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('menu_createlink').'') ?> diff --git a/modules/cms/ui/themes/default/html/views/folder/edit.php b/modules/cms/ui/themes/default/html/views/folder/edit.php @@ -19,7 +19,7 @@ </span> </th> </tr> - <?php $if1=(isset($up_url)); if($if1) { ?> + <?php $if3=(isset($up_url)); if($if3) { ?> <tr class="<?php echo O::escapeHtml('or-data or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <td><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('open') ?>" data-action="<?php echo O::escapeHtml('folder') ?>" data-method="<?php echo O::escapeHtml('') ?>" data-id="<?php echo O::escapeHtml(''.@$parentid.'') ?>" data-extra="<?php echo O::escapeHtml('[]') ?>" href="<?php echo O::escapeHtml('#/folder/'.@$parentid.'') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> @@ -55,7 +55,7 @@ </td> </tr> <?php } ?> - <?php $if1=(($object)==FALSE); if($if1) { ?> + <?php $if3=(($object)==FALSE); if($if3) { ?> <tr><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('2') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> diff --git a/modules/cms/ui/themes/default/html/views/folder/inherit.php b/modules/cms/ui/themes/default/html/views/folder/inherit.php @@ -7,7 +7,7 @@ <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('action') ?>" value="<?php echo O::escapeHtml('folder') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('subaction') ?>" value="<?php echo O::escapeHtml('inherit') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('id') ?>" value="<?php echo O::escapeHtml(''.@$_id.'') ?>" /><?php echo O::escapeHtml('') ?> - <?php $if1=($type=='folder'); if($if1) { ?> + <?php $if3=($type=='folder'); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('options').'') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/folder/pub.php b/modules/cms/ui/themes/default/html/views/folder/pub.php @@ -1,5 +1,5 @@ <?php /* THIS FILE IS GENERATED from pub.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <?php $if1=(O::config('security','nopublish')); if($if1) { ?> + <?php $if2=(O::config('security','nopublish')); if($if2) { ?> <div class="<?php echo O::escapeHtml('or-message warn') ?>"><?php echo O::escapeHtml('') ?> <span class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml(''.@O::lang('NOPUBLISH_DESC').'') ?> </span> @@ -13,7 +13,7 @@ <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('action') ?>" value="<?php echo O::escapeHtml('folder') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('subaction') ?>" value="<?php echo O::escapeHtml('pub') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('id') ?>" value="<?php echo O::escapeHtml(''.@$_id.'') ?>" /><?php echo O::escapeHtml('') ?> - <?php $if1=($pages); if($if1) { ?> + <?php $if3=($pages); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -32,7 +32,7 @@ </div> </section> <?php } ?> - <?php $if1=($files); if($if1) { ?> + <?php $if3=($files); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -59,7 +59,7 @@ </i> </h2> <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($subdirs)); if($if1) { ?> + <?php $if4=(isset($subdirs)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -78,7 +78,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($clean)); if($if1) { ?> + <?php $if4=(isset($clean)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> diff --git a/modules/cms/ui/themes/default/html/views/folder/show.php b/modules/cms/ui/themes/default/html/views/folder/show.php @@ -19,7 +19,7 @@ </span> </th> </tr> - <?php $if1=(isset($up_url)); if($if1) { ?> + <?php $if3=(isset($up_url)); if($if3) { ?> <tr class="<?php echo O::escapeHtml('or-data or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <td><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-folder') ?>"><?php echo O::escapeHtml('') ?> @@ -50,7 +50,7 @@ </td> </tr> <?php } ?> - <?php $if1=(($object)==FALSE); if($if1) { ?> + <?php $if3=(($object)==FALSE); if($if3) { ?> <tr><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('2') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> diff --git a/modules/cms/ui/themes/default/html/views/group/rights.php b/modules/cms/ui/themes/default/html/views/group/rights.php @@ -13,7 +13,7 @@ </i> </h2> <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(($rights)==FALSE); if($if1) { ?> + <?php $if7=(($rights)==FALSE); if($if7) { ?> <tr><?php echo O::escapeHtml('') ?> <td><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> @@ -21,7 +21,7 @@ </td> </tr> <?php } ?> - <?php $if1=!(($rights)==FALSE); if($if1) { ?> + <?php $if7=!(($rights)==FALSE); if($if7) { ?> <div class="<?php echo O::escapeHtml('or-table-wrapper') ?>"><?php echo O::escapeHtml('') ?> <div class="<?php echo O::escapeHtml('or-table-filter') ?>"><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('search') ?>" name="<?php echo O::escapeHtml('filter') ?>" placeholder="<?php echo O::escapeHtml(''.@O::lang('SEARCH_FILTER').'') ?>" class="<?php echo O::escapeHtml('or-input') ?>" /><?php echo O::escapeHtml('') ?> @@ -51,14 +51,14 @@ <?php foreach((array)$rights as $aclid=>$acl) { extract($acl); ?> <tr class="<?php echo O::escapeHtml('or-data or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <td><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($groupname)); if($if1) { ?> + <?php $if12=(isset($groupname)); if($if12) { ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-group') ?>"><?php echo O::escapeHtml('') ?> </i> <span><?php echo O::escapeHtml(''.@$groupname.'') ?> </span> <?php } ?> - <?php $if1=!(isset($username)); if($if1) { ?> - <?php $if1=!(isset($groupname)); if($if1) { ?> + <?php $if12=!(isset($username)); if($if12) { ?> + <?php $if13=!(isset($groupname)); if($if13) { ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-group') ?>"><?php echo O::escapeHtml('') ?> </i> <span><?php echo O::escapeHtml(''.@O::lang('all').'') ?> diff --git a/modules/cms/ui/themes/default/html/views/image/pub.php b/modules/cms/ui/themes/default/html/views/image/pub.php @@ -1,5 +1,5 @@ <?php /* THIS FILE IS GENERATED from pub.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <?php $if1=(O::config('security','nopublish')); if($if1) { ?> + <?php $if2=(O::config('security','nopublish')); if($if2) { ?> <div class="<?php echo O::escapeHtml('or-message warn') ?>"><?php echo O::escapeHtml('') ?> <span class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml(''.@O::lang('NOPUBLISH_DESC').'') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/image/size.php b/modules/cms/ui/themes/default/html/views/image/size.php @@ -25,7 +25,7 @@ </div> </div> </section> - <?php $if1=!(($formats)==FALSE); if($if1) { ?> + <?php $if3=!(($formats)==FALSE); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('IMAGE_NEW_SIZE').'') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/languagelist/show.php b/modules/cms/ui/themes/default/html/views/languagelist/show.php @@ -33,19 +33,19 @@ <span><?php echo O::escapeHtml(''.@$isocode.'') ?> </span> </td> - <?php $if1=(!$is_default); if($if1) { ?> + <?php $if5=(!$is_default); if($if5) { ?> <td class="<?php echo O::escapeHtml('or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($id)); if($if1) { ?> + <?php $if7=(isset($id)); if($if7) { ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('post') ?>" data-action="<?php echo O::escapeHtml('language') ?>" data-method="<?php echo O::escapeHtml('setdefault') ?>" data-id="<?php echo O::escapeHtml(''.@$id.'') ?>" data-extra="<?php echo O::escapeHtml('[]') ?>" data-data="<?php echo O::escapeHtml('{"action":"language","subaction":"setdefault","id":"'.@$id.'","token":"'.@$_token.'","none":"0"}') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('make_default').'') ?> </span> </a> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if7) { ?> <?php } ?> </td> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if5) { ?> <td><?php echo O::escapeHtml('') ?> <em><?php echo O::escapeHtml(''.@O::lang('is_default').'') ?> </em> diff --git a/modules/cms/ui/themes/default/html/views/login/login.php b/modules/cms/ui/themes/default/html/views/login/login.php @@ -7,125 +7,155 @@ <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('action') ?>" value="<?php echo O::escapeHtml('login') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('subaction') ?>" value="<?php echo O::escapeHtml('login') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('id') ?>" value="<?php echo O::escapeHtml(''.@$_id.'') ?>" /><?php echo O::escapeHtml('') ?> - <?php $if1=(O::config('login','logo','enabled')); if($if1) { ?> - <?php $if1=!((O::config('login','logo','url'))==FALSE); if($if1) { ?> + <?php $if3=(O::config('login','logo','enabled')); if($if3) { ?> + <?php $if4=!((O::config('login','logo','url'))==FALSE); if($if4) { ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-url="<?php echo O::escapeHtml(''.O::config('login','logo','url').'') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra="<?php echo O::escapeHtml('[]') ?>" href="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <img src="<?php echo O::escapeHtml(''.O::config('login','logo','image').'') ?>" /><?php echo O::escapeHtml('') ?> </a> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if4) { ?> <img src="<?php echo O::escapeHtml(''.O::config('login','logo','image').'') ?>" /><?php echo O::escapeHtml('') ?> <?php } ?> <?php } ?> - <?php $if1=!((O::config('login','motd'))==FALSE); if($if1) { ?> + <?php $if3=!((O::config('login','motd'))==FALSE); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-message info') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.O::config('login','motd').'') ?> </span> </div> <?php } ?> - <?php $if1=(O::config('login','nologin')); if($if1) { ?> - <div class="<?php echo O::escapeHtml('or-message error') ?>"><?php echo O::escapeHtml('') ?> - <span><?php echo O::escapeHtml(''.@O::lang('LOGIN_NOLOGIN_DESC').'') ?> - </span> - </div> - <?php } ?> - <?php $if1=(O::config('security','readonly')); if($if1) { ?> + <?php $if3=(O::config('security','readonly')); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-message warn') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('READONLY_DESC').'') ?> </span> </div> <?php } ?> - <?php $if1=(!O::config('login','nologin')); if($if1) { ?> - <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> - <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_USERNAME').'') ?> - </h3> - <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=!(isset($force_username)); if($if1) { ?> - <input name="<?php echo O::escapeHtml('login_name') ?>" required="<?php echo O::escapeHtml('required') ?>" placeholder="<?php echo O::escapeHtml(''.@O::lang('USER_USERNAME').'') ?>" autofocus="<?php echo O::escapeHtml('autofocus') ?>" type="<?php echo O::escapeHtml('text') ?>" maxlength="<?php echo O::escapeHtml('128') ?>" value="<?php echo O::escapeHtml(''.@$login_name.'') ?>" class="<?php echo O::escapeHtml('or-name or-input') ?>" /><?php echo O::escapeHtml('') ?> - <?php } ?> - <?php if(!$if1) { ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('login_name') ?>" value="<?php echo O::escapeHtml(''.@$login_name.'') ?>" /><?php echo O::escapeHtml('') ?> - <span><?php echo O::escapeHtml(''.@$force_username.'') ?> - </span> - <?php } ?> - </div> - </section> - <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> - <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_PASSWORD').'') ?> - </h3> - <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('password') ?>" name="<?php echo O::escapeHtml('login_password') ?>" size="<?php echo O::escapeHtml('20') ?>" maxlength="<?php echo O::escapeHtml('256') ?>" required="<?php echo O::escapeHtml('required') ?>" placeholder="<?php echo O::escapeHtml(''.@O::lang('USER_PASSWORD').'') ?>" value="<?php echo O::escapeHtml(''.@$login_password.'') ?>" class="<?php echo O::escapeHtml('or-name or-input') ?>" /><?php echo O::escapeHtml('') ?> - <label><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml('remember') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$remember){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> - <span class="<?php echo O::escapeHtml('or-form-label') ?>"><?php echo O::escapeHtml(''.@O::lang('REMEMBER_ME').'') ?> - </span> - </label> - </div> - </section> - <?php } ?> - <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-closed') ?>"><?php echo O::escapeHtml('') ?> - <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_NEW_PASSWORD').'') ?> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> - </i> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-open or-collapsible--on-open') ?>"><?php echo O::escapeHtml('') ?> - </i> - </h2> - <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> - <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_NEW_PASSWORD').'') ?> - </h3> - <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('password') ?>" name="<?php echo O::escapeHtml('password1') ?>" size="<?php echo O::escapeHtml('25') ?>" maxlength="<?php echo O::escapeHtml('256') ?>" placeholder="<?php echo O::escapeHtml(''.@O::lang('USER_NEW_PASSWORD').'') ?>" minlength="<?php echo O::escapeHtml(''.O::config('security','password','min_length').'') ?>" value="<?php echo O::escapeHtml(''.@$password1.'') ?>" class="<?php echo O::escapeHtml('or- or-input') ?>" /><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('password') ?>" name="<?php echo O::escapeHtml('password2') ?>" size="<?php echo O::escapeHtml('25') ?>" maxlength="<?php echo O::escapeHtml('256') ?>" placeholder="<?php echo O::escapeHtml(''.@O::lang('USER_NEW_PASSWORD_REPEAT').'') ?>" minlength="<?php echo O::escapeHtml(''.O::config('security','password','min_length').'') ?>" value="<?php echo O::escapeHtml(''.@$password2.'') ?>" class="<?php echo O::escapeHtml('or- or-input') ?>" /><?php echo O::escapeHtml('') ?> + <?php $if3=(!O::config('login','nologin')); if($if3) { ?> + <?php $if4=($enableOpenIdConnect); if($if4) { ?> + <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> + <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('login').'') ?> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> + </i> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-open or-collapsible--on-open') ?>"><?php echo O::escapeHtml('') ?> + </i> + </h2> + <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> + <?php foreach((array)$provider as $name=>$label) { ?> + <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('window') ?>" data-action="<?php echo O::escapeHtml('login') ?>" data-method="<?php echo O::escapeHtml('oidc') ?>" data-id="<?php echo O::escapeHtml(''.@$name.'') ?>" data-extra="<?php echo O::escapeHtml('[]') ?>" href="<?php echo O::escapeHtml('#/login/'.@$name.'') ?>" class="<?php echo O::escapeHtml('or-link or-btn') ?>"><?php echo O::escapeHtml('') ?> + <span><?php echo O::escapeHtml(''.@$label.'') ?> + </span> + </a> + <?php } ?> </div> </section> - </div> - </section> - <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-closed') ?>"><?php echo O::escapeHtml('') ?> - <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_TOKEN').'') ?> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> - </i> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-open or-collapsible--on-open') ?>"><?php echo O::escapeHtml('') ?> - </i> - </h2> - <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> - <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_TOKEN').'') ?> - </h3> - <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> - <input name="<?php echo O::escapeHtml('user_token') ?>" type="<?php echo O::escapeHtml('text') ?>" maxlength="<?php echo O::escapeHtml('30') ?>" value="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-input') ?>" /><?php echo O::escapeHtml('') ?> + <?php } ?> + <?php $if4=($enableUserPasswordLogin); if($if4) { ?> + <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> + <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_USERNAME').'') ?> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> + </i> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-open or-collapsible--on-open') ?>"><?php echo O::escapeHtml('') ?> + </i> + </h2> + <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> + <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> + <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_USERNAME').'') ?> + </h3> + <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> + <?php $if7=!(isset($force_username)); if($if7) { ?> + <input name="<?php echo O::escapeHtml('login_name') ?>" required="<?php echo O::escapeHtml('required') ?>" placeholder="<?php echo O::escapeHtml(''.@O::lang('USER_USERNAME').'') ?>" autofocus="<?php echo O::escapeHtml('autofocus') ?>" type="<?php echo O::escapeHtml('text') ?>" maxlength="<?php echo O::escapeHtml('128') ?>" value="<?php echo O::escapeHtml(''.@$login_name.'') ?>" class="<?php echo O::escapeHtml('or-name or-input') ?>" /><?php echo O::escapeHtml('') ?> + <?php } ?> + <?php if(!$if7) { ?> + <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('login_name') ?>" value="<?php echo O::escapeHtml(''.@$login_name.'') ?>" /><?php echo O::escapeHtml('') ?> + <span><?php echo O::escapeHtml(''.@$force_username.'') ?> + </span> + <?php } ?> + </div> + </section> + <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> + <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_PASSWORD').'') ?> + </h3> + <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> + <input type="<?php echo O::escapeHtml('password') ?>" name="<?php echo O::escapeHtml('login_password') ?>" size="<?php echo O::escapeHtml('20') ?>" maxlength="<?php echo O::escapeHtml('256') ?>" required="<?php echo O::escapeHtml('required') ?>" placeholder="<?php echo O::escapeHtml(''.@O::lang('USER_PASSWORD').'') ?>" value="<?php echo O::escapeHtml(''.@$login_password.'') ?>" class="<?php echo O::escapeHtml('or-name or-input') ?>" /><?php echo O::escapeHtml('') ?> + <label><?php echo O::escapeHtml('') ?> + <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml('remember') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$remember){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> + <span class="<?php echo O::escapeHtml('or-form-label') ?>"><?php echo O::escapeHtml(''.@O::lang('REMEMBER_ME').'') ?> + </span> + </label> + </div> + </section> </div> </section> - </div> - </section> - <?php $if1=(intval(1)<count($dbids)); if($if1) { ?> - <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> - <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('DATABASE').'') ?> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--database') ?>"><?php echo O::escapeHtml('') ?> - </i> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> - </i> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-open or-collapsible--on-open') ?>"><?php echo O::escapeHtml('') ?> - </i> - </h2> - <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> - <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('DATABASE').'') ?> - </h3> - <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> - <select name="<?php echo O::escapeHtml('dbid') ?>" size="<?php echo O::escapeHtml('1') ?>" class="<?php echo O::escapeHtml('or-input') ?>"><?php echo O::escapeHtml('') ?> - <?php foreach($dbids as $_key=>$_value) { ?> - <option value="<?php echo O::escapeHtml(''.@$_key.'') ?>" <?php if($_key==$dbid){ ?>selected="<?php echo O::escapeHtml('selected') ?>"<?php } ?>><?php echo O::escapeHtml(''.@$_value.'') ?> - </option> - <?php } ?> - </select> + <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-closed') ?>"><?php echo O::escapeHtml('') ?> + <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_NEW_PASSWORD').'') ?> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> + </i> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-open or-collapsible--on-open') ?>"><?php echo O::escapeHtml('') ?> + </i> + </h2> + <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> + <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> + <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_NEW_PASSWORD').'') ?> + </h3> + <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> + <input type="<?php echo O::escapeHtml('password') ?>" name="<?php echo O::escapeHtml('password1') ?>" size="<?php echo O::escapeHtml('25') ?>" maxlength="<?php echo O::escapeHtml('256') ?>" placeholder="<?php echo O::escapeHtml(''.@O::lang('USER_NEW_PASSWORD').'') ?>" minlength="<?php echo O::escapeHtml(''.O::config('security','password','min_length').'') ?>" value="<?php echo O::escapeHtml(''.@$password1.'') ?>" class="<?php echo O::escapeHtml('or- or-input') ?>" /><?php echo O::escapeHtml('') ?> + <input type="<?php echo O::escapeHtml('password') ?>" name="<?php echo O::escapeHtml('password2') ?>" size="<?php echo O::escapeHtml('25') ?>" maxlength="<?php echo O::escapeHtml('256') ?>" placeholder="<?php echo O::escapeHtml(''.@O::lang('USER_NEW_PASSWORD_REPEAT').'') ?>" minlength="<?php echo O::escapeHtml(''.O::config('security','password','min_length').'') ?>" value="<?php echo O::escapeHtml(''.@$password2.'') ?>" class="<?php echo O::escapeHtml('or- or-input') ?>" /><?php echo O::escapeHtml('') ?> + </div> + </section> + </div> + </section> + <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-closed') ?>"><?php echo O::escapeHtml('') ?> + <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_TOKEN').'') ?> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> + </i> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-open or-collapsible--on-open') ?>"><?php echo O::escapeHtml('') ?> + </i> + </h2> + <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> + <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> + <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('USER_TOKEN').'') ?> + </h3> + <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> + <input name="<?php echo O::escapeHtml('user_token') ?>" type="<?php echo O::escapeHtml('text') ?>" maxlength="<?php echo O::escapeHtml('30') ?>" value="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-input') ?>" /><?php echo O::escapeHtml('') ?> + </div> + </section> + </div> + </section> + <?php $if5=(intval(1)<count($dbids)); if($if5) { ?> + <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> + <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('DATABASE').'') ?> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--database') ?>"><?php echo O::escapeHtml('') ?> + </i> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> + </i> + <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-open or-collapsible--on-open') ?>"><?php echo O::escapeHtml('') ?> + </i> + </h2> + <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> + <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> + <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('DATABASE').'') ?> + </h3> + <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> + <select name="<?php echo O::escapeHtml('dbid') ?>" size="<?php echo O::escapeHtml('1') ?>" class="<?php echo O::escapeHtml('or-input') ?>"><?php echo O::escapeHtml('') ?> + <?php foreach($dbids as $_key=>$_value) { ?> + <option value="<?php echo O::escapeHtml(''.@$_key.'') ?>" <?php if($_key==$dbid){ ?>selected="<?php echo O::escapeHtml('selected') ?>"<?php } ?>><?php echo O::escapeHtml(''.@$_value.'') ?> + </option> + <?php } ?> + </select> + </div> + </section> </div> </section> - </div> - </section> + <?php } ?> + <?php if(!$if5) { ?> + <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('dbid') ?>" value="<?php echo O::escapeHtml(''.@$dbid.'') ?>" /><?php echo O::escapeHtml('') ?> + <?php } ?> + <?php } ?> <?php } ?> - <?php if(!$if1) { ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('dbid') ?>" value="<?php echo O::escapeHtml(''.@$dbid.'') ?>" /><?php echo O::escapeHtml('') ?> + <?php if(!$if3) { ?> + <div class="<?php echo O::escapeHtml('or-message error') ?>"><?php echo O::escapeHtml('') ?> + <span><?php echo O::escapeHtml(''.@O::lang('LOGIN_NOLOGIN_DESC').'') ?> + </span> + </div> <?php } ?> </div> <div class="<?php echo O::escapeHtml('or-form-actionbar') ?>"><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/login/login.tpl.src.xml b/modules/cms/ui/themes/default/html/views/login/login.tpl.src.xml @@ -16,57 +16,81 @@ <text value="${config:login/motd}"/> </part> </if> - <if true="${config:login/nologin}"> - <part class="message error"> - <text value="${message:LOGIN_NOLOGIN_DESC}"/> - </part> - </if> <if true="${config:security/readonly}"> <part class="message warn"> <text value="${message:READONLY_DESC}"/> </part> </if> + <if false="${config:login/nologin}"> - <fieldset label="${message:USER_USERNAME}"> - <if not="true" present="force_username"> - <input type="text" name="login_name" class="name" value="" hint="${message:USER_USERNAME}" size="20" - maxlength="128" focus="true" required="true"/> + + <if true="${enableOpenIdConnect}"> + <group title="${message:login}"> + <list list="${provider}" key="name" value="label"> + <link class="btn" type="window" action="login" subaction="oidc" id="${name}"> + <text value="${label}"/> + </link> + </list> + </group> + </if> + + <if true="${enableUserPasswordLogin}"> + <group title="${message:USER_USERNAME}"> + <fieldset label="${message:USER_USERNAME}"> + <if not="true" present="force_username"> + <input type="text" name="login_name" class="name" value="" hint="${message:USER_USERNAME}" + size="20" + maxlength="128" focus="true" required="true"/> + </if> + <else> + <hidden name="login_name"/> + <text value="${force_username}"/> + </else> + </fieldset> + <fieldset label="${message:USER_PASSWORD}"> + <password name="login_password" class="name" default="" size="20" + hint="${message:USER_PASSWORD}" + required="true"/> + <checkbox name="remember" default="false" label="${message:REMEMBER_ME}"/> + </fieldset> + </group> + + <group title="${message:USER_NEW_PASSWORD}" open="false" show="false"> + <fieldset label="${message:USER_NEW_PASSWORD}"> + <password name="password1" default="" size="25" + minlength="${config:security/password/min_length}" + hint="${message:USER_NEW_PASSWORD}"/> + <password name="password2" default="" size="25" + minlength="${config:security/password/min_length}" + hint="${message:USER_NEW_PASSWORD_REPEAT}"/> + </fieldset> + </group> + + <group title="${message:USER_TOKEN}" open="false" show="false"> + <fieldset label="${message:USER_TOKEN}"> + <input name="user_token" default="" size="25" maxlength="30"/> + </fieldset> + </group> + + <if value="${dbids}" greaterthan="1"> + <!-- More than 1 database available. --> + <group title="${message:DATABASE}" icon="database" open="true"> + <fieldset label="${message:DATABASE}"> + <selectbox name="dbid" list="dbids"/> + </fieldset> + </group> </if> <else> - <hidden name="login_name"/> - <text value="${force_username}"/> + <!-- Only 1 database is available. No input necessary. --> + <hidden name="dbid"/> </else> - </fieldset> - <fieldset label="${message:USER_PASSWORD}"> - <password name="login_password" class="name" default="" size="20" hint="${message:USER_PASSWORD}" - required="true"/> - <checkbox name="remember" default="false" label="${message:REMEMBER_ME}"/> - </fieldset> - </if> - <group title="${message:USER_NEW_PASSWORD}" open="false" show="false"> - <fieldset label="${message:USER_NEW_PASSWORD}"> - <password name="password1" default="" size="25" minlength="${config:security/password/min_length}" - hint="${message:USER_NEW_PASSWORD}"/> - <password name="password2" default="" size="25" minlength="${config:security/password/min_length}" - hint="${message:USER_NEW_PASSWORD_REPEAT}"/> - </fieldset> - </group> - <group title="${message:USER_TOKEN}" open="false" show="false"> - <fieldset label="${message:USER_TOKEN}"> - <input name="user_token" default="" size="25" maxlength="30"/> - </fieldset> - </group> - <if value="${dbids}" greaterthan="1"> - <!-- More than 1 database available. --> - <group title="${message:DATABASE}" icon="database" open="true"> - <fieldset label="${message:DATABASE}"> - <selectbox name="dbid" list="dbids"/> - </fieldset> - </group> + </if> + </if> <else> - <!-- Only 1 database is available. No input necessary. --> - <hidden name="dbid"/> + <part class="message error"> + <text value="${message:LOGIN_NOLOGIN_DESC}"/> + </part> </else> </form> </output> diff --git a/modules/cms/ui/themes/default/html/views/login/oidc.php b/modules/cms/ui/themes/default/html/views/login/oidc.php @@ -0,0 +1 @@ +<?php /* THIS FILE IS GENERATED from oidc.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?>+ \ No newline at end of file diff --git a/modules/cms/ui/themes/default/html/views/login/oidc.tpl.src.xml b/modules/cms/ui/themes/default/html/views/login/oidc.tpl.src.xml @@ -0,0 +1,3 @@ +<output xmlns="http://www.openrat.de/template" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.openrat.de/template ../../../../../../../template_engine/components/template.xsd" /> +<!-- empty, because this action does only make HTTP redirects -->+ \ No newline at end of file diff --git a/modules/cms/ui/themes/default/html/views/login/openid.php b/modules/cms/ui/themes/default/html/views/login/openid.php @@ -1,112 +0,0 @@ -<?php /* THIS FILE IS GENERATED from openid.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <form name="<?php echo O::escapeHtml('') ?>" target="<?php echo O::escapeHtml('_self') ?>" data-target="<?php echo O::escapeHtml('_top') ?>" action="<?php echo O::escapeHtml('./') ?>" data-method="<?php echo O::escapeHtml('login') ?>" data-action="<?php echo O::escapeHtml('login') ?>" data-id="<?php echo O::escapeHtml(''.@$_id.'') ?>" method="<?php echo O::escapeHtml('POST') ?>" enctype="<?php echo O::escapeHtml('application/x-www-form-urlencoded') ?>" data-async="<?php echo O::escapeHtml('') ?>" data-autosave="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-form or-login') ?>"><?php echo O::escapeHtml('') ?> - <div class="<?php echo O::escapeHtml('or-form-headline') ?>"><?php echo O::escapeHtml('') ?> - </div> - <div class="<?php echo O::escapeHtml('or-form-content') ?>"><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('token') ?>" value="<?php echo O::escapeHtml(''.@$_token.'') ?>" /><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('action') ?>" value="<?php echo O::escapeHtml('login') ?>" /><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('subaction') ?>" value="<?php echo O::escapeHtml('login') ?>" /><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('id') ?>" value="<?php echo O::escapeHtml(''.@$_id.'') ?>" /><?php echo O::escapeHtml('') ?> - <?php $if1=(O::config('security','openid','enable')); if($if1) { ?> - <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> - <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('OPENID').'') ?> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> - </i> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-open or-collapsible--on-open') ?>"><?php echo O::escapeHtml('') ?> - </i> - </h2> - <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> - <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> - </h3> - <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> - <div class="<?php echo O::escapeHtml('or-label') ?>"><?php echo O::escapeHtml('') ?> - <span><?php echo O::escapeHtml(''.@O::lang('openid_user').'') ?> - </span> - <?php $if1=!((O::config('security','openid','logo_url'))==FALSE); if($if1) { ?> - <img src="<?php echo O::escapeHtml(''.O::config('security','openid','logo_url').'') ?>" /><?php echo O::escapeHtml('') ?> - <?php } ?> - </div> - <div class="<?php echo O::escapeHtml('or-value') ?>"><?php echo O::escapeHtml('') ?> - <?php foreach( $openid_providers as $_key=>$_value) { ?> - <label><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('radio') ?>" name="<?php echo O::escapeHtml('openid_provider') ?>" value="<?php echo O::escapeHtml(''.@$_key.'') ?>" <?php if($_key==$openid_provider){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> /><?php echo O::escapeHtml('') ?> - <span><?php echo O::escapeHtml(''.@$_value.'') ?> - </span> - </label> - <br /><?php echo O::escapeHtml('') ?> - <?php } ?> - <?php $if1=($openid_user_identity); if($if1) { ?> - <input type="<?php echo O::escapeHtml('radio') ?>" name="<?php echo O::escapeHtml('openid_provider') ?>" value="<?php echo O::escapeHtml('identity') ?>" <?php if(@$openid_provider=='identity'){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-radio') ?>" /><?php echo O::escapeHtml('') ?> - <input name="<?php echo O::escapeHtml('openid_url') ?>" type="<?php echo O::escapeHtml('text') ?>" maxlength="<?php echo O::escapeHtml('256') ?>" value="<?php echo O::escapeHtml(''.@$openid_url.'') ?>" class="<?php echo O::escapeHtml('or-name or-input') ?>" /><?php echo O::escapeHtml('') ?> - <?php } ?> - </div> - </div> - </section> - </div> - </section> - <?php $if1=(intval(1)<count(size:dbids)); if($if1) { ?> - <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> - <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('DATABASE').'') ?> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--database') ?>"><?php echo O::escapeHtml('') ?> - </i> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> - </i> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-open or-collapsible--on-open') ?>"><?php echo O::escapeHtml('') ?> - </i> - </h2> - <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> - <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> - </h3> - <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> - <div class="<?php echo O::escapeHtml('or-label') ?>"><?php echo O::escapeHtml('') ?> - <label class="<?php echo O::escapeHtml('or-label') ?>"><?php echo O::escapeHtml('') ?> - <span><?php echo O::escapeHtml(''.@O::lang('DATABASE').'') ?> - </span> - </label> - </div> - <div class="<?php echo O::escapeHtml('or-value') ?>"><?php echo O::escapeHtml('') ?> - <select name="<?php echo O::escapeHtml('dbid') ?>" size="<?php echo O::escapeHtml('1') ?>" class="<?php echo O::escapeHtml('or-input') ?>"><?php echo O::escapeHtml('') ?> - <?php foreach($dbids as $_key=>$_value) { ?> - <option value="<?php echo O::escapeHtml(''.@$_key.'') ?>" <?php if($_key==$actdbid){ ?>selected="<?php echo O::escapeHtml('selected') ?>"<?php } ?>><?php echo O::escapeHtml(''.@$_value.'') ?> - </option> - <?php } ?> - </select> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('screenwidth') ?>" value="<?php echo O::escapeHtml('9999') ?>" /><?php echo O::escapeHtml('') ?> - </div> - </div> - </section> - </div> - </section> - <?php } ?> - <?php if(!$if1) { ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('dbid') ?>" value="<?php echo O::escapeHtml(''.@$actdbid.'') ?>" /><?php echo O::escapeHtml('') ?> - <?php } ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('objectid') ?>" value="<?php echo O::escapeHtml(''.@$objectid.'') ?>" /><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('modelid') ?>" value="<?php echo O::escapeHtml(''.@$modelid.'') ?>" /><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('projectid') ?>" value="<?php echo O::escapeHtml(''.@$projectid.'') ?>" /><?php echo O::escapeHtml('') ?> - <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('languageid') ?>" value="<?php echo O::escapeHtml(''.@$languageid.'') ?>" /><?php echo O::escapeHtml('') ?> - <?php } ?> - <?php if(!$if1) { ?> - <div class="<?php echo O::escapeHtml('or-message error') ?>"><?php echo O::escapeHtml('') ?> - <span><?php echo O::escapeHtml(''.@O::lang('OPENID_NOT_ENABLED').'') ?> - </span> - </div> - <?php } ?> - </div> - <div class="<?php echo O::escapeHtml('or-form-actionbar') ?>"><?php echo O::escapeHtml('') ?> - <div class="<?php echo O::escapeHtml('or-btn or-btn--secondary or-act-form-cancel') ?>"><?php echo O::escapeHtml('') ?> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--form-cancel') ?>"><?php echo O::escapeHtml('') ?> - </i> - <span class="<?php echo O::escapeHtml('or-form-btn-label') ?>"><?php echo O::escapeHtml(''.@O::lang('CANCEL').'') ?> - </span> - </div> - <div class="<?php echo O::escapeHtml('or-btn or-btn--primary or-act-form-save') ?>"><?php echo O::escapeHtml('') ?> - <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--form-ok') ?>"><?php echo O::escapeHtml('') ?> - </i> - <span class="<?php echo O::escapeHtml('or-form-btn-label') ?>"><?php echo O::escapeHtml(''.@O::lang('button_ok').'') ?> - </span> - </div> - </div> - </form>- \ No newline at end of file diff --git a/modules/cms/ui/themes/default/html/views/login/openid.tpl.src.xml b/modules/cms/ui/themes/default/html/views/login/openid.tpl.src.xml @@ -1,53 +0,0 @@ -<output xmlns="http://www.openrat.de/template" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openrat.de/template ../../../../../../../template_engine/components/template.xsd"> - <form action="login" subaction="login" target="_top"> - <!-- Open-Id --> - <if true="${config:security/openid/enable}"> - <group title="${message:OPENID}"> - <fieldset class="line" label=""> - <part class="label"> - <text value="${message:openid_user}"/> - <if not="true" empty="${config:security/openid/logo_url}"> - <image url="${config:security/openid/logo_url}"/> - </if> - </part> - <part class="value"> - <radiobox name="openid_provider" list="openid_providers"/> - <if true="${openid_user_identity}"> - <radio name="openid_provider" value="identity"/> - <input name="openid_url" class="name" size="20"/> - </if> - </part> - </fieldset> - </group> - <if value="size:dbids" greaterthan="1"> - <group title="${message:DATABASE}" icon="database"> - <fieldset class="line" label=""> - <part class="label"> - <label for="dbid"> - <text value="${message:DATABASE}"/> - </label> - </part> - <part class="value"> - <selectbox name="dbid" list="dbids" default="${actdbid}"/> - <hidden name="screenwidth" default="9999"/> - </part> - </fieldset> - </group> - </if> - <else> - <hidden name="dbid" default="${actdbid}"/> - </else> - <hidden name="objectid"/> - <hidden name="modelid"/> - <hidden name="projectid"/> - <hidden name="languageid"/> - </if> - <else> - <part class="message error"> - <text value="${message:OPENID_NOT_ENABLED}"/> - </part> - </else> - </form> - <!-- <newline></newline> <newline></newline> <link url="${config:login/gpl/url}" - target="_top" class="copyright"> <text value="${message:GPL}"></text> </link> --> -</output> diff --git a/modules/cms/ui/themes/default/html/views/login/password.php b/modules/cms/ui/themes/default/html/views/login/password.php @@ -1,5 +1,5 @@ <?php /* THIS FILE IS GENERATED from password.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <?php $if1=(O::config('login','send_password')); if($if1) { ?> + <?php $if2=(O::config('login','send_password')); if($if2) { ?> <form name="<?php echo O::escapeHtml('') ?>" target="<?php echo O::escapeHtml('_self') ?>" data-target="<?php echo O::escapeHtml('view') ?>" action="<?php echo O::escapeHtml('./') ?>" data-method="<?php echo O::escapeHtml('password') ?>" data-action="<?php echo O::escapeHtml('login') ?>" data-id="<?php echo O::escapeHtml(''.@$_id.'') ?>" method="<?php echo O::escapeHtml('POST') ?>" enctype="<?php echo O::escapeHtml('application/x-www-form-urlencoded') ?>" data-async="<?php echo O::escapeHtml('') ?>" data-autosave="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-form or-login') ?>"><?php echo O::escapeHtml('') ?> <div class="<?php echo O::escapeHtml('or-form-headline') ?>"><?php echo O::escapeHtml('') ?> </div> @@ -67,7 +67,7 @@ </div> </form> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if2) { ?> <div class="<?php echo O::escapeHtml('or-message error') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('PASSWORD_NOT_ENABLED').'') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/login/register.php b/modules/cms/ui/themes/default/html/views/login/register.php @@ -1,5 +1,5 @@ <?php /* THIS FILE IS GENERATED from register.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <?php $if1=(O::config('login','register')); if($if1) { ?> + <?php $if2=(O::config('login','register')); if($if2) { ?> <form name="<?php echo O::escapeHtml('') ?>" target="<?php echo O::escapeHtml('_self') ?>" data-target="<?php echo O::escapeHtml('view') ?>" action="<?php echo O::escapeHtml('./') ?>" data-method="<?php echo O::escapeHtml('register') ?>" data-action="<?php echo O::escapeHtml('login') ?>" data-id="<?php echo O::escapeHtml(''.@$_id.'') ?>" method="<?php echo O::escapeHtml('POST') ?>" enctype="<?php echo O::escapeHtml('application/x-www-form-urlencoded') ?>" data-async="<?php echo O::escapeHtml('') ?>" data-autosave="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-form or-login') ?>"><?php echo O::escapeHtml('') ?> <div class="<?php echo O::escapeHtml('or-form-headline') ?>"><?php echo O::escapeHtml('') ?> </div> @@ -61,7 +61,7 @@ </div> </form> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if2) { ?> <div class="<?php echo O::escapeHtml('or-message error') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('REGISTER_NOT_ENABLED').'') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/modellist/show.php b/modules/cms/ui/themes/default/html/views/modellist/show.php @@ -29,9 +29,9 @@ </span> </a> </td> - <?php $if1=(!$is_default); if($if1) { ?> + <?php $if5=(!$is_default); if($if5) { ?> <td class="<?php echo O::escapeHtml('or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($id)); if($if1) { ?> + <?php $if7=(isset($id)); if($if7) { ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('post') ?>" data-action="<?php echo O::escapeHtml('model') ?>" data-method="<?php echo O::escapeHtml('setdefault') ?>" data-id="<?php echo O::escapeHtml(''.@$id.'') ?>" data-extra="<?php echo O::escapeHtml('[]') ?>" data-data="<?php echo O::escapeHtml('{"action":"model","subaction":"setdefault","id":"'.@$id.'","token":"'.@$_token.'","none":"0"}') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('make_default').'') ?> </span> @@ -39,7 +39,7 @@ <?php } ?> </td> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if5) { ?> <td><?php echo O::escapeHtml('') ?> <em><?php echo O::escapeHtml(''.@O::lang('is_default').'') ?> </em> diff --git a/modules/cms/ui/themes/default/html/views/object/aclform.php b/modules/cms/ui/themes/default/html/views/object/aclform.php @@ -45,7 +45,7 @@ </select> </div> </section> - <?php $if1=(isset($groups)); if($if1) { ?> + <?php $if4=(isset($groups)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -104,12 +104,12 @@ <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> <?php foreach((array)$show as $k=>$t) { ?> <div class="<?php echo O::escapeHtml('or-') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=($t=='read'); if($if1) { ?> + <?php $if7=($t=='read'); if($if7) { ?> <?php { $$t= 1; ?> <?php } ?> <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml(''.@$t.'') ?>" disabled="<?php echo O::escapeHtml('disabled') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$$t){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if7) { ?> <?php { unset($$t) ?> <?php } ?> <label><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/object/info.php b/modules/cms/ui/themes/default/html/views/object/info.php @@ -153,7 +153,7 @@ </div> </div> </section> - <?php $if1=(isset($cache_filename)); if($if1) { ?> + <?php $if4=(isset($cache_filename)); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -206,7 +206,7 @@ </table> </div> </div> - <?php $if1=(($pages)==FALSE); if($if1) { ?> + <?php $if6=(($pages)==FALSE); if($if6) { ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> </span> <?php } ?> @@ -234,11 +234,11 @@ </span> </div> <div class="<?php echo O::escapeHtml('or-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=($is_valid); if($if1) { ?> + <?php $if8=($is_valid); if($if8) { ?> <span><?php echo O::escapeHtml(''.@O::lang('is_yes').'') ?> </span> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if8) { ?> <span><?php echo O::escapeHtml(''.@O::lang('is_no').'') ?> </span> <?php } ?> diff --git a/modules/cms/ui/themes/default/html/views/object/inherit.php b/modules/cms/ui/themes/default/html/views/object/inherit.php @@ -7,7 +7,7 @@ <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('action') ?>" value="<?php echo O::escapeHtml('object') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('subaction') ?>" value="<?php echo O::escapeHtml('inherit') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('id') ?>" value="<?php echo O::escapeHtml(''.@$_id.'') ?>" /><?php echo O::escapeHtml('') ?> - <?php $if1=($type=='folder'); if($if1) { ?> + <?php $if3=($type=='folder'); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('options').'') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/object/rights.php b/modules/cms/ui/themes/default/html/views/object/rights.php @@ -25,7 +25,7 @@ </span> </td> </tr> - <?php $if1=(($acls)==FALSE); if($if1) { ?> + <?php $if3=(($acls)==FALSE); if($if3) { ?> <tr class="<?php echo O::escapeHtml('or-data') ?>"><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('99') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> @@ -33,25 +33,25 @@ </td> </tr> <?php } ?> - <?php $if1=!(($acls)==FALSE); if($if1) { ?> + <?php $if3=!(($acls)==FALSE); if($if3) { ?> <?php } ?> <?php foreach((array)$acls as $aclid=>$acl) { extract($acl); ?> <tr class="<?php echo O::escapeHtml('or-data') ?>"><?php echo O::escapeHtml('') ?> <td><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($username)); if($if1) { ?> + <?php $if6=(isset($username)); if($if6) { ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-user') ?>"><?php echo O::escapeHtml('') ?> </i> <span><?php echo O::escapeHtml(''.@$username.'') ?> </span> <?php } ?> - <?php $if1=(isset($groupname)); if($if1) { ?> + <?php $if6=(isset($groupname)); if($if6) { ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-group') ?>"><?php echo O::escapeHtml('') ?> </i> <span><?php echo O::escapeHtml(''.@$groupname.'') ?> </span> <?php } ?> - <?php $if1=!(isset($username)); if($if1) { ?> - <?php $if1=!(isset($groupname)); if($if1) { ?> + <?php $if6=!(isset($username)); if($if6) { ?> + <?php $if7=!(isset($groupname)); if($if7) { ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-group') ?>"><?php echo O::escapeHtml('') ?> </i> <span><?php echo O::escapeHtml(''.@O::lang('all').'') ?> @@ -67,7 +67,7 @@ <td><?php echo O::escapeHtml('') ?> <?php { $bit= $acl[''.@$t.'']; ?> <?php } ?> - <?php $if1=($bit); if($if1) { ?> + <?php $if7=($bit); if($if7) { ?> <span><?php echo '&check;' ?> </span> <?php } ?> diff --git a/modules/cms/ui/themes/default/html/views/page/edit.php b/modules/cms/ui/themes/default/html/views/page/edit.php @@ -19,7 +19,7 @@ </span> </th> </tr> - <?php $if1=(($elements)==FALSE); if($if1) { ?> + <?php $if3=(($elements)==FALSE); if($if3) { ?> <tr><?php echo O::escapeHtml('') ?> <td><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> diff --git a/modules/cms/ui/themes/default/html/views/page/form.php b/modules/cms/ui/themes/default/html/views/page/form.php @@ -13,7 +13,7 @@ </div> <div class="<?php echo O::escapeHtml('or-table-area') ?>"><?php echo O::escapeHtml('') ?> <table width="<?php echo O::escapeHtml('100%') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(($el)==FALSE); if($if1) { ?> + <?php $if4=(($el)==FALSE); if($if4) { ?> <tr><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('4') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> @@ -21,7 +21,7 @@ </td> </tr> <?php } ?> - <?php $if1=!(($el)==FALSE); if($if1) { ?> + <?php $if4=!(($el)==FALSE); if($if4) { ?> <tr><?php echo O::escapeHtml('') ?> <td class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('PAGE_ELEMENT_NAME').'') ?> @@ -50,14 +50,14 @@ <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml(''.@$saveid.'') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$$saveid){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> </td> <td><?php echo O::escapeHtml('') ?> - <?php $if1=(in_array($type,explode(",",text,date,number)); if($if1) { ?> + <?php $if8=(in_array($type,explode(",",text,date,number)); if($if8) { ?> <input name="<?php echo O::escapeHtml(''.@$id.'') ?>" type="<?php echo O::escapeHtml('text') ?>" maxlength="<?php echo O::escapeHtml('255') ?>" value="<?php echo O::escapeHtml(''.@$value.'') ?>" class="<?php echo O::escapeHtml('or-input') ?>" /><?php echo O::escapeHtml('') ?> <?php } ?> - <?php $if1=($type=='longtext'); if($if1) { ?> + <?php $if8=($type=='longtext'); if($if8) { ?> <textarea name="<?php echo O::escapeHtml(''.@$id.'') ?>" class="<?php echo O::escapeHtml('or-input or-inputarea') ?>"><?php echo O::escapeHtml(''.@$value.'') ?> </textarea> <?php } ?> - <?php $if1=(in_array($type,explode(",",select,link,list)); if($if1) { ?> + <?php $if8=(in_array($type,explode(",",select,link,list)); if($if8) { ?> <select name="<?php echo O::escapeHtml(''.@$id.'') ?>" size="<?php echo O::escapeHtml('1') ?>" class="<?php echo O::escapeHtml('or-input') ?>"><?php echo O::escapeHtml('') ?> <?php foreach($list as $_key=>$_value) { ?> <option value="<?php echo O::escapeHtml(''.@$_key.'') ?>" <?php if($_key==$value){ ?>selected="<?php echo O::escapeHtml('selected') ?>"<?php } ?>><?php echo O::escapeHtml(''.@$_value.'') ?> @@ -80,7 +80,7 @@ </i> </h2> <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($release)); if($if1) { ?> + <?php $if4=(isset($release)); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-') ?>"><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml('release') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$release){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> <label class="<?php echo O::escapeHtml('or-label') ?>"><?php echo O::escapeHtml('') ?> @@ -91,7 +91,7 @@ </label> </div> <?php } ?> - <?php $if1=(isset($publish)); if($if1) { ?> + <?php $if4=(isset($publish)); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-') ?>"><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml('publish') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$publish){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> <label class="<?php echo O::escapeHtml('or-label') ?>"><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/page/info.php b/modules/cms/ui/themes/default/html/views/page/info.php @@ -158,7 +158,7 @@ </label> </div> <div class="<?php echo O::escapeHtml('or-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($templateid)); if($if1) { ?> + <?php $if6=(isset($templateid)); if($if6) { ?> <div class="<?php echo O::escapeHtml('or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('open') ?>" data-action="<?php echo O::escapeHtml('template') ?>" data-method="<?php echo O::escapeHtml('') ?>" data-id="<?php echo O::escapeHtml(''.@$templateid.'') ?>" data-extra="<?php echo O::escapeHtml('[]') ?>" href="<?php echo O::escapeHtml('#/template/'.@$templateid.'') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-template') ?>"><?php echo O::escapeHtml('') ?> @@ -168,7 +168,7 @@ </a> </div> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if6) { ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-template') ?>"><?php echo O::escapeHtml('') ?> </i> <span><?php echo O::escapeHtml(''.@$template_name.'') ?> diff --git a/modules/cms/ui/themes/default/html/views/page/pub.php b/modules/cms/ui/themes/default/html/views/page/pub.php @@ -1,5 +1,5 @@ <?php /* THIS FILE IS GENERATED from pub.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <?php $if1=(O::config('security','nopublish')); if($if1) { ?> + <?php $if2=(O::config('security','nopublish')); if($if2) { ?> <div class="<?php echo O::escapeHtml('or-message warn') ?>"><?php echo O::escapeHtml('') ?> <span class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml(''.@O::lang('NOPUBLISH_DESC').'') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/pageelement/advanced.php b/modules/cms/ui/themes/default/html/views/pageelement/advanced.php @@ -27,11 +27,11 @@ </td> <td title="<?php echo O::escapeHtml(''.@$text.'') ?>" class="<?php echo O::escapeHtml('or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('edit') ?>" data-action="<?php echo O::escapeHtml('pageelement') ?>" data-method="<?php echo O::escapeHtml('value') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-languageid="<?php echo O::escapeHtml(''.@$languageid.'') ?>" data-extra="<?php echo O::escapeHtml('{\'languageid\':\''.@$languageid.'\'}') ?>" href="<?php echo O::escapeHtml('#/pageelement') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=($date); if($if1) { ?> + <?php $if7=($date); if($if7) { ?> <?php include_once( 'modules/template_engine/components/html/component_date/component-date.php'); { component_date($date); ?> <?php } ?> <?php } ?> - <?php $if1=($text); if($if1) { ?> + <?php $if7=($text); if($if7) { ?> <span><?php echo O::escapeHtml(''.@$text.'') ?> </span> <?php } ?> diff --git a/modules/cms/ui/themes/default/html/views/pageelement/diff.php b/modules/cms/ui/themes/default/html/views/pageelement/diff.php @@ -33,7 +33,7 @@ </tr> <?php foreach((array)$diff as $list_key=>$list_value) { extract($list_value); ?> <tr class="<?php echo O::escapeHtml('or-diff') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($left)); if($if1) { ?> + <?php $if5=(isset($left)); if($if5) { ?> <td width="<?php echo O::escapeHtml('5%') ?>" class="<?php echo O::escapeHtml('or-line') ?>"><?php echo O::escapeHtml('') ?> <tt><?php echo O::escapeHtml(''.@$left['line'].'') ?> </tt> @@ -43,13 +43,13 @@ </span> </td> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if5) { ?> <td width="<?php echo O::escapeHtml('50%') ?>" colspan="<?php echo O::escapeHtml('2') ?>" class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(' ') ?> </span> </td> <?php } ?> - <?php $if1=(isset($right)); if($if1) { ?> + <?php $if5=(isset($right)); if($if5) { ?> <td width="<?php echo O::escapeHtml('5%') ?>" class="<?php echo O::escapeHtml('or-line') ?>"><?php echo O::escapeHtml('') ?> <tt><?php echo O::escapeHtml(''.@$right['line'].'') ?> </tt> @@ -59,7 +59,7 @@ </span> </td> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if5) { ?> <td width="<?php echo O::escapeHtml('50%') ?>" colspan="<?php echo O::escapeHtml('2') ?>" class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(' ') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/pageelement/history.php b/modules/cms/ui/themes/default/html/views/pageelement/history.php @@ -19,11 +19,11 @@ </span> </td> <td colspan="<?php echo O::escapeHtml('2') ?>" class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($compareid)); if($if1) { ?> + <?php $if6=(isset($compareid)); if($if6) { ?> <span><?php echo O::escapeHtml(''.@O::lang('COMPARE').'') ?> </span> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if6) { ?> <span><?php echo O::escapeHtml(' ') ?> </span> <?php } ?> @@ -49,7 +49,7 @@ </span> </td> </tr> - <?php $if1=(($el)==FALSE); if($if1) { ?> + <?php $if4=(($el)==FALSE); if($if4) { ?> <tr><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('8') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> @@ -64,19 +64,19 @@ </span> </td> <td><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($compareid)); if($if1) { ?> + <?php $if7=(isset($compareid)); if($if7) { ?> <input type="<?php echo O::escapeHtml('radio') ?>" name="<?php echo O::escapeHtml('compareid') ?>" value="<?php echo O::escapeHtml(''.@$id.'') ?>" <?php if(@$compareid=='${id}'){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-radio') ?>" /><?php echo O::escapeHtml('') ?> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if7) { ?> <span><?php echo O::escapeHtml(' ') ?> </span> <?php } ?> </td> <td><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($compareid)); if($if1) { ?> + <?php $if7=(isset($compareid)); if($if7) { ?> <input type="<?php echo O::escapeHtml('radio') ?>" name="<?php echo O::escapeHtml('withid') ?>" value="<?php echo O::escapeHtml(''.@$id.'') ?>" <?php if(@$withid=='${id}'){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-radio') ?>" /><?php echo O::escapeHtml('') ?> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if7) { ?> <span><?php echo O::escapeHtml(' ') ?> </span> <?php } ?> @@ -93,14 +93,14 @@ <span><?php echo O::escapeHtml(''.@$value.'') ?> </span> </td> - <?php $if1=($public); if($if1) { ?> + <?php $if6=($public); if($if6) { ?> <td><?php echo O::escapeHtml('') ?> <strong><?php echo O::escapeHtml(''.@O::lang('PUBLIC').'') ?> </strong> </td> <?php } ?> - <?php if(!$if1) { ?> - <?php $if1=(isset($releaseUrl)); if($if1) { ?> + <?php if(!$if6) { ?> + <?php $if7=(isset($releaseUrl)); if($if7) { ?> <td class="<?php echo O::escapeHtml('or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <a title="<?php echo O::escapeHtml(''.@O::lang('RELEASE_DESC').'') ?>" target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('post') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('release') ?>" data-id="<?php echo O::escapeHtml(''.@$objectid.'') ?>" data-extra-valueid="<?php echo O::escapeHtml(''.@$valueid.'') ?>" data-extra="<?php echo O::escapeHtml('{\'valueid\':\''.@$valueid.'\'}') ?>" data-data="<?php echo O::escapeHtml('{"action":"pageelement","subaction":"release","id":"'.@$objectid.'","token":"'.@$_token.'","valueid":"'.@$valueid.'","none":"0"}') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <strong><?php echo O::escapeHtml(''.@O::lang('RELEASE').'') ?> @@ -108,21 +108,21 @@ </a> </td> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if7) { ?> <td><?php echo O::escapeHtml('') ?> <em><?php echo O::escapeHtml(''.@O::lang('INACTIVE').'') ?> </em> </td> <?php } ?> <?php } ?> - <?php $if1=($active); if($if1) { ?> + <?php $if6=($active); if($if6) { ?> <td><?php echo O::escapeHtml('') ?> <em><?php echo O::escapeHtml(''.@O::lang('ACTIVE').'') ?> </em> </td> <?php } ?> - <?php if(!$if1) { ?> - <?php $if1=(isset($useUrl)); if($if1) { ?> + <?php if(!$if6) { ?> + <?php $if7=(isset($useUrl)); if($if7) { ?> <td class="<?php echo O::escapeHtml('or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <a title="<?php echo O::escapeHtml(''.@O::lang('USE_DESC').'') ?>" target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('post') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('use') ?>" data-id="<?php echo O::escapeHtml(''.@$objectid.'') ?>" data-extra-valueid="<?php echo O::escapeHtml(''.@$valueid.'') ?>" data-extra="<?php echo O::escapeHtml('{\'valueid\':\''.@$valueid.'\'}') ?>" data-data="<?php echo O::escapeHtml('{"action":"pageelement","subaction":"use","id":"'.@$objectid.'","token":"'.@$_token.'","valueid":"'.@$valueid.'","none":"0"}') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('USE').'') ?> @@ -130,7 +130,7 @@ </a> </td> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if7) { ?> <td><?php echo O::escapeHtml('') ?> </td> <?php } ?> diff --git a/modules/cms/ui/themes/default/html/views/pageelement/link.php b/modules/cms/ui/themes/default/html/views/pageelement/link.php @@ -23,8 +23,8 @@ </select> </td> </tr> - <?php $if1=(isset($release)); if($if1) { ?> - <?php $if1=(isset($publish)); if($if1) { ?> + <?php $if3=(isset($release)); if($if3) { ?> + <?php $if4=(isset($publish)); if($if4) { ?> <tr><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('2') ?>"><?php echo O::escapeHtml('') ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> @@ -41,7 +41,7 @@ </tr> <?php } ?> <?php } ?> - <?php $if1=(isset($release)); if($if1) { ?> + <?php $if3=(isset($release)); if($if3) { ?> <tr><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('2') ?>"><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml('release') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$release){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> @@ -54,7 +54,7 @@ </td> </tr> <?php } ?> - <?php $if1=(isset($publish)); if($if1) { ?> + <?php $if3=(isset($publish)); if($if3) { ?> <tr><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('2') ?>"><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml('publish') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$publish){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/pageelement/pub.php b/modules/cms/ui/themes/default/html/views/pageelement/pub.php @@ -1,5 +1,5 @@ <?php /* THIS FILE IS GENERATED from pub.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <?php $if1=(O::config('security','nopublish')); if($if1) { ?> + <?php $if2=(O::config('security','nopublish')); if($if2) { ?> <div class="<?php echo O::escapeHtml('or-message warn') ?>"><?php echo O::escapeHtml('') ?> <span class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml(''.@O::lang('NOPUBLISH_DESC').'') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/pageelement/value.php b/modules/cms/ui/themes/default/html/views/pageelement/value.php @@ -12,7 +12,7 @@ <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('value_time') ?>" value="<?php echo O::escapeHtml(''.@$value_time.'') ?>" /><?php echo O::escapeHtml('') ?> <span class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml(''.@$desc.'') ?> </span> - <?php $if1=($type=='date'); if($if1) { ?> + <?php $if3=($type=='date'); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('date').'') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> @@ -40,7 +40,7 @@ </div> </section> <?php } ?> - <?php $if1=($type=='text'); if($if1) { ?> + <?php $if3=($type=='text'); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -49,9 +49,9 @@ </div> </section> <?php } ?> - <?php $if1=($type=='longtext'); if($if1) { ?> + <?php $if3=($type=='longtext'); if($if3) { ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('format') ?>" value="<?php echo O::escapeHtml(''.@$format.'') ?>" /><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($preview)); if($if1) { ?> + <?php $if4=(isset($preview)); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-preview') ?>"><?php echo O::escapeHtml('') ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('page_preview').'') ?> @@ -67,16 +67,16 @@ </section> </div> <?php } ?> - <?php $if1=($editor=='markdown'); if($if1) { ?> + <?php $if4=($editor=='markdown'); if($if4) { ?> <textarea name="<?php echo O::escapeHtml('text') ?>" class="<?php echo O::escapeHtml('or-input or-editor or-markdown-editor') ?>"><?php echo O::escapeHtml(''.@$text.'') ?> </textarea> <?php } ?> - <?php $if1=($editor=='html'); if($if1) { ?> + <?php $if4=($editor=='html'); if($if4) { ?> <textarea name="<?php echo O::escapeHtml('text') ?>" id="<?php echo O::escapeHtml('pageelement_edit_editor') ?>" class="<?php echo O::escapeHtml('or-input or-editor or-html-editor') ?>"><?php echo ''.@$text.'' ?> </textarea> <?php } ?> - <?php $if1=($editor=='wiki'); if($if1) { ?> - <?php $if1=(isset($languagetext)); if($if1) { ?> + <?php $if4=($editor=='wiki'); if($if4) { ?> + <?php $if5=(isset($languagetext)); if($if5) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@$languagename.'') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> @@ -185,12 +185,12 @@ </div> </section> <?php } ?> - <?php $if1=($editor=='text'); if($if1) { ?> + <?php $if4=($editor=='text'); if($if4) { ?> <textarea name="<?php echo O::escapeHtml('text') ?>" class="<?php echo O::escapeHtml('or-input or-editor raw-editor') ?>"><?php echo O::escapeHtml(''.@$text.'') ?> </textarea> <?php } ?> <?php } ?> - <?php $if1=($type=='link'); if($if1) { ?> + <?php $if3=($type=='link'); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml('') ?> </h2> @@ -219,7 +219,7 @@ </div> </section> <?php } ?> - <?php $if1=($type=='list'); if($if1) { ?> + <?php $if3=($type=='list'); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml('') ?> </h2> @@ -235,7 +235,7 @@ </div> </section> <?php } ?> - <?php $if1=($type=='insert'); if($if1) { ?> + <?php $if3=($type=='insert'); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml('') ?> </h2> @@ -251,7 +251,7 @@ </div> </section> <?php } ?> - <?php $if1=($type=='number'); if($if1) { ?> + <?php $if3=($type=='number'); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml('') ?> </h2> @@ -263,7 +263,7 @@ </div> </section> <?php } ?> - <?php $if1=($type=='select'); if($if1) { ?> + <?php $if3=($type=='select'); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml('') ?> </h2> @@ -279,9 +279,9 @@ </div> </section> <?php } ?> - <?php $if1=($type=='longtext'); if($if1) { ?> - <?php $if1=($editor=='wiki'); if($if1) { ?> - <?php $if1=(isset($languages)); if($if1) { ?> + <?php $if3=($type=='longtext'); if($if3) { ?> + <?php $if4=($editor=='wiki'); if($if4) { ?> + <?php $if5=(isset($languages)); if($if5) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('editor_show_language').'') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> @@ -330,7 +330,7 @@ </i> </h2> <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($release)); if($if1) { ?> + <?php $if4=(isset($release)); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-') ?>"><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml('release') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$release){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> <label class="<?php echo O::escapeHtml('or-label') ?>"><?php echo O::escapeHtml('') ?> @@ -339,7 +339,7 @@ </label> </div> <?php } ?> - <?php $if1=(isset($publish)); if($if1) { ?> + <?php $if4=(isset($publish)); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-') ?>"><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('checkbox') ?>" name="<?php echo O::escapeHtml('publish') ?>" value="<?php echo O::escapeHtml('1') ?>" <?php if(@$publish){ ?>checked="<?php echo O::escapeHtml('checked') ?>"<?php } ?> class="<?php echo O::escapeHtml('or-form-checkbox') ?>" /><?php echo O::escapeHtml('') ?> <label class="<?php echo O::escapeHtml('or-label') ?>"><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/profile/memberships.php b/modules/cms/ui/themes/default/html/views/profile/memberships.php @@ -11,7 +11,7 @@ </span> </td> </tr> - <?php $if1=((groups)==FALSE); if($if1) { ?> + <?php $if3=((groups)==FALSE); if($if3) { ?> <tr class="<?php echo O::escapeHtml('or-data') ?>"><?php echo O::escapeHtml('') ?> <td><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> diff --git a/modules/cms/ui/themes/default/html/views/profile/pw.php b/modules/cms/ui/themes/default/html/views/profile/pw.php @@ -7,7 +7,7 @@ <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('action') ?>" value="<?php echo O::escapeHtml('profile') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('subaction') ?>" value="<?php echo O::escapeHtml('pw') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('id') ?>" value="<?php echo O::escapeHtml(''.@$_id.'') ?>" /><?php echo O::escapeHtml('') ?> - <?php $if1=($pwchange_enabled); if($if1) { ?> + <?php $if3=($pwchange_enabled); if($if3) { ?> <div class="<?php echo O::escapeHtml('or-line logo') ?>"><?php echo O::escapeHtml('') ?> <div class="<?php echo O::escapeHtml('or-label') ?>"><?php echo O::escapeHtml('') ?> <img src="<?php echo O::escapeHtml('themes/default/images/logo_changepassword.png') ?>" border="<?php echo O::escapeHtml('') ?>" /><?php echo O::escapeHtml('') ?> @@ -47,7 +47,7 @@ </div> </section> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if3) { ?> <div class="<?php echo O::escapeHtml('or-message message-warn') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('pwchange_not_allowed').'') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/project/edit.php b/modules/cms/ui/themes/default/html/views/project/edit.php @@ -22,7 +22,7 @@ </a> </td> </tr> - <?php $if1=($is_project_admin); if($if1) { ?> + <?php $if3=($is_project_admin); if($if3) { ?> <tr class="<?php echo O::escapeHtml('or-data') ?>"><?php echo O::escapeHtml('') ?> <td class="<?php echo O::escapeHtml('or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <a target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('open') ?>" data-action="<?php echo O::escapeHtml('templatelist') ?>" data-method="<?php echo O::escapeHtml('') ?>" data-id="<?php echo O::escapeHtml(''.@$projectid.'') ?>" data-extra="<?php echo O::escapeHtml('[]') ?>" href="<?php echo O::escapeHtml('#/templatelist/'.@$projectid.'') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/project/history.php b/modules/cms/ui/themes/default/html/views/project/history.php @@ -24,23 +24,23 @@ </td> </tr> <?php foreach((array)$timeline as $list_key=>$list_value) { extract($list_value); ?> - <?php $if1=($typeid=='1'); if($if1) { ?> + <?php $if4=($typeid=='1'); if($if4) { ?> <?php { $type= folder; ?> <?php } ?> <?php } ?> - <?php $if1=($typeid=='2'); if($if1) { ?> + <?php $if4=($typeid=='2'); if($if4) { ?> <?php { $type= file; ?> <?php } ?> <?php } ?> - <?php $if1=($typeid=='3'); if($if1) { ?> + <?php $if4=($typeid=='3'); if($if4) { ?> <?php { $type= page; ?> <?php } ?> <?php } ?> - <?php $if1=($typeid=='4'); if($if1) { ?> + <?php $if4=($typeid=='4'); if($if4) { ?> <?php { $type= link; ?> <?php } ?> <?php } ?> - <?php $if1=($typeid=='5'); if($if1) { ?> + <?php $if4=($typeid=='5'); if($if4) { ?> <?php { $type= url; ?> <?php } ?> <?php } ?> diff --git a/modules/cms/ui/themes/default/html/views/project/prop.php b/modules/cms/ui/themes/default/html/views/project/prop.php @@ -46,7 +46,7 @@ <input name="<?php echo O::escapeHtml('target_dir') ?>" type="<?php echo O::escapeHtml('text') ?>" maxlength="<?php echo O::escapeHtml('255') ?>" value="<?php echo O::escapeHtml(''.@$target_dir.'') ?>" class="<?php echo O::escapeHtml('or-filename or-input') ?>" /><?php echo O::escapeHtml('') ?> </div> </section> - <?php $if1=(O::config('publish','project','override_system_command')); if($if1) { ?> + <?php $if4=(O::config('publish','project','override_system_command')); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('PROJECT_CMD_AFTER_PUBLISH').'') ?> </h3> diff --git a/modules/cms/ui/themes/default/html/views/start/userprojecttimeline.php b/modules/cms/ui/themes/default/html/views/start/userprojecttimeline.php @@ -20,19 +20,19 @@ </td> </tr> <?php foreach((array)$timeline as $list_key=>$list_value) { extract($list_value); ?> - <?php $if1=($typeid=='1'); if($if1) { ?> + <?php $if4=($typeid=='1'); if($if4) { ?> <?php { $type= folder; ?> <?php } ?> <?php } ?> - <?php $if1=($typeid=='2'); if($if1) { ?> + <?php $if4=($typeid=='2'); if($if4) { ?> <?php { $type= file; ?> <?php } ?> <?php } ?> - <?php $if1=($typeid=='4'); if($if1) { ?> + <?php $if4=($typeid=='4'); if($if4) { ?> <?php { $type= link; ?> <?php } ?> <?php } ?> - <?php $if1=($typeid=='3'); if($if1) { ?> + <?php $if4=($typeid=='3'); if($if4) { ?> <?php { $type= page; ?> <?php } ?> <?php } ?> diff --git a/modules/cms/ui/themes/default/html/views/template/edit.php b/modules/cms/ui/themes/default/html/views/template/edit.php @@ -31,7 +31,7 @@ </td> </tr> <?php } ?> - <?php $if1=(($elements)==FALSE); if($if1) { ?> + <?php $if3=(($elements)==FALSE); if($if3) { ?> <tr><?php echo O::escapeHtml('') ?> <td colspan="<?php echo O::escapeHtml('2') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> diff --git a/modules/cms/ui/themes/default/html/views/template/listing.php b/modules/cms/ui/themes/default/html/views/template/listing.php @@ -22,7 +22,7 @@ </table> </div> </div> - <?php $if1=(($templates)==FALSE); if($if1) { ?> + <?php $if2=(($templates)==FALSE); if($if2) { ?> <span><?php echo O::escapeHtml(''.@O::lang('NO_TEMPLATES_AVAILABLE_DESC').'') ?> </span> <?php } ?> diff --git a/modules/cms/ui/themes/default/html/views/template/pub.php b/modules/cms/ui/themes/default/html/views/template/pub.php @@ -1,5 +1,5 @@ <?php /* THIS FILE IS GENERATED from pub.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <?php $if1=(O::config('security','nopublish')); if($if1) { ?> + <?php $if2=(O::config('security','nopublish')); if($if2) { ?> <div class="<?php echo O::escapeHtml('or-message warn') ?>"><?php echo O::escapeHtml('') ?> <span class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml(''.@O::lang('NOPUBLISH_DESC').'') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/template/srcelement.php b/modules/cms/ui/themes/default/html/views/template/srcelement.php @@ -7,7 +7,7 @@ <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('action') ?>" value="<?php echo O::escapeHtml('template') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('subaction') ?>" value="<?php echo O::escapeHtml('srcelement') ?>" /><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('id') ?>" value="<?php echo O::escapeHtml(''.@$_id.'') ?>" /><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($elements)); if($if1) { ?> + <?php $if3=(isset($elements)); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> @@ -30,7 +30,7 @@ </div> </section> <?php } ?> - <?php $if1=(isset($writable_elements)); if($if1) { ?> + <?php $if3=(isset($writable_elements)); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml('') ?> </h2> diff --git a/modules/cms/ui/themes/default/html/views/templatelist/show.php b/modules/cms/ui/themes/default/html/views/templatelist/show.php @@ -23,7 +23,7 @@ </td> </tr> <?php } ?> - <?php $if1=(($templates)==FALSE); if($if1) { ?> + <?php $if3=(($templates)==FALSE); if($if3) { ?> <tr><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NO_TEMPLATES_AVAILABLE_DESC').'') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/text/pub.php b/modules/cms/ui/themes/default/html/views/text/pub.php @@ -1,5 +1,5 @@ <?php /* THIS FILE IS GENERATED from pub.tpl.src.xml - DO NOT CHANGE */ defined('APP_STARTED') || die('Forbidden'); use \template_engine\Output as O; ?> - <?php $if1=(O::config('security','nopublish')); if($if1) { ?> + <?php $if2=(O::config('security','nopublish')); if($if2) { ?> <div class="<?php echo O::escapeHtml('or-message warn') ?>"><?php echo O::escapeHtml('') ?> <span class="<?php echo O::escapeHtml('or-help') ?>"><?php echo O::escapeHtml(''.@O::lang('NOPUBLISH_DESC').'') ?> </span> diff --git a/modules/cms/ui/themes/default/html/views/text/size.php b/modules/cms/ui/themes/default/html/views/text/size.php @@ -25,7 +25,7 @@ </div> </div> </section> - <?php $if1=!(($formats)==FALSE); if($if1) { ?> + <?php $if3=!(($formats)==FALSE); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('IMAGE_NEW_SIZE').'') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/title/show.php b/modules/cms/ui/themes/default/html/views/title/show.php @@ -9,7 +9,7 @@ <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--menu-menu') ?>"><?php echo O::escapeHtml('') ?> </i> </div> - <?php $if1=(isset($dbname)); if($if1) { ?> + <?php $if4=(isset($dbname)); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-toolbar-icon or-menu-category') ?>"><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--database') ?>"><?php echo O::escapeHtml('') ?> </i> @@ -27,7 +27,7 @@ </div> </div> <?php } ?> - <?php $if1=($isLoggedIn); if($if1) { ?> + <?php $if4=($isLoggedIn); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-toolbar-icon or-act-clickable or-filtered or-on-action-folder or-on-action-page or-on-action-file or-on-action-projectlist or-on-action-templatelist or-on-action-userlist or-on-action-grouplist or-on-action-languagelist or-on-action-modellist') ?>"><?php echo O::escapeHtml('') ?> <a title="<?php echo O::escapeHtml(''.@O::lang('menu_new_desc').'') ?>" target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('add') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('add') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':null,\'dialogMethod\':\'add\'}') ?>" href="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--method-add') ?>"><?php echo O::escapeHtml('') ?> @@ -35,7 +35,7 @@ </a> </div> <?php } ?> - <?php $if1=($isLoggedIn); if($if1) { ?> + <?php $if4=($isLoggedIn); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-toolbar-icon or-act-clickable or-filtered or-on-action-folder or-on-action-page or-on-action-file or-on-action-image or-on-action-text or-on-action-pageelement or-on-action-template') ?>"><?php echo O::escapeHtml('') ?> <a title="<?php echo O::escapeHtml(''.@O::lang('menu_pub_desc').'') ?>" target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('') ?>" data-method="<?php echo O::escapeHtml('pub') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('pub') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':null,\'dialogMethod\':\'pub\'}') ?>" href="<?php echo O::escapeHtml('') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--method-publish') ?>"><?php echo O::escapeHtml('') ?> @@ -43,7 +43,7 @@ </a> </div> <?php } ?> - <?php $if1=($isLoggedIn); if($if1) { ?> + <?php $if4=($isLoggedIn); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-toolbar-icon or-menu-category') ?>"><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-file') ?>"><?php echo O::escapeHtml('') ?> </i> @@ -159,7 +159,7 @@ </div> </div> <?php } ?> - <?php $if1=($isLoggedIn); if($if1) { ?> + <?php $if4=($isLoggedIn); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-toolbar-icon or-menu-category') ?>"><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--menu-edit or-menu-icon') ?>"><?php echo O::escapeHtml('') ?> </i> @@ -247,7 +247,7 @@ </div> </div> <?php } ?> - <?php $if1=($isLoggedIn); if($if1) { ?> + <?php $if4=($isLoggedIn); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-toolbar-icon or-menu-category') ?>"><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--menu-extra') ?>"><?php echo O::escapeHtml('') ?> </i> @@ -357,7 +357,7 @@ <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--dropdown or-menu-dropdown-icon') ?>"><?php echo O::escapeHtml('') ?> </i> <div class="<?php echo O::escapeHtml('or-dropdown') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=($isLoggedIn); if($if1) { ?> + <?php $if6=($isLoggedIn); if($if6) { ?> <div class="<?php echo O::escapeHtml('or-dropdown-entry or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <a title="<?php echo O::escapeHtml(''.@O::lang('menu_PROFILE_DESC').'') ?>" target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('profile') ?>" data-method="<?php echo O::escapeHtml('edit') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('profile') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('edit') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':\'profile\',\'dialogMethod\':\'edit\'}') ?>" href="<?php echo O::escapeHtml('#/profile') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-user') ?>"><?php echo O::escapeHtml('') ?> @@ -413,7 +413,7 @@ </a> </div> <?php } ?> - <?php if(!$if1) { ?> + <?php if(!$if6) { ?> <div class="<?php echo O::escapeHtml('or-dropdown-entry or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <a title="<?php echo O::escapeHtml(''.@O::lang('USER_LOGIN_DESC').'') ?>" target="<?php echo O::escapeHtml('_self') ?>" data-type="<?php echo O::escapeHtml('dialog') ?>" data-action="<?php echo O::escapeHtml('login') ?>" data-method="<?php echo O::escapeHtml('login') ?>" data-id="<?php echo O::escapeHtml('') ?>" data-extra-dialogAction="<?php echo O::escapeHtml('login') ?>" data-extra-dialogMethod="<?php echo O::escapeHtml('login') ?>" data-extra="<?php echo O::escapeHtml('{\'dialogAction\':\'login\',\'dialogMethod\':\'login\'}') ?>" href="<?php echo O::escapeHtml('#/login') ?>" class="<?php echo O::escapeHtml('or-link') ?>"><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--method-user') ?>"><?php echo O::escapeHtml('') ?> @@ -425,7 +425,7 @@ <?php } ?> </div> </div> - <?php $if1=($isLoggedIn); if($if1) { ?> + <?php $if4=($isLoggedIn); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-toolbar-icon or-menu-category or-search') ?>"><?php echo O::escapeHtml('') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--method-search') ?>"><?php echo O::escapeHtml('') ?> </i> diff --git a/modules/cms/ui/themes/default/html/views/user/info.php b/modules/cms/ui/themes/default/html/views/user/info.php @@ -33,7 +33,7 @@ </span> </div> </section> - <?php $if1=(O::config('security','user','show_admin_mail')); if($if1) { ?> + <?php $if4=(O::config('security','user','show_admin_mail')); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('user_mail').'') ?> </h3> diff --git a/modules/cms/ui/themes/default/html/views/user/prop.php b/modules/cms/ui/themes/default/html/views/user/prop.php @@ -29,7 +29,7 @@ <input name="<?php echo O::escapeHtml('fullname') ?>" type="<?php echo O::escapeHtml('text') ?>" maxlength="<?php echo O::escapeHtml('128') ?>" value="<?php echo O::escapeHtml(''.@$fullname.'') ?>" class="<?php echo O::escapeHtml('or-input') ?>" /><?php echo O::escapeHtml('') ?> </div> </section> - <?php $if1=(O::config('security','user','show_admin_mail')); if($if1) { ?> + <?php $if4=(O::config('security','user','show_admin_mail')); if($if4) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('user_mail').'') ?> </h3> @@ -102,13 +102,6 @@ </div> </section> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> - <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('user_ldapdn').'') ?> - </h3> - <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> - <input name="<?php echo O::escapeHtml('ldap_dn') ?>" type="<?php echo O::escapeHtml('text') ?>" maxlength="<?php echo O::escapeHtml('256') ?>" value="<?php echo O::escapeHtml(''.@$ldap_dn.'') ?>" class="<?php echo O::escapeHtml('or-input') ?>" /><?php echo O::escapeHtml('') ?> - </div> - </section> - <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml(''.@O::lang('user_style').'') ?> </h3> <div class="<?php echo O::escapeHtml('or-fieldset-value') ?>"><?php echo O::escapeHtml('') ?> diff --git a/modules/cms/ui/themes/default/html/views/user/prop.tpl.src.xml b/modules/cms/ui/themes/default/html/views/user/prop.tpl.src.xml @@ -31,10 +31,6 @@ <checkbox name="is_admin" label="${message:user_admin}"/> </fieldset> - <fieldset class="line" label="${message:user_ldapdn}"> - <input name="ldap_dn"/> - </fieldset> - <fieldset class="line" label="${message:user_style}"> <selectbox list="allstyles" name="style"/> </fieldset> diff --git a/modules/cms/ui/themes/default/html/views/user/pw.php b/modules/cms/ui/themes/default/html/views/user/pw.php @@ -15,7 +15,7 @@ <input type="<?php echo O::escapeHtml('hidden') ?>" name="<?php echo O::escapeHtml('password_proposal') ?>" value="<?php echo O::escapeHtml(''.@$password_proposal.'') ?>" /><?php echo O::escapeHtml('') ?> </div> </section> - <?php $if1=(O::config('mail','enabled')); if($if1) { ?> + <?php $if3=(O::config('mail','enabled')); if($if3) { ?> <section class="<?php echo O::escapeHtml('or-group or-collapsible or-collapsible--is-open or-collapsible--show') ?>"><?php echo O::escapeHtml('') ?> <h2 class="<?php echo O::escapeHtml('or-collapsible-title or-group-title or-collapsible-act-switch') ?>"><?php echo O::escapeHtml(''.@O::lang('options').'') ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--node-closed or-collapsible--on-closed') ?>"><?php echo O::escapeHtml('') ?> @@ -24,7 +24,7 @@ </i> </h2> <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($mail)); if($if1) { ?> + <?php $if5=(isset($mail)); if($if5) { ?> <section class="<?php echo O::escapeHtml('or-fieldset') ?>"><?php echo O::escapeHtml('') ?> <h3 class="<?php echo O::escapeHtml('or-fieldset-label') ?>"><?php echo O::escapeHtml('') ?> </h3> diff --git a/modules/cms/ui/themes/default/html/views/user/rights.php b/modules/cms/ui/themes/default/html/views/user/rights.php @@ -8,13 +8,13 @@ </i> </h2> <div class="<?php echo O::escapeHtml('or-collapsible-value or-group-value') ?>"><?php echo O::escapeHtml('') ?> - <?php $if1=(($rights)==FALSE); if($if1) { ?> + <?php $if4=(($rights)==FALSE); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@O::lang('NOT_FOUND').'') ?> </span> </div> <?php } ?> - <?php $if1=!(($rights)==FALSE); if($if1) { ?> + <?php $if4=!(($rights)==FALSE); if($if4) { ?> <div class="<?php echo O::escapeHtml('or-table-wrapper') ?>"><?php echo O::escapeHtml('') ?> <div class="<?php echo O::escapeHtml('or-table-filter') ?>"><?php echo O::escapeHtml('') ?> <input type="<?php echo O::escapeHtml('search') ?>" name="<?php echo O::escapeHtml('filter') ?>" placeholder="<?php echo O::escapeHtml(''.@O::lang('SEARCH_FILTER').'') ?>" class="<?php echo O::escapeHtml('or-input') ?>" /><?php echo O::escapeHtml('') ?> @@ -44,20 +44,20 @@ <?php foreach((array)$rights as $aclid=>$acl) { extract($acl); ?> <tr class="<?php echo O::escapeHtml('or-data or-act-clickable') ?>"><?php echo O::escapeHtml('') ?> <td><?php echo O::escapeHtml('') ?> - <?php $if1=(isset($username)); if($if1) { ?> + <?php $if9=(isset($username)); if($if9) { ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-user') ?>"><?php echo O::escapeHtml('') ?> </i> <span><?php echo O::escapeHtml(''.@$username.'') ?> </span> <?php } ?> - <?php $if1=(isset($groupname)); if($if1) { ?> + <?php $if9=(isset($groupname)); if($if9) { ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-group') ?>"><?php echo O::escapeHtml('') ?> </i> <span><?php echo O::escapeHtml(''.@$groupname.'') ?> </span> <?php } ?> - <?php $if1=!(isset($username)); if($if1) { ?> - <?php $if1=!(isset($groupname)); if($if1) { ?> + <?php $if9=!(isset($username)); if($if9) { ?> + <?php $if10=!(isset($groupname)); if($if10) { ?> <i class="<?php echo O::escapeHtml('or-image-icon or-image-icon--action-group') ?>"><?php echo O::escapeHtml('') ?> </i> <span><?php echo O::escapeHtml(''.@O::lang('all').'') ?> diff --git a/modules/cms/ui/themes/default/html/views/userlist/show.php b/modules/cms/ui/themes/default/html/views/userlist/show.php @@ -33,7 +33,7 @@ <td data-name="<?php echo O::escapeHtml(''.@$name.'') ?>" data-action="<?php echo O::escapeHtml('user') ?>" data-id="<?php echo O::escapeHtml(''.@$id.'') ?>" class="<?php echo O::escapeHtml('or-clickable') ?>"><?php echo O::escapeHtml('') ?> <span><?php echo O::escapeHtml(''.@$fullname.'') ?> </span> - <?php $if1=($isAdmin); if($if1) { ?> + <?php $if6=($isAdmin); if($if6) { ?> <span><?php echo O::escapeHtml('_(') ?> </span> <span><?php echo O::escapeHtml(''.@O::lang('USER_ADMIN').'') ?> diff --git a/modules/cms/ui/themes/default/script/openrat.js b/modules/cms/ui/themes/default/script/openrat.js @@ -268,6 +268,9 @@ jQuery.fn.orLinkify = function( options ) case 'external': window.open( $link.attr('data-url'),' _blank' ); break; + case 'window': + window.location.href = Openrat.Workbench.createUrl($link.attr('data-action'),$link.attr('data-method'),$link.attr('data-id')); + break; case 'popup': Openrat.Workbench.popupWindow = window.open( $link.attr('data-url'), 'Popup', 'location=no,menubar=no,scrollbars=yes,toolbar=no,resizable=yes'); diff --git a/modules/cms/ui/themes/default/script/openrat.min.js b/modules/cms/ui/themes/default/script/openrat.min.js @@ -116,7 +116,7 @@ else{this.currentItem.show()};if(this.fromOutside&&!e){s.push(function(t){this._ ;jQuery.fn.orSearch=function(e){var t=$.extend({'dropdown':$(),'select':function(e){},'afterSelect':function(){},'action':'search','method':'quicksearch'},e);return $(this).on('input',function(){let searchArgument=$(this).val();let dropdownEl=$(t.dropdown);if(searchArgument.length){$.ajax({'type':'GET',url:'./api/?action='+t.action+'&subaction='+t.method+'&output=json&search='+searchArgument,data:null,success:function(e,n,o){$(dropdownEl).empty();for(id in e.output.result){let result=e.output.result[id];let div=$('<div class="or-dropdown-entry or-search-result or-dropdown-entry--active" title="'+result.desc+'"></div>');div.data('object',{'name':result.name,'action':result.type,'id':result.id});let link=$('<a class="or-link"/>').attr('href',Openrat.Navigator.createShortUrl(result.type,result.id));link.click(function(e){e.preventDefault()});$(link).append('<i class="or-image-icon or-image-icon--action-'+result.type+'" />');$(link).append('<span class="or-dropdown-text">'+result.name+'</span>');$(div).append(link);$(dropdownEl).append(div)};if(e.output.result){$(dropdownEl).closest('.or-menu').addClass('menu--is-open');$(dropdownEl).addClass('dropdown--is-open')} else{$(dropdownEl).removeClass('dropdown--is-open')};$(dropdownEl).find('.or-search-result').click(function(e){t.select($(this).data('object'));t.afterSelect()})}})} else{$(dropdownEl).empty()}})}; -;jQuery.fn.orLinkify=function(t){var a=$.extend({'openAction':function(t,a,e){Openrat.Workbench.openNewAction(t,a,e)}},t);$(this).addClass('linkified');if($(this).is('a'))$(this).click(function(t){t.preventDefault()});else $(this).find('a').click(function(t){t.preventDefault()});return $(this).click(function(t){$el=$(this);if($el.is('a'))$link=$el;else $link=$el.find('a').first();let type=$link.attr('data-type');if($link.parent().hasClass('dropdown-entry--inactive'))return;switch(type){case'post':$form=$('<form />').attr('method','POST').addClass('invisible');$form.data('afterSuccess',$link.data('afterSuccess'));let params=jQuery.parseJSON($link.attr('data-data'));params.output='json';$.each(params,function(t,a){let $input=$('<input />').attr('type','hidden').attr('name',t).attr('value',a);$form.append($input)});let form=new Openrat.Form();form.initOnElement($form);form.submit();break;case'edit':case'dialog':Openrat.Workbench.startDialog($link.attr('data-name'),$link.attr('data-action'),$link.attr('data-method'),$link.attr('data-id'),$link.attr('data-extra'));break;case'external':window.open($link.attr('data-url'),' _blank');break;case'popup':Openrat.Workbench.popupWindow=window.open($link.attr('data-url'),'Popup','location=no,menubar=no,scrollbars=yes,toolbar=no,resizable=yes');break;case'help':help($link,$link.attr('data-url'),$link.attr('data-suffix'));break;case'fullscreen':fullscreen($link);break;case'open':a.openAction($link.text().trim(),$link.attr('data-action'),$link.attr('data-id'));break;default:throw'UI error: Unknown link type: '+type+' in link '+$link.html()}})}; +;jQuery.fn.orLinkify=function(t){var a=$.extend({'openAction':function(t,a,e){Openrat.Workbench.openNewAction(t,a,e)}},t);$(this).addClass('linkified');if($(this).is('a'))$(this).click(function(t){t.preventDefault()});else $(this).find('a').click(function(t){t.preventDefault()});return $(this).click(function(t){$el=$(this);if($el.is('a'))$link=$el;else $link=$el.find('a').first();let type=$link.attr('data-type');if($link.parent().hasClass('dropdown-entry--inactive'))return;switch(type){case'post':$form=$('<form />').attr('method','POST').addClass('invisible');$form.data('afterSuccess',$link.data('afterSuccess'));let params=jQuery.parseJSON($link.attr('data-data'));params.output='json';$.each(params,function(t,a){let $input=$('<input />').attr('type','hidden').attr('name',t).attr('value',a);$form.append($input)});let form=new Openrat.Form();form.initOnElement($form);form.submit();break;case'edit':case'dialog':Openrat.Workbench.startDialog($link.attr('data-name'),$link.attr('data-action'),$link.attr('data-method'),$link.attr('data-id'),$link.attr('data-extra'));break;case'external':window.open($link.attr('data-url'),' _blank');break;case'window':window.location.href=Openrat.Workbench.createUrl($link.attr('data-action'),$link.attr('data-method'),$link.attr('data-id'));break;case'popup':Openrat.Workbench.popupWindow=window.open($link.attr('data-url'),'Popup','location=no,menubar=no,scrollbars=yes,toolbar=no,resizable=yes');break;case'help':help($link,$link.attr('data-url'),$link.attr('data-suffix'));break;case'fullscreen':fullscreen($link);break;case'open':a.openAction($link.text().trim(),$link.attr('data-action'),$link.attr('data-id'));break;default:throw'UI error: Unknown link type: '+type+' in link '+$link.html()}})}; ;jQuery.fn.orTree=function(e){var n=$.extend({'openAction':function(e,n,o){}},e);let registerTreeBranchEvents=function(e){Openrat.Workbench.registerDraggable(e)};$(this).each(function(o,e){$(e).children('.or-navtree-node-control').click(function(){let $node=$(this).parent('.or-navtree-node');if($node.is('.or-navtree-node--is-open')){$node.children('ul').slideUp('fast').remove();$node.removeClass('navtree-node--is-open').addClass('navtree-node--is-closed').find('.or-navtree-tree-icon').removeClass('image-icon--node-open').addClass('image-icon--node-closed')} else{$(e).closest('div.view').addClass('loader');let $link=$node.find('a');let id=$link.data('id');let extraId=$link.data('extra');let loadBranchUrl='./?action=tree&subaction=branch&id='+id+'';if(typeof extraId==='string'){jQuery.each(jQuery.parseJSON(extraId.replace(/'/g,'"')),function(e,n){loadBranchUrl=loadBranchUrl+'&'+e+'='+n})} else if(typeof extraId==='object'){jQuery.each(extraId,function(e,n){loadBranchUrl=loadBranchUrl+'&'+e+'='+n})} diff --git a/modules/cms/ui/themes/default/script/plugin/jquery-plugin-orLinkify.js b/modules/cms/ui/themes/default/script/plugin/jquery-plugin-orLinkify.js @@ -74,6 +74,9 @@ jQuery.fn.orLinkify = function( options ) case 'external': window.open( $link.attr('data-url'),' _blank' ); break; + case 'window': + window.location.href = Openrat.Workbench.createUrl($link.attr('data-action'),$link.attr('data-method'),$link.attr('data-id')); + break; case 'popup': Openrat.Workbench.popupWindow = window.open( $link.attr('data-url'), 'Popup', 'location=no,menubar=no,scrollbars=yes,toolbar=no,resizable=yes'); diff --git a/modules/cms/update/Update.class.php b/modules/cms/update/Update.class.php @@ -12,11 +12,11 @@ use logger\Logger; class Update { // This is the required DB version: - const SUPPORTED_VERSION = 21; - // ----------------------------^^----------------------------- + const SUPPORTED_VERSION = 22; + // -----------------------^^----------------------------- const STATUS_UPDATE_PROGRESS = 0; - const STATUS_UPDATE_SUCCESS = 1; + const STATUS_UPDATE_SUCCESS = 1; public function isUpdateRequired(Database $db) { @@ -58,7 +58,7 @@ class Update $db->commit(); } - $updaterClassName = 'cms\update\version\DBVersion' . str_pad($installVersion, 6, '0', STR_PAD_LEFT); + $updaterClassName = __NAMESPACE__.'\version\DBVersion' . str_pad($installVersion, 6, '0', STR_PAD_LEFT); $db->start(); /** @var \database\DbVersion $updater */ diff --git a/modules/cms/update/version/DBVersion000022.class.php b/modules/cms/update/version/DBVersion000022.class.php @@ -0,0 +1,37 @@ +<?php + +namespace cms\update\version; + +use database\DbVersion; +use database\Column; + +/** + * External user ids from sso services like openid. + * The user name alone is not unique any more + * + * @author Jan Dankert + * + */ +class DBVersion000022 extends DbVersion +{ + /** + * + */ + public function update() + { + $table = $this->table('user'); + + $table->column('ldap_dn' )->drop(); + + $table->column('auth_type' )->type(Column::TYPE_INT )->size(2)->defaultValue(1 )->add(); + $table->column('issuer' )->type(Column::TYPE_VARCHAR )->size(255)->nullable()->add(); + $table->dropUniqueIndex( ['name'] ); + $table->addUniqueIndex ( ['name','auth_type','issuer'] ); + + // OpenId subject identifiers may have up to 255 chars, so we have to increase the length + $table->column('name' )->type(Column::TYPE_VARCHAR )->size(255)->modify(); + $table->column('fullname' )->type(Column::TYPE_VARCHAR )->size(255)->modify(); + + } +} + diff --git a/modules/configuration/Config.class.php b/modules/configuration/Config.class.php @@ -57,7 +57,7 @@ class Config $config = $this->config; foreach($names as $key ) - if (isset($this->config[$key]) && is_array($this->config[$key])) + if (isset($config[$key]) && is_array($config[$key])) $config = $config[$key]; else return new Config( [] ); diff --git a/modules/database/Column.class.php b/modules/database/Column.class.php @@ -61,9 +61,9 @@ class Column /** - * Creating a new column. + * Creating the column definition. */ - function add() + protected function getColumnDefinition() { $table = $this->table->getSqlName(); @@ -145,16 +145,27 @@ class Column } - $ddl = $this->db->sql('ALTER TABLE ' . $table . - ' ADD COLUMN ' . $this->name . ' ' . $dbmsInternalType . ($this->size != null ? '(' . $this->size . ')' : '') . + return $dbmsInternalType . ($this->size != null ? '(' . $this->size . ')' : '') . ($this->default !== null ? ' DEFAULT ' . (is_string($this->default) ? "'" : '') . $this->default . (is_string($this->default) ? "'" : '') : '') . - ' ' . ($this->nullable ? 'NULL' : 'NOT NULL') . ';' + ' ' . ($this->nullable ? 'NULL' : 'NOT NULL'); + } + + + public function add() { + $table = $this->table->getSqlName(); + $ddl = $this->db->sql('ALTER TABLE ' . $table . + ' ADD COLUMN ' . $this->name . ' ' . $this->getColumnDefinition(). ';' ); $ddl->query(); - - return $this; } + public function modify() { + $table = $this->table->getSqlName(); + $ddl = $this->db->sql('ALTER TABLE ' . $table . + ' MODIFY COLUMN ' . $this->name . ' ' . $this->getColumnDefinition() . ';' + ); + $ddl->query(); + } function drop() { diff --git a/modules/database/DbVersion.class.php b/modules/database/DbVersion.class.php @@ -9,7 +9,7 @@ abstract class DbVersion const TYPE_MYSQL = 1; const TYPE_POSTGRES = 2; const TYPE_SQLITE = 3; - const TYPE_ORACLE = 4; + const TYPE_ORACLE = 4; // Attention: ORACLE is NOT really supported. private $db; private $tablePrefix; @@ -34,7 +34,7 @@ abstract class DbVersion $dsnParts = explode(':', $db->conf['dsn']); $driver = $dsnParts[0]; }else { - $driver = $db['driver']; + $driver = $db->conf['driver']; } switch ($driver) { case 'mysql': diff --git a/modules/database/Statement.class.php b/modules/database/Statement.class.php @@ -253,7 +253,24 @@ class Statement */ function setIntOrNull( $name,$value ) { - $this->client->bind( $this->stmt, $name, $value ); + if ( is_int($value)) + $this->setInt( $name,$value); + else + $this->setNull( $name ); + } + + + /** + * Setzt eine Ganzzahl als Parameter.<br> + * @param $name string + * @param $value integer + */ + function setStringOrNull( $name,$value ) + { + if ( is_string($value)) + $this->setString( $name,$value); + else + $this->setNull( $name ); } diff --git a/modules/database/Table.class.php b/modules/database/Table.class.php @@ -151,15 +151,20 @@ class Table $ddl->query(); } - function dropIndex($indexName, $unique = false) + function dropIndex($columnNames) { - $ddl = $this->db->sql('DROP' . ($unique ? ' UNIQUE' : '') . ' INDEX ' . $indexName . ';'); + if (!is_array($columnNames)) + $columnNames = [$columnNames]; + + $indexName = $this->tablePrefix . self::INDEX_PREFIX . '_' . $this->name . '_' . implode('_', $columnNames) . $this->tableSuffix; + + $ddl = $this->db->sql('DROP INDEX ' . $indexName . ' ON ' . $this->getSqlName() . ';'); $ddl->query(); } public function dropUniqueIndex($indexName) { - $this->dropIndex($indexName, true); + $this->dropIndex($indexName); } public function dropPrimaryKey( $columnNames) diff --git a/modules/openid_connect/OpenIDConnectClient.php b/modules/openid_connect/OpenIDConnectClient.php @@ -0,0 +1,1735 @@ +<?php +/** + * + * Copyright MITRE 2020 + * + * OpenIDConnectClient for PHP5 + * Author: Michael Jett <mjett@mitre.org> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +namespace openid_connect; + +/** + * + * JWT signature verification support by Jonathan Reed <jdreed@mit.edu> + * Licensed under the same license as the rest of this file. + * + * phpseclib is required to validate the signatures of some tokens. + * It can be downloaded from: http://phpseclib.sourceforge.net/ + */ + +if (!class_exists('\phpseclib\Crypt\RSA') && !class_exists('Crypt_RSA')) { + user_error('Unable to find phpseclib Crypt/RSA.php. Ensure phpseclib is installed and in include_path before you include this file'); +} + +/** + * A wrapper around base64_decode which decodes Base64URL-encoded data, + * which is not the same alphabet as base64. + * @param string $base64url + * @return bool|string + */ +function base64url_decode($base64url) { + return base64_decode(b64url2b64($base64url)); +} + +/** + * Per RFC4648, "base64 encoding with URL-safe and filename-safe + * alphabet". This just replaces characters 62 and 63. None of the + * reference implementations seem to restore the padding if necessary, + * but we'll do it anyway. + * @param string $base64url + * @return string + */ +function b64url2b64($base64url) { + // "Shouldn't" be necessary, but why not + $padding = strlen($base64url) % 4; + if ($padding > 0) { + $base64url .= str_repeat('=', 4 - $padding); + } + return strtr($base64url, '-_', '+/'); +} + + +/** + * OpenIDConnect Exception Class + */ +class OpenIDConnectClientException extends \Exception +{ + +} + +/** + * Require the CURL and JSON PHP extensions to be installed + */ +if (!function_exists('curl_init')) { + throw new OpenIDConnectClientException('OpenIDConnect needs the CURL PHP extension.'); +} +if (!function_exists('json_decode')) { + throw new OpenIDConnectClientException('OpenIDConnect needs the JSON PHP extension.'); +} + +/** + * + * Please note this class stores nonces by default in $_SESSION['openid_connect_nonce'] + * + */ +class OpenIDConnectClient +{ + + /** + * @var string arbitrary id value + */ + private $clientID; + + /** + * @var string arbitrary name value + */ + private $clientName; + + /** + * @var string arbitrary secret value + */ + private $clientSecret; + + /** + * @var array holds the provider configuration + */ + private $providerConfig = array(); + + /** + * @var string http proxy if necessary + */ + private $httpProxy; + + /** + * @var string full system path to the SSL certificate + */ + private $certPath; + + /** + * @var bool Verify SSL peer on transactions + */ + private $verifyPeer = true; + + /** + * @var bool Verify peer hostname on transactions + */ + private $verifyHost = true; + + /** + * @var string if we acquire an access token it will be stored here + */ + protected $accessToken; + + /** + * @var string if we acquire a refresh token it will be stored here + */ + private $refreshToken; + + /** + * @var string if we acquire an id token it will be stored here + */ + protected $idToken; + + /** + * @var string stores the token response + */ + private $tokenResponse; + + /** + * @var array holds scopes + */ + private $scopes = array(); + + /** + * @var int|null Response code from the server + */ + private $responseCode; + + /** + * @var array holds response types + */ + private $responseTypes = array(); + + /** + * @var array holds a cache of info returned from the user info endpoint + */ + private $userInfo = array(); + + /** + * @var array holds authentication parameters + */ + private $authParams = array(); + + /** + * @var array holds additional registration parameters for example post_logout_redirect_uris + */ + private $registrationParams = array(); + + /** + * @var mixed holds well-known openid server properties + */ + private $wellKnown = false; + + /** + * @var mixed holds well-known opendid configuration parameters, like policy for MS Azure AD B2C User Flow + * @see https://docs.microsoft.com/en-us/azure/active-directory-b2c/user-flow-overview + */ + private $wellKnownConfigParameters = array(); + + /** + * @var int timeout (seconds) + */ + protected $timeOut = 60; + + /** + * @var int leeway (seconds) + */ + private $leeway = 300; + + /** + * @var array holds response types + */ + private $additionalJwks = array(); + + /** + * @var array holds verified jwt claims + */ + protected $verifiedClaims = array(); + + /** + * @var callable validator function for issuer claim + */ + private $issuerValidator; + + /** + * @var bool Allow OAuth 2 implicit flow; see http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth + */ + private $allowImplicitFlow = false; + /** + * @var string + */ + private $redirectURL; + + protected $enc_type = PHP_QUERY_RFC1738; + + /** + * @param $provider_url string optional + * + * @param $client_id string optional + * @param $client_secret string optional + * @param null $issuer + */ + public function __construct($provider_url = null, $client_id = null, $client_secret = null, $issuer = null) { + $this->setProviderURL($provider_url); + if ($issuer === null) { + $this->setIssuer($provider_url); + } else { + $this->setIssuer($issuer); + } + + $this->clientID = $client_id; + $this->clientSecret = $client_secret; + + $this->issuerValidator = function($iss){ + return ($iss === $this->getIssuer() || $iss === $this->getWellKnownIssuer() || $iss === $this->getWellKnownIssuer(true)); + }; + } + + /** + * @param $provider_url + */ + public function setProviderURL($provider_url) { + $this->providerConfig['providerUrl'] = $provider_url; + } + + /** + * @param $issuer + */ + public function setIssuer($issuer) { + $this->providerConfig['issuer'] = $issuer; + } + + /** + * @param $response_types + */ + public function setResponseTypes($response_types) { + $this->responseTypes = array_merge($this->responseTypes, (array)$response_types); + } + + /** + * @return bool + * @throws OpenIDConnectClientException + */ + public function authenticate() { + + // Do a preemptive check to see if the provider has thrown an error from a previous redirect + if (isset($_REQUEST['error'])) { + $desc = isset($_REQUEST['error_description']) ? ' Description: ' . $_REQUEST['error_description'] : ''; + throw new OpenIDConnectClientException('Error: ' . $_REQUEST['error'] .$desc); + } + + // If we have an authorization code then proceed to request a token + if (isset($_REQUEST['code'])) { + + $code = $_REQUEST['code']; + $token_json = $this->requestTokens($code); + + // Throw an error if the server returns one + if (isset($token_json->error)) { + if (isset($token_json->error_description)) { + throw new OpenIDConnectClientException($token_json->error_description); + } + throw new OpenIDConnectClientException('Got response: ' . $token_json->error); + } + + // Do an OpenID Connect session check + if ($_REQUEST['state'] !== $this->getState()) { + throw new OpenIDConnectClientException('Unable to determine state'); + } + + // Cleanup state + $this->unsetState(); + + if (!property_exists($token_json, 'id_token')) { + throw new OpenIDConnectClientException('User did not authorize openid scope.'); + } + + $claims = $this->decodeJWT($token_json->id_token, 1); + + // Verify the signature + if ($this->canVerifySignatures()) { + if (!$this->getProviderConfigValue('jwks_uri')) { + throw new OpenIDConnectClientException ('Unable to verify signature due to no jwks_uri being defined'); + } + if (!$this->verifyJWTsignature($token_json->id_token)) { + throw new OpenIDConnectClientException ('Unable to verify signature'); + } + } else { + user_error('Warning: JWT signature verification unavailable.'); + } + + // Save the id token + $this->idToken = $token_json->id_token; + + // Save the access token + $this->accessToken = $token_json->access_token; + + // If this is a valid claim + if ($this->verifyJWTclaims($claims, $token_json->access_token)) { + + // Clean up the session a little + $this->unsetNonce(); + + // Save the full response + $this->tokenResponse = $token_json; + + // Save the verified claims + $this->verifiedClaims = $claims; + + // Save the refresh token, if we got one + if (isset($token_json->refresh_token)) { + $this->refreshToken = $token_json->refresh_token; + } + + // Success! + return true; + + } + + throw new OpenIDConnectClientException ('Unable to verify JWT claims'); + } + + if ($this->allowImplicitFlow && isset($_REQUEST['id_token'])) { + // if we have no code but an id_token use that + $id_token = $_REQUEST['id_token']; + + $accessToken = null; + if (isset($_REQUEST['access_token'])) { + $accessToken = $_REQUEST['access_token']; + } + + // Do an OpenID Connect session check + if ($_REQUEST['state'] !== $this->getState()) { + throw new OpenIDConnectClientException('Unable to determine state'); + } + + // Cleanup state + $this->unsetState(); + + $claims = $this->decodeJWT($id_token, 1); + + // Verify the signature + if ($this->canVerifySignatures()) { + if (!$this->getProviderConfigValue('jwks_uri')) { + throw new OpenIDConnectClientException ('Unable to verify signature due to no jwks_uri being defined'); + } + if (!$this->verifyJWTsignature($id_token)) { + throw new OpenIDConnectClientException ('Unable to verify signature'); + } + } else { + user_error('Warning: JWT signature verification unavailable.'); + } + + // Save the id token + $this->idToken = $id_token; + + // If this is a valid claim + if ($this->verifyJWTclaims($claims, $accessToken)) { + + // Clean up the session a little + $this->unsetNonce(); + + // Save the verified claims + $this->verifiedClaims = $claims; + + // Save the access token + if ($accessToken) { + $this->accessToken = $accessToken; + } + + // Success! + return true; + + } + + throw new OpenIDConnectClientException ('Unable to verify JWT claims'); + } + + $this->requestAuthorization(); + return false; + + } + + /** + * It calls the end-session endpoint of the OpenID Connect provider to notify the OpenID + * Connect provider that the end-user has logged out of the relying party site + * (the client application). + * + * @param string $accessToken ID token (obtained at login) + * @param string|null $redirect URL to which the RP is requesting that the End-User's User Agent + * be redirected after a logout has been performed. The value MUST have been previously + * registered with the OP. Value can be null. + * + * @throws OpenIDConnectClientException + */ + public function signOut($accessToken, $redirect) { + $signout_endpoint = $this->getProviderConfigValue('end_session_endpoint'); + + $signout_params = null; + if($redirect === null){ + $signout_params = array('id_token_hint' => $accessToken); + } + else { + $signout_params = array( + 'id_token_hint' => $accessToken, + 'post_logout_redirect_uri' => $redirect); + } + + $signout_endpoint .= (strpos($signout_endpoint, '?') === false ? '?' : '&') . http_build_query( $signout_params, null, '&', $this->enc_type); + $this->redirect($signout_endpoint); + } + + /** + * @param array $scope - example: openid, given_name, etc... + */ + public function addScope($scope) { + $this->scopes = array_merge($this->scopes, (array)$scope); + } + + /** + * @param array $param - example: prompt=login + */ + public function addAuthParam($param) { + $this->authParams = array_merge($this->authParams, (array)$param); + } + + /** + * @param array $param - example: post_logout_redirect_uris=[http://example.com/successful-logout] + */ + public function addRegistrationParam($param) { + $this->registrationParams = array_merge($this->registrationParams, (array)$param); + } + + /** + * @param $jwk object - example: (object) array('kid' => ..., 'nbf' => ..., 'use' => 'sig', 'kty' => "RSA", 'e' => "", 'n' => "") + */ + protected function addAdditionalJwk($jwk) { + $this->additionalJwks[] = $jwk; + } + + /** + * Get's anything that we need configuration wise including endpoints, and other values + * + * @param string $param + * @param string $default optional + * @throws OpenIDConnectClientException + * @return string + * + */ + protected function getProviderConfigValue($param, $default = null) { + + // If the configuration value is not available, attempt to fetch it from a well known config endpoint + // This is also known as auto "discovery" + if (!isset($this->providerConfig[$param])) { + $this->providerConfig[$param] = $this->getWellKnownConfigValue($param, $default); + } + + return $this->providerConfig[$param]; + } + + /** + * Get's anything that we need configuration wise including endpoints, and other values + * + * @param string $param + * @param string $default optional + * @throws OpenIDConnectClientException + * @return string + * + */ + private function getWellKnownConfigValue($param, $default = null) { + + // If the configuration value is not available, attempt to fetch it from a well known config endpoint + // This is also known as auto "discovery" + if(!$this->wellKnown) { + $well_known_config_url = rtrim($this->getProviderURL(), '/') . '/.well-known/openid-configuration'; + if (count($this->wellKnownConfigParameters) > 0){ + $well_known_config_url .= '?' . http_build_query($this->wellKnownConfigParameters) ; + } + $this->wellKnown = json_decode($this->fetchURL($well_known_config_url)); + } + + $value = false; + if(isset($this->wellKnown->{$param})){ + $value = $this->wellKnown->{$param}; + } + + if ($value) { + return $value; + } + + if (isset($default)) { + // Uses default value if provided + return $default; + } + + throw new OpenIDConnectClientException("The provider {$param} could not be fetched. Make sure your provider has a well known configuration available."); + } + + /** + * Set optionnal parameters for .well-known/openid-configuration + * + * @param string $param + * + */ + public function setWellKnownConfigParameters(array $params = []){ + $this->wellKnownConfigParameters=$params; + } + + + /** + * @param string $url Sets redirect URL for auth flow + */ + public function setRedirectURL ($url) { + if (parse_url($url,PHP_URL_HOST) !== false) { + $this->redirectURL = $url; + } + } + + /** + * Gets the URL of the current page we are on, encodes, and returns it + * + * @return string + */ + public function getRedirectURL() { + + // If the redirect URL has been set then return it. + if (property_exists($this, 'redirectURL') && $this->redirectURL) { + return $this->redirectURL; + } + + // Other-wise return the URL of the current page + + /** + * Thank you + * http://stackoverflow.com/questions/189113/how-do-i-get-current-page-full-url-in-php-on-a-windows-iis-server + */ + + /* + * Compatibility with multiple host headers. + * The problem with SSL over port 80 is resolved and non-SSL over port 443. + * Support of 'ProxyReverse' configurations. + */ + + if (isset($_SERVER['HTTP_UPGRADE_INSECURE_REQUESTS']) && ($_SERVER['HTTP_UPGRADE_INSECURE_REQUESTS'] === '1')) { + $protocol = 'https'; + } else { + $protocol = @$_SERVER['HTTP_X_FORWARDED_PROTO'] + ?: @$_SERVER['REQUEST_SCHEME'] + ?: ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http'); + } + + $port = @intval($_SERVER['HTTP_X_FORWARDED_PORT']) + ?: @intval($_SERVER['SERVER_PORT']) + ?: (($protocol === 'https') ? 443 : 80); + + $host = @explode(':', $_SERVER['HTTP_HOST'])[0] + ?: @$_SERVER['SERVER_NAME'] + ?: @$_SERVER['SERVER_ADDR']; + + $port = (443 === $port) || (80 === $port) ? '' : ':' . $port; + + return sprintf('%s://%s%s/%s', $protocol, $host, $port, @trim(reset(explode('?', $_SERVER['REQUEST_URI'])), '/')); + } + + /** + * Used for arbitrary value generation for nonces and state + * + * @return string + */ + protected function generateRandString() { + return md5(uniqid(rand(), TRUE)); + } + + /** + * Start Here + * @return void + * @throws OpenIDConnectClientException + */ + private function requestAuthorization() { + + $auth_endpoint = $this->getProviderConfigValue('authorization_endpoint'); + $response_type = 'code'; + + // Generate and store a nonce in the session + // The nonce is an arbitrary value + $nonce = $this->setNonce($this->generateRandString()); + + // State essentially acts as a session key for OIDC + $state = $this->setState($this->generateRandString()); + + $auth_params = array_merge($this->authParams, array( + 'response_type' => $response_type, + 'redirect_uri' => $this->getRedirectURL(), + 'client_id' => $this->clientID, + 'nonce' => $nonce, + 'state' => $state, + 'scope' => 'openid' + )); + + // If the client has been registered with additional scopes + if (count($this->scopes) > 0) { + $auth_params = array_merge($auth_params, array('scope' => implode(' ', array_merge($this->scopes, array('openid'))))); + } + + // If the client has been registered with additional response types + if (count($this->responseTypes) > 0) { + $auth_params = array_merge($auth_params, array('response_type' => implode(' ', $this->responseTypes))); + } + + $auth_endpoint .= (strpos($auth_endpoint, '?') === false ? '?' : '&') . http_build_query($auth_params, null, '&', $this->enc_type); + + $this->commitSession(); + $this->redirect($auth_endpoint); + } + + /** + * Requests a client credentials token + * + * @throws OpenIDConnectClientException + */ + public function requestClientCredentialsToken() { + $token_endpoint = $this->getProviderConfigValue('token_endpoint'); + + $headers = []; + + $grant_type = 'client_credentials'; + + $post_data = array( + 'grant_type' => $grant_type, + 'client_id' => $this->clientID, + 'client_secret' => $this->clientSecret, + 'scope' => implode(' ', $this->scopes) + ); + + // Convert token params to string format + $post_params = http_build_query($post_data, null, '&', $this->enc_type); + + return json_decode($this->fetchURL($token_endpoint, $post_params, $headers)); + } + + + /** + * Requests a resource owner token + * (Defined in https://tools.ietf.org/html/rfc6749#section-4.3) + * + * @param boolean $bClientAuth Indicates that the Client ID and Secret be used for client authentication + * @return mixed + * @throws OpenIDConnectClientException + */ + public function requestResourceOwnerToken($bClientAuth = FALSE) { + $token_endpoint = $this->getProviderConfigValue('token_endpoint'); + + $headers = []; + + $grant_type = 'password'; + + $post_data = array( + 'grant_type' => $grant_type, + 'username' => $this->authParams['username'], + 'password' => $this->authParams['password'], + 'scope' => implode(' ', $this->scopes) + ); + + //For client authentication include the client values + if($bClientAuth) { + $post_data['client_id'] = $this->clientID; + $post_data['client_secret'] = $this->clientSecret; + } + + // Convert token params to string format + $post_params = http_build_query($post_data, null, '&', $this->enc_type); + + return json_decode($this->fetchURL($token_endpoint, $post_params, $headers)); + } + + + /** + * Requests ID and Access tokens + * + * @param string $code + * @return mixed + * @throws OpenIDConnectClientException + */ + protected function requestTokens($code) { + $token_endpoint = $this->getProviderConfigValue('token_endpoint'); + $token_endpoint_auth_methods_supported = $this->getProviderConfigValue('token_endpoint_auth_methods_supported', ['client_secret_basic']); + + $headers = []; + + $grant_type = 'authorization_code'; + + $token_params = array( + 'grant_type' => $grant_type, + 'code' => $code, + 'redirect_uri' => $this->getRedirectURL(), + 'client_id' => $this->clientID, + 'client_secret' => $this->clientSecret + ); + + # Consider Basic authentication if provider config is set this way + if (in_array('client_secret_basic', $token_endpoint_auth_methods_supported, true)) { + $headers = ['Authorization: Basic ' . base64_encode(urlencode($this->clientID) . ':' . urlencode($this->clientSecret))]; + unset($token_params['client_secret']); + unset($token_params['client_id']); + } + + // Convert token params to string format + $token_params = http_build_query($token_params, null, '&', $this->enc_type); + + $this->tokenResponse = json_decode($this->fetchURL($token_endpoint, $token_params, $headers)); + + return $this->tokenResponse; + } + + /** + * Requests Access token with refresh token + * + * @param string $refresh_token + * @return mixed + * @throws OpenIDConnectClientException + */ + public function refreshToken($refresh_token) { + $token_endpoint = $this->getProviderConfigValue('token_endpoint'); + + $grant_type = 'refresh_token'; + + $token_params = array( + 'grant_type' => $grant_type, + 'refresh_token' => $refresh_token, + 'client_id' => $this->clientID, + 'client_secret' => $this->clientSecret, + ); + + // Convert token params to string format + $token_params = http_build_query($token_params, null, '&', $this->enc_type); + + $json = json_decode($this->fetchURL($token_endpoint, $token_params)); + + if (isset($json->access_token)) { + $this->accessToken = $json->access_token; + } + + if (isset($json->refresh_token)) { + $this->refreshToken = $json->refresh_token; + } + + return $json; + } + + /** + * @param array $keys + * @param array $header + * @throws OpenIDConnectClientException + * @return object + */ + private function get_key_for_header($keys, $header) { + foreach ($keys as $key) { + if ($key->kty === 'RSA') { + if (!isset($header->kid) || $key->kid === $header->kid) { + return $key; + } + } else { + if (isset($key->alg) && $key->alg === $header->alg && $key->kid === $header->kid) { + return $key; + } + } + } + if ($this->additionalJwks) { + foreach ($this->additionalJwks as $key) { + if ($key->kty === 'RSA') { + if (!isset($header->kid) || $key->kid === $header->kid) { + return $key; + } + } else { + if (isset($key->alg) && $key->alg === $header->alg && $key->kid === $header->kid) { + return $key; + } + } + } + } + if (isset($header->kid)) { + throw new OpenIDConnectClientException('Unable to find a key for (algorithm, kid):' . $header->alg . ', ' . $header->kid . ')'); + } + + throw new OpenIDConnectClientException('Unable to find a key for RSA'); + } + + + /** + * @param string $hashtype + * @param object $key + * @param $payload + * @param $signature + * @param $signatureType + * @return bool + * @throws OpenIDConnectClientException + */ + private function verifyRSAJWTsignature($hashtype, $key, $payload, $signature, $signatureType) { + if (!class_exists('\phpseclib\Crypt\RSA') && !class_exists('Crypt_RSA')) { + throw new OpenIDConnectClientException('Crypt_RSA support unavailable.'); + } + if (!(property_exists($key, 'n') && property_exists($key, 'e'))) { + throw new OpenIDConnectClientException('Malformed key object'); + } + + /* We already have base64url-encoded data, so re-encode it as + regular base64 and use the XML key format for simplicity. + */ + $public_key_xml = "<RSAKeyValue>\r\n". + ' <Modulus>' . b64url2b64($key->n) . "</Modulus>\r\n" . + ' <Exponent>' . b64url2b64($key->e) . "</Exponent>\r\n" . + '</RSAKeyValue>'; + if(class_exists('Crypt_RSA', false)) { + $rsa = new Crypt_RSA(); + $rsa->setHash($hashtype); + if ($signatureType === 'PSS') { + $rsa->setMGFHash($hashtype); + } + $rsa->loadKey($public_key_xml, Crypt_RSA::PUBLIC_FORMAT_XML); + $rsa->signatureMode = $signatureType === 'PSS' ? Crypt_RSA::SIGNATURE_PSS : Crypt_RSA::SIGNATURE_PKCS1; + } else { + $rsa = new \phpseclib\Crypt\RSA(); + $rsa->setHash($hashtype); + if ($signatureType === 'PSS') { + $rsa->setMGFHash($hashtype); + } + $rsa->loadKey($public_key_xml, \phpseclib\Crypt\RSA::PUBLIC_FORMAT_XML); + $rsa->signatureMode = $signatureType === 'PSS' ? \phpseclib\Crypt\RSA::SIGNATURE_PSS : \phpseclib\Crypt\RSA::SIGNATURE_PKCS1; + } + return $rsa->verify($payload, $signature); + } + + /** + * @param string $hashtype + * @param object $key + * @param $payload + * @param $signature + * @return bool + * @throws OpenIDConnectClientException + */ + private function verifyHMACJWTsignature($hashtype, $key, $payload, $signature) + { + if (!function_exists('hash_hmac')) { + throw new OpenIDConnectClientException('hash_hmac support unavailable.'); + } + + $expected=hash_hmac($hashtype, $payload, $key, true); + + if (function_exists('hash_equals')) { + return hash_equals($signature, $expected); + } + + return self::hashEquals($signature, $expected); + } + + /** + * @param string $jwt encoded JWT + * @throws OpenIDConnectClientException + * @return bool + */ + public function verifyJWTsignature($jwt) { + if (!\is_string($jwt)) { + throw new OpenIDConnectClientException('Error token is not a string'); + } + $parts = explode('.', $jwt); + if (!isset($parts[0])) { + throw new OpenIDConnectClientException('Error missing part 0 in token'); + } + $signature = base64url_decode(array_pop($parts)); + if (false === $signature || '' === $signature) { + throw new OpenIDConnectClientException('Error decoding signature from token'); + } + $header = json_decode(base64url_decode($parts[0])); + if (null === $header || !\is_object($header)) { + throw new OpenIDConnectClientException('Error decoding JSON from token header'); + } + $payload = implode('.', $parts); + $jwks = json_decode($this->fetchURL($this->getProviderConfigValue('jwks_uri'))); + if ($jwks === NULL) { + throw new OpenIDConnectClientException('Error decoding JSON from jwks_uri'); + } + if (!isset($header->alg)) { + throw new OpenIDConnectClientException('Error missing signature type in token header'); + } + switch ($header->alg) { + case 'RS256': + case 'PS256': + case 'RS384': + case 'RS512': + $hashtype = 'sha' . substr($header->alg, 2); + $signatureType = $header->alg === 'PS256' ? 'PSS' : ''; + + $verified = $this->verifyRSAJWTsignature($hashtype, + $this->get_key_for_header($jwks->keys, $header), + $payload, $signature, $signatureType); + break; + case 'HS256': + case 'HS512': + case 'HS384': + $hashtype = 'SHA' . substr($header->alg, 2); + $verified = $this->verifyHMACJWTsignature($hashtype, $this->getClientSecret(), $payload, $signature); + break; + default: + throw new OpenIDConnectClientException('No support for signature type: ' . $header->alg); + } + return $verified; + } + + /** + * @param object $claims + * @param string|null $accessToken + * @return bool + */ + protected function verifyJWTclaims($claims, $accessToken = null) { + if(isset($claims->at_hash) && isset($accessToken)){ + if(isset($this->getIdTokenHeader()->alg) && $this->getIdTokenHeader()->alg !== 'none'){ + $bit = substr($this->getIdTokenHeader()->alg, 2, 3); + }else{ + // TODO: Error case. throw exception??? + $bit = '256'; + } + $len = ((int)$bit)/16; + $expected_at_hash = $this->urlEncode(substr(hash('sha'.$bit, $accessToken, true), 0, $len)); + } + return (($this->issuerValidator->__invoke($claims->iss)) + && (($claims->aud === $this->clientID) || in_array($this->clientID, $claims->aud, true)) + && ($claims->nonce === $this->getNonce()) + && ( !isset($claims->exp) || ((gettype($claims->exp) === 'integer') && ($claims->exp >= time() - $this->leeway))) + && ( !isset($claims->nbf) || ((gettype($claims->nbf) === 'integer') && ($claims->nbf <= time() + $this->leeway))) + && ( !isset($claims->at_hash) || $claims->at_hash === $expected_at_hash ) + ); + } + + /** + * @param string $str + * @return string + */ + protected function urlEncode($str) { + $enc = base64_encode($str); + $enc = rtrim($enc, '='); + $enc = strtr($enc, '+/', '-_'); + return $enc; + } + + /** + * @param string $jwt encoded JWT + * @param int $section the section we would like to decode + * @return object + */ + protected function decodeJWT($jwt, $section = 0) { + + $parts = explode('.', $jwt); + return json_decode(base64url_decode($parts[$section])); + } + + /** + * + * @param string|null $attribute optional + * + * Attribute Type Description + * user_id string REQUIRED Identifier for the End-User at the Issuer. + * name string End-User's full name in displayable form including all name parts, ordered according to End-User's locale and preferences. + * given_name string Given name or first name of the End-User. + * family_name string Surname or last name of the End-User. + * middle_name string Middle name of the End-User. + * nickname string Casual name of the End-User that may or may not be the same as the given_name. For instance, a nickname value of Mike might be returned alongside a given_name value of Michael. + * profile string URL of End-User's profile page. + * picture string URL of the End-User's profile picture. + * website string URL of End-User's web page or blog. + * email string The End-User's preferred e-mail address. + * verified boolean True if the End-User's e-mail address has been verified; otherwise false. + * gender string The End-User's gender: Values defined by this specification are female and male. Other values MAY be used when neither of the defined values are applicable. + * birthday string The End-User's birthday, represented as a date string in MM/DD/YYYY format. The year MAY be 0000, indicating that it is omitted. + * zoneinfo string String from zoneinfo [zoneinfo] time zone database. For example, Europe/Paris or America/Los_Angeles. + * locale string The End-User's locale, represented as a BCP47 [RFC5646] language tag. This is typically an ISO 639-1 Alpha-2 [ISO639‑1] language code in lowercase and an ISO 3166-1 Alpha-2 [ISO3166‑1] country code in uppercase, separated by a dash. For example, en-US or fr-CA. As a compatibility note, some implementations have used an underscore as the separator rather than a dash, for example, en_US; Implementations MAY choose to accept this locale syntax as well. + * phone_number string The End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED as the format of this Claim. For example, +1 (425) 555-1212 or +56 (2) 687 2400. + * address JSON object The End-User's preferred address. The value of the address member is a JSON [RFC4627] structure containing some or all of the members defined in Section 2.4.2.1. + * updated_time string Time the End-User's information was last updated, represented as a RFC 3339 [RFC3339] datetime. For example, 2011-01-03T23:58:42+0000. + * + * @return mixed + * + * @throws OpenIDConnectClientException + */ + public function requestUserInfo($attribute = null) { + + $user_info_endpoint = $this->getProviderConfigValue('userinfo_endpoint'); + $schema = 'openid'; + + $user_info_endpoint .= '?schema=' . $schema; + + //The accessToken has to be sent in the Authorization header. + // Accept json to indicate response type + $headers = ["Authorization: Bearer {$this->accessToken}", + 'Accept: application/json']; + + $user_json = json_decode($this->fetchURL($user_info_endpoint,null,$headers)); + if ($this->getResponseCode() <> 200) { + throw new OpenIDConnectClientException('The communication to retrieve user data has failed with status code '.$this->getResponseCode()); + } + $this->userInfo = $user_json; + + if($attribute === null) { + return $this->userInfo; + } + + if (property_exists($this->userInfo, $attribute)) { + return $this->userInfo->$attribute; + } + + return null; + } + + /** + * + * @param string|null $attribute optional + * + * Attribute Type Description + * exp int Expires at + * nbf int Not before + * ver string Version + * iss string Issuer + * sub string Subject + * aud string Audience + * nonce string nonce + * iat int Issued At + * auth_time int Authenatication time + * oid string Object id + * + * @return mixed + * + */ + public function getVerifiedClaims($attribute = null) { + + if($attribute === null) { + return $this->verifiedClaims; + } + + if (property_exists($this->verifiedClaims, $attribute)) { + return $this->verifiedClaims->$attribute; + } + + return null; + } + + /** + * @param string $url + * @param string | null $post_body string If this is set the post type will be POST + * @param array $headers Extra headers to be send with the request. Format as 'NameHeader: ValueHeader' + * @throws OpenIDConnectClientException + * @return mixed + */ + protected function fetchURL($url, $post_body = null, $headers = array()) { + + + // OK cool - then let's create a new cURL resource handle + $ch = curl_init(); + + // Determine whether this is a GET or POST + if ($post_body !== null) { + // curl_setopt($ch, CURLOPT_POST, 1); + // Alows to keep the POST method even after redirect + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_body); + + // Default content type is form encoded + $content_type = 'application/x-www-form-urlencoded'; + + // Determine if this is a JSON payload and add the appropriate content type + if (is_object(json_decode($post_body))) { + $content_type = 'application/json'; + } + + // Add POST-specific headers + $headers[] = "Content-Type: {$content_type}"; + + } + + // If we set some headers include them + if(count($headers) > 0) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + + // Set URL to download + curl_setopt($ch, CURLOPT_URL, $url); + + if (isset($this->httpProxy)) { + curl_setopt($ch, CURLOPT_PROXY, $this->httpProxy); + } + + // Include header in result? (0 = yes, 1 = no) + curl_setopt($ch, CURLOPT_HEADER, 0); + + // Allows to follow redirect + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + + /** + * Set cert + * Otherwise ignore SSL peer verification + */ + if (isset($this->certPath)) { + curl_setopt($ch, CURLOPT_CAINFO, $this->certPath); + } + + if($this->verifyHost) { + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + } else { + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + } + + if($this->verifyPeer) { + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + } else { + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + } + + // Should cURL return or print out the data? (true = return, false = print) + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + // Timeout in seconds + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeOut); + + // Download the given URL, and return output + $output = curl_exec($ch); + + // HTTP Response code from server may be required from subclass + $info = curl_getinfo($ch); + $this->responseCode = $info['http_code']; + + if ($output === false) { + throw new OpenIDConnectClientException('Curl error: (' . curl_errno($ch) . ') ' . curl_error($ch)); + } + + // Close the cURL resource, and free system resources + curl_close($ch); + + return $output; + } + + /** + * @param bool $appendSlash + * @return string + * @throws OpenIDConnectClientException + */ + public function getWellKnownIssuer($appendSlash = false) { + + return $this->getWellKnownConfigValue('issuer') . ($appendSlash ? '/' : ''); + } + + /** + * @return string + * @throws OpenIDConnectClientException + */ + public function getIssuer() { + + if (!isset($this->providerConfig['issuer'])) { + throw new OpenIDConnectClientException('The issuer has not been set'); + } + + return $this->providerConfig['issuer']; + } + + /** + * @return mixed + * @throws OpenIDConnectClientException + */ + public function getProviderURL() { + if (!isset($this->providerConfig['providerUrl'])) { + throw new OpenIDConnectClientException('The provider URL has not been set'); + } + + return $this->providerConfig['providerUrl']; + } + + /** + * @param string $url + */ + public function redirect($url) { + header('Location: ' . $url); + exit; + } + + /** + * @param string $httpProxy + */ + public function setHttpProxy($httpProxy) { + $this->httpProxy = $httpProxy; + } + + /** + * @param string $certPath + */ + public function setCertPath($certPath) { + $this->certPath = $certPath; + } + + /** + * @return string|null + */ + public function getCertPath() + { + return $this->certPath; + } + + /** + * @param bool $verifyPeer + */ + public function setVerifyPeer($verifyPeer) { + $this->verifyPeer = $verifyPeer; + } + + /** + * @param bool $verifyHost + */ + public function setVerifyHost($verifyHost) { + $this->verifyHost = $verifyHost; + } + + /** + * @return bool + */ + public function getVerifyHost() + { + return $this->verifyHost; + } + + /** + * @return bool + */ + public function getVerifyPeer() + { + return $this->verifyPeer; + } + + /** + * Use this for custom issuer validation + * The given function should accept the issuer string from the JWT claim as the only argument + * and return true if the issuer is valid, otherwise return false + * + * @param callable $issuerValidator + */ + public function setIssuerValidator($issuerValidator){ + $this->issuerValidator = $issuerValidator; + } + + /** + * @param bool $allowImplicitFlow + */ + public function setAllowImplicitFlow($allowImplicitFlow) { + $this->allowImplicitFlow = $allowImplicitFlow; + } + + /** + * @return bool + */ + public function getAllowImplicitFlow() + { + return $this->allowImplicitFlow; + } + + /** + * + * Use this to alter a provider's endpoints and other attributes + * + * @param array $array + * simple key => value + */ + public function providerConfigParam($array) { + $this->providerConfig = array_merge($this->providerConfig, $array); + } + + /** + * @param string $clientSecret + */ + public function setClientSecret($clientSecret) { + $this->clientSecret = $clientSecret; + } + + /** + * @param string $clientID + */ + public function setClientID($clientID) { + $this->clientID = $clientID; + } + + + /** + * Dynamic registration + * + * @throws OpenIDConnectClientException + */ + public function register() { + + $registration_endpoint = $this->getProviderConfigValue('registration_endpoint'); + + $send_object = (object ) array_merge($this->registrationParams, array( + 'redirect_uris' => array($this->getRedirectURL()), + 'client_name' => $this->getClientName() + )); + + $response = $this->fetchURL($registration_endpoint, json_encode($send_object)); + + $json_response = json_decode($response); + + // Throw some errors if we encounter them + if ($json_response === false) { + throw new OpenIDConnectClientException('Error registering: JSON response received from the server was invalid.'); + } + + if (isset($json_response->{'error_description'})) { + throw new OpenIDConnectClientException($json_response->{'error_description'}); + } + + $this->setClientID($json_response->{'client_id'}); + + // The OpenID Connect Dynamic registration protocol makes the client secret optional + // and provides a registration access token and URI endpoint if it is not present + if (isset($json_response->{'client_secret'})) { + $this->setClientSecret($json_response->{'client_secret'}); + } else { + throw new OpenIDConnectClientException('Error registering: + Please contact the OpenID Connect provider and obtain a Client ID and Secret directly from them'); + } + + } + + /** + * Introspect a given token - either access token or refresh token. + * @see https://tools.ietf.org/html/rfc7662 + * + * @param string $token + * @param string $token_type_hint + * @param string|null $clientId + * @param string|null $clientSecret + * @return mixed + * @throws OpenIDConnectClientException + */ + public function introspectToken($token, $token_type_hint = '', $clientId = null, $clientSecret = null) { + $introspection_endpoint = $this->getProviderConfigValue('introspection_endpoint'); + + $post_data = array( + 'token' => $token, + ); + if ($token_type_hint) { + $post_data['token_type_hint'] = $token_type_hint; + } + $clientId = $clientId !== null ? $clientId : $this->clientID; + $clientSecret = $clientSecret !== null ? $clientSecret : $this->clientSecret; + + // Convert token params to string format + $post_params = http_build_query($post_data, null, '&'); + $headers = ['Authorization: Basic ' . base64_encode(urlencode($clientId) . ':' . urlencode($clientSecret)), + 'Accept: application/json']; + + return json_decode($this->fetchURL($introspection_endpoint, $post_params, $headers)); + } + + /** + * Revoke a given token - either access token or refresh token. + * @see https://tools.ietf.org/html/rfc7009 + * + * @param string $token + * @param string $token_type_hint + * @param string|null $clientId + * @param string|null $clientSecret + * @return mixed + * @throws OpenIDConnectClientException + */ + public function revokeToken($token, $token_type_hint = '', $clientId = null, $clientSecret = null) { + $revocation_endpoint = $this->getProviderConfigValue('revocation_endpoint'); + + $post_data = array( + 'token' => $token, + ); + if ($token_type_hint) { + $post_data['token_type_hint'] = $token_type_hint; + } + $clientId = $clientId !== null ? $clientId : $this->clientID; + $clientSecret = $clientSecret !== null ? $clientSecret : $this->clientSecret; + + // Convert token params to string format + $post_params = http_build_query($post_data, null, '&'); + $headers = ['Authorization: Basic ' . base64_encode(urlencode($clientId) . ':' . urlencode($clientSecret)), + 'Accept: application/json']; + + return json_decode($this->fetchURL($revocation_endpoint, $post_params, $headers)); + } + + /** + * @return string + */ + public function getClientName() { + return $this->clientName; + } + + /** + * @param string $clientName + */ + public function setClientName($clientName) { + $this->clientName = $clientName; + } + + /** + * @return string + */ + public function getClientID() { + return $this->clientID; + } + + /** + * @return string + */ + public function getClientSecret() { + return $this->clientSecret; + } + + /** + * @return bool + */ + public function canVerifySignatures() { + return class_exists('\phpseclib\Crypt\RSA') || class_exists('Crypt_RSA'); + } + + /** + * Set the access token. + * + * May be required for subclasses of this Client. + * + * @param string $accessToken + * @return void + */ + public function setAccessToken($accessToken) { + $this->accessToken = $accessToken; + } + + /** + * @return string + */ + public function getAccessToken() { + return $this->accessToken; + } + + /** + * @return string + */ + public function getRefreshToken() { + return $this->refreshToken; + } + + /** + * @return string + */ + public function getIdToken() { + return $this->idToken; + } + + /** + * @return object + */ + public function getAccessTokenHeader() { + return $this->decodeJWT($this->accessToken); + } + + /** + * @return object + */ + public function getAccessTokenPayload() { + return $this->decodeJWT($this->accessToken, 1); + } + + /** + * @return object + */ + public function getIdTokenHeader() { + return $this->decodeJWT($this->idToken); + } + + /** + * @return object + */ + public function getIdTokenPayload() { + return $this->decodeJWT($this->idToken, 1); + } + + /** + * @return string + */ + public function getTokenResponse() { + return $this->tokenResponse; + } + + /** + * Stores nonce + * + * @param string $nonce + * @return string + */ + protected function setNonce($nonce) { + $this->setSessionKey('openid_connect_nonce', $nonce); + return $nonce; + } + + /** + * Get stored nonce + * + * @return string + */ + protected function getNonce() { + return $this->getSessionKey('openid_connect_nonce'); + } + + /** + * Cleanup nonce + * + * @return void + */ + protected function unsetNonce() { + $this->unsetSessionKey('openid_connect_nonce'); + } + + /** + * Stores $state + * + * @param string $state + * @return string + */ + protected function setState($state) { + $this->setSessionKey('openid_connect_state', $state); + return $state; + } + + /** + * Get stored state + * + * @return string + */ + protected function getState() { + return $this->getSessionKey('openid_connect_state'); + } + + /** + * Cleanup state + * + * @return void + */ + protected function unsetState() { + $this->unsetSessionKey('openid_connect_state'); + } + + /** + * Get the response code from last action/curl request. + * + * @return int + */ + public function getResponseCode() + { + return $this->responseCode; + } + + /** + * Set timeout (seconds) + * + * @param int $timeout + */ + public function setTimeout($timeout) + { + $this->timeOut = $timeout; + } + + /** + * @return int + */ + public function getTimeout() + { + return $this->timeOut; + } + + /** + * Safely calculate length of binary string + * @param string $str + * @return int + */ + private static function safeLength($str) + { + if (function_exists('mb_strlen')) { + return mb_strlen($str, '8bit'); + } + return strlen($str); + } + + /** + * Where has_equals is not available, this provides a timing-attack safe string comparison + * @param string $str1 + * @param string $str2 + * @return bool + */ + private static function hashEquals($str1, $str2) + { + $len1=static::safeLength($str1); + $len2=static::safeLength($str2); + + //compare strings without any early abort... + $len = min($len1, $len2); + $status = 0; + for ($i = 0; $i < $len; $i++) { + $status |= (ord($str1[$i]) ^ ord($str2[$i])); + } + //if strings were different lengths, we fail + $status |= ($len1 ^ $len2); + return ($status === 0); + } + + /** + * Use session to manage a nonce + */ + protected function startSession() { + if (!isset($_SESSION)) { + @session_start(); + } + } + + protected function commitSession() { + $this->startSession(); + + session_write_close(); + } + + protected function getSessionKey($key) { + $this->startSession(); + + return $_SESSION[$key]; + } + + protected function setSessionKey($key, $value) { + $this->startSession(); + + $_SESSION[$key] = $value; + } + + protected function unsetSessionKey($key) { + $this->startSession(); + + unset($_SESSION[$key]); + } + + public function setUrlEncoding($curEncoding) + { + switch ($curEncoding) + { + case PHP_QUERY_RFC1738: + $this->enc_type = PHP_QUERY_RFC1738; + break; + + case PHP_QUERY_RFC3986: + $this->enc_type = PHP_QUERY_RFC3986; + break; + + default: + break; + } + + } + + /** + * @return array + */ + public function getScopes() + { + return $this->scopes; + } + + /** + * @return array + */ + public function getResponseTypes() + { + return $this->responseTypes; + } + + /** + * @return array + */ + public function getAuthParams() + { + return $this->authParams; + } + + /** + * @return callable + */ + public function getIssuerValidator() + { + return $this->issuerValidator; + } + + /** + * @return int + */ + public function getLeeway() + { + return $this->leeway; + } +} diff --git a/modules/phpseclib/AUTHORS b/modules/phpseclib/AUTHORS @@ -0,0 +1,6 @@ +phpseclib Lead Developer: TerraFrost (Jim Wigginton) + +phpseclib Developers: monnerat (Patrick Monnerat) + bantu (Andreas Fischer) + petrich (Hans-Jürgen Petrich) + GrahamCampbell (Graham Campbell) diff --git a/modules/phpseclib/BACKERS.md b/modules/phpseclib/BACKERS.md @@ -0,0 +1,8 @@ +# Backers + +phpseclib ongoing development is made possible by [Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme) and by contributions by users like you. Thank you. + +## Backers + +- Zane Hooper +- [Setasign](https://www.setasign.com/)+ \ No newline at end of file diff --git a/modules/phpseclib/Crypt/RSA.class.php b/modules/phpseclib/Crypt/RSA.class.php @@ -0,0 +1,3203 @@ +<?php + +/** + * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA. + * + * PHP version 5 + * + * Here's an example of how to encrypt and decrypt text with this library: + * <code> + * <?php + * include 'vendor/autoload.php'; + * + * $rsa = new \phpseclib\Crypt\RSA(); + * extract($rsa->createKey()); + * + * $plaintext = 'terrafrost'; + * + * $rsa->loadKey($privatekey); + * $ciphertext = $rsa->encrypt($plaintext); + * + * $rsa->loadKey($publickey); + * echo $rsa->decrypt($ciphertext); + * ?> + * </code> + * + * Here's an example of how to create signatures and verify signatures with this library: + * <code> + * <?php + * include 'vendor/autoload.php'; + * + * $rsa = new \phpseclib\Crypt\RSA(); + * extract($rsa->createKey()); + * + * $plaintext = 'terrafrost'; + * + * $rsa->loadKey($privatekey); + * $signature = $rsa->sign($plaintext); + * + * $rsa->loadKey($publickey); + * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * ?> + * </code> + * + * @category Crypt + * @package RSA + * @author Jim Wigginton <terrafrost@php.net> + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +use phpseclib\Math\BigInteger; + +/** + * Pure-PHP PKCS#1 compliant implementation of RSA. + * + * @package RSA + * @author Jim Wigginton <terrafrost@php.net> + * @access public + */ +class RSA +{ + /**#@+ + * @access public + * @see self::encrypt() + * @see self::decrypt() + */ + /** + * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} + * (OAEP) for encryption / decryption. + * + * Uses sha1 by default. + * + * @see self::setHash() + * @see self::setMGFHash() + */ + const ENCRYPTION_OAEP = 1; + /** + * Use PKCS#1 padding. + * + * Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards + * compatibility with protocols (like SSH-1) written before OAEP's introduction. + */ + const ENCRYPTION_PKCS1 = 2; + /** + * Do not use any padding + * + * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy + * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc. + */ + const ENCRYPTION_NONE = 3; + /**#@-*/ + + /**#@+ + * @access public + * @see self::sign() + * @see self::verify() + * @see self::setHash() + */ + /** + * Use the Probabilistic Signature Scheme for signing + * + * Uses sha1 by default. + * + * @see self::setSaltLength() + * @see self::setMGFHash() + */ + const SIGNATURE_PSS = 1; + /** + * Use the PKCS#1 scheme by default. + * + * Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards + * compatibility with protocols (like SSH-2) written before PSS's introduction. + */ + const SIGNATURE_PKCS1 = 2; + /**#@-*/ + + /**#@+ + * @access private + * @see \phpseclib\Crypt\RSA::createKey() + */ + /** + * ASN1 Integer + */ + const ASN1_INTEGER = 2; + /** + * ASN1 Bit String + */ + const ASN1_BITSTRING = 3; + /** + * ASN1 Octet String + */ + const ASN1_OCTETSTRING = 4; + /** + * ASN1 Object Identifier + */ + const ASN1_OBJECT = 6; + /** + * ASN1 Sequence (with the constucted bit set) + */ + const ASN1_SEQUENCE = 48; + /**#@-*/ + + /**#@+ + * @access private + * @see \phpseclib\Crypt\RSA::__construct() + */ + /** + * To use the pure-PHP implementation + */ + const MODE_INTERNAL = 1; + /** + * To use the OpenSSL library + * + * (if enabled; otherwise, the internal implementation will be used) + */ + const MODE_OPENSSL = 2; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Crypt\RSA::createKey() + * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat() + */ + /** + * PKCS#1 formatted private key + * + * Used by OpenSSH + */ + const PRIVATE_FORMAT_PKCS1 = 0; + /** + * PuTTY formatted private key + */ + const PRIVATE_FORMAT_PUTTY = 1; + /** + * XML formatted private key + */ + const PRIVATE_FORMAT_XML = 2; + /** + * PKCS#8 formatted private key + */ + const PRIVATE_FORMAT_PKCS8 = 8; + /** + * OpenSSH formatted private key + */ + const PRIVATE_FORMAT_OPENSSH = 9; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Crypt\RSA::createKey() + * @see \phpseclib\Crypt\RSA::setPublicKeyFormat() + */ + /** + * Raw public key + * + * An array containing two \phpseclib\Math\BigInteger objects. + * + * The exponent can be indexed with any of the following: + * + * 0, e, exponent, publicExponent + * + * The modulus can be indexed with any of the following: + * + * 1, n, modulo, modulus + */ + const PUBLIC_FORMAT_RAW = 3; + /** + * PKCS#1 formatted public key (raw) + * + * Used by File/X509.php + * + * Has the following header: + * + * -----BEGIN RSA PUBLIC KEY----- + * + * Analogous to ssh-keygen's pem format (as specified by -m) + */ + const PUBLIC_FORMAT_PKCS1 = 4; + const PUBLIC_FORMAT_PKCS1_RAW = 4; + /** + * XML formatted public key + */ + const PUBLIC_FORMAT_XML = 5; + /** + * OpenSSH formatted public key + * + * Place in $HOME/.ssh/authorized_keys + */ + const PUBLIC_FORMAT_OPENSSH = 6; + /** + * PKCS#1 formatted public key (encapsulated) + * + * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) + * + * Has the following header: + * + * -----BEGIN PUBLIC KEY----- + * + * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 + * is specific to private keys it's basically creating a DER-encoded wrapper + * for keys. This just extends that same concept to public keys (much like ssh-keygen) + */ + const PUBLIC_FORMAT_PKCS8 = 7; + /**#@-*/ + + /** + * Precomputed Zero + * + * @var \phpseclib\Math\BigInteger + * @access private + */ + var $zero; + + /** + * Precomputed One + * + * @var \phpseclib\Math\BigInteger + * @access private + */ + var $one; + + /** + * Private Key Format + * + * @var int + * @access private + */ + var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1; + + /** + * Public Key Format + * + * @var int + * @access public + */ + var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8; + + /** + * Modulus (ie. n) + * + * @var \phpseclib\Math\BigInteger + * @access private + */ + var $modulus; + + /** + * Modulus length + * + * @var \phpseclib\Math\BigInteger + * @access private + */ + var $k; + + /** + * Exponent (ie. e or d) + * + * @var \phpseclib\Math\BigInteger + * @access private + */ + var $exponent; + + /** + * Primes for Chinese Remainder Theorem (ie. p and q) + * + * @var array + * @access private + */ + var $primes; + + /** + * Exponents for Chinese Remainder Theorem (ie. dP and dQ) + * + * @var array + * @access private + */ + var $exponents; + + /** + * Coefficients for Chinese Remainder Theorem (ie. qInv) + * + * @var array + * @access private + */ + var $coefficients; + + /** + * Hash name + * + * @var string + * @access private + */ + var $hashName; + + /** + * Hash function + * + * @var \phpseclib\Crypt\Hash + * @access private + */ + var $hash; + + /** + * Length of hash function output + * + * @var int + * @access private + */ + var $hLen; + + /** + * Length of salt + * + * @var int + * @access private + */ + var $sLen; + + /** + * Hash function for the Mask Generation Function + * + * @var \phpseclib\Crypt\Hash + * @access private + */ + var $mgfHash; + + /** + * Length of MGF hash function output + * + * @var int + * @access private + */ + var $mgfHLen; + + /** + * Encryption mode + * + * @var int + * @access private + */ + var $encryptionMode = self::ENCRYPTION_OAEP; + + /** + * Signature mode + * + * @var int + * @access private + */ + var $signatureMode = self::SIGNATURE_PSS; + + /** + * Public Exponent + * + * @var mixed + * @access private + */ + var $publicExponent = false; + + /** + * Password + * + * @var string + * @access private + */ + var $password = false; + + /** + * Components + * + * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions - + * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't. + * + * @see self::_start_element_handler() + * @var array + * @access private + */ + var $components = array(); + + /** + * Current String + * + * For use with parsing XML formatted keys. + * + * @see self::_character_handler() + * @see self::_stop_element_handler() + * @var mixed + * @access private + */ + var $current; + + /** + * OpenSSL configuration file name. + * + * Set to null to use system configuration file. + * @see self::createKey() + * @var mixed + * @Access public + */ + var $configFile; + + /** + * Public key comment field. + * + * @var string + * @access private + */ + var $comment = 'phpseclib-generated-key'; + + /** + * The constructor + * + * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason + * \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires + * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late. + * + * @return \phpseclib\Crypt\RSA + * @access public + */ + function __construct() + { + $this->configFile = dirname(__FILE__) . '/../openssl.cnf'; + + if (!defined('CRYPT_RSA_MODE')) { + switch (true) { + // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular, + // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger + // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either. + case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'): + define('CRYPT_RSA_MODE', self::MODE_INTERNAL); + break; + case extension_loaded('openssl') && file_exists($this->configFile): + // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work + $versions = array(); + + // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems) + if (strpos(ini_get('disable_functions'), 'phpinfo') === false) { + ob_start(); + @phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + + // Remove letter part in OpenSSL version + if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { + $versions[$matches[1][$i]] = $fullVersion; + } else { + $versions[$matches[1][$i]] = $m[0]; + } + } + } + } + + // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ + switch (true) { + case !isset($versions['Header']): + case !isset($versions['Library']): + case $versions['Header'] == $versions['Library']: + case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0: + define('CRYPT_RSA_MODE', self::MODE_OPENSSL); + break; + default: + define('CRYPT_RSA_MODE', self::MODE_INTERNAL); + define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); + } + break; + default: + define('CRYPT_RSA_MODE', self::MODE_INTERNAL); + } + } + + $this->zero = new BigInteger(); + $this->one = new BigInteger(1); + + $this->hash = new Hash('sha1'); + $this->hLen = $this->hash->getLength(); + $this->hashName = 'sha1'; + $this->mgfHash = new Hash('sha1'); + $this->mgfHLen = $this->mgfHash->getLength(); + } + + /** + * Create public / private key pair + * + * Returns an array with the following three elements: + * - 'privatekey': The private key. + * - 'publickey': The public key. + * - 'partialkey': A partially computed key (if the execution time exceeded $timeout). + * Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing. + * + * @access public + * @param int $bits + * @param int $timeout + * @param array $p + */ + function createKey($bits = 1024, $timeout = false, $partial = array()) + { + if (!defined('CRYPT_RSA_EXPONENT')) { + // http://en.wikipedia.org/wiki/65537_%28number%29 + define('CRYPT_RSA_EXPONENT', '65537'); + } + // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller + // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME + // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if + // CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::MODE_OPENSSL then + // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key + // generation when there's a chance neither gmp nor OpenSSL are installed) + if (!defined('CRYPT_RSA_SMALLEST_PRIME')) { + define('CRYPT_RSA_SMALLEST_PRIME', 4096); + } + + // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum + if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) { + $config = array(); + if (isset($this->configFile)) { + $config['config'] = $this->configFile; + } + $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config); + openssl_pkey_export($rsa, $privatekey, null, $config); + $publickey = openssl_pkey_get_details($rsa); + $publickey = $publickey['key']; + + $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1))); + $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1))); + + // clear the buffer of error strings stemming from a minimalistic openssl.cnf + while (openssl_error_string() !== false) { + } + + return array( + 'privatekey' => $privatekey, + 'publickey' => $publickey, + 'partialkey' => false + ); + } + + static $e; + if (!isset($e)) { + $e = new BigInteger(CRYPT_RSA_EXPONENT); + } + + extract($this->_generateMinMax($bits)); + $absoluteMin = $min; + $temp = $bits >> 1; // divide by two to see how many bits P and Q would be + if ($temp > CRYPT_RSA_SMALLEST_PRIME) { + $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME); + $temp = CRYPT_RSA_SMALLEST_PRIME; + } else { + $num_primes = 2; + } + extract($this->_generateMinMax($temp + $bits % $temp)); + $finalMax = $max; + extract($this->_generateMinMax($temp)); + + $generator = new BigInteger(); + + $n = $this->one->copy(); + if (!empty($partial)) { + extract(unserialize($partial)); + } else { + $exponents = $coefficients = $primes = array(); + $lcm = array( + 'top' => $this->one->copy(), + 'bottom' => false + ); + } + + $start = time(); + $i0 = count($primes) + 1; + + do { + for ($i = $i0; $i <= $num_primes; $i++) { + if ($timeout !== false) { + $timeout-= time() - $start; + $start = time(); + if ($timeout <= 0) { + return array( + 'privatekey' => '', + 'publickey' => '', + 'partialkey' => serialize(array( + 'primes' => $primes, + 'coefficients' => $coefficients, + 'lcm' => $lcm, + 'exponents' => $exponents + )) + ); + } + } + + if ($i == $num_primes) { + list($min, $temp) = $absoluteMin->divide($n); + if (!$temp->equals($this->zero)) { + $min = $min->add($this->one); // ie. ceil() + } + $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout); + } else { + $primes[$i] = $generator->randomPrime($min, $max, $timeout); + } + + if ($primes[$i] === false) { // if we've reached the timeout + if (count($primes) > 1) { + $partialkey = ''; + } else { + array_pop($primes); + $partialkey = serialize(array( + 'primes' => $primes, + 'coefficients' => $coefficients, + 'lcm' => $lcm, + 'exponents' => $exponents + )); + } + + return array( + 'privatekey' => '', + 'publickey' => '', + 'partialkey' => $partialkey + ); + } + + // the first coefficient is calculated differently from the rest + // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1]) + if ($i > 2) { + $coefficients[$i] = $n->modInverse($primes[$i]); + } + + $n = $n->multiply($primes[$i]); + + $temp = $primes[$i]->subtract($this->one); + + // textbook RSA implementations use Euler's totient function instead of the least common multiple. + // see http://en.wikipedia.org/wiki/Euler%27s_totient_function + $lcm['top'] = $lcm['top']->multiply($temp); + $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp); + + $exponents[$i] = $e->modInverse($temp); + } + + list($temp) = $lcm['top']->divide($lcm['bottom']); + $gcd = $temp->gcd($e); + $i0 = 1; + } while (!$gcd->equals($this->one)); + + $d = $e->modInverse($temp); + + $coefficients[2] = $primes[2]->modInverse($primes[1]); + + // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>: + // RSAPrivateKey ::= SEQUENCE { + // version Version, + // modulus INTEGER, -- n + // publicExponent INTEGER, -- e + // privateExponent INTEGER, -- d + // prime1 INTEGER, -- p + // prime2 INTEGER, -- q + // exponent1 INTEGER, -- d mod (p-1) + // exponent2 INTEGER, -- d mod (q-1) + // coefficient INTEGER, -- (inverse of q) mod p + // otherPrimeInfos OtherPrimeInfos OPTIONAL + // } + + return array( + 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), + 'publickey' => $this->_convertPublicKey($n, $e), + 'partialkey' => false + ); + } + + /** + * Convert a private key to the appropriate format. + * + * @access private + * @see self::setPrivateKeyFormat() + * @param string $RSAPrivateKey + * @return string + */ + function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) + { + $signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML; + $num_primes = count($primes); + $raw = array( + 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi + 'modulus' => $n->toBytes($signed), + 'publicExponent' => $e->toBytes($signed), + 'privateExponent' => $d->toBytes($signed), + 'prime1' => $primes[1]->toBytes($signed), + 'prime2' => $primes[2]->toBytes($signed), + 'exponent1' => $exponents[1]->toBytes($signed), + 'exponent2' => $exponents[2]->toBytes($signed), + 'coefficient' => $coefficients[2]->toBytes($signed) + ); + + // if the format in question does not support multi-prime rsa and multi-prime rsa was used, + // call _convertPublicKey() instead. + switch ($this->privateKeyFormat) { + case self::PRIVATE_FORMAT_XML: + if ($num_primes != 2) { + return false; + } + return "<RSAKeyValue>\r\n" . + ' <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" . + ' <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" . + ' <P>' . base64_encode($raw['prime1']) . "</P>\r\n" . + ' <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" . + ' <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" . + ' <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" . + ' <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" . + ' <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" . + '</RSAKeyValue>'; + break; + case self::PRIVATE_FORMAT_PUTTY: + if ($num_primes != 2) { + return false; + } + $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; + $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none'; + $key.= $encryption; + $key.= "\r\nComment: " . $this->comment . "\r\n"; + $public = pack( + 'Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($raw['publicExponent']), + $raw['publicExponent'], + strlen($raw['modulus']), + $raw['modulus'] + ); + $source = pack( + 'Na*Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($encryption), + $encryption, + strlen($this->comment), + $this->comment, + strlen($public), + $public + ); + $public = base64_encode($public); + $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n"; + $key.= chunk_split($public, 64); + $private = pack( + 'Na*Na*Na*Na*', + strlen($raw['privateExponent']), + $raw['privateExponent'], + strlen($raw['prime1']), + $raw['prime1'], + strlen($raw['prime2']), + $raw['prime2'], + strlen($raw['coefficient']), + $raw['coefficient'] + ); + if (empty($this->password) && !is_string($this->password)) { + $source.= pack('Na*', strlen($private), $private); + $hashkey = 'putty-private-key-file-mac-key'; + } else { + $private.= Random::string(16 - (strlen($private) & 15)); + $source.= pack('Na*', strlen($private), $private); + $sequence = 0; + $symkey = ''; + while (strlen($symkey) < 32) { + $temp = pack('Na*', $sequence++, $this->password); + $symkey.= pack('H*', sha1($temp)); + } + $symkey = substr($symkey, 0, 32); + $crypto = new AES(); + + $crypto->setKey($symkey); + $crypto->disablePadding(); + $private = $crypto->encrypt($private); + $hashkey = 'putty-private-key-file-mac-key' . $this->password; + } + + $private = base64_encode($private); + $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n"; + $key.= chunk_split($private, 64); + $hash = new Hash('sha1'); + $hash->setKey(pack('H*', sha1($hashkey))); + $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n"; + + return $key; + case self::PRIVATE_FORMAT_OPENSSH: + if ($num_primes != 2) { + return false; + } + $publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']); + $privateKey = pack( + 'Na*Na*Na*Na*Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($raw['modulus']), + $raw['modulus'], + strlen($raw['publicExponent']), + $raw['publicExponent'], + strlen($raw['privateExponent']), + $raw['privateExponent'], + strlen($raw['coefficient']), + $raw['coefficient'], + strlen($raw['prime1']), + $raw['prime1'], + strlen($raw['prime2']), + $raw['prime2'] + ); + $checkint = Random::string(4); + $paddedKey = pack( + 'a*Na*', + $checkint . $checkint . $privateKey, + strlen($this->comment), + $this->comment + ); + $paddingLength = (7 * strlen($paddedKey)) % 8; + for ($i = 1; $i <= $paddingLength; $i++) { + $paddedKey.= chr($i); + } + $key = pack( + 'Na*Na*Na*NNa*Na*', + strlen('none'), + 'none', + strlen('none'), + 'none', + 0, + '', + 1, + strlen($publicKey), + $publicKey, + strlen($paddedKey), + $paddedKey + ); + $key = "openssh-key-v1\0$key"; + + return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($key), 70) . + "-----END OPENSSH PRIVATE KEY-----"; + default: // eg. self::PRIVATE_FORMAT_PKCS1 + $components = array(); + foreach ($raw as $name => $value) { + $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value); + } + + $RSAPrivateKey = implode('', $components); + + if ($num_primes > 2) { + $OtherPrimeInfos = ''; + for ($i = 3; $i <= $num_primes; $i++) { + // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + // + // OtherPrimeInfo ::= SEQUENCE { + // prime INTEGER, -- ri + // exponent INTEGER, -- di + // coefficient INTEGER -- ti + // } + $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); + $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); + } + $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); + } + + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) { + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPrivateKey = pack( + 'Ca*a*Ca*a*', + self::ASN1_INTEGER, + "\01\00", + $rsaOID, + 4, + $this->_encodeLength(strlen($RSAPrivateKey)), + $RSAPrivateKey + ); + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + if (!empty($this->password) || is_string($this->password)) { + $salt = Random::string(8); + $iterationCount = 2048; + + $crypto = new DES(); + $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); + $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey); + + $parameters = pack( + 'Ca*a*Ca*N', + self::ASN1_OCTETSTRING, + $this->_encodeLength(strlen($salt)), + $salt, + self::ASN1_INTEGER, + $this->_encodeLength(4), + $iterationCount + ); + $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03"; + + $encryptionAlgorithm = pack( + 'Ca*a*Ca*a*', + self::ASN1_OBJECT, + $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)), + $pbeWithMD5AndDES_CBC, + self::ASN1_SEQUENCE, + $this->_encodeLength(strlen($parameters)), + $parameters + ); + + $RSAPrivateKey = pack( + 'Ca*a*Ca*a*', + self::ASN1_SEQUENCE, + $this->_encodeLength(strlen($encryptionAlgorithm)), + $encryptionAlgorithm, + self::ASN1_OCTETSTRING, + $this->_encodeLength(strlen($RSAPrivateKey)), + $RSAPrivateKey + ); + + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END ENCRYPTED PRIVATE KEY-----'; + } else { + $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END PRIVATE KEY-----'; + } + return $RSAPrivateKey; + } + + if (!empty($this->password) || is_string($this->password)) { + $iv = Random::string(8); + $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key + $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); + $des = new TripleDES(); + $des->setKey($symkey); + $des->setIV($iv); + $iv = strtoupper(bin2hex($iv)); + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + "Proc-Type: 4,ENCRYPTED\r\n" . + "DEK-Info: DES-EDE3-CBC,$iv\r\n" . + "\r\n" . + chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) . + '-----END RSA PRIVATE KEY-----'; + } else { + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END RSA PRIVATE KEY-----'; + } + + return $RSAPrivateKey; + } + } + + /** + * Convert a public key to the appropriate format + * + * @access private + * @see self::setPublicKeyFormat() + * @param string $RSAPrivateKey + * @return string + */ + function _convertPublicKey($n, $e) + { + $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML; + + $modulus = $n->toBytes($signed); + $publicExponent = $e->toBytes($signed); + + switch ($this->publicKeyFormat) { + case self::PUBLIC_FORMAT_RAW: + return array('e' => $e->copy(), 'n' => $n->copy()); + case self::PUBLIC_FORMAT_XML: + return "<RSAKeyValue>\r\n" . + ' <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" . + ' <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" . + '</RSAKeyValue>'; + break; + case self::PUBLIC_FORMAT_OPENSSH: + // from <http://tools.ietf.org/html/rfc4253#page-15>: + // string "ssh-rsa" + // mpint e + // mpint n + $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); + $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment; + + return $RSAPublicKey; + default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or self::PUBLIC_FORMAT_PKCS1 + // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>: + // RSAPublicKey ::= SEQUENCE { + // modulus INTEGER, -- n + // publicExponent INTEGER -- e + // } + $components = array( + 'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus), + 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent) + ); + + $RSAPublicKey = pack( + 'Ca*a*a*', + self::ASN1_SEQUENCE, + $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], + $components['publicExponent'] + ); + + if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) { + $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($RSAPublicKey), 64) . + '-----END RSA PUBLIC KEY-----'; + } else { + // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPublicKey = chr(0) . $RSAPublicKey; + $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; + + $RSAPublicKey = pack( + 'Ca*a*', + self::ASN1_SEQUENCE, + $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), + $rsaOID . $RSAPublicKey + ); + + $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($RSAPublicKey), 64) . + '-----END PUBLIC KEY-----'; + } + + return $RSAPublicKey; + } + } + + /** + * Break a public or private key down into its constituant components + * + * @access private + * @see self::_convertPublicKey() + * @see self::_convertPrivateKey() + * @param string|array $key + * @param int $type + * @return array|bool + */ + function _parseKey($key, $type) + { + if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) { + return false; + } + + switch ($type) { + case self::PUBLIC_FORMAT_RAW: + if (!is_array($key)) { + return false; + } + $components = array(); + switch (true) { + case isset($key['e']): + $components['publicExponent'] = $key['e']->copy(); + break; + case isset($key['exponent']): + $components['publicExponent'] = $key['exponent']->copy(); + break; + case isset($key['publicExponent']): + $components['publicExponent'] = $key['publicExponent']->copy(); + break; + case isset($key[0]): + $components['publicExponent'] = $key[0]->copy(); + } + switch (true) { + case isset($key['n']): + $components['modulus'] = $key['n']->copy(); + break; + case isset($key['modulo']): + $components['modulus'] = $key['modulo']->copy(); + break; + case isset($key['modulus']): + $components['modulus'] = $key['modulus']->copy(); + break; + case isset($key[1]): + $components['modulus'] = $key[1]->copy(); + } + return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; + case self::PRIVATE_FORMAT_PKCS1: + case self::PRIVATE_FORMAT_PKCS8: + case self::PUBLIC_FORMAT_PKCS1: + /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is + "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to + protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding + two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: + + http://tools.ietf.org/html/rfc1421#section-4.6.1.1 + http://tools.ietf.org/html/rfc1421#section-4.6.1.3 + + DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. + DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation + function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's + own implementation. ie. the implementation *is* the standard and any bugs that may exist in that + implementation are part of the standard, as well. + + * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ + if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { + $iv = pack('H*', trim($matches[2])); + $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key + $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8))); + // remove the Proc-Type / DEK-Info sections as they're no longer needed + $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); + $ciphertext = $this->_extractBER($key); + if ($ciphertext === false) { + $ciphertext = $key; + } + switch ($matches[1]) { + case 'AES-256-CBC': + $crypto = new AES(); + break; + case 'AES-128-CBC': + $symkey = substr($symkey, 0, 16); + $crypto = new AES(); + break; + case 'DES-EDE3-CFB': + $crypto = new TripleDES(Base::MODE_CFB); + break; + case 'DES-EDE3-CBC': + $symkey = substr($symkey, 0, 24); + $crypto = new TripleDES(); + break; + case 'DES-CBC': + $crypto = new DES(); + break; + default: + return false; + } + $crypto->setKey($symkey); + $crypto->setIV($iv); + $decoded = $crypto->decrypt($ciphertext); + } else { + $decoded = $this->_extractBER($key); + } + + if ($decoded !== false) { + $key = $decoded; + } + + $components = array(); + + if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + if ($this->_decodeLength($key) != strlen($key)) { + return false; + } + + $tag = ord($this->_string_shift($key)); + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 631 cons: SEQUENCE + 4:d=1 hl=2 l= 1 prim: INTEGER :00 + 7:d=1 hl=2 l= 13 cons: SEQUENCE + 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 20:d=2 hl=2 l= 0 prim: NULL + 22:d=1 hl=4 l= 609 prim: OCTET STRING + + ie. PKCS8 keys*/ + + if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { + $this->_string_shift($key, 3); + $tag = self::ASN1_SEQUENCE; + } + + if ($tag == self::ASN1_SEQUENCE) { + $temp = $this->_string_shift($key, $this->_decodeLength($key)); + if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) { + return false; + } + $length = $this->_decodeLength($temp); + switch ($this->_string_shift($temp, $length)) { + case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption + break; + case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC + /* + PBEParameter ::= SEQUENCE { + salt OCTET STRING (SIZE(8)), + iterationCount INTEGER } + */ + if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) { + return false; + } + if ($this->_decodeLength($temp) != strlen($temp)) { + return false; + } + $this->_string_shift($temp); // assume it's an octet string + $salt = $this->_string_shift($temp, $this->_decodeLength($temp)); + if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) { + return false; + } + $this->_decodeLength($temp); + list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT)); + $this->_string_shift($key); // assume it's an octet string + $length = $this->_decodeLength($key); + if (strlen($key) != $length) { + return false; + } + + $crypto = new DES(); + $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); + $key = $crypto->decrypt($key); + if ($key === false) { + return false; + } + return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1); + default: + return false; + } + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING */ + $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag + $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length + // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of + // unused bits in the final subsequent octet. The number shall be in the range zero to seven." + // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2) + if ($tag == self::ASN1_BITSTRING) { + $this->_string_shift($key); + } + if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + if ($this->_decodeLength($key) != strlen($key)) { + return false; + } + $tag = ord($this->_string_shift($key)); + } + if ($tag != self::ASN1_INTEGER) { + return false; + } + + $length = $this->_decodeLength($key); + $temp = $this->_string_shift($key, $length); + if (strlen($temp) != 1 || ord($temp) > 2) { + $components['modulus'] = new BigInteger($temp, 256); + $this->_string_shift($key); // skip over self::ASN1_INTEGER + $length = $this->_decodeLength($key); + $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); + + return $components; + } + if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) { + return false; + } + $length = $this->_decodeLength($key); + $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256)); + + if (!empty($key)) { + if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + $this->_decodeLength($key); + while (!empty($key)) { + if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + $this->_decodeLength($key); + $key = substr($key, 1); + $length = $this->_decodeLength($key); + $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256); + } + } + + return $components; + case self::PUBLIC_FORMAT_OPENSSH: + $parts = explode(' ', $key, 3); + + $key = isset($parts[1]) ? base64_decode($parts[1]) : false; + if ($key === false) { + return false; + } + + $comment = isset($parts[2]) ? $parts[2] : false; + + $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; + + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $publicExponent = new BigInteger($this->_string_shift($key, $length), -256); + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $modulus = new BigInteger($this->_string_shift($key, $length), -256); + + if ($cleanup && strlen($key)) { + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $realModulus = new BigInteger($this->_string_shift($key, $length), -256); + return strlen($key) ? false : array( + 'modulus' => $realModulus, + 'publicExponent' => $modulus, + 'comment' => $comment + ); + } else { + return strlen($key) ? false : array( + 'modulus' => $modulus, + 'publicExponent' => $publicExponent, + 'comment' => $comment + ); + } + // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue + // http://en.wikipedia.org/wiki/XML_Signature + case self::PRIVATE_FORMAT_XML: + case self::PUBLIC_FORMAT_XML: + $this->components = array(); + + $xml = xml_parser_create('UTF-8'); + xml_set_object($xml, $this); + xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); + xml_set_character_data_handler($xml, '_data_handler'); + // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added + if (!xml_parse($xml, '<xml>' . $key . '</xml>')) { + xml_parser_free($xml); + unset($xml); + return false; + } + + xml_parser_free($xml); + unset($xml); + + return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; + // from PuTTY's SSHPUBK.C + case self::PRIVATE_FORMAT_PUTTY: + $components = array(); + $key = preg_split('#\r\n|\r|\n#', $key); + $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); + if ($type != 'ssh-rsa') { + return false; + } + $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); + $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); + + $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); + $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); + $public = substr($public, 11); + extract(unpack('Nlength', $this->_string_shift($public, 4))); + $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256); + extract(unpack('Nlength', $this->_string_shift($public, 4))); + $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256); + + $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4])); + $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength)))); + + switch ($encryption) { + case 'aes256-cbc': + $symkey = ''; + $sequence = 0; + while (strlen($symkey) < 32) { + $temp = pack('Na*', $sequence++, $this->password); + $symkey.= pack('H*', sha1($temp)); + } + $symkey = substr($symkey, 0, 32); + $crypto = new AES(); + } + + if ($encryption != 'none') { + $crypto->setKey($symkey); + $crypto->disablePadding(); + $private = $crypto->decrypt($private); + if ($private === false) { + return false; + } + } + + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256); + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256)); + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256); + + $temp = $components['primes'][1]->subtract($this->one); + $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); + $temp = $components['primes'][2]->subtract($this->one); + $components['exponents'][] = $components['publicExponent']->modInverse($temp); + + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256)); + + return $components; + case self::PRIVATE_FORMAT_OPENSSH: + $components = array(); + $decoded = $this->_extractBER($key); + $magic = $this->_string_shift($decoded, 15); + if ($magic !== "openssh-key-v1\0") { + return false; + } + $options = $this->_string_shift($decoded, 24); + // \0\0\0\4none = ciphername + // \0\0\0\4none = kdfname + // \0\0\0\0 = kdfoptions + // \0\0\0\1 = numkeys + if ($options != "\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") { + return false; + } + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + if (strlen($decoded) < $length) { + return false; + } + $publicKey = $this->_string_shift($decoded, $length); + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + if (strlen($decoded) < $length) { + return false; + } + $paddedKey = $this->_string_shift($decoded, $length); + + if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") { + return false; + } + + $checkint1 = $this->_string_shift($paddedKey, 4); + $checkint2 = $this->_string_shift($paddedKey, 4); + if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) { + return false; + } + + if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") { + return false; + } + + $values = array( + &$components['modulus'], + &$components['publicExponent'], + &$components['privateExponent'], + &$components['coefficients'][2], + &$components['primes'][1], + &$components['primes'][2] + ); + + foreach ($values as &$value) { + extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); + if (strlen($paddedKey) < $length) { + return false; + } + $value = new BigInteger($this->_string_shift($paddedKey, $length), -256); + } + + extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); + if (strlen($paddedKey) < $length) { + return false; + } + $components['comment'] = $this->_string_shift($decoded, $length); + + $temp = $components['primes'][1]->subtract($this->one); + $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); + $temp = $components['primes'][2]->subtract($this->one); + $components['exponents'][] = $components['publicExponent']->modInverse($temp); + + return $components; + } + + return false; + } + + /** + * Returns the key size + * + * More specifically, this returns the size of the modulo in bits. + * + * @access public + * @return int + */ + function getSize() + { + return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); + } + + /** + * Start Element Handler + * + * Called by xml_set_element_handler() + * + * @access private + * @param resource $parser + * @param string $name + * @param array $attribs + */ + function _start_element_handler($parser, $name, $attribs) + { + //$name = strtoupper($name); + switch ($name) { + case 'MODULUS': + $this->current = &$this->components['modulus']; + break; + case 'EXPONENT': + $this->current = &$this->components['publicExponent']; + break; + case 'P': + $this->current = &$this->components['primes'][1]; + break; + case 'Q': + $this->current = &$this->components['primes'][2]; + break; + case 'DP': + $this->current = &$this->components['exponents'][1]; + break; + case 'DQ': + $this->current = &$this->components['exponents'][2]; + break; + case 'INVERSEQ': + $this->current = &$this->components['coefficients'][2]; + break; + case 'D': + $this->current = &$this->components['privateExponent']; + } + $this->current = ''; + } + + /** + * Stop Element Handler + * + * Called by xml_set_element_handler() + * + * @access private + * @param resource $parser + * @param string $name + */ + function _stop_element_handler($parser, $name) + { + if (isset($this->current)) { + $this->current = new BigInteger(base64_decode($this->current), 256); + unset($this->current); + } + } + + /** + * Data Handler + * + * Called by xml_set_character_data_handler() + * + * @access private + * @param resource $parser + * @param string $data + */ + function _data_handler($parser, $data) + { + if (!isset($this->current) || is_object($this->current)) { + return; + } + $this->current.= trim($data); + } + + /** + * Loads a public or private key + * + * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) + * + * @access public + * @param string|RSA|array $key + * @param bool|int $type optional + * @return bool + */ + function loadKey($key, $type = false) + { + if ($key instanceof RSA) { + $this->privateKeyFormat = $key->privateKeyFormat; + $this->publicKeyFormat = $key->publicKeyFormat; + $this->k = $key->k; + $this->hLen = $key->hLen; + $this->sLen = $key->sLen; + $this->mgfHLen = $key->mgfHLen; + $this->encryptionMode = $key->encryptionMode; + $this->signatureMode = $key->signatureMode; + $this->password = $key->password; + $this->configFile = $key->configFile; + $this->comment = $key->comment; + + if (is_object($key->hash)) { + $this->hash = new Hash($key->hash->getHash()); + } + if (is_object($key->mgfHash)) { + $this->mgfHash = new Hash($key->mgfHash->getHash()); + } + + if (is_object($key->modulus)) { + $this->modulus = $key->modulus->copy(); + } + if (is_object($key->exponent)) { + $this->exponent = $key->exponent->copy(); + } + if (is_object($key->publicExponent)) { + $this->publicExponent = $key->publicExponent->copy(); + } + + $this->primes = array(); + $this->exponents = array(); + $this->coefficients = array(); + + foreach ($this->primes as $prime) { + $this->primes[] = $prime->copy(); + } + foreach ($this->exponents as $exponent) { + $this->exponents[] = $exponent->copy(); + } + foreach ($this->coefficients as $coefficient) { + $this->coefficients[] = $coefficient->copy(); + } + + return true; + } + + if ($type === false) { + $types = array( + self::PUBLIC_FORMAT_RAW, + self::PRIVATE_FORMAT_PKCS1, + self::PRIVATE_FORMAT_XML, + self::PRIVATE_FORMAT_PUTTY, + self::PUBLIC_FORMAT_OPENSSH, + self::PRIVATE_FORMAT_OPENSSH + ); + foreach ($types as $type) { + $components = $this->_parseKey($key, $type); + if ($components !== false) { + break; + } + } + } else { + $components = $this->_parseKey($key, $type); + } + + if ($components === false) { + $this->comment = null; + $this->modulus = null; + $this->k = null; + $this->exponent = null; + $this->primes = null; + $this->exponents = null; + $this->coefficients = null; + $this->publicExponent = null; + + return false; + } + + if (isset($components['comment']) && $components['comment'] !== false) { + $this->comment = $components['comment']; + } + $this->modulus = $components['modulus']; + $this->k = strlen($this->modulus->toBytes()); + $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; + if (isset($components['primes'])) { + $this->primes = $components['primes']; + $this->exponents = $components['exponents']; + $this->coefficients = $components['coefficients']; + $this->publicExponent = $components['publicExponent']; + } else { + $this->primes = array(); + $this->exponents = array(); + $this->coefficients = array(); + $this->publicExponent = false; + } + + switch ($type) { + case self::PUBLIC_FORMAT_OPENSSH: + case self::PUBLIC_FORMAT_RAW: + $this->setPublicKey(); + break; + case self::PRIVATE_FORMAT_PKCS1: + switch (true) { + case strpos($key, '-BEGIN PUBLIC KEY-') !== false: + case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false: + $this->setPublicKey(); + } + } + + return true; + } + + /** + * Sets the password + * + * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false. + * Or rather, pass in $password such that empty($password) && !is_string($password) is true. + * + * @see self::createKey() + * @see self::loadKey() + * @access public + * @param string $password + */ + function setPassword($password = false) + { + $this->password = $password; + } + + /** + * Defines the public key + * + * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when + * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a + * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys + * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public + * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used + * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being + * public. + * + * Do note that when a new key is loaded the index will be cleared. + * + * Returns true on success, false on failure + * + * @see self::getPublicKey() + * @access public + * @param string $key optional + * @param int $type optional + * @return bool + */ + function setPublicKey($key = false, $type = false) + { + // if a public key has already been loaded return false + if (!empty($this->publicExponent)) { + return false; + } + + if ($key === false && !empty($this->modulus)) { + $this->publicExponent = $this->exponent; + return true; + } + + if ($type === false) { + $types = array( + self::PUBLIC_FORMAT_RAW, + self::PUBLIC_FORMAT_PKCS1, + self::PUBLIC_FORMAT_XML, + self::PUBLIC_FORMAT_OPENSSH + ); + foreach ($types as $type) { + $components = $this->_parseKey($key, $type); + if ($components !== false) { + break; + } + } + } else { + $components = $this->_parseKey($key, $type); + } + + if ($components === false) { + return false; + } + + if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) { + $this->modulus = $components['modulus']; + $this->exponent = $this->publicExponent = $components['publicExponent']; + return true; + } + + $this->publicExponent = $components['publicExponent']; + + return true; + } + + /** + * Defines the private key + * + * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force + * phpseclib to treat the key as a private key. This function will do that. + * + * Do note that when a new key is loaded the index will be cleared. + * + * Returns true on success, false on failure + * + * @see self::getPublicKey() + * @access public + * @param string $key optional + * @param int $type optional + * @return bool + */ + function setPrivateKey($key = false, $type = false) + { + if ($key === false && !empty($this->publicExponent)) { + $this->publicExponent = false; + return true; + } + + $rsa = new RSA(); + if (!$rsa->loadKey($key, $type)) { + return false; + } + $rsa->publicExponent = false; + + // don't overwrite the old key if the new key is invalid + $this->loadKey($rsa); + return true; + } + + /** + * Returns the public key + * + * The public key is only returned under two circumstances - if the private key had the public key embedded within it + * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this + * function won't return it since this library, for the most part, doesn't distinguish between public and private keys. + * + * @see self::getPublicKey() + * @access public + * @param string $key + * @param int $type optional + */ + function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8) + { + if (empty($this->modulus) || empty($this->publicExponent)) { + return false; + } + + $oldFormat = $this->publicKeyFormat; + $this->publicKeyFormat = $type; + $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent); + $this->publicKeyFormat = $oldFormat; + return $temp; + } + + /** + * Returns the public key's fingerprint + * + * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is + * no public key currently loaded, false is returned. + * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716) + * + * @access public + * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned + * for invalid values. + * @return mixed + */ + function getPublicKeyFingerprint($algorithm = 'md5') + { + if (empty($this->modulus) || empty($this->publicExponent)) { + return false; + } + + $modulus = $this->modulus->toBytes(true); + $publicExponent = $this->publicExponent->toBytes(true); + + $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); + + switch ($algorithm) { + case 'sha256': + $hash = new Hash('sha256'); + $base = base64_encode($hash->hash($RSAPublicKey)); + return substr($base, 0, strlen($base) - 1); + case 'md5': + return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1); + default: + return false; + } + } + + /** + * Returns the private key + * + * The private key is only returned if the currently loaded key contains the constituent prime numbers. + * + * @see self::getPublicKey() + * @access public + * @param string $key + * @param int $type optional + * @return mixed + */ + function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1) + { + if (empty($this->primes)) { + return false; + } + + $oldFormat = $this->privateKeyFormat; + $this->privateKeyFormat = $type; + $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients); + $this->privateKeyFormat = $oldFormat; + return $temp; + } + + /** + * Returns a minimalistic private key + * + * Returns the private key without the prime number constituants. Structurally identical to a public key that + * hasn't been set as the public key + * + * @see self::getPrivateKey() + * @access private + * @param string $key + * @param int $type optional + */ + function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8) + { + if (empty($this->modulus) || empty($this->exponent)) { + return false; + } + + $oldFormat = $this->publicKeyFormat; + $this->publicKeyFormat = $mode; + $temp = $this->_convertPublicKey($this->modulus, $this->exponent); + $this->publicKeyFormat = $oldFormat; + return $temp; + } + + /** + * __toString() magic method + * + * @access public + * @return string + */ + function __toString() + { + $key = $this->getPrivateKey($this->privateKeyFormat); + if ($key !== false) { + return $key; + } + $key = $this->_getPrivatePublicKey($this->publicKeyFormat); + return $key !== false ? $key : ''; + } + + /** + * __clone() magic method + * + * @access public + * @return Crypt_RSA + */ + function __clone() + { + $key = new RSA(); + $key->loadKey($this); + return $key; + } + + /** + * Generates the smallest and largest numbers requiring $bits bits + * + * @access private + * @param int $bits + * @return array + */ + function _generateMinMax($bits) + { + $bytes = $bits >> 3; + $min = str_repeat(chr(0), $bytes); + $max = str_repeat(chr(0xFF), $bytes); + $msb = $bits & 7; + if ($msb) { + $min = chr(1 << ($msb - 1)) . $min; + $max = chr((1 << $msb) - 1) . $max; + } else { + $min[0] = chr(0x80); + } + + return array( + 'min' => new BigInteger($min, 256), + 'max' => new BigInteger($max, 256) + ); + } + + /** + * DER-decode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @access private + * @param string $string + * @return int + */ + function _decodeLength(&$string) + { + $length = ord($this->_string_shift($string)); + if ($length & 0x80) { // definite length, long form + $length&= 0x7F; + $temp = $this->_string_shift($string, $length); + list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); + } + return $length; + } + + /** + * DER-encode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @access private + * @param int $length + * @return string + */ + function _encodeLength($length) + { + if ($length <= 0x7F) { + return chr($length); + } + + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * Determines the private key format + * + * @see self::createKey() + * @access public + * @param int $format + */ + function setPrivateKeyFormat($format) + { + $this->privateKeyFormat = $format; + } + + /** + * Determines the public key format + * + * @see self::createKey() + * @access public + * @param int $format + */ + function setPublicKeyFormat($format) + { + $this->publicKeyFormat = $format; + } + + /** + * Determines which hashing function should be used + * + * Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and + * decryption. If $hash isn't supported, sha1 is used. + * + * @access public + * @param string $hash + */ + function setHash($hash) + { + // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + switch ($hash) { + case 'md2': + case 'md5': + case 'sha1': + case 'sha256': + case 'sha384': + case 'sha512': + $this->hash = new Hash($hash); + $this->hashName = $hash; + break; + default: + $this->hash = new Hash('sha1'); + $this->hashName = 'sha1'; + } + $this->hLen = $this->hash->getLength(); + } + + /** + * Determines which hashing function should be used for the mask generation function + * + * The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's + * best if Hash and MGFHash are set to the same thing this is not a requirement. + * + * @access public + * @param string $hash + */ + function setMGFHash($hash) + { + // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + switch ($hash) { + case 'md2': + case 'md5': + case 'sha1': + case 'sha256': + case 'sha384': + case 'sha512': + $this->mgfHash = new Hash($hash); + break; + default: + $this->mgfHash = new Hash('sha1'); + } + $this->mgfHLen = $this->mgfHash->getLength(); + } + + /** + * Determines the salt length + * + * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}: + * + * Typical salt lengths in octets are hLen (the length of the output + * of the hash function Hash) and 0. + * + * @access public + * @param int $format + */ + function setSaltLength($sLen) + { + $this->sLen = $sLen; + } + + /** + * Integer-to-Octet-String primitive + * + * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}. + * + * @access private + * @param \phpseclib\Math\BigInteger $x + * @param int $xLen + * @return string + */ + function _i2osp($x, $xLen) + { + $x = $x->toBytes(); + if (strlen($x) > $xLen) { + user_error('Integer too large'); + return false; + } + return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); + } + + /** + * Octet-String-to-Integer primitive + * + * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. + * + * @access private + * @param string $x + * @return \phpseclib\Math\BigInteger + */ + function _os2ip($x) + { + return new BigInteger($x, 256); + } + + /** + * Exponentiate with or without Chinese Remainder Theorem + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}. + * + * @access private + * @param \phpseclib\Math\BigInteger $x + * @return \phpseclib\Math\BigInteger + */ + function _exponentiate($x) + { + switch (true) { + case empty($this->primes): + case $this->primes[1]->equals($this->zero): + case empty($this->coefficients): + case $this->coefficients[2]->equals($this->zero): + case empty($this->exponents): + case $this->exponents[1]->equals($this->zero): + return $x->modPow($this->exponent, $this->modulus); + } + + $num_primes = count($this->primes); + + if (defined('CRYPT_RSA_DISABLE_BLINDING')) { + $m_i = array( + 1 => $x->modPow($this->exponents[1], $this->primes[1]), + 2 => $x->modPow($this->exponents[2], $this->primes[2]) + ); + $h = $m_i[1]->subtract($m_i[2]); + $h = $h->multiply($this->coefficients[2]); + list(, $h) = $h->divide($this->primes[1]); + $m = $m_i[2]->add($h->multiply($this->primes[2])); + + $r = $this->primes[1]; + for ($i = 3; $i <= $num_primes; $i++) { + $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]); + + $r = $r->multiply($this->primes[$i - 1]); + + $h = $m_i->subtract($m); + $h = $h->multiply($this->coefficients[$i]); + list(, $h) = $h->divide($this->primes[$i]); + + $m = $m->add($r->multiply($h)); + } + } else { + $smallest = $this->primes[1]; + for ($i = 2; $i <= $num_primes; $i++) { + if ($smallest->compare($this->primes[$i]) > 0) { + $smallest = $this->primes[$i]; + } + } + + $one = new BigInteger(1); + + $r = $one->random($one, $smallest->subtract($one)); + + $m_i = array( + 1 => $this->_blind($x, $r, 1), + 2 => $this->_blind($x, $r, 2) + ); + $h = $m_i[1]->subtract($m_i[2]); + $h = $h->multiply($this->coefficients[2]); + list(, $h) = $h->divide($this->primes[1]); + $m = $m_i[2]->add($h->multiply($this->primes[2])); + + $r = $this->primes[1]; + for ($i = 3; $i <= $num_primes; $i++) { + $m_i = $this->_blind($x, $r, $i); + + $r = $r->multiply($this->primes[$i - 1]); + + $h = $m_i->subtract($m); + $h = $h->multiply($this->coefficients[$i]); + list(, $h) = $h->divide($this->primes[$i]); + + $m = $m->add($r->multiply($h)); + } + } + + return $m; + } + + /** + * Performs RSA Blinding + * + * Protects against timing attacks by employing RSA Blinding. + * Returns $x->modPow($this->exponents[$i], $this->primes[$i]) + * + * @access private + * @param \phpseclib\Math\BigInteger $x + * @param \phpseclib\Math\BigInteger $r + * @param int $i + * @return \phpseclib\Math\BigInteger + */ + function _blind($x, $r, $i) + { + $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i])); + $x = $x->modPow($this->exponents[$i], $this->primes[$i]); + + $r = $r->modInverse($this->primes[$i]); + $x = $x->multiply($r); + list(, $x) = $x->divide($this->primes[$i]); + + return $x; + } + + /** + * Performs blinded RSA equality testing + * + * Protects against a particular type of timing attack described. + * + * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)} + * + * Thanks for the heads up singpolyma! + * + * @access private + * @param string $x + * @param string $y + * @return bool + */ + function _equals($x, $y) + { + if (function_exists('hash_equals')) { + return hash_equals($x, $y); + } + + if (strlen($x) != strlen($y)) { + return false; + } + + $result = "\0"; + $x^= $y; + for ($i = 0; $i < strlen($x); $i++) { + $result|= $x[$i]; + } + + return $result === "\0"; + } + + /** + * RSAEP + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}. + * + * @access private + * @param \phpseclib\Math\BigInteger $m + * @return \phpseclib\Math\BigInteger + */ + function _rsaep($m) + { + if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { + user_error('Message representative out of range'); + return false; + } + return $this->_exponentiate($m); + } + + /** + * RSADP + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}. + * + * @access private + * @param \phpseclib\Math\BigInteger $c + * @return \phpseclib\Math\BigInteger + */ + function _rsadp($c) + { + if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) { + user_error('Ciphertext representative out of range'); + return false; + } + return $this->_exponentiate($c); + } + + /** + * RSASP1 + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}. + * + * @access private + * @param \phpseclib\Math\BigInteger $m + * @return \phpseclib\Math\BigInteger + */ + function _rsasp1($m) + { + if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { + user_error('Message representative out of range'); + return false; + } + return $this->_exponentiate($m); + } + + /** + * RSAVP1 + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}. + * + * @access private + * @param \phpseclib\Math\BigInteger $s + * @return \phpseclib\Math\BigInteger + */ + function _rsavp1($s) + { + if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { + user_error('Signature representative out of range'); + return false; + } + return $this->_exponentiate($s); + } + + /** + * MGF1 + * + * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}. + * + * @access private + * @param string $mgfSeed + * @param int $mgfLen + * @return string + */ + function _mgf1($mgfSeed, $maskLen) + { + // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output. + + $t = ''; + $count = ceil($maskLen / $this->mgfHLen); + for ($i = 0; $i < $count; $i++) { + $c = pack('N', $i); + $t.= $this->mgfHash->hash($mgfSeed . $c); + } + + return substr($t, 0, $maskLen); + } + + /** + * RSAES-OAEP-ENCRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and + * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}. + * + * @access private + * @param string $m + * @param string $l + * @return string + */ + function _rsaes_oaep_encrypt($m, $l = '') + { + $mLen = strlen($m); + + // Length checking + + // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + if ($mLen > $this->k - 2 * $this->hLen - 2) { + user_error('Message too long'); + return false; + } + + // EME-OAEP encoding + + $lHash = $this->hash->hash($l); + $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2); + $db = $lHash . $ps . chr(1) . $m; + $seed = Random::string($this->hLen); + $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); + $maskedDB = $db ^ $dbMask; + $seedMask = $this->_mgf1($maskedDB, $this->hLen); + $maskedSeed = $seed ^ $seedMask; + $em = chr(0) . $maskedSeed . $maskedDB; + + // RSA encryption + + $m = $this->_os2ip($em); + $c = $this->_rsaep($m); + $c = $this->_i2osp($c, $this->k); + + // Output the ciphertext C + + return $c; + } + + /** + * RSAES-OAEP-DECRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error + * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2: + * + * Note. Care must be taken to ensure that an opponent cannot + * distinguish the different error conditions in Step 3.g, whether by + * error message or timing, or, more generally, learn partial + * information about the encoded message EM. Otherwise an opponent may + * be able to obtain useful information about the decryption of the + * ciphertext C, leading to a chosen-ciphertext attack such as the one + * observed by Manger [36]. + * + * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}: + * + * Both the encryption and the decryption operations of RSAES-OAEP take + * the value of a label L as input. In this version of PKCS #1, L is + * the empty string; other uses of the label are outside the scope of + * this document. + * + * @access private + * @param string $c + * @param string $l + * @return string + */ + function _rsaes_oaep_decrypt($c, $l = '') + { + // Length checking + + // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { + user_error('Decryption error'); + return false; + } + + // RSA decryption + + $c = $this->_os2ip($c); + $m = $this->_rsadp($c); + if ($m === false) { + user_error('Decryption error'); + return false; + } + $em = $this->_i2osp($m, $this->k); + + // EME-OAEP decoding + + $lHash = $this->hash->hash($l); + $y = ord($em[0]); + $maskedSeed = substr($em, 1, $this->hLen); + $maskedDB = substr($em, $this->hLen + 1); + $seedMask = $this->_mgf1($maskedDB, $this->hLen); + $seed = $maskedSeed ^ $seedMask; + $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); + $db = $maskedDB ^ $dbMask; + $lHash2 = substr($db, 0, $this->hLen); + $m = substr($db, $this->hLen); + $hashesMatch = $this->_equals($lHash, $lHash2); + $leadingZeros = 1; + $patternMatch = 0; + $offset = 0; + for ($i = 0; $i < strlen($m); $i++) { + $patternMatch|= $leadingZeros & ($m[$i] === "\1"); + $leadingZeros&= $m[$i] === "\0"; + $offset+= $patternMatch ? 0 : 1; + } + + // we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation + // to protect against timing attacks + if (!$hashesMatch & !$patternMatch) { + user_error('Decryption error'); + return false; + } + + // Output the message M + + return substr($m, $offset + 1); + } + + /** + * Raw Encryption / Decryption + * + * Doesn't use padding and is not recommended. + * + * @access private + * @param string $m + * @return string + */ + function _raw_encrypt($m) + { + $temp = $this->_os2ip($m); + $temp = $this->_rsaep($temp); + return $this->_i2osp($temp, $this->k); + } + + /** + * RSAES-PKCS1-V1_5-ENCRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}. + * + * @access private + * @param string $m + * @return string + */ + function _rsaes_pkcs1_v1_5_encrypt($m) + { + $mLen = strlen($m); + + // Length checking + + if ($mLen > $this->k - 11) { + user_error('Message too long'); + return false; + } + + // EME-PKCS1-v1_5 encoding + + $psLen = $this->k - $mLen - 3; + $ps = ''; + while (strlen($ps) != $psLen) { + $temp = Random::string($psLen - strlen($ps)); + $temp = str_replace("\x00", '', $temp); + $ps.= $temp; + } + $type = 2; + // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done + if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { + $type = 1; + // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF" + $ps = str_repeat("\xFF", $psLen); + } + $em = chr(0) . chr($type) . $ps . chr(0) . $m; + + // RSA encryption + $m = $this->_os2ip($em); + $c = $this->_rsaep($m); + $c = $this->_i2osp($c, $this->k); + + // Output the ciphertext C + + return $c; + } + + /** + * RSAES-PKCS1-V1_5-DECRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. + * + * For compatibility purposes, this function departs slightly from the description given in RFC3447. + * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the + * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the + * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed + * to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the + * second byte is 2 or less. If it is, we'll accept the decrypted string as valid. + * + * As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt + * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but + * not private key encrypted ciphertext's. + * + * @access private + * @param string $c + * @return string + */ + function _rsaes_pkcs1_v1_5_decrypt($c) + { + // Length checking + + if (strlen($c) != $this->k) { // or if k < 11 + user_error('Decryption error'); + return false; + } + + // RSA decryption + + $c = $this->_os2ip($c); + $m = $this->_rsadp($c); + + if ($m === false) { + user_error('Decryption error'); + return false; + } + $em = $this->_i2osp($m, $this->k); + + // EME-PKCS1-v1_5 decoding + + if (ord($em[0]) != 0 || ord($em[1]) > 2) { + user_error('Decryption error'); + return false; + } + + $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); + $m = substr($em, strlen($ps) + 3); + + if (strlen($ps) < 8) { + user_error('Decryption error'); + return false; + } + + // Output M + + return $m; + } + + /** + * EMSA-PSS-ENCODE + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}. + * + * @access private + * @param string $m + * @param int $emBits + */ + function _emsa_pss_encode($m, $emBits) + { + // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8) + $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; + + $mHash = $this->hash->hash($m); + if ($emLen < $this->hLen + $sLen + 2) { + user_error('Encoding error'); + return false; + } + + $salt = Random::string($sLen); + $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; + $h = $this->hash->hash($m2); + $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2); + $db = $ps . chr(1) . $salt; + $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); + $maskedDB = $db ^ $dbMask; + $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0]; + $em = $maskedDB . $h . chr(0xBC); + + return $em; + } + + /** + * EMSA-PSS-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}. + * + * @access private + * @param string $m + * @param string $em + * @param int $emBits + * @return string + */ + function _emsa_pss_verify($m, $em, $emBits) + { + // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + $emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8); + $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; + + $mHash = $this->hash->hash($m); + if ($emLen < $this->hLen + $sLen + 2) { + return false; + } + + if ($em[strlen($em) - 1] != chr(0xBC)) { + return false; + } + + $maskedDB = substr($em, 0, -$this->hLen - 1); + $h = substr($em, -$this->hLen - 1, $this->hLen); + $temp = chr(0xFF << ($emBits & 7)); + if ((~$maskedDB[0] & $temp) != $temp) { + return false; + } + $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); + $db = $maskedDB ^ $dbMask; + $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0]; + $temp = $emLen - $this->hLen - $sLen - 2; + if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) { + return false; + } + $salt = substr($db, $temp + 1); // should be $sLen long + $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; + $h2 = $this->hash->hash($m2); + return $this->_equals($h, $h2); + } + + /** + * RSASSA-PSS-SIGN + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}. + * + * @access private + * @param string $m + * @return string + */ + function _rsassa_pss_sign($m) + { + // EMSA-PSS encoding + + $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1); + + // RSA signature + + $m = $this->_os2ip($em); + $s = $this->_rsasp1($m); + $s = $this->_i2osp($s, $this->k); + + // Output the signature S + + return $s; + } + + /** + * RSASSA-PSS-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}. + * + * @access private + * @param string $m + * @param string $s + * @return string + */ + function _rsassa_pss_verify($m, $s) + { + // Length checking + + if (strlen($s) != $this->k) { + user_error('Invalid signature'); + return false; + } + + // RSA verification + + $modBits = strlen($this->modulus->toBits()); + + $s2 = $this->_os2ip($s); + $m2 = $this->_rsavp1($s2); + if ($m2 === false) { + user_error('Invalid signature'); + return false; + } + $em = $this->_i2osp($m2, $this->k); + if ($em === false) { + user_error('Invalid signature'); + return false; + } + + // EMSA-PSS verification + + return $this->_emsa_pss_verify($m, $em, $modBits - 1); + } + + /** + * EMSA-PKCS1-V1_5-ENCODE + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}. + * + * @access private + * @param string $m + * @param int $emLen + * @return string + */ + function _emsa_pkcs1_v1_5_encode($m, $emLen) + { + $h = $this->hash->hash($m); + if ($h === false) { + return false; + } + + // see http://tools.ietf.org/html/rfc3447#page-43 + switch ($this->hashName) { + case 'md2': + $t = pack('H*', '3020300c06082a864886f70d020205000410'); + break; + case 'md5': + $t = pack('H*', '3020300c06082a864886f70d020505000410'); + break; + case 'sha1': + $t = pack('H*', '3021300906052b0e03021a05000414'); + break; + case 'sha256': + $t = pack('H*', '3031300d060960864801650304020105000420'); + break; + case 'sha384': + $t = pack('H*', '3041300d060960864801650304020205000430'); + break; + case 'sha512': + $t = pack('H*', '3051300d060960864801650304020305000440'); + } + $t.= $h; + $tLen = strlen($t); + + if ($emLen < $tLen + 11) { + user_error('Intended encoded message length too short'); + return false; + } + + $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); + + $em = "\0\1$ps\0$t"; + + return $em; + } + + /** + * RSASSA-PKCS1-V1_5-SIGN + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}. + * + * @access private + * @param string $m + * @return string + */ + function _rsassa_pkcs1_v1_5_sign($m) + { + // EMSA-PKCS1-v1_5 encoding + + $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + if ($em === false) { + user_error('RSA modulus too short'); + return false; + } + + // RSA signature + + $m = $this->_os2ip($em); + $s = $this->_rsasp1($m); + $s = $this->_i2osp($s, $this->k); + + // Output the signature S + + return $s; + } + + /** + * RSASSA-PKCS1-V1_5-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}. + * + * @access private + * @param string $m + * @return string + */ + function _rsassa_pkcs1_v1_5_verify($m, $s) + { + // Length checking + + if (strlen($s) != $this->k) { + user_error('Invalid signature'); + return false; + } + + // RSA verification + + $s = $this->_os2ip($s); + $m2 = $this->_rsavp1($s); + if ($m2 === false) { + user_error('Invalid signature'); + return false; + } + $em = $this->_i2osp($m2, $this->k); + if ($em === false) { + user_error('Invalid signature'); + return false; + } + + // EMSA-PKCS1-v1_5 encoding + + $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + if ($em2 === false) { + user_error('RSA modulus too short'); + return false; + } + + // Compare + return $this->_equals($em, $em2); + } + + /** + * Set Encryption Mode + * + * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1. + * + * @access public + * @param int $mode + */ + function setEncryptionMode($mode) + { + $this->encryptionMode = $mode; + } + + /** + * Set Signature Mode + * + * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1 + * + * @access public + * @param int $mode + */ + function setSignatureMode($mode) + { + $this->signatureMode = $mode; + } + + /** + * Set public key comment. + * + * @access public + * @param string $comment + */ + function setComment($comment) + { + $this->comment = $comment; + } + + /** + * Get public key comment. + * + * @access public + * @return string + */ + function getComment() + { + return $this->comment; + } + + /** + * Encryption + * + * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be. + * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will + * be concatenated together. + * + * @see self::decrypt() + * @access public + * @param string $plaintext + * @return string + */ + function encrypt($plaintext) + { + switch ($this->encryptionMode) { + case self::ENCRYPTION_NONE: + $plaintext = str_split($plaintext, $this->k); + $ciphertext = ''; + foreach ($plaintext as $m) { + $ciphertext.= $this->_raw_encrypt($m); + } + return $ciphertext; + case self::ENCRYPTION_PKCS1: + $length = $this->k - 11; + if ($length <= 0) { + return false; + } + + $plaintext = str_split($plaintext, $length); + $ciphertext = ''; + foreach ($plaintext as $m) { + $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m); + } + return $ciphertext; + //case self::ENCRYPTION_OAEP: + default: + $length = $this->k - 2 * $this->hLen - 2; + if ($length <= 0) { + return false; + } + + $plaintext = str_split($plaintext, $length); + $ciphertext = ''; + foreach ($plaintext as $m) { + $ciphertext.= $this->_rsaes_oaep_encrypt($m); + } + return $ciphertext; + } + } + + /** + * Decryption + * + * @see self::encrypt() + * @access public + * @param string $plaintext + * @return string + */ + function decrypt($ciphertext) + { + if ($this->k <= 0) { + return false; + } + + $ciphertext = str_split($ciphertext, $this->k); + $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT); + + $plaintext = ''; + + switch ($this->encryptionMode) { + case self::ENCRYPTION_NONE: + $decrypt = '_raw_encrypt'; + break; + case self::ENCRYPTION_PKCS1: + $decrypt = '_rsaes_pkcs1_v1_5_decrypt'; + break; + //case self::ENCRYPTION_OAEP: + default: + $decrypt = '_rsaes_oaep_decrypt'; + } + + foreach ($ciphertext as $c) { + $temp = $this->$decrypt($c); + if ($temp === false) { + return false; + } + $plaintext.= $temp; + } + + return $plaintext; + } + + /** + * Create a signature + * + * @see self::verify() + * @access public + * @param string $message + * @return string + */ + function sign($message) + { + if (empty($this->modulus) || empty($this->exponent)) { + return false; + } + + switch ($this->signatureMode) { + case self::SIGNATURE_PKCS1: + return $this->_rsassa_pkcs1_v1_5_sign($message); + //case self::SIGNATURE_PSS: + default: + return $this->_rsassa_pss_sign($message); + } + } + + /** + * Verifies a signature + * + * @see self::sign() + * @access public + * @param string $message + * @param string $signature + * @return bool + */ + function verify($message, $signature) + { + if (empty($this->modulus) || empty($this->exponent)) { + return false; + } + + switch ($this->signatureMode) { + case self::SIGNATURE_PKCS1: + return $this->_rsassa_pkcs1_v1_5_verify($message, $signature); + //case self::SIGNATURE_PSS: + default: + return $this->_rsassa_pss_verify($message, $signature); + } + } + + /** + * Extract raw BER from Base64 encoding + * + * @access private + * @param string $str + * @return string + */ + function _extractBER($str) + { + /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them + * above and beyond the ceritificate. + * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: + * + * Bag Attributes + * localKeyID: 01 00 00 00 + * subject=/O=organization/OU=org unit/CN=common name + * issuer=/O=organization/CN=common name + */ + $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#-+[^-]+-+#', '', $temp); + // remove new lines + $temp = str_replace(array("\r", "\n", ' '), '', $temp); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + return $temp != false ? $temp : $str; + } +} diff --git a/modules/phpseclib/LICENSE b/modules/phpseclib/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2011-2019 TerraFrost and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.+ \ No newline at end of file diff --git a/modules/phpseclib/Math/BigInteger.class.php b/modules/phpseclib/Math/BigInteger.class.php @@ -0,0 +1,3787 @@ +<?php + +/** + * Pure-PHP arbitrary precision integer arithmetic library. + * + * Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available, + * and an internal implementation, otherwise. + * + * PHP version 5 + * + * {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the + * {@link self::MODE_INTERNAL self::MODE_INTERNAL} mode) + * + * BigInteger uses base-2**26 to perform operations such as multiplication and division and + * base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible + * value when multiplying two base-2**26 numbers together is a base-2**52 number, double precision floating + * point numbers - numbers that should be supported on most hardware and whose significand is 53 bits - are + * used. As a consequence, bitwise operators such as >> and << cannot be used, nor can the modulo operator %, + * which only supports integers. Although this fact will slow this library down, the fact that such a high + * base is being used should more than compensate. + * + * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie. + * (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = array(0, 1) + * + * Useful resources are as follows: + * + * - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)} + * - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)} + * - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip + * + * Here's an example of how to use this library: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger(2); + * $b = new \phpseclib\Math\BigInteger(3); + * + * $c = $a->add($b); + * + * echo $c->toString(); // outputs 5 + * ?> + * </code> + * + * @category Math + * @package BigInteger + * @author Jim Wigginton <terrafrost@php.net> + * @copyright 2006 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +namespace phpseclib\Math; + +use phpseclib\Crypt\Random; + +/** + * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256 + * numbers. + * + * @package BigInteger + * @author Jim Wigginton <terrafrost@php.net> + * @access public + */ +class BigInteger +{ + /**#@+ + * Reduction constants + * + * @access private + * @see BigInteger::_reduce() + */ + /** + * @see BigInteger::_montgomery() + * @see BigInteger::_prepMontgomery() + */ + const MONTGOMERY = 0; + /** + * @see BigInteger::_barrett() + */ + const BARRETT = 1; + /** + * @see BigInteger::_mod2() + */ + const POWEROF2 = 2; + /** + * @see BigInteger::_remainder() + */ + const CLASSIC = 3; + /** + * @see BigInteger::__clone() + */ + const NONE = 4; + /**#@-*/ + + /**#@+ + * Array constants + * + * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and + * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. + * + * @access private + */ + /** + * $result[self::VALUE] contains the value. + */ + const VALUE = 0; + /** + * $result[self::SIGN] contains the sign. + */ + const SIGN = 1; + /**#@-*/ + + /**#@+ + * @access private + * @see BigInteger::_montgomery() + * @see BigInteger::_barrett() + */ + /** + * Cache constants + * + * $cache[self::VARIABLE] tells us whether or not the cached data is still valid. + */ + const VARIABLE = 0; + /** + * $cache[self::DATA] contains the cached data. + */ + const DATA = 1; + /**#@-*/ + + /**#@+ + * Mode constants. + * + * @access private + * @see BigInteger::__construct() + */ + /** + * To use the pure-PHP implementation + */ + const MODE_INTERNAL = 1; + /** + * To use the BCMath library + * + * (if enabled; otherwise, the internal implementation will be used) + */ + const MODE_BCMATH = 2; + /** + * To use the GMP library + * + * (if present; otherwise, either the BCMath or the internal implementation will be used) + */ + const MODE_GMP = 3; + /**#@-*/ + + /** + * Karatsuba Cutoff + * + * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? + * + * @access private + */ + const KARATSUBA_CUTOFF = 25; + + /**#@+ + * Static properties used by the pure-PHP implementation. + * + * @see __construct() + */ + protected static $base; + protected static $baseFull; + protected static $maxDigit; + protected static $msb; + + /** + * $max10 in greatest $max10Len satisfying + * $max10 = 10**$max10Len <= 2**$base. + */ + protected static $max10; + + /** + * $max10Len in greatest $max10Len satisfying + * $max10 = 10**$max10Len <= 2**$base. + */ + protected static $max10Len; + protected static $maxDigit2; + /**#@-*/ + + /** + * Holds the BigInteger's value. + * + * @var array + * @access private + */ + var $value; + + /** + * Holds the BigInteger's magnitude. + * + * @var bool + * @access private + */ + var $is_negative = false; + + /** + * Precision + * + * @see self::setPrecision() + * @access private + */ + var $precision = -1; + + /** + * Precision Bitmask + * + * @see self::setPrecision() + * @access private + */ + var $bitmask = false; + + /** + * Mode independent value used for serialization. + * + * If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for + * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value, + * however, $this->hex is only calculated when $this->__sleep() is called. + * + * @see self::__sleep() + * @see self::__wakeup() + * @var string + * @access private + */ + var $hex; + + /** + * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers. + * + * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using + * two's compliment. The sole exception to this is -10, which is treated the same as 10 is. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger('0x32', 16); // 50 in base-16 + * + * echo $a->toString(); // outputs 50 + * ?> + * </code> + * + * @param $x base-10 number or base-$base number if $base set. + * @param int $base + * @return \phpseclib\Math\BigInteger + * @access public + */ + function __construct($x = 0, $base = 10) + { + if (!defined('MATH_BIGINTEGER_MODE')) { + switch (true) { + case extension_loaded('gmp'): + define('MATH_BIGINTEGER_MODE', self::MODE_GMP); + break; + case extension_loaded('bcmath'): + define('MATH_BIGINTEGER_MODE', self::MODE_BCMATH); + break; + default: + define('MATH_BIGINTEGER_MODE', self::MODE_INTERNAL); + } + } + + if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work + $versions = array(); + + // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems) + if (strpos(ini_get('disable_functions'), 'phpinfo') === false) { + ob_start(); + @phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + + // Remove letter part in OpenSSL version + if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { + $versions[$matches[1][$i]] = $fullVersion; + } else { + $versions[$matches[1][$i]] = $m[0]; + } + } + } + } + + // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ + switch (true) { + case !isset($versions['Header']): + case !isset($versions['Library']): + case $versions['Header'] == $versions['Library']: + case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0: + define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); + break; + default: + define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); + } + } + + if (!defined('PHP_INT_SIZE')) { + define('PHP_INT_SIZE', 4); + } + + if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) { + switch (PHP_INT_SIZE) { + case 8: // use 64-bit integers if int size is 8 bytes + self::$base = 31; + self::$baseFull = 0x80000000; + self::$maxDigit = 0x7FFFFFFF; + self::$msb = 0x40000000; + self::$max10 = 1000000000; + self::$max10Len = 9; + self::$maxDigit2 = pow(2, 62); + break; + //case 4: // use 64-bit floats if int size is 4 bytes + default: + self::$base = 26; + self::$baseFull = 0x4000000; + self::$maxDigit = 0x3FFFFFF; + self::$msb = 0x2000000; + self::$max10 = 10000000; + self::$max10Len = 7; + self::$maxDigit2 = pow(2, 52); // pow() prevents truncation + } + } + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + switch (true) { + case is_resource($x) && get_resource_type($x) == 'GMP integer': + // PHP 5.6 switched GMP from using resources to objects + case $x instanceof \GMP: + $this->value = $x; + return; + } + $this->value = gmp_init(0); + break; + case self::MODE_BCMATH: + $this->value = '0'; + break; + default: + $this->value = array(); + } + + // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48 + // '0' is the only value like this per http://php.net/empty + if (empty($x) && (abs($base) != 256 || $x !== '0')) { + return; + } + + switch ($base) { + case -256: + if (ord($x[0]) & 0x80) { + $x = ~$x; + $this->is_negative = true; + } + case 256: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $this->value = function_exists('gmp_import') ? + gmp_import($x) : + gmp_init('0x' . bin2hex($x)); + if ($this->is_negative) { + $this->value = gmp_neg($this->value); + } + break; + case self::MODE_BCMATH: + // round $len to the nearest 4 (thanks, DavidMJ!) + $len = (strlen($x) + 3) & 0xFFFFFFFC; + + $x = str_pad($x, $len, chr(0), STR_PAD_LEFT); + + for ($i = 0; $i < $len; $i+= 4) { + $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32 + $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0); + } + + if ($this->is_negative) { + $this->value = '-' . $this->value; + } + + break; + // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb) + default: + while (strlen($x)) { + $this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base)); + } + } + + if ($this->is_negative) { + if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { + $this->is_negative = false; + } + $temp = $this->add(new static('-1')); + $this->value = $temp->value; + } + break; + case 16: + case -16: + if ($base > 0 && $x[0] == '-') { + $this->is_negative = true; + $x = substr($x, 1); + } + + $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x); + + $is_negative = false; + if ($base < 0 && hexdec($x[0]) >= 8) { + $this->is_negative = $is_negative = true; + $x = bin2hex(~pack('H*', $x)); + } + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = $this->is_negative ? '-0x' . $x : '0x' . $x; + $this->value = gmp_init($temp); + $this->is_negative = false; + break; + case self::MODE_BCMATH: + $x = (strlen($x) & 1) ? '0' . $x : $x; + $temp = new static(pack('H*', $x), 256); + $this->value = $this->is_negative ? '-' . $temp->value : $temp->value; + $this->is_negative = false; + break; + default: + $x = (strlen($x) & 1) ? '0' . $x : $x; + $temp = new static(pack('H*', $x), 256); + $this->value = $temp->value; + } + + if ($is_negative) { + $temp = $this->add(new static('-1')); + $this->value = $temp->value; + } + break; + case 10: + case -10: + // (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that + // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals) + // [^-0-9].*: find any non-numeric characters and then any characters that follow that + $x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x); + if (!strlen($x) || $x == '-') { + $x = '0'; + } + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $this->value = gmp_init($x); + break; + case self::MODE_BCMATH: + // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different + // results then doing it on '-1' does (modInverse does $x[0]) + $this->value = $x === '-' ? '0' : (string) $x; + break; + default: + $temp = new static(); + + $multiplier = new static(); + $multiplier->value = array(self::$max10); + + if ($x[0] == '-') { + $this->is_negative = true; + $x = substr($x, 1); + } + + $x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT); + while (strlen($x)) { + $temp = $temp->multiply($multiplier); + $temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256)); + $x = substr($x, self::$max10Len); + } + + $this->value = $temp->value; + } + break; + case 2: // base-2 support originally implemented by Lluis Pamies - thanks! + case -2: + if ($base > 0 && $x[0] == '-') { + $this->is_negative = true; + $x = substr($x, 1); + } + + $x = preg_replace('#^([01]*).*#', '$1', $x); + $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT); + + $str = '0x'; + while (strlen($x)) { + $part = substr($x, 0, 4); + $str.= dechex(bindec($part)); + $x = substr($x, 4); + } + + if ($this->is_negative) { + $str = '-' . $str; + } + + $temp = new static($str, 8 * $base); // ie. either -16 or +16 + $this->value = $temp->value; + $this->is_negative = $temp->is_negative; + + break; + default: + // base not supported, so we'll let $this == 0 + } + } + + /** + * Converts a BigInteger to a byte string (eg. base-256). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger('65'); + * + * echo $a->toBytes(); // outputs chr(65) + * ?> + * </code> + * + * @param bool $twos_compliment + * @return string + * @access public + * @internal Converts a base-2**26 number to base-2**8 + */ + function toBytes($twos_compliment = false) + { + if ($twos_compliment) { + $comparison = $this->compare(new static()); + if ($comparison == 0) { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + + $temp = $comparison < 0 ? $this->add(new static(1)) : $this->copy(); + $bytes = $temp->toBytes(); + + if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1 + $bytes = chr(0); + } + + if ($this->precision <= 0 && (ord($bytes[0]) & 0x80)) { + $bytes = chr(0) . $bytes; + } + + return $comparison < 0 ? ~$bytes : $bytes; + } + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + if (gmp_cmp($this->value, gmp_init(0)) == 0) { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + + if (function_exists('gmp_export')) { + $temp = gmp_export($this->value); + } else { + $temp = gmp_strval(gmp_abs($this->value), 16); + $temp = (strlen($temp) & 1) ? '0' . $temp : $temp; + $temp = pack('H*', $temp); + } + + return $this->precision > 0 ? + substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : + ltrim($temp, chr(0)); + case self::MODE_BCMATH: + if ($this->value === '0') { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + + $value = ''; + $current = $this->value; + + if ($current[0] == '-') { + $current = substr($current, 1); + } + + while (bccomp($current, '0', 0) > 0) { + $temp = bcmod($current, '16777216'); + $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value; + $current = bcdiv($current, '16777216', 0); + } + + return $this->precision > 0 ? + substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : + ltrim($value, chr(0)); + } + + if (!count($this->value)) { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + $result = $this->_int2bytes($this->value[count($this->value) - 1]); + + $temp = $this->copy(); + + for ($i = count($temp->value) - 2; $i >= 0; --$i) { + $temp->_base256_lshift($result, self::$base); + $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); + } + + return $this->precision > 0 ? + str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) : + $result; + } + + /** + * Converts a BigInteger to a hex string (eg. base-16)). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger('65'); + * + * echo $a->toHex(); // outputs '41' + * ?> + * </code> + * + * @param bool $twos_compliment + * @return string + * @access public + * @internal Converts a base-2**26 number to base-2**8 + */ + function toHex($twos_compliment = false) + { + return bin2hex($this->toBytes($twos_compliment)); + } + + /** + * Converts a BigInteger to a bit string (eg. base-2). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger('65'); + * + * echo $a->toBits(); // outputs '1000001' + * ?> + * </code> + * + * @param bool $twos_compliment + * @return string + * @access public + * @internal Converts a base-2**26 number to base-2**2 + */ + function toBits($twos_compliment = false) + { + $hex = $this->toHex($twos_compliment); + $bits = ''; + for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) { + $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits; + } + if ($start) { // hexdec('') == 0 + $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits; + } + $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); + + if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) { + return '0' . $result; + } + + return $result; + } + + /** + * Converts a BigInteger to a base-10 number. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger('50'); + * + * echo $a->toString(); // outputs 50 + * ?> + * </code> + * + * @return string + * @access public + * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10) + */ + function toString() + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + return gmp_strval($this->value); + case self::MODE_BCMATH: + if ($this->value === '0') { + return '0'; + } + + return ltrim($this->value, '0'); + } + + if (!count($this->value)) { + return '0'; + } + + $temp = $this->copy(); + $temp->bitmask = false; + $temp->is_negative = false; + + $divisor = new static(); + $divisor->value = array(self::$max10); + $result = ''; + while (count($temp->value)) { + list($temp, $mod) = $temp->divide($divisor); + $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT) . $result; + } + $result = ltrim($result, '0'); + if (empty($result)) { + $result = '0'; + } + + if ($this->is_negative) { + $result = '-' . $result; + } + + return $result; + } + + /** + * Copy an object + * + * PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee + * that all objects are passed by value, when appropriate. More information can be found here: + * + * {@link http://php.net/language.oop5.basic#51624} + * + * @access public + * @see self::__clone() + * @return \phpseclib\Math\BigInteger + */ + function copy() + { + $temp = new static(); + $temp->value = $this->value; + $temp->is_negative = $this->is_negative; + $temp->precision = $this->precision; + $temp->bitmask = $this->bitmask; + return $temp; + } + + /** + * __toString() magic method + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * toString(). + * + * @access public + * @internal Implemented per a suggestion by Techie-Michael - thanks! + */ + function __toString() + { + return $this->toString(); + } + + /** + * __clone() magic method + * + * Although you can call BigInteger::__toString() directly in PHP5, you cannot call BigInteger::__clone() directly + * in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5 + * only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and + * PHP5, call BigInteger::copy(), instead. + * + * @access public + * @see self::copy() + * @return \phpseclib\Math\BigInteger + */ + function __clone() + { + return $this->copy(); + } + + /** + * __sleep() magic method + * + * Will be called, automatically, when serialize() is called on a BigInteger object. + * + * @see self::__wakeup() + * @access public + */ + function __sleep() + { + $this->hex = $this->toHex(true); + $vars = array('hex'); + if ($this->precision > 0) { + $vars[] = 'precision'; + } + return $vars; + } + + /** + * __wakeup() magic method + * + * Will be called, automatically, when unserialize() is called on a BigInteger object. + * + * @see self::__sleep() + * @access public + */ + function __wakeup() + { + $temp = new static($this->hex, -16); + $this->value = $temp->value; + $this->is_negative = $temp->is_negative; + if ($this->precision > 0) { + // recalculate $this->bitmask + $this->setPrecision($this->precision); + } + } + + /** + * __debugInfo() magic method + * + * Will be called, automatically, when print_r() or var_dump() are called + * + * @access public + */ + function __debugInfo() + { + $opts = array(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $engine = 'gmp'; + break; + case self::MODE_BCMATH: + $engine = 'bcmath'; + break; + case self::MODE_INTERNAL: + $engine = 'internal'; + $opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit'; + } + if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + $opts[] = 'OpenSSL'; + } + if (!empty($opts)) { + $engine.= ' (' . implode('.', $opts) . ')'; + } + return array( + 'value' => '0x' . $this->toHex(true), + 'engine' => $engine + ); + } + + /** + * Adds two BigIntegers. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger('10'); + * $b = new \phpseclib\Math\BigInteger('20'); + * + * $c = $a->add($b); + * + * echo $c->toString(); // outputs 30 + * ?> + * </code> + * + * @param \phpseclib\Math\BigInteger $y + * @return \phpseclib\Math\BigInteger + * @access public + * @internal Performs base-2**52 addition + */ + function add($y) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_add($this->value, $y->value); + + return $this->_normalize($temp); + case self::MODE_BCMATH: + $temp = new static(); + $temp->value = bcadd($this->value, $y->value, 0); + + return $this->_normalize($temp); + } + + $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative); + + $result = new static(); + $result->value = $temp[self::VALUE]; + $result->is_negative = $temp[self::SIGN]; + + return $this->_normalize($result); + } + + /** + * Performs addition. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array + * @access private + */ + function _add($x_value, $x_negative, $y_value, $y_negative) + { + $x_size = count($x_value); + $y_size = count($y_value); + + if ($x_size == 0) { + return array( + self::VALUE => $y_value, + self::SIGN => $y_negative + ); + } elseif ($y_size == 0) { + return array( + self::VALUE => $x_value, + self::SIGN => $x_negative + ); + } + + // subtract, if appropriate + if ($x_negative != $y_negative) { + if ($x_value == $y_value) { + return array( + self::VALUE => array(), + self::SIGN => false + ); + } + + $temp = $this->_subtract($x_value, false, $y_value, false); + $temp[self::SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ? + $x_negative : $y_negative; + + return $temp; + } + + if ($x_size < $y_size) { + $size = $x_size; + $value = $y_value; + } else { + $size = $y_size; + $value = $x_value; + } + + $value[count($value)] = 0; // just in case the carry adds an extra digit + + $carry = 0; + for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) { + $sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry; + $carry = $sum >= self::$maxDigit2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum - self::$maxDigit2 : $sum; + + $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); + + $value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) + $value[$j] = $temp; + } + + if ($j == $size) { // ie. if $y_size is odd + $sum = $x_value[$i] + $y_value[$i] + $carry; + $carry = $sum >= self::$baseFull; + $value[$i] = $carry ? $sum - self::$baseFull : $sum; + ++$i; // ie. let $i = $j since we've just done $value[$i] + } + + if ($carry) { + for (; $value[$i] == self::$maxDigit; ++$i) { + $value[$i] = 0; + } + ++$value[$i]; + } + + return array( + self::VALUE => $this->_trim($value), + self::SIGN => $x_negative + ); + } + + /** + * Subtracts two BigIntegers. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger('10'); + * $b = new \phpseclib\Math\BigInteger('20'); + * + * $c = $a->subtract($b); + * + * echo $c->toString(); // outputs -10 + * ?> + * </code> + * + * @param \phpseclib\Math\BigInteger $y + * @return \phpseclib\Math\BigInteger + * @access public + * @internal Performs base-2**52 subtraction + */ + function subtract($y) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_sub($this->value, $y->value); + + return $this->_normalize($temp); + case self::MODE_BCMATH: + $temp = new static(); + $temp->value = bcsub($this->value, $y->value, 0); + + return $this->_normalize($temp); + } + + $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); + + $result = new static(); + $result->value = $temp[self::VALUE]; + $result->is_negative = $temp[self::SIGN]; + + return $this->_normalize($result); + } + + /** + * Performs subtraction. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array + * @access private + */ + function _subtract($x_value, $x_negative, $y_value, $y_negative) + { + $x_size = count($x_value); + $y_size = count($y_value); + + if ($x_size == 0) { + return array( + self::VALUE => $y_value, + self::SIGN => !$y_negative + ); + } elseif ($y_size == 0) { + return array( + self::VALUE => $x_value, + self::SIGN => $x_negative + ); + } + + // add, if appropriate (ie. -$x - +$y or +$x - -$y) + if ($x_negative != $y_negative) { + $temp = $this->_add($x_value, false, $y_value, false); + $temp[self::SIGN] = $x_negative; + + return $temp; + } + + $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative); + + if (!$diff) { + return array( + self::VALUE => array(), + self::SIGN => false + ); + } + + // switch $x and $y around, if appropriate. + if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + + $x_negative = !$x_negative; + + $x_size = count($x_value); + $y_size = count($y_value); + } + + // at this point, $x_value should be at least as big as - if not bigger than - $y_value + + $carry = 0; + for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) { + $sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry; + $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum + self::$maxDigit2 : $sum; + + $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); + + $x_value[$i] = (int) ($sum - self::$baseFull * $temp); + $x_value[$j] = $temp; + } + + if ($j == $y_size) { // ie. if $y_size is odd + $sum = $x_value[$i] - $y_value[$i] - $carry; + $carry = $sum < 0; + $x_value[$i] = $carry ? $sum + self::$baseFull : $sum; + ++$i; + } + + if ($carry) { + for (; !$x_value[$i]; ++$i) { + $x_value[$i] = self::$maxDigit; + } + --$x_value[$i]; + } + + return array( + self::VALUE => $this->_trim($x_value), + self::SIGN => $x_negative + ); + } + + /** + * Multiplies two BigIntegers + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger('10'); + * $b = new \phpseclib\Math\BigInteger('20'); + * + * $c = $a->multiply($b); + * + * echo $c->toString(); // outputs 200 + * ?> + * </code> + * + * @param \phpseclib\Math\BigInteger $x + * @return \phpseclib\Math\BigInteger + * @access public + */ + function multiply($x) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_mul($this->value, $x->value); + + return $this->_normalize($temp); + case self::MODE_BCMATH: + $temp = new static(); + $temp->value = bcmul($this->value, $x->value, 0); + + return $this->_normalize($temp); + } + + $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); + + $product = new static(); + $product->value = $temp[self::VALUE]; + $product->is_negative = $temp[self::SIGN]; + + return $this->_normalize($product); + } + + /** + * Performs multiplication. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array + * @access private + */ + function _multiply($x_value, $x_negative, $y_value, $y_negative) + { + //if ( $x_value == $y_value ) { + // return array( + // self::VALUE => $this->_square($x_value), + // self::SIGN => $x_sign != $y_value + // ); + //} + + $x_length = count($x_value); + $y_length = count($y_value); + + if (!$x_length || !$y_length) { // a 0 is being multiplied + return array( + self::VALUE => array(), + self::SIGN => false + ); + } + + return array( + self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ? + $this->_trim($this->_regularMultiply($x_value, $y_value)) : + $this->_trim($this->_karatsuba($x_value, $y_value)), + self::SIGN => $x_negative != $y_negative + ); + } + + /** + * Performs long multiplication on two BigIntegers + * + * Modeled after 'multiply' in MutableBigInteger.java. + * + * @param array $x_value + * @param array $y_value + * @return array + * @access private + */ + function _regularMultiply($x_value, $y_value) + { + $x_length = count($x_value); + $y_length = count($y_value); + + if (!$x_length || !$y_length) { // a 0 is being multiplied + return array(); + } + + if ($x_length < $y_length) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + + $x_length = count($x_value); + $y_length = count($y_value); + } + + $product_value = $this->_array_repeat(0, $x_length + $y_length); + + // the following for loop could be removed if the for loop following it + // (the one with nested for loops) initially set $i to 0, but + // doing so would also make the result in one set of unnecessary adds, + // since on the outermost loops first pass, $product->value[$k] is going + // to always be 0 + + $carry = 0; + + for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0 + $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$j] = (int) ($temp - self::$baseFull * $carry); + } + + $product_value[$j] = $carry; + + // the above for loop is what the previous comment was talking about. the + // following for loop is the "one with nested for loops" + for ($i = 1; $i < $y_length; ++$i) { + $carry = 0; + + for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { + $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$k] = (int) ($temp - self::$baseFull * $carry); + } + + $product_value[$k] = $carry; + } + + return $product_value; + } + + /** + * Performs Karatsuba multiplication on two BigIntegers + * + * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}. + * + * @param array $x_value + * @param array $y_value + * @return array + * @access private + */ + function _karatsuba($x_value, $y_value) + { + $m = min(count($x_value) >> 1, count($y_value) >> 1); + + if ($m < self::KARATSUBA_CUTOFF) { + return $this->_regularMultiply($x_value, $y_value); + } + + $x1 = array_slice($x_value, $m); + $x0 = array_slice($x_value, 0, $m); + $y1 = array_slice($y_value, $m); + $y0 = array_slice($y_value, 0, $m); + + $z2 = $this->_karatsuba($x1, $y1); + $z0 = $this->_karatsuba($x0, $y0); + + $z1 = $this->_add($x1, false, $x0, false); + $temp = $this->_add($y1, false, $y0, false); + $z1 = $this->_karatsuba($z1[self::VALUE], $temp[self::VALUE]); + $temp = $this->_add($z2, false, $z0, false); + $z1 = $this->_subtract($z1, false, $temp[self::VALUE], false); + + $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); + $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); + + $xy = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); + $xy = $this->_add($xy[self::VALUE], $xy[self::SIGN], $z0, false); + + return $xy[self::VALUE]; + } + + /** + * Performs squaring + * + * @param array $x + * @return array + * @access private + */ + function _square($x = false) + { + return count($x) < 2 * self::KARATSUBA_CUTOFF ? + $this->_trim($this->_baseSquare($x)) : + $this->_trim($this->_karatsubaSquare($x)); + } + + /** + * Performs traditional squaring on two BigIntegers + * + * Squaring can be done faster than multiplying a number by itself can be. See + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information. + * + * @param array $value + * @return array + * @access private + */ + function _baseSquare($value) + { + if (empty($value)) { + return array(); + } + $square_value = $this->_array_repeat(0, 2 * count($value)); + + for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) { + $i2 = $i << 1; + + $temp = $square_value[$i2] + $value[$i] * $value[$i]; + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $square_value[$i2] = (int) ($temp - self::$baseFull * $carry); + + // note how we start from $i+1 instead of 0 as we do in multiplication. + for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { + $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $square_value[$k] = (int) ($temp - self::$baseFull * $carry); + } + + // the following line can yield values larger 2**15. at this point, PHP should switch + // over to floats. + $square_value[$i + $max_index + 1] = $carry; + } + + return $square_value; + } + + /** + * Performs Karatsuba "squaring" on two BigIntegers + * + * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}. + * + * @param array $value + * @return array + * @access private + */ + function _karatsubaSquare($value) + { + $m = count($value) >> 1; + + if ($m < self::KARATSUBA_CUTOFF) { + return $this->_baseSquare($value); + } + + $x1 = array_slice($value, $m); + $x0 = array_slice($value, 0, $m); + + $z2 = $this->_karatsubaSquare($x1); + $z0 = $this->_karatsubaSquare($x0); + + $z1 = $this->_add($x1, false, $x0, false); + $z1 = $this->_karatsubaSquare($z1[self::VALUE]); + $temp = $this->_add($z2, false, $z0, false); + $z1 = $this->_subtract($z1, false, $temp[self::VALUE], false); + + $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); + $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); + + $xx = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); + $xx = $this->_add($xx[self::VALUE], $xx[self::SIGN], $z0, false); + + return $xx[self::VALUE]; + } + + /** + * Divides two BigIntegers. + * + * Returns an array whose first element contains the quotient and whose second element contains the + * "common residue". If the remainder would be positive, the "common residue" and the remainder are the + * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder + * and the divisor (basically, the "common residue" is the first positive modulo). + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger('10'); + * $b = new \phpseclib\Math\BigInteger('20'); + * + * list($quotient, $remainder) = $a->divide($b); + * + * echo $quotient->toString(); // outputs 0 + * echo "\r\n"; + * echo $remainder->toString(); // outputs 10 + * ?> + * </code> + * + * @param \phpseclib\Math\BigInteger $y + * @return array + * @access public + * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}. + */ + function divide($y) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $quotient = new static(); + $remainder = new static(); + + list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value); + + if (gmp_sign($remainder->value) < 0) { + $remainder->value = gmp_add($remainder->value, gmp_abs($y->value)); + } + + return array($this->_normalize($quotient), $this->_normalize($remainder)); + case self::MODE_BCMATH: + $quotient = new static(); + $remainder = new static(); + + $quotient->value = bcdiv($this->value, $y->value, 0); + $remainder->value = bcmod($this->value, $y->value); + + if ($remainder->value[0] == '-') { + $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0); + } + + return array($this->_normalize($quotient), $this->_normalize($remainder)); + } + + if (count($y->value) == 1) { + list($q, $r) = $this->_divide_digit($this->value, $y->value[0]); + $quotient = new static(); + $remainder = new static(); + $quotient->value = $q; + $remainder->value = array($r); + $quotient->is_negative = $this->is_negative != $y->is_negative; + return array($this->_normalize($quotient), $this->_normalize($remainder)); + } + + static $zero; + if (!isset($zero)) { + $zero = new static(); + } + + $x = $this->copy(); + $y = $y->copy(); + + $x_sign = $x->is_negative; + $y_sign = $y->is_negative; + + $x->is_negative = $y->is_negative = false; + + $diff = $x->compare($y); + + if (!$diff) { + $temp = new static(); + $temp->value = array(1); + $temp->is_negative = $x_sign != $y_sign; + return array($this->_normalize($temp), $this->_normalize(new static())); + } + + if ($diff < 0) { + // if $x is negative, "add" $y. + if ($x_sign) { + $x = $y->subtract($x); + } + return array($this->_normalize(new static()), $this->_normalize($x)); + } + + // normalize $x and $y as described in HAC 14.23 / 14.24 + $msb = $y->value[count($y->value) - 1]; + for ($shift = 0; !($msb & self::$msb); ++$shift) { + $msb <<= 1; + } + $x->_lshift($shift); + $y->_lshift($shift); + $y_value = &$y->value; + + $x_max = count($x->value) - 1; + $y_max = count($y->value) - 1; + + $quotient = new static(); + $quotient_value = &$quotient->value; + $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1); + + static $temp, $lhs, $rhs; + if (!isset($temp)) { + $temp = new static(); + $lhs = new static(); + $rhs = new static(); + } + $temp_value = &$temp->value; + $rhs_value = &$rhs->value; + + // $temp = $y << ($x_max - $y_max-1) in base 2**26 + $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value); + + while ($x->compare($temp) >= 0) { + // calculate the "common residue" + ++$quotient_value[$x_max - $y_max]; + $x = $x->subtract($temp); + $x_max = count($x->value) - 1; + } + + for ($i = $x_max; $i >= $y_max + 1; --$i) { + $x_value = &$x->value; + $x_window = array( + isset($x_value[$i]) ? $x_value[$i] : 0, + isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0, + isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0 + ); + $y_window = array( + $y_value[$y_max], + ($y_max > 0) ? $y_value[$y_max - 1] : 0 + ); + + $q_index = $i - $y_max - 1; + if ($x_window[0] == $y_window[0]) { + $quotient_value[$q_index] = self::$maxDigit; + } else { + $quotient_value[$q_index] = $this->_safe_divide( + $x_window[0] * self::$baseFull + $x_window[1], + $y_window[0] + ); + } + + $temp_value = array($y_window[1], $y_window[0]); + + $lhs->value = array($quotient_value[$q_index]); + $lhs = $lhs->multiply($temp); + + $rhs_value = array($x_window[2], $x_window[1], $x_window[0]); + + while ($lhs->compare($rhs) > 0) { + --$quotient_value[$q_index]; + + $lhs->value = array($quotient_value[$q_index]); + $lhs = $lhs->multiply($temp); + } + + $adjust = $this->_array_repeat(0, $q_index); + $temp_value = array($quotient_value[$q_index]); + $temp = $temp->multiply($y); + $temp_value = &$temp->value; + if (count($temp_value)) { + $temp_value = array_merge($adjust, $temp_value); + } + + $x = $x->subtract($temp); + + if ($x->compare($zero) < 0) { + $temp_value = array_merge($adjust, $y_value); + $x = $x->add($temp); + + --$quotient_value[$q_index]; + } + + $x_max = count($x_value) - 1; + } + + // unnormalize the remainder + $x->_rshift($shift); + + $quotient->is_negative = $x_sign != $y_sign; + + // calculate the "common residue", if appropriate + if ($x_sign) { + $y->_rshift($shift); + $x = $y->subtract($x); + } + + return array($this->_normalize($quotient), $this->_normalize($x)); + } + + /** + * Divides a BigInteger by a regular integer + * + * abc / x = a00 / x + b0 / x + c / x + * + * @param array $dividend + * @param array $divisor + * @return array + * @access private + */ + function _divide_digit($dividend, $divisor) + { + $carry = 0; + $result = array(); + + for ($i = count($dividend) - 1; $i >= 0; --$i) { + $temp = self::$baseFull * $carry + $dividend[$i]; + $result[$i] = $this->_safe_divide($temp, $divisor); + $carry = (int) ($temp - $divisor * $result[$i]); + } + + return array($result, $carry); + } + + /** + * Performs modular exponentiation. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger('10'); + * $b = new \phpseclib\Math\BigInteger('20'); + * $c = new \phpseclib\Math\BigInteger('30'); + * + * $c = $a->modPow($b, $c); + * + * echo $c->toString(); // outputs 10 + * ?> + * </code> + * + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger + * @access public + * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and + * and although the approach involving repeated squaring does vastly better, it, too, is impractical + * for our purposes. The reason being that division - by far the most complicated and time-consuming + * of the basic operations (eg. +,-,*,/) - occurs multiple times within it. + * + * Modular reductions resolve this issue. Although an individual modular reduction takes more time + * then an individual division, when performed in succession (with the same modulo), they're a lot faster. + * + * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction, + * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the + * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because + * the product of two odd numbers is odd), but what about when RSA isn't used? + * + * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a + * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the + * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however, + * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and + * the other, a power of two - and recombine them, later. This is the method that this modPow function uses. + * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates. + */ + function modPow($e, $n) + { + $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); + + if ($e->compare(new static()) < 0) { + $e = $e->abs(); + + $temp = $this->modInverse($n); + if ($temp === false) { + return false; + } + + return $this->_normalize($temp->modPow($e, $n)); + } + + if (MATH_BIGINTEGER_MODE == self::MODE_GMP) { + $temp = new static(); + $temp->value = gmp_powm($this->value, $e->value, $n->value); + + return $this->_normalize($temp); + } + + if ($this->compare(new static()) < 0 || $this->compare($n) > 0) { + list(, $temp) = $this->divide($n); + return $temp->modPow($e, $n); + } + + if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + $components = array( + 'modulus' => $n->toBytes(true), + 'publicExponent' => $e->toBytes(true) + ); + + $components = array( + 'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), + 'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']) + ); + + $RSAPublicKey = pack( + 'Ca*a*a*', + 48, + $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], + $components['publicExponent'] + ); + + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPublicKey = chr(0) . $RSAPublicKey; + $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; + + $encapsulated = pack( + 'Ca*a*', + 48, + $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), + $rsaOID . $RSAPublicKey + ); + + $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($encapsulated)) . + '-----END PUBLIC KEY-----'; + + $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT); + + if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) { + return new static($result, 256); + } + } + + if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { + $temp = new static(); + $temp->value = bcpowmod($this->value, $e->value, $n->value, 0); + + return $this->_normalize($temp); + } + + if (empty($e->value)) { + $temp = new static(); + $temp->value = array(1); + return $this->_normalize($temp); + } + + if ($e->value == array(1)) { + list(, $temp) = $this->divide($n); + return $this->_normalize($temp); + } + + if ($e->value == array(2)) { + $temp = new static(); + $temp->value = $this->_square($this->value); + list(, $temp) = $temp->divide($n); + return $this->_normalize($temp); + } + + return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT)); + + // the following code, although not callable, can be run independently of the above code + // although the above code performed better in my benchmarks the following could might + // perform better under different circumstances. in lieu of deleting it it's just been + // made uncallable + + // is the modulo odd? + if ($n->value[0] & 1) { + return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY)); + } + // if it's not, it's even + + // find the lowest set bit (eg. the max pow of 2 that divides $n) + for ($i = 0; $i < count($n->value); ++$i) { + if ($n->value[$i]) { + $temp = decbin($n->value[$i]); + $j = strlen($temp) - strrpos($temp, '1') - 1; + $j+= 26 * $i; + break; + } + } + // at this point, 2^$j * $n/(2^$j) == $n + + $mod1 = $n->copy(); + $mod1->_rshift($j); + $mod2 = new static(); + $mod2->value = array(1); + $mod2->_lshift($j); + + $part1 = ($mod1->value != array(1)) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static(); + $part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2); + + $y1 = $mod2->modInverse($mod1); + $y2 = $mod1->modInverse($mod2); + + $result = $part1->multiply($mod2); + $result = $result->multiply($y1); + + $temp = $part2->multiply($mod1); + $temp = $temp->multiply($y2); + + $result = $result->add($temp); + list(, $result) = $result->divide($n); + + return $this->_normalize($result); + } + + /** + * Performs modular exponentiation. + * + * Alias for modPow(). + * + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger + * @access public + */ + function powMod($e, $n) + { + return $this->modPow($e, $n); + } + + /** + * Sliding Window k-ary Modular Exponentiation + * + * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims, + * however, this function performs a modular reduction after every multiplication and squaring operation. + * As such, this function has the same preconditions that the reductions being used do. + * + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $n + * @param int $mode + * @return \phpseclib\Math\BigInteger + * @access private + */ + function _slidingWindow($e, $n, $mode) + { + static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function + //static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1 + + $e_value = $e->value; + $e_length = count($e_value) - 1; + $e_bits = decbin($e_value[$e_length]); + for ($i = $e_length - 1; $i >= 0; --$i) { + $e_bits.= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT); + } + + $e_length = strlen($e_bits); + + // calculate the appropriate window size. + // $window_size == 3 if $window_ranges is between 25 and 81, for example. + for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) { + } + + $n_value = $n->value; + + // precompute $this^0 through $this^$window_size + $powers = array(); + $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode); + $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode); + + // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end + // in a 1. ie. it's supposed to be odd. + $temp = 1 << ($window_size - 1); + for ($i = 1; $i < $temp; ++$i) { + $i2 = $i << 1; + $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); + } + + $result = array(1); + $result = $this->_prepareReduce($result, $n_value, $mode); + + for ($i = 0; $i < $e_length;) { + if (!$e_bits[$i]) { + $result = $this->_squareReduce($result, $n_value, $mode); + ++$i; + } else { + for ($j = $window_size - 1; $j > 0; --$j) { + if (!empty($e_bits[$i + $j])) { + break; + } + } + + // eg. the length of substr($e_bits, $i, $j + 1) + for ($k = 0; $k <= $j; ++$k) { + $result = $this->_squareReduce($result, $n_value, $mode); + } + + $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); + + $i += $j + 1; + } + } + + $temp = new static(); + $temp->value = $this->_reduce($result, $n_value, $mode); + + return $temp; + } + + /** + * Modular reduction + * + * For most $modes this will return the remainder. + * + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @param int $mode + * @return array + */ + function _reduce($x, $n, $mode) + { + switch ($mode) { + case self::MONTGOMERY: + return $this->_montgomery($x, $n); + case self::BARRETT: + return $this->_barrett($x, $n); + case self::POWEROF2: + $lhs = new static(); + $lhs->value = $x; + $rhs = new static(); + $rhs->value = $n; + return $x->_mod2($n); + case self::CLASSIC: + $lhs = new static(); + $lhs->value = $x; + $rhs = new static(); + $rhs->value = $n; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + case self::NONE: + return $x; + default: + // an invalid $mode was provided + } + } + + /** + * Modular reduction preperation + * + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @param int $mode + * @return array + */ + function _prepareReduce($x, $n, $mode) + { + if ($mode == self::MONTGOMERY) { + return $this->_prepMontgomery($x, $n); + } + return $this->_reduce($x, $n, $mode); + } + + /** + * Modular multiply + * + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $y + * @param array $n + * @param int $mode + * @return array + */ + function _multiplyReduce($x, $y, $n, $mode) + { + if ($mode == self::MONTGOMERY) { + return $this->_montgomeryMultiply($x, $y, $n); + } + $temp = $this->_multiply($x, false, $y, false); + return $this->_reduce($temp[self::VALUE], $n, $mode); + } + + /** + * Modular square + * + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @param int $mode + * @return array + */ + function _squareReduce($x, $n, $mode) + { + if ($mode == self::MONTGOMERY) { + return $this->_montgomeryMultiply($x, $x, $n); + } + return $this->_reduce($this->_square($x), $n, $mode); + } + + /** + * Modulos for Powers of Two + * + * Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1), + * we'll just use this function as a wrapper for doing that. + * + * @see self::_slidingWindow() + * @access private + * @param \phpseclib\Math\BigInteger + * @return \phpseclib\Math\BigInteger + */ + function _mod2($n) + { + $temp = new static(); + $temp->value = array(1); + return $this->bitwise_and($n->subtract($temp)); + } + + /** + * Barrett Modular Reduction + * + * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly, + * so as not to require negative numbers (initially, this script didn't support negative numbers). + * + * Employs "folding", as described at + * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from + * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x." + * + * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that + * usable on account of (1) its not using reasonable radix points as discussed in + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable + * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that + * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line + * comments for details. + * + * @see self::_slidingWindow() + * @access private + * @param array $n + * @param array $m + * @return array + */ + function _barrett($n, $m) + { + static $cache = array( + self::VARIABLE => array(), + self::DATA => array() + ); + + $m_length = count($m); + + // if ($this->_compare($n, $this->_square($m)) >= 0) { + if (count($n) > 2 * $m_length) { + $lhs = new static(); + $rhs = new static(); + $lhs->value = $n; + $rhs->value = $m; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + + // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced + if ($m_length < 5) { + return $this->_regularBarrett($n, $m); + } + + // n = 2 * m.length + + if (($key = array_search($m, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $m; + + $lhs = new static(); + $lhs_value = &$lhs->value; + $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1)); + $lhs_value[] = 1; + $rhs = new static(); + $rhs->value = $m; + + list($u, $m1) = $lhs->divide($rhs); + $u = $u->value; + $m1 = $m1->value; + + $cache[self::DATA][] = array( + 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1) + 'm1'=> $m1 // m.length + ); + } else { + extract($cache[self::DATA][$key]); + } + + $cutoff = $m_length + ($m_length >> 1); + $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1) + $msd = array_slice($n, $cutoff); // m.length >> 1 + $lsd = $this->_trim($lsd); + $temp = $this->_multiply($msd, false, $m1, false); + $n = $this->_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 + + if ($m_length & 1) { + return $this->_regularBarrett($n[self::VALUE], $m); + } + + // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 + $temp = array_slice($n[self::VALUE], $m_length - 1); + // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 + // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 + $temp = $this->_multiply($temp, false, $u, false); + // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 + // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + $temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1); + // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 + // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) + $temp = $this->_multiply($temp, false, $m, false); + + // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit + // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop + // following this comment would loop a lot (hence our calling _regularBarrett() in that situation). + + $result = $this->_subtract($n[self::VALUE], false, $temp[self::VALUE], false); + + while ($this->_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) { + $result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $m, false); + } + + return $result[self::VALUE]; + } + + /** + * (Regular) Barrett Modular Reduction + * + * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this + * is that this function does not fold the denominator into a smaller form. + * + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @return array + */ + function _regularBarrett($x, $n) + { + static $cache = array( + self::VARIABLE => array(), + self::DATA => array() + ); + + $n_length = count($n); + + if (count($x) > 2 * $n_length) { + $lhs = new static(); + $rhs = new static(); + $lhs->value = $x; + $rhs->value = $n; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + + if (($key = array_search($n, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $n; + $lhs = new static(); + $lhs_value = &$lhs->value; + $lhs_value = $this->_array_repeat(0, 2 * $n_length); + $lhs_value[] = 1; + $rhs = new static(); + $rhs->value = $n; + list($temp, ) = $lhs->divide($rhs); // m.length + $cache[self::DATA][] = $temp->value; + } + + // 2 * m.length - (m.length - 1) = m.length + 1 + $temp = array_slice($x, $n_length - 1); + // (m.length + 1) + m.length = 2 * m.length + 1 + $temp = $this->_multiply($temp, false, $cache[self::DATA][$key], false); + // (2 * m.length + 1) - (m.length - 1) = m.length + 2 + $temp = array_slice($temp[self::VALUE], $n_length + 1); + + // m.length + 1 + $result = array_slice($x, 0, $n_length + 1); + // m.length + 1 + $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1); + // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1) + + if ($this->_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) { + $corrector_value = $this->_array_repeat(0, $n_length + 1); + $corrector_value[count($corrector_value)] = 1; + $result = $this->_add($result, false, $corrector_value, false); + $result = $result[self::VALUE]; + } + + // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits + $result = $this->_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]); + while ($this->_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) { + $result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $n, false); + } + + return $result[self::VALUE]; + } + + /** + * Performs long multiplication up to $stop digits + * + * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved. + * + * @see self::_regularBarrett() + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @param int $stop + * @return array + * @access private + */ + function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) + { + $x_length = count($x_value); + $y_length = count($y_value); + + if (!$x_length || !$y_length) { // a 0 is being multiplied + return array( + self::VALUE => array(), + self::SIGN => false + ); + } + + if ($x_length < $y_length) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + + $x_length = count($x_value); + $y_length = count($y_value); + } + + $product_value = $this->_array_repeat(0, $x_length + $y_length); + + // the following for loop could be removed if the for loop following it + // (the one with nested for loops) initially set $i to 0, but + // doing so would also make the result in one set of unnecessary adds, + // since on the outermost loops first pass, $product->value[$k] is going + // to always be 0 + + $carry = 0; + + for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i + $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$j] = (int) ($temp - self::$baseFull * $carry); + } + + if ($j < $stop) { + $product_value[$j] = $carry; + } + + // the above for loop is what the previous comment was talking about. the + // following for loop is the "one with nested for loops" + + for ($i = 1; $i < $y_length; ++$i) { + $carry = 0; + + for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { + $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$k] = (int) ($temp - self::$baseFull * $carry); + } + + if ($k < $stop) { + $product_value[$k] = $carry; + } + } + + return array( + self::VALUE => $this->_trim($product_value), + self::SIGN => $x_negative != $y_negative + ); + } + + /** + * Montgomery Modular Reduction + * + * ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n. + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be + * improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function + * to work correctly. + * + * @see self::_prepMontgomery() + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @return array + */ + function _montgomery($x, $n) + { + static $cache = array( + self::VARIABLE => array(), + self::DATA => array() + ); + + if (($key = array_search($n, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $x; + $cache[self::DATA][] = $this->_modInverse67108864($n); + } + + $k = count($n); + + $result = array(self::VALUE => $x); + + for ($i = 0; $i < $k; ++$i) { + $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key]; + $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); + $temp = $this->_regularMultiply(array($temp), $n); + $temp = array_merge($this->_array_repeat(0, $i), $temp); + $result = $this->_add($result[self::VALUE], false, $temp, false); + } + + $result[self::VALUE] = array_slice($result[self::VALUE], $k); + + if ($this->_compare($result, false, $n, false) >= 0) { + $result = $this->_subtract($result[self::VALUE], false, $n, false); + } + + return $result[self::VALUE]; + } + + /** + * Montgomery Multiply + * + * Interleaves the montgomery reduction and long multiplication algorithms together as described in + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36} + * + * @see self::_prepMontgomery() + * @see self::_montgomery() + * @access private + * @param array $x + * @param array $y + * @param array $m + * @return array + */ + function _montgomeryMultiply($x, $y, $m) + { + $temp = $this->_multiply($x, false, $y, false); + return $this->_montgomery($temp[self::VALUE], $m); + + // the following code, although not callable, can be run independently of the above code + // although the above code performed better in my benchmarks the following could might + // perform better under different circumstances. in lieu of deleting it it's just been + // made uncallable + + static $cache = array( + self::VARIABLE => array(), + self::DATA => array() + ); + + if (($key = array_search($m, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $m; + $cache[self::DATA][] = $this->_modInverse67108864($m); + } + + $n = max(count($x), count($y), count($m)); + $x = array_pad($x, $n, 0); + $y = array_pad($y, $n, 0); + $m = array_pad($m, $n, 0); + $a = array(self::VALUE => $this->_array_repeat(0, $n + 1)); + for ($i = 0; $i < $n; ++$i) { + $temp = $a[self::VALUE][0] + $x[$i] * $y[0]; + $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); + $temp = $temp * $cache[self::DATA][$key]; + $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); + $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false); + $a = $this->_add($a[self::VALUE], false, $temp[self::VALUE], false); + $a[self::VALUE] = array_slice($a[self::VALUE], 1); + } + if ($this->_compare($a[self::VALUE], false, $m, false) >= 0) { + $a = $this->_subtract($a[self::VALUE], false, $m, false); + } + return $a[self::VALUE]; + } + + /** + * Prepare a number for use in Montgomery Modular Reductions + * + * @see self::_montgomery() + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @return array + */ + function _prepMontgomery($x, $n) + { + $lhs = new static(); + $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x); + $rhs = new static(); + $rhs->value = $n; + + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + + /** + * Modular Inverse of a number mod 2**26 (eg. 67108864) + * + * Based off of the bnpInvDigit function implemented and justified in the following URL: + * + * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js} + * + * The following URL provides more info: + * + * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85} + * + * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For + * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields + * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't + * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that + * the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the + * maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to + * 40 bits, which only 64-bit floating points will support. + * + * Thanks to Pedro Gimeno Fortea for input! + * + * @see self::_montgomery() + * @access private + * @param array $x + * @return int + */ + function _modInverse67108864($x) // 2**26 == 67,108,864 + { + $x = -$x[0]; + $result = $x & 0x3; // x**-1 mod 2**2 + $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4 + $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8 + $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16 + $result = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); // x**-1 mod 2**26 + return $result & self::$maxDigit; + } + + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger(30); + * $b = new \phpseclib\Math\BigInteger(17); + * + * $c = $a->modInverse($b); + * echo $c->toString(); // outputs 4 + * + * echo "\r\n"; + * + * $d = $a->multiply($c); + * list(, $d) = $d->divide($b); + * echo $d; // outputs 1 (as per the definition of modular inverse) + * ?> + * </code> + * + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger|false + * @access public + * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information. + */ + function modInverse($n) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_invert($this->value, $n->value); + + return ($temp->value === false) ? false : $this->_normalize($temp); + } + + static $zero, $one; + if (!isset($zero)) { + $zero = new static(); + $one = new static(1); + } + + // $x mod -$n == $x mod $n. + $n = $n->abs(); + + if ($this->compare($zero) < 0) { + $temp = $this->abs(); + $temp = $temp->modInverse($n); + return $this->_normalize($n->subtract($temp)); + } + + extract($this->extendedGCD($n)); + + if (!$gcd->equals($one)) { + return false; + } + + $x = $x->compare($zero) < 0 ? $x->add($n) : $x; + + return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x); + } + + /** + * Calculates the greatest common divisor and Bezout's identity. + * + * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that + * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which + * combination is returned is dependent upon which mode is in use. See + * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger(693); + * $b = new \phpseclib\Math\BigInteger(609); + * + * extract($a->extendedGCD($b)); + * + * echo $gcd->toString() . "\r\n"; // outputs 21 + * echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21 + * ?> + * </code> + * + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger + * @access public + * @internal Calculates the GCD using the binary xGCD algorithim described in + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes, + * the more traditional algorithim requires "relatively costly multiple-precision divisions". + */ + function extendedGCD($n) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + extract(gmp_gcdext($this->value, $n->value)); + + return array( + 'gcd' => $this->_normalize(new static($g)), + 'x' => $this->_normalize(new static($s)), + 'y' => $this->_normalize(new static($t)) + ); + case self::MODE_BCMATH: + // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works + // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is, + // the basic extended euclidean algorithim is what we're using. + + $u = $this->value; + $v = $n->value; + + $a = '1'; + $b = '0'; + $c = '0'; + $d = '1'; + + while (bccomp($v, '0', 0) != 0) { + $q = bcdiv($u, $v, 0); + + $temp = $u; + $u = $v; + $v = bcsub($temp, bcmul($v, $q, 0), 0); + + $temp = $a; + $a = $c; + $c = bcsub($temp, bcmul($a, $q, 0), 0); + + $temp = $b; + $b = $d; + $d = bcsub($temp, bcmul($b, $q, 0), 0); + } + + return array( + 'gcd' => $this->_normalize(new static($u)), + 'x' => $this->_normalize(new static($a)), + 'y' => $this->_normalize(new static($b)) + ); + } + + $y = $n->copy(); + $x = $this->copy(); + $g = new static(); + $g->value = array(1); + + while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) { + $x->_rshift(1); + $y->_rshift(1); + $g->_lshift(1); + } + + $u = $x->copy(); + $v = $y->copy(); + + $a = new static(); + $b = new static(); + $c = new static(); + $d = new static(); + + $a->value = $d->value = $g->value = array(1); + $b->value = $c->value = array(); + + while (!empty($u->value)) { + while (!($u->value[0] & 1)) { + $u->_rshift(1); + if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) { + $a = $a->add($y); + $b = $b->subtract($x); + } + $a->_rshift(1); + $b->_rshift(1); + } + + while (!($v->value[0] & 1)) { + $v->_rshift(1); + if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) { + $c = $c->add($y); + $d = $d->subtract($x); + } + $c->_rshift(1); + $d->_rshift(1); + } + + if ($u->compare($v) >= 0) { + $u = $u->subtract($v); + $a = $a->subtract($c); + $b = $b->subtract($d); + } else { + $v = $v->subtract($u); + $c = $c->subtract($a); + $d = $d->subtract($b); + } + } + + return array( + 'gcd' => $this->_normalize($g->multiply($v)), + 'x' => $this->_normalize($c), + 'y' => $this->_normalize($d) + ); + } + + /** + * Calculates the greatest common divisor + * + * Say you have 693 and 609. The GCD is 21. + * + * Here's an example: + * <code> + * <?php + * $a = new \phpseclib\Math\BigInteger(693); + * $b = new \phpseclib\Math\BigInteger(609); + * + * $gcd = a->extendedGCD($b); + * + * echo $gcd->toString() . "\r\n"; // outputs 21 + * ?> + * </code> + * + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger + * @access public + */ + function gcd($n) + { + extract($this->extendedGCD($n)); + return $gcd; + } + + /** + * Absolute value. + * + * @return \phpseclib\Math\BigInteger + * @access public + */ + function abs() + { + $temp = new static(); + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp->value = gmp_abs($this->value); + break; + case self::MODE_BCMATH: + $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value; + break; + default: + $temp->value = $this->value; + } + + return $temp; + } + + /** + * Compares two numbers. + * + * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is + * demonstrated thusly: + * + * $x > $y: $x->compare($y) > 0 + * $x < $y: $x->compare($y) < 0 + * $x == $y: $x->compare($y) == 0 + * + * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). + * + * @param \phpseclib\Math\BigInteger $y + * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @access public + * @see self::equals() + * @internal Could return $this->subtract($x), but that's not as fast as what we do do. + */ + function compare($y) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $r = gmp_cmp($this->value, $y->value); + if ($r < -1) { + $r = -1; + } + if ($r > 1) { + $r = 1; + } + return $r; + case self::MODE_BCMATH: + return bccomp($this->value, $y->value, 0); + } + + return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative); + } + + /** + * Compares two numbers. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return int + * @see self::compare() + * @access private + */ + function _compare($x_value, $x_negative, $y_value, $y_negative) + { + if ($x_negative != $y_negative) { + return (!$x_negative && $y_negative) ? 1 : -1; + } + + $result = $x_negative ? -1 : 1; + + if (count($x_value) != count($y_value)) { + return (count($x_value) > count($y_value)) ? $result : -$result; + } + $size = max(count($x_value), count($y_value)); + + $x_value = array_pad($x_value, $size, 0); + $y_value = array_pad($y_value, $size, 0); + + for ($i = count($x_value) - 1; $i >= 0; --$i) { + if ($x_value[$i] != $y_value[$i]) { + return ($x_value[$i] > $y_value[$i]) ? $result : -$result; + } + } + + return 0; + } + + /** + * Tests the equality of two numbers. + * + * If you need to see if one number is greater than or less than another number, use BigInteger::compare() + * + * @param \phpseclib\Math\BigInteger $x + * @return bool + * @access public + * @see self::compare() + */ + function equals($x) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + return gmp_cmp($this->value, $x->value) == 0; + default: + return $this->value === $x->value && $this->is_negative == $x->is_negative; + } + } + + /** + * Set Precision + * + * Some bitwise operations give different results depending on the precision being used. Examples include left + * shift, not, and rotates. + * + * @param int $bits + * @access public + */ + function setPrecision($bits) + { + $this->precision = $bits; + if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) { + $this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); + } else { + $this->bitmask = new static(bcpow('2', $bits, 0)); + } + + $temp = $this->_normalize($this); + $this->value = $temp->value; + } + + /** + * Logical And + * + * @param \phpseclib\Math\BigInteger $x + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat> + * @return \phpseclib\Math\BigInteger + */ + function bitwise_and($x) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_and($this->value, $x->value); + + return $this->_normalize($temp); + case self::MODE_BCMATH: + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new static($left & $right, 256)); + } + + $result = $this->copy(); + + $length = min(count($x->value), count($this->value)); + + $result->value = array_slice($result->value, 0, $length); + + for ($i = 0; $i < $length; ++$i) { + $result->value[$i]&= $x->value[$i]; + } + + return $this->_normalize($result); + } + + /** + * Logical Or + * + * @param \phpseclib\Math\BigInteger $x + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat> + * @return \phpseclib\Math\BigInteger + */ + function bitwise_or($x) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_or($this->value, $x->value); + + return $this->_normalize($temp); + case self::MODE_BCMATH: + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new static($left | $right, 256)); + } + + $length = max(count($this->value), count($x->value)); + $result = $this->copy(); + $result->value = array_pad($result->value, $length, 0); + $x->value = array_pad($x->value, $length, 0); + + for ($i = 0; $i < $length; ++$i) { + $result->value[$i]|= $x->value[$i]; + } + + return $this->_normalize($result); + } + + /** + * Logical Exclusive-Or + * + * @param \phpseclib\Math\BigInteger $x + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat> + * @return \phpseclib\Math\BigInteger + */ + function bitwise_xor($x) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_xor(gmp_abs($this->value), gmp_abs($x->value)); + return $this->_normalize($temp); + case self::MODE_BCMATH: + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new static($left ^ $right, 256)); + } + + $length = max(count($this->value), count($x->value)); + $result = $this->copy(); + $result->is_negative = false; + $result->value = array_pad($result->value, $length, 0); + $x->value = array_pad($x->value, $length, 0); + + for ($i = 0; $i < $length; ++$i) { + $result->value[$i]^= $x->value[$i]; + } + + return $this->_normalize($result); + } + + /** + * Logical Not + * + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat> + * @return \phpseclib\Math\BigInteger + */ + function bitwise_not() + { + // calculuate "not" without regard to $this->precision + // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) + $temp = $this->toBytes(); + if ($temp == '') { + return $this->_normalize(new static()); + } + $pre_msb = decbin(ord($temp[0])); + $temp = ~$temp; + $msb = decbin(ord($temp[0])); + if (strlen($msb) == 8) { + $msb = substr($msb, strpos($msb, '0')); + } + $temp[0] = chr(bindec($msb)); + + // see if we need to add extra leading 1's + $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8; + $new_bits = $this->precision - $current_bits; + if ($new_bits <= 0) { + return $this->_normalize(new static($temp, 256)); + } + + // generate as many leading 1's as we need to. + $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3); + $this->_base256_lshift($leading_ones, $current_bits); + + $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT); + + return $this->_normalize(new static($leading_ones | $temp, 256)); + } + + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. + * + * @param int $shift + * @return \phpseclib\Math\BigInteger + * @access public + * @internal The only version that yields any speed increases is the internal version. + */ + function bitwise_rightShift($shift) + { + $temp = new static(); + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + static $two; + + if (!isset($two)) { + $two = gmp_init('2'); + } + + $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift)); + + break; + case self::MODE_BCMATH: + $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0); + + break; + default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten + // and I don't want to do that... + $temp->value = $this->value; + $temp->_rshift($shift); + } + + return $this->_normalize($temp); + } + + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. + * + * @param int $shift + * @return \phpseclib\Math\BigInteger + * @access public + * @internal The only version that yields any speed increases is the internal version. + */ + function bitwise_leftShift($shift) + { + $temp = new static(); + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + static $two; + + if (!isset($two)) { + $two = gmp_init('2'); + } + + $temp->value = gmp_mul($this->value, gmp_pow($two, $shift)); + + break; + case self::MODE_BCMATH: + $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0); + + break; + default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten + // and I don't want to do that... + $temp->value = $this->value; + $temp->_lshift($shift); + } + + return $this->_normalize($temp); + } + + /** + * Logical Left Rotate + * + * Instead of the top x bits being dropped they're appended to the shifted bit string. + * + * @param int $shift + * @return \phpseclib\Math\BigInteger + * @access public + */ + function bitwise_leftRotate($shift) + { + $bits = $this->toBytes(); + + if ($this->precision > 0) { + $precision = $this->precision; + if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { + $mask = $this->bitmask->subtract(new static(1)); + $mask = $mask->toBytes(); + } else { + $mask = $this->bitmask->toBytes(); + } + } else { + $temp = ord($bits[0]); + for ($i = 0; $temp >> $i; ++$i) { + } + $precision = 8 * strlen($bits) - 8 + $i; + $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3); + } + + if ($shift < 0) { + $shift+= $precision; + } + $shift%= $precision; + + if (!$shift) { + return $this->copy(); + } + + $left = $this->bitwise_leftShift($shift); + $left = $left->bitwise_and(new static($mask, 256)); + $right = $this->bitwise_rightShift($precision - $shift); + $result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right); + return $this->_normalize($result); + } + + /** + * Logical Right Rotate + * + * Instead of the bottom x bits being dropped they're prepended to the shifted bit string. + * + * @param int $shift + * @return \phpseclib\Math\BigInteger + * @access public + */ + function bitwise_rightRotate($shift) + { + return $this->bitwise_leftRotate(-$shift); + } + + /** + * Generates a random BigInteger + * + * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not. + * + * @param int $length + * @return \phpseclib\Math\BigInteger + * @access private + */ + function _random_number_helper($size) + { + if (class_exists('\phpseclib\Crypt\Random')) { + $random = Random::string($size); + } else { + $random = ''; + + if ($size & 1) { + $random.= chr(mt_rand(0, 255)); + } + + $blocks = $size >> 1; + for ($i = 0; $i < $blocks; ++$i) { + // mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems + $random.= pack('n', mt_rand(0, 0xFFFF)); + } + } + + return new static($random, 256); + } + + /** + * Generate a random number + * + * Returns a random number between $min and $max where $min and $max + * can be defined using one of the two methods: + * + * $min->random($max) + * $max->random($min) + * + * @param \phpseclib\Math\BigInteger $arg1 + * @param \phpseclib\Math\BigInteger $arg2 + * @return \phpseclib\Math\BigInteger + * @access public + * @internal The API for creating random numbers used to be $a->random($min, $max), where $a was a BigInteger object. + * That method is still supported for BC purposes. + */ + function random($arg1, $arg2 = false) + { + if ($arg1 === false) { + return false; + } + + if ($arg2 === false) { + $max = $arg1; + $min = $this; + } else { + $min = $arg1; + $max = $arg2; + } + + $compare = $max->compare($min); + + if (!$compare) { + return $this->_normalize($min); + } elseif ($compare < 0) { + // if $min is bigger then $max, swap $min and $max + $temp = $max; + $max = $min; + $min = $temp; + } + + static $one; + if (!isset($one)) { + $one = new static(1); + } + + $max = $max->subtract($min->subtract($one)); + $size = strlen(ltrim($max->toBytes(), chr(0))); + + /* + doing $random % $max doesn't work because some numbers will be more likely to occur than others. + eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145 + would produce 5 whereas the only value of random that could produce 139 would be 139. ie. + not all numbers would be equally likely. some would be more likely than others. + + creating a whole new random number until you find one that is within the range doesn't work + because, for sufficiently small ranges, the likelihood that you'd get a number within that range + would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability + would be pretty high that $random would be greater than $max. + + phpseclib works around this using the technique described here: + + http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string + */ + $random_max = new static(chr(1) . str_repeat("\0", $size), 256); + $random = $this->_random_number_helper($size); + + list($max_multiple) = $random_max->divide($max); + $max_multiple = $max_multiple->multiply($max); + + while ($random->compare($max_multiple) >= 0) { + $random = $random->subtract($max_multiple); + $random_max = $random_max->subtract($max_multiple); + $random = $random->bitwise_leftShift(8); + $random = $random->add($this->_random_number_helper(1)); + $random_max = $random_max->bitwise_leftShift(8); + list($max_multiple) = $random_max->divide($max); + $max_multiple = $max_multiple->multiply($max); + } + list(, $random) = $random->divide($max); + + return $this->_normalize($random->add($min)); + } + + /** + * Generate a random prime number. + * + * If there's not a prime within the given range, false will be returned. + * If more than $timeout seconds have elapsed, give up and return false. + * + * @param \phpseclib\Math\BigInteger $arg1 + * @param \phpseclib\Math\BigInteger $arg2 + * @param int $timeout + * @return Math_BigInteger|false + * @access public + * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}. + */ + function randomPrime($arg1, $arg2 = false, $timeout = false) + { + if ($arg1 === false) { + return false; + } + + if ($arg2 === false) { + $max = $arg1; + $min = $this; + } else { + $min = $arg1; + $max = $arg2; + } + + $compare = $max->compare($min); + + if (!$compare) { + return $min->isPrime() ? $min : false; + } elseif ($compare < 0) { + // if $min is bigger then $max, swap $min and $max + $temp = $max; + $max = $min; + $min = $temp; + } + + static $one, $two; + if (!isset($one)) { + $one = new static(1); + $two = new static(2); + } + + $start = time(); + + $x = $this->random($min, $max); + + // gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>. + if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) { + $p = new static(); + $p->value = gmp_nextprime($x->value); + + if ($p->compare($max) <= 0) { + return $p; + } + + if (!$min->equals($x)) { + $x = $x->subtract($one); + } + + return $x->randomPrime($min, $x); + } + + if ($x->equals($two)) { + return $x; + } + + $x->_make_odd(); + if ($x->compare($max) > 0) { + // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range + if ($min->equals($max)) { + return false; + } + $x = $min->copy(); + $x->_make_odd(); + } + + $initial_x = $x->copy(); + + while (true) { + if ($timeout !== false && time() - $start > $timeout) { + return false; + } + + if ($x->isPrime()) { + return $x; + } + + $x = $x->add($two); + + if ($x->compare($max) > 0) { + $x = $min->copy(); + if ($x->equals($two)) { + return $x; + } + $x->_make_odd(); + } + + if ($x->equals($initial_x)) { + return false; + } + } + } + + /** + * Make the current number odd + * + * If the current number is odd it'll be unchanged. If it's even, one will be added to it. + * + * @see self::randomPrime() + * @access private + */ + function _make_odd() + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + gmp_setbit($this->value, 0); + break; + case self::MODE_BCMATH: + if ($this->value[strlen($this->value) - 1] % 2 == 0) { + $this->value = bcadd($this->value, '1'); + } + break; + default: + $this->value[0] |= 1; + } + } + + /** + * Checks a numer to see if it's prime + * + * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the + * $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads + * on a website instead of just one. + * + * @param \phpseclib\Math\BigInteger $t + * @return bool + * @access public + * @internal Uses the + * {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}. + */ + function isPrime($t = false) + { + $length = strlen($this->toBytes()); + + if (!$t) { + // see HAC 4.49 "Note (controlling the error probability)" + // @codingStandardsIgnoreStart + if ($length >= 163) { $t = 2; } // floor(1300 / 8) + else if ($length >= 106) { $t = 3; } // floor( 850 / 8) + else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8) + else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8) + else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8) + else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8) + else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8) + else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8) + else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8) + else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8) + else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8) + else { $t = 27; } + // @codingStandardsIgnoreEnd + } + + // ie. gmp_testbit($this, 0) + // ie. isEven() or !isOdd() + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + return gmp_prob_prime($this->value, $t) != 0; + case self::MODE_BCMATH: + if ($this->value === '2') { + return true; + } + if ($this->value[strlen($this->value) - 1] % 2 == 0) { + return false; + } + break; + default: + if ($this->value == array(2)) { + return true; + } + if (~$this->value[0] & 1) { + return false; + } + } + + static $primes, $zero, $one, $two; + + if (!isset($primes)) { + $primes = array( + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997 + ); + + if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { + for ($i = 0; $i < count($primes); ++$i) { + $primes[$i] = new static($primes[$i]); + } + } + + $zero = new static(); + $one = new static(1); + $two = new static(2); + } + + if ($this->equals($one)) { + return false; + } + + // see HAC 4.4.1 "Random search for probable primes" + if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { + foreach ($primes as $prime) { + list(, $r) = $this->divide($prime); + if ($r->equals($zero)) { + return $this->equals($prime); + } + } + } else { + $value = $this->value; + foreach ($primes as $prime) { + list(, $r) = $this->_divide_digit($value, $prime); + if (!$r) { + return count($value) == 1 && $value[0] == $prime; + } + } + } + + $n = $this->copy(); + $n_1 = $n->subtract($one); + $n_2 = $n->subtract($two); + + $r = $n_1->copy(); + $r_value = $r->value; + // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); + if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { + $s = 0; + // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier + while ($r->value[strlen($r->value) - 1] % 2 == 0) { + $r->value = bcdiv($r->value, '2', 0); + ++$s; + } + } else { + for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) { + $temp = ~$r_value[$i] & 0xFFFFFF; + for ($j = 1; ($temp >> $j) & 1; ++$j) { + } + if ($j != 25) { + break; + } + } + $s = 26 * $i + $j; + $r->_rshift($s); + } + + for ($i = 0; $i < $t; ++$i) { + $a = $this->random($two, $n_2); + $y = $a->modPow($r, $n); + + if (!$y->equals($one) && !$y->equals($n_1)) { + for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) { + $y = $y->modPow($two, $n); + if ($y->equals($one)) { + return false; + } + } + + if (!$y->equals($n_1)) { + return false; + } + } + } + return true; + } + + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits. + * + * @param int $shift + * @access private + */ + function _lshift($shift) + { + if ($shift == 0) { + return; + } + + $num_digits = (int) ($shift / self::$base); + $shift %= self::$base; + $shift = 1 << $shift; + + $carry = 0; + + for ($i = 0; $i < count($this->value); ++$i) { + $temp = $this->value[$i] * $shift + $carry; + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $this->value[$i] = (int) ($temp - $carry * self::$baseFull); + } + + if ($carry) { + $this->value[count($this->value)] = $carry; + } + + while ($num_digits--) { + array_unshift($this->value, 0); + } + } + + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits. + * + * @param int $shift + * @access private + */ + function _rshift($shift) + { + if ($shift == 0) { + return; + } + + $num_digits = (int) ($shift / self::$base); + $shift %= self::$base; + $carry_shift = self::$base - $shift; + $carry_mask = (1 << $shift) - 1; + + if ($num_digits) { + $this->value = array_slice($this->value, $num_digits); + } + + $carry = 0; + + for ($i = count($this->value) - 1; $i >= 0; --$i) { + $temp = $this->value[$i] >> $shift | $carry; + $carry = ($this->value[$i] & $carry_mask) << $carry_shift; + $this->value[$i] = $temp; + } + + $this->value = $this->_trim($this->value); + } + + /** + * Normalize + * + * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision + * + * @param \phpseclib\Math\BigInteger + * @return \phpseclib\Math\BigInteger + * @see self::_trim() + * @access private + */ + function _normalize($result) + { + $result->precision = $this->precision; + $result->bitmask = $this->bitmask; + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + if ($this->bitmask !== false) { + $flip = gmp_cmp($result->value, gmp_init(0)) < 0; + if ($flip) { + $result->value = gmp_neg($result->value); + } + $result->value = gmp_and($result->value, $result->bitmask->value); + if ($flip) { + $result->value = gmp_neg($result->value); + } + } + + return $result; + case self::MODE_BCMATH: + if (!empty($result->bitmask->value)) { + $result->value = bcmod($result->value, $result->bitmask->value); + } + + return $result; + } + + $value = &$result->value; + + if (!count($value)) { + $result->is_negative = false; + return $result; + } + + $value = $this->_trim($value); + + if (!empty($result->bitmask->value)) { + $length = min(count($value), count($this->bitmask->value)); + $value = array_slice($value, 0, $length); + + for ($i = 0; $i < $length; ++$i) { + $value[$i] = $value[$i] & $this->bitmask->value[$i]; + } + } + + return $result; + } + + /** + * Trim + * + * Removes leading zeros + * + * @param array $value + * @return \phpseclib\Math\BigInteger + * @access private + */ + function _trim($value) + { + for ($i = count($value) - 1; $i >= 0; --$i) { + if ($value[$i]) { + break; + } + unset($value[$i]); + } + + return $value; + } + + /** + * Array Repeat + * + * @param $input Array + * @param $multiplier mixed + * @return array + * @access private + */ + function _array_repeat($input, $multiplier) + { + return ($multiplier) ? array_fill(0, $multiplier, $input) : array(); + } + + /** + * Logical Left Shift + * + * Shifts binary strings $shift bits, essentially multiplying by 2**$shift. + * + * @param $x String + * @param $shift Integer + * @return string + * @access private + */ + function _base256_lshift(&$x, $shift) + { + if ($shift == 0) { + return; + } + + $num_bytes = $shift >> 3; // eg. floor($shift/8) + $shift &= 7; // eg. $shift % 8 + + $carry = 0; + for ($i = strlen($x) - 1; $i >= 0; --$i) { + $temp = ord($x[$i]) << $shift | $carry; + $x[$i] = chr($temp); + $carry = $temp >> 8; + } + $carry = ($carry != 0) ? chr($carry) : ''; + $x = $carry . $x . str_repeat(chr(0), $num_bytes); + } + + /** + * Logical Right Shift + * + * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder. + * + * @param $x String + * @param $shift Integer + * @return string + * @access private + */ + function _base256_rshift(&$x, $shift) + { + if ($shift == 0) { + $x = ltrim($x, chr(0)); + return ''; + } + + $num_bytes = $shift >> 3; // eg. floor($shift/8) + $shift &= 7; // eg. $shift % 8 + + $remainder = ''; + if ($num_bytes) { + $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes; + $remainder = substr($x, $start); + $x = substr($x, 0, -$num_bytes); + } + + $carry = 0; + $carry_shift = 8 - $shift; + for ($i = 0; $i < strlen($x); ++$i) { + $temp = (ord($x[$i]) >> $shift) | $carry; + $carry = (ord($x[$i]) << $carry_shift) & 0xFF; + $x[$i] = chr($temp); + } + $x = ltrim($x, chr(0)); + + $remainder = chr($carry >> $carry_shift) . $remainder; + + return ltrim($remainder, chr(0)); + } + + // one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long + // at 32-bits, while java's longs are 64-bits. + + /** + * Converts 32-bit integers to bytes. + * + * @param int $x + * @return string + * @access private + */ + function _int2bytes($x) + { + return ltrim(pack('N', $x), chr(0)); + } + + /** + * Converts bytes to 32-bit integers + * + * @param string $x + * @return int + * @access private + */ + function _bytes2int($x) + { + $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT)); + return $temp['int']; + } + + /** + * DER-encode an integer + * + * The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL + * + * @see self::modPow() + * @access private + * @param int $length + * @return string + */ + function _encodeASN1Length($length) + { + if ($length <= 0x7F) { + return chr($length); + } + + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } + + /** + * Single digit division + * + * Even if int64 is being used the division operator will return a float64 value + * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't + * have the precision of int64 this is a problem so, when int64 is being used, + * we'll guarantee that the dividend is divisible by first subtracting the remainder. + * + * @access private + * @param int $x + * @param int $y + * @return int + */ + function _safe_divide($x, $y) + { + if (self::$base === 26) { + return (int) ($x / $y); + } + + // self::$base === 31 + return ($x - ($x % $y)) / $y; + } +} diff --git a/modules/phpseclib/README.md b/modules/phpseclib/README.md @@ -0,0 +1,86 @@ +# phpseclib - PHP Secure Communications Library + +[![Build Status](https://travis-ci.org/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.org/phpseclib/phpseclib) + +## Supporting phpseclib + +- [Become a backer or sponsor on Patreon](https://www.patreon.com/phpseclib) +- [One-time donation via PayPal or crypto-currencies](http://sourceforge.net/donate/index.php?group_id=198487) +- [Subscribe to Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme) + +## Introduction + +MIT-licensed pure-PHP implementations of an arbitrary-precision integer +arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael, +AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509 + +* [Browse Git](https://github.com/phpseclib/phpseclib) + +## Documentation + +* [Documentation / Manual](http://phpseclib.sourceforge.net/) +* [API Documentation](https://api.phpseclib.org/2.0/) (generated by Doctum) + +## Branches + +### master + +* Development Branch +* Unstable API +* Do not use in production + +### 2.0 + +* Long term support (LTS) release +* Modernized version of 1.0 +* Minimum PHP version: 5.3.3 +* PSR-4 autoloading with namespace rooted at `\phpseclib` +* Install via Composer: `composer require phpseclib/phpseclib:~2.0` + +### 1.0 + +* Long term support (LTS) release +* PHP4 compatible +* Composer compatible (PSR-0 autoloading) +* Install using Composer: `composer require phpseclib/phpseclib:~1.0` +* Install using PEAR: See [phpseclib PEAR Channel Documentation](http://phpseclib.sourceforge.net/pear.htm) +* [Download 1.0.19 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.19.zip/download) + +## Security contact information + +To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. + +## Support + +Need Support? + +* [Checkout Questions and Answers on Stack Overflow](http://stackoverflow.com/questions/tagged/phpseclib) +* [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new) +* [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use) + +## Contributing + +1. Fork the Project + +2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/)) + +3. Install Development Dependencies + + ``` sh + composer install + ``` + +4. Create a Feature Branch + +5. (Recommended) Run the Test Suite + + ``` sh + vendor/bin/phpunit + ``` +6. (Recommended) Check whether your code conforms to our Coding Standards by running + + ``` sh + vendor/bin/phing -f build/build.xml sniff + ``` + +7. Send us a Pull Request diff --git a/modules/template_engine/engine/TemplateEngine.class.php b/modules/template_engine/engine/TemplateEngine.class.php @@ -172,7 +172,7 @@ class TemplateEngine $component->init(); foreach( $element->childNodes as $child ) { - $component->addChildComponent( $this->processElement( $child,$depth ) ); + $component->addChildComponent( $this->processElement( $child,$depth+1 ) ); } return $component; } @@ -195,7 +195,7 @@ class TemplateEngine $component->init(); foreach( $element->childNodes as $child ) { - $component->addChildComponent( $this->processElement( $child,$depth ) ); + $component->addChildComponent( $this->processElement( $child,$depth+1 ) ); } return $component;