#!/usr/bin/python # Copyright (C) 2004 Henning Jacobs # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # $Id: CoCuMa_Server.py 92 2004-11-28 15:34:44Z henning $ import SimpleXMLRPCServer, SocketServer from types import * import time import os import vcard import vcalendar from __version__ import __version__ import sys, signal import Preferences import debug import broadcaster class XMLRPCServer(SocketServer.TCPServer, SimpleXMLRPCServer.SimpleXMLRPCDispatcher): """Overridden SimpleXMLRPCServer We want allow_reuse_address==True""" def __init__(self, addr, requestHandler=SimpleXMLRPCServer.SimpleXMLRPCRequestHandler, logRequests=1): self.logRequests = logRequests self.allow_reuse_address = 1 SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self) SocketServer.TCPServer.__init__(self, addr, requestHandler) class CoCuMa_Server: def __init__(self, addressbook_fname, calendar_fname): self._cards_modified = False self._cal_modified = False self._addressbook_filename = addressbook_fname self._calendar_filename = calendar_fname broadcaster.Broadcast('Notification', 'Status', {'message':"Loading vCards from file '%s'..." % (self._addressbook_filename,)}) self._vcards = vcard.vCardList() self._vcards.LoadFromFile(self._addressbook_filename) broadcaster.Broadcast('Notification', 'Status', {'message':"Loading iCalendar from file '%s'..." % (self._calendar_filename,)}) self._vcalendar = vcalendar.vCalendar() self._vcalendar.LoadFromFile(self._calendar_filename) def _writeToDisk(self): "save our vcards and our vcalendar on disk" if self._cards_modified: self._vcards.SaveToFile(self._addressbook_filename) if self._cal_modified: self._vcalendar.SaveToFile(self._calendar_filename) def _shutdown(self): "Server Stop" # saving is now done on session exit: # Redundant: self._writeToDisk() pass def _signal_handler(self, signalnum, stackframe): "Trap Unix Signals" if signalnum == signal.SIGTERM: self._shutdown() sys.exit(0) def SessionInit(self): "Send My Identification to Client" return "CoCuMa_Server "+__version__ def SessionQuit(self): "Exit Session and save to disk" self._writeToDisk() return True def ListHandles(self, sortby=None): """Returns list of vCard Handles sortby is the fieldname to order by""" return self._vcards.sortedlist(sortby) def ListJournalHandles(self, sortby): "Returns list of vCalendar Handles" return self._vcalendar.sortedlist(sortby) def QueryAttributes(self, handles, attributes): """Returns a list of tuples of attribute values or a single list of strings if attributes is not a list or tuple""" return self._queryAttributes(self._vcards, handles, attributes) def QueryJournalAttributes(self, handles, attributes): """Returns a list of tuples of attribute values or a single list of strings if attributes is not a list or tuple""" return self._queryAttributes(self._vcalendar, handles, attributes) def _queryAttributes(self, vobj, handles, attributes): """Returns a list of tuples of attribute values or a single list of strings if attributes is not a list or tuple""" ret = [] try: if type(attributes)==ListType or type(attributes)==TupleType: for handle in handles: attrvals = [] for attr in attributes: attrvals.append(vobj[handle].getFieldValueStr(attr)) ret.append(tuple(attrvals)) else: for handle in handles: ret.append(vobj[handle].getFieldValueStr(attributes)) vobj.forgetLastReturnedField() finally: return ret def GetContact(self, handle): "Returns Contact as vCard" return self._vcards[handle].VCF_repr() def PutContact(self, handle, data): "Store Contact (overwrite)" self._vcards[handle] = vcard.vCard(data) # Our modified Contact lost its Handle: self._vcards[handle].sethandle(handle) # verify that our storing was correct: verify = self._vcards[handle].VCF_repr() == data if verify: # Update our Revision Date: self._vcards[handle].rev.set(time.gmtime()) self._cards_modified = True return verify def NewContact(self, initdict={}): "Create new vCard and return it's handle" card = vcard.vCard() # Set initial dictionary: for key, value in zip(initdict.keys(), initdict.values()): getattr(card, key).set(value) self._cards_modified = True return self._vcards.add(card) def DelContact(self, handle): "Delete Contact" self._cards_modified = True return self._vcards.delete(handle) def GetJournal(self, handle): "Returns Calendar Entry as vEvent" # Update our TimeStamp: self._vcalendar[handle].dtstamp.set(time.gmtime()) return self._vcalendar[handle].VCF_repr() def PutJournal(self, handle, data): "Store Calendar Entry (overwrite)" self._vcalendar[handle] = vcalendar.vEvent(data) # Our modified Journal lost its Handle: self._vcalendar[handle].sethandle(handle) # verify that our storing was correct: verify = self._vcalendar[handle].VCF_repr() == data if verify: # Update our Revision Date: self._vcalendar[handle].last_mod.set(time.gmtime()) self._cal_modified = True else: debug.echo("WARNING: CoCuMa_Server.PutJournal(): Verify Failed for #%d:" % handle) debug.echo(self._vcalendar[handle].VCF_repr()) return verify def NewJournal(self, initdict={}): "Create new vEvent and return it's handle" jour = vcalendar.vEvent() # Set initial dictionary: for key, value in zip(initdict.keys(), initdict.values()): getattr(jour, key).set(value) self._cal_modified = True return self._vcalendar.add(jour) def DelJournal(self, handle): "Delete Calendar Entry" self._cards_modified = True return self._vcalendar.delete(handle) class LogFile: "file-like class, appends to a file, or does nothing" def __init__(self, filename): self.fd = None if filename: self.fd = file(filename, 'ab') def write(self, obj): if self.fd: self.fd.write(obj) self.fd.flush() def run(): Preferences.Load() import getopt optlist, args = getopt.getopt(sys.argv[1:], "h:p:f:j:") for key, val in optlist: # Command Line Arguments override Preferences: if key == "-p": Preferences.set("server.listen_port",val) if key == "-h": Preferences.set("server.listen_host",val) if key == "-f": Preferences.set("server.addressbook_filename",val) if key == "-j": Preferences.set("server.calendar_filename",val) import socket try: xmlsrv = XMLRPCServer((Preferences.get("server.listen_host"), int(Preferences.get("server.listen_port")))) except socket.error: # Try to connect to ourself (check if we are already running): import CoCuMa_Client conn_str = "http://%s:%d" % (Preferences.get("server.listen_host"), int(Preferences.get("server.listen_port"))) client = CoCuMa_Client.CoCuMa_XMLRPCClient() if client.Connect(conn_str): sys.exit("CoCuMa_Server.run(): I'm already running. Terminating!") else: raise # Unhandled/unknown Error # Create our Server-Object: server = CoCuMa_Server(addressbook_fname = os.path.expanduser( Preferences.get("server.addressbook_filename")), calendar_fname = os.path.expanduser( Preferences.get("server.calendar_filename"))) # Register our public (XML) accessible Methods: xmlsrv.register_instance(server) # Trap UNIX-Signals: signal.signal(signal.SIGTERM, server._signal_handler) # Redirect Stderr and Stdout: log = LogFile(Preferences.get("server.log_filename")) sys.stderr = log sys.stdout = log xmlsrv.serve_forever() if __name__=='__main__': run()