Mercurial > pidgin
changeset 30369:613829cf571e
propagate from branch 'im.pidgin.pidgin' (head 3ccde42b2f5cfa1fae01e997cfa85f4f9b89abe5)
to branch 'im.pidgin.soc.2010.icq-tlc' (head 6290e1fa3bfba05e59462968ebbff460342c48dd)
author | ivan.komarov@soc.pidgin.im |
---|---|
date | Thu, 03 Jun 2010 06:33:24 +0000 |
parents | 98de21a2aa9a (current diff) 7fd5df3f519e (diff) |
children | 47392e1f491d |
files | libpurple/protocols/oscar/oscar.c |
diffstat | 14 files changed, 1151 insertions(+), 524 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/protocols/oscar/Makefile.am Thu Jun 03 04:10:13 2010 +0000 +++ b/libpurple/protocols/oscar/Makefile.am Thu Jun 03 06:33:24 2010 +0000 @@ -6,6 +6,7 @@ pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) OSCARSOURCES = \ + authorization.c \ bstream.c \ clientlogin.c \ family_admin.c \ @@ -44,6 +45,7 @@ snac.c \ snactypes.h \ tlv.c \ + userinfo.c \ util.c AM_CFLAGS = $(st)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/oscar/authorization.c Thu Jun 03 06:33:24 2010 +0000 @@ -0,0 +1,153 @@ +/* + * Purple's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +*/ + +/* + * Everything related to OSCAR authorization requests. + */ + +#include "oscar.h" +#include "request.h" + +static void +oscar_auth_request(struct name_data *data, char *msg) +{ + PurpleConnection *gc; + OscarData *od; + PurpleAccount *account; + PurpleBuddy *buddy; + PurpleGroup *group; + const char *bname, *gname; + + gc = data->gc; + od = purple_connection_get_protocol_data(gc); + account = purple_connection_get_account(gc); + buddy = purple_find_buddy(account, data->name); + if (buddy != NULL) + group = purple_buddy_get_group(buddy); + else + group = NULL; + + if (group != NULL) + { + bname = purple_buddy_get_name(buddy); + gname = purple_group_get_name(group); + purple_debug_info("oscar", "ssi: adding buddy %s to group %s\n", + bname, gname); + aim_ssi_sendauthrequest(od, data->name, msg ? msg : _("Please authorize me so I can add you to my buddy list.")); + if (!aim_ssi_itemlist_finditem(od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY)) + { + aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, TRUE); + + /* Mobile users should always be online */ + if (bname[0] == '+') { + purple_prpl_got_user_status(account, + purple_buddy_get_name(buddy), + OSCAR_STATUS_ID_AVAILABLE, NULL); + purple_prpl_got_user_status(account, + purple_buddy_get_name(buddy), + OSCAR_STATUS_ID_MOBILE, NULL); + } + } + } + + oscar_free_name_data(data); +} + +static void +oscar_auth_grant(gpointer cbdata) +{ + struct name_data *data = cbdata; + PurpleConnection *gc = data->gc; + OscarData *od = purple_connection_get_protocol_data(gc); + + aim_ssi_sendauthreply(od, data->name, 0x01, NULL); + + oscar_free_name_data(data); +} + +static void +oscar_auth_dontgrant(struct name_data *data, char *msg) +{ + PurpleConnection *gc = data->gc; + OscarData *od = purple_connection_get_protocol_data(gc); + + aim_ssi_sendauthreply(od, data->name, 0x00, msg ? msg : _("No reason given.")); + + oscar_free_name_data(data); +} + +static void +oscar_auth_dontgrant_msgprompt(gpointer cbdata) +{ + struct name_data *data = cbdata; + purple_request_input(data->gc, NULL, _("Authorization Denied Message:"), + NULL, _("No reason given."), TRUE, FALSE, NULL, + _("_OK"), G_CALLBACK(oscar_auth_dontgrant), + _("_Cancel"), G_CALLBACK(oscar_free_name_data), + purple_connection_get_account(data->gc), data->name, NULL, + data); +} + +/* When you ask other people for authorization */ +void +oscar_auth_sendrequest(PurpleConnection *gc, const char *name) +{ + struct name_data *data; + + data = g_new0(struct name_data, 1); + data->gc = gc; + data->name = g_strdup(name); + + purple_request_input(data->gc, NULL, _("Authorization Request Message:"), + NULL, _("Please authorize me!"), TRUE, FALSE, NULL, + _("_OK"), G_CALLBACK(oscar_auth_request), + _("_Cancel"), G_CALLBACK(oscar_free_name_data), + purple_connection_get_account(gc), name, NULL, + data); +} + +void +oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored) +{ + PurpleBuddy *buddy; + PurpleConnection *gc; + + g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); + + buddy = (PurpleBuddy *) node; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + oscar_auth_sendrequest(gc, purple_buddy_get_name(buddy)); +} + +/* When other people ask you for authorization */ +void +oscar_auth_recvrequest(PurpleConnection *gc, gchar *name, gchar *nick, gchar *reason) +{ + PurpleAccount* account = purple_connection_get_account(gc); + struct name_data *data = g_new(struct name_data, 1); + + data->gc = gc; + data->name = name; + data->nick = nick; + + purple_account_request_authorization(account, data->name, NULL, data->nick, + reason, purple_find_buddy(account, data->name) != NULL, + oscar_auth_grant, oscar_auth_dontgrant_msgprompt, data); +} \ No newline at end of file
--- a/libpurple/protocols/oscar/family_icbm.c Thu Jun 03 04:10:13 2010 +0000 +++ b/libpurple/protocols/oscar/family_icbm.c Thu Jun 03 06:33:24 2010 +0000 @@ -2495,181 +2495,6 @@ return 0; } -static void parse_status_note_text(OscarData *od, guchar *cookie, char *bn, ByteStream *bs) -{ - struct aim_icq_info *info; - struct aim_icq_info *prev_info; - char *response; - char *encoding; - char *stripped_encoding; - char *status_note_title; - char *status_note_text; - char *stripped_status_note_text; - char *status_note; - guint32 length; - guint16 version; - guint32 capability; - guint8 message_type; - guint16 status_code; - guint16 text_length; - guint32 request_length; - guint32 response_length; - guint32 encoding_length; - PurpleAccount *account; - PurpleBuddy *buddy; - PurplePresence *presence; - PurpleStatus *status; - - for (prev_info = NULL, info = od->icq_info; info != NULL; prev_info = info, info = info->next) - { - if (memcmp(&info->icbm_cookie, cookie, 8) == 0) - { - if (prev_info == NULL) - od->icq_info = info->next; - else - prev_info->next = info->next; - - break; - } - } - - if (info == NULL) - return; - - status_note_title = info->status_note_title; - g_free(info); - - length = byte_stream_getle16(bs); - if (length != 27) { - purple_debug_misc("oscar", "clientautoresp: incorrect header " - "size; expected 27, received %u.\n", length); - g_free(status_note_title); - return; - } - - version = byte_stream_getle16(bs); - if (version != 9) { - purple_debug_misc("oscar", "clientautoresp: incorrect version; " - "expected 9, received %u.\n", version); - g_free(status_note_title); - return; - } - - capability = aim_locate_getcaps(od, bs, 0x10); - if (capability != OSCAR_CAPABILITY_EMPTY) { - purple_debug_misc("oscar", "clientautoresp: plugin ID is not null.\n"); - g_free(status_note_title); - return; - } - - byte_stream_advance(bs, 2); /* unknown */ - byte_stream_advance(bs, 4); /* client capabilities flags */ - byte_stream_advance(bs, 1); /* unknown */ - byte_stream_advance(bs, 2); /* downcouner? */ - - length = byte_stream_getle16(bs); - if (length != 14) { - purple_debug_misc("oscar", "clientautoresp: incorrect header " - "size; expected 14, received %u.\n", length); - g_free(status_note_title); - return; - } - - byte_stream_advance(bs, 2); /* downcounter? */ - byte_stream_advance(bs, 12); /* unknown */ - - message_type = byte_stream_get8(bs); - if (message_type != 0x1a) { - purple_debug_misc("oscar", "clientautoresp: incorrect message " - "type; expected 0x1a, received 0x%x.\n", message_type); - g_free(status_note_title); - return; - } - - byte_stream_advance(bs, 1); /* message flags */ - - status_code = byte_stream_getle16(bs); - if (status_code != 0) { - purple_debug_misc("oscar", "clientautoresp: incorrect status " - "code; expected 0, received %u.\n", status_code); - g_free(status_note_title); - return; - } - - byte_stream_advance(bs, 2); /* priority code */ - - text_length = byte_stream_getle16(bs); - byte_stream_advance(bs, text_length); /* text */ - - length = byte_stream_getle16(bs); - byte_stream_advance(bs, 18); /* unknown */ - - request_length = byte_stream_getle32(bs); - if (length != 18 + 4 + request_length + 17) { - purple_debug_misc("oscar", "clientautoresp: incorrect block; " - "expected length is %u, got %u.\n", - 18 + 4 + request_length + 17, length); - g_free(status_note_title); - return; - } - - byte_stream_advance(bs, request_length); /* x request */ - byte_stream_advance(bs, 17); /* unknown */ - - length = byte_stream_getle32(bs); - response_length = byte_stream_getle32(bs); - response = byte_stream_getstr(bs, response_length); - encoding_length = byte_stream_getle32(bs); - if (length != 4 + response_length + 4 + encoding_length) { - purple_debug_misc("oscar", "clientautoresp: incorrect block; " - "expected length is %u, got %u.\n", - 4 + response_length + 4 + encoding_length, length); - g_free(status_note_title); - g_free(response); - return; - } - - encoding = byte_stream_getstr(bs, encoding_length); - - account = purple_connection_get_account(od->gc); - - stripped_encoding = oscar_encoding_extract(encoding); - status_note_text = oscar_encoding_to_utf8(account, stripped_encoding, response, response_length); - stripped_status_note_text = purple_markup_strip_html(status_note_text); - - if (stripped_status_note_text != NULL && stripped_status_note_text[0] != 0) - status_note = g_strdup_printf("%s: %s", status_note_title, stripped_status_note_text); - else - status_note = g_strdup(status_note_title); - - g_free(status_note_title); - g_free(response); - g_free(encoding); - g_free(stripped_encoding); - g_free(status_note_text); - g_free(stripped_status_note_text); - - buddy = purple_find_buddy(account, bn); - if (buddy == NULL) - { - purple_debug_misc("oscar", "clientautoresp: buddy %s was not found.\n", bn); - g_free(status_note); - return; - } - - purple_debug_misc("oscar", "clientautoresp: setting status " - "message to \"%s\".\n", status_note); - - presence = purple_buddy_get_presence(buddy); - status = purple_presence_get_active_status(presence); - - purple_prpl_got_user_status(account, bn, - purple_status_get_id(status), - "message", status_note, NULL); - - g_free(status_note); -} - /* * Subtype 0x000b - Receive the response from an ICQ status message * request (in which case this contains the ICQ status message) or
--- a/libpurple/protocols/oscar/family_icq.c Thu Jun 03 04:10:13 2010 +0000 +++ b/libpurple/protocols/oscar/family_icq.c Thu Jun 03 06:33:24 2010 +0000 @@ -25,75 +25,101 @@ #include "oscar.h" -#ifdef OLDSTYLE_ICQ_OFFLINEMSGS -int aim_icq_reqofflinemsgs(OscarData *od) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - int bslen; +#define AIM_ICQ_INFO_REQUEST 0x04b2 +#define AIM_ICQ_ALIAS_REQUEST 0x04ba - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) - return -EINVAL; +static +int compare_icq_infos(gconstpointer a, gconstpointer b) +{ + const struct aim_icq_info* aa = a; + const guint16* bb = b; + return aa->reqid - *bb; +} - purple_debug_info("oscar", "Requesting offline messages\n"); - - bslen = 2 + 4 + 2 + 2; - - byte_stream_new(&bs, 4 + bslen); +static void aim_icq_freeinfo(struct aim_icq_info *info) { + int i; - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0); - - /* For simplicity, don't bother using a tlvlist */ - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, bslen); - - byte_stream_putle16(&bs, bslen - 2); - byte_stream_putuid(&bs, od); - byte_stream_putle16(&bs, 0x003c); /* I command thee. */ - byte_stream_putle16(&bs, snacid); /* eh. */ - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; + if (!info) + return; + g_free(info->nick); + g_free(info->first); + g_free(info->last); + g_free(info->email); + g_free(info->homecity); + g_free(info->homestate); + g_free(info->homephone); + g_free(info->homefax); + g_free(info->homeaddr); + g_free(info->mobile); + g_free(info->homezip); + g_free(info->personalwebpage); + if (info->email2) + for (i = 0; i < info->numaddresses; i++) + g_free(info->email2[i]); + g_free(info->email2); + g_free(info->workcity); + g_free(info->workstate); + g_free(info->workphone); + g_free(info->workfax); + g_free(info->workaddr); + g_free(info->workzip); + g_free(info->workcompany); + g_free(info->workdivision); + g_free(info->workposition); + g_free(info->workwebpage); + g_free(info->info); + g_free(info->status_note_title); + g_free(info->auth_request_reason); } -int aim_icq_ackofflinemsgs(OscarData *od) +static +int error(OscarData *od, aim_modsnac_t *error_snac, ByteStream *bs) { - ByteStream bs; - FlapFrame *frame; - aim_snacid_t snacid; - int bslen; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) - return -EINVAL; - - purple_debug_info("oscar", "Acknowledged receipt of offline messages\n"); - - bslen = 2 + 4 + 2 + 2; - - byte_stream_new(&bs, 4 + bslen); + aim_snac_t *original_snac = aim_remsnac(od, error_snac->id); + guint16 *request_type; + GSList *original_info_ptr; + struct aim_icq_info *original_info; + guint16 reason; + gchar *uin; - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0); - - /* For simplicity, don't bother using a tlvlist */ - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, bslen); + if (!original_snac || (original_snac->family != SNAC_FAMILY_ICQ) || !original_snac->data) { + purple_debug_misc("oscar", "icq: the original snac for the error packet was not found"); + g_free(original_snac); + return 0; + } + + request_type = original_snac->data; + original_info_ptr = g_slist_find_custom(od->icq_info, &original_snac->id, compare_icq_infos); + original_info = original_info_ptr->data; + + if (!original_info_ptr) { + purple_debug_misc("oscar", "icq: the request info for the error packet was not found"); + g_free(original_snac); + return 0; + } + + reason = byte_stream_get16(bs); + uin = g_strdup_printf("%u", original_info->uin); + switch (*request_type) { + case AIM_ICQ_INFO_REQUEST: + oscar_user_info_display_error(od, reason, uin); + break; + case AIM_ICQ_ALIAS_REQUEST: + /* Couldn't retrieve an alias for the buddy requesting authorization; have to make do with UIN only. */ + if (original_info->for_auth_request) + oscar_auth_recvrequest(od->gc, uin, NULL, original_info->auth_request_reason); + break; + default: + purple_debug_misc("oscar", "icq: got an error packet with unknown request type %u", *request_type); + break; + } - byte_stream_putle16(&bs, bslen - 2); - byte_stream_putuid(&bs, od); - byte_stream_putle16(&bs, 0x003e); /* I command thee. */ - byte_stream_putle16(&bs, snacid); /* eh. */ - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; + aim_icq_freeinfo(original_info); + od->icq_info = g_slist_remove(od->icq_info, original_info_ptr); + g_free(original_snac->data); + g_free(original_snac); + return 1; } -#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */ int aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware) @@ -194,6 +220,7 @@ aim_snacid_t snacid; int bslen; struct aim_icq_info *info; + guint16 request_type = AIM_ICQ_INFO_REQUEST; if (!uin || uin[0] < '0' || uin[0] > '9') return -EINVAL; @@ -205,7 +232,7 @@ byte_stream_new(&bs, 4 + bslen); - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0); + snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type)); /* For simplicity, don't bother using a tlvlist */ byte_stream_put16(&bs, 0x0001); @@ -215,7 +242,7 @@ byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ - byte_stream_putle16(&bs, 0x04b2); /* shrug. */ + byte_stream_putle16(&bs, request_type); /* shrug. */ byte_stream_putle32(&bs, atoi(uin)); flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE); @@ -226,19 +253,19 @@ info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1); info->reqid = snacid; info->uin = atoi(uin); - info->next = od->icq_info; - od->icq_info = info; + od->icq_info = g_slist_prepend(od->icq_info, info); return 0; } -int aim_icq_getalias(OscarData *od, const char *uin) +int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason) { FlapConnection *conn; ByteStream bs; aim_snacid_t snacid; int bslen; struct aim_icq_info *info; + guint16 request_type = AIM_ICQ_ALIAS_REQUEST; if (!uin || uin[0] < '0' || uin[0] > '9') return -EINVAL; @@ -252,7 +279,7 @@ byte_stream_new(&bs, 4 + bslen); - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0); + snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type)); /* For simplicity, don't bother using a tlvlist */ byte_stream_put16(&bs, 0x0001); @@ -262,7 +289,7 @@ byte_stream_putuid(&bs, od); byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ byte_stream_putle16(&bs, snacid); /* eh. */ - byte_stream_putle16(&bs, 0x04ba); /* shrug. */ + byte_stream_putle16(&bs, request_type); /* shrug. */ byte_stream_putle32(&bs, atoi(uin)); flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE); @@ -273,89 +300,13 @@ info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1); info->reqid = snacid; info->uin = atoi(uin); - info->next = od->icq_info; - od->icq_info = info; + info->for_auth_request = for_auth_request; + info->auth_request_reason = g_strdup(auth_request_reason); + od->icq_info = g_slist_prepend(od->icq_info, info); return 0; } -int aim_icq_getsimpleinfo(OscarData *od, const char *uin) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - int bslen; - - if (!uin || uin[0] < '0' || uin[0] > '9') - return -EINVAL; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) - return -EINVAL; - - bslen = 2 + 4 + 2 + 2 + 2 + 4; - - byte_stream_new(&bs, 4 + bslen); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0); - - /* For simplicity, don't bother using a tlvlist */ - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, bslen); - - byte_stream_putle16(&bs, bslen - 2); - byte_stream_putuid(&bs, od); - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ - byte_stream_putle16(&bs, snacid); /* eh. */ - byte_stream_putle16(&bs, 0x051f); /* shrug. */ - byte_stream_putle32(&bs, atoi(uin)); - - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE); - - byte_stream_destroy(&bs); - - return 0; -} - -#if 0 -int aim_icq_sendxmlreq(OscarData *od, const char *xml) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - int bslen; - - if (!xml || !strlen(xml)) - return -EINVAL; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) - return -EINVAL; - - bslen = 2 + 10 + 2 + strlen(xml) + 1; - - byte_stream_new(&bs, 4 + bslen); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0); - - /* For simplicity, don't bother using a tlvlist */ - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, bslen); - - byte_stream_putle16(&bs, bslen - 2); - byte_stream_putuid(&bs, od); - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ - byte_stream_putle16(&bs, snacid); /* eh. */ - byte_stream_putle16(&bs, 0x0998); /* shrug. */ - byte_stream_putle16(&bs, strlen(xml) + 1); - byte_stream_putraw(&bs, (guint8 *)xml, strlen(xml) + 1); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} -#endif - /* * Send an SMS message. This is the non-US way. The US-way is to IM * their cell phone number (+19195551234). @@ -456,49 +407,35 @@ return 0; } -static void aim_icq_freeinfo(struct aim_icq_info *info) { - int i; +static int +gotalias(OscarData *od, struct aim_icq_info *info) +{ + PurpleConnection *gc = od->gc; + PurpleAccount *account = purple_connection_get_account(gc); + gchar who[16], *utf8; + PurpleBuddy *b; - if (!info) - return; - g_free(info->nick); - g_free(info->first); - g_free(info->last); - g_free(info->email); - g_free(info->homecity); - g_free(info->homestate); - g_free(info->homephone); - g_free(info->homefax); - g_free(info->homeaddr); - g_free(info->mobile); - g_free(info->homezip); - g_free(info->personalwebpage); - if (info->email2) - for (i = 0; i < info->numaddresses; i++) - g_free(info->email2[i]); - g_free(info->email2); - g_free(info->workcity); - g_free(info->workstate); - g_free(info->workphone); - g_free(info->workfax); - g_free(info->workaddr); - g_free(info->workzip); - g_free(info->workcompany); - g_free(info->workdivision); - g_free(info->workposition); - g_free(info->workwebpage); - g_free(info->info); - g_free(info->status_note_title); - g_free(info); + if (info->nick[0] && (utf8 = oscar_utf8_try_convert(account, od, info->nick))) { + if (info->for_auth_request) { + oscar_auth_recvrequest(gc, g_strdup_printf("%u", info->uin), utf8, info->auth_request_reason); + } else { + g_snprintf(who, sizeof(who), "%u", info->uin); + serv_got_alias(gc, who, utf8); + if ((b = purple_find_buddy(account, who))) { + purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", utf8); + } + g_free(utf8); + } + } + return 1; } /** * Subtype 0x0003 - Response to SNAC_FAMILY_ICQ/0x002, contains an ICQesque packet. */ static int -icqresponse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) +icqresponse(OscarData *od, aim_modsnac_t *snac, ByteStream *bs) { - int ret = 0; GSList *tlvlist; aim_tlv_t *datatlv; ByteStream qbs; @@ -520,53 +457,23 @@ purple_debug_misc("oscar", "icq response: %d bytes, %u, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid); - if (cmd == 0x0041) { /* offline message */ -#ifdef OLDSTYLE_ICQ_OFFLINEMSGS - struct aim_icq_offlinemsg msg; - aim_rxcallback_t userfunc; - - memset(&msg, 0, sizeof(msg)); - - msg.sender = byte_stream_getle32(&qbs); - msg.year = byte_stream_getle16(&qbs); - msg.month = byte_stream_getle8(&qbs); - msg.day = byte_stream_getle8(&qbs); - msg.hour = byte_stream_getle8(&qbs); - msg.minute = byte_stream_getle8(&qbs); - msg.type = byte_stream_getle8(&qbs); - msg.flags = byte_stream_getle8(&qbs); - msg.msglen = byte_stream_getle16(&qbs); - msg.msg = byte_stream_getstr(&qbs, msg.msglen); - - if ((userfunc = aim_callhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSG))) - ret = userfunc(od, conn, frame, &msg); - - g_free(msg.msg); - - } else if (cmd == 0x0042) { - aim_rxcallback_t userfunc; - - if ((userfunc = aim_callhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE))) - ret = userfunc(od, conn, frame); -#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */ - - } else if (cmd == 0x07da) { /* information */ + if (cmd == 0x07da) { /* information */ guint16 subtype; + GSList *info_ptr; struct aim_icq_info *info; - aim_rxcallback_t userfunc; subtype = byte_stream_getle16(&qbs); byte_stream_advance(&qbs, 1); /* 0x0a */ /* find other data from the same request */ - for (info = od->icq_info; info && (info->reqid != reqid); info = info->next); - if (!info) { - info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1); - info->reqid = reqid; - info->next = od->icq_info; - od->icq_info = info; + info_ptr = g_slist_find_custom(od->icq_info, &reqid, compare_icq_infos); + if (!info_ptr) { + struct aim_icq_info *new_info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1); + new_info->reqid = reqid; + info_ptr = od->icq_info = g_slist_prepend(od->icq_info, new_info); } + info = info_ptr->data; switch (subtype) { case 0x00a0: { /* hide ip status */ /* nothing */ @@ -818,8 +725,7 @@ memcpy(&info->icbm_cookie, cookie, 8); - info->next = od->icq_info; - od->icq_info = info; + od->icq_info = g_slist_prepend(od->icq_info, info); flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs, FALSE); @@ -834,35 +740,28 @@ if (!(snac->flags & 0x0001)) { if (subtype != 0x0104) - if ((userfunc = aim_callhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_INFO))) - ret = userfunc(od, conn, frame, info); + oscar_user_info_display_icq(od, info); if (info->uin && info->nick) - if ((userfunc = aim_callhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_ALIAS))) - ret = userfunc(od, conn, frame, info); + gotalias(od, info); - if (od->icq_info == info) { - od->icq_info = info->next; - } else { - struct aim_icq_info *cur; - for (cur=od->icq_info; (cur->next && (cur->next!=info)); cur=cur->next); - if (cur->next) - cur->next = cur->next->next; - } aim_icq_freeinfo(info); + od->icq_info = g_slist_remove(od->icq_info, info); } } aim_tlvlist_free(tlvlist); - return ret; + return 1; } static int snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { - if (snac->subtype == 0x0003) - return icqresponse(od, conn, mod, frame, snac, bs); + if (snac->subtype == 0x0001) + return error(od, snac, bs); + else if (snac->subtype == 0x0003) + return icqresponse(od, snac, bs); return 0; } @@ -870,15 +769,10 @@ static void icq_shutdown(OscarData *od, aim_module_t *mod) { - struct aim_icq_info *del; - - while (od->icq_info) { - del = od->icq_info; - od->icq_info = od->icq_info->next; - aim_icq_freeinfo(del); - } - - return; + GSList *cur; + for (cur = od->icq_info; cur; cur = cur->next) + aim_icq_freeinfo(cur->data); + g_slist_free(od->icq_info); } int
--- a/libpurple/protocols/oscar/family_locate.c Thu Jun 03 04:10:13 2010 +0000 +++ b/libpurple/protocols/oscar/family_locate.c Thu Jun 03 06:33:24 2010 +0000 @@ -1225,8 +1225,6 @@ static int error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { - int ret = 0; - aim_rxcallback_t userfunc; aim_snac_t *snac2; guint16 reason; char *bn; @@ -1253,14 +1251,12 @@ reason = byte_stream_get16(bs); - /* Notify the user that we do not have info for this buddy */ - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, reason, bn); + oscar_user_info_display_error(od, reason, bn); g_free(snac2->data); g_free(snac2); - return ret; + return 1; } /* @@ -1470,7 +1466,6 @@ userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { int ret = 0; - aim_rxcallback_t userfunc; aim_userinfo_t *userinfo, *userinfo2; GSList *tlvlist; aim_tlv_t *tlv = NULL; @@ -1522,8 +1517,7 @@ g_free(userinfo); /* Show the info to the user */ - if (userinfo2 != NULL && ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))) - ret = userfunc(od, conn, frame, userinfo2); + oscar_user_info_display_aim(od, userinfo2); return ret; }
--- a/libpurple/protocols/oscar/oscar.c Thu Jun 03 04:10:13 2010 +0000 +++ b/libpurple/protocols/oscar/oscar.c Thu Jun 03 06:33:24 2010 +0000 @@ -4651,8 +4651,7 @@ tmp2 = purple_markup_strip_html(tmp1); is_html = FALSE; } else { - /* ICQ 6 wants its HTML wrapped in these tags. Oblige it. */ - tmp2 = g_strdup_printf("<HTML><BODY>%s</BODY></HTML>", tmp1); + tmp2 = g_strdup(tmp1); is_html = TRUE; } g_free(tmp1);
--- a/libpurple/protocols/oscar/oscar.h Thu Jun 03 04:10:13 2010 +0000 +++ b/libpurple/protocols/oscar/oscar.h Thu Jun 03 06:33:24 2010 +0000 @@ -378,6 +378,22 @@ #define OSCAR_CAPABILITY_GENERICUNKNOWN 0x0000000080000000LL #define OSCAR_CAPABILITY_LAST 0x0000000100000000LL +#define OSCAR_STATUS_ID_INVISIBLE "invisible" +#define OSCAR_STATUS_ID_OFFLINE "offline" +#define OSCAR_STATUS_ID_AVAILABLE "available" +#define OSCAR_STATUS_ID_AWAY "away" +#define OSCAR_STATUS_ID_DND "dnd" +#define OSCAR_STATUS_ID_NA "na" +#define OSCAR_STATUS_ID_OCCUPIED "occupied" +#define OSCAR_STATUS_ID_FREE4CHAT "free4chat" +#define OSCAR_STATUS_ID_CUSTOM "custom" +#define OSCAR_STATUS_ID_MOBILE "mobile" +#define OSCAR_STATUS_ID_EVIL "evil" +#define OSCAR_STATUS_ID_DEPRESSION "depression" +#define OSCAR_STATUS_ID_ATHOME "athome" +#define OSCAR_STATUS_ID_ATWORK "atwork" +#define OSCAR_STATUS_ID_LUNCH "lunch" + /* * Byte Stream type. Sort of. * @@ -526,7 +542,7 @@ */ IcbmCookie *msgcookies; - struct aim_icq_info *icq_info; + GSList *icq_info; /** Only used when connecting with the old-style BUCP login. */ struct aim_authresp_info *authinfo; @@ -1036,6 +1052,7 @@ void aim_icbm_makecookie(guchar* cookie); gchar *oscar_encoding_extract(const char *encoding); gchar *oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen); +gchar *oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg); gchar *purple_plugin_oscar_decode_im_part(PurpleAccount *account, const char *sourcebn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen); @@ -1342,19 +1359,6 @@ #define AIM_ICQ_INFO_UNKNOWN 0x100 #define AIM_ICQ_INFO_HAVEALL 0x1ff -#ifdef OLDSTYLE_ICQ_OFFLINEMSGS -struct aim_icq_offlinemsg -{ - guint32 sender; - guint16 year; - guint8 month, day, hour, minute; - guint8 type; - guint8 flags; - char *msg; - int msglen; -}; -#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */ - struct aim_icq_info { guint16 reqid; @@ -1410,22 +1414,17 @@ guint16 numaddresses; char **email2; - /* we keep track of these in a linked list because we're 1337 */ - struct aim_icq_info *next; - /* status note info */ guint8 icbm_cookie[8]; char *status_note_title; + + gboolean for_auth_request; + char *auth_request_reason; }; -#ifdef OLDSTYLE_ICQ_OFFLINEMSGS -int aim_icq_reqofflinemsgs(OscarData *od); -int aim_icq_ackofflinemsgs(OscarData *od); -#endif int aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware); int aim_icq_changepasswd(OscarData *od, const char *passwd); -int aim_icq_getsimpleinfo(OscarData *od, const char *uin); -int aim_icq_getalias(OscarData *od, const char *uin); +int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason); int aim_icq_getallinfo(OscarData *od, const char *uin); int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias); @@ -1573,7 +1572,7 @@ gboolean oscar_util_valid_name_icq(const char *bn); gboolean oscar_util_valid_name_sms(const char *bn); int oscar_util_name_compare(const char *bn1, const char *bn2); - +gchar *oscar_util_format_string(const char *str, const char *name); @@ -1727,6 +1726,45 @@ void flap_connection_destroy_chat(OscarData *od, FlapConnection *conn); +/* userinfo.c - displaying user information */ + +void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean strip_html_tags); +void oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo); +void oscar_user_info_display_error(OscarData *od, guint16 error_reason, char *buddy); +void oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info); +void oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo); + +/* authorization.c - OSCAR authorization requests */ +void oscar_auth_sendrequest(PurpleConnection *gc, const char *name); +void oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored); +void oscar_auth_recvrequest(PurpleConnection *gc, gchar *name, gchar *nick, gchar *reason); + +struct buddyinfo +{ + gboolean typingnot; + guint32 ipaddr; + + unsigned long ico_me_len; + unsigned long ico_me_csum; + time_t ico_me_time; + gboolean ico_informed; + + unsigned long ico_len; + unsigned long ico_csum; + time_t ico_time; + gboolean ico_need; + gboolean ico_sent; +}; + +struct name_data +{ + PurpleConnection *gc; + gchar *name; + gchar *nick; +}; + +void oscar_free_name_data(struct name_data *data); + #ifdef __cplusplus } #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/oscar/userinfo.c Thu Jun 03 06:33:24 2010 +0000 @@ -0,0 +1,576 @@ +/* + * Purple's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +*/ + +/* + * Displaying various information about buddies. + */ + +#include "oscar.h" + +static gchar * +oscar_caps_to_string(guint64 caps) +{ + GString *str; + const gchar *tmp; + guint64 bit = 1; + + str = g_string_new(""); + + if (!caps) { + return NULL; + } else while (bit <= OSCAR_CAPABILITY_LAST) { + if (bit & caps) { + switch (bit) { + case OSCAR_CAPABILITY_BUDDYICON: + tmp = _("Buddy Icon"); + break; + case OSCAR_CAPABILITY_TALK: + tmp = _("Voice"); + break; + case OSCAR_CAPABILITY_DIRECTIM: + tmp = _("AIM Direct IM"); + break; + case OSCAR_CAPABILITY_CHAT: + tmp = _("Chat"); + break; + case OSCAR_CAPABILITY_GETFILE: + tmp = _("Get File"); + break; + case OSCAR_CAPABILITY_SENDFILE: + tmp = _("Send File"); + break; + case OSCAR_CAPABILITY_GAMES: + case OSCAR_CAPABILITY_GAMES2: + tmp = _("Games"); + break; + case OSCAR_CAPABILITY_XTRAZ: + case OSCAR_CAPABILITY_NEWCAPS: + tmp = _("ICQ Xtraz"); + break; + case OSCAR_CAPABILITY_ADDINS: + tmp = _("Add-Ins"); + break; + case OSCAR_CAPABILITY_SENDBUDDYLIST: + tmp = _("Send Buddy List"); + break; + case OSCAR_CAPABILITY_ICQ_DIRECT: + tmp = _("ICQ Direct Connect"); + break; + case OSCAR_CAPABILITY_APINFO: + tmp = _("AP User"); + break; + case OSCAR_CAPABILITY_ICQRTF: + tmp = _("ICQ RTF"); + break; + case OSCAR_CAPABILITY_EMPTY: + tmp = _("Nihilist"); + break; + case OSCAR_CAPABILITY_ICQSERVERRELAY: + tmp = _("ICQ Server Relay"); + break; + case OSCAR_CAPABILITY_UNICODEOLD: + tmp = _("Old ICQ UTF8"); + break; + case OSCAR_CAPABILITY_TRILLIANCRYPT: + tmp = _("Trillian Encryption"); + break; + case OSCAR_CAPABILITY_UNICODE: + tmp = _("ICQ UTF8"); + break; + case OSCAR_CAPABILITY_HIPTOP: + tmp = _("Hiptop"); + break; + case OSCAR_CAPABILITY_SECUREIM: + tmp = _("Security Enabled"); + break; + case OSCAR_CAPABILITY_VIDEO: + tmp = _("Video Chat"); + break; + /* Not actually sure about this one... WinAIM doesn't show anything */ + case OSCAR_CAPABILITY_ICHATAV: + tmp = _("iChat AV"); + break; + case OSCAR_CAPABILITY_LIVEVIDEO: + tmp = _("Live Video"); + break; + case OSCAR_CAPABILITY_CAMERA: + tmp = _("Camera"); + break; + case OSCAR_CAPABILITY_ICHAT_SCREENSHARE: + tmp = _("Screen Sharing"); + break; + default: + tmp = NULL; + break; + } + if (tmp) + g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp); + } + bit <<= 1; + } + + return g_string_free(str, FALSE); +} + +static void +oscar_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *name, const char *value) +{ + if (value && value[0]) { + purple_notify_user_info_add_pair(user_info, name, value); + } +} + +static void +oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info, + const char *name, const char *value) +{ + gchar *utf8; + + if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) { + purple_notify_user_info_add_pair(user_info, name, utf8); + g_free(utf8); + } +} + +/** + * @brief Append the status information to a user_info struct + * + * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML. + * + * @param gc The PurpleConnection + * @param user_info A PurpleNotifyUserInfo object to which status information will be added + * @param b The PurpleBuddy whose status is desired. This or the aim_userinfo_t (or both) must be passed to oscar_user_info_append_status(). + * @param userinfo The aim_userinfo_t of the buddy whose status is desired. This or the PurpleBuddy (or both) must be passed to oscar_user_info_append_status(). + * @param strip_html_tags If strip_html_tags is TRUE, tags embedded in the status message will be stripped, returning a non-formatted string. The string will still be HTML escaped. + */ +void +oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean strip_html_tags) +{ + PurpleAccount *account = purple_connection_get_account(gc); + OscarData *od; + PurplePresence *presence = NULL; + PurpleStatus *status = NULL; + gchar *message = NULL, *itmsurl = NULL, *tmp; + gboolean is_away; + + od = purple_connection_get_protocol_data(gc); + + if (b == NULL && userinfo == NULL) + return; + + if (b == NULL) + b = purple_find_buddy(purple_connection_get_account(gc), userinfo->bn); + else + userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b)); + + if (b) { + presence = purple_buddy_get_presence(b); + status = purple_presence_get_active_status(presence); + } + + /* If we have both b and userinfo we favor userinfo, because if we're + viewing someone's profile then we want the HTML away message, and + the "message" attribute of the status contains only the plaintext + message. */ + if (userinfo) { + if ((userinfo->flags & AIM_FLAG_AWAY) + && userinfo->away_len > 0 + && userinfo->away != NULL + && userinfo->away_encoding != NULL) + { + /* Away message */ + tmp = oscar_encoding_extract(userinfo->away_encoding); + message = oscar_encoding_to_utf8(account, + tmp, userinfo->away, userinfo->away_len); + g_free(tmp); + } else { + /* + * Available message or non-HTML away message (because that's + * all we have right now. + */ + if ((userinfo->status != NULL) && userinfo->status[0] != '\0') { + message = oscar_encoding_to_utf8(account, + userinfo->status_encoding, userinfo->status, + userinfo->status_len); + } +#if defined (_WIN32) || defined (__APPLE__) + if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) + itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding, + userinfo->itmsurl, userinfo->itmsurl_len); +#endif + } + } else { + message = g_strdup(purple_status_get_attr_string(status, "message")); + itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl")); + } + + is_away = ((status && !purple_status_is_available(status)) || + (userinfo && (userinfo->flags & AIM_FLAG_AWAY))); + + if (strip_html_tags) { + /* Away messages are HTML, but available messages were originally plain text. + * We therefore need to strip away messages but not available messages if we're asked to remove HTML tags. + */ + /* + * It seems like the above comment no longer applies. All messages need + * to be escaped. + */ + if (message) { + gchar *tmp2; + tmp = purple_markup_strip_html(message); + g_free(message); + tmp2 = g_markup_escape_text(tmp, -1); + g_free(tmp); + message = tmp2; + } + + } else { + if (itmsurl) { + tmp = g_strdup_printf("<a href=\"%s\">%s</a>", + itmsurl, message); + g_free(message); + message = tmp; + } + } + g_free(itmsurl); + + if (message) { + tmp = oscar_util_format_string(message, purple_account_get_username(account)); + g_free(message); + message = tmp; + } + + if (b) { + if (purple_presence_is_online(presence)) { + if (oscar_util_valid_name_icq(purple_buddy_get_name(b)) || is_away || !message || !(*message)) { + /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message. + * If the status name and the message are the same, only show one. */ + const char *status_name = purple_status_get_name(status); + if (status_name && message && !strcmp(status_name, message)) + status_name = NULL; + + tmp = g_strdup_printf("%s%s%s", + status_name ? status_name : "", + ((status_name && message) && *message) ? ": " : "", + (message && *message) ? message : ""); + g_free(message); + message = tmp; + } + + } else if (aim_ssi_waitingforauth(od->ssi.local, + aim_ssi_itemlist_findparentname(od->ssi.local, purple_buddy_get_name(b)), + purple_buddy_get_name(b))) + { + /* Note if an offline buddy is not authorized */ + tmp = g_strdup_printf("%s%s%s", + _("Not Authorized"), + (message && *message) ? ": " : "", + (message && *message) ? message : ""); + g_free(message); + message = tmp; + } else { + g_free(message); + message = g_strdup(_("Offline")); + } + } + + if (presence) { + const char *mood; + const char *description; + status = purple_presence_get_status(presence, "mood"); + mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); + description = icq_get_custom_icon_description(mood); + if (description && *description) + purple_notify_user_info_add_pair(user_info, _("Mood"), _(description)); + } + + purple_notify_user_info_add_pair(user_info, _("Status"), message); + g_free(message); +} + +void +oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo) +{ + OscarData *od; + PurpleAccount *account; + PurplePresence *presence = NULL; + PurpleStatus *status = NULL; + PurpleGroup *g = NULL; + struct buddyinfo *bi = NULL; + char *tmp; + const char *bname = NULL, *gname = NULL; + + od = purple_connection_get_protocol_data(gc); + account = purple_connection_get_account(gc); + + if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL))) + return; + + if (userinfo == NULL) + userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b)); + + if (b == NULL) + b = purple_find_buddy(account, userinfo->bn); + + if (b != NULL) { + bname = purple_buddy_get_name(b); + g = purple_buddy_get_group(b); + gname = purple_group_get_name(g); + presence = purple_buddy_get_presence(b); + status = purple_presence_get_active_status(presence); + } + + if (userinfo != NULL) + bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn)); + + if ((bi != NULL) && (bi->ipaddr != 0)) { + tmp = g_strdup_printf("%hhu.%hhu.%hhu.%hhu", + (bi->ipaddr & 0xff000000) >> 24, + (bi->ipaddr & 0x00ff0000) >> 16, + (bi->ipaddr & 0x0000ff00) >> 8, + (bi->ipaddr & 0x000000ff)); + oscar_user_info_add_pair(user_info, _("IP Address"), tmp); + g_free(tmp); + } + + if ((userinfo != NULL) && (userinfo->warnlevel != 0)) { + tmp = g_strdup_printf("%d", (int)(userinfo->warnlevel/10.0 + .5)); + oscar_user_info_add_pair(user_info, _("Warning Level"), tmp); + g_free(tmp); + } + + if ((b != NULL) && (bname != NULL) && (g != NULL) && (gname != NULL)) { + tmp = aim_ssi_getcomment(od->ssi.local, gname, bname); + if (tmp != NULL) { + char *tmp2 = g_markup_escape_text(tmp, strlen(tmp)); + g_free(tmp); + + oscar_user_info_convert_and_add(account, od, user_info, _("Buddy Comment"), tmp2); + g_free(tmp2); + } + } +} + +void +oscar_user_info_display_error(OscarData *od, guint16 error_reason, gchar *buddy) +{ + PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); + gchar *buf = g_strdup_printf(_("User information not available: %s"), oscar_get_msgerr_reason(error_reason)); + purple_notify_user_info_add_pair(user_info, NULL, buf); + purple_notify_userinfo(od->gc, buddy, user_info, NULL, NULL); + purple_notify_user_info_destroy(user_info); + purple_conv_present_error(buddy, purple_connection_get_account(od->gc), buf); + g_free(buf); +} + +void +oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info) +{ + PurpleConnection *gc = od->gc; + PurpleAccount *account = purple_connection_get_account(gc); + PurpleBuddy *buddy; + struct buddyinfo *bi; + gchar who[16]; + PurpleNotifyUserInfo *user_info; + gchar *utf8; + gchar *buf; + const gchar *alias; + + if (!info->uin) + return; + + user_info = purple_notify_user_info_new(); + + g_snprintf(who, sizeof(who), "%u", info->uin); + buddy = purple_find_buddy(account, who); + if (buddy != NULL) + bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, purple_buddy_get_name(buddy))); + else + bi = NULL; + + purple_notify_user_info_add_pair(user_info, _("UIN"), who); + oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick); + if ((bi != NULL) && (bi->ipaddr != 0)) { + char *tstr = g_strdup_printf("%hhu.%hhu.%hhu.%hhu", + (bi->ipaddr & 0xff000000) >> 24, + (bi->ipaddr & 0x00ff0000) >> 16, + (bi->ipaddr & 0x0000ff00) >> 8, + (bi->ipaddr & 0x000000ff)); + purple_notify_user_info_add_pair(user_info, _("IP Address"), tstr); + g_free(tstr); + } + oscar_user_info_convert_and_add(account, od, user_info, _("First Name"), info->first); + oscar_user_info_convert_and_add(account, od, user_info, _("Last Name"), info->last); + if (info->email && info->email[0] && (utf8 = oscar_utf8_try_convert(account, od, info->email))) { + buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8); + purple_notify_user_info_add_pair(user_info, _("Email Address"), buf); + g_free(buf); + g_free(utf8); + } + if (info->numaddresses && info->email2) { + int i; + for (i = 0; i < info->numaddresses; i++) { + if (info->email2[i] && info->email2[i][0] && (utf8 = oscar_utf8_try_convert(account, od, info->email2[i]))) { + buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8); + purple_notify_user_info_add_pair(user_info, _("Email Address"), buf); + g_free(buf); + g_free(utf8); + } + } + } + oscar_user_info_convert_and_add(account, od, user_info, _("Mobile Phone"), info->mobile); + + if (info->gender != 0) + purple_notify_user_info_add_pair(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male"))); + + if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) { + /* Initialize the struct properly or strftime() will crash + * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */ + time_t t = time(NULL); + struct tm *tm = localtime(&t); + + tm->tm_mday = (int)info->birthday; + tm->tm_mon = (int)info->birthmonth - 1; + tm->tm_year = (int)info->birthyear - 1900; + + /* To be 100% sure that the fields are re-normalized. + * If you're sure strftime() ALWAYS does this EVERYWHERE, + * feel free to remove it. --rlaager */ + mktime(tm); + + oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm)); + } + if ((info->age > 0) && (info->age < 255)) { + char age[5]; + snprintf(age, sizeof(age), "%hhd", info->age); + purple_notify_user_info_add_pair(user_info, _("Age"), age); + } + if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(account, od, info->personalwebpage))) { + buf = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8); + purple_notify_user_info_add_pair(user_info, _("Personal Web Page"), buf); + g_free(buf); + g_free(utf8); + } + + if (buddy != NULL) + oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* strip_html_tags */ FALSE); + + oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info); + purple_notify_user_info_add_section_break(user_info); + + if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) { + purple_notify_user_info_add_section_header(user_info, _("Home Address")); + + oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->homeaddr); + oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->homecity); + oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->homestate); + oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->homezip); + } + if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) { + purple_notify_user_info_add_section_header(user_info, _("Work Address")); + + oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->workaddr); + oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->workcity); + oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->workstate); + oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->workzip); + } + if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) { + purple_notify_user_info_add_section_header(user_info, _("Work Information")); + + oscar_user_info_convert_and_add(account, od, user_info, _("Company"), info->workcompany); + oscar_user_info_convert_and_add(account, od, user_info, _("Division"), info->workdivision); + oscar_user_info_convert_and_add(account, od, user_info, _("Position"), info->workposition); + + if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(account, od, info->workwebpage))) { + char *webpage = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8); + purple_notify_user_info_add_pair(user_info, _("Web Page"), webpage); + g_free(webpage); + g_free(utf8); + } + } + + if (buddy != NULL) + alias = purple_buddy_get_alias(buddy); + else + alias = who; + purple_notify_userinfo(gc, who, user_info, NULL, NULL); + purple_notify_user_info_destroy(user_info); +} + +void +oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo) +{ + PurpleConnection *gc = od->gc; + PurpleAccount *account = purple_connection_get_account(gc); + PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); + gchar *tmp = NULL, *info_utf8 = NULL, *base_profile_url = NULL; + + oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* strip_html_tags */ FALSE); + + if ((userinfo->present & AIM_USERINFO_PRESENT_IDLE) && userinfo->idletime != 0) { + tmp = purple_str_seconds_to_string(userinfo->idletime*60); + oscar_user_info_add_pair(user_info, _("Idle"), tmp); + g_free(tmp); + } + + oscar_user_info_append_extra_info(gc, user_info, NULL, userinfo); + + if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !oscar_util_valid_name_sms(userinfo->bn)) { + /* An SMS contact is always online; its Online Since value is not useful */ + time_t t = userinfo->onlinesince; + oscar_user_info_add_pair(user_info, _("Online Since"), purple_date_format_full(localtime(&t))); + } + + if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) { + time_t t = userinfo->membersince; + oscar_user_info_add_pair(user_info, _("Member Since"), purple_date_format_full(localtime(&t))); + } + + if (userinfo->capabilities != 0) { + tmp = oscar_caps_to_string(userinfo->capabilities); + oscar_user_info_add_pair(user_info, _("Capabilities"), tmp); + g_free(tmp); + } + + /* Info */ + if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) { + tmp = oscar_encoding_extract(userinfo->info_encoding); + info_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->info, + userinfo->info_len); + g_free(tmp); + if (info_utf8 != NULL) { + tmp = oscar_util_format_string(info_utf8, purple_account_get_username(account)); + purple_notify_user_info_add_section_break(user_info); + oscar_user_info_add_pair(user_info, _("Profile"), tmp); + g_free(tmp); + g_free(info_utf8); + } + } + + purple_notify_user_info_add_section_break(user_info); + base_profile_url = oscar_util_valid_name_icq(userinfo->bn) ? "http://www.icq.com/people" : "http://profiles.aim.com"; + tmp = g_strdup_printf("<a href=\"%s/%s\">%s</a>", + base_profile_url, purple_normalize(account, userinfo->bn), _("View web profile")); + purple_notify_user_info_add_pair(user_info, NULL, tmp); + g_free(tmp); + + purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL); + purple_notify_user_info_destroy(user_info); +} \ No newline at end of file
--- a/libpurple/protocols/oscar/util.c Thu Jun 03 04:10:13 2010 +0000 +++ b/libpurple/protocols/oscar/util.c Thu Jun 03 06:33:24 2010 +0000 @@ -38,8 +38,8 @@ static const char * const msgerrreason[] = { N_("Invalid error"), N_("Invalid SNAC"), - N_("Rate to host"), - N_("Rate to client"), + N_("Server rate limit exceeded"), + N_("Client rate limit exceeded"), N_("Not logged in"), N_("Service unavailable"), N_("Service not defined"), @@ -323,3 +323,67 @@ return 0; } + +/** + * Looks for %n, %d, or %t in a string, and replaces them with the + * specified name, date, and time, respectively. + * + * @param str The string that may contain the special variables. + * @param name The sender name. + * + * @return A newly allocated string where the special variables are + * expanded. This should be g_free'd by the caller. + */ +gchar * +oscar_util_format_string(const char *str, const char *name) +{ + char *c; + GString *cpy; + time_t t; + struct tm *tme; + + g_return_val_if_fail(str != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + /* Create an empty GString that is hopefully big enough for most messages */ + cpy = g_string_sized_new(1024); + + t = time(NULL); + tme = localtime(&t); + + c = (char *)str; + while (*c) { + switch (*c) { + case '%': + if (*(c + 1)) { + switch (*(c + 1)) { + case 'n': + /* append name */ + g_string_append(cpy, name); + c++; + break; + case 'd': + /* append date */ + g_string_append(cpy, purple_date_format_short(tme)); + c++; + break; + case 't': + /* append time */ + g_string_append(cpy, purple_time_format(tme)); + c++; + break; + default: + g_string_append_c(cpy, *c); + } + } else { + g_string_append_c(cpy, *c); + } + break; + default: + g_string_append_c(cpy, *c); + } + c++; + } + + return g_string_free(cpy, FALSE); +}
--- a/pidgin/gtkaccount.c Thu Jun 03 04:10:13 2010 +0000 +++ b/pidgin/gtkaccount.c Thu Jun 03 06:33:24 2010 +0000 @@ -2418,35 +2418,38 @@ g_free(buffer); } -struct auth_and_add { +struct auth_request +{ PurpleAccountRequestAuthorizationCb auth_cb; PurpleAccountRequestAuthorizationCb deny_cb; void *data; char *username; char *alias; PurpleAccount *account; + gboolean add_buddy_after_auth; }; static void -free_auth_and_add(struct auth_and_add *aa) +free_auth_request(struct auth_request *ar) { - g_free(aa->username); - g_free(aa->alias); - g_free(aa); + g_free(ar->username); + g_free(ar->alias); + g_free(ar); } static void -authorize_and_add_cb(struct auth_and_add *aa) +authorize_and_add_cb(struct auth_request *ar) { - aa->auth_cb(aa->data); - purple_blist_request_add_buddy(aa->account, aa->username, - NULL, aa->alias); + ar->auth_cb(ar->data); + if (ar->add_buddy_after_auth) { + purple_blist_request_add_buddy(ar->account, ar->username, NULL, ar->alias); + } } static void -deny_no_add_cb(struct auth_and_add *aa) +deny_no_add_cb(struct auth_request *ar) { - aa->deny_cb(aa->data); + ar->deny_cb(ar->data); } static void * @@ -2463,49 +2466,48 @@ char *buffer; PurpleConnection *gc; GtkWidget *alert; + GdkPixbuf *prpl_icon; + struct auth_request *aa; gc = purple_account_get_connection(account); if (message != NULL && *message == '\0') message = NULL; - buffer = g_strdup_printf(_("%s%s%s%s wants to add %s to his or her buddy list%s%s"), + buffer = g_strdup_printf(_("%s%s%s%s wants to add you (%s) to his or her buddy list%s%s"), remote_user, - (alias != NULL ? " (" : ""), - (alias != NULL ? alias : ""), - (alias != NULL ? ")" : ""), - (id != NULL - ? id - : (purple_connection_get_display_name(gc) != NULL - ? purple_connection_get_display_name(gc) - : purple_account_get_username(account))), - (message != NULL ? ": " : "."), - (message != NULL ? message : "")); - - - if (!on_list) { - struct auth_and_add *aa = g_new0(struct auth_and_add, 1); - aa->auth_cb = auth_cb; - aa->deny_cb = deny_cb; - aa->data = user_data; - aa->username = g_strdup(remote_user); - aa->alias = g_strdup(alias); - aa->account = account; - alert = pidgin_make_mini_dialog(gc, PIDGIN_STOCK_DIALOG_QUESTION, - _("Authorize buddy?"), buffer, aa, - _("Authorize"), authorize_and_add_cb, - _("Deny"), deny_no_add_cb, - NULL); - g_signal_connect_swapped(G_OBJECT(alert), "destroy", G_CALLBACK(free_auth_and_add), aa); - } else { - alert = pidgin_make_mini_dialog(gc, PIDGIN_STOCK_DIALOG_QUESTION, - _("Authorize buddy?"), buffer, user_data, - _("Authorize"), auth_cb, - _("Deny"), deny_cb, - NULL); - } + (alias != NULL ? " (" : ""), + (alias != NULL ? alias : ""), + (alias != NULL ? ")" : ""), + (id != NULL + ? id + : (purple_connection_get_display_name(gc) != NULL + ? purple_connection_get_display_name(gc) + : purple_account_get_username(account))), + (message != NULL ? ": " : "."), + (message != NULL ? message : "")); + + + prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + + aa = g_new0(struct auth_request, 1); + aa->auth_cb = auth_cb; + aa->deny_cb = deny_cb; + aa->data = user_data; + aa->username = g_strdup(remote_user); + aa->alias = g_strdup(alias); + aa->account = account; + aa->add_buddy_after_auth = !on_list; + + alert = pidgin_make_mini_dialog_with_custom_icon( + gc, prpl_icon, + _("Authorize buddy?"), buffer, aa, + _("Authorize"), authorize_and_add_cb, + _("Deny"), deny_no_add_cb, + NULL); + + g_signal_connect_swapped(G_OBJECT(alert), "destroy", G_CALLBACK(free_auth_request), aa); + g_signal_connect(G_OBJECT(alert), "destroy", G_CALLBACK(purple_account_request_close), NULL); pidgin_blist_add_alert(alert); - g_signal_connect(G_OBJECT(alert), "destroy", - G_CALLBACK(purple_account_request_close), NULL); g_free(buffer);
--- a/pidgin/gtkutils.c Thu Jun 03 04:10:13 2010 +0000 +++ b/pidgin/gtkutils.c Thu Jun 03 06:33:24 2010 +0000 @@ -2611,18 +2611,11 @@ } } -GtkWidget * -pidgin_make_mini_dialog(PurpleConnection *gc, - const char *icon_name, - const char *primary, - const char *secondary, - void *user_data, - ...) +static void +mini_dialog_init(PidginMiniDialog *mini_dialog, PurpleConnection *gc, void *user_data, va_list args) { - PidginMiniDialog *mini_dialog; const char *button_text; GList *cb_datas = NULL; - va_list args; static gboolean first_call = TRUE; if (first_call) { @@ -2632,12 +2625,10 @@ PURPLE_CALLBACK(connection_signed_off_cb), NULL); } - mini_dialog = pidgin_mini_dialog_new(primary, secondary, icon_name); g_object_set_data(G_OBJECT(mini_dialog), "gc" ,gc); g_signal_connect(G_OBJECT(mini_dialog), "destroy", G_CALLBACK(alert_killed_cb), NULL); - va_start(args, user_data); while ((button_text = va_arg(args, char*))) { struct _old_button_clicked_cb_data *data = NULL; PidginMiniDialogCallback wrapper_cb = NULL; @@ -2654,12 +2645,40 @@ wrapper_cb, data); cb_datas = g_list_append(cb_datas, data); } - va_end(args); g_signal_connect(G_OBJECT(mini_dialog), "destroy", G_CALLBACK(old_mini_dialog_destroy_cb), cb_datas); - +} + +#define INIT_AND_RETURN_MINI_DIALOG(mini_dialog) \ + va_list args; \ + va_start(args, user_data); \ + mini_dialog_init(mini_dialog, gc, user_data, args); \ + va_end(args); \ return GTK_WIDGET(mini_dialog); + +GtkWidget * +pidgin_make_mini_dialog(PurpleConnection *gc, + const char *icon_name, + const char *primary, + const char *secondary, + void *user_data, + ...) +{ + PidginMiniDialog *mini_dialog = pidgin_mini_dialog_new(primary, secondary, icon_name); + INIT_AND_RETURN_MINI_DIALOG(mini_dialog); +} + +GtkWidget * +pidgin_make_mini_dialog_with_custom_icon(PurpleConnection *gc, + GdkPixbuf *custom_icon, + const char *primary, + const char *secondary, + void *user_data, + ...) +{ + PidginMiniDialog *mini_dialog = pidgin_mini_dialog_new_with_custom_icon(primary, secondary, custom_icon); + INIT_AND_RETURN_MINI_DIALOG(mini_dialog); } /*
--- a/pidgin/gtkutils.h Thu Jun 03 04:10:13 2010 +0000 +++ b/pidgin/gtkutils.h Thu Jun 03 06:33:24 2010 +0000 @@ -715,6 +715,17 @@ void *user_data, ...) G_GNUC_NULL_TERMINATED; /** + * Does exactly what pidgin_make_mini_dialog() does, except you can specify + * a custom icon for the dialog. + */ +GtkWidget *pidgin_make_mini_dialog_with_custom_icon(PurpleConnection *gc, + GdkPixbuf *custom_icon, + const char *primary, + const char *secondary, + void *user_data, + ...) G_GNUC_NULL_TERMINATED; + +/** * This is a callback function to be used for Ctrl+F searching in treeviews. * Sample Use: * gtk_tree_view_set_search_equal_func(treeview,
--- a/pidgin/minidialog.c Thu Jun 03 04:10:13 2010 +0000 +++ b/pidgin/minidialog.c Thu Jun 03 06:33:24 2010 +0000 @@ -75,6 +75,7 @@ PROP_TITLE = 1, PROP_DESCRIPTION, PROP_ICON_NAME, + PROP_CUSTOM_ICON, LAST_PROPERTY } HazeConnectionProperties; @@ -93,17 +94,32 @@ #define PIDGIN_MINI_DIALOG_GET_PRIVATE(dialog) \ ((PidginMiniDialogPrivate *) ((dialog)->priv)) +static PidginMiniDialog * +mini_dialog_new(const gchar *title, const gchar *description) +{ + return g_object_new(PIDGIN_TYPE_MINI_DIALOG, + "title", title, + "description", description, + NULL); +} + PidginMiniDialog * pidgin_mini_dialog_new(const gchar *title, const gchar *description, const gchar *icon_name) { - PidginMiniDialog *mini_dialog = g_object_new(PIDGIN_TYPE_MINI_DIALOG, - "title", title, - "description", description, - "icon-name", icon_name, - NULL); + PidginMiniDialog *mini_dialog = mini_dialog_new(title, description); + pidgin_mini_dialog_set_icon_name(mini_dialog, icon_name); + return mini_dialog; +} +PidginMiniDialog * +pidgin_mini_dialog_new_with_custom_icon(const gchar *title, + const gchar *description, + GdkPixbuf *custom_icon) +{ + PidginMiniDialog *mini_dialog = mini_dialog_new(title, description); + pidgin_mini_dialog_set_custom_icon(mini_dialog, custom_icon); return mini_dialog; } @@ -125,7 +141,13 @@ pidgin_mini_dialog_set_icon_name(PidginMiniDialog *mini_dialog, const char *icon_name) { - g_object_set(G_OBJECT(mini_dialog), "icon_name", icon_name, NULL); + g_object_set(G_OBJECT(mini_dialog), "icon-name", icon_name, NULL); +} + +void +pidgin_mini_dialog_set_custom_icon(PidginMiniDialog *mini_dialog, GdkPixbuf *custom_icon) +{ + g_object_set(G_OBJECT(mini_dialog), "custom-icon", custom_icon, NULL); } struct _mini_dialog_button_clicked_cb_data @@ -233,6 +255,9 @@ g_value_set_string(value, icon_name); break; } + case PROP_CUSTOM_ICON: + g_value_set_object(value, gtk_image_get_pixbuf(priv->icon)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -305,6 +330,8 @@ gtk_image_set_from_stock(priv->icon, g_value_get_string(value), gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)); break; + case PROP_CUSTOM_ICON: + gtk_image_set_from_pixbuf(priv->icon, g_value_get_object(value)); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -355,6 +382,13 @@ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ICON_NAME, param_spec); + + param_spec = g_param_spec_object("custom-icon", "custom-icon", + "Pixbuf to use as the dialog's icon", + GDK_TYPE_PIXBUF, + G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_CUSTOM_ICON, param_spec); } /* 16 is the width of the icon, due to PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL */
--- a/pidgin/minidialog.h Thu Jun 03 04:10:13 2010 +0000 +++ b/pidgin/minidialog.h Thu Jun 03 06:33:24 2010 +0000 @@ -73,6 +73,8 @@ * <dd>The Gtk stock id of an icon for the dialog, or @c NULL for no icon. * @see pidginstock.h * </dd> + * <dt><tt>"custom-icon"</tt> (<tt>GdkPixbuf *</tt>)</dt> + * <dd>The custom icon to use instead of a stock one (overrides the "icon-name" property).</dd> * </dl> */ typedef struct { @@ -108,13 +110,20 @@ /** Get the GType of #PidginMiniDialog. */ GType pidgin_mini_dialog_get_type (void); -/** Creates a new #PidginMiniDialog. This is a shortcut for creating the dialog +/** Creates a new #PidginMiniDialog with a stock icon. This is a shortcut for creating the dialog * with @c g_object_new() then setting each property yourself. * @return a new #PidginMiniDialog. */ PidginMiniDialog *pidgin_mini_dialog_new(const gchar *title, const gchar *description, const gchar *icon_name); +/** Creates a new #PidginMiniDialog with a custom icon. This is a shortcut for creating the dialog + * with @c g_object_new() then setting each property yourself. + * @return a new #PidginMiniDialog. + */ +PidginMiniDialog *pidgin_mini_dialog_new_with_custom_icon(const gchar *title, + const gchar *description, GdkPixbuf *custom_icon); + /** Shortcut for setting a mini-dialog's title via GObject properties. * @param mini_dialog a mini-dialog * @param title the new title for @a mini_dialog @@ -137,6 +146,13 @@ void pidgin_mini_dialog_set_icon_name(PidginMiniDialog *mini_dialog, const char *icon_name); +/** Shortcut for setting a mini-dialog's custom icon via GObject properties. + * @param mini_dialog a mini-dialog + * @param icon_name the pixbuf to use as a custom icon + */ +void pidgin_mini_dialog_set_custom_icon(PidginMiniDialog *mini_dialog, + GdkPixbuf *custom_icon); + /** Adds a new button to a mini-dialog, and attaches the supplied callback to * its <tt>clicked</tt> signal. After a button is clicked, the dialog is * destroyed.