/* (c) Alexander Oelzant (aoe) alexander@oelzant.priv.at 2005-2010 
 * $Id: bsw-proxy.c,v 1.20 2010-10-31 00:02:23 aoe Exp $
 * Licensed under the GNU General Public License version 3 or later
 * (GPL3, see http://www.gnu.org/copyleft)
 */

#include <assert.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
/* isprint */
#include <ctype.h>
#include <time.h>
/* gettimeofday */
#include <sys/time.h>
/* varargs/va_list */
#include <stdarg.h>

#include "bsw-global.h"
#include "bsw-proxy.h"


#define debug(bsw,level,a...) {if (bsw->log && (bsw->debug >= level)) {fprintf (bsw->log,a);}}
#define error_exit(bsw,a...) {debug(bsw,a);exit(1);}

int send_bsw_packet(int fd, bswdata_t * bswarg,int type, char *fmt, ...);

#define initpacketscount (sizeof(initpackettypes)/sizeof(initpackettypes[0]))
char *initpackets[initpacketscount];
int initpacketslen[initpacketscount];
int initpacketsinited=0;

void usage (char *argv0) {
	fprintf(stderr,"%s: proxies BrettSpielWelt.de requests to\n\ta BSW client and multiple IRC clients\n"
			"usage:\n"
			"-h           this help\n"
			"-d           debug on\n"
			"-f           don't fork (run in foreground)\n"
			"-i port      listening port instead of default %i\n"
			"-L           address to listen on instead of default %s\n"
			"-I port      irc listening port instead of default %i\n"
			"-p port      server port instead of %i\n"
			"-s address   server address instead of %s\n"
			"-l           logfile (default: stderr for fg, %s for daemon)\n"
			"-U user      username\n"
			"-P pass      password\n"
			, argv0, LISTENPORT, LISTENADDR, IRCPORT, SERVERPORT, SERVER, LOGFILE
			);
}

#define dumpwrite(fd,debug,s...) { \
	snprintf(string1,sizeof(string1),s); \
	string[sizeof(string1)-2]='\0'; \
	if (fd >= 0 && write(fd,string1,strlen(string1)) < 0) \
		perror("write to irc proxy"); \
	if (debug) printf(s); }



int bsw_packet_handle(bswdata_t *bswarg, char *dir, char *data, int len) {
	unsigned int want;
	char string[512];
	char string1[512];
	char *rcptstring, *messagestring, *typestring;
	int rcptlen, messagelen;
	int userinfo,i;

	want=0x10000*((unsigned char)data[0]&0xff)+
		0x100*((unsigned char)data[1]&0xff)+
		((unsigned char)data[2]&0xff);
	if (len <= 0)
		return -1;

	if (want == len) {
		unsigned int dlen, type1,type2;
		int count; // number of subpackets
		int count1; // number of subpackets
		char infouser[256];
		char *msgptr=data+3;
		char *msgptr1=data+3;
		char contentstring[65536];
		parsed_packet_t *first=NULL, *next=NULL;

		count=0;
		count1=0;
		// min 2 msg type bytes
		type1=0x100*((unsigned char)msgptr[0]&0xff)+
			((unsigned char)msgptr[1]&0xff);
		typestring = NULL;
		userinfo = 0;

		// parse packet - free at end of conditional
		msgptr1+=2;
		while ((msgptr1)<(data+len)) {
			type2=(unsigned char)msgptr1[0]&0xff;
			if (type2) {
				contentstring[count1]=type2;
				count1++; // increase marker for number of string fields
				if (!next) {
					next=malloc(sizeof(parsed_packet_t));
					if (!next)
						perror("malloc");
					next->next=NULL;
				}
				if (!first)
					first=next;
				next->type=type2;
				if (bsw_datasize[type2] > 0) {
					dlen=bsw_datasize[type2];
					next->content=msgptr1+1;
					next->length=dlen;
					msgptr1+=1+dlen; // type + fixed len
				} else if (bsw_datasize[type2] == -1) { 
					dlen=0x100*((unsigned char)msgptr1[1]&0xff)+
						((unsigned char)msgptr1[2]&0xff);
					next->content=msgptr1+1+2;
					next->length=dlen;
					msgptr1+=1+2+dlen;
				} else {
					break;
				}
			} else {
				break;
			}
			next=NULL;
		}

		// cache init packets
		/* only take menu items from server */
		if (dir[0] == '<') 
			for (i=0; i<initpacketscount; i++) {
				if (type1 == initpackettypes[i]) {
					if (initpackets[i]) {
						free(initpackets[i]);
					};
					initpackets[i]=malloc(len);
					memcpy(initpackets[i],data,len);
					initpacketslen[i]=len;
					printf("got cache item %04x with len %i\n",initpackettypes[i],len);
					send(bswarg->clientfd, initpackets[i], initpacketslen[i],MSG_DONTWAIT);
				}

		}

		dumpwrite(-1,bswarg->debug,"msg(%5i) 0x%04x ",want,type1);
			
		switch (type1) {
			case BSWMSG_QUIT:
				dumpwrite(-1,bswarg->debug,"[Quit] ");
				return -1; /* don't forward this packet */
				break;
			case BSWMSG_NOTICE:
				dumpwrite(-1,bswarg->debug,"[ServerMessage] ");
				/* don't repeat packets the client sent */
				if (dir[0] == '<') {
					typestring="PRIVMSG";
				/* client packets are supposed to be location changes .... */
				} else {
					/* don't allow room change packets before login */
					if (bswarg->logged_in == 0 && bswarg->room_location)
						return -1;

/*
>  00 00 33 00 02 03 00 06  43 37 38 2d 34 36 03 00 ..3.....C78-46..
>  0d 44 6f 6d 69 6e 69 6f  6e 42 6f 61 72 64 03 00 .DominionBoard..
>  02 65 6e 03 00 03 61 6f  65 03 00 07 31 2e 36 2e .en...aoe...1.6.
>  30 5f 30                                         0_0
*/
					if (count1 >= 5 && strncmp(contentstring, "\3\3\3\3\3",5) == 0) {
						if (bswarg->room_location) 
							free(bswarg->room_location);
						if (bswarg->room_board) 
							free(bswarg->room_board);
						if (bswarg->lang) 
							free(bswarg->lang);

						next=first;
						if (next && next->content) {	
							bswarg->room_location=malloc(next->length+1);
							strncpy(bswarg->room_location,next->content,next->length);
							bswarg->room_location[len]='\0';
							next=next->next;
						}
						if (next && next->content) {	
							bswarg->room_board=malloc(next->length+1);
							strncpy(bswarg->room_board,next->content,next->length);
							bswarg->room_board[len]='\0';
							next=next->next;
						}
						if (next && next->content) {	
							bswarg->lang=malloc(next->length+1);
							strncpy(bswarg->lang,next->content,next->length);
							bswarg->lang[len]='\0';
							next=next->next;
						}
						if (next && next->content) {	
							/* skip username */
							next=next->next;
						}
						/* java version */
						if (next && next->content && !strncmp(next->content,JAVA_VERSION_DEFAULT,strlen(JAVA_VERSION_DEFAULT))) {	
							if (bswarg->java_ver) 
								free(bswarg->java_ver);
							bswarg->java_ver=malloc(next->length+1);
							strncpy(bswarg->java_ver,next->content,next->length);
							bswarg->java_ver[len]='\0';
						}
					} else {
						debug(bswarg,BSWDEBUG_INFO,"room change packet doesn't seem to have enough fields (%i)\n",count1);
					}
				}
				debug(bswarg,BSWDEBUG_INFO,"room %s (%s), lang %s\n",bswarg->room_location, bswarg->room_board, bswarg->lang);
					
				break;
			case BSWMSG_MESSAGE:
				dumpwrite(-1,bswarg->debug,"[ChatMessage] ");
				typestring="NOTICE";
				break;
			case BSWMSG_PING:
				dumpwrite(-1,bswarg->debug,"[Ping] ");
				if (dir[0] == '<') 
					send_bsw_packet(bswarg->serverfd,bswarg,BSWMSG_PONG,"");
				else
					send_bsw_packet(bswarg->clientfd,bswarg,BSWMSG_PONG,"");
				return -1;
				break;
			case BSWMSG_PONG: 
				dumpwrite(-1,bswarg->debug,"[Pong] ");
				break;
			case BSWMSG_CHANNEL: 
				if (count1 >= 4 && strncmp(contentstring, "\3\3\3\3",4) == 0) {

					next=first;
					if (next && next->content && next->length > 0) {	
						if (bswarg->lang) 
							free(bswarg->lang);
						bswarg->lang=malloc(next->length+1);
						strncpy(bswarg->lang,next->content,next->length);
						bswarg->lang[len]='\0';
					}
					if (next->next) {
						next=next->next;
					}
					/* skip username - TODO: track nickname changes*/
					if (next && next->content && next->length > 0) {	
						if (bswarg->room_location) 
							free(bswarg->room_location);
						bswarg->room_location=malloc(next->length+1);
						strncpy(bswarg->room_location,next->content,next->length);
						bswarg->room_location[len]='\0';
					}
					if (next->next) {
						next=next->next;
					}
					if (next && next->content && next->length > 0) {	
						if (bswarg->room_board) 
							free(bswarg->room_board);
						bswarg->room_board=malloc(next->length+1);
						strncpy(bswarg->room_board,next->content,next->length);
						bswarg->room_board[len]='\0';
					}
					if (next->next) {
						next=next->next;
					}
					debug(bswarg,BSWDEBUG_INFO,"room %s (%s), lang %s\n",bswarg->room_location, bswarg->room_board, bswarg->lang);
				} else {
					debug(bswarg,BSWDEBUG_INFO,"channel info does not have enough fields (%i) to cache the room\n",count1);
				}
					
				break;
			case BSWMSG_SOLO: 
				dumpwrite(-1,bswarg->debug,"[Solitaire] ");
				break;
			case BSWMSG_SOUND: 
				dumpwrite(-1,bswarg->debug,"[Sound] ");
				break;
			case BSWMSG_SHOWTOOL: 
				/* TODO: implement/call a better parse_bsw ... 
				{
					char *infop,itemlen;
					char *packet_copy, * name, * birthday, * email, *status, *city,
						* online, *desc, *userimage, *guild;
					short *sp;

					packet_copy=malloc(len);
					memcpy(packet_copy,data,len);
					infop=packet_copy+3;
					* skip UserInfoTool string *
					* TODO: check whether this is really a UserInfoTool message *
					if ((infop+3)<(packet_copy+len)) {
						sp=(short *)&infop[1];
						itemlen=ntohs(*sp);
						infop += 3 + itemlen;
					}
					* add \0 to name, set *name *
					if ((infop+3)<(packet_copy+len)) {
						sp=(short *)&infop[1];
						itemlen=ntohs(*sp);
						infop += 3 + itemlen;
					}
					
				}
				*/
				/* send extra UIT display packet? */
				/* send_bsw_packet(bswarg->clientfd,bswarg,BSWMSG_SHOWTOOL2,"\3","UserInfoTool"); */
				/* send info packet twice, ... */
				/* send(bswarg->clientfd,data,len,MSG_DONTWAIT); */
				/* send extra UIT layout packet for first info invocation ... */
/*
<  00 00 33 27 11 03 00 05  54 6f 6f 6c 73 03 00 0e ..3'....Tools...
<  34 3b 55 73 65 72 49 6e  66 6f 54 6f 6f 6c 03 00 4;UserInfoTool..
<  12 49 6e 66 6f 3b 31 3b  32 30 30 3b 2d 31 3b 2d .Info;1;200;-1;-
<  31 3b 6f                                         1;o
*/
				if (count1 >= 1 && !strncmp(first->content,"UserInfoTool",strlen("UserInfoTool"))) {
						/* kludge: output info below as userinfo */
						userinfo = 1;
						send_bsw_packet(bswarg->clientfd,bswarg,BSWMSG_SHOWTOOL,"\3\3\3","Tools","4;UserInfoTool", "Info;1;200;-1;-1;o");
				}
				break;
			case BSWMSG_SHOWTOOL2: 
/* >  00 00 14 4e 20 03 00 0c  55 73 65 72 49 6e 66 6f ...N ...UserInfo
 * >  54 6f 6f 6c                                      Tool
 * ignore for now, maybe quench the UserInfoTool packet?
 */
				break;
			case BSWMSG_LOGIN: 

				/* only send menu if client tries to log in */
				if (dir[0] == '>') {
					dumpwrite(-1,bswarg->debug,"[Login] ");
					for (i=0; i<initpacketscount; i++) {
						if (initpackets[i]) {
							printf("sent cache item %04x with len %i\n",initpackettypes[i],len);
							send(bswarg->clientfd, initpackets[i], initpacketslen[i],MSG_DONTWAIT);
						};
					}
					/* take java version from real client TODO: write an actual parser */
					printf("copying java ver ... ");
					char *datap;
					size_t j_len;
					datap=data+3; // skip length
printf("data: %p, datap: %p",data,datap);
					datap=datap+2; // skip packet type
printf(", datap: %p",datap);
					datap+=1+2+datap[2]; // skip string byte + len + username 
printf(", datap: %p",datap);
					datap+=1+2+datap[2]; // skip string byte + len + pass
printf(", datap: %p",datap);
					datap+=1+2+datap[2]; // skip string byte + len + client type
printf(", datap: %p",datap);
					datap+=1+2+datap[2]; // skip string byte + len + "BSW" string
printf(", datap: %p",datap);
					j_len=datap[2];
					datap+=3; // skip string byte + len
printf(", datap: %p, j_len: %i",datap,j_len);
					// java ver:
					if (!strncmp(datap,JAVA_VERSION_DEFAULT,strlen(JAVA_VERSION_DEFAULT))) {	
						if (bswarg->java_ver) 
							free(bswarg->java_ver);
						bswarg->java_ver=malloc(j_len+1);
						strncpy(bswarg->java_ver,datap,j_len);
						bswarg->java_ver[j_len]='\0';
					}
					printf(", java ver: %s\n",bswarg->java_ver);
					bswarg->logged_in=1;
				}
				return -1; /* don't forward this packet */
				break;
			default:
				break;
		}
		msgptr+=2;
		while ((msgptr+3)<(data+len)) {
			type2=(unsigned char)msgptr[0]&0xff;
			dlen=0x100*((unsigned char)msgptr[1]&0xff)+
				((unsigned char)msgptr[2]&0xff);
			dumpwrite(-1,bswarg->debug,"0x%02x(%i) ",
				type2, dlen);
			if ((msgptr+3+dlen)<=(data+len)) {
				char *c;
				switch(type2) {
					case '\3': // Ascii?
						// memcpy(string,&msgptr[3],sizeof(string)-1);

						if (dlen>(sizeof(string)-2))
							dlen=sizeof(string)-2;
						memcpy(string,&msgptr[3],dlen);
						if (count == 0) {
							messagestring=&msgptr[3];
							messagelen=dlen;
						}
						/* last string is supposed to be recipient, so set it to each string */
						rcptstring=&msgptr[3];
						rcptlen=dlen;

						/* remove unprintables */
						for (c=&string[dlen-1]; c>string; c--)
							if (!isprint(*c))
								*c='.';

						string[dlen]='\0';
						/* special processing for info messages */
						if (userinfo) {
							if (count == 0) {
								/* check whether this is a UserInfoTool msg (not Tools) */
								if (dlen != strlen("UserInfoTool") ||
										strncmp(string,"UserInfoTool",strlen("UserInfoTool")))
									userinfo=0;
							} else if (count == 1) {
								int infouserlen;

								infouserlen=strchr(string,' ')-string;
								if (infouserlen>0 && infouserlen < (sizeof(infouser)-1)) {
									strncpy(infouser,string,infouserlen);
									infouser[infouserlen]='\0';
								}
								else
									strcpy(infouser,"unknown");
								dumpwrite(bswarg->sv[0],0,"311 %s %s %s %s * :%s: %s\n",bswarg->username,infouser,infouser,"localhost",infostrings[count],string);
							} else if (count == 7) {
								dumpwrite(bswarg->sv[0],0,"320 %s %s :%s: %s%s\n",bswarg->username,infouser,infostrings[count],PLAYERIMAGEBASE,string);
							} else 
								dumpwrite(bswarg->sv[0],0,"320 %s %s :%s: %s\n",bswarg->username,infouser,infostrings[count],string);
						}
						break;
					case '\1':
					default:
						break;
				}

				dumpwrite(-1,bswarg->debug,"%s ",string);
				count++; // increase marker for number of string fields
			}
			msgptr+=3+dlen;
		}
		if (userinfo && count > 0) {
			dumpwrite(bswarg->sv[0],0,"318 %s %s :End of /WHOIS list.\n",bswarg->username,infouser);
		}
		userinfo = 0;
		dumpwrite(-1,bswarg->debug,"\n");
		if (typestring) {
			char *fromstring;
			int fromlen;

			// copy up to first space, skip colon ...
			fromstring=memchr(messagestring,' ',messagelen);
			if (messagestring[0] != '/' && fromstring)
				fromlen=(fromstring-messagestring);
			else
				fromlen=0;
			if (fromlen > 0 && fromlen < messagelen) {
				if (messagestring[fromlen-1] == ':')
					fromlen--;
				memcpy(string,messagestring,fromlen);
				string[fromlen]='\0';
				dumpwrite(bswarg->sv[0],0,":%s ",string);
				fromlen++;
				if (messagestring[fromlen-1] == ':') {
					fromlen ++;
				}
			}

			dumpwrite(bswarg->sv[0],0,"%s ",typestring);
			if (messagestring != rcptstring) {
				memcpy(string,rcptstring,rcptlen);
				string[rcptlen]='\0';
				dumpwrite(bswarg->sv[0],0,"%s ",string);
			} else 
				dumpwrite(bswarg->sv[0],0,"%s ",bswarg->username);
				
			memcpy(string,messagestring+fromlen,messagelen-fromlen);
			string[messagelen-fromlen]='\0';
			dumpwrite(bswarg->sv[0],0,":%s\n",string);
		}

		// free space used for packet pointer cache:
		while (first) {
			next=first->next;
			free(first);
			first=next;
		}
	} else {
		debug(bswarg,BSWDEBUG_INFO,"%s unknown data (%06x)\n",dir,want);
	}
	
	return 0;
}


void dump(bswdata_t *bswarg, char *dir, char *data, int len) {
	int i;
	unsigned int want;

	if (len <= 0)
		return;


	want=0x10000*((unsigned char)data[0]&0xff)+
		0x100*((unsigned char)data[1]&0xff)+
		((unsigned char)data[2]&0xff);
	
	if (bswarg->debug)
	while (len>0) {
		printf("%s ",dir);
		for (i=0; i<8 && i<len; i++)
			printf("%02x ",data[i]&0xff);
		printf(" ");
		for (i=8; i<16 && i<len; i++)
			printf("%02x ",data[i]&0xff);
		for (i=0; i<(16-len); i++)
			printf("   ");
		for (i=0; i<16 && i<len; i++)
			if (isprint(data[i]))
					printf("%c",data[i]&0xff);
			else
					printf(".");
		printf("\n");
		len -= 16;
		data += 16;
	}
}

int proxy_connect (int *port, int *fd, char **listenaddr) {
	static int insock,resp;
	static struct sockaddr_in my_name, peer_name;
	static protoent_t * proto_tcp;
	static socklen_t insize;
	int rc;

	// printf("proxy_connect port %i listen fd *%i\n",*port,*fd);
	if(*fd < 0) {
		proto_tcp = getprotobyname("tcp");

		//if (!proto_tcp)
		//	error_exit(bswarg,"proto_tcp is NULL");

		my_name.sin_family = AF_INET;
		if (*listenaddr == NULL) {
			my_name.sin_addr.s_addr = htonl(INADDR_ANY);
		} else if (inet_aton(*listenaddr, &my_name.sin_addr) == 0) {
			printf("inet_aton failed for address %s\n",*listenaddr);
			exit(1);
		}
		//my_name.sin_addr.s_addr = htonl(INADDR_ANY);
		my_name.sin_port = htons(*port);

		insize = sizeof(peer_name);

		listen(insock, 1);
		insock = socket(PF_INET, SOCK_STREAM, proto_tcp->p_proto);

		/* be able to restart server quicker in case of WAIT1/2 procs */
		if (insock == -1) {
			perror("socket");
			return insock;
			// error_exit(bswarg,"no socket");
		}



		rc=1;
		if (setsockopt(insock, SOL_SOCKET, SO_REUSEADDR,  (char *)&rc, sizeof(rc)) < 0) {
			perror("setsockopt SO_REUSEADDR\n");
			//	error_exit(bswarg,"setsockopt SO_REUSEADDR\n");
		}

		if (bind(insock, (struct sockaddr *) &my_name, sizeof(my_name)) < 0) {
			close(insock);
			perror("bind");
		}
		listen(insock, 1);

		{ 
			int flags = O_NONBLOCK;
			fcntl(insock, F_SETFL, flags);
		}
		*fd=insock;
	} else {
		insock=*fd;
	}

	/* wait for connection ... */
	resp = accept(insock, (struct sockaddr *) & peer_name, &insize);
	//if (resp < 0) {
	//	perror("accept");
	//}
	/* close listening socket so it can be reused by next process */
	/* close(insock); */

	/* insert ssl init here */
	return resp;
}

/* e. g.
 * send_bsw_packet(bswarg->serverfd,bswarg,BSWMSG_LOGIN,"\3\3\3\3\3\3",bswarg->username,bswarg->password,"bsw-proxy $Revision: 1.20 $ Client","BSW",(bswarg->java_ver?bswarg->java_ver:JAVA_VERSION_DEFAULT),mac); 
 */

int send_bsw_packet(int fd, bswdata_t * bswarg,int type, char *fmt, ...) {
	va_list ap;
	int d;
	char c, *s;
	char packet[0x1000];
	int plen,slen;
	short * sp;
	int * lp;

	va_start(ap, fmt);

	plen=3;
	
	sp=(short *)&packet[plen];
	*sp=htons(type);
	plen += 2;

	while (*fmt) {
		packet[plen]=*fmt;
		plen ++;
		switch(*fmt) {
			case BSWTYPE_LONG:           /* int */
				d = va_arg(ap, int);
				lp=(int *)&packet[plen];
				*lp=htonl(d);;
				plen += 4;
				printf("int %d\n", d);
				break;
			case BSWTYPE_BYTE:           /* char */
				/* need a cast here since va_arg only
					 takes fully promoted types */
				c = (char) va_arg(ap, int);
				packet[plen]=c&0xff;
				plen ++;
				printf("char %c\n", c);
				break;
			case BSWTYPE_STRING:           /* string */
				s = va_arg(ap, char *);
				slen=strlen(s);
				sp=(short *)&packet[plen];
				*sp=htons(slen);
				plen += 2;
				strcpy(&packet[plen],s);
				plen += slen;
				printf("string %s\n", s);
				break;
			case BSWTYPE_LIST:
			case BSWTYPE_BLOB1:
			case BSWTYPE_SUBLIST:
			case BSWTYPE_BLOB2:
			case BSWTYPE_BLOB3:
			default:
				s = va_arg(ap, char *);
				slen=va_arg(ap, int);
				sp=(short *)&packet[plen];
				*sp=htons(slen);
				plen += 2;
				strcpy(&packet[plen],s);
				plen += slen;
				printf("string %s\n", s);
				break;
		}
		fmt++;
	}
	va_end(ap);
	packet[0]='\0';
	sp=(short *)&packet[1];
	*sp=htons(plen);
	printf("will send:\n");
	dump(bswarg,"! ",packet,plen);
	return send(fd,packet,plen,0);
	
}

/* bsw_pack_list(bswarg->clientfd,BSWMSG_LOGIN,bswarg->username,bswarg->password,"Client"); */

int bsw_pack_list(char ** list, int * size, int count, ...) {
	va_list ap;
	int i;
	short *sp;
	char *listcontent;

	*list=listcontent;
	
	*size=0;

	/* find size of list for malloc 
	*/
	va_start(ap, count);
	for (i=0; i<count; i++) {
		*size += 2;
		*size += va_arg(ap, int);
		va_arg(ap, char *);
	}
	va_end(ap);
	
	/* alloc space for list */
	listcontent=malloc(*size);
	if (!listcontent) 
		return -1;
	/* fill list */
	va_start(ap, count);
	for (i=0; i<count; i++) {
		sp=(short *)listcontent;
		listcontent+=2;
		*sp=va_arg(ap, int);
		memcpy(listcontent,va_arg(ap, char *),*sp);
	}
	va_end(ap);
	
	return count;
	
}

/* feed irc clients, process rfc1459/rfc2812 commands 
 * http://www.faqs.org/rfcs/rfc1459.html
 * http://www.faqs.org/rfcs/rfc2812.html
 */
int irc_proxy(bswdata_t * bswarg) {
	int resp;
	unsigned int len, flags;
	int sret, cret;
	char cbuf[65536];
	char sbuf[65536];
	char msg[512];
	fdlist_t * tmpfdlist, ** prevfdlistp;
	int cwarned=0; /* about "no irc clients" */
	
	len=sizeof(cbuf);
	flags=MSG_DONTWAIT; //MSG_WAITALL;
	/* TODO URGENT: implement safe recv */
	sret=-1;

	/* loop accept/send/recv */

	while (1) {
		/* wait for (irc?) client here */
		resp = proxy_connect(&bswarg->ircport,&(bswarg->irclistenfd),&(bswarg->listenaddr));

		if (resp >= 0) {
			char welcome[256];
			tmpfdlist=malloc(sizeof(fdlist_t));
			tmpfdlist->fd=resp;
			tmpfdlist->next=bswarg->ircfdlist;
			bswarg->ircfdlist=(struct fdlist *)tmpfdlist;
			snprintf(welcome,sizeof(welcome)-1,"001 %s :welcome\n",bswarg->username);
			welcome[sizeof(welcome)-1]='\0';
			write(bswarg->ircfdlist->fd,welcome,strlen(welcome));
			// TODO: cache/send MOTD
			debug(bswarg,BSWDEBUG_INFO,"irc proxy accepted connection\n");
		}

		/* read from socket, talk irc protocol ... */ 
		cret = recv(bswarg->sv[1], cbuf, len, flags);

		if ((cret > 0) && (bswarg->ircfdlist)) {
			int client_count=0;

			tmpfdlist=bswarg->ircfdlist;
			prevfdlistp=&bswarg->ircfdlist;
			while (tmpfdlist) {
				if ((write (tmpfdlist->fd,cbuf,cret)) < 0) {
					perror("send to irc client");
					/* clean tmpfd */
					close(tmpfdlist->fd);
					/* link next entry back */
					*prevfdlistp=tmpfdlist->next;
					/* free */
					free(tmpfdlist);
					/* walk back so next entry is really next */
					tmpfdlist=*prevfdlistp;
				}
				if (tmpfdlist) {
					prevfdlistp=&tmpfdlist->next;
					tmpfdlist=tmpfdlist->next;
				}
				client_count++;
			}
			debug(bswarg,BSWDEBUG_INFO,"counted %i clients on writing\n",client_count);
		}
		/* receive from irc clients in turn */
		if (bswarg->ircfdlist) {
			tmpfdlist=bswarg->ircfdlist;
			prevfdlistp=&bswarg->ircfdlist;
			int middle_names=0;
			int irc_message=0;

			cwarned=0;

			while (tmpfdlist) {
				if ((sret = recv(tmpfdlist->fd, sbuf, len-1, flags)) >= 0 || errno == EAGAIN) {
					if (sret > 0) {
						char *line,*end;

						debug(bswarg,BSWDEBUG_INFO,"received %i from irc client\n",sret);
						sbuf[sret]='\0';
						sbuf[sizeof(sbuf)-1]='\0';
						line=sbuf;
						/* split lines: dirty, should do proper receive and split above */
						while ((end=strchr(line,'\n')) < (sbuf+sret) && (end > line)) {
							char * pstart, * pend;
							char * first, * command, * middle, * tail;
							char * bsw_command;
							int type;

							*end='\0';
							if (*(end-1) == '\r')
								*(end-1) = '\0';

							debug(bswarg,BSWDEBUG_INFO,"irc: %s\n",line);
							/* first part (irrelevant) and start of command */
							if (line[0] == ':') {
								pend=strchr(line,' ');
								if (pend) {
									*pend='\0';
									command=pend+1;
								}
								first=line+1; /* not yet used, is this relevant for bsw? */
							} else {
								command=line;
								first=NULL;
							}

							/* find part after command (next "middle" according to rfc1459) */
							if ((pend=strchr(command,' ')) && (pend > command) && (pend < (line+MAX_IRC_COMMAND_LENGTH))) {
								*pend='\0';
								middle=pend+1;

							} else {
								debug(bswarg,BSWDEBUG_ERROR,"error: no end to command %s\n",command);
								/* TODO: check handling; improve on recv above */
								middle=NULL;
								
							}
							
							/* separate tail from middle */
							if (middle) {
								pstart=middle;
								while ((pstart[0] != ':') && ((pend=strchr(pstart,' ')) > pstart) && (pend < end)) {
									pstart=pend+1;
									debug(bswarg,BSWDEBUG_INFO,"middle expanded from %s\n",pstart);
								}
								if (pstart[0] == ':') {
									*(pstart-1)='\0';
									tail=pstart+1;
								} else
									tail=NULL;
							}
							
							/* make command uppercase for easier comparison */
							pstart=command;
							// while ((*pstart=toupper(*pstart++)));
							while (*pstart) {
								*pstart=toupper(*pstart);
								pstart++;
							}

							if (first && command && middle && tail) {
								debug(bswarg,BSWDEBUG_INFO,"parsed irc command line to: from %s command %s middle %s tail %s\n", first, command, middle, tail);
							} else {
								debug(bswarg,BSWDEBUG_INFO,"malparsed irc command line %s\n", line);
							}

							bsw_command=NULL;
							irc_message=middle_names=0;

							if (!strncmp(command,"PRIVMSG",strlen("PRIVMSG"))) {
								type=BSWMSG_MESSAGE;

								if (middle && middle[0] == '#')
									bsw_command="/gtell ";
								else
									bsw_command="/tell ";
								middle_names=1;
							} else if (!strncmp(command,"NOTICE",strlen("NOTICE"))) {
								type=BSWMSG_MESSAGE;
								bsw_command="";
							} else if (!strncmp(command,"QUIT",strlen("QUIT"))) {
								snprintf(msg,sizeof(msg)-1,"ERROR :Closing Link: %s (Client Quit)\r\n",bswarg->username);
								msg[sizeof(msg)-1]='\0';
								write(tmpfdlist->fd,msg,strlen(msg));
								debug(bswarg,BSWDEBUG_INFO,"irc proxy closing connection\n");
								close (tmpfdlist->fd);

							} else if (!strncmp(command,"JOIN",strlen("JOIN"))) {
								snprintf(msg,sizeof(msg)-1,
								":%s!%s@localhost JOIN :%%s\r\n"
								"MODE %%s +n\r\n"
								"353 %s = %%s :%s\r\n"
								"366 %s %%s :End of /NAMES list.\r\n"
								,bswarg->username,bswarg->username,
								bswarg->username,bswarg->username,bswarg->username);
								msg[sizeof(msg)-1]='\0';
								type=BSWMSG_MESSAGE;
								bsw_command="/channel ";
								middle_names=irc_message=1;
							} else if (!strncmp(command,"TOPIC",strlen("TOPIC"))) {
								type=BSWMSG_MESSAGE;
								bsw_command="/topic ";
							} else if (!strncmp(command,"PART",strlen("PART"))) {
								// snprintf(msg,sizeof(msg)-1,":%s!%s@localhost PART %s :\r\n",bswarg->username,bswarg->username,strchr(pstart,' ')+1);
								snprintf(msg,sizeof(msg)-1,":%s PART %s :%s\r\n",bswarg->username,bswarg->username,tail);
								msg[sizeof(msg)-1]='\0';
								type=BSWMSG_MESSAGE;
								bsw_command="/leave ";
								middle_names=irc_message=1;
							} else if (!strncmp(command,"WHOIS",strlen("WHOIS"))) {
								type=BSWMSG_MESSAGE;
								bsw_command="/info ";
								middle_names=irc_message=1;
							} else if (!strncmp(command,"WHOIS",strlen("WHOIS"))) {
								type=BSWMSG_MESSAGE;
								bsw_command="/info ";
								middle_names=irc_message=1;
							} else if (!strncmp(command,"OLIST",strlen("OLIST"))) {
								; // TODO: send channel user list request
							} else if (!strncmp(command,"NICK",strlen("NICK"))) {
								type=BSWMSG_MESSAGE;
								bsw_command="/name ";
								middle_names=irc_message=1;
							} else if (!strncmp(command,"PING",strlen("PING"))) {
								type=BSWMSG_MESSAGE;
								write (tmpfdlist->fd,"PONG",strlen("PONG"));
								if (middle) {
									write (tmpfdlist->fd," ",1);
									write (tmpfdlist->fd,middle,strlen(middle));
								}
								if (tail) {
									write (tmpfdlist->fd," :",1);
									write (tmpfdlist->fd,tail,strlen(tail));
								}
								write (tmpfdlist->fd,"\r\n",2);
								bsw_command=NULL;
							} else {
								type=BSWMSG_MESSAGE;
								bsw_command=NULL;
								irc_message=middle_names=0;
							}
							
							/* TODO: split this into smaller chunks ... */
							if (bsw_command || irc_message) {
								cbuf[0]='\0';

								if (middle_names) {

									if (middle) {
										char *pmark, * channel;

										pend=middle+strlen(middle);

										pstart=middle;
										while (pstart >= middle && pstart < pend) {
											struct fdlist *tmp2fdlist=bswarg->ircfdlist;
											channel=pstart;
											pmark=strchr(pstart,',');
											if (pmark)
												*pmark='\0';
											
											if (irc_message) {
												char msg2[512];
												/* compile string for irc messages */
												snprintf(msg2,sizeof(msg2),msg,channel,channel,channel,channel);
												debug(bswarg,BSWDEBUG_INFO,"found name %s\n",pstart);
												debug(bswarg,BSWDEBUG_INFO,"%s",msg2);
												while (tmp2fdlist) {
													write(tmp2fdlist->fd,msg2,strlen(msg2));
													tmp2fdlist=tmp2fdlist->next;
												}
											}
											if (command) {
												char * rcptptr;

												cbuf[0]='\0';
												/* construct message: chosen command ... */
												strcat(cbuf,bsw_command);

												rcptptr=channel;
												if (rcptptr[0] == '#')
													rcptptr++;
												strcat(cbuf,rcptptr);

												if (tail) {
													strcat(cbuf," ");
													strcat(cbuf,tail);
												}

												/* remove trailing space */
												pend=cbuf+strlen(cbuf);
												while (isspace(*--pend))
													*pend='\0';

												debug(bswarg,BSWDEBUG_VERBOSE,"sending packet command %s type %04x %s\n",bsw_command,type,cbuf);
												send_bsw_packet(bswarg->sv[1],bswarg,type,"\x3",cbuf);
											}

											if (pmark)
												pstart=pmark+1;
											else
												break;
										}
									}
								} else {
									struct fdlist *tmp2fdlist=bswarg->ircfdlist;

									if (irc_message) {
										/* compile string for irc messages */
										debug(bswarg,BSWDEBUG_INFO,"%s",msg);
										while (tmp2fdlist) {
											write(tmp2fdlist->fd,msg,strlen(msg));
											tmp2fdlist=tmp2fdlist->next;
										}
									}
									if (command) {
										char * rcptptr;

										cbuf[0]='\0';
										/* construct message: chosen command ... */
										strcat(cbuf,bsw_command);
										if (middle) {
											rcptptr=middle;
											if (rcptptr[0] == '#')
												rcptptr++;
											strcat(cbuf,rcptptr);
										}

										if (tail) {
											strcat(cbuf," ");
											strcat(cbuf,tail);
										}

										/* remove trailing space */
										pend=cbuf+strlen(cbuf);
										while (isspace(*--pend))
											*pend='\0';

										debug(bswarg,BSWDEBUG_VERBOSE,"sending packet command %s type %04x %s\n",bsw_command,type,cbuf);
										send_bsw_packet(bswarg->sv[1],bswarg,type,"\x3",cbuf);
									}

								}
							}
							line=end+1;
						}
					}
					if (sret < 0)
						if (errno != EAGAIN)
							sret = 0;
				} else {
					perror("recv from irc client");
					/* clean tmpfd */
					close(tmpfdlist->fd);
					/* link next entry back */
					*prevfdlistp=tmpfdlist->next;
					/* free */
					free(tmpfdlist);
					/* walk back so next entry is really next */
					tmpfdlist=*prevfdlistp;
				}

				if (tmpfdlist) {
					prevfdlistp=&tmpfdlist->next;
					tmpfdlist=tmpfdlist->next;
				}
			}
		} else if (!cwarned) {
			bswarg->logged_in=0;
			cwarned=1;
			debug(bswarg,BSWDEBUG_INFO,"no irc clients\n");
		}
		// usleep(100000000);
		myusleep(USLEEPTIME);
	}

	return 0;
}

int bsw_recv(int s, char *buf, size_t len, int flags) {
	int tlen=0;
	unsigned int want;
	int ret;

	
	want=3;
	while (want > 0) {
		ret = recv(s, buf+tlen, want, flags);
		if (ret > 0) {
			want-=ret;
			tlen+=ret;
		}
		if (tlen == 0 && ret <= 0) {
			/* error */
			return ret;
		}
	}
	want=0x10000*((unsigned char)buf[0]&0xff)+
		0x100*((unsigned char)buf[1]&0xff)+
		((unsigned char)buf[2]&0xff);
	if (want>len) {
		fprintf(stderr,"got %i > %i, only reading to limit\n",want,len);
		want=len-tlen;
	} else {
		want-=tlen;
	}
	while (want > 0) {
		ret = recv(s, buf+tlen, want, flags);
		if (ret > 0) {
			want-=ret;
			tlen+=ret;
		}
		if (ret == 0 || (ret < 0 && errno != EAGAIN)) {
			/* error */
			return -1;
		}
	}
	return tlen;
}


int bsw_handle(bswdata_t * bswarg) {
	unsigned int len, flags;
	int sret, cret;
	int resp;
	time_t lastconnect = 0;
	char cbuf[65536];
	char sbuf[65536];
	struct sockaddr_in dest_addr; /* server to which we are connecting */
	int cwarned=0; /* about client disconnect */
	int swarned=0; /* about "server disconnected on read" */

	len=sizeof(cbuf);
	flags=MSG_DONTWAIT; //MSG_WAITALL;

	/* send_bsw_packet test:
		 printf("type %i user %s pass %s\n",BSWMSG_LOGIN,bswarg->username,bswarg->password);
		 send_bsw_packet(bswarg->clientfd,  bswarg,BSWMSG_LOGIN,"sss",bswarg->username,bswarg->password,"Client");
		 exit(0);
	 */
	while (1) {
		/* connect client - TODO: defer! */
		if (bswarg->clientfd == -1) {
			resp = proxy_connect(&bswarg->inport,&(bswarg->clientlistenfd),&(bswarg->listenaddr));
			if (resp >= 0) {
				bswarg->clientfd=resp;
				// TODO: init client (spv, room, menu, motd, userlists?)
				//send_bsw_packet(bswarg->clientfd,
				// bsw_init_client(bswarg)
			} else {
				sleep(1);
				perror("accept");
				bswarg->clientfd=-1;
			}
		}


		/* connect server - TODO: defer? */
		if (bswarg->serverfd == -1 && lastconnect < (time(NULL)-CONNECTRETRY)) {
			debug(bswarg,BSWDEBUG_INFO,"trying to connect to server\n");
			dest_addr.sin_family = AF_INET;
			dest_addr.sin_addr.s_addr = inet_addr (bswarg->servername);
			dest_addr.sin_port = htons (bswarg->outport);
			bzero (dest_addr.sin_zero, 8);

			bswarg->serverfd = socket (AF_INET, SOCK_STREAM, 0);
			if (bswarg->serverfd < 0) {
				perror("socket");
				// error_exit(bswarg,"server socket\n");
				return -1;
			}
			if (connect(bswarg->serverfd, (const  struct sockaddr *) &dest_addr,
						sizeof (struct sockaddr)) < 0) {
				perror("connect");
				// error_exit(bswarg,"server connect\n");
			} else {
				char mac[1000];
				struct timeval tv;
				struct timezone tz;
				uint64_t jiffies;

				debug(bswarg,BSWDEBUG_INFO,"connected to server %s:%i with %i\n",bswarg->servername,bswarg->outport,bswarg->serverfd);
				if (bswarg->room_board && bswarg->room_location && bswarg->lang && bswarg->java_ver) {
					send_bsw_packet(bswarg->serverfd,bswarg,BSWMSG_NOTICE,"\3\3\3\3\3",bswarg->room_location,bswarg->room_board,bswarg->lang,bswarg->username,bswarg->java_ver); 
				} else { 
					debug(bswarg,BSWDEBUG_INFO,"not enough info for room change\n");
				}
				/* send_bsw_packet(bswarg->serverfd,bswarg,BSWMSG_NOTICE,"\3\3\3\3","19","ManagerBoard","de",bswarg->username); */
				/* send_bsw_packet(bswarg->serverfd,bswarg,BSWMSG_LOGIN,"\3\3\3",bswarg->username,bswarg->password,"Client"); */
				/* send_bsw_packet(bswarg->serverfd,bswarg,BSWMSG_LOGIN,"\3\3\3\3\3",bswarg->username,bswarg->password,"Client","BSW","1.5.0") */; 
				gettimeofday(&tv,&tz);
				jiffies=(uint64_t)tv.tv_sec*(uint64_t)1000+(uint64_t)tv.tv_usec/(uint64_t)1000;
				sprintf(mac,"MAC:%x%08x",(unsigned int)(jiffies/(uint64_t)0x10000/(uint64_t)0x10000+(uint64_t)0x00020000),(unsigned int)(jiffies&0xffffffff));
//printf ("%s\n",mac);
//exit(0);
				send_bsw_packet(bswarg->serverfd,bswarg,BSWMSG_LOGIN,"\3\3\3\3\3\3",bswarg->username,bswarg->password,"bsw-proxy $Revision: 1.20 $ Client","BSW",(bswarg->java_ver?bswarg->java_ver:JAVA_VERSION_DEFAULT),mac); 

			}
			lastconnect = time(NULL);

		}

		cret = bsw_recv(bswarg->clientfd, cbuf, len, flags);

		/* check for client receive error and set fd accordingly */
		if (cret == 0 || (cret < 0 && errno != EAGAIN)) {
			cret = 0;
			close(bswarg->clientfd);
			bswarg->clientfd = -1;
			bswarg->logged_in=0;
			if (!cwarned) {
				cwarned=1;
				debug(bswarg,BSWDEBUG_INFO,"client disconnected on recv\n");
			}
		}

		if (cret > 0) {
			dump(bswarg,"> ",cbuf,cret);
			if (!bsw_packet_handle(bswarg,"> ",cbuf,cret)) {
				if ((send (bswarg->serverfd,cbuf,cret,0)) < 0) {
					close(bswarg->serverfd);
					bswarg->serverfd = -1;
					if (!swarned) {
						perror("send to server");
						debug(bswarg,BSWDEBUG_INFO,"server disconnected on send\n");
						swarned=1;
					}
				} else {
					swarned=0;
				}
			}
		}
		
		sret = bsw_recv(bswarg->serverfd, sbuf, len, flags);
		
		/* check for server receive error and set fd accordingly */
		if (sret == 0 || (sret < 0 && errno != EAGAIN)) {
			sret = 0;
			close(bswarg->serverfd);
			bswarg->serverfd = -1;
			if (!swarned) {
				debug(bswarg,BSWDEBUG_INFO,"server disconnected on recv\n");
				swarned=1;
			}
		} else
			swarned=0;

		if (sret > 0) {
			dump(bswarg,"< ",sbuf,sret);
			if (!bsw_packet_handle(bswarg,"< ",sbuf,sret))
				if ((send (bswarg->clientfd,sbuf,sret,0)) < 0) {
					perror("send to client");
					close(bswarg->clientfd);
					bswarg->clientfd = -1;
					bswarg->logged_in=0;
					if (!cwarned) {
						cwarned=1;
						debug(bswarg,BSWDEBUG_INFO,"client disconnected on send\n");
					}
				}
		}
		/* read from irc proxy - should be processed data in bsw packet format ...*/
		sret = bsw_recv(bswarg->sv[0], sbuf, len, flags);
		
		/* check for server receive error and set fd accordingly */
		if (sret == 0 || (sret < 0 && errno != EAGAIN)) {
			sret = 0;
		}

		/* send data received from irc to server ... or not */
		if (sret > 0) {
			dump(bswarg,"[ ",sbuf,sret);
			if ((send (bswarg->serverfd,sbuf,sret,0)) < 0) {
				perror("send data from irc to server");
				close(bswarg->serverfd);
				bswarg->serverfd = -1;
				debug(bswarg,BSWDEBUG_INFO,"server disconnected on send from irc\n");
			}
		}

		if (sret <= 0 && cret <= 0) {
			myusleep(USLEEPTIME);
		} else {
			debug(bswarg,BSWDEBUG_INFO,"received %i from server, %i from client\n",sret,cret);
		}
	}
	return 0;
		
}

int main(int argc, char ** argv) {
	bswdata_t * bswarg;

	/* getopt */
	int c,i;

	bswarg=malloc(sizeof(bswdata_t));
	for (i=0; i<initpacketscount; i++) {
		initpackets[i]=NULL;
		initpacketslen[i]=0;
	}

	/* defaults */
	bswarg->logfile = LOGFILE;
	bswarg->log = LOGSTREAM;
	bswarg->inport = LISTENPORT;
	bswarg->ircport = IRCPORT;
	bswarg->outport = SERVERPORT;
	bswarg->servername = SERVER;
	bswarg->serverfd=-1;
	bswarg->clientfd=-1;
	bswarg->ircfdlist=(fdlist_t *)NULL;
	bswarg->clientlistenfd=-1;
	bswarg->irclistenfd=-1;
	bswarg->debug=1;
	bswarg->daemon=1;
	bswarg->listenaddr=NULL;
	/* TODO: config file/cache file for version and MAC? */
	bswarg->java_ver=NULL;
	bswarg->room_board=NULL; /* cache room */
	bswarg->room_location=NULL; /* cache room */
	bswarg->lang=NULL; /* cache room */
	bswarg->logged_in=0; /* and don't accept room command before login ... */


	while (1) {
		c = getopt(argc, argv, "fhi:I:p:s:l:L:dU:P:q"
				);
		if (c == -1)
			break;
		switch (c) {
			case 'h':
				usage(argv[0]);
				exit(0);
				break;
			case 'd':
				bswarg->debug++;
				debug(bswarg,BSWDEBUG_INFO,"debug level increased to %i\n",bswarg->debug);
				break;
			case 'q':
				bswarg->debug--;
				debug(bswarg,BSWDEBUG_INFO,"debug level decreased to %i\n",bswarg->debug);
				break;
			case 'f':
				fprintf(stderr,"foreground mode enabled, no forks (no irc)\n");
				bswarg->daemon=0;
				break;
			case 'i':
				bswarg->inport = 0xffff&atoi(optarg);
				break;
			case 'I':
				bswarg->ircport = 0xffff&atoi(optarg);
				break;
			case 'p':
				bswarg->outport = 0xffff&atoi(optarg);
				break;
			case 'l':
				bswarg->logfile = optarg;
				bswarg->log = fopen (bswarg->logfile, "a");
				break;
			case 'L':
				bswarg->listenaddr= optarg;
				break;
			case 'U':
				bswarg->username = optarg;
				break;
			case 'P':
				bswarg->password = optarg;
				break;
			case 's':
				bswarg->servername = optarg;
				break;

			default:
				fprintf(stderr, "unknown argument %c\n", c);
				usage(argv[0]);
				exit(1);
		}
	}

	/* set up signals (SIGPIPE notably annoys us in bsw_irc_proxy) */
	signal(SIGPIPE,SIG_IGN);

	/* fork/exit here for daemon ... */

	socketpair(AF_UNIX,SOCK_STREAM,0,bswarg->sv);
	{ 
		int flags = O_NONBLOCK;
		fcntl(bswarg->sv[1], F_SETFL, flags);
		fcntl(bswarg->sv[0], F_SETFL, flags);
	}
	
	if (bswarg->daemon == 1) {
		if (fork() == 0) {
			while (1) {
				(void)bsw_handle(bswarg);
				sleep(1);
				printf("client gone again\n");
			}
			exit(0);
		}
		if (fork() == 0) {
			while (1) {
				(void)irc_proxy(bswarg);
				printf("irc proxy gone again\n");
				sleep(1);
			}
			exit(0);
		}
	} else
		while (1) {
			(void)bsw_handle(bswarg);
			sleep(1);
			printf("client gone again\n");
		}

	exit(0);
}


/* 
$Log: bsw-proxy.c,v $
Revision 1.20  2010-10-31 00:02:23  aoe
room cache (mostly works, but only by caching packets for room desc :-/)
java default version "(unknown)"; ignored from upstream
make client show info applet on first call by sending a geometry (?) packet 0x2711
rudimentary packet preparsing for string extraction (-> parsed_packet_t)
removed struct bswmsg since it was never used

Revision 1.19  2010-10-26 19:24:13  aoe
cache client java version and pass it along to the server

Revision 1.18  2010-10-26 18:23:00  aoe
added version (CVS Id, Log, Revision in bsw client string)

*/

