~ozzloy/emacs

c6c42e4a72fc9c26086d7e9f0bcd70999a1bc213 — Daniel Watson 7 months ago f26622b emacs-29-bug-63941
; upload newline terminated files via EWW (Bug#63941)

; 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 prior 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 here before the following boundary
;    "--BOUNDARY--\r\n")

; this 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" preceding 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.
2 files changed, 133 insertions(+), 3 deletions(-)

M lisp/gnus/mm-url.el
A test/lisp/gnus/mm-url-tests.el
M lisp/gnus/mm-url.el => lisp/gnus/mm-url.el +2 -3
@@ 433,13 433,12 @@ Lowercase strings above are literals and uppercase are not."
	      (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)))


A test/lisp/gnus/mm-url-tests.el => test/lisp/gnus/mm-url-tests.el +131 -0
@@ 0,0 1,131 @@
;;; 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 <https://www.gnu.org/licenses/>.

;;; Commentary:

;;; Code:

(require 'ert)
(require 'mm-url)


(ert-deftest mm-url-encode-multipart-form-data ()
  ;; nil
  (should
   (string=
    (mm-url-encode-multipart-form-data '() "BOUNDARY")
    "--BOUNDARY--\r\n"))

  ;; key value pair
  (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")))

  ;; 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")))

  ;; file ending in newline
  (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"
     "d\n\r\n"
     "--BOUNDARY--\r\n")))

  ;; stress test combining parts: key-value, submit, file
  (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"
     "d\r\n"
     "--BOUNDARY--\r\n")))

  ;; two files, newline at EOF, before final and non-final BOUNDARY
  (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"
     "d\n\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"
     "h\n\r\n"
     "--BOUNDARY--\r\n"))))


;;; mm-url-tests.el ends here