Created
January 14, 2015 09:32
-
-
Save jelly/359d34915f8064b9b8d0 to your computer and use it in GitHub Desktop.
WebAccess To WebApp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# MAPI imports | |
from MAPI import * | |
from MAPI.Util import * | |
import pprint | |
import re | |
import json | |
import sys | |
import sys | |
import codecs | |
try: | |
codecs.lookup_error('surrogateescape') | |
default_errors = 'surrogateescape' | |
except LookupError: | |
default_errors = 'strict' | |
try: | |
from StringIO import StringIO as BytesIO | |
except ImportError: | |
from io import BytesIO as BytesIO | |
try: | |
unicode | |
except NameError: | |
# Python 3 | |
unicode = str | |
basestring = (bytes, str) | |
try: | |
long | |
except NameError: | |
# Python 3 | |
long = int | |
try: | |
xrange | |
except NameError: | |
xrange = range | |
def load(fp, charset='utf-8', errors=default_errors, decode_strings=False, | |
object_hook=None, array_hook=None): | |
"""Read a string from the open file object `fp` and interpret it as a | |
data stream of PHP-serialized objects, reconstructing and returning | |
the original object hierarchy. | |
`fp` must provide a `read()` method that takes an integer argument. Both | |
method should return strings. Thus `fp` can be a file object opened for | |
reading, a `StringIO` object (`BytesIO` on Python 3), or any other custom | |
object that meets this interface. | |
`load` will read exactly one object from the stream. See the docstring of | |
the module for this chained behavior. | |
If an object hook is given object-opcodes are supported in the serilization | |
format. The function is called with the class name and a dict of the | |
class data members. The data member names are in PHP format which is | |
usually not what you want. The `simple_object_hook` function can convert | |
them to Python identifier names. | |
If an `array_hook` is given that function is called with a list of pairs | |
for all array items. This can for example be set to | |
`collections.OrderedDict` for an ordered, hashed dictionary. | |
""" | |
if array_hook is None: | |
array_hook = dict | |
def _expect(e): | |
v = fp.read(len(e)) | |
if v != e: | |
raise ValueError('failed expectation, expected %r got %r' % (e, v)) | |
def _read_until(delim): | |
buf = [] | |
while 1: | |
char = fp.read(1) | |
if char == delim: | |
break | |
elif not char: | |
raise ValueError('unexpected end of stream') | |
buf.append(char) | |
return b''.join(buf) | |
def _load_array(): | |
items = int(_read_until(b':')) * 2 | |
_expect(b'{') | |
result = [] | |
last_item = Ellipsis | |
for idx in xrange(items): | |
item = _unserialize() | |
if last_item is Ellipsis: | |
last_item = item | |
else: | |
result.append((last_item, item)) | |
last_item = Ellipsis | |
_expect(b'}') | |
return result | |
def _unserialize(): | |
type_ = fp.read(1).lower() | |
if type_ == b'n': | |
_expect(b';') | |
return None | |
if type_ in b'idb': | |
_expect(b':') | |
data = _read_until(b';') | |
if type_ == b'i': | |
return int(data) | |
if type_ == b'd': | |
return float(data) | |
return int(data) != 0 | |
if type_ == b's': | |
_expect(b':') | |
length = int(_read_until(b':')) | |
_expect(b'"') | |
data = fp.read(length) | |
_expect(b'"') | |
if decode_strings: | |
data = data.decode(charset, errors) | |
_expect(b';') | |
return data | |
if type_ == b'a': | |
_expect(b':') | |
return array_hook(_load_array()) | |
if type_ == b'o': | |
if object_hook is None: | |
raise ValueError('object in serialization dump but ' | |
'object_hook not given.') | |
_expect(b':') | |
name_length = int(_read_until(b':')) | |
_expect(b'"') | |
name = fp.read(name_length) | |
_expect(b'":') | |
if decode_strings: | |
name = name.decode(charset, errors) | |
return object_hook(name, dict(_load_array())) | |
raise ValueError('unexpected opcode') | |
return _unserialize() | |
def loads(data, charset='utf-8', errors=default_errors, decode_strings=False, | |
object_hook=None, array_hook=None): | |
"""Read a PHP-serialized object hierarchy from a string. Characters in the | |
string past the object's representation are ignored. On Python 3 the | |
string must be a bytestring. | |
""" | |
return load(BytesIO(data), charset, errors, decode_strings, | |
object_hook, array_hook) | |
def check_input(): | |
if len(sys.argv) < 2: | |
sys.exit('Usage: %s username' % sys.argv[0]) | |
def read_settings(data = None): | |
settings = None | |
if not data: | |
PR_EC_WEBACCESS_SETTINGS = PROP_TAG(PT_STRING8, PR_EC_BASE+0x70) | |
s = OpenECSession(sys.argv[1], '', 'file:///var/run/zarafa') | |
st = GetDefaultStore(s) | |
try: | |
settings = st.OpenProperty(PR_EC_WEBACCESS_SETTINGS, IID_IStream, 0, 0) | |
data = settings.Read(4096) | |
except: | |
return {} | |
# Somehow the serialized data can be corrupt, which means that for example ";s:3":"word" | |
# is invalid which the phpserialize library does not parse, so we need to replace these | |
# entries manually with the correct length. | |
result = re.findall('s:(\d+):"(.*?)"', data) | |
for r in result: | |
if int(r[0]) != len(r[1]): | |
data = data.replace('s:%s:"%s"' % (r[0], r[1]), 's:%s:"%s"' % (len(r[1]), r[1])) | |
return loads(data) | |
def write_webapp_settings(data): | |
s = OpenECSession(sys.argv[1], '', 'file:///var/run/zarafa') | |
st = GetDefaultStore(s) | |
PR_EC_WEBACCESS_SETTINGS_JSON = PROP_TAG(PT_STRING8, PR_EC_BASE+0x72) | |
settings = st.OpenProperty(PR_EC_WEBACCESS_SETTINGS_JSON, IID_IStream, 0, MAPI_MODIFY|MAPI_CREATE) | |
settings.SetSize(0) | |
settings.Seek(0, STREAM_SEEK_END) | |
writesettings = settings.Write(data) | |
if writesettings: | |
print "Settings for user '%s' were applied." % sys.argv[1] | |
else: | |
print "Settings for user '%s' failed to be applied." % sys.argv[1] | |
settings.Commit(0) | |
if __name__ == '__main__': | |
check_input() | |
debug = False | |
if debug: | |
pp = pprint.PrettyPrinter(indent=4) | |
if len(sys.argv) > 3: | |
webaccess_dict = read_settings(open(sys.argv[2]).read().rstrip()) | |
else: | |
webaccess_dict = read_settings() | |
if webaccess_dict == {}: | |
print 'No webaccess settings for user' | |
sys.exit(1) | |
if debug: | |
pp.pprint(webaccess_dict) | |
webaccess_dict = webaccess_dict['settings'] | |
webapp_settings_full = json.loads('{"settings":{"zarafa":{"v1":{"main":{"language":"nl_NL.UTF-8","default_context":"mail","start_working_hour":540,"end_working_hour":1020,"week_start":1,"show_welcome":false},"contexts":{"mail":{},"calendar":{"default_zoom_level":30,"datepicker_show_busy":true}},"state":{"models":{"note":{"current_data_mode":0}},"contexts":{"mail":{"current_view":0,"current_view_mode":1}}}}}}}') | |
if debug: | |
pp.pprint(webapp_settings_full) | |
webapp_settings = webapp_settings_full['settings']['zarafa']['v1'] | |
# CALENDAR | |
webapp_calendar = webapp_settings['contexts']['calendar'] | |
if 'calendar' in webaccess_dict: | |
webapp_calendar['default_reminder'] = webaccess_dict['calendar']['reminder'] # reminder true/false | |
webapp_calendar['default_reminder_time'] = webaccess_dict['calendar']['reminder_minutes'] # reminder minutes | |
#webapp_calendar['default_allday_reminder_time'] = webaccess_dict['calendar']['reminder'] # ??? not sure if this exists in webaccess | |
webapp_settings['main']['end_working_hour'] = webaccess_dict['calendar']['workdayend'] | |
webapp_settings['main']['start_working_hour'] = webaccess_dict['calendar']['workdaystart'] | |
#webapp_calendar['default_zoom_level'] webaccess_dict['calendar']['']# calendar resolution | |
# GENERAL | |
webapp_settings['main']['language'] = webaccess_dict['global']['language'] # language | |
# keyboard shortcuts | |
if 'shortcuts' in webaccess_dict['global']: | |
if webaccess_dict['global']['shortcuts']['enabled']: # True or empty string | |
webapp_settings['main']['keycontrols_enabled'] = True | |
# Location of preview pane | |
# Webapp 0 => Off, 1 => Right, 2 => Bottom | |
if 'previewpane' in webaccess_dict['global']: | |
if webaccess_dict['global']['previewpane'] == 'right': | |
webapp_settings['state']['contexts']['mail']['current_view_mode'] = 1 | |
elif webaccess_dict['global']['previewpane'] == 'bottom': | |
webapp_settings['state']['contexts']['mail']['current_view_mode'] = 2 | |
else: | |
webapp_settings['state']['contexts']['mail']['current_view_mode'] = 0 | |
# TODO: startup folder | |
#if webaccess_dict['global']['startup']['folder'] != 'inbox': | |
# read receipt: webapp "always"/"never"/"ask" | |
if 'readreceipt_handling' in webaccess_dict['global']: | |
webapp_settings['contexts']['mail']['readreceipt_handling'] = webaccess_dict['global']['readreceipt_handling'] | |
# mark mails as read after | |
if 'mail_readflagtime' in webaccess_dict['global']: | |
webapp_settings['contexts']['mail']['readflag_time'] = webaccess_dict['global']['mail_readflagtime'] | |
# autosave email | |
if 'autosave' in webaccess_dict['createmail']: | |
if webaccess_dict['createmail']['autosave']: | |
# Webaccess saves autosave interval in minutes, WebApp in seconds. | |
webapp_settings['contexts']['mail']['autosave_time'] = webaccess_dict['createmail']['autosave_interval'] * 60 | |
# EDITOR | |
# Create part of tree | |
webapp_settings['contexts']['mail']['dialogs'] = {'mailcreate': {}} | |
if 'mailformat' in webaccess_dict['createmail']: | |
webapp_settings['contexts']['mail']['dialogs']['mailcreate']['use_html_editor'] = webaccess_dict['createmail']['mailformat'] | |
# SIGNATURES | |
if webaccess_dict['createmail']['signatures']: | |
sigs = webaccess_dict['createmail']['signatures']['id_lookup'].split(';') | |
webapp_settings['contexts']['mail'] = {'signatures': { 'all': {}}} | |
for sig in sigs: | |
# content / type / name | |
tmp = webaccess_dict['createmail']['signatures'][int(sig)] | |
webapp_settings['contexts']['mail']['signatures']['all'][int(sig)] = { 'name': tmp['name'], 'content': tmp['content'], 'isHTML': tmp['type'] == 'html' } | |
webapp_settings['contexts']['mail']['signatures']['new_message'] = webaccess_dict['createmail']['signatures']['select_sig_newmsg'] or 0 | |
webapp_settings['contexts']['mail']['signatures']['replyforward_message'] = webaccess_dict['createmail']['signatures']['select_sig_replyfwd'] or 0 | |
# FONT | |
#if webaccess_dict['createmail']['maildefaultfont'] == 'Tahoma': | |
# ADDRESSBOOK | |
# Default selected folder/addressbook | |
#webapp_settings['main']['default_addressbook'] = webaccess_dict['addressbook']['default'] | |
# Write settings | |
write_webapp_settings(json.dumps(webapp_settings_full)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment