From 4628276e4a8f159cded83f8dbf7ec8239705f84a Mon Sep 17 00:00:00 2001
From: Radek Czajka <rczajka@rczajka.pl>
Date: Thu, 12 Oct 2023 10:19:53 +0200
Subject: [PATCH] Reject interrupted requests.

---
 src/redakcja/static/js/wiki/base.js      |   5 +++--
 src/wiki/locale/pl/LC_MESSAGES/django.mo | Bin 3829 -> 4080 bytes
 src/wiki/locale/pl/LC_MESSAGES/django.po |  17 +++++++++++++----
 src/wiki/views.py                        |  21 ++++++++++++++++++++-
 4 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/src/redakcja/static/js/wiki/base.js b/src/redakcja/static/js/wiki/base.js
index 1b031cad..ad0ca835 100644
--- a/src/redakcja/static/js/wiki/base.js
+++ b/src/redakcja/static/js/wiki/base.js
@@ -308,7 +308,7 @@
                 var span = $("*[data-ui-error-for='"+field_name+"']", this.$elem);
 
                 if(!span.length) {
-                    unassigned.push(field_name);
+                    unassigned.push(errors[field_name]);
                     continue;
                 }
 
@@ -316,7 +316,8 @@
             }
 
             if(unassigned.length > 0)
-                global.text( global.text() + 'W formularzu wystąpiły błędy');
+                global.text(
+                    global.text() + 'Wystąpił błąd: ' + unassigned.join(', '));
         }
     }
 
diff --git a/src/wiki/locale/pl/LC_MESSAGES/django.mo b/src/wiki/locale/pl/LC_MESSAGES/django.mo
index 4ac2575246bba2fb5dea5461b7ac35b9ca30eab9..084d16c83ab45aa31b39328a7c3f73e2d7e6f9aa 100644
GIT binary patch
delta 1693
zcmZwHS!h&O9LMpKF=}jF<CfMX*Sc$*ET-<ehz}Km`XB)XZ!*_$Ot^P)Ghv!Mcx$K|
zEh>gmtS?pcK@n6&P|#7(jHt9ei2CFLg1)Gr(BeaV@cSE&(1IuPKc9Q<a_;{uH~&m{
zTNOR6DE-w?RukiihL|x;xUQ6na=yPY<#+)HVjjm}0n6|)4!{>U6kpr+zqTFYqV6Ao
zx^D*6IIO|p#zf|O4hC^z3s&NG{01ZZ7Jo-2khk7JCGrp_;u9Q=A5b}0a8cLCQq^D;
zj=)Cbg~r9f7-Bi^HxUPV@Mj!`hl>}C`2+cxvs4=CMf?i$sDy9h*LWZKnb%ZG^l#J)
z^bk3jA*g;wS*M`h*MOD0-z?-nH*UlVY(jO^hU)kS)D-@Rv+y^2{)Y7~>V@}l2tLEP
z_{O#;k!>yQ8dT!jsQTefjFgGb0n24NtX-&tBBT$q7d6sDs6BETca<1(%U)kYwwjss
zI2M~w3ALm8-Gxf*Cse;jZ~`9H{Ey<`5+^j$KT&J`0JT={Q5~1j+fp2fBXI+26M4wb
z?55Jp9YpnW9JMD-qY}Q3O5`zWK+jQo<JDm1Uzv@g(k7XSywfbUu0;lEHlaqg6?xWl
zqB`tBCAc58`+M#66Q~iMLnZVVYCsQB_rJ8hi#Sl`AM6do=tWcHpdOfR+Y3+$E=O&q
z4X8w1)YAAk4fmr)cn;OiW#nhBQ|Y;T*oaSUJBo9*o)gPZFKkDRxCb@DUr;YTV%sNC
z9iKrZZ~-+l*HDQ(LbgV6+QBpsGl@lNP!@lpxB(r$cC(BVa|nIQnli1M*7`d_YpwqY
z)r6)|rS22OdO5X5t%Ro%N=Spz*Rp^hmtv{u&6ie1`WH9Do>8hQ8sdCH|8f3XCefHj
zEFrXDvxrrMcI#|HyL2+4xmD2=tsu0PRfMKe)55Zvg~Uo@;`|vig;1#_VuU_aEtt0R
z=b~?Ndhdb$`O4LSpYi;R)8_ff%nm2?+B;LBmx$-q3<zR#H-~|rbXxv*Pj&GouOs86
z!=Tx1ZtHSV^bv-g>5LxCm6enXtas{a7uD70t_*rrvUFAGwmQC>EcD#WWm{b*Xbru>
zu{L*S-<}pP890eTPiHbH9P8WXWOSt^<#fC0u8ixa+<5L(`N3G@`jnRr-E4|mgRJ|7
rC#s#o)xJFm9`VS_%_f3$kO-R9><&6IF3)kk+Y7U<@5OuThu-`MMj_Ew

delta 1441
zcmYk+OGs2v9LMqhOxN)>W=^I}Wsc8Gr^(68*OW#uK|9+-n<5C@MG&<qyc;13q!Ejd
zY*k6Ff+kw@*f>NGEu#lALIn}nqKIk{Mc?1;2p;BtKj)r%=bZm}%zSP9(cmw|ylY0;
zPi!Gt!)8Y@>2aatO3W&83QO@O*5Ms2!zUQQXSfz$y8A_U{{i*>H`iXq%bU5c#&u@C
zbx|p&A%mMRi_LffHSmNphe}`;x8iNwfCW^p@7(hx+{yhXtid|I#6Z@8Q5-<^n?;ZD
z?J$*U8jiRJW5~zOaM29UV+Bs261{?PoIyU8=b{cDqXu}6jBT$_{VqC}QHlP>ji}Be
zjBjx&F-)L3>OtPIVbszc#7;cs+Ale$Py^0j70x*yU^n**sD!`c8vKJwAVed(VG;CI
ziBeI<G2|VKqh{EQIujWj4{<Ks^S3OAR^&aGdi;V)#3Ot4SA$BZ9@TFfHeeEK@epc)
z$17QX?e!Q9+M~;;j&I<4oI@R!N2mc_qgLu8s)L`XGw>IcXe}=(fi!9&eW<fAf=cK#
z>P(CyU$I=2^;cz<1}0~BQ8T)aeC!1mb@&FASP^x~KO-Oe#YHpo=v@glqbAacdVk27
zMI}D!o}Wam%vs+xTyPDOs0^>74$mBFpogd}DPTJmQ8V<&Mjch5607B+@3lC4a2NLn
zP>G#GP4psaV*V5r4S3x(%%eKIhrz(86)B(+SVWFOuq|LcL<iBY8_K|cf&<q`Jwqf3
ztx~W>I7I9wSdnbdc^5ZYJ{6tZm9m#g8=;I_2_=*!wCAf!YP#q(`uYpo=9+^a7<J8Y
z524gomjpMx#2}%K+D?oRI(3~y6QPsIY=fe;+ec`r^zWlJ)^C$-us&kgEM+^Wv=Ayf
zc40z`uZ_}Q+e*=ouzh;I<Z0fIoC@D+Di4)*C(^xx>Hd62#d0V=8l4PJr>YnJ0m74P
AAOHXW

diff --git a/src/wiki/locale/pl/LC_MESSAGES/django.po b/src/wiki/locale/pl/LC_MESSAGES/django.po
index 0866257c..352c3db8 100644
--- a/src/wiki/locale/pl/LC_MESSAGES/django.po
+++ b/src/wiki/locale/pl/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Platforma Redakcyjna\n"
 "Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2023-10-10 15:18+0200\n"
+"PO-Revision-Date: 2023-10-12 10:12+0200\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org."
 "pl>\n"
@@ -275,15 +275,24 @@ msgstr "Wstaw referencję"
 msgid "Visual editor"
 msgstr "Edytor wizualny"
 
-#: wiki/views.py:326
+#: wiki/views.py:138
+msgid "Wrong content length, request probably interrupted."
+msgstr ""
+"Nieprawidłowa długość treści, żądanie prawdopodobnie zostało przerwane."
+
+#: wiki/views.py:146
+msgid "Content length required."
+msgstr "Brak nagłówka określającego długość treści zapytania."
+
+#: wiki/views.py:347
 msgid "Published"
 msgstr "Opublikowano"
 
-#: wiki/views.py:347
+#: wiki/views.py:368
 msgid "Revision marked"
 msgstr "Wersja oznaczona"
 
-#: wiki/views.py:349
+#: wiki/views.py:370
 msgid "Nothing changed"
 msgstr "Nic nie uległo zmianie"
 
diff --git a/src/wiki/views.py b/src/wiki/views.py
index aea44390..e22c0286 100644
--- a/src/wiki/views.py
+++ b/src/wiki/views.py
@@ -2,6 +2,7 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from datetime import datetime
+import json
 import os
 import logging
 from time import mktime
@@ -11,7 +12,7 @@ from django.apps import apps
 from django.conf import settings
 from django.urls import reverse
 from django import http
-from django.http import Http404, HttpResponseForbidden
+from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseBadRequest
 from django.middleware.gzip import GZipMiddleware
 from django.utils.decorators import decorator_from_middleware
 from django.utils.formats import localize
@@ -38,6 +39,10 @@ logger = logging.getLogger("fnp.wiki")
 MAX_LAST_DOCS = 10
 
 
+class HttpResponseLengthRequired(HttpResponse):
+    status_code = 411
+
+
 @never_cache
 def editor(request, slug, chunk=None, template_name='wiki/document_details.html'):
     try:
@@ -129,6 +134,20 @@ def text(request, chunk_id):
         return HttpResponseForbidden("Not authorized.")
 
     if request.method == 'POST':
+        # Check length to reject broken request.
+        try:
+            expected_cl = int(request.META['CONTENT_LENGTH'])
+        except:
+            return HttpResponseLengthRequired(json.dumps(
+                {"__message": _("Content length required.")}
+            ))
+        # 411 if missing
+        cl = len(request.body)
+        if cl != expected_cl:
+            return HttpResponseBadRequest(json.dumps(
+                {"__message": _("Wrong content length, request probably interrupted.")}
+            ))
+
         form = forms.DocumentTextSaveForm(request.POST, user=request.user, prefix="textsave")
         if form.is_valid():
             if request.user.is_authenticated:
-- 
2.20.1