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
|
|
|
*/ |
69
|
|
|
function parseOneProcmail($user) { |
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" "[email protected]", |
201
|
|
|
header :contains "To" "[email protected]") |
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
|
|
|
|
348
|
|
|
$acriteria=array( |
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 ! |
373
|
|
|
procmail2sieve(); |
374
|
|
|
|
375
|
|
|
|