enowars5-service-stldoctor

STL-Analyzing A/D Service for ENOWARS5 in 2021
git clone https://git.sinitax.com/sinitax/enowars5-service-stldoctor
Log | Files | Refs | README | LICENSE | sfeed.txt

main.c (9932B)


      1#include <stdio.h>
      2#include <string.h>
      3#include <stdarg.h>
      4#include <unistd.h>
      5#include <fcntl.h>
      6#include <time.h>
      7#include <errno.h>
      8#include <signal.h>
      9
     10#include <sys/stat.h>
     11#include <sys/file.h>
     12
     13#include "stlfile.h"
     14#include "util.h"
     15
     16#define MAXFILESIZE 1000000
     17
     18struct command {
     19	const char *name;
     20	void (*func)(const char *);
     21	const char *desc;
     22};
     23
     24int save_submission(struct parseinfo *info, char *data, int len);
     25
     26void cat_cmd(const char *arg);
     27void help_cmd(const char *arg);
     28void exit_cmd(const char *arg);
     29void echo_cmd(const char *arg);
     30void upload_cmd(const char *arg);
     31void search_cmd(const char *arg);
     32void list_cmd(const char *arg);
     33void auth_cmd(const char *arg);
     34
     35void cleanexit();
     36
     37struct command commands[] = {
     38	{ "cat",    cat_cmd,    "Cat cmd go prrrrr." },
     39	{ "help",   help_cmd,   "You already know what this does." },
     40	{ "exit",   exit_cmd,   "Closes the session." },
     41	{ "echo",   echo_cmd,   "Repeat after me!" },
     42	{ "upload", upload_cmd, "Upload an STL file to analyze." },
     43	{ "search", search_cmd, "Search for an STL file by model name." },
     44	{ "list",   list_cmd,   "List your uploaded files." },
     45	{ "auth",   auth_cmd,   "Login to upload files to a private dir." }
     46};
     47
     48struct parseinfo cached = { 0 };
     49char *resultdir;
     50int echo = 0, loggedin = 0;
     51
     52FILE*
     53lockfile(const char *path, const char* mode, int locktype)
     54{
     55	FILE *f;
     56
     57	if (!(f = fopen(path, mode))) {
     58		ERR("Failed to open index file\n");
     59		return NULL;
     60	}
     61	if (flock(fileno(f), locktype)) {
     62		fclose(f);
     63		ERR("Failed to acquire lock on index file\n");
     64		return NULL;
     65	}
     66	fflush(f); 
     67	return f;
     68}
     69
     70void
     71unlockfile(FILE **f)
     72{
     73	if (*f) {
     74		fflush(*f); 
     75		flock(fileno(*f), LOCK_UN);
     76		fclose(*f);
     77		*f = NULL;
     78	}
     79}
     80
     81int
     82authorized(char prefix)
     83{
     84	return ((prefix == '.') == (loggedin > 0));
     85}
     86
     87int
     88save_submission(struct parseinfo *info, char *stldata, int stlsize)
     89{
     90	char *dirpath = NULL, *infopath = NULL, *modeldir = NULL,
     91	     *indexpath = NULL, *modelpath = NULL;
     92	FILE *f = NULL;
     93	int status = OK;
     94
     95	modeldir = aprintf("%s%s-%i", loggedin ? "." : "",
     96			  info->hash, time(NULL));
     97	dirpath = aprintf("%s/%s", resultdir, modeldir);
     98	if (mkdir(dirpath, S_IRWXU | S_IRWXG | S_IRWXO)) goto fail;
     99
    100	modelpath = aprintf("%s/%s", dirpath, "model");
    101	if (!(f = fopen(modelpath, "w+"))) goto fail;
    102	if (fwrite(stldata, 1, stlsize, f) != stlsize) goto fail;
    103	FCLOSE(f);
    104
    105	infopath = aprintf("%s/%s", dirpath, "info");
    106	if (!(f = fopen(infopath, "w+"))) goto fail;
    107	if (save_info(info, f) != OK) goto fail;
    108	FCLOSE(f);
    109
    110	indexpath = aprintf("%s/.index", resultdir);
    111	if (!(f = lockfile(indexpath, "a+", LOCK_EX)))
    112		goto exit;
    113	fwrite(modeldir, 1, strlen(modeldir), f);
    114	fputc('\n', f);
    115	unlockfile(&f);
    116
    117exit:
    118	FCLOSE(f);
    119
    120	free(dirpath);
    121	free(modelpath);
    122	free(infopath);
    123	free(indexpath);
    124	free(modeldir);
    125
    126	return status;
    127
    128fail:
    129	if (infopath) remove(infopath);
    130	if (modelpath) remove(modelpath);
    131	if (dirpath) remove(dirpath);
    132
    133	status = FAIL;
    134	goto exit;
    135}
    136
    137int
    138handle_download(const char *scandir)
    139{
    140	char *infopath = NULL, *modelpath = NULL;
    141	size_t i, size;
    142	int status = OK;
    143	FILE *f;
    144
    145	infopath = aprintf("%s/%s", scandir, "info");
    146	if (!(f = fopen(infopath, "r"))) {
    147		ERR("Selected result is missing!\n");
    148		goto fail;
    149	}
    150
    151	free_info(&cached);
    152	if (load_info(&cached, f) != OK) {
    153		ERR("Failed to parse info file!\n");
    154		goto fail;
    155	}
    156	FCLOSE(f);
    157
    158	print_info(&cached);
    159
    160	if (strchr(ask("> Download model? "), 'y')) {
    161		modelpath = aprintf("%s/%s", scandir, "model");
    162		if (!(f = fopen(modelpath, "r"))) {
    163			ERR("Failed to access file!\n");
    164			goto fail;
    165		}
    166		fseek(f, 0, SEEK_END);
    167		size = ftell(f);
    168		fseek(f, 0, SEEK_SET);
    169		if (size >= MAXFILESIZE) {
    170			ERR("File is too large!\n");
    171			goto fail;
    172		}
    173		printf("Here you go.. (%liB)\n", size);
    174		while ((i = getc(f)) != EOF)
    175			putc(i, stdout);
    176		FCLOSE(f);
    177	}
    178
    179exit:
    180	if (f) fclose(f);
    181	free(infopath);
    182	free(modelpath);
    183	return status;
    184
    185fail:
    186	status = FAIL;
    187	goto exit;
    188}
    189
    190void
    191motd()
    192{
    193	char linebuf[80];
    194	int msgc, msgi, i;
    195	FILE *f;
    196
    197	if (!(f = fopen("msgs/motd", "r"))) return;
    198
    199	if (!fgets(linebuf, sizeof(linebuf), f))
    200		goto exit;
    201
    202	srand(time(NULL));
    203	if ((msgc = atoi(linebuf))) {
    204		msgi = rand() % msgc;
    205		for (i = 0; i < msgi + 1; i++)
    206			if (!fgets(linebuf, sizeof(linebuf), f))
    207				return;
    208		printf("%s\n", linebuf);
    209	}
    210
    211exit:
    212	fclose(f);
    213}
    214
    215void
    216cat_cmd(const char *arg)
    217{
    218	if (arg && !strncmp(arg, "flag", 4))
    219		dump("msgs/cat_flag");
    220	else
    221		printf("meow\n");
    222}
    223
    224void
    225help_cmd(const char *arg)
    226{
    227	int i;
    228
    229	for (i = 0; arg && i < ARRSIZE(commands); i++) {
    230		if (!strcmp(commands[i].name, arg)) {
    231			printf("%s\n", commands[i].desc);
    232			return;
    233		}
    234	}
    235
    236	printf("Available commands:\n");
    237	for (i = 0; i < ARRSIZE(commands); i++)
    238		printf("%s%s", i ? " " : "", commands[i].name);
    239	printf("\n");
    240}
    241
    242void
    243exit_cmd(const char *arg)
    244{
    245	printf("bye!\n");
    246	exit(0);
    247}
    248
    249void
    250echo_cmd(const char *arg)
    251{
    252	echo ^= 1;
    253	printf("Echo is %s\n", echo ? "enabled" : "disabled");
    254}
    255
    256void
    257upload_cmd(const char *arg)
    258{
    259	char *end, *contents = NULL, *modelname = NULL;
    260	const char *resp;
    261	size_t len;
    262
    263	modelname = checkp(strdup(ask("> Model name: ")));
    264	if (!strlen(modelname)) {
    265		ERR("Empty model names are not allowed");
    266		goto exit;
    267	}
    268
    269	resp = ask("> File size: ");
    270	len = strtoul(resp, &end, 10);
    271	if (len <= 0 || len >= MAXFILESIZE || *end) {
    272		ERR("Invalid file length!\n");
    273		goto exit;
    274	}
    275
    276	printf("Ok! Im listening..\n");
    277	contents = checkp(malloc(len + 1));
    278	if (fread(contents, 1, len, stdin) != len) {
    279		ERR("Not enough data received!\n");
    280		goto exit;
    281	}
    282	contents[len] = '\0';
    283
    284	if ((cached.valid = parse_file(&cached, contents, len, &modelname))) {
    285		if (save_submission(&cached, contents, len) != OK)
    286			ERR("Failed to save your submission!\n");
    287		else
    288			printf("Your file was saved with ID %s!\n", cached.hash);
    289	}
    290
    291exit:
    292	free(contents);
    293	free(modelname);
    294}
    295
    296void
    297search_cmd(const char *arg)
    298{
    299	const char *hash, *resp = NULL;
    300	char *indexpath = NULL, *filename = NULL,
    301	     *seldir = NULL;
    302	int i, c, matchlen, reslen;
    303	FILE *f;
    304
    305	if (arg && !strcmp(arg, "last")) {
    306		if (!cached.valid) {
    307			ERR("No cached info report available\n");
    308			return;
    309		}
    310		hash = cached.hash;
    311	} else {
    312		hash = mhash(arg ? arg : ask("> Model name: "), -1);
    313	}
    314
    315	indexpath = aprintf("%s/.index", resultdir);
    316	if (!(f = lockfile(indexpath, "r", LOCK_SH)))
    317		goto exit;
    318
    319	reslen = matchlen = 0;
    320	filename = aprintf("%s%s", loggedin ? "." : "", hash);
    321	while ((c = fgetc(f)) > 0) {
    322		if (c == '\n') {
    323			matchlen = 0;
    324		} else if (!matchlen && !authorized(c)) {
    325			matchlen = -1;
    326		} else if (matchlen >= 0 && c == filename[matchlen]) {
    327			matchlen += 1;
    328			if (matchlen == strlen(filename)) {
    329				fseek(f, -matchlen, SEEK_CUR);
    330				putchar(' ');
    331				while ((c = fgetc(f)) > 0 && c != '\n')
    332					putchar(c);
    333				putchar('\n');
    334				matchlen = 0;
    335				reslen += 1;
    336			}
    337		} else {
    338			matchlen = -1;
    339		}
    340	}
    341
    342	unlockfile(&f);
    343
    344	if (!reslen) {
    345		ERR("Sorry, no files matching that name\n");
    346		goto exit;
    347	}
    348
    349	while (1) {
    350		resp = ask("> Enter %s [q to quit]: ",
    351				resp ? "another" : "hash");
    352		if (strchr(resp, 'q') || !*resp) break;
    353		if (checkalph(resp, ".abcdef0123456789-") != OK) {
    354			ERR("Invalid model id specified\n");
    355			goto exit;
    356		}
    357
    358		seldir = aprintf("%s/%s", resultdir, resp);
    359		if (handle_download(seldir) != OK) goto exit;
    360		FREE(seldir);
    361	}
    362
    363exit:
    364	free(filename);
    365	free(seldir);
    366	free(indexpath);
    367}
    368
    369void
    370list_cmd(const char *arg)
    371{
    372	struct parseinfo info;
    373	char buf[256], *path;
    374	FILE *f, *fn;
    375
    376	if (!loggedin) {
    377		ERR("Not logged in!\n");
    378		return;
    379	}
    380
    381	path = aprintf("%s/.index", resultdir);
    382	f = lockfile(path, "r", LOCK_SH);
    383	free(path);
    384	if (!f) return;
    385
    386	while (fgets(buf, sizeof(buf), f)) {
    387		if (*buf && buf[strlen(buf)-1] == '\n')
    388			buf[strlen(buf)-1] = '\0';
    389
    390		if (!authorized(*buf)) continue;
    391
    392		printf(">> %s\n", buf);
    393		path = aprintf("%s/%s/info", resultdir, buf);
    394		if ((fn = fopen(path, "r")) && load_info(&info, fn) == OK) {
    395			print_info(&info);
    396			free_info(&info);
    397		} else {
    398			ERR("Failed to read file info!\n");
    399		}
    400		if (fn) fclose(fn);
    401		free(path);
    402	}
    403
    404	unlockfile(&f);
    405}
    406
    407void
    408auth_cmd(const char *arg)
    409{
    410	const char *hash;
    411	char *ndir, *indexpath;
    412	int ret, existed;
    413	FILE *f;
    414
    415	if (loggedin) {
    416		ERR("Already logged in!\n");
    417		return;
    418	}
    419
    420	existed = 0;
    421	hash = mhash(arg ? arg : ask("> Enter a password: "), -1);
    422	ndir = aprintf("%s/.%s", resultdir, hash);
    423	ret = mkdir(ndir, S_IRWXU | S_IRWXG | S_IRWXO);
    424	if (!ret) {
    425		printf("Logged in with ID %s!\n", hash);
    426	} else if (ret && errno == EEXIST) {
    427		existed = 1;
    428		printf("Logged in with ID %s!\nWelcome back!\n", hash);
    429	} else {
    430		ERR("Auth failed!\n");
    431		return;
    432	}
    433
    434	if (!existed) {
    435		indexpath = aprintf("%s/.index", resultdir);
    436		if (!(f = lockfile(indexpath, "a+", LOCK_EX)))
    437			return;
    438		fputc('.', f);
    439		fwrite(hash, 1, strlen(hash), f);
    440		fputc('\n', f);
    441		unlockfile(&f);
    442		free(indexpath);
    443	}
    444
    445	free(resultdir);
    446	resultdir = ndir;
    447	loggedin = 1;
    448	free_info(&cached);
    449}
    450
    451void
    452cleanexit()
    453{
    454	free_info(&cached);
    455	free(resultdir);
    456}
    457
    458void
    459timeout()
    460{
    461	die("time's up!\n");
    462}
    463
    464int
    465main()
    466{
    467	const char *cmd, *envstr;
    468	int exit, i, cmdlen;
    469	char *cp, *arg;
    470
    471	signal(SIGALRM, timeout);
    472	alarm(180);
    473
    474	if (!(envstr = getenv("RESULTDIR")))
    475		die("RESULTDIR not defined\n");
    476
    477	resultdir = checkp(strdup(envstr));
    478
    479	setvbuf(stdin, NULL, _IONBF, 0);
    480	setvbuf(stdout, NULL, _IONBF, 0);
    481	setvbuf(stderr, NULL, _IONBF, 0);
    482
    483	atexit(cleanexit);
    484
    485	dump("msgs/banner");
    486	motd();
    487
    488	exit = 0;
    489	while (!exit) {
    490		errno = 0;
    491		cmd = ask("\r$ ");
    492		if (!*cmd && errno == EBADMSG) break;
    493		if (!*cmd) continue;
    494
    495		cp = strchr(cmd, ' ');
    496		arg = cp ? cp + 1 : NULL;
    497		cmdlen = cp ? cp - cmd : strlen(cmd);
    498
    499		for (i = 0; i < ARRSIZE(commands); i++) {
    500			if (!strncmp(commands[i].name, cmd, cmdlen)
    501					&& !commands[i].name[cmdlen]) {
    502				commands[i].func(arg);
    503				break;
    504			}
    505		}
    506
    507		if (i == ARRSIZE(commands) && strlen(cmd) != 0)
    508			ERR("No such command!\n");
    509	}
    510
    511	return EXIT_SUCCESS;
    512}