merge fixes from stable
authorNicolas Chauvat <nicolas.chauvat@logilab.fr>
Mon, 03 Dec 2012 00:03:03 +0100
changeset 8609 112a04c0473d
parent 8596 bd4f5052a532 (current diff)
parent 8608 1a87ccdf12a3 (diff)
child 8620 61c4bdd70dd8
merge fixes from stable
__pkginfo__.py
_exceptions.py
i18n/de.po
i18n/en.po
i18n/es.po
i18n/fr.po
web/application.py
web/request.py
web/views/basecontrollers.py
--- a/.hgtags	Fri Nov 16 11:53:17 2012 +0100
+++ b/.hgtags	Mon Dec 03 00:03:03 2012 +0100
@@ -268,3 +268,5 @@
 70cb36c826df86de465f9b69647cef7096dcf12c cubicweb-debian-version-3.15.4-1
 b0e086f451b7213fe63141438edc91a6b2da9072 cubicweb-version-3.15.5
 19e115ae5442c427c0adbda8b9d8ceccf2931b5c cubicweb-debian-version-3.15.5-1
+0163bd9f4880d5531e433c1500f9298a0adef6b7 cubicweb-version-3.15.6
+b05e156b8fe720494293b08e7060ba43ad57a5c8 cubicweb-debian-version-3.15.6-1
--- a/__pkginfo__.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/__pkginfo__.py	Mon Dec 03 00:03:03 2012 +0100
@@ -22,7 +22,7 @@
 
 modname = distname = "cubicweb"
 
-numversion = (3, 15, 5)
+numversion = (3, 15, 6)
 version = '.'.join(str(num) for num in numversion)
 
 description = "a repository of entities / relations for knowledge management"
--- a/_exceptions.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/_exceptions.py	Mon Dec 03 00:03:03 2012 +0100
@@ -103,6 +103,10 @@
         except Exception, ex:
             return str(ex)
 
+class Forbidden(SecurityError):
+    """raised when a user tries to perform a forbidden action
+    """
+
 # source exceptions ###########################################################
 
 class EidNotInSource(SourceException):
--- a/debian/changelog	Fri Nov 16 11:53:17 2012 +0100
+++ b/debian/changelog	Mon Dec 03 00:03:03 2012 +0100
@@ -1,3 +1,9 @@
+cubicweb (3.15.6-1) squeeze; urgency=low
+
+  * New upstream release
+
+ -- David Douard <david.douard@logilab.fr>  Fri, 30 Nov 2012 19:25:20 +0100
+
 cubicweb (3.15.5-1) unstable; urgency=low
 
   * New upstream release
--- a/doc/book/en/admin/cubicweb-ctl.rst	Fri Nov 16 11:53:17 2012 +0100
+++ b/doc/book/en/admin/cubicweb-ctl.rst	Mon Dec 03 00:03:03 2012 +0100
@@ -44,7 +44,7 @@
 Create an instance
 -------------------
 
-You must ensure `~/cubicweb.d/` exists prior to this. On windows, the
+You must ensure `~/etc/cubicweb.d/` exists prior to this. On windows, the
 '~' part will probably expand to 'Documents and Settings/user'.
 
 To create an instance from an existing cube, execute the following
--- a/doc/book/en/admin/setup.rst	Fri Nov 16 11:53:17 2012 +0100
+++ b/doc/book/en/admin/setup.rst	Mon Dec 03 00:03:03 2012 +0100
@@ -51,7 +51,7 @@
 Depending on the distribution you are using, add the appropriate line to your
 `list of sources` (for example by editing ``/etc/apt/sources.list``).
 
-For Debian Squeeze (stable)::
+For Debian 6.0 Squeeze (stable)::
 
   deb http://download.logilab.org/production/ squeeze/
 
@@ -59,13 +59,9 @@
 
   deb http://download.logilab.org/production/ sid/
 
-For Ubuntu Lucid (Long Term Support) and newer::
-
-  deb http://download.logilab.org/production/ lucid/
+For Ubuntu 12.04 Precise Pangolin (Long Term Support) and newer::
 
-Note that for Ubuntu Maverick and newer, you shall use the `lucid`
-repository and install the ``libgecode19`` package from `lucid
-universe <http://packages.ubuntu.com/lucid/libgecode19>`_.
+  deb http://download.logilab.org/production/ precise/
 
 The repositories are signed with the `Logilab's gnupg key`_. You can download
 and register the key to avoid warnings::
--- a/i18n/de.po	Fri Nov 16 11:53:17 2012 +0100
+++ b/i18n/de.po	Mon Dec 03 00:03:03 2012 +0100
@@ -869,6 +869,11 @@
 msgid "This WorkflowTransition"
 msgstr "Dieser Workflow-Übergang"
 
+msgid ""
+"This action is forbidden. If you think it should be allowed, please contact "
+"the site administrator."
+msgstr ""
+
 msgid "This entity type permissions:"
 msgstr "Berechtigungen für diesen Entitätstyp"
 
--- a/i18n/en.po	Fri Nov 16 11:53:17 2012 +0100
+++ b/i18n/en.po	Mon Dec 03 00:03:03 2012 +0100
@@ -845,6 +845,11 @@
 msgid "This WorkflowTransition"
 msgstr "This workflow-transition"
 
+msgid ""
+"This action is forbidden. If you think it should be allowed, please contact "
+"the site administrator."
+msgstr ""
+
 msgid "This entity type permissions:"
 msgstr ""
 
--- a/i18n/es.po	Fri Nov 16 11:53:17 2012 +0100
+++ b/i18n/es.po	Mon Dec 03 00:03:03 2012 +0100
@@ -870,6 +870,11 @@
 msgid "This WorkflowTransition"
 msgstr "Esta transición de Workflow"
 
+msgid ""
+"This action is forbidden. If you think it should be allowed, please contact "
+"the site administrator."
+msgstr ""
+
 msgid "This entity type permissions:"
 msgstr "Permisos para este tipo de entidad:"
 
--- a/i18n/fr.po	Fri Nov 16 11:53:17 2012 +0100
+++ b/i18n/fr.po	Mon Dec 03 00:03:03 2012 +0100
@@ -871,6 +871,13 @@
 msgid "This WorkflowTransition"
 msgstr "Cette transition workflow"
 
+msgid ""
+"This action is forbidden. If you think it should be allowed, please contact "
+"the site administrator."
+msgstr ""
+"Cette action est interdite. Si toutefois vous pensez qu'elle devrait être "
+"autorisée, veuillez contacter l'administrateur du site."
+
 msgid "This entity type permissions:"
 msgstr "Permissions pour ce type d'entité"
 
--- a/mail.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/mail.py	Mon Dec 03 00:03:03 2012 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -15,7 +15,7 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""Common utilies to format / semd emails."""
+"""Common utilies to format / send emails."""
 
 __docformat__ = "restructuredtext en"
 
--- a/web/application.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/web/application.py	Mon Dec 03 00:03:03 2012 +0100
@@ -34,7 +34,8 @@
 
 from cubicweb import set_log_methods, cwvreg
 from cubicweb import (
-    ValidationError, Unauthorized, AuthenticationError, NoSelectableObject,
+    ValidationError, Unauthorized, Forbidden,
+    AuthenticationError, NoSelectableObject,
     BadConnectionId, CW_EVENT_MANAGER)
 from cubicweb.dbapi import DBAPISession, anonymous_session
 from cubicweb.web import LOGGER, component
@@ -469,6 +470,11 @@
                                        'If you think you should, please contact the site administrator.')
             req.status_out = httplib.UNAUTHORIZED
             result = self.error_handler(req, ex, tb=False)
+        except Forbidden, ex:
+            req.data['errmsg'] = req._('This action is forbidden. '
+                                       'If you think it should be allowed, please contact the site administrator.')
+            req.status_out = httplib.FORBIDDEN
+            result = self.error_handler(req, ex, tb=False)
         except (BadRQLQuery, RequestError), ex:
             result = self.error_handler(req, ex, tb=False)
         ### pass through exception
--- a/web/facet.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/web/facet.py	Mon Dec 03 00:03:03 2012 +0100
@@ -1388,11 +1388,16 @@
             return
         if isinstance(value, list):
             value = reduce(lambda x, y: int(x) | int(y), value)
+        else:
+            value = int(value)
         attr_var = self.select.make_variable()
         self.select.add_relation(self.filtered_variable, self.rtype, attr_var)
         comp = nodes.Comparison('=', nodes.Constant(value, 'Int'))
-        comp.append(nodes.MathExpression('&', nodes.variable_ref(attr_var),
-                                         nodes.Constant(value, 'Int')))
+        if value == 0:
+            comp.append(nodes.variable_ref(attr_var))
+        else:
+            comp.append(nodes.MathExpression('&', nodes.variable_ref(attr_var),
+                                             nodes.Constant(value, 'Int')))
         having = self.select.having
         if having:
             self.select.replace(having[0], nodes.And(having[0], comp))
@@ -1402,7 +1407,7 @@
     def rset_vocabulary(self, rset):
         mask = reduce(lambda x, y: x | (y[0] or 0), rset, 0)
         return sorted([(self._cw._(label), val) for label, val in self.choices
-                       if val & mask])
+                       if not val or val & mask])
 
     def possible_values(self):
         return [unicode(val) for label, val in self.vocabulary()]
--- a/web/request.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/web/request.py	Mon Dec 03 00:03:03 2012 +0100
@@ -22,6 +22,7 @@
 import time
 import random
 import base64
+import urllib
 from hashlib import sha1 # pylint: disable=E0611
 from Cookie import SimpleCookie
 from calendar import timegm
@@ -38,7 +39,6 @@
 from logilab.mtconverter import xml_escape
 
 from cubicweb.dbapi import DBAPIRequest
-from cubicweb.mail import header
 from cubicweb.uilib import remove_html_tags, js
 from cubicweb.utils import SizeConstrainedList, HTMLHead, make_uid
 from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE_NOEXT
@@ -608,10 +608,17 @@
             content_type += ';charset=' + (encoding or self.encoding)
         self.set_header('content-type', content_type)
         if filename:
-            if isinstance(filename, unicode):
-                filename = header(filename).encode()
-            self.set_header('content-disposition', 'inline; filename=%s'
-                            % filename)
+            header = ['attachment']
+            try:
+                filename = filename.encode('ascii')
+                header.append('filename=' + filename)
+            except UnicodeEncodeError:
+                # fallback filename for very old browser
+                header.append('filename=' + filename.encode('ascii', 'ignore'))
+                # encoded filename according RFC5987
+                filename = urllib.quote(filename.encode('utf-8'), '')
+                header.append("filename*=utf-8''" + filename)
+            self.set_header('content-disposition', ';'.join(header))
 
     # high level methods for HTML headers management ##########################
 
--- a/web/test/unittest_facet.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/web/test/unittest_facet.py	Mon Dec 03 00:03:03 2012 +0100
@@ -221,6 +221,25 @@
         self.assertEqual(f.select.as_string(),
                           "DISTINCT Any  WHERE X ordernum XO, X is CWAttribute, X ordernum C HAVING 3 = (C & 3)")
 
+    def test_bitfield_0_value(self):
+        req, rset, rqlst, filtered_variable = self.prepare_rqlst(
+            'CWAttribute X WHERE X ordernum XO',
+            expected_baserql='Any X WHERE X ordernum XO, X is CWAttribute',
+            expected_preparedrql='DISTINCT Any  WHERE X ordernum XO, X is CWAttribute')
+        f = facet.BitFieldFacet(req, rset=rset,
+                                select=rqlst.children[0],
+                                filtered_variable=filtered_variable)
+        f.choices = [('zero', 0,), ('un', 1,), ('deux', 2,)]
+        f.rtype = 'ordernum'
+        self.assertEqual(f.vocabulary(),
+                          [(u'deux', 2), (u'un', 1), (u'zero', 0)])
+        self.assertEqual(f.possible_values(),
+                          ['2', '1', '0'])
+        req.form[f.__regid__] = '0'
+        f.add_rql_restrictions()
+        self.assertEqual(f.select.as_string(),
+                          "DISTINCT Any  WHERE X ordernum XO, X is CWAttribute, X ordernum C HAVING 0 = C")
+
     def test_rql_path_eid(self):
         req, rset, rqlst, filtered_variable = self.prepare_rqlst()
         class RPF(facet.RQLPathFacet):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/unittest_idownloadable.py	Mon Dec 03 00:03:03 2012 +0100
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+# copyright 2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb 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, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+from __future__ import with_statement
+
+from functools import partial
+
+from logilab.common.testlib import unittest_main
+
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb import view
+from cubicweb.predicates import is_instance
+
+
+class IDownloadableTC(CubicWebTC):
+
+    def setUp(self):
+        super(IDownloadableTC, self).setUp()
+        class IDownloadableUser(view.EntityAdapter):
+            __regid__ = 'IDownloadable'
+            __select__ = is_instance('CWUser')
+
+            def download_content_type(self):
+                """return MIME type of the downloadable content"""
+                return 'text/plain'
+
+            def download_encoding(self):
+                """return encoding of the downloadable content"""
+                return 'ascii'
+
+            def download_file_name(self):
+                """return file name of the downloadable content"""
+                return  self.entity.name() + '.txt'
+
+            def download_data(self):
+                return 'Babar is not dead!'
+        self.vreg.register(IDownloadableUser)
+        self.addCleanup(partial(self.vreg.unregister, IDownloadableUser))
+
+    def test_header_simple_case(self):
+        req = self.request()
+        req.form['vid'] = 'download'
+        req.form['eid'] = str(req.user.eid)
+        data = self.ctrl_publish(req,'view')
+        get = req.headers_out.getRawHeaders
+        self.assertEqual(['attachment;filename=admin.txt'],
+                         get('content-disposition'))
+        self.assertEqual(['text/plain;charset=ascii'],
+                         get('content-type'))
+        self.assertEqual('Babar is not dead!', data)
+
+    def test_header_unicode_filename(self):
+        req = self.request()
+        self.create_user(req, login=u'cécilia', password='babar')
+        self.commit()
+        with self.login(u'cécilia', password='babar'):
+            req = self.request()
+            req.form['vid'] = 'download'
+            req.form['eid'] = str(req.user.eid)
+            self.ctrl_publish(req,'view')
+            get = req.headers_out.getRawHeaders
+            self.assertEqual(["attachment;filename=ccilia.txt;filename*=utf-8''c%C3%A9cilia.txt"],
+                             get('content-disposition'))
+
+    def test_header_unicode_long_filename(self):
+        req = self.request()
+        name = u'Bèrte_hô_grand_nôm_ça_va_totallement_déborder_de_la_limite_là'
+        self.create_user(req, login=name, password='babar')
+        self.commit()
+        with self.login(name, password='babar'):
+            req = self.request()
+            req.form['vid'] = 'download'
+            req.form['eid'] = str(req.user.eid)
+            self.ctrl_publish(req,'view')
+            get = req.headers_out.getRawHeaders
+            self.assertEqual(["attachment;filename=Brte_h_grand_nm_a_va_totallement_dborder_de_la_limite_l.txt;filename*=utf-8''B%C3%A8rte_h%C3%B4_grand_n%C3%B4m_%C3%A7a_va_totallement_d%C3%A9border_de_la_limite_l%C3%A0.txt"],
+                             get('content-disposition'))
+
+if __name__ == '__main__':
+    unittest_main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/unittest_views_errorform.py	Mon Dec 03 00:03:03 2012 +0100
@@ -0,0 +1,101 @@
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb 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, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import with_statement
+
+from logilab.common.testlib import unittest_main
+from logilab.mtconverter import html_unescape
+
+from cubicweb import Forbidden, ValidationError
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.utils import json
+from cubicweb.view import StartupView, TRANSITIONAL_DOCTYPE_NOEXT
+from cubicweb.web import Redirect
+from cubicweb.web.htmlwidgets import TableWidget
+from cubicweb.web.views import vid_from_rset
+
+import re
+import hmac
+
+class ErrorViewTC(CubicWebTC):
+    def setUp(self):
+        super(ErrorViewTC, self).setUp()
+        self.req = self.request()
+        self.vreg.config['submit-mail'] = "test@logilab.fr"
+        self.vreg.config['print-traceback'] = "yes"
+
+    def test_error_generation(self):
+        """
+        tests
+        """
+
+        class MyWrongView(StartupView):
+            __regid__ = 'my-view'
+            def call(self):
+                raise ValueError('This is wrong')
+
+        with self.temporary_appobjects(MyWrongView):
+            try:
+                self.view('my-view')
+            except Exception, e:
+                import sys
+                self.req.data['excinfo'] = sys.exc_info()
+                self.req.data['ex'] = e
+                html = self.view('error', req=self.req)
+                self.failUnless(re.search(r'^<input name="__signature" type="hidden" value="[0-9a-f]{32}" />$',
+                                          html.source, re.M))
+
+
+    def test_error_submit_nosig(self):
+        """
+        tests that the reportbug controller refuses submission if
+        there is not content signature
+        """
+
+        self.req.form = {'description': u'toto',
+                         }
+        with self.assertRaises(Forbidden) as cm:
+            self.ctrl_publish(self.req, 'reportbug')
+
+    def test_error_submit_wrongsig(self):
+        """
+        tests that the reportbug controller refuses submission if the
+        content signature is invalid
+        """
+
+        self.req.form = {'__signature': 'X',
+                         'description': u'toto',
+                         }
+        with self.assertRaises(Forbidden) as cm:
+            self.ctrl_publish(self.req, 'reportbug')
+
+    def test_error_submit_ok(self):
+        """
+        tests that the reportbug controller accept the email submission if the
+        content signature is valid
+        """
+
+        sign = self.vreg.config.sign_text('toto')
+        self.req.form = {'__signature': sign,
+                         'description': u'toto',
+                         }
+        with self.assertRaises(Redirect) as cm:
+            self.ctrl_publish(self.req, 'reportbug')
+
+if __name__ == '__main__':
+    unittest_main()
--- a/web/test/unittest_views_json.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/web/test/unittest_views_json.py	Mon Dec 03 00:03:03 2012 +0100
@@ -1,8 +1,34 @@
+# -*- coding: utf-8 -*-
+# copyright 2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb 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, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 from cubicweb.devtools.testlib import CubicWebTC
 
 from cubicweb.utils import json
 
+from cubicweb.web.application import anonymized_request
+
 class JsonViewsTC(CubicWebTC):
+    anonymize = True
+    res_jsonp_data = '[["guests", 1]]'
+
+    def setUp(self):
+        super(JsonViewsTC, self).setUp()
+        self.config.global_set_option('anonymize-jsonp-queries', self.anonymize)
 
     def test_json_rsetexport(self):
         req = self.request()
@@ -19,7 +45,7 @@
         data = self.ctrl_publish(req, ctrl='jsonp')
         self.assertEqual(req.headers_out.getRawHeaders('content-type'), ['application/javascript'])
         # because jsonp anonymizes data, only 'guests' group should be found
-        self.assertEqual(data, 'foo([["guests", 1]])')
+        self.assertEqual(data, 'foo(%s)' % self.res_jsonp_data)
 
     def test_json_rsetexport_with_jsonp_and_bad_vid(self):
         req = self.request()
@@ -30,7 +56,7 @@
         data = self.ctrl_publish(req, ctrl='jsonp')
         self.assertEqual(req.headers_out.getRawHeaders('content-type'), ['application/javascript'])
         # result should be plain json, not the table view
-        self.assertEqual(data, 'foo([["guests", 1]])')
+        self.assertEqual(data, 'foo(%s)' % self.res_jsonp_data)
 
     def test_json_ersetexport(self):
         req = self.request()
@@ -41,6 +67,10 @@
         self.assertEqual(data[1]['name'], 'managers')
 
 
+class NotAnonymousJsonViewsTC(JsonViewsTC):
+    anonymize = False
+    res_jsonp_data = '[["guests", 1], ["managers", 1]]'
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/web/views/basecontrollers.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/web/views/basecontrollers.py	Mon Dec 03 00:03:03 2012 +0100
@@ -27,7 +27,8 @@
 from logilab.common.deprecation import deprecated
 
 from cubicweb import (NoSelectableObject, ObjectNotFound, ValidationError,
-                      AuthenticationError, typed_eid, UndoTransactionException)
+                      AuthenticationError, typed_eid, UndoTransactionException,
+                      Forbidden)
 from cubicweb.utils import json_dumps
 from cubicweb.predicates import (authenticated_user, anonymous_user,
                                 match_form_params)
@@ -277,9 +278,15 @@
 
     def publish(self, rset=None):
         req = self._cw
+        desc = req.form['description']
+        # The description is generated and signed by cubicweb itself, check
+        # description's signature so we don't want to send spam here
+        sign = req.form.get('__signature', '')
+        if not (sign and req.vreg.config.check_text_sign(desc, sign)):
+            raise Forbidden('Invalid content')
         self.sendmail(req.vreg.config['submit-mail'],
                       req._('%s error report') % req.vreg.config.appid,
-                      req.form['description'])
+                      desc)
         raise Redirect(req.build_url(__message=req._('bug report sent')))
 
 
--- a/web/views/json.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/web/views/json.py	Mon Dec 03 00:03:03 2012 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -50,14 +50,20 @@
                 self._cw.form['vid'] = 'jsonexport'
         else: # if no vid is specified, use jsonexport
             self._cw.form['vid'] = 'jsonexport'
-        with anonymized_request(self._cw):
-            json_data = super(JsonpController, self).publish(rset)
-            if 'callback' in self._cw.form: # jsonp
-                json_padding = self._cw.form['callback']
-                # use ``application/javascript`` is ``callback`` parameter is
-                # provided, let ``application/json`` otherwise
-                self._cw.set_content_type('application/javascript')
-                json_data = '%s(%s)' % (json_padding, json_data)
+        if self._cw.vreg.config['anonymize-jsonp-queries']:
+            with anonymized_request(self._cw):
+                return self._get_json_data(rset)
+        else:
+            return self._get_json_data(rset)
+
+    def _get_json_data(self, rset):
+        json_data = super(JsonpController, self).publish(rset)
+        if 'callback' in self._cw.form: # jsonp
+            json_padding = self._cw.form['callback']
+            # use ``application/javascript`` is ``callback`` parameter is
+            # provided, let ``application/json`` otherwise
+            self._cw.set_content_type('application/javascript')
+            json_data = '%s(%s)' % (json_padding, json_data)
         return json_data
 
 
--- a/web/views/management.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/web/views/management.py	Mon Dec 03 00:03:03 2012 +0100
@@ -20,6 +20,7 @@
 __docformat__ = "restructuredtext en"
 _ = unicode
 
+
 from logilab.mtconverter import xml_escape
 from logilab.common.registry import yes
 
@@ -148,6 +149,8 @@
             form.add_hidden('description', binfo,
                             # we must use a text area to keep line breaks
                             widget=wdgs.TextArea({'class': 'hidden'}))
+            # add a signature so one can't send arbitrary text
+            form.add_hidden('__signature', req.vreg.config.sign_text(binfo))
             form.add_hidden('__bugreporting', '1')
             form.form_buttons = [wdgs.SubmitButton(MAIL_SUBMIT_MSGID)]
             form.action = req.build_url('reportbug')
@@ -171,7 +174,7 @@
     """A textual stats output for monitoring tools such as munin """
 
     __regid__ = 'processinfo'
-    content_type = 'text/txt'
+    content_type = 'text/plain'
     templatable = False
     __select__ = none_rset() & match_user_groups('users', 'managers')
 
--- a/web/webconfig.py	Fri Nov 16 11:53:17 2012 +0100
+++ b/web/webconfig.py	Mon Dec 03 00:03:03 2012 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -21,10 +21,12 @@
 _ = unicode
 
 import os
+import hmac
+from uuid import uuid4
 from os.path import join, exists, split, isdir
 from warnings import warn
 
-from logilab.common.decorators import cached
+from logilab.common.decorators import cached, cachedproperty
 from logilab.common.deprecation import deprecated
 
 from cubicweb import ConfigurationError
@@ -219,6 +221,12 @@
           'help': 'use modconcat-like URLS to concat and serve JS / CSS files',
           'group': 'web', 'level': 2,
           }),
+        ('anonymize-jsonp-queries',
+         {'type': 'yn',
+          'default': True,
+          'help': 'anonymize the connection before executing any jsonp query.',
+          'group': 'web', 'level': 1
+          }),
         ))
 
     def fckeditor_installed(self):
@@ -251,7 +259,7 @@
 
     def anonymous_user(self):
         """return a login and password to use for anonymous users.
-        
+
         None may be returned for both if anonymous connection is not
         allowed or if an empty login is used in configuration
         """
@@ -266,6 +274,25 @@
             raise ConfigurationError("anonymous information should only contains ascii")
         return user, passwd
 
+    @cachedproperty
+    def _instance_salt(self):
+        """This random key/salt is used to sign content to be sent back by
+        browsers, eg. in the error report form.
+        """
+        return str(uuid4())
+
+    def sign_text(self, text):
+        """sign some text for later checking"""
+        # replace \r\n so we do not depend on whether a browser "reencode"
+        # original message using \r\n or not
+        return hmac.new(self._instance_salt,
+                        text.strip().replace('\r\n', '\n')).hexdigest()
+
+    def check_text_sign(self, text, signature):
+        """check the text signature is equal to the given signature"""
+        return self.sign_text(text) == signature
+
+
     def locate_resource(self, rid):
         """return the (directory, filename) where the given resource
         may be found