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  *	qchat 1.6 protocol implementation
23  *
24  * (c) Saulius Menkevicius 2001-2004
25  */
26 
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <assert.h>
32 #include <sys/types.h>
33 
34 #include "qcproto.h"
35 #include "supp.h"
36 #include "link.h"
37 #include "p_qchat.h"
38 
39 #define ADDCHAR(ch) do{msg_buf[(*pmsg_len)++]=(ch);}while(0)
40 #define ADDSTR(s) do{\
41 	if(s==NULL){free(msg_buf);errno=ENOMSG;return NULL;}	\
42 	memcpy(msg_buf+*pmsg_len,(s),slen=strlen(s)+1);	\
43 	*pmsg_len+=slen;				\
44 	}while(0)
45 #define ADDCHAN(s) do{ADDCHAR('#');ADDSTR(s);}while(0)
46 
47 char *
qcs__make_qchat_msg(const qcs_msg * msg,ssize_t * pmsg_len)48 qcs__make_qchat_msg(
49 	const qcs_msg * msg,
50 	ssize_t * pmsg_len )
51 {
52 	char * msg_buf;
53 	ssize_t slen;
54 
55 	assert(msg && pmsg_len);
56 
57 	msg_buf = (char*) malloc(QCP_MAXUDPSIZE);
58 	*pmsg_len = 0;
59 	if( msg_buf==NULL ) {
60 		errno = ENOMEM;
61 		return NULL;
62 	}
63 
64 	switch(msg->msg) {
65 	case QCS_MSG_REFRESH_REQUEST:
66 		ADDCHAR('0');
67 		ADDSTR(msg->src);
68 		break;
69 	case QCS_MSG_REFRESH_ACK:
70 		ADDCHAR('1');
71 		ADDSTR(msg->dst);
72 		ADDSTR(msg->src);
73 		ADDCHAR(qcs__net_qcumode(msg->umode));
74 		ADDCHAR('0');
75 		ADDCHAR(qcs__net_qcuactive(msg->uactive));
76 		break;
77 	case QCS_MSG_CHANNEL_BROADCAST:
78 		ADDCHAR('2');
79 		ADDCHAN(msg->chan);
80 		ADDSTR(msg->src);
81 		ADDSTR(msg->text);
82 		break;
83 	case QCS_MSG_CHANNEL_JOIN:
84 		ADDCHAR('4');
85 		ADDSTR(msg->src);
86 		ADDCHAN(msg->chan);
87 		ADDCHAR(qcs__net_qcumode(msg->umode));
88 		ADDCHAR('0');		/* unknown purpose !! */
89 		break;
90 	case QCS_MSG_CHANNEL_LEAVE:
91 		ADDCHAR('5');
92 		ADDSTR(msg->src);
93 		ADDCHAN(msg->chan);
94 		ADDCHAR('0');		/* unknown */
95 		break;
96 	case QCS_MSG_CHANNEL_ME:
97 		ADDCHAR('A');
98 		ADDCHAN(msg->chan);
99 		ADDSTR(msg->src);
100 		ADDSTR(msg->text);
101 		break;
102 	case QCS_MSG_MESSAGE_ACK:
103 		ADDCHAR('7');
104 		ADDCHAR(qcs__net_qcumode(msg->umode));
105 		ADDSTR(msg->dst);
106 		ADDSTR(msg->src);
107 		ADDCHAR('0');		/* unknown purpose */
108 		ADDSTR(msg->text);
109 		break;
110 	case QCS_MSG_MESSAGE_MASS:
111 		ADDCHAR('E');
112 		ADDSTR(msg->src);
113 		ADDSTR(msg->dst);
114 		ADDSTR(msg->text);
115 		break;
116 	case QCS_MSG_MESSAGE_SEND:
117 		ADDCHAR('6');
118 		ADDSTR(msg->src);
119 		ADDSTR(msg->dst);
120 		ADDSTR(msg->text);
121 		break;
122 	case QCS_MSG_RENAME:
123 		ADDCHAR('3');
124 		ADDSTR(msg->src);
125 		ADDSTR(msg->text);
126 		ADDCHAR('0');		/* unknown */
127 		break;
128 	case QCS_MSG_MODE_CHANGE:
129 		ADDCHAR('D');
130 		ADDSTR(msg->src);
131 		ADDCHAR(qcs__net_qcumode(msg->umode));
132 		ADDCHAR('0');		/* unknown */
133 		break;
134 	case QCS_MSG_ACTIVE_CHANGE:
135 		ADDCHAR('M');
136 		ADDSTR(msg->src);
137 		ADDCHAR(qcs__net_qcuactive(msg->uactive));
138 		break;
139 	case QCS_MSG_TOPIC_REPLY:
140 		ADDCHAR('C');
141 		ADDSTR(msg->dst);
142 		ADDSTR(msg->text);
143 		if(msg->chan==NULL || strcasecmp(msg->chan, "Main")) {
144 			free(msg_buf);
145 			errno = ENOMSG;
146 			return NULL;
147 		}
148 		break;
149 	case QCS_MSG_TOPIC_CHANGE:
150 		/* check that the channel is "Main",
151 		 * in QC1.x topic is the same for all channels
152 		 * (while each channel has it's topic in Vypress Chat)
153 		 */
154 		if(msg->chan==NULL || strcasecmp(msg->chan, "Main")) {
155 			free(msg_buf);
156 			errno = ENOMSG;
157 			return NULL;
158 		}
159 
160 		ADDCHAR('B');
161 		ADDSTR(msg->text);
162 		break;
163 	case QCS_MSG_INFO_REPLY:
164 		ADDCHAR('G');
165 		ADDSTR(msg->dst);
166 		ADDSTR(msg->src);
167 		ADDSTR(msg->text);
168 		ADDSTR("(Unknown)");
169 		ADDSTR("0 %");
170 		ADDSTR("0 Kb");
171 		ADDSTR(msg->chan);
172 		ADDSTR(msg->supp);
173 		break;
174 	case QCS_MSG_INFO_REQUEST:
175 		ADDCHAR('F');
176 		ADDSTR(msg->dst);
177 		ADDSTR(msg->src);
178 		break;
179 	case QCS_MSG_CHANMEMBER_REPLY:
180 		ADDCHAR('K');
181 		ADDSTR(msg->dst);
182 		ADDSTR(msg->chan);
183 		ADDSTR(msg->src);
184 		break;
185 	case QCS_MSG_CHANMEMBER_REQUEST:
186 		ADDCHAR('L');
187 		ADDSTR(msg->src);
188 		break;
189 	case QCS_MSG_CHANLIST_REPLY:
190 		ADDCHAR('O');
191 		ADDSTR(msg->dst);
192 		ADDSTR(msg->chan);
193 		break;
194 	case QCS_MSG_CHANLIST_REQUEST:
195 		ADDCHAR('N');
196 		ADDSTR(msg->src);
197 		break;
198 	case QCS_MSG_BEEP_SEND:
199 	case QCS_MSG_BEEP_ACK:
200 		ADDCHAR('H');
201 		ADDCHAR((msg->msg==QCS_MSG_BEEP_ACK)?'1':'0');
202 		ADDSTR(msg->dst);
203 		ADDSTR(msg->src);
204 		if(msg->msg==QCS_MSG_BEEP_ACK) {
205 			ADDCHAR('0');	/* dummy ? */
206 		}
207 		break;
208 	case QCS_MSG_PRIVATE_ME:
209 	case QCS_MSG_PRIVATE_TEXT:
210 		ADDCHAR('J');
211 		ADDCHAR(msg->msg==QCS_MSG_PRIVATE_ME ? '3': '2');
212 		ADDSTR(msg->src);
213 		ADDSTR(msg->dst);
214 		ADDSTR(msg->text);
215 		break;
216 	case QCS_MSG_PRIVATE_OPEN:
217 	case QCS_MSG_PRIVATE_CLOSE:
218 		ADDCHAR('J');
219 		ADDCHAR(msg->msg==QCS_MSG_PRIVATE_CLOSE ? '1': '0');
220 		ADDSTR(msg->src);
221 		ADDSTR(msg->dst);
222 		ADDCHAR('0');		/* dummy/unknown */
223 		break;
224 	default:
225 		errno = ENOMSG;
226 		free(msg_buf);
227 		return NULL;
228 	}
229 
230 	return msg_buf;
231 }
232 
233 #define GETMODE(c) do {\
234 	GETCHAR((c));\
235 	(c)=qcs__local_qcumode(c);\
236 	if((c)==QCS_UMODE_INVALID){\
237 		qcs__cleanupmsg(msg);\
238 		errno=ENOMSG;return 0;}\
239 	}while(0)
240 #define	GETACTIVE(a) do {\
241 	GETCHAR((a));\
242 	(a)=qcs__local_qcuactive(a);\
243 	}while(0)
244 #define GETCHAR(c) do {\
245 	if(!pmsg_len){qcs__cleanupmsg(msg);errno=ENOMSG; return 0;}	\
246 	(c)=*(pmsg++);pmsg_len--;\
247 	}while(0)
248 #define GETSTR(sp) do {\
249 	if(((sp)=qcs__gatherstr(&pmsg,&pmsg_len))==NULL) {\
250 		errno=ENOMSG;\
251 		qcs__cleanupmsg(msg);return 0;}\
252 	}while(0)
253 #define GETCHAN(s) do{\
254 	GETCHAR(ch);if(ch!='#'){errno=ENOMSG;return 0;}\
255 	GETSTR(s);} while(0)
256 
qcs__parse_qchat_msg(const char * pmsg,ssize_t pmsg_len,qcs_msg * msg)257 int qcs__parse_qchat_msg(
258 	const char * pmsg, ssize_t pmsg_len,
259 	qcs_msg * msg )
260 {
261 	char ch;
262 
263 	assert( pmsg && pmsg_len && msg );
264 
265 	qcs__cleanupmsg(msg);
266 
267 	GETCHAR(ch);
268 	switch(ch) {
269 	case '0':
270 		msg->msg = QCS_MSG_REFRESH_REQUEST;
271 		GETSTR(msg->src);
272 		break;
273 	case '1':
274 		msg->msg = QCS_MSG_REFRESH_ACK;
275 		GETSTR(msg->dst);
276 		GETSTR(msg->src);
277 		GETMODE(msg->umode);
278 		if( pmsg_len ) {
279 			/* check for `ACTIVE' bit in qc16 messages */
280 			GETACTIVE(msg->uactive);
281 		} else {
282 			/* uactive=TRUE per default */
283 			msg->uactive = 1;
284 		}
285 		break;
286 	case '2':
287 		msg->msg = QCS_MSG_CHANNEL_BROADCAST;
288 		GETCHAN(msg->chan);
289 		GETSTR(msg->src);
290 		GETSTR(msg->text);
291 		break;
292 	case '4':
293 		msg->msg = QCS_MSG_CHANNEL_JOIN;
294 		GETSTR(msg->src);
295 		GETCHAN(msg->chan);
296 		GETMODE(msg->umode);
297 		/*dummy byte skipped*/
298 		break;
299 	case '5':
300 		msg->msg = QCS_MSG_CHANNEL_LEAVE;
301 		GETSTR(msg->src);
302 		GETCHAN(msg->chan);
303 		/*dummy*/
304 		break;
305 	case 'A':
306 		msg->msg = QCS_MSG_CHANNEL_ME;
307 		GETCHAN(msg->chan);
308 		GETSTR(msg->src);
309 		GETSTR(msg->text);
310 		break;
311 	case '7':
312 		msg->msg = QCS_MSG_MESSAGE_ACK;
313 		GETMODE(msg->umode);
314 		GETSTR(msg->dst);
315 		GETSTR(msg->src);
316 		GETCHAR(ch);	/* dummy */
317 		GETSTR(msg->text);
318 		break;
319 	case 'E':
320 		msg->msg = QCS_MSG_MESSAGE_MASS;
321 		GETSTR(msg->src);
322 		GETSTR(msg->dst);
323 		GETSTR(msg->text);
324 		break;
325 	case '6':
326 		msg->msg = QCS_MSG_MESSAGE_SEND;
327 		GETSTR(msg->src);
328 		GETSTR(msg->dst);
329 		GETSTR(msg->text);
330 		break;
331 	case '3':
332 		msg->msg = QCS_MSG_RENAME;
333 		GETSTR(msg->src);
334 		GETSTR(msg->text);
335 		/*dummy*/
336 		break;
337 	case 'D':
338 		msg->msg = QCS_MSG_MODE_CHANGE;
339 		GETSTR(msg->src);
340 		GETMODE(msg->umode);
341 		/* dummy byte passed by */
342 		break;
343 	case 'M':
344 		msg->msg = QCS_MSG_ACTIVE_CHANGE;
345 		GETSTR(msg->src);
346 		GETACTIVE(msg->uactive);
347 		break;
348 	case 'C':
349 		msg->msg = QCS_MSG_TOPIC_REPLY;
350 		GETSTR(msg->dst);
351 		GETSTR(msg->text);
352 
353 		/* add "Main" as msg->chan */
354 		msg->chan = malloc(5);
355 		if(!msg->chan) {
356 			qcs__cleanupmsg(msg);
357 			errno = ENOMEM;
358 			return 0;
359 		}
360 		memcpy(msg->chan, "Main", 5);
361 		break;
362 	case 'B':
363 		msg->msg = QCS_MSG_TOPIC_CHANGE;
364 		GETSTR(msg->text);
365 
366 		/* fill in "Main": this is unsupported in qc,
367 		 * thus we need to fill it in */
368 		msg->chan = malloc(5);
369 		if(!msg->chan) {
370 			qcs__cleanupmsg(msg);
371 			errno = ENOMEM;
372 			return 0;
373 		}
374 		memcpy(msg->chan, "Main", 5);
375 		break;
376 	case 'G':
377 		msg->msg = QCS_MSG_INFO_REPLY;
378 		GETSTR(msg->dst);
379 		GETSTR(msg->src);
380 		GETSTR(msg->text);
381 		/* skip 3 strings - not used */
382 		GETSTR(msg->chan);free(msg->chan);
383 		GETSTR(msg->chan);free(msg->chan);
384 		GETSTR(msg->chan);free(msg->chan);
385 		GETSTR(msg->chan);
386 		GETSTR(msg->supp);
387 		break;
388 	case 'F':
389 		msg->msg = QCS_MSG_INFO_REQUEST;
390 		GETSTR(msg->dst);
391 		GETSTR(msg->src);
392 		break;
393 	case 'K':
394 		msg->msg = QCS_MSG_CHANMEMBER_REPLY;
395 		GETSTR(msg->dst);
396 		GETSTR(msg->chan);
397 		GETSTR(msg->src);
398 		break;
399 	case 'L':
400 		msg->msg = QCS_MSG_CHANMEMBER_REQUEST;
401 		GETSTR(msg->src);
402 		break;
403 	case 'O':
404 		msg->msg = QCS_MSG_CHANLIST_REPLY;
405 		GETSTR(msg->dst);
406 		GETSTR(msg->chan);
407 		break;
408 	case 'N':
409 		msg->msg = QCS_MSG_CHANLIST_REQUEST;
410 		GETSTR(msg->src);
411 		break;
412 	case 'H':
413 		GETCHAR(ch);
414 		switch(ch) {
415 		case '1': msg->msg = QCS_MSG_BEEP_ACK;	break;
416 		case '0': msg->msg = QCS_MSG_BEEP_SEND;	break;
417 		default: errno = ENOMSG;
418 			 return 0;
419 		}
420 		GETSTR(msg->dst);
421 		GETSTR(msg->src);
422 		/* dummy byte when 'H1' */
423 		break;
424 	case 'J':
425 		GETCHAR(ch);
426 		switch(ch) {
427 		case '0':msg->msg = QCS_MSG_PRIVATE_OPEN;	break;
428 		case '1':msg->msg = QCS_MSG_PRIVATE_CLOSE;	break;
429 		case '2':msg->msg = QCS_MSG_PRIVATE_TEXT;	break;
430 		case '3':msg->msg = QCS_MSG_PRIVATE_ME;		break;
431 		default: errno = ENOMSG;
432 			 return 0;
433 		}
434 		GETSTR(msg->src);
435 		GETSTR(msg->dst);
436 		if(ch=='2'||ch=='3') {
437 			GETSTR(msg->text);
438 		}
439 		break;
440 	default:
441 		errno = ENOMSG;
442 		return 0;
443 	}
444 	return 1;
445 }
446