Benjamin Renard commited on 2013-12-25 20:10:39
Showing 6 changed files, with 684 additions and 0 deletions.
... | ... |
@@ -0,0 +1,96 @@ |
1 |
+Thunderbird to SOGo scripts |
|
2 |
+=========================== |
|
3 |
+ |
|
4 |
+Convert Thunderbird mail directory to Maildir format |
|
5 |
+---------------------------------------------------- |
|
6 |
+ |
|
7 |
+The thunderbird2maildir script permit to transform Thunderbird mail |
|
8 |
+directory in Maildir format. This script take Thunderbird mail |
|
9 |
+directory path (-m parameter) and inspect content to find mbox file |
|
10 |
+and convert relative path in maildir hierarchic path. You could use |
|
11 |
+-r parameter to replace accents in maildir path to avoid encoding |
|
12 |
+problems. It use perfect_maildir.pl [1] script (path could be specify |
|
13 |
+by using -P parameter) to convert mbox file to Maildir. |
|
14 |
+ |
|
15 |
+[1] perfect_maildir.pl : http://perfectmaildir.home-dn.net/ |
|
16 |
+ |
|
17 |
+ Usage: thunderbird2maildir [options] |
|
18 |
+ |
|
19 |
+ Options: |
|
20 |
+ -h, --help show this help message and exit |
|
21 |
+ -m MBDIR The Mbox Thunderbird mail directory |
|
22 |
+ -M MDDIR The maildir directory |
|
23 |
+ -P PERFECTMAILDIR The perfect_maildir.pl script path |
|
24 |
+ -t, --just-try |
|
25 |
+ -r, --replace-accents |
|
26 |
+ Remove accent in folder names |
|
27 |
+ -v, --verbose |
|
28 |
+ -d, --debug |
|
29 |
+ |
|
30 |
+Convert Thunderbird mail filters to SOGo JSON format |
|
31 |
+---------------------------------------------------- |
|
32 |
+ |
|
33 |
+The convertFilters script permit to convert Thunderbird mail filters |
|
34 |
+to SOGo filters. It take as input msgFilterRules.dat path (-f parameter), |
|
35 |
+parse file and try to convert filters in SOGo equivalent. Some filters |
|
36 |
+conditions or actions can't be converted as SOGo filters, so warning or |
|
37 |
+critical messages will be inform you of possible conversions problems. |
|
38 |
+Output is in JSON format. By default, JSON string is write and stdout |
|
39 |
+but you could provide with -o parameter a file path where JSON have to |
|
40 |
+be write. |
|
41 |
+ |
|
42 |
+ Usage: convertFilters [options] |
|
43 |
+ |
|
44 |
+ Options: |
|
45 |
+ -h, --help show this help message and exit |
|
46 |
+ -f FILE The msgFilterRules.dat path |
|
47 |
+ -o OUT Output path (default : '-' => stdout) |
|
48 |
+ -p, --pretty Pretty JSON output |
|
49 |
+ -j, --just-try Just-try mode (no output) |
|
50 |
+ --dont-warn-cc Don't warn about cc -> to_or_cc convertion |
|
51 |
+ -r, --replace-accents |
|
52 |
+ Remove accent in folder names |
|
53 |
+ -v, --verbose |
|
54 |
+ -d, --debug |
|
55 |
+ |
|
56 |
+The thunderbirdFilters.py library is inspired by tbprocmailconv.py script : |
|
57 |
+ |
|
58 |
+http://sourceforge.net/projects/tbprocmailconv/ |
|
59 |
+ |
|
60 |
+Load JSON in SOGo profile |
|
61 |
+~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
62 |
+ |
|
63 |
+Run as sogo user : |
|
64 |
+ |
|
65 |
+ echo "[USER]:[PASS]" > /tmp/[USER].pass |
|
66 |
+ /usr/sbin/sogo-tool user-preferences set defaults [USER] -p /tmp/[USER].pass \ |
|
67 |
+ SOGoSieveFilters -f /tmp/[USER].json |
|
68 |
+ rm [USER].pass |
|
69 |
+ |
|
70 |
+With : |
|
71 |
+ |
|
72 |
+ * [USER] : user's login |
|
73 |
+ * [PASS] : user's password |
|
74 |
+ |
|
75 |
+The JSON file must be /tmp/[USER].json. |
|
76 |
+ |
|
77 |
+Copyright |
|
78 |
+========= |
|
79 |
+ |
|
80 |
+Copyright (c) 2013 Benjamin Renard |
|
81 |
+ |
|
82 |
+License |
|
83 |
+======= |
|
84 |
+ |
|
85 |
+This program is free software; you can redistribute it and/or |
|
86 |
+modify it under the terms of the GNU General Public License version 2 |
|
87 |
+as published by the Free Software Foundation. |
|
88 |
+ |
|
89 |
+This program is distributed in the hope that it will be useful, |
|
90 |
+but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
91 |
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
92 |
+GNU General Public License for more details. |
|
93 |
+ |
|
94 |
+You should have received a copy of the GNU General Public License |
|
95 |
+along with this program; if not, write to the Free Software |
|
96 |
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
... | ... |
@@ -0,0 +1,221 @@ |
1 |
+#!/usr/bin/python |
|
2 |
+# -*- coding: utf-8 -*- |
|
3 |
+# |
|
4 |
+# This script permit to convert Thunderbird mail filters to SOGo |
|
5 |
+# filters. It take as input msgFilterRules.dat path (-f parameter), |
|
6 |
+# parse file and try to convert filters in SOGo equivalent. Some |
|
7 |
+# filters conditions or actions can't be converted as SOGo filters, |
|
8 |
+# so warning or critical messages will be inform you of possible |
|
9 |
+# conversions problems. Output is in JSON format. By default, JSON |
|
10 |
+# string is write and stdout but you could provide with -o parameter |
|
11 |
+# a file path where JSON have to be write. |
|
12 |
+# |
|
13 |
+# To know how to load JSON in SOGo, see README file. |
|
14 |
+# |
|
15 |
+# Author : Benjamin Renard <brenard@zionetrix.net> |
|
16 |
+# Date : Wed, 25 Dec 2013 20:41:39 +0100 |
|
17 |
+# Source : http://git.zionetrix.net/thunderbird2sogo |
|
18 |
+ |
|
19 |
+from optparse import OptionParser |
|
20 |
+import sys |
|
21 |
+import thunderbirdFilters |
|
22 |
+import logging |
|
23 |
+import json |
|
24 |
+import re |
|
25 |
+ |
|
26 |
+parser = OptionParser() |
|
27 |
+ |
|
28 |
+parser.add_option('-f', |
|
29 |
+ action="store", |
|
30 |
+ type="string", |
|
31 |
+ dest="file", |
|
32 |
+ help="The msgFilterRules.dat path", |
|
33 |
+ default=None) |
|
34 |
+parser.add_option('-o', |
|
35 |
+ action="store", |
|
36 |
+ type="string", |
|
37 |
+ dest="out", |
|
38 |
+ help="Output path (default : '-' => stdout) ", |
|
39 |
+ default='-') |
|
40 |
+parser.add_option('-p','--pretty', |
|
41 |
+ action="store_true", |
|
42 |
+ dest="pretty", |
|
43 |
+ help="Pretty JSON output") |
|
44 |
+parser.add_option('-j','--just-try', |
|
45 |
+ action="store_true", |
|
46 |
+ dest="justtry", |
|
47 |
+ help="Just-try mode (no output)") |
|
48 |
+parser.add_option('--dont-warn-cc', |
|
49 |
+ action="store_true", |
|
50 |
+ dest="dontwarncc", |
|
51 |
+ help="Don't warn about cc -> to_or_cc convertion", |
|
52 |
+ default=False) |
|
53 |
+parser.add_option('-r', |
|
54 |
+ '--replace-accents', |
|
55 |
+ help='Remove accent in folder names', |
|
56 |
+ action="store_true", |
|
57 |
+ dest="replaceaccents") |
|
58 |
+parser.add_option('-v', |
|
59 |
+ '--verbose', |
|
60 |
+ action="store_true", |
|
61 |
+ dest="verbose") |
|
62 |
+parser.add_option('-d', |
|
63 |
+ '--debug', |
|
64 |
+ action="store_true", |
|
65 |
+ dest="debug") |
|
66 |
+ |
|
67 |
+(options, args) = parser.parse_args() |
|
68 |
+ |
|
69 |
+if options.debug: |
|
70 |
+ loglevel=logging.DEBUG |
|
71 |
+elif options.verbose: |
|
72 |
+ loglevel=logging.INFO |
|
73 |
+else: |
|
74 |
+ loglevel=logging.WARNING |
|
75 |
+ |
|
76 |
+logging.basicConfig(level=loglevel,format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') |
|
77 |
+ |
|
78 |
+if options.file is None: |
|
79 |
+ logging.fatal('You must provide msgFilterRules.dat path (-f)') |
|
80 |
+ sys.exit(1) |
|
81 |
+ |
|
82 |
+if options.out!='-': |
|
83 |
+ try: |
|
84 |
+ output_file=open(options.out,'w') |
|
85 |
+ except Exception,e: |
|
86 |
+ logging.fatal('Error opening output file %s : %s' % (options.out,e)) |
|
87 |
+ sys.exit(2) |
|
88 |
+ |
|
89 |
+(tbf,warning)=thunderbirdFilters.read_from_file(options.file) |
|
90 |
+if len(warning)!=0: |
|
91 |
+ logging.warning('Parsing msgFilterRules.dat return warnings :\n - %s' % '\n - '.join(warning)) |
|
92 |
+logging.debug('Parsing return :\n%s' % tbf) |
|
93 |
+sfs=[] |
|
94 |
+ |
|
95 |
+for f in tbf: |
|
96 |
+ if f['type'] != "17": |
|
97 |
+ logging.error("Filter type '%s' no recognized, pass this filter" % f['type']) |
|
98 |
+ continue |
|
99 |
+ try: |
|
100 |
+ sf={} |
|
101 |
+ if f["enabled"]=='yes': |
|
102 |
+ sf["active"]=1 |
|
103 |
+ else: |
|
104 |
+ sf["active"]=0 |
|
105 |
+ if 'name' not in f: |
|
106 |
+ logging.error('Thunderbird filter does not have name ! Pass this filter : %s' % f) |
|
107 |
+ continue |
|
108 |
+ sf['name']=f['name'] |
|
109 |
+ fc=thunderbirdFilters.convert_conditions(f["condition"]) |
|
110 |
+ bool_ops=[] |
|
111 |
+ sfr=[] |
|
112 |
+ for c in fc: |
|
113 |
+ r={} |
|
114 |
+ if c['bool_operator'] not in bool_ops: |
|
115 |
+ bool_ops.append(c['bool_operator']) |
|
116 |
+ |
|
117 |
+ if c['cri_operator']=='contains' or c['cri_operator']=='is': |
|
118 |
+ r['operator']=c['cri_operator'] |
|
119 |
+ elif c['cri_operator']=="isn't": |
|
120 |
+ r['operator']='is_not' |
|
121 |
+ else: |
|
122 |
+ raise Exception('Condition operator "%s" not recognized. Pass' % c['cri_operator']) |
|
123 |
+ if c['cri_operand'] in ['subject','to','from']: |
|
124 |
+ r['field']=c['cri_operand'] |
|
125 |
+ elif c['cri_operand']=='cc': |
|
126 |
+ r['field']='to_or_cc' |
|
127 |
+ if not options.dontwarncc: |
|
128 |
+ logging.warning('Filter %s : Condition operator cc convert to to_or_cc' % f['name']) |
|
129 |
+ elif c['cri_operand'].startswith('"') and c['cri_operand'].endswith('"'): |
|
130 |
+ r['field']='header' |
|
131 |
+ r['custom_header']=re.sub('^"(.*)"$',r'\1',c['cri_operand'],count=1) |
|
132 |
+ else: |
|
133 |
+ raise Exception('Condition operand "%s" not recognized. Pass' % c['cri_operand']) |
|
134 |
+ r['value']=c['value'] |
|
135 |
+ sfr.append(r) |
|
136 |
+ if len(sfr)==0: |
|
137 |
+ logging.error('Filter %s : No condition found ! Pass this filter' % f['name']) |
|
138 |
+ continue |
|
139 |
+ sf['rules']=sfr |
|
140 |
+ |
|
141 |
+ if len(bool_ops)==1: |
|
142 |
+ if bool_ops[0]=='AND': |
|
143 |
+ sf['match']='all' |
|
144 |
+ elif bool_ops[0]=='OR': |
|
145 |
+ sf['match']='any' |
|
146 |
+ else: |
|
147 |
+ logging.error("Filter %s : Boolean operator not recognized %s, pass this filter." % (f['name'],bool_ops[0])) |
|
148 |
+ continue |
|
149 |
+ else: |
|
150 |
+ logging.error('Filter %s : Multiple boolean operator not supported (%s). Pass this filter.' % (f['name'],','.join(bool_ops))) |
|
151 |
+ continue |
|
152 |
+ sfa=[] |
|
153 |
+ for a in f['actions']: |
|
154 |
+ if a[0]=='Move to folder': |
|
155 |
+ sa={ |
|
156 |
+ 'method': 'fileinto', |
|
157 |
+ 'argument': thunderbirdFilters.convert_uri_path_to_maildir(a[1],separator='/',replaceaccents=options.replaceaccents) |
|
158 |
+ } |
|
159 |
+ elif a[0]=='Forward': |
|
160 |
+ sa={ |
|
161 |
+ 'method': 'redirect', |
|
162 |
+ 'argument': a[1] |
|
163 |
+ } |
|
164 |
+ elif a[0]=='Mark read': |
|
165 |
+ sa={ |
|
166 |
+ 'method': 'addflag', |
|
167 |
+ 'argument': 'seen' |
|
168 |
+ } |
|
169 |
+ elif a[0]=='JunkScore': |
|
170 |
+ if a[1]==0: |
|
171 |
+ argument='not_junk' |
|
172 |
+ else: |
|
173 |
+ argument='junk' |
|
174 |
+ sa={ |
|
175 |
+ 'method': 'addflag', |
|
176 |
+ 'argument': argument |
|
177 |
+ } |
|
178 |
+ elif a[0]=='Mark flagged': |
|
179 |
+ sa={ |
|
180 |
+ 'method': 'addflag', |
|
181 |
+ 'argument': 'flagged' |
|
182 |
+ } |
|
183 |
+ elif a[0]=='Stop execution': |
|
184 |
+ sa={ |
|
185 |
+ 'method': 'stop', |
|
186 |
+ 'argument': None |
|
187 |
+ } |
|
188 |
+ elif a[0] in ['Mark unread','Copy to folder','Change priority','Reply']: |
|
189 |
+ raise Exception("Action %s doesn't have equivalent in SOGo" % a[0]) |
|
190 |
+ else: |
|
191 |
+ raise Exception('Filter %s : Action %s not supported' % (f['name'],a[0])) |
|
192 |
+ sfa.append(sa) |
|
193 |
+ if len(sfa)==0: |
|
194 |
+ logging.error('Filter %s : no action found ! Pass this filter.' % f['name']) |
|
195 |
+ continue |
|
196 |
+ sf['actions']=sfa |
|
197 |
+ sfs.append(sf) |
|
198 |
+ except Exception,e: |
|
199 |
+ if 'name' in f: |
|
200 |
+ name=f['name'] |
|
201 |
+ else: |
|
202 |
+ name=f |
|
203 |
+ logging.fatal('Failed to convert filter %s, pass : %s' % (name,e)) |
|
204 |
+ continue |
|
205 |
+ |
|
206 |
+output_data={'SOGoSieveFilters': sfs} |
|
207 |
+ |
|
208 |
+if options.justtry: |
|
209 |
+ sys.exit(0) |
|
210 |
+ |
|
211 |
+if options.pretty: |
|
212 |
+ output_text=json.dumps(output_data,indent=4, separators=(',', ': ')) |
|
213 |
+else: |
|
214 |
+ output_text=json.dumps(output_data) |
|
215 |
+ |
|
216 |
+if options.out=='-': |
|
217 |
+ print output_text |
|
218 |
+else: |
|
219 |
+ logging.info('Write SOGo filters on %s file' % options.out) |
|
220 |
+ output_file.write(output_text) |
|
221 |
+ output_file.close() |
... | ... |
@@ -0,0 +1,73 @@ |
1 |
+#!/usr/bin/python |
|
2 |
+# -*- coding: utf-8 -*- |
|
3 |
+# |
|
4 |
+# This simple library provide function to replace accents |
|
5 |
+# in string. It's part of thunderbird2sogo scripts. |
|
6 |
+# |
|
7 |
+# Author : Benjamin Renard <brenard@zionetrix.net> |
|
8 |
+# Date : Wed, 25 Dec 2013 20:41:39 +0100 |
|
9 |
+# Source : http://git.zionetrix.net/thunderbird2sogo |
|
10 |
+ |
|
11 |
+import string |
|
12 |
+ |
|
13 |
+def replace_accents(s): |
|
14 |
+ replacements={ |
|
15 |
+ "à": "a", |
|
16 |
+ "á": "a", |
|
17 |
+ "â": "a", |
|
18 |
+ "ã": "a", |
|
19 |
+ "ä": "a", |
|
20 |
+ "ç": "c", |
|
21 |
+ "è": "e", |
|
22 |
+ "é": "e", |
|
23 |
+ "ê": "e", |
|
24 |
+ "ë": "e", |
|
25 |
+ "ì": "i", |
|
26 |
+ "í": "i", |
|
27 |
+ "î": "i", |
|
28 |
+ "ï": "i", |
|
29 |
+ "ñ": "n", |
|
30 |
+ "ò": "o", |
|
31 |
+ "ó": "o", |
|
32 |
+ "ô": "o", |
|
33 |
+ "õ": "o", |
|
34 |
+ "ö": "o", |
|
35 |
+ "ù": "u", |
|
36 |
+ "ú": "u", |
|
37 |
+ "û": "u", |
|
38 |
+ "ü": "u", |
|
39 |
+ "ý": "y", |
|
40 |
+ "ÿ": "y", |
|
41 |
+ "À": "A", |
|
42 |
+ "Á": "A", |
|
43 |
+ "Â": "A", |
|
44 |
+ "Ã": "A", |
|
45 |
+ "Ä": "A", |
|
46 |
+ "Ç": "C", |
|
47 |
+ "È": "E", |
|
48 |
+ "É": "E", |
|
49 |
+ "Ê": "E", |
|
50 |
+ "Ë": "E", |
|
51 |
+ "Ì": "I", |
|
52 |
+ "Í": "I", |
|
53 |
+ "Î": "I", |
|
54 |
+ "Ï": "I", |
|
55 |
+ "Ñ": "N", |
|
56 |
+ "Ò": "O", |
|
57 |
+ "Ó": "O", |
|
58 |
+ "Ô": "O", |
|
59 |
+ "Õ": "O", |
|
60 |
+ "Ö": "O", |
|
61 |
+ "Ù": "U", |
|
62 |
+ "Ú": "U", |
|
63 |
+ "Û": "U", |
|
64 |
+ "Ü": "U", |
|
65 |
+ "Ý": "Y" |
|
66 |
+ } |
|
67 |
+ for f in replacements: |
|
68 |
+ s=s.replace(f,replacements[f]) |
|
69 |
+ return s |
|
70 |
+ |
|
71 |
+if __name__ == '__main__': |
|
72 |
+ test='éàèôù' |
|
73 |
+ print '%s -> %s' % (test,replace_accents(test)) |
... | ... |
@@ -0,0 +1,168 @@ |
1 |
+#!/usr/bin/python |
|
2 |
+# -*- coding: utf-8 -*- |
|
3 |
+# |
|
4 |
+# This script permit to transform Thunderbird mail directory in |
|
5 |
+# Maildir format. This script take Thunderbird mail directory path |
|
6 |
+# (-m parameter) and inspect content to find mbox file and convert |
|
7 |
+# relative path in maildir hierarchic path. You could use -r |
|
8 |
+# parameter to replace accents in maildir path to avoid encoding |
|
9 |
+# problems. It use perfect_maildir.pl [1] script (path could be |
|
10 |
+# specify by using -P parameter) to convert mbox file to Maildir. |
|
11 |
+# |
|
12 |
+# Author : Benjamin Renard <brenard@zionetrix.net> |
|
13 |
+# Date : Wed, 25 Dec 2013 20:41:39 +0100 |
|
14 |
+# Source : http://git.zionetrix.net/thunderbird2sogo |
|
15 |
+ |
|
16 |
+from optparse import OptionParser |
|
17 |
+ |
|
18 |
+import os |
|
19 |
+import re |
|
20 |
+import logging |
|
21 |
+import sys |
|
22 |
+import subprocess |
|
23 |
+from replace_accents import replace_accents |
|
24 |
+ |
|
25 |
+parser = OptionParser() |
|
26 |
+ |
|
27 |
+parser.add_option('-m', |
|
28 |
+ action="store", |
|
29 |
+ type="string", |
|
30 |
+ dest="mbdir", |
|
31 |
+ help="The Mbox Thunderbird mail directory", |
|
32 |
+ default=None) |
|
33 |
+ |
|
34 |
+parser.add_option('-M', |
|
35 |
+ action="store", |
|
36 |
+ type="string", |
|
37 |
+ dest="mddir", |
|
38 |
+ help="The maildir directory", |
|
39 |
+ default=None) |
|
40 |
+ |
|
41 |
+parser.add_option('-P', |
|
42 |
+ action="store", |
|
43 |
+ type="string", |
|
44 |
+ dest="perfectmaildir", |
|
45 |
+ help="The perfect_maildir.pl script path", |
|
46 |
+ default='perfect_maildir.pl') |
|
47 |
+ |
|
48 |
+parser.add_option('-t', |
|
49 |
+ '--just-try', |
|
50 |
+ action="store_true", |
|
51 |
+ dest="justtry") |
|
52 |
+ |
|
53 |
+parser.add_option('-r', |
|
54 |
+ '--replace-accents', |
|
55 |
+ help='Remove accent in folder names', |
|
56 |
+ action="store_true", |
|
57 |
+ dest="replaceaccents") |
|
58 |
+ |
|
59 |
+parser.add_option('-v', |
|
60 |
+ '--verbose', |
|
61 |
+ action="store_true", |
|
62 |
+ dest="verbose") |
|
63 |
+ |
|
64 |
+parser.add_option('-d', |
|
65 |
+ '--debug', |
|
66 |
+ action="store_true", |
|
67 |
+ dest="debug") |
|
68 |
+ |
|
69 |
+(options, args) = parser.parse_args() |
|
70 |
+ |
|
71 |
+if options.debug: |
|
72 |
+ loglevel=logging.DEBUG |
|
73 |
+elif options.verbose: |
|
74 |
+ loglevel=logging.INFO |
|
75 |
+else: |
|
76 |
+ loglevel=logging.WARNING |
|
77 |
+ |
|
78 |
+logging.basicConfig(level=loglevel,format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') |
|
79 |
+ |
|
80 |
+if not options.mbdir or not options.mddir: |
|
81 |
+ logging.fatal('You must provide Mbox directory and Maildir directory') |
|
82 |
+ sys.exit(1) |
|
83 |
+ |
|
84 |
+if not os.path.isdir(options.mbdir): |
|
85 |
+ logging.fatal('The directory %s does not exists' % options.mbdir) |
|
86 |
+ sys.exit(1) |
|
87 |
+ |
|
88 |
+options.mbdir=os.path.abspath(options.mbdir) |
|
89 |
+ |
|
90 |
+if os.path.exists(options.mddir): |
|
91 |
+ logging.fatal('The destination Maildir directory already exists') |
|
92 |
+ sys.exit(1) |
|
93 |
+ |
|
94 |
+def maildir_make(dir): |
|
95 |
+ if options.justtry: |
|
96 |
+ logging.info('Maildir make %s' % dir) |
|
97 |
+ return |
|
98 |
+ os.mkdir(dir) |
|
99 |
+ for s in ['cur','new','tmp']: |
|
100 |
+ os.mkdir('%s/%s' % (dir,s)) |
|
101 |
+ |
|
102 |
+def isMboxFile(file): |
|
103 |
+ return open(file,'r').readline().startswith('From ') |
|
104 |
+ |
|
105 |
+maildir_make(options.mddir) |
|
106 |
+options.mddir=os.path.abspath(options.mddir) |
|
107 |
+ |
|
108 |
+def listMbox(dir,mboxlist): |
|
109 |
+ for fname in os.listdir(dir): |
|
110 |
+ path="%s%s" % (dir,fname) |
|
111 |
+ if os.path.isfile(path): |
|
112 |
+ if not re.search('\.(msf|dat|html|wdseml)$',fname) and isMboxFile(path): |
|
113 |
+ md=re.sub('\.sbd/','/',path) |
|
114 |
+ md=re.sub('^\.\/','.',md) |
|
115 |
+ md=re.sub('\/+','.',md) |
|
116 |
+ md=re.sub('^\.Inbox(\.|$)','INBOX.',md) |
|
117 |
+ if options.replaceaccents: |
|
118 |
+ md=replace_accents(md) |
|
119 |
+ md=md.decode('utf-8').encode('utf-7') |
|
120 |
+ md=re.sub('\+','&',md) |
|
121 |
+ mboxlist[path]=md |
|
122 |
+ elif options.debug: |
|
123 |
+ logging.debug('File %s is not mbox' % path) |
|
124 |
+ elif os.path.isdir(path): |
|
125 |
+ path="%s/" % path |
|
126 |
+ listMbox(path,mboxlist) |
|
127 |
+ else: |
|
128 |
+ print "unknown path '%s'" % path |
|
129 |
+ |
|
130 |
+mboxlist={} |
|
131 |
+os.chdir(options.mbdir) |
|
132 |
+listMbox('./',mboxlist) |
|
133 |
+ |
|
134 |
+def runPerfectMaildir(maildir,mbox): |
|
135 |
+ if options.justtry: |
|
136 |
+ logging.info("Run perfect_maildir.pl on maildir '%s' with mbox file '%s'" % (maildir, mbox)) |
|
137 |
+ return |
|
138 |
+ logging.info("Read mbox file %s" % mbox) |
|
139 |
+ f=open(mbox,'r') |
|
140 |
+ data=f.read() |
|
141 |
+ f.close() |
|
142 |
+ logging.info("Running perfect_maildir.pl on maildir '%s' with mbox file '%s'" % (maildir,mbox)) |
|
143 |
+ p = subprocess.Popen(['perfect_maildir.pl',maildir],stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE) |
|
144 |
+ output, err = p.communicate(data) |
|
145 |
+ logging.info("End of perfect_maildir.pl : %s" % (output)) |
|
146 |
+ |
|
147 |
+ |
|
148 |
+if './Inbox' in mboxlist: |
|
149 |
+ runPerfectMaildir(options.mddir,'%s/Inbox' %options.mbdir) |
|
150 |
+ del mboxlist['./Inbox'] |
|
151 |
+ |
|
152 |
+subs=[] |
|
153 |
+for mbox in sorted(mboxlist.keys()): |
|
154 |
+ maildir_make("%s/%s" % (options.mddir,mboxlist[mbox])) |
|
155 |
+ runPerfectMaildir('%s/%s' % (options.mddir,mboxlist[mbox]),'%s/%s' % (options.mbdir,mbox)) |
|
156 |
+ subs.append(re.sub('^\.','',mboxlist[mbox])) |
|
157 |
+ |
|
158 |
+ |
|
159 |
+if not options.justtry: |
|
160 |
+ f=open('%s/subscriptions' % options.mddir,'w') |
|
161 |
+logging.info('Write in subscriptions file :') |
|
162 |
+for s in subs: |
|
163 |
+ logging.info(s) |
|
164 |
+ if not options.justtry: |
|
165 |
+ f.write("%s\n" % s) |
|
166 |
+ |
|
167 |
+if not options.justtry: |
|
168 |
+ f.close() |
... | ... |
@@ -0,0 +1,124 @@ |
1 |
+#!/usr/bin/python |
|
2 |
+ |
|
3 |
+# This library is inspired by tbprocmailconv.py script |
|
4 |
+# writed by Michael Scheper : |
|
5 |
+# http://tbprocmailconv.sourceforge.net/ |
|
6 |
+# Some part of this script is simply copy from original |
|
7 |
+# script with some corrections and adjustments. |
|
8 |
+# |
|
9 |
+# It's part of thunderbird2sogo scripts. |
|
10 |
+# |
|
11 |
+# Author : Benjamin Renard <brenard@zionetrix.net> |
|
12 |
+# Date : Wed, 25 Dec 2013 20:41:39 +0100 |
|
13 |
+# Source : http://git.zionetrix.net/thunderbird2sogo |
|
14 |
+ |
|
15 |
+import os |
|
16 |
+import re |
|
17 |
+import sys |
|
18 |
+import urllib |
|
19 |
+import logging |
|
20 |
+from replace_accents import replace_accents |
|
21 |
+ |
|
22 |
+def read_from_file(file): |
|
23 |
+ "Read the Thunderbird filters file into a list of maps." |
|
24 |
+ |
|
25 |
+ filters = [] |
|
26 |
+ current_filter = None |
|
27 |
+ current_actions = [] |
|
28 |
+ warnings = [] |
|
29 |
+ |
|
30 |
+ # Each value gets run through each of these regex replacements. |
|
31 |
+ value_replacements = ( |
|
32 |
+ (re.compile(r'\\\"'), '"'), |
|
33 |
+ (re.compile(r'^ *\"(.*)\"\r*\n?$'), r'\1'), |
|
34 |
+ ) |
|
35 |
+ |
|
36 |
+ for line in open(file): |
|
37 |
+ (key, value) = line.split('=',1) |
|
38 |
+ for (regex, repl) in value_replacements: |
|
39 |
+ value = regex.sub(repl, value) |
|
40 |
+ |
|
41 |
+ logging.debug('Key %s => %s' % (key,value)) |
|
42 |
+ # Each filter starts with a name. |
|
43 |
+ if key == 'name': |
|
44 |
+ if current_filter is not None: |
|
45 |
+ if any(current_actions): |
|
46 |
+ current_filter['actions'] = current_actions |
|
47 |
+ logging.debug("Add filter : %s" % current_filter) |
|
48 |
+ filters.append(current_filter) |
|
49 |
+ logging.debug("Initialize new filter") |
|
50 |
+ current_filter = {} |
|
51 |
+ current_actions = [] |
|
52 |
+ |
|
53 |
+ # If we're still not up to the first filter, ignore this line. |
|
54 |
+ if current_filter is None: |
|
55 |
+ continue |
|
56 |
+ |
|
57 |
+ # Each filter can have multiple actions. Each action has an optional |
|
58 |
+ # actionValue. |
|
59 |
+ if key == 'action': |
|
60 |
+ logging.debug('New action : %s' % value) |
|
61 |
+ # We'll add the value later if there is one. |
|
62 |
+ current_actions.append((value, None)) |
|
63 |
+ elif key == 'actionValue': |
|
64 |
+ if not any(current_actions): |
|
65 |
+ warnings.append('Ignored unexpected action value "%s" in ' |
|
66 |
+ '"%s"' % (current_actions[-1][0], |
|
67 |
+ current_filter.get('name', '(unnamed filter)'))) |
|
68 |
+ elif current_actions[-1][1] is not None: |
|
69 |
+ warnings.append('Ignored extra value "%s" for action "%s" ' |
|
70 |
+ 'in "%s"' % (value, current_actions[-1][0], |
|
71 |
+ current_filter.get('name', '(unnamed filter)'))) |
|
72 |
+ else: |
|
73 |
+ current_action = current_actions.pop() |
|
74 |
+ current_actions.append((current_action[0], value)) |
|
75 |
+ else: |
|
76 |
+ if key in current_filter: |
|
77 |
+ warnings.append('Ignored extra %s ("%s") in %s' % (key, value, |
|
78 |
+ current_filter.get('name', '(unnamed filter)'))) |
|
79 |
+ else: |
|
80 |
+ current_filter[key] = value |
|
81 |
+ if any(current_actions): |
|
82 |
+ current_filter['actions'] = current_actions |
|
83 |
+ logging.debug("Add filter : %s" % current_filter) |
|
84 |
+ filters.append(current_filter) |
|
85 |
+ return (filters, warnings) |
|
86 |
+ |
|
87 |
+condition_regex = re.compile('(AND|OR) \(([^,]+),([^,]+),([^)]+)\) *') |
|
88 |
+def convert_conditions(tb_condition): |
|
89 |
+ "Converts a Thunderbird condition string into procmail condition strings." |
|
90 |
+ conditions = [] |
|
91 |
+ |
|
92 |
+ for match in condition_regex.finditer(tb_condition): |
|
93 |
+ |
|
94 |
+ (bool_operator, cri_operand, cri_operator, value) = match.groups() |
|
95 |
+ |
|
96 |
+ conditions.append({ |
|
97 |
+ 'bool_operator': bool_operator, |
|
98 |
+ 'cri_operand': cri_operand, |
|
99 |
+ 'cri_operator': cri_operator, |
|
100 |
+ 'value': value |
|
101 |
+ }) |
|
102 |
+ |
|
103 |
+ return conditions |
|
104 |
+ |
|
105 |
+_convert_uri_path_to_sogo=re.compile('^[a-z]*\:\/\/[^\/]*\/(.*)$') |
|
106 |
+def convert_uri_path_to_maildir(path,separator='.',replaceaccents=False): |
|
107 |
+ # URL decode |
|
108 |
+ path=urllib.unquote(path) |
|
109 |
+ # Remove URI prefix |
|
110 |
+ path=_convert_uri_path_to_sogo.sub(r'\1',path) |
|
111 |
+ # Replace accents |
|
112 |
+ if replaceaccents: |
|
113 |
+ path=replace_accents(path) |
|
114 |
+ # UTF7 encode |
|
115 |
+ path=path.decode('UTF-8').encode('UTF-7') |
|
116 |
+ # Replace '+' by '&' |
|
117 |
+ path=re.sub('\+','&',path) |
|
118 |
+ # Replace ^Inbox by INBOX |
|
119 |
+ path=re.sub('^Inbox\/','INBOX/',path) |
|
120 |
+ # Replace '/' by separator |
|
121 |
+ if separator!='/': |
|
122 |
+ path=re.sub('\/',separator,path) |
|
123 |
+ return path |
|
124 |
+ |
|
0 | 125 |