Moved juci ubus module to juci repo

This commit is contained in:
Martin Schröder 2015-06-23 23:47:44 +02:00
parent 3ea6e0e0e6
commit eaa6d45037
9 changed files with 0 additions and 4585 deletions

View file

@ -1,52 +0,0 @@
#
# Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
#
# Licensed under the Apache License, Version 2.0.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=ubus-mod-juci
#PKG_VERSION:=$(shell git --git-dir=$(CURDIR)/../.git log -1 --pretty="%ci %h" | awk '{ print $$1 "-" $$4 }')
PKG_VERSION:=15.6.8
PKG_MAINTAINER:=Martin K. Schroder <mkschreder.uk@gmail.com>
PKG_LICENSE:=Apache-2.0
PKG_LICENSE_FILES:=
PKG_BUILD_PARALLEL:=1
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/cmake.mk
define Build/Prepare
$(INSTALL_DIR) $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/ubus-mod-juci
SECTION:=ubus-mod-juci
CATEGORY:=UBUS
TITLE:=UBUS UCI Plugin
DEPENDS:=+rpcd +libubox +libubus
endef
define Package/ubus-mod-juci/description
Provides support for writing lightweight ubus plugins
endef
define Package/ubus-mod-juci/install
$(INSTALL_DIR) $(1)/usr/lib/rpcd
$(INSTALL_BIN) $(PKG_BUILD_DIR)/ubus-mod-juci.so $(1)/usr/lib/rpcd/
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
$(CP) ./access.json $(1)/usr/share/rpcd/acl.d/ubus-mod-juci.json
$(INSTALL_DIR) $(1)/usr/libexec $(1)/www/cgi-bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/io/juci-cgi $(1)/usr/libexec/
$(LN) /usr/libexec/juci-cgi $(1)/www/cgi-bin/juci-upload
$(LN) /usr/libexec/juci-cgi $(1)/www/cgi-bin/juci-backup
endef
define Package/ubus-mod-juci/postinst
endef
$(eval $(call BuildPackage,ubus-mod-juci))

View file

@ -1,435 +0,0 @@
{
"juciguest": {
"description": "Functions allowed for non logged-in users",
"read": {
"ubus": {
"juci.ui": [
"themes"
]
}
}
},
"core": {
"description": "Core functions for JUCI",
"read": {
"ubus": {
"juci.ui": [
"*"
],
"session": [
"access",
"destroy"
],
"uci": [
"*"
]
}
}
},
"status": {
"description": "Status information display",
"read": {
"ubus": {
"iwinfo": [
"devices",
"info",
"assoclist",
"phyname"
],
"router": [
"dslstats",
"info",
"clients",
"igmptable"
],
"system": [
"info",
"board"
],
"network.interface": [
"status"
],
"juci.network": [
"conntrack_count",
"dhcp_leases",
"dhcp6_leases",
"arp_table",
"routes",
"routes6"
],
"juci.system": [
"diskfree",
"syslog",
"dmesg",
"process_list"
],
"service": [
"list"
]
}
},
"write": {
"ubus": {
"juci.system": [
"process_signal"
]
}
}
},
"system": {
"description": "General system settings",
"read": {
"ubus": {
"system": [
"info",
"board"
],
"juci.system": [
"init_list"
]
},
"uci": [
"juci"
]
},
"write": {
"ubus": {
"juci.system": [
"init_action"
]
},
"uci": [
"juci"
]
}
},
"admin": {
"description": "Authentication and SSH settings",
"read": {
"ubus": {
"juci.system": [
"sshkeys_get",
"upgrade_test",
"upgrade_clean",
"upgrade_check",
"upgrade_start"
],
"asterisk": [
"status"
],
"asterisk.call_log": [
"list",
"sshkeys_get"
],
"wps": [
"genpin",
"setpin",
"pbc",
"showpin",
"stapin",
"status",
"stop"
],
"router": [
"networks"
],
"network.interface": [
"dump"
]
},
"uci": [
"dropbear",
"boardpanel",
"hosts",
"voice_client",
"juci"
]
},
"write": {
"ubus": {
"juci.system": [
"sshkeys_set",
"password_set"
],
"asterisk.call_log": [
"list"
],
"wps": [
"genpin",
"setpin",
"pbc",
"showpin",
"stapin",
"status",
"stop"
]
},
"uci": [
"dropbear",
"boardpanel",
"hosts",
"voice_client",
"juci"
]
}
},
"users": {
"description": "Guest login settings",
"read": {
"uci": [
"rpcd"
]
},
"write": {
"uci": [
"rpcd"
]
}
},
"software": {
"description": "Package management",
"read": {
"ubus": {
"system": [
"info",
"board"
],
"juci.opkg": [
"list",
"list_installed",
"find",
"config_get"
]
}
},
"write": {
"ubus": {
"juci.opkg": [
"install",
"remove",
"update",
"config_set"
]
}
}
},
"upgrade": {
"description": "Firmware upgrade",
"read": {
"ubus": {
"juci.system": [
"upgrade_test",
"reset_test"
]
}
},
"write": {
"juci-io": [
"upload"
],
"ubus": {
"juci.system": [
"upgrade_start",
"upgrade_clean",
"reset_start",
"reboot"
]
}
}
},
"backup": {
"description": "Backup and Restore",
"read": {
"juci-io": [
"backup"
],
"ubus": {
"juci.system": [
"backup_config_get",
"backup_list"
]
}
},
"write": {
"ubus": {
"juci.system": [
"backup_clean",
"backup_config_set",
"backup_restore",
"reboot"
]
}
}
},
"startup": {
"description": "System boot settings",
"read": {
"ubus": {
"juci.system": [
"init_list",
"rclocal_get"
]
}
},
"write": {
"ubus": {
"juci.system": [
"init_action",
"rclocal_set"
]
}
}
},
"cron": {
"description": "Crontab management",
"read": {
"ubus": {
"juci.system": [
"crontab_get"
]
}
},
"write": {
"ubus": {
"juci.system": [
"crontab_set"
]
}
}
},
"leds": {
"description": "Hardware LED configuration",
"read": {
"ubus": {
"network.device": [
"status"
],
"juci.system": [
"led_list",
"usb_list"
]
},
"uci": [
"system"
]
},
"write": {
"uci": [
"system"
]
}
},
"diagnostics": {
"description": "Network diagnostic tools",
"read": {
"ubus": {
"juci.network": [
"ping",
"ping6",
"traceroute",
"traceroute6",
"nslookup"
]
}
}
},
"hostnames": {
"description": "Host entry management",
"read": {
"uci": [
"dhcp"
]
},
"write": {
"uci": [
"dhcp"
]
}
},
"network": {
"description": "Network, switch and routing configuration",
"read": {
"ubus": {
"network": [
"get_proto_handlers"
],
"network.device": [
"status"
],
"network.interface": [
"dump"
],
"network.wireless": [
"status"
],
"juci.network": [
"switch_list",
"switch_info",
"switch_status",
"device_list",
"conntrack_table",
"dslstats"
],
"juci.network.bwmon": [
"devices",
"statistics"
],
"router": [ "radios" ]
},
"uci": [
"network",
"wireless",
"broadcom",
"ddns"
]
},
"write": {
"uci": [
"network",
"wireless"
]
}
},
"wireless": {
"description": "Wireless configuration",
"read": {
"uci": [
"wireless"
]
},
"write": {
"uci": [
"wireless",
"ddns"
]
}
},
"firewall": {
"description": "Firewall configuration",
"read": {
"uci": [
"firewall"
]
},
"write": {
"uci": [
"firewall"
]
}
}
}

View file

@ -1,13 +0,0 @@
config 'settings' 'settings'
option theme 'vodafone'
option lang 'en'
list themes 'vodaphone'
list plugins 'core'
list plugins 'internet'
list plugins 'phone'
list plugins 'settings'
list plugins 'status'
list plugins 'wifi'
list languages 'en'
list languages 'se'
list languages 'de'

View file

@ -1,16 +0,0 @@
cmake_minimum_required(VERSION 2.6)
PROJECT(ubus-mod-juci C)
ADD_SUBDIRECTORY(io)
ADD_DEFINITIONS(-Os -Wall -Wno-format-y2k -W -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wcast-align -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls -Wmissing-field-initializers -Wextra -Wformat=2 -Wno-format-nonliteral -Wpointer-arith -Wno-missing-braces --std=gnu99 -g3 -Wmissing-declarations -Iinclude)
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
ADD_LIBRARY(ubus-mod-juci MODULE juci.c)
TARGET_LINK_LIBRARIES(ubus-mod-juci ubox ubus )
SET_TARGET_PROPERTIES(ubus-mod-juci PROPERTIES OUTPUT_NAME ubus-mod-juci PREFIX "")
INSTALL(TARGETS ubus-mod-juci LIBRARY DESTINATION lib)

View file

@ -1,19 +0,0 @@
cmake_minimum_required(VERSION 2.6)
PROJECT(luciexpress-io C)
INCLUDE(CheckFunctionExists)
ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations)
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
IF(APPLE)
INCLUDE_DIRECTORIES(/opt/local/include)
LINK_DIRECTORIES(/opt/local/lib)
ENDIF()
ADD_EXECUTABLE(juci-cgi main.c multipart_parser.c)
TARGET_LINK_LIBRARIES(juci-cgi ubox ubus)
INSTALL(TARGETS juci-cgi RUNTIME DESTINATION sbin)

View file

@ -1,688 +0,0 @@
/*
* juci-io - JUCI non-RPC helper
*
* Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <libubus.h>
#include <libubox/blobmsg.h>
#include "multipart_parser.h"
enum part {
PART_UNKNOWN,
PART_SESSIONID,
PART_FILENAME,
PART_FILEMODE,
PART_FILEDATA
};
const char *parts[] = {
"(bug)",
"sessionid",
"filename",
"filemode",
"filedata",
};
struct state
{
bool is_content_disposition;
enum part parttype;
char *sessionid;
char *filename;
bool filedata;
int filemode;
int filefd;
};
enum {
SES_ACCESS,
__SES_MAX,
};
static const struct blobmsg_policy ses_policy[__SES_MAX] = {
[SES_ACCESS] = { .name = "access", .type = BLOBMSG_TYPE_BOOL },
};
static struct state st;
static void
session_access_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
struct blob_attr *tb[__SES_MAX];
bool *allow = (bool *)req->priv;
if (!msg)
return;
blobmsg_parse(ses_policy, __SES_MAX, tb, blob_data(msg), blob_len(msg));
if (tb[SES_ACCESS])
*allow = blobmsg_get_bool(tb[SES_ACCESS]);
}
static bool
session_access(const char *sid, const char *obj, const char *func)
{
uint32_t id;
bool allow = false;
struct ubus_context *ctx;
static struct blob_buf req;
ctx = ubus_connect(NULL);
if (!ctx || ubus_lookup_id(ctx, "session", &id))
goto out;
blob_buf_init(&req, 0);
blobmsg_add_string(&req, "ubus_rpc_session", sid);
blobmsg_add_string(&req, "scope", "juci-io");
blobmsg_add_string(&req, "object", obj);
blobmsg_add_string(&req, "function", func);
ubus_invoke(ctx, id, "access", req.head, session_access_cb, &allow, 500);
out:
if (ctx)
ubus_free(ctx);
return allow;
}
static char *
md5sum(const char *file)
{
pid_t pid;
int fds[2];
static char md5[33];
if (pipe(fds))
return NULL;
switch ((pid = fork()))
{
case -1:
return NULL;
case 0:
uloop_done();
dup2(fds[1], 1);
close(0);
close(2);
close(fds[0]);
close(fds[1]);
if (execl("/bin/busybox", "/bin/busybox", "md5sum", file, NULL));
return NULL;
break;
default:
memset(md5, 0, sizeof(md5));
read(fds[0], md5, 32);
waitpid(pid, NULL, 0);
close(fds[0]);
close(fds[1]);
}
return md5;
}
static char *
datadup(const void *in, size_t len)
{
char *out = malloc(len + 1);
if (!out)
return NULL;
memcpy(out, in, len);
*(out + len) = 0;
return out;
}
static bool
urldecode(char *buf)
{
char *c, *p;
if (!buf || !*buf)
return true;
#define hex(x) \
(((x) <= '9') ? ((x) - '0') : \
(((x) <= 'F') ? ((x) - 'A' + 10) : \
((x) - 'a' + 10)))
for (c = p = buf; *p; c++)
{
if (*p == '%')
{
if (!isxdigit(*(p + 1)) || !isxdigit(*(p + 2)))
return false;
*c = (char)(16 * hex(*(p + 1)) + hex(*(p + 2)));
p += 3;
}
else if (*p == '+')
{
*c = ' ';
p++;
}
else
{
*c = *p++;
}
}
*c = 0;
return true;
}
static bool
postdecode(char **fields, int n_fields)
{
char *p;
const char *var;
static char buf[1024];
int i, len, field, found = 0;
var = getenv("CONTENT_TYPE");
if (!var || strncmp(var, "application/x-www-form-urlencoded", 33))
return false;
memset(buf, 0, sizeof(buf));
if ((len = read(0, buf, sizeof(buf) - 1)) > 0)
{
for (p = buf, i = 0; i <= len; i++)
{
if (buf[i] == '=')
{
buf[i] = 0;
for (field = 0; field < (n_fields * 2); field += 2)
{
if (!strcmp(p, fields[field]))
{
fields[field + 1] = buf + i + 1;
found++;
}
}
}
else if (buf[i] == '&' || buf[i] == '\0')
{
buf[i] = 0;
if (found >= n_fields)
break;
p = buf + i + 1;
}
}
}
for (field = 0; field < (n_fields * 2); field += 2)
if (!urldecode(fields[field + 1]))
return false;
return (found >= n_fields);
}
static int
response(bool success, const char *message)
{
char *md5;
struct stat s;
printf("Status: 200 OK\r\n");
printf("Content-Type: text/plain\r\n\r\n{\n");
if (success)
{
if (!stat(st.filename, &s) && (md5 = md5sum(st.filename)) != NULL)
printf("\t\"size\": %u,\n\t\"checksum\": \"%s\"\n",
(unsigned int)s.st_size, md5);
}
else
{
if (message)
printf("\t\"message\": \"%s\",\n", message);
printf("\t\"failure\": [ %u, \"%s\" ]\n", errno, strerror(errno));
if (st.filefd > -1)
unlink(st.filename);
}
printf("}\n");
return -1;
}
static int
failure(int e, const char *message)
{
printf("Status: 500 Internal Server failure\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("%s", message);
if (e)
printf(": %s", strerror(e));
return -1;
}
/*
static int
filecopy(void)
{
int len;
char buf[4096];
if (!st.filedata)
{
close(st.tempfd);
errno = EINVAL;
return response(false, "No file data received");
}
if (lseek(st.tempfd, 0, SEEK_SET) < 0)
{
close(st.tempfd);
return response(false, "Failed to rewind temp file");
}
st.filefd = open(st.filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (st.filefd < 0)
{
close(st.tempfd);
return response(false, "Failed to open target file");
}
while ((len = read(st.tempfd, buf, sizeof(buf))) > 0)
{
if (write(st.filefd, buf, len) != len)
{
close(st.tempfd);
close(st.filefd);
return response(false, "I/O failure while writing target file");
}
}
close(st.tempfd);
close(st.filefd);
if (chmod(st.filename, st.filemode))
return response(false, "Failed to chmod target file");
return 0;
}
*/
static int
header_field(multipart_parser *p, const char *data, size_t len)
{
st.is_content_disposition = !strncasecmp(data, "Content-Disposition", len);
return 0;
}
static int
header_value(multipart_parser *p, const char *data, size_t len)
{
int i, j;
if (!st.is_content_disposition)
return 0;
if (len < 10 || strncasecmp(data, "form-data", 9))
return 0;
for (data += 9, len -= 9; *data == ' ' || *data == ';'; data++, len--);
if (len < 8 || strncasecmp(data, "name=\"", 6))
return 0;
for (data += 6, len -= 6, i = 0; i <= len; i++)
{
if (*(data + i) != '"')
continue;
for (j = 1; j < sizeof(parts) / sizeof(parts[0]); j++)
if (!strncmp(data, parts[j], i))
st.parttype = j;
break;
}
return 0;
}
static int
data_begin_cb(multipart_parser *p)
{
if (st.parttype == PART_FILEDATA)
{
if (!st.sessionid)
return response(false, "File data without session");
if (!st.filename)
return response(false, "File data without name");
st.filefd = open(st.filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (st.filefd < 0)
return response(false, "Failed to create file");
//unlink(tmpname);
}
return 0;
}
static int
data_cb(multipart_parser *p, const char *data, size_t len)
{
switch (st.parttype)
{
case PART_SESSIONID:
st.sessionid = datadup(data, len);
break;
case PART_FILENAME:
st.filename = datadup(data, len);
break;
case PART_FILEMODE:
st.filemode = strtoul(data, NULL, 8);
break;
case PART_FILEDATA:
if (write(st.filefd, data, len) != len)
{
close(st.filefd);
return response(false, "I/O failure while writing temporary file");
}
if (!st.filedata)
st.filedata = !!len;
break;
default:
break;
}
return 0;
}
static int
data_end_cb(multipart_parser *p)
{
if (st.parttype == PART_SESSIONID)
{
if (!session_access(st.sessionid, "upload", "write"))
{
errno = EPERM;
return response(false, "Upload permission denied");
}
}
else if (st.parttype == PART_FILEDATA)
{
if (st.filefd < 0)
return response(false, "Internal program failure");
//if (filecopy())
// return -1;
return response(true, NULL);
}
st.parttype = PART_UNKNOWN;
return 0;
}
static multipart_parser *
init_parser(void)
{
char *boundary;
const char *var;
multipart_parser *p;
static multipart_parser_settings s = {
.on_part_data = data_cb,
.on_headers_complete = data_begin_cb,
.on_part_data_end = data_end_cb,
.on_header_field = header_field,
.on_header_value = header_value
};
var = getenv("CONTENT_TYPE");
if (!var || strncmp(var, "multipart/form-data;", 20))
return NULL;
for (var += 20; *var && *var != '='; var++);
if (*var++ != '=')
return NULL;
boundary = malloc(strlen(var) + 3);
if (!boundary)
return NULL;
strcpy(boundary, "--");
strcpy(boundary + 2, var);
st.filefd = -1;
st.filemode = 0600;
p = multipart_parser_init(boundary, &s);
free(boundary);
return p;
}
static int
main_upload(int argc, char *argv[])
{
int rem, len;
char buf[4096];
multipart_parser *p;
p = init_parser();
if (!p)
{
errno = EINVAL;
return response(false, "Invalid request");
}
while ((len = read(0, buf, sizeof(buf))) > 0)
{
rem = multipart_parser_execute(p, buf, len);
if (rem < len)
break;
}
multipart_parser_free(p);
/* read remaining post data */
while ((len = read(0, buf, sizeof(buf))) > 0);
return 0;
}
/*
static int exec_command(const char *cmd, char **out){
FILE *fd;
int buffer_size = 4096;
char *data = malloc(buffer_size);
char *pdata = data;
int c;
if ((fd = popen(cmd, "r"))) {
while((c = fgetc(fd)) != EOF){
*pdata = c;
pdata++;
if(pdata == (data + buffer_size)){
size_t size = buffer_size;
buffer_size += 4096;
data = realloc(data, buffer_size);
pdata = data + size;
}
}
pclose(fd);
}
*out = data;
return (int)(pdata - data);
}*/
static void run_child(const char *cmd){
FILE *fd;
int c;
if ((fd = popen(cmd, "r"))) {
while((c = fgetc(fd)) != EOF){
putc(c, stdout);
}
pclose(fd);
}
}
static int
main_backup(int argc, char **argv)
{
//pid_t pid;
time_t now;
/*int len;
int fds[2];
char buf[4096];*/
char datestr[16] = { 0 };
char hostname[64] = { 0 };
char *fields[] = { "sessionid", NULL, "password", NULL };
if (!postdecode(fields, 2) || !session_access(fields[1], "backup", "read"))
return failure(0, "Backup permission denied");
now = time(NULL);
strftime(datestr, sizeof(datestr) - 1, "%Y-%m-%d", localtime(&now));
if (gethostname(hostname, sizeof(hostname) - 1))
sprintf(hostname, "OpenWrt");
printf("Status: 200 OK\r\n");
char cmd[256];
const char *enctype = "";
if(fields[3] && strlen(fields[3]) > 0){
snprintf(cmd, sizeof(cmd), "sysupgrade --create-backup - --password %s", fields[3]);
enctype = "encrypted";
} else {
snprintf(cmd, sizeof(cmd), "sysupgrade --create-backup -");
}
printf("Content-Type: application/x-targz\r\n");
printf("Content-Disposition: attachment; "
"filename=\"backup-%s-%s-%s.tar.gz\"\r\n\r\n", enctype, hostname, datestr);
run_child(cmd);
/*
if (pipe(fds))
return failure(errno, "Failed to spawn pipe");
switch ((pid = fork()))
{
case -1:
return failure(errno, "Failed to fork process");
case 0:
dup2(fds[1], 1);
close(0);
close(2);
close(fds[0]);
close(fds[1]);
chdir("/");
if(fields[3]){
execl("/sbin/sysupgrade", "/sbin/sysupgrade",
"--create-backup", "-", "--password", fields[3] , NULL);
} else {
execl("/sbin/sysupgrade", "/sbin/sysupgrade",
"--create-backup", "-", NULL);
}
return -1;
default:
now = time(NULL);
strftime(datestr, sizeof(datestr) - 1, "%Y-%m-%d", localtime(&now));
if (gethostname(hostname, sizeof(hostname) - 1))
sprintf(hostname, "OpenWrt");
printf("Status: 200 OK\r\n");
printf("Content-Type: application/x-targz\r\n");
printf("Content-Disposition: attachment; "
"filename=\"backup-%s-%s.tar.gz\"\r\n\r\n", hostname, datestr);
while ((len = read(fds[0], buf, sizeof(buf))) > 0)
fwrite(buf, len, 1, stdout);
waitpid(pid, NULL, 0);
close(fds[0]);
close(fds[1]);
return 0;
}*/
return 0;
}
int main(int argc, char **argv)
{
if (strstr(argv[0], "juci-upload"))
return main_upload(argc, argv);
else if (strstr(argv[0], "juci-backup"))
return main_backup(argc, argv);
return -1;
}

View file

@ -1,309 +0,0 @@
/* Based on node-formidable by Felix Geisendörfer
* Igor Afonov - afonov@gmail.com - 2012
* MIT License - http://www.opensource.org/licenses/mit-license.php
*/
#include "multipart_parser.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
static void multipart_log(const char * format, ...)
{
#ifdef DEBUG_MULTIPART
va_list args;
va_start(args, format);
fprintf(stderr, "[HTTP_MULTIPART_PARSER] %s:%d: ", __FILE__, __LINE__);
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
#endif
}
#define NOTIFY_CB(FOR) \
do { \
if (p->settings->on_##FOR) { \
if (p->settings->on_##FOR(p) != 0) { \
return i; \
} \
} \
} while (0)
#define EMIT_DATA_CB(FOR, ptr, len) \
do { \
if (p->settings->on_##FOR) { \
if (p->settings->on_##FOR(p, ptr, len) != 0) { \
return i; \
} \
} \
} while (0)
#define LF 10
#define CR 13
struct multipart_parser {
void * data;
size_t index;
size_t boundary_length;
unsigned char state;
const multipart_parser_settings* settings;
char* lookbehind;
char multipart_boundary[1];
};
enum state {
s_uninitialized = 1,
s_start,
s_start_boundary,
s_header_field_start,
s_header_field,
s_headers_almost_done,
s_header_value_start,
s_header_value,
s_header_value_almost_done,
s_part_data_start,
s_part_data,
s_part_data_almost_boundary,
s_part_data_boundary,
s_part_data_almost_end,
s_part_data_end,
s_part_data_final_hyphen,
s_end
};
multipart_parser* multipart_parser_init
(const char *boundary, const multipart_parser_settings* settings) {
multipart_parser* p = malloc(sizeof(multipart_parser) +
strlen(boundary) +
strlen(boundary) + 9);
strcpy(p->multipart_boundary, boundary);
p->boundary_length = strlen(boundary);
p->lookbehind = (p->multipart_boundary + p->boundary_length + 1);
p->index = 0;
p->state = s_start;
p->settings = settings;
return p;
}
void multipart_parser_free(multipart_parser* p) {
free(p);
}
void multipart_parser_set_data(multipart_parser *p, void *data) {
p->data = data;
}
void *multipart_parser_get_data(multipart_parser *p) {
return p->data;
}
size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len) {
size_t i = 0;
size_t mark = 0;
char c, cl;
int is_last = 0;
while(i < len) {
c = buf[i];
is_last = (i == (len - 1));
switch (p->state) {
case s_start:
multipart_log("s_start");
p->index = 0;
p->state = s_start_boundary;
/* fallthrough */
case s_start_boundary:
multipart_log("s_start_boundary");
if (p->index == p->boundary_length) {
if (c != CR) {
return i;
}
p->index++;
break;
} else if (p->index == (p->boundary_length + 1)) {
if (c != LF) {
return i;
}
p->index = 0;
NOTIFY_CB(part_data_begin);
p->state = s_header_field_start;
break;
}
if (c != p->multipart_boundary[p->index]) {
return i;
}
p->index++;
break;
case s_header_field_start:
multipart_log("s_header_field_start");
mark = i;
p->state = s_header_field;
/* fallthrough */
case s_header_field:
multipart_log("s_header_field");
if (c == CR) {
p->state = s_headers_almost_done;
break;
}
if (c == '-') {
break;
}
if (c == ':') {
EMIT_DATA_CB(header_field, buf + mark, i - mark);
p->state = s_header_value_start;
break;
}
cl = tolower(c);
if (cl < 'a' || cl > 'z') {
multipart_log("invalid character in header name");
return i;
}
if (is_last)
EMIT_DATA_CB(header_field, buf + mark, (i - mark) + 1);
break;
case s_headers_almost_done:
multipart_log("s_headers_almost_done");
if (c != LF) {
return i;
}
p->state = s_part_data_start;
break;
case s_header_value_start:
multipart_log("s_header_value_start");
if (c == ' ') {
break;
}
mark = i;
p->state = s_header_value;
/* fallthrough */
case s_header_value:
multipart_log("s_header_value");
if (c == CR) {
EMIT_DATA_CB(header_value, buf + mark, i - mark);
p->state = s_header_value_almost_done;
}
if (is_last)
EMIT_DATA_CB(header_value, buf + mark, (i - mark) + 1);
break;
case s_header_value_almost_done:
multipart_log("s_header_value_almost_done");
if (c != LF) {
return i;
}
p->state = s_header_field_start;
break;
case s_part_data_start:
multipart_log("s_part_data_start");
NOTIFY_CB(headers_complete);
mark = i;
p->state = s_part_data;
/* fallthrough */
case s_part_data:
multipart_log("s_part_data");
if (c == CR) {
EMIT_DATA_CB(part_data, buf + mark, i - mark);
mark = i;
p->state = s_part_data_almost_boundary;
p->lookbehind[0] = CR;
break;
}
if (is_last)
EMIT_DATA_CB(part_data, buf + mark, (i - mark) + 1);
break;
case s_part_data_almost_boundary:
multipart_log("s_part_data_almost_boundary");
if (c == LF) {
p->state = s_part_data_boundary;
p->lookbehind[1] = LF;
p->index = 0;
break;
}
EMIT_DATA_CB(part_data, p->lookbehind, 1);
p->state = s_part_data;
mark = i --;
break;
case s_part_data_boundary:
multipart_log("s_part_data_boundary");
if (p->multipart_boundary[p->index] != c) {
EMIT_DATA_CB(part_data, p->lookbehind, 2 + p->index);
p->state = s_part_data;
mark = i --;
break;
}
p->lookbehind[2 + p->index] = c;
if ((++ p->index) == p->boundary_length) {
NOTIFY_CB(part_data_end);
p->state = s_part_data_almost_end;
}
break;
case s_part_data_almost_end:
multipart_log("s_part_data_almost_end");
if (c == '-') {
p->state = s_part_data_final_hyphen;
break;
}
if (c == CR) {
p->state = s_part_data_end;
break;
}
return i;
case s_part_data_final_hyphen:
multipart_log("s_part_data_final_hyphen");
if (c == '-') {
NOTIFY_CB(body_end);
p->state = s_end;
break;
}
return i;
case s_part_data_end:
multipart_log("s_part_data_end");
if (c == LF) {
p->state = s_header_field_start;
NOTIFY_CB(part_data_begin);
break;
}
return i;
case s_end:
multipart_log("s_end: %02X", (int) c);
break;
default:
multipart_log("Multipart parser unrecoverable error");
return 0;
}
++ i;
}
return len;
}

View file

@ -1,48 +0,0 @@
/* Based on node-formidable by Felix Geisendörfer
* Igor Afonov - afonov@gmail.com - 2012
* MIT License - http://www.opensource.org/licenses/mit-license.php
*/
#ifndef _multipart_parser_h
#define _multipart_parser_h
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdlib.h>
#include <ctype.h>
typedef struct multipart_parser multipart_parser;
typedef struct multipart_parser_settings multipart_parser_settings;
typedef struct multipart_parser_state multipart_parser_state;
typedef int (*multipart_data_cb) (multipart_parser*, const char *at, size_t length);
typedef int (*multipart_notify_cb) (multipart_parser*);
struct multipart_parser_settings {
multipart_data_cb on_header_field;
multipart_data_cb on_header_value;
multipart_data_cb on_part_data;
multipart_notify_cb on_part_data_begin;
multipart_notify_cb on_headers_complete;
multipart_notify_cb on_part_data_end;
multipart_notify_cb on_body_end;
};
multipart_parser* multipart_parser_init
(const char *boundary, const multipart_parser_settings* settings);
void multipart_parser_free(multipart_parser* p);
size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len);
void multipart_parser_set_data(multipart_parser* p, void* data);
void * multipart_parser_get_data(multipart_parser* p);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

File diff suppressed because it is too large Load diff