3ac3091d2f3c96f626ddca1a32c680e55c789515
Benjamin Renard 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)
Benjamin Renard Add support to 'all address...

Benjamin Renard authored 9 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)
Benjamin Renard 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) 
Benjamin Renard Add support to 'ALL' Thunde...

Benjamin Renard authored 9 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'
Benjamin Renard Add support to 'doesn't con...

Benjamin Renard authored 9 years ago

127) 				elif c['cri_operator']=="doesn't contain":
128) 					r['operator']='contains_not'
Benjamin Renard Add support to 'ALL' Thunde...

Benjamin Renard authored 9 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']
Benjamin Renard Add support to 'to or cc' T...

Benjamin Renard authored 9 years ago

133) 				elif c['cri_operand']=='to or cc':
134) 					r['field']='to_or_cc'
Benjamin Renard Add support to 'ALL' Thunde...

Benjamin Renard authored 9 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'])
Benjamin Renard Add support to 'all address...

Benjamin Renard authored 9 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'
Benjamin Renard Add support to 'ALL' Thunde...

Benjamin Renard authored 9 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'])
Benjamin Renard Add support to 'all address...

Benjamin Renard authored 9 years ago

167) 				if c['bool_operator'] not in bool_ops:
168) 					bool_ops.append(c['bool_operator'])
Benjamin Renard Add support to 'ALL' Thunde...

Benjamin Renard authored 9 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
Benjamin Renard Initial commit

Benjamin Renard authored 10 years ago

184) 			else:
Benjamin Renard Add support to 'ALL' Thunde...

Benjamin Renard authored 9 years ago

185) 				logging.error('Filter %s : Multiple boolean operator not supported (%s). Pass this filter.' % (f['name'],','.join(bool_ops)))