diff -ru bitlbee-20050224/protocols/nogaim.c bitlbee-0.92/protocols/nogaim.c --- bitlbee-20050224/protocols/nogaim.c Wed Feb 23 10:47:58 2005 +++ bitlbee-0.92/protocols/nogaim.c Mon Mar 7 23:33:09 2005 @@ -829,7 +829,7 @@ irc_usermsg( b->gc->irc, "User %s added to conversation %d", handle, b->id ); /* It might be yourself! */ - if( g_strcasecmp( handle, b->gc->user->username ) == 0 ) + if( handle_cmp( handle, b->gc->user->username, b->gc->protocol ) == 0 ) { u = user_find( b->gc->irc, b->gc->irc->nick ); if( !b->joined ) diff -ru bitlbee-20050224/protocols/oscar/aim.h bitlbee-0.92/protocols/oscar/aim.h --- bitlbee-20050224/protocols/oscar/aim.h Sun Oct 10 07:24:06 2004 +++ bitlbee-0.92/protocols/oscar/aim.h Fri Mar 18 16:25:53 2005 @@ -439,6 +439,7 @@ int aim_addtlvtochain_raw(aim_tlvlist_t **list, const guint16 t, const guint16 l, const guint8 *v); int aim_addtlvtochain_caps(aim_tlvlist_t **list, const guint16 t, const guint32 caps); int aim_addtlvtochain_noval(aim_tlvlist_t **list, const guint16 type); +int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance); int aim_addtlvtochain_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *ui); int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl); int aim_counttlvchain(aim_tlvlist_t **list); @@ -633,6 +634,12 @@ unsigned short instance; }; +struct aim_chat_invitation { + struct gaim_connection * gc; + char * name; + guint8 exchange; +}; + #define AIM_IMFLAGS_AWAY 0x0001 /* mark as an autoreply */ #define AIM_IMFLAGS_ACK 0x0002 /* request a receipt notice */ #define AIM_IMFLAGS_UNICODE 0x0004 @@ -811,6 +818,7 @@ aim_conn_t *aim_directim_connect(aim_session_t *, const char *sn, const char *addr, const guint8 *cookie); int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type); +int aim_send_im_ch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, unsigned short exchange, const char *roomname, unsigned short instance); aim_conn_t *aim_sendfile_initiate(aim_session_t *, const char *destsn, const char *filename, guint16 numfiles, guint32 totsize); aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn); diff -ru bitlbee-20050224/protocols/oscar/chat.c bitlbee-0.92/protocols/oscar/chat.c --- bitlbee-20050224/protocols/oscar/chat.c Sun Oct 10 07:24:06 2004 +++ bitlbee-0.92/protocols/oscar/chat.c Sat Mar 26 16:47:45 2005 @@ -182,31 +182,6 @@ return 0; } -static int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) -{ - guint8 *buf; - int buflen; - aim_bstream_t bs; - - buflen = 2 + 1 + strlen(roomname) + 2; - - if (!(buf = g_malloc(buflen))) - return 0; - - aim_bstream_init(&bs, buf, buflen); - - aimbs_put16(&bs, exchange); - aimbs_put8(&bs, strlen(roomname)); - aimbs_putraw(&bs, (guint8 *)roomname, strlen(roomname)); - aimbs_put16(&bs, instance); - - aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf); - - g_free(buf); - - return 0; -} - /* * Join a room of name roomname. This is the first step to joining an * already created room. It's basically a Service Request for diff -ru bitlbee-20050224/protocols/oscar/im.c bitlbee-0.92/protocols/oscar/im.c --- bitlbee-20050224/protocols/oscar/im.c Sun Oct 10 07:24:06 2004 +++ bitlbee-0.92/protocols/oscar/im.c Sat Mar 26 17:02:04 2005 @@ -365,6 +365,94 @@ } /* + * Subtype 0x0006 - Send a chat invitation. + */ +int aim_send_im_ch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int i; + aim_msgcookie_t *cookie; + struct aim_invite_priv *priv; + guint8 ck[8]; + aim_tlvlist_t *otl = NULL, *itl = NULL; + guint8 *hdr; + int hdrlen; + aim_bstream_t hdrbs; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!sn || !msg || !roomname) + return -EINVAL; + + for (i = 0; i < 8; i++) + ck[i] = (guint8)rand(); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* XXX should be uncached by an unwritten 'invite accept' handler */ + if ((priv = g_malloc(sizeof(struct aim_invite_priv)))) { + priv->sn = g_strdup(sn); + priv->roomname = g_strdup(roomname); + priv->exchange = exchange; + priv->instance = instance; + } + + if ((cookie = aim_mkcookie(ck, AIM_COOKIETYPE_INVITE, priv))) + aim_cachecookie(sess, cookie); + else + g_free(priv); + + /* ICBM Header */ + aimbs_putraw(&fr->data, ck, 8); /* Cookie */ + aimbs_put16(&fr->data, 0x0002); /* Channel */ + aimbs_put8(&fr->data, strlen(sn)); /* Screename length */ + aimbs_putraw(&fr->data, sn, strlen(sn)); /* Screenname */ + + /* + * TLV t(0005) + * + * Everything else is inside this TLV. + * + * Sigh. AOL was rather inconsistent right here. So we have + * to play some minor tricks. Right inside the type 5 is some + * raw data, followed by a series of TLVs. + * + */ + hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2; + hdr = g_malloc(hdrlen); + aim_bstream_init(&hdrbs, hdr, hdrlen); + + aimbs_put16(&hdrbs, 0x0000); /* Unknown! */ + aimbs_putraw(&hdrbs, ck, sizeof(ck)); /* I think... */ + aim_putcap(&hdrbs, AIM_CAPS_CHAT); + + aim_addtlvtochain16(&itl, 0x000a, 0x0001); + aim_addtlvtochain_noval(&itl, 0x000f); + aim_addtlvtochain_raw(&itl, 0x000c, strlen(msg), msg); + aim_addtlvtochain_chatroom(&itl, 0x2711, exchange, roomname, instance); + aim_writetlvchain(&hdrbs, &itl); + + aim_addtlvtochain_raw(&otl, 0x0005, aim_bstream_curpos(&hdrbs), hdr); + + aim_writetlvchain(&fr->data, &otl); + + g_free(hdr); + aim_freetlvchain(&itl); + aim_freetlvchain(&otl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* * This is also performance sensitive. (If you can believe it...) * */ diff -ru bitlbee-20050224/protocols/oscar/oscar.c bitlbee-0.92/protocols/oscar/oscar.c --- bitlbee-20050224/protocols/oscar/oscar.c Wed Feb 23 17:26:59 2005 +++ bitlbee-0.92/protocols/oscar/oscar.c Sat Mar 26 16:43:22 2005 @@ -54,7 +54,7 @@ /* Don't know if support for UTF8 is really working. For now it's UTF16 here. static int gaim_caps = AIM_CAPS_UTF8; */ -static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT; +static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_CHAT; static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02}; struct oscar_data { @@ -148,7 +148,6 @@ return tmp; } -#if 0 static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int id) { GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats; struct chat_connection *c = NULL; @@ -163,7 +162,7 @@ return c; } -#endif + static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc, aim_conn_t *conn) { @@ -1071,33 +1070,6 @@ return 1; } -static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) { -#if 0 - struct gaim_connection *gc = sess->aux_data; -#endif - - if (args->status != AIM_RENDEZVOUS_PROPOSE) - return 1; -#if 0 - if (args->reqclass & AIM_CAPS_CHAT) { - char *name = extract_name(args->info.chat.roominfo.name); - int *exch = g_new0(int, 1); - GList *m = NULL; - m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name)); - *exch = args->info.chat.roominfo.exchange; - m = g_list_append(m, exch); - serv_got_chat_invite(gc, - name ? name : args->info.chat.roominfo.name, - userinfo->sn, - (char *)args->msg, - m); - if (name) - g_free(name); - } -#endif - return 1; -} - static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) { char *uin, message; struct oscar_data *od = (struct oscar_data *)data->gc->proto_data; @@ -1201,6 +1173,8 @@ return 1; } +static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args); + static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) { int channel, ret = 0; aim_userinfo_t *userinfo; @@ -2314,6 +2288,171 @@ } } +int oscar_chat_send(struct gaim_connection * gc, int id, char *message) +{ + struct oscar_data * od = (struct oscar_data*)gc->proto_data; + struct chat_connection * ccon; + + if(!(ccon = find_oscar_chat(gc, id))) + return -1; + + int ret; + guint8 len = strlen(message); + char *s; + + for (s = message; *s; s++) + if (*s & 128) + break; + + /* Message contains high ASCII chars, time for some translation! */ + if (*s) { + s = g_malloc(BUF_LONG); + /* Try if we can put it in an ISO8859-1 string first. + If we can't, fall back to UTF16. */ + if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) { + len = ret; + } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) { + len = ret; + } else { + /* OOF, translation failed... Oh well.. */ + g_free( s ); + s = message; + } + } else { + s = message; + } + + ret = aim_chat_send_im(od->sess, ccon->conn, AIM_CHATFLAGS_NOREFLECT, s, len); + + if (s != message) { + g_free(s); + } + + return (ret >= 0); +} + +void oscar_chat_invite(struct gaim_connection * gc, int id, char *message, char *who) +{ + struct oscar_data * od = (struct oscar_data *)gc->proto_data; + struct chat_connection *ccon = find_oscar_chat(gc, id); + + if (ccon == NULL) + return; + + aim_send_im_ch2_chatinvite(od->sess, who, message ? message : "", + ccon->exchange, ccon->name, 0x0); +} + +void oscar_chat_kill(struct gaim_connection *gc, struct chat_connection *cc) +{ + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + + /* Notify the conversation window that we've left the chat */ + serv_got_chat_left(gc, cc->id); + + /* Destroy the chat_connection */ + od->oscar_chats = g_slist_remove(od->oscar_chats, cc); + if (cc->inpa > 0) + gaim_input_remove(cc->inpa); + aim_conn_kill(od->sess, &cc->conn); + g_free(cc->name); + g_free(cc->show); + g_free(cc); +} + +void oscar_chat_leave(struct gaim_connection * gc, int id) +{ + struct chat_connection * ccon = find_oscar_chat(gc, id); + + if(ccon == NULL) + return; + + oscar_chat_kill(gc, ccon); +} + +int oscar_chat_join(struct gaim_connection * gc, char * name) +{ + struct oscar_data * od = (struct oscar_data *)gc->proto_data; + + aim_conn_t * cur; + + if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) { + + return (aim_chatnav_createroom(od->sess, cur, name, 4) == 0); + + } else { + struct create_room * cr = g_new0(struct create_room, 1); + cr->exchange = 4; + cr->name = g_strdup(name); + od->create_rooms = g_slist_append(od->create_rooms, cr); + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV); + return 1; + } +} + +int oscar_chat_open(struct gaim_connection * gc, char *who) +{ + struct oscar_data * od = (struct oscar_data *)gc->proto_data; + + static int chat_id = 0; + char * chatname = g_new0(char, strlen(gc->username)+4); + g_snprintf(chatname, strlen(gc->username) + 4, "%s%d", gc->username, chat_id++); + + int ret = oscar_chat_join(gc, chatname); + + aim_send_im_ch2_chatinvite(od->sess, who, "", + 4, chatname, 0x0); + g_free(chatname); + + return ret; +} + +void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv) +{ + oscar_chat_join(inv->gc, inv->name); + g_free(inv->name); + g_free(inv); +} + +void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv) +{ + g_free(inv->name); + g_free(inv); +} + +static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) { + struct gaim_connection *gc = sess->aux_data; + + if (args->status != AIM_RENDEZVOUS_PROPOSE) + return 1; + + if (args->reqclass & AIM_CAPS_CHAT) { + char *name = extract_name(args->info.chat.roominfo.name); + int *exch = g_new0(int, 1); + GList *m = NULL; + m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name)); + *exch = args->info.chat.roominfo.exchange; + m = g_list_append(m, exch); + + char txt[1024]; + + g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg ); + + struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1); + + inv->gc = gc; + inv->exchange = *exch; + inv->name = g_strdup(name); + + do_ask_dialog( gc, txt, inv, oscar_accept_chat, oscar_reject_chat); + + if (name) + g_free(name); + } + + return 1; +} + static struct prpl *my_protocol = NULL; void oscar_init(struct prpl *ret) { @@ -2327,6 +2466,10 @@ ret->get_away = oscar_get_away; ret->add_buddy = oscar_add_buddy; ret->remove_buddy = oscar_remove_buddy; + ret->chat_send = oscar_chat_send; + ret->chat_invite = oscar_chat_invite; + ret->chat_leave = oscar_chat_leave; + ret->chat_open = oscar_chat_open; ret->add_permit = oscar_add_permit; ret->add_deny = oscar_add_deny; ret->rem_permit = oscar_rem_permit; diff -ru bitlbee-20050224/protocols/oscar/tlv.c bitlbee-0.92/protocols/oscar/tlv.c --- bitlbee-20050224/protocols/oscar/tlv.c Tue Sep 21 16:34:44 2004 +++ bitlbee-0.92/protocols/oscar/tlv.c Sat Mar 26 16:48:17 2005 @@ -373,6 +373,31 @@ return buflen; } +int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) +{ + guint8 *buf; + int buflen; + aim_bstream_t bs; + + buflen = 2 + 1 + strlen(roomname) + 2; + + if (!(buf = g_malloc(buflen))) + return 0; + + aim_bstream_init(&bs, buf, buflen); + + aimbs_put16(&bs, exchange); + aimbs_put8(&bs, strlen(roomname)); + aimbs_putraw(&bs, (guint8 *)roomname, strlen(roomname)); + aimbs_put16(&bs, instance); + + aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf); + + g_free(buf); + + return 0; +} + /** * aim_writetlvchain - Write a TLV chain into a data buffer. * @buf: Destination buffer