Initial commit
Benjamin Renard authored 10 years ago
|
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)
|
Add support to 'all address...
Benjamin Renard authored 10 years ago
|
53) parser.add_option('--dont-warn-all-addresses',
54) action="store_true",
55) dest="dontwarnalladdresses",
56) help="Don't warn about all addresses -> from + to_or_cc convertion",
57) default=False)
|
Initial commit
Benjamin Renard authored 10 years ago
|
58) parser.add_option('-r',
59) '--replace-accents',
60) help='Remove accent in folder names',
61) action="store_true",
62) dest="replaceaccents")
63) parser.add_option('-v',
64) '--verbose',
65) action="store_true",
66) dest="verbose")
67) parser.add_option('-d',
68) '--debug',
69) action="store_true",
70) dest="debug")
71)
72) (options, args) = parser.parse_args()
73)
74) if options.debug:
75) loglevel=logging.DEBUG
76) elif options.verbose:
77) loglevel=logging.INFO
78) else:
79) loglevel=logging.WARNING
80)
81) logging.basicConfig(level=loglevel,format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
82)
83) if options.file is None:
84) logging.fatal('You must provide msgFilterRules.dat path (-f)')
85) sys.exit(1)
86)
87) if options.out!='-':
88) try:
89) output_file=open(options.out,'w')
90) except Exception,e:
91) logging.fatal('Error opening output file %s : %s' % (options.out,e))
92) sys.exit(2)
93)
94) (tbf,warning)=thunderbirdFilters.read_from_file(options.file)
95) if len(warning)!=0:
96) logging.warning('Parsing msgFilterRules.dat return warnings :\n - %s' % '\n - '.join(warning))
97) logging.debug('Parsing return :\n%s' % tbf)
98) sfs=[]
99)
100) for f in tbf:
101) if f['type'] != "17":
102) logging.error("Filter type '%s' no recognized, pass this filter" % f['type'])
103) continue
104) try:
105) sf={}
106) if f["enabled"]=='yes':
107) sf["active"]=1
108) else:
109) sf["active"]=0
110) if 'name' not in f:
111) logging.error('Thunderbird filter does not have name ! Pass this filter : %s' % f)
112) continue
113) sf['name']=f['name']
114)
|
Add support to 'ALL' Thunde...
Benjamin Renard authored 10 years ago
|
115) if f["condition"]=='ALL':
116) sf['match']='allmessages'
117) else:
118) fc=thunderbirdFilters.convert_conditions(f["condition"])
119) bool_ops=[]
120) sfr=[]
121) for c in fc:
122) r={}
123) if c['cri_operator']=='contains' or c['cri_operator']=='is':
124) r['operator']=c['cri_operator']
125) elif c['cri_operator']=="isn't":
126) r['operator']='is_not'
|
Add support to 'doesn't con...
Benjamin Renard authored 10 years ago
|
127) elif c['cri_operator']=="doesn't contain":
128) r['operator']='contains_not'
|
Add support to 'ALL' Thunde...
Benjamin Renard authored 10 years ago
|
129) else:
130) raise Exception('Condition operator "%s" not recognized. Pass' % c['cri_operator'])
131) if c['cri_operand'] in ['subject','to','from']:
132) r['field']=c['cri_operand']
|
Add support to 'to or cc' T...
Benjamin Renard authored 10 years ago
|
133) elif c['cri_operand']=='to or cc':
134) r['field']='to_or_cc'
|
Add support to 'ALL' Thunde...
Benjamin Renard authored 10 years ago
|
135) elif c['cri_operand']=='cc':
136) r['field']='to_or_cc'
137) if not options.dontwarncc:
138) logging.warning('Filter %s : Condition operator cc convert to to_or_cc' % f['name'])
|
Add support to 'all address...
Benjamin Renard authored 10 years ago
|
139) elif c['cri_operand']=='all addresses':
140) if c['cri_operator']=='contains' or c['cri_operator']=='is':
141) # bool operand must be OR
142) if c['bool_operator']=='AND':
143) if len(fc)==1:
144) c['bool_operator']='OR'
145) else:
146) raise Exception('Condition operand "all addresses" with condition operator "%s" and bool operator "AND" supported only if only one condition' % c['cri_operator'])
147) elif c['cri_operator']=="doesn't contain" or c['cri_operator']=="isn't":
148) # bool operand must be AND
149) if c['bool_operator']=='OR':
150) if len(fc)==1:
151) c['bool_operator']='AND'
152) else:
153) raise Exception('Condition operand "all addresses" with condition operator "%s" and bool operator "OR" supported only if only one condition' % c['cri_operator'])
154) sfr.append({
155) 'field': 'from',
156) 'operator': r['operator'],
157) 'value': c['value']
158) })
159) if not options.dontwarnalladdresses:
160) logging.warning('Filter %s : Condition operator "all addresses" convert to two filters matching from, to or cc but not bcc field as Thunderbird' % f['name'])
161) r['field']='to_or_cc'
|
Add support to 'ALL' Thunde...
Benjamin Renard authored 10 years ago
|
162) elif c['cri_operand'].startswith('"') and c['cri_operand'].endswith('"'):
163) r['field']='header'
164) r['custom_header']=re.sub('^"(.*)"$',r'\1',c['cri_operand'],count=1)
165) else:
166) raise Exception('Condition operand "%s" not recognized. Pass' % c['cri_operand'])
|
Add support to 'all address...
Benjamin Renard authored 10 years ago
|
167) if c['bool_operator'] not in bool_ops:
168) bool_ops.append(c['bool_operator'])
|
Add support to 'ALL' Thunde...
Benjamin Renard authored 10 years ago
|
169) r['value']=c['value']
170) sfr.append(r)
171) if len(sfr)==0:
172) logging.error('Filter %s : No condition found ! Pass this filter' % f['name'])
173) continue
174) sf['rules']=sfr
175)
176) if len(bool_ops)==1:
177) if bool_ops[0]=='AND':
178) sf['match']='all'
179) elif bool_ops[0]=='OR':
180) sf['match']='any'
181) else:
182) logging.error("Filter %s : Boolean operator not recognized %s, pass this filter." % (f['name'],bool_ops[0]))
183) continue
|
Initial commit
Benjamin Renard authored 10 years ago
|
184) else:
|
Add support to 'ALL' Thunde...
Benjamin Renard authored 10 years ago
|
185) logging.error('Filter %s : Multiple boolean operator not supported (%s). Pass this filter.' % (f['name'],','.join(bool_ops)))
|