Re: [Zope3-Users] Re: Is this a bug of HTTP response's head handling? (publisherhttpserver.py)

2006-09-21 Thread Simon Hang
Thanks Philipp, here is the unified diffs.
--- httptask.py.orig Fri Jan 06 02:15:48 2006+++ httptask.py Thu Sep 21 17:31:17 2006@@ -126,6 +126,15 @@ else: close_it = 1 elif version == '1.1':+ #modified by Simon
+ thisflag = False+ for each in self.accumulated_headers:+ if each.lower() == 'connection: keep-alive':+ thisflag = True+ break
+ if thisflag == False:+ close_it = 1+ if connection == 'close': close_it = 1 elif 'Transfer-Encoding' in response_headers:@@ -134,8 +143,15 @@
 elif self.status == '304': # Replying with headers only. pass+ #modified by simon elif not ('Content-Length' in response_headers):
- close_it = 1+ thisflag = False+ for each in self.accumulated_headers:+ if each[:14].lower() == 'content-length':+ thisflag = True
+ break+ if thisflag == False: #only content_length not exist in accumulated headers too+ close_it = 1 else: # Close if unrecognized HTTP version.
 close_it = 1
On 9/21/06, Philipp von Weitershausen [EMAIL PROTECTED] wrote:
Simon Hang wrote: How about below changes?There aren't many of us who know the zope.server
 code that well,unfortunately. This is one of the reasons we want to get out of theserver business.That said, it would *much* easier to understand what you're trying to doif you provided *unified diffs* (create them with svn diff or diff -u).
Philipp in httptask.py def prepareResponseHeaders(self): version = self.version # Figure out whether the connection should be closed.
 connection = self.request_data.headers.get('CONNECTION', '').lower() close_it = 0 response_headers = self.response_headers if version == '1.0': if connection == 'keep-alive':
 if not ('Content-Length' in response_headers): close_it = 1 else: response_headers['Connection'] = 'Keep-Alive' else:
 close_it = 1 elif version == '1.1': #insert by Simon thisflag = False for each in self.accumulated_headers: if 
each.lower() == 'connection: keep-alive': thisflag = True break if thisflag == False: close_it = 1 #end of insert
 if connection == 'close': close_it = 1 elif 'Transfer-Encoding' in response_headers: if not response_headers['Transfer-Encoding'] == 'chunked':
 close_it = 1 elif self.status == '304': # Replying with headers only. pass #insert by simon elif not ('Content-Length' in response_headers):
 thisflag = False for each in self.accumulated_headers: if each[:14].upper() == 'CONTENT-LENGTH': thisflag = True
 break if thisflag == False: #only CONTENT_LENGTH not exist in accumulated headers too #end of insert close_it = 1
 else: # Close if unrecognized HTTP version. close_it = 1 self.close_on_finish = close_it if close_it: self.response_headers
['Connection'] = 'close' On 9/19/06, *Simon Hang* [EMAIL PROTECTED] mailto:
[EMAIL PROTECTED] wrote: Dear all, While I was exploring my options to implement NTLM authentication, I found some strange behavior of Zope HTTP server.
 in zope.server.http.wsgihttpserver.WSGIHTTPServer, It use below function to handle response's head: def start_response(status, headers): # Prepare the headers for output
 status, reason = re.match('([0-9]*) (.*)', status).groups() task.setResponseStatus(status, reason) task.appendResponseHeaders(['%s: %s' % i for i in headers])
 # Return the write method used to write the response data. return fakeWrite The result is all response's head from the content part of program
 will be stored in task.accumulated_headers. See below function from zope.server.http.httptask.HTTPTask. def appendResponseHeaders(self, lst): See 
zope.publisher.interfaces.http.IHeaderOutput accum = self.accumulated_headers if accum is None: self.accumulated_headers = accum = [] 
accum.extend(lst) But, the problem is while httptask to determin whether to close the connection or not, it use below code. The code is only checking self.response_headers which has nothing to do with the response our
 application generated. So zope ends up to disconnect connection for each single request.def prepareResponseHeaders(self): version = self.version # Figure out whether the connection should be closed.
 connection = self.request_data.headers.get('CONNECTION', '').lower() close_it = 0 response_headers = self.response_headers if version == '
1.0': if connection == 'keep-alive': if not ('Content-Length' in response_headers): close_it = 1 else: response_headers['Connection'] = 'Keep-Alive'
 else: close_it = 1 elif version == '1.1': thisflag = False if connection == 'close': close_it = 1
 elif 'Transfer-Encoding' in response_headers: if not response_headers['Transfer-Encoding'] == 'chunked': close_it = 1 elif 
self.status == '304': # Replying with headers only. pass elif not ('Content-Length' in response_headers): close_it = 1
 else: # Close if unrecognized HTTP version. close_it = 1 self.close_on_finish = close_it if close_it:
 self.response_headers['Connection'] = 'close' Can somebody tell me why the thing is implement like this, is there special reason to do this? Or can we change it a little bit to let
 zope support persistence connection? Thanks, Simon

Re: [Zope3-Users] Re: Is this a bug of HTTP response's head handling? (publisherhttpserver.py)

2006-09-21 Thread Simon Hang
Philipp,
Sorry for being lazy, and thanks for the tips. Here is my update version.
--- httptask.py.orig Fri Jan 06 02:15:48 2006+++ httptask.py Fri Sep 22 09:13:48 2006@@ -126,6 +126,11 @@ else: close_it = 1 elif version == '1.1':+ #modified by Simon
+ if 'connection: close' in (header.lower() for header in+ self.accumulated_headers):+ #Close if 'connection: close' found in http response's header
+ close_it = 1 if connection == 'close': close_it = 1 elif 'Transfer-Encoding' in response_headers:@@ -134,8 +139,13 @@ elif self.status
 == '304': # Replying with headers only. pass+ #modified by simon elif not ('Content-Length' in response_headers):- close_it = 1
+ if 'content-length' not in (header[:14].lower() for header in+ self.accumulated_headers):+ #Close if 'content-length' not found in
+ #http response's header and self.response_headers+ close_it = 1 else: # Close if unrecognized HTTP version. close_it = 1

On 9/21/06, Philipp von Weitershausen [EMAIL PROTECTED] wrote:
Hi Simon,I have a few comments regarding style. First::if thisflag == False:...
is unnecessarily long. Just write::if not thisflag:...Also, what is thisflag? It'd be better to give it a descriptive name. --- httptask.py.origFri Jan 06 02:15:48 2006
 +++ httptask.py Thu Sep 21 17:31:17 2006 @@ -126,6 +126,15 @@else:close_it = 1elif version == '1.1': +#modified by Simon
 +thisflag = False +for each in self.accumulated_headers: +if each.lower() == 'connection: keep-alive': +thisflag = True +break
 +if thisflag == False: +close_it = 1 +I think you make this a lot simpler::if 'connection: keep-alive' not in (header.lower() for header in
self.accumulated_headers):close_it = 1(instead of the lines you added)if connection == 'close':close_it = 1elif 'Transfer-Encoding' in response_headers:
 @@ -134,8 +143,15 @@elif self.status == '304':# Replying with headers only.pass +#modified by simonelif not ('Content-Length' in response_headers):
 -close_it = 1 +thisflag = False +for each in self.accumulated_headers: +if each[:14].lower() == 'content-length': +thisflag = True
 +break +if thisflag == False: #only content_length not exist in accumulated headers too +close_it = 1I don't understand the comment (English grammar not correct), but my
suggestion would apply here as well, I think.
___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users


[Zope3-Users] Re: Is this a bug of HTTP response's head handling? (publisherhttpserver.py)

2006-09-20 Thread Simon Hang
How about below changes?

in httptask.py

 def prepareResponseHeaders(self): version = self.version # Figure out whether the connection should be closed. connection = self.request_data.headers.get('CONNECTION', '').lower()
 close_it = 0 response_headers = self.response_headers
 if version == '1.0': if connection == 'keep-alive': if not ('Content-Length' in response_headers): close_it = 1 else: response_headers['Connection'] = 'Keep-Alive'
 else: close_it = 1 elif version == '1.1': #insert by Simon thisflag = False for each in self.accumulated_headers: if 
each.lower() == 'connection: keep-alive': thisflag = True break if thisflag == False: close_it = 1 #end of insert if connection == 'close':
 close_it = 1 elif 'Transfer-Encoding' in response_headers: if not response_headers['Transfer-Encoding'] == 'chunked': close_it = 1 elif 
self.status == '304': # Replying with headers only. pass #insert by simon elif not ('Content-Length' in response_headers): thisflag = False
 for each in self.accumulated_headers: if each[:14].upper() == 'CONTENT-LENGTH': thisflag = True break if thisflag == False: #only CONTENT_LENGTH not exist in accumulated headers too

 #end of insert close_it = 1 else: # Close if unrecognized HTTP version. close_it = 1
 self.close_on_finish = close_it if close_it: self.response_headers['Connection'] = 'close'
On 9/19/06, Simon Hang [EMAIL PROTECTED] wrote:


Dear all,

While I was exploringmy options to implement NTLM authentication, I found some strange behavior of Zope HTTP server.

in zope.server.http.wsgihttpserver.WSGIHTTPServer, 
It use below function to handle response's head:

 def start_response(status, headers): # Prepare the headers for output status, reason = re.match('([0-9]*) (.*)', status).groups() task.setResponseStatus(status, reason) 
 task.appendResponseHeaders(['%s: %s' % i for i in headers])
 # Return the write method used to write the response data. return fakeWrite
The result is all response's head from the content part of program will be stored in task.accumulated_headers. See below function from zope.server.http.httptask.HTTPTask.
 def appendResponseHeaders(self, lst): See zope.publisher.interfaces.http.IHeaderOutput accum = self.accumulated_headers if accum is None: 
self.accumulated_headers = accum = [] accum.extend(lst)
But, the problem is while httptask to determin whether to close the connection or not, it use below code. The code is only checking self.response_headers which has nothing to do with the response our application generated. So zope ends up to disconnect connection for each single request. 

 def prepareResponseHeaders(self): version = self.version # Figure out whether the connection should be closed. connection = self.request_data.headers.get('CONNECTION', '').lower()
 close_it = 0 response_headers = self.response_headers
 if version == '1.0': if connection == 'keep-alive': if not ('Content-Length' in response_headers): close_it = 1 else: response_headers['Connection'] = 'Keep-Alive' 
 else: close_it = 1 elif version == '1.1': thisflag = False if connection == 'close': close_it = 1 elif 'Transfer-Encoding' in response_headers: 
 if not response_headers['Transfer-Encoding'] == 'chunked': close_it = 1 elif self.status == '304': # Replying with headers only. pass 
 elif not ('Content-Length' in response_headers): close_it = 1 else: # Close if unrecognized HTTP version. close_it = 1
 self.close_on_finish = close_it if close_it: self.response_headers['Connection'] = 'close'
Can somebody tell me why the thing is implement like this, is there special reason to do this? Or can we change it a little bit to let zope support persistence connection?
Thanks,Simon

___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users


Re: [Zope3-Users] Re: NTLM credential plugin

2006-09-19 Thread Simon Hang
Good news. I believe I found a way to do NTLM 4-way handshake with
zope3 and using PAU's plugin. But I need to modify zope3's http server
a little bit, to let zope3 support HTTP/1.1 persistent connection.

Currently I can 
1. send NTLM challenge
2. receive NTLM type-1 message
3. send NTLM type-2 message
4. receive NTLM type-3 message.

Only thing to do is decode type-3 message.

My concern is I need to modify zope3's http server, althogh only add
several lines. Is there anybody can validate my modifcation and
implement the change in proper zope3's way?

Currently I modified zope.server.http.httptask.HTTPTask, method
prepareResponseHeaders(). to let this function also check
accumulated_headers before decides to close the connection. Sorry, I
left the modified code in my other pc, can't post the detail.

Anybody can help?

Thanks,
SimonOn 9/15/06, Simon Hang [EMAIL PROTECTED] wrote:
Hi,

Why zope3 can not maintain active connections? Is this because
zope3 is using asynchronous socket(asyncore.py) to serve the request?
Errr... why zope3 is doing this? Won't this method cause overhead?

Sorry for lots of questions, but I don't understand.

Thanks,
Simon
On 9/13/06, Gary Poster 
[EMAIL PROTECTED] wrote:
On Sep 13, 2006, at 2:30 AM, Philipp von Weitershausen wrote:
 Simon Hang wrote: Hi,
I'm thinging to write a NTLM credential plugin for zope3. But as I know, ntlm use 4-way handshake procedure, that means it needs two round-trips between server(zope3) and client(browser).
When I look in the credential plugins, it has challenge mothed. But seems it is only design for 1 round-trip protocol. It can issue one challenge, and return to parent script.
 I don't see how the PAU only allows one round-trip.AIUI (I just looked up NTLM last night out of curiosity: see http://
www.innovation.ch/personal/ronald/ntlm.html
), the problem is that the4 way handshake has to happen *within a single connection*.Apparently MS abuses HTTP to perform this.Implementing it inpluggable auth made me scratch my head a bit, so I didn't reply.You
would need to slurp the request, then push back to the response, thenslurp the same request again, then push back to the response, thenslurp one more time, and finally reply with the real request.Describing the problem to Benji, he mentioned WSGI--that does seem
like the only way I can imagine this working, and that would betricky enough, especially if you needed to reach into Zope for themanaged credentials.Once the WSGI plugin did its magic, it wouldneed to put something in the WSGI request that a pluggable auth
plugin was willing to accept as authentication.On the bright side, if you did this with WSGI you might be able tooffer this as a generic Python WSGI NTLM tool that required onlyminimal integration with the back end app server.
I'm glad I'm not tasked with this. :-DIt sounds interesting,though.Also, maybe I misunderstand: read the link if you want tocome up with your own interpretation.Gary___
Zope3-users mailing listZope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users



___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users


[Zope3-Users] Is this a bug of HTTP response's head handling? (publisherhttpserver.py)

2006-09-18 Thread Simon Hang
Dear all,

While I was exploringmy options to implement NTLM authentication, I found some strange behavior of Zope HTTP server.

in zope.server.http.wsgihttpserver.WSGIHTTPServer, 
It use below function to handle response's head:

 def start_response(status, headers): # Prepare the headers for output status, reason = re.match('([0-9]*) (.*)', status).groups() task.setResponseStatus(status, reason)
 task.appendResponseHeaders(['%s: %s' % i for i in headers])
 # Return the write method used to write the response data. return fakeWrite
The result is all response's head from the content part of program will be stored in task.accumulated_headers. See below function from zope.server.http.httptask.HTTPTask.
 def appendResponseHeaders(self, lst): See zope.publisher.interfaces.http.IHeaderOutput accum = self.accumulated_headers if accum is None: 
self.accumulated_headers = accum = [] accum.extend(lst)
But, the problem is while httptask to determin whether to close the connection or not, it use below code. The code is only checking self.response_headers which has nothing to do with the response our application generated. So zope ends up to disconnect connection for each single request.

 def prepareResponseHeaders(self): version = self.version # Figure out whether the connection should be closed. connection = self.request_data.headers.get('CONNECTION', '').lower()
 close_it = 0 response_headers = self.response_headers
 if version == '1.0': if connection == 'keep-alive': if not ('Content-Length' in response_headers): close_it = 1 else: response_headers['Connection'] = 'Keep-Alive'
 else: close_it = 1 elif version == '1.1': thisflag = False if connection == 'close': close_it = 1 elif 'Transfer-Encoding' in response_headers:
 if not response_headers['Transfer-Encoding'] == 'chunked': close_it = 1 elif self.status == '304': # Replying with headers only. pass
 elif not ('Content-Length' in response_headers): close_it = 1 else: # Close if unrecognized HTTP version. close_it = 1
 self.close_on_finish = close_it if close_it: self.response_headers['Connection'] = 'close'
Can somebody tell me why the thing is implement like this, is there special reason to do this? Or can we change it a little bit to let zope support persistence connection?
Thanks,Simon

___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users


[Zope3-Users] NTLM credential plugin

2006-09-12 Thread Simon Hang
Hi,

I'm thinging to write a NTLM credential plugin for zope3. But as I know, ntlm use 4-way handshake procedure, that means it needs two round-trips between server(zope3) and client(browser).

When I look in the credential plugins, it has challenge mothed. But seems it is only design for 1 round-trip protocol. It can issue one challenge, and return to parent script. 

The question is: How can I do this kind 4-way handshake within the challenge mothed or is there any other way design for this kind protocol?

Thanks,
Simon

Attach: NTLM handshake
1: C  -- S   GET ...

2: C --  S   401 Unauthorized
  WWW-Authenticate: NTLM

3: C  -- S   GET ...
  Authorization: NTLM base64-encoded type-1-message

4: C --  S   401 Unauthorized
  WWW-Authenticate: NTLM base64-encoded type-2-message

5: C  -- S   GET ...
  Authorization: NTLM base64-encoded type-3-message

6: C --  S   200 Ok

___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users


Re: [Zope3-Users] apache as zope3's frontend and NTLM

2005-11-10 Thread Simon Hang
Not under heavy load. Any machine should be able to handle this.
I use the config below
VirtualHost *:808 DocumentRoot/myplace/httpdoc Servernamemyserver ErrorLog logs/myserver-error.log CustomLog logs/myserver-access.log common Location /  RewriteEngine On
 RewriteRule ^c:/ftscompass/htdocs(/?.*) http://localhost:8080/++vh++http:myserver:808/++$1 [P,L] IfModule mod_ntlm.c AuthName NTLM
 AuthType NTLM NTLMAuth On NTLMAuthoritative On NTLMDomainmydomain NTLMOfferBasic On NTLMBasicPreferred Off require valid-user
 /IfModule /Location
/VirtualHost

But now, next question is how to let zope know the authenticated user?
REMOTE_USER is only available in CGI  SSL mode.
How to pass this before apache proxy the request?

Anybody can help?

Thanks,
Simon

On 11/11/05, Chris Withers [EMAIL PROTECTED] wrote:
How much load are you putting this under?ChrisSimon Hang wrote: Hi Chris,Yes, it's working.
I'm using Unofficial MOD_NTLM Apache Module from http://modntlm.jamiekerwick.co.uk/.It looks like can make both thing working, and I will double check...
 I will update tomorrow if any progress..SimonOn 11/10/05, Chris Withers [EMAIL PROTECTED] wrote:Simon Hang wrote:
1. Installed mod_ntlm for apache 1.3, and tested.Really? Did you get this to work? I've heard it was flakey and/orbroken...cheers,
Chris--Simplistix - Content Management, Zope  Python Consulting- http://www.simplistix.co.uk
--Simplistix - Content Management, Zope  Python Consulting - http://www.simplistix.co.uk
___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users


[Zope3-Users] apache as zope3's frontend and NTLM

2005-11-09 Thread Simon Hang
Dear all,

I'm trying to use apache as zope3's frontend, and do NTLM authentication as well.

I've done:
1. Installed mod_ntlm for apache 1.3, and tested.
2. Create a VirtualHost for zope3 instance, forwarding http request using rewrite engine. And tested.

Now I try to put things together = A virtualhost can do NTLM authentication and forward request to zope3, my virtualconfigration of apache as below:

VirtualHost *:808 DocumentRoot c:/myroot Servernamemyserver ErrorLog logs/myerror.log CustomLog logs/myaccess.log common RewriteEngine On RewriteRule ^(/?.*) 
http://localhost:8080/++vh++http:myserver:808/++$1 [P,L]  Location / IfModule mod_ntlm.c AuthName realm AuthType NTLM NTLMAuth On
 NTLMAuthoritative On NTLMDomainmydomain NTLMOfferBasic Off NTLMBasicPreferred Off require valid-user /IfModule /Location
/VirtualHost

Everytime I try to access the page, the brower show me error message as below:

Authorization RequiredThis server could not verify that you are authorized to access the document requested. Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.


What's wrong in my settings? 

Thanks in advance,
Simon
___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users


Re: [Zope3-Users] MessageBoard tutorial deprecation warnings

2005-11-02 Thread Simon Hang
Hi Brad,

You need to install the workflow add-on package for zope3 and try again. URL as below:
http://www.zope.org/Products/Zope3-Packages
Deprecation Warnings are only warning. The code won't break because of warnings.

Cheers,
Simon
On 10/31/05, Brad Allen [EMAIL PROTECTED] wrote:
The messageboard tutorial in the Zope 3 Developer's Handbookdoesn't seem to work for me. I'm using the copy fromsvn co svn://svn.zope.org/repos/main/book/trunk
The SVN url listed in the book didn't work, but I'm notsure this is the right one.Anyway, I'm using the step13 folder, and when I registeredit with my Zope 3.1 instance on Ubuntu, and run Zope, I get
the following in my transcript.log:ConfigurationError: ('Invalid value for', 'interface', Couldn'timport zope.app.workflow.interfaces, No module named workflow.interfacesin zope.app.workflow.interfaces.IProcessInstanceContainerAdaptable
)I wanted to see more detail on that error, so I imported the offendingmodule at the Python interactive prompt. Along the way severaldeprecation warnings cropped up (see below). Is there a newerversion of this tutorial I'm missing out on, or should I spend
some time trying to clean this up?Thanks![EMAIL PROTECTED]:/var/lib/zope3/instance/sandbox/lib/python$ pythonPython 2.4.2 (#2, Sep 30 2005, 21:19:01)[GCC 4.0.2 20050808 (prerelease) (Ubuntu 4.0.1-4ubuntu8
)] on linux2Type help, copyright, credits or license for more information. import book.messageboard.browser.messageboardbook/messageboard/browser/messageboard.py:22: DeprecationWarning:
ActiveStatus: ActiveStatus is now available inzope.app.component.interfaces.registration. Will be gone in X3.3.from zope.app.registration.interfaces import ActiveStatusbook/messageboard/browser/messageboard.py:23: DeprecationWarning: ISite:
This interface has been moved to zope.app.component.interfaces. Thereference will be gone in X3.3.from zope.app.site.interfaces import ISitebook/messageboard/browser/messageboard.py:24: DeprecationWarning:
SiteManager: This class has been moved to zope.app.component.site. Thereference will be gone in X3.3.from zope.app.site.service import SiteManager, ServiceRegistrationbook/messageboard/browser/messageboard.py:24: DeprecationWarning:
ServiceRegistration: The concept of services has been removed. Useutilities instead. The reference will be gone in X3.3.from zope.app.site.service import SiteManager, ServiceRegistrationbook/messageboard/browser/messageboard.py:25: DeprecationWarning:
LocalUtilityService: Services have been removed. Use site manager API.The reference will be gone in X3.3.from zope.app.utility.utility import LocalUtilityService,UtilityRegistrationbook/messageboard/browser/messageboard.py:25: DeprecationWarning:
UtilityRegistration: This class has been moved tozope.app.component.site. The reference will be gone in X3.3.from zope.app.utility.utility import LocalUtilityService,UtilityRegistrationTraceback (most recent call last):
File stdin, line 1, in ?File book/messageboard/browser/messageboard.py, line 26, in ?from zope.app.workflow.interfaces importIProcessDefinitionImportHandlerImportError: No module named 
workflow.interfaces___Zope3-users mailing listZope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users
___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users


[Zope3-Users] Any document for use ZopeDatabaseAdapter?

2005-10-28 Thread Simon Hang
Hi,

Is there any document or examples for ZopeDatabaseAdapter?

I need to do some sql query in my content object.

Thanks,
Simon


___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users


[Zope3-Users] Any document for use ZopeDatabaseAdapter?

2005-10-27 Thread Simon Hang

Hi,

Is there any document or examples for ZopeDatabaseAdapter?

I need to do some sql query in my content object.

Thanks,
Simon
___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users