Annotation of ChivanetAimPidgin/oscarprpl/src/c/family_icbm.c, revision 1.1.1.1
1.1 snw 1: /*
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.
5: *
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.
10: *
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.
15: *
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
19: */
20:
21: /*
22: * Family 0x0004 - Routines for sending/receiving Instant Messages.
23: *
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.
35: *
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
39: * use of this field.
40: *
41: * TODO: Split this up into an im.c file an an icbm.c file. It
42: * will be beautiful, you'll see.
43: *
44: * Make sure flap_connection_findbygroup is used by all functions.
45: */
46:
47: #include "encoding.h"
48: #include "oscar.h"
49: #include "peer.h"
50:
51: #ifdef _WIN32
52: #include "win32dep.h"
53: #endif
54:
55: #include "util.h"
56:
57: static const char * const errcodereason[] = {
58: N_("Invalid error"),
59: N_("Not logged in"),
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")
74: };
75: static const int errcodereasonlen = G_N_ELEMENTS(errcodereason);
76:
77: /**
78: * Add a standard ICBM header to the given bstream with the given
79: * information.
80: *
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.
86: */
87: static int aim_im_puticbm(ByteStream *bs, const guchar *c, guint16 channel, const char *bn)
88: {
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);
94: }
95:
96: /**
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?
100: */
101: void aim_icbm_makecookie(guchar *cookie)
102: {
103: int i;
104:
105: /* Should be like "21CBF95" and null terminated */
106: for (i = 0; i < 7; i++)
107: cookie[i] = 0x30 + ((guchar)rand() % 10);
108: cookie[7] = '\0';
109: }
110:
111: /*
112: * Subtype 0x0001 - Error
113: */
114: static int
115: error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
116: {
117: aim_snac_t *snac2;
118: guint16 reason, errcode = 0;
119: const char *bn;
120: GSList *tlvlist;
121: PurpleConnection *gc = od->gc;
122: #ifdef TODOFT
123: PurpleXfer *xfer;
124: #endif
125: const char *reason_str;
126: char *buf;
127:
128: snac2 = aim_remsnac(od, snac->id);
129: if (!snac2) {
130: purple_debug_misc("oscar", "icbm error: received response from unknown request!\n");
131: return 1;
132: }
133:
134: if (snac2->family != SNAC_FAMILY_ICBM) {
135: purple_debug_misc("oscar", "icbm error: received response from invalid request! %d\n", snac2->family);
136: g_free(snac2->data);
137: g_free(snac2);
138: return 1;
139: }
140:
141: /* Data is assumed to be the destination bn */
142: bn = snac2->data;
143: if (!bn || bn[0] == '\0') {
144: purple_debug_misc("oscar", "icbm error: received response from request without a buddy name!\n");
145: g_free(snac2->data);
146: g_free(snac2);
147: return 1;
148: }
149:
150: reason = byte_stream_get16(bs);
151:
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);
156:
157: purple_debug_error("oscar",
158: "Message error with bn %s and reason %hu and errcode %hu\n",
159: bn, reason, errcode);
160:
161: #ifdef TODOFT
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);
165: return 1;
166: }
167: #endif
168:
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]));
174: else
175: buf = g_strdup_printf(_("Unable to send message: %s"), reason_str);
176:
177: if (!purple_conv_present_error(bn, purple_connection_get_account(gc), buf)) {
178: g_free(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]));
183: else
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);
187: }
188: g_free(buf);
189:
190: g_free(snac2->data);
191: g_free(snac2);
192:
193: return 1;
194: }
195:
196: /**
197: * Subtype 0x0002 - Set ICBM parameters.
198: *
199: * I definitely recommend sending this. If you don't, you'll be stuck
200: * with the rather unreasonable defaults.
201: *
202: */
203: int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params)
204: {
205: FlapConnection *conn;
206: ByteStream bs;
207: aim_snacid_t snacid;
208:
209: if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
210: return -EINVAL;
211:
212: if (!params)
213: return -EINVAL;
214:
215: byte_stream_new(&bs, 16);
216:
217: /* This is read-only (see Parameter Reply). Must be set to zero here. */
218: byte_stream_put16(&bs, 0x0000);
219:
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);
226:
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);
229:
230: byte_stream_destroy(&bs);
231:
232: return 0;
233: }
234:
235: /**
236: * Subtype 0x0004 - Request ICBM parameter information.
237: *
238: */
239: int aim_im_reqparams(OscarData *od)
240: {
241: FlapConnection *conn;
242:
243: if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
244: return -EINVAL;
245:
246: aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_ICBM, 0x0004);
247:
248: return 0;
249: }
250:
251: /**
252: * Subtype 0x0005 - Receive parameter information.
253: *
254: */
255: static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
256: {
257: struct aim_icbmparameters params;
258:
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);
265:
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;
274:
275: aim_im_setparams(od, ¶ms);
276:
277: return 0;
278: }
279:
280: /**
281: * Subtype 0x0006 - Send an ICBM (instant message).
282: *
283: *
284: * Possible flags:
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).
288: *
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.
294: */
295: int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
296: {
297: FlapConnection *conn;
298: aim_snacid_t snacid;
299: ByteStream data;
300: guchar cookie[8];
301: int msgtlvlen;
302:
303: if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
304: return -EINVAL;
305:
306: if (!args)
307: return -EINVAL;
308:
309: if (!args->msg || (args->msglen <= 0))
310: return -EINVAL;
311:
312: if (args->msglen > MAXMSGLEN)
313: return -E2BIG;
314:
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;
320:
321: byte_stream_new(&data, msgtlvlen + 128);
322:
323: /* Generate an ICBM cookie */
324: aim_icbm_makecookie(cookie);
325:
326: /* ICBM header */
327: aim_im_puticbm(&data, cookie, 0x0001, args->destbn);
328:
329: /* Message TLV (type 0x0002) */
330: byte_stream_put16(&data, 0x0002);
331: byte_stream_put16(&data, msgtlvlen);
332:
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);
337:
338: /* Insert message text in a TLV (type 0x0101) */
339: byte_stream_put16(&data, 0x0101);
340:
341: /* Message block length */
342: byte_stream_put16(&data, args->msglen + 0x04);
343:
344: /* Character set */
345: byte_stream_put16(&data, args->charset);
346: /* Character subset -- we always use 0 here */
347: byte_stream_put16(&data, 0x0);
348:
349: /* Message. Not terminated */
350: byte_stream_putraw(&data, (guchar *)args->msg, args->msglen);
351:
352: /* Set the Autoresponse flag */
353: if (args->flags & AIM_IMFLAGS_AWAY) {
354: byte_stream_put16(&data, 0x0004);
355: byte_stream_put16(&data, 0x0000);
356: } else {
357: /* Set the Request Acknowledge flag */
358: byte_stream_put16(&data, 0x0003);
359: byte_stream_put16(&data, 0x0000);
360:
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);
365: }
366: }
367:
368: /*
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.
372: */
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);
380: }
381:
382: /*
383: * Set the Buddy Icon Requested flag.
384: * XXX - Every time? Surely not...
385: */
386: if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
387: byte_stream_put16(&data, 0x0009);
388: byte_stream_put16(&data, 0x0000);
389: }
390:
391: /* XXX - should be optional */
392: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, args->destbn, strlen(args->destbn)+1);
393:
394: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &data);
395: byte_stream_destroy(&data);
396:
397: /* clean out SNACs over 60sec old */
398: aim_cleansnacs(od, 60);
399:
400: return 0;
401: }
402:
403: /*
404: * Subtype 0x0006 - Send a chat invitation.
405: */
406: int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
407: {
408: FlapConnection *conn;
409: ByteStream bs;
410: aim_snacid_t snacid;
411: IcbmCookie *msgcookie;
412: struct aim_invite_priv *priv;
413: guchar cookie[8];
414: GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
415: ByteStream hdrbs;
416:
417: if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
418: return -EINVAL;
419:
420: if (!bn || !msg || !roomname)
421: return -EINVAL;
422:
423: aim_icbm_makecookie(cookie);
424:
425: byte_stream_new(&bs, 1142+strlen(bn)+strlen(roomname)+strlen(msg));
426:
427: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, bn, strlen(bn)+1);
428:
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;
435:
436: if ((msgcookie = aim_mkcookie(cookie, AIM_COOKIETYPE_INVITE, priv)))
437: aim_cachecookie(od, msgcookie);
438: else
439: g_free(priv);
440:
441: /* ICBM Header */
442: aim_im_puticbm(&bs, cookie, 0x0002, bn);
443:
444: /*
445: * TLV t(0005)
446: *
447: * Everything else is inside this TLV.
448: *
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.
452: *
453: */
454: byte_stream_new(&hdrbs, 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2);
455:
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);
459:
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);
465:
466: aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
467: byte_stream_destroy(&hdrbs);
468:
469: aim_tlvlist_write(&bs, &outer_tlvlist);
470:
471: aim_tlvlist_free(inner_tlvlist);
472: aim_tlvlist_free(outer_tlvlist);
473:
474: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
475:
476: byte_stream_destroy(&bs);
477:
478: return 0;
479: }
480:
481: /**
482: * Subtype 0x0006 - Send your icon to a given user.
483: *
484: * This is also performance sensitive. (If you can believe it...)
485: *
486: */
487: int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum)
488: {
489: FlapConnection *conn;
490: ByteStream bs;
491: aim_snacid_t snacid;
492: guchar cookie[8];
493:
494: if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
495: return -EINVAL;
496:
497: if (!bn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
498: return -EINVAL;
499:
500: aim_icbm_makecookie(cookie);
501:
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);
503:
504: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
505:
506: /* ICBM header */
507: aim_im_puticbm(&bs, cookie, 0x0002, bn);
508:
509: /*
510: * TLV t(0005)
511: *
512: * Encompasses everything below.
513: */
514: byte_stream_put16(&bs, 0x0005);
515: byte_stream_put16(&bs, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
516:
517: byte_stream_put16(&bs, 0x0000);
518: byte_stream_putraw(&bs, cookie, 8);
519: byte_stream_putcaps(&bs, OSCAR_CAPABILITY_BUDDYICON);
520:
521: /* TLV t(000a) */
522: byte_stream_put16(&bs, 0x000a);
523: byte_stream_put16(&bs, 0x0002);
524: byte_stream_put16(&bs, 0x0001);
525:
526: /* TLV t(000f) */
527: byte_stream_put16(&bs, 0x000f);
528: byte_stream_put16(&bs, 0x0000);
529:
530: /* TLV t(2711) */
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);
539:
540: /* TLV t(0003) */
541: byte_stream_put16(&bs, 0x0003);
542: byte_stream_put16(&bs, 0x0000);
543:
544: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
545:
546: byte_stream_destroy(&bs);
547:
548: return 0;
549: }
550:
551: /**
552: * Cancel a rendezvous invitation. It could be an invitation to
553: * establish a direct connection, or a file-send, or a chat invite.
554: */
555: void
556: aim_im_sendch2_cancel(PeerConnection *peer_conn)
557: {
558: OscarData *od;
559: FlapConnection *conn;
560: ByteStream bs;
561: aim_snacid_t snacid;
562: GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
563: ByteStream hdrbs;
564:
565: od = peer_conn->od;
566: conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
567: if (conn == NULL)
568: return;
569:
570: byte_stream_new(&bs, 118+strlen(peer_conn->bn));
571:
572: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
573:
574: /* ICBM header */
575: aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn);
576:
577: aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
578:
579: byte_stream_new(&hdrbs, 64);
580:
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);
584:
585: /* This TLV means "cancel!" */
586: aim_tlvlist_add_16(&inner_tlvlist, 0x000b, 0x0001);
587: aim_tlvlist_write(&hdrbs, &inner_tlvlist);
588:
589: aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
590: byte_stream_destroy(&hdrbs);
591:
592: aim_tlvlist_write(&bs, &outer_tlvlist);
593:
594: aim_tlvlist_free(inner_tlvlist);
595: aim_tlvlist_free(outer_tlvlist);
596:
597: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
598:
599: byte_stream_destroy(&bs);
600: }
601:
602: /**
603: * Subtype 0x0006 - Send an "I accept and I've connected to
604: * you" message.
605: */
606: void
607: aim_im_sendch2_connected(PeerConnection *peer_conn)
608: {
609: OscarData *od;
610: FlapConnection *conn;
611: ByteStream bs;
612: aim_snacid_t snacid;
613:
614: od = peer_conn->od;
615: conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
616: if (conn == NULL)
617: return;
618:
619: byte_stream_new(&bs, 11+strlen(peer_conn->bn) + 4+2+8+16);
620:
621: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
622:
623: /* ICBM header */
624: aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn);
625:
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);
631:
632: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
633:
634: byte_stream_destroy(&bs);
635: }
636:
637: /**
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"
643: */
644: void
645: aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber)
646: {
647: FlapConnection *conn;
648: ByteStream bs;
649: aim_snacid_t snacid;
650: GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
651: ByteStream hdrbs;
652:
653: conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
654: if (conn == NULL)
655: return;
656:
657: byte_stream_new(&bs, 246+strlen(bn));
658:
659: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
660:
661: /* ICBM header */
662: aim_im_puticbm(&bs, cookie, 0x0002, bn);
663:
664: aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
665:
666: byte_stream_new(&hdrbs, 128);
667:
668: byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
669: byte_stream_putraw(&hdrbs, cookie, 8);
670: byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM);
671:
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);
678:
679: aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
680: byte_stream_destroy(&hdrbs);
681:
682: aim_tlvlist_write(&bs, &outer_tlvlist);
683:
684: aim_tlvlist_free(inner_tlvlist);
685: aim_tlvlist_free(outer_tlvlist);
686:
687: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
688:
689: byte_stream_destroy(&bs);
690: }
691:
692: /**
693: * Subtype 0x0006 - Send a direct connect rendezvous ICBM asking the
694: * remote user to connect to us via a proxy server.
695: */
696: void
697: aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber)
698: {
699: FlapConnection *conn;
700: ByteStream bs;
701: aim_snacid_t snacid;
702: GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
703: ByteStream hdrbs;
704: guint8 ip_comp[4];
705:
706: conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
707: if (conn == NULL)
708: return;
709:
710: byte_stream_new(&bs, 246+strlen(bn));
711:
712: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
713:
714: /* ICBM header */
715: aim_im_puticbm(&bs, cookie, 0x0002, bn);
716:
717: aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
718:
719: byte_stream_new(&hdrbs, 128);
720:
721: byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
722: byte_stream_putraw(&hdrbs, cookie, 8);
723: byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM);
724:
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);
731:
732: /* Send the bitwise complement of the port and ip. As a check? */
733: ip_comp[0] = ~ip[0];
734: ip_comp[1] = ~ip[1];
735: ip_comp[2] = ~ip[2];
736: ip_comp[3] = ~ip[3];
737: aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
738: aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
739:
740: aim_tlvlist_write(&hdrbs, &inner_tlvlist);
741:
742: aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
743: byte_stream_destroy(&hdrbs);
744:
745: aim_tlvlist_write(&bs, &outer_tlvlist);
746:
747: aim_tlvlist_free(inner_tlvlist);
748: aim_tlvlist_free(outer_tlvlist);
749:
750: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
751:
752: byte_stream_destroy(&bs);
753: }
754:
755: /**
756: * Subtype 0x0006 - Send an "I want to send you this file" message
757: *
758: */
759: void
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)
761: {
762: FlapConnection *conn;
763: ByteStream bs;
764: aim_snacid_t snacid;
765: GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
766: ByteStream hdrbs;
767:
768: g_return_if_fail(bn != NULL);
769: g_return_if_fail(ip != NULL);
770:
771: conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
772: if (conn == NULL)
773: return;
774:
775: byte_stream_new(&bs, 1014);
776:
777: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
778:
779: /* ICBM header */
780: aim_im_puticbm(&bs, cookie, 0x0002, bn);
781:
782: aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
783:
784: byte_stream_new(&hdrbs, 512);
785:
786: byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
787: byte_stream_putraw(&hdrbs, cookie, 8);
788: byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE);
789:
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 */
796:
797: if (filename != NULL)
798: {
799: ByteStream inner_bs;
800:
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);
806:
807: /* Filename - NULL terminated, for some odd reason */
808: byte_stream_putstr(&inner_bs, filename);
809: byte_stream_put8(&inner_bs, 0x00);
810:
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) */
814: }
815:
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);
819:
820: aim_tlvlist_write(&bs, &outer_tlvlist);
821:
822: aim_tlvlist_free(inner_tlvlist);
823: aim_tlvlist_free(outer_tlvlist);
824:
825: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
826:
827: byte_stream_destroy(&bs);
828: }
829:
830: /**
831: * Subtype 0x0006 - Send a sendfile connect rendezvous ICBM asking the
832: * remote user to connect to us via a proxy server.
833: */
834: void
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)
836: {
837: FlapConnection *conn;
838: ByteStream bs;
839: aim_snacid_t snacid;
840: GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
841: ByteStream hdrbs;
842: guint8 ip_comp[4];
843:
844: conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
845: if (conn == NULL)
846: return;
847:
848: byte_stream_new(&bs, 1014);
849:
850: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
851:
852: /* ICBM header */
853: aim_im_puticbm(&bs, cookie, 0x0002, bn);
854:
855: aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
856:
857: byte_stream_new(&hdrbs, 512);
858:
859: byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
860: byte_stream_putraw(&hdrbs, cookie, 8);
861: byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE);
862:
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);
869:
870: /* Send the bitwise complement of the port and ip. As a check? */
871: ip_comp[0] = ~ip[0];
872: ip_comp[1] = ~ip[1];
873: ip_comp[2] = ~ip[2];
874: ip_comp[3] = ~ip[3];
875: aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
876: aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
877:
878: if (filename != NULL)
879: {
880: ByteStream filename_bs;
881:
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);
887:
888: /* Filename - NULL terminated, for some odd reason */
889: byte_stream_putstr(&filename_bs, filename);
890: byte_stream_put8(&filename_bs, 0x00);
891:
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) */
895: }
896:
897: aim_tlvlist_write(&hdrbs, &inner_tlvlist);
898:
899: aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
900: byte_stream_destroy(&hdrbs);
901:
902: aim_tlvlist_write(&bs, &outer_tlvlist);
903:
904: aim_tlvlist_free(inner_tlvlist);
905: aim_tlvlist_free(outer_tlvlist);
906:
907: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
908:
909: byte_stream_destroy(&bs);
910: }
911:
912: static void
913: incomingim_ch1_parsemsg(OscarData *od, aim_userinfo_t *userinfo, ByteStream *message, struct aim_incomingim_ch1_args *args)
914: {
915: PurpleAccount *account = purple_connection_get_account(od->gc);
916: /*
917: * We're interested in the inner TLV 0x101, which contains precious, precious message.
918: */
919: while (byte_stream_bytes_left(message) >= 4) {
920: guint16 type = byte_stream_get16(message);
921: guint16 length = byte_stream_get16(message);
922: if (type == 0x101) {
923: gchar *msg;
924: guint16 msglen = length - 4; /* charset + charsubset */
925: guint16 charset = byte_stream_get16(message);
926: byte_stream_advance(message, 2); /* charsubset */
927:
928: msg = byte_stream_getstr(message, msglen);
929: args->msg = oscar_decode_im(account, userinfo->bn, charset, msg, msglen);
930: g_free(msg);
931: } else {
932: byte_stream_advance(message, length);
933: }
934: }
935: }
936:
937: static int
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)
939: {
940: guint16 type, length;
941: aim_rxcallback_t userfunc;
942: int ret = 0;
943: struct aim_incomingim_ch1_args args;
944: unsigned int endpos;
945:
946: memset(&args, 0, sizeof(args));
947:
948: /*
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.
952: */
953: while (byte_stream_bytes_left(bs) >= 4)
954: {
955: type = byte_stream_get16(bs);
956: length = byte_stream_get16(bs);
957:
958: if (length > byte_stream_bytes_left(bs))
959: {
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);
961: break;
962: }
963:
964: endpos = byte_stream_curpos(bs) + length;
965:
966: if (type == 0x0002) { /* Message Block */
967: ByteStream tlv02;
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. */
975: /*
976: * This flag is set on incoming offline messages for both
977: * AIM and ICQ accounts.
978: */
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);
985:
986: /*
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.
991: *
992: * You really shouldn't advertise a zero-length icon
993: * anyway.
994: *
995: */
996: if (args.iconlen)
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) {
1003: /*
1004: * UTC timestamp for when the message was sent. Only
1005: * provided for offline messages.
1006: */
1007: args.timestamp = byte_stream_get32(bs);
1008: }
1009:
1010: /*
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.
1016: *
1017: */
1018: byte_stream_setpos(bs, endpos);
1019: }
1020:
1021:
1022: if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
1023: ret = userfunc(od, conn, frame, channel, userinfo, &args);
1024:
1025: g_free(args.msg);
1026: return ret;
1027: }
1028:
1029: static void
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)
1031: {
1032: /*
1033: * This goes like this...
1034: *
1035: * group name length
1036: * group name
1037: * num of buddies in group
1038: * buddy name length
1039: * buddy name
1040: * buddy name length
1041: * buddy name
1042: * ...
1043: * group name length
1044: * group name
1045: * num of buddies in group
1046: * buddy name length
1047: * buddy name
1048: * ...
1049: * ...
1050: */
1051: while (byte_stream_bytes_left(servdata))
1052: {
1053: guint16 gnlen, numb;
1054: int i;
1055: char *gn;
1056:
1057: gnlen = byte_stream_get16(servdata);
1058: gn = byte_stream_getstr(servdata, gnlen);
1059: numb = byte_stream_get16(servdata);
1060:
1061: for (i = 0; i < numb; i++) {
1062: guint16 bnlen;
1063: char *bn;
1064:
1065: bnlen = byte_stream_get16(servdata);
1066: bn = byte_stream_getstr(servdata, bnlen);
1067:
1068: purple_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->bn, gn, bn);
1069:
1070: g_free(bn);
1071: }
1072:
1073: g_free(gn);
1074: }
1075:
1076: return;
1077: }
1078:
1079: static void
1080: incomingim_ch2_buddyicon_free(OscarData *od, IcbmArgsCh2 *args)
1081: {
1082: g_free(args->info.icon.icon);
1083:
1084: return;
1085: }
1086:
1087: static void
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)
1089: {
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);
1094:
1095: args->destructor = (void *)incomingim_ch2_buddyicon_free;
1096:
1097: return;
1098: }
1099:
1100: static void
1101: incomingim_ch2_chat_free(OscarData *od, IcbmArgsCh2 *args)
1102: {
1103: /* XXX - aim_chat_roominfo_free() */
1104: g_free(args->info.chat.roominfo.name);
1105:
1106: return;
1107: }
1108:
1109: static void
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)
1111: {
1112: /*
1113: * Chat room info.
1114: */
1115: aim_chat_readroominfo(servdata, &args->info.chat.roominfo);
1116:
1117: args->destructor = (void *)incomingim_ch2_chat_free;
1118: }
1119:
1120: static void
1121: incomingim_ch2_icqserverrelay_free(OscarData *od, IcbmArgsCh2 *args)
1122: {
1123: g_free((char *)args->info.rtfmsg.msg);
1124: }
1125:
1126: /*
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.
1130: *
1131: * Note that this is all little-endian. Cringe.
1132: *
1133: */
1134: static void
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)
1136: {
1137: guint16 hdrlen, msglen;
1138:
1139: args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
1140:
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"); \
1145: return; \
1146: } \
1147: byte_stream_advance(servdata, hdrlen);
1148:
1149: SKIP_HEADER(0x001b);
1150: SKIP_HEADER(0x000e);
1151:
1152: args->info.rtfmsg.msgtype = byte_stream_get8(servdata);
1153: /*
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
1158: *
1159: * We don't need any of these, so just skip them.
1160: */
1161: byte_stream_advance(servdata, 1 + 2 + 2);
1162:
1163: msglen = byte_stream_getle16(servdata);
1164: args->info.rtfmsg.msg = byte_stream_getstr(servdata, msglen);
1165: }
1166:
1167: static void
1168: incomingim_ch2_sendfile_free(OscarData *od, IcbmArgsCh2 *args)
1169: {
1170: g_free(args->info.sendfile.filename);
1171: }
1172:
1173: /* Someone is sending us a file */
1174: static void
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)
1176: {
1177: int flen;
1178:
1179: args->destructor = (void *)incomingim_ch2_sendfile_free;
1180:
1181: /* Maybe there is a better way to tell what kind of sendfile
1182: * this is? Maybe TLV t(000a)? */
1183:
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);
1188:
1189: /*
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?
1194: */
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);
1201:
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 */
1208: }
1209:
1210: typedef void (*ch2_args_destructor_t)(OscarData *od, IcbmArgsCh2 *args);
1211:
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)
1213: {
1214: aim_rxcallback_t userfunc;
1215: aim_tlv_t *block1, *servdatatlv;
1216: GSList *list2;
1217: aim_tlv_t *tlv;
1218: IcbmArgsCh2 args;
1219: ByteStream bbs, sdbs, *sdbsptr = NULL;
1220: guint8 *cookie2;
1221: int ret = 0;
1222:
1223: char proxyip[30] = {""};
1224: char clientip[30] = {""};
1225: char verifiedip[30] = {""};
1226:
1227: memset(&args, 0, sizeof(args));
1228:
1229: /*
1230: * There's another block of TLVs embedded in the type 5 here.
1231: */
1232: block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1);
1233: if (block1 == NULL)
1234: {
1235: /* The server sent us ch2 ICBM without ch2 info? Weird. */
1236: return 1;
1237: }
1238: byte_stream_init(&bbs, block1->value, block1->length);
1239:
1240: /*
1241: * First two bytes represent the status of the connection.
1242: * One of the AIM_RENDEZVOUS_ defines.
1243: *
1244: * 0 is a request, 1 is a cancel, 2 is an accept
1245: */
1246: args.status = byte_stream_get16(&bbs);
1247:
1248: /*
1249: * Next comes the cookie. Should match the ICBM cookie.
1250: */
1251: cookie2 = byte_stream_getraw(&bbs, 8);
1252: if (memcmp(cookie, cookie2, 8) != 0)
1253: {
1254: purple_debug_warning("oscar",
1255: "Cookies don't match in rendezvous ICBM, bailing out.\n");
1256: g_free(cookie2);
1257: return 1;
1258: }
1259: memcpy(args.cookie, cookie2, 8);
1260: g_free(cookie2);
1261:
1262: /*
1263: * The next 16bytes are a capability block so we can
1264: * identify what type of rendezvous this is.
1265: */
1266: args.type = aim_locate_getcaps(od, &bbs, 0x10);
1267:
1268: /*
1269: * What follows may be TLVs or nothing, depending on the
1270: * purpose of the message.
1271: *
1272: * Ack packets for instance have nothing more to them.
1273: */
1274: list2 = aim_tlvlist_read(&bbs);
1275:
1276: /*
1277: * IP address to proxy the file transfer through.
1278: *
1279: * TODO: I don't like this. Maybe just read in an int? Or inet_ntoa...
1280: */
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]);
1286:
1287: /*
1288: * IP address from the perspective of the client.
1289: */
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]);
1295:
1296: /*
1297: * Verified IP address (from the perspective of Oscar).
1298: *
1299: * This is added by the server.
1300: */
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]);
1306:
1307: /*
1308: * Port number for something.
1309: */
1310: if (aim_tlv_gettlv(list2, 0x0005, 1))
1311: args.port = aim_tlv_get16(list2, 0x0005, 1);
1312:
1313: /*
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
1318: */
1319: if (aim_tlv_gettlv(list2, 0x000a, 1))
1320: args.requestnumber = aim_tlv_get16(list2, 0x000a, 1);
1321:
1322: /*
1323: * Terminate connection/error code. 0x0001 means the other user
1324: * cancelled the connection.
1325: */
1326: if (aim_tlv_gettlv(list2, 0x000b, 1))
1327: args.errorcode = aim_tlv_get16(list2, 0x000b, 1);
1328:
1329: /*
1330: * Invitation message / chat description.
1331: */
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);
1335: }
1336:
1337: /*
1338: * Character set.
1339: */
1340: if (aim_tlv_gettlv(list2, 0x000d, 1))
1341: args.encoding = aim_tlv_getstr(list2, 0x000d, 1);
1342:
1343: /*
1344: * Language.
1345: */
1346: if (aim_tlv_gettlv(list2, 0x000e, 1))
1347: args.language = aim_tlv_getstr(list2, 0x000e, 1);
1348:
1349: /*
1350: * Flag meaning we should proxy the file transfer through an AIM server
1351: */
1352: if (aim_tlv_gettlv(list2, 0x0010, 1))
1353: args.use_proxy = TRUE;
1354:
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;
1361:
1362: /*
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).
1366: *
1367: * Service Data blocks are module-specific in format.
1368: */
1369: if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) {
1370:
1371: byte_stream_init(&sdbs, servdatatlv->value, servdatatlv->length);
1372: sdbsptr = &sdbs;
1373:
1374: /*
1375: * The rest of the handling depends on what type it is.
1376: *
1377: * Not all of them have special handling (yet).
1378: */
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);
1389: }
1390:
1391: if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
1392: ret = userfunc(od, conn, frame, channel, userinfo, &args);
1393:
1394:
1395: if (args.destructor)
1396: ((ch2_args_destructor_t)args.destructor)(od, &args);
1397:
1398: g_free((char *)args.msg);
1399: g_free((char *)args.encoding);
1400: g_free((char *)args.language);
1401:
1402: aim_tlvlist_free(list2);
1403:
1404: return ret;
1405: }
1406:
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)
1408: {
1409: ByteStream meat;
1410: aim_rxcallback_t userfunc;
1411: aim_tlv_t *block;
1412: struct aim_incomingim_ch4_args args;
1413: int ret = 0;
1414:
1415: /*
1416: * Make a bstream for the meaty part. Yum. Meat.
1417: */
1418: if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1)))
1419: return -1;
1420: byte_stream_init(&meat, block->value, block->length);
1421:
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;
1428: else
1429: args.msglen = byte_stream_getle16(&meat);
1430: args.msg = (gchar *)byte_stream_getraw(&meat, args.msglen);
1431:
1432: if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
1433: ret = userfunc(od, conn, frame, channel, userinfo, &args);
1434:
1435: g_free(args.msg);
1436:
1437: return ret;
1438: }
1439:
1440: /*
1441: * Subtype 0x0007
1442: *
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.
1446: *
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.
1451: *
1452: */
1453: static int incomingim(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
1454: {
1455: int ret = 0;
1456: guchar *cookie;
1457: guint16 channel;
1458: aim_userinfo_t userinfo;
1459:
1460: memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
1461:
1462: /*
1463: * Read ICBM Cookie.
1464: */
1465: cookie = byte_stream_getraw(bs, 8);
1466:
1467: /*
1468: * Channel ID.
1469: *
1470: * Channel 0x0001 is the message channel. It is
1471: * used to send basic ICBMs.
1472: *
1473: * Channel 0x0002 is the Rendezvous channel, which
1474: * is where Chat Invitiations and various client-client
1475: * connection negotiations come from.
1476: *
1477: * Channel 0x0003 is used for chat messages.
1478: *
1479: * Channel 0x0004 is used for ICQ authorization, or
1480: * possibly any system notice.
1481: *
1482: */
1483: channel = byte_stream_get16(bs);
1484:
1485: /*
1486: * Extract the standard user info block.
1487: *
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.
1494: *
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.
1498: *
1499: */
1500: aim_info_extract(od, bs, &userinfo);
1501:
1502: /*
1503: * From here on, its depends on what channel we're on.
1504: *
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).
1508: */
1509: if (channel == 1) {
1510:
1511: ret = incomingim_ch1(od, conn, mod, frame, snac, channel, &userinfo, bs, cookie);
1512:
1513: } else if (channel == 2) {
1514: GSList *tlvlist;
1515:
1516: /*
1517: * Read block of TLVs (not including the userinfo data). All
1518: * further data is derived from what is parsed here.
1519: */
1520: tlvlist = aim_tlvlist_read(bs);
1521:
1522: ret = incomingim_ch2(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
1523:
1524: aim_tlvlist_free(tlvlist);
1525:
1526: } else if (channel == 4) {
1527: GSList *tlvlist;
1528:
1529: tlvlist = aim_tlvlist_read(bs);
1530: ret = incomingim_ch4(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
1531: aim_tlvlist_free(tlvlist);
1532:
1533: } else {
1534: purple_debug_misc("oscar", "icbm: ICBM received on an unsupported channel. Ignoring. (chan = %04x)\n", channel);
1535: }
1536:
1537: aim_info_free(&userinfo);
1538: g_free(cookie);
1539:
1540: return ret;
1541: }
1542:
1543: /* Subtype 0x000a */
1544: static int missedcall(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
1545: {
1546: int ret = 0;
1547: aim_rxcallback_t userfunc;
1548: guint16 channel, nummissed, reason;
1549: aim_userinfo_t userinfo;
1550:
1551: while (byte_stream_bytes_left(bs)) {
1552:
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);
1557:
1558: if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
1559: ret = userfunc(od, conn, frame, channel, &userinfo, nummissed, reason);
1560:
1561: aim_info_free(&userinfo);
1562: }
1563:
1564: return ret;
1565: }
1566:
1567: /*
1568: * Subtype 0x000b
1569: *
1570: * Possible codes:
1571: * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
1572: *
1573: */
1574: int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code)
1575: {
1576: FlapConnection *conn;
1577: ByteStream bs;
1578: aim_snacid_t snacid;
1579: GSList *tlvlist = NULL;
1580:
1581: if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
1582: return -EINVAL;
1583:
1584: byte_stream_new(&bs, 8+2+1+strlen(bn)+6);
1585:
1586: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
1587:
1588: byte_stream_putraw(&bs, cookie, 8);
1589:
1590: byte_stream_put16(&bs, 0x0002); /* channel */
1591: byte_stream_put8(&bs, strlen(bn));
1592: byte_stream_putstr(&bs, bn);
1593:
1594: aim_tlvlist_add_16(&tlvlist, 0x0003, code);
1595: aim_tlvlist_write(&bs, &tlvlist);
1596: aim_tlvlist_free(tlvlist);
1597:
1598: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b, snacid, &bs);
1599:
1600: byte_stream_destroy(&bs);
1601:
1602: return 0;
1603: }
1604:
1605: /*
1606: * Subtype 0x000b.
1607: * Send confirmation for a channel 2 message (Miranda wants it by default).
1608: */
1609: void
1610: aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie)
1611: {
1612: FlapConnection *conn;
1613: ByteStream bs;
1614: aim_snacid_t snacid;
1615: guint32 header_size, data_size;
1616: guint16 cookie2 = (guint16)g_random_int();
1617:
1618: purple_debug_misc("oscar", "Sending message ack to %s\n", bn);
1619:
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);
1623:
1624: /* The message header. */
1625: aim_im_puticbm(&bs, cookie, 0x0002, bn);
1626: byte_stream_put16(&bs, 0x0003); /* reason */
1627:
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 */
1646:
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);
1650: if (conn) {
1651: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b,
1652: snacid, &bs);
1653: }
1654: byte_stream_destroy(&bs);
1655: }
1656:
1657: /*
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.
1661: */
1662: static int clientautoresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
1663: {
1664: int ret = 0;
1665: aim_rxcallback_t userfunc;
1666: guint16 channel, reason;
1667: char *bn;
1668: guchar *cookie;
1669: guint8 bnlen;
1670: char *xml = NULL;
1671: guint16 hdrlen;
1672: int curpos;
1673: guint16 num1, num2;
1674: PurpleAccount *account;
1675: PurpleBuddy *buddy;
1676: PurplePresence *presence;
1677: PurpleStatus *status;
1678:
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);
1684:
1685: if (channel == 0x0002)
1686: {
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);
1693:
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");
1699: if (xml) {
1700: GString *xstatus;
1701: char *tmp1, *tmp2, *unescaped_xstatus;
1702:
1703: /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", xml); */
1704:
1705: xstatus = g_string_new(NULL);
1706:
1707: tmp1 = strstr(xml, "<title>");
1708: if (tmp1 != NULL) {
1709: tmp1 += 13;
1710: tmp2 = strstr(tmp1, "</title>");
1711: if (tmp2 != NULL)
1712: g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
1713: }
1714: tmp1 = strstr(xml, "<desc>");
1715: if (tmp1 != NULL) {
1716: tmp1 += 12;
1717: tmp2 = strstr(tmp1, "</desc>");
1718: if (tmp2 != NULL) {
1719: if (xstatus->len > 0 && tmp2 > tmp1)
1720: g_string_append(xstatus, " - ");
1721: g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
1722: }
1723: }
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");
1732: if (status) {
1733: purple_prpl_got_user_status(account, bn,
1734: "mood",
1735: PURPLE_MOOD_NAME, purple_status_get_attr_string(status, PURPLE_MOOD_NAME),
1736: PURPLE_MOOD_COMMENT, unescaped_xstatus, NULL);
1737: }
1738: }
1739: g_free(unescaped_xstatus);
1740: } else {
1741: purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n");
1742: }
1743: } else {
1744: purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n");
1745: }
1746:
1747: }
1748:
1749: } else if (channel == 0x0004) { /* ICQ message */
1750: switch (reason) {
1751: case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */
1752: guint8 statusmsgtype, *msg;
1753: guint16 len;
1754: guint32 state;
1755:
1756: len = byte_stream_getle16(bs); /* Should be 0x001b */
1757: byte_stream_advance(bs, len); /* Unknown */
1758:
1759: len = byte_stream_getle16(bs); /* Should be 0x000e */
1760: byte_stream_advance(bs, len); /* Unknown */
1761:
1762: statusmsgtype = byte_stream_getle8(bs);
1763: switch (statusmsgtype) {
1764: case 0xe8:
1765: state = AIM_ICQ_STATE_AWAY;
1766: break;
1767: case 0xe9:
1768: state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
1769: break;
1770: case 0xea:
1771: state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
1772: break;
1773: case 0xeb:
1774: state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
1775: break;
1776: case 0xec:
1777: state = AIM_ICQ_STATE_CHAT;
1778: break;
1779: default:
1780: state = 0;
1781: break;
1782: }
1783:
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 */
1787:
1788: len = byte_stream_getle16(bs);
1789: msg = byte_stream_getraw(bs, len);
1790:
1791: if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
1792: ret = userfunc(od, conn, frame, channel, bn, reason, state, msg);
1793:
1794: g_free(msg);
1795: } break;
1796:
1797: default: {
1798: if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
1799: ret = userfunc(od, conn, frame, channel, bn, reason);
1800: } break;
1801: } /* end switch */
1802: }
1803:
1804: g_free(cookie);
1805: g_free(bn);
1806: g_free(xml);
1807:
1808: return ret;
1809: }
1810:
1811: /*
1812: * Subtype 0x000c - Receive an ack after sending an ICBM. The ack contains the ICBM header of the message you sent.
1813: */
1814: static int msgack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
1815: {
1816: guchar *cookie;
1817: char *bn;
1818: int ret = 0;
1819:
1820: cookie = byte_stream_getraw(bs, 8);
1821: byte_stream_get16(bs); /* ch */
1822: bn = byte_stream_getstr(bs, byte_stream_get8(bs));
1823:
1824: purple_debug_info("oscar", "Sent message to %s.\n", bn);
1825:
1826: g_free(bn);
1827: g_free(cookie);
1828:
1829: return ret;
1830: }
1831:
1832: /*
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.
1837: *
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.
1843: */
1844: int aim_im_reqofflinemsgs(OscarData *od)
1845: {
1846: FlapConnection *conn;
1847:
1848: if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
1849: return -EINVAL;
1850:
1851: aim_genericreq_n(od, conn, SNAC_FAMILY_ICBM, 0x0010);
1852:
1853: return 0;
1854: }
1855:
1856: /*
1857: * Subtype 0x0014 - Send a mini typing notification (mtn) packet.
1858: *
1859: * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
1860: * and Purple 0.60 and newer.
1861: *
1862: */
1863: int aim_im_sendmtn(OscarData *od, guint16 channel, const char *bn, guint16 event)
1864: {
1865: FlapConnection *conn;
1866: ByteStream bs;
1867: aim_snacid_t snacid;
1868:
1869: if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
1870: return -EINVAL;
1871:
1872: if (!bn)
1873: return -EINVAL;
1874:
1875: byte_stream_new(&bs, 11 + strlen(bn) + 2);
1876:
1877: snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0014, 0x0000, NULL, 0);
1878:
1879: /* ICBM cookie */
1880: byte_stream_put32(&bs, 0x00000000);
1881: byte_stream_put32(&bs, 0x00000000);
1882:
1883: /*
1884: * Channel (should be 0x0001 for mtn)
1885: */
1886: byte_stream_put16(&bs, channel);
1887:
1888: /*
1889: * Dest buddy name
1890: */
1891: byte_stream_put8(&bs, strlen(bn));
1892: byte_stream_putstr(&bs, bn);
1893:
1894: /*
1895: * Event (should be 0x0000, 0x0001, or 0x0002 for mtn)
1896: */
1897: byte_stream_put16(&bs, event);
1898:
1899: flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0014, snacid, &bs);
1900:
1901: byte_stream_destroy(&bs);
1902:
1903: return 0;
1904: }
1905:
1906: /*
1907: * Subtype 0x0006 - Send eXtra Status request
1908: */
1909: int icq_im_xstatus_request(OscarData *od, const char *sn)
1910: {
1911: FlapConnection *conn;
1912: aim_snacid_t snacid;
1913: guchar cookie[8];
1914: GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
1915: ByteStream bs, header, plugindata;
1916: PurpleAccount *account;
1917: const char *fmt;
1918: char *statxml;
1919: int xmllen;
1920:
1921: static const guint8 pluginid[] = {
1922: 0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1,
1923: 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00
1924: };
1925:
1926: static const guint8 c_plugindata[] = {
1927: 0x1B, 0x00, 0x0A,
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
1937: };
1938:
1939: if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
1940: return -EINVAL;
1941:
1942: if (!sn)
1943: return -EINVAL;
1944:
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";
1946:
1947: account = purple_connection_get_account(od->gc);
1948:
1949: statxml = g_strdup_printf(fmt, account->username);
1950: xmllen = strlen(statxml);
1951:
1952: aim_icbm_makecookie(cookie);
1953:
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
1957: + 2 + 2);
1958:
1959: snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
1960: aim_im_puticbm(&bs, cookie, 0x0002, sn);
1961:
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 */
1966:
1967: aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
1968: aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
1969:
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);
1974:
1975: aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, (sizeof(c_plugindata) + xmllen), plugindata.data);
1976:
1977: aim_tlvlist_write(&header, &inner_tlvlist);
1978: aim_tlvlist_free(inner_tlvlist);
1979:
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 */
1982:
1983: aim_tlvlist_write(&bs, &outer_tlvlist);
1984:
1985: purple_debug_misc("oscar", "X-Status Request\n");
1986: flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, snacid, &bs, TRUE);
1987:
1988: aim_tlvlist_free(outer_tlvlist);
1989: byte_stream_destroy(&header);
1990: byte_stream_destroy(&plugindata);
1991: byte_stream_destroy(&bs);
1992: g_free(statxml);
1993:
1994: return 0;
1995: }
1996:
1997: int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
1998: {
1999: FlapConnection *conn;
2000: ByteStream bs;
2001: aim_snacid_t snacid;
2002: PurpleAccount *account;
2003: PurpleStatus *status;
2004: const char *fmt;
2005: const char *formatted_msg;
2006: char *msg;
2007: char *statxml;
2008: const char *title;
2009: int len;
2010:
2011: static const guint8 plugindata[] = {
2012: 0x1B, 0x00,
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
2026: };
2027:
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";
2029:
2030: if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
2031: return -EINVAL;
2032:
2033: if (!sn)
2034: return -EINVAL;
2035:
2036: account = purple_connection_get_account(od->gc);
2037: if (!account)
2038: return -EINVAL;
2039:
2040: /* if (purple_strequal(account->username, sn))
2041: icq_im_xstatus_request(od, sn); */
2042:
2043: status = purple_presence_get_active_status(account->presence);
2044: if (!status)
2045: return -EINVAL;
2046:
2047: title = purple_status_get_name(status);
2048: if (!title)
2049: return -EINVAL;
2050:
2051: formatted_msg = purple_status_get_attr_string(status, "message");
2052: if (!formatted_msg)
2053: return -EINVAL;
2054:
2055: msg = purple_markup_strip_html(formatted_msg);
2056: if (!msg)
2057: return -EINVAL;
2058:
2059: statxml = g_strdup_printf(fmt, account->username, title, msg);
2060: len = strlen(statxml);
2061:
2062: purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg);
2063:
2064: byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + sizeof(plugindata) + len); /* 16 extra */
2065:
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);
2071:
2072: flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, snacid, &bs, TRUE);
2073:
2074: g_free(statxml);
2075: g_free(msg);
2076: byte_stream_destroy(&bs);
2077:
2078: return 0;
2079: }
2080:
2081: /*
2082: * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
2083: *
2084: * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
2085: * and Purple 0.60 and newer.
2086: *
2087: */
2088: static int mtn_receive(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
2089: {
2090: int ret = 0;
2091: aim_rxcallback_t userfunc;
2092: char *bn;
2093: guint8 bnlen;
2094: guint16 channel, event;
2095:
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);
2101:
2102: if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
2103: ret = userfunc(od, conn, frame, channel, bn, event);
2104:
2105: g_free(bn);
2106:
2107: return ret;
2108: }
2109:
2110: static int
2111: snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
2112: {
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);
2127:
2128: return 0;
2129: }
2130:
2131: int
2132: msg_modfirst(OscarData *od, aim_module_t *mod)
2133: {
2134: mod->family = SNAC_FAMILY_ICBM;
2135: mod->version = 0x0001;
2136: mod->toolid = 0x0110;
2137: mod->toolversion = 0x0629;
2138: mod->flags = 0;
2139: strncpy(mod->name, "messaging", sizeof(mod->name));
2140: mod->snachandler = snachandler;
2141:
2142: return 0;
2143: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>