changeset 786:503f0b91ba41

Merge with main.
author Alexander Schremmer <alex AT alexanderweb DOT de>
date Wed, 07 Jun 2006 14:50:19 +0200
parents f3c1ea6ef86e (current diff) 9595eaf676a7 (diff)
children 9d74a2f53323
files MoinMoin/xmlrpc/__init__.py
diffstat 3 files changed, 142 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/server/standalone.py	Sun Jun 04 23:14:13 2006 +0200
+++ b/MoinMoin/server/standalone.py	Wed Jun 07 14:50:19 2006 +0200
@@ -382,6 +382,77 @@
         shutil.copyfileobj(source, outputfile, length=self.bufferSize)
 
 
+try:
+    from tlslite.api import TLSSocketServerMixIn, X509, X509CertChain, SessionCache, parsePEMKey, TLSError
+    from tlslite.TLSConnection import TLSConnection
+except ImportError:
+    pass
+else:
+    class SecureRequestRedirect(BaseHTTPServer.BaseHTTPRequestHandler):
+        def handle(self):
+            self.close_connection = 1
+            try:
+                self.raw_requestline = self.rfile.readline()
+            except socket.error:
+                return
+            if self.parse_request():
+                host = self.headers.get('Host', socket.gethostname())
+                path = self.path
+            else:
+                host = '%s:%s' % (socket.gethostname(), 
+                        self.request.getsockname()[1])
+                path = '/'
+                
+            self.requestline = 'ERROR: Redirecting to https://%s/%s' % (host, path)
+            self.request_version = 'HTTP/1.1'
+            self.command = 'GET'
+            self.send_response(301, 'Document Moved')
+            self.send_header('Date', self.date_time_string())
+            self.send_header('Location', 'https://%s%s' % (host, path))
+            self.send_header('Connection', 'close')
+            self.send_header('Content-Length', '0')
+            self.wfile.write('\r\n')
+            
+    class SecureThreadPoolServer(TLSSocketServerMixIn, ThreadPoolServer):
+        def __init__(self, config):
+            ThreadPoolServer.__init__(self, config)
+            
+            cert = open(config.certificatePath).read()
+            x509 = X509()
+            x509.parse(cert)
+            self.certChain = X509CertChain([x509])
+            
+            priv = open(config.privateKeyPath).read()
+            self.privateKey = parsePEMKey(priv, private=True)
+            
+            self.sessionCache = SessionCache()
+            
+        def finish_request(self, sock, client_address):
+            # Peek into the packet, if it starts with GET or POS(T) then
+            # redirect, otherwise let TLSLite handle the connection.
+            peek = sock.recv(3, socket.MSG_PEEK)
+            if peek.lower() == 'get' or peek.lower() == 'pos':
+                response = SecureRequestRedirect(sock, client_address, self)
+            tlsConnection = TLSConnection(sock)
+            if self.handshake(tlsConnection) == True:
+                self.RequestHandlerClass(tlsConnection, client_address, self)
+            else:
+                # This will probably fail because the TLSConnection has 
+                # already written SSL stuff to the socket. But not sure what
+                # else we should do.
+                response = SecureRequestRedirect(sock, client_address, self)
+                
+        def handshake(self, tlsConnection):
+            try:
+                tlsConnection.handshakeServer(certChain = self.certChain,
+                        privateKey = self.privateKey,
+                        sessionCache = self.sessionCache)
+                tlsConnection.ignoreAbruptClose = True
+                return True
+            except:
+                return False
+
+                
 def memoryProfileDecorator(func, profile):
     """ Return a profiled function """
     def profiledFunction(*args, **kw):
--- a/MoinMoin/xmlrpc/__init__.py	Sun Jun 04 23:14:13 2006 +0200
+++ b/MoinMoin/xmlrpc/__init__.py	Wed Jun 07 14:50:19 2006 +0200
@@ -5,7 +5,7 @@
     If you want to use wikirpc function "putPage", read the comments in
     xmlrpc_putPage or it won't work!
     
-    Parts of this code are based on Jürgen Hermann's wikirpc.py,
+    Parts of this code are based on Jrgen Hermann's wikirpc.py,
     Les Orchard's "xmlrpc.cgi" and further work by Gustavo Niemeyer.
 
     See http://www.ecyrd.com/JSPWiki/Wiki.jsp?page=WikiRPCInterface
@@ -27,12 +27,13 @@
 
 modules = pysupport.getPackageModules(__file__)
 
-import sys, time, xmlrpclib
+import os, sys, time, xmlrpclib
 
 from MoinMoin import config, user, wikiutil
 from MoinMoin.Page import Page
 from MoinMoin.PageEditor import PageEditor
 from MoinMoin.logfile import editlog
+from MoinMoin.action import AttachFile
 
 _debug = 0
 
@@ -495,6 +496,72 @@
         from MoinMoin import version
         return (version.project, version.release, version.revision)
 
+    def xmlrpc_listAttachments(self, pagename):
+        """ Get all attachments associated with pagename
+        
+        @param pagename: pagename (utf-8)
+        @rtype: list
+        @return: a list of utf-8 attachment names
+        """    
+        pagename = self._instr(pagename)
+        # User may read page?
+        if not self.request.user.may.read(pagename):
+            return self.notAllowedFault()
+        
+        result = AttachFile._get_files(self.request, pagename)
+        return result
+        
+    def xmlrpc_getAttachment(self, pagename, attachname):
+        """ Get attachname associated with pagename
+        
+        @param pagename: pagename (utf-8)
+        @param attachname: attachment name (utf-8)
+        @rtype base64
+        @return base64 data
+        """
+        pagename = self._instr(pagename)
+        # User may read page?
+        if not self.request.user.may.read(pagename):
+            return self.notAllowedFault()
+        
+        filename = wikiutil.taintfilename(filename)
+        filename = AttachFile.getFilename(self.request, pagename, attachname)
+        if not os.path.isfile(filename):
+            return self.noSuchPageFault()
+        return self._outlob(open(filename, 'rb').read())
+        
+    def xmlrpc_putAttachment(self, pagename, attachname, data):
+        """ Set attachname associated with pagename to data
+        
+        @param pagename: pagename (utf-8)
+        @param attachname: attachment name (utf-8)
+        @param data: file data (base64)
+        @rtype boolean
+        @return True if attachment was set
+        """
+        pagename = self._instr(pagename)
+        # User may read page?
+        if not self.request.user.may.read(pagename):
+            return self.notAllowedFault()
+
+        if not self.request.cfg.xmlrpc_putpage_enabled:
+            return xmlrpclib.Boolean(0)
+        if self.request.cfg.xmlrpc_putpage_trusted_only and not self.request.user.trusted:
+            return xmlrpclib.Fault(1, "You are not allowed to edit this page")
+        # also check ACLs
+        if not self.request.user.may.write(pagename):
+            return xmlrpclib.Fault(1, "You are not allowed to edit this page")
+        
+        attachname = wikiutil.taintfilename(attachname)
+        filename = AttachFile.getFilename(self.request, pagename, attachname)
+        if os.path.exists(filename) and not os.path.isfile(filename):
+            return self.noSuchPageFault()
+        open(filename, 'wb+').write(data.data)
+        os.chmod(filename, 0666 & config.umask)
+        AttachFile._addLogEntry(self.request, 'ATTNEW', pagename, filename)
+        return xmlrpclib.Boolean(1)
+
+
 class XmlRpc1(XmlRpcBase):
     
     def __init__(self, request):
--- a/docs/CHANGES	Sun Jun 04 23:14:13 2006 +0200
+++ b/docs/CHANGES	Wed Jun 07 14:50:19 2006 +0200
@@ -77,6 +77,8 @@
     * You can have a common cache_dir for your farm (will save a bit space
       and cpu time as it shares some stuff).
       You need to set "cache_dir = '/some/farm/cachedir' in your farmconfig.
+    * Added XMLRPC methods for attachment handling. Thanks to Matthew Gilbert.
+    * Added TLS/SSL support to the standalone server. Thanks to Matthew Gilbert.
 
   Bugfixes:
     * on action "info" page, "revert" link will not be displayed for empty page