Compare commits

...

13 Commits

Author SHA1 Message Date
e029aae1d3 Import xwrite(), cstr_set(), resolve_..._template()
From degesch and json-rpc-shell.
2020-10-10 04:31:52 +02:00
b9457c321f Rename cstr_transform() argument
It does not always have to be tolower().
2020-10-10 04:30:19 +02:00
2201becca4 Mark some issues 2020-10-10 04:29:41 +02:00
7023c51347 Get rid of CMake dev warnings 2020-10-02 06:47:34 +02:00
d21f8466b5 Bump copyright years 2020-10-02 06:43:16 +02:00
7f919025ee Add iscntrl_ascii()
It's too easy to miss the DEL character.
2020-10-02 06:31:46 +02:00
1a76b2032e Add a slogan of sorts 2020-08-01 14:03:23 +02:00
722ef65c1f Name change 2020-08-01 14:02:25 +02:00
317dfcb6e2 Improve setjmp safety in config parser 2020-04-19 07:02:13 +02:00
bca7167d03 Fix the SCGI parser and tests 2018-10-18 06:34:16 +02:00
3e4e4e5103 Allow aborting the FastCGI protocol parser 2018-10-18 04:08:47 +02:00
9494e8e2af Add some comments 2018-10-11 21:02:45 +02:00
8ffe20c0e8 Add missing include for "struct iovec" 2018-06-24 06:09:40 +02:00
8 changed files with 140 additions and 52 deletions

View File

@@ -1,4 +1,4 @@
Copyright (c) 2014 - 2018, Přemysl Janouch <p@janouch.name> Copyright (c) 2014 - 2020, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted. purpose with or without fee is hereby granted.

View File

@@ -1,17 +1,17 @@
# Public Domain # Public Domain
find_package (PkgConfig REQUIRED) find_package (PkgConfig REQUIRED)
pkg_check_modules (NCURSESW QUIET ncursesw) pkg_check_modules (Ncursesw QUIET ncursesw)
# OpenBSD doesn't provide a pkg-config file # OpenBSD doesn't provide a pkg-config file
set (required_vars NCURSESW_LIBRARIES) set (required_vars Ncursesw_LIBRARIES)
if (NOT NCURSESW_FOUND) if (NOT Ncursesw_FOUND)
find_library (NCURSESW_LIBRARIES NAMES ncursesw) find_library (Ncursesw_LIBRARIES NAMES ncursesw)
find_path (NCURSESW_INCLUDE_DIRS ncurses.h) find_path (Ncursesw_INCLUDE_DIRS ncurses.h)
list (APPEND required_vars NCURSESW_INCLUDE_DIRS) list (APPEND required_vars Ncursesw_INCLUDE_DIRS)
endif (NOT NCURSESW_FOUND) endif (NOT Ncursesw_FOUND)
include (FindPackageHandleStandardArgs) include (FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (NCURSESW DEFAULT_MSG ${required_vars}) FIND_PACKAGE_HANDLE_STANDARD_ARGS (Ncursesw DEFAULT_MSG ${required_vars})
mark_as_advanced (NCURSESW_LIBRARIES NCURSESW_INCLUDE_DIRS) mark_as_advanced (Ncursesw_LIBRARIES Ncursesw_INCLUDE_DIRS)

View File

@@ -1,10 +1,10 @@
# Public Domain # Public Domain
find_path (UNISTRING_INCLUDE_DIRS unistr.h) find_path (Unistring_INCLUDE_DIRS unistr.h)
find_library (UNISTRING_LIBRARIES NAMES unistring libunistring) find_library (Unistring_LIBRARIES NAMES unistring libunistring)
include (FindPackageHandleStandardArgs) include (FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (UNISTRING DEFAULT_MSG FIND_PACKAGE_HANDLE_STANDARD_ARGS (Unistring DEFAULT_MSG
UNISTRING_INCLUDE_DIRS UNISTRING_LIBRARIES) Unistring_INCLUDE_DIRS Unistring_LIBRARIES)
mark_as_advanced (UNISTRING_LIBRARIES UNISTRING_INCLUDE_DIRS) mark_as_advanced (Unistring_LIBRARIES Unistring_INCLUDE_DIRS)

View File

@@ -1,7 +1,7 @@
/* /*
* liberty-proto.c: the ultimate C unlibrary: protocols * liberty-proto.c: the ultimate C unlibrary: protocols
* *
* Copyright (c) 2014 - 2016, Přemysl Janouch <p@janouch.name> * Copyright (c) 2014 - 2016, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.
@@ -213,6 +213,7 @@ irc_fnmatch (const char *pattern, const char *string)
char x_pattern[pattern_size], x_string[string_size]; char x_pattern[pattern_size], x_string[string_size];
irc_strxfrm (x_pattern, pattern, pattern_size); irc_strxfrm (x_pattern, pattern, pattern_size);
irc_strxfrm (x_string, string, string_size); irc_strxfrm (x_string, string, string_size);
// FIXME: this supports [], which is not mentioned in RFC 2812
return fnmatch (x_pattern, x_string, 0); return fnmatch (x_pattern, x_string, 0);
} }
@@ -663,10 +664,11 @@ scgi_parser_push (struct scgi_parser *self,
if (digit == ':') if (digit == ':')
{ {
self->state = SCGI_READING_NAME; self->state = SCGI_READING_NAME;
str_remove_slice (&self->input, 0, 1);
break; break;
} }
if (digit < '0' || digit >= '9') if (digit < '0' || digit > '9')
return error_set (e, "invalid header netstring"); return error_set (e, "invalid header netstring");
size_t new_len = self->headers_len * 10 + (digit - '0'); size_t new_len = self->headers_len * 10 + (digit - '0');
@@ -699,6 +701,7 @@ scgi_parser_push (struct scgi_parser *self,
self->state = SCGI_READING_VALUE; self->state = SCGI_READING_VALUE;
str_remove_slice (&self->input, 0, 1); str_remove_slice (&self->input, 0, 1);
self->headers_len--;
break; break;
} }
case SCGI_READING_VALUE: case SCGI_READING_VALUE:
@@ -727,6 +730,7 @@ scgi_parser_push (struct scgi_parser *self,
} }
str_remove_slice (&self->input, 0, 1); str_remove_slice (&self->input, 0, 1);
self->headers_len--;
break; break;
} }
case SCGI_READING_CONTENT: case SCGI_READING_CONTENT:
@@ -791,7 +795,8 @@ enum fcgi_protocol_status
struct fcgi_parser; struct fcgi_parser;
typedef void (*fcgi_message_fn) /// Message handler, returns false if further processing should be stopped
typedef bool (*fcgi_message_fn)
(const struct fcgi_parser *parser, void *user_data); (const struct fcgi_parser *parser, void *user_data);
enum fcgi_parser_state enum fcgi_parser_state
@@ -853,7 +858,7 @@ fcgi_parser_unpack_header (struct fcgi_parser *self)
str_remove_slice (&self->input, 0, unpacker.offset); str_remove_slice (&self->input, 0, unpacker.offset);
} }
static void static bool
fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len) fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
{ {
// This could be made considerably faster for high-throughput applications // This could be made considerably faster for high-throughput applications
@@ -865,14 +870,14 @@ fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
{ {
case FCGI_READING_HEADER: case FCGI_READING_HEADER:
if (self->input.len < FCGI_HEADER_LEN) if (self->input.len < FCGI_HEADER_LEN)
return; return true;
fcgi_parser_unpack_header (self); fcgi_parser_unpack_header (self);
self->state = FCGI_READING_CONTENT; self->state = FCGI_READING_CONTENT;
break; break;
case FCGI_READING_CONTENT: case FCGI_READING_CONTENT:
if (self->input.len < self->content_length) if (self->input.len < self->content_length)
return; return true;
// Move an appropriate part of the input buffer to the content buffer // Move an appropriate part of the input buffer to the content buffer
str_reset (&self->content); str_reset (&self->content);
@@ -882,10 +887,11 @@ fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
break; break;
case FCGI_READING_PADDING: case FCGI_READING_PADDING:
if (self->input.len < self->padding_length) if (self->input.len < self->padding_length)
return; return true;
// Call the callback to further process the message // Call the callback to further process the message
self->on_message (self, self->user_data); if (!self->on_message (self, self->user_data))
return false;
// Remove the padding from the input buffer // Remove the padding from the input buffer
str_remove_slice (&self->input, 0, self->padding_length); str_remove_slice (&self->input, 0, self->padding_length);
@@ -1188,8 +1194,10 @@ ws_parser_unmask (char *payload, uint64_t len, uint32_t mask)
{ {
case 3: case 3:
payload[end + 2] ^= (mask >> 8) & 0xFF; payload[end + 2] ^= (mask >> 8) & 0xFF;
// Fall-through
case 2: case 2:
payload[end + 1] ^= (mask >> 16) & 0xFF; payload[end + 1] ^= (mask >> 16) & 0xFF;
// Fall-through
case 1: case 1:
payload[end ] ^= (mask >> 24) & 0xFF; payload[end ] ^= (mask >> 24) & 0xFF;
} }
@@ -1738,6 +1746,8 @@ mpd_client_send_command (struct mpd_client *self, const char *command, ...)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/// "On success for all commands, OK is returned. If a command fails, no more
/// commands are executed and the appropriate ACK error is returned"
static void static void
mpd_client_list_begin (struct mpd_client *self) mpd_client_list_begin (struct mpd_client *self)
{ {

View File

@@ -1,7 +1,7 @@
/* /*
* liberty-tui.c: the ultimate C unlibrary: TUI * liberty-tui.c: the ultimate C unlibrary: TUI
* *
* Copyright (c) 2016 - 2017, Přemysl Janouch <p@janouch.name> * Copyright (c) 2016 - 2017, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.

View File

@@ -1,7 +1,7 @@
/* /*
* liberty.c: the ultimate C unlibrary * liberty.c: the ultimate C unlibrary
* *
* Copyright (c) 2014 - 2018, Přemysl Janouch <p@janouch.name> * Copyright (c) 2014 - 2020, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.
@@ -37,6 +37,7 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/uio.h>
#include <fcntl.h> #include <fcntl.h>
#include <poll.h> #include <poll.h>
#include <signal.h> #include <signal.h>
@@ -116,6 +117,9 @@ extern char **environ;
#define CONTAINER_OF(pointer, type, member) \ #define CONTAINER_OF(pointer, type, member) \
((type *) ((char *) pointer - offsetof (type, member))) ((type *) ((char *) pointer - offsetof (type, member)))
char *liberty = "They who can give up essential liberty to obtain a little "
"temporary safety deserve neither liberty nor safety.";
// --- Logging ----------------------------------------------------------------- // --- Logging -----------------------------------------------------------------
static void static void
@@ -727,6 +731,21 @@ set_blocking (int fd, bool blocking)
return prev; return prev;
} }
static bool
xwrite (int fd, const char *data, size_t len, struct error **e)
{
size_t written = 0;
while (written < len)
{
ssize_t res = write (fd, data + written, len - written);
if (res >= 0)
written += res;
else if (errno != EINTR)
return error_set (e, "%s", strerror (errno));
}
return true;
}
static void static void
xclose (int fd) xclose (int fd)
{ {
@@ -1092,7 +1111,7 @@ struct async
LIST_HEADER (struct async) LIST_HEADER (struct async)
struct async_manager *manager; ///< Our manager object struct async_manager *manager; ///< Our manager object
// "cancelled" may not be accesed or modified by the worker thread // "cancelled" may not be accessed or modified by the worker thread
pthread_t worker; ///< Worker thread ID pthread_t worker; ///< Worker thread ID
bool started; ///< Worker thread ID is valid bool started; ///< Worker thread ID is valid
@@ -1353,6 +1372,7 @@ struct poller_idle
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// The heap could definitely be made faster but we'll prefer simplicity
struct poller_timers struct poller_timers
{ {
struct poller_timer **heap; ///< Min-heap of timers struct poller_timer **heap; ///< Min-heap of timers
@@ -2668,6 +2688,12 @@ strncasecmp_ascii (const char *a, const char *b, size_t n)
return 0; return 0;
} }
static bool
iscntrl_ascii (int c)
{
return (c >= 0 && c < 32) || c == 0x7f;
}
static bool static bool
isalpha_ascii (int c) isalpha_ascii (int c)
{ {
@@ -2913,6 +2939,13 @@ base64_encode (const void *data, size_t len, struct str *output)
// --- Utilities --------------------------------------------------------------- // --- Utilities ---------------------------------------------------------------
static void
cstr_set (char **s, char *new)
{
free (*s);
*s = new;
}
static void static void
cstr_split (const char *s, const char *delimiters, bool ignore_empty, cstr_split (const char *s, const char *delimiters, bool ignore_empty,
struct strv *out) struct strv *out)
@@ -2942,10 +2975,10 @@ cstr_strip_in_place (char *s, const char *stripped_chars)
} }
static void static void
cstr_transform (char *s, int (*tolower) (int c)) cstr_transform (char *s, int (*xform) (int c))
{ {
for (; *s; s++) for (; *s; s++)
*s = tolower (*s); *s = xform (*s);
} }
static char * static char *
@@ -2992,6 +3025,7 @@ iconv_xstrdup (iconv_t conv, char *in, size_t in_len, size_t *out_len)
char *in_ptr = in; char *in_ptr = in;
if (in_len == (size_t) -1) if (in_len == (size_t) -1)
// XXX: out_len will be one character longer than the string!
in_len = strlen (in) + 1; in_len = strlen (in) + 1;
while (iconv (conv, (char **) &in_ptr, &in_len, while (iconv (conv, (char **) &in_ptr, &in_len,
@@ -3251,16 +3285,8 @@ resolve_relative_data_filename (const char *filename)
} }
static char * static char *
resolve_relative_runtime_filename (const char *filename) resolve_relative_runtime_filename_finish (struct str path)
{ {
struct str path = str_make ();
const char *runtime_dir = getenv ("XDG_RUNTIME_DIR");
if (runtime_dir && *runtime_dir == '/')
str_append (&path, runtime_dir);
else
get_xdg_home_dir (&path, "XDG_DATA_HOME", ".local/share");
str_append_printf (&path, "/%s/%s", PROGRAM_NAME, filename);
// Try to create the file's ancestors; // Try to create the file's ancestors;
// typically the user will want to immediately create a file in there // typically the user will want to immediately create a file in there
const char *last_slash = strrchr (path.str, '/'); const char *last_slash = strrchr (path.str, '/');
@@ -3273,6 +3299,41 @@ resolve_relative_runtime_filename (const char *filename)
return str_steal (&path); return str_steal (&path);
} }
static char *
resolve_relative_runtime_filename (const char *filename)
{
struct str path = str_make ();
const char *runtime_dir = getenv ("XDG_RUNTIME_DIR");
if (runtime_dir && *runtime_dir == '/')
str_append (&path, runtime_dir);
else
get_xdg_home_dir (&path, "XDG_DATA_HOME", ".local/share");
str_append_printf (&path, "/%s/%s", PROGRAM_NAME, filename);
return resolve_relative_runtime_filename_finish (path);
}
/// This differs from resolve_relative_runtime_filename() in that we expect
/// the filename to be something like a pattern for mkstemp(), so the resulting
/// path can reside in a system-wide directory with no risk of a conflict.
/// However, we have to take care about permissions. Do we even need this?
static char *
resolve_relative_runtime_template (const char *template)
{
struct str path = str_make ();
const char *runtime_dir = getenv ("XDG_RUNTIME_DIR");
const char *tmpdir = getenv ("TMPDIR");
if (runtime_dir && *runtime_dir == '/')
str_append_printf (&path, "%s/%s", runtime_dir, PROGRAM_NAME);
else if (tmpdir && *tmpdir == '/')
str_append_printf (&path, "%s/%s.%d", tmpdir, PROGRAM_NAME, geteuid ());
else
str_append_printf (&path, "/tmp/%s.%d", PROGRAM_NAME, geteuid ());
str_append_printf (&path, "/%s", template);
return resolve_relative_runtime_filename_finish (path);
}
static char * static char *
try_expand_tilde (const char *filename) try_expand_tilde (const char *filename)
{ {
@@ -5253,7 +5314,9 @@ static struct config_item *
config_item_parse (const char *script, size_t len, config_item_parse (const char *script, size_t len,
bool single_value_only, struct error **e) bool single_value_only, struct error **e)
{ {
struct config_parser parser = config_parser_make (script, len); volatile struct config_parser parser = config_parser_make (script, len);
struct config_parser *volatile self = (struct config_parser *) &parser;
struct config_item *volatile object = NULL; struct config_item *volatile object = NULL;
jmp_buf err; jmp_buf err;
@@ -5275,13 +5338,13 @@ config_item_parse (const char *script, size_t len,
// This is really only intended for in-program configuration // This is really only intended for in-program configuration
// and telling the line number would look awkward // and telling the line number would look awkward
parser.tokenizer.report_line = false; parser.tokenizer.report_line = false;
object = config_parser_parse_value (&parser, err); object = config_parser_parse_value (self, err);
} }
else else
object = config_parser_parse_object (&parser, err); object = config_parser_parse_object (self, err);
config_parser_expect (&parser, CONFIG_T_ABORT, err); config_parser_expect (self, CONFIG_T_ABORT, err);
end: end:
config_parser_free (&parser); config_parser_free (self);
return object; return object;
} }

View File

@@ -1,7 +1,7 @@
/* /*
* tests/liberty.c * tests/liberty.c
* *
* Copyright (c) 2015 - 2016, Přemysl Janouch <p@janouch.name> * Copyright (c) 2015 - 2016, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.

View File

@@ -1,7 +1,7 @@
/* /*
* tests/proto.c * tests/proto.c
* *
* Copyright (c) 2015, Přemysl Janouch <p@janouch.name> * Copyright (c) 2015, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.
@@ -105,10 +105,20 @@ test_http_parser (void)
http_protocol_destroy (iter); http_protocol_destroy (iter);
} }
struct scgi_fixture
{
struct scgi_parser parser;
bool seen_headers;
bool seen_content;
};
static bool static bool
test_scgi_parser_on_headers_read (void *user_data) test_scgi_parser_on_headers_read (void *user_data)
{ {
struct scgi_parser *parser = user_data; struct scgi_fixture *fixture = user_data;
struct scgi_parser *parser = &fixture->parser;
fixture->seen_headers = true;
soft_assert (parser->headers.len == 4); soft_assert (parser->headers.len == 4);
soft_assert (!strcmp (str_map_find (&parser->headers, soft_assert (!strcmp (str_map_find (&parser->headers,
"CONTENT_LENGTH"), "27")); "CONTENT_LENGTH"), "27"));
@@ -124,7 +134,9 @@ test_scgi_parser_on_headers_read (void *user_data)
static bool static bool
test_scgi_parser_on_content (void *user_data, const void *data, size_t len) test_scgi_parser_on_content (void *user_data, const void *data, size_t len)
{ {
(void) user_data; struct scgi_fixture *fixture = user_data;
fixture->seen_content = true;
soft_assert (!strncmp (data, "What is the answer to life?", len)); soft_assert (!strncmp (data, "What is the answer to life?", len));
return true; return true;
} }
@@ -132,10 +144,12 @@ test_scgi_parser_on_content (void *user_data, const void *data, size_t len)
static void static void
test_scgi_parser (void) test_scgi_parser (void)
{ {
struct scgi_parser parser = scgi_parser_make (); struct scgi_fixture fixture = { scgi_parser_make(), false, false };
parser.on_headers_read = test_scgi_parser_on_headers_read; struct scgi_parser *parser = &fixture.parser;
parser.on_content = test_scgi_parser_on_content;
parser.user_data = &parser; parser->on_headers_read = test_scgi_parser_on_headers_read;
parser->on_content = test_scgi_parser_on_content;
parser->user_data = &fixture;
// This is an example straight from the specification // This is an example straight from the specification
const char example[] = const char example[] =
@@ -147,8 +161,9 @@ test_scgi_parser (void)
"," ","
"What is the answer to life?"; "What is the answer to life?";
soft_assert (scgi_parser_push (&parser, example, sizeof example, NULL)); soft_assert (scgi_parser_push (parser, example, sizeof example, NULL));
scgi_parser_free (&parser); soft_assert (fixture.seen_headers && fixture.seen_content);
scgi_parser_free (parser);
} }
static bool static bool