我将一个HTML表单放在C中的CGI文件中,其中包含fprintf语句:
{{1}}
我想知道是否可以获取文件输入(使用multipart / form-data主体从表单中请求的值),将其转换为C代码(通过获取url数据来处理值),并上传那里的文件?顺便说一下,我不允许使用PHP作为我目前正在使用的机器,空间非常有限。
答案 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;
}