]>
Commit | Line | Data |
---|---|---|
9de3ca7e | 1 | /* |
37ee990e | 2 | * Handle ChatNav. |
9de3ca7e | 3 | * |
37ee990e | 4 | * [The ChatNav(igation) service does various things to keep chat |
5 | * alive. It provides room information, room searching and creating, | |
6 | * as well as giving users the right ("permission") to use chat.] | |
9de3ca7e | 7 | * |
8 | */ | |
9 | ||
37ee990e | 10 | #define FAIM_INTERNAL |
dd60ff8b | 11 | #include <aim.h> |
9de3ca7e | 12 | |
0c20631f | 13 | /* |
14 | * conn must be a chatnav connection! | |
15 | */ | |
78b3fb13 | 16 | faim_export unsigned long aim_chatnav_reqrights(struct aim_session_t *sess, |
17 | struct aim_conn_t *conn) | |
0c20631f | 18 | { |
0c20631f | 19 | |
79e0a001 | 20 | return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002); |
0c20631f | 21 | } |
22 | ||
78b3fb13 | 23 | faim_export unsigned long aim_chatnav_clientready(struct aim_session_t *sess, |
24 | struct aim_conn_t *conn) | |
0c20631f | 25 | { |
5b79dc93 | 26 | struct command_tx_struct *newpacket; |
0c20631f | 27 | int i; |
28 | ||
646c6b52 | 29 | if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 0x20))) |
5b79dc93 | 30 | return -1; |
0c20631f | 31 | |
5b79dc93 | 32 | newpacket->lock = 1; |
0c20631f | 33 | |
5b79dc93 | 34 | i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid); |
0c20631f | 35 | |
5b79dc93 | 36 | i+= aimutil_put16(newpacket->data+i, 0x000d); |
37 | i+= aimutil_put16(newpacket->data+i, 0x0001); | |
0c20631f | 38 | |
5b79dc93 | 39 | i+= aimutil_put16(newpacket->data+i, 0x0004); |
40 | i+= aimutil_put16(newpacket->data+i, 0x0001); | |
0c20631f | 41 | |
5b79dc93 | 42 | i+= aimutil_put16(newpacket->data+i, 0x0001); |
43 | i+= aimutil_put16(newpacket->data+i, 0x0003); | |
0c20631f | 44 | |
5b79dc93 | 45 | i+= aimutil_put16(newpacket->data+i, 0x0004); |
46 | i+= aimutil_put16(newpacket->data+i, 0x0686); | |
47 | ||
48 | aim_tx_enqueue(sess, newpacket); | |
0c20631f | 49 | |
50 | return (sess->snac_nextid++); | |
51 | } | |
52 | ||
78b3fb13 | 53 | faim_export unsigned long aim_chatnav_createroom(struct aim_session_t *sess, |
54 | struct aim_conn_t *conn, | |
55 | char *name, | |
56 | u_short exchange) | |
0c20631f | 57 | { |
5b79dc93 | 58 | struct command_tx_struct *newpacket; |
0c20631f | 59 | int i; |
0c20631f | 60 | |
646c6b52 | 61 | if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+12+strlen("invite")+strlen(name)))) |
5b79dc93 | 62 | return -1; |
0c20631f | 63 | |
5b79dc93 | 64 | newpacket->lock = 1; |
0c20631f | 65 | |
5b79dc93 | 66 | i = aim_putsnac(newpacket->data, 0x000d, 0x0008, 0x0000, sess->snac_nextid); |
0c20631f | 67 | |
68 | /* exchange */ | |
5b79dc93 | 69 | i+= aimutil_put16(newpacket->data+i, exchange); |
0c20631f | 70 | |
71 | /* room cookie */ | |
5b79dc93 | 72 | i+= aimutil_put8(newpacket->data+i, strlen("invite")); |
73 | i+= aimutil_putstr(newpacket->data+i, "invite", strlen("invite")); | |
0c20631f | 74 | |
75 | /* instance */ | |
5b79dc93 | 76 | i+= aimutil_put16(newpacket->data+i, 0xffff); |
0c20631f | 77 | |
78 | /* detail level */ | |
5b79dc93 | 79 | i+= aimutil_put8(newpacket->data+i, 0x01); |
0c20631f | 80 | |
81 | /* tlvcount */ | |
5b79dc93 | 82 | i+= aimutil_put16(newpacket->data+i, 0x0001); |
0c20631f | 83 | |
84 | /* room name */ | |
5b79dc93 | 85 | i+= aim_puttlv_str(newpacket->data+i, 0x00d3, strlen(name), name); |
0c20631f | 86 | |
1ea867e3 | 87 | aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0); |
0c20631f | 88 | |
5b79dc93 | 89 | aim_tx_enqueue(sess, newpacket); |
0c20631f | 90 | |
1ea867e3 | 91 | return sess->snac_nextid; |
0c20631f | 92 | } |
00ef5271 | 93 | |
94 | static int parseinfo_perms(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen, struct aim_snac_t *snac2) | |
95 | { | |
e677fc43 | 96 | aim_rxcallback_t userfunc; |
00ef5271 | 97 | int ret = 0; |
98 | struct aim_tlvlist_t *tlvlist; | |
99 | struct aim_chat_exchangeinfo *exchanges = NULL; | |
100 | int curexchange = 0; | |
101 | struct aim_tlv_t *exchangetlv; | |
102 | unsigned char maxrooms = 0; | |
103 | struct aim_tlvlist_t *innerlist; | |
104 | ||
105 | tlvlist = aim_readtlvchain(data, datalen); | |
106 | ||
107 | /* | |
108 | * Type 0x0002: Maximum concurrent rooms. | |
109 | */ | |
110 | if (aim_gettlv(tlvlist, 0x0002, 1)) | |
111 | maxrooms = aim_gettlv8(tlvlist, 0x0002, 1); | |
112 | ||
113 | /* | |
114 | * Type 0x0003: Exchange information | |
115 | * | |
116 | * There can be any number of these, each one | |
117 | * representing another exchange. | |
118 | * | |
119 | */ | |
120 | curexchange = 0; | |
121 | while ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1))) { | |
122 | curexchange++; | |
123 | exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo)); | |
124 | /* exchange number */ | |
125 | exchanges[curexchange-1].number = aimutil_get16(exchangetlv->value); | |
126 | innerlist = aim_readtlvchain(exchangetlv->value+2, exchangetlv->length-2); | |
127 | ||
128 | /* | |
129 | * Type 0x000d: Unknown. | |
130 | */ | |
131 | if (aim_gettlv(innerlist, 0x000d, 1)) | |
132 | ; | |
133 | ||
134 | /* | |
135 | * Type 0x0004: Unknown | |
136 | */ | |
137 | if (aim_gettlv(innerlist, 0x0004, 1)) | |
138 | ; | |
139 | ||
140 | /* | |
141 | * Type 0x0002: Unknown | |
142 | */ | |
143 | if (aim_gettlv(innerlist, 0x0002, 1)) { | |
144 | unsigned short classperms; | |
145 | ||
146 | classperms = aim_gettlv16(innerlist, 0x0002, 1); | |
147 | ||
148 | faimdprintf(sess, 1, "faim: class permissions %x\n", classperms); | |
149 | } | |
150 | ||
151 | /* | |
152 | * Type 0x00c9: Unknown | |
153 | */ | |
154 | if (aim_gettlv(innerlist, 0x00c9, 1)) | |
155 | ; | |
156 | ||
157 | /* | |
158 | * Type 0x00ca: Creation Date | |
159 | */ | |
160 | if (aim_gettlv(innerlist, 0x00ca, 1)) | |
161 | ; | |
162 | ||
163 | /* | |
164 | * Type 0x00d0: Mandatory Channels? | |
165 | */ | |
166 | if (aim_gettlv(innerlist, 0x00d0, 1)) | |
167 | ; | |
168 | ||
169 | /* | |
170 | * Type 0x00d1: Maximum Message length | |
171 | */ | |
172 | if (aim_gettlv(innerlist, 0x00d1, 1)) | |
173 | ; | |
174 | ||
175 | /* | |
176 | * Type 0x00d2: Maximum Occupancy? | |
177 | */ | |
178 | if (aim_gettlv(innerlist, 0x00d2, 1)) | |
179 | ; | |
180 | ||
181 | /* | |
182 | * Type 0x00d3: Exchange Name | |
183 | */ | |
184 | if (aim_gettlv(innerlist, 0x00d3, 1)) | |
185 | exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1); | |
186 | else | |
187 | exchanges[curexchange-1].name = NULL; | |
188 | ||
189 | /* | |
190 | * Type 0x00d5: Creation Permissions | |
191 | * | |
192 | * 0 Creation not allowed | |
193 | * 1 Room creation allowed | |
194 | * 2 Exchange creation allowed | |
195 | * | |
196 | */ | |
197 | if (aim_gettlv(innerlist, 0x00d5, 1)) { | |
198 | unsigned char createperms; | |
199 | ||
200 | createperms = aim_gettlv8(innerlist, 0x00d5, 1); | |
201 | } | |
202 | ||
203 | /* | |
204 | * Type 0x00d6: Character Set (First Time) | |
205 | */ | |
206 | if (aim_gettlv(innerlist, 0x00d6, 1)) | |
207 | exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1); | |
208 | else | |
209 | exchanges[curexchange-1].charset1 = NULL; | |
210 | ||
211 | /* | |
212 | * Type 0x00d7: Language (First Time) | |
213 | */ | |
214 | if (aim_gettlv(innerlist, 0x00d7, 1)) | |
215 | exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1); | |
216 | else | |
217 | exchanges[curexchange-1].lang1 = NULL; | |
218 | ||
219 | /* | |
220 | * Type 0x00d8: Character Set (Second Time) | |
221 | */ | |
222 | if (aim_gettlv(innerlist, 0x00d8, 1)) | |
223 | exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1); | |
224 | else | |
225 | exchanges[curexchange-1].charset2 = NULL; | |
226 | ||
227 | /* | |
228 | * Type 0x00d9: Language (Second Time) | |
229 | */ | |
230 | if (aim_gettlv(innerlist, 0x00d9, 1)) | |
231 | exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1); | |
232 | else | |
233 | exchanges[curexchange-1].lang2 = NULL; | |
234 | ||
235 | aim_freetlvchain(&innerlist); | |
236 | } | |
237 | ||
238 | /* | |
239 | * Call client. | |
240 | */ | |
241 | if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) | |
242 | ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges); | |
243 | curexchange--; | |
244 | while(curexchange >= 0) { | |
245 | free(exchanges[curexchange].name); | |
246 | free(exchanges[curexchange].charset1); | |
247 | free(exchanges[curexchange].lang1); | |
248 | free(exchanges[curexchange].charset2); | |
249 | free(exchanges[curexchange].lang2); | |
250 | curexchange--; | |
251 | } | |
252 | free(exchanges); | |
253 | aim_freetlvchain(&tlvlist); | |
254 | ||
255 | return ret; | |
256 | } | |
257 | ||
258 | static int parseinfo_create(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen, struct aim_snac_t *snac2) | |
259 | { | |
e677fc43 | 260 | aim_rxcallback_t userfunc; |
00ef5271 | 261 | struct aim_tlvlist_t *tlvlist, *innerlist; |
262 | char *ck = NULL, *fqcn = NULL, *name = NULL; | |
263 | unsigned short exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0; | |
264 | unsigned long createtime = 0; | |
265 | unsigned char createperms = 0; | |
266 | int i = 0, cklen; | |
267 | struct aim_tlv_t *bigblock; | |
268 | int ret = 0; | |
269 | ||
270 | if (!(tlvlist = aim_readtlvchain(data, datalen))) { | |
271 | faimdprintf(sess, 0, "unable to read top tlv in create room response\n"); | |
272 | return 0; | |
273 | } | |
274 | ||
275 | if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) { | |
276 | faimdprintf(sess, 0, "no bigblock in top tlv in create room response\n"); | |
277 | aim_freetlvchain(&tlvlist); | |
278 | return 0; | |
279 | } | |
280 | ||
281 | exchange = aimutil_get16(bigblock->value+i); | |
282 | i += 2; | |
283 | ||
284 | cklen = aimutil_get8(bigblock->value+i); | |
285 | i++; | |
286 | ||
287 | ck = malloc(cklen+1); | |
288 | memcpy(ck, bigblock->value+i, cklen); | |
289 | ck[cklen] = '\0'; | |
290 | i += cklen; | |
291 | ||
292 | instance = aimutil_get16(bigblock->value+i); | |
293 | i += 2; | |
294 | ||
295 | if (aimutil_get8(bigblock->value+i) != 0x02) { | |
296 | faimdprintf(sess, 0, "unknown detaillevel in create room response (0x%02x)\n", aimutil_get8(bigblock->value+i)); | |
297 | aim_freetlvchain(&tlvlist); | |
298 | free(ck); | |
299 | return 0; | |
300 | } | |
301 | i += 1; | |
302 | ||
303 | unknown = aimutil_get16(bigblock->value+i); | |
304 | i += 2; | |
305 | ||
306 | if (!(innerlist = aim_readtlvchain(bigblock->value+i, bigblock->length-i))) { | |
307 | faimdprintf(sess, 0, "unable to read inner tlv chain in create room response\n"); | |
308 | aim_freetlvchain(&tlvlist); | |
309 | free(ck); | |
310 | return 0; | |
311 | } | |
312 | ||
313 | if (aim_gettlv(innerlist, 0x006a, 1)) | |
314 | fqcn = aim_gettlv_str(innerlist, 0x006a, 1); | |
315 | ||
316 | if (aim_gettlv(innerlist, 0x00c9, 1)) | |
317 | flags = aim_gettlv16(innerlist, 0x00c9, 1); | |
318 | ||
319 | if (aim_gettlv(innerlist, 0x00ca, 1)) | |
320 | createtime = aim_gettlv32(innerlist, 0x00ca, 1); | |
321 | ||
322 | if (aim_gettlv(innerlist, 0x00d1, 1)) | |
323 | maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1); | |
324 | ||
325 | if (aim_gettlv(innerlist, 0x00d2, 1)) | |
326 | maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1); | |
327 | ||
328 | if (aim_gettlv(innerlist, 0x00d3, 1)) | |
329 | name = aim_gettlv_str(innerlist, 0x00d3, 1); | |
330 | ||
331 | if (aim_gettlv(innerlist, 0x00d5, 1)) | |
332 | createperms = aim_gettlv8(innerlist, 0x00d5, 1); | |
333 | ||
334 | if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) | |
335 | ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck); | |
336 | ||
337 | free(ck); | |
338 | free(name); | |
339 | free(fqcn); | |
340 | aim_freetlvchain(&innerlist); | |
341 | aim_freetlvchain(&tlvlist); | |
342 | ||
343 | return ret; | |
344 | } | |
345 | ||
346 | /* | |
347 | * Since multiple things can trigger this callback, | |
348 | * we must lookup the snacid to determine the original | |
349 | * snac subtype that was called. | |
350 | */ | |
351 | static int parseinfo(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen) | |
352 | { | |
353 | struct aim_snac_t *snac2; | |
354 | int ret = 0; | |
355 | ||
356 | if (!(snac2 = aim_remsnac(sess, snac->id))) { | |
357 | faimdprintf(sess, 0, "faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snac->id); | |
358 | return 0; | |
359 | } | |
360 | ||
361 | if (snac2->family != 0x000d) { | |
362 | faimdprintf(sess, 0, "faim: chatnav_parse_info: recieved response that maps to corrupt request! (fam=%04x)\n", snac2->family); | |
363 | return 0; | |
364 | } | |
365 | ||
366 | /* | |
367 | * We now know what the original SNAC subtype was. | |
368 | */ | |
369 | if (snac2->type == 0x0002) /* request chat rights */ | |
370 | ret = parseinfo_perms(sess, mod, rx, snac, data, datalen, snac2); | |
371 | else if (snac2->type == 0x0003) /* request exchange info */ | |
372 | faimdprintf(sess, 0, "chatnav_parse_info: resposne to exchange info\n"); | |
373 | else if (snac2->type == 0x0004) /* request room info */ | |
374 | faimdprintf(sess, 0, "chatnav_parse_info: response to room info\n"); | |
375 | else if (snac2->type == 0x0005) /* request more room info */ | |
376 | faimdprintf(sess, 0, "chatnav_parse_info: response to more room info\n"); | |
377 | else if (snac2->type == 0x0006) /* request occupant list */ | |
378 | faimdprintf(sess, 0, "chatnav_parse_info: response to occupant info\n"); | |
379 | else if (snac2->type == 0x0007) /* search for a room */ | |
380 | faimdprintf(sess, 0, "chatnav_parse_info: search results\n"); | |
381 | else if (snac2->type == 0x0008) /* create room */ | |
382 | ret = parseinfo_create(sess, mod, rx, snac, data, datalen, snac2); | |
383 | else | |
384 | faimdprintf(sess, 0, "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type); | |
385 | ||
386 | if (snac2) | |
387 | free(snac2->data); | |
388 | free(snac2); | |
389 | ||
390 | return ret; | |
391 | } | |
392 | ||
393 | static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen) | |
394 | { | |
395 | ||
396 | if (snac->subtype == 0x0009) | |
397 | return parseinfo(sess, mod, rx, snac, data, datalen); | |
398 | ||
399 | return 0; | |
400 | } | |
401 | ||
402 | faim_internal int chatnav_modfirst(struct aim_session_t *sess, aim_module_t *mod) | |
403 | { | |
404 | ||
405 | mod->family = 0x000d; | |
406 | mod->version = 0x0000; | |
407 | mod->flags = 0; | |
408 | strncpy(mod->name, "chatnav", sizeof(mod->name)); | |
409 | mod->snachandler = snachandler; | |
410 | ||
411 | return 0; | |
412 | } |