1<?php 2class helper_plugin_twofactorsmsgateway extends Twofactor_Auth_Module { 3 /** 4 * If the user has a valid email address in their profile, then this can be used. 5 */ 6 public function canUse($user = null){ 7 return ($this->_settingExists("verified", $user) && $this->getConf('enable') === 1); 8 } 9 10 /** 11 * This module can not provide authentication functionality at the main login screen. 12 */ 13 public function canAuthLogin() { 14 return false; 15 } 16 17 /** 18 * This user will need to supply a phone number and their cell provider. 19 */ 20 public function renderProfileForm(){ 21 $elements = array(); 22 // Provide an input for the phone number. 23 $phone = $this->_settingGet("phone", ''); 24 # This is to move the phone number from shared settings into this 25 # module if not already present. 26 if (!$phone) { 27 $phone = $this->_sharedSettingGet('phone',''); 28 if ($phone) { 29 $this->_settingSet('phone', $phone); 30 $this->attribute->del('twofactor', 'phone'); 31 } 32 } 33 34 $elements['phone'] = form_makeTextField('phone', $phone, $this->getLang('phone'), '', 'block', array('size'=>'50')); 35 $providers = array_keys($this->_getProviders()); 36 $provider = $this->_settingGet("provider",$providers[0]); 37 $elements[] = form_makeListboxField('smsgateway_provider', $providers, $provider, $this->getLang('provider'), '', 'block'); 38 39 // If the phone number has not been verified, then do so here. 40 if ($phone) { 41 if (!$this->_settingExists("verified")) { 42 // Render the HTML to prompt for the verification/activation OTP. 43 $elements[] = '<span>'.$this->getLang('verifynotice').'</span>'; 44 $elements[] = form_makeTextField('smsgateway_verify', '', $this->getLang('verifymodule'), '', 'block', array('size'=>'50', 'autocomplete'=>'off')); 45 $elements[] = form_makeCheckboxField('smsgateway_send', '1', $this->getLang('resendcode'),'','block'); 46 } 47 // Render the element to remove the phone since it exists. 48 $elements[] = form_makeCheckboxField('smsgateway_disable', '1', $this->getLang('killmodule'), '', 'block'); 49 } 50 return $elements; 51 } 52 53 /** 54 * Process any user configuration. 55 */ 56 public function processProfileForm(){ 57 global $INPUT; 58 if ($INPUT->bool('smsgateway_disable', false)) { 59 $this->_settingDelete("phone"); 60 $this->_settingDelete("provider"); 61 // Also delete the verified setting. Otherwise the system will still expect the user to login with OTP. 62 $this->_settingDelete("verified"); 63 return true; 64 } 65 $oldphone = $this->_settingGet("phone", ''); 66 if ($oldphone) { 67 if ($INPUT->bool('smsgateway_send', false)) { 68 return 'otp'; 69 } 70 $otp = $INPUT->str('smsgateway_verify', ''); 71 if ($otp !== '') { // The user will use SMS. 72 $checkResult = $this->processLogin($otp); 73 // If the code works, then flag this account to use SMS Gateway. 74 if ($checkResult == false) { 75 return 'failed'; 76 } 77 else { 78 $this->_settingSet("verified", true); 79 return 'verified'; 80 } 81 } 82 } 83 84 $changed = null; 85 $phone = $INPUT->str('phone', ''); 86 if ($phone !== '') { 87 if (preg_match('/^[0-9]{5,}$/',$phone) != false) { 88 if ($phone != $oldphone) { 89 if ($this->_settingSet("phone", $phone)== false) { 90 msg("TwoFactor: Error setting phone.", -1); 91 } 92 // Delete the verification for the phone number if it was changed. 93 $this->_settingDelete("verified"); 94 $changed = true; 95 } 96 } 97 else { 98 msg($this->getLang('invalidnumber'), -1); 99 } 100 } 101 $oldprovider = $this->_settingGet("provider", ''); 102 $provider = $INPUT->str('smsgateway_provider', ''); 103 if ($provider != $oldprovider && $provider !== '') { 104 if ($this->_settingSet("provider", $provider)== false) { 105 msg("TwoFactor: Error setting provider.", -1); 106 } 107 // Delete the verification for the phone number if the carrier was changed. 108 $this->_settingDelete("verified"); 109 $changed = true; 110 } 111 112 // If the data changed and we have everything needed to use this module, send an otp. 113 if (!is_null($changed) && $this->_settingGet("provider", '') != '') { 114 $changed = 'otp'; 115 } 116 return $changed; 117 } 118 119 /** 120 * This module can send messages. 121 */ 122 public function canTransmitMessage(){ 123 return true; 124 } 125 126 /** 127 * Transmit the message via email to the address on file. 128 * As a special case, configure the mail settings to send only via text. 129 */ 130 public function transmitMessage($subject, $message, $force = false){ 131 if (!$this->canUse() && !$force) { return false; } 132 global $USERINFO, $conf; 133 $phone = $this->_settingGet("phone"); 134 # This is to move the phone number from shared settings into this 135 # module if not already present. 136 if (!$phone) { 137 $phone = $this->_sharedSettingGet('phone',''); 138 if ($phone) { 139 $this->_settingSet('phone', $phone); 140 $this->_sharedSettingDelete('phone'); 141 } 142 } 143 if (!$phone) { 144 msg("TwoFactor: User has not defined a phone number. Failing.", -1); 145 // If there is no phone number, then fail. 146 return false; 147 } 148 $gateway = $this->_settingGet("provider"); 149 150 $providers = $this->_getProviders(); 151 if (array_key_exists($gateway, $providers)) { 152 $to = "{$phone}@{$providers[$gateway]}"; 153 } 154 else { 155 $to = ''; 156 } 157 if (!$to) { 158 msg($this->getLang('invalidprovider'), -1); 159 // If there is no recipient address, then fail. 160 return false; 161 } 162 // Create the email object. 163 $mail = new Mailer(); 164 $mail->to($to); 165 $mail->subject($subject); 166 $mail->setText($message); 167 $mail->setHTML(''); // No HTML to the SMS gateway, please! 168 $result = $mail->send(); 169 return $result; 170 } 171 172 /** 173 * This module uses the default authentication. 174 */ 175 //public function processLogin($code); 176 177 178 /** 179 * Produce an array of SMS gateway email domains with the keys as the 180 * cellular providers. Reads the gateway.txt and gateway.override 181 * (if present) files to generate the list. 182 * Create the gateway.override file to add your own custom gateways, 183 * otherwise your changes will be lost on upgrade. 184 * @return array - keys are providers, values are the email domains used 185 * to email an SMS to a phone user. 186 */ 187 private function _getProviders() { 188 $filename = dirname(__FILE__).'/gateway.txt'; 189 $local_filename = dirname(__FILE__).'/gateway.override'; 190 $providers = array(); 191 $contents = explode("\n", io_readFile($filename)); 192 $local_contents = io_readFile($local_filename); 193 if ($local_contents) { 194 // The override file IS processed twice- first to make its entries 195 // appear at the top, then again so they override any default 196 // values. 197 $contents = array_merge(explode("\n", $local_contents), $contents, explode("\n", $local_contents)); 198 } 199 foreach($contents as $line) { 200 if (strstr($line, '@')) { 201 list($provider, $domain) = explode("@", trim($line), 2); 202 $providers[$provider] = $domain; 203 } 204 } 205 return $providers; 206 } 207}