]>
Commit | Line | Data |
---|---|---|
9de3ca7e | 1 | /* |
2 | * aim_im.c | |
3 | * | |
4 | * The routines for sending/receiving Instant Messages. | |
5 | * | |
6 | */ | |
7 | ||
a25832e6 | 8 | #include <faim/aim.h> |
9de3ca7e | 9 | |
10 | /* | |
11 | * Send an ICBM (instant message). | |
12 | * | |
13 | * | |
14 | * Possible flags: | |
15 | * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse | |
16 | * AIM_IMFLAGS_ACK -- Requests that the server send an ack | |
17 | * when the message is received (of type 0x0004/0x000c) | |
18 | * | |
9de3ca7e | 19 | */ |
a25832e6 | 20 | u_long aim_send_im(struct aim_session_t *sess, |
21 | struct aim_conn_t *conn, | |
22 | char *destsn, u_int flags, char *msg) | |
9de3ca7e | 23 | { |
24 | ||
49c8a2fa | 25 | int curbyte,i; |
5b79dc93 | 26 | struct command_tx_struct *newpacket; |
9de3ca7e | 27 | |
91931247 | 28 | if (strlen(msg) >= MAXMSGLEN) |
29 | return -1; | |
30 | ||
b69540e3 | 31 | if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, strlen(msg)+256))) |
5b79dc93 | 32 | return -1; |
9de3ca7e | 33 | |
5b79dc93 | 34 | newpacket->lock = 1; /* lock struct */ |
9de3ca7e | 35 | |
36 | curbyte = 0; | |
5b79dc93 | 37 | curbyte += aim_putsnac(newpacket->data+curbyte, |
a25832e6 | 38 | 0x0004, 0x0006, 0x0000, sess->snac_nextid); |
9de3ca7e | 39 | |
49c8a2fa | 40 | /* |
41 | * Generate a random message cookie | |
a25832e6 | 42 | * |
43 | * We could cache these like we do SNAC IDs. (In fact, it | |
44 | * might be a good idea.) In the message error functions, | |
45 | * the 8byte message cookie is returned as well as the | |
46 | * SNAC ID. | |
47 | * | |
49c8a2fa | 48 | */ |
49 | for (i=0;i<8;i++) | |
5b79dc93 | 50 | curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) random()); |
9de3ca7e | 51 | |
49c8a2fa | 52 | /* |
53 | * Channel ID | |
54 | */ | |
5b79dc93 | 55 | curbyte += aimutil_put16(newpacket->data+curbyte,0x0001); |
9de3ca7e | 56 | |
49c8a2fa | 57 | /* |
58 | * Destination SN (prepended with byte length) | |
59 | */ | |
5b79dc93 | 60 | curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn)); |
61 | curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); | |
9de3ca7e | 62 | |
49c8a2fa | 63 | /* |
64 | * metaTLV start. | |
65 | */ | |
5b79dc93 | 66 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); |
67 | curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x0d); | |
9de3ca7e | 68 | |
49c8a2fa | 69 | /* |
70 | * Flag data? | |
71 | */ | |
5b79dc93 | 72 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0501); |
73 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); | |
74 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0101); | |
75 | curbyte += aimutil_put8 (newpacket->data+curbyte, 0x01); | |
9de3ca7e | 76 | |
49c8a2fa | 77 | /* |
78 | * Message block length. | |
79 | */ | |
5b79dc93 | 80 | curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x04); |
9de3ca7e | 81 | |
49c8a2fa | 82 | /* |
83 | * Character set data? | |
84 | */ | |
5b79dc93 | 85 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); |
86 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
9de3ca7e | 87 | |
49c8a2fa | 88 | /* |
89 | * Message. Not terminated. | |
90 | */ | |
5b79dc93 | 91 | curbyte += aimutil_putstr(newpacket->data+curbyte,msg, strlen(msg)); |
9de3ca7e | 92 | |
49c8a2fa | 93 | /* |
94 | * Set the Request Acknowledge flag. | |
95 | */ | |
5b79dc93 | 96 | if (flags & AIM_IMFLAGS_ACK) { |
97 | curbyte += aimutil_put16(newpacket->data+curbyte,0x0003); | |
98 | curbyte += aimutil_put16(newpacket->data+curbyte,0x0000); | |
99 | } | |
49c8a2fa | 100 | |
101 | /* | |
102 | * Set the Autoresponse flag. | |
103 | */ | |
5b79dc93 | 104 | if (flags & AIM_IMFLAGS_AWAY) { |
105 | curbyte += aimutil_put16(newpacket->data+curbyte,0x0004); | |
106 | curbyte += aimutil_put16(newpacket->data+curbyte,0x0000); | |
107 | } | |
49c8a2fa | 108 | |
5b79dc93 | 109 | newpacket->commandlen = curbyte; |
110 | newpacket->lock = 0; | |
9de3ca7e | 111 | |
5b79dc93 | 112 | aim_tx_enqueue(sess, newpacket); |
49c8a2fa | 113 | |
9de3ca7e | 114 | #ifdef USE_SNAC_FOR_IMS |
115 | { | |
116 | struct aim_snac_t snac; | |
117 | ||
a25832e6 | 118 | snac.id = sess->snac_nextid; |
9de3ca7e | 119 | snac.family = 0x0004; |
120 | snac.type = 0x0006; | |
121 | snac.flags = 0x0000; | |
122 | ||
123 | snac.data = malloc(strlen(destsn)+1); | |
124 | memcpy(snac.data, destsn, strlen(destsn)+1); | |
125 | ||
a25832e6 | 126 | aim_newsnac(sess, &snac); |
9de3ca7e | 127 | } |
128 | ||
a25832e6 | 129 | aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */ |
9de3ca7e | 130 | #endif |
131 | ||
a25832e6 | 132 | return (sess->snac_nextid++); |
9de3ca7e | 133 | } |
134 | ||
b69540e3 | 135 | struct aim_directim_priv { |
136 | unsigned char cookie[8]; | |
137 | char sn[MAXSNLEN+1]; | |
138 | }; | |
139 | ||
140 | int aim_send_im_direct(struct aim_session_t *sess, | |
141 | struct aim_conn_t *conn, | |
142 | char *msg) | |
143 | { | |
144 | struct command_tx_struct *newpacket; | |
145 | struct aim_directim_priv *priv = NULL; | |
146 | int i; | |
147 | ||
148 | if (strlen(msg) >= MAXMSGLEN) | |
149 | return -1; | |
150 | ||
151 | if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !conn->priv) { | |
152 | printf("faim: directim: invalid arguments\n"); | |
153 | return -1; | |
154 | } | |
155 | ||
156 | if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0001, conn, strlen(msg)))) { | |
157 | printf("faim: directim: tx_new failed\n"); | |
158 | return -1; | |
159 | } | |
160 | ||
161 | newpacket->lock = 1; /* lock struct */ | |
162 | ||
163 | priv = (struct aim_directim_priv *)conn->priv; | |
164 | ||
165 | newpacket->hdr.oft.hdr2len = 0x44; | |
166 | ||
167 | if (!(newpacket->hdr.oft.hdr2 = malloc(newpacket->hdr.oft.hdr2len))) { | |
168 | free(newpacket); | |
169 | return -1; | |
170 | } | |
171 | ||
172 | i = 0; | |
173 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0006); | |
174 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
175 | ||
176 | i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, priv->cookie, 8); | |
177 | ||
178 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
179 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
180 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
181 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
182 | ||
183 | i += aimutil_put32(newpacket->hdr.oft.hdr2+i, strlen(msg)); | |
184 | ||
185 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
186 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
187 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
188 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
189 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
190 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
191 | ||
192 | i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, sess->logininfo.screen_name, strlen(sess->logininfo.screen_name)); | |
193 | ||
194 | i = 52; | |
195 | i += aimutil_put8(newpacket->hdr.oft.hdr2+i, 0x00); | |
196 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
197 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
198 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
199 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
200 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
201 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
202 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
203 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
204 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
205 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
206 | i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
207 | ||
208 | memcpy(newpacket->data, msg, strlen(msg)); | |
209 | ||
210 | newpacket->lock = 0; | |
211 | ||
212 | aim_tx_enqueue(sess, newpacket); | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
49c8a2fa | 217 | /* |
218 | * It can easily be said that parsing ICBMs is THE single | |
219 | * most difficult thing to do in the in AIM protocol. In | |
220 | * fact, I think I just did say that. | |
221 | * | |
222 | * Below is the best damned solution I've come up with | |
223 | * over the past sixteen months of battling with it. This | |
224 | * can parse both away and normal messages from every client | |
225 | * I have access to. Its not fast, its not clean. But it works. | |
226 | * | |
227 | * We should also support at least minimal parsing of | |
228 | * Channel 2, so that we can at least know the name of the | |
229 | * room we're invited to, but obviously can't attend... | |
230 | * | |
231 | */ | |
a25832e6 | 232 | int aim_parse_incoming_im_middle(struct aim_session_t *sess, |
233 | struct command_rx_struct *command) | |
9de3ca7e | 234 | { |
26af6789 | 235 | u_int i = 0,z; |
9de3ca7e | 236 | rxcallback_t userfunc = NULL; |
49c8a2fa | 237 | u_char cookie[8]; |
238 | int channel; | |
239 | struct aim_tlvlist_t *tlvlist; | |
26af6789 | 240 | struct aim_userinfo_s userinfo; |
49c8a2fa | 241 | u_short wastebits; |
49c8a2fa | 242 | |
95d7332a | 243 | memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s)); |
244 | ||
49c8a2fa | 245 | i = 10; /* Skip SNAC header */ |
246 | ||
9de3ca7e | 247 | /* |
49c8a2fa | 248 | * Read ICBM Cookie. And throw away. |
9de3ca7e | 249 | */ |
49c8a2fa | 250 | for (z=0; z<8; z++,i++) |
251 | cookie[z] = command->data[i]; | |
9de3ca7e | 252 | |
49c8a2fa | 253 | /* |
254 | * Channel ID. | |
255 | * | |
256 | * Channel 0x0001 is the message channel. There are | |
257 | * other channels for things called "rendevous" | |
258 | * which represent chat and some of the other new | |
26af6789 | 259 | * features of AIM2/3/3.5. |
260 | * | |
261 | * Channel 0x0002 is the Rendevous channel, which | |
50443ea0 | 262 | * is where Chat Invitiations and various client-client |
263 | * connection negotiations come from. | |
26af6789 | 264 | * |
49c8a2fa | 265 | */ |
266 | channel = aimutil_get16(command->data+i); | |
9de3ca7e | 267 | i += 2; |
26af6789 | 268 | |
269 | /* | |
270 | * | |
271 | */ | |
272 | if ((channel != 0x01) && (channel != 0x02)) | |
9de3ca7e | 273 | { |
49c8a2fa | 274 | printf("faim: icbm: ICBM received on an unsupported channel. Ignoring.\n (chan = %04x)", channel); |
275 | return 1; | |
9de3ca7e | 276 | } |
277 | ||
49c8a2fa | 278 | /* |
279 | * Source screen name. | |
280 | */ | |
281 | memcpy(userinfo.sn, command->data+i+1, (int)command->data[i]); | |
282 | userinfo.sn[(int)command->data[i]] = '\0'; | |
283 | i += 1 + (int)command->data[i]; | |
95d7332a | 284 | |
49c8a2fa | 285 | /* |
26af6789 | 286 | * Warning Level |
49c8a2fa | 287 | */ |
26af6789 | 288 | userinfo.warnlevel = aimutil_get16(command->data+i); /* guess */ |
49c8a2fa | 289 | i += 2; |
26af6789 | 290 | |
291 | /* | |
292 | * Number of TLVs that follow. Not needed. | |
293 | */ | |
49c8a2fa | 294 | wastebits = aimutil_get16(command->data+i); |
9de3ca7e | 295 | i += 2; |
26af6789 | 296 | |
49c8a2fa | 297 | /* |
298 | * Read block of TLVs. All further data is derived | |
299 | * from what is parsed here. | |
300 | */ | |
301 | tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); | |
9de3ca7e | 302 | |
49c8a2fa | 303 | /* |
26af6789 | 304 | * From here on, its depends on what channel we're on. |
49c8a2fa | 305 | */ |
26af6789 | 306 | if (channel == 1) |
49c8a2fa | 307 | { |
26af6789 | 308 | u_int j = 0, y = 0, z = 0; |
309 | char *msg = NULL; | |
310 | u_int icbmflags = 0; | |
311 | struct aim_tlv_t *msgblocktlv, *tmptlv; | |
312 | u_char *msgblock; | |
313 | u_short flag1,flag2; | |
95d7332a | 314 | |
26af6789 | 315 | /* |
316 | * Check Autoresponse status. If it is an autoresponse, | |
317 | * it will contain a second type 0x0004 TLV, with zero length. | |
318 | */ | |
319 | if (aim_gettlv(tlvlist, 0x0004, 2)) | |
320 | icbmflags |= AIM_IMFLAGS_AWAY; | |
321 | ||
322 | /* | |
323 | * Check Ack Request status. | |
324 | */ | |
325 | if (aim_gettlv(tlvlist, 0x0003, 2)) | |
326 | icbmflags |= AIM_IMFLAGS_ACK; | |
327 | ||
328 | /* | |
329 | * Extract the various pieces of the userinfo struct. | |
330 | */ | |
331 | /* Class. */ | |
332 | if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1))) | |
333 | userinfo.class = aimutil_get16(tmptlv->value); | |
334 | /* Member-since date. */ | |
335 | if ((tmptlv = aim_gettlv(tlvlist, 0x0002, 1))) | |
336 | { | |
337 | /* If this is larger than 4, its probably the message block, skip */ | |
338 | if (tmptlv->length <= 4) | |
339 | userinfo.membersince = aimutil_get32(tmptlv->value); | |
340 | } | |
341 | /* On-since date */ | |
342 | if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1))) | |
343 | userinfo.onlinesince = aimutil_get32(tmptlv->value); | |
344 | /* Idle-time */ | |
345 | if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1))) | |
346 | userinfo.idletime = aimutil_get16(tmptlv->value); | |
347 | /* Session Length (AIM) */ | |
348 | if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1))) | |
349 | userinfo.sessionlen = aimutil_get16(tmptlv->value); | |
350 | /* Session Length (AOL) */ | |
351 | if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1))) | |
352 | userinfo.sessionlen = aimutil_get16(tmptlv->value); | |
353 | ||
354 | /* | |
355 | * Message block. | |
356 | * | |
357 | * XXX: Will the msgblock always be the second 0x0002? | |
358 | */ | |
359 | msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1); | |
360 | if (!msgblocktlv) | |
361 | { | |
362 | printf("faim: icbm: major error! no message block TLV found!\n"); | |
363 | aim_freetlvchain(&tlvlist); | |
e6b05d80 | 364 | return 1; |
26af6789 | 365 | } |
366 | ||
367 | /* | |
368 | * Extracting the message from the unknown cruft. | |
369 | * | |
370 | * This is a bit messy, and I'm not really qualified, | |
371 | * even as the author, to comment on it. At least | |
372 | * its not as bad as a while loop shooting into infinity. | |
373 | * | |
374 | * "Do you believe in magic?" | |
375 | * | |
376 | */ | |
377 | msgblock = msgblocktlv->value; | |
378 | j = 0; | |
379 | ||
380 | wastebits = aimutil_get8(msgblock+j++); | |
381 | wastebits = aimutil_get8(msgblock+j++); | |
382 | ||
383 | y = aimutil_get16(msgblock+j); | |
384 | j += 2; | |
385 | for (z = 0; z < y; z++) | |
386 | wastebits = aimutil_get8(msgblock+j++); | |
387 | wastebits = aimutil_get8(msgblock+j++); | |
388 | wastebits = aimutil_get8(msgblock+j++); | |
389 | ||
390 | /* | |
391 | * Message string length, including flag words. | |
392 | */ | |
393 | i = aimutil_get16(msgblock+j); | |
394 | j += 2; | |
395 | ||
396 | /* | |
397 | * Flag words. | |
398 | * | |
399 | * Its rumored that these can kick in some funky | |
400 | * 16bit-wide char stuff that used to really kill | |
401 | * libfaim. Hopefully the latter is no longer true. | |
402 | * | |
403 | * Though someone should investiagte the former. | |
404 | * | |
405 | */ | |
406 | flag1 = aimutil_get16(msgblock+j); | |
407 | j += 2; | |
408 | flag2 = aimutil_get16(msgblock+j); | |
409 | j += 2; | |
410 | ||
411 | if (flag1 || flag2) | |
412 | printf("faim: icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2); | |
413 | ||
414 | /* | |
415 | * Message string. | |
416 | */ | |
417 | i -= 4; | |
418 | msg = (char *)malloc(i+1); | |
419 | memcpy(msg, msgblock+j, i); | |
420 | msg[i] = '\0'; | |
421 | ||
422 | /* | |
423 | * Call client. | |
424 | */ | |
425 | userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); | |
426 | if (userfunc) | |
427 | i = userfunc(sess, command, channel, &userinfo, msg, icbmflags, flag1, flag2); | |
428 | else | |
429 | i = 0; | |
430 | ||
431 | free(msg); | |
49c8a2fa | 432 | } |
26af6789 | 433 | else if (channel == 0x0002) |
0c20631f | 434 | { |
26af6789 | 435 | struct aim_tlv_t *block1; |
436 | struct aim_tlvlist_t *list2; | |
437 | struct aim_tlv_t *tmptlv; | |
040457cc | 438 | unsigned short reqclass = 0; |
b69540e3 | 439 | unsigned short status = 0; |
26af6789 | 440 | |
441 | /* Class. */ | |
442 | if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1))) | |
443 | userinfo.class = aimutil_get16(tmptlv->value); | |
444 | /* On-since date */ | |
445 | if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1))) | |
446 | userinfo.onlinesince = aimutil_get32(tmptlv->value); | |
447 | /* Idle-time */ | |
448 | if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1))) | |
449 | userinfo.idletime = aimutil_get16(tmptlv->value); | |
450 | /* Session Length (AIM) */ | |
451 | if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1))) | |
452 | userinfo.sessionlen = aimutil_get16(tmptlv->value); | |
453 | /* Session Length (AOL) */ | |
454 | if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1))) | |
455 | userinfo.sessionlen = aimutil_get16(tmptlv->value); | |
456 | ||
457 | /* | |
458 | * There's another block of TLVs embedded in the type 5 here. | |
459 | */ | |
460 | block1 = aim_gettlv(tlvlist, 0x0005, 1); | |
040457cc | 461 | if (!block1) { |
462 | printf("faim: no tlv 0x0005 in rendezvous transaction!\n"); | |
463 | aim_freetlvchain(&tlvlist); | |
26af6789 | 464 | return 1; /* major problem */ |
040457cc | 465 | } |
26af6789 | 466 | |
b69540e3 | 467 | /* |
468 | * First two bytes represent the status of the connection. | |
469 | * | |
470 | * 0 is a request, 2 is an accept | |
471 | */ | |
472 | status = aimutil_get16(block1->value+0); | |
473 | ||
474 | /* | |
475 | * Next comes the cookie. Should match the ICBM cookie. | |
476 | */ | |
477 | if (memcmp(block1->value+2, cookie, 8) != 0) | |
478 | printf("faim: rend: warning cookies don't match!\n"); | |
26af6789 | 479 | |
0c20631f | 480 | /* |
b69540e3 | 481 | * The next 16bytes are a capability block so we can |
482 | * identify what type of rendezvous this is. | |
0c20631f | 483 | * |
b69540e3 | 484 | * Thanks to Eric Warmenhoven <warmenhoven@linux.com> (of GAIM) |
485 | * for pointing some of this out to me. In fact, a lot of | |
486 | * the client-to-client info comes from the work of the GAIM | |
487 | * developers. Thanks! | |
488 | * | |
489 | * Read off one capability string and we should have it ID'd. | |
490 | * | |
0c20631f | 491 | */ |
b69540e3 | 492 | reqclass = aim_getcap(block1->value+2+8, 0x10); |
493 | if (reqclass == 0x0000) { | |
494 | printf("faim: rend: no ID block\n"); | |
040457cc | 495 | aim_freetlvchain(&tlvlist); |
496 | return 1; | |
497 | } | |
498 | ||
b69540e3 | 499 | /* |
500 | * What follows may be TLVs or nothing, depending on the | |
501 | * purpose of the message. | |
502 | * | |
503 | * Ack packets for instance have nothing more to them. | |
504 | */ | |
505 | list2 = aim_readtlvchain(block1->value+2+8+16, block1->length-2-8-16); | |
506 | ||
507 | if (!list2 || ((reqclass != AIM_CAPS_IMIMAGE) && !(aim_gettlv(list2, 0x2711, 1)))) { | |
040457cc | 508 | struct aim_msgcookie_t *cook; |
509 | ||
510 | if ((cook = aim_uncachecookie(sess, cookie)) == NULL) { | |
b69540e3 | 511 | printf("faim: non-data rendezvous thats not in cache!\n"); |
040457cc | 512 | aim_freetlvchain(&list2); |
0c20631f | 513 | aim_freetlvchain(&tlvlist); |
514 | return 1; | |
26af6789 | 515 | } |
516 | ||
b69540e3 | 517 | if (cook->type == AIM_CAPS_SENDFILE) { |
040457cc | 518 | struct aim_filetransfer_t *ft; |
26af6789 | 519 | |
040457cc | 520 | if (cook->data) { |
521 | struct aim_tlv_t *errortlv; | |
522 | int errorcode = -1; | |
523 | ||
524 | ft = (struct aim_filetransfer_t *)cook->data; | |
525 | ||
526 | if ((errortlv = aim_gettlv(list2, 0x000b, 1))) { | |
527 | errorcode = aimutil_get16(errortlv->value); | |
0c20631f | 528 | } |
b69540e3 | 529 | if (errorcode) { |
530 | printf("faim: transfer from %s (%s) for %s cancelled (error code %d)\n", ft->sender, ft->ip, ft->filename, errorcode); | |
531 | } else if (status == 0x0002) { /* connection accepted */ | |
532 | printf("faim: transfer from %s (%s) for %s accepted\n", ft->sender, ft->ip, ft->filename); | |
533 | } | |
040457cc | 534 | free(cook->data); |
535 | } else { | |
536 | printf("faim: not data attached to file transfer\n"); | |
537 | } | |
b69540e3 | 538 | } else if (cook->type == AIM_CAPS_VOICE) { |
539 | printf("faim: voice request cancelled\n"); | |
040457cc | 540 | } else { |
541 | printf("faim: unknown cookie cache type %d\n", cook->type); | |
542 | } | |
543 | ||
544 | free(cook); | |
b69540e3 | 545 | if (list2) |
546 | aim_freetlvchain(&list2); | |
547 | aim_freetlvchain(&tlvlist); | |
040457cc | 548 | return 1; |
549 | } | |
550 | ||
040457cc | 551 | /* |
b69540e3 | 552 | * The rest of the handling depends on what type it is. |
040457cc | 553 | */ |
b69540e3 | 554 | if (reqclass & AIM_CAPS_BUDDYICON) { |
f21cc652 | 555 | |
556 | /* | |
557 | * Call client. | |
558 | */ | |
559 | userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); | |
560 | if (userfunc || (i = 0)) | |
561 | i = userfunc(sess, | |
562 | command, | |
563 | channel, | |
564 | reqclass, | |
565 | &userinfo); | |
566 | ||
b69540e3 | 567 | } else if (reqclass & AIM_CAPS_VOICE) { |
568 | struct aim_msgcookie_t cachedcook; | |
569 | ||
570 | printf("faim: rend: voice!\n"); | |
571 | ||
572 | memcpy(cachedcook.cookie, cookie, 8); | |
573 | cachedcook.type = AIM_CAPS_VOICE; | |
574 | cachedcook.data = NULL; | |
575 | if (aim_cachecookie(sess, &cachedcook) != 0) | |
576 | printf("faim: ERROR caching message cookie\n"); | |
040457cc | 577 | |
578 | /* XXX: implement all this */ | |
579 | ||
580 | /* | |
581 | * Call client. | |
582 | */ | |
583 | userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); | |
584 | if (userfunc || (i = 0)) { | |
585 | i = userfunc(sess, | |
586 | command, | |
587 | channel, | |
588 | reqclass, | |
589 | &userinfo); | |
590 | } | |
b69540e3 | 591 | } else if (reqclass & AIM_CAPS_IMIMAGE) { |
040457cc | 592 | char ip[30]; |
040457cc | 593 | struct aim_msgcookie_t cachedcook; |
040457cc | 594 | |
595 | memset(ip, 0, sizeof(ip)); | |
596 | ||
597 | if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0003, 1)) { | |
598 | struct aim_tlv_t *iptlv, *porttlv; | |
0c20631f | 599 | |
040457cc | 600 | iptlv = aim_gettlv(list2, 0x0003, 1); |
601 | porttlv = aim_gettlv(list2, 0x0005, 1); | |
602 | ||
603 | snprintf(ip, sizeof(ip)-1, "%d.%d.%d.%d:%d", | |
604 | aimutil_get8(iptlv->value+0), | |
605 | aimutil_get8(iptlv->value+1), | |
606 | aimutil_get8(iptlv->value+2), | |
607 | aimutil_get8(iptlv->value+3), | |
b69540e3 | 608 | 4443 /*aimutil_get16(porttlv->value)*/); |
040457cc | 609 | } |
610 | ||
b69540e3 | 611 | printf("faim: rend: directIM request from %s (%s)\n", |
040457cc | 612 | userinfo.sn, |
040457cc | 613 | ip); |
b69540e3 | 614 | |
f21cc652 | 615 | #if 0 |
b69540e3 | 616 | { |
617 | struct aim_conn_t *newconn; | |
618 | ||
619 | newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, ip); | |
620 | if (!newconn || (newconn->fd == -1)) { | |
621 | printf("could not connect to %s\n", ip); | |
622 | perror("aim_newconn"); | |
623 | aim_conn_kill(sess, &newconn); | |
624 | } else { | |
625 | struct aim_directim_priv *priv; | |
626 | priv = (struct aim_directim_priv *)malloc(sizeof(struct aim_directim_priv)); | |
627 | memcpy(priv->cookie, cookie, 8); | |
628 | strncpy(priv->sn, userinfo.sn, MAXSNLEN); | |
629 | newconn->priv = priv; | |
630 | printf("faim: connected to peer (fd = %d)\n", newconn->fd); | |
631 | } | |
632 | } | |
f21cc652 | 633 | #endif |
040457cc | 634 | |
b69540e3 | 635 | #if 0 |
040457cc | 636 | memcpy(cachedcook.cookie, cookie, 8); |
040457cc | 637 | |
638 | ft = malloc(sizeof(struct aim_filetransfer_t)); | |
639 | strncpy(ft->sender, userinfo.sn, sizeof(ft->sender)); | |
640 | strncpy(ft->ip, ip, sizeof(ft->ip)); | |
641 | ft->filename = strdup(miscinfo->value+8); | |
b69540e3 | 642 | cachedcook.type = AIM_CAPS_SENDFILE; |
040457cc | 643 | cachedcook.data = ft; |
644 | ||
645 | if (aim_cachecookie(sess, &cachedcook) != 0) | |
646 | printf("faim: ERROR caching message cookie\n"); | |
b69540e3 | 647 | #endif |
040457cc | 648 | |
f21cc652 | 649 | /* |
650 | * Call client. | |
651 | */ | |
652 | userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); | |
653 | if (userfunc || (i = 0)) | |
654 | i = userfunc(sess, | |
655 | command, | |
656 | channel, | |
657 | reqclass, | |
658 | &userinfo); | |
b69540e3 | 659 | |
660 | } else if (reqclass & AIM_CAPS_CHAT) { | |
661 | struct aim_tlv_t *miscinfo; | |
040457cc | 662 | struct aim_chat_roominfo roominfo; |
663 | char *msg=NULL,*encoding=NULL,*lang=NULL; | |
664 | ||
b69540e3 | 665 | miscinfo = aim_gettlv(list2, 0x2711, 1); |
040457cc | 666 | aim_chat_readroominfo(miscinfo->value, &roominfo); |
667 | ||
668 | if (aim_gettlv(list2, 0x000c, 1)) | |
669 | msg = aim_gettlv_str(list2, 0x000c, 1); | |
0c20631f | 670 | |
040457cc | 671 | if (aim_gettlv(list2, 0x000d, 1)) |
672 | encoding = aim_gettlv_str(list2, 0x000d, 1); | |
0c20631f | 673 | |
040457cc | 674 | if (aim_gettlv(list2, 0x000e, 1)) |
675 | lang = aim_gettlv_str(list2, 0x000e, 1); | |
26af6789 | 676 | |
040457cc | 677 | /* |
678 | * Call client. | |
679 | */ | |
680 | userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); | |
681 | if (userfunc || (i = 0)) | |
682 | i = userfunc(sess, | |
683 | command, | |
684 | channel, | |
f21cc652 | 685 | reqclass, |
040457cc | 686 | &userinfo, |
687 | &roominfo, | |
688 | msg, | |
689 | encoding?encoding+1:NULL, | |
690 | lang?lang+1:NULL); | |
0c20631f | 691 | free(roominfo.name); |
692 | free(msg); | |
693 | free(encoding); | |
694 | free(lang); | |
b69540e3 | 695 | } else if (reqclass & AIM_CAPS_GETFILE) { |
f21cc652 | 696 | |
697 | /* | |
698 | * Call client. | |
699 | */ | |
700 | userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); | |
701 | if (userfunc || (i = 0)) | |
702 | i = userfunc(sess, | |
703 | command, | |
704 | channel, | |
705 | reqclass, | |
706 | &userinfo); | |
707 | ||
b69540e3 | 708 | } else if (reqclass & AIM_CAPS_SENDFILE) { |
f21cc652 | 709 | /* |
710 | * Call client. | |
711 | */ | |
712 | userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); | |
713 | if (userfunc || (i = 0)) | |
714 | i = userfunc(sess, | |
715 | command, | |
716 | channel, | |
717 | reqclass, | |
718 | &userinfo); | |
719 | #if 0 | |
b69540e3 | 720 | char ip[30]; |
721 | char *desc = NULL; | |
722 | struct aim_msgcookie_t cachedcook; | |
723 | struct aim_filetransfer_t *ft; | |
724 | struct aim_tlv_t *miscinfo; | |
725 | ||
726 | memset(ip, 0, sizeof(ip)); | |
727 | ||
728 | miscinfo = aim_gettlv(list2, 0x2711, 1); | |
729 | ||
730 | if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0003, 1)) { | |
731 | struct aim_tlv_t *iptlv, *porttlv; | |
732 | ||
733 | iptlv = aim_gettlv(list2, 0x0003, 1); | |
734 | porttlv = aim_gettlv(list2, 0x0005, 1); | |
735 | ||
736 | snprintf(ip, sizeof(ip)-1, "%d.%d.%d.%d:%d", | |
737 | aimutil_get8(iptlv->value+0), | |
738 | aimutil_get8(iptlv->value+1), | |
739 | aimutil_get8(iptlv->value+2), | |
740 | aimutil_get8(iptlv->value+3), | |
741 | aimutil_get16(porttlv->value)); | |
742 | } | |
743 | ||
744 | if (aim_gettlv(list2, 0x000c, 1)) { | |
745 | desc = aim_gettlv_str(list2, 0x000c, 1); | |
746 | } | |
747 | ||
748 | printf("faim: rend: file transfer request from %s for %s: %s (%s)\n", | |
749 | userinfo.sn, | |
750 | miscinfo->value+8, | |
751 | desc, | |
752 | ip); | |
753 | ||
754 | memcpy(cachedcook.cookie, cookie, 8); | |
755 | ||
756 | ft = malloc(sizeof(struct aim_filetransfer_t)); | |
757 | strncpy(ft->sender, userinfo.sn, sizeof(ft->sender)); | |
758 | strncpy(ft->ip, ip, sizeof(ft->ip)); | |
759 | ft->filename = strdup(miscinfo->value+8); | |
760 | cachedcook.type = AIM_CAPS_SENDFILE; | |
761 | cachedcook.data = ft; | |
762 | ||
763 | if (aim_cachecookie(sess, &cachedcook) != 0) | |
764 | printf("faim: ERROR caching message cookie\n"); | |
765 | ||
766 | ||
767 | aim_accepttransfer(sess, command->conn, ft->sender, cookie, AIM_CAPS_SENDFILE); | |
768 | ||
769 | free(desc); | |
f21cc652 | 770 | #endif |
040457cc | 771 | i = 1; |
b69540e3 | 772 | } else { |
773 | printf("faim: rend: unknown rendezvous 0x%04x\n", reqclass); | |
040457cc | 774 | } |
040457cc | 775 | |
26af6789 | 776 | aim_freetlvchain(&list2); |
777 | } | |
9de3ca7e | 778 | |
49c8a2fa | 779 | /* |
780 | * Free up the TLV chain. | |
781 | */ | |
782 | aim_freetlvchain(&tlvlist); | |
26af6789 | 783 | |
49c8a2fa | 784 | |
26af6789 | 785 | return i; |
49c8a2fa | 786 | } |
787 | ||
b69540e3 | 788 | u_long aim_accepttransfer(struct aim_session_t *sess, |
789 | struct aim_conn_t *conn, | |
790 | char *sender, | |
791 | char *cookie, | |
792 | unsigned short rendid) | |
793 | { | |
794 | struct command_tx_struct *newpacket; | |
795 | int curbyte, i; | |
796 | ||
797 | if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(sender)+4+2+8+16))) | |
798 | return -1; | |
799 | ||
800 | newpacket->lock = 1; | |
801 | ||
802 | curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid); | |
803 | for (i = 0; i < 8; i++) | |
804 | curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); | |
805 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); | |
806 | curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sender)); | |
807 | curbyte += aimutil_putstr(newpacket->data+curbyte, sender, strlen(sender)); | |
808 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); | |
809 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x001a); | |
810 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002 /* accept */); | |
811 | for (i = 0; i < 8; i++) | |
812 | curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); | |
813 | curbyte += aim_putcap(newpacket->data+curbyte, 0x10, rendid); | |
814 | ||
815 | newpacket->lock = 0; | |
816 | aim_tx_enqueue(sess, newpacket); | |
817 | ||
818 | return (sess->snac_nextid++); | |
819 | } | |
820 | ||
040457cc | 821 | /* |
822 | * Possible codes: | |
823 | * AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support" | |
824 | * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer" | |
825 | * AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers" | |
826 | * | |
827 | */ | |
828 | u_long aim_denytransfer(struct aim_session_t *sess, | |
829 | struct aim_conn_t *conn, | |
830 | char *sender, | |
831 | char *cookie, | |
832 | unsigned short code) | |
833 | { | |
834 | struct command_tx_struct *newpacket; | |
835 | int curbyte, i; | |
836 | ||
b69540e3 | 837 | if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(sender)+6))) |
040457cc | 838 | return -1; |
839 | ||
840 | newpacket->lock = 1; | |
841 | ||
842 | curbyte = aim_putsnac(newpacket->data, 0x0004, 0x000b, 0x0000, sess->snac_nextid); | |
843 | for (i = 0; i < 8; i++) | |
844 | curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); | |
845 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); | |
846 | curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sender)); | |
847 | curbyte += aimutil_putstr(newpacket->data+curbyte, sender, strlen(sender)); | |
848 | curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0003, code); | |
849 | ||
850 | newpacket->lock = 0; | |
851 | aim_tx_enqueue(sess, newpacket); | |
852 | ||
853 | return (sess->snac_nextid++); | |
854 | } | |
855 | ||
49c8a2fa | 856 | /* |
857 | * Not real sure what this does, nor does anyone I've talk to. | |
858 | * | |
859 | * Didn't use to send it. But now I think it might be a good | |
860 | * idea. | |
861 | * | |
862 | */ | |
a25832e6 | 863 | u_long aim_seticbmparam(struct aim_session_t *sess, |
864 | struct aim_conn_t *conn) | |
49c8a2fa | 865 | { |
5b79dc93 | 866 | struct command_tx_struct *newpacket; |
49c8a2fa | 867 | int curbyte; |
868 | ||
b69540e3 | 869 | if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+16))) |
5b79dc93 | 870 | return -1; |
871 | ||
872 | newpacket->lock = 1; | |
873 | ||
874 | curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid); | |
875 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
876 | curbyte += aimutil_put32(newpacket->data+curbyte, 0x00000003); | |
877 | curbyte += aimutil_put8(newpacket->data+curbyte, 0x1f); | |
878 | curbyte += aimutil_put8(newpacket->data+curbyte, 0x40); | |
879 | curbyte += aimutil_put8(newpacket->data+curbyte, 0x03); | |
880 | curbyte += aimutil_put8(newpacket->data+curbyte, 0xe7); | |
881 | curbyte += aimutil_put8(newpacket->data+curbyte, 0x03); | |
882 | curbyte += aimutil_put8(newpacket->data+curbyte, 0xe7); | |
883 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
884 | curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
885 | ||
886 | newpacket->lock = 0; | |
887 | aim_tx_enqueue(sess, newpacket); | |
a25832e6 | 888 | |
889 | return (sess->snac_nextid++); | |
890 | } | |
891 | ||
892 | int aim_parse_msgerror_middle(struct aim_session_t *sess, | |
893 | struct command_rx_struct *command) | |
894 | { | |
895 | u_long snacid = 0x000000000; | |
896 | struct aim_snac_t *snac = NULL; | |
897 | int ret = 0; | |
898 | rxcallback_t userfunc = NULL; | |
899 | ||
900 | /* | |
901 | * Get SNAC from packet and look it up | |
902 | * the list of unrepliedto/outstanding | |
903 | * SNACs. | |
904 | * | |
905 | * After its looked up, the SN that the | |
906 | * message should've gone to will be | |
907 | * in the ->data element of the snac struct. | |
908 | * | |
909 | */ | |
910 | snacid = aimutil_get32(command->data+6); | |
911 | snac = aim_remsnac(sess, snacid); | |
912 | ||
46b6130d | 913 | if (!snac) { |
914 | printf("faim: msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid); | |
915 | } | |
a25832e6 | 916 | |
917 | /* | |
918 | * Call client. | |
919 | */ | |
920 | userfunc = aim_callhandler(command->conn, 0x0004, 0x0001); | |
921 | if (userfunc) | |
922 | ret = userfunc(sess, command, (snac)?snac->data:"(UNKNOWN)"); | |
923 | else | |
924 | ret = 0; | |
925 | ||
46b6130d | 926 | if (snac) { |
927 | free(snac->data); | |
928 | free(snac); | |
929 | } | |
49c8a2fa | 930 | |
a25832e6 | 931 | return ret; |
9de3ca7e | 932 | } |
e6b05d80 | 933 | |
934 |