diff options
author | Wojtek Kosior <koszko@koszko.org> | 2021-07-22 10:12:58 +0200 |
---|---|---|
committer | Wojtek Kosior <koszko@koszko.org> | 2021-07-22 10:12:58 +0200 |
commit | 91d4ce9f14668a5a04b51158b1921e83d51ba9a0 (patch) | |
tree | e0c10d59c4da49681d8d558169d3fc382e188144 | |
parent | e34205c764d10d50d982d1c63e23520d393946db (diff) | |
download | hydrilla-91d4ce9f14668a5a04b51158b1921e83d51ba9a0.tar.gz hydrilla-91d4ce9f14668a5a04b51158b1921e83d51ba9a0.zip |
initial codebase
-rw-r--r-- | Makefile | 21 | ||||
-rw-r--r-- | README.txt | 8 | ||||
-rw-r--r-- | copyright | 60 | ||||
-rw-r--r-- | hashtable.c | 565 | ||||
-rw-r--r-- | hashtable.h | 143 | ||||
-rw-r--r-- | licenses/agpl-3.0.txt | 661 | ||||
-rw-r--r-- | licenses/alicense.txt | 32 | ||||
-rw-r--r-- | licenses/cc-by-4.0.txt | 396 | ||||
-rw-r--r-- | licenses/cc0.txt | 121 | ||||
-rw-r--r-- | main.c | 182 | ||||
-rw-r--r-- | scriptbase.h | 77 | ||||
-rw-r--r-- | scriptbase_build.c | 534 | ||||
-rw-r--r-- | scriptbase_json_query.c | 213 | ||||
-rw-r--r-- | scriptbase_query.c | 278 | ||||
-rw-r--r-- | serve.c | 203 | ||||
-rw-r--r-- | string_buf.c | 308 | ||||
-rw-r--r-- | string_buf.h | 59 |
17 files changed, 3860 insertions, 1 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bcdd123 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +# part of Hydrilla +# +# Copyright (C) 2021 Wojtek Kosior +# Redistribution terms are gathered in the `copyright' file. + +CC=gcc +CFLAGS=-Wall -Werror -pedantic -std=c99 -D_USE_INLINE -O0 -g +#CFLAGS=-Wall -Werror -std=c89 -O0 -g + +EXECNAME=hydrilla + +$(EXECNAME): string_buf.o hashtable.o serve.o scriptbase_build.o \ + scriptbase_query.o scriptbase_json_query.o main.o + gcc $(filter %.o,$^) -lmicrohttpd -lcjson -o $@ + +clean: + rm -f $(EXECNAME) *.o + +.PHONY: clean + +*.o $(EXECNAME): Makefile @@ -1 +1,7 @@ -This is where Hydrilla will be developed. +This is the repository of Hydrilla, a repository software to work with browser +extension Hachette. + +See [1] and [2] for details. + +[1] https://hachettebugs.koszko.org/projects/hydrilla +[2] https://hachettebugs.koszko.org/projects/hachette diff --git a/copyright b/copyright new file mode 100644 index 0000000..6ba447d --- /dev/null +++ b/copyright @@ -0,0 +1,60 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Hydrilla +Source: https://git.koszko.org/hydrilla/ + +Files: * +Copyright: 2021 Wojtek Kosior <koszko@koszko.org> +License: AGPL-3+ or Alicense-1.0 + +Files: serve.c +Copyright: 2021 Wojtek Kosior <koszko@koszko.org> + 2008-2010, 2012 Christian Grothoff +License: AGPL-3+ or Alicense-1.0, and public-domain + +Files: Makefile README.txt copyright +Copyright: 2021 Wojtek Kosior <koszko@koszko.org> +License: CC0 + +Files: licenses/* +Copyright: 2001, 2002, 2011-2013 Creative Commons +License: CC-BY-4.0 + +Files: licenses/agpl-3.0.txt +Copyright: 2007 Free Software Foundation, Inc. <https://fsf.org/> +License: no-changing + Everyone is permitted to copy and distribute verbatim copies of + this license document, but changing it is not allowed. + +Files: licenses/alicense.txt +Copyright: 1988-1990 Regents of the University of California + 2021 Wojtek Kosior <koszko@koszko.org> +License: CC0 and public-domain + +License: Alicense-1.0 + You can use and redistribute this program with or without + modification under then terms of "A" license version 1.0. You + should have received a copy of "A" license along with this + program. If not, you can get it from + `https://koszko.org/alicense.txt'. + Also see `https://koszko.org/en/articles/my-new-license.html' + for more explanation. + +License: AGPL-3+ + 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/>. + +License: CC0 + See `licenses/cc0.txt' + +License: CC-BY-4.0 + See `licenses/cc-by-4.0.txt' diff --git a/hashtable.c b/hashtable.c new file mode 100644 index 0000000..a397879 --- /dev/null +++ b/hashtable.c @@ -0,0 +1,565 @@ +/** + * C hashtable implementation + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +/* + * GENERAL INFO + * + * You might want to read the beginning of hashtable.h first. + * + * In some places "rehashing" and in other ones "resizing" seemed to + * be the right word to use. They mean more or less the same. + * + * Functions starting with ht_ are part of the API. Internal functions + * are declared static. I also made some of them inline (either + * because they were extremely short or only called from 1 place). + * + * Hashtable size is always a power of 2. + * + * When the hashtable is ¾ full, a new, 2x bigger table is allocated + * and whenever one of 4 basic operations (adding, removing, setting, + * getting) occurs, 4 slots are being rehashed from old table into 8 + * slots in new table. Similarly, when hashtable is ¼ full, a new, + * 2x smaller table is allocated and each of subsequent operations + * rehashes 8 entries from old table into 4 in new table. + * This mechanism has been made lazier: getting and removing don't + * trigger growing of ht even if it's 3/4 full. Similarly, getting, + * setting and adding don't trigger shrinking. + * Once resizing is triggered, however, any of the operations will + * contribute to rehashing. Even if, for example, the operation is + * ADD and the table is being shrinked. + * This means, that if we have a hashtable of size n which is ¾ full + * and growing is triggered, then each subsequent call to + * ht_{add,rem,get,set}() rehashes some entries and, depending on + * how frequently and how successfully each of these 4 funcs was + * called, at the end of resizing we get a size 2n hashtable which is + * between ¼ and ½ full. Similarly, if shrinking of a ¼ full + * hashtable of size n is triggered, then after some operations we + * get a size ½n hashtable, that is somewhere between ¼ and ¾ full. + * One can see now, that we always keep the hashtable between ¼ and ¾ + * full (with the exception of a minimal size one, that can be empty). + */ + +#include "hashtable.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdbool.h> + +#ifdef _USE_INLINE +#define INLINE inline +#else +#define INLINE +#endif + +/* + * We won't shrink hashtable below this size. Newly created one will + * be this big. + */ +#define MIN_SIZE 4 + +/* Special value of ht->rehashing_position. */ +#define NOT_REHASHING ((ssize_t) -1) + +/* + * Those are possible return values of do_resizing_related_stuff() + * and rehash_some_entries() (which only returns the first 2). + */ +#define REHASHING_IN_PROGRESS 0 +#define REHASHING_NOT_IN_PROGRESS 1 +#define REHASHING_NO_MEM 2 + +/* Struct used to store a pair. */ +struct ht_node +{ + const void *key; + const void *val; + struct ht_node *next; +}; + +enum op + { + GET, + GET_THREADSAFE, + ADD, + SET, + REM, + }; + +int ht_init(hashtable_t *ht, + size_t (*hash)(const void *key), + int (*cmp)(const void *key1, const void *key2)) +{ + if (!(ht->tab = calloc(MIN_SIZE, sizeof(struct ht_node**)))) + return HT_NO_MEM; + + ht->tab_size = MIN_SIZE; + ht->rehashing_position = NOT_REHASHING; + ht->entries = 0; + ht->hashfunc = hash; + ht->cmpfunc = cmp; + + return HT_OK; +} + +/* First come some utilities :) */ + +static INLINE size_t min(size_t n1, size_t n2) +{ + return n1 < n2 ? n1 : n2; +} + +static INLINE size_t hash2(size_t n) +{ + /* I found this "hash improver" on the internet. */ + n ^= (n >> 20) ^ (n >> 12); + return n ^ (n >> 7) ^ (n >> 4); +} + +/* Below are 2 list-handling utility functions. */ +static INLINE struct ht_node *join_lists(struct ht_node *l1, + struct ht_node *l2) +{ + if (!l1) return l2; + if (!l2) return l1; + + struct ht_node *l1_last; + + for (l1_last = l1; l1_last->next; l1_last = l1_last->next); + + /* Append l2 to the end of l1. */ + l1_last->next = l2; + + /* For convenience return the first element of the resulting list. */ + return l1; +} + + +static INLINE void push(struct ht_node *node, struct ht_node **list) +{ + node->next = *list; + *list = node; +} + +/* + * The following 2 rehash_* functions are helpers of rehash_some_entries(). + * This func rehashes 1 chain of entries in tab[] into 2 chains in newtab[]. + */ +static INLINE void rehash_position_growing(hashtable_t *ht) +{ + /* There are 2 possible new positions of an entry in a 2x bigger ht. */ + struct ht_node *list0 = NULL, *list1 = NULL; + + size_t old_position = ht->rehashing_position, + new_position0 = old_position, + new_position1 = old_position | ht->tab_size; + + struct ht_node *pair = ht->tab[old_position], *next_pair; + + while (pair) + { + next_pair = pair->next; + + size_t new_position = hash2(ht->hashfunc(pair->key)) + & (ht->new_size - 1); + + push(pair, new_position == new_position1 ? &list1 : &list0); + + pair = next_pair; + } + + ht->newtab[new_position0] = list0; + ht->newtab[new_position1] = list1; + + ht->rehashing_position++; +} + +/* This func rehashes 2 chains of entries in tab[] into 1 chain in newtab[]. */ +static INLINE void rehash_2positions_shrinking(hashtable_t *ht) +{ + size_t new_position = ht->rehashing_position, + old_position0 = new_position, + old_position1 = new_position | ht->new_size; + + ht->newtab[new_position] = join_lists(ht->tab[old_position0], + ht->tab[old_position1]); + + ht->rehashing_position++; +} + +/* + * Rehashes 4(8) positions from tab to newtab. If those were the last + * enetries to rehash, the function takes care of everything + * (like deallocating old tab) and returns REHASHING_NOT_IN_PROGRESS. + * Otherwise, returns REHASHING_IN_PROGRESS. + * Caller must make sure rehashing was started b4 calling this func. + */ +static int rehash_some_entries(hashtable_t *ht) +{ + int rehashes_left = 4; + + if (ht->new_size > ht->tab_size) /* growing ht */ + { + while(rehashes_left--) rehash_position_growing(ht); + if (ht->rehashing_position != ht->tab_size) + return REHASHING_IN_PROGRESS; + } + else /* shrinking ht */ + { + while(rehashes_left--) rehash_2positions_shrinking(ht); + if (ht->rehashing_position != ht->new_size) + return REHASHING_IN_PROGRESS; + } + + /* rehashing finishes */ + ht->rehashing_position = NOT_REHASHING; + ht->tab_size = ht->new_size; + free(ht->tab); + ht->tab = ht->newtab; + + return REHASHING_NOT_IN_PROGRESS; +} + +static INLINE bool resizing_taking_place(hashtable_t *ht) +{ + return !(ht->rehashing_position == NOT_REHASHING); +} + +void ht_finish_resizing(hashtable_t *ht) +{ + if (resizing_taking_place(ht)) + while (rehash_some_entries(ht) == REHASHING_IN_PROGRESS); +} + +static INLINE bool needs_growing(hashtable_t *ht) +{ + return ht->entries >= 3 * ht->tab_size / 4; +} + +static INLINE bool needs_shrinking(hashtable_t *ht) +{ + return ht->tab_size > MIN_SIZE + && ht->entries <= ht->tab_size / 4; +} +/* + * Each of hashtable operations (add, set, rem, get) should also + * attempt to do part of resizing. This way resizing operation + * which is O(n) is distributed among many hashtable accesses + * each of them still being O(1). Without this the the amortized + * complexity of ht accesses would still be O(1), but a single access + * would sometimes be O(n). + * Other function that adds, sets, gets or removes sth from ht uses + * this one to do this "part of resizing" mentioned above. + * This func returns REHASHING_NO_MEM on failed malloc (won't happen + * for GET or REM operation) and REHASHING_[NOT_]IN_PROGRESS otherwise. + */ +static INLINE int +do_resizing_related_stuff(hashtable_t *ht, const void *key, enum op op) +{ + bool resizing = resizing_taking_place(ht); + + if (!resizing) + { + size_t new_size; + + switch (op) + { + case GET: + goto dont_start_resizing; + case ADD: + case SET: + if (needs_growing(ht)) + new_size = ht->tab_size * 2; + else + goto dont_start_resizing; + break; + default: /* case REM */ + if (needs_shrinking(ht)) + new_size = ht->tab_size / 2; + else + goto dont_start_resizing; + } + + struct ht_node **newtab; + if (!(newtab = malloc(new_size * sizeof(struct ht_node*)))) + return op == REM ? REHASHING_NOT_IN_PROGRESS : REHASHING_NO_MEM; + + ht->newtab = newtab; + ht->new_size = new_size; + ht->rehashing_position = 0; + + resizing = true; + } + + dont_start_resizing: + + return resizing ? + rehash_some_entries(ht) : REHASHING_NOT_IN_PROGRESS; +} + +/* + * This is a chaining hashtable, so each element in the array (table) + * is actually a list of entries. All operations (adding, removing, + * etc.) need to find the right list of entries (here called "bucket") + * for a given key first, so it makes sense to do it in a separate + * function. The bucket may be in tab or newtab if resizing is taking + * place. Being informed by the caller if resizing is in progress, + * this func does not need to check for it by itself. + */ +static INLINE struct ht_node **find_bucket(hashtable_t *ht, const void *key, + bool resizing_in_progress) +{ + size_t hash = hash2(ht->hashfunc(key)), + destination_tab_size, position; + + struct ht_node **destination_tab; + + if (resizing_in_progress) + /* + * Here we must check whether our key's bucket is still + * in ht->tab or already rehashed to ht->newtab. + */ + { + size_t smaller_tab_size = min(ht->tab_size, ht->new_size), + smaller_tab_position = hash & (smaller_tab_size - 1); + + if (smaller_tab_position < ht->rehashing_position) + { + destination_tab = ht->newtab; + destination_tab_size = ht->new_size; + } + else + { + destination_tab = ht->tab; + destination_tab_size = ht->tab_size; + } + } + else + /* In this case we know, we're working on ht->tab and not newtab. */ + { + destination_tab = ht->tab; + destination_tab_size = ht->tab_size; + } + + position = hash & (destination_tab_size - 1); + return &destination_tab[position]; +} + +/* + * Operations of adding, removing, etc. all work on list of entries + * (bucket) to wchich key hashes and they have some common logic, so + * it made sense to make a single function, that does the right + * operation based on an enum passed to it. + */ +static INLINE int +perform_operation_on_bucket(hashtable_t *ht, struct ht_node **bucket, + const void *key, const void *val, + void **keyptr, void **valptr, + enum op op) +{ + struct ht_node **pairptr, *pair; + + for (pairptr = bucket, pair = *pairptr; + pair; + pairptr = &pair->next, pair = pair->next) + + if (!ht->cmpfunc(key, pair->key)) + { + if (op == ADD) + return HT_KEY_PRESENT; + + if (keyptr) *keyptr = (void*) pair->key; + if (valptr) *valptr = (void*) pair->val; + + switch (op) + { + case GET: + case GET_THREADSAFE: + { + return HT_OK; + } + case SET: + { + pair->key = key; + pair->val = val; + return HT_OK; + } + default: /* case REM */ + { + *pairptr = pair->next; + free(pair); + ht->entries--; + return HT_OK; + } + } + } + + if (op == GET || op == GET_THREADSAFE || op == REM) + return HT_KEY_ABSENT; + + /* op == ADD || op == SET */ + + struct ht_node *new_pair = malloc(sizeof(struct ht_node)); + if (!new_pair) + return HT_NO_MEM; + + *new_pair = (struct ht_node) {.key = key, .val = val}; + push(new_pair, bucket); + ht->entries++; + + return HT_OK; +} + +/* Generic function for performing of adding, removing, setting and getting. */ +static int perform_operation(hashtable_t *ht, const void *key, const void *val, + void **keyptr, void **valptr, enum op op) +{ + bool resizing_in_progress; + + if (op == GET_THREADSAFE) { + resizing_in_progress = resizing_taking_place(ht); + goto skip_resizing; + } + + switch (do_resizing_related_stuff(ht, key, op)) + { + case REHASHING_IN_PROGRESS: + resizing_in_progress = true; + break; + case REHASHING_NOT_IN_PROGRESS: + resizing_in_progress = false; + break; + default: /* case REHASHING_NO_MEM */ + return HT_NO_MEM; + } + + struct ht_node **bucket; + + skip_resizing: + bucket = find_bucket(ht, key, resizing_in_progress); + + return perform_operation_on_bucket(ht, bucket, key, val, + keyptr, valptr, op); +} + +/* The 5 functions below are the main part of the API. */ +int ht_get(hashtable_t *ht, const void *key, + void **storedkey, void **val) +{ + return perform_operation(ht, key, NULL, storedkey, val, GET); +} + +int ht_get_threadsafe(hashtable_t *ht, const void *key, + void **storedkey, void **val) +{ + return perform_operation(ht, key, NULL, storedkey, val, + GET_THREADSAFE); +} + +int ht_add(hashtable_t *ht, const void *key, const void *val) +{ + return perform_operation(ht, key, val, NULL, NULL, ADD); +} + +int ht_set(hashtable_t *ht, const void *key, const void *val, + void **oldkey, void **oldval) +{ + return perform_operation(ht, key, val, oldkey, oldval, SET); +} + +int ht_rem(hashtable_t *ht, const void *key, + void **storedkey, void **val) +{ + return perform_operation(ht, key, NULL, storedkey, val, REM); +} + +/* + * As mentioned in hashtable.h, this func does not deallocate keys + * nor vals. One could use ht_map_destroy() if that is needed. + */ +void ht_destroy(hashtable_t *ht) +{ + ssize_t position; + + if (!ht->entries) + goto free_tab; + + ht_finish_resizing(ht); + + struct ht_node **tab = ht->tab; + + for (position = ht->tab_size - 1; position >= 0; position--) + { + struct ht_node *pair = tab[position], *nextpair; + + while (pair) + { + nextpair = pair->next; + free(pair); + pair = nextpair; + } + } + + free_tab: + free(ht->tab); +} + +void ht_map(hashtable_t *ht, void *arg, + void (*mapfunc)(const void *key, void *val, void *arg)) +{ + ssize_t position; + + if (!ht->entries) + return; + + ht_finish_resizing(ht); + + struct ht_node **tab = ht->tab, *pair; + + for (position = ht->tab_size - 1; position >= 0; position--) + { + for (pair = tab[position]; pair; pair = pair->next) + mapfunc(pair->key, (void*) pair->val, arg); + } +} + +void ht_map_destroy(hashtable_t *ht, void *arg, + void (*mapfunc)(void *key, void *val, void *arg)) +{ + /* + * If mapfunc() deallocates keys, the following 2 lines make + * assumption on ht_destroy(), that it doesn't call ht->hashfunc() + * or ht->cmpfunc() on keys. + */ + ht_map(ht, arg, (void (*)(const void*, void*, void*)) mapfunc); + ht_destroy(ht); +} + +/* + * These 2 functions are for easy making of hashtable with strings as + * keys. Note that this hash is *not* secure against DoS attacks. + */ +size_t ht_string_hash(const char *key) +{ + size_t i = 0, hash = (size_t) 0xa1bad2dead3beef4; + + do + { + char shift = ((unsigned char) key[i]) % sizeof(size_t); + hash += ((hash >> shift) | (hash << (sizeof(size_t) - shift))) + ^ key[i]; + } + while (key[i++]); + + return hash; +} + +int ht_string_init(hashtable_t *ht) +{ + return ht_init(ht, (size_t (*)(const void*)) &ht_string_hash, + (int (*)(const void*, const void*)) &strcmp); +} diff --git a/hashtable.h b/hashtable.h new file mode 100644 index 0000000..19a3897 --- /dev/null +++ b/hashtable.h @@ -0,0 +1,143 @@ +/** + * C hashtable implementation + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +/* + * https://git.koszko.org/C-hashtable + * Note that this version is likely to be more up-to-date than the one linked. + * + * This is a separate chaining hashtable for general use. It's not + * universal: it uses malloc() and free(), so it requires a standard + * library to function and it's for single-threaded use only. It does, + * however, have one advantage: it rehashes automatically, both when + * it grows in size and when it shrinks, while retaining O(1) access + * time. A normal hashtable with rehashing would have amortized O(1) + * access time, but there would be single access with O(n) time + * complexity for each rehashing. In this hashtable, rehashing is done + * in parts. For example, a ht_add(), aside from adding an entry, + * might also rehash 4 other entries from old table to the new one and + * leave the rest unrehashed. + * Of course, it is assumed that a good hash function is provided + * by the programmer. If not, accesses may still degenerate to O(n). + * Hence, this hashtable is not secure against DoS attacks. + */ + +#ifndef HASHTABLE_H +#define HASHTABLE_H + +#include <sys/types.h> /* for ssize_t */ + +/* These are possible return values of some ht_ functions (see below). */ +#define HT_OK 0 +#define HT_NO_MEM -1 +#define HT_KEY_PRESENT -2 +#define HT_KEY_ABSENT -3 + +typedef struct +{ + /* All members are considered implementation details, except for "entries", + * which can be read, but should not be modified by external code. + */ + size_t entries; + + /* + * tab[] is where entries (chains of entries) are stored. + * When rehashing, newtab[] is also used. + */ + struct ht_node **tab, **newtab; + + /* sizes of tab[] and newtab[], obviously */ + size_t tab_size, new_size; + + size_t (*hashfunc)(const void* key); + int (*cmpfunc)(const void* key1, const void *key2); + + /* + * When no rehashing is taking place, rehashing_position is -1 (#define'd as + * NOT_REHASHING in hashtable.c). At any other time, rehashing_position is the + * lowest not yet rehashed position in the smaller table. + */ + ssize_t rehashing_position; +} hashtable_t; + +/* + * All int functions return 0 (#define'd as HT_OK) on success and in + * case of failure they return error codes, as described below. + */ + +/* May fail with HT_NO_MEM. */ +int ht_init(hashtable_t *ht, + size_t (*hash)(const void* key), + int (*cmp)(const void* key1, const void *key2)); + +/* May fail with HT_NO_MEM and HT_KEY_PRESENT. */ +int ht_add(hashtable_t *ht, const void *key, const void *val); + +/* + * May fail with HT_NO_MEM. If key was not yet present in hashtable, *oldkey and + * *oldval are not modified. Otherwise, just-replaced pair is stored in them. + */ +int ht_set(hashtable_t *ht, const void *key, const void *val, + void **oldkey, void **oldval); + +/* + * If present, the looked for pair is stored in *storedkey and *val. Otherwise, + * they're not modified and HT_KEY_ABSENT is returned. storedkey and/or val can + * be NULL. + */ +int ht_get(hashtable_t *ht, const void *key, void **storedkey, void **val); + +/* + * Works like ht_get() but is thread-safe with regard to other calls to + * ht_get_threadsafe() on the same hashtable. Note that the hash and compare + * functions supplied to the hashtable also have to be thread-safe (that + * requirement is of course met for those used by ht_string_init()). + */ +int ht_get_threadsafe(hashtable_t *ht, const void *key, + void **storedkey, void **val); + +/* Works like the above but also removes the pair from ht if found. */ +int ht_rem(hashtable_t *ht, const void *key, void **storedkey, void **val); + +/* + * De-initializes the hashtable freeing all its structures. The programmer is + * responsible for freeing keys and values if they were allocated from the heap + * (see ht_map_destroy() below). + */ +void ht_destroy(hashtable_t *ht); + +/* Calls ht_finish_resizing(), then maps through ht. */ +void ht_map(hashtable_t *ht, void *arg, + void (*mapfunc)(const void *key, void *val, void *arg)); + +/* + * It might be tempting to use ht_map() to free() all keys and values stored in + * ht and then call ht_destroy(). If you think about it, ht_map() would leave + * hashtable in a broken state - with keys being deallocated. Depending on the + * implementation, ht_destroy() could cope with that, but we'd rather not + * guarrantee anything, so here's another function just for that - mapping + * through entries and destroying the hashtable immediately after, explicitly + * allowing the mapping function to deallocate keys. + */ +void ht_map_destroy(hashtable_t *ht, void *arg, + void (*mapfunc)(void *key, void *val, void *arg)); + +/* + * If hashtable is in the process of being rehashed, this function + * processes it to the end. Otherwise - it does nothing. + */ +void ht_finish_resizing(hashtable_t *ht); + +/* Included, since strings are commonly used as keys. */ +size_t ht_string_hash(const char *key); + +/* + * May fail with HT_NO_MEM. Initializes ht for use with string keys + * (using ht_string_hash() and strcmp()). + */ +int ht_string_init(hashtable_t *ht); + +#endif /* HASHTABLE_H */ diff --git a/licenses/agpl-3.0.txt b/licenses/agpl-3.0.txt new file mode 100644 index 0000000..be3f7b2 --- /dev/null +++ b/licenses/agpl-3.0.txt @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +<https://www.gnu.org/licenses/>. diff --git a/licenses/alicense.txt b/licenses/alicense.txt new file mode 100644 index 0000000..2513c24 --- /dev/null +++ b/licenses/alicense.txt @@ -0,0 +1,32 @@ +Asshole license 1.0 + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. You agree that you're an asshole. +2. You agree for the copyright owner of this software/work to call you an + asshole, both privately and publicly. +3. You agree and promise not to sue the copyright owner of this + software/work nor anyone acting on behalf of the copyright owner of this + software/work for calling you an asshole. +4. You agree and promise not to sue the copyright owner of this + software/work, nor anyone acting on behalf of the copyright owner of this + software/work, in relation with reverse-engineering actions performed on + your software/products. +5. You agree and promise not issue DMCA claims against any software + developed and/or distributed by the copyright owner of this + software/work or anyone acting on behalf of the copyright owner of this + software/work. Should such claims happen regardless, they will be + automatically void. + +THIS SOFTWARE/WORK IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE/WORK, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/licenses/cc-by-4.0.txt b/licenses/cc-by-4.0.txt new file mode 100644 index 0000000..da6ab6c --- /dev/null +++ b/licenses/cc-by-4.0.txt @@ -0,0 +1,396 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + diff --git a/licenses/cc0.txt b/licenses/cc0.txt new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/licenses/cc0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. @@ -0,0 +1,182 @@ +/** + * part of Hydrilla + * Program entry point. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +#define _POSIX_C_SOURCE 200809L /* S_IFMT, S_IFDIR */ + +#include <dirent.h> +#include <errno.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> + +#include <cjson/cJSON.h> + +#include "string_buf.h" +#include "hashtable.h" + +#include "scriptbase.h" + +#define PRESERVE_ERRNO(call) \ + do { \ + int VERY_UNLIKELY_TO_COLLIDE_NAME_$$$$$$$$##__LINE__ = errno; \ + call; \ + errno = VERY_UNLIKELY_TO_COLLIDE_NAME_$$$$$$$$##__LINE__; \ + } while (0) + +static const char default_search_path[] = "/var/lib/hydrilla/content/"; + +static int process_scriptbase_subdir(struct scriptbase *base, + struct dirent *subdir, + struct stringbuf *path_buf, + struct stringbuf *json_buf) +{ + struct stat statbuf; + cJSON *index_json; + int retval = -1; + size_t initial_len = path_buf->buf_filled, len_dirname; + + if (sb_string(path_buf, subdir->d_name)) + return -1; + + len_dirname = path_buf->buf_filled; + + if (stat(path_buf->buf, &statbuf)) + return -1; + + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) + return 0; + + if (sb_string(path_buf, "/index.json")) + return -1; + + printf("Reading %s\n", path_buf->buf); + + stringbuf_truncate(json_buf, 0); + if (sb_filepath(json_buf, path_buf->buf)) + return -1; + + index_json = cJSON_Parse(json_buf->buf); + if (!index_json) { + fprintf(stderr, "Failed to parse json.\n"); + return -1; + } + + stringbuf_truncate(path_buf, len_dirname); + + retval = catalogue_component(path_buf->buf + initial_len, + index_json, base); + + PRESERVE_ERRNO(cJSON_Delete(index_json)); + + return retval; +} + +static int prepare_scriptbase_from_dir(const char *dir_path, + struct scriptbase *base) +{ + DIR *maindir; + struct dirent *subdir; + struct stringbuf path_buf; + struct stringbuf json_buf; + size_t base_path_len; + int retval = -1; + + printf("Searching %s\n", dir_path); + stringbuf_init(&path_buf); + stringbuf_init(&json_buf); + + maindir = opendir(dir_path); + if (!maindir) + goto end; + + if (scriptbase_init(base, "https://hydrilla.koszko.org/resources")) + goto end; + + if (sb_sprintf(&path_buf, "%s/", dir_path)) + goto end; + + base_path_len = path_buf.buf_filled; + + while (true) { + errno = 0; + subdir = readdir(maindir); + if (!subdir) { + if (!errno) + retval = 0; + break; + } + + if (!strcmp(subdir->d_name, "..") || + !strcmp(subdir->d_name, ".")) + continue; + + stringbuf_truncate(&path_buf, base_path_len); + + errno = 0; + if (process_scriptbase_subdir(base, subdir, + &path_buf, &json_buf)) { + fprintf(stderr, "Error processing subdirectory %s%s", + subdir->d_name, errno ? ": " : ".\n"); + if (errno) + perror(NULL); + } + } + +end: + if (errno) + perror(NULL); + stringbuf_destroy(&path_buf); + stringbuf_destroy(&json_buf); + if (maindir) + closedir(maindir); + if (retval) + scriptbase_destroy(base); + + printf("Search in %s %s.\n", dir_path, retval ? "failed" : "finished"); + + return retval; +} + +static void print_component_name(const void *key, void *val, void *arg) +{ + char type = (char) (size_t) arg; + bool unfilled; + + unfilled = + (type == 's' && !((struct script*) val)->filled) || + (type == 'b' && !((struct bag*) val)->filled); + + printf("%s%s\n", (const char*) key, + unfilled ? " (referenced only)" : ""); +} + +int serve_scriptbase(struct scriptbase *base); + +int main(int argc, char *argv[]) +{ + struct scriptbase base; + const char *search_path = default_search_path; + + if (argc > 1) + search_path = argv[1]; + + if (prepare_scriptbase_from_dir(search_path, &base)) + return -1; + puts("## LOADED SCRIPTS:"); + ht_map(&base.scripts, (void*) 's', print_component_name); + puts("## LOADED BAGS:"); + ht_map(&base.bags, (void*) 'b', print_component_name); + puts("## LOADED PAGES:"); + ht_map(&base.pages, (void*) 'p', print_component_name); + + if (serve_scriptbase(&base)) + fprintf(stderr, "Error serving scriptbase.\n"); + + scriptbase_destroy(&base); + return 0; +} diff --git a/scriptbase.h b/scriptbase.h new file mode 100644 index 0000000..f7694f7 --- /dev/null +++ b/scriptbase.h @@ -0,0 +1,77 @@ +/** + * part of Hydrilla + * Scriptbase struct and functions operating on it. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +#ifndef SCRIPTBASE_H +#define SCRIPTBASE_H + +#include <stdbool.h> + +#include <cjson/cJSON.h> + +#include "hashtable.h" + +union component { + struct script *script; + struct bag *bag; + void *any; +}; + +struct component_ref { + struct component_ref *next; + union component component; + const char *component_type; +}; + +struct script { + char *name; + char *location; + char *sha256; + bool filled; +}; + +struct bag { + char *name; + struct component_ref *components, *last_component; + bool filled; +}; + +struct page { + char *pattern; + union component payload; + const char *payload_type; +}; + +struct scriptbase { + char *repo_url; + hashtable_t scripts; + hashtable_t bags; + hashtable_t pages; +}; + +int catalogue_component(const char *path, cJSON *index_json, + struct scriptbase *base); + +int scriptbase_init(struct scriptbase *base, const char *repo_url); + +void scriptbase_destroy(struct scriptbase *base); + +const struct script *get_script(const char *name, struct scriptbase *base); +const struct bag *get_bag(const char *name, struct scriptbase *base); +const struct page *get_pattern(const char *pattern, struct scriptbase *base); + +char *get_script_json(const char *name, struct scriptbase *base); +char *get_bag_json(const char *name, struct scriptbase *base); +char *get_pattern_json(const char *name, struct scriptbase *base); +char *get_page_query_json(const char *name, struct scriptbase *base); + +int init_url_lookup_regex(void); +void destroy_url_lookup_regex(void); +int lookup_url(const char *url, struct scriptbase *base, + int (*callback)(struct page*, void*), void *data); + +#endif /* SCRIPTBASE_H */ diff --git a/scriptbase_build.c b/scriptbase_build.c new file mode 100644 index 0000000..2bb9e80 --- /dev/null +++ b/scriptbase_build.c @@ -0,0 +1,534 @@ +/** + * part of Hydrilla + * Routines for building scriptbase from resources and configuratons (stored in + * `/var/lib/hydrilla/content/' or another, user-specified location. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +#define _POSIX_C_SOURCE 200809L /* strdup() */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "hashtable.h" +#include "string_buf.h" + +#include "scriptbase.h" + +static const char scriptstr[] = "script", bagstr[] = "bag", pagestr[] = "page"; + + +static void *add_or_get(const char *component_name, char component_type, + struct scriptbase *base); + +static struct script *script_create(const char *name) +{ + struct script *script; + + script = malloc(sizeof(struct script)); + if (!script) + return NULL; + + script->name = strdup(name); + + if (!script->name) + goto free_script; + + script->location = NULL; + script->sha256 = NULL; + script->filled = false; + + return script; + +free_script: + free(script); + + return NULL; +} + +static int script_fill(struct script *script, const char *dir, + const char *filename, const char *sha256) +{ + char *location = NULL; + size_t location_len = 0, location_filled = 0; + + if (sb_raw_sprintf(&location, &location_len, &location_filled, + "%s/%s", dir, filename)) + goto free_location; + script->location = location; + + script->sha256 = strdup(sha256); + if (!script->sha256) + goto free_location; + + script->filled = true; + + return 0; + +free_location: + free(location); + script->location = NULL; + + return -1; +} + +static void script_free(struct script *script) +{ + if (!script) + return; + + free(script->name); + free(script->location); + free(script->sha256); + free(script); +} + +static struct bag *bag_create(const char *name) +{ + struct bag *bag; + + bag = malloc(sizeof(struct bag)); + if (!bag) + return NULL; + + bag->name = strdup(name); + + if (!bag->name) + goto free_bag; + + bag->components = NULL; + bag->last_component = NULL; + bag->filled = false; + + return bag; + +free_bag: + free(bag); + + return NULL; +} + +static int bag_add(struct bag *bag, const char *component_name, + const char *component_type, struct scriptbase *base) +{ + struct component_ref *new_ref; + + new_ref = malloc(sizeof(struct component_ref)); + if (!new_ref) + return -1; + + new_ref->component.any = + add_or_get(component_name, *component_type, base); + + if (!new_ref->component.any) + goto free_ref; + + new_ref->component_type = component_type; + new_ref->next = NULL; + + *(bag->components ? &bag->last_component->next : &bag->components) = + new_ref; + bag->last_component = new_ref; + bag->filled = true; + + return 0; + +free_ref: + free(new_ref); + + return -1; +} + +static void bag_free(struct bag *bag) +{ + struct component_ref *tmp1, *tmp2; + + if (!bag) + return; + + tmp1 = bag->components; + + while (tmp1) { + tmp2 = tmp1->next; + free(tmp1); + tmp1 = tmp2; + } + + free(bag->name); + free(bag); +} + +static struct page *page_create(const char *pattern, const char *payload_name, + const char *payload_type, + struct scriptbase *base) +{ + struct page *page; + + page = malloc(sizeof(struct page)); + if (!page) + return NULL; + + page->pattern = strdup(pattern); + if (!page->pattern) + goto free_page; + + if (payload_name) { + page->payload.any = + add_or_get(payload_name, *payload_type, base); + if (!page->payload.any) + goto free_pattern; + } else { + page->payload.any = NULL; + } + + page->payload_type = payload_type; + + return page; + +free_pattern: + free(page->pattern); + +free_page: + free(page); + + return NULL; +} + +static void page_free(struct page *page) +{ + if (!page) + return; + + free(page->pattern); + free(page); +} + +static void *add_or_get(const char *component_name, char component_type, + struct scriptbase *base) +{ + void *component; + hashtable_t *relevant_ht; + bool found = true; + + relevant_ht = component_type == *bagstr ? &base->bags : &base->scripts; + if (ht_get(relevant_ht, component_name, NULL, &component)) { + if (component_type == *bagstr) + component = bag_create(component_name); + else + component = script_create(component_name); + + found = false; + } + + if (!component) + return NULL; + + /* Name is at the same position in both struct bag and struct script. */ + if (!found) { + switch (ht_add(relevant_ht, ((struct bag*) component)->name, + component)) { + case HT_NO_MEM: + errno = ENOMEM; + case HT_KEY_PRESENT: + goto free_component; + } + } + + return component; + +free_component: + if (component_type == *bagstr) + bag_free(component); + else + script_free(component); + + return NULL; +} + +int scriptbase_init(struct scriptbase *base, const char *repo_url) +{ + base->repo_url = strdup(repo_url); + if (!base->repo_url) + goto end; + + if (ht_string_init(&base->scripts)) + goto free_url; + + if (ht_string_init(&base->bags)) + goto free_scripts; + + if (ht_string_init(&base->pages)) + goto free_bags; + + return 0; + +free_bags: + ht_destroy(&base->bags); + +free_scripts: + ht_destroy(&base->scripts); + +free_url: + free(base->repo_url); + +end: + errno = ENOMEM; + return -1; +} + +static void destroy_cb(void *key, void *val, void *arg) +{ + char type = *((const char*) arg); + + if (type == 's') + script_free(val); + else if (type == 'b') + bag_free(val); + else + page_free(val); +} + +void scriptbase_destroy(struct scriptbase *base) +{ + char keys[] = {'s', 'b', 'p'}; + + ht_map_destroy(&base->scripts, keys, destroy_cb); + ht_map_destroy(&base->bags, keys + 1, destroy_cb); + ht_map_destroy(&base->pages, keys + 2, destroy_cb); + + free(base->repo_url); +} + +static int add_component_to_base(const char *name, const void *component, + const char *type, struct scriptbase *base) +{ + hashtable_t *ht = *type == 's' ? &base->scripts : + *type == 'b' ? &base->bags : &base->pages; + bool name_collision = false; + void *old_val = NULL; + + if (ht_set(ht, name, component, NULL, &old_val)) { + errno = ENOMEM; + return -1; + } + + if (old_val) { + if (*type == 's') { + name_collision = ((struct script*) old_val)->filled; + script_free(old_val); + } else if (*type == 'b') { + name_collision = ((struct bag*) old_val)->filled; + bag_free(old_val); + } else { + name_collision = true; + page_free(old_val); + } + } + + if (name_collision) { + fprintf(stderr, "Multiple occurences of %s %s.\n", type, name); + errno = 0; + return -1; + } + + return 0; +} + +static int catalogue_script(const char *path, cJSON *index_json, + struct scriptbase *base) +{ + const cJSON *name, *sha256, *location; + const char *bad = NULL; + struct script *script; + + name = cJSON_GetObjectItemCaseSensitive(index_json, "name"); + sha256 = cJSON_GetObjectItemCaseSensitive(index_json, "sha256"); + location = cJSON_GetObjectItemCaseSensitive(index_json, "location"); + + if (!cJSON_IsString(name) || name->valuestring == NULL) + bad = "name"; + else if (!cJSON_IsString(sha256) || sha256->valuestring == NULL) + bad = "sha256"; + else if (!cJSON_IsString(location) || location->valuestring == NULL) + bad = "location"; + + if (bad) { + fprintf(stderr, "Missing or invalid field \"%s\".\n", bad); + errno = 0; + return -1; + } + + script = script_create(name->valuestring); + if (!script) + return -1; + + if (script_fill(script, path, location->valuestring, + sha256->valuestring)) + goto free_script; + + return add_component_to_base(script->name, script, scriptstr, base); + +free_script: + script_free(script); + return -1; +} + +static int component_ref_from_json(const cJSON *component_cJSON, + const char **component_type, + const char **component_name) +{ + const cJSON *component_type_cJSON, *component_name_cJSON; + + if (!cJSON_IsArray(component_cJSON)) + return -1; + + component_type_cJSON = component_cJSON->child; + if (!component_type_cJSON) + return -1; + component_name_cJSON = component_type_cJSON->next; + if (!component_name_cJSON || component_name_cJSON->next) + return -1; + + if (!cJSON_IsString(component_type_cJSON) || + !cJSON_IsString(component_name_cJSON)) + return -1; + + if (!strcmp(scriptstr, component_type_cJSON->valuestring)) + *component_type = scriptstr; + else if (!strcmp(bagstr, component_type_cJSON->valuestring)) + *component_type = bagstr; + else + return -1; + + *component_name = component_name_cJSON->valuestring; + + return 0; +} + +static int catalogue_bag(const char *path, cJSON *index_json, + struct scriptbase *base) +{ + const cJSON *name, *components, *component; + const char *component_type, *component_name; + struct bag *bag; + + name = cJSON_GetObjectItemCaseSensitive(index_json, "name"); + + if (!cJSON_IsString(name) || name->valuestring == NULL) { + fprintf(stderr, "Missing or invalid field \"name\".\n"); + errno = 0; + return -1; + } + + bag = bag_create(name->valuestring); + if (!bag) + return -1; + + bag->filled = true; + + components = cJSON_GetObjectItemCaseSensitive(index_json, "components"); + if (!components) + return 0; + if (!cJSON_IsArray(components)) + goto invalid_components; + + cJSON_ArrayForEach(component, components) { + if (component_ref_from_json(component, + &component_type, &component_name)) + goto invalid_components; + + /* + * component_type now points to a static buffer and + * component_name to cJSON-owned memory + */ + + if (bag_add(bag, component_name, component_type, base)) + goto free_bag; + } + + if (add_component_to_base(bag->name, bag, bagstr, base)) + goto free_bag; + + return 0; + +invalid_components: + fprintf(stderr, "Invalid field \"components\""); + errno = 0; + +free_bag: + bag_free(bag); + return -1; +} + +static int catalogue_page(const char *path, cJSON *index_json, + struct scriptbase *base) +{ + const cJSON *pattern, *payload; + const char *payload_type = "", *payload_name = NULL; + struct page *page; + + pattern = cJSON_GetObjectItemCaseSensitive(index_json, "pattern"); + payload = cJSON_GetObjectItemCaseSensitive(index_json, "payload"); + + if (!cJSON_IsString(pattern) || pattern->valuestring == NULL) { + fprintf(stderr, "Missing or invalid field \"pattern\".\n"); + errno = 0; + return -1; + } + + if (!payload) + goto skip_payload; + + if (component_ref_from_json(payload, &payload_type, &payload_name)) + goto invalid_payload; + +skip_payload: + page = page_create(pattern->valuestring, payload_name, payload_type, + base); + if (!page) + goto free_page; + + if (add_component_to_base(page->pattern, page, pagestr, base)) + goto free_page; + + return 0; + +invalid_payload: + fprintf(stderr, "Invalid field \"payload\""); + errno = 0; + +free_page: + page_free(page); + return -1; +} + +int catalogue_component(const char *path, cJSON *index_json, + struct scriptbase *base) +{ + const cJSON *type; + + type = cJSON_GetObjectItemCaseSensitive(index_json, "type"); + if (!cJSON_IsString(type) || type->valuestring == NULL) + goto bad_type; + + if (!strcmp(type->valuestring, scriptstr)) + return catalogue_script(path, index_json, base); + else if (!strcmp(type->valuestring, bagstr)) + return catalogue_bag(path, index_json, base); + else if (!strcmp(type->valuestring, pagestr)) + return catalogue_page(path, index_json, base); + else + goto bad_type; + + return 0; + +bad_type: + fprintf(stderr, "Missing or invalid type."); + + errno = 0; + return -1; +} diff --git a/scriptbase_json_query.c b/scriptbase_json_query.c new file mode 100644 index 0000000..e413b3e --- /dev/null +++ b/scriptbase_json_query.c @@ -0,0 +1,213 @@ +/** + * part of Hydrilla + * Routines that perform queries on in-memory scriptbase and return results in + * the form of JSON strings. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +#include <errno.h> + +#include "scriptbase.h" + +#define ADD_COMPONENT(object, key, construct, adder) \ + new = cJSON_Create##construct; \ + if (!new) \ + goto free_json; \ + adder(object, key, new) + +#define ADD_TO_OBJECT(object, key, construct) \ + ADD_COMPONENT(object, key, construct, cJSON_AddItemToObject) + +#define ADD_KEY(key, construct) ADD_TO_OBJECT(json, key, construct) + +#define ARRAY_ADDER(object, key, var) cJSON_AddItemToArray(object, var) + +#define ADD_TO_ARRAY(object, construct) \ + ADD_COMPONENT(object, dummy, construct, ARRAY_ADDER) + +char *get_script_json(const char *name, struct scriptbase *base) +{ + const struct script *script; + cJSON *json, *new; + char *printed = NULL; + + script = get_script(name, base); + if (!script || !script->filled) + return NULL; + + json = cJSON_CreateObject(); + if (!json) + goto free_json; + + ADD_KEY("name", String(script->name)); + ADD_KEY("location", String(script->location)); + ADD_KEY("sha256", String(script->sha256)); + + printed = cJSON_Print(json); + +free_json: + cJSON_Delete(json); + + if (!printed) + errno = ENOMEM; + + return printed; +} + +char *get_bag_json(const char *name, struct scriptbase *base) +{ + const struct bag *bag; + cJSON *json, *new, *components, *current_component; + struct component_ref *ref; + char type_prefix[] = "\0"; + char *printed = NULL; + + bag = get_bag(name, base); + if (!bag || !bag->filled) + return NULL; + + json = cJSON_CreateObject(); + if (!json) + goto free_json; + + ADD_KEY("name", String(bag->name)); + ADD_KEY("components", Array()); + components = new; + + for (ref = bag->components; ref; ref = ref->next) { + ADD_TO_ARRAY(components, Array()); + current_component = new; + type_prefix[0] = ref->component_type[0]; + ADD_TO_ARRAY(current_component, String(type_prefix)); + /* name is at the same offset in struct bag and struct script */ + ADD_TO_ARRAY(current_component, + String(ref->component.bag->name)); + } + + printed = cJSON_Print(json); + +free_json: + cJSON_Delete(json); + + if (!printed) + errno = ENOMEM; + + return printed; +} + +cJSON *page_to_cJSON(const struct page *page) +{ + cJSON *json, *new, *payload; + char type_prefix[] = "\0"; + + json = cJSON_CreateObject(); + if (!json) + goto free_json; + + ADD_KEY("pattern", String(page->pattern)); + if (!page->payload.any) + goto skip_payload; + ADD_KEY("payload", Array()); + payload = new; + type_prefix[0] = page->payload_type[0]; + ADD_TO_ARRAY(payload, String(type_prefix)); + /* name is at the same offset in struct bag and struct script */ + ADD_TO_ARRAY(payload, String(page->payload.bag->name)); + +skip_payload: + return json; + +free_json: + cJSON_Delete(json); + + errno = ENOMEM; + + return NULL; +} + +char *get_pattern_json(const char *pattern, struct scriptbase *base) +{ + const struct page *page; + cJSON *json; + char *printed = NULL; + + page = get_pattern(pattern, base); + if (!page) + return NULL; + + json = page_to_cJSON(page); + if (!json) + return NULL; + + printed = cJSON_Print(json); + cJSON_Delete(json); + + if (!printed) + errno = ENOMEM; + + return printed; +} + +struct page_array_building { + cJSON *page_array; + bool OOM_error; +}; + +int lookup_callback(struct page *page, void *data) +{ + struct page_array_building *building = data; + cJSON *page_json; + + page_json = page_to_cJSON(page); + if (!page_json) { + building->OOM_error = true; + return -1; + } + + cJSON_AddItemToArray(building->page_array, page_json); + + return 0; +} + +#include <stdio.h> +char *get_page_query_json(const char *url, struct scriptbase *base) +{ + struct page_array_building building = {NULL, false}; + char *printed = NULL; + int result = -2; + + building.page_array = cJSON_CreateArray(); + if (!building.page_array) + goto free_json; + + result = lookup_url(url, base, lookup_callback, &building); + + printf("lookup returned value is %d\n", result); + + if (building.OOM_error) + result = -2; + if (result < 0) + goto free_json; + + printed = cJSON_Print(building.page_array); + if (!printed) + result = -2; + +free_json: + cJSON_Delete(building.page_array); + + switch (result) { + case 0: + break; + case -1: + case -3: + errno = EINVAL; + break; + case -2: + errno = ENOMEM; + } + + return printed; +} diff --git a/scriptbase_query.c b/scriptbase_query.c new file mode 100644 index 0000000..fe9a910 --- /dev/null +++ b/scriptbase_query.c @@ -0,0 +1,278 @@ +/** + * part of Hydrilla + * Routines for querying in-memory scriptbase, operating on data structures from + * `scripbase.h'. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +#include <stddef.h> +#include <regex.h> +#include <stdbool.h> +#include <string.h> + +#include "hashtable.h" +#include "string_buf.h" + +#include "scriptbase.h" + +const struct script *get_script(const char *name, struct scriptbase *base) +{ + void *val; + + if (ht_get_threadsafe(&base->scripts, name, NULL, &val)) + return NULL; + + return ((struct script*) val)->filled ? val : NULL; +} + +const struct bag *get_bag(const char *name, struct scriptbase *base) +{ + void *val; + + if (ht_get_threadsafe(&base->bags, name, NULL, &val)) + return NULL; + + return ((struct bag*) val)->filled ? val : NULL; +} + +const struct page *get_pattern(const char *pattern, struct scriptbase *base) +{ + void *val = NULL; + + ht_get_threadsafe(&base->pages, pattern, NULL, &val); + + return val; +} + +static const char url_regex[] = + "^" + "([a-zA-Z]{1,20}://)" /* protocol */ + "([^/?#]{1,253})" /* domain */ + "(/[^?#]*)?" /* path */ + "\\\\?[^#]*" /* query */ + "#?.*" /* target */ + "$"; + +static regex_t url_regex_comp; +static bool url_regex_ready; + +int init_url_lookup_regex(void) +{ + int retval; + + retval = regcomp(&url_regex_comp, url_regex, REG_EXTENDED); + + url_regex_ready = !retval; + + return retval; +} + +void destroy_url_lookup_regex(void) +{ + if (!url_regex_ready) { + fprintf(stderr, "Attempt to destroy uninitialized regex in " __FILE__ "\n"); + return; + } + + regfree(&url_regex_comp); +} + +#define URL_REGEX_NMATCH 4 + +#define PROTOCOL_MATCH 1 +#define DOMAIN_MATCH 2 +#define PATH_MATCH 3 + +static int lookup_url_path(const char *path_begin, const char *path_end, + struct stringbuf *buf, struct scriptbase *base, + int (*callback)(struct page*, void*), void *data) +{ + const char *segment_end = path_begin; + int segments_dropped = 0; + int initial_len = buf->buf_filled; + size_t len_path, previous_segment; + void *val; + bool trailing_dash = path_end != path_begin && path_end[-1] == '/'; + char asterisks[] = "/***"; + int trailing_asterisks = 0, i; + int result; + + while (true) { + do { + if (path_begin >= path_end) + goto after_path_normalization; + } while (*(path_begin++) == '/'); + path_begin -= 2; + + segment_end = path_begin + 1; + while (*segment_end != '/' && ++segment_end < path_end); + + if (sb_bytes(buf, path_begin, segment_end - path_begin)) + return -2; + + path_begin = segment_end; + } + +after_path_normalization: +#define TRY_WILDCARD(condition, wildcard) \ + if (condition) { \ + stringbuf_truncate(buf, len_path); \ + if (sb_string(buf, wildcard)) \ + return -2; \ + \ + result = ht_get_threadsafe(&base->pages, buf->buf, \ + NULL, &val); \ + if (!result && callback(val, data)) \ + return 1; \ + } + + while (true) { + len_path = buf->buf_filled; + previous_segment = len_path; + while (previous_segment > initial_len && + buf->buf[--previous_segment] != '/'); + + if (!trailing_asterisks) {/* only on first iteration */ + trailing_asterisks = -1; + + for (i = 3; i > 0; i--) { + asterisks[i + 1] = '\0'; + + if (strncmp(buf->buf + previous_segment, + asterisks, i + 1)) + continue; + + trailing_asterisks = i; + + if (i != 3) + break; + + if (buf->buf[previous_segment + i + 1] == '*') + trailing_asterisks = -1; + + break; + } + } + + TRY_WILDCARD(segments_dropped == 0, ""); + TRY_WILDCARD(segments_dropped == 0 && trailing_dash, "/"); + TRY_WILDCARD(segments_dropped == 1 && trailing_asterisks != 1, + "/*"); + TRY_WILDCARD(segments_dropped > 1, "/**"); + TRY_WILDCARD(segments_dropped > 0 && + (segments_dropped > 1 || trailing_asterisks != 3), + "/***"); + + stringbuf_truncate(buf, previous_segment); + + if (previous_segment == len_path) + return 0; + + /* + * We only ever care if this count is 0, 1 or > 1, + * hence size_t is not necessary. + */ + if (segments_dropped < 2) + segments_dropped++; + } + +#undef TRY_WILDCARD +} + +static int lookup_url_domain(const char *domain_begin, const char *domain_end, + const char *path_begin, const char *path_end, + struct stringbuf *buf, struct scriptbase *base, + int (*callback)(struct page*, void*), void *data) +{ + const char *next_label = domain_begin; + int labels_dropped = 0; + int initial_len = buf->buf_filled; + int result; + +#define TRY_WILDCARD(condition, wildcard) \ + if (condition) { \ + stringbuf_truncate(buf, initial_len); \ + if (sb_string(buf, wildcard) || \ + sb_bytes(buf, domain_begin, domain_end - domain_begin)) \ + return -2; \ + \ + result = lookup_url_path(path_begin, path_end, \ + buf, base, callback, data); \ + if (result) \ + return result; \ + } + + while (true) { + domain_begin = next_label; + + while (*(next_label++) != '.') { + if (next_label >= domain_end) + return 0; + } + + TRY_WILDCARD(labels_dropped == 0, ""); + TRY_WILDCARD(labels_dropped == 1, "*."); + TRY_WILDCARD(labels_dropped > 0, "**."); + TRY_WILDCARD(true, "***."); + + labels_dropped++; + } + +#undef TRY_WILDCARD +} + +static int lookup_url_proto(const char *proto_begin, const char *proto_end, + const char *domain_begin, const char *domain_end, + const char *path_begin, const char *path_end, + struct stringbuf *buf, struct scriptbase *base, + int (*callback)(struct page*, void*), void *data) +{ + if (sb_bytes(buf, proto_begin, proto_end - proto_begin)) + return -2; + + return lookup_url_domain(domain_begin, domain_end, path_begin, path_end, + buf, base, callback, data); +} + +int lookup_url(const char *url, struct scriptbase *base, + int (*callback)(struct page*, void*), void *data) +{ + regmatch_t reg_matched[URL_REGEX_NMATCH]; + struct stringbuf buf; + const char *path_begin, *path_end; + int retval; + + if (!url_regex_ready) { + fprintf(stderr, "Regex not initialized in " __FILE__ "\n"); + return -3; + } + + printf("matching: %s\n", url); + + if (regexec(&url_regex_comp, url, + URL_REGEX_NMATCH, reg_matched, 0) || + reg_matched[DOMAIN_MATCH].rm_so == -1) + return -1; + + stringbuf_init(&buf); + + path_begin = url + reg_matched[PATH_MATCH].rm_so; + path_end = url + reg_matched[PATH_MATCH].rm_eo; + if (path_begin == url - 1) { + path_begin = NULL; + path_end = NULL; + } + + retval = lookup_url_proto(url + reg_matched[PROTOCOL_MATCH].rm_so, + url + reg_matched[PROTOCOL_MATCH].rm_eo, + url + reg_matched[DOMAIN_MATCH].rm_so, + url + reg_matched[DOMAIN_MATCH].rm_eo, + path_begin, path_end, + &buf, base, callback, data); + + stringbuf_destroy(&buf); + + return retval; +} @@ -0,0 +1,203 @@ +/** + * part of Hydrilla + * Serving data queries over HTTP using libmicrohttpd. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + * + * This file is based on Christian Grothoff's public comain code from + * `doc/examples/logging.c' in libmicrohttpd source tree. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> + +#include <microhttpd.h> + +#include "scriptbase.h" + +#define PORT 10111 + +#define ARRLEN(array) (sizeof(array) / sizeof(*array)) + +static struct MHD_Response *default_response, *error_response, + *not_found_response; + +static struct { + struct MHD_Response **response; + const char *const text; +} static_responses[] = { + {&default_response, "<html><body>Nothing to see here</body></html>"}, + {&error_response, "<html><body>Error occured</body></html>"}, + {¬_found_response, "<html><body>Not found</body></html>"} +}; + +static int add_CORS_header(struct MHD_Response *response) +{ + return -1 * (MHD_add_response_header(response, + "Access-Control-Allow-Origin", + "*") == MHD_NO); +} + +typedef char *(*request_handler_t)(const char *queried, + struct scriptbase *base); + +static struct { + const char *path; + request_handler_t request_handler; +} resources[] = { + {"/script", get_script_json}, + {"/bag", get_bag_json}, + {"/pattern", get_pattern_json}, + {"/query", get_page_query_json} +}; + +struct request_handling { + struct scriptbase *base; + request_handler_t handler; + char *json_response; + int error_number; +}; + +static int handle_query_argument(void *cls, enum MHD_ValueKind kind, + const char *key, const char *value) +{ + struct request_handling *handling = cls; + + if (strcmp(key, "n")) { + fprintf(stderr, "Unknown argument: \"%s\" = \"%s\"\n", + key, value); + return MHD_YES; + } + + errno = 0; + + handling->json_response = handling->handler(value, handling->base); + + handling->error_number = errno; + + return MHD_NO; +} + +static int answer_with(struct MHD_Connection *connection, + request_handler_t handler, struct scriptbase *base) +{ + struct request_handling handling = {base, handler, NULL, EINVAL}; + struct MHD_Response *response; + int retval; + + MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, + handle_query_argument, &handling); + + if (!handling.json_response) { + if (handling.error_number) + goto send_error; + goto send_not_found; + } + + response = MHD_create_response_from_buffer + (strlen(handling.json_response), handling.json_response, + MHD_RESPMEM_MUST_FREE); + if (!response || add_CORS_header(response)) + goto send_error; + + retval = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + + return retval; + +send_not_found: + return MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, + not_found_response); + +send_error: + free(handling.json_response); + return MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, + error_response); +} + +static int answer(void *data, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **ptr) +{ + static int aptr; + int i; + struct scriptbase *base = data; + + printf("New %s request for %s using version %s\n", method, url, version); + + if (strcmp(method, "GET")) + return MHD_NO; + + if (&aptr != *ptr) { + /* do never respond on first call */ + *ptr = &aptr; + return MHD_YES; + } + *ptr = NULL; /* reset when done */ + + for (i = 0; i < ARRLEN(resources); i++) { + if (strcmp(resources[i].path, url)) + continue; + + return answer_with(connection, resources[i].request_handler, + base); + } + + return MHD_queue_response(connection, MHD_HTTP_OK, default_response); +} + +int getchar(void); + +int serve_scriptbase(struct scriptbase *base) +{ + int i; + struct MHD_Daemon *daemon; + int retval = -1; + + if (init_url_lookup_regex()) + return -1; + + for (i = 0; i < ARRLEN(static_responses); i++) { + *static_responses[i].response = MHD_create_response_from_buffer + (strlen(static_responses[i].text), + (char*) static_responses[i].text, + MHD_RESPMEM_PERSISTENT); + + if (!*static_responses[i].response || + add_CORS_header(*static_responses[i].response)) + goto free_resources; + } + + daemon = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, + NULL, &answer, (void*) base, MHD_OPTION_END); + if (!daemon) + goto free_resources; + + getchar(); + MHD_stop_daemon(daemon); + + retval = 0; + +free_resources: + for (i = 0; i < ARRLEN(static_responses); i++) { + if (*static_responses[i].response) + MHD_destroy_response(*static_responses[i].response); + *static_responses[i].response = NULL; + } + + destroy_url_lookup_regex(); + + return retval; +} diff --git a/string_buf.c b/string_buf.c new file mode 100644 index 0000000..332d023 --- /dev/null +++ b/string_buf.c @@ -0,0 +1,308 @@ +/** + * C string buffers for easy construction of complex strings + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdarg.h> +#include <unistd.h> + +#define STRING_BUF_C +#include "string_buf.h" + +#define MINIMUM_EXTEND_BYTES 63 + +void stringbuf_init(struct stringbuf *sb) +{ + sb->buf = NULL; + sb->buf_len = 0; + sb->buf_filled = 0; +} + +void stringbuf_destroy(struct stringbuf *sb) +{ + free(sb->buf); +} + +void stringbuf_truncate(struct stringbuf *sb, size_t len) +{ + if (sb->buf_len <= len) + return; + + sb->buf_filled = len; + sb->buf[len] = '\0'; +} + +#define _UNPACKED_ARGS &sb->buf, &sb->buf_len, &sb->buf_filled + +int extend_buf(struct stringbuf *sb, size_t extend_len) +{ + return extend_buf_raw(_UNPACKED_ARGS, extend_len); +} + +int extend_buf_raw(_RAW_BUF_ARGS, size_t extend_len) +{ + ssize_t space_left = *buf_len - *buf_filled - 1; + size_t new_size, size_required, size_more_space; + char *new_buf; + + if (space_left >= 0 && space_left >= extend_len) + return 0; + + size_required = *buf_filled + extend_len + 1; + size_more_space = size_required + MINIMUM_EXTEND_BYTES; + new_size = *buf_len * 2; + if (new_size < size_more_space) + new_size = size_more_space; + + new_buf = realloc(*buf, new_size); + if (!new_buf) { + new_size = size_required; + new_buf = realloc(*buf, new_size); + } + if (!new_buf) + return -1; + + *buf = new_buf; + *buf_len = new_size; + return 0; +} + +_SB_HEAD(bytes, const void *bytes, size_t bytes_len) +{ + return sb_raw_bytes(_UNPACKED_ARGS, bytes, bytes_len); +} + +_SB_RAW_HEAD(bytes, const void *bytes, size_t bytes_len) +{ + if (extend_buf_raw(buf, buf_len, buf_filled, bytes_len)) + return -1; + + memcpy(*buf + *buf_filled, bytes, bytes_len); + (*buf)[*buf_filled + bytes_len] = '\0'; + *buf_filled += bytes_len; + + return 0; +} + +_SB_HEAD(string, const char *string) +{ + return sb_raw_string(_UNPACKED_ARGS, string); +} + +_SB_RAW_HEAD(string, const char *string) +{ + size_t string_len = strlen(string); + + return sb_raw_bytes(buf, buf_len, buf_filled, string, string_len); +} + +_SB_HEAD(char, char c) +{ + return sb_raw_char(_UNPACKED_ARGS, c); +} + +_SB_RAW_HEAD(char, char c) +{ + return sb_raw_bytes(buf, buf_len, buf_filled, &c, 1); +} + +_SB_HEAD(long, long num) +{ + return sb_raw_long(_UNPACKED_ARGS, num); +} + +_SB_RAW_HEAD(long, long num) +{ + unsigned char repr[3 * sizeof(long) + 1]; + int i; + bool neg = num < 0; + + for (i = sizeof(repr); num; num /= 10) + repr[--i] = '0' + num % 10; + + if (i == sizeof(repr)) + repr[--i] = '0'; + else if (neg) + repr[--i] = '-'; + + sb_raw_bytes(buf, buf_len, buf_filled, + repr + i, sizeof(repr) - i); + + return 0; +} + +_SB_HEAD(file, FILE *file) +{ + return sb_raw_file(_UNPACKED_ARGS, file); +} + +_SB_RAW_HEAD(file, FILE *file) +{ + long file_size; + + if (fseek(file, 0, SEEK_END)) + return -1; + + file_size = ftell(file); + if (file_size < 0) + return -1; + + if (extend_buf_raw(buf, buf_len, buf_filled, file_size)) + return -1; + + rewind(file); + + if (fread(*buf + *buf_filled, file_size, 1, file) != 1) + return -1; + + (*buf)[*buf_filled + file_size] = '\0'; + *buf_filled += file_size; + + return 0; +} + +_SB_HEAD(filepath, const char *path) +{ + return sb_raw_filepath(_UNPACKED_ARGS, path); +} + +_SB_RAW_HEAD(filepath, const char *path) +{ + FILE *file; + int retval; + + file = fopen(path, "r"); + if (!file) + return -1; + + retval = sb_raw_file(buf, buf_len, buf_filled, file); + + fclose(file); + return retval; +} + +_SB_HEAD(vsprintf, const char *fmt, va_list ap) +{ + return sb_raw_vsprintf(_UNPACKED_ARGS, fmt, ap); +} + +_SB_RAW_HEAD(vsprintf, const char *fmt, va_list ap) +{ + const unsigned char *in_pos = (const unsigned char*) fmt; + char c; + size_t i = 0; + bool percent = false; + + long num_arg; + int (*sb_cb)(char**, size_t*, size_t*, void*); + + while (in_pos[i]) { + c = in_pos[i++]; + + if (!percent) { + if (c == '%') { + percent = true; + if (sb_raw_bytes(buf, buf_len, buf_filled, + in_pos, i - 1)) + return -1; + } + + continue; + } + + percent = false; + in_pos += i; + i = 0; + + switch (c) { + case 'd': + case 'u': + num_arg = c == 'd' ? + va_arg(ap, int) : va_arg(ap, unsigned); + + if (sb_raw_long(buf, buf_len, buf_filled, num_arg)) + return -1; + break; + case 'f': + if (sb_raw_file(buf, buf_len, buf_filled, + va_arg(ap, FILE*))) + return -1; + break; + case 'p': + if (sb_raw_filepath(buf, buf_len, buf_filled, + va_arg(ap, const char*))) + return -1; + break; + case 's': + if (sb_raw_string(buf, buf_len, buf_filled, + va_arg(ap, const char*))) + return -1; + break; + case '_': + sb_cb = va_arg(ap, int (*)(char**, size_t*, + size_t*, void*)); + if (sb_cb(buf, buf_len, buf_filled, + va_arg(ap, void*))) + return -1; + break; + case '%': + in_pos--; + i++; + } + } + + if (!percent && sb_raw_bytes(buf, buf_len, buf_filled, in_pos, i)) + return -1; + + return 0; +} + +_SB_HEAD(sprintf, const char *fmt, ...) +{ + va_list ap; + int res; + + va_start(ap, fmt); + res = sb_raw_vsprintf(_UNPACKED_ARGS, fmt, ap); + va_end(ap); + + return res; +} + +_SB_RAW_HEAD(sprintf, const char *fmt, ...) +{ + va_list ap; + int res; + + va_start(ap, fmt); + res = sb_raw_vsprintf(buf, buf_len, buf_filled, fmt, ap); + va_end(ap); + + return res; +} + +int crop_buf(struct stringbuf *sb) +{ + return crop_buf_raw(_UNPACKED_ARGS); +} + +int crop_buf_raw(_RAW_BUF_ARGS) +{ + char *new_buf; + + if (*buf_len <= *buf_filled + 1) + return 0; + + new_buf = realloc(*buf, *buf_filled + 1); + if (!new_buf) + return -1; + + *buf = new_buf; + *buf_len = *buf_filled + 1; + return 0; +} diff --git a/string_buf.h b/string_buf.h new file mode 100644 index 0000000..160b7cb --- /dev/null +++ b/string_buf.h @@ -0,0 +1,59 @@ +/** + * C string buffers for easy construction of complex strings + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +#ifndef STRING_BUF_H +#define STRING_BUF_H + +#include <stdio.h> +#include <stdarg.h> + +struct stringbuf { + char *buf; + size_t buf_len; + size_t buf_filled; +}; + +void stringbuf_init(struct stringbuf *sb); +void stringbuf_destroy(struct stringbuf *sb); +void stringbuf_truncate(struct stringbuf *sb, size_t len); + +#define _RAW_BUF_ARGS char **buf, size_t *buf_len, size_t *buf_filled + +#define _SB_HEAD(name, ...) \ + int sb_##name(struct stringbuf *sb, __VA_ARGS__) + +#define _SB_RAW_HEAD(name, ...) \ + int sb_raw_##name(_RAW_BUF_ARGS, __VA_ARGS__) + +#define _SB_DEFINE_2(name, ...) \ + _SB_HEAD(name, __VA_ARGS__); \ + _SB_RAW_HEAD(name, __VA_ARGS__) + +int extend_buf_raw(_RAW_BUF_ARGS, size_t extend_len); +int extend_buf(struct stringbuf *sb, size_t extend_len); + +int crop_buf_raw(_RAW_BUF_ARGS); +int crop_buf(struct stringbuf *sb); + +_SB_DEFINE_2(bytes, const void *bytes, size_t bytes_len); +_SB_DEFINE_2(string, const char *string); +_SB_DEFINE_2(char, char c); +_SB_DEFINE_2(long, long num); +_SB_DEFINE_2(file, FILE *file); +_SB_DEFINE_2(filepath, const char *path); +_SB_DEFINE_2(vsprintf, const char *fmt, va_list ap); +_SB_DEFINE_2(sprintf, const char *fmt, ...); + +#undef _SB_DEFINE_2 + +#ifndef STRING_BUF_C +#undef _RAW_BUF_ARGS +#undef _SB_HEAD +#undef _SB_RAW_HEAD +#endif + +#endif /* STRING_BUF_H */ |