2 * Purple's oscar protocol plugin
3 * This file is the legal property of its developers.
4 * Please see the AUTHORS file distributed alongside this file.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 * Family 0x0004 - Routines for sending/receiving Instant Messages.
24 * Note the term ICBM (Inter-Client Basic Message) which blankets
25 * all types of generically routed through-server messages. Within
26 * the ICBM types (family 4), a channel is defined. Each channel
27 * represents a different type of message. Channel 1 is used for
28 * what would commonly be called an "instant message". Channel 2
29 * is used for negotiating "rendezvous". These transactions end in
30 * something more complex happening, such as a chat invitation, or
31 * a file transfer. Channel 3 is used for chat messages (not in
32 * the same family as these channels). Channel 4 is used for
33 * various ICQ messages. Examples are normal messages, URLs, and
34 * old-style authorization.
36 * In addition to the channel, every ICBM contains a cookie. For
37 * standard IMs, these are only used for error messages. However,
38 * the more complex rendezvous messages make suitably more complex
41 * TODO: Split this up into an im.c file an an icbm.c file. It
42 * will be beautiful, you'll see.
44 * Make sure flap_connection_findbygroup is used by all functions.
57 static const char * const errcodereason
[] = {
60 N_("Cannot receive IM due to parental controls"),
61 N_("Cannot send SMS without accepting terms"),
62 N_("Cannot send SMS"), /* SMS_WITHOUT_DISCLAIMER is weird */
63 N_("Cannot send SMS to this country"),
64 N_("Unknown error"), /* Undocumented */
65 N_("Unknown error"), /* Undocumented */
66 N_("Cannot send SMS to unknown country"),
67 N_("Bot accounts cannot initiate IMs"),
68 N_("Bot account cannot IM this user"),
69 N_("Bot account reached IM limit"),
70 N_("Bot account reached daily IM limit"),
71 N_("Bot account reached monthly IM limit"),
72 N_("Unable to receive offline messages"),
73 N_("Offline message store full")
75 static const int errcodereasonlen
= G_N_ELEMENTS(errcodereason
);
78 * Add a standard ICBM header to the given bstream with the given
81 * @param bs The bstream to write the ICBM header to.
82 * @param c c is for cookie, and cookie is for me.
83 * @param channel The ICBM channel (1 through 4).
84 * @param bn Null-terminated scrizeen nizame.
85 * @return The number of bytes written. It's really not useful.
87 static int aim_im_puticbm(ByteStream
*bs
, const guchar
*c
, guint16 channel
, const char *bn
)
89 byte_stream_putraw(bs
, c
, 8);
90 byte_stream_put16(bs
, channel
);
91 byte_stream_put8(bs
, strlen(bn
));
92 byte_stream_putstr(bs
, bn
);
93 return 8+2+1+strlen(bn
);
97 * Generates a random ICBM cookie in a character array of length 8
98 * and copies it into the variable passed as cookie
99 * TODO: Maybe we should stop limiting our characters to the visible range?
101 void aim_icbm_makecookie(guchar
*cookie
)
105 /* Should be like "21CBF95" and null terminated */
106 for (i
= 0; i
< 7; i
++)
107 cookie
[i
] = 0x30 + ((guchar
)rand() % 10);
112 * Subtype 0x0001 - Error
115 error(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
118 guint16 reason
, errcode
= 0;
121 PurpleConnection
*gc
= od
->gc
;
125 const char *reason_str
;
128 snac2
= aim_remsnac(od
, snac
->id
);
130 purple_debug_misc("oscar", "icbm error: received response from unknown request!\n");
134 if (snac2
->family
!= SNAC_FAMILY_ICBM
) {
135 purple_debug_misc("oscar", "icbm error: received response from invalid request! %d\n", snac2
->family
);
141 /* Data is assumed to be the destination bn */
143 if (!bn
|| bn
[0] == '\0') {
144 purple_debug_misc("oscar", "icbm error: received response from request without a buddy name!\n");
150 reason
= byte_stream_get16(bs
);
152 tlvlist
= aim_tlvlist_read(bs
);
153 if (aim_tlv_gettlv(tlvlist
, 0x0008, 1))
154 errcode
= aim_tlv_get16(tlvlist
, 0x0008, 1);
155 aim_tlvlist_free(tlvlist
);
157 purple_debug_error("oscar",
158 "Message error with bn %s and reason %hu and errcode %hu\n",
159 bn
, reason
, errcode
);
162 /* If this was a file transfer request, bn is a cookie */
163 if ((xfer
= oscar_find_xfer_by_cookie(od
->file_transfers
, bn
))) {
164 purple_xfer_cancel_remote(xfer
);
169 /* Notify the user that the message wasn't delivered */
170 reason_str
= oscar_get_msgerr_reason(reason
);
171 if (errcode
!= 0 && errcode
< errcodereasonlen
)
172 buf
= g_strdup_printf(_("Unable to send message: %s (%s)"), reason_str
,
173 _(errcodereason
[errcode
]));
175 buf
= g_strdup_printf(_("Unable to send message: %s"), reason_str
);
177 if (!purple_conv_present_error(bn
, purple_connection_get_account(gc
), buf
)) {
179 if (errcode
!= 0 && errcode
< errcodereasonlen
)
180 buf
= g_strdup_printf(_("Unable to send message to %s: %s (%s)"),
181 bn
? bn
: "(unknown)", reason_str
,
182 _(errcodereason
[errcode
]));
184 buf
= g_strdup_printf(_("Unable to send message to %s: %s"),
185 bn
? bn
: "(unknown)", reason_str
);
186 purple_notify_error(od
->gc
, NULL
, buf
, reason_str
);
197 * Subtype 0x0002 - Set ICBM parameters.
199 * I definitely recommend sending this. If you don't, you'll be stuck
200 * with the rather unreasonable defaults.
203 int aim_im_setparams(OscarData
*od
, struct aim_icbmparameters
*params
)
205 FlapConnection
*conn
;
209 if (!od
|| !(conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
)))
215 byte_stream_new(&bs
, 16);
217 /* This is read-only (see Parameter Reply). Must be set to zero here. */
218 byte_stream_put16(&bs
, 0x0000);
220 /* These are all read-write */
221 byte_stream_put32(&bs
, params
->flags
);
222 byte_stream_put16(&bs
, params
->maxmsglen
);
223 byte_stream_put16(&bs
, params
->maxsenderwarn
);
224 byte_stream_put16(&bs
, params
->maxrecverwarn
);
225 byte_stream_put32(&bs
, params
->minmsginterval
);
227 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0002, 0x0000, NULL
, 0);
228 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0002, snacid
, &bs
);
230 byte_stream_destroy(&bs
);
236 * Subtype 0x0004 - Request ICBM parameter information.
239 int aim_im_reqparams(OscarData
*od
)
241 FlapConnection
*conn
;
243 if (!od
|| !(conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
)))
246 aim_genericreq_n_snacid(od
, conn
, SNAC_FAMILY_ICBM
, 0x0004);
252 * Subtype 0x0005 - Receive parameter information.
255 static int aim_im_paraminfo(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
257 struct aim_icbmparameters params
;
259 params
.maxchan
= byte_stream_get16(bs
);
260 params
.flags
= byte_stream_get32(bs
);
261 params
.maxmsglen
= byte_stream_get16(bs
);
262 params
.maxsenderwarn
= byte_stream_get16(bs
);
263 params
.maxrecverwarn
= byte_stream_get16(bs
);
264 params
.minmsginterval
= byte_stream_get32(bs
);
266 params
.flags
= AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED
267 | AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED
268 | AIM_IMPARAM_FLAG_EVENTS_ALLOWED
269 | AIM_IMPARAM_FLAG_SMS_SUPPORTED
270 | AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED
271 | AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ
;
272 params
.maxmsglen
= 8000;
273 params
.minmsginterval
= 0;
275 aim_im_setparams(od
, ¶ms
);
281 * Subtype 0x0006 - Send an ICBM (instant message).
285 * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse
286 * AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
287 * online (probably ICQ only).
289 * Implementation note: Since this is one of the most-used functions
290 * in all of libfaim, it is written with performance in mind. As such,
291 * it is not as clear as it could be in respect to how this message is
292 * supposed to be layed out. Most obviously, tlvlists should be used
293 * instead of writing out the bytes manually.
295 int aim_im_sendch1_ext(OscarData
*od
, struct aim_sendimext_args
*args
)
297 FlapConnection
*conn
;
303 if (!od
|| !(conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
)))
309 if (!args
->msg
|| (args
->msglen
<= 0))
312 if (args
->msglen
> MAXMSGLEN
)
315 /* Painfully calculate the size of the message TLV */
316 msgtlvlen
= 1 + 1; /* 0501 */
317 msgtlvlen
+= 2 + args
->featureslen
;
318 msgtlvlen
+= 2 /* 0101 */ + 2 /* block len */;
319 msgtlvlen
+= 4 /* charset */ + args
->msglen
;
321 byte_stream_new(&data
, msgtlvlen
+ 128);
323 /* Generate an ICBM cookie */
324 aim_icbm_makecookie(cookie
);
327 aim_im_puticbm(&data
, cookie
, 0x0001, args
->destbn
);
329 /* Message TLV (type 0x0002) */
330 byte_stream_put16(&data
, 0x0002);
331 byte_stream_put16(&data
, msgtlvlen
);
333 /* Features TLV (type 0x0501) */
334 byte_stream_put16(&data
, 0x0501);
335 byte_stream_put16(&data
, args
->featureslen
);
336 byte_stream_putraw(&data
, args
->features
, args
->featureslen
);
338 /* Insert message text in a TLV (type 0x0101) */
339 byte_stream_put16(&data
, 0x0101);
341 /* Message block length */
342 byte_stream_put16(&data
, args
->msglen
+ 0x04);
345 byte_stream_put16(&data
, args
->charset
);
346 /* Character subset -- we always use 0 here */
347 byte_stream_put16(&data
, 0x0);
349 /* Message. Not terminated */
350 byte_stream_putraw(&data
, (guchar
*)args
->msg
, args
->msglen
);
352 /* Set the Autoresponse flag */
353 if (args
->flags
& AIM_IMFLAGS_AWAY
) {
354 byte_stream_put16(&data
, 0x0004);
355 byte_stream_put16(&data
, 0x0000);
357 /* Set the Request Acknowledge flag */
358 byte_stream_put16(&data
, 0x0003);
359 byte_stream_put16(&data
, 0x0000);
361 if (args
->flags
& AIM_IMFLAGS_OFFLINE
) {
362 /* Allow this message to be queued as an offline message */
363 byte_stream_put16(&data
, 0x0006);
364 byte_stream_put16(&data
, 0x0000);
369 * Set the I HAVE A REALLY PURTY ICON flag.
370 * XXX - This should really only be sent on initial
371 * IMs and when you change your icon.
373 if (args
->flags
& AIM_IMFLAGS_HASICON
) {
374 byte_stream_put16(&data
, 0x0008);
375 byte_stream_put16(&data
, 0x000c);
376 byte_stream_put32(&data
, args
->iconlen
);
377 byte_stream_put16(&data
, 0x0001);
378 byte_stream_put16(&data
, args
->iconsum
);
379 byte_stream_put32(&data
, args
->iconstamp
);
383 * Set the Buddy Icon Requested flag.
384 * XXX - Every time? Surely not...
386 if (args
->flags
& AIM_IMFLAGS_BUDDYREQ
) {
387 byte_stream_put16(&data
, 0x0009);
388 byte_stream_put16(&data
, 0x0000);
391 /* XXX - should be optional */
392 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0006, 0x0000, args
->destbn
, strlen(args
->destbn
)+1);
394 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0006, snacid
, &data
);
395 byte_stream_destroy(&data
);
397 /* clean out SNACs over 60sec old */
398 aim_cleansnacs(od
, 60);
404 * Subtype 0x0006 - Send a chat invitation.
406 int aim_im_sendch2_chatinvite(OscarData
*od
, const char *bn
, const char *msg
, guint16 exchange
, const char *roomname
, guint16 instance
)
408 FlapConnection
*conn
;
411 IcbmCookie
*msgcookie
;
412 struct aim_invite_priv
*priv
;
414 GSList
*outer_tlvlist
= NULL
, *inner_tlvlist
= NULL
;
417 if (!od
|| !(conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
)))
420 if (!bn
|| !msg
|| !roomname
)
423 aim_icbm_makecookie(cookie
);
425 byte_stream_new(&bs
, 1142+strlen(bn
)+strlen(roomname
)+strlen(msg
));
427 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0006, 0x0000, bn
, strlen(bn
)+1);
429 /* XXX should be uncached by an unwritten 'invite accept' handler */
430 priv
= g_malloc(sizeof(struct aim_invite_priv
));
431 priv
->bn
= g_strdup(bn
);
432 priv
->roomname
= g_strdup(roomname
);
433 priv
->exchange
= exchange
;
434 priv
->instance
= instance
;
436 if ((msgcookie
= aim_mkcookie(cookie
, AIM_COOKIETYPE_INVITE
, priv
)))
437 aim_cachecookie(od
, msgcookie
);
442 aim_im_puticbm(&bs
, cookie
, 0x0002, bn
);
447 * Everything else is inside this TLV.
449 * Sigh. AOL was rather inconsistent right here. So we have
450 * to play some minor tricks. Right inside the type 5 is some
451 * raw data, followed by a series of TLVs.
454 byte_stream_new(&hdrbs
, 2+8+16+6+4+4+strlen(msg
)+4+2+1+strlen(roomname
)+2);
456 byte_stream_put16(&hdrbs
, 0x0000); /* Unknown! */
457 byte_stream_putraw(&hdrbs
, cookie
, sizeof(cookie
)); /* I think... */
458 byte_stream_putcaps(&hdrbs
, OSCAR_CAPABILITY_CHAT
);
460 aim_tlvlist_add_16(&inner_tlvlist
, 0x000a, 0x0001);
461 aim_tlvlist_add_noval(&inner_tlvlist
, 0x000f);
462 aim_tlvlist_add_str(&inner_tlvlist
, 0x000c, msg
);
463 aim_tlvlist_add_chatroom(&inner_tlvlist
, 0x2711, exchange
, roomname
, instance
);
464 aim_tlvlist_write(&hdrbs
, &inner_tlvlist
);
466 aim_tlvlist_add_raw(&outer_tlvlist
, 0x0005, byte_stream_curpos(&hdrbs
), hdrbs
.data
);
467 byte_stream_destroy(&hdrbs
);
469 aim_tlvlist_write(&bs
, &outer_tlvlist
);
471 aim_tlvlist_free(inner_tlvlist
);
472 aim_tlvlist_free(outer_tlvlist
);
474 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0006, snacid
, &bs
);
476 byte_stream_destroy(&bs
);
482 * Subtype 0x0006 - Send your icon to a given user.
484 * This is also performance sensitive. (If you can believe it...)
487 int aim_im_sendch2_icon(OscarData
*od
, const char *bn
, const guint8
*icon
, int iconlen
, time_t stamp
, guint16 iconsum
)
489 FlapConnection
*conn
;
494 if (!od
|| !(conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
)))
497 if (!bn
|| !icon
|| (iconlen
<= 0) || (iconlen
>= MAXICONLEN
))
500 aim_icbm_makecookie(cookie
);
502 byte_stream_new(&bs
, 8+2+1+strlen(bn
)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen
+strlen(AIM_ICONIDENT
)+2+2);
504 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0006, 0x0000, NULL
, 0);
507 aim_im_puticbm(&bs
, cookie
, 0x0002, bn
);
512 * Encompasses everything below.
514 byte_stream_put16(&bs
, 0x0005);
515 byte_stream_put16(&bs
, 2+8+16+6+4+4+iconlen
+4+4+4+strlen(AIM_ICONIDENT
));
517 byte_stream_put16(&bs
, 0x0000);
518 byte_stream_putraw(&bs
, cookie
, 8);
519 byte_stream_putcaps(&bs
, OSCAR_CAPABILITY_BUDDYICON
);
522 byte_stream_put16(&bs
, 0x000a);
523 byte_stream_put16(&bs
, 0x0002);
524 byte_stream_put16(&bs
, 0x0001);
527 byte_stream_put16(&bs
, 0x000f);
528 byte_stream_put16(&bs
, 0x0000);
531 byte_stream_put16(&bs
, 0x2711);
532 byte_stream_put16(&bs
, 4+4+4+iconlen
+strlen(AIM_ICONIDENT
));
533 byte_stream_put16(&bs
, 0x0000);
534 byte_stream_put16(&bs
, iconsum
);
535 byte_stream_put32(&bs
, iconlen
);
536 byte_stream_put32(&bs
, stamp
);
537 byte_stream_putraw(&bs
, icon
, iconlen
);
538 byte_stream_putstr(&bs
, AIM_ICONIDENT
);
541 byte_stream_put16(&bs
, 0x0003);
542 byte_stream_put16(&bs
, 0x0000);
544 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0006, snacid
, &bs
);
546 byte_stream_destroy(&bs
);
552 * Cancel a rendezvous invitation. It could be an invitation to
553 * establish a direct connection, or a file-send, or a chat invite.
556 aim_im_sendch2_cancel(PeerConnection
*peer_conn
)
559 FlapConnection
*conn
;
562 GSList
*outer_tlvlist
= NULL
, *inner_tlvlist
= NULL
;
566 conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
);
570 byte_stream_new(&bs
, 118+strlen(peer_conn
->bn
));
572 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0006, 0x0000, NULL
, 0);
575 aim_im_puticbm(&bs
, peer_conn
->cookie
, 0x0002, peer_conn
->bn
);
577 aim_tlvlist_add_noval(&outer_tlvlist
, 0x0003);
579 byte_stream_new(&hdrbs
, 64);
581 byte_stream_put16(&hdrbs
, AIM_RENDEZVOUS_CANCEL
);
582 byte_stream_putraw(&hdrbs
, peer_conn
->cookie
, 8);
583 byte_stream_putcaps(&hdrbs
, peer_conn
->type
);
585 /* This TLV means "cancel!" */
586 aim_tlvlist_add_16(&inner_tlvlist
, 0x000b, 0x0001);
587 aim_tlvlist_write(&hdrbs
, &inner_tlvlist
);
589 aim_tlvlist_add_raw(&outer_tlvlist
, 0x0005, byte_stream_curpos(&hdrbs
), hdrbs
.data
);
590 byte_stream_destroy(&hdrbs
);
592 aim_tlvlist_write(&bs
, &outer_tlvlist
);
594 aim_tlvlist_free(inner_tlvlist
);
595 aim_tlvlist_free(outer_tlvlist
);
597 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0006, snacid
, &bs
);
599 byte_stream_destroy(&bs
);
603 * Subtype 0x0006 - Send an "I accept and I've connected to
607 aim_im_sendch2_connected(PeerConnection
*peer_conn
)
610 FlapConnection
*conn
;
615 conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
);
619 byte_stream_new(&bs
, 11+strlen(peer_conn
->bn
) + 4+2+8+16);
621 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0006, 0x0000, NULL
, 0);
624 aim_im_puticbm(&bs
, peer_conn
->cookie
, 0x0002, peer_conn
->bn
);
626 byte_stream_put16(&bs
, 0x0005);
627 byte_stream_put16(&bs
, 0x001a);
628 byte_stream_put16(&bs
, AIM_RENDEZVOUS_CONNECTED
);
629 byte_stream_putraw(&bs
, peer_conn
->cookie
, 8);
630 byte_stream_putcaps(&bs
, peer_conn
->type
);
632 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0006, snacid
, &bs
);
634 byte_stream_destroy(&bs
);
638 * Subtype 0x0006 - Send a direct connect rendezvous ICBM. This
639 * could have a number of meanings, depending on the content:
640 * "I want you to connect to me"
641 * "I want to connect to you"
642 * "I want to connect through a proxy server"
645 aim_im_sendch2_odc_requestdirect(OscarData
*od
, guchar
*cookie
, const char *bn
, const guint8
*ip
, guint16 port
, guint16 requestnumber
)
647 FlapConnection
*conn
;
650 GSList
*outer_tlvlist
= NULL
, *inner_tlvlist
= NULL
;
653 conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
);
657 byte_stream_new(&bs
, 246+strlen(bn
));
659 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0006, 0x0000, NULL
, 0);
662 aim_im_puticbm(&bs
, cookie
, 0x0002, bn
);
664 aim_tlvlist_add_noval(&outer_tlvlist
, 0x0003);
666 byte_stream_new(&hdrbs
, 128);
668 byte_stream_put16(&hdrbs
, AIM_RENDEZVOUS_PROPOSE
);
669 byte_stream_putraw(&hdrbs
, cookie
, 8);
670 byte_stream_putcaps(&hdrbs
, OSCAR_CAPABILITY_DIRECTIM
);
672 aim_tlvlist_add_raw(&inner_tlvlist
, 0x0002, 4, ip
);
673 aim_tlvlist_add_raw(&inner_tlvlist
, 0x0003, 4, ip
);
674 aim_tlvlist_add_16(&inner_tlvlist
, 0x0005, port
);
675 aim_tlvlist_add_16(&inner_tlvlist
, 0x000a, requestnumber
);
676 aim_tlvlist_add_noval(&inner_tlvlist
, 0x000f);
677 aim_tlvlist_write(&hdrbs
, &inner_tlvlist
);
679 aim_tlvlist_add_raw(&outer_tlvlist
, 0x0005, byte_stream_curpos(&hdrbs
), hdrbs
.data
);
680 byte_stream_destroy(&hdrbs
);
682 aim_tlvlist_write(&bs
, &outer_tlvlist
);
684 aim_tlvlist_free(inner_tlvlist
);
685 aim_tlvlist_free(outer_tlvlist
);
687 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0006, snacid
, &bs
);
689 byte_stream_destroy(&bs
);
693 * Subtype 0x0006 - Send a direct connect rendezvous ICBM asking the
694 * remote user to connect to us via a proxy server.
697 aim_im_sendch2_odc_requestproxy(OscarData
*od
, guchar
*cookie
, const char *bn
, const guint8
*ip
, guint16 pin
, guint16 requestnumber
)
699 FlapConnection
*conn
;
702 GSList
*outer_tlvlist
= NULL
, *inner_tlvlist
= NULL
;
706 conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
);
710 byte_stream_new(&bs
, 246+strlen(bn
));
712 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0006, 0x0000, NULL
, 0);
715 aim_im_puticbm(&bs
, cookie
, 0x0002, bn
);
717 aim_tlvlist_add_noval(&outer_tlvlist
, 0x0003);
719 byte_stream_new(&hdrbs
, 128);
721 byte_stream_put16(&hdrbs
, AIM_RENDEZVOUS_PROPOSE
);
722 byte_stream_putraw(&hdrbs
, cookie
, 8);
723 byte_stream_putcaps(&hdrbs
, OSCAR_CAPABILITY_DIRECTIM
);
725 aim_tlvlist_add_raw(&inner_tlvlist
, 0x0002, 4, ip
);
726 aim_tlvlist_add_raw(&inner_tlvlist
, 0x0003, 4, ip
);
727 aim_tlvlist_add_16(&inner_tlvlist
, 0x0005, pin
);
728 aim_tlvlist_add_16(&inner_tlvlist
, 0x000a, requestnumber
);
729 aim_tlvlist_add_noval(&inner_tlvlist
, 0x000f);
730 aim_tlvlist_add_noval(&inner_tlvlist
, 0x0010);
732 /* Send the bitwise complement of the port and ip. As a check? */
737 aim_tlvlist_add_raw(&inner_tlvlist
, 0x0016, 4, ip_comp
);
738 aim_tlvlist_add_16(&inner_tlvlist
, 0x0017, ~pin
);
740 aim_tlvlist_write(&hdrbs
, &inner_tlvlist
);
742 aim_tlvlist_add_raw(&outer_tlvlist
, 0x0005, byte_stream_curpos(&hdrbs
), hdrbs
.data
);
743 byte_stream_destroy(&hdrbs
);
745 aim_tlvlist_write(&bs
, &outer_tlvlist
);
747 aim_tlvlist_free(inner_tlvlist
);
748 aim_tlvlist_free(outer_tlvlist
);
750 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0006, snacid
, &bs
);
752 byte_stream_destroy(&bs
);
756 * Subtype 0x0006 - Send an "I want to send you this file" message
760 aim_im_sendch2_sendfile_requestdirect(OscarData
*od
, guchar
*cookie
, const char *bn
, const guint8
*ip
, guint16 port
, guint16 requestnumber
, const gchar
*filename
, guint32 size
, guint16 numfiles
)
762 FlapConnection
*conn
;
765 GSList
*outer_tlvlist
= NULL
, *inner_tlvlist
= NULL
;
768 g_return_if_fail(bn
!= NULL
);
769 g_return_if_fail(ip
!= NULL
);
771 conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
);
775 byte_stream_new(&bs
, 1014);
777 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0006, 0x0000, NULL
, 0);
780 aim_im_puticbm(&bs
, cookie
, 0x0002, bn
);
782 aim_tlvlist_add_noval(&outer_tlvlist
, 0x0003);
784 byte_stream_new(&hdrbs
, 512);
786 byte_stream_put16(&hdrbs
, AIM_RENDEZVOUS_PROPOSE
);
787 byte_stream_putraw(&hdrbs
, cookie
, 8);
788 byte_stream_putcaps(&hdrbs
, OSCAR_CAPABILITY_SENDFILE
);
790 aim_tlvlist_add_raw(&inner_tlvlist
, 0x0002, 4, ip
);
791 aim_tlvlist_add_raw(&inner_tlvlist
, 0x0003, 4, ip
);
792 aim_tlvlist_add_16(&inner_tlvlist
, 0x0005, port
);
793 aim_tlvlist_add_16(&inner_tlvlist
, 0x000a, requestnumber
);
794 aim_tlvlist_add_noval(&inner_tlvlist
, 0x000f);
795 /* TODO: Send 0x0016 and 0x0017 */
797 if (filename
!= NULL
)
801 /* Begin TLV t(2711) */
802 byte_stream_new(&inner_bs
, 2+2+4+strlen(filename
)+1);
803 byte_stream_put16(&inner_bs
, (numfiles
> 1) ? 0x0002 : 0x0001);
804 byte_stream_put16(&inner_bs
, numfiles
);
805 byte_stream_put32(&inner_bs
, size
);
807 /* Filename - NULL terminated, for some odd reason */
808 byte_stream_putstr(&inner_bs
, filename
);
809 byte_stream_put8(&inner_bs
, 0x00);
811 aim_tlvlist_add_raw(&inner_tlvlist
, 0x2711, inner_bs
.len
, inner_bs
.data
);
812 byte_stream_destroy(&inner_bs
);
813 /* End TLV t(2711) */
816 aim_tlvlist_write(&hdrbs
, &inner_tlvlist
);
817 aim_tlvlist_add_raw(&outer_tlvlist
, 0x0005, byte_stream_curpos(&hdrbs
), hdrbs
.data
);
818 byte_stream_destroy(&hdrbs
);
820 aim_tlvlist_write(&bs
, &outer_tlvlist
);
822 aim_tlvlist_free(inner_tlvlist
);
823 aim_tlvlist_free(outer_tlvlist
);
825 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0006, snacid
, &bs
);
827 byte_stream_destroy(&bs
);
831 * Subtype 0x0006 - Send a sendfile connect rendezvous ICBM asking the
832 * remote user to connect to us via a proxy server.
835 aim_im_sendch2_sendfile_requestproxy(OscarData
*od
, guchar
*cookie
, const char *bn
, const guint8
*ip
, guint16 pin
, guint16 requestnumber
, const gchar
*filename
, guint32 size
, guint16 numfiles
)
837 FlapConnection
*conn
;
840 GSList
*outer_tlvlist
= NULL
, *inner_tlvlist
= NULL
;
844 conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
);
848 byte_stream_new(&bs
, 1014);
850 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0006, 0x0000, NULL
, 0);
853 aim_im_puticbm(&bs
, cookie
, 0x0002, bn
);
855 aim_tlvlist_add_noval(&outer_tlvlist
, 0x0003);
857 byte_stream_new(&hdrbs
, 512);
859 byte_stream_put16(&hdrbs
, AIM_RENDEZVOUS_PROPOSE
);
860 byte_stream_putraw(&hdrbs
, cookie
, 8);
861 byte_stream_putcaps(&hdrbs
, OSCAR_CAPABILITY_SENDFILE
);
863 aim_tlvlist_add_raw(&inner_tlvlist
, 0x0002, 4, ip
);
864 aim_tlvlist_add_raw(&inner_tlvlist
, 0x0003, 4, ip
);
865 aim_tlvlist_add_16(&inner_tlvlist
, 0x0005, pin
);
866 aim_tlvlist_add_16(&inner_tlvlist
, 0x000a, requestnumber
);
867 aim_tlvlist_add_noval(&inner_tlvlist
, 0x000f);
868 aim_tlvlist_add_noval(&inner_tlvlist
, 0x0010);
870 /* Send the bitwise complement of the port and ip. As a check? */
875 aim_tlvlist_add_raw(&inner_tlvlist
, 0x0016, 4, ip_comp
);
876 aim_tlvlist_add_16(&inner_tlvlist
, 0x0017, ~pin
);
878 if (filename
!= NULL
)
880 ByteStream filename_bs
;
882 /* Begin TLV t(2711) */
883 byte_stream_new(&filename_bs
, 2+2+4+strlen(filename
)+1);
884 byte_stream_put16(&filename_bs
, (numfiles
> 1) ? 0x0002 : 0x0001);
885 byte_stream_put16(&filename_bs
, numfiles
);
886 byte_stream_put32(&filename_bs
, size
);
888 /* Filename - NULL terminated, for some odd reason */
889 byte_stream_putstr(&filename_bs
, filename
);
890 byte_stream_put8(&filename_bs
, 0x00);
892 aim_tlvlist_add_raw(&inner_tlvlist
, 0x2711, filename_bs
.len
, filename_bs
.data
);
893 byte_stream_destroy(&filename_bs
);
894 /* End TLV t(2711) */
897 aim_tlvlist_write(&hdrbs
, &inner_tlvlist
);
899 aim_tlvlist_add_raw(&outer_tlvlist
, 0x0005, byte_stream_curpos(&hdrbs
), hdrbs
.data
);
900 byte_stream_destroy(&hdrbs
);
902 aim_tlvlist_write(&bs
, &outer_tlvlist
);
904 aim_tlvlist_free(inner_tlvlist
);
905 aim_tlvlist_free(outer_tlvlist
);
907 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0006, snacid
, &bs
);
909 byte_stream_destroy(&bs
);
913 incomingim_ch1_parsemsg(OscarData
*od
, aim_userinfo_t
*userinfo
, ByteStream
*message
, struct aim_incomingim_ch1_args
*args
)
915 PurpleAccount
*account
= purple_connection_get_account(od
->gc
);
917 * We're interested in the inner TLV 0x101, which contains precious, precious message.
919 while (byte_stream_bytes_left(message
) >= 4) {
920 guint16 type
= byte_stream_get16(message
);
921 guint16 length
= byte_stream_get16(message
);
924 guint16 msglen
= length
- 4; /* charset + charsubset */
925 guint16 charset
= byte_stream_get16(message
);
926 byte_stream_advance(message
, 2); /* charsubset */
928 msg
= byte_stream_getstr(message
, msglen
);
929 args
->msg
= oscar_decode_im(account
, userinfo
->bn
, charset
, msg
, msglen
);
932 byte_stream_advance(message
, length
);
938 incomingim_ch1(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, guint16 channel
, aim_userinfo_t
*userinfo
, ByteStream
*bs
, guint8
*cookie
)
940 guint16 type
, length
;
941 aim_rxcallback_t userfunc
;
943 struct aim_incomingim_ch1_args args
;
946 memset(&args
, 0, sizeof(args
));
949 * This used to be done using tlvchains. For performance reasons,
950 * I've changed it to process the TLVs in-place. This avoids lots
951 * of per-IM memory allocations.
953 while (byte_stream_bytes_left(bs
) >= 4)
955 type
= byte_stream_get16(bs
);
956 length
= byte_stream_get16(bs
);
958 if (length
> byte_stream_bytes_left(bs
))
960 purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo
->bn
);
964 endpos
= byte_stream_curpos(bs
) + length
;
966 if (type
== 0x0002) { /* Message Block */
968 byte_stream_init(&tlv02
, bs
->data
+ bs
->offset
, length
);
969 incomingim_ch1_parsemsg(od
, userinfo
, &tlv02
, &args
);
970 } else if (type
== 0x0003) { /* Server Ack Requested */
971 args
.icbmflags
|= AIM_IMFLAGS_ACK
;
972 } else if (type
== 0x0004) { /* Message is Auto Response */
973 args
.icbmflags
|= AIM_IMFLAGS_AWAY
;
974 } else if (type
== 0x0006) { /* Message was received offline. */
976 * This flag is set on incoming offline messages for both
977 * AIM and ICQ accounts.
979 args
.icbmflags
|= AIM_IMFLAGS_OFFLINE
;
980 } else if (type
== 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
981 args
.iconlen
= byte_stream_get32(bs
);
982 byte_stream_get16(bs
); /* 0x0001 */
983 args
.iconsum
= byte_stream_get16(bs
);
984 args
.iconstamp
= byte_stream_get32(bs
);
987 * This looks to be a client bug. MacAIM 4.3 will
988 * send this tag, but with all zero values, in the
989 * first message of a conversation. This makes no
990 * sense whatsoever, so I'm going to say its a bug.
992 * You really shouldn't advertise a zero-length icon
997 args
.icbmflags
|= AIM_IMFLAGS_HASICON
;
998 } else if (type
== 0x0009) {
999 args
.icbmflags
|= AIM_IMFLAGS_BUDDYREQ
;
1000 } else if (type
== 0x000b) { /* Non-direct connect typing notification */
1001 args
.icbmflags
|= AIM_IMFLAGS_TYPINGNOT
;
1002 } else if (type
== 0x0016) {
1004 * UTC timestamp for when the message was sent. Only
1005 * provided for offline messages.
1007 args
.timestamp
= byte_stream_get32(bs
);
1011 * This is here to protect ourselves from ourselves. That
1012 * is, if something above doesn't completely parse its value
1013 * section, or, worse, overparses it, this will set the
1014 * stream where it needs to be in order to land on the next
1015 * TLV when the loop continues.
1018 byte_stream_setpos(bs
, endpos
);
1022 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
)))
1023 ret
= userfunc(od
, conn
, frame
, channel
, userinfo
, &args
);
1030 incomingim_ch2_buddylist(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, aim_userinfo_t
*userinfo
, IcbmArgsCh2
*args
, ByteStream
*servdata
)
1033 * This goes like this...
1037 * num of buddies in group
1045 * num of buddies in group
1051 while (byte_stream_bytes_left(servdata
))
1053 guint16 gnlen
, numb
;
1057 gnlen
= byte_stream_get16(servdata
);
1058 gn
= byte_stream_getstr(servdata
, gnlen
);
1059 numb
= byte_stream_get16(servdata
);
1061 for (i
= 0; i
< numb
; i
++) {
1065 bnlen
= byte_stream_get16(servdata
);
1066 bn
= byte_stream_getstr(servdata
, bnlen
);
1068 purple_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo
->bn
, gn
, bn
);
1080 incomingim_ch2_buddyicon_free(OscarData
*od
, IcbmArgsCh2
*args
)
1082 g_free(args
->info
.icon
.icon
);
1088 incomingim_ch2_buddyicon(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, aim_userinfo_t
*userinfo
, IcbmArgsCh2
*args
, ByteStream
*servdata
)
1090 args
->info
.icon
.checksum
= byte_stream_get32(servdata
);
1091 args
->info
.icon
.length
= byte_stream_get32(servdata
);
1092 args
->info
.icon
.timestamp
= byte_stream_get32(servdata
);
1093 args
->info
.icon
.icon
= byte_stream_getraw(servdata
, args
->info
.icon
.length
);
1095 args
->destructor
= (void *)incomingim_ch2_buddyicon_free
;
1101 incomingim_ch2_chat_free(OscarData
*od
, IcbmArgsCh2
*args
)
1103 /* XXX - aim_chat_roominfo_free() */
1104 g_free(args
->info
.chat
.roominfo
.name
);
1110 incomingim_ch2_chat(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, aim_userinfo_t
*userinfo
, IcbmArgsCh2
*args
, ByteStream
*servdata
)
1115 aim_chat_readroominfo(servdata
, &args
->info
.chat
.roominfo
);
1117 args
->destructor
= (void *)incomingim_ch2_chat_free
;
1121 incomingim_ch2_icqserverrelay_free(OscarData
*od
, IcbmArgsCh2
*args
)
1123 g_free((char *)args
->info
.rtfmsg
.msg
);
1127 * The relationship between OSCAR_CAPABILITY_ICQSERVERRELAY and OSCAR_CAPABILITY_ICQRTF is
1128 * kind of odd. This sends the client ICQRTF since that is all that I've seen
1129 * SERVERRELAY used for.
1131 * Note that this is all little-endian. Cringe.
1135 incomingim_ch2_icqserverrelay(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, aim_userinfo_t
*userinfo
, IcbmArgsCh2
*args
, ByteStream
*servdata
)
1137 guint16 hdrlen
, msglen
;
1139 args
->destructor
= (void *)incomingim_ch2_icqserverrelay_free
;
1141 #define SKIP_HEADER(expected_hdrlen) \
1142 hdrlen = byte_stream_getle16(servdata); \
1143 if (hdrlen != expected_hdrlen) { \
1144 purple_debug_warning("oscar", "Expected to find a header with length " #expected_hdrlen "; ignoring message"); \
1147 byte_stream_advance(servdata, hdrlen);
1149 SKIP_HEADER(0x001b);
1150 SKIP_HEADER(0x000e);
1152 args
->info
.rtfmsg
.msgtype
= byte_stream_get8(servdata
);
1154 * Copied from http://iserverd.khstu.ru/oscar/message.html:
1155 * xx byte message flags
1156 * xx xx word (LE) status code
1157 * xx xx word (LE) priority code
1159 * We don't need any of these, so just skip them.
1161 byte_stream_advance(servdata
, 1 + 2 + 2);
1163 msglen
= byte_stream_getle16(servdata
);
1164 args
->info
.rtfmsg
.msg
= byte_stream_getstr(servdata
, msglen
);
1168 incomingim_ch2_sendfile_free(OscarData
*od
, IcbmArgsCh2
*args
)
1170 g_free(args
->info
.sendfile
.filename
);
1173 /* Someone is sending us a file */
1175 incomingim_ch2_sendfile(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, aim_userinfo_t
*userinfo
, IcbmArgsCh2
*args
, ByteStream
*servdata
)
1179 args
->destructor
= (void *)incomingim_ch2_sendfile_free
;
1181 /* Maybe there is a better way to tell what kind of sendfile
1182 * this is? Maybe TLV t(000a)? */
1184 /* subtype is one of AIM_OFT_SUBTYPE_* */
1185 args
->info
.sendfile
.subtype
= byte_stream_get16(servdata
);
1186 args
->info
.sendfile
.totfiles
= byte_stream_get16(servdata
);
1187 args
->info
.sendfile
.totsize
= byte_stream_get32(servdata
);
1190 * I hope to God I'm right when I guess that there is a
1191 * 32 char max filename length for single files. I think
1192 * OFT tends to do that. Gotta love inconsistency. I saw
1193 * a 26 byte filename?
1195 /* AAA - create an byte_stream_getnullstr function (don't anymore)(maybe) */
1196 /* Use an inelegant way of getting the null-terminated filename,
1197 * since there's no easy bstream routine. */
1198 for (flen
= 0; byte_stream_get8(servdata
); flen
++);
1199 byte_stream_advance(servdata
, -flen
-1);
1200 args
->info
.sendfile
.filename
= byte_stream_getstr(servdata
, flen
);
1202 /* There is sometimes more after the null-terminated filename,
1203 * but I'm unsure of its format. */
1204 /* I don't believe him. */
1205 /* There is sometimes a null byte inside a unicode filename,
1206 * but as far as I can tell the filename is the last
1207 * piece of data that will be in this message. --Jonathan */
1210 typedef void (*ch2_args_destructor_t
)(OscarData
*od
, IcbmArgsCh2
*args
);
1212 static int incomingim_ch2(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, guint16 channel
, aim_userinfo_t
*userinfo
, GSList
*tlvlist
, guint8
*cookie
)
1214 aim_rxcallback_t userfunc
;
1215 aim_tlv_t
*block1
, *servdatatlv
;
1219 ByteStream bbs
, sdbs
, *sdbsptr
= NULL
;
1223 char proxyip
[30] = {""};
1224 char clientip
[30] = {""};
1225 char verifiedip
[30] = {""};
1227 memset(&args
, 0, sizeof(args
));
1230 * There's another block of TLVs embedded in the type 5 here.
1232 block1
= aim_tlv_gettlv(tlvlist
, 0x0005, 1);
1235 /* The server sent us ch2 ICBM without ch2 info? Weird. */
1238 byte_stream_init(&bbs
, block1
->value
, block1
->length
);
1241 * First two bytes represent the status of the connection.
1242 * One of the AIM_RENDEZVOUS_ defines.
1244 * 0 is a request, 1 is a cancel, 2 is an accept
1246 args
.status
= byte_stream_get16(&bbs
);
1249 * Next comes the cookie. Should match the ICBM cookie.
1251 cookie2
= byte_stream_getraw(&bbs
, 8);
1252 if (memcmp(cookie
, cookie2
, 8) != 0)
1254 purple_debug_warning("oscar",
1255 "Cookies don't match in rendezvous ICBM, bailing out.\n");
1259 memcpy(args
.cookie
, cookie2
, 8);
1263 * The next 16bytes are a capability block so we can
1264 * identify what type of rendezvous this is.
1266 args
.type
= aim_locate_getcaps(od
, &bbs
, 0x10);
1269 * What follows may be TLVs or nothing, depending on the
1270 * purpose of the message.
1272 * Ack packets for instance have nothing more to them.
1274 list2
= aim_tlvlist_read(&bbs
);
1277 * IP address to proxy the file transfer through.
1279 * TODO: I don't like this. Maybe just read in an int? Or inet_ntoa...
1281 tlv
= aim_tlv_gettlv(list2
, 0x0002, 1);
1282 if ((tlv
!= NULL
) && (tlv
->length
== 4))
1283 snprintf(proxyip
, sizeof(proxyip
), "%hhu.%hhu.%hhu.%hhu",
1284 tlv
->value
[0], tlv
->value
[1],
1285 tlv
->value
[2], tlv
->value
[3]);
1288 * IP address from the perspective of the client.
1290 tlv
= aim_tlv_gettlv(list2
, 0x0003, 1);
1291 if ((tlv
!= NULL
) && (tlv
->length
== 4))
1292 snprintf(clientip
, sizeof(clientip
), "%hhu.%hhu.%hhu.%hhu",
1293 tlv
->value
[0], tlv
->value
[1],
1294 tlv
->value
[2], tlv
->value
[3]);
1297 * Verified IP address (from the perspective of Oscar).
1299 * This is added by the server.
1301 tlv
= aim_tlv_gettlv(list2
, 0x0004, 1);
1302 if ((tlv
!= NULL
) && (tlv
->length
== 4))
1303 snprintf(verifiedip
, sizeof(verifiedip
), "%hhu.%hhu.%hhu.%hhu",
1304 tlv
->value
[0], tlv
->value
[1],
1305 tlv
->value
[2], tlv
->value
[3]);
1308 * Port number for something.
1310 if (aim_tlv_gettlv(list2
, 0x0005, 1))
1311 args
.port
= aim_tlv_get16(list2
, 0x0005, 1);
1314 * File transfer "request number":
1315 * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy
1316 * 0x0002 - "Reply request" for a stage 2 proxy (receiver wants to use proxy)
1317 * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers
1319 if (aim_tlv_gettlv(list2
, 0x000a, 1))
1320 args
.requestnumber
= aim_tlv_get16(list2
, 0x000a, 1);
1323 * Terminate connection/error code. 0x0001 means the other user
1324 * cancelled the connection.
1326 if (aim_tlv_gettlv(list2
, 0x000b, 1))
1327 args
.errorcode
= aim_tlv_get16(list2
, 0x000b, 1);
1330 * Invitation message / chat description.
1332 if (aim_tlv_gettlv(list2
, 0x000c, 1)) {
1333 args
.msg
= aim_tlv_getstr(list2
, 0x000c, 1);
1334 args
.msglen
= aim_tlv_getlength(list2
, 0x000c, 1);
1340 if (aim_tlv_gettlv(list2
, 0x000d, 1))
1341 args
.encoding
= aim_tlv_getstr(list2
, 0x000d, 1);
1346 if (aim_tlv_gettlv(list2
, 0x000e, 1))
1347 args
.language
= aim_tlv_getstr(list2
, 0x000e, 1);
1350 * Flag meaning we should proxy the file transfer through an AIM server
1352 if (aim_tlv_gettlv(list2
, 0x0010, 1))
1353 args
.use_proxy
= TRUE
;
1355 if (strlen(proxyip
))
1356 args
.proxyip
= (char *)proxyip
;
1357 if (strlen(clientip
))
1358 args
.clientip
= (char *)clientip
;
1359 if (strlen(verifiedip
))
1360 args
.verifiedip
= (char *)verifiedip
;
1363 * This must be present in PROPOSALs, but will probably not
1364 * exist in CANCELs and ACCEPTs. Also exists in ICQ Lite
1365 * Beta 4.0 URLs (OSCAR_CAPABILITY_ICQSERVERRELAY).
1367 * Service Data blocks are module-specific in format.
1369 if ((servdatatlv
= aim_tlv_gettlv(list2
, 0x2711 /* 10001 */, 1))) {
1371 byte_stream_init(&sdbs
, servdatatlv
->value
, servdatatlv
->length
);
1375 * The rest of the handling depends on what type it is.
1377 * Not all of them have special handling (yet).
1379 if (args
.type
& OSCAR_CAPABILITY_BUDDYICON
)
1380 incomingim_ch2_buddyicon(od
, conn
, mod
, frame
, snac
, userinfo
, &args
, sdbsptr
);
1381 else if (args
.type
& OSCAR_CAPABILITY_SENDBUDDYLIST
)
1382 incomingim_ch2_buddylist(od
, conn
, mod
, frame
, snac
, userinfo
, &args
, sdbsptr
);
1383 else if (args
.type
& OSCAR_CAPABILITY_CHAT
)
1384 incomingim_ch2_chat(od
, conn
, mod
, frame
, snac
, userinfo
, &args
, sdbsptr
);
1385 else if (args
.type
& OSCAR_CAPABILITY_ICQSERVERRELAY
)
1386 incomingim_ch2_icqserverrelay(od
, conn
, mod
, frame
, snac
, userinfo
, &args
, sdbsptr
);
1387 else if (args
.type
& OSCAR_CAPABILITY_SENDFILE
)
1388 incomingim_ch2_sendfile(od
, conn
, mod
, frame
, snac
, userinfo
, &args
, sdbsptr
);
1391 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
)))
1392 ret
= userfunc(od
, conn
, frame
, channel
, userinfo
, &args
);
1395 if (args
.destructor
)
1396 ((ch2_args_destructor_t
)args
.destructor
)(od
, &args
);
1398 g_free((char *)args
.msg
);
1399 g_free((char *)args
.encoding
);
1400 g_free((char *)args
.language
);
1402 aim_tlvlist_free(list2
);
1407 static int incomingim_ch4(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, guint16 channel
, aim_userinfo_t
*userinfo
, GSList
*tlvlist
, guint8
*cookie
)
1410 aim_rxcallback_t userfunc
;
1412 struct aim_incomingim_ch4_args args
;
1416 * Make a bstream for the meaty part. Yum. Meat.
1418 if (!(block
= aim_tlv_gettlv(tlvlist
, 0x0005, 1)))
1420 byte_stream_init(&meat
, block
->value
, block
->length
);
1422 args
.uin
= byte_stream_getle32(&meat
);
1423 args
.type
= byte_stream_getle8(&meat
);
1424 args
.flags
= byte_stream_getle8(&meat
);
1425 if (args
.type
== 0x1a)
1426 /* There seems to be a problem with the length in SMS msgs from server, this fixed it */
1427 args
.msglen
= block
->length
- 6;
1429 args
.msglen
= byte_stream_getle16(&meat
);
1430 args
.msg
= (gchar
*)byte_stream_getraw(&meat
, args
.msglen
);
1432 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
)))
1433 ret
= userfunc(od
, conn
, frame
, channel
, userinfo
, &args
);
1443 * It can easily be said that parsing ICBMs is THE single
1444 * most difficult thing to do in the in AIM protocol. In
1445 * fact, I think I just did say that.
1447 * Below is the best damned solution I've come up with
1448 * over the past sixteen months of battling with it. This
1449 * can parse both away and normal messages from every client
1450 * I have access to. Its not fast, its not clean. But it works.
1453 static int incomingim(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
1458 aim_userinfo_t userinfo
;
1460 memset(&userinfo
, 0x00, sizeof(aim_userinfo_t
));
1465 cookie
= byte_stream_getraw(bs
, 8);
1470 * Channel 0x0001 is the message channel. It is
1471 * used to send basic ICBMs.
1473 * Channel 0x0002 is the Rendezvous channel, which
1474 * is where Chat Invitiations and various client-client
1475 * connection negotiations come from.
1477 * Channel 0x0003 is used for chat messages.
1479 * Channel 0x0004 is used for ICQ authorization, or
1480 * possibly any system notice.
1483 channel
= byte_stream_get16(bs
);
1486 * Extract the standard user info block.
1488 * Note that although this contains TLVs that appear contiguous
1489 * with the TLVs read below, they are two different pieces. The
1490 * userinfo block contains the number of TLVs that contain user
1491 * information, the rest are not even though there is no separation.
1492 * You can start reading the message TLVs after aim_info_extract()
1493 * parses out the standard userinfo block.
1495 * That also means that TLV types can be duplicated between the
1496 * userinfo block and the rest of the message, however there should
1497 * never be two TLVs of the same type in one block.
1500 aim_info_extract(od
, bs
, &userinfo
);
1503 * From here on, its depends on what channel we're on.
1505 * Technically all channels have a TLV list have this, however,
1506 * for the common channel 1 case, in-place parsing is used for
1507 * performance reasons (less memory allocation).
1511 ret
= incomingim_ch1(od
, conn
, mod
, frame
, snac
, channel
, &userinfo
, bs
, cookie
);
1513 } else if (channel
== 2) {
1517 * Read block of TLVs (not including the userinfo data). All
1518 * further data is derived from what is parsed here.
1520 tlvlist
= aim_tlvlist_read(bs
);
1522 ret
= incomingim_ch2(od
, conn
, mod
, frame
, snac
, channel
, &userinfo
, tlvlist
, cookie
);
1524 aim_tlvlist_free(tlvlist
);
1526 } else if (channel
== 4) {
1529 tlvlist
= aim_tlvlist_read(bs
);
1530 ret
= incomingim_ch4(od
, conn
, mod
, frame
, snac
, channel
, &userinfo
, tlvlist
, cookie
);
1531 aim_tlvlist_free(tlvlist
);
1534 purple_debug_misc("oscar", "icbm: ICBM received on an unsupported channel. Ignoring. (chan = %04x)\n", channel
);
1537 aim_info_free(&userinfo
);
1543 /* Subtype 0x000a */
1544 static int missedcall(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
1547 aim_rxcallback_t userfunc
;
1548 guint16 channel
, nummissed
, reason
;
1549 aim_userinfo_t userinfo
;
1551 while (byte_stream_bytes_left(bs
)) {
1553 channel
= byte_stream_get16(bs
);
1554 aim_info_extract(od
, bs
, &userinfo
);
1555 nummissed
= byte_stream_get16(bs
);
1556 reason
= byte_stream_get16(bs
);
1558 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
)))
1559 ret
= userfunc(od
, conn
, frame
, channel
, &userinfo
, nummissed
, reason
);
1561 aim_info_free(&userinfo
);
1571 * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
1574 int aim_im_denytransfer(OscarData
*od
, const char *bn
, const guchar
*cookie
, guint16 code
)
1576 FlapConnection
*conn
;
1578 aim_snacid_t snacid
;
1579 GSList
*tlvlist
= NULL
;
1581 if (!od
|| !(conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
)))
1584 byte_stream_new(&bs
, 8+2+1+strlen(bn
)+6);
1586 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x000b, 0x0000, NULL
, 0);
1588 byte_stream_putraw(&bs
, cookie
, 8);
1590 byte_stream_put16(&bs
, 0x0002); /* channel */
1591 byte_stream_put8(&bs
, strlen(bn
));
1592 byte_stream_putstr(&bs
, bn
);
1594 aim_tlvlist_add_16(&tlvlist
, 0x0003, code
);
1595 aim_tlvlist_write(&bs
, &tlvlist
);
1596 aim_tlvlist_free(tlvlist
);
1598 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x000b, snacid
, &bs
);
1600 byte_stream_destroy(&bs
);
1607 * Send confirmation for a channel 2 message (Miranda wants it by default).
1610 aim_im_send_icq_confirmation(OscarData
*od
, const char *bn
, const guchar
*cookie
)
1612 FlapConnection
*conn
;
1614 aim_snacid_t snacid
;
1615 guint32 header_size
, data_size
;
1616 guint16 cookie2
= (guint16
)g_random_int();
1618 purple_debug_misc("oscar", "Sending message ack to %s\n", bn
);
1620 header_size
= 8 + 2 + 1 + strlen(bn
) + 2;
1621 data_size
= 2 + 1 + 16 + 4*2 + 2*3 + 4*3 + 1*2 + 2*3 + 1;
1622 byte_stream_new(&bs
, header_size
+ data_size
);
1624 /* The message header. */
1625 aim_im_puticbm(&bs
, cookie
, 0x0002, bn
);
1626 byte_stream_put16(&bs
, 0x0003); /* reason */
1628 /* The actual message. */
1629 byte_stream_putle16(&bs
, 0x1b); /* subheader #1 length */
1630 byte_stream_put8(&bs
, 0x08); /* protocol version */
1631 byte_stream_putcaps(&bs
, OSCAR_CAPABILITY_EMPTY
);
1632 byte_stream_put32(&bs
, 0x3); /* client features */
1633 byte_stream_put32(&bs
, 0x0004); /* DC type */
1634 byte_stream_put16(&bs
, cookie2
); /* a cookie, chosen by fair dice roll */
1635 byte_stream_putle16(&bs
, 0x0e); /* header #2 len? */
1636 byte_stream_put16(&bs
, cookie2
); /* the same cookie again */
1637 byte_stream_put32(&bs
, 0); /* unknown */
1638 byte_stream_put32(&bs
, 0); /* unknown */
1639 byte_stream_put32(&bs
, 0); /* unknown */
1640 byte_stream_put8(&bs
, 0x01); /* plain text message */
1641 byte_stream_put8(&bs
, 0x00); /* no message flags */
1642 byte_stream_put16(&bs
, 0x0000); /* no icq status */
1643 byte_stream_put16(&bs
, 0x0100); /* priority */
1644 byte_stream_putle16(&bs
, 1); /* query message len */
1645 byte_stream_put8(&bs
, 0x00); /* empty query message */
1647 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x000b, 0x0000, NULL
, 0);
1648 conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
);
1649 g_warn_if_fail(conn
);
1651 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x000b,
1654 byte_stream_destroy(&bs
);
1658 * Subtype 0x000b - Receive the response from an ICQ status message
1659 * request (in which case this contains the ICQ status message) or
1660 * a file transfer or direct IM request was declined.
1662 static int clientautoresp(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
1665 aim_rxcallback_t userfunc
;
1666 guint16 channel
, reason
;
1674 PurpleAccount
*account
;
1676 PurplePresence
*presence
;
1677 PurpleStatus
*status
;
1679 cookie
= byte_stream_getraw(bs
, 8);
1680 channel
= byte_stream_get16(bs
);
1681 bnlen
= byte_stream_get8(bs
);
1682 bn
= byte_stream_getstr(bs
, bnlen
);
1683 reason
= byte_stream_get16(bs
);
1685 if (channel
== 0x0002)
1687 hdrlen
= byte_stream_getle16(bs
);
1688 if (hdrlen
== 27 && bs
->len
> (27 + 51)) {
1689 byte_stream_advance(bs
, 51);
1690 num1
= byte_stream_getle16(bs
);
1691 num2
= byte_stream_getle16(bs
);
1692 purple_debug_misc("oscar", "X-Status: num1 %hu, num2 %hu\n", num1
, num2
);
1694 if (num1
== 0x4f00 && num2
== 0x3b00) {
1695 byte_stream_advance(bs
, 86);
1696 curpos
= byte_stream_curpos(bs
);
1697 xml
= byte_stream_getstr(bs
, bs
->len
- curpos
);
1698 purple_debug_misc("oscar", "X-Status: Received XML reply\n");
1701 char *tmp1
, *tmp2
, *unescaped_xstatus
;
1703 /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", xml); */
1705 xstatus
= g_string_new(NULL
);
1707 tmp1
= strstr(xml
, "<title>");
1710 tmp2
= strstr(tmp1
, "</title>");
1712 g_string_append_len(xstatus
, tmp1
, tmp2
- tmp1
);
1714 tmp1
= strstr(xml
, "<desc>");
1717 tmp2
= strstr(tmp1
, "</desc>");
1719 if (xstatus
->len
> 0 && tmp2
> tmp1
)
1720 g_string_append(xstatus
, " - ");
1721 g_string_append_len(xstatus
, tmp1
, tmp2
- tmp1
);
1724 unescaped_xstatus
= purple_unescape_text(xstatus
->str
);
1725 g_string_free(xstatus
, TRUE
);
1726 if (*unescaped_xstatus
) {
1727 purple_debug_misc("oscar", "X-Status reply: %s\n", unescaped_xstatus
);
1728 account
= purple_connection_get_account(od
->gc
);
1729 buddy
= purple_find_buddy(account
, bn
);
1730 presence
= purple_buddy_get_presence(buddy
);
1731 status
= purple_presence_get_status(presence
, "mood");
1733 purple_prpl_got_user_status(account
, bn
,
1735 PURPLE_MOOD_NAME
, purple_status_get_attr_string(status
, PURPLE_MOOD_NAME
),
1736 PURPLE_MOOD_COMMENT
, unescaped_xstatus
, NULL
);
1739 g_free(unescaped_xstatus
);
1741 purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n");
1744 purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n");
1749 } else if (channel
== 0x0004) { /* ICQ message */
1751 case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */
1752 guint8 statusmsgtype
, *msg
;
1756 len
= byte_stream_getle16(bs
); /* Should be 0x001b */
1757 byte_stream_advance(bs
, len
); /* Unknown */
1759 len
= byte_stream_getle16(bs
); /* Should be 0x000e */
1760 byte_stream_advance(bs
, len
); /* Unknown */
1762 statusmsgtype
= byte_stream_getle8(bs
);
1763 switch (statusmsgtype
) {
1765 state
= AIM_ICQ_STATE_AWAY
;
1768 state
= AIM_ICQ_STATE_AWAY
| AIM_ICQ_STATE_BUSY
;
1771 state
= AIM_ICQ_STATE_AWAY
| AIM_ICQ_STATE_OUT
;
1774 state
= AIM_ICQ_STATE_AWAY
| AIM_ICQ_STATE_DND
| AIM_ICQ_STATE_BUSY
;
1777 state
= AIM_ICQ_STATE_CHAT
;
1784 byte_stream_getle8(bs
); /* Unknown - 0x03 Maybe this means this is an auto-reply */
1785 byte_stream_getle16(bs
); /* Unknown - 0x0000 */
1786 byte_stream_getle16(bs
); /* Unknown - 0x0000 */
1788 len
= byte_stream_getle16(bs
);
1789 msg
= byte_stream_getraw(bs
, len
);
1791 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
)))
1792 ret
= userfunc(od
, conn
, frame
, channel
, bn
, reason
, state
, msg
);
1798 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
)))
1799 ret
= userfunc(od
, conn
, frame
, channel
, bn
, reason
);
1812 * Subtype 0x000c - Receive an ack after sending an ICBM. The ack contains the ICBM header of the message you sent.
1814 static int msgack(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
1820 cookie
= byte_stream_getraw(bs
, 8);
1821 byte_stream_get16(bs
); /* ch */
1822 bn
= byte_stream_getstr(bs
, byte_stream_get8(bs
));
1824 purple_debug_info("oscar", "Sent message to %s.\n", bn
);
1833 * Subtype 0x0010 - Request any offline messages that are waiting for
1834 * us. This is the "new" way of handling offline messages which is
1835 * used for both AIM and ICQ. The old way is to use the ugly
1836 * aim_icq_reqofflinemsgs() function, but that is no longer necessary.
1838 * We set the 0x00000100 flag on the ICBM message parameters, which
1839 * tells the oscar servers that we support offline messages. When we
1840 * set that flag the servers do not automatically send us offline
1841 * messages. Instead we must request them using this function. This
1842 * should happen after sending the 0x0001/0x0002 "client online" SNAC.
1844 int aim_im_reqofflinemsgs(OscarData
*od
)
1846 FlapConnection
*conn
;
1848 if (!od
|| !(conn
= flap_connection_findbygroup(od
, 0x0002)))
1851 aim_genericreq_n(od
, conn
, SNAC_FAMILY_ICBM
, 0x0010);
1857 * Subtype 0x0014 - Send a mini typing notification (mtn) packet.
1859 * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
1860 * and Purple 0.60 and newer.
1863 int aim_im_sendmtn(OscarData
*od
, guint16 channel
, const char *bn
, guint16 event
)
1865 FlapConnection
*conn
;
1867 aim_snacid_t snacid
;
1869 if (!od
|| !(conn
= flap_connection_findbygroup(od
, 0x0002)))
1875 byte_stream_new(&bs
, 11 + strlen(bn
) + 2);
1877 snacid
= aim_cachesnac(od
, SNAC_FAMILY_ICBM
, 0x0014, 0x0000, NULL
, 0);
1880 byte_stream_put32(&bs
, 0x00000000);
1881 byte_stream_put32(&bs
, 0x00000000);
1884 * Channel (should be 0x0001 for mtn)
1886 byte_stream_put16(&bs
, channel
);
1891 byte_stream_put8(&bs
, strlen(bn
));
1892 byte_stream_putstr(&bs
, bn
);
1895 * Event (should be 0x0000, 0x0001, or 0x0002 for mtn)
1897 byte_stream_put16(&bs
, event
);
1899 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_ICBM
, 0x0014, snacid
, &bs
);
1901 byte_stream_destroy(&bs
);
1907 * Subtype 0x0006 - Send eXtra Status request
1909 int icq_im_xstatus_request(OscarData
*od
, const char *sn
)
1911 FlapConnection
*conn
;
1912 aim_snacid_t snacid
;
1914 GSList
*outer_tlvlist
= NULL
, *inner_tlvlist
= NULL
;
1915 ByteStream bs
, header
, plugindata
;
1916 PurpleAccount
*account
;
1921 static const guint8 pluginid
[] = {
1922 0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1,
1923 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00
1926 static const guint8 c_plugindata
[] = {
1928 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1929 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00,
1930 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00,
1931 0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C,
1932 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70,
1933 0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
1934 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41,
1935 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00
1939 if (!od
|| !(conn
= flap_connection_findbygroup(od
, 0x0004)))
1945 fmt
= "<N><QUERY><Q><PluginID>srvMng</PluginID></Q></QUERY><NOTIFY><srv><id>cAwaySrv</id><req><id>AwayStat</id><trans>2</trans><senderId>%s</senderId></req></srv></NOTIFY></N>\r\n";
1947 account
= purple_connection_get_account(od
->gc
);
1949 statxml
= g_strdup_printf(fmt
, account
->username
);
1950 xmllen
= strlen(statxml
);
1952 aim_icbm_makecookie(cookie
);
1954 byte_stream_new(&bs
, 10 + 8 + 2 + 1 + strlen(sn
) + 2
1955 + 2 + 2 + 8 + 16 + 2 + 2 + 2 + 2 + 2
1956 + 2 + 2 + sizeof(c_plugindata
) + xmllen
1959 snacid
= aim_cachesnac(od
, 0x0004, 0x0006, 0x0000, NULL
, 0);
1960 aim_im_puticbm(&bs
, cookie
, 0x0002, sn
);
1962 byte_stream_new(&header
, (7*2) + 16 + 8 + 2 + sizeof(c_plugindata
) + xmllen
); /* TLV 0x0005 Stream + Size */
1963 byte_stream_put16(&header
, 0x0000); /* Message Type: Request */
1964 byte_stream_putraw(&header
, cookie
, sizeof(cookie
)); /* Message ID */
1965 byte_stream_putraw(&header
, pluginid
, sizeof(pluginid
)); /* Plugin ID */
1967 aim_tlvlist_add_16(&inner_tlvlist
, 0x000a, 0x0001);
1968 aim_tlvlist_add_noval(&inner_tlvlist
, 0x000f);
1970 /* Add Plugin Specific Data */
1971 byte_stream_new(&plugindata
, (sizeof(c_plugindata
) + xmllen
));
1972 byte_stream_putraw(&plugindata
, c_plugindata
, sizeof(c_plugindata
)); /* Content of TLV 0x2711 */
1973 byte_stream_putraw(&plugindata
, (const guint8
*)statxml
, xmllen
);
1975 aim_tlvlist_add_raw(&inner_tlvlist
, 0x2711, (sizeof(c_plugindata
) + xmllen
), plugindata
.data
);
1977 aim_tlvlist_write(&header
, &inner_tlvlist
);
1978 aim_tlvlist_free(inner_tlvlist
);
1980 aim_tlvlist_add_raw(&outer_tlvlist
, 0x0005, byte_stream_curpos(&header
), header
.data
);
1981 aim_tlvlist_add_noval(&outer_tlvlist
, 0x0003); /* Empty TLV 0x0003 */
1983 aim_tlvlist_write(&bs
, &outer_tlvlist
);
1985 purple_debug_misc("oscar", "X-Status Request\n");
1986 flap_connection_send_snac_with_priority(od
, conn
, 0x0004, 0x0006, snacid
, &bs
, TRUE
);
1988 aim_tlvlist_free(outer_tlvlist
);
1989 byte_stream_destroy(&header
);
1990 byte_stream_destroy(&plugindata
);
1991 byte_stream_destroy(&bs
);
1997 int icq_relay_xstatus(OscarData
*od
, const char *sn
, const guchar
*cookie
)
1999 FlapConnection
*conn
;
2001 aim_snacid_t snacid
;
2002 PurpleAccount
*account
;
2003 PurpleStatus
*status
;
2005 const char *formatted_msg
;
2011 static const guint8 plugindata
[] = {
2013 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2014 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2015 0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1,
2016 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2017 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F,
2018 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0,
2019 0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00,
2020 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75,
2021 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
2022 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
2023 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00,
2024 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2025 0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00
2028 fmt
= "<NR><RES><ret event='OnRemoteNotification'><srv><id>cAwaySrv</id><val srv_id='cAwaySrv'><Root><CASXtraSetAwayMessage></CASXtraSetAwayMessage>&l t;uin>%s</uin><index>1</index><title>%s</title><desc>%s</desc></Root></val></srv><srv><id>cRandomizerSrv</id><val srv_id='cRandomizerSrv'>undefined</val></srv></ret></RES></NR>\r\n";
2030 if (!od
|| !(conn
= flap_connection_findbygroup(od
, 0x0002)))
2036 account
= purple_connection_get_account(od
->gc
);
2040 /* if (purple_strequal(account->username, sn))
2041 icq_im_xstatus_request(od, sn); */
2043 status
= purple_presence_get_active_status(account
->presence
);
2047 title
= purple_status_get_name(status
);
2051 formatted_msg
= purple_status_get_attr_string(status
, "message");
2055 msg
= purple_markup_strip_html(formatted_msg
);
2059 statxml
= g_strdup_printf(fmt
, account
->username
, title
, msg
);
2060 len
= strlen(statxml
);
2062 purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg
, msg
);
2064 byte_stream_new(&bs
, 10 + 8 + 2 + 1 + strlen(sn
) + 2 + sizeof(plugindata
) + len
); /* 16 extra */
2066 snacid
= aim_cachesnac(od
, 0x0004, 0x000b, 0x0000, NULL
, 0);
2067 aim_im_puticbm(&bs
, cookie
, 0x0002, sn
);
2068 byte_stream_put16(&bs
, 0x0003);
2069 byte_stream_putraw(&bs
, plugindata
, sizeof(plugindata
));
2070 byte_stream_putraw(&bs
, (const guint8
*)statxml
, len
);
2072 flap_connection_send_snac_with_priority(od
, conn
, 0x0004, 0x000b, snacid
, &bs
, TRUE
);
2076 byte_stream_destroy(&bs
);
2082 * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
2084 * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
2085 * and Purple 0.60 and newer.
2088 static int mtn_receive(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
2091 aim_rxcallback_t userfunc
;
2094 guint16 channel
, event
;
2096 byte_stream_advance(bs
, 8); /* ICBM cookie */
2097 channel
= byte_stream_get16(bs
);
2098 bnlen
= byte_stream_get8(bs
);
2099 bn
= byte_stream_getstr(bs
, bnlen
);
2100 event
= byte_stream_get16(bs
);
2102 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
)))
2103 ret
= userfunc(od
, conn
, frame
, channel
, bn
, event
);
2111 snachandler(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
2113 if (snac
->subtype
== 0x0001)
2114 return error(od
, conn
, mod
, frame
, snac
, bs
);
2115 else if (snac
->subtype
== 0x0005)
2116 return aim_im_paraminfo(od
, conn
, mod
, frame
, snac
, bs
);
2117 else if (snac
->subtype
== 0x0007)
2118 return incomingim(od
, conn
, mod
, frame
, snac
, bs
);
2119 else if (snac
->subtype
== 0x000a)
2120 return missedcall(od
, conn
, mod
, frame
, snac
, bs
);
2121 else if (snac
->subtype
== 0x000b)
2122 return clientautoresp(od
, conn
, mod
, frame
, snac
, bs
);
2123 else if (snac
->subtype
== 0x000c)
2124 return msgack(od
, conn
, mod
, frame
, snac
, bs
);
2125 else if (snac
->subtype
== 0x0014)
2126 return mtn_receive(od
, conn
, mod
, frame
, snac
, bs
);
2132 msg_modfirst(OscarData
*od
, aim_module_t
*mod
)
2134 mod
->family
= SNAC_FAMILY_ICBM
;
2135 mod
->version
= 0x0001;
2136 mod
->toolid
= 0x0110;
2137 mod
->toolversion
= 0x0629;
2139 strncpy(mod
->name
, "messaging", sizeof(mod
->name
));
2140 mod
->snachandler
= snachandler
;