diff -Bur bitlbee-0.92/bitlbee.c bitlbee-all/bitlbee.c --- bitlbee-0.92/bitlbee.c 2005-02-23 10:48:31.000000000 -0500 +++ bitlbee-all/bitlbee.c 2005-04-16 18:08:48.000000000 -0400 @@ -262,7 +262,7 @@ { fgetc( fp ); line = deobfucrypt( irc, s ); - root_command_string( irc, ru, line ); + root_command_string( irc, ru, line, 0 ); g_free( line ); } fclose( fp ); @@ -280,7 +280,7 @@ if( set_getint( irc, "auto_connect" ) ) { strcpy( s, "account on" ); /* Can't do this directly because r_c_s alters the string */ - root_command_string( irc, ru, s ); + root_command_string( irc, ru, s, 0 ); } return( 1 ); @@ -451,7 +451,7 @@ g_main_quit( global.loop ); } -int root_command_string( irc_t *irc, user_t *u, char *command ) +int root_command_string( irc_t *irc, user_t *u, char *command, int flags ) { char *cmd[IRC_MAX_ARGS]; char *s; diff -Bur bitlbee-0.92/bitlbee.h bitlbee-all/bitlbee.h --- bitlbee-0.92/bitlbee.h 2005-02-23 17:26:59.000000000 -0500 +++ bitlbee-all/bitlbee.h 2005-04-16 18:00:20.000000000 -0400 @@ -119,7 +119,7 @@ gboolean bitlbee_io_current_client_read( GIOChannel *source, GIOCondition condition, gpointer data ); gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condition, gpointer data ); -int root_command_string( irc_t *irc, user_t *u, char *command ); +int root_command_string( irc_t *irc, user_t *u, char *command, int flags ); int root_command( irc_t *irc, char *command[] ); int bitlbee_load( irc_t *irc, char *password ); int bitlbee_save( irc_t *irc ); diff -Bur bitlbee-0.92/commands.c bitlbee-all/commands.c --- bitlbee-0.92/commands.c 2005-02-08 16:48:35.000000000 -0500 +++ bitlbee-all/commands.c 2005-04-16 18:04:27.000000000 -0400 @@ -128,7 +128,7 @@ if( checkie == -2 ) { setpassnc( irc, cmd[1] ); - root_command_string( irc, user_find( irc, irc->mynick ), "save" ); + root_command_string( irc, user_find( irc, irc->mynick ), "save", 0 ); irc->status = USTATUS_IDENTIFIED; } else diff -Bur bitlbee-0.92/irc.c bitlbee-all/irc.c --- bitlbee-0.92/irc.c 2005-02-23 18:32:02.000000000 -0500 +++ bitlbee-all/irc.c 2005-05-23 18:56:42.000000000 -0400 @@ -576,7 +576,7 @@ else irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); } - else if( g_strcasecmp( cmd[0], "PRIVMSG" ) == 0 ) + else if( g_strcasecmp( cmd[0], "PRIVMSG" ) == 0 || g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) { if( !( cmd[1] && cmd[2] ) ) { @@ -620,7 +620,7 @@ { irc->is_private = 1; } - irc_send( irc, cmd[1], cmd[2] ); + irc_send( irc, cmd[1], cmd[2], (g_strcasecmp( cmd[0], "NOTICE") == 0) ? IM_FLAG_AWAY : 0 ); } } else if( g_strcasecmp( cmd[0], "QUIT" ) == 0 ) @@ -1289,7 +1289,7 @@ irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); } -int irc_send( irc_t *irc, char *nick, char *s ) +int irc_send( irc_t *irc, char *nick, char *s, int flags ) { struct conversation *c = NULL; user_t *u = NULL; @@ -1377,7 +1377,7 @@ } if( u->send_handler ) - return( u->send_handler( irc, u, s ) ); + return( u->send_handler( irc, u, s, flags ) ); } else if( c && c->gc && c->gc->prpl ) { @@ -1392,7 +1392,7 @@ user_t *u = data; u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */ - serv_send_im( u->gc->irc, u, u->sendbuf ); + serv_send_im( u->gc->irc, u, u->sendbuf, 0 ); g_free( u->sendbuf ); u->sendbuf = NULL; @@ -1402,7 +1402,7 @@ return( FALSE ); } -int buddy_send_handler( irc_t *irc, user_t *u, char *msg ) +int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ) { if( !u || !u->gc ) return( 0 ); @@ -1432,7 +1432,7 @@ } else { - return( serv_send_im( irc, u, msg ) ); + return( serv_send_im( irc, u, msg, flags ) ); } } diff -Bur bitlbee-0.92/irc.h bitlbee-all/irc.h --- bitlbee-0.92/irc.h 2005-02-08 15:58:30.000000000 -0500 +++ bitlbee-all/irc.h 2005-04-16 17:58:09.000000000 -0400 @@ -137,11 +137,11 @@ void irc_whois( irc_t *irc, char *nick ); int irc_away( irc_t *irc, char *away ); -int irc_send( irc_t *irc, char *nick, char *s ); +int irc_send( irc_t *irc, char *nick, char *s, int flags ); int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg ); int irc_msgfrom( irc_t *irc, char *nick, char *msg ); int irc_noticefrom( irc_t *irc, char *nick, char *msg ); -int buddy_send_handler( irc_t *irc, user_t *u, char *msg ); +int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ); #endif diff -Bur bitlbee-0.92/protocols/nogaim.c bitlbee-all/protocols/nogaim.c --- bitlbee-0.92/protocols/nogaim.c 2005-02-23 10:47:58.000000000 -0500 +++ bitlbee-all/protocols/nogaim.c 2005-05-15 22:51:55.000000000 -0400 @@ -167,7 +167,7 @@ should be a compare function inside the PRPL module, but I do it this way for now because I don't want to touch the Gaim code too much since it's not going to be here for too long anymore. */ -int handle_cmp( char *a, char *b, int protocol ) +int handle_cmp( const char *a, const char *b, int protocol ) { if( protocol == PROTO_TOC || protocol == PROTO_ICQ ) { @@ -283,6 +283,9 @@ msg = buf; else msg = text; + + if( g_strcasecmp( set_getstr(gc->irc, "html" ), "strip" ) == 0 ) + strip_html( msg ); irc_usermsg( gc->irc, "%s - %s", proto_name[gc->protocol], msg ); } @@ -596,6 +599,11 @@ for( c = gc->conversations; c; c = c->next ) remove_chat_buddy_silent( c, handle ); } + + if( ( type & UC_UNAVAILABLE ) && ( gc->prpl->get_away ) ) + { + // gc->prpl->get_away( gc, handle ); + } if( ( type & UC_UNAVAILABLE ) && ( gc->protocol == PROTO_OSCAR || gc->protocol == PROTO_TOC ) ) { @@ -628,6 +636,20 @@ } } +void serv_got_away( struct gaim_connection *gc, char *handle, char *away) +{ + user_t *u; + + u = user_findhandle( gc, handle ); + if(!u || !away) return; + if(u->away) + g_free(u->away); + + u->away = g_strdup(away); + if( g_strcasecmp( set_getstr( gc->irc, "html" ), "strip" ) == 0 ) + strip_html(u->away); +} + void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 flags, time_t mtime, gint len ) { irc_t *irc = gc->irc; @@ -829,7 +851,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 ) @@ -993,15 +1015,21 @@ return( set_eval_bool( irc, set, value ) ); } -int serv_send_im( irc_t *irc, user_t *u, char *msg ) +int serv_send_im( irc_t *irc, user_t *u, char *msg, int flags ) { char buf[8192]; if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 && do_iconv( set_getstr( irc, "charset" ), "UTF-8", msg, buf, 0, 8192 ) != -1 ) msg = buf; + + if( u->gc->flags & OPT_CONN_HTML) { + char * html = escape_html(msg); + strncpy(buf, html, 8192); + g_free(html); + } - return( ((struct gaim_connection *)u->gc)->prpl->send_im( u->gc, u->handle, msg, strlen( msg ), 0 ) ); + return( ((struct gaim_connection *)u->gc)->prpl->send_im( u->gc, u->handle, msg, strlen( msg ), flags ) ); } int serv_send_chat( irc_t *irc, struct gaim_connection *gc, int id, char *msg ) @@ -1011,6 +1039,12 @@ if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 && do_iconv( set_getstr( irc, "charset" ), "UTF-8", msg, buf, 0, 8192 ) != -1 ) msg = buf; + + if( gc->flags & OPT_CONN_HTML) { + char * html = escape_html(msg); + strncpy(buf, html, 8192); + g_free(html); + } return( gc->prpl->chat_send( gc, id, msg ) ); } diff -Bur bitlbee-0.92/protocols/nogaim.h bitlbee-all/protocols/nogaim.h --- bitlbee-0.92/protocols/nogaim.h 2004-10-29 19:42:07.000000000 -0400 +++ bitlbee-all/protocols/nogaim.h 2005-05-15 20:19:20.000000000 -0400 @@ -249,7 +249,7 @@ extern struct prpl *proto_prpl[16]; /* nogaim.c */ -int serv_send_im(irc_t *irc, user_t *u, char *msg); +int serv_send_im(irc_t *irc, user_t *u, char *msg, int flags); int serv_send_chat(irc_t *irc, struct gaim_connection *gc, int id, char *msg ); G_MODULE_EXPORT signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf ); @@ -258,7 +258,7 @@ void nogaim_init(); int proto_away( struct gaim_connection *gc, char *away ); char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value ); -int handle_cmp( char *a, char *b, int protocol ); +int handle_cmp(const char *a,const char *b, int protocol ); gboolean auto_reconnect( gpointer data ); void cancel_auto_reconnect( struct account *a ); @@ -297,6 +297,7 @@ /* server.c */ G_MODULE_EXPORT void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, int evil, time_t signon, time_t idle, int type, guint caps ); +G_MODULE_EXPORT void serv_got_away( struct gaim_connection *gc, char *handle, char *away); G_MODULE_EXPORT void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 flags, time_t mtime, gint len ); G_MODULE_EXPORT void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout ); G_MODULE_EXPORT void serv_got_chat_invite( struct gaim_connection *gc, char *handle, char *who, char *msg, GList *data ); @@ -314,6 +315,7 @@ G_MODULE_EXPORT char *normalize( const char *s ); G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec ); G_MODULE_EXPORT void strip_html( char *msg ); +G_MODULE_EXPORT char * escape_html(const char *html); G_MODULE_EXPORT void info_string_append(GString *str, char *newline, char *name, char *value); #ifdef WITH_MSN diff -Bur bitlbee-0.92/protocols/oscar/aim.h bitlbee-all/protocols/oscar/aim.h --- bitlbee-0.92/protocols/oscar/aim.h 2004-10-10 07:24:06.000000000 -0400 +++ bitlbee-all/protocols/oscar/aim.h 2005-05-23 21:57:53.000000000 -0400 @@ -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,9 @@ 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); +int aim_im_sendmtn(aim_session_t *sess, guint16 type1, const char *sn, guint16 type2); + 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 -Bur bitlbee-0.92/protocols/oscar/aim_cbtypes.h bitlbee-all/protocols/oscar/aim_cbtypes.h --- bitlbee-0.92/protocols/oscar/aim_cbtypes.h 2004-07-14 08:28:33.000000000 -0400 +++ bitlbee-all/protocols/oscar/aim_cbtypes.h 2005-04-05 21:35:58.000000000 -0400 @@ -99,6 +99,7 @@ #define AIM_CB_MSG_MISSEDCALL 0x000a #define AIM_CB_MSG_CLIENTAUTORESP 0x000b #define AIM_CB_MSG_ACK 0x000c +#define AIM_CB_MSG_MTN 0x0014 #define AIM_CB_MSG_DEFAULT 0xffff /* diff -Bur bitlbee-0.92/protocols/oscar/chat.c bitlbee-all/protocols/oscar/chat.c --- bitlbee-0.92/protocols/oscar/chat.c 2004-10-10 07:24:06.000000000 -0400 +++ bitlbee-all/protocols/oscar/chat.c 2005-03-26 16:47:45.000000000 -0500 @@ -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 -Bur bitlbee-0.92/protocols/oscar/im.c bitlbee-all/protocols/oscar/im.c --- bitlbee-0.92/protocols/oscar/im.c 2004-10-10 07:24:06.000000000 -0400 +++ bitlbee-all/protocols/oscar/im.c 2005-05-23 21:55:50.000000000 -0400 @@ -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...) * */ @@ -1987,6 +2075,90 @@ return ret; } +/* + * Subtype 0x0014 - Send a mini typing notification (mtn) packet. + * + * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, + * and Gaim 0.60 and newer. + * + */ +int aim_im_sendmtn(aim_session_t *sess, guint16 type1, const char *sn, guint16 type2) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002))) + return -EINVAL; + + if (!sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+11+strlen(sn)+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0014, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0014, 0x0000, snacid); + + /* + * 8 days of light + * Er, that is to say, 8 bytes of 0's + */ + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + + /* + * Type 1 (should be 0x0001 for mtn) + */ + aimbs_put16(&fr->data, type1); + + /* + * Dest sn + */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + /* + * Type 2 (should be 0x0000, 0x0001, or 0x0002 for mtn) + */ + aimbs_put16(&fr->data, type2); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0014 - Receive a mini typing notification (mtn) packet. + * + * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, + * and Gaim 0.60 and newer. + * + */ +static int mtn_receive(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + char *sn; + guint8 snlen; + guint16 type1, type2; + + aim_bstream_advance(bs, 8); /* Unknown - All 0's */ + type1 = aimbs_get16(bs); + snlen = aimbs_get8(bs); + sn = aimbs_getstr(bs, snlen); + type2 = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, type1, sn, type2); + + g_free(sn); + + return ret; +} + static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { @@ -2002,6 +2174,8 @@ return clientautoresp(sess, mod, rx, snac, bs); else if (snac->subtype == 0x000c) return msgack(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0014) + return mtn_receive(sess, mod, rx, snac, bs); return 0; } diff -Bur bitlbee-0.92/protocols/oscar/info.c bitlbee-all/protocols/oscar/info.c --- bitlbee-0.92/protocols/oscar/info.c 2004-10-10 07:24:06.000000000 -0400 +++ bitlbee-all/protocols/oscar/info.c 2005-03-29 19:19:41.000000000 -0500 @@ -614,8 +614,10 @@ { aim_userinfo_t userinfo; char *text_encoding = NULL, *text = NULL; + guint16 text_length = 0; aim_rxcallback_t userfunc; aim_tlvlist_t *tlvlist; + aim_tlv_t *tlv; aim_snac_t *origsnac = NULL; struct aim_priv_inforeq *inforeq; int ret = 0; @@ -649,10 +651,18 @@ */ if (inforeq->infotype == AIM_GETINFO_GENERALINFO) { text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1); - text = aim_gettlv_str(tlvlist, 0x0002, 1); + if((tlv = aim_gettlv(tlvlist, 0x0002, 1))) { + text = g_new0(char, tlv->length); + memcpy(text, tlv->value, tlv->length); + text_length = tlv->length; + } } else if (inforeq->infotype == AIM_GETINFO_AWAYMESSAGE) { text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1); - text = aim_gettlv_str(tlvlist, 0x0004, 1); + if((tlv = aim_gettlv(tlvlist, 0x0004, 1))) { + text = g_new0(char, tlv->length); + memcpy(text, tlv->value, tlv->length); + text_length = tlv->length; + } } else if (inforeq->infotype == AIM_GETINFO_CAPABILITIES) { aim_tlv_t *ct; @@ -667,7 +677,7 @@ } if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, &userinfo, inforeq->infotype, text_encoding, text); + ret = userfunc(sess, rx, &userinfo, inforeq->infotype, text_encoding, text, text_length); g_free(text_encoding); g_free(text); diff -Bur bitlbee-0.92/protocols/oscar/oscar.c bitlbee-all/protocols/oscar/oscar.c --- bitlbee-0.92/protocols/oscar/oscar.c 2005-02-23 17:26:59.000000000 -0500 +++ bitlbee-all/protocols/oscar/oscar.c 2005-05-24 22:33:02.000000000 -0400 @@ -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 { @@ -82,6 +82,8 @@ gboolean icq; GSList *evilhack; + GSList *info_requests; /*Used to separate away message requests the user made from automatic ones*/ + struct { guint maxbuddies; /* max users you can watch */ guint maxwatchers; /* max users who can watch you */ @@ -148,7 +150,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 +164,7 @@ return c; } -#endif + static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc, aim_conn_t *conn) { @@ -215,6 +216,8 @@ static int gaim_ssi_parseack (aim_session_t *, aim_frame_t *, ...); static int gaim_icqinfo (aim_session_t *, aim_frame_t *, ...); +static int gaim_parseaiminfo (aim_session_t *, aim_frame_t *, ...); +static int gaim_parsemtn (aim_session_t *, aim_frame_t *, ...); static char *msgerrreason[] = { "Invalid error", @@ -422,6 +425,11 @@ odata->create_rooms = g_slist_remove(odata->create_rooms, cr); g_free(cr); } + while(odata->info_requests) { + char * data = odata->info_requests->data; + odata->info_requests = g_slist_remove(odata->info_requests, data); + g_free(data); + } if (odata->email) g_free(odata->email); if (odata->newp) @@ -548,6 +556,8 @@ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parseaiminfo, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parsemtn, 0); ((struct oscar_data *)gc->proto_data)->conn = bosconn; for (i = 0; i < (int)strlen(info->bosip); i++) { @@ -1071,33 +1081,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 +1184,12 @@ return 1; } +int handle_cmp_aim(const char * a, const char * b) { + return handle_cmp(a, b, PROTO_TOC); +} + +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; @@ -1357,6 +1346,10 @@ char *destn; guint16 reason; char buf[1024]; + GSList *l; + struct oscar_data *od; + + od = ((struct gaim_connection*)sess->aux_data)->proto_data; va_start(ap, fr); reason = (guint16)va_arg(ap, unsigned int); @@ -1367,6 +1360,11 @@ (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown")); do_error_dialog(sess->aux_data, buf, _("Gaim - Error")); + if((l = g_slist_find_custom(od->info_requests, destn, (GCompareFunc)handle_cmp_aim))) { + g_free(l->data); + od->info_requests = g_slist_remove(od->info_requests, l); + } + return 1; } @@ -1663,6 +1661,7 @@ va_end(ap); /* Maybe senderwarn and recverwarn should be user preferences... */ + params->flags = 0x0000000b; params->maxmsglen = 8000; params->minmsginterval = 0; @@ -1869,11 +1868,12 @@ struct oscar_data *odata = (struct oscar_data *)g->proto_data; if (odata->icq) aim_icq_getallinfo(odata->sess, name); - else - /* people want the away message on the top, so we get the away message - * first and then get the regular info, since it's too difficult to - * insert in the middle. i hate people. */ + else { aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE); + aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO); + if(!g_slist_find_custom(odata->info_requests, name, (GCompareFunc)handle_cmp_aim)) + odata->info_requests = g_slist_append(odata->info_requests, g_strdup(name)); + } } static void oscar_get_away(struct gaim_connection *g, char *who) { @@ -1885,7 +1885,7 @@ if (budlight->caps & AIM_CAPS_ICQSERVERRELAY) aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7); } else - aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_GENERALINFO); + aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE); } static void oscar_set_away_aim(struct gaim_connection *gc, struct oscar_data *od, const char *message) @@ -1977,7 +1977,8 @@ static void oscar_remove_buddy(struct gaim_connection *g, char *name, char *group) { struct oscar_data *odata = (struct oscar_data *)g->proto_data; struct aim_ssi_item *ssigroup; - while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1)); + while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && + !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1)); } static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) { @@ -2286,6 +2287,144 @@ } +static char *oscar_encoding_extract(const char *encoding) +{ + char *ret = NULL; + char *begin, *end; + + g_return_val_if_fail(encoding != NULL, NULL); + + /* Make sure encoding begins with charset= */ + if (strncmp(encoding, "text/plain; charset=", 20) && + strncmp(encoding, "text/aolrtf; charset=", 21) && + strncmp(encoding, "text/x-aolrtf; charset=", 23)) + { + return NULL; + } + + begin = strchr(encoding, '"'); + end = strrchr(encoding, '"'); + + if ((begin == NULL) || (end == NULL) || (begin >= end)) + return NULL; + + ret = g_strndup(begin+1, (end-1) - begin); + + return ret; +} + +static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen) +{ + char *utf8 = g_new0(char, 8192); + + if ((encoding == NULL) || encoding[0] == '\0') { + /* gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/ + } else if (!g_strcasecmp(encoding, "iso-8859-1")) { + do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192); + } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) { + do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192); + } else if (!g_strcasecmp(encoding, "unicode-2-0")) { + do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192); + } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) { + /* gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", " + "attempting to convert to UTF-8 anyway\n", encoding);*/ + do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192); + } + + /* + * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or + * we have been unable to convert the text to utf-8 from the encoding + * that was specified. So we check if the text is valid utf-8 then + * just copy it. + */ + if (*utf8 == 0) { + if (textlen != 0 && *text != '\0' + && !g_utf8_validate(text, textlen, NULL)) + strcpy(utf8, _("(There was an error receiving this message. The buddy you are speaking to most likely has a buggy client.)")); + else + strncpy(utf8, text, textlen); + } + + return utf8; +} + +static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...) +{ + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = gc->proto_data; + va_list ap; + aim_userinfo_t *userinfo; + guint16 infotype; + char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL; + guint16 text_length; + char *utf8 = NULL; + GSList *l; + + va_start(ap, fr); + userinfo = va_arg(ap, aim_userinfo_t *); + infotype = va_arg(ap, int); + text_encoding = va_arg(ap, char*); + text = va_arg(ap, char*); + text_length = va_arg(ap, int); + va_end(ap); + + if(text_encoding) + extracted_encoding = oscar_encoding_extract(text_encoding); + if(infotype == AIM_GETINFO_GENERALINFO) { + /*Display idle time*/ + char buff[256]; + struct tm idletime; + if(userinfo->idletime) { + memset(&idletime, 0, sizeof(struct tm)); + idletime.tm_mday = (userinfo->idletime / 60) / 24; + idletime.tm_hour = (userinfo->idletime / 60) % 24; + idletime.tm_min = userinfo->idletime % 60; + idletime.tm_sec = 0; + strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime); + serv_got_crap(gc, "%s: %s", _("Idle Time"), buff); + } + + if(text) { + utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length); + serv_got_crap(gc, "%s\n%s", _("User Info"), utf8); + } else { + serv_got_crap(gc, _("No user info available.")); + } + } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) { + utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length); + if((l = g_slist_find_custom(od->info_requests, userinfo->sn, (GCompareFunc)handle_cmp_aim))) { + /*If the user requested info, display it, otherwise just pass it on*/ + g_free(l->data); + od->info_requests = g_slist_remove(od->info_requests, l); + serv_got_crap(gc, "%s\n%s", _("Away Message"), utf8); + } + serv_got_away(gc, userinfo->sn, utf8); + } + + g_free(utf8); + + return 1; +} + +int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...) +{ + struct gaim_connection * gc = sess->aux_data; + va_list ap; + guint16 type1, type2; + char * sn; + + va_start(ap, fr); + type1 = va_arg(ap, int); + sn = va_arg(ap, char*); + type2 = va_arg(ap, int); + va_end(ap); + + if(type2 == 0x0001 || type2 == 0x0002) + serv_got_typing(gc, sn, 0); + + return 1; +} + static char *oscar_get_status_string( struct gaim_connection *gc, int number ) { struct oscar_data *od = gc->proto_data; @@ -2314,6 +2453,177 @@ } } +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; +} + +int oscar_send_typing(struct gaim_connection *gc, char * who, int typing) +{ + struct oscar_data *od = gc->proto_data; + return( aim_im_sendmtn(od->sess, 1, who, typing ? 0x0002 : 0x0000) ); +} + static struct prpl *my_protocol = NULL; void oscar_init(struct prpl *ret) { @@ -2327,6 +2637,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; @@ -2335,5 +2649,7 @@ ret->keepalive = oscar_keepalive; ret->get_status_string = oscar_get_status_string; + ret->send_typing = oscar_send_typing; + my_protocol = ret; } diff -Bur bitlbee-0.92/protocols/oscar/tlv.c bitlbee-all/protocols/oscar/tlv.c --- bitlbee-0.92/protocols/oscar/tlv.c 2004-09-21 16:34:44.000000000 -0400 +++ bitlbee-all/protocols/oscar/tlv.c 2005-03-26 16:48:17.000000000 -0500 @@ -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 diff -Bur bitlbee-0.92/protocols/util.c bitlbee-all/protocols/util.c --- bitlbee-0.92/protocols/util.c 2004-10-10 07:24:06.000000000 -0400 +++ bitlbee-all/protocols/util.c 2005-04-23 15:47:58.000000000 -0400 @@ -280,7 +280,7 @@ { "lt", '<' }, { "gt", '>' }, { "amp", '&' }, - { "quot", '\'' }, + { "quot", '"' }, { "dquot", '"' }, { "aacute", 'á' }, { "eacute", 'é' }, @@ -325,6 +325,8 @@ if( *in ) { + if( g_strncasecmp( cs+1, "br", 2) == 0 ) + *(s++) = '\n'; in ++; } else @@ -365,7 +367,40 @@ strcpy( start, out ); g_free( out ); -} +} + +char * escape_html(const char *html) +{ + const char *c = html; + GString *ret; + + if (html == NULL) + return NULL; + + ret = g_string_new(""); + + while (*c) { + switch (*c) { + case '&': + ret = g_string_append(ret, "&"); + break; + case '<': + ret = g_string_append(ret, "<"); + break; + case '>': + ret = g_string_append(ret, ">"); + break; + case '"': + ret = g_string_append(ret, """); + break; + default: + ret = g_string_append_c(ret, *c); + } + c++; + } + + return g_string_free(ret, FALSE); +} void info_string_append(GString *str, char *newline, char *name, char *value) { diff -Bur bitlbee-0.92/user.h bitlbee-all/user.h --- bitlbee-0.92/user.h 2004-10-29 19:58:00.000000000 -0400 +++ bitlbee-all/user.h 2005-04-16 18:08:07.000000000 -0400 @@ -43,7 +43,7 @@ int sendbuf_len; guint sendbuf_timer; - int (*send_handler) ( irc_t *irc, struct __USER *u, char *msg ); + int (*send_handler) ( irc_t *irc, struct __USER *u, char *msg, int flags ); struct __USER *next; } user_t;