NanoHttpd / nanohttpd

Tiny, easily embeddable HTTP server in Java.
http://nanohttpd.org
BSD 3-Clause "New" or "Revised" License
6.94k stars 1.69k forks source link

Nanohttpd doesnt handle spaces in uploaded filenames #9

Closed psh closed 11 years ago

psh commented 11 years ago

I modified the file upload test to send 3 files, to verify that uploading more than one file was working. As I was testing I noticed a bug.

3 files as input:

Parameters in "serve()":

Headers in "serve()":

It looks like decodeMultipartData() relying on a StringTokenizer using "; " as a delimiter might be incorrectly handling the filenames.

Crypto90 commented 11 years ago

Hihi, i was just looking for a small code to get a password authentification to work. I saw your post and I had that problem too. I fixed it for me, just replace your decodeMultipartData with mine:

 /**
         * Decodes the Multipart Body data and put it into java Properties' key
         * - value pairs.
        *
         */
        private void decodeMultipartData(String boundary, byte[] fbuf, BufferedReader in, Properties parms, Properties files)
                throws InterruptedException {
            try {
                int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes());
                int boundarycount = 1;
                String mpline = in.readLine();
                while (mpline != null) {
                    if (mpline.indexOf(boundary) == -1) {
                        sendError(HTTP_BADREQUEST, "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html");
                    }
                    boundarycount++;
                    Properties item = new Properties();
                    mpline = in.readLine();
                    while (mpline != null && mpline.trim().length() > 0) {
                        int p = mpline.indexOf(':');
                        if (p != -1) {
                            item.put(mpline.substring(0, p).trim().toLowerCase(), mpline.substring(p + 1).trim());
                        }
                        mpline = in.readLine();
                    }
                    if (mpline != null) {
                        String contentDisposition = item.getProperty("content-disposition");
                        if (contentDisposition == null) {
                            sendError(HTTP_BADREQUEST, "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html");
                        }
                        StringTokenizer st = new StringTokenizer(contentDisposition, "; ");
                        Properties disposition = new Properties();
                        boolean filenameBegins = false;
                        String fullFileName = "";
                        while (st.hasMoreTokens()) {
                            String token = st.nextToken();
                            int p = token.indexOf('=');
                            if (p != -1 || filenameBegins == true) {
                                if (filenameBegins == false && token.substring(0, p).trim().toLowerCase().equals("filename")) {
                                    filenameBegins = true;
                                    fullFileName = token.substring(p + 1).trim();
                                    if (fullFileName.lastIndexOf("\"") == fullFileName.length()-1) {
                                        filenameBegins = false;
                                        disposition.put("filename", fullFileName);
                                    }
                                }
                                else if (filenameBegins == true) {
                                    fullFileName += "_" + token.trim();
                                    if (fullFileName.lastIndexOf("\"") == fullFileName.length()-1) {
                                        filenameBegins = false;
                                        disposition.put("filename", fullFileName);
                                    }
                                } else {
                                    disposition.put(token.substring(0, p).trim().toLowerCase(), token.substring(p + 1).trim());
                                }
                            }
                        }
                        String pname = disposition.getProperty("name");
                        pname = pname.substring(1, pname.length() - 1);
                        String value = "";
                        if (item.getProperty("content-type") == null) {
                            while (mpline != null && mpline.indexOf(boundary) == -1) {
                                mpline = in.readLine();
                                if (mpline != null) {
                                    int d = mpline.indexOf(boundary);
                                    if (d == -1) {
                                        value += mpline;
                                    } else {
                                        value += mpline.substring(0, d - 2);
                                    }
                                }
                            }
                        } else {
                            if (boundarycount > bpositions.length) {
                                sendError(HTTP_INTERNALERROR, "Error processing request");
                            }
                            int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]);
                            String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4);
                            files.put(pname, path);
                            value = disposition.getProperty("filename");
                            value = value.substring(1, value.length() - 1);
                            do {
                                mpline = in.readLine();
                            } while (mpline != null && mpline.indexOf(boundary) == -1);
                        }
                        parms.put(pname, value);
                    }
                }
            } catch (IOException ioe) {
                sendError(HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
            }
        }

I hope I could help you and some others ;)

elonen commented 11 years ago

Thank you. I've commited a fix that should fix this.