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