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