1<?php 2 3/** 4 * DirectAdmin Password Driver 5 * 6 * Driver to change passwords via DirectAdmin Control Panel 7 * 8 * @version 2.2 9 * @author Victor Benincasa <vbenincasa @ gmail.com> 10 * 11 * Copyright (C) The Roundcube Dev Team 12 * 13 * This program is free software: you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation, either version 3 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program. If not, see http://www.gnu.org/licenses/. 25 */ 26 27class rcube_directadmin_password 28{ 29 public function save($curpass, $passwd) 30 { 31 $rcmail = rcmail::get_instance(); 32 $Socket = new HTTPSocket; 33 34 $da_user = $_SESSION['username']; 35 $da_curpass = $curpass; 36 $da_newpass = $passwd; 37 $da_host = $rcmail->config->get('password_directadmin_host'); 38 $da_port = $rcmail->config->get('password_directadmin_port'); 39 40 if (strpos($da_user, '@') === false) { 41 return ['code' => PASSWORD_ERROR, 'message' => 'Change the SYSTEM user password through control panel!']; 42 } 43 44 $da_host = str_replace('%h', $_SESSION['imap_host'], $da_host); 45 $da_host = str_replace('%d', $rcmail->user->get_username('domain'), $da_host); 46 47 $Socket->connect($da_host,$da_port); 48 $Socket->set_method('POST'); 49 $Socket->query('/CMD_CHANGE_EMAIL_PASSWORD', [ 50 'email' => $da_user, 51 'oldpassword' => $da_curpass, 52 'password1' => $da_newpass, 53 'password2' => $da_newpass, 54 'api' => '1' 55 ]); 56 57 $response = $Socket->fetch_parsed_body(); 58 59 //DEBUG 60 //rcube::console("Password Plugin: [USER: $da_user] [HOST: $da_host] - Response: [SOCKET: ".$Socket->result_status_code."] [DA ERROR: ".strip_tags($response['error'])."] [TEXT: ".$response[text]."]"); 61 62 if ($Socket->result_status_code != 200) { 63 return ['code' => PASSWORD_CONNECT_ERROR, 'message' => $Socket->error[0]]; 64 } 65 66 if ($response['error'] == 1) { 67 return ['code' => PASSWORD_ERROR, 'message' => strip_tags($response['text'])]; 68 } 69 70 return PASSWORD_SUCCESS; 71 } 72} 73 74 75/** 76 * Socket communication class. 77 * 78 * Originally designed for use with DirectAdmin's API, this class will fill any HTTP socket need. 79 * 80 * Very, very basic usage: 81 * $Socket = new HTTPSocket; 82 * echo $Socket->get('http://user:pass@somesite.com/somedir/some.file?query=string&this=that'); 83 * 84 * @author Phi1 'l0rdphi1' Stier <l0rdphi1@liquenox.net> 85 * @package HTTPSocket 86 * @version 3.0.2 87 */ 88class HTTPSocket 89{ 90 var $version = '3.0.2'; 91 92 // all vars are private except $error, $query_cache, and $doFollowLocationHeader 93 94 var $method = 'GET'; 95 96 var $remote_host; 97 var $remote_port; 98 var $remote_uname; 99 var $remote_passwd; 100 101 var $result; 102 var $result_header; 103 var $result_body; 104 var $result_status_code; 105 106 var $lastTransferSpeed; 107 var $bind_host; 108 var $error = []; 109 var $warn = []; 110 var $query_cache = []; 111 var $doFollowLocationHeader = true; 112 var $redirectURL; 113 var $max_redirects = 5; 114 var $ssl_setting_message = 'DirectAdmin appears to be using SSL. Change your script to connect to ssl://'; 115 var $extra_headers = []; 116 117 /** 118 * Create server "connection". 119 * 120 */ 121 function connect($host, $port = '') 122 { 123 if (!is_numeric($port)) { 124 $port = 2222; 125 } 126 127 $this->remote_host = $host; 128 $this->remote_port = $port; 129 } 130 131 function bind($ip = '') 132 { 133 if ($ip == '') { 134 $ip = $_SERVER['SERVER_ADDR']; 135 } 136 137 $this->bind_host = $ip; 138 } 139 140 /** 141 * Change the method being used to communicate. 142 * 143 * @param string|null request method. supports GET, POST, and HEAD. default is GET 144 */ 145 function set_method($method = 'GET') 146 { 147 $this->method = strtoupper($method); 148 } 149 150 /** 151 * Specify a username and password. 152 * 153 * @param string|null username. default is null 154 * @param string|null password. default is null 155 */ 156 function set_login($uname = '', $passwd = '') 157 { 158 if (strlen($uname) > 0) { 159 $this->remote_uname = $uname; 160 } 161 162 if (strlen($passwd) > 0) { 163 $this->remote_passwd = $passwd; 164 } 165 } 166 167 /** 168 * Query the server 169 * 170 * @param string containing properly formatted server API. See DA API docs and examples. Http:// URLs O.K. too. 171 * @param string|array query to pass to url 172 */ 173 function query($request, $content = '') 174 { 175 $this->error = $this->warn = []; 176 $this->result_status_code = null; 177 178 $is_ssl = false; 179 180 // is our request a http:// ... ? 181 if (preg_match('!^http://!i',$request) || preg_match('!^https://!i',$request)) { 182 $location = parse_url($request); 183 if (preg_match('!^https://!i',$request)) { 184 $this->connect('https://'.$location['host'],$location['port']); 185 } 186 else { 187 $this->connect('http://'.$location['host'],$location['port']); 188 } 189 190 $this->set_login($location['user'], $location['pass']); 191 192 $request = $location['path']; 193 $content = $location['query']; 194 195 if (strlen($request) < 1) { 196 $request = '/'; 197 } 198 } 199 200 if (preg_match('!^ssl://!i', $this->remote_host)) { 201 $this->remote_host = 'https://'.substr($this->remote_host, 6); 202 } 203 204 if (preg_match('!^tcp://!i', $this->remote_host)) { 205 $this->remote_host = 'http://'.substr($this->remote_host, 6); 206 } 207 208 if (preg_match('!^https://!i', $this->remote_host)) { 209 $is_ssl = true; 210 } 211 212 $array_headers = [ 213 'Host' => $this->remote_port == 80 ? $this->remote_host : "$this->remote_host:$this->remote_port", 214 'Accept' => '*/*', 215 'Connection' => 'Close' 216 ]; 217 218 foreach ($this->extra_headers as $key => $value) { 219 $array_headers[$key] = $value; 220 } 221 222 $this->result = $this->result_header = $this->result_body = ''; 223 224 // was content sent as an array? if so, turn it into a string 225 if (is_array($content)) { 226 $pairs = []; 227 228 foreach ($content as $key => $value) { 229 $pairs[] = "$key=".urlencode($value); 230 } 231 232 $content = join('&',$pairs); 233 unset($pairs); 234 } 235 236 $OK = true; 237 238 if ($this->method == 'GET') { 239 $request .= '?'.$content; 240 } 241 242 $ch = curl_init($this->remote_host.':'.$this->remote_port.$request); 243 244 if ($is_ssl) { 245 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //1 246 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //2 247 //curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); 248 } 249 250 curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 251 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); 252 curl_setopt($ch, CURLOPT_USERAGENT, "HTTPSocket/$this->version"); 253 curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); 254 curl_setopt($ch, CURLOPT_TIMEOUT, 100); 255 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); 256 curl_setopt($ch, CURLOPT_HEADER, 1); 257 258 curl_setopt($ch, CURLOPT_LOW_SPEED_LIMIT, 512); 259 curl_setopt($ch, CURLOPT_LOW_SPEED_TIME, 120); 260 261 // instance connection 262 if ($this->bind_host) { 263 curl_setopt($ch, CURLOPT_INTERFACE, $this->bind_host); 264 } 265 266 // if we have a username and password, add the header 267 if (isset($this->remote_uname) && isset($this->remote_passwd)) { 268 curl_setopt($ch, CURLOPT_USERPWD, $this->remote_uname.':'.$this->remote_passwd); 269 } 270 271 // for DA skins: if $this->remote_passwd is NULL, try to use the login key system 272 if (isset($this->remote_uname) && $this->remote_passwd == NULL) { 273 $array_headers['Cookie'] = "session={$_SERVER['SESSION_ID']}; key={$_SERVER['SESSION_KEY']}"; 274 } 275 276 // if method is POST, add content length & type headers 277 if ($this->method == 'POST') { 278 curl_setopt($ch, CURLOPT_POST, 1); 279 curl_setopt($ch, CURLOPT_POSTFIELDS, $content); 280 281 //$array_headers['Content-type'] = 'application/x-www-form-urlencoded'; 282 $array_headers['Content-length'] = strlen($content); 283 } 284 285 curl_setopt($ch, CURLOPT_HTTPHEADER, $array_headers); 286 287 if (!($this->result = curl_exec($ch))) { 288 $this->error[] = curl_error($ch); 289 $OK = false; 290 } 291 292 $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); 293 $this->result_header = substr($this->result, 0, $header_size); 294 $this->result_body = substr($this->result, $header_size); 295 $this->result_status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 296 $this->lastTransferSpeed = curl_getinfo($ch, CURLINFO_SPEED_DOWNLOAD) / 1024; 297 298 curl_close($ch); 299 300 $this->query_cache[] = $this->remote_host.':'.$this->remote_port.$request; 301 302 $headers = $this->fetch_header(); 303 304 // did we get the full file? 305 if (!empty($headers['content-length']) && $headers['content-length'] != strlen($this->result_body)) { 306 $this->result_status_code = 206; 307 } 308 309 // now, if we're being passed a location header, should we follow it? 310 if ($this->doFollowLocationHeader) { 311 //dont bother if we didn't even setup the script correctly 312 if (isset($headers['x-use-https']) && $headers['x-use-https'] == 'yes') { 313 die($this->ssl_setting_message); 314 } 315 316 if (isset($headers['location'])) { 317 if ($this->max_redirects <= 0) { 318 die("Too many redirects on: ".$headers['location']); 319 } 320 321 $this->max_redirects--; 322 $this->redirectURL = $headers['location']; 323 $this->query($headers['location']); 324 } 325 } 326 } 327 328 function getTransferSpeed() 329 { 330 return $this->lastTransferSpeed; 331 } 332 333 /** 334 * The quick way to get a URL's content :) 335 * 336 * @param string $location URL 337 * @param bool $asArray return as array? (like PHP's file() command) 338 * 339 * @return string result body 340 */ 341 function get($location, $asArray = false) 342 { 343 $this->query($location); 344 345 if ($this->get_status_code() == 200) { 346 if ($asArray) { 347 return preg_split("/\n/", $this->fetch_body()); 348 } 349 350 return $this->fetch_body(); 351 } 352 353 return false; 354 } 355 356 /** 357 * Returns the last status code. 358 * 200 = OK; 359 * 403 = FORBIDDEN; 360 * etc. 361 * 362 * @return int status code 363 */ 364 function get_status_code() 365 { 366 return $this->result_status_code; 367 } 368 369 /** 370 * Adds a header, sent with the next query. 371 * 372 * @param string header name 373 * @param string header value 374 */ 375 function add_header($key, $value) 376 { 377 $this->extra_headers[$key] = $value; 378 } 379 380 /** 381 * Clears any extra headers. 382 * 383 */ 384 function clear_headers() 385 { 386 $this->extra_headers = []; 387 } 388 389 /** 390 * Return the result of a query. 391 * 392 * @return string result 393 */ 394 function fetch_result() 395 { 396 return $this->result; 397 } 398 399 /** 400 * Return the header of result (stuff before body). 401 * 402 * @param string (optional) header to return 403 * @return array result header 404 */ 405 function fetch_header($header = '') 406 { 407 $array_headers = preg_split("/\r\n/", $this->result_header); 408 409 $array_return = [0 => $array_headers[0]]; 410 unset($array_headers[0]); 411 412 foreach ($array_headers as $pair) { 413 if ($pair == '' || $pair == "\r\n") continue; 414 list($key,$value) = preg_split("/: /", $pair, 2); 415 $array_return[strtolower($key)] = $value; 416 } 417 418 if ($header != '') { 419 return $array_return[strtolower($header)]; 420 } 421 422 return $array_return; 423 } 424 425 /** 426 * Return the body of result (stuff after header). 427 * 428 * @return string result body 429 */ 430 function fetch_body() 431 { 432 return $this->result_body; 433 } 434 435 /** 436 * Return parsed body in array format. 437 * 438 * @return array result parsed 439 */ 440 function fetch_parsed_body() 441 { 442 parse_str($this->result_body, $x); 443 return $x; 444 } 445 446 /** 447 * Set a specific message on how to change the SSL setting, in the event that it's not set correctly. 448 */ 449 function set_ssl_setting_message($str) 450 { 451 $this->ssl_setting_message = $str; 452 } 453} 454