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