diff --git a/stress-test/Makefile b/stress-test/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..bf775847ec8b46116b6435e5c9fc13b7bb6e7f92
--- /dev/null
+++ b/stress-test/Makefile
@@ -0,0 +1,12 @@
+PROJECT:=client
+OBJS:=client.o
+CFLAGS:=-Wall -std=gnu11 -ggdb
+LDFLAGS:=-lcurl -ltidy
+
+all: client
+
+%.o: %.c
+	gcc $< -c $(CFLAGS)
+
+$PROJECT: $(OBJS)
+	gcc $@-o $$ $(LDFLAGS)
diff --git a/stress-test/client.c b/stress-test/client.c
new file mode 100644
index 0000000000000000000000000000000000000000..cea43bc72fd6374bed6f86edecd1c38f7aea892b
--- /dev/null
+++ b/stress-test/client.c
@@ -0,0 +1,325 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <curl/curl.h>
+#include <tidy/tidy.h>
+#include <tidy/tidybuffio.h>
+#include <time.h>
+#include "config.h"
+
+struct memory {
+	char *response;
+	size_t size;
+};
+
+struct submit_action {
+	int contest_id;
+	int task_id;
+	char *local_file;
+	char *reported_filename;
+};
+
+static size_t write_callback(void *data, size_t size, size_t nmemb, void *userp) {
+	size_t realsize = size *nmemb;
+	struct memory *mem = (struct memory *)userp;
+
+	char *ptr = realloc(mem->response, mem->size + realsize + 1);
+	if (ptr == NULL)
+		return 0;
+
+	mem->response = ptr;
+	memcpy(&(mem->response[mem->size]), data, realsize);
+	mem->size += realsize;
+	mem->response[mem->size] = 0;
+
+	return realsize;
+}
+
+static void debug_print_cookies(CURL *curl) {
+	CURLcode res;
+	struct curl_slist *cookies;
+	struct curl_slist *nc;
+	int i;
+
+	printf("Cookies, curl knows:\n");
+	res = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies);
+	if (res != CURLE_OK) {
+		fprintf(stderr, "Curl curl_easy_getinfo failed: %s\n", curl_easy_strerror(res));
+		return;
+	}
+	nc = cookies;
+	i = 0;
+	while (nc) {
+		printf("[%d]: %s\n", i, nc->data);
+		nc = nc->next;
+		i++;
+	}
+	if (i == 0) {
+		printf("(none)\n");
+	}
+	curl_slist_free_all(cookies);
+}
+
+
+void setup_curl(CURL *curl,  struct memory *chunk) {
+	chunk->response = NULL;
+	chunk->size = 0;
+	curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // skip cert verification
+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // skip hostname verification (is it measureably faster?)
+
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk);
+	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+}
+
+void free_body(struct memory *chunk) {
+	free(chunk->response);
+	chunk->response = NULL;
+	chunk->size=0;
+}
+
+void print_body(CURL *curl, struct memory *chunk) {
+	printf("%li\n", chunk->size);
+	printf("BEGIN >>>>>>\n");
+	for (int i = 0; i < chunk->size; i++) {
+		printf("%c", chunk->response[i]);
+	}
+	printf("\n<<<<<< END\n");
+}
+
+void perform_get(CURL *curl, char *url) {
+	CURLcode res;
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+
+	res = curl_easy_perform(curl);
+	if (res != CURLE_OK)
+		fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
+}
+
+void search_csrf(TidyDoc doc, TidyNode tnod, char **csrf) {
+	TidyNode child;
+	for (child = tidyGetChild(tnod); child; child = tidyGetNext(child) ) {
+		ctmbstr name = tidyNodeGetName(child);
+		if (name) {
+			// It is HTML tag
+			TidyAttr attr;
+
+			bool it_is_csrf = false;
+			const char *it_has_value = NULL;
+			for (attr = tidyAttrFirst(child); attr; attr = tidyAttrNext(attr) ) {
+				if (!strcmp(tidyAttrName(attr), "id")) {
+					if (!strcmp(tidyAttrValue(attr), "csrf_token")) {
+						it_is_csrf = true;
+					}
+				}
+				if (!strcmp(tidyAttrName(attr), "value")) {
+					it_has_value = tidyAttrValue(attr);
+				}
+			}
+			if (it_is_csrf && it_has_value) {
+				//printf("Yay! csrf token = %s\n", it_has_value);
+				if (*csrf)
+					free(*csrf);
+				*csrf = strdup(it_has_value);
+			}
+		}
+		search_csrf(doc, child, csrf);
+	}
+}
+
+void extract_csrf(struct memory *chunk, char **csrf) {
+	TidyDoc tdoc;
+	TidyBuffer docbuf = {0};
+	TidyBuffer tidy_errbuf = {0};
+
+	tdoc = tidyCreate();
+
+	tidySetErrorBuffer(tdoc, &tidy_errbuf);
+	tidyBufInit(&docbuf);
+
+	// copy response buffer
+	tidyBufAppend(&docbuf, chunk->response, chunk->size);
+
+	tidyParseBuffer(tdoc, &docbuf); // real parse
+	search_csrf(tdoc, tidyGetRoot(tdoc), csrf); // find element
+	//fprintf(stderr, "%s\n", tidy_errbuf.bp); // print errors
+
+	tidyBufFree(&docbuf);
+	tidyBufFree(&tidy_errbuf);
+	tidyRelease(tdoc);
+}
+
+void load_login_page (CURL *curl, struct memory *chunk, char **csrf) {
+	perform_get(curl, BASE_URL "/");
+	free_body(chunk);
+
+	perform_get(curl, BASE_URL "/auth/login");
+	//print_body(curl, chunk);
+	//print_cookies(curl);
+	extract_csrf(chunk, csrf);
+	//printf("csrf token = %s\n", csrf);
+	free_body(chunk);
+}
+
+
+void do_login (CURL *curl, struct memory *chunk, char **csrf, char *login_email, char *login_password) {
+	char *enc_email = curl_easy_escape(curl, login_email, strlen(login_email));
+	char *enc_password = curl_easy_escape(curl, login_password, strlen(login_password));
+	char *enc_submit = curl_easy_escape(curl, "Přihlásit se", strlen("Přihlásit se"));
+	char *post_data;
+	asprintf(&post_data, "csrf_token=%s&email=%s&passwd=%s&submit=%s&next=", *csrf, enc_email, enc_password, enc_submit);
+	curl_free(enc_email);
+	curl_free(enc_password);
+	curl_free(enc_submit);
+
+	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
+
+	perform_get(curl, BASE_URL "/auth/login");
+	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
+	curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
+	free(post_data);
+	free_body(chunk);
+}
+
+void load_contest (CURL *curl, struct memory *chunk, int contest_id) {
+	char *url;
+	asprintf(&url, BASE_URL "/user/contest/%i/", contest_id);
+	perform_get(curl, url);
+	free(url);
+
+	//print_body(curl, chunk);
+	free_body(chunk);
+}
+
+void load_task (CURL *curl, struct memory *chunk, char **csrf,  int contest_id, int task_id) {
+	char *url;
+	asprintf(&url, BASE_URL "/user/contest/%i/task/%i/", contest_id, task_id);
+	perform_get(curl, url);
+	free(url);
+
+	//print_body(curl, chunk);
+	extract_csrf(chunk, csrf);
+	//printf("csrf token = %s\n", csrf);
+	free_body(chunk);
+}
+
+bool submit_task (CURL *curl, struct memory *chunk, char **csrf, int contest_id, int task_id, char *local_file, char *reported_filename) {
+	char *url;
+	asprintf(&url, BASE_URL "/user/contest/%i/task/%i/", contest_id, task_id);
+	curl_mime *mime;
+	curl_mimepart *part;
+
+	mime = curl_mime_init(curl);
+
+	part = curl_mime_addpart(mime);
+	curl_mime_data(part, *csrf, CURL_ZERO_TERMINATED);
+	curl_mime_name(part, "csrf_token");
+
+	part = curl_mime_addpart(mime);
+	curl_mime_data(part, "Jenduv automatizovany test submit", CURL_ZERO_TERMINATED);
+	curl_mime_name(part, "note");
+
+	part = curl_mime_addpart(mime);
+	curl_mime_data(part, "Odevzdat", CURL_ZERO_TERMINATED);
+	curl_mime_name(part, "submit");
+
+	part = curl_mime_addpart(mime);
+	curl_mime_filedata(part, local_file);
+	curl_mime_filename(part, reported_filename);
+	curl_mime_name(part, "file");
+
+	curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+
+	char errbuf[CURL_ERROR_SIZE];
+	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
+	errbuf[0] = 0;
+
+	CURLcode res = curl_easy_perform(curl);
+
+	if(res != CURLE_OK) {
+		size_t len = strlen(errbuf);
+		fprintf(stderr, "\nlibcurl: (%d) ", res);
+		if(len)
+			fprintf(stderr, "%s%s", errbuf,
+					((errbuf[len - 1] != '\n') ? "\n" : ""));
+		else
+			fprintf(stderr, "%s\n", curl_easy_strerror(res));
+	}
+
+	free(url);
+	curl_easy_setopt(curl, CURLOPT_MIMEPOST, NULL);
+	curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
+	curl_mime_free(mime);
+
+	long http_code = 0;
+	curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
+	if (http_code != 200) {
+		printf("Post file failed:\n");
+		print_body(curl, chunk);
+		free_body(chunk);
+		return false;
+	}
+	free_body(chunk);
+	return true;
+}
+
+bool full_test(char *login_email, char *login_password, struct submit_action *actions) {
+	bool ret = true;
+	CURL *curl;
+	curl_global_init(CURL_GLOBAL_DEFAULT);
+	curl = curl_easy_init();
+
+	struct memory chunk;
+	char *csrf = NULL;
+	if (curl) {
+		setup_curl(curl, &chunk);
+		load_login_page(curl, &chunk, &csrf);
+
+		do_login(curl, &chunk, &csrf, login_email, login_password);
+
+		while (actions->contest_id) {
+			/*
+			printf("performing action %i %i %s %s\n", actions->contest_id, actions->task_id,
+					actions->local_file, actions->reported_filename);
+			*/
+			load_contest(curl, &chunk, actions->contest_id);
+			load_task(curl,  &chunk,&csrf, actions->contest_id, actions->task_id);
+
+			if (!submit_task(curl, &chunk, &csrf,
+					actions->contest_id, actions->task_id,
+					actions->local_file, actions->reported_filename)) {
+				ret = false;
+			}
+
+			actions++;
+		}
+
+		// cleanup
+		curl_easy_cleanup(curl);
+	}
+	curl_global_cleanup();
+	if (csrf)
+		free(csrf);
+	return ret;
+}
+
+int main() {
+	char *login_email = "had+ucastnik@kam.mff.cuni.cz";
+	char *login_password = "UTX0seFkVim51vkOwER5G8GjB5eEq7G9";
+
+	struct submit_action actions[] = {
+		//{ 25, 3, "/home/had/BratruvZpevnik_v4-0.pdf", "zpevnik.pdf"},
+		//{ 25, 4, "testsubmit_02.pdf", "moje_reseni.pdf"},
+		//{ 25, 5, "/home/had/sada3.pdf", "sendvic.pdf"},
+		//{ 25, 6, "/home/had/stitek.pdf", "sken.pdf"},
+		{ 0, 0, NULL, NULL}
+	};
+
+	full_test(login_email, login_password, actions);
+
+	return 0;
+}
diff --git a/stress-test/config.h b/stress-test/config.h
new file mode 100644
index 0000000000000000000000000000000000000000..8eeb474cc81ffa06c40f1b905ccd8cd10c402861
--- /dev/null
+++ b/stress-test/config.h
@@ -0,0 +1,6 @@
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+#define BASE_URL	"https://mo.mff.cuni.cz/osmo-test"
+
+#endif