22483e4e4a
FossilOrigin-Name: 8d7d245daca7e9a58256654103e005da7d41785ef2ec8e8153fd3a3730b3c0c3
191 lines
4.7 KiB
C
191 lines
4.7 KiB
C
/*
|
|
* Treebird - Lightweight frontend for Pleroma
|
|
* Copyright (C) 2022 Nekobit
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <fcgi_stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include "mime.h"
|
|
#include "string.h"
|
|
|
|
char* get_mime_boundary(char* content_type_str, char** bound)
|
|
{
|
|
// If neither values are set, get out
|
|
if (!(getenv("CONTENT_TYPE") || content_type_str))
|
|
return NULL;
|
|
|
|
char* content = content_type_str ? content_type_str : getenv("CONTENT_TYPE");
|
|
|
|
// Data gets changed in place
|
|
char* content_type = malloc(strlen(content)+1);
|
|
if (!content_type)
|
|
{
|
|
perror("malloc");
|
|
exit(1);
|
|
}
|
|
strcpy(content_type, content);
|
|
|
|
char* bound_str;
|
|
char* boundary;
|
|
|
|
/* Tmp reading variables */
|
|
char* tmp;
|
|
|
|
if (strstr(content_type, "multipart/form-data") == NULL ||
|
|
(bound_str = strstr(content_type, "boundary")) == NULL)
|
|
goto error;
|
|
|
|
bound_str += sizeof("boundary")-1;
|
|
|
|
boundary = (tmp = strchr(bound_str, '\"')) ? tmp :
|
|
strchr(bound_str, '=');
|
|
if (!boundary)
|
|
goto error;
|
|
boundary++;
|
|
|
|
if ((tmp = strchr(boundary, '\"')))
|
|
*tmp = '\0';
|
|
|
|
*bound = boundary;
|
|
|
|
return content_type;
|
|
error:
|
|
free(content_type);
|
|
return NULL;
|
|
}
|
|
|
|
static char* strnws(char* str)
|
|
{
|
|
for (; isblank(*str); ++str);
|
|
return str;
|
|
}
|
|
|
|
// Function assumes character is at ';' char
|
|
static char* read_key(char** r, char* key)
|
|
{
|
|
char* val, *read_val;
|
|
size_t key_len = strlen(key);
|
|
*r = strnws(*r+1);
|
|
if (strncmp(*r, key, key_len) != 0)
|
|
return NULL;
|
|
*r = strnws(*r + key_len);
|
|
if (**r != '=')
|
|
return NULL;
|
|
*r = strnws(*r + 1);
|
|
if (**r == '\"') ++(*r);
|
|
val = *r;
|
|
// Chop off string
|
|
if ((read_val = strchr(*r, '\"')) ||
|
|
(read_val = strchr(*r, ';')) ||
|
|
(read_val = strchr(*r, '\r')))
|
|
*read_val = '\0';
|
|
// Jump to end
|
|
*r += strlen(val)+1;
|
|
|
|
return val;
|
|
}
|
|
|
|
#define STRSCMP(begin, str) strncmp((begin), (str), sizeof(str)-1)
|
|
|
|
char* read_form_data(char* boundary,
|
|
char* begin,
|
|
struct http_form_info* info,
|
|
size_t size)
|
|
{
|
|
ptrdiff_t data_size, begin_to_value_size;
|
|
// Step 1: Parse name
|
|
// Parser variables
|
|
char* r, *read_val;
|
|
size_t bound_len = strlen(boundary);
|
|
if (strncmp(begin, "--", 2) != 0 ||
|
|
strncmp(begin+2, boundary, bound_len) != 0)
|
|
return NULL;
|
|
|
|
// Check if this is the end
|
|
if (strncmp(begin + 2 + bound_len, "--", 2) == 0)
|
|
return NULL;
|
|
|
|
r = begin + 2 + bound_len + 2; /* Skip over LRSF */
|
|
|
|
if (STRSCMP(r, "Content-Disposition") != 0)
|
|
return NULL;
|
|
|
|
r = strnws(r + sizeof("Content-Disposition")-1);
|
|
if (*r != ':') // Fucked up
|
|
return NULL;
|
|
|
|
r = strnws(r + 1);
|
|
if (STRSCMP(r, "form-data") != 0)
|
|
return NULL;
|
|
r = strnws(r + sizeof("form-data")-1);
|
|
if (*r != ';')
|
|
return NULL;
|
|
|
|
info->name = read_key(&r, "name");
|
|
// Step 2: Parse filename (if there)
|
|
if (*r == ';')
|
|
{
|
|
info->filename = read_key(&r, "filename");
|
|
}
|
|
else
|
|
info->filename = NULL;
|
|
|
|
if (*r == '\r')
|
|
r += 2;
|
|
|
|
// Step 3: Parse Content-type (if there)
|
|
if (STRSCMP(r, "Content-Type") == 0)
|
|
{
|
|
r = strnws(r + sizeof("Content-type")-1);
|
|
if (*r != ':')
|
|
return NULL;
|
|
|
|
r = strnws(r + 1);
|
|
info->content_type = r;
|
|
if ((r = strchr(r, '\r')))
|
|
*r = '\0';
|
|
else
|
|
return NULL;
|
|
r += 4;
|
|
}
|
|
else if (*r == '\r') {
|
|
info->content_type = NULL;
|
|
r += 2;
|
|
}
|
|
|
|
// Last step: Find data
|
|
info->value = r;
|
|
begin_to_value_size = info->value - begin;
|
|
|
|
// Look for end
|
|
if ((r = strnstr(r, boundary, size - begin_to_value_size)) &&
|
|
r && strnstr(r-4, "\r\n--", size - begin_to_value_size))
|
|
{
|
|
r[-4] = '\0';
|
|
data_size = (r-4) - info->value;
|
|
info->value_size = data_size;
|
|
}
|
|
else
|
|
return NULL;
|
|
|
|
// Go back for next read
|
|
r -= 2;
|
|
|
|
return r;
|
|
}
|