/** ** bsflite - bs-free AIM client ** ultralight version. ** ** (C) 2003-2007 by Claudio Leite ** ** NO WARRANTY. Read the file COPYING for more details. **/ #include "bsf.h" #ifndef __MINGW32__ #include #else #include #endif #ifdef PLAN9 #include #endif #ifdef __DJGPP__ void clrscr(void); int getch(void); int kbhit(void); #endif #include struct Conn *conn; struct BuddyList *buddylist; struct Waiting *waiting = NULL; char buf[256]; char inputbuf[512]; int proto = 0; #ifndef __MINGW32__ struct termios t_attr; struct termios saved_attr; int istat, attrs_saved; #endif int screen_cols; int screen_lines; int prompt_len; int ratestatus = 0; time_t last_status_time = 0; time_t last_keystroke_time; /* PROTO */ void addtoinputbuf(char inchr) { conn->istyping = 1; if (strlen(inputbuf) == 511) return; putchar(inchr); inputbuf[strlen(inputbuf)] = inchr; fflush(stdout); } int main(void) { int xx; int needpass = 0, needuser = 0; struct Waiting *wtemp, *wtemp2; conn = malloc(sizeof(struct Conn)); conn->clist = NULL; conn->squelchidle = 0; conn->isidle = 0; conn->rawinput = 0; conn->single_log = 0; conn->squelchaway = 0; conn->squelchconnect = 0; conn->respond_idle_only = 0; conn->report_idle = 1; conn->netspeak_filter = 0; conn->awaymsgs = NULL; conn->awaymsg = NULL; conn->timestamps = 1; conn->colors = 0; conn->auto_reconnect = 0; conn->bell_on_incoming = 0; conn->idle_rep_time = 600; conn->windowtitle = NULL; conn->username = NULL; conn->password = NULL; #ifdef __MINGW32__ conn->set_window_title = 1; #else conn->set_window_title = 0; #endif conn->proxy = NULL; conn->proxytype = PROXY_TYPE_NONE; conn->oscarport = 0; switch (read_conf()) { case -1: /* * config file missing */ b_echostr_s(); printf("Could not open config file.\n"); case 0: needuser = 1; needpass = 1; break; case 1: break; case 2: needpass = 1; break; } setup_tty(); get_screen_size(); /* signal(SIGINT, SIG_IGN); */ signal(SIGINT, &sigint_handler); #if !defined(__MINGW32__) && !defined(__DJGPP__) #ifndef PLAN9 signal(SIGWINCH, &sigwinch_handler); #endif signal(SIGUSR1, &sigusr1_handler); signal(SIGTSTP, SIG_IGN); #endif open_log_dir(); conn->lastsn = 0; conn->istyping = 0; conn->isaway = 0; conn->isinvisible = 0; conn->buddiesonline = 0; buddylist = 0; b_echostr_s(); printf("bsflite %s started.\n", BSFLITE_VERSION); memset(inputbuf, 0, sizeof(inputbuf)); if (needuser) { b_getusername(); } else { b_echostr(); if (conn->timestamps) { putchar(' '); addts(); } printf(" Logging in as %s...\n", conn->username); } if (needpass) { b_getpassword(); } read_away_messages(); create_new_connection(); if (conn->proxytype != PROXY_TYPE_NONE) { imcomm_set_proxy(conn->clist->conn, conn->proxytype, conn->proxy, (uint16_t) conn->proxyport); } if (conn->oscarport != 0) { imcomm_set_oscar_port(conn->clist->conn, conn->oscarport); } imcomm_im_signon(conn->clist->conn, conn->username, conn->password); conn->conn = conn->clist->conn; signal(SIGINT, SIG_IGN); last_keystroke_time = time(NULL); show_prompt(); while (1) { fd_set readfs; unsigned char inchr; int nchr; struct timeval tm; FD_ZERO(&readfs); #if defined(__MINGW32__) || defined(__DJGPP__) tm.tv_sec = 0; tm.tv_usec = 500; #else tm.tv_sec = 2; tm.tv_usec = 500000; FD_SET(STDIN_FILENO, &readfs); #endif if (imcomm_select(STDIN_FILENO + 1, &readfs, NULL, NULL, &tm) != IMCOMM_RET_OK) { if (errno == EINTR) continue; } #if defined(__MINGW32__) || defined(__DJGPP__) if (kbhit()) { inchr = getch(); #else if (FD_ISSET(STDIN_FILENO, &readfs)) { nchr = read(0, &inchr, 1); #endif if (nchr == 1) { if (conn->report_idle) { last_keystroke_time = time(NULL); if (conn->isidle == 1) { if (conn->conn) imcomm_set_idle_time(conn->conn, 0); conn->isidle = 0; eraseline(); show_prompt(); } } switch (inchr) { case '\n': case '\r': parse_input(); memset(inputbuf, 0, sizeof(inputbuf)); conn->istyping = 0; conn->rawinput = 0; for (wtemp = waiting; wtemp != NULL;) { wtemp2 = wtemp; wtemp = wtemp->next; free(wtemp2->sn); free(wtemp2); } waiting = NULL; show_prompt(); break; case 18: /* CTRL-R */ if (conn->rawinput == 1) conn->rawinput = 0; else conn->rawinput = 1; eraseline(); show_prompt(); break; case 21: /* CTRL-U */ eraseline(); memset(inputbuf, 0, sizeof(inputbuf)); conn->istyping = 0; conn->rawinput = 0; show_prompt(); break; case 12: #if !defined(__MINGW32__) && !defined(PLAN9) #ifdef __DJGPP__ clrscr(); #else printf("\033[2J\033[1;1H"); #endif show_prompt(); #endif break; case '\t': if (! (inputbuf[0] == 'm' || inputbuf[0] == 'i' || inputbuf[0] == 'l' || inputbuf[0] == 'A' || inputbuf[0] == 'P' || inputbuf[0] == 'p')) break; else { int spaces = 0; int match = 0; struct BuddyList *trav; for (xx = 0; xx < strlen(inputbuf); xx++) if (inputbuf[xx] == ' ') { spaces = 1; break; } if (spaces) break; trav = buddylist; while (trav != NULL) { if (strncmp (trav->sn, inputbuf + 1, strlen(inputbuf) - 1) == 0) { match++; } trav = trav->next; } if (match > 1) break; else if (match == 0) break; else { trav = buddylist; while (trav != NULL) { if (strncmp (trav->sn, inputbuf + 1, strlen(inputbuf) - 1) == 0) break; trav = trav->next; } eraseline(); memcpy(inputbuf + 1, trav->sn, strlen(trav->sn)); if (inputbuf[0] == 'm') inputbuf[strlen(inputbuf)] = ' '; show_prompt(); } } break; case '\b': case 127: case 4: if (inputbuf[0] == 0) { conn->istyping = 0; break; } printf("\b \b"); inputbuf[strlen(inputbuf) - 1] = 0; fflush(stdout); break; case 2: if (strlen(inputbuf) > 509) break; printf("
"); fflush(stdout); #if defined(__OpenBSD__) strlcat(inputbuf, "
", sizeof(inputbuf)); #else strcat(inputbuf, "
"); #endif break; case '"': if (!conn->rawinput) { if (strlen(inputbuf) > 507) break; printf("""); fflush(stdout); #if defined(__OpenBSD__) strlcat(inputbuf, """, sizeof(inputbuf)); #else strcat(inputbuf, """); #endif } else { addtoinputbuf(inchr); } break; case '<': if (!conn->rawinput) { if (strlen(inputbuf) > 509) break; printf("<"); fflush(stdout); #if defined(__OpenBSD__) strlcat(inputbuf, "<", sizeof(inputbuf)); #else strcat(inputbuf, "<"); #endif } else { addtoinputbuf(inchr); } break; case '>': if (!conn->rawinput) { if (strlen(inputbuf) > 509) break; printf(">"); fflush(stdout); #if defined(__OpenBSD__) strlcat(inputbuf, ">", sizeof(inputbuf)); #else strcat(inputbuf, ">"); #endif } else { addtoinputbuf(inchr); } break; case '&': if (!conn->rawinput) { if (strlen(inputbuf) > 508) break; printf("&"); fflush(stdout); #if defined(__OpenBSD__) strlcat(inputbuf, "&", sizeof(inputbuf)); #else strcat(inputbuf, "&"); #endif } else { addtoinputbuf(inchr); } break; case 'r': case 'I': case 'L': if (inputbuf[0] == 0) { if (conn->lastsn) { conn->istyping = 1; if (inchr == 'r') snprintf(inputbuf, sizeof(inputbuf), "m%s ", conn->lastsn); else if (inchr == 'I') snprintf(inputbuf, sizeof(inputbuf), "i%s", conn->lastsn); else snprintf(inputbuf, sizeof(inputbuf), "l%s", conn->lastsn); printf("%s", inputbuf); fflush(stdout); } else if (conn->single_log && inchr == 'L') { snprintf(inputbuf, sizeof(inputbuf), "lg"); printf("%s", inputbuf); fflush(stdout); } break; } default: if (inchr < 32) break; addtoinputbuf(inchr); break; } } } if (ratestatus == 1) { if ((time(NULL) - last_status_time) > 30) { eraseline(); ratestatus = 0; show_prompt(); } } if (conn->report_idle && nchr == 1) { if (conn->isidle == 0) { if (time(NULL) - last_keystroke_time > conn->idle_rep_time) { conn->isidle = 1; if (conn->conn) imcomm_set_idle_time(conn->conn, time(NULL) - last_keystroke_time); eraseline(); show_prompt(); } } } } } /* * this is from micq - mreadline.c * * rewrite this sometime. */ /* PROTO */ void setup_tty(void) { #ifdef __MINGW32__ extern HANDLE hOut; #endif #if !defined(__MINGW32__) && !defined(PLAN9) istat = 0; if (tcgetattr(fileno(stdin), &t_attr) != 0) return; if (!attrs_saved) { saved_attr = t_attr; attrs_saved = 1; } t_attr.c_lflag &= ~(ECHO | ICANON); #ifdef __BEOS__ t_attr.c_cc[VMIN] = 0; #else t_attr.c_cc[VMIN] = 1; #endif if (tcsetattr(fileno(stdin), TCSAFLUSH, &t_attr) != 0) perror("can't change tty modes."); if (conn->set_window_title) { set_title("bsflite"); } #else #ifdef PLAN9 int consctl, label; label = open("/dev/label", O_WRONLY); if (label >= 0) { write(label, "bsflite", 7); } consctl = open("/dev/consctl", O_WRONLY); if (consctl < 0) { perror("Can't open consctl"); exit(-1); } write(consctl, "rawon", 5); #endif #ifdef __MINGW32__ SetConsoleTitle("bsflite"); hOut = GetStdHandle(STD_OUTPUT_HANDLE); #endif #endif } /* PROTO */ void restore_tty(void) { #ifndef __MINGW32__ tcsetattr(fileno(stdin), TCSAFLUSH, &saved_attr); #endif } /* PROTO */ void show_prompt(void) { struct Waiting *wtemp; if (conn->isaway) prompt_len = strlen(BSF_PROMPT_AWAY) + 1; else if (conn->isidle) prompt_len = strlen(BSF_PROMPT_IDLE) + 1; else if (conn->rawinput) prompt_len = strlen(BSF_PROMPT_RAWINPUT) + 1; else prompt_len = strlen(BSF_PROMPT) + 1; if (ratestatus == 1) { printf(" "); prompt_len += 4; } else if (ratestatus == 2) { printf(" "); prompt_len += 5; } set_color(COLOR_WAITING); if (waiting != NULL) { for (wtemp = waiting; wtemp != NULL; wtemp = wtemp->next) { prompt_len += strlen(wtemp->sn) + 3; #ifdef MESSAGE_QUEUE if (waiting->mqueue != NULL) { printf("[%s*] ", wtemp->sn); prompt_len += 1; } else { printf("[%s] ", wtemp->sn); } #else printf("[%s] ", wtemp->sn); #endif } } set_color(COLOR_PROMPT); if (conn->isaway) printf("%s", BSF_PROMPT_AWAY); else if (conn->isidle) printf("%s", BSF_PROMPT_IDLE); else if (conn->rawinput) printf("%s", BSF_PROMPT_RAWINPUT); else printf("%s", BSF_PROMPT); set_color(0); printf(" %s", inputbuf); fflush(stdout); } /* PROTO */ void sigwinch_handler(int a) { get_screen_size(); } /* PROTO */ void sigint_handler(int a) { restore_tty(); exit(1); } /* PROTO */ void sigusr1_handler(int a) { set_profile(conn->clist->conn); read_away_messages(); } /* PROTO */ void set_profile(void *handle) { FILE *prof; char buf[1024]; #if !defined(__MINGW32__) && !defined(__DJGPP__) char *home; #endif char *profile; int ch; int len = 0; size_t proflen; #if !defined(__MINGW32__) && !defined(__DJGPP__) #ifdef PLAN9 if ((home = getenv("home")) == NULL) home = "."; snprintf(buf, sizeof(buf), "%s/lib/bsflite/profile", home); #else if ((home = getenv("HOME")) == NULL) home = "."; snprintf(buf, sizeof(buf), "%s/.bsflite/profile", home); #endif /* PLAN9 */ #else snprintf(buf, sizeof(buf), "profile.htm"); #endif prof = fopen(buf, "r"); if (prof == NULL) { char *profdef = "bsf: ultralight AIM."; size_t profstlen; char *profst; profstlen = strlen(profdef) + strlen(ID_STR) + 1; profst = malloc(profstlen); #if defined(__OpenBSD__) strlcpy(profst, profdef, profstlen); strlcat(profst, ID_STR, profstlen); #else strcpy(profst, profdef); strcat(profst, ID_STR); #endif imcomm_set_profile(handle, profst); free(profst); return; } while (1) { if (fgetc(prof) == EOF) if (feof(prof)) break; len++; } rewind(prof); proflen = len + strlen(ID_STR) + 1; profile = malloc(proflen); memset(profile, 0, proflen); while (1) { if ((ch = fgetc(prof)) == EOF) if (feof(prof)) break; profile[strlen(profile)] = ch; } #ifdef __OpenBSD__ strlcat(profile, ID_STR, proflen); #else strcat(profile, ID_STR); #endif imcomm_set_profile(handle, profile); fclose(prof); free(profile); } /* PROTO */ void set_icon(void *handle) { FILE *icon; char buf[1024]; #if !defined(__MINGW32__) && !defined(__DJGPP__) char *home; #endif uint8_t *icondata; int ch; int len = 0, read = 0; #if !defined(__MINGW32__) && !defined(__DJGPP__) #ifdef PLAN9 if ((home = getenv("home")) == NULL) home = "."; snprintf(buf, sizeof(buf), "%s/lib/bsflite/icon", home); #else if ((home = getenv("HOME")) == NULL) home = "."; snprintf(buf, sizeof(buf), "%s/.bsflite/icon", home); #endif /* PLAN9 */ #else snprintf(buf, sizeof(buf), "icon.bin"); #endif icon = fopen(buf, "rb"); if (icon == NULL) { return; } while (1) { if (fgetc(icon) == EOF) if (feof(icon)) break; len++; } rewind(icon); icondata = malloc(len); while (1) { if ((ch = fgetc(icon)) == EOF) if (feof(icon)) break; icondata[read] = (unsigned char) ch; read++; } /* icon support is pretty broken */ /* * imcomm_upload_icon(handle, icondata, (uint16_t) len); */ fclose(icon); free(icondata); } /* PROTO */ void load_buddylist(void *handle) { FILE *buddy; char buf[1024]; #if !defined(__MINGW32__) && !defined(__DJGPP__) char *home; #ifdef PLAN9 if ((home = getenv("home")) == NULL) home = "."; snprintf(buf, sizeof(buf), "%s/lib/bsflite/buddies", home); #else if ((home = getenv("HOME")) == NULL) home = "."; snprintf(buf, sizeof(buf), "%s/.bsflite/buddies", home); #endif /* PLAN9 */ #else snprintf(buf, sizeof(buf), "buddies.txt"); #endif buddy = fopen(buf, "r"); if (buddy == NULL) return; while (!feof(buddy)) { if (fgets(buf, sizeof(buf), buddy) == NULL) break; if (buf[strlen(buf) - 2] == '\r') buf[strlen(buf) - 2] = 0; if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = 0; imcomm_im_add_buddy(handle, buf); } fclose(buddy); } /* PROTO */ void error_callback(void *handle, int event, uint32_t data) { if (event != IMCOMM_RATE_LIMIT_WARN) { eraseline(); b_echostr_s(); if (conn->timestamps) { addts(); putchar(' '); } } switch (event) { case IMCOMM_STATUS_CONNECTED: printf("Connected.\n"); load_buddylist(handle); set_icon(handle); break; case IMCOMM_ERROR_INVALID_LOGIN: printf("Login failed.\n"); break; case IMCOMM_ERROR_DISCONNECTED: printf("Disconnected.\n"); imcomm_delete_handle(handle); break; case IMCOMM_ERROR_OTHER_SIGNON: wordwrap_print ("You've been disconnected because you signed on at a different location.", 14); break; case IMCOMM_STATUS_AUTHDONE: printf("Authentication succeeded.\n"); break; case IMCOMM_STATUS_MIGRATIONDONE: printf("Migration completed.\n"); break; case IMCOMM_ERROR_USER_OFFLINE: printf("Error: Recipient is not online.\n"); break; case IMCOMM_RATE_LIMIT_WARN: switch (data) { case 0x02: ratestatus = 1; last_status_time = time(NULL); break; case 0x03: ratestatus = 2; break; case 0x04: ratestatus = 0; break; default: eraseline(); b_echostr_s(); printf("Unknown rate info: %u\n", (unsigned int) data); break; } break; case IMCOMM_ERROR_PROXY: switch (data) { case PROXY_ERROR_AUTH: printf("Proxy requires authentication.\n"); break; case PROXY_ERROR_CONNECT: printf("Could not connect to proxy.\n"); break; case PROXY_ERROR_PROXYCONNECT: printf("Proxy could not connect to destination.\n"); break; case PROXY_ERROR_UNKNOWN: printf("Unknown proxy error or response.\n"); break; } break; case IMCOMM_WARN_PAUSE: printf("Server sent PAUSE request.\n"); break; case IMCOMM_WARN_UNPAUSE: printf("Server sent UNPAUSE request.\n"); break; default: printf("ERROR: Unknown error type: %d\n", event); break; } if(event != IMCOMM_RATE_LIMIT_WARN) show_prompt(); } /* PROTO */ void handle_deleted(void *handle) { struct ConnPtr *temp, *trav; delete_buddylist(buddylist); buddylist = NULL; conn->buddiesonline = 0; conn->conn = NULL; if (conn->clist->conn == handle) { temp = conn->clist; conn->clist = conn->clist->next; if (temp->username) free(temp->username); free(temp); } else { for (trav = conn->clist; trav->next;) { if (trav->next->conn == handle) { temp = trav->next; trav->next = trav->next->next; if (temp->username) free(temp->username); free(temp); } } } if (conn->auto_reconnect != 0) { eraseline(); b_echostr_s(); if (conn->timestamps) { addts(); putchar(' '); } printf("Reconnecting in %d seconds...", conn->auto_reconnect); fflush(stdout); sleep(conn->auto_reconnect); printf("\n"); create_new_connection(); if (conn->proxytype != PROXY_TYPE_NONE) { imcomm_set_proxy(conn->clist->conn, conn->proxytype, conn->proxy, (uint16_t) conn->proxyport); } if (conn->oscarport != 0) { imcomm_set_oscar_port(conn->clist->conn, conn->oscarport); } imcomm_im_signon(conn->clist->conn, conn->username, conn->password); conn->conn = conn->clist->conn; if (conn->isaway) imcomm_set_away(conn->conn, conn->awaymsg); } } /* PROTO */ void b_getusername(void) { char ch; char passbuf[256]; memset(passbuf, 0, sizeof(passbuf)); printf("%s Enter screen name: ", BSF_PROMPT); fflush(stdout); while (strlen(passbuf) < sizeof(passbuf)) { #if defined(__MINGW32__) || defined(__DJGPP__) ch = getch(); #else ch = getchar(); #endif if (ch == '\b' || ch == 127 || ch == 4) { if (passbuf[0] == 0) continue; printf("\b \b"); passbuf[strlen(passbuf) - 1] = 0; fflush(stdout); continue; } if (ch == '\n' || ch == '\r') break; if (ch == ' ' || ch < 32) continue; if (ch >= 'A' && ch <= 'Z') ch = tolower(ch); passbuf[strlen(passbuf)] = ch; putchar(ch); fflush(stdout); } conn->username = strdup(passbuf); putchar('\n'); } /* PROTO */ void b_getpassword(void) { char ch; char passbuf[256]; int nchr; memset(passbuf, 0, sizeof(passbuf)); printf("%s Enter password: ", BSF_PROMPT); fflush(stdout); while (strlen(passbuf) < sizeof(passbuf)) { #if defined(__MINGW32__) || defined(__DJGPP__) ch = getch(); #else /* ch = getchar(); */ nchr = read(0, &ch, 1); if (nchr < 1) continue; #endif if (ch == '\b' || ch == 127 || ch == 4) { if (passbuf[0] == 0) continue; printf("\b \b"); passbuf[strlen(passbuf) - 1] = 0; fflush(stdout); continue; } if (ch == '\n' || ch == '\r') break; passbuf[strlen(passbuf)] = ch; putchar('.'); fflush(stdout); } conn->password = strdup(passbuf); putchar('\n'); }