Code

dded a script to generate the pdf from the online manual
[gosa.git] / include / class_sieve.inc
1 <?
3 /*
4  * $Id: class_sieve.inc,v 1.1 2005/02/21 09:33:01 cajus Exp $ 
5  *
6  * Copyright 2001 Dan Ellis <danellis@rushmore.com>
7  *
8  * See the enclosed file COPYING for license information (GPL).  If you
9  * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
10  */
12 // TODO before next release:    remove ::status() and dependencies
15 define ("F_NO", 0);             
16 define ("F_OK", 1);
17 define ("F_DATA", 2);
18 define ("F_HEAD", 3);
20 define ("EC_NOT_LOGGED_IN", 0);
21 define ("EC_QUOTA", 10);
22 define ("EC_NOSCRIPTS", 20);
23 define ("EC_UNKNOWN", 255);
24 /*
26 SIEVE-PHP.LIB VERSION 0.0.8
28 (C) 2001 Dan Ellis.
30 PLEASE READ THE README FILE FOR MORE INFORMATION.
32 Basically, this is the first re-release.  Things are much better than before.
34 Notes:
35 This program/libary has bugs.
36         .       This was quickly hacked out, so please let me know what is wrong and if you feel ambitious submit
37                 a patch :).
39 Todo:
40         .       Provide better error diagnostics.                       (mostly done with ver 0.0.5)
41         .       Allow other auth mechanisms besides plain               (in progress)
42         .       Have timing mechanism when port problems arise.         (not done yet)
43         .       Maybe add the NOOP function.                            (not done yet)
44         .       Other top secret stuff....                              (some done, believe me?)
46 Dan Ellis (danellis@rushmore.com)
48 This program is released under the GNU Public License.
50 You should have received a copy of the GNU Public
51  License along with this package; if not, write to the
52  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
53  Boston, MA 02111-1307, USA.        
55 See CHANGES for updates since last release
57 Contributers of patches:
58         Atif Ghaffar
59         Andrew Sterling Hanenkamp <sterling@hanenkamp.com>
60 */
63 class sieve
64 {
65   var $host;
66   var $port;
67   var $user;
68   var $pass;
69   var $auth_types;              /* a comma seperated list of allowed auth types, in order of preference */
70   var $auth_in_use;             /* type of authentication attempted */
71   
72   var $line;
73   var $fp;
74   var $retval;
75   var $tmpfile;
76   var $fh;
77   var $len;
78   var $script;
80   var $loggedin;
81   var $capabilities;
82   var $error;
83   var $error_raw;
84   var $responses;
86   //maybe we should add an errorlvl that the user will pass to new sieve = sieve(,,,,E_WARN)
87   //so we can decide how to handle certain errors?!?
89   //also add a connection type, like PLAIN, MD5, etc...
92   function get_response()
93   {
94     if($this->loggedin == false or feof($this->fp)){
95         $this->error = EC_NOT_LOGGED_IN;
96         $this->error_raw = "You are not logged in.";
97         return false;
98     }
100     unset($this->response);
101     unset($this->error);
102     unset($this->error_raw);
104     $this->line=fgets($this->fp,1024);
105     $this->token = split(" ", $this->line, 2);
107     if($this->token[0] == "NO"){
108         /* we need to try and extract the error code from here.  There are two possibilites: one, that it will take the form of:
109            NO ("yyyyy") "zzzzzzz" or, two, NO {yyyyy} "zzzzzzzzzzz" */
110         $this->x = 0;
111         list($this->ltoken, $this->mtoken, $this->rtoken) = split(" ", $this->line." ", 3);
112         if($this->mtoken[0] == "{"){
113             while($this->mtoken[$this->x] != "}" or $this->err_len < 1){
114                 $this->err_len = substr($this->mtoken, 1, $this->x);
115                 $this->x++;    
116             }
117             //print "<br>Trying to receive $this->err_len bytes for result<br>";
118             $this->line = fgets($this->fp,$this->err_len);
119             $this->error_raw[]=substr($this->line, 0, strlen($this->line) -2);    //we want to be nice and strip crlf's
120             $this->err_recv = strlen($this->line);
122             /* Avoid loop till memory is full 
123                This can happen if sieve script is modified via external scripts and not by gosa .. */
124             $max = 10000;
125             $cur = 0 ; 
126             while($this->err_recv < $this->err_len && ($cur < $max)){
127                 //print "<br>Trying to receive ".($this->err_len-$this->err_recv)." bytes for result<br>";
128                 $cur ++ ; 
129                 $this->line = fgets($this->fp, ($this->err_len-$this->err_recv));
130                 $this->error_raw[]=substr($this->line, 0, strlen($this->line) -2);    //we want to be nice and strip crlf's
131                 $this->err_recv += strlen($this->line);
132             } /* end while */
133             $this->line = fgets($this->fp, 1024);       //we need to grab the last crlf, i think.  this may be a bug...
134             $this->error=EC_UNKNOWN;
135       
136         } /* end if */
137         elseif($this->mtoken[0] == "("){
138             switch($this->mtoken){
139                 case "(\"QUOTA\")":
140                     $this->error = EC_QUOTA;
141                     $this->error_raw=$this->rtoken;
142                     break;
143                 default:
144                     $this->error = EC_UNKNOWN;
145                     $this->error_raw=$this->rtoken;
146                     break;
147             } /* end switch */
148         } /* end elseif */
149         else{
150             $this->error = EC_UNKNOWN;
151             $this->error_raw = $this->line;
152         }     
153         return false;
155     } /* end if */
156     elseif(substr($this->token[0],0,-2) == "OK"){
157          return true;
158     } /* end elseif */
159     elseif($this->token[0][0] == "{"){
160         
161         /* Unable wild assumption:  that the only function that gets here is the get_script(), doesn't really matter though */       
163         /* the first line is the len field {xx}, which we don't care about at this point */
164         $this->line = fgets($this->fp,1024);
165         while(substr($this->line,0,2) != "OK" and substr($this->line,0,2) != "NO"){
166             $this->response[]=$this->line;
167             $this->line = fgets($this->fp, 1024);
168         }
169         if(substr($this->line,0,2) == "OK")
170             return true;
171         else
172             return false;
173     } /* end elseif */
174     elseif($this->token[0][0] == "\""){
176         /* I'm going under the _assumption_ that the only function that will get here is the listscripts().
177            I could very well be mistaken here, if I am, this part needs some rework */
179         $this->found_script=false;        
181         while(substr($this->line,0,2) != "OK" and substr($this->line,0,2) != "NO"){
182             $this->found_script=true;
183             list($this->ltoken, $this->rtoken) = explode(" ", $this->line." ",2);
184                 //hmmm, a bug in php, if there is no space on explode line, a warning is generated...
185            
186             if(strcmp(rtrim($this->rtoken), "ACTIVE")==0){
187                 $this->response["ACTIVE"] = substr(rtrim($this->ltoken),1,-1);  
188             }
189             else
190                 $this->response[] = substr(rtrim($this->ltoken),1,-1);
191             $this->line = fgets($this->fp, 1024);
192         } /* end while */
193         
194         return true;
195         
196     } /* end elseif */
197     else{
198             $this->error = EC_UNKNOWN;
199             $this->error_raw = $this->line;
200             print "<b><i>UNKNOWN ERROR (Please report this line to danellis@rushmore.com to include in future releases): $this->line</i></b><br>";
201             return false;
202     } /* end else */   
203   } /* end get_response() */
205   function sieve($host, $port, $user, $pass, $auth="", $auth_types="PLAIN DIGEST-MD5")
206   {
207     $this->host=$host;
208     $this->port=$port;
209     $this->user=$user;
210     $this->pass=$pass;
211     if(!strcmp($auth, ""))              /* If there is no auth user, we deem the user itself to be the auth'd user */
212         $this->auth = $this->user;
213     else
214         $this->auth = $auth;
215     $this->auth_types=$auth_types;      /* Allowed authentication types */
216     $this->fp=0;
217     $this->line="";
218     $this->retval="";
219     $this->tmpfile="";
220     $this->fh=0;
221     $this->len=0;
222     $this->capabilities="";
223     $this->loggedin=false;
224     $this->error= "";
225     $this->error_raw="";
226   }
228   function parse_for_quotes($string)
229   {
230       /* This function tokenizes a line of input by quote marks and returns them as an array */
232       $start = -1;
233       $index = 0;
235       for($ptr = 0; $ptr < strlen($string); $ptr++){
236           if($string[$ptr] == '"' and $string[$ptr] != '\\'){
237               if($start == -1){
238                   $start = $ptr;
239               } /* end if */
240               else{
241                   $token[$index++] = substr($string, $start + 1, $ptr - $start - 1);
242                   $found = true;
243                   $start = -1;
244               } /* end else */
246           } /* end if */  
248       } /* end for */
250       if(isset($token))
251           return $token;
252       else
253           return false;
254   } /* end function */            
256   function status($string)
257   {
258       //this should probably be replaced by a smarter parser.
260       /*  Need to remove this and all dependencies from the class */
262       switch (substr($string, 0,2)){
263           case "NO":
264               return F_NO;              //there should be some function to extract the error code from this line
265                                         //NO ("quota") "You are oly allowed x number of scripts"
266               break;
267           case "OK":
268               return F_OK;
269               break;
270           default:
271               switch ($string[0]){
272                   case "{":
273                       //do parse here for {}'s  maybe modify parse_for_quotes to handle any parse delimiter?
274                       return F_HEAD;
275                       break;
276                   default:
277                       return F_DATA;
278                       break;
279               } /* end switch */
280         } /* end switch */
281   } /* end status() */
283   function sieve_login()
284   {
286     $this->fp=fsockopen($this->host,$this->port);
287     if($this->fp == false)
288         return false;
289  
290     $this->line=fgets($this->fp,1024);
292     //Hack for older versions of Sieve Server.  They do not respond with the Cyrus v2. standard
293     //response.  They repsond as follows: "Cyrus timsieved v1.0.0" "SASL={PLAIN,........}"
294     //So, if we see IMLEMENTATION in the first line, then we are done.
296     if(ereg("IMPLEMENTATION",$this->line))
297     {
298       //we're on the Cyrus V2 sieve server
299       while(sieve::status($this->line) == F_DATA){
301           $this->item = sieve::parse_for_quotes($this->line);
303           if(strcmp($this->item[0], "IMPLEMENTATION") == 0)
304               $this->capabilities["implementation"] = $this->item[1];
305         
306           elseif(strcmp($this->item[0], "SIEVE") == 0 or strcmp($this->item[0], "SASL") == 0){
308               if(strcmp($this->item[0], "SIEVE") == 0)
309                   $this->cap_type="modules";
310               else
311                   $this->cap_type="auth";            
313               $this->modules = split(" ", $this->item[1]);
314               if(is_array($this->modules)){
315                   foreach($this->modules as $this->module)
316                       $this->capabilities[$this->cap_type][$this->module]=true;
317               } /* end if */
318               elseif(is_string($this->modules))
319                   $this->capabilites[$this->cap_type][$this->modules]=true;
320           }    
321           else{ 
322               $this->capabilities["unknown"][]=$this->line;
323           }    
324       $this->line=fgets($this->fp,1024);
326        }// end while
327     }
328     else
329     {
330         //we're on the older Cyrus V1. server  
331         //this version does not support module reporting.  We only have auth types.
332         $this->cap_type="auth";
333        
334         //break apart at the "Cyrus timsieve...." "SASL={......}"
335         $this->item = sieve::parse_for_quotes($this->line);
337         $this->capabilities["implementation"] = $this->item[0];
339         //we should have "SASL={..........}" now.  Break out the {xx,yyy,zzzz}
340         $this->modules = substr($this->item[1], strpos($this->item[1], "{"),strlen($this->item[1])-1);
342         //then split again at the ", " stuff.
343         $this->modules = split($this->modules, ", ");
344  
345         //fill up our $this->modules property
346         if(is_array($this->modules)){
347             foreach($this->modules as $this->module)
348                 $this->capabilities[$this->cap_type][$this->module]=true;
349         } /* end if */
350         elseif(is_string($this->modules))
351             $this->capabilites[$this->cap_type][$this->module]=true;
352     }
357     if(sieve::status($this->line) == F_NO){             //here we should do some returning of error codes?
358         $this->error=EC_UNKNOWN;
359         $this->error_raw = "Server not allowing connections.";
360         return false;
361     }
363     /* decision login to decide what type of authentication to use... */
365      /* Loop through each allowed authentication type and see if the server allows the type */
366      foreach(split(" ",$this->auth_types) as $auth_type)
367      {
368         if ($this->capabilities["auth"][$auth_type])
369         {
370             /* We found an auth type that is allowed. */
371             $this->auth_in_use = $auth_type;
372             break;
373         }
374      }
375     
376     /* Fill error message if no auth types are present */
377     if (!isset($this->capabilities["auth"])){
378         $this->error=EC_UNKNOWN;
379         $this->error_raw = "No authentication methods found - please check your sieve setup for missing sasl modules";
380         return false;
381     }
383      /* call our authentication program */  
384     return sieve::authenticate();
386   }
388   function sieve_logout()
389   {
390     if($this->loggedin==false)
391         return false;
393     fputs($this->fp,"LOGOUT\r\n");
394     fclose($this->fp);
395     $this->loggedin=false;
396     return true;
397   }
399   function sieve_sendscript($scriptname, $script)
400   {
401     if($this->loggedin==false)
402         return false;
403     $this->script=stripslashes($script);
404     $len=strlen($this->script);
405     fputs($this->fp, "PUTSCRIPT \"$scriptname\" {".$len."+}\r\n");
406     fputs($this->fp, "$this->script\r\n");
407   
408     return sieve::get_response();
410   }  
411   
412   //it appears the timsieved does not honor the NUMBER type.  see lex.c in timsieved src.
413   //don't expect this function to work yet.  I might have messed something up here, too.
414   function sieve_havespace($scriptname, $scriptsize)
415   {
416     if($this->loggedin==false)
417         return false;
418     fputs($this->fp, "HAVESPACE \"$scriptname\" $scriptsize\r\n");
419     return sieve::get_response();
421   }  
423   function sieve_setactivescript($scriptname)
424   {
425     if($this->loggedin==false)
426         return false;
428     fputs($this->fp, "SETACTIVE \"$scriptname\"\r\n");   
429     return sieve::get_response();
431   }
432   
433   function sieve_getscript($scriptname)
434   {
435     unset($this->script);
436     if($this->loggedin==false)
437         return false;
439     fputs($this->fp, "GETSCRIPT \"$scriptname\"\r\n");
440     return sieve::get_response();
441    
442   }
445   function sieve_deletescript($scriptname)
446   {
447     if($this->loggedin==false)
448         return false;
450     fputs($this->fp, "DELETESCRIPT \"$scriptname\"\r\n");    
452     return sieve::get_response();
453   }
455   
456   function sieve_listscripts() 
457    { 
458      fputs($this->fp, "LISTSCRIPTS\r\n"); 
459      sieve::get_response();             //should always return true, even if there are no scripts...
460      if(isset($this->found_script) and $this->found_script)
461          return true;
462      else{
463          $this->error=EC_NOSCRIPTS;     //sieve::getresponse has no way of telling wether a script was found...
464          $this->error_raw="No scripts found for this account.";
465          return false;
466      }
467    }
469   function sieve_alive()
470   {
471       if(!isset($this->fp) or $this->fp==0){
472           $this->error = EC_NOT_LOGGED_IN;
473           return false;
474       }
475       elseif(feof($this->fp)){                  
476           $this->error = EC_NOT_LOGGED_IN;
477           return false;
478       }
479       else
480           return true;
481   }
483   function authenticate()
484   {
486     switch ($this->auth_in_use) {
487     
488         case "PLAIN":
489             $auth=base64_encode("$this->user\0$this->auth\0$this->pass");
490    
491             $this->len=strlen($auth);                   
492             fputs($this->fp, "AUTHENTICATE \"PLAIN\" {".$this->len."+}\r\n");
493             fputs($this->fp, "$auth\r\n");
495             $this->line=fgets($this->fp,1024);          
496             while(sieve::status($this->line) == F_DATA)
497                $this->line=fgets($this->fp,1024);
499              if(sieve::status($this->line) == F_NO)
500                return false;
501              $this->loggedin=true;
502                return true;    
503              break;
505         default:
506             return false;
507             break;
509     }//end switch
512   }//end authenticate()
519 ?>