[openerp] Initial import (#817271)

leamas leamas at fedoraproject.org
Mon Jul 2 20:18:54 UTC 2012


commit 119c6e863865df06930563e23f21427540208e1f
Author: Alec Leamas <alec at tests.notat.diaspora.com>
Date:   Mon Jul 2 22:18:37 2012 +0200

    Initial import (#817271)

 .gitignore                                         |    1 +
 LICENSING                                          |  102 +
 README.fedora                                      |   72 +
 openerp-fsf-fix.patch                              |  847 ++++++
 openerp-gen-cert                                   |   53 +
 ...rp-server-relicense-dict_tools-to-LGPL2.1.patch |   25 +
 openerp-unbundle-pyftpdlib.patch                   | 3075 ++++++++++++++++++++
 openerp.service                                    |   16 +
 openerp.spec                                       |  227 ++
 sources                                            |    1 +
 10 files changed, 4419 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index e69de29..cd3e38f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/openerp-6.1-20120505-233516.tar.gz
diff --git a/LICENSING b/LICENSING
new file mode 100644
index 0000000..8c3be98
--- /dev/null
+++ b/LICENSING
@@ -0,0 +1,102 @@
+Unless stated below, openerp is published under the Affero General
+Public License  version 3 (AGPLv3)
+
+BSD licensed files
+------------------
+
+./bin/report/pyPdf/utils.py: BSD (3 clause)
+./bin/report/pyPdf/filters.py: BSD (3 clause)
+./bin/report/pyPdf/pdf.py: BSD (3 clause)
+./bin/report/pyPdf/generic.py: BSD (3 clause)
+./bin/tools/threadinglocal.py: BSD (3 clause)
+./bin/addons/l10n_cr/__init__.py: BSD (2 clause)
+./bin/addons/l10n_cr/__openerp__.py: BSD (2 clause)
+../bin/addons/auction/barcode/common.py: BSD (4 clause)
+./bin/addons/auction/barcode/code128.py: BSD (4 clause)
+./bin/addons/auction/barcode/usps.py: BSD (4 clause)
+./bin/addons/auction/barcode/code93.py: BSD (4 clause)
+./bin/addons/auction/barcode/__init__.py: BSD (4 clause)
+./bin/addons/auction/barcode/code39.py: BSD (4 clause)
+./bin/addons/auction/barcode/fourstate.py: BSD (4 clause)
+./bin/addons/crm/scripts/php/xmlrpc.inc: BSD (3 clause)
+./bin/addons/wiki/web/widgets/rss/feedparser.py: BSD (2 clause)
+
+
+GPL version 2 or later, GPL version 3.
+--------------------------------------
+
+All these files are published under the terms of GPL version 3.
+
+./bin/netsvc.py: GPL (v3 or later)
+./bin/service/http_server.py: GPL (v2 or later)
+./bin/service/websrv_lib.py: GPL (v2 or later)
+./bin/addons/report_webkit_sample/report/__init__.py: GPL (v2 or later)
+./bin/addons/report_webkit_sample/__init__.py: GPL (v2 or later)
+./bin/addons/report_webkit_sample/__openerp__.py: GPL (v2 or later)
+./bin/addons/l10n_es/__init__.py: GPL (v3 or later)
+./bin/addons/l10n_es/__openerp__.py: GPL (v3 or later)
+./bin/addons/email_template/email_template.py: GPL (v3 or later)
+./bin/addons/email_template/email_template_account.py: GPL (v3 or later)
+./bin/addons/email_template/__init__.py: GPL (v3 or later)
+./bin/addons/email_template/__openerp__.py: GPL (v3 or later)
+./bin/addons/email_template/wizard/email_template_send_wizard.py: GPL (v3 or later)
+./bin/addons/email_template/wizard/__init__.py: GPL (v3 or later)
+./bin/addons/email_template/email_template_mailbox.py: GPL (v3 or later)
+./bin/addons/l10n_in/__init__.py: GPL (v2 or later)
+./bin/addons/thunderbird/__openerp__.py: GPL (v3 or later)
+./bin/addons/document_webdav/webdav_server.py: GPL (v3 or later)
+./bin/addons/document_webdav/test_davclient.py: GPL (v2 or later)
+./bin/addons/document_webdav/__openerp__.py: GPL (v2 or later)
+./bin/addons/document/document_storage.py: GPL (v3 or later)
+./bin/addons/l10n_nl/__init__.py: GPL (v2 or later)
+./bin/addons/l10n_nl/__openerp__.py: GPL (v2 or later)
+./bin/addons/l10n_gt/__init__.py: GPL (v3 or later)
+./bin/addons/l10n_gt/__openerp__.py: GPL (v3 or later)
+./bin/addons/l10n_mx/__init__.py: GPL (v3 or later)
+./bin/addons/l10n_mx/__openerp__.py: GPL (v3 or later)
+./bin/addons/l10n_ve/__init__.py: GPL (v3 or later)
+./bin/addons/l10n_ve/__openerp__.py: GPL (v3 or later)
+./bin/addons/account_sequence/account_sequence.py: GPL (v3 or later)
+./bin/addons/base_crypt/crypt.py: GPL (v2 or later)
+./bin/addons/base_vat/base_vat.py: GPL (v3 or later)
+./bin/addons/l10n_ca/__init__.py: GPL (v3 or later)
+./bin/addons/l10n_gr/__init__.py: GPL (v3 or later)
+./bin/addons/l10n_gr/__openerp__.py: GPL (v3 or later)
+./bin/addons/l10n_br/__init__.py: GPL (v3 or later)
+./bin/addons/l10n_br/__openerp__.py: GPL (v3 or later)
+./bin/addons/l10n_cn/__openerp__.py: GPL (v3 or later)
+./bin/addons/l10n_fr/report/compute_resultant_report.py: GPL (v2 or later)
+./bin/addons/l10n_fr/report/base_report.py: GPL (v2 or later)
+./bin/addons/l10n_fr/report/__init__.py: GPL (v2 or later)
+./bin/addons/l10n_fr/report/bilan_report.py: GPL (v2 or later)
+./bin/addons/l10n_fr/__init__.py: GPL (v2 or later)
+./bin/addons/l10n_fr/__openerp__.py: GPL (v2 or later)
+./bin/addons/l10n_fr/wizard/fr_report_bilan.py: GPL (v2 or later)
+./bin/addons/l10n_fr/wizard/__init__.py: GPL (v2 or later)
+./bin/addons/l10n_fr/wizard/fr_report_compute_resultant.py: GPL (v2 or later)
+./bin/addons/anonymization/anonymization.py: GPL (v3 or later)
+./bin/addons/anonymization/__init__.py: GPL (v3 or later)
+./bin/addons/anonymization/__openerp__.py: GPL (v3 or later)
+./bin/addons/l10n_ro/__init__.py: GPL (v3 or later)
+./bin/addons/l10n_ro/res_partner.py: GPL (v3 or later)
+./bin/addons/l10n_ro/__openerp__.py: GPL (v3 or later)
+./bin/addons/l10n_uk/__init__.py: GPL (v2 or later)
+./bin/addons/l10n_ma/l10n_ma.py: GPL (v3 or later)
+./bin/addons/l10n_ch/report/__init__.py: GPL (v2 or later)
+./bin/addons/report_webkit/ir_report.py: GPL (v2 or later)
+./bin/addons/report_webkit/webkit_report.py: GPL (v2 or later)
+./bin/addons/report_webkit/header.py: GPL (v2 or later)
+./bin/addons/report_webkit/company.py: GPL (v2 or later)
+./bin/addons/report_webkit/__init__.py: GPL (v2 or later)
+./bin/addons/report_webkit/__openerp__.py: GPL (v2 or later)
+./bin/addons/report_webkit/wizard/report_webkit_actions.py: GPL (v2 or later)
+./bin/addons/report_webkit/wizard/__init__.py: GPL (v2 or later)
+./bin/addons/report_webkit/report_helper.py: GPL (v2 or later)
+./bin/addons/account/report/account_general_ledger.py: GPL (v2 or later)
+./bin/addons/account/account_cash_statement.py: GPL (v3 or later)
+./bin/addons/l10n_pl/__init__.py: GPL (v3 or later)
+./bin/addons/l10n_pl/__openerp__.py: GPL (v3 or later)
+
+Files under LGPLv2+:
+--------------------
+./bin/addons/document/dict_tools.py: LGPL
diff --git a/README.fedora b/README.fedora
new file mode 100644
index 0000000..9c7ccf2
--- /dev/null
+++ b/README.fedora
@@ -0,0 +1,72 @@
+# Fedora getting-started for Fedora >= F16
+
+There are several steps to get the server running:
+
+- Install postgresql and start the server.
+- Generate openerp server key and certificate.
+- Create the postgresql openerp user and it's database
+- Start the server.
+
+## Install postgresql.
+
+    $ sudo  yum -y install postgresql-server
+    $ sudo postgresql-setup initdb
+    $ sudo su - postgres
+    bash-2.4$ sed -i  '/^host/s/[^ ]*$/password/' \
+                       /var/lib/pgsql/data/pg_hba.conf
+    bash-2.4$ exit
+    $ sudo systemctl enable postgresql.service
+    $ sudo systemctl start postgresql.service
+
+The modifications of pg_hba.conf let the local postgresql user run also
+as postgres db user without authenticationĀ§, while all TCP-IP connections
+need a password.
+
+See: /usr/share/doc/postgresql-*/README.rpm-dist
+
+## Install openerp and generate server key and certificate.
+
+To install and generate a simple certificate and a key, both stored
+in /etc/openerp::
+
+    $ sudo yum install openerp
+    $ sudo openerp-gen-cert
+
+## Create database user. and it's database
+
+To create the openerp user, you need to set the password by
+editing /etc/openerp/openerp-server.conf. You should set
+db_name, db_user and db_password. To create database,
+assuming db_name == db_user == 'openerp'::
+
+    $ sudo su - postgres
+    bash-4.2$  createuser --createdb --no-createrole --pwprompt openerp
+    Enter password for new role: XXXXXXXXXX
+    Enter it again: XXXXXXXXXX
+    Shall the new role be a superuser? (y/n) y
+    CREATE ROLE
+    bash-4.2$ createdb -W -U openerp openerp
+    Password: XXXXXXXX
+    bash-4.2$ exit
+
+All three passwords are the same as db_password in
+/etc/openerp/openerp-server.conf.
+
+## Start openerp server
+
+    $ sudo systemctl start openerp.service
+
+and verify that /var/log/messages looks OK. You can make server start at
+boot using 'systemctl enable openerp.service'
+
+## Continue installation
+
+From now on you need to create an openerp database. For this, you need the
+gtk client:
+
+    $ sudo yum install openerp-client
+    $ openerp-client
+
+and proceed according to
+http://doc.openerp.com/v6.0/book/1/1_1_Inst_Config/1_1_Inst_Config_db_create.html
+
diff --git a/openerp-fsf-fix.patch b/openerp-fsf-fix.patch
new file mode 100644
index 0000000..5f21fdd
--- /dev/null
+++ b/openerp-fsf-fix.patch
@@ -0,0 +1,847 @@
+diff --git a/openerp/addons/account/report/account_general_ledger.py b/openerp/addons/account/report/account_general_ledger.py
+index 294c7fa..620f5be 100644
+--- a/openerp/addons/account/report/account_general_ledger.py
++++ b/openerp/addons/account/report/account_general_ledger.py
+@@ -23,7 +23,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/base_crypt/crypt.py b/openerp/addons/base_crypt/crypt.py
+index c2fd0a2..575d5cd 100644
+--- a/openerp/addons/base_crypt/crypt.py
++++ b/openerp/addons/base_crypt/crypt.py
+@@ -32,8 +32,8 @@
+ # with this program; if not, write to the:
+ #
+ # Free Software Foundation, Inc.
+-# 59 Temple Place - Suite 330
+-# Boston, MA  02111-1307
++# 51 Franklin Street, Fifth Floor
++# Boston, MA 02110-1301
+ # USA.
+ 
+ from random import seed, sample
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/About.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/About.py
+index 401cf88..f7b8264 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/About.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/About.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/AddAttachment.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/AddAttachment.py
+index c65ba32..4d6be2b 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/AddAttachment.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/AddAttachment.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Change.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Change.py
+index cdc5c75..9707d5b 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Change.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Change.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ConvertBracesToField.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ConvertBracesToField.py
+index bb80c3e..8a9227c 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ConvertBracesToField.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ConvertBracesToField.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ConvertFieldsToBraces.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ConvertFieldsToBraces.py
+index 892061c..fb5516f 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ConvertFieldsToBraces.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ConvertFieldsToBraces.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ExportToRML.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ExportToRML.py
+index dd00f17..70f9c2b 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ExportToRML.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ExportToRML.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Expression.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Expression.py
+index ecdf134..839d2d8 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Expression.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Expression.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Fields.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Fields.py
+index 92044f7..3e8113d 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Fields.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Fields.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/LoginTest.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/LoginTest.py
+index 85059d7..4b7896f 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/LoginTest.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/LoginTest.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ModifyExistingReport.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ModifyExistingReport.py
+index 2ccd835..568c2f9 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ModifyExistingReport.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ModifyExistingReport.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/NewReport.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/NewReport.py
+index 6022e44..b95e967 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/NewReport.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/NewReport.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Repeatln.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Repeatln.py
+index 0eab6d1..3747737 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Repeatln.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Repeatln.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/SendToServer.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/SendToServer.py
+index 3a6d799..7425162 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/SendToServer.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/SendToServer.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ServerParameter.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ServerParameter.py
+index 9b69e4a..b22a27c 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ServerParameter.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ServerParameter.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Translation.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Translation.py
+index 73146f1..864ae63 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Translation.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Translation.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/__init__.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/__init__.py
+index 0ccd1c9..8b773dc 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/__init__.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/__init__.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/compile_all.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/compile_all.py
+index 4740c61..082b2cc 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/compile_all.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/compile_all.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/actions.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/actions.py
+index 8511bb8..39127b8 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/actions.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/actions.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/error.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/error.py
+index c0a3e5b..9a31e44 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/error.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/error.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/functions.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/functions.py
+index 29f1cd8..eac97c0 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/functions.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/functions.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/gui.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/gui.py
+index 975aa38..faffde6 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/gui.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/lib/gui.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/modify.py b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/modify.py
+index fc3d667..0f60b6b 100644
+--- a/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/modify.py
++++ b/openerp/addons/base_report_designer/plugin/openerp_report_designer/bin/script/modify.py
+@@ -18,7 +18,7 @@
+ # 
+ #   You should have received a copy of the GNU Lesser General Public 
+ #   License along with this library; if not, write to the Free Software 
+-#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
++#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
+ # 
+ #   See:  http://www.gnu.org/licenses/lgpl.html 
+ #
+diff --git a/openerp/addons/document_webdav/__openerp__.py b/openerp/addons/document_webdav/__openerp__.py
+index 82541b7..09245e7 100644
+--- a/openerp/addons/document_webdav/__openerp__.py
++++ b/openerp/addons/document_webdav/__openerp__.py
+@@ -24,7 +24,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/document_webdav/test_davclient.py b/openerp/addons/document_webdav/test_davclient.py
+index 3c20cfe..44be73a 100755
+--- a/openerp/addons/document_webdav/test_davclient.py
++++ b/openerp/addons/document_webdav/test_davclient.py
+@@ -24,7 +24,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ###############################################################################
+ 
+ """ A trivial HTTP/WebDAV client, used for testing the server
+diff --git a/openerp/addons/document_webdav/webdav_server.py b/openerp/addons/document_webdav/webdav_server.py
+index ae75d0b..dd39d55 100644
+--- a/openerp/addons/document_webdav/webdav_server.py
++++ b/openerp/addons/document_webdav/webdav_server.py
+@@ -30,7 +30,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ###############################################################################
+ 
+ 
+diff --git a/openerp/addons/l10n_ch/report/__init__.py b/openerp/addons/l10n_ch/report/__init__.py
+index e8703f1..eecc67f 100644
+--- a/openerp/addons/l10n_ch/report/__init__.py
++++ b/openerp/addons/l10n_ch/report/__init__.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_fr/__init__.py b/openerp/addons/l10n_fr/__init__.py
+index 8d5728e..d2ec220 100644
+--- a/openerp/addons/l10n_fr/__init__.py
++++ b/openerp/addons/l10n_fr/__init__.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_fr/__openerp__.py b/openerp/addons/l10n_fr/__openerp__.py
+index 839907c..c375d73 100644
+--- a/openerp/addons/l10n_fr/__openerp__.py
++++ b/openerp/addons/l10n_fr/__openerp__.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ {
+diff --git a/openerp/addons/l10n_fr/report/__init__.py b/openerp/addons/l10n_fr/report/__init__.py
+index 9907a2f..5056318 100644
+--- a/openerp/addons/l10n_fr/report/__init__.py
++++ b/openerp/addons/l10n_fr/report/__init__.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_fr/report/base_report.py b/openerp/addons/l10n_fr/report/base_report.py
+index 2c44b06..8cb0009 100644
+--- a/openerp/addons/l10n_fr/report/base_report.py
++++ b/openerp/addons/l10n_fr/report/base_report.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_fr/report/bilan_report.py b/openerp/addons/l10n_fr/report/bilan_report.py
+index fafacd8..b47b0c3 100644
+--- a/openerp/addons/l10n_fr/report/bilan_report.py
++++ b/openerp/addons/l10n_fr/report/bilan_report.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_fr/report/compute_resultant_report.py b/openerp/addons/l10n_fr/report/compute_resultant_report.py
+index e91f9fc..5ea6dd4 100644
+--- a/openerp/addons/l10n_fr/report/compute_resultant_report.py
++++ b/openerp/addons/l10n_fr/report/compute_resultant_report.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_fr/wizard/__init__.py b/openerp/addons/l10n_fr/wizard/__init__.py
+index 6e29471..32d4c5e 100644
+--- a/openerp/addons/l10n_fr/wizard/__init__.py
++++ b/openerp/addons/l10n_fr/wizard/__init__.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_fr/wizard/fr_report_bilan.py b/openerp/addons/l10n_fr/wizard/fr_report_bilan.py
+index cec2e51..1676ba6 100644
+--- a/openerp/addons/l10n_fr/wizard/fr_report_bilan.py
++++ b/openerp/addons/l10n_fr/wizard/fr_report_bilan.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_fr/wizard/fr_report_compute_resultant.py b/openerp/addons/l10n_fr/wizard/fr_report_compute_resultant.py
+index d90466e..bd0b29a 100644
+--- a/openerp/addons/l10n_fr/wizard/fr_report_compute_resultant.py
++++ b/openerp/addons/l10n_fr/wizard/fr_report_compute_resultant.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_in/__init__.py b/openerp/addons/l10n_in/__init__.py
+index 19de03b..c45b21d 100644
+--- a/openerp/addons/l10n_in/__init__.py
++++ b/openerp/addons/l10n_in/__init__.py
+@@ -23,7 +23,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_nl/__init__.py b/openerp/addons/l10n_nl/__init__.py
+index 57a3bb1..4235db8 100644
+--- a/openerp/addons/l10n_nl/__init__.py
++++ b/openerp/addons/l10n_nl/__init__.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/l10n_nl/__openerp__.py b/openerp/addons/l10n_nl/__openerp__.py
+index 562fd0a..561d23b 100644
+--- a/openerp/addons/l10n_nl/__openerp__.py
++++ b/openerp/addons/l10n_nl/__openerp__.py
+@@ -22,7 +22,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ #
+diff --git a/openerp/addons/l10n_pe/__init__.py b/openerp/addons/l10n_pe/__init__.py
+index 92da5db..e8fb5de 100644
+--- a/openerp/addons/l10n_pe/__init__.py
++++ b/openerp/addons/l10n_pe/__init__.py
+@@ -23,7 +23,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit/__init__.py b/openerp/addons/report_webkit/__init__.py
+index dd44517..8c7533a 100644
+--- a/openerp/addons/report_webkit/__init__.py
++++ b/openerp/addons/report_webkit/__init__.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit/__openerp__.py b/openerp/addons/report_webkit/__openerp__.py
+index f0bf0d1..ee782d0 100644
+--- a/openerp/addons/report_webkit/__openerp__.py
++++ b/openerp/addons/report_webkit/__openerp__.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit/company.py b/openerp/addons/report_webkit/company.py
+index eaf3c02..2859454 100644
+--- a/openerp/addons/report_webkit/company.py
++++ b/openerp/addons/report_webkit/company.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit/convert.py b/openerp/addons/report_webkit/convert.py
+index b4d4232..645437b 100644
+--- a/openerp/addons/report_webkit/convert.py
++++ b/openerp/addons/report_webkit/convert.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit/header.py b/openerp/addons/report_webkit/header.py
+index b052c61..5b7cc8f 100644
+--- a/openerp/addons/report_webkit/header.py
++++ b/openerp/addons/report_webkit/header.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit/ir_report.py b/openerp/addons/report_webkit/ir_report.py
+index d2709ea..0ed89f2 100644
+--- a/openerp/addons/report_webkit/ir_report.py
++++ b/openerp/addons/report_webkit/ir_report.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit/report_helper.py b/openerp/addons/report_webkit/report_helper.py
+index 12651f3..4408f00 100644
+--- a/openerp/addons/report_webkit/report_helper.py
++++ b/openerp/addons/report_webkit/report_helper.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit/webkit_report.py b/openerp/addons/report_webkit/webkit_report.py
+index 4d0bd62..4cfb858 100644
+--- a/openerp/addons/report_webkit/webkit_report.py
++++ b/openerp/addons/report_webkit/webkit_report.py
+@@ -26,7 +26,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit/wizard/__init__.py b/openerp/addons/report_webkit/wizard/__init__.py
+index e8ae8b7..649ad01 100644
+--- a/openerp/addons/report_webkit/wizard/__init__.py
++++ b/openerp/addons/report_webkit/wizard/__init__.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit/wizard/report_webkit_actions.py b/openerp/addons/report_webkit/wizard/report_webkit_actions.py
+index bb2c531..e32db38 100644
+--- a/openerp/addons/report_webkit/wizard/report_webkit_actions.py
++++ b/openerp/addons/report_webkit/wizard/report_webkit_actions.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit_sample/__init__.py b/openerp/addons/report_webkit_sample/__init__.py
+index c1d68d5..783e271 100644
+--- a/openerp/addons/report_webkit_sample/__init__.py
++++ b/openerp/addons/report_webkit_sample/__init__.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ import wizard
+diff --git a/openerp/addons/report_webkit_sample/__openerp__.py b/openerp/addons/report_webkit_sample/__openerp__.py
+index 01eed24..e0ab052 100644
+--- a/openerp/addons/report_webkit_sample/__openerp__.py
++++ b/openerp/addons/report_webkit_sample/__openerp__.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/report_webkit_sample/report/__init__.py b/openerp/addons/report_webkit_sample/report/__init__.py
+index e8703f1..eecc67f 100644
+--- a/openerp/addons/report_webkit_sample/report/__init__.py
++++ b/openerp/addons/report_webkit_sample/report/__init__.py
+@@ -25,7 +25,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ #
+ ##############################################################################
+ 
+diff --git a/openerp/addons/resource/faces/observer.py b/openerp/addons/resource/faces/observer.py
+index 8943452..de5e990 100644
+--- a/openerp/addons/resource/faces/observer.py
++++ b/openerp/addons/resource/faces/observer.py
+@@ -22,7 +22,7 @@
+ #   You should have received a copy of the GNU General Public License
+ #   along with this program; if not, write to the
+ #   Free Software Foundation, Inc.,
+-#   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++#   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ############################################################################
+ 
+ #@-node:<< Copyright >>
+diff --git a/openerp/addons/resource/faces/pcalendar.py b/openerp/addons/resource/faces/pcalendar.py
+index c82c49d..213aac7 100644
+--- a/openerp/addons/resource/faces/pcalendar.py
++++ b/openerp/addons/resource/faces/pcalendar.py
+@@ -22,7 +22,7 @@
+ #   You should have received a copy of the GNU General Public License
+ #   along with this program; if not, write to the
+ #   Free Software Foundation, Inc.,
+-#   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++#   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ############################################################################
+ 
+ #@-node:<< Copyright >>
+diff --git a/openerp/addons/resource/faces/plocale.py b/openerp/addons/resource/faces/plocale.py
+index 61bfa91..5b2d308 100644
+--- a/openerp/addons/resource/faces/plocale.py
++++ b/openerp/addons/resource/faces/plocale.py
+@@ -17,7 +17,7 @@
+ #   You should have received a copy of the GNU General Public License
+ #   along with this program; if not, write to the
+ #   Free Software Foundation, Inc.,
+-#   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++#   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ############################################################################
+ 
+ import gettext
+diff --git a/openerp/addons/resource/faces/resource.py b/openerp/addons/resource/faces/resource.py
+index 3451806..e8246fc 100644
+--- a/openerp/addons/resource/faces/resource.py
++++ b/openerp/addons/resource/faces/resource.py
+@@ -22,7 +22,7 @@
+ #   You should have received a copy of the GNU General Public License
+ #   along with this program; if not, write to the
+ #   Free Software Foundation, Inc.,
+-#   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++#   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ############################################################################
+ 
+ #@-node:<< Copyright >>
+diff --git a/openerp/addons/resource/faces/task.py b/openerp/addons/resource/faces/task.py
+index f524011..b2ad264 100644
+--- a/openerp/addons/resource/faces/task.py
++++ b/openerp/addons/resource/faces/task.py
+@@ -22,7 +22,7 @@
+ #   You should have received a copy of the GNU General Public License
+ #   along with this program; if not, write to the
+ #   Free Software Foundation, Inc.,
+-#   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++#   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ############################################################################
+ 
+ #@-node:<< Copyright >>
+diff --git a/openerp/addons/resource/faces/timescale.py b/openerp/addons/resource/faces/timescale.py
+index 3e7c3c1..50f8e09 100644
+--- a/openerp/addons/resource/faces/timescale.py
++++ b/openerp/addons/resource/faces/timescale.py
+@@ -17,7 +17,7 @@
+ #   You should have received a copy of the GNU General Public License
+ #   along with this program; if not, write to the
+ #   Free Software Foundation, Inc.,
+-#   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++#   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ############################################################################
+ 
+ import faces.pcalendar as pcal
+diff --git a/openerp/addons/resource/faces/utils.py b/openerp/addons/resource/faces/utils.py
+index 5498576..92fc88e 100644
+--- a/openerp/addons/resource/faces/utils.py
++++ b/openerp/addons/resource/faces/utils.py
+@@ -17,7 +17,7 @@
+ #   You should have received a copy of the GNU General Public License
+ #   along with this program; if not, write to the
+ #   Free Software Foundation, Inc.,
+-#   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++#   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ############################################################################
+ 
+ import observer
+diff --git a/openerp/report/render/rml2html/rml2html.py b/openerp/report/render/rml2html/rml2html.py
+index 5044927..e9bd07e 100644
+--- a/openerp/report/render/rml2html/rml2html.py
++++ b/openerp/report/render/rml2html/rml2html.py
+@@ -33,7 +33,7 @@
+ #
+ # You should have received a copy of the GNU Lesser General Public
+ # License along with this library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ 
+ import sys
+ import cStringIO
+diff --git a/openerp/report/render/rml2pdf/utils.py b/openerp/report/render/rml2pdf/utils.py
+index 273d581..0e24d35 100644
+--- a/openerp/report/render/rml2pdf/utils.py
++++ b/openerp/report/render/rml2pdf/utils.py
+@@ -34,7 +34,7 @@
+ #
+ # You should have received a copy of the GNU Lesser General Public
+ # License along with this library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ 
+ import copy
+ import locale
+diff --git a/openerp/service/http_server.py b/openerp/service/http_server.py
+index 07e31ea..bcb41a4 100644
+--- a/openerp/service/http_server.py
++++ b/openerp/service/http_server.py
+@@ -23,7 +23,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ###############################################################################
+ 
+ #.apidoc title: HTTP and XML-RPC Server
+diff --git a/openerp/service/websrv_lib.py b/openerp/service/websrv_lib.py
+index 4da6536..e693111 100644
+--- a/openerp/service/websrv_lib.py
++++ b/openerp/service/websrv_lib.py
+@@ -21,7 +21,7 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ ###############################################################################
+ 
+ #.apidoc title: HTTP Layer library (websrv_lib)
diff --git a/openerp-gen-cert b/openerp-gen-cert
new file mode 100755
index 0000000..57fe784
--- /dev/null
+++ b/openerp-gen-cert
@@ -0,0 +1,53 @@
+#!/bin/bash
+KEYFILE=/etc/openerp/server.pem
+CERT_FILE=/etc/openerp/server.cert
+
+gen_cert()
+{
+    if [ ! -f $KEYFILE ] ; then
+        if [ ! -r /dev/urandom ]; then
+            echo "Your system doesn't seem to support urandom(4)" >&2
+            echo "Cannot create server key. Sorry" >&2
+            echo "Use \"openssl genrsa\" to create $KEYFILE manually >&2"
+            exit 2
+        fi
+        umask o-rwx g-w
+        if /usr/bin/openssl genrsa -rand /dev/urandom 1024 > $KEYFILE
+        then
+            echo "Created new key $KEYFILE"
+	    chown root:openerp $KEY_FILE
+        else
+            echo "Error creating server key" >&2
+            exit 1
+        fi
+    else
+        echo "Using existing key $KEYFILE"
+    fi
+
+    umask o+r
+    /usr/bin/openssl req -new -key $KEYFILE -x509 -days 365 \
+        -set_serial $RANDOM -extensions v3_req -out $CERT_FILE
+
+    if (( $? == 0 )); then
+	echo "Created a self-signed SSL certificate for OpenERP."
+	echo "You may want to revise it or get a real one."
+	chown root:openerp $CERT_FILE
+    else
+        echo "Error creating certificate"
+        rm -f $CERT_FILE
+    fi
+}
+
+
+if [ -r $CERT_FILE ] ; then
+    echo "Certificate $CERT_FILE already in place"
+    echo "Remove before creating a new one. Goodbye"
+    exit 0
+fi
+
+if [ "$UID" != 0 ]; then
+    echo "You must be root to do this" >&2
+    exit 1
+fi
+
+gen_cert
diff --git a/openerp-server-relicense-dict_tools-to-LGPL2.1.patch b/openerp-server-relicense-dict_tools-to-LGPL2.1.patch
new file mode 100644
index 0000000..a077a08
--- /dev/null
+++ b/openerp-server-relicense-dict_tools-to-LGPL2.1.patch
@@ -0,0 +1,25 @@
+diff --git a/openerp/addons/document/dict_tools.py b/bin/addons/document/dict_tools.py
+index 10d2fdc..49fdcf8 100644
+--- a/openerp/addons/document/dict_tools.py
++++ b/openerp/addons/document/dict_tools.py
+@@ -4,7 +4,8 @@
+ #
+ # This program is Free Software; you can redistribute it and/or
+ # modify it under the terms of the GNU Lesser General Public License
+-# as published by the Free Software Foundation; version 2 of the License.
++# as published by the Free Software Foundation; version 2.1 of the
++# License or any later version.
+ #
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+@@ -12,8 +13,8 @@
+ # GNU Lesser General Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License
+-# along with this program; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++# along with this program; if not, write to the Free Software Foundation
++# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ ###############################################################################
+ 
+ 
diff --git a/openerp-unbundle-pyftpdlib.patch b/openerp-unbundle-pyftpdlib.patch
new file mode 100644
index 0000000..7388340
--- /dev/null
+++ b/openerp-unbundle-pyftpdlib.patch
@@ -0,0 +1,3075 @@
+diff --git a/openerp/addons/document_ftp/ftpserver/ftpserver.py b/openerp/addons/document_ftp/ftpserver/ftpserver.py
+deleted file mode 100755
+index bd72742..0000000
+--- a/openerp/addons/document_ftp/ftpserver/ftpserver.py
++++ /dev/null
+@@ -1,3047 +0,0 @@
+-#!/usr/bin/env python
+-# -*- encoding: utf-8 -*-
+-# ftpserver.py
+-#
+-#  pyftpdlib is released under the MIT license, reproduced below:
+-#  ======================================================================
+-#  Copyright (C) 2007 Giampaolo Rodola' <g.rodola at gmail.com>
+-#  Hacked by Fabien Pinckaers (C) 2008 <fp at tinyerp.com>
+-#
+-#                         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
+-#  Giampaolo Rodola' not be used in advertising or publicity pertaining to
+-#  distribution of the software without specific, written prior
+-#  permission.
+-#
+-#  Giampaolo Rodola' DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+-#  INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+-#  NO EVENT Giampaolo Rodola' 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.
+-#  ======================================================================
+-
+-
+-"""pyftpdlib: RFC-959 asynchronous FTP server.
+-
+-pyftpdlib implements a fully functioning asynchronous FTP server as
+-defined in RFC-959.  A hierarchy of classes outlined below implement
+-the backend functionality for the FTPd:
+-
+-    [FTPServer] - the base class for the backend.
+-
+-    [FTPHandler] - a class representing the server-protocol-interpreter
+-    (server-PI, see RFC-959). Each time a new connection occurs
+-    FTPServer will create a new FTPHandler instance to handle the
+-    current PI session.
+-
+-    [ActiveDTP], [PassiveDTP] - base classes for active/passive-DTP
+-    backends.
+-
+-    [DTPHandler] - this class handles processing of data transfer
+-    operations (server-DTP, see RFC-959).
+-
+-    [DummyAuthorizer] - an "authorizer" is a class handling FTPd
+-    authentications and permissions. It is used inside FTPHandler class
+-    to verify user passwords, to get user's home directory and to get
+-    permissions when a filesystem read/write occurs. "DummyAuthorizer"
+-    is the base authorizer class providing a platform independent
+-    interface for managing virtual users.
+-
+-    [AbstractedFS] - class used to interact with the file system,
+-    providing a high level, cross-platform interface compatible
+-    with both Windows and UNIX style filesystems.
+-
+-    [AuthorizerError] - base class for authorizers exceptions.
+-
+-
+-pyftpdlib also provides 3 different logging streams through 3 functions
+-which can be overridden to allow for custom logging.
+-
+-    [log] - the main logger that logs the most important messages for
+-    the end user regarding the FTPd.
+-
+-    [logline] - this function is used to log commands and responses
+-    passing through the control FTP channel.
+-
+-    [logerror] - log traceback outputs occurring in case of errors.
+-
+-
+-Usage example:
+-
+->>> from pyftpdlib import ftpserver
+->>> authorizer = ftpserver.DummyAuthorizer()
+->>> authorizer.add_user('user', 'password', '/home/user', perm='elradfmw')
+->>> authorizer.add_anonymous('/home/nobody')
+->>> ftp_handler = ftpserver.FTPHandler
+->>> ftp_handler.authorizer = authorizer
+->>> address = ("127.0.0.1", 21)
+->>> ftpd = ftpserver.FTPServer(address, ftp_handler)
+->>> ftpd.serve_forever()
+-Serving FTP on 127.0.0.1:21
+-[]127.0.0.1:2503 connected.
+-127.0.0.1:2503 ==> 220 Ready.
+-127.0.0.1:2503 <== USER anonymous
+-127.0.0.1:2503 ==> 331 Username ok, send password.
+-127.0.0.1:2503 <== PASS ******
+-127.0.0.1:2503 ==> 230 Login successful.
+-[anonymous]@127.0.0.1:2503 User anonymous logged in.
+-127.0.0.1:2503 <== TYPE A
+-127.0.0.1:2503 ==> 200 Type set to: ASCII.
+-127.0.0.1:2503 <== PASV
+-127.0.0.1:2503 ==> 227 Entering passive mode (127,0,0,1,9,201).
+-127.0.0.1:2503 <== LIST
+-127.0.0.1:2503 ==> 150 File status okay. About to open data connection.
+-[anonymous]@127.0.0.1:2503 OK LIST "/". Transfer starting.
+-127.0.0.1:2503 ==> 226 Transfer complete.
+-[anonymous]@127.0.0.1:2503 Transfer complete. 706 bytes transmitted.
+-127.0.0.1:2503 <== QUIT
+-127.0.0.1:2503 ==> 221 Goodbye.
+-[anonymous]@127.0.0.1:2503 Disconnected.
+-"""
+-
+-
+-import asyncore
+-import asynchat
+-import socket
+-import os
+-import sys
+-import traceback
+-import errno
+-import time
+-import glob
+-import fnmatch
+-import tempfile
+-import warnings
+-import random
+-import stat
+-from collections import deque
+-from tarfile import filemode
+-
+-LOG_ACTIVE = True
+-
+-__all__ = ['proto_cmds', 'Error', 'log', 'logline', 'logerror', 'DummyAuthorizer',
+-           'FTPHandler', 'FTPServer', 'PassiveDTP', 'ActiveDTP', 'DTPHandler',
+-           'FileProducer', 'IteratorProducer', 'BufferedIteratorProducer',
+-           'AbstractedFS',]
+-
+-
+-__pname__   = 'Python FTP server library (pyftpdlib)'
+-__ver__     = '0.4.0'
+-__date__    = '2008-05-16'
+-__author__  = "Giampaolo Rodola' <g.rodola at gmail.com>"
+-__web__     = 'http://code.google.com/p/pyftpdlib/'
+-
+-
+-proto_cmds = {
+-    'ABOR': 'Syntax: ABOR (abort transfer).',
+-    'ALLO': 'Syntax: ALLO <SP> bytes (obsolete; allocate storage).',
+-    'APPE': 'Syntax: APPE <SP> file-name (append data to an existent file).',
+-    'CDUP': 'Syntax: CDUP (go to parent directory).',
+-    'CWD' : 'Syntax: CWD <SP> dir-name (change current working directory).',
+-    'DELE': 'Syntax: DELE <SP> file-name (delete file).',
+-    'EPRT': 'Syntax: EPRT <SP> |proto|ip|port| (set server in extended active mode).',
+-    'EPSV': 'Syntax: EPSV [<SP> proto/"ALL"] (set server in extended passive mode).',
+-    'FEAT': 'Syntax: FEAT (list all new features supported).',
+-    'HELP': 'Syntax: HELP [<SP> cmd] (show help).',
+-    'LIST': 'Syntax: LIST [<SP> path-name] (list files).',
+-    'MDTM': 'Syntax: MDTM <SP> file-name (get last modification time).',
+-    'MLSD': 'Syntax: MLSD [<SP> dir-name] (list files in a machine-processable form)',
+-    'MLST': 'Syntax: MLST [<SP> path-name] (show a path in a machine-processable form)',
+-    'MODE': 'Syntax: MODE <SP> mode (obsolete; set data transfer mode).',
+-    'MKD' : 'Syntax: MDK <SP> dir-name (create directory).',
+-    'NLST': 'Syntax: NLST [<SP> path-name] (list files in a compact form).',
+-    'NOOP': 'Syntax: NOOP (just do nothing).',
+-    'OPTS': 'Syntax: OPTS <SP> ftp-command [<SP> option] (specify options for FTP commands)',
+-    'PASS': 'Syntax: PASS <SP> user-name (set user password).',
+-    'PASV': 'Syntax: PASV (set server in passive mode).',
+-    'PORT': 'Syntax: PORT <sp> h1,h2,h3,h4,p1,p2 (set server in active mode).',
+-    'PWD' : 'Syntax: PWD (get current working directory).',
+-    'QUIT': 'Syntax: QUIT (quit current session).',
+-    'REIN': 'Syntax: REIN (reinitialize / flush account).',
+-    'REST': 'Syntax: REST <SP> marker (restart file position).',
+-    'RETR': 'Syntax: RETR <SP> file-name (retrieve a file).',
+-    'RMD' : 'Syntax: RMD <SP> dir-name (remove directory).',
+-    'RNFR': 'Syntax: RNFR <SP> file-name (file renaming (source name)).',
+-    'RNTO': 'Syntax: RNTO <SP> file-name (file renaming (destination name)).',
+-    'SIZE': 'Syntax: HELP <SP> file-name (get file size).',
+-    'STAT': 'Syntax: STAT [<SP> path name] (status information [list files]).',
+-    'STOR': 'Syntax: STOR <SP> file-name (store a file).',
+-    'STOU': 'Syntax: STOU [<SP> file-name] (store a file with a unique name).',
+-    'STRU': 'Syntax: STRU <SP> type (obsolete; set file structure).',
+-    'SYST': 'Syntax: SYST (get operating system type).',
+-    'TYPE': 'Syntax: TYPE <SP> [A | I] (set transfer type).',
+-    'USER': 'Syntax: USER <SP> user-name (set username).',
+-    'XCUP': 'Syntax: XCUP (obsolete; go to parent directory).',
+-    'XCWD': 'Syntax: XCWD <SP> dir-name (obsolete; change current directory).',
+-    'XMKD': 'Syntax: XMDK <SP> dir-name (obsolete; create directory).',
+-    'XPWD': 'Syntax: XPWD (obsolete; get current dir).',
+-    'XRMD': 'Syntax: XRMD <SP> dir-name (obsolete; remove directory).',
+-    }
+-
+-
+-def _strerror(err):
+-    """A wrap around os.strerror() which may be not available on all
+-    platforms (e.g. pythonCE).
+-
+-     - (instance) err: an EnvironmentError or derived class instance.
+-    """
+-    if hasattr(os, 'strerror'):
+-        return os.strerror(err.errno)
+-    else:
+-        return err.strerror
+-
+-def _to_unicode(s):
+-    try:
+-        return s.decode('utf-8')
+-    except UnicodeError:
+-        pass
+-    try:
+-        return s.decode('latin')
+-    except UnicodeError:
+-        pass
+-    try:
+-        return s.encode('ascii')
+-    except UnicodeError:
+-        return s
+-
+-def _to_decode(s):
+-    try:
+-        return s.encode('utf-8')
+-    except UnicodeError:
+-        pass
+-    try:
+-        return s.encode('latin')
+-    except UnicodeError:
+-        pass
+-    try:
+-        return s.decode('ascii')
+-    except UnicodeError:
+-        return s
+-
+-# --- library defined exceptions
+-
+-class Error(Exception):
+-    """Base class for module exceptions."""
+-
+-class AuthorizerError(Error):
+-    """Base class for authorizer exceptions."""
+-
+-
+-# --- loggers
+-
+-def log(msg):
+-    """Log messages intended for the end user."""
+-    if LOG_ACTIVE:
+-        print msg
+-
+-def logline(msg):
+-    """Log commands and responses passing through the command channel."""
+-    if LOG_ACTIVE:
+-        print msg
+-
+-def logerror(msg):
+-    """Log traceback outputs occurring in case of errors."""
+-    sys.stderr.write(str(msg) + '\n')
+-    sys.stderr.flush()
+-
+-
+-# --- authorizers
+-
+-class DummyAuthorizer:
+-    """Basic "dummy" authorizer class, suitable for subclassing to
+-    create your own custom authorizers.
+-
+-    An "authorizer" is a class handling authentications and permissions
+-    of the FTP server.  It is used inside FTPHandler class for verifying
+-    user's password, getting users home directory, checking user
+-    permissions when a file read/write event occurs and changing user
+-    before accessing the filesystem.
+-
+-    DummyAuthorizer is the base authorizer, providing a platform
+-    independent interface for managing "virtual" FTP users. System
+-    dependent authorizers can by written by subclassing this base
+-    class and overriding appropriate methods as necessary.
+-    """
+-
+-    read_perms = "elr"
+-    write_perms = "adfmw"
+-
+-    def __init__(self):
+-        self.user_table = {}
+-
+-    def add_user(self, username, password, homedir, perm='elr',
+-                    msg_login="Login successful.", msg_quit="Goodbye."):
+-        """Add a user to the virtual users table.
+-
+-        AuthorizerError exceptions raised on error conditions such as
+-        invalid permissions, missing home directory or duplicate usernames.
+-
+-        Optional perm argument is a string referencing the user's
+-        permissions explained below:
+-
+-        Read permissions:
+-         - "e" = change directory (CWD command)
+-         - "l" = list files (LIST, NLST, MLSD commands)
+-         - "r" = retrieve file from the server (RETR command)
+-
+-        Write permissions:
+-         - "a" = append data to an existing file (APPE command)
+-         - "d" = delete file or directory (DELE, RMD commands)
+-         - "f" = rename file or directory (RNFR, RNTO commands)
+-         - "m" = create directory (MKD command)
+-         - "w" = store a file to the server (STOR, STOU commands)
+-
+-        Optional msg_login and msg_quit arguments can be specified to
+-        provide customized response strings when user log-in and quit.
+-        """
+-        if self.has_user(username):
+-            raise AuthorizerError('User "%s" already exists' %username)
+-        homedir = os.path.realpath(homedir)
+-        if not os.path.isdir(homedir):
+-            raise AuthorizerError('No such directory: "%s"' %homedir)
+-        for p in perm:
+-            if p not in 'elradfmw':
+-                raise AuthorizerError('No such permission "%s"' %p)
+-        for p in perm:
+-            if (p in self.write_perms) and (username == 'anonymous'):
+-                warnings.warn("write permissions assigned to anonymous user.",
+-                              RuntimeWarning)
+-                break
+-        dic = {'pwd': str(password),
+-               'home': homedir,
+-               'perm': perm,
+-               'msg_login': str(msg_login),
+-               'msg_quit': str(msg_quit)
+-               }
+-        self.user_table[username] = dic
+-
+-    def add_anonymous(self, homedir, **kwargs):
+-        """Add an anonymous user to the virtual users table.
+-
+-        AuthorizerError exception raised on error conditions such as
+-        invalid permissions, missing home directory, or duplicate
+-        anonymous users.
+-
+-        The keyword arguments in kwargs are the same expected by
+-        add_user method: "perm", "msg_login" and "msg_quit".
+-
+-        The optional "perm" keyword argument is a string defaulting to
+-        "elr" referencing "read-only" anonymous user's permissions.
+-
+-        Using write permission values ("adfmw") results in a
+-        RuntimeWarning.
+-        """
+-        DummyAuthorizer.add_user(self, 'anonymous', '', homedir, **kwargs)
+-
+-    def remove_user(self, username):
+-        """Remove a user from the virtual users table."""
+-        del self.user_table[username]
+-
+-    def validate_authentication(self, username, password):
+-        """Return True if the supplied username and password match the
+-        stored credentials."""
+-        return self.user_table[username]['pwd'] == password
+-
+-    def impersonate_user(self, username, password):
+-        """Impersonate another user (noop).
+-
+-        It is always called before accessing the filesystem.
+-        By default it does nothing.  The subclass overriding this
+-        method is expected to provide a mechanism to change the
+-        current user.
+-        """
+-
+-    def terminate_impersonation(self):
+-        """Terminate impersonation (noop).
+-
+-        It is always called after having accessed the filesystem.
+-        By default it does nothing.  The subclass overriding this
+-        method is expected to provide a mechanism to switch back
+-        to the original user.
+-        """
+-
+-    def has_user(self, username):
+-        """Whether the username exists in the virtual users table."""
+-        return username in self.user_table
+-
+-    def has_perm(self, username, perm, path=None):
+-        """Whether the user has permission over path (an absolute
+-        pathname of a file or a directory).
+-
+-        Expected perm argument is one of the following letters:
+-        "elradfmw".
+-        """
+-        return perm in self.user_table[username]['perm']
+-
+-    def get_perms(self, username):
+-        """Return current user permissions."""
+-        return self.user_table[username]['perm']
+-
+-    def get_home_dir(self, username):
+-        """Return the user's home directory."""
+-        return self.user_table[username]['home']
+-
+-    def get_msg_login(self, username):
+-        """Return the user's login message."""
+-        return self.user_table[username]['msg_login']
+-
+-    def get_msg_quit(self, username):
+-        """Return the user's quitting message."""
+-        return self.user_table[username]['msg_quit']
+-
+-
+-# --- DTP classes
+-
+-class PassiveDTP(asyncore.dispatcher):
+-    """This class is an asyncore.disptacher subclass.  It creates a
+-    socket listening on a local port, dispatching the resultant
+-    connection to DTPHandler.
+-    """
+-
+-    def __init__(self, cmd_channel, extmode=False):
+-        """Initialize the passive data server.
+-
+-         - (instance) cmd_channel: the command channel class instance.
+-         - (bool) extmode: wheter use extended passive mode response type.
+-        """
+-        asyncore.dispatcher.__init__(self)
+-        self.cmd_channel = cmd_channel
+-
+-        ip = self.cmd_channel.getsockname()[0]
+-        self.create_socket(self.cmd_channel.af, socket.SOCK_STREAM)
+-
+-        if not self.cmd_channel.passive_ports:
+-        # By using 0 as port number value we let kernel choose a free
+-        # unprivileged random port.
+-            self.bind((ip, 0))
+-        else:
+-            ports = list(self.cmd_channel.passive_ports)
+-            while ports:
+-                port = ports.pop(random.randint(0, len(ports) -1))
+-                try:
+-                    self.bind((ip, port))
+-                except socket.error, why:
+-                    if why[0] == errno.EADDRINUSE:  # port already in use
+-                        if ports:
+-                            continue
+-                        # If cannot use one of the ports in the configured
+-                        # range we'll use a kernel-assigned port, and log
+-                        # a message reporting the issue.
+-                        # By using 0 as port number value we let kernel
+-                        # choose a free unprivileged random port.
+-                        else:
+-                            self.bind((ip, 0))
+-                            self.cmd_channel.log(
+-                                "Can't find a valid passive port in the "
+-                                "configured range. A random kernel-assigned "
+-                                "port will be used."
+-                                )
+-                    else:
+-                        raise
+-                else:
+-                    break
+-        self.listen(5)
+-        port = self.socket.getsockname()[1]
+-        if not extmode:
+-            if self.cmd_channel.masquerade_address:
+-                ip = self.cmd_channel.masquerade_address
+-            # The format of 227 response in not standardized.
+-            # This is the most expected:
+-            self.cmd_channel.respond('227 Entering passive mode (%s,%d,%d).' %(
+-                    ip.replace('.', ','), port / 256, port % 256))
+-        else:
+-            self.cmd_channel.respond('229 Entering extended passive mode '
+-                                     '(|||%d|).' %port)
+-
+-    # --- connection / overridden
+-
+-    def handle_accept(self):
+-        """Called when remote client initiates a connection."""
+-        sock, addr = self.accept()
+-
+-        # Check the origin of data connection.  If not expressively
+-        # configured we drop the incoming data connection if remote
+-        # IP address does not match the client's IP address.
+-        if (self.cmd_channel.remote_ip != addr[0]):
+-            if not self.cmd_channel.permit_foreign_addresses:
+-                try:
+-                    sock.close()
+-                except socket.error:
+-                    pass
+-                msg = 'Rejected data connection from foreign address %s:%s.' \
+-                        %(addr[0], addr[1])
+-                self.cmd_channel.respond("425 %s" %msg)
+-                self.cmd_channel.log(msg)
+-                # do not close listening socket: it couldn't be client's blame
+-                return
+-            else:
+-                # site-to-site FTP allowed
+-                msg = 'Established data connection with foreign address %s:%s.'\
+-                        %(addr[0], addr[1])
+-                self.cmd_channel.log(msg)
+-        # Immediately close the current channel (we accept only one
+-        # connection at time) and avoid running out of max connections
+-        # limit.
+-        self.close()
+-        # delegate such connection to DTP handler
+-        handler = self.cmd_channel.dtp_handler(sock, self.cmd_channel)
+-        self.cmd_channel.data_channel = handler
+-        self.cmd_channel.on_dtp_connection()
+-
+-    def writable(self):
+-        return 0
+-
+-    def handle_error(self):
+-        """Called to handle any uncaught exceptions."""
+-        try:
+-            raise
+-        except (KeyboardInterrupt, SystemExit, asyncore.ExitNow):
+-            raise
+-        logerror(traceback.format_exc())
+-        self.close()
+-
+-    def handle_close(self):
+-        """Called on closing the data connection."""
+-        self.close()
+-
+-
+-class ActiveDTP(asyncore.dispatcher):
+-    """This class is an asyncore.disptacher subclass. It creates a
+-    socket resulting from the connection to a remote user-port,
+-    dispatching it to DTPHandler.
+-    """
+-
+-    def __init__(self, ip, port, cmd_channel):
+-        """Initialize the active data channel attemping to connect
+-        to remote data socket.
+-
+-         - (str) ip: the remote IP address.
+-         - (int) port: the remote port.
+-         - (instance) cmd_channel: the command channel class instance.
+-        """
+-        asyncore.dispatcher.__init__(self)
+-        self.cmd_channel = cmd_channel
+-        self.create_socket(self.cmd_channel.af, socket.SOCK_STREAM)
+-        try:
+-            self.connect((ip, port))
+-        except socket.gaierror:
+-            self.cmd_channel.respond("425 Can't connect to specified address.")
+-            self.close()
+-
+-    # --- connection / overridden
+-
+-    def handle_write(self):
+-        """NOOP, must be overridden to prevent unhandled write event."""
+-
+-    def handle_connect(self):
+-        """Called when connection is established."""
+-        self.cmd_channel.respond('200 Active data connection established.')
+-        # delegate such connection to DTP handler
+-        handler = self.cmd_channel.dtp_handler(self.socket, self.cmd_channel)
+-        self.cmd_channel.data_channel = handler
+-        self.cmd_channel.on_dtp_connection()
+-
+-    def handle_expt(self):
+-        self.cmd_channel.respond("425 Can't connect to specified address.")
+-        self.close()
+-
+-    def handle_error(self):
+-        """Called to handle any uncaught exceptions."""
+-        try:
+-            raise
+-        except (KeyboardInterrupt, SystemExit, asyncore.ExitNow):
+-            raise
+-        except socket.error:
+-            pass
+-        except:
+-            logerror(traceback.format_exc())
+-        self.cmd_channel.respond("425 Can't connect to specified address.")
+-        self.close()
+-
+-class DTPHandler(asyncore.dispatcher):
+-    """Class handling server-data-transfer-process (server-DTP, see
+-    RFC-959) managing data-transfer operations involving sending
+-    and receiving data.
+-
+-    Instance attributes defined in this class, initialized when
+-    channel is opened:
+-
+-     - (instance) cmd_channel: the command channel class instance.
+-     - (file) file_obj: the file transferred (if any).
+-     - (bool) receive: True if channel is used for receiving data.
+-     - (bool) transfer_finished: True if transfer completed successfully.
+-     - (int) tot_bytes_sent: the total bytes sent.
+-     - (int) tot_bytes_received: the total bytes received.
+-
+-    DTPHandler implementation note:
+-
+-    When a producer is consumed and close_when_done() has been called
+-    previously, refill_buffer() erroneously calls close() instead of
+-    handle_close() - (see: http://bugs.python.org/issue1740572)
+-
+-    To avoid this problem DTPHandler is implemented as a subclass of
+-    asyncore.dispatcher instead of asynchat.async_chat.
+-    This implementation follows the same approach that asynchat module
+-    should use in Python 2.6.
+-
+-    The most important change in the implementation is related to
+-    producer_fifo, which is a pure deque object instead of a
+-    producer_fifo instance.
+-
+-    Since we don't want to break backward compatibily with older python
+-    versions (deque has been introduced in Python 2.4), if deque is not
+-    available we use a list instead.
+-    """
+-
+-    ac_in_buffer_size = 8192
+-    ac_out_buffer_size  = 8192
+-
+-    def __init__(self, sock_obj, cmd_channel):
+-        """Initialize the command channel.
+-
+-         - (instance) sock_obj: the socket object instance of the newly
+-            established connection.
+-         - (instance) cmd_channel: the command channel class instance.
+-        """
+-        asyncore.dispatcher.__init__(self, sock_obj)
+-        # we toss the use of the asynchat's "simple producer" and
+-        # replace it  with a pure deque, which the original fifo
+-        # was a wrapping of
+-        self.producer_fifo = deque()
+-
+-        self.cmd_channel = cmd_channel
+-        self.file_obj = None
+-        self.receive = False
+-        self.transfer_finished = False
+-        self.tot_bytes_sent = 0
+-        self.tot_bytes_received = 0
+-        self.data_wrapper = lambda x: x
+-
+-    # --- utility methods
+-
+-    def enable_receiving(self, type):
+-        """Enable receiving of data over the channel. Depending on the
+-        TYPE currently in use it creates an appropriate wrapper for the
+-        incoming data.
+-
+-         - (str) type: current transfer type, 'a' (ASCII) or 'i' (binary).
+-        """
+-        if type == 'a':
+-            self.data_wrapper = lambda x: x.replace('\r\n', os.linesep)
+-        elif type == 'i':
+-            self.data_wrapper = lambda x: x
+-        else:
+-            raise TypeError, "Unsupported type"
+-        self.receive = True
+-
+-    def get_transmitted_bytes(self):
+-        "Return the number of transmitted bytes."
+-        return self.tot_bytes_sent + self.tot_bytes_received
+-
+-    def transfer_in_progress(self):
+-        "Return True if a transfer is in progress, else False."
+-        return self.get_transmitted_bytes() != 0
+-
+-    # --- connection
+-
+-    def handle_read(self):
+-        """Called when there is data waiting to be read."""
+-        try:
+-            chunk = self.recv(self.ac_in_buffer_size)
+-        except socket.error:
+-            self.handle_error()
+-        else:
+-            self.tot_bytes_received += len(chunk)
+-            if not chunk:
+-                self.transfer_finished = True
+-                #self.close()  # <-- asyncore.recv() already do that...
+-                return
+-            # while we're writing on the file an exception could occur
+-            # in case  that filesystem gets full;  if this happens we
+-            # let handle_error() method handle this exception, providing
+-            # a detailed error message.
+-            self.file_obj.write(self.data_wrapper(chunk))
+-
+-    def handle_write(self):
+-        """Called when data is ready to be written, initiates send."""
+-        self.initiate_send()
+-
+-    def push(self, data):
+-        """Push data onto the deque and initiate send."""
+-        sabs = self.ac_out_buffer_size
+-        if len(data) > sabs:
+-            for i in xrange(0, len(data), sabs):
+-                self.producer_fifo.append(data[i:i+sabs])
+-        else:
+-            self.producer_fifo.append(data)
+-        self.initiate_send()
+-
+-    def push_with_producer(self, producer):
+-        """Push data using a producer and initiate send."""
+-        self.producer_fifo.append(producer)
+-        self.initiate_send()
+-
+-    def readable(self):
+-        """Predicate for inclusion in the readable for select()."""
+-        return self.receive
+-
+-    def writable(self):
+-        """Predicate for inclusion in the writable for select()."""
+-        return self.producer_fifo or (not self.connected)
+-
+-    def close_when_done(self):
+-        """Automatically close this channel once the outgoing queue is empty."""
+-        self.producer_fifo.append(None)
+-
+-    def initiate_send(self):
+-        """Attempt to send data in fifo order."""
+-        while self.producer_fifo and self.connected:
+-            first = self.producer_fifo[0]
+-            # handle empty string/buffer or None entry
+-            if not first:
+-                del self.producer_fifo[0]
+-                if first is None:
+-                    self.transfer_finished = True
+-                    self.handle_close()
+-                    return
+-
+-            # handle classic producer behavior
+-            obs = self.ac_out_buffer_size
+-            try:
+-                data = buffer(first, 0, obs)
+-            except TypeError:
+-                data = first.more()
+-                if data:
+-                    self.producer_fifo.appendleft(data)
+-                else:
+-                    del self.producer_fifo[0]
+-                continue
+-
+-            # send the data
+-            try:
+-                num_sent = self.send(data)
+-            except socket.error:
+-                self.handle_error()
+-                return
+-
+-            if num_sent:
+-                self.tot_bytes_sent += num_sent
+-                if num_sent < len(data) or obs < len(first):
+-                    self.producer_fifo[0] = first[num_sent:]
+-                else:
+-                    del self.producer_fifo[0]
+-            # we tried to send some actual data
+-            return
+-
+-    def handle_expt(self):
+-        """Called on "exceptional" data events."""
+-        self.cmd_channel.respond("426 Connection error; transfer aborted.")
+-        self.close()
+-
+-    def handle_error(self):
+-        """Called when an exception is raised and not otherwise handled."""
+-        try:
+-            raise
+-        except (KeyboardInterrupt, SystemExit, asyncore.ExitNow):
+-            raise
+-        except socket.error, err:
+-            # fix around asyncore bug (http://bugs.python.org/issue1736101)
+-            if err[0] in (errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN, \
+-                          errno.ECONNABORTED):
+-                self.handle_close()
+-                return
+-            else:
+-                error = str(err[1])
+-        # an error could occur in case we fail reading / writing
+-        # from / to file (e.g. file system gets full)
+-        except EnvironmentError, err:
+-            error = _strerror(err)
+-        except:
+-            # some other exception occurred;  we don't want to provide
+-            # confidential error messages
+-            logerror(traceback.format_exc())
+-            error = "Internal error"
+-        self.cmd_channel.respond("426 %s; transfer aborted." %error)
+-        self.close()
+-
+-    def handle_close(self):
+-        """Called when the socket is closed."""
+-        # If we used channel for receiving we assume that transfer is
+-        # finished when client close connection , if we used channel
+-        # for sending we have to check that all data has been sent
+-        # (responding with 226) or not (responding with 426).
+-        if self.receive:
+-            self.transfer_finished = True
+-            action = 'received'
+-        else:
+-            action = 'sent'
+-        if self.transfer_finished:
+-            self.cmd_channel.respond("226 Transfer complete.")
+-            if self.file_obj:
+-                fname = self.file_obj.name
+-                self.cmd_channel.log('"%s" %s.' %(fname, action))
+-        else:
+-            tot_bytes = self.get_transmitted_bytes()
+-            msg = "Transfer aborted; %d bytes transmitted." %tot_bytes
+-            self.cmd_channel.respond("426 " + msg)
+-            self.cmd_channel.log(msg)
+-        self.close()
+-
+-    def close(self):
+-        """Close the data channel, first attempting to close any remaining
+-        file handles."""
+-        if self.file_obj and not self.file_obj.closed:
+-            self.file_obj.close()
+-        asyncore.dispatcher.close(self)
+-        self.cmd_channel.on_dtp_close()
+-
+-
+-# --- producers
+-
+-class FileProducer:
+-    """Producer wrapper for file[-like] objects."""
+-
+-    buffer_size = 65536
+-
+-    def __init__(self, file, type):
+-        """Initialize the producer with a data_wrapper appropriate to TYPE.
+-
+-         - (file) file: the file[-like] object.
+-         - (str) type: the current TYPE, 'a' (ASCII) or 'i' (binary).
+-        """
+-        self.done = False
+-        self.file = file
+-        if type == 'a':
+-            self.data_wrapper = lambda x: x.replace(os.linesep, '\r\n')
+-        elif type == 'i':
+-            self.data_wrapper = lambda x: x
+-        else:
+-            raise TypeError, "Unsupported type"
+-
+-    def more(self):
+-        """Attempt a chunk of data of size self.buffer_size."""
+-        if self.done:
+-            return ''
+-        data = self.data_wrapper(self.file.read(self.buffer_size))
+-        if not data:
+-            self.done = True
+-            if not self.file.closed:
+-                self.file.close()
+-        return data
+-
+-
+-class IteratorProducer:
+-    """Producer for iterator objects."""
+-
+-    def __init__(self, iterator):
+-        self.iterator = iterator
+-
+-    def more(self):
+-        """Attempt a chunk of data from iterator by calling its next()
+-        method.
+-        """
+-        try:
+-            return self.iterator.next()
+-        except StopIteration:
+-            return ''
+-
+-
+-class BufferedIteratorProducer:
+-    """Producer for iterator objects with buffer capabilities."""
+-    # how many times iterator.next() will be called before
+-    # returning some data
+-    loops = 20
+-
+-    def __init__(self, iterator):
+-        self.iterator = iterator
+-
+-    def more(self):
+-        """Attempt a chunk of data from iterator by calling
+-        its next() method different times.
+-        """
+-        buffer = []
+-        for x in xrange(self.loops):
+-            try:
+-                buffer.append(self.iterator.next())
+-            except StopIteration:
+-                break
+-        return ''.join(buffer)
+-
+-
+-# --- filesystem
+-
+-class AbstractedFS:
+-    """A class used to interact with the file system, providing a high
+-    level, cross-platform interface compatible with both Windows and
+-    UNIX style filesystems.
+-
+-    It provides some utility methods and some wraps around operations
+-    involved in file creation and file system operations like moving
+-    files or removing directories.
+-
+-    Instance attributes:
+-     - (str) root: the user home directory.
+-     - (str) cwd: the current working directory.
+-     - (str) rnfr: source file to be renamed.
+-    """
+-
+-    def __init__(self):
+-        self.root = None
+-        self.cwd = '/'
+-        self.rnfr = None
+-
+-    # --- Pathname / conversion utilities
+-
+-    def ftpnorm(self, ftppath):
+-        """Normalize a "virtual" ftp pathname (tipically the raw string
+-        coming from client) depending on the current working directory.
+-
+-        Example (having "/foo" as current working directory):
+-        'x' -> '/foo/x'
+-
+-        Note: directory separators are system independent ("/").
+-        Pathname returned is always absolutized.
+-        """
+-        if os.path.isabs(ftppath):
+-            p = os.path.normpath(ftppath)
+-        else:
+-            p = os.path.normpath(os.path.join(self.cwd, ftppath))
+-        # normalize string in a standard web-path notation having '/'
+-        # as separator.
+-        p = p.replace("\\", "/")
+-        # os.path.normpath supports UNC paths (e.g. "//a/b/c") but we
+-        # don't need them.  In case we get an UNC path we collapse
+-        # redundant separators appearing at the beginning of the string
+-        while p[:2] == '//':
+-            p = p[1:]
+-        # Anti path traversal: don't trust user input, in the event
+-        # that self.cwd is not absolute, return "/" as a safety measure.
+-        # This is for extra protection, maybe not really necessary.
+-        if not os.path.isabs(p):
+-            p = "/"
+-        return p
+-
+-    def ftp2fs(self, ftppath):
+-        """Translate a "virtual" ftp pathname (tipically the raw string
+-        coming from client) into equivalent absolute "real" filesystem
+-        pathname.
+-
+-        Example (having "/home/user" as root directory):
+-        'x' -> '/home/user/x'
+-
+-        Note: directory separators are system dependent.
+-        """
+-        # as far as I know, it should always be path traversal safe...
+-        if os.path.normpath(self.root) == os.sep:
+-            return os.path.normpath(self.ftpnorm(ftppath))
+-        else:
+-            p = self.ftpnorm(ftppath)[1:]
+-            return os.path.normpath(os.path.join(self.root, p))
+-
+-    def fs2ftp(self, fspath):
+-        """Translate a "real" filesystem pathname into equivalent
+-        absolute "virtual" ftp pathname depending on the user's
+-        root directory.
+-
+-        Example (having "/home/user" as root directory):
+-        '/home/user/x' -> '/x'
+-
+-        As for ftpnorm, directory separators are system independent
+-        ("/") and pathname returned is always absolutized.
+-
+-        On invalid pathnames escaping from user's root directory
+-        (e.g. "/home" when root is "/home/user") always return "/".
+-        """
+-        if os.path.isabs(fspath):
+-            p = os.path.normpath(fspath)
+-        else:
+-            p = os.path.normpath(os.path.join(self.root, fspath))
+-        if not self.validpath(p):
+-            return '/'
+-        p = p.replace(os.sep, "/")
+-        p = p[len(self.root):]
+-        if not p.startswith('/'):
+-            p = '/' + p
+-        return p
+-
+-    # alias for backward compatibility with 0.2.0
+-    normalize = ftpnorm
+-    translate = ftp2fs
+-
+-    def validpath(self, path):
+-        """Check whether the path belongs to user's home directory.
+-        Expected argument is a "real" filesystem pathname.
+-
+-        If path is a symbolic link it is resolved to check its real
+-        destination.
+-
+-        Pathnames escaping from user's root directory are considered
+-        not valid.
+-        """
+-        root = self.realpath(self.root)
+-        path = self.realpath(path)
+-        if not self.root.endswith(os.sep):
+-            root = self.root + os.sep
+-        if not path.endswith(os.sep):
+-            path = path + os.sep
+-        if path[0:len(root)] == root:
+-            return True
+-        return False
+-
+-    # --- Wrapper methods around open() and tempfile.mkstemp
+-
+-    def open(self, filename, mode):
+-        """Open a file returning its handler."""
+-        return open(filename, mode)
+-
+-    def mkstemp(self, suffix='', prefix='', dir=None, mode='wb'):
+-        """A wrap around tempfile.mkstemp creating a file with a unique
+-        name.  Unlike mkstemp it returns an object with a file-like
+-        interface.
+-        """
+-        class FileWrapper:
+-            def __init__(self, fd, name):
+-                self.file = fd
+-                self.name = name
+-            def __getattr__(self, attr):
+-                return getattr(self.file, attr)
+-
+-        text = not 'b' in mode
+-        # max number of tries to find out a unique file name
+-        tempfile.TMP_MAX = 50
+-        fd, name = tempfile.mkstemp(suffix, prefix, dir, text=text)
+-        file = os.fdopen(fd, mode)
+-        return FileWrapper(file, name)
+-
+-    # --- Wrapper methods around os.*
+-
+-    def chdir(self, path):
+-        """Change the current directory."""
+-        # temporarily join the specified directory to see if we have
+-        # permissions to do so
+-        basedir = os.getcwd()
+-        try:
+-            os.chdir(path)
+-        except os.error:
+-            raise
+-        else:
+-            os.chdir(basedir)
+-            self.cwd = self.fs2ftp(path)
+-
+-    def mkdir(self, path, basename):
+-        """Create the specified directory."""
+-        os.mkdir(os.path.join(path, basename))
+-
+-    def listdir(self, path):
+-        """List the content of a directory."""
+-        return os.listdir(path)
+-
+-    def rmdir(self, path):
+-        """Remove the specified directory."""
+-        os.rmdir(path)
+-
+-    def remove(self, path):
+-        """Remove the specified file."""
+-        os.remove(path)
+-
+-    def rename(self, src, dst):
+-        """Rename the specified src file to the dst filename."""
+-        os.rename(src, dst)
+-
+-    def stat(self, path):
+-        """Perform a stat() system call on the given path."""
+-        return os.stat(path)
+-
+-    def lstat(self, path):
+-        """Like stat but does not follow symbolic links."""
+-        return os.lstat(path)
+-
+-    if not hasattr(os, 'lstat'):
+-        lstat = stat
+-
+-    # --- Wrapper methods around os.path.*
+-
+-    def isfile(self, path):
+-        """Return True if path is a file."""
+-        return os.path.isfile(path)
+-
+-    def islink(self, path):
+-        """Return True if path is a symbolic link."""
+-        return os.path.islink(path)
+-
+-    def isdir(self, path):
+-        """Return True if path is a directory."""
+-        return os.path.isdir(path)
+-
+-    def getsize(self, path):
+-        """Return the size of the specified file in bytes."""
+-        return os.path.getsize(path)
+-
+-    def getmtime(self, path):
+-        """Return the last modified time as a number of seconds since
+-        the epoch."""
+-        return os.path.getmtime(path)
+-
+-    def realpath(self, path):
+-        """Return the canonical version of path eliminating any
+-        symbolic links encountered in the path (if they are
+-        supported by the operating system).
+-        """
+-        return os.path.realpath(path)
+-
+-    def lexists(self, path):
+-        """Return True if path refers to an existing path, including
+-        a broken or circular symbolic link.
+-        """
+-        if hasattr(os.path, 'lexists'):
+-            return os.path.lexists(path)
+-        # grant backward compatibility with python 2.3
+-        elif hasattr(os, 'lstat'):
+-            try:
+-                os.lstat(path)
+-            except os.error:
+-                return False
+-            return True
+-        # fallback
+-        else:
+-            return os.path.exists(path)
+-
+-    exists = lexists  # alias for backward compatibility with 0.2.0
+-
+-    def glob1(self, dirname, pattern):
+-        """Return a list of files matching a dirname pattern
+-        non-recursively.
+-
+-        Unlike glob.glob1 raises exception if os.listdir() fails.
+-        """
+-        names = self.listdir(dirname)
+-        if pattern[0] != '.':
+-            names = filter(lambda x: x[0] != '.', names)
+-        return fnmatch.filter(names, pattern)
+-
+-    # --- Listing utilities
+-
+-    # note: the following operations are no more blocking
+-
+-    def get_list_dir(self, datacr):
+-        """"Return an iterator object that yields a directory listing
+-        in a form suitable for LIST command.
+-        """
+-        raise DeprecationWarning()
+-
+-    def get_stat_dir(self, rawline):
+-        """Return an iterator object that yields a list of files
+-        matching a dirname pattern non-recursively in a form
+-        suitable for STAT command.
+-
+-         - (str) rawline: the raw string passed by client as command
+-         argument.
+-        """
+-        ftppath = self.ftpnorm(rawline)
+-        if not glob.has_magic(ftppath):
+-            return self.get_list_dir(self.ftp2fs(rawline))
+-        else:
+-            basedir, basename = os.path.split(ftppath)
+-            if glob.has_magic(basedir):
+-                return iter(['Directory recursion not supported.\r\n'])
+-            else:
+-                basedir = self.ftp2fs(basedir)
+-                listing = self.glob1(basedir, basename)
+-                if listing:
+-                    listing.sort()
+-                return self.format_list(basedir, listing)
+-
+-    def format_list(self, basedir, listing, ignore_err=True):
+-        """Return an iterator object that yields the entries of given
+-        directory emulating the "/bin/ls -lA" UNIX command output.
+-
+-         - (str) basedir: the absolute dirname.
+-         - (list) listing: the names of the entries in basedir
+-         - (bool) ignore_err: when False raise exception if os.lstat()
+-         call fails.
+-
+-        On platforms which do not support the pwd and grp modules (such
+-        as Windows), ownership is printed as "owner" and "group" as a
+-        default, and number of hard links is always "1". On UNIX
+-        systems, the actual owner, group, and number of links are
+-        printed.
+-
+-        This is how output appears to client:
+-
+-        -rw-rw-rw-   1 owner   group    7045120 Sep 02  3:47 music.mp3
+-        drwxrwxrwx   1 owner   group          0 Aug 31 18:50 e-books
+-        -rw-rw-rw-   1 owner   group        380 Sep 02  3:40 module.py
+-        """
+-        for basename in listing:
+-            file = os.path.join(basedir, basename)
+-            try:
+-                st = self.lstat(file)
+-            except os.error:
+-                if ignore_err:
+-                    continue
+-                raise
+-            perms = filemode(st.st_mode)  # permissions
+-            nlinks = st.st_nlink  # number of links to inode
+-            if not nlinks:  # non-posix system, let's use a bogus value
+-                nlinks = 1
+-            size = st.st_size  # file size
+-            uname = st.st_uid or "owner"
+-            gname = st.st_gid or "group"
+-
+-            # stat.st_mtime could fail (-1) if last mtime is too old
+-            # in which case we return the local time as last mtime
+-            try:
+-                mtime = time.strftime("%b %d %H:%M", time.localtime(st.st_mtime))
+-            except ValueError:
+-                mtime = time.strftime("%b %d %H:%M")
+-            # if the file is a symlink, resolve it, e.g. "symlink -> realfile"
+-            if stat.S_ISLNK(st.st_mode):
+-                basename = basename + " -> " + os.readlink(file)
+-
+-            # formatting is matched with proftpd ls output
+-            yield "%s %3s %-8s %-8s %8s %s %s\r\n" %(perms, nlinks, uname, gname,
+-                                                     size, mtime, basename)
+-
+-    def format_mlsx(self, basedir, listing, perms, facts, ignore_err=True):
+-        """Return an iterator object that yields the entries of a given
+-        directory or of a single file in a form suitable with MLSD and
+-        MLST commands.
+-
+-        Every entry includes a list of "facts" referring the listed
+-        element.  See RFC-3659, chapter 7, to see what every single
+-        fact stands for.
+-
+-         - (str) basedir: the absolute dirname.
+-         - (list) listing: the names of the entries in basedir
+-         - (str) perms: the string referencing the user permissions.
+-         - (str) facts: the list of "facts" to be returned.
+-         - (bool) ignore_err: when False raise exception if os.stat()
+-         call fails.
+-
+-        Note that "facts" returned may change depending on the platform
+-        and on what user specified by using the OPTS command.
+-
+-        This is how output could appear to the client issuing
+-        a MLSD request:
+-
+-        type=file;size=156;perm=r;modify=20071029155301;unique=801cd2; music.mp3
+-        type=dir;size=0;perm=el;modify=20071127230206;unique=801e33; ebooks
+-        type=file;size=211;perm=r;modify=20071103093626;unique=801e32; module.py
+-        """
+-        permdir = ''.join([x for x in perms if x not in 'arw'])
+-        permfile = ''.join([x for x in perms if x not in 'celmp'])
+-        if ('w' in perms) or ('a' in perms) or ('f' in perms):
+-            permdir += 'c'
+-        if 'd' in perms:
+-            permdir += 'p'
+-        type = size = perm = modify = create = unique = mode = uid = gid = ""
+-        for basename in listing:
+-            file = os.path.join(basedir, basename)
+-            try:
+-                st = self.stat(file)
+-            except OSError:
+-                if ignore_err:
+-                    continue
+-                raise
+-            # type + perm
+-            if stat.S_ISDIR(st.st_mode):
+-                if 'type' in facts:
+-                    if basename == '.':
+-                        type = 'type=cdir;'
+-                    elif basename == '..':
+-                        type = 'type=pdir;'
+-                    else:
+-                        type = 'type=dir;'
+-                if 'perm' in facts:
+-                    perm = 'perm=%s;' %permdir
+-            else:
+-                if 'type' in facts:
+-                    type = 'type=file;'
+-                if 'perm' in facts:
+-                    perm = 'perm=%s;' %permfile
+-            if 'size' in facts:
+-                size = 'size=%s;' %st.st_size  # file size
+-            # last modification time
+-            if 'modify' in facts:
+-                try:
+-                    modify = 'modify=%s;' %time.strftime("%Y%m%d%H%M%S",
+-                                           time.localtime(st.st_mtime))
+-                except ValueError:
+-                    # stat.st_mtime could fail (-1) if last mtime is too old
+-                    modify = ""
+-            if 'create' in facts:
+-                # on Windows we can provide also the creation time
+-                try:
+-                    create = 'create=%s;' %time.strftime("%Y%m%d%H%M%S",
+-                                           time.localtime(st.st_ctime))
+-                except ValueError:
+-                    create = ""
+-            # UNIX only
+-            if 'unix.mode' in facts:
+-                mode = 'unix.mode=%s;' %oct(st.st_mode & 0777)
+-            if 'unix.uid' in facts:
+-                uid = 'unix.uid=%s;' %st.st_uid
+-            if 'unix.gid' in facts:
+-                gid = 'unix.gid=%s;' %st.st_gid
+-            # We provide unique fact (see RFC-3659, chapter 7.5.2) on
+-            # posix platforms only; we get it by mixing st_dev and
+-            # st_ino values which should be enough for granting an
+-            # uniqueness for the file listed.
+-            # The same approach is used by pure-ftpd.
+-            # Implementors who want to provide unique fact on other
+-            # platforms should use some platform-specific method (e.g.
+-            # on Windows NTFS filesystems MTF records could be used).
+-            if 'unique' in facts:
+-                unique = "unique=%x%x;" %(st.st_dev, st.st_ino)
+-
+-            yield "%s%s%s%s%s%s%s%s%s %s\r\n" %(type, size, perm, modify, create,
+-                                                mode, uid, gid, unique, basename)
+-
+-
+-# --- FTP
+-
+-class FTPExceptionSent(Exception):
+-    """An FTP exception that FTPHandler has processed
+-    """
+-    pass
+-
+-class FTPHandler(asynchat.async_chat):
+-    """Implements the FTP server Protocol Interpreter (see RFC-959),
+-    handling commands received from the client on the control channel.
+-
+-    All relevant session information is stored in class attributes
+-    reproduced below and can be modified before instantiating this
+-    class.
+-
+-     - (str) banner: the string sent when client connects.
+-
+-     - (int) max_login_attempts:
+-        the maximum number of wrong authentications before disconnecting
+-        the client (default 3).
+-
+-     - (bool)permit_foreign_addresses:
+-        FTP site-to-site transfer feature: also referenced as "FXP" it
+-        permits for transferring a file between two remote FTP servers
+-        without the transfer going through the client's host (not
+-        recommended for security reasons as described in RFC-2577).
+-        Having this attribute set to False means that all data
+-        connections from/to remote IP addresses which do not match the
+-        client's IP address will be dropped (defualt False).
+-
+-     - (bool) permit_privileged_ports:
+-        set to True if you want to permit active data connections (PORT)
+-        over privileged ports (not recommended, defaulting to False).
+-
+-     - (str) masquerade_address:
+-        the "masqueraded" IP address to provide along PASV reply when
+-        pyftpdlib is running behind a NAT or other types of gateways.
+-        When configured pyftpdlib will hide its local address and
+-        instead use the public address of your NAT (default None).
+-
+-     - (list) passive_ports:
+-        what ports ftpd will use for its passive data transfers.
+-        Value expected is a list of integers (e.g. range(60000, 65535)).
+-        When configured pyftpdlib will no longer use kernel-assigned
+-        random ports (default None).
+-
+-
+-    All relevant instance attributes initialized when client connects
+-    are reproduced below.  You may be interested in them in case you
+-    want to subclass the original FTPHandler.
+-
+-     - (bool) authenticated: True if client authenticated himself.
+-     - (str) username: the name of the connected user (if any).
+-     - (int) attempted_logins: number of currently attempted logins.
+-     - (str) current_type: the current transfer type (default "a")
+-     - (int) af: the address family (IPv4/IPv6)
+-     - (instance) server: the FTPServer class instance.
+-     - (instance) data_server: the data server instance (if any).
+-     - (instance) data_channel: the data channel instance (if any).
+-    """
+-    # these are overridable defaults
+-
+-    # default classes
+-    authorizer = DummyAuthorizer()
+-    active_dtp = ActiveDTP
+-    passive_dtp = PassiveDTP
+-    dtp_handler = DTPHandler
+-    abstracted_fs = AbstractedFS
+-
+-    # session attributes (explained in the docstring)
+-    banner = "pyftpdlib %s ready." %__ver__
+-    max_login_attempts = 3
+-    permit_foreign_addresses = False
+-    permit_privileged_ports = False
+-    masquerade_address = None
+-    passive_ports = None
+-
+-    def __init__(self, conn, server):
+-        """Initialize the command channel.
+-
+-         - (instance) conn: the socket object instance of the newly
+-            established connection.
+-         - (instance) server: the ftp server class instance.
+-        """
+-        try:
+-            asynchat.async_chat.__init__(self, conn=conn) # python2.5
+-        except TypeError:
+-            asynchat.async_chat.__init__(self, sock=conn) # python2.6
+-        self.server = server
+-        self.remote_ip, self.remote_port = self.socket.getpeername()[:2]
+-        self.in_buffer = []
+-        self.in_buffer_len = 0
+-        self.set_terminator("\r\n")
+-
+-        # session attributes
+-        self.fs = self.abstracted_fs()
+-        self.authenticated = False
+-        self.username = ""
+-        self.password = ""
+-        self.attempted_logins = 0
+-        self.current_type = 'a'
+-        self.restart_position = 0
+-        self.quit_pending = False
+-        self._epsvall = False
+-        self.__in_dtp_queue = None
+-        self.__out_dtp_queue = None
+-
+-        self.__errno_responses = {
+-                errno.EPERM: 553,
+-                errno.EINVAL: 504,
+-                errno.ENOENT: 550,
+-                errno.EREMOTE: 450,
+-                errno.EEXIST: 521,
+-                }
+-
+-        # mlsx facts attributes
+-        self.current_facts = ['type', 'perm', 'size', 'modify']
+-        self.current_facts.append('unique')
+-        self.available_facts = self.current_facts[:]
+-        self.available_facts += ['unix.mode', 'unix.uid', 'unix.gid']
+-        self.available_facts.append('create')
+-
+-        # dtp attributes
+-        self.data_server = None
+-        self.data_channel = None
+-
+-        if hasattr(self.socket, 'family'):
+-            self.af = self.socket.family
+-        else:  # python < 2.5
+-            ip, port = self.socket.getsockname()[:2]
+-            self.af = socket.getaddrinfo(ip, port, socket.AF_UNSPEC,
+-                                         socket.SOCK_STREAM)[0][0]
+-
+-    def handle(self):
+-        """Return a 220 'Ready' response to the client over the command
+-        channel.
+-        """
+-        if len(self.banner) <= 75:
+-            self.respond("220 %s" %str(self.banner))
+-        else:
+-            self.push('220-%s\r\n' %str(self.banner))
+-            self.respond('220 ')
+-
+-    def handle_max_cons(self):
+-        """Called when limit for maximum number of connections is reached."""
+-        msg = "Too many connections. Service temporary unavailable."
+-        self.respond("421 %s" %msg)
+-        self.log(msg)
+-        # If self.push is used, data could not be sent immediately in
+-        # which case a new "loop" will occur exposing us to the risk of
+-        # accepting new connections.  Since this could cause asyncore to
+-        # run out of fds (...and exposes the server to DoS attacks), we
+-        # immediately close the channel by using close() instead of
+-        # close_when_done(). If data has not been sent yet client will
+-        # be silently disconnected.
+-        self.close()
+-
+-    def handle_max_cons_per_ip(self):
+-        """Called when too many clients are connected from the same IP."""
+-        msg = "Too many connections from the same IP address."
+-        self.respond("421 %s" %msg)
+-        self.log(msg)
+-        self.close_when_done()
+-
+-    # --- asyncore / asynchat overridden methods
+-
+-    def readable(self):
+-        # if there's a quit pending we stop reading data from socket
+-        return not self.quit_pending
+-
+-    def collect_incoming_data(self, data):
+-        """Read incoming data and append to the input buffer."""
+-        self.in_buffer.append(data)
+-        self.in_buffer_len += len(data)
+-        # Flush buffer if it gets too long (possible DoS attacks).
+-        # RFC-959 specifies that a 500 response could be given in
+-        # such cases
+-        buflimit = 2048
+-        if self.in_buffer_len > buflimit:
+-            self.respond('500 Command too long.')
+-            self.log('Command received exceeded buffer limit of %s.' %(buflimit))
+-            self.in_buffer = []
+-            self.in_buffer_len = 0
+-
+-    # commands accepted before authentication
+-    unauth_cmds = ('FEAT','HELP','NOOP','PASS','QUIT','STAT','SYST','USER')
+-
+-    # commands needing an argument
+-    arg_cmds = ('ALLO','APPE','DELE','EPRT','MDTM','MODE','MKD','OPTS','PORT',
+-                'REST','RETR','RMD','RNFR','RNTO','SIZE', 'STOR','STRU',
+-                'TYPE','USER','XMKD','XRMD')
+-
+-    # commands needing no argument
+-    unarg_cmds = ('ABOR','CDUP','FEAT','NOOP','PASV','PWD','QUIT','REIN',
+-                  'SYST','XCUP','XPWD')
+-
+-    def found_terminator(self):
+-        r"""Called when the incoming data stream matches the \r\n
+-        terminator.
+-
+-        Depending on the command received it calls the command's
+-        corresponding method (e.g. for received command "MKD pathname",
+-        ftp_MKD() method is called with "pathname" as the argument).
+-        """
+-        line = ''.join(self.in_buffer)
+-        self.in_buffer = []
+-        self.in_buffer_len = 0
+-
+-        cmd = line.split(' ')[0].upper()
+-        space = line.find(' ')
+-        if space != -1:
+-            arg = line[space + 1:]
+-        else:
+-            arg = ""
+-
+-        if cmd != 'PASS':
+-            self.logline("<== %s" %line)
+-        else:
+-            self.logline("<== %s %s" %(line.split(' ')[0], '*' * 6))
+-
+-        # let's check if user provided an argument for those commands
+-        # needing one
+-        if not arg and cmd in self.arg_cmds:
+-            self.respond("501 Syntax error: command needs an argument.")
+-            return
+-
+-        # let's do the same for those commands requiring no argument.
+-        elif arg and cmd in self.unarg_cmds:
+-            self.respond("501 Syntax error: command does not accept arguments.")
+-            return
+-
+-        # provide a limited set of commands if user isn't
+-        # authenticated yet
+-        if (not self.authenticated):
+-            if cmd in self.unauth_cmds:
+-                # we permit STAT during this phase but we don't want
+-                # STAT to return a directory LISTing if the user is
+-                # not authenticated yet (this could happen if STAT
+-                # is used with an argument)
+-                if (cmd == 'STAT') and arg:
+-                    self.respond("530 Log in with USER and PASS first.")
+-                else:
+-                    method = getattr(self, 'ftp_' + cmd)
+-                    method(arg)  # call the proper ftp_* method
+-            elif cmd in proto_cmds:
+-                self.respond("530 Log in with USER and PASS first.")
+-            else:
+-                self.respond('500 Command "%s" not understood.' %line)
+-
+-        # provide full command set
+-        elif (self.authenticated) and (cmd in proto_cmds):
+-            if not (self.__check_path(arg, arg)): # and self.__check_perm(cmd, arg)):
+-                return
+-            method = getattr(self, 'ftp_' + cmd)
+-            method(arg)  # call the proper ftp_* method
+-
+-        else:
+-            # recognize those commands having "special semantics"
+-            if 'ABOR' in cmd:
+-                self.ftp_ABOR("")
+-            elif 'STAT' in cmd:
+-                self.ftp_STAT("")
+-            # unknown command
+-            else:
+-                self.respond('500 Command "%s" not understood.' %line)
+-
+-    def __check_path(self, cmd, line):
+-        """Check whether a path is valid."""
+-
+-        # Always true, we will only check later, once we have a cursor
+-        return True
+-
+-    def __check_perm(self, cmd, line, datacr):
+-        """Check permissions depending on issued command."""
+-        map = {'CWD':'e', 'XCWD':'e', 'CDUP':'e', 'XCUP':'e',
+-               'LIST':'l', 'NLST':'l', 'MLSD':'l', 'STAT':'l',
+-               'RETR':'r',
+-               'APPE':'a',
+-               'DELE':'d', 'RMD':'d', 'XRMD':'d',
+-               'RNFR':'f',
+-               'MKD':'m', 'XMKD':'m',
+-               'STOR':'w'}
+-        raise NotImplementedError
+-        if cmd in map:
+-            if cmd == 'STAT' and not line:
+-                return True
+-            perm = map[cmd]
+-            if not line and (cmd in ('LIST','NLST','MLSD')):
+-                path = self.fs.ftp2fs(self.fs.cwd, datacr)
+-            else:
+-                path = self.fs.ftp2fs(line, datacr)
+-            if not self.authorizer.has_perm(self.username, perm, path):
+-                self.log('FAIL %s "%s". Not enough privileges.' \
+-                         %(cmd, self.fs.ftpnorm(line)))
+-                self.respond("550 Can't %s. Not enough privileges." %cmd)
+-                return False
+-        return True
+-
+-    def handle_expt(self):
+-        """Called when there is out of band (OOB) data for the socket
+-        connection.  This could happen in case of such commands needing
+-        "special action" (typically STAT and ABOR) in which case we
+-        append OOB data to incoming buffer.
+-        """
+-        if hasattr(socket, 'MSG_OOB'):
+-            try:
+-                data = self.socket.recv(1024, socket.MSG_OOB)
+-            except socket.error:
+-                pass
+-            else:
+-                self.in_buffer.append(data)
+-                return
+-        self.log("Can't handle OOB data.")
+-        self.close()
+-
+-    def handle_error(self):
+-        try:
+-            raise
+-        except (KeyboardInterrupt, SystemExit, asyncore.ExitNow):
+-            raise
+-        except socket.error, err:
+-            # fix around asyncore bug (http://bugs.python.org/issue1736101)
+-            if err[0] in (errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN, \
+-                          errno.ECONNABORTED):
+-                self.handle_close()
+-                return
+-            else:
+-                logerror(traceback.format_exc())
+-        except:
+-            logerror(traceback.format_exc())
+-        self.close()
+-
+-    def handle_close(self):
+-        self.close()
+-
+-    _closed = False
+-    def close(self):
+-        """Close the current channel disconnecting the client."""
+-        if not self._closed:
+-            self._closed = True
+-            if self.data_server:
+-                self.data_server.close()
+-                del self.data_server
+-
+-            if self.data_channel:
+-                self.data_channel.close()
+-                del self.data_channel
+-
+-            del self.__out_dtp_queue
+-            del self.__in_dtp_queue
+-
+-            # remove client IP address from ip map
+-            self.server.ip_map.remove(self.remote_ip)
+-            asynchat.async_chat.close(self)
+-            self.log("Disconnected.")
+-
+-    # --- callbacks
+-
+-    def on_dtp_connection(self):
+-        """Called every time data channel connects (either active or
+-        passive).
+-
+-        Incoming and outgoing queues are checked for pending data.
+-        If outbound data is pending, it is pushed into the data channel.
+-        If awaiting inbound data, the data channel is enabled for
+-        receiving.
+-        """
+-        if self.data_server:
+-            self.data_server.close()
+-        self.data_server = None
+-
+-        # check for data to send
+-        if self.__out_dtp_queue:
+-            data, isproducer, file = self.__out_dtp_queue
+-            if file:
+-                self.data_channel.file_obj = file
+-            if not isproducer:
+-                self.data_channel.push(data)
+-            else:
+-                self.data_channel.push_with_producer(data)
+-            if self.data_channel:
+-                self.data_channel.close_when_done()
+-            self.__out_dtp_queue = None
+-
+-        # check for data to receive
+-        elif self.__in_dtp_queue:
+-            self.data_channel.file_obj = self.__in_dtp_queue
+-            self.data_channel.enable_receiving(self.current_type)
+-            self.__in_dtp_queue = None
+-
+-    def on_dtp_close(self):
+-        """Called every time the data channel is closed."""
+-        self.data_channel = None
+-        if self.quit_pending:
+-            self.close_when_done()
+-
+-    # --- utility
+-
+-    def respond(self, resp):
+-        """Send a response to the client using the command channel."""
+-        self.push(resp + '\r\n')
+-        self.logline('==> %s' % resp)
+-
+-    def push_dtp_data(self, data, isproducer=False, file=None):
+-        """Pushes data into the data channel.
+-
+-        It is usually called for those commands requiring some data to
+-        be sent over the data channel (e.g. RETR).
+-        If data channel does not exist yet, it queues the data to send
+-        later; data will then be pushed into data channel when
+-        on_dtp_connection() will be called.
+-
+-         - (str/classobj) data: the data to send which may be a string
+-            or a producer object).
+-         - (bool) isproducer: whether treat data as a producer.
+-         - (file) file: the file[-like] object to send (if any).
+-        """
+-        if self.data_channel:
+-            self.respond("125 Data connection already open. Transfer starting.")
+-            if file:
+-                self.data_channel.file_obj = file
+-            if not isproducer:
+-                self.data_channel.push(data)
+-            else:
+-                self.data_channel.push_with_producer(data)
+-            if self.data_channel:
+-                self.data_channel.close_when_done()
+-        else:
+-            self.respond("150 File status okay. About to open data connection.")
+-            self.__out_dtp_queue = (data, isproducer, file)
+-
+-    def log(self, msg):
+-        """Log a message, including additional identifying session data."""
+-        log("[%s]@%s:%s %s" %(self.username, self.remote_ip,
+-                              self.remote_port, msg))
+-
+-    def logline(self, msg):
+-        """Log a line including additional indentifying session data."""
+-        logline("%s:%s %s" %(self.remote_ip, self.remote_port, msg))
+-
+-    def flush_account(self):
+-        """Flush account information by clearing attributes that need
+-        to be reset on a REIN or new USER command.
+-        """
+-        if self.data_channel:
+-            if not self.data_channel.transfer_in_progress():
+-                self.data_channel.close()
+-                self.data_channel = None
+-        if self.data_server:
+-            self.data_server.close()
+-            self.data_server = None
+-
+-        self.fs.rnfr = None
+-        self.authenticated = False
+-        self.username = ""
+-        self.password = ""
+-        self.attempted_logins = 0
+-        self.current_type = 'a'
+-        self.restart_position = 0
+-        self.quit_pending = False
+-        self.__in_dtp_queue = None
+-        self.__out_dtp_queue = None
+-
+-    def run_as_current_user(self, function, *args, **kwargs):
+-        """Execute a function impersonating the current logged-in user."""
+-        self.authorizer.impersonate_user(self.username, self.password)
+-        try:
+-            return function(*args, **kwargs)
+-        finally:
+-            self.authorizer.terminate_impersonation()
+-
+-        # --- connection
+-
+-    def try_as_current_user(self, function, args=None, kwargs=None, line=None, errno_resp=None):
+-        """run function as current user, auto-respond in exceptions
+-           @param args,kwargs the arguments, in list and dict respectively
+-           @param errno_resp a dictionary of responses to IOError, OSError
+-        """
+-        if errno_resp:
+-            eresp = self.__errno_responses.copy()
+-            eresp.update(errno_resp)
+-        else:
+-            eresp = self.__errno_responses
+-
+-        uline = ''
+-        if line:
+-            uline = ' "%s"' % _to_unicode(line)
+-        try:
+-            if args is None:
+-                args = ()
+-            if kwargs is None:
+-                kwargs = {}
+-            return self.run_as_current_user(function, *args, **kwargs)
+-        except NotImplementedError, err:
+-            cmdname = function.__name__
+-            why = err.args[0] or 'Not implemented'
+-            self.log('FAIL %s() not implemented:  %s.' %(cmdname, why))
+-            self.respond('502 %s.' %why)
+-            raise FTPExceptionSent(why)
+-        except EnvironmentError, err:
+-            cmdname = function.__name__
+-            try:
+-                logline(traceback.format_exc())
+-            except Exception:
+-                pass
+-            ret_code = eresp.get(err.errno, '451')
+-            why = (err.strerror) or 'Error in command'
+-            self.log('FAIL %s() %s errno=%s:  %s.' %(cmdname, uline, err.errno, why))
+-            self.respond('%s %s.' % (str(ret_code), why))
+-
+-            raise FTPExceptionSent(why)
+-        except Exception, err:
+-            cmdname = function.__name__
+-            try:
+-                logerror(traceback.format_exc())
+-            except Exception:
+-                pass
+-            why = (err.args and err.args[0]) or 'Exception'
+-            self.log('FAIL %s() %s Exception:  %s.' %(cmdname, uline, why))
+-            self.respond('451 %s.' % why)
+-            raise FTPExceptionSent(why)
+-
+-    def get_crdata2(self, *args, **kwargs):
+-        return self.try_as_current_user(self.fs.get_crdata, args, kwargs, line=args[0])
+-
+-    def _make_eport(self, ip, port):
+-        """Establish an active data channel with remote client which
+-        issued a PORT or EPRT command.
+-        """
+-        # FTP bounce attacks protection: according to RFC-2577 it's
+-        # recommended to reject PORT if IP address specified in it
+-        # does not match client IP address.
+-        if not self.permit_foreign_addresses:
+-            if ip != self.remote_ip:
+-                self.log("Rejected data connection to foreign address %s:%s."
+-                         %(ip, port))
+-                self.respond("501 Can't connect to a foreign address.")
+-                return
+-
+-        # ...another RFC-2577 recommendation is rejecting connections
+-        # to privileged ports (< 1024) for security reasons.
+-        if not self.permit_privileged_ports:
+-            if port < 1024:
+-                self.log('PORT against the privileged port "%s" refused.' %port)
+-                self.respond("501 Can't connect over a privileged port.")
+-                return
+-
+-        # close existent DTP-server instance, if any.
+-        if self.data_server:
+-            self.data_server.close()
+-            self.data_server = None
+-        if self.data_channel:
+-            self.data_channel.close()
+-            self.data_channel = None
+-
+-        # make sure we are not hitting the max connections limit
+-        if self.server.max_cons:
+-            if len(self._map) >= self.server.max_cons:
+-                msg = "Too many connections. Can't open data channel."
+-                self.respond("425 %s" %msg)
+-                self.log(msg)
+-                return
+-
+-        # open data channel
+-        self.active_dtp(ip, port, self)
+-
+-    def _make_epasv(self, extmode=False):
+-        """Initialize a passive data channel with remote client which
+-        issued a PASV or EPSV command.
+-        If extmode argument is False we assume that client issued EPSV in
+-        which case extended passive mode will be used (see RFC-2428).
+-        """
+-        # close existing DTP-server instance, if any
+-        if self.data_server:
+-            self.data_server.close()
+-            self.data_server = None
+-
+-        if self.data_channel:
+-            self.data_channel.close()
+-            self.data_channel = None
+-
+-        # make sure we are not hitting the max connections limit
+-        if self.server.max_cons:
+-            if len(self._map) >= self.server.max_cons:
+-                msg = "Too many connections. Can't open data channel."
+-                self.respond("425 %s" %msg)
+-                self.log(msg)
+-                return
+-
+-        # open data channel
+-        self.data_server = self.passive_dtp(self, extmode)
+-
+-    def ftp_PORT(self, line):
+-        """Start an active data channel by using IPv4."""
+-        if self._epsvall:
+-            self.respond("501 PORT not allowed after EPSV ALL.")
+-            return
+-        if self.af != socket.AF_INET:
+-            self.respond("425 You cannot use PORT on IPv6 connections. "
+-                         "Use EPRT instead.")
+-            return
+-        # Parse PORT request for getting IP and PORT.
+-        # Request comes in as:
+-        # > h1,h2,h3,h4,p1,p2
+-        # ...where the client's IP address is h1.h2.h3.h4 and the TCP
+-        # port number is (p1 * 256) + p2.
+-        try:
+-            addr = map(int, line.split(','))
+-            assert len(addr) == 6
+-            for x in addr[:4]:
+-                assert 0 <= x <= 255
+-            ip = '%d.%d.%d.%d' %tuple(addr[:4])
+-            port = (addr[4] * 256) + addr[5]
+-            assert 0 <= port <= 65535
+-        except (AssertionError, ValueError, OverflowError):
+-            self.respond("501 Invalid PORT format.")
+-            return
+-        self._make_eport(ip, port)
+-
+-    def ftp_EPRT(self, line):
+-        """Start an active data channel by choosing the network protocol
+-        to use (IPv4/IPv6) as defined in RFC-2428.
+-        """
+-        if self._epsvall:
+-            self.respond("501 EPRT not allowed after EPSV ALL.")
+-            return
+-        # Parse EPRT request for getting protocol, IP and PORT.
+-        # Request comes in as:
+-        # # <d>proto<d>ip<d>port<d>
+-        # ...where <d> is an arbitrary delimiter character (usually "|") and
+-        # <proto> is the network protocol to use (1 for IPv4, 2 for IPv6).
+-        try:
+-            af, ip, port = line.split(line[0])[1:-1]
+-            port = int(port)
+-            assert 0 <= port <= 65535
+-        except (AssertionError, ValueError, IndexError, OverflowError):
+-            self.respond("501 Invalid EPRT format.")
+-            return
+-
+-        if af == "1":
+-            if self.af != socket.AF_INET:
+-                self.respond('522 Network protocol not supported (use 2).')
+-            else:
+-                try:
+-                    octs = map(int, ip.split('.'))
+-                    assert len(octs) == 4
+-                    for x in octs:
+-                        assert 0 <= x <= 255
+-                except (AssertionError, ValueError, OverflowError):
+-                    self.respond("501 Invalid EPRT format.")
+-                else:
+-                    self._make_eport(ip, port)
+-        elif af == "2":
+-            if self.af == socket.AF_INET:
+-                self.respond('522 Network protocol not supported (use 1).')
+-            else:
+-                self._make_eport(ip, port)
+-        else:
+-            if self.af == socket.AF_INET:
+-                self.respond('501 Unknown network protocol (use 1).')
+-            else:
+-                self.respond('501 Unknown network protocol (use 2).')
+-
+-    def ftp_PASV(self, line):
+-        """Start a passive data channel by using IPv4."""
+-        if self._epsvall:
+-            self.respond("501 PASV not allowed after EPSV ALL.")
+-            return
+-        if self.af != socket.AF_INET:
+-            self.respond("425 You cannot use PASV on IPv6 connections. "
+-                         "Use EPSV instead.")
+-        else:
+-            self._make_epasv(extmode=False)
+-
+-    def ftp_EPSV(self, line):
+-        """Start a passive data channel by using IPv4 or IPv6 as defined
+-        in RFC-2428.
+-        """
+-        # RFC-2428 specifies that if an optional parameter is given,
+-        # we have to determine the address family from that otherwise
+-        # use the same address family used on the control connection.
+-        # In such a scenario a client may use IPv4 on the control channel
+-        # and choose to use IPv6 for the data channel.
+-        # But how could we use IPv6 on the data channel without knowing
+-        # which IPv6 address to use for binding the socket?
+-        # Unfortunately RFC-2428 does not provide satisfing information
+-        # on how to do that.  The assumption is that we don't have any way
+-        # to know which address to use, hence we just use the same address
+-        # family used on the control connection.
+-        if not line:
+-            self._make_epasv(extmode=True)
+-        elif line == "1":
+-            if self.af != socket.AF_INET:
+-                self.respond('522 Network protocol not supported (use 2).')
+-            else:
+-                self._make_epasv(extmode=True)
+-        elif line == "2":
+-            if self.af == socket.AF_INET:
+-                self.respond('522 Network protocol not supported (use 1).')
+-            else:
+-                self._make_epasv(extmode=True)
+-        elif line.lower() == 'all':
+-            self._epsvall = True
+-            self.respond('220 Other commands other than EPSV are now disabled.')
+-        else:
+-            if self.af == socket.AF_INET:
+-                self.respond('501 Unknown network protocol (use 1).')
+-            else:
+-                self.respond('501 Unknown network protocol (use 2).')
+-
+-    def ftp_QUIT(self, line):
+-        """Quit the current session."""
+-        # From RFC-959:
+-        # This command terminates a USER and if file transfer is not
+-        # in progress, the server closes the control connection.
+-        # If file transfer is in progress, the connection will remain
+-        # open for result response and the server will then close it.
+-        if self.authenticated:
+-            msg_quit = self.authorizer.get_msg_quit(self.username)
+-        else:
+-            msg_quit = "Goodbye."
+-        if len(msg_quit) <= 75:
+-            self.respond("221 %s" %msg_quit)
+-        else:
+-            self.push("221-%s\r\n" %msg_quit)
+-            self.respond("221 ")
+-
+-        if not self.data_channel:
+-            self.close_when_done()
+-        else:
+-            # tell the cmd channel to stop responding to commands.
+-            self.quit_pending = True
+-
+-
+-        # --- data transferring
+-
+-    def ftp_LIST(self, line):
+-        """Return a list of files in the specified directory to the
+-        client.
+-        """
+-        # - If no argument, fall back on cwd as default.
+-        # - Some older FTP clients erroneously issue /bin/ls-like LIST
+-        #   formats in which case we fall back on cwd as default.
+-        if not line or line.lower() in ('-a', '-l', '-al', '-la'):
+-            line = ''
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line, mode='list')
+-            iterator = self.try_as_current_user(self.fs.get_list_dir, (datacr,))
+-        except FTPExceptionSent:
+-            self.fs.close_cr(datacr)
+-            return
+-
+-        try:
+-            self.log('OK LIST "%s". Transfer starting.' % line)
+-            producer = BufferedIteratorProducer(iterator)
+-            self.push_dtp_data(producer, isproducer=True)
+-        finally:
+-            self.fs.close_cr(datacr)
+-
+-
+-    def ftp_NLST(self, line):
+-        """Return a list of files in the specified directory in a
+-        compact form to the client.
+-        """
+-        if not line:
+-            line = ''
+-
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line, mode='list')
+-            if not datacr:
+-                datacr = ( None, None, None )
+-            if self.fs.isdir(datacr[1]):
+-                nodelist = self.try_as_current_user(self.fs.listdir, (datacr,))
+-            else:
+-                # if path is a file we just list its name
+-                nodelist = [datacr[1],]
+-
+-            listing = []
+-            for nl in nodelist:
+-                if isinstance(nl.path, (list, tuple)):
+-                    listing.append(nl.path[-1])
+-                else:
+-                    listing.append(nl.path)    # assume string
+-        except FTPExceptionSent:
+-            self.fs.close_cr(datacr)
+-            return
+-
+-        self.fs.close_cr(datacr)
+-        data = ''
+-        if listing:
+-            listing.sort()
+-            data =  ''.join([ _to_decode(x) + '\r\n' for x in listing ])
+-        self.log('OK NLST "%s". Transfer starting.' %line)
+-        self.push_dtp_data(data)
+-
+-        # --- MLST and MLSD commands
+-
+-    # The MLST and MLSD commands are intended to standardize the file and
+-    # directory information returned by the server-FTP process.  These
+-    # commands differ from the LIST command in that the format of the
+-    # replies is strictly defined although extensible.
+-
+-    def ftp_MLST(self, line):
+-        """Return information about a pathname in a machine-processable
+-        form as defined in RFC-3659.
+-        """
+-        # if no argument, fall back on cwd as default
+-        if not line:
+-            line = ''
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line, mode='list')
+-            perms = self.authorizer.get_perms(self.username)
+-            iterator = self.try_as_current_user(self.fs.format_mlsx, (datacr[0], datacr[1].parent,
+-                       [datacr[1],], perms, self.current_facts), {'ignore_err':False})
+-            data = ''.join(iterator)
+-        except FTPExceptionSent:
+-            self.fs.close_cr(datacr)
+-            return
+-        else:
+-            self.fs.close_cr(datacr)
+-            # since TVFS is supported (see RFC-3659 chapter 6), a fully
+-            # qualified pathname should be returned
+-            data = data.split(' ')[0] + ' %s\r\n' %line
+-            # response is expected on the command channel
+-            self.push('250-Listing "%s":\r\n' %line)
+-            # the fact set must be preceded by a space
+-            self.push(' ' + data)
+-            self.respond('250 End MLST.')
+-
+-    def ftp_MLSD(self, line):
+-        """Return contents of a directory in a machine-processable form
+-        as defined in RFC-3659.
+-        """
+-        # if no argument, fall back on cwd as default
+-        if not line:
+-            line = ''
+-
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line, mode='list')
+-            # RFC-3659 requires 501 response code if path is not a directory
+-            if not self.fs.isdir(datacr[1]):
+-                err = 'No such directory'
+-                self.log('FAIL MLSD "%s". %s.' %(line, err))
+-                self.respond("501 %s." %err)
+-                return
+-            listing = self.try_as_current_user(self.fs.listdir, (datacr,))
+-        except FTPExceptionSent:
+-            self.fs.close_cr(datacr)
+-            return
+-        else:
+-            self.fs.close_cr(datacr)
+-            perms = self.authorizer.get_perms(self.username)
+-            iterator = self.fs.format_mlsx(datacr[0], datacr[1], listing, perms,
+-                       self.current_facts)
+-            producer = BufferedIteratorProducer(iterator)
+-            self.log('OK MLSD "%s". Transfer starting.' %line)
+-            self.push_dtp_data(producer, isproducer=True)
+-
+-    def ftp_RETR(self, line):
+-        """Retrieve the specified file (transfer from the server to the
+-        client)
+-        """
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line, mode='file')
+-            fd = self.try_as_current_user(self.fs.open, (datacr, 'rb'))
+-        except FTPExceptionSent:
+-            self.fs.close_cr(datacr)
+-            return
+-
+-        if self.restart_position:
+-            # Make sure that the requested offset is valid (within the
+-            # size of the file being resumed).
+-            # According to RFC-1123 a 554 reply may result in case that
+-            # the existing file cannot be repositioned as specified in
+-            # the REST.
+-            ok = 0
+-            try:
+-                assert not self.restart_position > self.fs.getsize(datacr)
+-                fd.seek(self.restart_position)
+-                ok = 1
+-            except AssertionError:
+-                why = "Invalid REST parameter"
+-            except IOError, err:
+-                why = _strerror(err)
+-            self.restart_position = 0
+-            if not ok:
+-                self.respond('554 %s' %why)
+-                self.log('FAIL RETR "%s". %s.' %(line, why))
+-                self.fs.close_cr(datacr)
+-                return
+-        self.log('OK RETR "%s". Download starting.' %line)
+-        producer = FileProducer(fd, self.current_type)
+-        self.push_dtp_data(producer, isproducer=True, file=fd)
+-        self.fs.close_cr(datacr)
+-
+-    def ftp_STOR(self, line, mode='w'):
+-        """Store a file (transfer from the client to the server)."""
+-        # A resume could occur in case of APPE or REST commands.
+-        # In that case we have to open file object in different ways:
+-        # STOR: mode = 'w'
+-        # APPE: mode = 'a'
+-        # REST: mode = 'r+' (to permit seeking on file object)
+-        if 'a' in mode:
+-            cmd = 'APPE'
+-        else:
+-            cmd = 'STOR'
+-
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line,mode='create')
+-            if self.restart_position:
+-                mode = 'r+'
+-            fd = self.try_as_current_user(self.fs.create, (datacr, datacr[2], mode + 'b'))
+-            assert fd
+-        except FTPExceptionSent:
+-            self.fs.close_cr(datacr)
+-            return
+-
+-        if self.restart_position:
+-            # Make sure that the requested offset is valid (within the
+-            # size of the file being resumed).
+-            # According to RFC-1123 a 554 reply may result in case
+-            # that the existing file cannot be repositioned as
+-            # specified in the REST.
+-            ok = 0
+-            try:
+-                assert not self.restart_position > self.fs.getsize(datacr)
+-                fd.seek(self.restart_position)
+-                ok = 1
+-            except AssertionError:
+-                why = "Invalid REST parameter"
+-            except IOError, err:
+-                why = _strerror(err)
+-            self.restart_position = 0
+-            if not ok:
+-                self.fs.close_cr(datacr)
+-                self.respond('554 %s' %why)
+-                self.log('FAIL %s "%s". %s.' %(cmd, line, why))
+-                return
+-
+-        self.log('OK %s "%s". Upload starting.' %(cmd, line))
+-        if self.data_channel:
+-            self.respond("125 Data connection already open. Transfer starting.")
+-            self.data_channel.file_obj = fd
+-            self.data_channel.enable_receiving(self.current_type)
+-        else:
+-            self.respond("150 File status okay. About to open data connection.")
+-            self.__in_dtp_queue = fd
+-        self.fs.close_cr(datacr)
+-
+-
+-    def ftp_STOU(self, line):
+-        """Store a file on the server with a unique name."""
+-        # Note 1: RFC-959 prohibited STOU parameters, but this
+-        # prohibition is obsolete.
+-        # Note 2: 250 response wanted by RFC-959 has been declared
+-        # incorrect in RFC-1123 that wants 125/150 instead.
+-        # Note 3: RFC-1123 also provided an exact output format
+-        # defined to be as follow:
+-        # > 125 FILE: pppp
+-        # ...where pppp represents the unique path name of the
+-        # file that will be written.
+-
+-        # watch for STOU preceded by REST, which makes no sense.
+-        if self.restart_position:
+-            self.respond("450 Can't STOU while REST request is pending.")
+-            return
+-
+-
+-        if line:
+-            datacr = self.get_crdata2(line, mode='create')
+-            # TODO
+-        else:
+-            # TODO
+-            basedir = self.fs.ftp2fs(self.fs.cwd, datacr)
+-            prefix = 'ftpd.'
+-        try:
+-            fd = self.try_as_current_user(self.fs.mkstemp, kwargs={'prefix':prefix,
+-                                          'dir': basedir}, line=line )
+-        except FTPExceptionSent:
+-            self.fs.close_cr(datacr)
+-            return
+-        except IOError, err: # TODO
+-            # hitted the max number of tries to find out file with
+-            # unique name
+-            if err.errno == errno.EEXIST:
+-                why = 'No usable unique file name found'
+-            # something else happened
+-            else:
+-                why = _strerror(err)
+-            self.respond("450 %s." %why)
+-            self.log('FAIL STOU "%s". %s.' %(self.fs.ftpnorm(line), why))
+-            self.fs.close_cr(datacr)
+-            return
+-
+-        filename = line
+-        if not self.authorizer.has_perm(self.username, 'w', filename):
+-            self.log('FAIL STOU "%s". Not enough privileges'
+-                     %self.fs.ftpnorm(line))
+-            self.respond("550 Can't STOU: not enough privileges.")
+-            self.fs.close_cr(datacr)
+-            return
+-
+-        # now just acts like STOR except that restarting isn't allowed
+-        self.log('OK STOU "%s". Upload starting.' %filename)
+-        if self.data_channel:
+-            self.respond("125 FILE: %s" %filename)
+-            self.data_channel.file_obj = fd
+-            self.data_channel.enable_receiving(self.current_type)
+-        else:
+-            self.respond("150 FILE: %s" %filename)
+-            self.__in_dtp_queue = fd
+-        self.fs.close_cr(datacr)
+-
+-
+-    def ftp_APPE(self, line):
+-        """Append data to an existing file on the server."""
+-        # watch for APPE preceded by REST, which makes no sense.
+-        if self.restart_position:
+-            self.respond("550 Can't APPE while REST request is pending.")
+-        else:
+-            self.ftp_STOR(line, mode='a')
+-
+-    def ftp_REST(self, line):
+-        """Restart a file transfer from a previous mark."""
+-        try:
+-            marker = int(line)
+-            if marker < 0:
+-                raise ValueError
+-        except (ValueError, OverflowError):
+-            self.respond("501 Invalid parameter.")
+-        else:
+-            self.respond("350 Restarting at position %s. " \
+-                        "Now use RETR/STOR for resuming." %marker)
+-            self.log("OK REST %s." %marker)
+-            self.restart_position = marker
+-
+-    def ftp_ABOR(self, line):
+-        """Abort the current data transfer."""
+-
+-        # ABOR received while no data channel exists
+-        if (self.data_server is None) and (self.data_channel is None):
+-            resp = "225 No transfer to abort."
+-        else:
+-            # a PASV was received but connection wasn't made yet
+-            if self.data_server:
+-                self.data_server.close()
+-                self.data_server = None
+-                resp = "225 ABOR command successful; data channel closed."
+-
+-            # If a data transfer is in progress the server must first
+-            # close the data connection, returning a 426 reply to
+-            # indicate that the transfer terminated abnormally, then it
+-            # must send a 226 reply, indicating that the abort command
+-            # was successfully processed.
+-            # If no data has been transmitted we just respond with 225
+-            # indicating that no transfer was in progress.
+-            if self.data_channel:
+-                if self.data_channel.transfer_in_progress():
+-                    self.data_channel.close()
+-                    self.data_channel = None
+-                    self.respond("426 Connection closed; transfer aborted.")
+-                    self.log("OK ABOR. Transfer aborted, data channel closed.")
+-                    resp = "226 ABOR command successful."
+-                else:
+-                    self.data_channel.close()
+-                    self.data_channel = None
+-                    self.log("OK ABOR. Data channel closed.")
+-                    resp = "225 ABOR command successful; data channel closed."
+-        self.respond(resp)
+-
+-
+-        # --- authentication
+-
+-    def ftp_USER(self, line):
+-        """Set the username for the current session."""
+-        # we always treat anonymous user as lower-case string.
+-        if line.lower() == "anonymous":
+-            line = "anonymous"
+-
+-        # RFC-959 specifies a 530 response to the USER command if the
+-        # username is not valid.  If the username is valid is required
+-        # ftpd returns a 331 response instead.  In order to prevent a
+-        # malicious client from determining valid usernames on a server,
+-        # it is suggested by RFC-2577 that a server always return 331 to
+-        # the USER command and then reject the combination of username
+-        # and password for an invalid username when PASS is provided later.
+-        if not self.authenticated:
+-            self.respond('331 Username ok, send password.')
+-        else:
+-            # a new USER command could be entered at any point in order
+-            # to change the access control flushing any user, password,
+-            # and account information already supplied and beginning the
+-            # login sequence again.
+-            self.flush_account()
+-            msg = 'Previous account information was flushed'
+-            self.log('OK USER "%s". %s.' %(line, msg))
+-            self.respond('331 %s, send password.' %msg)
+-        self.username = line
+-
+-    def ftp_PASS(self, line):
+-        """Check username's password against the authorizer."""
+-
+-        if self.authenticated:
+-            self.respond("503 User already authenticated.")
+-            return
+-        if not self.username:
+-            self.respond("503 Login with USER first.")
+-            return
+-
+-        # username ok
+-        if self.authorizer.has_user(self.username):
+-            if self.username == 'anonymous' \
+-            or self.authorizer.validate_authentication(self.username, line):
+-                msg_login = self.authorizer.get_msg_login(self.username)
+-                if len(msg_login) <= 75:
+-                    self.respond('230 %s' %msg_login)
+-                else:
+-                    self.push("230-%s\r\n" %msg_login)
+-                    self.respond("230 ")
+-
+-                self.authenticated = True
+-                self.password = line
+-                self.attempted_logins = 0
+-                self.fs.root = self.authorizer.get_home_dir(self.username)
+-                self.fs.username=self.username
+-                self.fs.password=line
+-                self.log("User %s logged in." %self.username)
+-            else:
+-                self.attempted_logins += 1
+-                if self.attempted_logins >= self.max_login_attempts:
+-                    self.respond("530 Maximum login attempts. Disconnecting.")
+-                    self.close()
+-                else:
+-                    self.respond("530 Authentication failed.")
+-                self.log('Authentication failed (user: "%s").' %self.username)
+-                self.username = ""
+-
+-        # wrong username
+-        else:
+-            self.attempted_logins += 1
+-            if self.attempted_logins >= self.max_login_attempts:
+-                self.log('Authentication failed: unknown username "%s".'
+-                            %self.username)
+-                self.respond("530 Maximum login attempts. Disconnecting.")
+-                self.close()
+-            elif self.username.lower() == 'anonymous':
+-                self.respond("530 Anonymous access not allowed.")
+-                self.log('Authentication failed: anonymous access not allowed.')
+-            else:
+-                self.respond("530 Authentication failed.")
+-                self.log('Authentication failed: unknown username "%s".'
+-                            %self.username)
+-                self.username = ""
+-
+-    def ftp_REIN(self, line):
+-        """Reinitialize user's current session."""
+-        # From RFC-959:
+-        # REIN command terminates a USER, flushing all I/O and account
+-        # information, except to allow any transfer in progress to be
+-        # completed.  All parameters are reset to the default settings
+-        # and the control connection is left open.  This is identical
+-        # to the state in which a user finds himself immediately after
+-        # the control connection is opened.
+-        self.log("OK REIN. Flushing account information.")
+-        self.flush_account()
+-        # Note: RFC-959 erroneously mention "220" as the correct response
+-        # code to be given in this case, but this is wrong...
+-        self.respond("230 Ready for new user.")
+-
+-
+-        # --- filesystem operations
+-
+-    def ftp_PWD(self, line):
+-        """Return the name of the current working directory to the client."""
+-        cwd = self.fs.get_cwd()
+-        self.respond('257 "%s" is the current directory.' % cwd)
+-
+-    def ftp_CWD(self, line):
+-        """Change the current working directory."""
+-        # check: a lot of FTP servers go back to root directory if no
+-        # arg is provided but this is not specified in RFC-959.
+-        # Search for official references about this behaviour.
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line,'cwd')
+-            self.try_as_current_user(self.fs.chdir, (datacr,), line=line, errno_resp={2: 530})
+-            cwd = self.fs.get_cwd()
+-            self.log('OK CWD "%s".' % cwd)
+-            self.respond('250 "%s" is the current directory.' % cwd)
+-        except FTPExceptionSent:
+-            return
+-        finally:
+-            self.fs.close_cr(datacr)
+-
+-    def ftp_CDUP(self, line):
+-        """Change into the parent directory."""
+-        # Note: RFC-959 says that code 200 is required but it also says
+-        # that CDUP uses the same codes as CWD.
+-        self.ftp_CWD('..')
+-
+-    def ftp_SIZE(self, line):
+-        """Return size of file in a format suitable for using with
+-        RESTart as defined in RFC-3659.
+-
+-        Implementation note:
+-        properly handling the SIZE command when TYPE ASCII is used would
+-        require to scan the entire file to perform the ASCII translation
+-        logic (file.read().replace(os.linesep, '\r\n')) and then
+-        calculating the len of such data which may be different than
+-        the actual size of the file on the server.  Considering that
+-        calculating such result could be very resource-intensive it
+-        could be easy for a malicious client to try a DoS attack, thus
+-        we do not perform the ASCII translation.
+-
+-        However, clients in general should not be resuming downloads in
+-        ASCII mode.  Resuming downloads in binary mode is the recommended
+-        way as specified in RFC-3659.
+-        """
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line, mode='file')
+-            size = self.try_as_current_user(self.fs.getsize,(datacr,), line=line)
+-        except FTPExceptionSent:
+-            self.fs.close_cr(datacr)
+-            return
+-        else:
+-            self.respond("213 %s" %size)
+-            self.log('OK SIZE "%s".' %line)
+-        self.fs.close_cr(datacr)
+-
+-    def ftp_MDTM(self, line):
+-        """Return last modification time of file to the client as an ISO
+-        3307 style timestamp (YYYYMMDDHHMMSS) as defined in RFC-3659.
+-        """
+-        datacr = None
+-
+-        try:
+-            if line.find('/', 1) < 0:
+-                # root or db, just return local
+-                lmt = None
+-            else:
+-                datacr = self.get_crdata2(line)
+-                if not datacr:
+-                    raise IOError(errno.ENOENT, "%s is not retrievable" %line)
+-
+-                lmt = self.try_as_current_user(self.fs.getmtime, (datacr,), line=line)
+-            lmt = time.strftime("%Y%m%d%H%M%S", time.localtime(lmt))
+-            self.respond("213 %s" %lmt)
+-            self.log('OK MDTM "%s".' %line)
+-        except FTPExceptionSent:
+-            return
+-        finally:
+-            self.fs.close_cr(datacr)
+-
+-    def ftp_MKD(self, line):
+-        """Create the specified directory."""
+-        try:
+-            datacr = self.get_crdata2(line, mode='create')
+-            self.try_as_current_user(self.fs.mkdir, (datacr, datacr[2]), line=line)
+-        except FTPExceptionSent:
+-            self.fs.close_cr(datacr)
+-            return
+-        else:
+-            self.log('OK MKD "%s".' %line)
+-            self.respond("257 Directory created.")
+-        self.fs.close_cr(datacr)
+-
+-    def ftp_RMD(self, line):
+-        """Remove the specified directory."""
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line, mode='delete')
+-            if not datacr[1]:
+-                msg = "Can't remove root directory."
+-                self.respond("553 %s" %msg)
+-                self.log('FAIL MKD "/". %s' %msg)
+-                self.fs.close_cr(datacr)
+-                return
+-            self.try_as_current_user(self.fs.rmdir, (datacr,), line=line)
+-            self.log('OK RMD "%s".' %line)
+-            self.respond("250 Directory removed.")
+-        except FTPExceptionSent:
+-            pass
+-        self.fs.close_cr(datacr)
+-
+-    def ftp_DELE(self, line):
+-        """Delete the specified file."""
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line, mode='delete')
+-            self.try_as_current_user(self.fs.remove, (datacr,), line=line)
+-            self.log('OK DELE "%s".' %line)
+-            self.respond("250 File removed.")
+-        except FTPExceptionSent:
+-            pass
+-        self.fs.close_cr(datacr)
+-
+-    def ftp_RNFR(self, line):
+-        """Rename the specified (only the source name is specified
+-        here, see RNTO command)"""
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line, mode='rfnr')
+-            if not datacr[1]:
+-                self.respond("550 No such file or directory.")
+-            elif not datacr[1]:
+-                self.respond("553 Can't rename the home directory.")
+-            else:
+-                self.fs.rnfr = datacr[1]
+-                self.respond("350 Ready for destination name.")
+-        except FTPExceptionSent:
+-            pass
+-        self.fs.close_cr(datacr)
+-
+-    def ftp_RNTO(self, line):
+-        """Rename file (destination name only, source is specified with
+-        RNFR).
+-        """
+-        if not self.fs.rnfr:
+-            self.respond("503 Bad sequence of commands: use RNFR first.")
+-            return
+-        datacr = None
+-        try:
+-            datacr = self.get_crdata2(line,'create')
+-            oldname = self.fs.rnfr.path
+-            if isinstance(oldname, (list, tuple)):
+-                oldname = '/'.join(oldname)
+-            self.try_as_current_user(self.fs.rename, (self.fs.rnfr, datacr), line=line)
+-            self.fs.rnfr = None
+-            self.log('OK RNFR/RNTO "%s ==> %s".' % \
+-                    (_to_unicode(oldname), _to_unicode(line)))
+-            self.respond("250 Renaming ok.")
+-        except FTPExceptionSent:
+-            pass
+-        finally:
+-            self.fs.rnfr = None
+-            self.fs.close_cr(datacr)
+-
+-
+-        # --- others
+-
+-    def ftp_TYPE(self, line):
+-        """Set current type data type to binary/ascii"""
+-        line = line.upper()
+-        if line in ("A", "AN", "A N"):
+-            self.respond("200 Type set to: ASCII.")
+-            self.current_type = 'a'
+-        elif line in ("I", "L8", "L 8"):
+-            self.respond("200 Type set to: Binary.")
+-            self.current_type = 'i'
+-        else:
+-            self.respond('504 Unsupported type "%s".' %line)
+-
+-    def ftp_STRU(self, line):
+-        """Set file structure (obsolete)."""
+-        # obsolete (backward compatibility with older ftp clients)
+-        if line in ('f','F'):
+-            self.respond('200 File transfer structure set to: F.')
+-        else:
+-            self.respond('504 Unimplemented STRU type.')
+-
+-    def ftp_MODE(self, line):
+-        """Set data transfer mode (obsolete)"""
+-        # obsolete (backward compatibility with older ftp clients)
+-        if line in ('s', 'S'):
+-            self.respond('200 Transfer mode set to: S')
+-        else:
+-            self.respond('504 Unimplemented MODE type.')
+-
+-    def ftp_STAT(self, line):
+-        """Return statistics about current ftp session. If an argument
+-        is provided return directory listing over command channel.
+-
+-        Implementation note:
+-
+-        RFC-959 do not explicitly mention globbing; this means that FTP
+-        servers are not required to support globbing in order to be
+-        compliant.  However, many FTP servers do support globbing as a
+-        measure of convenience for FTP clients and users.
+-
+-        In order to search for and match the given globbing expression,
+-        the code has to search (possibly) many directories, examine
+-        each contained filename, and build a list of matching files in
+-        memory.  Since this operation can be quite intensive, both CPU-
+-        and memory-wise, we limit the search to only one directory
+-        non-recursively, as LIST does.
+-        """
+-        # return STATus information about ftpd
+-        if not line:
+-            s = []
+-            s.append('Connected to: %s:%s' %self.socket.getsockname()[:2])
+-            if self.authenticated:
+-                s.append('Logged in as: %s' %self.username)
+-            else:
+-                if not self.username:
+-                    s.append("Waiting for username.")
+-                else:
+-                    s.append("Waiting for password.")
+-            if self.current_type == 'a':
+-                type = 'ASCII'
+-            else:
+-                type = 'Binary'
+-            s.append("TYPE: %s; STRUcture: File; MODE: Stream" %type)
+-            if self.data_server:
+-                s.append('Passive data channel waiting for connection.')
+-            elif self.data_channel:
+-                bytes_sent = self.data_channel.tot_bytes_sent
+-                bytes_recv = self.data_channel.tot_bytes_received
+-                s.append('Data connection open:')
+-                s.append('Total bytes sent: %s' %bytes_sent)
+-                s.append('Total bytes received: %s' %bytes_recv)
+-            else:
+-                s.append('Data connection closed.')
+-
+-            self.push('211-FTP server status:\r\n')
+-            self.push(''.join([' %s\r\n' %item for item in s]))
+-            self.respond('211 End of status.')
+-        # return directory LISTing over the command channel
+-        else:
+-            datacr = None
+-            try:
+-                datacr = self.fs.get_cr(line)
+-                iterator = self.try_as_current_user(self.fs.get_stat_dir, (line, datacr), line=line)
+-            except FTPExceptionSent:
+-                pass
+-            else:
+-                self.push('213-Status of "%s":\r\n' %self.fs.ftpnorm(line))
+-                self.push_with_producer(BufferedIteratorProducer(iterator))
+-                self.respond('213 End of status.')
+-            self.fs.close_cr(datacr)
+-
+-    def ftp_FEAT(self, line):
+-        """List all new features supported as defined in RFC-2398."""
+-        features = ['EPRT','EPSV','MDTM','MLSD','REST STREAM','SIZE','TVFS']
+-        s = ''
+-        for fact in self.available_facts:
+-            if fact in self.current_facts:
+-                s += fact + '*;'
+-            else:
+-                s += fact + ';'
+-        features.append('MLST ' + s)
+-        features.sort()
+-        self.push("211-Features supported:\r\n")
+-        self.push("".join([" %s\r\n" %x for x in features]))
+-        self.respond('211 End FEAT.')
+-
+-    def ftp_OPTS(self, line):
+-        """Specify options for FTP commands as specified in RFC-2389."""
+-        try:
+-            assert (not line.count(' ') > 1), 'Invalid number of arguments'
+-            if ' ' in line:
+-                cmd, arg = line.split(' ')
+-                assert (';' in arg), 'Invalid argument'
+-            else:
+-                cmd, arg = line, ''
+-            # actually the only command able to accept options is MLST
+-            assert (cmd.upper() == 'MLST'), 'Unsupported command "%s"' %cmd
+-        except AssertionError, err:
+-            self.respond('501 %s.' %err)
+-        else:
+-            facts = [x.lower() for x in arg.split(';')]
+-            self.current_facts = [x for x in facts if x in self.available_facts]
+-            f = ''.join([x + ';' for x in self.current_facts])
+-            self.respond('200 MLST OPTS ' + f)
+-
+-    def ftp_NOOP(self, line):
+-        """Do nothing."""
+-        self.respond("200 I successfully done nothin'.")
+-
+-    def ftp_SYST(self, line):
+-        """Return system type (always returns UNIX type: L8)."""
+-        # This command is used to find out the type of operating system
+-        # at the server.  The reply shall have as its first word one of
+-        # the system names listed in RFC-943.
+-        # Since that we always return a "/bin/ls -lA"-like output on
+-        # LIST we  prefer to respond as if we would on Unix in any case.
+-        self.respond("215 UNIX Type: L8")
+-
+-    def ftp_ALLO(self, line):
+-        """Allocate bytes for storage (obsolete)."""
+-        # obsolete (always respond with 202)
+-        self.respond("202 No storage allocation necessary.")
+-
+-    def ftp_HELP(self, line):
+-        """Return help text to the client."""
+-        if line:
+-            if line.upper() in proto_cmds:
+-                self.respond("214 %s" %proto_cmds[line.upper()])
+-            else:
+-                self.respond("501 Unrecognized command.")
+-        else:
+-            # provide a compact list of recognized commands
+-            def formatted_help():
+-                cmds = []
+-                keys = proto_cmds.keys()
+-                keys.sort()
+-                while keys:
+-                    elems = tuple((keys[0:8]))
+-                    cmds.append(' %-6s' * len(elems) %elems + '\r\n')
+-                    del keys[0:8]
+-                return ''.join(cmds)
+-
+-            self.push("214-The following commands are recognized:\r\n")
+-            self.push(formatted_help())
+-            self.respond("214 Help command successful.")
+-
+-
+-        # --- support for deprecated cmds
+-
+-    # RFC-1123 requires that the server treat XCUP, XCWD, XMKD, XPWD
+-    # and XRMD commands as synonyms for CDUP, CWD, MKD, LIST and RMD.
+-    # Such commands are obsoleted but some ftp clients (e.g. Windows
+-    # ftp.exe) still use them.
+-
+-    def ftp_XCUP(self, line):
+-        """Change to the parent directory. Synonym for CDUP. Deprecated."""
+-        self.ftp_CDUP(line)
+-
+-    def ftp_XCWD(self, line):
+-        """Change the current working directory. Synonym for CWD. Deprecated."""
+-        self.ftp_CWD(line)
+-
+-    def ftp_XMKD(self, line):
+-        """Create the specified directory. Synonym for MKD. Deprecated."""
+-        self.ftp_MKD(line)
+-
+-    def ftp_XPWD(self, line):
+-        """Return the current working directory. Synonym for PWD. Deprecated."""
+-        self.ftp_PWD(line)
+-
+-    def ftp_XRMD(self, line):
+-        """Remove the specified directory. Synonym for RMD. Deprecated."""
+-        self.ftp_RMD(line)
+-
+-
+-class FTPServer(asyncore.dispatcher):
+-    """This class is an asyncore.disptacher subclass.  It creates a FTP
+-    socket listening on <address>, dispatching the requests to a <handler>
+-    (typically FTPHandler class).
+-
+-    Depending on the type of address specified IPv4 or IPv6 connections
+-    (or both, depending from the underlying system) will be accepted.
+-
+-    All relevant session information is stored in class attributes
+-    described below.
+-    Overriding them is strongly recommended to avoid running out of
+-    file descriptors (DoS)!
+-
+-     - (int) max_cons:
+-        number of maximum simultaneous connections accepted (defaults
+-        to 0 == unlimited).
+-
+-     - (int) max_cons_per_ip:
+-        number of maximum connections accepted for the same IP address
+-        (defaults to 0 == unlimited).
+-    """
+-
+-    max_cons = 0
+-    max_cons_per_ip = 0
+-
+-    def __init__(self, address, handler):
+-        """Initiate the FTP server opening listening on address.
+-
+-         - (tuple) address: the host:port pair on which the command
+-           channel will listen.
+-
+-         - (classobj) handler: the handler class to use.
+-        """
+-        asyncore.dispatcher.__init__(self)
+-        self.handler = handler
+-        self.ip_map = []
+-        host, port = address
+-
+-        # AF_INET or AF_INET6 socket
+-        # Get the correct address family for our host (allows IPv6 addresses)
+-        try:
+-            info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
+-                                      socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
+-        except socket.gaierror:
+-            # Probably a DNS issue. Assume IPv4.
+-            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+-            self.set_reuse_addr()
+-            self.bind((host, port))
+-        else:
+-            for res in info:
+-                af, socktype, proto, canonname, sa = res
+-                try:
+-                    self.create_socket(af, socktype)
+-                    self.set_reuse_addr()
+-                    self.bind(sa)
+-                except socket.error, msg:
+-                    if self.socket:
+-                        self.socket.close()
+-                    self.socket = None
+-                    continue
+-                break
+-            if not self.socket:
+-                raise socket.error, msg
+-        self.listen(5)
+-
+-    def set_reuse_addr(self):
+-        # Overridden for convenience. Avoid to reuse address on Windows.
+-        if (os.name in ('nt', 'ce')) or (sys.platform == 'cygwin'):
+-            return
+-        asyncore.dispatcher.set_reuse_addr(self)
+-
+-    def serve_forever(self, **kwargs):
+-        """A wrap around asyncore.loop(); starts the asyncore polling
+-        loop.
+-
+-        The keyword arguments in kwargs are the same expected by
+-        asyncore.loop() function: timeout, use_poll, map and count.
+-        """
+-        if not 'count' in kwargs:
+-            log("Serving FTP on %s:%s" %self.socket.getsockname()[:2])
+-
+-        # backward compatibility for python < 2.4
+-        if not hasattr(self, '_map'):
+-            if not 'map' in kwargs:
+-                map = asyncore.socket_map
+-            else:
+-                map = kwargs['map']
+-            self._map = self.handler._map = map
+-
+-        try:
+-            # FIX #16, #26
+-            # use_poll specifies whether to use select module's poll()
+-            # with asyncore or whether to use asyncore's own poll()
+-            # method Python versions < 2.4 need use_poll set to False
+-            # This breaks on OS X systems if use_poll is set to True.
+-            # All systems seem to work fine with it set to False
+-            # (tested on Linux, Windows, and OS X platforms)
+-            if kwargs:
+-                asyncore.loop(**kwargs)
+-            else:
+-                asyncore.loop(timeout=1.0, use_poll=False)
+-        except (KeyboardInterrupt, SystemExit, asyncore.ExitNow):
+-            log("Shutting down FTPd.")
+-            self.close_all()
+-
+-    def handle_accept(self):
+-        """Called when remote client initiates a connection."""
+-        sock_obj, addr = self.accept()
+-        log("[]%s:%s Connected." %addr[:2])
+-
+-        handler = self.handler(sock_obj, self)
+-        ip = addr[0]
+-        self.ip_map.append(ip)
+-
+-        # For performance and security reasons we should always set a
+-        # limit for the number of file descriptors that socket_map
+-        # should contain.  When we're running out of such limit we'll
+-        # use the last available channel for sending a 421 response
+-        # to the client before disconnecting it.
+-        if self.max_cons:
+-            if len(self._map) > self.max_cons:
+-                handler.handle_max_cons()
+-                return
+-
+-        # accept only a limited number of connections from the same
+-        # source address.
+-        if self.max_cons_per_ip:
+-            if self.ip_map.count(ip) > self.max_cons_per_ip:
+-                handler.handle_max_cons_per_ip()
+-                return
+-
+-        handler.handle()
+-
+-    def writable(self):
+-        return 0
+-
+-    def handle_error(self):
+-        """Called to handle any uncaught exceptions."""
+-        try:
+-            raise
+-        except (KeyboardInterrupt, SystemExit, asyncore.ExitNow):
+-            raise
+-        logerror(traceback.format_exc())
+-        self.close()
+-
+-    def close_all(self, map=None, ignore_all=False):
+-        """Stop serving; close all existent connections disconnecting
+-        clients.
+-
+-         - (dict) map:
+-            A dictionary whose items are the channels to close.
+-            If map is omitted, the default asyncore.socket_map is used.
+-
+-         - (bool) ignore_all:
+-            having it set to False results in raising exception in case
+-            of unexpected errors.
+-
+-        Implementation note:
+-
+-        Instead of using the current asyncore.close_all() function
+-        which only close sockets, we iterate over all existent channels
+-        calling close() method for each one of them, avoiding memory
+-        leaks.
+-
+-        This is how asyncore.close_all() function should work in
+-        Python 2.6.
+-        """
+-        if map is None:
+-            map = self._map
+-        for x in map.values():
+-            try:
+-                x.close()
+-            except OSError, x:
+-                if x[0] == errno.EBADF:
+-                    pass
+-                elif not ignore_all:
+-                    raise
+-            except (asyncore.ExitNow, KeyboardInterrupt, SystemExit):
+-                raise
+-            except:
+-                if not ignore_all:
+-                    raise
+-        map.clear()
+-
+-
+-def test():
+-    # cmd line usage (provide a read-only anonymous ftp server):
+-    # python -m pyftpdlib.FTPServer
+-    authorizer = DummyAuthorizer()
+-    authorizer.add_anonymous(os.getcwd(), perm='elradfmw')
+-    FTPHandler.authorizer = authorizer
+-    address = ('', 8021)
+-    ftpd = FTPServer(address, FTPHandler)
+-    ftpd.serve_forever()
+-
+-if __name__ == '__main__':
+-    test()
+-
+-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+diff --git a/openerp/addons/document_ftp/wizard/ftp_browse.py b/openerp/addons/document_ftp/wizard/ftp_browse.py
+index b1120bc..ccc49d6 100644
+--- a/openerp/addons/document_ftp/wizard/ftp_browse.py
++++ b/openerp/addons/document_ftp/wizard/ftp_browse.py
+@@ -21,7 +21,7 @@
+
+ from osv import osv, fields
+ # from tools.translate import _
+-from .. import ftpserver
++import ftpserver
+
+ class document_ftp_browse(osv.osv_memory):
+     _name = 'document.ftp.browse'
+@@ -46,7 +46,7 @@ class document_ftp_browse(osv.osv_memory):
+                 if url[-1] == '/':
+                     url = url[:-1]
+             else:
+-                url = '%s:%s' %(ftpserver.HOST, ftpserver.PORT)
++                url = '%s:%s' % ftpserver.address
+             res['url'] = 'ftp://%s@%s'%(current_user.login, url)
+         return res
+
diff --git a/openerp.service b/openerp.service
new file mode 100644
index 0000000..5d2a3d7
--- /dev/null
+++ b/openerp.service
@@ -0,0 +1,16 @@
+[Unit]
+Description=OpenERP Server
+
+# Remove postgresql.service if db is running on another host.
+After=syslog.target network.target postgresql.service
+
+[Service]
+Type=simple
+User=openerp
+ExecStartPre=run-parts /etc/openerp/start.d
+ExecStart=/usr/bin/openerp-server \
+    -c /etc/openerp/openerp-server.conf \
+    --logfile=/var/log/openerp/openerp-server.log
+
+[Install]
+WantedBy=multi-user.target
diff --git a/openerp.spec b/openerp.spec
new file mode 100644
index 0000000..92e48af
--- /dev/null
+++ b/openerp.spec
@@ -0,0 +1,227 @@
+%global     oe_rel -20120505-233516
+            # tarball's source directory,
+%global     oe_src_dir openerp-%{version}%{oe_rel}
+
+Name:       openerp
+Version:    6.1
+Release:    1%{?dist}
+            # See LICENSING
+License:    AGPLv3 and GPLv3 and BSD and LGPLv2+
+Group:      System Environment/Daemons
+Summary:    Business Applications Server
+URL:        http://www.openerp.com
+BuildArch:  noarch
+#Source0:    http://nightly.openerp.com/6.1/releases/openerp-%{version}%{?oe_rel}.tar.gz
+Source0:    http://nightly.openerp.com/6.1/nightly/src/openerp-%{version}%{oe_rel}.tar.gz
+Source1:    openerp.service
+Source2:    openerp-gen-cert
+Source3:    README.fedora
+Source4:    LICENSING
+            # https://bugs.launchpad.net/bugs/993408
+Patch0:     openerp-fsf-fix.patch
+            # Patch is not usable upstream.
+Patch1:     openerp-unbundle-pyftpdlib.patch
+            # Relicensed by copyright owner, see
+            # https://bugzilla.redhat.com/show_bug.cgi?id=693425#c57
+Patch10:    openerp-server-relicense-dict_tools-to-LGPL2.1.patch
+
+Requires:   ghostscript
+Requires:   postgresql-python
+Requires:   pychart
+Requires:   pydot
+Requires:   pyftpdlib
+Requires:   pyparsing
+Requires:   pywebdav
+Requires:   python-babel
+Requires:   python-dateutil
+# See BZ 817268
+# Requires:   python-faces
+Requires:   python-feedparser
+Requires:   python-gdata
+Requires:   python-imaging
+Requires:   python-ldap
+Requires:   python-lxml
+Requires:   python-mako
+Requires:   python-openid
+Requires:   python-psycopg2
+Requires:   python-reportlab
+Requires:   python-simplejson
+# https://fedorahosted.org/fpc/ticket/171
+#Requires:  python-trml2pdf
+Requires:   python-vatnumber
+Requires:   python-vobject
+Requires:   python-werkzeug
+Requires:   python-xlwt
+Requires:   python-ZSI
+Requires:   pytz
+Requires:   PyXML
+Requires:   PyYAML
+
+BuildRequires:  desktop-file-utils
+BuildRequires:  libxslt-python
+BuildRequires:  pygtk2-devel
+BuildRequires:  python
+BuildRequires:  python-babel
+BuildRequires:  python-setuptools
+BuildRequires:  python2-devel
+BuildRequires:  systemd-units
+
+Requires(pre):    shadow-utils
+Requires(post):   systemd-units
+Requires(preun):  systemd-units
+Requires(postun): systemd-units
+
+
+%description
+Server package for OpenERP.
+
+OpenERP is a free Enterprise Resource Planning and Customer Relationship
+Management software. It is mainly developed to meet changing needs.
+
+The main functional features are: CRM & SRM, analytic and financial accounting,
+double-entry stock management, sales and purchases management, tasks automation,
+help desk, marketing campaign, ... and vertical modules for very specific
+businesses.
+
+Technical features include a distributed server, flexible work-flows, an object
+database, dynamic GUIs, custom reports, NET-RPC and XML-RPC interfaces, ...
+
+For more information, please visit: http://www.openerp.com
+
+This server package contains the core (server) of OpenERP system and all
+additions of the official distribution. You may need the GTK client to connect
+to this server, or the web-client, which serves to HTML browsers. You can
+also find more additions (aka. modules) for this ERP system in:
+http://www.openerp.com/ or  http://apps.openerp.com/
+
+
+%prep
+%setup -q -n %{oe_src_dir}
+%patch0 -p1
+%patch1 -p1
+%patch10 -p1
+
+# https://bugs.launchpad.net/bugs/993414
+find . -name \*.py -a -perm 644 | \
+    xargs sed -i -e '\;/usr/bin/env;d' -e '\;/usr/bin/python;d'
+find . -name \*.html -o -name \*yml -o -name \*.js -o -name \*.po  \
+    -o -name \*.css -o -iname readme* -o -name \*.csv \
+    -o -name account_asset_change_duration.py \
+    -o -name base_quality_interrogation.py |
+        xargs chmod 644
+chmod 644 $( find openerp/addons/account_asset -name \*.py )
+chmod 644       openerp/addons/l10n_ch/test/test*.v11
+
+find . -name \*.html | xargs sed -i 's/\r//'
+sed -i 's/\r//' openerp/addons/l10n_ch/test/test*.v11
+sed -i 's/\r//' openerp/addons/account_asset/security/ir.model.access.csv
+
+find . -name .hg_* | xargs rm -f
+rm -f openerp/addons/.bzrignore
+
+# Empty and of no use.
+rm openerp/addons/base_report_designer/openerp_sxw2rml/office.dtd
+
+# Prebuilt binaries, bundled libs and foreign packaging
+rm -rf win32 debian setup.nsi
+rm -rf bin/pychart
+rm -rf openerp/addons/outlook/plugin/openerp-outlook-addin.exe \
+       openerp/addons/thunderbird openerp/addons/plugin_thunderbird
+
+# Client-side plugin, until we can build it under Fedora.
+rm -rf openerp/addons/outlook/plugin/
+
+# Wiki contains other licenses and bundled modules, skipped
+# for now. Web modules are anyway better packaged in the web-client.
+rm -rf openerp/addons/wiki/web openerp/addons/wiki/static\
+       openerp/addons/web_graph openerp/addons/web_calendar
+
+
+%build
+NO_INSTALL_REQS=1 python ./setup.py --quiet build
+
+
+%install
+python ./setup.py --quiet install --root=%{buildroot}
+sed -i "s|%{buildroot}||" %{buildroot}%{_bindir}/openerp-server
+rm  %{buildroot}/usr/openerp/.apidoc
+rm -r  %{buildroot}%{python_sitelib}/openerp
+mv %{buildroot}/usr/openerp %{buildroot}%{python_sitelib}
+rm -r %{buildroot}/usr/localedata
+
+install -m 644 -D install/openerp-server.conf  \
+    %{buildroot}%{_sysconfdir}/openerp/openerp-server.conf
+install -d %{buildroot}%{_sysconfdir}/openerp/start.d
+install -d %{buildroot}%{_sysconfdir}/openerp/stop.d
+
+install -D -m 755 %SOURCE2 %{buildroot}%{_sbindir}/openerp-gen-cert
+install -D -m 644 %SOURCE1 %{buildroot}%{_unitdir}/openerp.service
+install -m 644  %{SOURCE3} %{SOURCE4} .
+
+install -m 644 openerp/import_xml.rng %{buildroot}%{python_sitelib}/openerp
+install -d %{buildroot}%{python_sitelib}/openerp/addons/base/security
+install -m 644 openerp/addons/base/security/* \
+    %{buildroot}%{python_sitelib}/openerp/addons/base/security
+
+install -d %{buildroot}/%{_datadir}/openerp/pixmaps
+install -m 644 -D install/*.png  %{buildroot}/%{_datadir}/openerp/pixmaps
+
+install -D -m 644 install/openerp-server.1 \
+    %{buildroot}/%{_mandir}/man1/openerp-server.1
+install -D -m 644 install/openerp_serverrc.5 \
+    %{buildroot}/%{_mandir}/man5/openerp-serverrc.5
+
+install -d %{buildroot}%{_localstatedir}/spool/openerp
+install -d %{buildroot}%{_localstatedir}/run/openerp
+
+
+%files
+%doc LICENSE README  README.fedora LICENSING
+%{_bindir}/*
+%{_sbindir}/*
+%{_unitdir}/*
+%{_mandir}/man1/*
+%{_mandir}/man5/*
+%{_datadir}/openerp
+%{python_sitelib}/openerp
+%{python_sitelib}/openerp-%{version}*-py%{python_version}.egg-info
+%attr(0755,openerp,openerp) %{_localstatedir}/run/openerp
+
+%attr(0755,root,openerp) %dir %{_sysconfdir}/openerp
+%dir %{_sysconfdir}/openerp/start.d
+%dir %{_sysconfdir}/openerp/stop.d
+%attr(0660,root,openerp)  %config(noreplace) %{_sysconfdir}/openerp/openerp-server.conf
+
+
+%pre
+getent group openerp >/dev/null || groupadd -r openerp
+getent passwd openerp >/dev/null || \
+    useradd -r -g openerp -d /var/run/openerp -s /sbin/nologin \
+    -c "OpenERP Server" openerp
+usermod -d /var/run/openerp openerp >/dev/null 2>&1 || :
+
+%post
+if [ $1 -eq 1 ] ; then
+    # Initial installation
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+fi
+
+%preun
+if [ $1 -eq 0 ] ; then
+    # Package removal, not upgrade
+    /bin/systemctl --no-reload disable \
+        openerp.service > /dev/null 2>&1 || :
+    /bin/systemctl stop openerp.service > /dev/null 2>&1 || :
+fi
+
+%postun
+/bin/systemctl daemon-reload >/dev/null 2>&1 || :
+if [ $1 -ge 1 ] ; then
+    # Package upgrade, not uninstall
+    /bin/systemctl try-restart openrep-server.service >/dev/null 2>&1 || :
+fi
+
+
+%changelog
+* Sat Apr 26 2012 Alec Leamas <leamas at nowhere.com> 6.1-1
+  - Initial package, based on 693425
diff --git a/sources b/sources
index e69de29..d1e9806 100644
--- a/sources
+++ b/sources
@@ -0,0 +1 @@
+572987da13d71942c252339b38d5d575  openerp-6.1-20120505-233516.tar.gz


More information about the scm-commits mailing list