main.c (10162B)
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); /* discard buffered data */ 67 return f; 68} 69 70void 71unlockfile(FILE **f) 72{ 73 if (*f) { 74 fflush(*f); /* flush output to file */ 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 /* either choose cached or request new entry */ 306 if (arg && !strcmp(arg, "last")) { 307 if (!cached.valid) { 308 ERR("No cached info report available\n"); 309 return; 310 } 311 hash = cached.hash; 312 } else { 313 hash = mhash(arg ? arg : ask("> Model name: "), -1); 314 } 315 316 /* open and lock index file access */ 317 indexpath = aprintf("%s/.index", resultdir); 318 if (!(f = lockfile(indexpath, "r", LOCK_SH))) 319 goto exit; 320 321 /* output lines that have hash as prefix */ 322 reslen = matchlen = 0; 323 filename = aprintf("%s%s", loggedin ? "." : "", hash); 324 while ((c = fgetc(f)) > 0) { 325 if (c == '\n') { 326 matchlen = 0; 327 } else if (!matchlen && !authorized(c)) { 328 matchlen = -1; 329 } else if (matchlen >= 0 && c == filename[matchlen]) { 330 matchlen += 1; 331 if (matchlen == strlen(filename)) { 332 fseek(f, -matchlen, SEEK_CUR); 333 putchar(' '); 334 while ((c = fgetc(f)) > 0 && c != '\n') 335 putchar(c); 336 putchar('\n'); 337 matchlen = 0; 338 reslen += 1; 339 } 340 } else { 341 matchlen = -1; 342 } 343 } 344 345 unlockfile(&f); 346 347 if (!reslen) { 348 ERR("Sorry, no files matching that name\n"); 349 goto exit; 350 } 351 352 /* allow user to choose files to access */ 353 while (1) { 354 resp = ask("> Enter %s [q to quit]: ", 355 resp ? "another" : "hash"); 356 if (strchr(resp, 'q') || !*resp) break; 357 if (checkalph(resp, ".abcdef0123456789-") != OK) { 358 ERR("Invalid model id specified\n"); 359 goto exit; 360 } 361 362 seldir = aprintf("%s/%s", resultdir, resp); 363 if (handle_download(seldir) != OK) goto exit; 364 FREE(seldir); 365 } 366 367exit: 368 free(filename); 369 free(seldir); 370 free(indexpath); 371} 372 373void 374list_cmd(const char *arg) 375{ 376 struct parseinfo info; 377 char buf[256], *path; 378 FILE *f, *fn; 379 380 if (!loggedin) { 381 ERR("Not logged in!\n"); 382 return; 383 } 384 385 path = aprintf("%s/.index", resultdir); 386 f = lockfile(path, "r", LOCK_SH); 387 free(path); 388 if (!f) return; 389 390 while (fgets(buf, sizeof(buf), f)) { 391 if (*buf && buf[strlen(buf)-1] == '\n') 392 buf[strlen(buf)-1] = '\0'; 393 394 if (!authorized(*buf)) continue; 395 396 printf(">> %s\n", buf); 397 path = aprintf("%s/%s/info", resultdir, buf); 398 if ((fn = fopen(path, "r")) && load_info(&info, fn) == OK) { 399 print_info(&info); 400 free_info(&info); 401 } else { 402 ERR("Failed to read file info!\n"); 403 } 404 if (fn) fclose(fn); 405 free(path); 406 } 407 408 unlockfile(&f); 409} 410 411void 412auth_cmd(const char *arg) 413{ 414 const char *hash; 415 char *ndir, *indexpath; 416 int ret, existed; 417 FILE *f; 418 419 if (loggedin) { 420 ERR("Already logged in!\n"); 421 return; 422 } 423 424 existed = 0; 425 hash = mhash(arg ? arg : ask("> Enter a password: "), -1); 426 ndir = aprintf("%s/.%s", resultdir, hash); 427 ret = mkdir(ndir, S_IRWXU | S_IRWXG | S_IRWXO); 428 if (!ret) { 429 printf("Logged in with ID %s!\n", hash); 430 } else if (ret && errno == EEXIST) { 431 existed = 1; 432 printf("Logged in with ID %s!\nWelcome back!\n", hash); 433 } else { 434 ERR("Auth failed!\n"); 435 return; 436 } 437 438 if (!existed) { 439 indexpath = aprintf("%s/.index", resultdir); 440 if (!(f = lockfile(indexpath, "a+", LOCK_EX))) 441 return; 442 fputc('.', f); 443 fwrite(hash, 1, strlen(hash), f); 444 fputc('\n', f); 445 unlockfile(&f); 446 free(indexpath); 447 } 448 449 free(resultdir); 450 resultdir = ndir; 451 loggedin = 1; 452 free_info(&cached); 453} 454 455void 456cleanexit() 457{ 458 free_info(&cached); 459 free(resultdir); 460} 461 462void 463timeout() 464{ 465 die("time's up!\n"); 466} 467 468int 469main() 470{ 471 const char *cmd, *envstr; 472 int exit, i, cmdlen; 473 char *cp, *arg; 474 475 signal(SIGALRM, timeout); 476 alarm(180); 477 478 if (!(envstr = getenv("RESULTDIR"))) 479 die("RESULTDIR not defined\n"); 480 481 resultdir = checkp(strdup(envstr)); 482 483 setvbuf(stdin, NULL, _IONBF, 0); 484 setvbuf(stdout, NULL, _IONBF, 0); 485 setvbuf(stderr, NULL, _IONBF, 0); 486 487 atexit(cleanexit); 488 489 dump("msgs/banner"); 490 motd(); 491 492 exit = 0; 493 while (!exit) { 494 errno = 0; 495 cmd = ask("\r$ "); 496 if (!*cmd && errno == EBADMSG) break; 497 if (!*cmd) continue; 498 499 cp = strchr(cmd, ' '); 500 arg = cp ? cp + 1 : NULL; 501 cmdlen = cp ? cp - cmd : strlen(cmd); 502 503 for (i = 0; i < ARRSIZE(commands); i++) { 504 if (!strncmp(commands[i].name, cmd, cmdlen) 505 && !commands[i].name[cmdlen]) { 506 commands[i].func(arg); 507 break; 508 } 509 } 510 511 if (i == ARRSIZE(commands) && strlen(cmd) != 0) 512 ERR("No such command!\n"); 513 } 514 515 return EXIT_SUCCESS; 516}