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}