Mercurial > pidgin
annotate libfaim/aim_im.c @ 319:cea40691706c
[gaim-migrate @ 329]
A few good oscar changes before release.
committer: Tailor Script <tailor@pidgin.im>
author | Eric Warmenhoven <eric@warmenhoven.org> |
---|---|
date | Fri, 02 Jun 2000 19:45:16 +0000 |
parents | bafaf1b68f9a |
children | 9d258a0aa560 |
rev | line source |
---|---|
2 | 1 /* |
2 * aim_im.c | |
3 * | |
4 * The routines for sending/receiving Instant Messages. | |
5 * | |
6 */ | |
7 | |
283
0f14e6d8a51b
[gaim-migrate @ 293]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
237
diff
changeset
|
8 #include <faim/aim.h> |
2 | 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 * | |
19 */ | |
237 | 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) | |
2 | 23 { |
24 | |
237 | 25 int curbyte,i; |
26 struct command_tx_struct *newpacket; | |
2 | 27 |
283
0f14e6d8a51b
[gaim-migrate @ 293]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
237
diff
changeset
|
28 if (strlen(msg) >= MAXMSGLEN) |
0f14e6d8a51b
[gaim-migrate @ 293]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
237
diff
changeset
|
29 return -1; |
0f14e6d8a51b
[gaim-migrate @ 293]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
237
diff
changeset
|
30 |
0f14e6d8a51b
[gaim-migrate @ 293]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
237
diff
changeset
|
31 if (!(newpacket = aim_tx_new(0x0002, conn, strlen(msg)+256))) |
237 | 32 return -1; |
2 | 33 |
237 | 34 newpacket->lock = 1; /* lock struct */ |
2 | 35 |
36 curbyte = 0; | |
237 | 37 curbyte += aim_putsnac(newpacket->data+curbyte, |
38 0x0004, 0x0006, 0x0000, sess->snac_nextid); | |
39 | |
40 /* | |
41 * Generate a random message cookie | |
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 * | |
48 */ | |
49 for (i=0;i<8;i++) | |
50 curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) random()); | |
2 | 51 |
237 | 52 /* |
53 * Channel ID | |
54 */ | |
55 curbyte += aimutil_put16(newpacket->data+curbyte,0x0001); | |
56 | |
57 /* | |
58 * Destination SN (prepended with byte length) | |
59 */ | |
60 curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn)); | |
61 curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); | |
62 | |
63 /* | |
64 * metaTLV start. | |
65 */ | |
66 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); | |
67 curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x0d); | |
2 | 68 |
237 | 69 /* |
70 * Flag data? | |
71 */ | |
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); | |
76 | |
77 /* | |
78 * Message block length. | |
79 */ | |
80 curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x04); | |
81 | |
82 /* | |
83 * Character set data? | |
84 */ | |
85 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
86 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
2 | 87 |
237 | 88 /* |
89 * Message. Not terminated. | |
90 */ | |
91 curbyte += aimutil_putstr(newpacket->data+curbyte,msg, strlen(msg)); | |
2 | 92 |
237 | 93 /* |
94 * Set the Request Acknowledge flag. | |
95 */ | |
96 if (flags & AIM_IMFLAGS_ACK) { | |
97 curbyte += aimutil_put16(newpacket->data+curbyte,0x0003); | |
98 curbyte += aimutil_put16(newpacket->data+curbyte,0x0000); | |
99 } | |
100 | |
101 /* | |
102 * Set the Autoresponse flag. | |
103 */ | |
104 if (flags & AIM_IMFLAGS_AWAY) { | |
105 curbyte += aimutil_put16(newpacket->data+curbyte,0x0004); | |
106 curbyte += aimutil_put16(newpacket->data+curbyte,0x0000); | |
107 } | |
108 | |
109 newpacket->commandlen = curbyte; | |
110 newpacket->lock = 0; | |
111 | |
112 aim_tx_enqueue(sess, newpacket); | |
2 | 113 |
114 #ifdef USE_SNAC_FOR_IMS | |
115 { | |
116 struct aim_snac_t snac; | |
117 | |
237 | 118 snac.id = sess->snac_nextid; |
2 | 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 | |
237 | 126 aim_newsnac(sess, &snac); |
2 | 127 } |
128 | |
237 | 129 aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */ |
2 | 130 #endif |
131 | |
237 | 132 return (sess->snac_nextid++); |
2 | 133 } |
134 | |
237 | 135 /* |
136 * It can easily be said that parsing ICBMs is THE single | |
137 * most difficult thing to do in the in AIM protocol. In | |
138 * fact, I think I just did say that. | |
139 * | |
140 * Below is the best damned solution I've come up with | |
141 * over the past sixteen months of battling with it. This | |
142 * can parse both away and normal messages from every client | |
143 * I have access to. Its not fast, its not clean. But it works. | |
144 * | |
145 * We should also support at least minimal parsing of | |
146 * Channel 2, so that we can at least know the name of the | |
147 * room we're invited to, but obviously can't attend... | |
148 * | |
149 */ | |
150 int aim_parse_incoming_im_middle(struct aim_session_t *sess, | |
151 struct command_rx_struct *command) | |
2 | 152 { |
237 | 153 u_int i = 0,z; |
154 rxcallback_t userfunc = NULL; | |
155 u_char cookie[8]; | |
156 int channel; | |
157 struct aim_tlvlist_t *tlvlist; | |
158 struct aim_userinfo_s userinfo; | |
159 u_short wastebits; | |
160 | |
161 memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s)); | |
162 | |
163 i = 10; /* Skip SNAC header */ | |
2 | 164 |
237 | 165 /* |
166 * Read ICBM Cookie. And throw away. | |
167 */ | |
168 for (z=0; z<8; z++,i++) | |
169 cookie[z] = command->data[i]; | |
2 | 170 |
237 | 171 /* |
172 * Channel ID. | |
173 * | |
174 * Channel 0x0001 is the message channel. There are | |
175 * other channels for things called "rendevous" | |
176 * which represent chat and some of the other new | |
177 * features of AIM2/3/3.5. | |
178 * | |
179 * Channel 0x0002 is the Rendevous channel, which | |
180 * is where Chat Invitiations and various client-client | |
181 * connection negotiations come from. | |
182 * | |
183 */ | |
184 channel = aimutil_get16(command->data+i); | |
185 i += 2; | |
2 | 186 |
237 | 187 /* |
188 * | |
189 */ | |
190 if ((channel != 0x01) && (channel != 0x02)) | |
191 { | |
192 printf("faim: icbm: ICBM received on an unsupported channel. Ignoring.\n (chan = %04x)", channel); | |
193 return 1; | |
194 } | |
195 | |
196 /* | |
197 * Source screen name. | |
198 */ | |
199 memcpy(userinfo.sn, command->data+i+1, (int)command->data[i]); | |
200 userinfo.sn[(int)command->data[i]] = '\0'; | |
201 i += 1 + (int)command->data[i]; | |
202 | |
203 /* | |
204 * Warning Level | |
205 */ | |
206 userinfo.warnlevel = aimutil_get16(command->data+i); /* guess */ | |
207 i += 2; | |
208 | |
209 /* | |
210 * Number of TLVs that follow. Not needed. | |
211 */ | |
212 wastebits = aimutil_get16(command->data+i); | |
2 | 213 i += 2; |
214 | |
237 | 215 /* |
216 * Read block of TLVs. All further data is derived | |
217 * from what is parsed here. | |
218 */ | |
219 tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); | |
2 | 220 |
237 | 221 /* |
222 * From here on, its depends on what channel we're on. | |
223 */ | |
224 if (channel == 1) | |
225 { | |
226 u_int j = 0, y = 0, z = 0; | |
227 char *msg = NULL; | |
228 u_int icbmflags = 0; | |
229 struct aim_tlv_t *msgblocktlv, *tmptlv; | |
230 u_char *msgblock; | |
231 u_short flag1,flag2; | |
232 | |
233 /* | |
234 * Check Autoresponse status. If it is an autoresponse, | |
235 * it will contain a second type 0x0004 TLV, with zero length. | |
236 */ | |
237 if (aim_gettlv(tlvlist, 0x0004, 2)) | |
238 icbmflags |= AIM_IMFLAGS_AWAY; | |
239 | |
240 /* | |
241 * Check Ack Request status. | |
242 */ | |
243 if (aim_gettlv(tlvlist, 0x0003, 2)) | |
244 icbmflags |= AIM_IMFLAGS_ACK; | |
245 | |
246 /* | |
247 * Extract the various pieces of the userinfo struct. | |
248 */ | |
249 /* Class. */ | |
250 if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1))) | |
251 userinfo.class = aimutil_get16(tmptlv->value); | |
252 /* Member-since date. */ | |
253 if ((tmptlv = aim_gettlv(tlvlist, 0x0002, 1))) | |
254 { | |
255 /* If this is larger than 4, its probably the message block, skip */ | |
256 if (tmptlv->length <= 4) | |
257 userinfo.membersince = aimutil_get32(tmptlv->value); | |
258 } | |
259 /* On-since date */ | |
260 if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1))) | |
261 userinfo.onlinesince = aimutil_get32(tmptlv->value); | |
262 /* Idle-time */ | |
263 if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1))) | |
264 userinfo.idletime = aimutil_get16(tmptlv->value); | |
265 /* Session Length (AIM) */ | |
266 if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1))) | |
267 userinfo.sessionlen = aimutil_get16(tmptlv->value); | |
268 /* Session Length (AOL) */ | |
269 if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1))) | |
270 userinfo.sessionlen = aimutil_get16(tmptlv->value); | |
271 | |
272 /* | |
273 * Message block. | |
274 * | |
275 * XXX: Will the msgblock always be the second 0x0002? | |
276 */ | |
277 msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1); | |
278 if (!msgblocktlv) | |
279 { | |
280 printf("faim: icbm: major error! no message block TLV found!\n"); | |
281 aim_freetlvchain(&tlvlist); | |
282 return 1; | |
283 } | |
284 | |
285 /* | |
286 * Extracting the message from the unknown cruft. | |
287 * | |
288 * This is a bit messy, and I'm not really qualified, | |
289 * even as the author, to comment on it. At least | |
290 * its not as bad as a while loop shooting into infinity. | |
291 * | |
292 * "Do you believe in magic?" | |
293 * | |
294 */ | |
295 msgblock = msgblocktlv->value; | |
296 j = 0; | |
297 | |
298 wastebits = aimutil_get8(msgblock+j++); | |
299 wastebits = aimutil_get8(msgblock+j++); | |
300 | |
301 y = aimutil_get16(msgblock+j); | |
302 j += 2; | |
303 for (z = 0; z < y; z++) | |
304 wastebits = aimutil_get8(msgblock+j++); | |
305 wastebits = aimutil_get8(msgblock+j++); | |
306 wastebits = aimutil_get8(msgblock+j++); | |
307 | |
308 /* | |
309 * Message string length, including flag words. | |
310 */ | |
311 i = aimutil_get16(msgblock+j); | |
312 j += 2; | |
313 | |
314 /* | |
315 * Flag words. | |
316 * | |
317 * Its rumored that these can kick in some funky | |
318 * 16bit-wide char stuff that used to really kill | |
319 * libfaim. Hopefully the latter is no longer true. | |
320 * | |
321 * Though someone should investiagte the former. | |
322 * | |
323 */ | |
324 flag1 = aimutil_get16(msgblock+j); | |
325 j += 2; | |
326 flag2 = aimutil_get16(msgblock+j); | |
327 j += 2; | |
328 | |
329 if (flag1 || flag2) | |
330 printf("faim: icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2); | |
331 | |
332 /* | |
333 * Message string. | |
334 */ | |
335 i -= 4; | |
336 msg = (char *)malloc(i+1); | |
337 memcpy(msg, msgblock+j, i); | |
338 msg[i] = '\0'; | |
339 | |
340 /* | |
341 * Call client. | |
342 */ | |
343 userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); | |
344 if (userfunc) | |
345 i = userfunc(sess, command, channel, &userinfo, msg, icbmflags, flag1, flag2); | |
346 else | |
347 i = 0; | |
348 | |
349 free(msg); | |
350 } | |
351 else if (channel == 0x0002) | |
352 { | |
353 int rendtype; | |
354 struct aim_tlv_t *block1; | |
355 struct aim_tlvlist_t *list2; | |
356 struct aim_tlv_t *tmptlv; | |
357 int a; | |
358 | |
359 /* Class. */ | |
360 if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1))) | |
361 userinfo.class = aimutil_get16(tmptlv->value); | |
362 /* On-since date */ | |
363 if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1))) | |
364 userinfo.onlinesince = aimutil_get32(tmptlv->value); | |
365 /* Idle-time */ | |
366 if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1))) | |
367 userinfo.idletime = aimutil_get16(tmptlv->value); | |
368 /* Session Length (AIM) */ | |
369 if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1))) | |
370 userinfo.sessionlen = aimutil_get16(tmptlv->value); | |
371 /* Session Length (AOL) */ | |
372 if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1))) | |
373 userinfo.sessionlen = aimutil_get16(tmptlv->value); | |
2 | 374 |
237 | 375 /* |
376 * There's another block of TLVs embedded in the type 5 here. | |
377 */ | |
378 block1 = aim_gettlv(tlvlist, 0x0005, 1); | |
379 if (!block1) | |
380 return 1; /* major problem */ | |
381 | |
382 a = 0x1a; /* skip -- not sure what this information is! */ | |
383 | |
384 /* | |
385 * XXX: Ignore if there's no data, only cookie information. | |
386 * | |
387 * Its probably just an accepted invitation or something. | |
388 * | |
389 */ | |
390 if (block1->length <= 0x1a) | |
391 { | |
392 aim_freetlvchain(&tlvlist); | |
393 return 1; | |
394 } | |
395 | |
396 list2 = aim_readtlvchain(block1->value+a, block1->length-a); | |
397 | |
398 if (aim_gettlv(list2, 0x0004, 1) /* start connection */ || | |
399 aim_gettlv(list2, 0x000b, 1) /* close conncetion */) | |
400 { | |
401 rendtype = 1; /* voice request */ | |
2 | 402 |
237 | 403 /* |
404 * Call client. | |
405 */ | |
406 userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); | |
407 if (userfunc) | |
408 i = userfunc(sess, | |
409 command, | |
410 channel, | |
411 rendtype, | |
412 &userinfo); | |
413 else | |
414 i = 0; | |
415 } | |
2 | 416 else |
237 | 417 { |
418 struct aim_chat_roominfo roominfo; | |
419 char *msg=NULL,*encoding=NULL,*lang=NULL; | |
420 | |
421 rendtype = 0; /* chat invite */ | |
422 if (aim_gettlv(list2, 0x2711, 1)) | |
423 { | |
424 struct aim_tlv_t *nametlv; | |
425 | |
426 nametlv = aim_gettlv(list2, 0x2711, 1); | |
427 aim_chat_readroominfo(nametlv->value, &roominfo); | |
428 } | |
429 | |
430 if (aim_gettlv(list2, 0x000c, 1)) | |
431 msg = aim_gettlv_str(list2, 0x000c, 1); | |
432 | |
433 if (aim_gettlv(list2, 0x000d, 1)) | |
434 encoding = aim_gettlv_str(list2, 0x000d, 1); | |
435 | |
436 if (aim_gettlv(list2, 0x000e, 1)) | |
437 lang = aim_gettlv_str(list2, 0x000e, 1); | |
438 | |
439 /* | |
440 * Call client. | |
441 */ | |
442 userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); | |
443 if (userfunc) | |
444 i = userfunc(sess, | |
445 command, | |
446 channel, | |
447 rendtype, | |
448 &userinfo, | |
449 &roominfo, | |
450 msg, | |
451 encoding?encoding+1:NULL, | |
452 lang?lang+1:NULL); | |
453 else | |
454 i = 0; | |
455 | |
456 free(roominfo.name); | |
457 free(msg); | |
458 free(encoding); | |
459 free(lang); | |
460 } | |
461 aim_freetlvchain(&list2); | |
2 | 462 } |
463 | |
237 | 464 /* |
465 * Free up the TLV chain. | |
466 */ | |
467 aim_freetlvchain(&tlvlist); | |
2 | 468 |
469 | |
470 return i; | |
471 } | |
237 | 472 |
473 /* | |
474 * Not real sure what this does, nor does anyone I've talk to. | |
475 * | |
476 * Didn't use to send it. But now I think it might be a good | |
477 * idea. | |
478 * | |
479 */ | |
480 u_long aim_seticbmparam(struct aim_session_t *sess, | |
481 struct aim_conn_t *conn) | |
482 { | |
483 struct command_tx_struct *newpacket; | |
484 int curbyte; | |
485 | |
486 if(!(newpacket = aim_tx_new(0x0002, conn, 10+16))) | |
487 return -1; | |
488 | |
489 newpacket->lock = 1; | |
490 | |
491 curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid); | |
492 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
493 curbyte += aimutil_put32(newpacket->data+curbyte, 0x00000003); | |
494 curbyte += aimutil_put8(newpacket->data+curbyte, 0x1f); | |
495 curbyte += aimutil_put8(newpacket->data+curbyte, 0x40); | |
496 curbyte += aimutil_put8(newpacket->data+curbyte, 0x03); | |
497 curbyte += aimutil_put8(newpacket->data+curbyte, 0xe7); | |
498 curbyte += aimutil_put8(newpacket->data+curbyte, 0x03); | |
499 curbyte += aimutil_put8(newpacket->data+curbyte, 0xe7); | |
500 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
501 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
502 | |
503 newpacket->lock = 0; | |
504 aim_tx_enqueue(sess, newpacket); | |
505 | |
506 return (sess->snac_nextid++); | |
507 } | |
508 | |
509 int aim_parse_msgerror_middle(struct aim_session_t *sess, | |
510 struct command_rx_struct *command) | |
511 { | |
512 u_long snacid = 0x000000000; | |
513 struct aim_snac_t *snac = NULL; | |
514 int ret = 0; | |
515 rxcallback_t userfunc = NULL; | |
516 | |
517 /* | |
518 * Get SNAC from packet and look it up | |
519 * the list of unrepliedto/outstanding | |
520 * SNACs. | |
521 * | |
522 * After its looked up, the SN that the | |
523 * message should've gone to will be | |
524 * in the ->data element of the snac struct. | |
525 * | |
526 */ | |
527 snacid = aimutil_get32(command->data+6); | |
528 snac = aim_remsnac(sess, snacid); | |
529 | |
284
bafaf1b68f9a
[gaim-migrate @ 294]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
283
diff
changeset
|
530 if (!snac) { |
bafaf1b68f9a
[gaim-migrate @ 294]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
283
diff
changeset
|
531 printf("faim: msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid); |
bafaf1b68f9a
[gaim-migrate @ 294]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
283
diff
changeset
|
532 } |
237 | 533 |
534 /* | |
535 * Call client. | |
536 */ | |
537 userfunc = aim_callhandler(command->conn, 0x0004, 0x0001); | |
538 if (userfunc) | |
539 ret = userfunc(sess, command, (snac)?snac->data:"(UNKNOWN)"); | |
540 else | |
541 ret = 0; | |
542 | |
284
bafaf1b68f9a
[gaim-migrate @ 294]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
283
diff
changeset
|
543 if (snac) { |
bafaf1b68f9a
[gaim-migrate @ 294]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
283
diff
changeset
|
544 free(snac->data); |
bafaf1b68f9a
[gaim-migrate @ 294]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
283
diff
changeset
|
545 free(snac); |
bafaf1b68f9a
[gaim-migrate @ 294]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
283
diff
changeset
|
546 } |
237 | 547 |
548 return ret; | |
549 } | |
550 | |
551 |