使用C中的CGI将HTML表单数据文件上传到服务器?

时间:2017-07-11 06:55:03

标签: html c server cgi

我将一个HTML表单放在C中的CGI文件中,其中包含fprintf语句:

{{1}}

我想知道是否可以获取文件输入(使用multipart / form-data主体从表单中请求的值),将其转换为C代码(通过获取url数据来处理值),并上传那里的文件?顺便说一下,我不允许使用PHP作为我目前正在使用的机器,空间非常有限。

2 个答案:

答案 0 :(得分:1)

这是一个简单的例子,说明如何通过CGI处理文件上传。这有许多缺点,例如它不对文件大小,名称和类型进行任何检查 - 例如只是连接客户端提供的文件名危险,它可能包含..。请勿在生产中使用此处显示的代码!

此示例使用我在github上找到的Multipart form data parser。您需要更多代码来实际解析其他表单字段。您可能希望从looking at this answer开始,包括一个带有multipart-form-data的请求主体的示例。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "multipart_parser.h"

struct parsedata
{
    int inContentDisposition;  // flag for the right header to look for fields
    char *partname;            // field name
    char *filename;            // file name for an upload field
    FILE *saveto;              // file to save contents to
};

void showForm(void)
{
    puts("<form action=\"upload.cgi\" method=\"post\" "
            "enctype=\"multipart/form-data\">"
        "<p>Photo to Upload: <input type=\"file\" name=\"photo\" /></p>"
        "<p>Your Email Address: <input type=\"text\" "
            "name=\"email_address\" /></p>"
        "<p><input type=\"submit\" name=\"Submit\" value=\"Submit Form\""
        "/></p></form>");
}

int handle_headername(multipart_parser *parser, const char *at, size_t length)
{
    struct parsedata *data = multipart_parser_get_data(parser);
    data->inContentDisposition = !strncmp(at, "Content-Disposition", length);
    return 0;
}

int handle_headervalue(multipart_parser *parser, const char *at, size_t length)
{
    char localfilename[1024];

    struct parsedata *data = multipart_parser_get_data(parser);
    if (data->inContentDisposition)
    {
        char *hdrval = calloc(1, length + 1);
        strncpy(hdrval, at, length);
        if (strtok(hdrval, "; "))
        {
            char *tok;
            while ((tok = strtok(0, "; ")))
            {
                char *rquot;
                if (!strncmp(tok, "name=\"", 6) && 
                        ((rquot = strrchr(tok, '"')) > tok + 6))
                {
                    *rquot = 0;
                    free(data->partname);
                    data->partname = malloc(strlen(tok + 6) + 1);
                    strcpy(data->partname, tok + 6);
                }
                else if (!strncmp(tok, "filename=\"", 10) &&
                        ((rquot = strrchr(tok, '"')) > tok + 10))
                {
                    *rquot = 0;
                    free(data->filename);
                    data->filename = malloc(strlen(tok + 10) + 1);
                    strcpy(data->filename, tok + 10);
                    if (data->saveto) fclose(data->saveto);

                    // determine local location, adapt to your needs:
                    // for production code, ADD SANITY CHECKS, the following code
                    // allows an attacker to write any location of your server
                    // with a filename containing relative paths!
                    snprintf(localfilename, 1024, "uploads/%s", data->filename);
                    data->saveto = fopen(localfilename, "w");
                }
            }
        }
        free(hdrval);
    }
    return 0;
}

int handle_contentdata(multipart_parser *parser, const char *at, size_t length)
{
    struct parsedata *data = multipart_parser_get_data(parser);

    // only handle file upload of field "photo"
    // you have to extend this to get the values of other form fields
    if (data->partname && data->filename && !strcmp(data->partname, "photo"))
    {
        fwrite(at, length, 1, data->saveto);
    }

    return 0;
}

char *upload(void)
{
    // can only upload with POST
    const char *method = getenv("REQUEST_METHOD");
    if (!method || strcmp(method, "POST")) return 0;

    // check for multipart/form-data and extract boundary if present
    char boundary[128] = "--"; // boundary starts with double dash
    const char *conttype = getenv("CONTENT_TYPE");
    if (!conttype || sscanf(conttype,
                "multipart/form-data; boundary=%125s", boundary+2)
            < 1) return 0;

    // see https://github.com/iafonov/multipart-parser-c
    multipart_parser_settings callbacks = {0};
    callbacks.on_header_field = handle_headername;
    callbacks.on_header_value = handle_headervalue;
    callbacks.on_part_data = handle_contentdata;

    struct parsedata data = {0};

    multipart_parser *parser = multipart_parser_init(boundary, &callbacks);
    multipart_parser_set_data(parser, &data);

    // read body from stdin:
    char reqdata[64 * 1024];
    size_t length;
    while ((length = fread(reqdata, 1, 64 * 1024, stdin)) > 0)
    {
        // and feed it to the parser:
        multipart_parser_execute(parser, reqdata, length);
    }

    multipart_parser_free(parser);

    free(data.partname);
    if (data.filename && data.saveto)
    {
        fclose(data.saveto);
        return data.filename;
    }
    free(data.filename);

    return 0;
}

int main(void)
{
    char *uploaded = upload();

    puts("Content-Type: text/html\n");
    puts("<html><head><title>Test</title></head><body>");

    if (uploaded)
    {
        printf("<b>%s</b> uploaded.", uploaded);
        free(uploaded);
    }
    else
    {
        showForm();
    }

    puts("</body></html>");
}

答案 1 :(得分:1)

此示例使用任何非标准库:

#include <stdio.h>
#include <stdlib.h>

#define MAXLEN 80
#define EXTRA 5
/* 4 for field name "data", 1 for "=" */
#define MAXINPUT MAXLEN + EXTRA + 2
/* 1 for added line break, 1 for trailing NUL */
#define DATAFILE "../data/data.txt"

void unencode(char * src, char * last, char * dest) {
    for (; src != last; src++, dest++)
        if ( * src == '+')
            * dest = ' ';
        else if ( * src == '%') {
        int code;
        if (sscanf(src + 1, "%2x", & code) != 1) code = '?'; * dest = code;
        src += 2;
    } else
        *dest = * src; * dest = '\n'; * ++dest = '\0';
}

int main(void) {
    char * lenstr;
    char input[MAXINPUT], data[MAXINPUT];
    long len;
    printf("%s%c%c\n",
        "Content-Type:text/html;charset=iso-8859-1", 13, 10);
    printf("<TITLE>Response</TITLE>\n");
    lenstr = getenv("CONTENT_LENGTH");
    if (lenstr == NULL || sscanf(lenstr, "%ld", & len) != 1 || len > MAXLEN)
        printf("<P>Error in invocation - wrong FORM probably.");
    else {
        FILE * f;
        fgets(input, len + 1, stdin);
        unencode(input + EXTRA, input + len, data);
        f = fopen(DATAFILE, "a");
        if (f == NULL)
            printf("<P>Sorry, cannot store your data.");
        else
            fputs(data, f);
        fclose(f);
        printf("<P>Thank you! The following contribution of yours has \
been stored:<BR>%s", data);
    }
    return 0;
}