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