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