mystic.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <unistd.h> // usleep()
  4. #include <pty.h>
  5. #include <termios.h>
  6. #include <fcntl.h>
  7. #include <sys/select.h>
  8. #include <sys/wait.h>
  9. #include <signal.h> // handle Ctrl-C/SIGINT
  10. #include <time.h> // usleep(), nanonsleep() ?
  11. #include <strings.h> // strcasecmp
  12. #include <stdlib.h> // random()
  13. #include <ctype.h>
  14. #define TARGET "./mySTIC"
  15. #define BSIZE 1024
  16. #define LOGGING
  17. #ifdef LOGGING
  18. FILE * fp;
  19. #endif
  20. const char * it_is_now(void) {
  21. static char buffer[100];
  22. time_t timer;
  23. struct tm* tm_info;
  24. timer = time(NULL);
  25. tm_info = localtime(&timer);
  26. strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);
  27. return buffer;
  28. }
  29. void slow_write(int fd, int speed, char * buffer, int len) {
  30. int x;
  31. for( x = 0; x < len; x++) {
  32. usleep(speed);
  33. write( fd, &buffer[x], 1);
  34. }
  35. }
  36. struct render {
  37. int speed;
  38. int effect;
  39. } current_render;
  40. int render_overlimit = 0;
  41. void reset_render(void) {
  42. current_render.speed = 0;
  43. current_render.effect = 0;
  44. render_overlimit = 0;
  45. }
  46. #define TRIGGER "^"
  47. #define SLEEP_LIMIT 30
  48. int ms_sleep(unsigned int ms) {
  49. int result = 0;
  50. struct timespec ts = {
  51. ms / 1000,
  52. (ms % 1000) * 1000000L
  53. };
  54. do {
  55. struct timespec ts_sleep = ts;
  56. result = nanosleep(&ts_sleep, &ts);
  57. } while ( (-1 == result));
  58. return result;
  59. }
  60. void render_sleep(void) {
  61. if (render_overlimit)
  62. return;
  63. if (current_render.speed) { // * 100 still too slow.
  64. ms_sleep(current_render.speed * 10);
  65. }
  66. }
  67. void write_color(int fd, int color) {
  68. char buffer[10];
  69. sprintf(buffer, "\x1b[%dm", color);
  70. write(fd, buffer, strlen(buffer));
  71. }
  72. const char * process_trigger(int fd, const char * cp) {
  73. char ch;
  74. int i, x, y;
  75. ch = toupper(*cp);
  76. cp++;
  77. switch(ch) {
  78. case 'D':
  79. i = 0;
  80. if (isdigit(*cp)) {
  81. i = (*cp) - '0';
  82. cp++;
  83. };
  84. if (isdigit(*cp)) {
  85. i *= 10;
  86. i += (*cp) - '0';
  87. cp++;
  88. };
  89. if ((i > 0) && (i < 80)) {
  90. #ifdef BUGGER
  91. fprintf(fp, "DEL %02d\n", i);
  92. #endif
  93. for (x = 0; x < i; x++ ) {
  94. write(fd, "\b \b", 3);
  95. }
  96. };
  97. break;
  98. case 'C':
  99. i = 0;
  100. if (isdigit(*cp)) {
  101. i = (*cp) - '0';
  102. cp++;
  103. };
  104. if (isdigit(*cp)) {
  105. i *= 10;
  106. i += (*cp) - '0';
  107. cp++;
  108. };
  109. write_color(fd, i);
  110. break;
  111. case 'G':
  112. x = 0;
  113. if (isdigit(*cp)) {
  114. x = (*cp) - '0';
  115. cp++;
  116. };
  117. if (isdigit(*cp)) {
  118. x *= 10;
  119. x += (*cp) - '0';
  120. cp++;
  121. };
  122. y = 0;
  123. if (isdigit(*cp)) {
  124. y = (*cp) - '0';
  125. cp++;
  126. };
  127. if (isdigit(*cp)) {
  128. y *= 10;
  129. y += (*cp) - '0';
  130. cp++;
  131. };
  132. break;
  133. case 'R':
  134. i = 0;
  135. if (isdigit(*cp)) {
  136. i = (*cp) - '0';
  137. cp++;
  138. };
  139. if ( ( i > 0 ) && ( i < 10) ) {
  140. #ifdef BUGGER
  141. fprintf(fp, "RENDER %d\n", i);
  142. #endif
  143. current_render.effect = i;
  144. } else {
  145. current_render.effect = 0;
  146. }
  147. break;
  148. case 'S':
  149. i = 0;
  150. if (isdigit(*cp)) {
  151. i = (*cp) - '0';
  152. cp++;
  153. };
  154. if ( ( i > 0 ) && ( i < 10) ) {
  155. #ifdef BUGGER
  156. fprintf(fp, "SPEED %d\n", i);
  157. #endif
  158. current_render.speed = i;
  159. } else {
  160. current_render.speed = 0;
  161. }
  162. break;
  163. case 'P':
  164. i = 0;
  165. if (isdigit(*cp)) {
  166. i = (*cp) - '0';
  167. cp++;
  168. };
  169. if ( ( i > 0 ) && ( i < 10) ) {
  170. #ifdef BUGGER
  171. fprintf(fp, "PAWS %d\n", i);
  172. #endif
  173. // sleep(i);
  174. if (!render_overlimit) {
  175. sleep(i);
  176. };
  177. }
  178. break;
  179. }
  180. return cp;
  181. }
  182. void render_effect(int fd, char ch) {
  183. int effect = current_render.effect;
  184. int l;
  185. char space = ' ';
  186. char bs = '\b';
  187. switch (effect) {
  188. case 1:
  189. // CHAR + SPC + BS
  190. render_sleep();
  191. write(fd, &ch, 1);
  192. render_sleep(); // Maybe extra sleep here?
  193. write(fd, &space, 1);
  194. render_sleep();
  195. write(fd, &bs, 1);
  196. break;
  197. case 2:
  198. // CHAR + 8 spaces + 8 BS
  199. render_sleep();
  200. write(fd, &ch, 1);
  201. for(l = 0; l < 8; l++) {
  202. render_sleep();
  203. write(fd, &space, 1);
  204. }
  205. for(l = 0; l < 8; l++) {
  206. render_sleep();
  207. write(fd, &bs, 1);
  208. }
  209. break;
  210. case 0:
  211. default:
  212. // NORMAL
  213. render_sleep();
  214. write(fd, &ch, 1);
  215. break;
  216. }
  217. }
  218. void render( int fd, const char * string_out ) {
  219. const char * cp = string_out;
  220. const char * trigger = cp;
  221. time_t start = time(NULL);
  222. int elapsed;
  223. int over = 0;
  224. reset_render();
  225. #ifdef BUGGER
  226. fprintf(fp, "render(%d, %s)\n", fd, string_out);
  227. fflush(fp);
  228. #endif
  229. // Check our time from time to time.
  230. // If we start running long, kill sleeps.
  231. while ( (trigger = strstr(cp, TRIGGER)) != NULL) {
  232. // There is special things to handle in here.
  233. while ( cp != trigger ) {
  234. elapsed = time(NULL)-start;
  235. if (elapsed > SLEEP_LIMIT) {
  236. render_overlimit = 1;
  237. current_render.speed = 0;
  238. };
  239. #ifdef BUGGER
  240. fprintf(fp, "re(%c)\n", *cp);
  241. fflush(fp);
  242. #endif
  243. // write(fd, cp, 1 );
  244. render_effect(fd, *cp);
  245. cp++;
  246. };
  247. #ifdef BUGGER
  248. fprintf(fp, "at trigger: (%s)\n", cp);
  249. fflush(fp);
  250. #endif
  251. cp += strlen(TRIGGER);
  252. // Ok, we're pointing at the trigger -- do something.
  253. cp = process_trigger(fd, cp);
  254. #ifdef BUGGER
  255. fprintf(fp, "after trigger: (%s)\n", cp);
  256. fflush(fp);
  257. #endif
  258. };
  259. // We still might be under a rendering effect.
  260. while (*cp != 0) {
  261. elapsed = time(NULL)-start;
  262. if (elapsed > SLEEP_LIMIT) {
  263. render_overlimit = 1;
  264. current_render.speed = 0;
  265. };
  266. // write(fd, cp, 1);
  267. render_effect(fd, *cp);
  268. cp++;
  269. }
  270. }
  271. void harry_event(int fd) {
  272. // Make something happen
  273. char buffer[100];
  274. int r = random() % 5;
  275. // This is no where near finished, BUT!
  276. // TO FIX: Remember the last phrase used,
  277. // and don't repeat (the last two)!
  278. const char * phrases[] =
  279. {
  280. "Hahaha",
  281. "Snicker, snicker",
  282. "Boo!",
  283. "MeOW",
  284. "I see U"
  285. };
  286. const char * cp;
  287. cp = phrases[r];
  288. sprintf(buffer, "^S2%s^P2^D%02d", cp, (int)strlen(cp));
  289. #ifdef LOGGING
  290. fprintf(fp, "harry_event: render(%d, \"%s\")\n", fd, buffer);
  291. fflush(fp);
  292. #endif
  293. render(fd, buffer);
  294. }
  295. /*
  296. NOTE: Logging is single file, don't use in production!
  297. It won't handle multiple writers.
  298. */
  299. char * username = NULL;
  300. char * fullname = NULL;
  301. void pcopy(char * pstring, char * str) {
  302. int len = (int)*pstring;
  303. strncpy( str, pstring+1, len );
  304. str[len] = 0;
  305. }
  306. /*
  307. This only works for those few idiots that use SSH,
  308. the Mystic broken SSH!
  309. */
  310. int locate_user(const char *alias) {
  311. FILE * user;
  312. char buffer[0x600];
  313. char temp[100];
  314. user = fopen("data/users.dat", "rb");
  315. if (user == NULL)
  316. return 0;
  317. // Carry on!
  318. while (fread(buffer, 0x600, 1, user) == 1) {
  319. pcopy( buffer + 0x6d, temp );
  320. if ( strcasecmp( temp, username) == 0) {
  321. pcopy(buffer + 0x8c, temp );
  322. fullname = strdup(temp);
  323. break;
  324. }
  325. /*
  326. printf("Alias: %s\n", temp);
  327. pcopy(buffer + 0x8c, temp );
  328. printf("Full Name: %s\n", temp );
  329. */
  330. }
  331. fclose(user);
  332. return 1;
  333. }
  334. // Buffers are BSIZE + 1, so a buffer that size can strcpy safely.
  335. void mangle( char * buffer ) {
  336. // Ok, we want to look for words to:
  337. // MaNGlE , or transpose (both?!)
  338. // or possibly transpose words
  339. }
  340. int main(int argc, char * argv[])
  341. {
  342. int master;
  343. pid_t pid;
  344. #ifdef LOGGING
  345. // FILE * fp;
  346. fp = fopen("mystic_pipe.data", "wb");
  347. if ( fp == NULL ) {
  348. return 2;
  349. }
  350. #endif
  351. srandom(time(NULL));
  352. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz -PUWISHPASSWORD
  353. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
  354. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz -PUP2LAT3
  355. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
  356. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
  357. // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML0 -SL1 -ST0 -CUnknown
  358. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz -PUP2LAT3
  359. // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML1 -SL1 -ST2 -CUnknown -Ubugz -PUP2LAT3
  360. // SSH: -ML1 -ST2
  361. // Telnet: -ML0 -ST0
  362. // Locate username (if given) in the command line
  363. // -U<username>
  364. for (int x = 0; x < argc; x++) {
  365. if (strncmp("-U", argv[x], 2) == 0) {
  366. username = argv[x] + 2;
  367. #ifdef LOGGING
  368. fprintf(fp, "Username: [%s]\n", username);
  369. #endif
  370. break;
  371. }
  372. }
  373. if (username != NULL) {
  374. locate_user(username);
  375. #ifdef LOGGING
  376. fprintf(fp, "Username: [%s] A.K.A. [%s]\n", username, fullname);
  377. #endif
  378. }
  379. // With IGNBRK I don't think I need this anymore. (Nope!)
  380. // signal(SIGINT, SIG_IGN);
  381. pid = forkpty(&master, NULL, NULL, NULL);
  382. // impossible to fork
  383. if (pid < 0) {
  384. return 1;
  385. }
  386. // child
  387. else if (pid == 0) {
  388. char *args[20]; // max 20 args
  389. int x;
  390. args[0] = TARGET;
  391. for ( x = 1; x < argc; x++ ) {
  392. args[x] = argv[x];
  393. };
  394. args[x] = NULL;
  395. // run Mystic, run!
  396. execvp( TARGET, args);
  397. }
  398. // parent
  399. else {
  400. // remove the echo
  401. // ICANON - raw mode. No more line buffering!
  402. struct termios tios, orig1;
  403. struct timeval timeout;
  404. int last_event;
  405. tcgetattr(master, &tios);
  406. tios.c_lflag &= ~(ECHO | ECHONL | ICANON );
  407. tcsetattr(master, TCSAFLUSH, &tios);
  408. tcgetattr(1, &orig1);
  409. tios = orig1;
  410. tios.c_iflag &= ~(ICRNL | IXON); // Disable software flow control
  411. tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN );
  412. // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
  413. // ISIG should be Ctrl-C and Ctrl-Z IGNBRK +
  414. tcsetattr(1, TCSAFLUSH, &tios);
  415. for (;;) {
  416. // define estruturas para o select, que serve para verificar qual
  417. // se tornou "pronto pra uso"
  418. fd_set read_fd;
  419. fd_set write_fd;
  420. fd_set except_fd;
  421. // inicializa as estruturas
  422. FD_ZERO(&read_fd);
  423. FD_ZERO(&write_fd);
  424. FD_ZERO(&except_fd);
  425. // atribui o descritor master, obtido pelo forkpty, ao read_fd
  426. FD_SET(master, &read_fd);
  427. // atribui o stdin ao read_fd
  428. FD_SET(STDIN_FILENO, &read_fd);
  429. // o descritor tem que ser unico para o programa, a documentacao
  430. // recomenda um calculo entre os descritores sendo usados + 1
  431. // we're in luck! The last parameter is time interval/timeout. :D
  432. timeout.tv_sec = 10;
  433. timeout.tv_usec = 0;
  434. // select(master+1, &read_fd, &write_fd, &except_fd, NULL);
  435. if ( select(master+1, &read_fd, &write_fd, &except_fd, &timeout) == 0 ) {
  436. // This means timeout!
  437. harry_event(STDOUT_FILENO);
  438. #ifdef BUGGER
  439. fprintf(fp, "%s : TICK\n", it_is_now());
  440. fprintf(fp, "STDOUT is %d\n", STDOUT_FILENO);
  441. #endif
  442. }
  443. char input[BSIZE + 1];
  444. char output[BSIZE + 1];
  445. int total;
  446. // read_fd esta atribuido com read_fd?
  447. if (FD_ISSET(master, &read_fd))
  448. {
  449. // leia o que bc esta mandando
  450. if ((total = read(master, &output, BSIZE)) != -1) {
  451. // e escreva isso na saida padrao
  452. output[total] = 0;
  453. if (random() % 20 < 3) {
  454. mangle( output );
  455. }
  456. write(STDOUT_FILENO, &output, total);
  457. // This is OUTPUT from the BBS
  458. #ifdef LOGGING
  459. fprintf(fp, ">> [%s]\n", output);
  460. #endif
  461. } else
  462. break;
  463. }
  464. // read_fd esta atribuido com a entrada padrao?
  465. if (FD_ISSET(STDIN_FILENO, &read_fd))
  466. {
  467. // leia a entrada padrao
  468. total = read(STDIN_FILENO, &input, BSIZE);
  469. input[total] = 0;
  470. // e escreva no bc
  471. write(master, &input, total);
  472. // This is INPUT from the USER
  473. #ifdef LOGGING
  474. fprintf(fp, "<< [%s]\n", input);
  475. #endif
  476. }
  477. }
  478. // Restore terminal
  479. tcsetattr(1, TCSAFLUSH, &orig1);
  480. #ifdef LOGGING
  481. fclose(fp);
  482. #endif
  483. }
  484. return 0;
  485. }