[CalendarServer-changes] [4297] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue May 19 14:28:47 PDT 2009
Revision: 4297
http://trac.macosforge.org/projects/calendarserver/changeset/4297
Author: sagen at apple.com
Date: 2009-05-19 14:28:46 -0700 (Tue, 19 May 2009)
Log Message:
-----------
Mail gateway's /inbox now requires authentication
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/tap/caldav.py
CalendarServer/trunk/twistedcaldav/mail.py
CalendarServer/trunk/twistedcaldav/scheduling/imip.py
CalendarServer/trunk/twistedcaldav/util.py
Modified: CalendarServer/trunk/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/caldav.py 2009-05-19 21:02:35 UTC (rev 4296)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py 2009-05-19 21:28:46 UTC (rev 4297)
@@ -75,7 +75,7 @@
from twistedcaldav.static import CalendarHomeProvisioningFile
from twistedcaldav.static import IScheduleInboxFile
from twistedcaldav.static import TimezoneServiceFile
-from twistedcaldav.mail import IMIPInboxResource
+from twistedcaldav.mail import IMIPReplyInboxResource
from twistedcaldav.timezones import TimezoneCache
from twistedcaldav.upgrade import upgradeData
from twistedcaldav.pdmonster import PDClientAddressWrapper
@@ -339,7 +339,7 @@
principalResourceClass = DirectoryPrincipalProvisioningResource
calendarResourceClass = CalendarHomeProvisioningFile
iScheduleResourceClass = IScheduleInboxFile
- imipResourceClass = IMIPInboxResource
+ imipResourceClass = IMIPReplyInboxResource
timezoneServiceResourceClass = TimezoneServiceFile
webCalendarResourceClass = WebCalendarResource
webAdminResourceClass = WebAdminResource
Modified: CalendarServer/trunk/twistedcaldav/mail.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/mail.py 2009-05-19 21:02:35 UTC (rev 4296)
+++ CalendarServer/trunk/twistedcaldav/mail.py 2009-05-19 21:28:46 UTC (rev 4297)
@@ -20,36 +20,46 @@
"""
from __future__ import with_statement
+from calendarserver.provision.root import RootResource
+
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from twisted.application import internet, service
+from twisted.cred.portal import Portal
from twisted.internet import protocol, defer, ssl, reactor
+from twisted.internet.address import IPv4Address
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twisted.mail import pop3client, imap4
from twisted.mail.smtp import messageid, rfc822date, ESMTPSenderFactory
from twisted.plugin import IPlugin
-from twisted.python.usage import Options, UsageError
from twisted.python import failure
-from twisted.web import resource, server, client
-from twisted.web2 import responsecode
+from twisted.python.reflect import namedClass
+from twisted.python.usage import Options, UsageError
+from twisted.web import client
+from twisted.web2 import resource, server, responsecode
+from twisted.web2.channel.http import HTTPFactory
+from twisted.web2.dav import auth
from twisted.web2.dav import davxml
from twisted.web2.dav.noneprops import NonePropertyStore
from twisted.web2.http import Response, HTTPError
from twisted.web2.http_headers import MimeType
-from twistedcaldav.directory.digest import QopDigestCredentialFactory
from twistedcaldav import ical, caldavxml
+from twistedcaldav import memcachepool
from twistedcaldav.config import config, defaultConfig, defaultConfigFile
+from twistedcaldav.directory.digest import QopDigestCredentialFactory
+from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
+from twistedcaldav.directory.util import NotFilePath
from twistedcaldav.ical import Property
+from twistedcaldav.localization import translationTo
from twistedcaldav.log import Logger, LoggingMixIn
-from twistedcaldav.directory.util import NotFilePath
-from twistedcaldav.scheduling.scheduler import IMIPScheduler
from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
-from twistedcaldav.static import CalDAVFile, deliverSchedulePrivilegeSet
+from twistedcaldav.scheduling.scheduler import IMIPScheduler
from twistedcaldav.sql import AbstractSQLDatabase
-from twistedcaldav.localization import translationTo
+from twistedcaldav.static import CalDAVFile, deliverSchedulePrivilegeSet
+from twistedcaldav.util import AuthorizedHTTPGetter
from zope.interface import implements
@@ -57,8 +67,6 @@
import email.utils
import os
import uuid
-from hashlib import md5, sha1
-import base64
try:
from cStringIO import StringIO
@@ -251,22 +259,6 @@
response.headers.setHeader("content-type", MimeType("text", "html"))
return response
- @inlineCallbacks
- def http_POST(self, request):
- """
- The IMIP delivery POST method.
- """
-
- # Check authentication and access controls
- yield self.authorize(request, (caldavxml.ScheduleDeliver(),))
-
- # Inject using the IMIPScheduler.
- scheduler = IMIPScheduler(request, self)
-
- # Do the POST processing treating this as a non-local schedule
- result = (yield scheduler.doSchedulingViaPOST(use_request_headers=True))
- returnValue(result.response())
-
##
# File
##
@@ -299,230 +291,54 @@
return succeed(deliverSchedulePrivilegeSet)
+class IMIPReplyInboxResource(IMIPInboxResource):
+ @inlineCallbacks
+ def http_POST(self, request):
+ """
+ The IMIP reply POST method (inbound)
+ """
-algorithms = {
- 'md5': md5,
- 'md5-sess': md5,
- 'sha': sha1,
-}
+ # Check authentication and access controls
+ yield self.authorize(request, (caldavxml.ScheduleDeliver(),))
-# DigestCalcHA1
-def calcHA1(
- pszAlg,
- pszUserName,
- pszRealm,
- pszPassword,
- pszNonce,
- pszCNonce,
- preHA1=None
-):
- """
- @param pszAlg: The name of the algorithm to use to calculate the digest.
- Currently supported are md5 md5-sess and sha.
+ # Inject using the IMIPScheduler.
+ scheduler = IMIPScheduler(request, self)
- @param pszUserName: The username
- @param pszRealm: The realm
- @param pszPassword: The password
- @param pszNonce: The nonce
- @param pszCNonce: The cnonce
+ # Do the POST processing treating this as a non-local schedule
+ result = (yield scheduler.doSchedulingViaPOST(use_request_headers=True))
+ returnValue(result.response())
- @param preHA1: If available this is a str containing a previously
- calculated HA1 as a hex string. If this is given then the values for
- pszUserName, pszRealm, and pszPassword are ignored.
- """
- if (preHA1 and (pszUserName or pszRealm or pszPassword)):
- raise TypeError(("preHA1 is incompatible with the pszUserName, "
- "pszRealm, and pszPassword arguments"))
+class IMIPInvitationInboxResource(IMIPInboxResource):
- if preHA1 is None:
- # We need to calculate the HA1 from the username:realm:password
- m = algorithms[pszAlg]()
- m.update(pszUserName)
- m.update(":")
- m.update(pszRealm)
- m.update(":")
- m.update(pszPassword)
- HA1 = m.digest()
- else:
- # We were given a username:realm:password
- HA1 = preHA1.decode('hex')
+ def __init__(self, parent, mailer):
+ super(IMIPInvitationInboxResource, self).__init__(parent)
+ self.mailer = mailer
- if pszAlg == "md5-sess":
- m = algorithms[pszAlg]()
- m.update(HA1)
- m.update(":")
- m.update(pszNonce)
- m.update(":")
- m.update(pszCNonce)
- HA1 = m.digest()
+ @inlineCallbacks
+ def http_POST(self, request):
+ """
+ The IMIP invitation POST method (outbound)
+ """
- return HA1.encode('hex')
+ # Check authentication and access controls
+ yield self.authorize(request, (caldavxml.ScheduleDeliver(),))
-# DigestCalcResponse
-def calcResponse(
- HA1,
- algo,
- pszNonce,
- pszNonceCount,
- pszCNonce,
- pszQop,
- pszMethod,
- pszDigestUri,
- pszHEntity,
-):
- m = algorithms[algo]()
- m.update(pszMethod)
- m.update(":")
- m.update(pszDigestUri)
- if pszQop == "auth-int":
- m.update(":")
- m.update(pszHEntity)
- HA2 = m.digest().encode('hex')
+ # Compute token, add to db, generate email and send it
+ calendar = (yield ical.Component.fromIStream(request.stream))
+ originator = request.headers.getRawHeaders("originator")[0]
+ recipient = request.headers.getRawHeaders("recipient")[0]
+ language = config.Localization.Language
- m = algorithms[algo]()
- m.update(HA1)
- m.update(":")
- m.update(pszNonce)
- m.update(":")
- if pszNonceCount and pszCNonce and pszQop:
- m.update(pszNonceCount)
- m.update(":")
- m.update(pszCNonce)
- m.update(":")
- m.update(pszQop)
- m.update(":")
- m.update(HA2)
- respHash = m.digest().encode('hex')
- return respHash
+ if not (yield self.mailer.outbound(originator,
+ recipient, calendar, language=language)):
+ returnValue(Response(code=responsecode.BAD_REQUEST))
-class Unauthorized(Exception):
- pass
+ returnValue(Response(code=responsecode.OK))
-class AuthorizedHTTPGetter(client.HTTPPageGetter, LoggingMixIn):
- def handleStatus_401(self):
- self.quietLoss = 1
- self.transport.loseConnection()
-
- if not hasattr(self.factory, "username"):
- self.factory.deferred.errback(failure.Failure(Unauthorized("Mail gateway not able to process reply; authentication required for calendar server")))
- return self.factory.deferred
-
- if hasattr(self.factory, "retried"):
- self.factory.deferred.errback(failure.Failure(Unauthorized("Mail gateway not able to process reply; could not authenticate user %s with calendar server" % (self.factory.username,))))
- return self.factory.deferred
-
- self.factory.retried = True
-
- # self.log_debug("Got a 401 trying to inject [%s]" % (self.headers,))
- details = {}
- basicAvailable = digestAvailable = False
- wwwauth = self.headers.get("www-authenticate")
- for item in wwwauth:
- if item.startswith("basic "):
- basicAvailable = True
- if item.startswith("digest "):
- digestAvailable = True
- wwwauth = item[7:]
- def unq(s):
- if s[0] == s[-1] == '"':
- return s[1:-1]
- return s
- parts = wwwauth.split(',')
- for (k, v) in [p.split('=', 1) for p in parts]:
- details[k.strip()] = unq(v.strip())
-
- user = self.factory.username
- pswd = self.factory.password
-
- if digestAvailable and details:
- digest = calcResponse(
- calcHA1(
- details.get('algorithm'),
- user,
- details.get('realm'),
- pswd,
- details.get('nonce'),
- details.get('cnonce')
- ),
- details.get('algorithm'),
- details.get('nonce'),
- details.get('nc'),
- details.get('cnonce'),
- details.get('qop'),
- self.factory.method,
- self.factory.url,
- None
- )
-
- if details.get('qop'):
- response = (
- 'Digest username="%s", realm="%s", nonce="%s", uri="%s", '
- 'response=%s, algorithm=%s, cnonce="%s", qop=%s, nc=%s' %
- (
- user,
- details.get('realm'),
- details.get('nonce'),
- self.factory.url,
- digest,
- details.get('algorithm'),
- details.get('cnonce'),
- details.get('qop'),
- details.get('nc'),
- )
- )
- else:
- response = (
- 'Digest username="%s", realm="%s", nonce="%s", uri="%s", '
- 'response=%s, algorithm=%s' %
- (
- user,
- details.get('realm'),
- details.get('nonce'),
- self.factory.url,
- digest,
- details.get('algorithm'),
- )
- )
-
- self.factory.headers['Authorization'] = response
-
- if self.factory.scheme == 'https':
- reactor.connectSSL(self.factory.host, self.factory.port,
- self.factory, ssl.ClientContextFactory())
- else:
- reactor.connectTCP(self.factory.host, self.factory.port,
- self.factory)
- # self.log_debug("Retrying with digest after 401")
-
- return self.factory.deferred
-
- elif basicAvailable:
- basicauth = "%s:%s" % (user, pswd)
- basicauth = "Basic " + base64.encodestring( basicauth )
- basicauth = basicauth.replace( "\n", "" )
-
- self.factory.headers['Authorization'] = basicauth
-
- if self.factory.scheme == 'https':
- reactor.connectSSL(self.factory.host, self.factory.port,
- self.factory, ssl.ClientContextFactory())
- else:
- reactor.connectTCP(self.factory.host, self.factory.port,
- self.factory)
- # self.log_debug("Retrying with basic after 401")
-
- return self.factory.deferred
-
-
- else:
- self.factory.deferred.errback(failure.Failure(Unauthorized("Mail gateway not able to process reply; calendar server returned 401 and doesn't support basic or digest")))
- return self.factory.deferred
-
-
def injectMessage(organizer, attendee, calendar, msgId, reactor=None):
if reactor is None:
@@ -726,6 +542,16 @@
def makeService(self, options):
+ if config.Memcached.ClientEnabled:
+ memcachepool.installPool(
+ IPv4Address(
+ "TCP",
+ config.Memcached.BindAddress,
+ config.Memcached.Port,
+ ),
+ config.Memcached.MaxClients,
+ )
+
multiService = service.MultiService()
settings = config.Scheduling['iMIP']
@@ -747,7 +573,10 @@
client.setServiceParent(multiService)
+
+ # Set up /inbox -- server POSTs to it to send out iMIP invites
IScheduleService(settings, mailer).setServiceParent(multiService)
+
else:
self.log_info("Mail Gateway Service not enabled")
@@ -762,12 +591,43 @@
def __init__(self, settings, mailer):
self.settings = settings
self.mailer = mailer
- root = resource.Resource()
- root.putChild('', self.HomePage())
- root.putChild('inbox', self.IScheduleInbox(mailer))
- self.site = server.Site(root)
- self.server = internet.TCPServer(settings['MailGatewayPort'], self.site)
+ directoryClass = namedClass(config.DirectoryService.type)
+ directory = directoryClass(config.DirectoryService.params)
+
+ principalCollection = DirectoryPrincipalProvisioningResource(
+ "/principals/",
+ directory,
+ )
+
+ root = RootResource(
+ config.DocumentRoot,
+ principalCollections=(principalCollection,),
+ )
+
+ # Authenticated /inbox
+ credentialFactories = []
+ portal = Portal(auth.DavRealm())
+ portal.registerChecker(directory)
+ realm = directory.realmName or ""
+ schemeConfig = config.Authentication.Digest
+ digestCredentialFactory = QopDigestCredentialFactory(
+ schemeConfig["Algorithm"],
+ schemeConfig["Qop"],
+ realm,
+ )
+ root.putChild('inbox',
+ auth.AuthenticationWrapper(
+ IMIPInvitationInboxResource(root, mailer),
+ portal,
+ (digestCredentialFactory,),
+ (auth.IPrincipal,),
+ )
+ )
+ self.factory = HTTPFactory(server.Site(root))
+ self.server = internet.TCPServer(settings['MailGatewayPort'],
+ self.factory)
+
def startService(self):
self.server.startService()
@@ -775,45 +635,8 @@
self.server.stopService()
- class HomePage(resource.Resource):
- def render(self, request):
- return """
- <html>
- <head><title>ISchedule - IMIP Gateway</title></head>
- <body>ISchedule - IMIP Gateway</body>
- </html>
- """
- class IScheduleInbox(resource.Resource):
- def __init__(self, mailer):
- resource.Resource.__init__(self)
- self.mailer = mailer
-
- def render_GET(self, request):
- return """
- <html>
- <head><title>ISchedule Inbox</title></head>
- <body>ISchedule Inbox</body>
- </html>
- """
-
- def render_POST(self, request):
- # Compute token, add to db, generate email and send it
- calendar = ical.Component.fromString(request.content.read())
- headers = request.getAllHeaders()
- language = config.Localization.Language
- self.mailer.outbound(headers['originator'], headers['recipient'],
- calendar, language=language)
-
- # TODO: what to return?
- return """
- <html>
- <head><title>ISchedule Inbox</title></head>
- <body>ISchedule Inbox</body>
- </html>
- """
-
class MailHandler(LoggingMixIn):
def __init__(self, dataRoot=None):
@@ -1059,10 +882,12 @@
def _success(result, msgId, fromAddr, toAddr):
self.log_info("Mail gateway sent message %s from %s to %s" %
(msgId, fromAddr, toAddr))
+ return True
def _failure(failure, msgId, fromAddr, toAddr):
self.log_error("Mail gateway failed to send message %s from %s to %s (Reason: %s)" %
(msgId, fromAddr, toAddr, failure.getErrorMessage()))
+ return False
deferred = defer.Deferred()
@@ -1080,6 +905,7 @@
reactor.connectTCP(settings['Server'], settings['Port'], factory)
deferred.addCallback(_success, msgId, fromAddr, toAddr)
deferred.addErrback(_failure, msgId, fromAddr, toAddr)
+ return deferred
def getIconPath(self, details, canceled, language='en'):
@@ -1507,7 +1333,7 @@
self.log_error("IMAP Error: %s" % (error,))
def ebAuthenticateFailed(self, reason):
- self.log_info("IMAP authenticate failed for %s, trying login" %
+ self.log_debug("IMAP authenticate failed for %s, trying login" %
(self.factory.settings["Username"],))
return self.login(self.factory.settings["Username"],
self.factory.settings["Password"]
Modified: CalendarServer/trunk/twistedcaldav/scheduling/imip.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/imip.py 2009-05-19 21:02:35 UTC (rev 4296)
+++ CalendarServer/trunk/twistedcaldav/scheduling/imip.py 2009-05-19 21:28:46 UTC (rev 4297)
@@ -27,6 +27,7 @@
from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.config import config
from twistedcaldav.log import Logger
+from twistedcaldav.util import AuthorizedHTTPGetter
from twistedcaldav.scheduling.delivery import DeliveryService
from twistedcaldav.scheduling.itip import iTIPRequestStatus
@@ -98,6 +99,13 @@
}
factory = client.HTTPClientFactory(url, method='POST', headers=headers,
postdata=caldata, agent="CalDAV server")
+
+ if config.Scheduling.iMIP.Username:
+ factory.username = config.Scheduling.iMIP.Username
+ factory.password = config.Scheduling.iMIP.Password
+
+ factory.noisy = False
+ factory.protocol = AuthorizedHTTPGetter
reactor.connectTCP(mailGatewayServer, mailGatewayPort, factory)
return factory.deferred
Modified: CalendarServer/trunk/twistedcaldav/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/util.py 2009-05-19 21:02:35 UTC (rev 4296)
+++ CalendarServer/trunk/twistedcaldav/util.py 2009-05-19 21:28:46 UTC (rev 4297)
@@ -19,6 +19,13 @@
import sys
from subprocess import Popen, PIPE, STDOUT
+from twisted.internet import ssl, reactor
+from twisted.web import client
+from twistedcaldav.log import Logger, LoggingMixIn
+from twisted.python import failure
+from hashlib import md5, sha1
+import base64
+
##
# getNCPU
##
@@ -161,3 +168,232 @@
else:
error = "Keychain access utility ('security') not found"
raise KeychainAccessError(error)
+
+
+
+
+##
+# Digest/Basic-capable HTTP GET factory
+##
+
+algorithms = {
+ 'md5': md5,
+ 'md5-sess': md5,
+ 'sha': sha1,
+}
+
+# DigestCalcHA1
+def calcHA1(
+ pszAlg,
+ pszUserName,
+ pszRealm,
+ pszPassword,
+ pszNonce,
+ pszCNonce,
+ preHA1=None
+):
+ """
+ @param pszAlg: The name of the algorithm to use to calculate the digest.
+ Currently supported are md5 md5-sess and sha.
+
+ @param pszUserName: The username
+ @param pszRealm: The realm
+ @param pszPassword: The password
+ @param pszNonce: The nonce
+ @param pszCNonce: The cnonce
+
+ @param preHA1: If available this is a str containing a previously
+ calculated HA1 as a hex string. If this is given then the values for
+ pszUserName, pszRealm, and pszPassword are ignored.
+ """
+
+ if (preHA1 and (pszUserName or pszRealm or pszPassword)):
+ raise TypeError(("preHA1 is incompatible with the pszUserName, "
+ "pszRealm, and pszPassword arguments"))
+
+ if preHA1 is None:
+ # We need to calculate the HA1 from the username:realm:password
+ m = algorithms[pszAlg]()
+ m.update(pszUserName)
+ m.update(":")
+ m.update(pszRealm)
+ m.update(":")
+ m.update(pszPassword)
+ HA1 = m.digest()
+ else:
+ # We were given a username:realm:password
+ HA1 = preHA1.decode('hex')
+
+ if pszAlg == "md5-sess":
+ m = algorithms[pszAlg]()
+ m.update(HA1)
+ m.update(":")
+ m.update(pszNonce)
+ m.update(":")
+ m.update(pszCNonce)
+ HA1 = m.digest()
+
+ return HA1.encode('hex')
+
+# DigestCalcResponse
+def calcResponse(
+ HA1,
+ algo,
+ pszNonce,
+ pszNonceCount,
+ pszCNonce,
+ pszQop,
+ pszMethod,
+ pszDigestUri,
+ pszHEntity,
+):
+ m = algorithms[algo]()
+ m.update(pszMethod)
+ m.update(":")
+ m.update(pszDigestUri)
+ if pszQop == "auth-int":
+ m.update(":")
+ m.update(pszHEntity)
+ HA2 = m.digest().encode('hex')
+
+ m = algorithms[algo]()
+ m.update(HA1)
+ m.update(":")
+ m.update(pszNonce)
+ m.update(":")
+ if pszNonceCount and pszCNonce and pszQop:
+ m.update(pszNonceCount)
+ m.update(":")
+ m.update(pszCNonce)
+ m.update(":")
+ m.update(pszQop)
+ m.update(":")
+ m.update(HA2)
+ respHash = m.digest().encode('hex')
+ return respHash
+
+class Unauthorized(Exception):
+ pass
+
+class AuthorizedHTTPGetter(client.HTTPPageGetter, LoggingMixIn):
+
+ def handleStatus_401(self):
+
+ self.quietLoss = 1
+ self.transport.loseConnection()
+
+ if not hasattr(self.factory, "username"):
+ self.factory.deferred.errback(failure.Failure(Unauthorized("Mail gateway not able to process reply; authentication required for calendar server")))
+ return self.factory.deferred
+
+ if hasattr(self.factory, "retried"):
+ self.factory.deferred.errback(failure.Failure(Unauthorized("Mail gateway not able to process reply; could not authenticate user %s with calendar server" % (self.factory.username,))))
+ return self.factory.deferred
+
+ self.factory.retried = True
+
+ # self.log_debug("Got a 401 trying to inject [%s]" % (self.headers,))
+ details = {}
+ basicAvailable = digestAvailable = False
+ wwwauth = self.headers.get("www-authenticate")
+ for item in wwwauth:
+ if item.startswith("basic "):
+ basicAvailable = True
+ if item.startswith("digest "):
+ digestAvailable = True
+ wwwauth = item[7:]
+ def unq(s):
+ if s[0] == s[-1] == '"':
+ return s[1:-1]
+ return s
+ parts = wwwauth.split(',')
+ for (k, v) in [p.split('=', 1) for p in parts]:
+ details[k.strip()] = unq(v.strip())
+
+ user = self.factory.username
+ pswd = self.factory.password
+
+ if digestAvailable and details:
+ digest = calcResponse(
+ calcHA1(
+ details.get('algorithm'),
+ user,
+ details.get('realm'),
+ pswd,
+ details.get('nonce'),
+ details.get('cnonce')
+ ),
+ details.get('algorithm'),
+ details.get('nonce'),
+ details.get('nc'),
+ details.get('cnonce'),
+ details.get('qop'),
+ self.factory.method,
+ self.factory.url,
+ None
+ )
+
+ if details.get('qop'):
+ response = (
+ 'Digest username="%s", realm="%s", nonce="%s", uri="%s", '
+ 'response=%s, algorithm=%s, cnonce="%s", qop=%s, nc=%s' %
+ (
+ user,
+ details.get('realm'),
+ details.get('nonce'),
+ self.factory.url,
+ digest,
+ details.get('algorithm'),
+ details.get('cnonce'),
+ details.get('qop'),
+ details.get('nc'),
+ )
+ )
+ else:
+ response = (
+ 'Digest username="%s", realm="%s", nonce="%s", uri="%s", '
+ 'response=%s, algorithm=%s' %
+ (
+ user,
+ details.get('realm'),
+ details.get('nonce'),
+ self.factory.url,
+ digest,
+ details.get('algorithm'),
+ )
+ )
+
+ self.factory.headers['Authorization'] = response
+
+ if self.factory.scheme == 'https':
+ reactor.connectSSL(self.factory.host, self.factory.port,
+ self.factory, ssl.ClientContextFactory())
+ else:
+ reactor.connectTCP(self.factory.host, self.factory.port,
+ self.factory)
+ # self.log_debug("Retrying with digest after 401")
+
+ return self.factory.deferred
+
+ elif basicAvailable:
+ basicauth = "%s:%s" % (user, pswd)
+ basicauth = "Basic " + base64.encodestring( basicauth )
+ basicauth = basicauth.replace( "\n", "" )
+
+ self.factory.headers['Authorization'] = basicauth
+
+ if self.factory.scheme == 'https':
+ reactor.connectSSL(self.factory.host, self.factory.port,
+ self.factory, ssl.ClientContextFactory())
+ else:
+ reactor.connectTCP(self.factory.host, self.factory.port,
+ self.factory)
+ # self.log_debug("Retrying with basic after 401")
+
+ return self.factory.deferred
+
+
+ else:
+ self.factory.deferred.errback(failure.Failure(Unauthorized("Mail gateway not able to process reply; calendar server returned 401 and doesn't support basic or digest")))
+ return self.factory.deferred
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090519/3f7f8383/attachment-0001.html>
More information about the calendarserver-changes
mailing list