mystic.c 13 KB

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