[CalendarServer-changes] [4405] CalendarServer/branches/users/sagen/pending-invites-4403
source_changes at macosforge.org
source_changes at macosforge.org
Wed Jul 1 19:08:19 PDT 2009
Revision: 4405
http://trac.macosforge.org/projects/calendarserver/changeset/4405
Author: sagen at apple.com
Date: 2009-07-01 19:08:18 -0700 (Wed, 01 Jul 2009)
Log Message:
-----------
New approach to pending invites -- adding a new utility process that runs jobs on demand
Modified Paths:
--------------
CalendarServer/branches/users/sagen/pending-invites-4403/calendarserver/tap/caldav.py
CalendarServer/branches/users/sagen/pending-invites-4403/twisted/plugins/caldav.py
CalendarServer/branches/users/sagen/pending-invites-4403/twistedcaldav/scheduling/scheduler.py
Modified: CalendarServer/branches/users/sagen/pending-invites-4403/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/users/sagen/pending-invites-4403/calendarserver/tap/caldav.py 2009-07-02 02:05:56 UTC (rev 4404)
+++ CalendarServer/branches/users/sagen/pending-invites-4403/calendarserver/tap/caldav.py 2009-07-02 02:08:18 UTC (rev 4405)
@@ -18,6 +18,7 @@
"CalDAVService",
"CalDAVOptions",
"CalDAVServiceMaker",
+ "CalDAVUtilityServiceMaker",
]
import os
@@ -39,6 +40,7 @@
from twisted.python.usage import Options, UsageError
from twisted.python.reflect import namedClass
from twisted.plugin import IPlugin
+from twisted.internet.defer import DeferredList, succeed, inlineCallbacks, returnValue
from twisted.internet.reactor import callLater
from twisted.internet.process import ProcessExitedAlready
from twisted.internet.protocol import Protocol, Factory
@@ -1105,6 +1107,24 @@
monitor.addProcess("mailgateway", mailGatewayArgv, env=parentEnv)
+ self.log_info("Adding utility service")
+
+ utilityArgv = [
+ sys.executable,
+ config.Twisted.twistd,
+ ]
+ if config.UserName:
+ utilityArgv.extend(("-u", config.UserName))
+ if config.GroupName:
+ utilityArgv.extend(("-g", config.GroupName))
+ utilityArgv.extend((
+ "--reactor=%s" % (config.Twisted.reactor,),
+ "-n", "caldav_utility",
+ "-f", options["config"],
+ ))
+
+ monitor.addProcess("caldav_utility", utilityArgv, env=parentEnv)
+
stats = CalDAVStatisticsServer(logger)
statsService = UNIXServer(config.GlobalStatsSocket, stats)
statsService.setServiceParent(s)
@@ -1159,6 +1179,367 @@
self.log_warn("Deleting stale socket file (not accepting connections): %s" % checkSocket)
os.remove(checkSocket)
+
+from twistedcaldav.scheduling.cuaddress import LocalCalendarUser
+from twistedcaldav.scheduling.scheduler import DirectScheduler
+from twistedcaldav.ical import Component
+from twisted.web2.http_headers import Headers
+
+
+class FakeRequest(object):
+
+ def __init__(self, rootResource, method):
+ self.rootResource = rootResource
+ self.method = method
+ self._resourcesByURL = {}
+ self._urlsByResource = {}
+ self.headers = Headers()
+
+
+ @inlineCallbacks
+ def _getChild(self, resource, segments):
+ if not segments:
+ returnValue(resource)
+
+ child, remaining = (yield resource.locateChild(self, segments))
+ returnValue((yield self._getChild(child, remaining)))
+
+ @inlineCallbacks
+ def locateResource(self, url):
+ url = url.strip("/")
+ segments = url.split("/")
+ resource = (yield self._getChild(self.rootResource, segments))
+ if resource:
+ self._rememberResource(resource, url)
+ returnValue(resource)
+
+ def _rememberResource(self, resource, url):
+ self._resourcesByURL[url] = resource
+ self._urlsByResource[resource] = url
+ return resource
+
+ def urlForResource(self, resource):
+ url = self._urlsByResource.get(resource, None)
+ if url is None:
+ raise NoURLForResourceError(resource)
+ return url
+
+ def addResponseFilter(*args, **kwds):
+ pass
+
+ at inlineCallbacks
+def processInbox(rootResource, directory, inboxFile, uuid):
+ print "INSIDE PROCESS INBOX"
+ print rootResource, directory, inboxFile, uuid
+
+ principals = rootResource.getChild("principals")
+ ownerPrincipal = principals.principalForUID(uuid)
+ print "Owner principal", ownerPrincipal
+ cua = "urn:uuid:%s" % (uuid,)
+ owner = LocalCalendarUser(cua, ownerPrincipal,
+ inboxFile, ownerPrincipal.scheduleInboxURL())
+ print "Owner", owner
+
+ for name in inboxFile.listChildren():
+ icsFile = inboxFile.getChild(name)
+ data = icsFile.iCalendarText()
+ calendar = Component.fromString(data)
+ try:
+ method = calendar.propertyValue("METHOD")
+ except ValueError:
+ returnValue(None)
+
+ if method == "REPLY":
+ # originator is attendee sending reply
+ originator = calendar.getAttendees()[0]
+ else:
+ # originator is the organizer
+ originator = calendar.getOrganizer()
+
+ originatorPrincipal = principals.principalForCalendarUserAddress(originator)
+ originator = LocalCalendarUser(originator, originatorPrincipal)
+ recipients = (owner,)
+ scheduler = DirectScheduler(FakeRequest(rootResource, "PUT"), icsFile)
+ result = (yield scheduler.doSchedulingViaPUT(originator, recipients,
+ calendar, internal_request=False))
+
+ if os.path.exists(icsFile.fp.path):
+ os.remove(icsFile.fp.path)
+
+class UtilityJob(object):
+
+ def __init__(self, service, jobFile):
+ self.service = service
+ self.jobFile = jobFile
+
+ @inlineCallbacks
+ def run(self):
+ log.info("Running job %s" % (self.jobFile))
+
+ # Hardcoded job: process pending invites in all inboxes
+ calendars = self.service.root.getChild("calendars")
+ uidDir = calendars.getChild("__uids__")
+
+ for first in os.listdir(uidDir.fp.path):
+ if len(first) == 2:
+ firstPath = os.path.join(uidDir.fp.path, first)
+ for second in os.listdir(firstPath):
+ if len(second) == 2:
+ secondPath = os.path.join(firstPath, second)
+ for uuid in os.listdir(secondPath):
+ homeFile = uidDir.getChild(uuid)
+ inboxFile = homeFile.getChild("inbox")
+ if inboxFile:
+ yield processInbox(
+ self.service.root,
+ self.service.directory,
+ inboxFile,
+ uuid
+ )
+
+ os.remove(os.path.join(self.service.processingDir, self.jobFile))
+
+class CalDAVUtilityService(Service):
+
+ def __init__(self, root, directory):
+ self.root = root
+ self.directory = directory
+ self.seconds = 5
+ self.utilityDir = os.path.join(config.DataRoot, "utility")
+ # New job files are placed into "incoming"
+ self.incomingDir = os.path.join(self.utilityDir, "incoming")
+ # Job files get moved into "processing" and then removed when complete
+ self.processingDir = os.path.join(self.utilityDir, "processing")
+
+ def startService(self):
+ log.info("Starting utility service")
+
+ if not os.path.exists(self.utilityDir):
+ os.mkdir(self.utilityDir)
+ if not os.path.exists(self.incomingDir):
+ os.mkdir(self.incomingDir)
+ if not os.path.exists(self.processingDir):
+ os.mkdir(self.processingDir)
+
+ callLater(self.seconds, self.periodic, first=True)
+
+
+ def periodic(self, first=False):
+ deferreds = []
+
+ try:
+ log.info("PERIODIC, first=%s" % (first,))
+
+ if first:
+ # check the processing directory to see if there are any jobs
+ # that didn't complete during the last server run; start those
+ for child in os.listdir(self.processingDir):
+ deferreds.append(UtilityJob(self, child).run())
+
+ for child in os.listdir(self.incomingDir):
+ os.rename(os.path.join(self.incomingDir, child),
+ os.path.join(self.processingDir, child))
+ deferreds.append(UtilityJob(self, child).run())
+
+ finally:
+ callLater(self.seconds, self.periodic)
+
+ return DeferredList(deferreds)
+
+
+class CalDAVUtilityOptions(Options):
+ optParameters = [[
+ "config", "f", defaultConfigFile, "Path to configuration file."
+ ]]
+
+ def __init__(self, *args, **kwargs):
+ super(CalDAVUtilityOptions, self).__init__(*args, **kwargs)
+
+ self.overrides = {}
+
+ def _coerceOption(self, configDict, key, value):
+ """
+ Coerce the given C{val} to type of C{configDict[key]}
+ """
+ if key in configDict:
+ if isinstance(configDict[key], bool):
+ value = value == "True"
+
+ elif isinstance(configDict[key], (int, float, long)):
+ value = type(configDict[key])(value)
+
+ elif isinstance(configDict[key], (list, tuple)):
+ value = value.split(',')
+
+ elif isinstance(configDict[key], dict):
+ raise UsageError(
+ "Dict options not supported on the command line"
+ )
+
+ elif value == 'None':
+ value = None
+
+ return value
+
+ def _setOverride(self, configDict, path, value, overrideDict):
+ """
+ Set the value at path in configDict
+ """
+ key = path[0]
+
+ if len(path) == 1:
+ overrideDict[key] = self._coerceOption(configDict, key, value)
+ return
+
+ if key in configDict:
+ if not isinstance(configDict[key], dict):
+ raise UsageError(
+ "Found intermediate path element that is not a dictionary"
+ )
+
+ if key not in overrideDict:
+ overrideDict[key] = {}
+
+ self._setOverride(
+ configDict[key], path[1:],
+ value, overrideDict[key]
+ )
+
+
+ def opt_option(self, option):
+ """
+ Set an option to override a value in the config file. True, False, int,
+ and float options are supported, as well as comma seperated lists. Only
+ one option may be given for each --option flag, however multiple
+ --option flags may be specified.
+ """
+
+ if "=" in option:
+ path, value = option.split('=')
+ self._setOverride(
+ defaultConfig,
+ path.split('/'),
+ value,
+ self.overrides
+ )
+ else:
+ self.opt_option('%s=True' % (option,))
+
+ opt_o = opt_option
+
+ def postOptions(self):
+ config.loadConfig(self['config'])
+ config.updateDefaults(self.overrides)
+ self.parent['pidfile'] = None
+
+class CalDAVUtilityServiceMaker (LoggingMixIn):
+ implements(IPlugin, IServiceMaker)
+
+ tapname = "caldav_utility"
+ description = "Calendar Server Utility Process"
+ options = CalDAVUtilityOptions
+
+ #
+ # Default resource classes
+ #
+ rootResourceClass = RootResource
+ principalResourceClass = DirectoryPrincipalProvisioningResource
+ calendarResourceClass = CalendarHomeProvisioningFile
+ iScheduleResourceClass = IScheduleInboxFile
+ imipResourceClass = IMIPReplyInboxResource
+ timezoneServiceResourceClass = TimezoneServiceFile
+ webCalendarResourceClass = WebCalendarResource
+ webAdminResourceClass = WebAdminResource
+
+ def makeService(self, options):
+
+ print "CalDAVUtilityServiceMaker -- makeService"
+ #
+ # Change default log level to "info" as its useful to have
+ # that during startup
+ #
+ oldLogLevel = logLevelForNamespace(None)
+ setLogLevelForNamespace(None, "info")
+
+ #
+ # Setup the Directory
+ #
+ directories = []
+
+ directoryClass = namedClass(config.DirectoryService.type)
+
+ self.log_info("Configuring directory service of type: %s"
+ % (config.DirectoryService.type,))
+
+ directory = directoryClass(config.DirectoryService.params)
+
+ # Wait for the directory to become available
+ while not directory.isAvailable():
+ sleep(5)
+
+ #
+ # Configure Memcached Client Pool
+ #
+ if config.Memcached.ClientEnabled:
+ memcachepool.installPool(
+ IPv4Address(
+ "TCP",
+ config.Memcached.BindAddress,
+ config.Memcached.Port,
+ ),
+ config.Memcached.MaxClients,
+ )
+
+ #
+ # Configure NotificationClient
+ #
+ if config.Notifications.Enabled:
+ installNotificationClient(
+ config.Notifications.InternalNotificationHost,
+ config.Notifications.InternalNotificationPort,
+ )
+
+ #
+ # Setup Resource hierarchy
+ #
+ self.log_info("Setting up document root at: %s"
+ % (config.DocumentRoot,))
+ self.log_info("Setting up principal collection: %r"
+ % (self.principalResourceClass,))
+
+ principalCollection = self.principalResourceClass(
+ "/principals/",
+ directory,
+ )
+
+ self.log_info("Setting up calendar collection: %r"
+ % (self.calendarResourceClass,))
+
+ calendarCollection = self.calendarResourceClass(
+ os.path.join(config.DocumentRoot, "calendars"),
+ directory, "/calendars/",
+ )
+
+ self.log_info("Setting up root resource: %r"
+ % (self.rootResourceClass,))
+
+ root = self.rootResourceClass(
+ config.DocumentRoot,
+ principalCollections=(principalCollection,),
+ )
+
+ root.putChild("principals", principalCollection)
+ root.putChild("calendars", calendarCollection)
+
+ service = CalDAVUtilityService(root, directory)
+
+ # Change log level back to what it was before
+ setLogLevelForNamespace(None, oldLogLevel)
+
+ return service
+
+
+
class TwistdSlaveProcess(object):
prefix = "caldav"
Modified: CalendarServer/branches/users/sagen/pending-invites-4403/twisted/plugins/caldav.py
===================================================================
--- CalendarServer/branches/users/sagen/pending-invites-4403/twisted/plugins/caldav.py 2009-07-02 02:05:56 UTC (rev 4404)
+++ CalendarServer/branches/users/sagen/pending-invites-4403/twisted/plugins/caldav.py 2009-07-02 02:08:18 UTC (rev 4405)
@@ -34,5 +34,6 @@
TwistedCalDAV = TAP("calendarserver.tap.caldav.CalDAVServiceMaker")
+CalDAVUtility = TAP("calendarserver.tap.caldav.CalDAVUtilityServiceMaker")
CalDAVNotifier = TAP("twistedcaldav.notify.NotificationServiceMaker")
CalDAVMailGateway = TAP("twistedcaldav.mail.MailGatewayServiceMaker")
Modified: CalendarServer/branches/users/sagen/pending-invites-4403/twistedcaldav/scheduling/scheduler.py
===================================================================
--- CalendarServer/branches/users/sagen/pending-invites-4403/twistedcaldav/scheduling/scheduler.py 2009-07-02 02:05:56 UTC (rev 4404)
+++ CalendarServer/branches/users/sagen/pending-invites-4403/twistedcaldav/scheduling/scheduler.py 2009-07-02 02:08:18 UTC (rev 4405)
@@ -55,6 +55,7 @@
"CalDAVScheduler",
"IScheduleScheduler",
"IMIPScheduler",
+ "DirectScheduler",
]
@@ -386,7 +387,7 @@
# Now process iMIP recipients
if imip_recipients:
yield self.generateIMIPSchedulingResponses(imip_recipients, responses, freebusy)
-
+
# Return with final response if we are done
returnValue(responses)
@@ -789,6 +790,31 @@
log.err(msg)
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"), description=msg))
+
+class DirectScheduler(Scheduler):
+
+ def checkAuthorization(self):
+ pass
+
+ def checkOrganizer(self):
+ pass
+
+ def checkOrganizerAsOriginator(self):
+ pass
+
+ def checkAttendeeAsOriginator(self):
+ pass
+
+ def securityChecks(self):
+ pass
+
+ def checkOriginator(self):
+ pass
+
+ def checkRecipients(self):
+ pass
+
+
class IMIPScheduler(Scheduler):
def checkAuthorization(self):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090701/b6e3fb62/attachment-0001.html>
More information about the calendarserver-changes
mailing list