1 /**
2  * libqcproto: Vypress/QChat protocol interface library
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 /*
20  * QCS: qChat 1.6/VypressChat link interface
21  *
22  *	vypress chat protocol implementation
23  *
24  * (c) Saulius Menkevicius 2001-2004
25  */
26 
27 #include <errno.h>
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
32 
33 #include "qcproto.h"
34 #include "supp.h"
35 #include "link.h"
36 #include "p_vypress.h"
37 #include "p_qchat.h"
38 
39 #define ADDCHAR(ch) do{raw_msg[(raw_len)++]=(ch);}while(0)
40 #define ADDSTR(s) do{\
41 	if(s==NULL){free(raw_msg);errno=ENOMSG;return NULL;}	\
42 	memcpy(raw_msg+raw_len,(s),slen=strlen(s)+1);	\
43 	raw_len+=slen;				\
44 	}while(0)
45 #define ADDCHAN(s) do{ADDCHAR('#');ADDSTR(s);}while(0)
46 
qcs__make_vypress_msg(const qcs_msg * msg,ssize_t * pmsg_len)47 char * qcs__make_vypress_msg(
48 	const qcs_msg * msg,
49 	ssize_t * pmsg_len )
50 {
51 	ssize_t raw_len = 0, slen;
52 	char * msg_buf,
53 		* raw_msg = (char *)malloc(QCP_MAXUDPSIZE);
54 
55 	if( raw_msg==NULL ) {
56 		errno = ENOMEM;
57 		return NULL;
58 	}
59 
60 	switch(msg->msg) {
61 		/* process differences of qc16 and vypresschat10 */
62 	case QCS_MSG_TOPIC_CHANGE:
63 		ADDCHAR('B');
64 		ADDCHAN(msg->chan);
65 		ADDSTR(msg->text);
66 		break;
67 
68 	case QCS_MSG_TOPIC_REPLY:
69 		ADDCHAR('C');
70 		ADDSTR(msg->dst);
71 		ADDCHAN(msg->chan);
72 		ADDSTR(msg->text);
73 		break;
74 
75 	case QCS_MSG_INFO_REPLY:
76 		ADDCHAR('G');
77 		ADDSTR(msg->dst);
78 		ADDSTR(msg->src);
79 		ADDSTR(msg->text);
80 		ADDSTR("(Unknown)");
81 		ADDSTR("(Unknown)");
82 		ADDSTR(msg->chan);
83 		ADDSTR(msg->supp);
84 		break;
85 	default:
86 		/* fallback to qc16 message format */
87 		free(raw_msg);
88 		raw_msg = (char*)qcs__make_qchat_msg(msg, &raw_len);
89 		break;
90 	}
91 
92 	/* allocate buffer for final version */
93 	msg_buf = malloc(QCP_MAXUDPSIZE+10);
94 	if( msg_buf==NULL ) {
95 		free(raw_msg);
96 		errno = ENOMEM;
97 		return NULL;
98 	}
99 
100 	/* append msg id and register for duplicates */
101 	qcs__generate_signature( msg_buf );
102 
103 	/* build up final "version" */
104 	memcpy(msg_buf+10, raw_msg, raw_len);
105 	if( pmsg_len ) {
106 		*pmsg_len = raw_len + 10;
107 	}
108 
109 	free(raw_msg);
110 
111 	return msg_buf;
112 }
113 
114 #define GETCHAR(c) do {\
115 	if(!src_len){qcs__cleanupmsg(msg);errno=ENOMSG;return 0;}\
116 	(c)=*(src++);	\
117 	src_len--;	\
118 	}while(0)
119 #define GETSTR(sp) do {\
120 	if(((sp)=qcs__gatherstr(&src,&src_len))==NULL) {\
121 		errno=ENOMSG;		\
122 		qcs__cleanupmsg(msg);	\
123 		return 0;}	\
124 	}while(0)
125 #define GETCHAN(s) do{\
126 	GETCHAR(ch);if(ch!='#'){errno=ENOMSG;qcs__cleanupmsg(msg);return 0;}\
127 	GETSTR(s);} while(0)
128 
qcs__parse_vypress_msg(const char * src,ssize_t src_len,qcs_msg * msg)129 int qcs__parse_vypress_msg(
130 	const char * src, ssize_t src_len,
131 	qcs_msg * msg )
132 {
133 	int parsed_ok;
134 	char ch;
135 
136 	qcs__cleanupmsg(msg);
137 
138 	/* check for signature consistency and duplicates */
139 	if( src_len < (QCS_SIGNATURE_LENGTH + 2) ) {
140 		/* not a vypress extension packet */
141 		errno = ENOMSG;
142 		return 0;
143 	}
144 
145 	if(*src!='X') {
146 		/* every vypress chat packet begins with 'X' prefix */
147 		return 0;
148 	}
149 
150 	if( qcs__known_dup_entry(src+1)) {
151 		/* duplicate encountered: ignore */
152 		return 1;
153 	} else {
154 		/* not seen before: register */
155 		qcs__insert_dup_entry(src+1);
156 	}
157 
158 	/* skip signature: already parsed */
159 	src += QCS_SIGNATURE_LENGTH + 1;
160 	src_len -= QCS_SIGNATURE_LENGTH + 1;
161 
162 	/* parse message contents */
163 	switch( *(src++) )
164 	{
165 	case 'B':
166 		msg->msg = QCS_MSG_TOPIC_CHANGE;
167 		GETCHAN(msg->chan);
168 		GETSTR(msg->text);
169 		break;
170 	case 'C':
171 		msg->msg = QCS_MSG_TOPIC_REPLY;
172 		GETSTR(msg->dst);
173 		GETCHAN(msg->chan);
174 		GETSTR(msg->text);
175 		break;
176 	case 'G':
177 		msg->msg = QCS_MSG_INFO_REPLY;
178 		GETSTR(msg->dst);
179 		GETSTR(msg->src);
180 		GETSTR(msg->text);
181 		GETSTR(msg->chan);free(msg->chan);
182 		GETSTR(msg->chan);free(msg->chan);
183 		GETSTR(msg->chan);
184 		GETSTR(msg->supp);
185 		break;
186 	default:
187 		/* where it doesn't diff: parse with qc parser */
188 		parsed_ok = qcs__parse_qchat_msg(src-1, src_len, msg);
189 
190 		/* flush msg id cache if the user has left the net
191 		 * (seems like, vypress chat v1.0 repeats the same
192 		 * msg id, if restarted: unseeded rand() ??)
193 		 */
194 		if(parsed_ok && msg->msg==QCS_MSG_CHANNEL_LEAVE
195 			&& !strcasecmp(msg->chan, "main"))
196 		{
197 			qcs__cleanup_dup();
198 		}
199 
200 		return parsed_ok;
201 	}
202 
203 	return 1;
204 }
205