Proof of Concept Multi-Part Formdata Parser in C++

Dieser Parser ist aus einem geplanten Projekt, das ich verworfen habe. Ich hoffe jemand kann ihn noch gebrauchen.

 * are permitted in any medium without royalty. This file is offered
 * as-is, without any warranty.
 */

#ifndef MPFD_H
#define MPFD_H

#include <functional>
#include <exception>
#include <string>
namespace mpfd
{
    class ParserException: public std::runtime_error {
    public:
        ParserException():runtime_error("Something went wrong during parsing"){}
        ParserException(std::string msg):runtime_error(msg.c_str()){}
    };

    class HeaderData
    {
    public:
        std::string name;
        std::string filename;
        std::string mimetype;
    };

    class Parser
    {
    private:
    size_t pos;
    std::string content;
    std::string boundary;
    std::function<void (mpfd::HeaderData, std::string)> gotpart;
    protected:
    bool read_boundary() {
        static size_t boundlen = boundary.length();
        int ret = content.compare (pos, boundlen, boundary);
        if (ret == 0) {
            pos += boundlen;
            return true;
        }
        return false;
    }
    bool continue_reading() {
        if (content.compare(pos, 2, "
") == 0) {
            pos += 2;
            return true;
        }
        if (content.compare(pos, 2, "--") == 0)
            return false;
        throw ParserException();
    }
    void read_header(std::string &name, std::string &filename, std::string &mimetype) {
        while (content[pos] != ';') {
            if (content[pos] == '\r' || content[pos] == '
') throw ParserException();
            pos++;
        }

        static const std::string filenamestr = "filename=";
        static const std::string namestr = "name=";

        while (content.compare(pos, 2, "
") != 0) {

            /* Skip if position when the current character occurs to be ';' or ' ' */
            if (content[pos] == ';' || content[pos] == ' ') {
                pos += 1;

            } else if (content.compare(pos, filenamestr.length(), filenamestr) == 0) {
                pos += filenamestr.length();
                if (content[pos] == '"') {
                    pos += 1;
                    while (content[pos] != '"') {
                        if(content.compare(pos, 2, "
") == 0) {
                            throw ParserException();
                        }
                        filename += content[pos];
                        pos += 1;
                    }
                    pos += 1;
                }
            } else if (content.compare(pos, namestr.length(), namestr) == 0) {
                pos += namestr.length();
                if (content[pos] == '"') {
                    pos += 1;
                    while (content[pos] != '"') {
                        if(content.compare(pos, 2, "
") == 0) {
                            throw ParserException();
                        }
                        name += content[pos];
                        pos += 1;
                    }
                    pos += 1;
                }
            } else {
                throw ParserException();
                break;
            }
        }
        pos += 2;
        static const std::string contenttypestr = "Content-Type: ";
        if (content.compare(pos, contenttypestr.length(), contenttypestr) == 0) {
            pos += contenttypestr.length();
            while (content[pos] != '
') {
                if (content[pos] != '\r') {
                    mimetype += content[pos];
                }
                pos += 1;
            }
            pos += 1;
        }

        /* Checking for last newline */
        int i;
        for (i = 0; i < 2; i++) {
            if (content.compare(pos, 2, "
") == 0) {
                pos += 2;
            }
        }
    }
    void read_part_data(std::string &data) {
        while (!read_boundary()) {
            data += content[pos];
            pos += 1;
        }


        if (data.back() == '
') {
            data.erase(data.end()-1, data.end());
            if (data.back() == '\r') {
                data.erase(data.end()-1, data.end());
            }
        }
    }
    public:
    Parser(std::function<void (mpfd::HeaderData, std::string)> gotpart) {
        this->gotpart  = gotpart;
    }
    ~Parser() {}
    void parse(std::string boundary, std::string content) {
        pos = 0;
        this->boundary = boundary;
        this->content = content;
        read_boundary();
        while (continue_reading()) {
            HeaderData header;
            std::string data;

            read_header(header.name, header.filename, header.mimetype);
            read_part_data(data);
            this->gotpart(header, data);
        }
    }
    };
}

#endif // MPFD_H

Hier ist ein Beispiel, wie man den Parser benutzt:

#include "mpfd.h"

using namespace std;

int main()
{
    string str = "--AaB03x
"
    "Content-Disposition: form-data; name=\"submit-name\"

"

    "Larry
"
    "--AaB03x
"
    "Content-Disposition: form-data; name=\"files\"; filename=\"file1.txt\"
"
    "Content-Type: text/plain

"

    "... contents of file1.txt ...
"
    "--AaB03x--";

    mpfd::Parser post_parser([](mpfd::HeaderData header, std::string content){
        std::cout << "_" << header.name << "_" << header.filename << "_" << header.mimetype << std::endl;
        std::cout << "_" << content << std::endl;
    });
    post_parser.parse("--AaB03x", str);

    return 0;
}