You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

directadmin.php 13KB

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