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}