From b3e2f07367c6e9836b3a7635b86335bf7104b2b9 Mon Sep 17 00:00:00 2001 From: Daniel Watson Date: Fri, 21 Jul 2023 00:03:06 -0700 Subject: [PATCH] ; allow uploading files ending in newline via EWW ; Ensure that every boundary in HTTP message is preceded by "\r\n". ; According to RFC 2046, section 5, the "\r\n" preceding the boundary ; is not considered part of the preceding content, and is instead ; attached to the boundary that follows it. ; ; Consider a file named "1nl", consisting only of the single character ; '\n'. ; ; The old version of =mm-url-encode-multipart-form-data= creates the ; following HTTP message: ; ; (concat ; "--BOUNDARY\r\n" ; "Content-Disposition: form-data; name=\"a\"; filename=\"1nl\"\r\n" ; "Content-Transfer-Encoding: binary\r\n" ; "Content-Type: c\r\n" ; "\r\n" ; ; ;; file content ; "\n" ; ; ;; NOTE "\r\n" is absent before the following boundary ; "--BOUNDARY--\r\n") ; ; the new version of =mm-url-encode-multipart-form-data= creates this ; HTTP message: ; ; (concat ; "--BOUNDARY\r\n" ; "Content-Disposition: form-data; name=\"a\"; filename=\"1nl\"\r\n" ; "Content-Transfer-Encoding: binary\r\n" ; "Content-Type: c\r\n" ; "\r\n" ; ; ;; file content ; "\n" ; ; ;; NOTE "\r\n" precedes the boundary ; "\r\n" ; "--BOUNDARY--\r\n") ; ; The new code ensures all boundaries after the one at the very ; beginning are preceded by "\r\n", whether they are the final, or ; other internal boundaries. --- lisp/gnus/mm-url.el | 5 +- test/lisp/gnus/mm-url-tests.el | 160 +++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 test/lisp/gnus/mm-url-tests.el diff --git a/lisp/gnus/mm-url.el b/lisp/gnus/mm-url.el index 11847a79f17..5b68b25ec2e 100644 --- a/lisp/gnus/mm-url.el +++ b/lisp/gnus/mm-url.el @@ -433,13 +433,12 @@ mm-url-encode-multipart-form-data (insert (number-to-string filedata)))))) ((equal name "submit") (insert - "Content-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit\r\n")) + "Content-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit")) (t (insert (format "Content-Disposition: form-data; name=%S\r\n\r\n" name)) (insert value))) - (unless (bolp) - (insert "\r\n")))) + (insert "\r\n"))) (insert "--" boundary "--\r\n") (buffer-string))) diff --git a/test/lisp/gnus/mm-url-tests.el b/test/lisp/gnus/mm-url-tests.el new file mode 100644 index 00000000000..7b8d45b6061 --- /dev/null +++ b/test/lisp/gnus/mm-url-tests.el @@ -0,0 +1,160 @@ +;;; mm-url-tests.el --- -*- lexical-binding:t -*- + +;; Copyright (C) 2021-2023 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs 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 General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;;; Code: + +(require 'ert) +(require 'mm-url) + + +(ert-deftest test-mm-url-encode-multipart-form-data:nil () + (should + (string= + (mm-url-encode-multipart-form-data '() "BOUNDARY") + "--BOUNDARY--\r\n"))) + +(ert-deftest test-mm-url-encode-multipart-form-data:name-value () + (should + (string= + (mm-url-encode-multipart-form-data + '(("key" . "value")) "BOUNDARY") + (concat "--BOUNDARY\r\n" + "Content-Disposition: form-data; name=\"key\"\r\n" + "\r\n" + "value\r\n" + "--BOUNDARY--\r\n")))) + +(ert-deftest test-mm-url-encode-multipart-form-data:submit () + (should + (string= + (mm-url-encode-multipart-form-data '(("submit")) "BOUNDARY") + (concat "--BOUNDARY\r\n" + "Content-Disposition: form-data; name=\"submit\"\r\n" + "\r\n" + "Submit\r\n" + "--BOUNDARY--\r\n")))) + +(ert-deftest test-mm-url-encode-multipart-form-data:file () + (should + (string= + (mm-url-encode-multipart-form-data + '(("file" . (("name" . "a") + ("filename" . "b") + ("content-type" . "c") + ("filedata" . "d\n")))) + "BOUNDARY") + + (concat + "--BOUNDARY\r\n" + "Content-Disposition: form-data; name=\"a\"; filename=\"b\"\r\n" + "Content-Transfer-Encoding: binary\r\n" + "Content-Type: c\r\n" + "\r\n" + + ;; file content + "d\n" + + ;; rfc 2046 section 5 + ;; https://www.rfc-editor.org/rfc/rfc2046#section-5 + ;; "The boundary delimiter MUST occur at the beginning of a + ;; line, i.e., following a CRLF, and the initial CRLF is + ;; considered to be attached to the boundary delimiter line + ;; rather than part of the preceding part." + "\r\n" + + "--BOUNDARY--\r\n")))) + +(ert-deftest test-mm-url-encode-multipart-form-data--all-parts () + (should + (string= + (mm-url-encode-multipart-form-data + '(("name" . "value") + ("submit") + ("file" . (("name" . "a") + ("filename" . "b") + ("content-type" . "c") + ("filedata" . "d")))) + "BOUNDARY") + (concat + "--BOUNDARY\r\n" + "Content-Disposition: form-data; name=\"name\"\r\n" + "\r\n" + "value\r\n" + "--BOUNDARY\r\n" + "Content-Disposition: form-data; name=\"submit\"\r\n" + "\r\n" + "Submit\r\n" + "--BOUNDARY\r\n" + "Content-Disposition: form-data; name=\"a\"; filename=\"b\"\r\n" + "Content-Transfer-Encoding: binary\r\n" + "Content-Type: c\r\n" + "\r\n" + + ;; file content + "d" + + ;; rfc 2046 section 5 + ;; the \r\n is attached to the boundary below it + "\r\n" + "--BOUNDARY--\r\n")))) + +(ert-deftest test-mm-url-encode-multipart-form-data-two-files () + (should + (string= + (mm-url-encode-multipart-form-data + '(("file" . (("name" . "a") + ("filename" . "b") + ("content-type" . "c") + ("filedata" . "d\n"))) + ("file" . (("name" . "e") + ("filename" . "f") + ("content-type" . "g") + ("filedata" . "h\n")))) + "BOUNDARY") + (concat + "--BOUNDARY\r\n" + "Content-Disposition: form-data; name=\"a\"; filename=\"b\"\r\n" + "Content-Transfer-Encoding: binary\r\n" + "Content-Type: c\r\n" + "\r\n" + + ;; file content + "d\n" + + ;; rfc2046 section 5 + ;; the \r\n is attached to the boundary below it + "\r\n" + "--BOUNDARY\r\n" + "Content-Disposition: form-data; name=\"e\"; filename=\"f\"\r\n" + "Content-Transfer-Encoding: binary\r\n" + "Content-Type: g\r\n" + "\r\n" + + ;; file content + "h\n" + + ;; rfc 2046 section 5 + ;; the \r\n is attached to the boundary below it + "\r\n" + "--BOUNDARY--\r\n")))) + + +;;; mm-url-tests.el ends here -- 2.39.2