* Copyright (C) 2015 Red Hat, Inc.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#ifdef G_LOG_DOMAIN
#undef G_LOG_DOMAIN
#endif
#define G_LOG_DOMAIN "cockpit-protocol"
#include "cockpitwebinject.h"
#include <string.h>
* CockpitWebInject
*
* This is a CockpitWebFilter which looks for a marker data
* and inject additional data after that point. The data is
* not injected more than the specified number of times.
*/
struct _CockpitWebInject {
GObject parent;
* e.g. "ABA" with marker "ABAC" will result in [TRUE, FALSE, TRUE]
*/
GArray *partial_matches;
GBytes *marker;
GBytes *inject;
guint maximum;
guint injected;
};
static void cockpit_web_filter_inject_iface (CockpitWebFilterInterface *iface);
G_DEFINE_TYPE_WITH_CODE (CockpitWebInject, cockpit_web_inject, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (COCKPIT_TYPE_WEB_FILTER, cockpit_web_filter_inject_iface)
)
static void
cockpit_web_inject_init (CockpitWebInject *self)
{
}
static void
cockpit_web_inject_finalize (GObject *object)
{
CockpitWebInject *self = COCKPIT_WEB_INJECT (object);
if (self->partial_matches)
g_array_unref (self->partial_matches);
if (self->marker)
g_bytes_unref (self->marker);
if (self->inject)
g_bytes_unref (self->inject);
G_OBJECT_CLASS (cockpit_web_inject_parent_class)->finalize (object);
}
static void
cockpit_web_inject_class_init (CockpitWebInjectClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = cockpit_web_inject_finalize;
}
static void
cockpit_web_inject_push (CockpitWebFilter *filter,
GBytes *block,
void (* function) (gpointer, GBytes *),
gpointer func_data)
{
CockpitWebInject *self = (CockpitWebInject *)filter;
const gchar *mark, *data, *pos = NULL;
gsize mark_len, data_len, at;
GBytes *bytes;
gsize written;
gint partial_len, remaining_len;
mark = g_bytes_get_data (self->marker, &mark_len);
data = g_bytes_get_data (block, &data_len);
if (data_len == 0)
return;
written = at = 0;
* longest partial matches have precedence (they either get longer or don't pan out)
* only do this if we haven't reached the maximum yet
*/
if (self->injected < self->maximum)
{
for (partial_len = self->partial_matches->len - 1; partial_len >= 0; --partial_len)
{
if (g_array_index (self->partial_matches, gboolean, partial_len))
{
g_array_index (self->partial_matches, gboolean, partial_len) = FALSE;
remaining_len = mark_len - partial_len;
if (remaining_len > data_len)
{
if (memcmp (mark + partial_len, data, data_len) == 0)
g_array_index (self->partial_matches, gboolean, partial_len + data_len) = TRUE;
}
else if (memcmp (mark + partial_len, data, remaining_len) == 0)
{
at = remaining_len;
pos = data + at;
g_array_set_size(self->partial_matches, 0);
g_array_set_size(self->partial_matches, mark_len);
break;
}
}
}
}
for(;;)
{
if (at != written)
{
bytes = g_bytes_new_from_bytes (block, written, at - written);
function (func_data, bytes);
g_bytes_unref (bytes);
written = at;
if (pos == (data + at) && self->injected < self->maximum)
{
function (func_data, self->inject);
self->injected++;
}
}
if (at >= data_len)
break;
if (self->injected < self->maximum &&
data_len >= (at + mark_len) &&
(pos = memmem (data + at, data_len - at, mark, mark_len)))
{
pos += mark_len;
at = pos - data;
}
else
{
at = data_len;
pos = NULL;
}
}
partial_len = mark_len - 1;
if (partial_len > data_len)
partial_len = data_len;
while (partial_len > 0)
{
if (memcmp (mark, data + data_len - partial_len, partial_len) == 0)
g_array_index (self->partial_matches, gboolean, partial_len) = TRUE;
partial_len--;
}
}
static void
cockpit_web_filter_inject_iface (CockpitWebFilterInterface *iface)
{
iface->push = cockpit_web_inject_push;
}
* cockpit_web_filter_new:
* @marker: marker to search for
* @inject: bytes to inject after marker
* @count: number of times to inject
*
* Create a new CockpitWebFilter which injects @inject bytes
* after the @marker. It injects the data once.
*
* Returns: A new CockpitWebFilter
*/
CockpitWebFilter *
cockpit_web_inject_new (const gchar *marker,
GBytes *inject,
guint count)
{
CockpitWebInject *self;
gsize len;
g_return_val_if_fail (marker != NULL, NULL);
g_return_val_if_fail (inject != NULL, NULL);
len = strlen (marker);
g_return_val_if_fail (len > 0, NULL);
self = g_object_new (COCKPIT_TYPE_WEB_INJECT, NULL);
self->marker = g_bytes_new (marker, len);
self->partial_matches = g_array_sized_new(FALSE, TRUE, sizeof(gboolean), len);
g_array_set_size(self->partial_matches, len);
self->inject = g_bytes_ref (inject);
self->maximum = count;
return COCKPIT_WEB_FILTER (self);
}