Alternc  latest
Alternc logiel libre pour l'hébergement
procmail_to_sieve.php
Go to the documentation of this file.
1 #!/usr/bin/php
2 <?php
3 /*
4  AlternC - Web Hosting System
5  Copyright (C) 2002 by the AlternC Development Team.
6  http://alternc.org/
7  ----------------------------------------------------------------------
8  LICENSE
9 
10  This program is free software; you can redistribute it and/or
11  modify it under the terms of the GNU General Public License (GPL)
12  as published by the Free Software Foundation; either version 2
13  of the License, or (at your option) any later version.
14 
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  To read the license please visit http://www.gnu.org/copyleft/gpl.html
21  ----------------------------------------------------------------------
22  Purpose of file: Based on procmail-builder module, converts
23  the procmail generated by AlternC<=1.0.3 to AlternC>=3.0
24  ----------------------------------------------------------------------
25 */
26 
27 /* ----------------------------------------------------------------- */
28 /**
29  MAIN FUNCTION
30  Read all the mail folders and search for .procmailrc's
31 */
32 function procmail2sieve() {
33  global $ROOT;
34  $d=@opendir($ROOT);
35  if ($d) {
36  while ($c=readdir($d)) {
37  if (substr($c,0,1)==".") continue; // skip hidden files
38  if (is_dir($ROOT."/".$c)) {
39  // Go to level 2.
40  $e=@opendir($ROOT."/".$c);
41  if ($e) {
42  while ($f=readdir($e)) {
43  if (substr($f,0,1)==".") continue; // skip hidden files
44  if (is_file($ROOT."/".$c."/".$f."/.procmailrc")) {
45  // We found one .procmailrc, let's parse it on behalf of his user...
46  parseOneProcmail($f); /* ################## SUB FUNCTION ###################### */
47  }
48  }
49  closedir($e);
50  } else {
51  echo "ERROR: Cannot open ".$ROOT."/".$c."\n";
52  }
53 
54  }
55  }
56  closedir($d);
57  } else {
58  echo "FATAL: cannot open ".$ROOT."\n";
59  exit();
60  }
61 } /* procmail2sieve */
62 
63 
64 
65 /* ----------------------------------------------------------------- */
66 /** Parse ONE procmailrc, and write its sieve rules
67  * @param string $user
68  */
70  global $SIEVEROOT;
71  $email=preg_replace("#_([^_]*)$#","@$1",$user);
72  if ($rules=readrules($user)) { /* ################## SUB FUNCTION ###################### */
73  for($i=0; $i<count($rules); $i++) {
74  list($rules[$i]["conds"],$rules[$i]["actionparam"])=describe($rules[$i]); /* ################## SUB FUNCTION ###################### */
75  }
76 
77  // Now we have $rule["type"] = the ACTION to accomplish + (if 1 or 4) $actionparam
78  // and a list of $rule["conds"][0]=condition type & $rule["conds"][1]=condition parameter (if not 5)
79  // Let's write a sieve script :)
80  $u=substr($u,0,1);
81  @mkdir($SIEVEROOT."/".$u."/".$user."/sieve");
82  @mkdir($SIEVEROOT."/".$u."/".$user."/sieve/tmp");
83  $uid=fileowner($SIEVEROOT."/".$u."/".$user);
84  chown($SIEVEROOT."/".$u."/".$user."/sieve",$uid);
85  chgrp($SIEVEROOT."/".$u."/".$user."/sieve","vmail");
86  chown($SIEVEROOT."/".$u."/".$user."/sieve/tmp",$uid);
87  chgrp($SIEVEROOT."/".$u."/".$user."/sieve/tmp","vmail");
88 
89  $f=fopen($SIEVEROOT."/".$u."/".$user."/sieve/tmp/phpscript.sieve","wb");
90  if (!$f) {
91  echo "ERROR: Can't open '$user' in '$SIEVEROOT' for writing\n";
92  } else {
93  echo "OK: writing sieve script for $user (".count($rules)." rules)\n";
94 
95  $avelsieveversion=array("major" => 1, "minor"=>9, "release" => 9, "string" => "1.9.9");
96  fputs($f,'# This script has been automatically generated by avelsieve
97 # (Sieve Mail Filters Plugin for Squirrelmail)
98 # Warning: If you edit this manually, then the changes will not
99 # be reflected in the users\' front-end!
100 #AVELSIEVE_VERSION'.urlencode(base64_encode(serialize($avelsieveversion))).'
101 #AVELSIEVE_CREATED'.time().'
102 #AVELSIEVE_MODIFIED'.time().'
103 require ["fileinto","envelope","reject","vacation","imap4flags","relational","comparator-i;ascii-numeric","regex","body","date"];
104 ');
105  foreach($rules as $rule) {
106  if ($rule["type"]==2) continue; // IGNORE "Filter the message through SpamAssassin"
107 
108  // Create the avelsieve array:
109  $avelrule=array();
110  // And sieve script:
111  $script="if ";
112  $avelrule["condition"]="and";
113  $avelrule["type"]=1;
114  if (!count($rule["conds"])) {
115  // no conditions
116  $script.="true\n";
117  $avelrule["cond"][]=array("kind" => "message", "type" => "all");
118  } else { // have conditions
119  $script.="allof (";
120  $first=true;
121  foreach($conds as $cond) {
122  if (!$first) $script.=",\n";
123  $first=false;
124  // What kind of condition?
125  switch($cond[0]) {
126  case 0: // subject
127  $script.='header :contains "Subject" "'.str_replace('"','\\"',$cond[1]).'"';
128  $avelrule["cond"][]=array("kind" => "message", "type" => "header", "header" => "Subject", "matchtype" => "contains", "headermatch" => $cond[1] );
129  break;
130  case 1: // sender
131  $script.='header :contains "From" "'.str_replace('"','\\"',$cond[1]).'"';
132  $avelrule["cond"][]=array("kind" => "message", "type" => "header", "header" => "From", "matchtype" => "contains", "headermatch" => $cond[1] );
133  break;
134  case 2: // recipient
135  $script.='address :contains ["to", "cc"] "'.str_replace('"','\\"',$cond[1]).'"';
136  $avelrule["cond"][]=array("kind" => "message", "type" => "address", "address" => "toorcc", "matchtype" => "contains", "addressmatch" => $cond[1] );
137  break;
138  case 3: // List-Post
139  $script.='header :contains "List-Post" "'.str_replace('"','\\"',$cond[1]).'"';
140  $avelrule["cond"][]=array("kind" => "message", "type" => "header", "header" => "List-Post", "matchtype" => "contains", "headermatch" => $cond[1] );
141  break;
142  case 4: // List-Id
143  $script.='header :contains "List-Id" "'.str_replace('"','\\"',$cond[1]).'"';
144  $avelrule["cond"][]=array("kind" => "message", "type" => "header", "header" => "List-Id", "matchtype" => "contains", "headermatch" => $cond[1] );
145  break;
146  case 5: // Spamassassin
147  $script.='header :contains "X-Spam-Status" "Yes"';
148  $avelrule["cond"][]=array("kind" => "message", "type" => "header", "header" => "X-Spam-Status", "matchtype" => "contains", "headermatch" => "Yes" );
149  break;
150  case 6: // Delivered-To
151  $script.='envelope :contains "to" "'.str_replace('"','\\"',$cond[1]).'"';
152  $avelrule["cond"][]=array("kind" => "message", "type" => "envelope", "envelope" => "to", "matchtype" => "contains", "envelopematch" => $cond[1] );
153  break;
154 
155  }
156  }
157  $script.=")\n{\n";
158  }
159  // Now the ACTION:
160  switch($rule["type"]) {
161  case 1: // move to
162  $script.='fileinto "'.str_replace('"','\\"',$rule["actionparam"]).'";
163 stop;
164 ';
165  $avelrule["action"] = 5;
166  $avelrule["folder"] = $rule["actionparam"];
167  $avelrule["stop"] = "on";
168  break;
169  case 3: // Discard (for good)
170  $script.='discard;
171 stop;
172 ';
173  $avelrule["action"] = 2;
174  $avelrule["stop"] = "on";
175  break;
176  case 4: // Forward To (copy)
177  $script.='redirect "'.str_replace('"','\\"',$rule["actionparam"]).'";
178 ';
179  $avelrule["action"] = 4;
180  $avelrule["redirectemail"] = $rule["actionparam"];
181  break;
182  case 5: // Auto-Reply
183  $script.='vacation :days 7 :addresses ["'.$email.'"@ :subject "Auto Reply" text:
184 '.str_replace("\\'","'",@file_get_contents($ROOT."/".$u."/".$user."/".$user.".txt")).'
185 .
186 ;
187 ';
188  $avelrule["action"] = 6;
189  $avelrule["vac_addresses"] = $email;
190  $avelrule["vac_subject"] = "Auto Reply";
191  $avelrule["vac_days"] = 7;
192  $avelrule["vac_message"] = @file_get_contents($ROOT."/".$u."/".$user."/".$user.".txt");
193  break;
194  }
195  $script.="}\n";
196  // Now put it into the script file :
197  fputs($f,"#START_SIEVE_RULE".urlencode(base64_encode(serialize($avelrule)))."END_SIEVE_RULE\n");
198  fputs($f,$script);
199  /*
200 if allof (header :contains "From" "expediteur@coin.pan",
201 header :contains "To" "destinataire@coin.pan")
202 {
203 fileinto "INBOX.test";
204 stop;
205 }
206  */
207  } /* for each rule */
208 
209  fclose($f);
210 
211  // Then Move it to the right place
212  @unlink($SIEVEROOT."/".$u."/".$user."/sieve/phpscript.sieve");
213  rename(
214  $SIEVEROOT."/".$u."/".$user."/sieve/tmp/phpscript.sieve",
215  $SIEVEROOT."/".$u."/".$user."/sieve/phpscript.sieve"
216  );
217  chown($SIEVEROOT."/".$u."/".$user."/sieve/phpscript.sieve",$uid);
218  chgrp($SIEVEROOT."/".$u."/".$user."/sieve/phpscript.sieve","vmail");
219  }
220  } else {
221  echo "ERROR: can't read rules for $user\n";
222  }
223 
224 } /* parseOneProcmauil */
225 
226 
227 
228 /* ----------------------------------------------------------------- */
229 /** Read rules, fill an array
230  from m_procmail.php original file
231  yeah I know, ereg() is deprecated ;)
232 */
233 function readrules($user="") {
234  if (!$user) $user=$this->user;
235  $u=substr($user,0,1);
236  if (!file_exists(ALTERNC_MAIL."/$u/$user/.procmailrc")) {
237  return false;
238  }
239  $f=fopen(ALTERNC_MAIL."/$u/$user/.procmailrc","rb");
240  $state=0; $rulenum=0; $ligne=0;
241  $res=array();
242  while (!feof($f)) {
243  $found=false; // found allow us to know if we found something for each loop
244  $s=fgets($f,1024);
245  $s=trim($s);
246  if ($state==1 && !ereg("^# RuleEnd$",$s)) {
247  $res[$rulenum]["rule"][$res[$rulenum]["count"]++]=$s;
248  $found=true;
249  }
250  if ($state==1 && ereg("^# RuleEnd$",$s)) {
251  $state=0;
252  $rulenum++;
253  $found=true;
254  }
255  if ($state==0 && ereg("^# RuleType ([0-9][0-9]) -- (.*)?$",$s,$r)) {
256  $state=1;
257  $res[$rulenum]["type"]=$r[1];
258  $res[$rulenum]["name"]=$r[2];
259  $res[$rulenum]["count"]=0;
260  $found=true;
261  }
262  if (!$found && $state!=0) {
263  return false;
264  }
265  $ligne++;
266  }
267  fclose($f);
268  return $res;
269 }
270 
271 
272 /* ----------------------------------------------------------------- */
273 /** Take ONE rule array, extract properly
274  returns one array with the conditions (which are arrays with Condition Type and Value)
275  and the action parameter
276 */
277 function describe($rule) {
278 
279  // Lecture des conditions :
280  $cond=array();
281  switch ($rule["type"]) {
282  case 5:
283  $i=1;
284  while ($rule["rule"][$i]!="* !^FROM_DAEMON" && $rule["rule"][$i]!="") {
285  $cond[]=$rule["rule"][$i];
286  $i++;
287  }
288  break;
289  default:
290  $i=1;
291  while (substr($rule["rule"][$i],0,1)=="*") {
292  $cond[]=$rule["rule"][$i];
293  $i++;
294  }
295  break;
296  }
297 
298  // $cond is an array of conditions
299  // let's parse the condition : (see arrays at the top of this file)
300  $conds=array();
301 
302  for($i=0;$i<count($cond);$i++) {
303  if (ereg("^\\* \\^Subject\\.\\*(.*)$",$cond[$i],$t)) {
304  $conds[]=array( 0, $t[1] );
305  }
306  if (ereg("^\\* \\^From\\.\\*(.*)$",$cond[$i],$t)) {
307  $conds[]=array( 1, $t[1] );
308  }
309  if (ereg("^\\* \\^TO_(.*)$",$cond[$i],$t)) {
310  $conds[]=array( 2, $t[1] );
311  }
312  if (ereg("^\\* \\^List-Post: (.*)$",$cond[$i],$t)) {
313  $conds[]=array( 3, $t[1] );
314  }
315  if (ereg("^\\* \\^List-Id: (.*)$",$cond[$i],$t)) {
316  $conds[]=array( 4, $t[1] );
317  }
318  if (ereg("^\\* \\^X-Spam-Status: Yes$",$cond[$i])) {
319  $conds[]=array( 5 );
320  }
321  if (ereg("^\\* \\^Delivered-To:\\.\\*(.*)$",$cond[$i],$t)) {
322  $conds[]=array( 6, $t[1] );
323  }
324  }
325 
326  // Action :
327  $actionparam=false;
328  switch ($rule["type"]) {
329  case 1:
330  $actionparam=$rule["rule"][count($rule["rule"])-2]; /* Folder */
331  break;
332  case 4:
333  $actionparam = substr($rule["rule"][count($rule["rule"])-2],0,15); /* Recipient */
334  break;
335  }
336  return array($conds,$actionparam);
337 }
338 
339 
340 
341 
342 
343 
344 
345 
346 /* Help for humans ... */
347 
349  0 => "Le sujet du message contient ...",
350  1 => "L'expediteur du message est contient ...",
351  2 => "L'un des destinataires du message contient ...",
352  3 => "L'en-tete 'List-Post' du message est ...",
353  4 => "L'en-tete 'List-Id' du message est ...",
354  5 => "SpamAssassin considere qu'il s'agit d'un Spam",
355  6 => "L'en-tete 'Delivered-To' du message contient ...",
356  );
357 $aactions=array(
358  1 => "Move the message to this folder",
359  2 => "Filter the message through SpamAssassin",
360  3 => "Discard the message (for good !)",
361  4 => "Forward the mail to",
362  5 => "Auto-reply",
363  );
364 
365 
366 /* ----------------------------------------------------------------- */
367 // CONFIGURATION :
368 include_once('/usr/share/alternc/panel/class/local.php');
369 $ROOT=$L_ALTERNC_MAIL;
370 
371 $SIEVEROOT="/var/lib/dovecot/sieve";
372 // GO !
374 
exit
Definition: adm_doadd.php:70
const ALTERNC_MAIL
Definition: bootstrap.php:9
$c
Definition: bootstrap.php:47
$r
Definition: aws_add.php:75
$found
Definition: aws_del.php:29
$d
$res
Definition: index.php:111
$uid
$user
Definition: bootstrap.php:84
parseOneProcmail($user)
Parse ONE procmailrc, and write its sieve rules.
procmail2sieve()
MAIN FUNCTION Read all the mail folders and search for .procmailrc's.
readrules($user="")
Read rules, fill an array from m_procmail.php original file yeah I know, ereg() is deprecated ;)
describe($rule)
Take ONE rule array, extract properly returns one array with the conditions (which are arrays with Co...
foreach($domaines_user as $domaine) $t
$i