123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h> // usleep()
- #include <pty.h>
- #include <termios.h>
- #include <fcntl.h>
- #include <sys/select.h>
- #include <sys/wait.h>
- #include <signal.h> // handle Ctrl-C/SIGINT
- #include <time.h> // usleep(), nanonsleep() ?
- #include <strings.h> // strcasecmp
- #include <stdlib.h> // random()
- #include <ctype.h>
- #define TARGET "./mySTIC"
- #define BSIZE 1024
- #define LOGGING
- #ifdef LOGGING
- FILE * fp;
- #endif
- const char * it_is_now(void) {
- static char buffer[100];
- time_t timer;
- struct tm* tm_info;
- timer = time(NULL);
- tm_info = localtime(&timer);
- strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);
- return buffer;
- }
- void slow_write(int fd, int speed, char * buffer, int len) {
- int x;
- for( x = 0; x < len; x++) {
- usleep(speed);
- write( fd, &buffer[x], 1);
- }
- }
- struct render {
- int speed;
- int effect;
- } current_render;
- int render_overlimit = 0;
- void reset_render(void) {
- current_render.speed = 0;
- current_render.effect = 0;
- render_overlimit = 0;
- }
- #define TRIGGER "^"
- #define SLEEP_LIMIT 30
- int ms_sleep(unsigned int ms) {
- int result = 0;
- struct timespec ts = {
- ms / 1000,
- (ms % 1000) * 1000000L
- };
- do {
- struct timespec ts_sleep = ts;
- result = nanosleep(&ts_sleep, &ts);
- } while ( (-1 == result));
- return result;
- }
- void render_sleep(void) {
- if (render_overlimit)
- return;
- if (current_render.speed) { // * 100 still too slow.
- ms_sleep(current_render.speed * 10);
- }
- }
- void write_color(int fd, int color) {
- char buffer[10];
- sprintf(buffer, "\x1b[%dm", color);
- write(fd, buffer, strlen(buffer));
- }
- const char * process_trigger(int fd, const char * cp) {
- char ch;
- int i, x, y;
- ch = toupper(*cp);
- cp++;
- switch(ch) {
- case 'D':
- i = 0;
- if (isdigit(*cp)) {
- i = (*cp) - '0';
- cp++;
- };
- if (isdigit(*cp)) {
- i *= 10;
- i += (*cp) - '0';
- cp++;
- };
- if ((i > 0) && (i < 80)) {
- #ifdef BUGGER
- fprintf(fp, "DEL %02d\n", i);
- #endif
- for (x = 0; x < i; x++ ) {
- write(fd, "\b \b", 3);
- }
- };
- break;
- case 'C':
- i = 0;
- if (isdigit(*cp)) {
- i = (*cp) - '0';
- cp++;
- };
- if (isdigit(*cp)) {
- i *= 10;
- i += (*cp) - '0';
- cp++;
- };
- write_color(fd, i);
- break;
- case 'G':
- x = 0;
- if (isdigit(*cp)) {
- x = (*cp) - '0';
- cp++;
- };
- if (isdigit(*cp)) {
- x *= 10;
- x += (*cp) - '0';
- cp++;
- };
- y = 0;
- if (isdigit(*cp)) {
- y = (*cp) - '0';
- cp++;
- };
- if (isdigit(*cp)) {
- y *= 10;
- y += (*cp) - '0';
- cp++;
- };
- break;
- case 'R':
- i = 0;
- if (isdigit(*cp)) {
- i = (*cp) - '0';
- cp++;
- };
- if ( ( i > 0 ) && ( i < 10) ) {
- #ifdef BUGGER
- fprintf(fp, "RENDER %d\n", i);
- #endif
- current_render.effect = i;
- } else {
- current_render.effect = 0;
- }
- break;
- case 'S':
- i = 0;
- if (isdigit(*cp)) {
- i = (*cp) - '0';
- cp++;
- };
- if ( ( i > 0 ) && ( i < 10) ) {
- #ifdef BUGGER
- fprintf(fp, "SPEED %d\n", i);
- #endif
- current_render.speed = i;
- } else {
- current_render.speed = 0;
- }
- break;
- case 'P':
- i = 0;
- if (isdigit(*cp)) {
- i = (*cp) - '0';
- cp++;
- };
- if ( ( i > 0 ) && ( i < 10) ) {
- #ifdef BUGGER
- fprintf(fp, "PAWS %d\n", i);
- #endif
- // sleep(i);
- if (!render_overlimit) {
- sleep(i);
- };
- }
- break;
-
- }
- return cp;
- }
- void render_effect(int fd, char ch) {
- int effect = current_render.effect;
- int l;
- char space = ' ';
- char bs = '\b';
- switch (effect) {
- case 1:
- // CHAR + SPC + BS
- render_sleep();
- write(fd, &ch, 1);
- render_sleep(); // Maybe extra sleep here?
- write(fd, &space, 1);
- render_sleep();
- write(fd, &bs, 1);
- break;
- case 2:
- // CHAR + 8 spaces + 8 BS
- render_sleep();
- write(fd, &ch, 1);
- for(l = 0; l < 8; l++) {
- render_sleep();
- write(fd, &space, 1);
- }
- for(l = 0; l < 8; l++) {
- render_sleep();
- write(fd, &bs, 1);
- }
- break;
- case 0:
- default:
- // NORMAL
- render_sleep();
- write(fd, &ch, 1);
- break;
- }
- }
- void render( int fd, const char * string_out ) {
- const char * cp = string_out;
- const char * trigger = cp;
- time_t start = time(NULL);
- int elapsed;
- int over = 0;
- reset_render();
- #ifdef BUGGER
- fprintf(fp, "render(%d, %s)\n", fd, string_out);
- fflush(fp);
- #endif
- // Check our time from time to time.
- // If we start running long, kill sleeps.
- while ( (trigger = strstr(cp, TRIGGER)) != NULL) {
- // There is special things to handle in here.
- while ( cp != trigger ) {
- elapsed = time(NULL)-start;
- if (elapsed > SLEEP_LIMIT) {
- render_overlimit = 1;
- current_render.speed = 0;
- };
- #ifdef BUGGER
- fprintf(fp, "re(%c)\n", *cp);
- fflush(fp);
- #endif
- // write(fd, cp, 1 );
- render_effect(fd, *cp);
- cp++;
- };
- #ifdef BUGGER
- fprintf(fp, "at trigger: (%s)\n", cp);
- fflush(fp);
- #endif
- cp += strlen(TRIGGER);
- // Ok, we're pointing at the trigger -- do something.
- cp = process_trigger(fd, cp);
- #ifdef BUGGER
- fprintf(fp, "after trigger: (%s)\n", cp);
- fflush(fp);
- #endif
- };
- // We still might be under a rendering effect.
- while (*cp != 0) {
- elapsed = time(NULL)-start;
- if (elapsed > SLEEP_LIMIT) {
- render_overlimit = 1;
- current_render.speed = 0;
- };
- // write(fd, cp, 1);
- render_effect(fd, *cp);
- cp++;
- }
- }
- void harry_event(int fd) {
- // Make something happen
- char buffer[100];
- int r = random() % 5;
- // This is no where near finished, BUT!
- // TO FIX: Remember the last phrase used,
- // and don't repeat (the last two)!
- const char * phrases[] =
- {
- "Hahaha",
- "Snicker, snicker",
- "Boo!",
- "MeOW",
- "I see U"
- };
- const char * cp;
- cp = phrases[r];
- sprintf(buffer, "^S2%s^P2^D%02d", cp, (int)strlen(cp));
- #ifdef LOGGING
- fprintf(fp, "harry_event: render(%d, \"%s\")\n", fd, buffer);
- fflush(fp);
- #endif
- render(fd, buffer);
- }
- /*
- NOTE: Logging is single file, don't use in production!
- It won't handle multiple writers.
- */
- char * username = NULL;
- char * fullname = NULL;
- void pcopy(char * pstring, char * str) {
- int len = (int)*pstring;
- strncpy( str, pstring+1, len );
- str[len] = 0;
- }
- /*
- This only works for those few idiots that use SSH,
- the Mystic broken SSH!
- */
- int locate_user(const char *alias) {
- FILE * user;
- char buffer[0x600];
- char temp[100];
- user = fopen("data/users.dat", "rb");
- if (user == NULL)
- return 0;
- // Carry on!
- while (fread(buffer, 0x600, 1, user) == 1) {
- pcopy( buffer + 0x6d, temp );
- if ( strcasecmp( temp, username) == 0) {
- pcopy(buffer + 0x8c, temp );
- fullname = strdup(temp);
- break;
- }
- /*
- printf("Alias: %s\n", temp);
- pcopy(buffer + 0x8c, temp );
- printf("Full Name: %s\n", temp );
- */
- }
- fclose(user);
- return 1;
- }
- // Buffers are BSIZE + 1, so a buffer that size can strcpy safely.
- void mangle( char * buffer ) {
- // Ok, we want to look for words to:
- // MaNGlE , or transpose (both?!)
- // or possibly transpose words
- }
- int main(int argc, char * argv[])
- {
- int master;
- pid_t pid;
-
- #ifdef LOGGING
- // FILE * fp;
- fp = fopen("mystic_pipe.data", "wb");
- if ( fp == NULL ) {
- return 2;
- }
- #endif
- srandom(time(NULL));
- // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz -PUWISHPASSWORD
- // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
- // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz -PUP2LAT3
- // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
- // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
- // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML0 -SL1 -ST0 -CUnknown
- // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz -PUP2LAT3
- // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML1 -SL1 -ST2 -CUnknown -Ubugz -PUP2LAT3
- // SSH: -ML1 -ST2
- // Telnet: -ML0 -ST0
- // Locate username (if given) in the command line
- // -U<username>
- for (int x = 0; x < argc; x++) {
- if (strncmp("-U", argv[x], 2) == 0) {
- username = argv[x] + 2;
- #ifdef LOGGING
- fprintf(fp, "Username: [%s]\n", username);
- #endif
- break;
- }
- }
- if (username != NULL) {
- locate_user(username);
- #ifdef LOGGING
- fprintf(fp, "Username: [%s] A.K.A. [%s]\n", username, fullname);
- #endif
- }
- // With IGNBRK I don't think I need this anymore. (Nope!)
- // signal(SIGINT, SIG_IGN);
- pid = forkpty(&master, NULL, NULL, NULL);
- // impossible to fork
- if (pid < 0) {
- return 1;
- }
- // child
- else if (pid == 0) {
- char *args[20]; // max 20 args
- int x;
- args[0] = TARGET;
- for ( x = 1; x < argc; x++ ) {
- args[x] = argv[x];
- };
- args[x] = NULL;
- // run Mystic, run!
- execvp( TARGET, args);
- }
- // parent
- else {
- // remove the echo
- // ICANON - raw mode. No more line buffering!
- struct termios tios, orig1;
- struct timeval timeout;
- int last_event;
- tcgetattr(master, &tios);
- tios.c_lflag &= ~(ECHO | ECHONL | ICANON );
- tcsetattr(master, TCSAFLUSH, &tios);
- tcgetattr(1, &orig1);
- tios = orig1;
- tios.c_iflag &= ~(ICRNL | IXON); // Disable software flow control
- tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN );
- // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
- // ISIG should be Ctrl-C and Ctrl-Z IGNBRK +
- tcsetattr(1, TCSAFLUSH, &tios);
- for (;;) {
- // define estruturas para o select, que serve para verificar qual
- // se tornou "pronto pra uso"
- fd_set read_fd;
- fd_set write_fd;
- fd_set except_fd;
- // inicializa as estruturas
- FD_ZERO(&read_fd);
- FD_ZERO(&write_fd);
- FD_ZERO(&except_fd);
- // atribui o descritor master, obtido pelo forkpty, ao read_fd
- FD_SET(master, &read_fd);
- // atribui o stdin ao read_fd
- FD_SET(STDIN_FILENO, &read_fd);
- // o descritor tem que ser unico para o programa, a documentacao
- // recomenda um calculo entre os descritores sendo usados + 1
- // we're in luck! The last parameter is time interval/timeout. :D
- timeout.tv_sec = 10;
- timeout.tv_usec = 0;
- // select(master+1, &read_fd, &write_fd, &except_fd, NULL);
- if ( select(master+1, &read_fd, &write_fd, &except_fd, &timeout) == 0 ) {
- // This means timeout!
- harry_event(STDOUT_FILENO);
- #ifdef BUGGER
- fprintf(fp, "%s : TICK\n", it_is_now());
- fprintf(fp, "STDOUT is %d\n", STDOUT_FILENO);
- #endif
- }
- char input[BSIZE + 1];
- char output[BSIZE + 1];
- int total;
- // read_fd esta atribuido com read_fd?
- if (FD_ISSET(master, &read_fd))
- {
- // leia o que bc esta mandando
- if ((total = read(master, &output, BSIZE)) != -1) {
- // e escreva isso na saida padrao
- output[total] = 0;
- if (random() % 20 < 3) {
- mangle( output );
- }
- write(STDOUT_FILENO, &output, total);
- // This is OUTPUT from the BBS
- #ifdef LOGGING
- fprintf(fp, ">> [%s]\n", output);
- #endif
- } else
- break;
- }
- // read_fd esta atribuido com a entrada padrao?
- if (FD_ISSET(STDIN_FILENO, &read_fd))
- {
- // leia a entrada padrao
- total = read(STDIN_FILENO, &input, BSIZE);
- input[total] = 0;
- // e escreva no bc
- write(master, &input, total);
- // This is INPUT from the USER
- #ifdef LOGGING
- fprintf(fp, "<< [%s]\n", input);
- #endif
- }
- }
- // Restore terminal
- tcsetattr(1, TCSAFLUSH, &orig1);
- #ifdef LOGGING
- fclose(fp);
- #endif
- }
- return 0;
- }
|