From patchwork Mon Oct 11 11:21:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefano Babic X-Patchwork-Id: 1539233 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20210112 header.b=ITNovxRU; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4864:20::43d; helo=mail-wr1-x43d.google.com; envelope-from=swupdate+bncbcxploxj6ikrb254scfqmgqez4ch7hi@googlegroups.com; receiver=) Received: from mail-wr1-x43d.google.com (mail-wr1-x43d.google.com [IPv6:2a00:1450:4864:20::43d]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HSbxC5J0Gz9sRN for ; Mon, 11 Oct 2021 22:22:23 +1100 (AEDT) Received: by mail-wr1-x43d.google.com with SMTP id d13-20020adfa34d000000b00160aa1cc5f1sf13029093wrb.14 for ; Mon, 11 Oct 2021 04:22:23 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1633951340; cv=pass; d=google.com; s=arc-20160816; b=lniohLisOYwuyvJYkt0uLfkZ3c7k4KfriqVS7QqJrrk9+qJIsFXxkprdx0/Ott8wJq f26dvWdQQiblzHHvnx6NTK5pYbD8JJSjLGmyfpqoOF4qbfMdzZmu03zNjy95K8uzZEMJ UjAv2cEETy2XeOj8HooehBzC+usvyx8uN7S5myNVGSxVf/eycSUvDXVeWNOf6ZWXMmst 3z+qU5IbAOiZcEtkMq2GkTTv93n+/s2P8s+5kx/0ZkgAx7h8nRriF11nZOjRmkKsxCf2 zzpCT+sxI8wp72I2yWyNYa4+hhHsvQrcl6GncBQ06sanHHt0bXmXgck57SDlFGDf9maz /gzg== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:dkim-signature; bh=vus8v6wx5IEfU24Revv/PSIhTmaaxQ70ly4nJfRe1lM=; b=1IdkX8o4o1Tc+u36DRW7hCYjyM58e6gVrMUGto8eLNE0iEcF9CJkCM6moijvDs3SiA cZelA2Bu6j6guxQiIo1KywrQaOcpnkQrxN5M9zskOCVCcOqKal4hdfkQwiJj8MbIv0aU 9lGgGS8jYSrO9OFK6AMBA08sJgcvtyqyw7xF3NBnVeUWPIgt7kbV0rpAgegQIGlm160T 3OolF5d0MkKG5yANSoOyO/YhA4onq0usW3seIav7579E76+OYExB5a9h6WfkZnFfDoWX MBDDQm70mxchARuCxAhQyRN2Ds20ovxHLg6dazC7V6GXTtumX5x1Nw/4QumwKevgKJ6M gt9Q== ARC-Authentication-Results: i=2; gmr-mx.google.com; spf=neutral (google.com: 212.18.0.10 is neither permitted nor denied by domain of sbabic@denx.de) smtp.mailfrom=sbabic@denx.de DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20210112; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=vus8v6wx5IEfU24Revv/PSIhTmaaxQ70ly4nJfRe1lM=; b=ITNovxRUQ+ZBvbi9CvzO+pv8cMKmXNIP7ku70V7Pvr/McRfTlwQWbwmVfrQUHYq7a/ 7GqtSckCTuTCnGoPTeS1tJBH+RSEIRGIrVh9bp6SB7WwQRXBhAAEWEsNpxl48KxSccMX 9icyoy5CVPpO9ZZQ2uFojIKL2pRx5qSFms0PP3Q3tBVPJcuh3JdJXnEBUvY88VpN+gra n9agIRPsRDsk0f6jPt5nYSVQv62FFfhz89vYiOZAhFqNke75qvUki7HyOfbLWfJ/FCg2 mueKCyplNxjjrKNVM3gckrvaBuR0ggCgimGPXULNhKQ4vr/XLSP4DkTj+B8lcAUaaYNO 1Vmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :x-spam-checked-in-group:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=vus8v6wx5IEfU24Revv/PSIhTmaaxQ70ly4nJfRe1lM=; b=tUU1DEJdd2PuaQ61kX5su0I0N1ZGSq2i2AxDaPttUpTf3Rvf2D0jinoB3ydOiB7Kh6 oAEgXs8gIni5KAyD3nzrpXCpdeNplCvozj7wk3uA1RJCPXVKIKMAKryB0GCBdtFnLvp3 Srqu8kKksQJquFmcfdQ7lgwOKLhdt/mYFGgCQpDXwdkxgpEmsuz+g0gbtnRdbrmRZl8S 0NOI2wgvdHFBJBzrbVLum6W4fii4g6t1Tm+z2Yp0/rfJ+9O5JJQ2krD/PX9HYgulKNQA VmCMi4DDkM6c5VGDzg8IgbGMUE9tfPF3MjaWqgjhUqmcUejsI3dPZQBx6JuXlBzn1iiA h3Bg== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM5313H9mdTmzuOLJnlucBArb6IW2VUzYr17p63Qg/7X4qwxwvyxkP p8rvq/Ikgt4VvNh/986FLLo= X-Google-Smtp-Source: ABdhPJwrGXaKd206K7o/3hd3WLkpkXsWnWx0PosFHxj8111Tyz0NaM6psmA2d7GfJgWWEXm9rg3ukw== X-Received: by 2002:a1c:770b:: with SMTP id t11mr2594357wmi.19.1633951340144; Mon, 11 Oct 2021 04:22:20 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a05:600c:1caa:: with SMTP id k42ls7322213wms.3.canary-gmail; Mon, 11 Oct 2021 04:22:19 -0700 (PDT) X-Received: by 2002:a1c:7c02:: with SMTP id x2mr19594916wmc.165.1633951339362; Mon, 11 Oct 2021 04:22:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1633951339; cv=none; d=google.com; s=arc-20160816; b=W2qVqodz823CDtqg1zWRLafyCmJujbGaRku7+t1+mGPU1n4T6RFqoEMnyq2rvOOBIx ZzFGN29NsvqYIiL88tWlObZegPpun6M/2Ie7xevTacODlgavzYSd8FvzLA7jphrUBFGs kI30F1X/kX9QSYotqrcCjvgnhWGc9wFDcfboW0Z5Gw+/SAJvFZC2VlZPKfPNSuRisp6M 3GQ9KyinTDCKgamtfto7pGo5gwAKP++/PyPH0+SsrA2RhwnRz7T8mv7CssEzRX/DLryo gfns7AObDXn5M83OVJAervzAtK3pCf6tjgW3HC4CPipBQvCu+6fZlmePKnxIUa5deVKd XHhA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=VcaUqUQvvrrvEpfiP7HlotMxwK7bwjjLz3T4SjNrvT8=; b=vw+Hx6EXZSg2ejlJ9Z8SV7mGtik8ZwL3cDDwxOwTTwl2ReGJJVFKfpO7N//I3AAFZN zd59Cw+kr0Nr9j5Wi7C0PAd36zE2L7dAwn68zDl3aNXIUovC3rzRvk0xkT8xhLY4jNQG bTTmFqGX8bRhBOg5btIQ5l//w+XJa/f8VxdoEFIyxZCaLvE7HN3Ie2HHGStjWSK3pung 4xYwEq5eCto5jNUuLoIpIrz/pDitIXLTzYvgEROT45vI8UJ0MI3P2x/XTk8l0DSb/4Cn 83o9GiK/EgMB1JxDQ4qeRqa3be+QkT6LRrm/pr5cXOJlr4Y/7gIc+kiFsOZJ0mOheadJ 3rZA== ARC-Authentication-Results: i=1; gmr-mx.google.com; spf=neutral (google.com: 212.18.0.10 is neither permitted nor denied by domain of sbabic@denx.de) smtp.mailfrom=sbabic@denx.de Received: from mail-out.m-online.net (mail-out.m-online.net. [212.18.0.10]) by gmr-mx.google.com with ESMTPS id f9si47034wmg.2.2021.10.11.04.22.19 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 11 Oct 2021 04:22:19 -0700 (PDT) Received-SPF: neutral (google.com: 212.18.0.10 is neither permitted nor denied by domain of sbabic@denx.de) client-ip=212.18.0.10; Received: from frontend01.mail.m-online.net (unknown [192.168.8.182]) by mail-out.m-online.net (Postfix) with ESMTP id 4HSbx713Rdz1sByw; Mon, 11 Oct 2021 13:22:19 +0200 (CEST) Received: from localhost (dynscan1.mnet-online.de [192.168.6.70]) by mail.m-online.net (Postfix) with ESMTP id 4HSbx70wZhz1qqkB; Mon, 11 Oct 2021 13:22:19 +0200 (CEST) X-Virus-Scanned: amavisd-new at mnet-online.de Received: from mail.mnet-online.de ([192.168.8.182]) by localhost (dynscan1.mail.m-online.net [192.168.6.70]) (amavisd-new, port 10024) with ESMTP id GtMDl7K1o0Tg; Mon, 11 Oct 2021 13:22:17 +0200 (CEST) Received: from babic.homelinux.org (host-88-217-136-221.customer.m-online.net [88.217.136.221]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.mnet-online.de (Postfix) with ESMTPS; Mon, 11 Oct 2021 13:22:17 +0200 (CEST) Received: from localhost (mail.babic.homelinux.org [127.0.0.1]) by babic.homelinux.org (Postfix) with ESMTP id 24EE34540BF1; Mon, 11 Oct 2021 13:22:17 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at babic.homelinux.org Received: from babic.homelinux.org ([127.0.0.1]) by localhost (mail.babic.homelinux.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id fHqDgHWDS34y; Mon, 11 Oct 2021 13:22:14 +0200 (CEST) Received: from paperino.fritz.box (paperino.fritz.box [192.168.178.48]) by babic.homelinux.org (Postfix) with ESMTP id DE6DF4541719; Mon, 11 Oct 2021 13:22:04 +0200 (CEST) From: Stefano Babic To: swupdate@googlegroups.com Cc: Stefano Babic Subject: [swupdate] [PATCH 07/29] Import small multipart library Date: Mon, 11 Oct 2021 13:21:34 +0200 Message-Id: <20211011112156.44192-8-sbabic@denx.de> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211011112156.44192-1-sbabic@denx.de> References: <20211011112156.44192-1-sbabic@denx.de> MIME-Version: 1.0 X-Original-Sender: sbabic@denx.de X-Original-Authentication-Results: gmr-mx.google.com; spf=neutral (google.com: 212.18.0.10 is neither permitted nor denied by domain of sbabic@denx.de) smtp.mailfrom=sbabic@denx.de Precedence: list Mailing-list: list swupdate@googlegroups.com; contact swupdate+owners@googlegroups.com List-ID: X-Spam-Checked-In-Group: swupdate@googlegroups.com X-Google-Group-Id: 605343134186 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , This is a lightweight parser for C licensed under MIT license. Sources are available at https://github.com/iafonov/multipart-parser-c. Fix a warning due to missing va_end() and set SPDX header. Signed-off-by: Stefano Babic --- corelib/Makefile | 1 + corelib/multipart_parser.c | 306 +++++++++++++++++++++++++++++++++++++ include/multipart_parser.h | 49 ++++++ 3 files changed, 356 insertions(+) create mode 100644 corelib/multipart_parser.c create mode 100644 include/multipart_parser.h diff --git a/corelib/Makefile b/corelib/Makefile index 037e72f..d6ea6a2 100644 --- a/corelib/Makefile +++ b/corelib/Makefile @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: GPL-2.0-only +lib-y += multipart_parser.o lib-$(CONFIG_DOWNLOAD) += downloader.o lib-$(CONFIG_MTD) += mtd-interface.o lib-$(CONFIG_LUA) += lua_interface.o lua_compat.o diff --git a/corelib/multipart_parser.c b/corelib/multipart_parser.c new file mode 100644 index 0000000..5014dc8 --- /dev/null +++ b/corelib/multipart_parser.c @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2012 Igor Afonov afonov@gmail.com + * + * SPDX-License-Identifier: MIT + */ +#include "multipart_parser.h" +#include +#include +#include + +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"); + va_end(args); +#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 == ':') { + EMIT_DATA_CB(header_field, buf + mark, i - mark); + p->state = s_header_value_start; + break; + } + + cl = tolower(c); + if ((c != '-') && (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; + break; + } + 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; +} diff --git a/include/multipart_parser.h b/include/multipart_parser.h new file mode 100644 index 0000000..015e8ad --- /dev/null +++ b/include/multipart_parser.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012 Igor Afonov afonov@gmail.com + * + * SPDX-License-Identifier: MIT + */ +#ifndef _multipart_parser_h +#define _multipart_parser_h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +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