/* ARISA - Shell, Standalone Executable to communicate with Arisa * Copyright (C) 2003, 2004 Carl Ritson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include "arisa-sh.h" /** Macros **/ #define MAX(x,y) ((x) >= (y) ? (x) : (y)) #define MIN(x,y) ((x) <= (y) ? (x) : (y)) /** Global Variables **/ static int sok = -1; static int batch = 0; static char *prompt = NULL; static char *sokbuf = NULL; static int sokpos = 0; static int timestamp = 1; /** Functions **/ static int has_pending_data(int s) { struct pollfd pfd; int ret; pfd.fd = s; pfd.events = POLLIN; ret = poll(&pfd,1,0); if(ret == -1) return -1; if(pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) return -1; else if(pfd.revents & POLLIN) return 1; else return 0; } static int recv_line(char *buffer, size_t bufsize) { int i,ret; if(sokpos != 0) strncpy(buffer,sokbuf,sokpos); ret = recv(sok,buffer+sokpos,bufsize-sokpos,MSG_PEEK); if(ret <= 0) return -1; for(i = 0; i < (ret+sokpos); ++i) { if(buffer[i] == '\n') { ret = recv(sok,buffer+sokpos,(i+1)-sokpos,0); if(ret <= 0) return -1; buffer[i] = '\0'; if(i != 0) if(buffer[i-1] == '\r') buffer[i-1] = '\0'; sokpos = 0; return strlen(buffer); } } ret = recv(sok,sokbuf+sokpos,ret,0); if(ret <= 0) return -1; sokpos += ret; if(sokpos >= 512) sokpos = 0; // clear return 0; } /** text based implementation **/ #include "sh-text.c" /** (n)curses based implementation **/ #if defined(HAVE_NCURSES) || defined(HAVE_CURSES) #include "sh-curses.c" #else static int ncurses_main(void) { fprintf(stderr,"Curses support is not compiled in."); return 1; } #endif /* defined(HAVE_NCURSES) || defined(HAVE_CURSES) */ static void usage(void) { fprintf(stderr, "arisa-sh [-b] [-c] | \n" " -b\t\tbatch mode, no output to console\n" " -c\t\tuse curses\n" " -t\t\tturn off timestamp\n" " -?\t\tthis dialog\n" "\nReport Bugs to .\n" ); exit(0); } static void connect_dccserver(const char *path) { struct sockaddr_in addr; char buffer[512]; struct hostent *hent; int ret,len; char *host,*port; host = strdup(path); port = strchr(host,':'); if(port == NULL) { fprintf(stderr,"Invalid Address: %s\n",path); exit(1); } *(port++) = '\0'; hent = gethostbyname(host); if(hent == NULL) { fprintf(stderr,"Unable to resolve host %s: %s\n", host,gai_strerror(ret)); exit(1); } addr.sin_family = AF_INET; addr.sin_port = htons((unsigned short)atoi(port)); memcpy(&(addr.sin_addr),hent->h_addr,sizeof(struct in_addr)); ret = socket(PF_INET,SOCK_STREAM,0); if(ret == -1) { perror("Failed to get socket"); exit(1); } sok = ret; ret = connect(sok,(struct sockaddr *)&addr,sizeof(addr)); if(ret == -1) { perror("Failed to connect"); exit(1); } free(host); len = sprintf(buffer,"100 Admin\r\n"); ret = send(sok,buffer,len,0); if(ret == -1) { perror("Failed to negotiate"); exit(1); } alarm(60); while((ret = recv_line(buffer,sizeof(buffer))) == 0) ; if(ret == -1) { perror("Failed to negotiate"); exit(1); } host = strchr(buffer,' '); if(host != NULL) *host = '\0'; if(atoi(buffer) != 101) { fprintf(stderr,"Failed to negotiate with dccserver: %s\n",buffer); exit(1); } ret = 1; setsockopt(sok,SOL_SOCKET,SO_KEEPALIVE,&ret,sizeof(ret)); alarm(0); } #ifdef HAVE_SYS_UN_H static void connect_unix(const char *path) { struct sockaddr_un addr; int ret; addr.sun_family = AF_UNIX; snprintf(addr.sun_path,sizeof(addr.sun_path),"%s",path); ret = socket(PF_UNIX,SOCK_STREAM,0); if(ret == -1) { perror("Failed to get socket"); exit(1); } sok = ret; ret = connect(sok,(struct sockaddr *)&addr,sizeof(addr)); if(ret == -1) { perror("Failed to connect"); exit(1); } ret = 1; setsockopt(sok,SOL_SOCKET,SO_KEEPALIVE,&ret,sizeof(ret)); } #else static void connect_unix(const char *path) { fprintf(stderr,"Unix socket support is not compiled, unable to connect to %s.\n",path); } #endif /* HAVE_SYS_UN_H */ static void login(void) { char buffer[512],linebuf[96],*line; int ret,len; alarm(60); for(;;) { while((ret = recv_line(buffer,sizeof(buffer))) == 0) ; if(ret == -1) { perror("Socket Error"); exit(1); } else if(buffer[0] == '[' && buffer[strlen(buffer)-1] == ']') { prompt = strdup(buffer); if(!batch) printf("%s\n",buffer); break; } if(!batch) { printf("%s\n",buffer); line = NULL; #ifdef HAVE_TERMIOS_H if(strcasecmp(buffer,"Password:") == 0) { struct termios old,new; if(tcgetattr(fileno(stdin),&old) == 0) { new = old; new.c_lflag &= ~ECHO; tcsetattr(fileno(stdin),TCSAFLUSH,&new); } line = lreadline(stdin,linebuf,sizeof(linebuf)); if(line == NULL) line = ""; tcsetattr(fileno(stdin),TCSAFLUSH,&old); } else line = NULL; #endif /* HAVE_TERMIOS_H */ } else line = NULL; if(line == NULL) line = lreadline(stdin,linebuf,sizeof(linebuf)); if(line == NULL) exit(1); len = sprintf(buffer,"%s\r\n",line); ret = send(sok,buffer,len,0); if(ret == -1) { perror("Socket Error"); exit(1); } } alarm(0); } static void sigalarm(int sig) { // print in signal handler is bad, don't try this at home kids, mm'kay fprintf(stderr,"Timed Out\n"); exit(1); } int main(int argc, char *argv[]) { const char *opts = "bcet"; char *path = NULL; int c,curses = 0; for(;;) { c = getopt(argc,argv,opts); if(c == -1) break; switch(c) { case 'b': batch = 1; curses = 0; break; case 'e': timestamp = 2; break; case 'c': curses = 1; break; case 't': timestamp = 0; break; case '?': usage(); break; } } if(optind < argc) path = argv[optind]; if(path == NULL) usage(); sokbuf = malloc(1024); signal(SIGHUP,SIG_IGN); signal(SIGALRM,sigalarm); if(strchr(path,':') != NULL) connect_dccserver(path); else connect_unix(path); login(); if(curses == 0) return console_main(); else return ncurses_main(); }