Skip to content

Instantly share code, notes, and snippets.

@costastf
Created September 21, 2017 08:44
Show Gist options
  • Save costastf/c5fc1dd0f73e566d312e7d6c5484b140 to your computer and use it in GitHub Desktop.
Save costastf/c5fc1dd0f73e566d312e7d6c5484b140 to your computer and use it in GitHub Desktop.
Legacy code to translate a thunderbird msgFilterRules.dat to gmail.xml format
#!/usr/bin/env python2.7
# -*- coding: UTF-8 -*-
# File: moz_filter_to_gmail
#
__author__ = 'Costas Tyfoxylos <costas.tyf@gmail.com>'
__docformat__ = 'plaintext'
__date__ = '2015-04-14'
import sys
import logging
import argparse
import os
from string import Template
# This is the main prefix used for logging
logger_basename = 'thunderbird2gmail'
logger = logging.getLogger(logger_basename)
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# create formatter
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
logger.setLevel(logging.INFO)
class Filter(object):
def __init__(self, filter_condition):
self.properties = []
self.active = True
self.label = ''
self.name = ''
self.render_filter(filter_condition)
def render_filter(self, filter_condition):
if not all(text in filter_condition for text in
['name', 'actionValue', 'condition']):
self.active = False
return False
self.name = self.__get_name(filter_condition)
self.label = self.__get_label(filter_condition)
self.properties = self.__get_properties(filter_condition)
@staticmethod
def __get_name(filter_condition):
name = ''
for line in filter_condition.splitlines():
if line.startswith('name'):
name = line.split('"')[1]
break
return name
@staticmethod
def __get_properties(filter_condition):
found = False
filter_type = ''
properties = []
for line in filter_condition.splitlines():
if line.startswith('condition'):
condition = line.split('"')[1]
break
if 'AND' in condition:
conditions = condition.split('AND ')[1:]
filter_type = '&&'
elif 'OR' in condition:
conditions = condition.split('OR ')[1:]
filter_type = '||'
for condition in conditions:
condition = condition.replace('(', '').replace(')',
'').strip().split(
',')
if not properties:
properties.append([condition[0], condition[2]])
else:
for prop in properties:
if condition[0] == prop[0]:
prop[1] = '{property} {type} {condition}'.format(
property=prop[1],
type=filter_type,
condition=condition[2])
found = True
if not found:
properties.append([condition[0], condition[2]])
return properties
@staticmethod
def __get_label(filter_condition):
value = ''
for line in filter_condition.splitlines():
if line.startswith('actionValue'):
value = line.split('//')[1].partition('/')[2].replace('"', '')
break
return value
class MozToGmail(object):
def __init__(self, filter_file):
self.filters = self.__read_file(filter_file)
self.records = []
self.email = ''
self.name = ''
self.__populate()
self.root_template = '''<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>
<title>Mail Filters</title>
<id>tag:mail.google.com,2008:filters:$ids</id>
<updated>2015-04-14T13:17:46Z</updated>
<author>
<name>$name</name>
<email>$email</email>
</author>
$entries
</feed>'''
self.entry_template = '''<entry>
<category term='filter'></category>
<title>Mail Filter</title>
<id>tag:mail.google.com,2008:filter:$id</id>
<updated>2015-04-14T13:17:46Z</updated>
<content></content>
$properties
<apps:property name='label' value='$label'/>
<apps:property name='shouldArchive' value='true'/>
<apps:property name='sizeOperator' value='s_sl'/>
<apps:property name='sizeUnit' value='s_smb'/>
</entry>
'''
self.property_template = "<apps:property name='$name' value='$value'/>"
@staticmethod
def __read_file(filter_file):
try:
filters = open(filter_file).read()
except Exception:
logger.error('Unable to read file')
raise SystemExit(1)
return filters
def __populate(self):
for line in self.filters.splitlines():
if line.startswith('actionValue="imap'):
self.email = line.split('//')[1].split('@')[0].replace('%40',
'@')
self.name = self.email.split('@')[0]
break
for filter in self.filters.split('\nname'):
record = Filter('\nname' + filter)
if record.active:
self.records.append(record)
def render(self, file_path):
ids = ','.join([str(num) for num in range(len(self.records))])
entries = ''
properties = ''
for index, record in enumerate(self.records):
entry = Template(self.entry_template)
property = Template(self.property_template)
for item in record.properties:
properties += property.substitute(name=item[0], value=item[1])
entries += entry.substitute(id=index, properties=properties,
label=record.label)
properties = ''
xml = Template(self.root_template)
text = xml.substitute(ids=ids, name=self.name, email=self.email,
entries=entries)
with open(os.path.join(file_path, 'mailFilters.xml'), 'w') as out_file:
out_file.write(text)
out_file.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='thunderbird to gmail filter converter')
parser.add_argument('--input', '-i',
dest='input',
action='store',
help="thunderbird msgFilterRules.dat file",
required=True)
parser.add_argument('--output', '-o',
dest='output',
action='store',
help="path to save the google xml",
required=True)
args = parser.parse_args()
filters = MozToGmail(args.input)
filters.render(args.output)
@spotlesscoder
Copy link

Is there a license so I can reuse it?

@costastf
Copy link
Author

costastf commented Nov 3, 2019 via email

@spotlesscoder
Copy link

Thx

@mlocati
Copy link

mlocati commented Feb 4, 2021

Why is this "legacy"? Is there a newer version?

@costastf
Copy link
Author

costastf commented Feb 4, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment