Index: XMLRPCMethod/current/README.txt
===================================================================
--- XMLRPCMethod/current/README.txt	(revision 19)
+++ XMLRPCMethod/current/README.txt	(revision 19)
@@ -0,0 +1,90 @@
+XMLRPC Methods
+
+  The XMLRPC Method product provides support for remote procedure
+  calls with the "XML-RPC":http://www.xml-rpc.com standard.
+  The intent is to create a plug-in replacement for an external method.
+
+  Not everything is callable: For instance you can't call title or id
+  on a folder, but you can call 'title_or_id', 'title_and_id', 'getId'
+  and 'getPhysicalPath'. Essentially everything that will also work as
+  'http://remote.com/folder/title_or_id'. The method 'propertyItems' returns
+  an especially complex and interesting result.
+
+  You can also do privileged things that requires a login. You simply
+  add the username and password to the remote url the conventional way:
+
+    http://username:password@remote.com/
+
+  Then you can call methods such as 'document_src' on DTML methods etc.
+
+Sending arguments
+
+  XMLRPC allows you to send a number of arguments to the remote
+  procedure. These arguments can be integers, strings, lists, dictionaries
+  etc. and combinations hereof.
+
+  To test with the XMLRPCLIB example, create a XMLRPC method with the
+  name 'statelookup', the URL 'http://betty.userland.com' and the method
+  'examples.getStateName'.
+
+  The create a DTML method with the call: '<dtml-var "statelookup(5)">'
+  and see what happens.
+
+Sending arguments to remote DTML Methods
+
+  Try this: create a DTML method with address /welcome on the remote
+  computer with the two lines::
+
+    <dtml-call "REQUEST.set('res', 'Welcome to ' + place )">
+    <dtml-return "REQUEST['res']">
+
+  Then on the local computer create an XMLRPC method called querywelcome
+  with the URL http://remote.com/, and a method of welcome.
+
+  To call the remote procedure from a DTML Method do::
+
+    <dtml-var "querywelcome( {'place':'my world'}) ">
+
+  If the method returns a list or a dictionary, you can use the &lt;dtml-in *method* mapping>
+ 
+Calling a Z SQL Method:
+
+  You can't do it directly. The Z SQL Method returns objects, so you
+  must use a DTML Method to build a list of dictionaries. Let's assume
+  we have a table with the two columns 'ISSUE_ID' and 'ISSUE_NAME'. Then
+  your DTML Method will look like this::
+
+    <dtml-call "REQUEST.set('result',[])">
+    <dtml-in select_all_from_issues>
+    <dtml-call "REQUEST['result'].append({'ID': ISSUE_ID, 'NAME':ISSUE_NAME}) ">
+    </dtml-in>
+    <dtml-return "REQUEST['result']">
+
+Other things to note:
+
+  When an object is called via XML-RPC, there's nothing in the REQUEST
+  variable. You can create variables in the namespace, but that's it. Some
+  versions of Zope can't see simple variables.  So in the example above you
+  have to write '<dtml-return "REQUEST[&apos;result&apos;]">' rather than
+  '<dtml-return result>' to be one the safe side.
+
+  Due to its nature XMLRPC methods can take a long time to execute or
+  not return at all if the remote server is unresponsive. Therefore make
+  use of the timeout value. The timeout functionality is implemented
+  with threads.
+
+  Another optimization technique available to you is to cache the results.
+  We have made XMLRPCMethod cacheable with RAMCacheManager.
+
+Bugs
+
+  XML-RPC takes only positional parameters. Since arguments given in
+  the 'QUERY_STRING' are keyword arguments as in ?x=1&amp;y=2
+  it isn't possible to specify arguments in GET or POST statements.
+
+  The &lt;dateTime.iso8601&gt; element is difficult to use. It is not
+  converted to a Zope DateTime.
+
+References
+
+  "Directory of services":http://www.xmlrpc.com/directory/1568/services
Index: XMLRPCMethod/current/XMLRPCMethod.py
===================================================================
--- XMLRPCMethod/current/XMLRPCMethod.py	(revision 19)
+++ XMLRPCMethod/current/XMLRPCMethod.py	(revision 19)
@@ -0,0 +1,245 @@
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is XMLRPCMethod version 1.0.
+#
+# The Initial Developer of the Original Code is European Environment
+# Agency (EEA).  Portions created by Finsiel Romania are
+# Copyright (C) European Environment Agency.  All
+# Rights Reserved.
+#
+# Contributor(s):
+# Soren Roug, EEA
+# Cornel Nitu, Finsiel Romania
+
+""" XMLRPC Method Product
+
+    This product provides support for remote methods.
+"""
+__version__='$Revision: 1.11 $'[11:-2]
+import xmlrpclib, httplib
+from base64 import encodestring
+from string import split, join, lower, replace
+import sys, os, stat, traceback
+from cgi import escape
+from urllib import splithost, splitpasswd, splittype, splituser
+from threading import Thread
+
+from Globals import Persistent, DTMLFile, MessageDialog, HTML
+import OFS.SimpleItem, Acquisition
+from OFS.Cache import Cacheable
+import AccessControl.Role
+from OFS.SimpleItem import pretty_tb
+from App.Management import Navigation
+
+_marker = []  # Create a new marker object
+
+manage_addXMLRPCMethodForm=DTMLFile('dtml/methodAdd', globals())
+
+def manage_addXMLRPCMethod(self, id, title, remoteurl, function, timeout, REQUEST=None):
+    """ Add an external method to a folder
+
+        An addition to the standard object-creation arguments,
+        'id' and title, the following arguments are defined:
+
+        method -- The name of the remote method. This can be a
+          an ordinary Python function, or a bound method.
+
+        remoteurl -- The URL of the RPC dispatcher to call.
+    """
+    id=str(id)
+    title=str(title)
+    remoteurl=str(remoteurl)
+    function=str(function)
+    timeout=float(timeout)
+
+    i=XMLRPCMethod(id,title,remoteurl,function,timeout)
+    self._setObject(id,i)
+    return self.manage_main(self,REQUEST)
+
+class XMLRPCMethod(OFS.SimpleItem.Item, Persistent, Acquisition.Explicit,
+                     AccessControl.Role.RoleManager, Navigation, Cacheable):
+    """ Web-callable functions that encapsulate remote methods.
+
+    """
+
+    meta_type='XMLRPC Method'
+
+    ZopeTime=Acquisition.Acquired
+    HelpSys=Acquisition.Acquired
+
+    manage_options=(
+        (
+        {'label':'Properties', 'action':'manage_main',
+         'help':('XMLRPCMethod','XMLRPC-Method_Properties.stx')},
+        {'label':'Test', 'action':'',
+         'help':('XMLRPCMethod','XMLRPC-Method_Try-It.stx')},
+        )
+        +OFS.SimpleItem.Item.manage_options
+        +AccessControl.Role.RoleManager.manage_options
+        +Cacheable.manage_options
+        )
+
+    __ac_permissions__=(
+        ('View management screens', ('manage_main',)),
+        ('Change XMLRPC Methods', ('manage_edit',)),
+        ('View', ('__call__','')),
+        )
+
+    def __init__(self, id, title, remoteurl, function,timeout):
+        self.id=id
+        self.manage_edit(title, remoteurl, function,timeout)
+
+    def __setstate__(self,state):
+        XMLRPCMethod.inheritedAttribute('__setstate__')(self, state)
+        if not hasattr(self,'_timeout'):
+            self._timeout = 5.0
+
+    manage_main=DTMLFile('dtml/methodEdit', globals())
+
+    def manage_edit(self, title, remoteurl, function, timeout, REQUEST=None):
+        """Change the external method
+
+        See the description of manage_addXMLRPCMethod for a
+        description of the arguments 'remoteurl' and 'function'.
+        """
+        title=str(title)
+        remoteurl=str(remoteurl)
+        function=str(function)
+        timeout=float(timeout)
+
+        self.title=title
+        if remoteurl:
+            urltype, url = splittype(remoteurl)
+        if urltype != "http":
+            raise IOError, "Only http scheme supported"
+
+        host, selector = splithost(url)
+
+        if not host: raise IOError, ('http error', 'No host given')
+
+        self._remoteurl = remoteurl
+        self._function = function
+        self._timeout = timeout
+        self.ZCacheable_invalidate()
+        if REQUEST:
+            message="XMLRPC Method Created."
+            return self.manage_main(self,REQUEST,manage_tabs_message=message)
+
+    def __call__(self, *args):
+        """ Call an XMLRPC Method """
+
+        __traceback_info__ = args
+        # Retrieve the value from the cache.
+        keyset = None
+        if self.ZCacheable_isCachingEnabled():
+            # Strange; I can't just use args
+            keyset = { '*':args }
+            # Prepare a cache key.
+            results = self.ZCacheable_get(keywords=keyset, default=_marker)
+            if results is not _marker:
+                return results
+
+        ut = RPCThread(self._remoteurl, self._function, args=args)
+        ut.start()
+        ut.join(self._timeout)
+
+        results = ut.getresult()
+        if keyset is not None:
+            if results is not None:
+                self.ZCacheable_set(results, keywords=keyset)
+        return results
+
+    def function(self): return self._function
+    def remoteurl(self): return self._remoteurl
+    def timeout(self): return self._timeout
+
+class RPCThread(Thread):
+
+    def __init__(self,remoteurl,function,args=()):
+        Thread.__init__(self)
+        self.args=args
+        self.result = None
+        self.rpcerror = None
+        self.remoteurl = remoteurl
+        self.function = function
+
+    def getresult(self):
+        if self.rpcerror:
+            raise IOError, escape(self.rpcerror)
+        else:
+            return self.result
+
+    def run(self):
+        self.rpcerror = None
+        urltype, url = splittype(self.remoteurl)
+        if urltype != "http":
+            self.rpcerror = "Only http scheme supported"
+            raise IOError, "Only http scheme supported"
+
+        host, selector = splithost(url)
+
+        if not host: raise IOError, ('http error', 'No host given')
+
+        user_passwd, host = splituser(host)
+
+        url = "http://"+ host + selector
+
+        if user_passwd:
+            server = xmlrpclib.Server(url, BasicAuthTransport(user_passwd))
+        else:
+            server = xmlrpclib.Server(url)
+        f = server.__getattr__(self.function)
+        try: res = apply(f,self.args)
+        except (xmlrpclib.Fault,xmlrpclib.ProtocolError,xmlrpclib.ResponseError), v:
+            self.rpcerror = str(v)
+        #raise IOError, escape(self.rpcerror)
+        else:    
+            self.result = res
+
+
+class BasicAuthTransport(xmlrpclib.Transport):
+    def __init__(self, userpassword=None):
+        self.userpassword=userpassword
+
+    def request(self, host, handler, request_body):
+        """ issue XML-RPC request """
+
+        h = httplib.HTTP(host)
+        h.putrequest("POST", handler)
+
+        # required by HTTP/1.1
+        h.putheader("Host", host)
+
+        # required by XML-RPC
+        h.putheader("User-Agent", self.user_agent)
+        h.putheader("Content-Type", "text/xml")
+        h.putheader("Content-Length", str(len(request_body)))
+
+        # basic auth
+        if self.userpassword is not None:
+            h.putheader("AUTHORIZATION", "Basic %s" % replace(
+                encodestring("%s" % (self.userpassword)),
+                "\012", ""))
+        h.endheaders()
+
+        if request_body:
+            h.send(request_body)
+
+        errcode, errmsg, headers = h.getreply()
+
+        if errcode != 200:
+            raise xmlrpclib.ProtocolError(
+            host + handler,
+            errcode, errmsg,
+            headers
+            )
+
+        return self.parse_response(h.getfile())
Index: XMLRPCMethod/current/__init__.py
===================================================================
--- XMLRPCMethod/current/__init__.py	(revision 19)
+++ XMLRPCMethod/current/__init__.py	(revision 19)
@@ -0,0 +1,39 @@
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is RDFSummary version 1.0.
+#
+# The Initial Developer of the Original Code is European Environment
+# Agency (EEA).  Portions created by EEA are
+# Copyright (C) European Environment Agency.  All
+# Rights Reserved.
+#
+# Contributor(s):
+# Soren Roug, EEA
+
+__doc__='''XMLRPC Method Product Initialization
+$Id: __init__.py,v 1.2 2003/06/06 13:26:46 finrocvs Exp $'''
+__version__='$Revision: 1.2 $'[11:-2]
+
+import XMLRPCMethod
+
+# This is the new way to initialize products.  It is hoped
+# that this more direct mechanism will be more understandable.
+def initialize(context):
+
+    context.registerClass(
+        XMLRPCMethod.XMLRPCMethod,
+        constructors=(XMLRPCMethod.manage_addXMLRPCMethodForm,
+                       XMLRPCMethod.manage_addXMLRPCMethod),
+        icon='xmlrpc.gif',
+        )
+
+    context.registerHelp()
+    context.registerHelpTitle('Zope Help')
Index: XMLRPCMethod/current/dtml/methodAdd.dtml
===================================================================
--- XMLRPCMethod/current/dtml/methodAdd.dtml	(revision 19)
+++ XMLRPCMethod/current/dtml/methodAdd.dtml	(revision 19)
@@ -0,0 +1,81 @@
+<dtml-var manage_page_header>
+
+<dtml-var "manage_form_title(this(), _,
+           form_title='Add XML-RPC Method',
+           help_product='XMLRPCMethod',
+           help_topic='XMLRPC-Method_Add.stx'
+	   )">
+
+<p class="form-help">
+XML-RPC Methods allow you to add functionality to Zope by calling functions
+on other webservers with the XML-RPC protocol
+The <em>method name</em> should name a callable method of the URL.
+</p>
+
+<form action="manage_addXMLRPCMethod" method="post">
+<table cellspacing="0" cellpadding="2" border="0">
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Id
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="id" size="40" />
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-optional">
+    Title
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="title" size="40" />
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    URL
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="remoteurl" size="40" /><br>
+    http://<i>user</i>:<i>password</i>@<i>hostname</i>/<i>path</i>
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Method Name
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="function" size="40" />
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Timeout
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="timeout:float" size="5" value="2.5" /> seconds
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    </td>
+    <td align="left" valign="top">
+    <div class="form-element">
+    <input class="form-element" type="submit" name="submit" 
+     value=" Add " /> 
+    </div>
+    </td>
+  </tr>
+</table>
+</form>
+
+<dtml-var manage_page_footer>
Index: XMLRPCMethod/current/dtml/methodEdit.dtml
===================================================================
--- XMLRPCMethod/current/dtml/methodEdit.dtml	(revision 19)
+++ XMLRPCMethod/current/dtml/methodEdit.dtml	(revision 19)
@@ -0,0 +1,63 @@
+<dtml-var manage_page_header>
+<dtml-var manage_tabs>
+
+<form action="manage_edit" method="post">
+<table cellspacing="0" cellpadding="2" border="0">
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-optional">
+    Title
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="title" size="40"
+     value="<dtml-var title html_quote>" />
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    URL
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="remoteurl" size="40"
+     value="<dtml-var remoteurl html_quote>" />
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Method Name
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="function" size="40"
+     value="<dtml-var function html_quote>" />
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Timeout
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="timeout:float" size="5"
+     value="<dtml-var timeout>" /> seconds
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    </td>
+    <td align="left" valign="top">
+    <div class="form-element">
+    <input class="form-element" type="submit" name="submit" 
+     value="Save Changes" /> 
+    </div>
+    </td>
+  </tr>
+</table>
+</form>
+
+<dtml-var manage_page_footer>
Index: XMLRPCMethod/current/help/XMLRPC-Method.stx
===================================================================
--- XMLRPCMethod/current/help/XMLRPC-Method.stx	(revision 19)
+++ XMLRPCMethod/current/help/XMLRPC-Method.stx	(revision 19)
@@ -0,0 +1,5 @@
+XMLRPC Method: Call Python methods from Zope
+  
+  XML-RPC Methods associate a function on a remote server with
+  an object in Zope.  This object can be called from DTML and Python
+  like other Zope methods.
Index: XMLRPCMethod/current/help/XMLRPC-Method_Add.stx
===================================================================
--- XMLRPCMethod/current/help/XMLRPC-Method_Add.stx	(revision 19)
+++ XMLRPCMethod/current/help/XMLRPC-Method_Add.stx	(revision 19)
@@ -0,0 +1,20 @@
+XMLRPC Method - Add: Create a new XMLRPC Method.
+  
+  Description
+
+    Creates a new XML-RPC method and adds it to the 
+    current Folder.
+
+  Controls
+
+    'ID' -- Specifies the id of the remote method.
+    
+    'Title' -- Allows you to specify the title of the remote method.
+    
+    'URL' -- Specifies the URL of the object defining the method.
+    
+    'Method name' -- Allows you to specify the name of the function 
+    to use for the external method.
+
+    'timeout' -- The number of seconds the procedure call may take.
+    It is a floating point number.
Index: XMLRPCMethod/current/help/XMLRPC-Method_Properties.stx
===================================================================
--- XMLRPCMethod/current/help/XMLRPC-Method_Properties.stx	(revision 19)
+++ XMLRPCMethod/current/help/XMLRPC-Method_Properties.stx	(revision 19)
@@ -0,0 +1,20 @@
+XMLRPC Method - Properties: Manage the properties of an XMLRPC method.
+  
+  Description
+
+    View and manage the properties of an XMLRPC Method. 
+
+  Controls
+
+    'ID' -- Specifies the id of the external method.
+    
+    'Title' -- Allows you to specify the title of the external method.
+    
+    'URL' -- Specifies the URL of the object defining the method.
+    
+    'Method name' -- Allows you to specify the name of the function 
+    (method) to use for the external method.
+
+    'timeout' -- The number of seconds the procedure call may take.
+    It is a floating point number.
+
Index: XMLRPCMethod/current/help/XMLRPC-Method_Try-It.stx
===================================================================
--- XMLRPCMethod/current/help/XMLRPC-Method_Try-It.stx	(revision 19)
+++ XMLRPCMethod/current/help/XMLRPC-Method_Try-It.stx	(revision 19)
@@ -0,0 +1,9 @@
+XMLRPC Method - Try It: Test the remote method.
+  
+  Description
+
+    This view executes the remote method and returns the results if
+    any.  Only parameters from the web request are passed the
+    remote method.
+
+
Index: XMLRPCMethod/current/help/XMLRPCMethod.py
===================================================================
--- XMLRPCMethod/current/help/XMLRPCMethod.py	(revision 19)
+++ XMLRPCMethod/current/help/XMLRPCMethod.py	(revision 19)
@@ -0,0 +1,160 @@
+##############################################################################
+# 
+# Zope Public License (ZPL) Version 1.0
+# -------------------------------------
+# 
+# Copyright (c) Digital Creations.  All rights reserved.
+# 
+# This license has been certified as Open Source(tm).
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 
+# 1. Redistributions in source code must retain the above copyright
+#    notice, this list of conditions, and the following disclaimer.
+# 
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions, and the following disclaimer in
+#    the documentation and/or other materials provided with the
+#    distribution.
+# 
+# 3. Digital Creations requests that attribution be given to Zope
+#    in any manner possible. Zope includes a "Powered by Zope"
+#    button that is installed by default. While it is not a license
+#    violation to remove this button, it is requested that the
+#    attribution remain. A significant investment has been put
+#    into Zope, and this effort will continue if the Zope community
+#    continues to grow. This is one way to assure that growth.
+# 
+# 4. All advertising materials and documentation mentioning
+#    features derived from or use of this software must display
+#    the following acknowledgement:
+# 
+#      "This product includes software developed by Digital Creations
+#      for use in the Z Object Publishing Environment
+#      (http://www.zope.org/)."
+# 
+#    In the event that the product being advertised includes an
+#    intact Zope distribution (with copyright and license included)
+#    then this clause is waived.
+# 
+# 5. Names associated with Zope or Digital Creations must not be used to
+#    endorse or promote products derived from this software without
+#    prior written permission from Digital Creations.
+# 
+# 6. Modified redistributions of any form whatsoever must retain
+#    the following acknowledgment:
+# 
+#      "This product includes software developed by Digital Creations
+#      for use in the Z Object Publishing Environment
+#      (http://www.zope.org/)."
+# 
+#    Intact (re-)distributions of any official Zope release do not
+#    require an external acknowledgement.
+# 
+# 7. Modifications are encouraged but must be packaged separately as
+#    patches to official Zope releases.  Distributions that do not
+#    clearly separate the patches from the original work must be clearly
+#    labeled as unofficial distributions.  Modifications which do not
+#    carry the name Zope may be packaged in any form, as long as they
+#    conform to all of the clauses above.
+# 
+# 
+# Disclaimer
+# 
+#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
+#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
+#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+#   SUCH DAMAGE.
+# 
+# 
+# This software consists of contributions made by Digital Creations and
+# many individuals on behalf of Digital Creations.  Specific
+# attributions are listed in the accompanying credits file.
+# 
+##############################################################################
+
+
+
+def manage_addXMLRPCMethod(id, title, module, function):
+    """
+    Add an XMLRPC method to an
+    'ObjectManager'.
+  
+    In addition to the standard object-creation arguments,
+    'id' and title, the following arguments are defined:
+
+      function -- The name of the python function. This can be a
+        an ordinary Python function, or a bound method.
+
+      module -- The name of the file containing the function
+        definition.
+
+    The module normally resides in the 'Extensions'
+    directory, however, the file name may have a prefix of
+    'product.', indicating that it should be found in a product
+    directory.
+
+    For example, if the module is: 'ACMEWidgets.foo', then an
+    attempt will first be made to use the file
+    'lib/python/Products/ACMEWidgets/Extensions/foo.py'. If this
+    failes, then the file 'Extensions/ACMEWidgets.foo.py' will be
+    used.
+
+    """
+
+class XMLRPCMethod:
+    """
+    Web-callable functions that encapsulate remote functions.
+    """
+
+    __constructor__=manage_addXMLRPCMethod
+
+    def manage_edit(title, module, function, REQUEST=None):
+        """
+        Change the
+        XMLRPC Method.
+
+        See the description of manage_addXMLRPCMethod for a
+        description of the arguments 'module' and 'function'.
+
+        Note that calling 'manage_edit' causes the "module" to be
+        effectively reloaded.  This is useful during debugging to see
+        the effects of changes, but can lead to problems of functions
+        rely on shared global data.
+
+        """
+
+    def __call__(*args, **kw):
+
+        """
+        Call the
+        XMLRPC Method.
+
+        Calling an XMLRPC Method is roughly equivalent to calling
+        the original actual function from Python.  Positional and
+        keyword parameters can be passed as usual.  Note however that
+        unlike the case of a normal Python method, the "self" argument
+        must be passed explicitly.  An exception to this rule is made
+        if:
+
+        - The supplied number of arguments is one less than the
+          required number of arguments, and
+
+        - The name of the function's first argument is 'self'.
+
+        In this case, the URL parent of the object is supplied as the
+        first argument.
+
+        """
+
+
Index: XMLRPCMethod/current/timeoutsocket.py
===================================================================
--- XMLRPCMethod/current/timeoutsocket.py	(revision 19)
+++ XMLRPCMethod/current/timeoutsocket.py	(revision 19)
@@ -0,0 +1,424 @@
+
+####
+# Copyright 2000,2001 by Timothy O'Malley <timo@alum.mit.edu>
+# 
+#                All Rights Reserved
+# 
+# Permission to use, copy, modify, and distribute this software
+# and its documentation for any purpose and without fee is hereby
+# granted, provided that the above copyright notice appear in all
+# copies and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of
+# Timothy O'Malley  not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission. 
+# 
+# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
+# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE. 
+#
+####
+
+"""Timeout Socket
+
+This module enables a timeout mechanism on all TCP connections.  It
+does this by inserting a shim into the socket module.  After this module
+has been imported, all socket creation goes through this shim.  As a
+result, every TCP connection will support a timeout.
+
+The beauty of this method is that it immediately and transparently
+enables the entire python library to support timeouts on TCP sockets.
+As an example, if you wanted to SMTP connections to have a 20 second
+timeout:
+
+    import timeoutsocket
+    import smtplib
+    timeoutsocket.setDefaultSocketTimeout(20)
+
+
+The timeout applies to the socket functions that normally block on
+execution:  read, write, connect, and accept.  If any of these 
+operations exceeds the specified timeout, the exception Timeout
+will be raised.
+
+The default timeout value is set to None.  As a result, importing
+this module does not change the default behavior of a socket.  The
+timeout mechanism only activates when the timeout has been set to
+a numeric value.  (This behavior mimics the behavior of the
+select.select() function.)
+
+This module implements two classes: TimeoutSocket and TimeoutFile.
+
+The TimeoutSocket class defines a socket-like object that attempts to
+avoid the condition where a socket may block indefinitely.  The
+TimeoutSocket class raises a Timeout exception whenever the
+current operation delays too long. 
+
+The TimeoutFile class defines a file-like object that uses the TimeoutSocket
+class.  When the makefile() method of TimeoutSocket is called, it returns
+an instance of a TimeoutFile.
+
+Each of these objects adds two methods to manage the timeout value:
+
+    get_timeout()   -->  returns the timeout of the socket or file
+    set_timeout()   -->  sets the timeout of the socket or file
+
+
+As an example, one might use the timeout feature to create httplib
+connections that will timeout after 30 seconds:
+
+    import timeoutsocket
+    import httplib
+    H = httplib.HTTP("www.python.org")
+    H.sock.set_timeout(30)
+
+Note:  When used in this manner, the connect() routine may still
+block because it happens before the timeout is set.  To avoid
+this, use the 'timeoutsocket.setDefaultSocketTimeout()' function.
+
+Good Luck!
+
+"""
+
+__version__ = "$Revision: 1.1 $"
+__author__  = "Timothy O'Malley <timo@alum.mit.edu>"
+
+#
+# Imports
+#
+import select, string
+import socket
+if not hasattr(socket, "_no_timeoutsocket"):
+    _socket = socket.socket
+else:
+    _socket = socket._no_timeoutsocket
+
+
+#
+# Set up constants to test for Connected and Blocking operations.
+# We delete 'os' and 'errno' to keep our namespace clean(er).
+# Thanks to Alex Martelli and G. Li for the Windows error codes.
+#
+import os
+if os.name == "nt":
+    _IsConnected = ( 10022, 10056 )
+    _ConnectBusy = ( 10035, )
+    _AcceptBusy  = ( 10035, )
+else:
+    import errno
+    _IsConnected = ( errno.EISCONN, )
+    _ConnectBusy = ( errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK )
+    _AcceptBusy  = ( errno.EAGAIN, errno.EWOULDBLOCK )
+    del errno
+del os
+
+
+#
+# Default timeout value for ALL TimeoutSockets
+#
+_DefaultTimeout = None
+def setDefaultSocketTimeout(timeout):
+    global _DefaultTimeout
+    _DefaultTimeout = timeout
+def getDefaultSocketTimeout():
+    return _DefaultTimeout
+
+#
+# Exceptions for socket errors and timeouts
+#
+Error = socket.error
+class Timeout(Exception):
+    pass
+
+
+#
+# Factory function
+#
+from socket import AF_INET, SOCK_STREAM
+def timeoutsocket(family=AF_INET, type=SOCK_STREAM, proto=None):
+    if family != AF_INET or type != SOCK_STREAM:
+        if proto:
+            return _socket(family, type, proto)
+        else:
+            return _socket(family, type)
+    return TimeoutSocket( _socket(family, type), _DefaultTimeout )
+# end timeoutsocket
+
+#
+# The TimeoutSocket class definition
+#
+class TimeoutSocket:
+    """TimeoutSocket object
+    Implements a socket-like object that raises Timeout whenever
+    an operation takes too long.
+    The definition of 'too long' can be changed using the
+    set_timeout() method.
+    """
+
+    _copies = 0
+    _blocking = 1
+    
+    def __init__(self, sock, timeout):
+        self._sock     = sock
+        self._timeout  = timeout
+    # end __init__
+
+    def __getattr__(self, key):
+        return getattr(self._sock, key)
+    # end __getattr__
+
+    def get_timeout(self):
+        return self._timeout
+    # end set_timeout
+
+    def set_timeout(self, timeout=None):
+        self._timeout = timeout
+    # end set_timeout
+
+    def setblocking(self, blocking):
+        self._blocking = blocking
+        return self._sock.setblocking(blocking)
+    # end set_timeout
+
+    def connect_ex(self, addr):
+        errcode = 0
+        try:
+            self.connect(addr)
+        except Error, why:
+            errcode = why[0]
+        return errcode
+    # end connect_ex
+        
+    def connect(self, addr, port=None, dumbhack=None):
+        # In case we were called as connect(host, port)
+        if port != None:  addr = (addr, port)
+
+        # Shortcuts
+        sock    = self._sock
+        timeout = self._timeout
+        blocking = self._blocking
+
+        # First, make a non-blocking call to connect
+        try:
+            sock.setblocking(0)
+            sock.connect(addr)
+            sock.setblocking(blocking)
+            return
+        except Error, why:
+            # Set the socket's blocking mode back
+            sock.setblocking(blocking)
+            
+            # If we are not blocking, re-raise
+            if not blocking:
+                raise
+            
+            # If we are already connected, then return success.
+            # If we got a genuine error, re-raise it.
+            errcode = why[0]
+            if dumbhack and errcode in _IsConnected:
+                return
+            elif errcode not in _ConnectBusy:
+                raise
+            
+        # Now, wait for the connect to happen
+        # ONLY if dumbhack indicates this is pass number one.
+        #   If select raises an error, we pass it on.
+        #   Is this the right behavior?
+        if not dumbhack:
+            r,w,e = select.select([], [sock], [], timeout)
+            if w:
+                return self.connect(addr, dumbhack=1)
+
+        # If we get here, then we should raise Timeout
+        raise Timeout("Attempted connect to %s timed out." % str(addr) )
+    # end connect
+
+    def accept(self, dumbhack=None):
+        # Shortcuts
+        sock     = self._sock
+        timeout  = self._timeout
+        blocking = self._blocking
+
+        # First, make a non-blocking call to accept
+        #  If we get a valid result, then convert the
+        #  accept'ed socket into a TimeoutSocket.
+        # Be carefult about the blocking mode of ourselves.
+        try:
+            sock.setblocking(0)
+            newsock, addr = sock.accept()
+            sock.setblocking(blocking)
+            timeoutnewsock = self.__class__(newsock, timeout)
+            timeoutnewsock.setblocking(blocking)
+            return (timeoutnewsock, addr)
+        except Error, why:
+            # Set the socket's blocking mode back
+            sock.setblocking(blocking)
+
+            # If we are not supposed to block, then re-raise
+            if not blocking:
+                raise
+            
+            # If we got a genuine error, re-raise it.
+            errcode = why[0]
+            if errcode not in _AcceptBusy:
+                raise
+            
+        # Now, wait for the accept to happen
+        # ONLY if dumbhack indicates this is pass number one.
+        #   If select raises an error, we pass it on.
+        #   Is this the right behavior?
+        if not dumbhack:
+            r,w,e = select.select([sock], [], [], timeout)
+            if r:
+                return self.accept(dumbhack=1)
+
+        # If we get here, then we should raise Timeout
+        raise Timeout("Attempted accept timed out.")
+    # end accept
+
+    def send(self, data, flags=0):
+        sock = self._sock
+        if self._blocking:
+            r,w,e = select.select([],[sock],[], self._timeout)
+            if not w:
+                raise Timeout("Send timed out")
+        return sock.send(data, flags)
+    # end send
+
+    def recv(self, bufsize, flags=0):
+        sock = self._sock
+        if self._blocking:
+            r,w,e = select.select([sock], [], [], self._timeout)
+            if not r:
+                raise Timeout("Recv timed out")
+        return sock.recv(bufsize, flags)
+    # end recv
+
+    def makefile(self, flags="r", bufsize=-1):
+        self._copies = self._copies +1
+        return TimeoutFile(self, flags, bufsize)
+    # end makefile
+
+    def close(self):
+        if self._copies <= 0:
+            self._sock.close()
+        else:
+            self._copies = self._copies -1
+    # end close
+
+# end TimeoutSocket
+
+
+class TimeoutFile:
+    """TimeoutFile object
+    Implements a file-like object on top of TimeoutSocket.
+    """
+    
+    def __init__(self, sock, mode="r", bufsize=4096):
+        self._sock          = sock
+        self._bufsize       = 4096
+        if bufsize > 0: self._bufsize = bufsize
+        if not hasattr(sock, "_inqueue"): self._sock._inqueue = ""
+
+    # end __init__
+
+    def __getattr__(self, key):
+        return getattr(self._sock, key)
+    # end __getattr__
+
+    def close(self):
+        self._sock.close()
+        self._sock = None
+    # end close
+    
+    def write(self, data):
+        self.send(data)
+    # end write
+
+    def read(self, size=-1):
+        _sock = self._sock
+        _bufsize = self._bufsize
+        while 1:
+            datalen = len(_sock._inqueue)
+            if datalen >= size >= 0:
+                break
+            bufsize = _bufsize
+            if size > 0:
+                bufsize = min(bufsize, size - datalen )
+            buf = self.recv(bufsize)
+            if not buf:
+                break
+            _sock._inqueue = _sock._inqueue + buf
+        data = _sock._inqueue
+        _sock._inqueue = ""
+        if size > 0 and datalen > size:
+            _sock._inqueue = data[size:]
+            data = data[:size]
+        return data
+    # end read
+
+    def readline(self, size=-1):
+        _sock = self._sock
+        _bufsize = self._bufsize
+        while 1:
+            idx = string.find(_sock._inqueue, "\n")
+            if idx >= 0:
+                break
+            datalen = len(_sock._inqueue)
+            if datalen >= size >= 0:
+                break
+            bufsize = _bufsize
+            if size > 0:
+                bufsize = min(bufsize, size - datalen )
+            buf = self.recv(bufsize)
+            if not buf:
+                break
+            _sock._inqueue = _sock._inqueue + buf
+
+        data = _sock._inqueue
+        _sock._inqueue = ""
+        if idx >= 0:
+            idx = idx + 1
+            _sock._inqueue = data[idx:]
+            data = data[:idx]
+        elif size > 0 and datalen > size:
+            _sock._inqueue = data[size:]
+            data = data[:size]
+        return data
+    # end readline
+
+    def readlines(self, sizehint=-1):
+        result = []
+        data = self.read()
+        while data:
+            idx = string.find(data, "\n")
+            if idx >= 0:
+                idx = idx + 1
+                result.append( data[:idx] )
+                data = data[idx:]
+            else:
+                result.append( data )
+                data = ""
+        return result
+    # end readlines
+
+    def flush(self):  pass
+
+# end TimeoutFile
+
+
+#
+# Silently replace the socket() builtin function with
+# our timeoutsocket() definition.
+#
+if not hasattr(socket, "_no_timeoutsocket"):
+    socket._no_timeoutsocket = socket.socket
+    socket.socket = timeoutsocket
+del socket
+socket = timeoutsocket
+# Finis
Index: XMLRPCMethod/current/version.txt
===================================================================
--- XMLRPCMethod/current/version.txt	(revision 19)
+++ XMLRPCMethod/current/version.txt	(revision 19)
@@ -0,0 +1,1 @@
+XMLRPC Method-1-1
