", face,
- msim_point_to_purple_size(session, msim_height_to_point(session, height)));
- } else {
- g_string_printf(gs_begin, "");
- }
-
- /* No support for font-size CSS? */
- /* g_string_printf(gs_begin, "", face,
- msim_height_to_point(height)); */
-
- gs_end = g_string_new("");
-
- if (decor & MSIM_TEXT_BOLD) {
- g_string_append(gs_begin, "");
- g_string_prepend(gs_end, "");
- }
-
- if (decor & MSIM_TEXT_ITALIC) {
- g_string_append(gs_begin, "");
- g_string_append(gs_end, "");
- }
-
- if (decor & MSIM_TEXT_UNDERLINE) {
- g_string_append(gs_begin, "");
- g_string_append(gs_end, "");
- }
-
-
- *begin = gs_begin->str;
- *end = gs_end->str;
-}
-
-/** Convert a msim markup color to a color suitable for libpurple.
- *
- * @param msim Either a color name, or an rgb(x,y,z) code.
- *
- * @return A new string, either a color name or #rrggbb code. Must g_free().
- */
-static char *
-msim_color_to_purple(const char *msim)
-{
- guint red, green, blue;
-
- if (!msim) {
- return g_strdup("black");
- }
-
- if (sscanf(msim, "rgb(%d,%d,%d)", &red, &green, &blue) != 3) {
- /* Color name. */
- return g_strdup(msim);
- }
- /* TODO: rgba (alpha). */
-
- return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue);
-}
-
-/** Convert the msim markup (anchor) tag into HTML. */
-static void
-msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- const gchar *href;
-
- href = xmlnode_get_attrib(root, "h");
- if (!href) {
- href = "";
- }
-
- *begin = g_strdup_printf("%s", href, href);
- *end = g_strdup("");
-}
-
-/** Convert the msim markup (paragraph) tag into HTML. */
-static void
-msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- /* Just pass through unchanged.
- *
- * Note: attributes currently aren't passed, if there are any. */
- *begin = g_strdup("
");
- *end = g_strdup("
");
-}
-
-/** Convert the msim markup tag (text color) into HTML. TODO: Test */
-static void
-msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- const gchar *color;
- gchar *purple_color;
-
- color = xmlnode_get_attrib(root, "v");
- if (!color) {
- purple_debug_info("msim", "msim_markup_c_to_html: tag w/o v attr");
- *begin = g_strdup("");
- *end = g_strdup("");
- /* TODO: log as unrecognized */
- return;
- }
-
- purple_color = msim_color_to_purple(color);
-
- *begin = g_strdup_printf("", purple_color);
-
- g_free(purple_color);
-
- /* *begin = g_strdup_printf("", color); */
- *end = g_strdup("");
-}
-
-/** Convert the msim markup tag (background color) into HTML. TODO: Test */
-static void
-msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- const gchar *color;
- gchar *purple_color;
-
- color = xmlnode_get_attrib(root, "v");
- if (!color) {
- *begin = g_strdup("");
- *end = g_strdup("");
- purple_debug_info("msim", "msim_markup_b_to_html: w/o v attr");
- /* TODO: log as unrecognized. */
- return;
- }
-
- purple_color = msim_color_to_purple(color);
-
- /* TODO: find out how to set background color. */
- *begin = g_strdup_printf("",
- purple_color);
- g_free(purple_color);
-
- *end = g_strdup("");
-}
-
-/** Convert the msim markup tag (emoticon image) into HTML. */
-static void
-msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
-{
- const gchar *name;
- guint i;
- struct MSIM_EMOTICON *emote;
-
- name = xmlnode_get_attrib(root, "n");
- if (!name) {
- purple_debug_info("msim", "msim_markup_i_to_html: w/o n");
- *begin = g_strdup("");
- *end = g_strdup("");
- /* TODO: log as unrecognized */
- return;
- }
-
- /* Find and use canonical form of smiley symbol. */
- for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) {
- if (!strcmp(name, emote->name)) {
- *begin = g_strdup(emote->symbol);
- *end = g_strdup("");
- return;
- }
- }
-
- /* Couldn't find it, sorry. Try to degrade gracefully. */
- *begin = g_strdup_printf("**%s**", name);
- *end = g_strdup("");
-}
-
-/** Convert an individual msim markup tag to HTML. */
-static void
-msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin,
- gchar **end)
-{
- if (!strcmp(root->name, "f")) {
- msim_markup_f_to_html(session, root, begin, end);
- } else if (!strcmp(root->name, "a")) {
- msim_markup_a_to_html(session, root, begin, end);
- } else if (!strcmp(root->name, "p")) {
- msim_markup_p_to_html(session, root, begin, end);
- } else if (!strcmp(root->name, "c")) {
- msim_markup_c_to_html(session, root, begin, end);
- } else if (!strcmp(root->name, "b")) {
- msim_markup_b_to_html(session, root, begin, end);
- } else if (!strcmp(root->name, "i")) {
- msim_markup_i_to_html(session, root, begin, end);
- } else {
- purple_debug_info("msim", "msim_markup_tag_to_html: "
- "unknown tag name=%s, ignoring",
- (root && root->name) ? root->name : "(NULL)");
- *begin = g_strdup("");
- *end = g_strdup("");
- }
-}
-
-/** Convert an individual HTML tag to msim markup. */
-static void
-html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin,
- gchar **end)
-{
- /* TODO: Coalesce nested tags into one tag!
- * Currently, the 's' value will be overwritten when b/i/u is nested
- * within another one, and only the inner-most formatting will be
- * applied to the text. */
- if (!purple_utf8_strcasecmp(root->name, "root")) {
- *begin = g_strdup("");
- *end = g_strdup("");
- } else if (!purple_utf8_strcasecmp(root->name, "b")) {
- *begin = g_strdup_printf("", MSIM_TEXT_BOLD);
- *end = g_strdup("");
- } else if (!purple_utf8_strcasecmp(root->name, "i")) {
- *begin = g_strdup_printf("", MSIM_TEXT_ITALIC);
- *end = g_strdup("");
- } else if (!purple_utf8_strcasecmp(root->name, "u")) {
- *begin = g_strdup_printf("", MSIM_TEXT_UNDERLINE);
- *end = g_strdup("");
- } else if (!purple_utf8_strcasecmp(root->name, "a")) {
- const gchar *href, *link_text;
-
- href = xmlnode_get_attrib(root, "href");
-
- if (!href) {
- href = xmlnode_get_attrib(root, "HREF");
- }
-
- link_text = xmlnode_get_data(root);
-
- if (href) {
- if (!strcmp(link_text, href)) {
- /* Purple gives us: URL
- * Translate to
- * Displayed as text of URL with link to URL
- */
- *begin = g_strdup_printf("", href);
- } else {
- /* But if we get: text
- * Translate to: text:
- *
- * Because official client only supports self-closed
- * tags; you can't change the link text.
- */
- *begin = g_strdup_printf("%s: ", link_text, href);
- }
- } else {
- *begin = g_strdup("");
- }
-
- /* Sorry, kid. MySpace doesn't support you within tags. */
- xmlnode_free(root->child);
- root->child = NULL;
-
- *end = g_strdup("");
- } else if (!purple_utf8_strcasecmp(root->name, "font")) {
- const gchar *size;
- const gchar *face;
-
- size = xmlnode_get_attrib(root, "size");
- face = xmlnode_get_attrib(root, "face");
-
- if (face && size) {
- *begin = g_strdup_printf("", face,
- msim_point_to_height(session,
- msim_purple_size_to_point(session, atoi(size))));
- } else if (face) {
- *begin = g_strdup_printf("", face);
- } else if (size) {
- *begin = g_strdup_printf("",
- msim_point_to_height(session,
- msim_purple_size_to_point(session, atoi(size))));
- } else {
- *begin = g_strdup("");
- }
-
- *end = g_strdup("");
-
- /* TODO: color (bg uses ), emoticons */
- } else {
- *begin = g_strdup_printf("[%s]", root->name);
- *end = g_strdup_printf("[/%s]", root->name);
- }
-}
-
-/** Convert an xmlnode of msim markup or HTML to an HTML string or msim markup.
- *
- * @param f Function to convert tags.
- *
- * @return An HTML string. Caller frees.
- */
-static gchar *
-msim_convert_xmlnode(MsimSession *session, xmlnode *root, MSIM_XMLNODE_CONVERT f)
-{
- xmlnode *node;
- gchar *begin, *inner, *end;
- GString *final;
-
- if (!root || !root->name) {
- return g_strdup("");
- }
-
- purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n",
- root->name);
-
- begin = inner = end = NULL;
-
- final = g_string_new("");
-
- f(session, root, &begin, &end);
-
- g_string_append(final, begin);
-
- /* Loop over all child nodes. */
- for (node = root->child; node != NULL; node = node->next) {
- switch (node->type) {
- case XMLNODE_TYPE_ATTRIB:
- /* Attributes handled above. */
- break;
-
- case XMLNODE_TYPE_TAG:
- /* A tag or tag with attributes. Recursively descend. */
- inner = msim_convert_xmlnode(session, node, f);
- g_return_val_if_fail(inner != NULL, NULL);
-
- purple_debug_info("msim", " ** node name=%s\n",
- (node && node->name) ? node->name : "(NULL)");
- break;
-
- case XMLNODE_TYPE_DATA:
- /* Literal text. */
- inner = g_new0(char, node->data_sz + 1);
- strncpy(inner, node->data, node->data_sz);
- inner[node->data_sz] = 0;
-
- purple_debug_info("msim", " ** node data=%s\n",
- inner ? inner : "(NULL)");
- break;
-
- default:
- purple_debug_info("msim",
- "msim_convert_xmlnode: strange node\n");
- inner = g_strdup("");
- }
-
- if (inner) {
- g_string_append(final, inner);
- }
- }
-
- /* TODO: Note that msim counts each piece of text enclosed by as
- * a paragraph and will display each on its own line. You actually have
- * to _nest_ tags to intersperse different text in one paragraph!
- * Comment out this line below to see. */
- g_string_append(final, end);
-
- purple_debug_info("msim", "msim_markup_xmlnode_to_gtkhtml: RETURNING %s\n",
- (final && final->str) ? final->str : "(NULL)");
-
- return final->str;
-}
-
-/** Convert XML to something based on MSIM_XMLNODE_CONVERT. */
-static gchar *
-msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f)
-{
- xmlnode *root;
- gchar *str;
- gchar *enclosed_raw;
-
- g_return_val_if_fail(raw != NULL, NULL);
-
- /* Enclose text in one root tag, to try to make it valid XML for parsing. */
- enclosed_raw = g_strconcat("", raw, "", NULL);
-
- root = xmlnode_from_str(enclosed_raw, -1);
-
- if (!root) {
- purple_debug_info("msim", "msim_markup_to_html: couldn't parse "
- "%s as XML, returning raw: %s\n", enclosed_raw, raw);
- /* TODO: msim_unrecognized */
- g_free(enclosed_raw);
- return g_strdup(raw);
- }
-
- g_free(enclosed_raw);
-
- str = msim_convert_xmlnode(session, root, f);
- g_return_val_if_fail(str != NULL, NULL);
- purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str);
-
- xmlnode_free(root);
-
- return str;
-}
-
-/** Convert plaintext smileys to markup tags.
- *
- * @param before Original text with ASCII smileys. Will be freed.
- * @return A new string with tags, if applicable. Must be g_free()'d.
- */
-static gchar *
-msim_convert_smileys_to_markup(gchar *before)
-{
- gchar *old, *new, *replacement;
- guint i;
- struct MSIM_EMOTICON *emote;
-
- old = before;
- new = NULL;
-
- for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) {
- gchar *name, *symbol;
-
- name = emote->name;
- symbol = emote->symbol;
-
- replacement = g_strdup_printf("", name);
-
- purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n",
- symbol ? symbol : "(NULL)",
- replacement ? replacement : "(NULL)");
- new = str_replace(old, symbol, replacement);
-
- g_free(replacement);
- g_free(old);
-
- old = new;
- }
-
- return new;
-}
-
-
-/** High-level function to convert MySpaceIM markup to Purple (HTML) markup.
- *
- * @return Purple markup string, must be g_free()'d. */
-static gchar *
-msim_markup_to_html(MsimSession *session, const gchar *raw)
-{
- return msim_convert_xml(session, raw,
- (MSIM_XMLNODE_CONVERT)(msim_markup_tag_to_html));
-}
-
-/** High-level function to convert Purple (HTML) to MySpaceIM markup.
- *
- * @return HTML markup string, must be g_free()'d. */
-static gchar *
-html_to_msim_markup(MsimSession *session, const gchar *raw)
-{
- gchar *markup;
-
- markup = msim_convert_xml(session, raw,
- (MSIM_XMLNODE_CONVERT)(html_tag_to_msim_markup));
-
- if (purple_account_get_bool(session->account, "emoticons", TRUE)) {
- /* Frees markup and allocates a new one. */
- markup = msim_convert_smileys_to_markup(markup);
- }
-
- return markup;
-}
-
-/** Get the MsimUser from a PurpleBuddy, creating it if needed. */
-static MsimUser *
-msim_get_user_from_buddy(PurpleBuddy *buddy)
-{
- MsimUser *user;
-
- if (!buddy) {
- return NULL;
- }
-
- if (!buddy->proto_data) {
- /* No MsimUser for this buddy; make one. */
-
- /* TODO: where is this freed? */
- user = g_new0(MsimUser, 1);
- user->buddy = buddy;
- buddy->proto_data = (gpointer)user;
- }
-
- user = (MsimUser *)(buddy->proto_data);
-
- return user;
-}
-
-/** Find and return an MsimUser * representing a user on the buddy list, or NULL. */
-static MsimUser *
-msim_find_user(MsimSession *session, const gchar *username)
-{
- PurpleBuddy *buddy;
- MsimUser *user;
-
- buddy = purple_find_buddy(session->account, username);
- if (!buddy) {
- return NULL;
- }
-
- user = msim_get_user_from_buddy(buddy);
-
- return user;
-}
-
/** Record the client version in the buddy list, from an incoming message. */
static gboolean
@@ -1655,7 +712,7 @@
* @param msg An MsimMessage that was unrecognized, or NULL.
* @param note Information on what was unrecognized, or NULL.
*/
-static void
+void
msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note)
{
/* TODO: Some more context, outwardly equivalent to a backtrace,
@@ -1669,8 +726,8 @@
* by Alexandr Shutko, who maintains OSCAR protocol documentation). */
purple_debug_info("msim", "Unrecognized data on account for %s\n",
- session->account->username ? session->account->username
- : "(NULL)");
+ (session && session->account && session->account->username) ?
+ session->account->username : "(NULL)");
if (note) {
purple_debug_info("msim", "(Note: %s)\n", note);
}
@@ -1680,60 +737,6 @@
}
}
-/** Process an incoming zap. */
-static gboolean
-msim_incoming_zap(MsimSession *session, MsimMessage *msg)
-{
- gchar *msg_text, *username;
- gint zap;
- const gchar *zap_past_tense[10];
-#ifdef MSIM_USE_ATTENTION_API
- MsimAttentionType attn;
-#else
- gchar *zap_text;
-#endif
-
- zap_past_tense[0] = _("zapped");
- zap_past_tense[1] = _("whacked");
- zap_past_tense[2] = _("torched");
- zap_past_tense[3] = _("smooched");
- zap_past_tense[4] = _("hugged");
- zap_past_tense[5] = _("bslapped");
- zap_past_tense[6] = _("goosed");
- zap_past_tense[7] = _("hi-fived");
- zap_past_tense[8] = _("punk'd");
- zap_past_tense[9] = _("raspberried");
-
- msg_text = msim_msg_get_string(msg, "msg");
- username = msim_msg_get_string(msg, "_username");
-
- g_return_val_if_fail(msg_text != NULL, FALSE);
- g_return_val_if_fail(username != NULL, FALSE);
-
- g_return_val_if_fail(sscanf(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", &zap) == 1, FALSE);
-
- zap = CLAMP(zap, 0, sizeof(zap_past_tense) / sizeof(zap_past_tense[0]));
-
- /* TODO:ZAP: use msim_attention_types */
-#ifdef MSIM_USE_ATTENTION_API
- attn.incoming_description = zap_past_tense[zap];
- attn.outgoing_description = NULL;
- attn.icon = NULL; /* TODO: icon */
-
- serv_got_attention(session->gc, username, &attn, TRUE);
-#else
- zap_text = g_strdup_printf(_("*** You have been %s! ***"), zap_past_tense[zap]);
- serv_got_im(session->gc, username, zap_text,
- PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_SYSTEM, time(NULL));
- g_free(zap_text);
-#endif
-
- g_free(msg_text);
- g_free(username);
-
- return TRUE;
-}
-
/**
* Handle an incoming action message.
*
@@ -1761,13 +764,13 @@
purple_debug_info("msim", "msim_incoming_action: action <%s> from <%d>\n",
msg_text, username);
- if (strcmp(msg_text, "%typing%") == 0) {
+ if (g_str_equal(msg_text, "%typing%")) {
/* TODO: find out if msim repeatedly sends typing messages, so we can
* give it a timeout. Right now, there does seem to be an inordinately
* amount of time between typing stopped-typing notifications. */
serv_got_typing(session->gc, username, 0, PURPLE_TYPING);
rc = TRUE;
- } else if (strcmp(msg_text, "%stoptyping%") == 0) {
+ } else if (g_str_equal(msg_text, "%stoptyping%")) {
serv_got_typing_stopped(session->gc, username);
rc = TRUE;
} else if (strstr(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_")) {
@@ -1784,7 +787,7 @@
return rc;
}
-/* Process an incoming media (buddy icon) message. */
+/* Process an incoming media (message background?) message. */
static gboolean
msim_incoming_media(MsimSession *session, MsimMessage *msg)
{
@@ -1902,95 +905,7 @@
return 0;
}
-/** Format the "now playing" indicator, showing the artist and song.
- * @return Return a new string (must be g_free()'d), or NULL.
- */
-static gchar *
-msim_format_now_playing(gchar *band, gchar *song)
-{
- if ((band && strlen(band)) || (song && strlen(song))) {
- return g_strdup_printf("%s - %s",
- (band && strlen(band)) ? band : "Unknown Artist",
- (song && strlen(song)) ? song : "Unknown Song");
- } else {
- return NULL;
- }
-}
-
-/** Append user information to a PurpleNotifyUserInfo, given an MsimUser.
- * Used by msim_tooltip_text() and msim_get_info_cb() to show a user's profile.
- */
-static void
-msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full)
-{
- gchar *str;
- guint uid;
- guint cv;
-
- /* Useful to identify the account the tooltip refers to.
- * Other prpls show this. */
- if (user->username) {
- purple_notify_user_info_add_pair(user_info, _("User"), user->username);
- }
-
- uid = purple_blist_node_get_int(&user->buddy->node, "UserID");
-
- if (full) {
- /* TODO: link to username, if available */
- purple_notify_user_info_add_pair(user_info, _("Profile"),
- g_strdup_printf("http://myspace.com/%d",
- uid, uid));
- }
-
-
- /* a/s/l...the vitals */
- if (user->age) {
- purple_notify_user_info_add_pair(user_info, _("Age"),
- g_strdup_printf("%d", user->age));
- }
-
- if (user->gender && strlen(user->gender)) {
- purple_notify_user_info_add_pair(user_info, _("Gender"), user->gender);
- }
-
- if (user->location && strlen(user->location)) {
- purple_notify_user_info_add_pair(user_info, _("Location"), user->location);
- }
-
- /* Other information */
- if (user->headline && strlen(user->headline)) {
- purple_notify_user_info_add_pair(user_info, _("Headline"), user->headline);
- }
-
- str = msim_format_now_playing(user->band_name, user->song_name);
- if (str && strlen(str)) {
- purple_notify_user_info_add_pair(user_info, _("Song"), str);
- }
-
- /* Note: total friends only available if looked up by uid, not username. */
- if (user->total_friends) {
- purple_notify_user_info_add_pair(user_info, _("Total Friends"),
- g_strdup_printf("%d", user->total_friends));
- }
-
- if (full) {
- /* Client information */
-
- str = user->client_info;
- cv = user->client_cv;
-
- if (str && cv != 0) {
- purple_notify_user_info_add_pair(user_info, _("Client Version"),
- g_strdup_printf("%s (build %d)", str, cv));
- } else if (str) {
- purple_notify_user_info_add_pair(user_info, _("Client Version"),
- g_strdup(str));
- } else if (cv) {
- purple_notify_user_info_add_pair(user_info, _("Client Version"),
- g_strdup_printf("Build %d", cv));
- }
- }
-}
+
/** Callback for msim_get_info(), for when user info is received. */
static void
@@ -2377,42 +1292,27 @@
guint i, n;
const gchar *froms[5], *tos[5], *urls[5], *subjects[5];
- /* Three parallel arrays for each new inbox message type. */
- static const gchar *inbox_keys[] =
- {
- "Mail",
- "BlogComment",
- "ProfileComment",
- "FriendRequest",
- "PictureComment"
+ /* Information for each new inbox message type. */
+ static struct
+ {
+ const gchar *key;
+ guint bit;
+ const gchar *url;
+ const gchar *text;
+ } message_types[] = {
+ { "Mail", MSIM_INBOX_MAIL, "http://messaging.myspace.com/index.cfm?fuseaction=mail.inbox", NULL },
+ { "BlogComment", MSIM_INBOX_BLOG_COMMENT, "http://blog.myspace.com/index.cfm?fuseaction=blog", NULL },
+ { "ProfileComment", MSIM_INBOX_PROFILE_COMMENT, "http://home.myspace.com/index.cfm?fuseaction=user", NULL },
+ { "FriendRequest", MSIM_INBOX_FRIEND_REQUEST, "http://messaging.myspace.com/index.cfm?fuseaction=mail.friendRequests", NULL },
+ { "PictureComment", MSIM_INBOX_PICTURE_COMMENT, "http://home.myspace.com/index.cfm?fuseaction=user", NULL }
};
- static const guint inbox_bits[] =
- {
- MSIM_INBOX_MAIL,
- MSIM_INBOX_BLOG_COMMENT,
- MSIM_INBOX_PROFILE_COMMENT,
- MSIM_INBOX_FRIEND_REQUEST,
- MSIM_INBOX_PICTURE_COMMENT
- };
-
- static const gchar *inbox_urls[] =
- {
- "http://messaging.myspace.com/index.cfm?fuseaction=mail.inbox",
- "http://blog.myspace.com/index.cfm?fuseaction=blog",
- "http://home.myspace.com/index.cfm?fuseaction=user",
- "http://messaging.myspace.com/index.cfm?fuseaction=mail.friendRequests",
- "http://home.myspace.com/index.cfm?fuseaction=user"
- };
-
- static const gchar *inbox_text[5];
-
/* Can't write _()'d strings in array initializers. Workaround. */
- inbox_text[0] = _("New mail messages");
- inbox_text[1] = _("New blog comments");
- inbox_text[2] = _("New profile comments");
- inbox_text[3] = _("New friend requests!");
- inbox_text[4] = _("New picture comments");
+ message_types[0].text = _("New mail messages");
+ message_types[1].text = _("New blog comments");
+ message_types[2].text = _("New profile comments");
+ message_types[3].text = _("New friend requests!");
+ message_types[4].text = _("New picture comments");
g_return_if_fail(reply != NULL);
@@ -2427,12 +1327,12 @@
n = 0;
- for (i = 0; i < sizeof(inbox_keys) / sizeof(inbox_keys[0]); ++i) {
+ for (i = 0; i < sizeof(message_types) / sizeof(message_types[0]); ++i) {
const gchar *key;
guint bit;
- key = inbox_keys[i];
- bit = inbox_bits[i];
+ key = message_types[i].key;
+ bit = message_types[i].bit;
if (msim_msg_get(body, key)) {
/* Notify only on when _changes_ from no mail -> has mail
@@ -2441,13 +1341,13 @@
purple_debug_info("msim", "msim_check_inbox_cb: got %s, at %d\n",
key ? key : "(NULL)", n);
- subjects[n] = inbox_text[i];
+ subjects[n] = message_types[i].text;
froms[n] = _("MySpace");
tos[n] = session->username;
/* TODO: append token, web challenge, so automatically logs in.
* Would also need to free strings because they won't be static
*/
- urls[n] = inbox_urls[i];
+ urls[n] = message_types[i].url;
++n;
} else {
@@ -2520,6 +1420,9 @@
* some of the time, but can vary. This is our own user ID. */
session->userid = msim_msg_get_integer(msg, "userid");
+ /* Save uid to account so this account can be looked up by uid. */
+ purple_account_set_int(session->account, "uid", session->userid);
+
/* Not sure what profileid is used for. */
if (msim_msg_get_integer(msg, "profileid") != session->userid) {
msim_unrecognized(session, msg,
@@ -2531,6 +1434,10 @@
* address and not username. Will be freed in msim_session_destroy(). */
session->username = msim_msg_get_string(msg, "uniquenick");
+ /* If a local alias wasn't set, set it to user's username. */
+ if (!session->account->alias || !strlen(session->account->alias))
+ session->account->alias = session->username;
+
/* The session is now set up, ready to be connected. This emits the
* signedOn signal, so clients can now do anything with msimprpl, and
* we're ready for it (session key, userid, username all setup). */
@@ -2597,10 +1504,14 @@
(GSourceFunc)msim_check_alive, session);
#endif
- purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK,
- (GSourceFunc)msim_check_inbox, session);
-
- msim_check_inbox(session);
+ /* Check mail if they want to. */
+ if (purple_account_get_check_mail(session->account)) {
+ purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK,
+ (GSourceFunc)msim_check_inbox, session);
+ msim_check_inbox(session);
+ }
+
+ msim_get_contact_list(session, MSIM_CONTACT_LIST_INITIAL_FRIENDS);
return TRUE;
}
@@ -2641,166 +1552,6 @@
}
}
-/** Callback for when a buddy icon finished being downloaded. */
-static void
-msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data,
- gpointer user_data,
- const gchar *url_text,
- gsize len,
- const gchar *error_message)
-{
- MsimUser *user;
-
- user = (MsimUser *)user_data;
-
- purple_debug_info("msim_downloaded_buddy_icon",
- "Downloaded %d bytes\n", len);
-
- purple_buddy_icons_set_for_user(user->buddy->account,
- user->buddy->name,
- (gchar *)url_text, len,
- /* Use URL itself as buddy icon "checksum" */
- user->image_url);
-}
-
-/** Store a field of information about a buddy. */
-static void
-msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user)
-{
- if (!strcmp(key_str, "UserID") || !strcmp(key_str, "ContactID")) {
- /* Save to buddy list, if it exists, for quick cached uid lookup with msim_uid2username_from_blist(). */
- if (user->buddy)
- {
- purple_debug_info("msim", "associating uid %s with username %s\n", key_str, user->buddy->name);
- purple_blist_node_set_int(&user->buddy->node, "UserID", atol(value_str));
- }
- /* Need to store in MsimUser, too? What if not on blist? */
- } else if (!strcmp(key_str, "Age")) {
- user->age = atol(value_str);
- } else if (!strcmp(key_str, "Gender")) {
- user->gender = g_strdup(value_str);
- } else if (!strcmp(key_str, "Location")) {
- user->location = g_strdup(value_str);
- } else if (!strcmp(key_str, "TotalFriends")) {
- user->total_friends = atol(value_str);
- } else if (!strcmp(key_str, "DisplayName")) {
- user->display_name = g_strdup(value_str);
- } else if (!strcmp(key_str, "BandName")) {
- user->band_name = g_strdup(value_str);
- } else if (!strcmp(key_str, "SongName")) {
- user->song_name = g_strdup(value_str);
- } else if (!strcmp(key_str, "UserName") || !strcmp(key_str, "IMName") || !strcmp(key_str, "NickName")) {
- /* Ignore because PurpleBuddy knows this already */
- ;
- } else if (!strcmp(key_str, "ImageURL") || !strcmp(key_str, "AvatarURL")) {
- const gchar *previous_url;
-
- user->image_url = g_strdup(value_str);
-
- previous_url = purple_buddy_icons_get_checksum_for_user(user->buddy);
-
- /* Only download if URL changed */
- if (!previous_url || strcmp(previous_url, user->image_url)) {
- purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
- }
- } else if (!strcmp(key_str, "Headline")) {
- user->headline = g_strdup(value_str);
- } else {
- /* TODO: other fields in MsimUser */
- gchar *msg;
-
- msg = g_strdup_printf("msim_store_user_info_each: unknown field %s=%s",
- key_str, value_str);
-
- msim_unrecognized(NULL, NULL, msg);
-
- g_free(msg);
- }
-}
-
-/** Save buddy information to the buddy list from a user info reply message.
- *
- * @param session
- * @param msg The user information reply, with any amount of information.
- * @param user The structure to save to, or NULL to save in PurpleBuddy->proto_data.
- *
- * Variable information is saved to the passed MsimUser structure. Permanent
- * information (UserID) is stored in the blist node of the buddy list (and
- * ends up in blist.xml, persisted to disk) if it exists.
- *
- * If the function has no buddy information, this function
- * is a no-op (and returns FALSE).
- *
- */
-static gboolean
-msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user)
-{
- gchar *username;
- MsimMessage *body, *body_node;
-
- g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
- g_return_val_if_fail(msg != NULL, FALSE);
-
- body = msim_msg_get_dictionary(msg, "body");
- if (!body) {
- return FALSE;
- }
-
- username = msim_msg_get_string(body, "UserName");
-
- if (!username) {
- purple_debug_info("msim",
- "msim_process_reply: not caching body, no UserName\n");
- msim_msg_free(body);
- g_free(username);
- return FALSE;
- }
-
- /* Null user = find and store in PurpleBuddy's proto_data */
- if (!user) {
- user = msim_find_user(session, username);
- if (!user) {
- msim_msg_free(body);
- g_free(username);
- return FALSE;
- }
- }
-
- /* TODO: make looping over MsimMessage's easier. */
- for (body_node = body;
- body_node != NULL;
- body_node = msim_msg_get_next_element_node(body_node))
- {
- const gchar *key_str;
- gchar *value_str;
- MsimMessageElement *elem;
-
- elem = (MsimMessageElement *)body_node->data;
- key_str = elem->name;
-
- value_str = msim_msg_get_string_from_element(elem);
- msim_store_user_info_each(key_str, value_str, user);
- g_free(value_str);
- }
-
- if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN &&
- msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID) {
- /* TODO: do something with our own IM info, if we need it for some
- * specific purpose. Otherwise it is available on the buddy list,
- * if the user has themselves as their own buddy.
- *
- * However, much of the info is already available in MsimSession,
- * stored in msim_we_are_logged_on(). */
- } else if (msim_msg_get_integer(msg, "dsn") == MG_OWN_MYSPACE_INFO_DSN &&
- msim_msg_get_integer(msg, "lid") == MG_OWN_MYSPACE_INFO_LID) {
- /* TODO: same as above, but for MySpace info. */
- }
-
- msim_msg_free(body);
-
- return TRUE;
-}
-
/** Process the initial server information from the server. */
static gboolean
msim_process_server_info(MsimSession *session, MsimMessage *msg)
@@ -2986,9 +1737,9 @@
*/
list = msim_msg_get_list(msg, "msg");
- status_code = atoi(g_list_nth_data(list, MSIM_STATUS_ORDINAL_ONLINE));
+ status_code = msim_msg_get_integer_from_element(g_list_nth_data(list, MSIM_STATUS_ORDINAL_ONLINE));
purple_debug_info("msim", "msim_status: %s's status code = %d\n", username, status_code);
- status_headline = g_list_nth_data(list, MSIM_STATUS_ORDINAL_HEADLINE);
+ status_headline = msim_msg_get_string_from_element(g_list_nth_data(list, MSIM_STATUS_ORDINAL_HEADLINE));
blist = purple_get_blist();
@@ -3012,7 +1763,8 @@
purple_debug_info("msim", "msim_status: found buddy %s\n", username);
}
- user->headline = g_strdup(status_headline);
+ /* don't copy; let the MsimUser own the headline, memory-wise */
+ user->headline = status_headline;
/* Set user status */
switch (status_code) {
@@ -3034,9 +1786,9 @@
break;
default:
- purple_debug_info("msim", "msim_status for %s, unknown status code %d, treating as available\n",
+ purple_debug_info("msim", "msim_status for %s, unknown status code %d, treating as available\n",
username, status_code);
- purple_status_code = PURPLE_STATUS_AVAILABLE;
+ purple_status_code = PURPLE_STATUS_AVAILABLE;
}
purple_prpl_got_user_status(session->account, username, purple_primitive_get_id_from_type(purple_status_code), NULL);
@@ -3056,6 +1808,14 @@
}
#endif
+ if (status_code != MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN) {
+ /* Get information when they come online.
+ * TODO: periodically refresh?
+ */
+ purple_debug_info("msim_incoming_status", "%s came online, looking up\n", username);
+ msim_lookup_user(session, username, NULL, NULL);
+ }
+
g_free(username);
msim_msg_list_free(list);
@@ -3169,7 +1929,7 @@
fmt_string = msim_msg_pack_element_data(elem);
uid_str = g_strdup_printf("%d", uid);
- new_str = str_replace(fmt_string, "", uid_str);
+ new_str = purple_strreplace(fmt_string, "", uid_str);
g_free(uid_str);
g_free(fmt_string);
@@ -3207,7 +1967,7 @@
msim_postprocess_outgoing_cb(MsimSession *session, MsimMessage *userinfo,
gpointer data)
{
- gchar *uid_field_name, *uid_before;
+ gchar *uid_field_name, *uid_before, *username;
guint uid;
MsimMessage *msg, *body;
@@ -3222,6 +1982,19 @@
uid = msim_msg_get_integer(body, "UserID");
msim_msg_free(body);
+ username = msim_msg_get_string(msg, "_username");
+
+ if (!uid) {
+ gchar *msg;
+
+ msg = g_strdup_printf(_("No such user: %s"), username);
+ purple_notify_error(NULL, NULL, _("User lookup"), msg);
+ g_free(msg);
+ g_free(username);
+ //msim_msg_free(msg);
+ return;
+ }
+
uid_field_name = msim_msg_get_string(msg, "_uid_field_name");
uid_before = msim_msg_get_string(msg, "_uid_before");
@@ -3238,6 +2011,7 @@
*/
g_free(uid_field_name);
g_free(uid_before);
+ g_free(username);
//msim_msg_free(msg);
}
@@ -3532,7 +2306,7 @@
* 1) MSIM_USER_LOOKUP_CB - make it for PERSIST_REPLY, not just user lookup
* 2) data - make it an MsimMessage?
*/
-static guint
+guint
msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb,
gpointer data)
{
@@ -3580,79 +2354,7 @@
gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, msim_input_cb, gc);
}
-/* Session methods */
-
-/**
- * Create a new MSIM session.
- *
- * @param acct The account to create the session from.
- *
- * @return Pointer to a new session. Free with msim_session_destroy.
- */
-MsimSession *
-msim_session_new(PurpleAccount *acct)
-{
- MsimSession *session;
-
- g_return_val_if_fail(acct != NULL, NULL);
-
- session = g_new0(MsimSession, 1);
-
- session->magic = MSIM_SESSION_STRUCT_MAGIC;
- session->account = acct;
- session->gc = purple_account_get_connection(acct);
- session->sesskey = 0;
- session->userid = 0;
- session->username = NULL;
- session->fd = -1;
-
- /* TODO: Remove. */
- session->user_lookup_cb = g_hash_table_new_full(g_direct_hash,
- g_direct_equal, NULL, NULL); /* do NOT free function pointers! (values) */
- session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash,
- g_direct_equal, NULL, NULL);/* TODO: we don't know what the values are,
- they could be integers inside gpointers
- or strings, so I don't freed them.
- Figure this out, once free cache. */
-
- /* Created in msim_process_server_info() */
- session->server_info = NULL;
-
- session->rxoff = 0;
- session->rxbuf = g_new0(gchar, MSIM_READ_BUF_SIZE);
- session->next_rid = 1;
- session->last_comm = time(NULL);
- session->inbox_status = 0;
-
- return session;
-}
-
-/**
- * Free a session.
- *
- * @param session The session to destroy.
- */
-void
-msim_session_destroy(MsimSession *session)
-{
- g_return_if_fail(MSIM_SESSION_VALID(session));
-
- session->magic = -1;
-
- g_free(session->rxbuf);
- g_free(session->username);
-
- /* TODO: Remove. */
- g_hash_table_destroy(session->user_lookup_cb);
- g_hash_table_destroy(session->user_lookup_cb_data);
-
- if (session->server_info) {
- msim_msg_free(session->server_info);
- }
-
- g_free(session);
-}
-
+
/**
* Close the connection.
*
@@ -3686,106 +2388,6 @@
/**
- * Check if a string is a userid (all numeric).
- *
- * @param user The user id, email, or name.
- *
- * @return TRUE if is userid, FALSE if not.
- */
-static gboolean
-msim_is_userid(const gchar *user)
-{
- g_return_val_if_fail(user != NULL, FALSE);
-
- return strspn(user, "0123456789") == strlen(user);
-}
-
-/**
- * Check if a string is an email address (contains an @).
- *
- * @param user The user id, email, or name.
- *
- * @return TRUE if is an email, FALSE if not.
- *
- * This function is not intended to be used as a generic
- * means of validating email addresses, but to distinguish
- * between a user represented by an email address from
- * other forms of identification.
- */
-static gboolean
-msim_is_email(const gchar *user)
-{
- g_return_val_if_fail(user != NULL, FALSE);
-
- return strchr(user, '@') != NULL;
-}
-
-
-/**
- * Asynchronously lookup user information, calling callback when receive result.
- *
- * @param session
- * @param user The user id, email address, or username. Not freed.
- * @param cb Callback, called with user information when available.
- * @param data An arbitray data pointer passed to the callback.
- */
-/* TODO: change to not use callbacks */
-static void
-msim_lookup_user(MsimSession *session, const gchar *user,
- MSIM_USER_LOOKUP_CB cb, gpointer data)
-{
- MsimMessage *body;
- gchar *field_name;
- guint rid, cmd, dsn, lid;
-
- g_return_if_fail(MSIM_SESSION_VALID(session));
- g_return_if_fail(user != NULL);
- g_return_if_fail(cb != NULL);
-
- purple_debug_info("msim", "msim_lookup_userid: "
- "asynchronously looking up <%s>\n", user);
-
- msim_msg_dump("msim_lookup_user: data=%s\n", (MsimMessage *)data);
-
- /* Setup callback. Response will be associated with request using 'rid'. */
- rid = msim_new_reply_callback(session, cb, data);
-
- /* Send request */
-
- cmd = MSIM_CMD_GET;
-
- if (msim_is_userid(user)) {
- field_name = "UserID";
- dsn = MG_MYSPACE_INFO_BY_ID_DSN;
- lid = MG_MYSPACE_INFO_BY_ID_LID;
- } else if (msim_is_email(user)) {
- field_name = "Email";
- dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
- lid = MG_MYSPACE_INFO_BY_STRING_LID;
- } else {
- field_name = "UserName";
- dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
- lid = MG_MYSPACE_INFO_BY_STRING_LID;
- }
-
- body = msim_msg_new(
- field_name, MSIM_TYPE_STRING, g_strdup(user),
- NULL);
-
- g_return_if_fail(msim_send(session,
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, 1,
- "dsn", MSIM_TYPE_INTEGER, dsn,
- "uid", MSIM_TYPE_INTEGER, session->userid,
- "lid", MSIM_TYPE_INTEGER, lid,
- "rid", MSIM_TYPE_INTEGER, rid,
- "body", MSIM_TYPE_DICTIONARY, body,
- NULL));
-}
-
-
-/**
* Obtain the status text for a buddy.
*
* @param buddy The buddy to obtain status text for.
@@ -3908,6 +2510,8 @@
group_name = msim_msg_get_string(contact_info, "GroupName");
if (group_name) {
group = purple_group_new(group_name);
+ purple_debug_info("msim_add_contact_from_server_cb",
+ "adding to GroupName: %s\n", group_name);
g_free(group_name);
} else {
group = purple_group_new(_("IM Friends"));
@@ -3916,13 +2520,17 @@
/* 2. Get or create buddy */
buddy = purple_find_buddy(session->account, username);
if (!buddy) {
+ purple_debug_info("msim_add_contact_from_server_cb",
+ "creating new buddy: %s\n", username);
buddy = purple_buddy_new(session->account, username, NULL);
}
+ /* Add group to beginning. See #2752. */
+ purple_blist_add_group(group, NULL);
+
/* TODO: use 'Position' in contact_info to take into account where buddy is */
purple_blist_add_buddy(buddy, NULL, group, NULL /* insertion point */);
-
/* 3. Update buddy information */
user = msim_get_user_from_buddy(buddy);
@@ -3943,14 +2551,14 @@
*
* @return TRUE if added.
* */
-static void
+static gboolean
msim_add_contact_from_server(MsimSession *session, MsimMessage *contact_info)
{
guint uid;
const gchar *username;
uid = msim_msg_get_integer(contact_info, "ContactID");
- g_return_if_fail(uid != 0);
+ g_return_val_if_fail(uid != 0, FALSE);
/* Lookup the username, since NickName and IMName is unreliable */
username = msim_uid2username_from_blist(session, uid);
@@ -3965,6 +2573,10 @@
} else {
msim_add_contact_from_server_cb(session, NULL, (gpointer)msim_msg_clone(contact_info));
}
+
+ /* Say that the contact was added, even if we're still looking up
+ * their username. */
+ return TRUE;
}
/** Called when contact list is received from server. */
@@ -3972,12 +2584,16 @@
msim_got_contact_list(MsimSession *session, MsimMessage *reply, gpointer user_data)
{
MsimMessage *body, *body_node;
+ gchar *msg;
+ guint buddy_count;
msim_msg_dump("msim_got_contact_list: reply=%s", reply);
body = msim_msg_get_dictionary(reply, "body");
g_return_if_fail(body != NULL);
+ buddy_count = 0;
+
for (body_node = body;
body_node != NULL;
body_node = msim_msg_get_next_element_node(body_node))
@@ -3986,16 +2602,52 @@
elem = (MsimMessageElement *)body_node->data;
- if (!strcmp(elem->name, "ContactID"))
+ if (g_str_equal(elem->name, "ContactID"))
{
/* Will look for first contact in body_node */
- msim_add_contact_from_server(session, body_node);
+ if (msim_add_contact_from_server(session, body_node)) {
+ ++buddy_count;
+ }
}
}
+ switch (GPOINTER_TO_UINT(user_data)) {
+ case MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS:
+ msg = g_strdup_printf(_("%d buddies were added or updated"), buddy_count);
+ purple_notify_info(session->account, _("Add contacts from server"), msg, NULL);
+ g_free(msg);
+ break;
+
+ case MSIM_CONTACT_LIST_IMPORT_TOP_FRIENDS:
+ /* TODO */
+ break;
+
+ case MSIM_CONTACT_LIST_INITIAL_FRIENDS:
+ /* Nothing */
+ break;
+ }
+
msim_msg_free(body);
}
+/* Get contact list, calling msim_got_contact_list() with what_to_do_after as user_data gpointer. */
+static gboolean
+msim_get_contact_list(MsimSession *session, int what_to_do_after)
+{
+ return msim_send(session,
+ "persist", MSIM_TYPE_INTEGER, 1,
+ "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
+ "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET,
+ "dsn", MSIM_TYPE_INTEGER, MG_LIST_ALL_CONTACTS_DSN,
+ "lid", MSIM_TYPE_INTEGER, MG_LIST_ALL_CONTACTS_LID,
+ "uid", MSIM_TYPE_INTEGER, session->userid,
+ "rid", MSIM_TYPE_INTEGER,
+ msim_new_reply_callback(session, msim_got_contact_list, GUINT_TO_POINTER(what_to_do_after)),
+ "body", MSIM_TYPE_STRING, g_strdup(""),
+ NULL);
+}
+
+
/** Called when friends have been imported to buddy list on server. */
static void
msim_import_friends_cb(MsimSession *session, MsimMessage *reply, gpointer user_data)
@@ -4010,7 +2662,7 @@
completed = msim_msg_get_string(body, "Completed");
g_return_if_fail(body != NULL);
msim_msg_free(body);
- if (strcmp(completed, "True"))
+ if (!g_str_equal(completed, "True"))
{
purple_debug_info("msim_import_friends_cb",
"failed to import friends: %s", completed);
@@ -4024,17 +2676,7 @@
purple_debug_info("msim_import_friends_cb",
"added friends to server-side buddy list, requesting new contacts from server");
- g_return_if_fail(msim_send(session,
- "persist", MSIM_TYPE_INTEGER, 1,
- "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
- "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET,
- "dsn", MSIM_TYPE_INTEGER, MG_LIST_ALL_CONTACTS_DSN,
- "lid", MSIM_TYPE_INTEGER, MG_LIST_ALL_CONTACTS_LID,
- "uid", MSIM_TYPE_INTEGER, session->userid,
- "rid", MSIM_TYPE_INTEGER,
- msim_new_reply_callback(session, msim_got_contact_list, NULL),
- "body", MSIM_TYPE_STRING, g_strdup(""),
- NULL));
+ msim_get_contact_list(session, MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS);
/* TODO: show, X friends have been added */
}
@@ -4263,7 +2905,7 @@
packed_expected = "\\bx\\WFhY\\k1\\v1\\k1\\42\\k1"
"\\v43\\k1\\v52/1xxx/2yyy\\k1\\v7\\final\\";
- if (0 != strcmp(packed, packed_expected)) {
+ if (!g_str_equal(packed, packed_expected)) {
purple_debug_info("msim", "!!!(%d), msim_msg_pack not what expected: %s != %s\n",
++failures, packed, packed_expected);
}
@@ -4273,7 +2915,7 @@
packed_cloned = msim_msg_pack(msg_cloned);
purple_debug_info("msim", "msg cloned=%s\n", packed_cloned);
- if (0 != strcmp(packed, packed_cloned)) {
+ if (!g_str_equal(packed, packed_cloned)) {
purple_debug_info("msim", "!!!(%d), msim_msg_pack on cloned message not equal to original: %s != %s\n",
++failures, packed_cloned, packed);
}
@@ -4328,7 +2970,7 @@
escaped = msim_escape(raw);
purple_debug_info("msim", "msim_test_escaping: raw=%s, escaped=%s\n", raw, escaped);
expected = "hello/1world/2hello/1world";
- if (0 != strcmp(escaped, expected)) {
+ if (!g_str_equal(escaped, expected)) {
purple_debug_info("msim", "!!!(%d), msim_escape failed: %s != %s\n",
++failures, escaped, expected);
}
@@ -4337,7 +2979,7 @@
unescaped = msim_unescape(escaped);
g_free(escaped);
purple_debug_info("msim", "msim_test_escaping: unescaped=%s\n", unescaped);
- if (0 != strcmp(raw, unescaped)) {
+ if (!g_str_equal(raw, unescaped)) {
purple_debug_info("msim", "!!!(%d), msim_unescape failed: %s != %s\n",
++failures, raw, unescaped);
}
@@ -4346,16 +2988,156 @@
}
#endif
+static gboolean
+msim_uri_handler(const gchar *proto, const gchar *cmd, GHashTable *params)
+{
+ PurpleAccount *account;
+ MsimSession *session;
+ GList *l;
+ gchar *uid_str, *cid_str;
+ guint uid, cid;
+
+ if (g_ascii_strcasecmp(proto, "myim"))
+ return FALSE;
+
+ /* Parameters are case-insensitive. */
+ uid_str = g_hash_table_lookup(params, "uid");
+ cid_str = g_hash_table_lookup(params, "cid");
+
+ uid = uid_str ? atol(uid_str) : 0;
+ cid = cid_str ? atol(cid_str) : 0;
+
+ /* Need a contact. */
+ g_return_val_if_fail(cid != 0, FALSE);
+
+ /* TODO: if auto=true, "Add all the people on this page to my IM List!", on
+ * http://collect.myspace.com/index.cfm?fuseaction=im.friendslist. Don't need a cid. */
+
+ /* Convert numeric contact ID back to a string. Needed for looking up. Don't just
+ * directly use cid directly from parameters, because it might not be numeric.
+ * It is trivial to change this to allow cID to be a username, but that's not how
+ * the official MySpaceIM client works, so don't provide that functionality. */
+ cid_str = g_strdup_printf("%d", cid);
+
+
+ /* Find our account with specified user id, or use first connected account if uid=0. */
+ account = NULL;
+ l = purple_accounts_get_all();
+ while (l) {
+ if (purple_account_is_connected(l->data) &&
+ (uid == 0 || purple_account_get_int(l->data, "uid", 0) == uid)) {
+ account = l->data;
+ break;
+ }
+ l = l->next;
+ }
+
+ if (!account) {
+ purple_notify_error(NULL, _("myim URL handler"),
+ _("No suitable MySpaceIM account could be found to open this myim URL."),
+ _("Enable the proper MySpaceIM account and try again."));
+ g_free(cid_str);
+ return FALSE;
+ }
+
+ session = (MsimSession *)account->gc->proto_data;
+ g_return_val_if_fail(session != NULL, FALSE);
+
+ /* Lookup userid to username. TODO: push this down, to IM sending/contact
+ * adding functions. */
+
+ /* myim:sendIM?uID=USERID&cID=CONTACTID */
+ if (!g_ascii_strcasecmp(cmd, "sendIM")) {
+ msim_lookup_user(session, cid_str, (MSIM_USER_LOOKUP_CB)msim_uri_handler_sendIM_cb, NULL);
+ g_free(cid_str);
+ return TRUE;
+
+ /* myim:addContact?uID=USERID&cID=CONTACTID */
+ } else if (!g_ascii_strcasecmp(cmd, "addContact")) {
+ msim_lookup_user(session, cid_str, (MSIM_USER_LOOKUP_CB)msim_uri_handler_addContact_cb, NULL);
+ g_free(cid_str);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* TODO: move uid->username resolving to IM sending and buddy adding functions,
+ * so that user can manually add or IM by userid and username automatically
+ * looked up if possible? */
+
+/** Handle a myim:sendIM URI command, after username has been looked up. */
+static void
+msim_uri_handler_sendIM_cb(MsimSession *session, MsimMessage *userinfo, gpointer data)
+{
+ PurpleConversation *conv;
+ MsimMessage *body;
+ gchar *username;
+
+ body = msim_msg_get_dictionary(userinfo, "body");
+ username = msim_msg_get_string(body, "UserName");
+ msim_msg_free(body);
+
+ if (!username) {
+ guint uid;
+
+ uid = msim_msg_get_integer(userinfo, "UserID");
+ g_return_if_fail(uid != 0);
+
+ username = g_strdup_printf("%d", uid);
+ }
+
+
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, username, session->account);
+ if (!conv) {
+ purple_debug_info("msim_uri_handler", "creating new conversation for %s\n", username);
+ conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, username);
+ }
+
+ /* Just open the window so the user can send an IM. */
+ purple_conversation_present(conv);
+
+ g_free(username);
+}
+
+/** Handle a myim:addContact command, after username has been looked up. */
+static void
+msim_uri_handler_addContact_cb(MsimSession *session, MsimMessage *userinfo, gpointer data)
+{
+ MsimMessage *body;
+ gchar *username;
+
+ body = msim_msg_get_dictionary(userinfo, "body");
+ username = msim_msg_get_string(body, "UserName");
+ msim_msg_free(body);
+
+ if (!username) {
+ guint uid;
+
+ uid = msim_msg_get_integer(userinfo, "UserID");
+ g_return_if_fail(uid != 0);
+
+ username = g_strdup_printf("%d", uid);
+ }
+
+
+ purple_blist_request_add_buddy(session->account, username, _("Buddies"), NULL);
+
+ g_free(username);
+}
+
/** Initialize plugin. */
void
init_plugin(PurplePlugin *plugin)
{
- PurpleAccountOption *option;
#ifdef MSIM_SELF_TEST
msim_test_all();
exit(0);
#endif /* MSIM_SELF_TEST */
+ PurpleAccountOption *option;
+ static gboolean initialized = FALSE;
+
/* TODO: default to automatically try different ports. Make the user be
* able to set the first port to try (like LastConnectedPort in Windows client). */
@@ -4386,21 +3168,14 @@
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
#endif
- /* TODO: /zap command. Problem with this is that there are different kinds of zaps,
- * and the selection is best made available in a drop-down menu, instead of forcing
- * the user to type the kind of zap and memorizing available zaps (or putting it in the
- * help menu). A new "attention" API, for zap/buzz/nudge (different protocols) will
- * solve this. */
-#if 0
- purple_cmd_register("zap", /* cmd */
- "w", /* args - accept a single word */
- PURPLE_CMD_P_PRPL, /* priority */
- PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, /* flags */
- "prpl-myspace", /* prpl_id */
- msim_cmd_zap, /* func */
- _("zap: zap a user to get their attention"), /* helpstr */
- NULL); /* data */
-#endif
+ /* Code below only runs once. Based on oscar.c's oscar_init(). */
+ if (initialized)
+ return;
+
+ initialized = TRUE;
+
+ purple_signal_connect(purple_get_core(), "uri-handler", &initialized,
+ PURPLE_CALLBACK(msim_uri_handler), NULL);
}
PURPLE_INIT_PLUGIN(myspace, init_plugin, info);
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/myspace/myspace.h
--- a/libpurple/protocols/myspace/myspace.h Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.h Thu Aug 30 00:09:47 2007 +0000
@@ -46,9 +46,15 @@
#include "util.h" /* for base64 */
#include "debug.h" /* for purple_debug_info */
#include "xmlnode.h"
+#include "core.h"
/* MySpaceIM includes */
+#include "persist.h"
#include "message.h"
+#include "session.h"
+#include "zap.h"
+#include "markup.h"
+#include "user.h"
/* Conditional compilation options */
/* Send third-party client version? (Recognized by us and Miranda's plugin) */
@@ -69,7 +75,7 @@
/* Use the attention API for zaps? */
/* Can't have until >=2.2.0, since is a new API. */
-/*#define MSIM_USE_ATTENTION_API */
+#define MSIM_USE_ATTENTION_API
/* Constants */
@@ -90,7 +96,7 @@
#define MSIM_LANGUAGE_NAME_ENGLISH "ENGLISH"
/* msimprpl version string of this plugin */
-#define MSIM_PRPL_VERSION_STRING "0.14"
+#define MSIM_PRPL_VERSION_STRING "0.16"
/* Default server */
#define MSIM_SERVER "im.myspace.akadns.net"
@@ -157,77 +163,18 @@
#define MSIM_STATUS_CODE_IDLE 2
#define MSIM_STATUS_CODE_AWAY 5
-/* Text formatting bits for */
-#define MSIM_TEXT_BOLD 1
-#define MSIM_TEXT_ITALIC 2
-#define MSIM_TEXT_UNDERLINE 4
-/* Default baseline size of purple's fonts, in points. What is size 3 in points.
- * _font_scale specifies scaling factor relative to this point size. Note this
- * is only the default; it is configurable in account options. */
-#define MSIM_BASE_FONT_POINT_SIZE 8
-
-/* Default display's DPI. 96 is common but it can differ. Also configurable
- * in account options. */
-#define MSIM_DEFAULT_DPI 96
-
-
-/* Random number in every MsimSession, to ensure it is valid. */
-#define MSIM_SESSION_STRUCT_MAGIC 0xe4a6752b
-
-/* Inbox status bitfield values for MsimSession.inbox_status */
+/* Inbox status bitfield values for MsimSession.inbox_status. */
#define MSIM_INBOX_MAIL (1 << 0)
#define MSIM_INBOX_BLOG_COMMENT (1 << 1)
#define MSIM_INBOX_PROFILE_COMMENT (1 << 2)
#define MSIM_INBOX_FRIEND_REQUEST (1 << 3)
#define MSIM_INBOX_PICTURE_COMMENT (1 << 4)
-/* Everything needed to keep track of a session (proto_data field in PurpleConnection) */
-typedef struct _MsimSession
-{
- guint magic; /**< MSIM_SESSION_STRUCT_MAGIC */
- PurpleAccount *account;
- PurpleConnection *gc;
- guint sesskey; /**< Session key from server */
- guint userid; /**< This user's numeric user ID */
- gchar *username; /**< This user's unique username */
- gint fd; /**< File descriptor to/from server */
-
- /* TODO: Remove. */
- GHashTable *user_lookup_cb; /**< Username -> userid lookup callback */
- GHashTable *user_lookup_cb_data; /**< Username -> userid lookup callback data */
-
- MsimMessage *server_info; /**< Parameters from server */
-
- gchar *rxbuf; /**< Receive buffer */
- guint rxoff; /**< Receive buffer offset */
- guint next_rid; /**< Next request/response ID */
- time_t last_comm; /**< Time received last communication */
- guint inbox_status; /**< Bit field of inbox notifications */
-} MsimSession;
-
-/* Check if an MsimSession is valid */
-#define MSIM_SESSION_VALID(s) (session != NULL && session->magic == MSIM_SESSION_STRUCT_MAGIC)
-
-/* Hold ephemeral information about buddies, for proto_data of PurpleBuddy. */
-/* GHashTable? */
-typedef struct _MsimUser
-{
- PurpleBuddy *buddy;
- guint client_cv;
- gchar *client_info;
- guint age;
- gchar *gender;
- gchar *location;
- guint total_friends;
- gchar *headline;
- gchar *display_name;
- /* Note: uid is in &buddy->node (set_blist_node_int), since it never changes */
- gchar *username;
- gchar *band_name, *song_name;
- gchar *image_url;
-} MsimUser;
-
+/* Codes for msim_got_contact_list(), to tell what to do afterwards. */
+#define MSIM_CONTACT_LIST_INITIAL_FRIENDS 0
+#define MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS 1
+#define MSIM_CONTACT_LIST_IMPORT_TOP_FRIENDS 2
#ifdef MSIM_USE_ATTENTION_API
#define MsimAttentionType PurpleAttentionType
@@ -246,33 +193,17 @@
};
#endif
-gchar *str_replace(const gchar *str, const gchar *old, const gchar *new);
-
-/* Callback function pointer type for when a user's information is received,
- * initiated from a user lookup. */
-typedef void (*MSIM_USER_LOOKUP_CB)(MsimSession *session, MsimMessage *userinfo, gpointer data);
-
/* Functions */
gboolean msim_load(PurplePlugin *plugin);
GList *msim_status_types(PurpleAccount *acct);
-GList *msim_attention_types(PurpleAccount *acct);
-gboolean msim_send_attention(PurpleConnection *gc, gchar *username, guint code);
-
-GList *msim_blist_node_menu(PurpleBlistNode *node);
-
const gchar *msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy);
-
gboolean msim_send_raw(MsimSession *session, const gchar *msg);
void msim_login(PurpleAccount *acct);
-
-int msim_send_im(PurpleConnection *gc, const gchar *who, const gchar *message,
-PurpleMessageFlags flags);
+int msim_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags);
+unsigned int msim_send_typing(PurpleConnection *gc, const gchar *name, PurpleTypingState state);
-typedef void (*MSIM_XMLNODE_CONVERT)(MsimSession *, xmlnode *, gchar **, gchar **);
-
-unsigned int msim_send_typing(PurpleConnection *gc, const gchar *name, PurpleTypingState state);
void msim_get_info(PurpleConnection *gc, const gchar *name);
void msim_set_status(PurpleAccount *account, PurpleStatus *status);
@@ -283,9 +214,6 @@
gboolean msim_offline_message(const PurpleBuddy *buddy);
-MsimSession *msim_session_new(PurpleAccount *acct);
-void msim_session_destroy(MsimSession *session);
-
void msim_close(PurpleConnection *gc);
char *msim_status_text(PurpleBuddy *buddy);
@@ -298,6 +226,13 @@
int msim_test_escaping(void);
#endif
+gboolean msim_send_bm(MsimSession *session, const gchar *who, const gchar *text, int type);
+
+
+void msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note);
+guint msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb, gpointer data);
+
+
void init_plugin(PurplePlugin *plugin);
#endif /* !_MYSPACE_MYSPACE_H */
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/myspace/persist.h
--- a/libpurple/protocols/myspace/persist.h Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/myspace/persist.h Thu Aug 30 00:09:47 2007 +0000
@@ -42,8 +42,8 @@
/** Define a set of _DSN and _LID constants for a persistance request. */
#define MSIM_PERSIST_DSN_LID(name,dsn,lid) \
- const int name##_DSN = dsn; \
- const int name##_LID = lid;
+ static const int name##_DSN = dsn; \
+ static const int name##_LID = lid;
/* Can't do this, errors:
* persist.h:51:3: error: '#' is not followed by a macro parameter
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/myspace/release.sh
--- a/libpurple/protocols/myspace/release.sh Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/myspace/release.sh Thu Aug 30 00:09:47 2007 +0000
@@ -4,7 +4,7 @@
# Package a new msimprpl for release. Must be run with bash.
-VERSION=0.14
+VERSION=0.16
make
# Include 'myspace' directory in archive, so it can easily be unextracted
# into ~/pidgin/libpurple/protocols at the correct location.
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/myspace/session.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/myspace/session.c Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,95 @@
+/* MySpaceIM Protocol Plugin, session
+ *
+ * Copyright (C) 2007, Jeff Connelly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "myspace.h"
+
+/* Session methods */
+
+/**
+ * Create a new MSIM session.
+ *
+ * @param acct The account to create the session from.
+ *
+ * @return Pointer to a new session. Free with msim_session_destroy.
+ */
+MsimSession *
+msim_session_new(PurpleAccount *acct)
+{
+ MsimSession *session;
+
+ g_return_val_if_fail(acct != NULL, NULL);
+
+ session = g_new0(MsimSession, 1);
+
+ session->magic = MSIM_SESSION_STRUCT_MAGIC;
+ session->account = acct;
+ session->gc = purple_account_get_connection(acct);
+ session->sesskey = 0;
+ session->userid = 0;
+ session->username = NULL;
+ session->fd = -1;
+
+ /* TODO: Remove. */
+ session->user_lookup_cb = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal, NULL, NULL); /* do NOT free function pointers! (values) */
+ session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal, NULL, NULL);/* TODO: we don't know what the values are,
+ they could be integers inside gpointers
+ or strings, so I don't freed them.
+ Figure this out, once free cache. */
+
+ /* Created in msim_process_server_info() */
+ session->server_info = NULL;
+
+ session->rxoff = 0;
+ session->rxbuf = g_new0(gchar, MSIM_READ_BUF_SIZE);
+ session->next_rid = 1;
+ session->last_comm = time(NULL);
+ session->inbox_status = 0;
+
+ return session;
+}
+
+/**
+ * Free a session.
+ *
+ * @param session The session to destroy.
+ */
+void
+msim_session_destroy(MsimSession *session)
+{
+ g_return_if_fail(MSIM_SESSION_VALID(session));
+
+ session->magic = -1;
+
+ g_free(session->rxbuf);
+ g_free(session->username);
+
+ /* TODO: Remove. */
+ g_hash_table_destroy(session->user_lookup_cb);
+ g_hash_table_destroy(session->user_lookup_cb_data);
+
+ if (session->server_info) {
+ msim_msg_free(session->server_info);
+ }
+
+ g_free(session);
+}
+
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/myspace/session.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/myspace/session.h Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,57 @@
+/* MySpaceIM Protocol Plugin, session
+ *
+ * Copyright (C) 2007, Jeff Connelly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MYSPACE_SESSION_H
+#define _MYSPACE_SESSION_H
+
+/* Random number in every MsimSession, to ensure it is valid. */
+#define MSIM_SESSION_STRUCT_MAGIC 0xe4a6752b
+
+/* Everything needed to keep track of a session (proto_data field in PurpleConnection) */
+typedef struct _MsimSession
+{
+ guint magic; /**< MSIM_SESSION_STRUCT_MAGIC */
+ PurpleAccount *account;
+ PurpleConnection *gc;
+ guint sesskey; /**< Session key from server */
+ guint userid; /**< This user's numeric user ID */
+ gchar *username; /**< This user's unique username */
+ gint fd; /**< File descriptor to/from server */
+
+ /* TODO: Remove. */
+ GHashTable *user_lookup_cb; /**< Username -> userid lookup callback */
+ GHashTable *user_lookup_cb_data; /**< Username -> userid lookup callback data */
+
+ MsimMessage *server_info; /**< Parameters from server */
+
+ gchar *rxbuf; /**< Receive buffer */
+ guint rxoff; /**< Receive buffer offset */
+ guint next_rid; /**< Next request/response ID */
+ time_t last_comm; /**< Time received last communication */
+ guint inbox_status; /**< Bit field of inbox notifications */
+} MsimSession;
+
+/* Check if an MsimSession is valid */
+#define MSIM_SESSION_VALID(s) (session != NULL && session->magic == MSIM_SESSION_STRUCT_MAGIC)
+
+
+MsimSession *msim_session_new(PurpleAccount *acct);
+void msim_session_destroy(MsimSession *session);
+
+#endif /* !_MYSPACE_SESSION_H */
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/myspace/user.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/myspace/user.c Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,437 @@
+/* MySpaceIM Protocol Plugin, header file
+ *
+ * Copyright (C) 2007, Jeff Connelly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "myspace.h"
+
+static void msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user);
+static gchar *msim_format_now_playing(gchar *band, gchar *song);
+static void msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text,
+ gsize len, const gchar *error_message);
+
+/** Format the "now playing" indicator, showing the artist and song.
+ * @return Return a new string (must be g_free()'d), or NULL.
+ */
+static gchar *
+msim_format_now_playing(gchar *band, gchar *song)
+{
+ if ((band && strlen(band)) || (song && strlen(song))) {
+ return g_strdup_printf("%s - %s",
+ (band && strlen(band)) ? band : "Unknown Artist",
+ (song && strlen(song)) ? song : "Unknown Song");
+ } else {
+ return NULL;
+ }
+}
+/** Get the MsimUser from a PurpleBuddy, creating it if needed. */
+MsimUser *
+msim_get_user_from_buddy(PurpleBuddy *buddy)
+{
+ MsimUser *user;
+
+ if (!buddy) {
+ return NULL;
+ }
+
+ if (!buddy->proto_data) {
+ /* No MsimUser for this buddy; make one. */
+
+ /* TODO: where is this freed? */
+ user = g_new0(MsimUser, 1);
+ user->buddy = buddy;
+ buddy->proto_data = (gpointer)user;
+ }
+
+ user = (MsimUser *)(buddy->proto_data);
+
+ return user;
+}
+
+/** Find and return an MsimUser * representing a user on the buddy list, or NULL. */
+MsimUser *
+msim_find_user(MsimSession *session, const gchar *username)
+{
+ PurpleBuddy *buddy;
+ MsimUser *user;
+
+ buddy = purple_find_buddy(session->account, username);
+ if (!buddy) {
+ return NULL;
+ }
+
+ user = msim_get_user_from_buddy(buddy);
+
+ return user;
+}
+
+/** Append user information to a PurpleNotifyUserInfo, given an MsimUser.
+ * Used by msim_tooltip_text() and msim_get_info_cb() to show a user's profile.
+ */
+void
+msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full)
+{
+ gchar *str;
+ guint uid;
+ guint cv;
+
+ /* Useful to identify the account the tooltip refers to.
+ * Other prpls show this. */
+ if (user->username) {
+ purple_notify_user_info_add_pair(user_info, _("User"), user->username);
+ }
+
+ uid = purple_blist_node_get_int(&user->buddy->node, "UserID");
+
+ if (full) {
+ /* TODO: link to username, if available */
+ purple_notify_user_info_add_pair(user_info, _("Profile"),
+ g_strdup_printf("http://myspace.com/%d",
+ uid, uid));
+ }
+
+
+ /* a/s/l...the vitals */
+ if (user->age) {
+ purple_notify_user_info_add_pair(user_info, _("Age"),
+ g_strdup_printf("%d", user->age));
+ }
+
+ if (user->gender && strlen(user->gender)) {
+ purple_notify_user_info_add_pair(user_info, _("Gender"), user->gender);
+ }
+
+ if (user->location && strlen(user->location)) {
+ purple_notify_user_info_add_pair(user_info, _("Location"), user->location);
+ }
+
+ /* Other information */
+ if (user->headline && strlen(user->headline)) {
+ purple_notify_user_info_add_pair(user_info, _("Headline"), user->headline);
+ }
+
+ str = msim_format_now_playing(user->band_name, user->song_name);
+ if (str && strlen(str)) {
+ purple_notify_user_info_add_pair(user_info, _("Song"), str);
+ }
+
+ /* Note: total friends only available if looked up by uid, not username. */
+ if (user->total_friends) {
+ purple_notify_user_info_add_pair(user_info, _("Total Friends"),
+ g_strdup_printf("%d", user->total_friends));
+ }
+
+ if (full) {
+ /* Client information */
+
+ str = user->client_info;
+ cv = user->client_cv;
+
+ if (str && cv != 0) {
+ purple_notify_user_info_add_pair(user_info, _("Client Version"),
+ g_strdup_printf("%s (build %d)", str, cv));
+ } else if (str) {
+ purple_notify_user_info_add_pair(user_info, _("Client Version"),
+ g_strdup(str));
+ } else if (cv) {
+ purple_notify_user_info_add_pair(user_info, _("Client Version"),
+ g_strdup_printf("Build %d", cv));
+ }
+ }
+}
+
+/** Store a field of information about a buddy. */
+void
+msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user)
+{
+ if (g_str_equal(key_str, "UserID") || g_str_equal(key_str, "ContactID")) {
+ /* Save to buddy list, if it exists, for quick cached uid lookup with msim_uid2username_from_blist(). */
+ if (user->buddy)
+ {
+ purple_debug_info("msim", "associating uid %s with username %s\n", key_str, user->buddy->name);
+ purple_blist_node_set_int(&user->buddy->node, "UserID", atol(value_str));
+ }
+ /* Need to store in MsimUser, too? What if not on blist? */
+ } else if (g_str_equal(key_str, "Age")) {
+ user->age = atol(value_str);
+ } else if (g_str_equal(key_str, "Gender")) {
+ user->gender = g_strdup(value_str);
+ } else if (g_str_equal(key_str, "Location")) {
+ user->location = g_strdup(value_str);
+ } else if (g_str_equal(key_str, "TotalFriends")) {
+ user->total_friends = atol(value_str);
+ } else if (g_str_equal(key_str, "DisplayName")) {
+ user->display_name = g_strdup(value_str);
+ } else if (g_str_equal(key_str, "BandName")) {
+ user->band_name = g_strdup(value_str);
+ } else if (g_str_equal(key_str, "SongName")) {
+ user->song_name = g_strdup(value_str);
+ } else if (g_str_equal(key_str, "UserName") || g_str_equal(key_str, "IMName") || g_str_equal(key_str, "NickName")) {
+ /* Ignore because PurpleBuddy knows this already */
+ ;
+ } else if (g_str_equal(key_str, "ImageURL") || g_str_equal(key_str, "AvatarURL")) {
+ const gchar *previous_url;
+
+ user->image_url = g_strdup(value_str);
+
+ /* Instead of showing 'no photo' picture, show nothing. */
+ if (g_str_equal(user->image_url, "http://x.myspace.com/images/no_pic.gif"))
+ {
+ purple_buddy_icons_set_for_user(user->buddy->account,
+ user->buddy->name,
+ NULL, 0, NULL);
+ return;
+ }
+
+ /* TODO: use ETag for checksum */
+ previous_url = purple_buddy_icons_get_checksum_for_user(user->buddy);
+
+ /* Only download if URL changed */
+ if (!previous_url || !g_str_equal(previous_url, user->image_url)) {
+ purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
+ }
+ } else if (g_str_equal(key_str, "LastImageUpdated")) {
+ /* TODO: use somewhere */
+ user->last_image_updated = atol(value_str);
+ } else if (g_str_equal(key_str, "Headline")) {
+ user->headline = g_strdup(value_str);
+ } else {
+ /* TODO: other fields in MsimUser */
+ gchar *msg;
+
+ msg = g_strdup_printf("msim_store_user_info_each: unknown field %s=%s",
+ key_str, value_str);
+
+ msim_unrecognized(NULL, NULL, msg);
+
+ g_free(msg);
+ }
+}
+
+/** Save buddy information to the buddy list from a user info reply message.
+ *
+ * @param session
+ * @param msg The user information reply, with any amount of information.
+ * @param user The structure to save to, or NULL to save in PurpleBuddy->proto_data.
+ *
+ * Variable information is saved to the passed MsimUser structure. Permanent
+ * information (UserID) is stored in the blist node of the buddy list (and
+ * ends up in blist.xml, persisted to disk) if it exists.
+ *
+ * If the function has no buddy information, this function
+ * is a no-op (and returns FALSE).
+ *
+ */
+gboolean
+msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user)
+{
+ gchar *username;
+ MsimMessage *body, *body_node;
+
+ g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
+ g_return_val_if_fail(msg != NULL, FALSE);
+
+ body = msim_msg_get_dictionary(msg, "body");
+ if (!body) {
+ return FALSE;
+ }
+
+ username = msim_msg_get_string(body, "UserName");
+
+ if (!username) {
+ purple_debug_info("msim",
+ "msim_process_reply: not caching body, no UserName\n");
+ msim_msg_free(body);
+ g_free(username);
+ return FALSE;
+ }
+
+ /* Null user = find and store in PurpleBuddy's proto_data */
+ if (!user) {
+ user = msim_find_user(session, username);
+ if (!user) {
+ msim_msg_free(body);
+ g_free(username);
+ return FALSE;
+ }
+ }
+
+ /* TODO: make looping over MsimMessage's easier. */
+ for (body_node = body;
+ body_node != NULL;
+ body_node = msim_msg_get_next_element_node(body_node))
+ {
+ const gchar *key_str;
+ gchar *value_str;
+ MsimMessageElement *elem;
+
+ elem = (MsimMessageElement *)body_node->data;
+ key_str = elem->name;
+
+ value_str = msim_msg_get_string_from_element(elem);
+ msim_store_user_info_each(key_str, value_str, user);
+ g_free(value_str);
+ }
+
+ if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN &&
+ msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID) {
+ /* TODO: do something with our own IM info, if we need it for some
+ * specific purpose. Otherwise it is available on the buddy list,
+ * if the user has themselves as their own buddy.
+ *
+ * However, much of the info is already available in MsimSession,
+ * stored in msim_we_are_logged_on(). */
+ } else if (msim_msg_get_integer(msg, "dsn") == MG_OWN_MYSPACE_INFO_DSN &&
+ msim_msg_get_integer(msg, "lid") == MG_OWN_MYSPACE_INFO_LID) {
+ /* TODO: same as above, but for MySpace info. */
+ }
+
+ msim_msg_free(body);
+
+ return TRUE;
+}
+
+/**
+ * Asynchronously lookup user information, calling callback when receive result.
+ *
+ * @param session
+ * @param user The user id, email address, or username. Not freed.
+ * @param cb Callback, called with user information when available.
+ * @param data An arbitray data pointer passed to the callback.
+ */
+/* TODO: change to not use callbacks */
+void
+msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data)
+{
+ MsimMessage *body;
+ gchar *field_name;
+ guint rid, cmd, dsn, lid;
+
+ g_return_if_fail(MSIM_SESSION_VALID(session));
+ g_return_if_fail(user != NULL);
+ /* Callback can be null to not call anything, just lookup & store information. */
+ /*g_return_if_fail(cb != NULL);*/
+
+ purple_debug_info("msim", "msim_lookup_userid: "
+ "asynchronously looking up <%s>\n", user);
+
+ msim_msg_dump("msim_lookup_user: data=%s\n", (MsimMessage *)data);
+
+ /* Setup callback. Response will be associated with request using 'rid'. */
+ rid = msim_new_reply_callback(session, cb, data);
+
+ /* Send request */
+
+ cmd = MSIM_CMD_GET;
+
+ if (msim_is_userid(user)) {
+ field_name = "UserID";
+ dsn = MG_MYSPACE_INFO_BY_ID_DSN;
+ lid = MG_MYSPACE_INFO_BY_ID_LID;
+ } else if (msim_is_email(user)) {
+ field_name = "Email";
+ dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
+ lid = MG_MYSPACE_INFO_BY_STRING_LID;
+ } else {
+ field_name = "UserName";
+ dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
+ lid = MG_MYSPACE_INFO_BY_STRING_LID;
+ }
+
+ body = msim_msg_new(
+ field_name, MSIM_TYPE_STRING, g_strdup(user),
+ NULL);
+
+ g_return_if_fail(msim_send(session,
+ "persist", MSIM_TYPE_INTEGER, 1,
+ "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
+ "cmd", MSIM_TYPE_INTEGER, 1,
+ "dsn", MSIM_TYPE_INTEGER, dsn,
+ "uid", MSIM_TYPE_INTEGER, session->userid,
+ "lid", MSIM_TYPE_INTEGER, lid,
+ "rid", MSIM_TYPE_INTEGER, rid,
+ "body", MSIM_TYPE_DICTIONARY, body,
+ NULL));
+}
+
+
+/**
+ * Check if a string is a userid (all numeric).
+ *
+ * @param user The user id, email, or name.
+ *
+ * @return TRUE if is userid, FALSE if not.
+ */
+gboolean
+msim_is_userid(const gchar *user)
+{
+ g_return_val_if_fail(user != NULL, FALSE);
+
+ return strspn(user, "0123456789") == strlen(user);
+}
+
+/**
+ * Check if a string is an email address (contains an @).
+ *
+ * @param user The user id, email, or name.
+ *
+ * @return TRUE if is an email, FALSE if not.
+ *
+ * This function is not intended to be used as a generic
+ * means of validating email addresses, but to distinguish
+ * between a user represented by an email address from
+ * other forms of identification.
+ */
+gboolean
+msim_is_email(const gchar *user)
+{
+ g_return_val_if_fail(user != NULL, FALSE);
+
+ return strchr(user, '@') != NULL;
+}
+
+
+/** Callback for when a buddy icon finished being downloaded. */
+static void
+msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data,
+ gpointer user_data,
+ const gchar *url_text,
+ gsize len,
+ const gchar *error_message)
+{
+ MsimUser *user;
+
+ user = (MsimUser *)user_data;
+
+ purple_debug_info("msim_downloaded_buddy_icon",
+ "Downloaded %d bytes\n", len);
+
+ if (!url_text) {
+ purple_debug_info("msim_downloaded_buddy_icon",
+ "failed to download icon for %s",
+ user->buddy->name);
+ return;
+ }
+
+ purple_buddy_icons_set_for_user(user->buddy->account,
+ user->buddy->name,
+ g_memdup((gchar *)url_text, len), len,
+ /* Use URL itself as buddy icon "checksum" (TODO: ETag) */
+ user->image_url); /* checksum */
+}
+
+
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/myspace/user.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/myspace/user.h Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,55 @@
+/* MySpaceIM Protocol Plugin, header file
+ *
+ * Copyright (C) 2007, Jeff Connelly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MYSPACE_USER_H
+#define _MYSPACE_USER_H
+
+/* Hold ephemeral information about buddies, for proto_data of PurpleBuddy. */
+/* GHashTable? */
+typedef struct _MsimUser
+{
+ PurpleBuddy *buddy;
+ guint client_cv;
+ gchar *client_info;
+ guint age;
+ gchar *gender;
+ gchar *location;
+ guint total_friends;
+ gchar *headline;
+ gchar *display_name;
+ /* Note: uid is in &buddy->node (set_blist_node_int), since it never changes */
+ gchar *username;
+ gchar *band_name, *song_name;
+ gchar *image_url;
+ guint last_image_updated;
+} MsimUser;
+
+/* Callback function pointer type for when a user's information is received,
+ * initiated from a user lookup. */
+typedef void (*MSIM_USER_LOOKUP_CB)(MsimSession *session, MsimMessage *userinfo, gpointer data);
+
+MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy);
+MsimUser *msim_find_user(MsimSession *session, const gchar *username);
+void msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full);
+gboolean msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user);
+gboolean msim_is_userid(const gchar *user);
+gboolean msim_is_email(const gchar *user);
+void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data);
+
+#endif /* !_MYSPACE_USER_H */
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/myspace/zap.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/myspace/zap.c Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,265 @@
+/* MySpaceIM Protocol Plugin - zap support
+ *
+ * Copyright (C) 2007, Jeff Connelly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "myspace.h"
+#include "zap.h"
+
+static gboolean msim_send_zap(MsimSession *session, const gchar *username, guint code);
+static void msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr);
+
+
+/** Get zap types. */
+GList *
+msim_attention_types(PurpleAccount *acct)
+{
+ static GList *types = NULL;
+ MsimAttentionType* attn;
+
+ if (!types) {
+#define _MSIM_ADD_NEW_ATTENTION(icn, nme, incoming, outgoing) \
+ attn = g_new0(MsimAttentionType, 1); \
+ attn->icon_name = icn; \
+ attn->name = nme; \
+ attn->incoming_description = incoming; \
+ attn->outgoing_description = outgoing; \
+ types = g_list_append(types, attn);
+
+ /* TODO: icons for each zap */
+ _MSIM_ADD_NEW_ATTENTION(NULL, _("zap"), _("zapped"), _("Zapping"));
+ _MSIM_ADD_NEW_ATTENTION(NULL, _("whack"), _("whacked"), _("Whacking"));
+ _MSIM_ADD_NEW_ATTENTION(NULL, _("torch"), _("torched"), _("Torching"));
+ _MSIM_ADD_NEW_ATTENTION(NULL, _("smooch"), _("smooched"), _("Smooching"));
+ _MSIM_ADD_NEW_ATTENTION(NULL, _("hug"), _("hugged"), _("Hugging"));
+ _MSIM_ADD_NEW_ATTENTION(NULL, _("bslap"), _("bslapped"), _("Bslapping"));
+ _MSIM_ADD_NEW_ATTENTION(NULL, _("goose"), _("goosed"), _("Goosing"));
+ _MSIM_ADD_NEW_ATTENTION(NULL, _("hi-five"), _("hi-fived"), _("Hi-fiving"));
+ _MSIM_ADD_NEW_ATTENTION(NULL, _("punk"), _("punk'd"), _("Punking"));
+ _MSIM_ADD_NEW_ATTENTION(NULL, _("raspberry"), _("raspberried"), _("Raspberry'ing"));
+ }
+
+ return types;
+}
+
+/** Send a zap */
+gboolean
+msim_send_attention(PurpleConnection *gc, const gchar *username, guint code)
+{
+ GList *types;
+ MsimSession *session;
+ MsimAttentionType *attn;
+ PurpleBuddy *buddy;
+
+ session = (MsimSession *)gc->proto_data;
+
+ /* Look for this attention type, by the code index given. */
+ types = msim_attention_types(gc->account);
+ attn = (MsimAttentionType *)g_list_nth_data(types, code);
+
+ if (!attn) {
+ purple_debug_info("msim_send_attention", "got invalid zap code %d\n", code);
+ return FALSE;
+ }
+
+ buddy = purple_find_buddy(session->account, username);
+ if (!buddy) {
+ return FALSE;
+ }
+
+ msim_send_zap(session, username, code);
+
+ return TRUE;
+}
+
+/** Send a zap to a user. */
+static gboolean
+msim_send_zap(MsimSession *session, const gchar *username, guint code)
+{
+ gchar *zap_string;
+ gboolean rc;
+#ifndef MSIM_USE_ATTENTION_API
+ GList *types;
+ MsimAttentionType *attn;
+ gchar *zap_description;
+#endif
+
+ g_return_val_if_fail(session != NULL, FALSE);
+ g_return_val_if_fail(username != NULL, FALSE);
+
+
+#ifdef MSIM_USE_ATTENTION_API
+ /* serv_send_attention(session->gc, username, code); */
+#else
+ types = msim_attention_types(session->account);
+
+ attn = g_list_nth_data(types, code);
+ if (!attn) {
+ return FALSE;
+ }
+
+
+ zap_description = g_strdup_printf("*** Attention: %s %s ***", attn->outgoing_description,
+ username);
+
+ serv_got_im(session->gc, username, zap_description,
+ PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+ g_free(zap_description);
+#endif
+
+ /* Construct and send the actual zap command. */
+ zap_string = g_strdup_printf("!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", code);
+
+ if (!msim_send_bm(session, username, zap_string, MSIM_BM_ACTION)) {
+ purple_debug_info("msim_send_zap_from_menu", "msim_send_bm failed: zapping %s with %s",
+ username, zap_string);
+ rc = FALSE;
+ } else {
+ rc = TRUE;
+ }
+
+ g_free(zap_string);
+
+ return rc;
+
+}
+
+/** Zap someone. Callback from msim_blist_node_menu zap menu. */
+static void
+msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr)
+{
+ PurpleBuddy *buddy;
+ PurpleAccount *account;
+ PurpleConnection *gc;
+ MsimSession *session;
+ guint zap;
+
+ if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ /* Only know about buddies for now. */
+ return;
+ }
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+ buddy = (PurpleBuddy *)node;
+
+ /* Find the session */
+ account = buddy->account;
+ gc = purple_account_get_connection(account);
+ session = (MsimSession *)gc->proto_data;
+
+ zap = GPOINTER_TO_INT(zap_num_ptr);
+
+#ifdef MSIM_USE_ATTENTION_API
+ serv_send_attention(session->gc, buddy->name, zap);
+#else
+ g_return_if_fail(msim_send_zap(session, buddy->name, zap));
+#endif
+}
+
+/** Return menu, if any, for a buddy list node. */
+GList *
+msim_blist_node_menu(PurpleBlistNode *node)
+{
+ GList *menu, *zap_menu;
+ GList *types;
+ PurpleMenuAction *act;
+ /* Warning: hardcoded to match that in msim_attention_types. */
+ const gchar *zap_names[10];
+ guint i;
+
+ if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+ /* Only know about buddies for now. */
+ return NULL;
+ }
+
+ /* Names from official client. */
+ types = msim_attention_types(NULL);
+ i = 0;
+ do
+ {
+ MsimAttentionType *attn;
+
+ attn = (MsimAttentionType *)types->data;
+ zap_names[i] = attn->name;
+ ++i;
+ } while ((types = g_list_next(types)));
+
+ menu = zap_menu = NULL;
+
+ /* TODO: get rid of once is accessible directly in GUI */
+ for (i = 0; i < sizeof(zap_names) / sizeof(zap_names[0]); ++i) {
+ act = purple_menu_action_new(zap_names[i], PURPLE_CALLBACK(msim_send_zap_from_menu),
+ GUINT_TO_POINTER(i), NULL);
+ zap_menu = g_list_append(zap_menu, act);
+ }
+
+ act = purple_menu_action_new(_("Zap"), NULL, NULL, zap_menu);
+ menu = g_list_append(menu, act);
+
+ return menu;
+}
+
+/** Process an incoming zap. */
+gboolean
+msim_incoming_zap(MsimSession *session, MsimMessage *msg)
+{
+ gchar *msg_text, *username;
+ gint zap;
+#ifndef MSIM_USE_ATTENTION_API
+ const gchar *zap_past_tense[10];
+ gchar *zap_text;
+
+ zap_past_tense[0] = _("zapped");
+ zap_past_tense[1] = _("whacked");
+ zap_past_tense[2] = _("torched");
+ zap_past_tense[3] = _("smooched");
+ zap_past_tense[4] = _("hugged");
+ zap_past_tense[5] = _("bslapped");
+ zap_past_tense[6] = _("goosed");
+ zap_past_tense[7] = _("hi-fived");
+ zap_past_tense[8] = _("punk'd");
+ zap_past_tense[9] = _("raspberried");
+#endif
+
+ msg_text = msim_msg_get_string(msg, "msg");
+ username = msim_msg_get_string(msg, "_username");
+
+ g_return_val_if_fail(msg_text != NULL, FALSE);
+ g_return_val_if_fail(username != NULL, FALSE);
+
+ g_return_val_if_fail(sscanf(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", &zap) == 1, FALSE);
+
+ zap = CLAMP(zap, 0, 9);
+
+#ifdef MSIM_USE_ATTENTION_API
+ serv_got_attention(session->gc, username, zap);
+#else
+ zap_text = g_strdup_printf(_("*** You have been %s! ***"), zap_past_tense[zap]);
+ serv_got_im(session->gc, username, zap_text,
+ PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_SYSTEM, time(NULL));
+ g_free(zap_text);
+#endif
+
+ g_free(msg_text);
+ g_free(username);
+
+ return TRUE;
+}
+
+
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/myspace/zap.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/myspace/zap.h Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,28 @@
+/* MySpaceIM Protocol Plugin - zap support
+ *
+ * Copyright (C) 2007, Jeff Connelly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MYSPACE_ZAP_H
+#define _MYSPACE_ZAP_H
+
+GList *msim_attention_types(PurpleAccount *acct);
+gboolean msim_send_attention(PurpleConnection *gc, const gchar *username, guint code);
+GList *msim_blist_node_menu(PurpleBlistNode *node);
+gboolean msim_incoming_zap(MsimSession *session, MsimMessage *msg);
+
+#endif /* !_MYSPACE_ZAP_H */
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/oscar/oscar.c
--- a/libpurple/protocols/oscar/oscar.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c Thu Aug 30 00:09:47 2007 +0000
@@ -3529,6 +3529,7 @@
PurpleConnection *gc;
PurpleAccount *account;
PurpleStatus *status;
+ PurplePresence *presence;
const char *message, *itmsurl;
char *tmp;
va_list ap;
@@ -3572,7 +3573,8 @@
aim_srv_setextrainfo(od, FALSE, 0, TRUE, tmp, itmsurl);
g_free(tmp);
- aim_srv_setidle(od, 0);
+ presence = purple_status_get_presence(status);
+ aim_srv_setidle(od, purple_presence_is_idle(presence) ? 0 : time(NULL) - purple_presence_get_idle_time(presence));
if (od->icq) {
aim_icq_reqofflinemsgs(od);
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/qq/sys_msg.c
--- a/libpurple/protocols/qq/sys_msg.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/qq/sys_msg.c Thu Aug 30 00:09:47 2007 +0000
@@ -166,7 +166,7 @@
message = g_strdup_printf(_("You have been added by %s"), from);
_qq_sys_msg_log_write(gc, message, from);
purple_request_action(gc, NULL, message,
- _("Would like to add him?"), 2,
+ _("Would you like to add him?"), 2,
purple_connection_get_account(gc), name, NULL,
g, 3,
_("Cancel"), NULL,
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/sametime/sametime.c
--- a/libpurple/protocols/sametime/sametime.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/sametime/sametime.c Thu Aug 30 00:09:47 2007 +0000
@@ -3183,13 +3183,12 @@
PurpleConnection *gc;
struct mwPurplePluginData *pd;
struct mwAwareIdBlock t = { mwAware_USER, b->name, NULL };
- const char *ret;
-
- gc = b->account->gc;
- pd = gc->proto_data;
-
- ret = mwServiceAware_getText(pd->srvc_aware, &t);
-
+ const char *ret = NULL;
+
+ if ((gc = purple_account_get_connection(b->account))
+ && (pd = gc->proto_data))
+ ret = mwServiceAware_getText(pd->srvc_aware, &t);
+
return (ret && g_utf8_validate(ret, -1, NULL)) ? g_markup_escape_text(ret, -1): NULL;
}
@@ -3242,17 +3241,17 @@
static void mw_prpl_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) {
PurpleConnection *gc;
- struct mwPurplePluginData *pd;
+ struct mwPurplePluginData *pd = NULL;
struct mwAwareIdBlock idb = { mwAware_USER, b->name, NULL };
- const char *message;
+ const char *message = NULL;
const char *status;
char *tmp;
- gc = b->account->gc;
- pd = gc->proto_data;
-
- message = mwServiceAware_getText(pd->srvc_aware, &idb);
+ if ((gc = purple_account_get_connection(b->account))
+ && (pd = gc->proto_data))
+ message = mwServiceAware_getText(pd->srvc_aware, &idb);
+
status = status_text(b);
if(message != NULL && g_utf8_validate(message, -1, NULL) && purple_utf8_strcasecmp(status, message)) {
@@ -3264,7 +3263,7 @@
purple_notify_user_info_add_pair(user_info, _("Status"), status);
}
- if(full) {
+ if(full && pd != NULL) {
tmp = user_supports_text(pd->srvc_aware, b->name);
if(tmp) {
purple_notify_user_info_add_pair(user_info, _("Supports"), tmp);
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/silc/Makefile.mingw
--- a/libpurple/protocols/silc/Makefile.mingw Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/silc/Makefile.mingw Thu Aug 30 00:09:47 2007 +0000
@@ -8,8 +8,8 @@
include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
TARGET = libsilc
-NEEDED_DLLS = $(SILC_TOOLKIT)/lib/silc.dll \
- $(SILC_TOOLKIT)/lib/silcclient.dll
+NEEDED_DLLS = $(SILC_TOOLKIT)/bin/libsilc-1-1-2.dll \
+ $(SILC_TOOLKIT)/bin/libsilcclient-1-1-2.dll
TYPE = PLUGIN
# Static or Plugin...
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/silc/silc.c
--- a/libpurple/protocols/silc/silc.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/silc/silc.c Thu Aug 30 00:09:47 2007 +0000
@@ -1975,9 +1975,6 @@
silc_log_set_debug_string("*client*");
#endif
-#ifdef _WIN32
- silc_net_win32_init();
-#endif
}
PURPLE_INIT_PLUGIN(silc, init_plugin, info);
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/yahoo/yahoo.c
--- a/libpurple/protocols/yahoo/yahoo.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c Thu Aug 30 00:09:47 2007 +0000
@@ -238,14 +238,18 @@
case 8: /* how many online buddies we have */
break;
case 7: /* the current buddy */
- if (name && f) /* update the previous buddy before changing the variables */
- yahoo_update_status(gc, name, f);
- name = pair->value;
- if (name && g_utf8_validate(name, -1, NULL))
+ /* update the previous buddy before changing the variables */
+ if (f) {
+ if (message)
+ yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
+ if (name)
+ yahoo_update_status(gc, name, f);
+ }
+ name = message = NULL;
+ f = NULL;
+ if (pair->value && g_utf8_validate(pair->value, -1, NULL)) {
+ name = pair->value;
f = yahoo_friend_find_or_new(gc, name);
- else {
- f = NULL;
- name = NULL;
}
break;
case 10: /* state */
@@ -568,6 +572,11 @@
purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol);
}
break;
+ case 317: /* Stealth Setting */
+ if (f && (strtol(pair->value, NULL, 10) == 2)) {
+ f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
+ }
+ break;
/* case 242: */ /* this seems related to 241 */
/* break; */
}
@@ -768,7 +777,13 @@
if (bud)
yahoo_update_status(gc, from, f);
}
+ } else if (!g_ascii_strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {
+ PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, gc->account);
+ char *buf = g_strdup_printf(_("%s has sent you a webcam invite, which is not yet supported."), from);
+ purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
+ g_free(buf);
}
+
}
@@ -884,6 +899,8 @@
PurpleConversation *c;
char *username, *str;
+ str = NULL;
+
account = purple_connection_get_account(gc);
c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, im->from);
@@ -892,10 +909,13 @@
else
username = g_markup_escape_text(im->from, -1);
+#ifdef YAHOO_USE_ATTENTION_API
+ serv_got_attention(gc, username, YAHOO_BUZZ);
+#else
str = g_strdup_printf(_("%s just sent you a Buzz!"), username);
purple_conversation_write(c, NULL, str, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, im->time);
-
+#endif
g_free(username);
g_free(str);
g_free(m);
@@ -964,7 +984,7 @@
yahoo_packet_hash(pkt, "ssiii", 1, add_req->id, 5, add_req->who, 241, add_req->protocol,
13, 1, 334, 0);
yahoo_packet_send_and_free(pkt, yd);
-
+
g_free(add_req->id);
g_free(add_req->who);
g_free(add_req->msg);
@@ -976,19 +996,20 @@
struct yahoo_packet *pkt;
char *encoded_msg = NULL;
struct yahoo_data *yd = add_req->gc->proto_data;
-
- if (msg)
+ PurpleAccount *account = purple_connection_get_account(add_req->gc);
+
+ if (msg && *msg)
encoded_msg = yahoo_string_encode(add_req->gc, msg, NULL);
- pkt = yahoo_packet_new(YAHOO_SERVICE_REJECTCONTACT,
+ pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15,
YAHOO_STATUS_AVAILABLE, 0);
- yahoo_packet_hash(pkt, "sss",
- 1, purple_normalize(add_req->gc->account,
- purple_account_get_username(
- purple_connection_get_account(
- add_req->gc))),
- 7, add_req->who,
+ yahoo_packet_hash(pkt, "ssiiis",
+ 1, purple_normalize(account, purple_account_get_username(account)),
+ 5, add_req->who,
+ 13, 2,
+ 334, 0,
+ 97, 1,
14, encoded_msg ? encoded_msg : "");
yahoo_packet_send_and_free(pkt, yd);
@@ -1018,51 +1039,129 @@
add_req);
}
+static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason)
+{
+ char *notify_msg;
+ struct yahoo_data *yd = gc->proto_data;
+
+ if (who == NULL)
+ return;
+
+ if (reason != NULL) {
+ char *msg2 = yahoo_string_decode(gc, reason, FALSE);
+ notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2);
+ g_free(msg2);
+ } else
+ notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list."), who);
+
+ purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg);
+ g_free(notify_msg);
+
+ g_hash_table_remove(yd->friends, who);
+ purple_prpl_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */
+ /* TODO: Shouldn't we remove the buddy from our local list? */
+}
+
static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *pkt) {
- struct yahoo_add_request *add_req;
- char *msg = NULL;
GSList *l = pkt->hash;
-
- add_req = g_new0(struct yahoo_add_request, 1);
- add_req->gc = gc;
-
- while (l) {
- struct yahoo_pair *pair = l->data;
-
- switch (pair->key) {
- case 5:
- add_req->id = g_strdup(pair->value);
- break;
- case 4:
- add_req->who = g_strdup(pair->value);
- break;
- case 241:
- add_req->protocol = strtol(pair->value, NULL, 10);
- break;
- case 14:
- msg = pair->value;
- break;
+ const char *msg = NULL;
+
+ /* Buddy authorized/declined our addition */
+ if (pkt->status == 1) {
+ const char *who = NULL;
+ int response = 0;
+
+ while (l) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+ case 4:
+ who = pair->value;
+ break;
+ case 13:
+ response = strtol(pair->value, NULL, 10);
+ break;
+ case 14:
+ msg = pair->value;
+ break;
+ }
+ l = l->next;
}
- l = l->next;
+
+ if (response == 1) /* Authorized */
+ purple_debug_info("yahoo", "Received authorization from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
+ else if (response == 2) { /* Declined */
+ purple_debug_info("yahoo", "Received authorization decline from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
+ yahoo_buddy_denied_our_add(gc, who, msg);
+ } else
+ purple_debug_error("yahoo", "Received unknown authorization response of %d from buddy '%s'.\n", response, who ? who : "(Unknown Buddy)");
+
}
-
- if (add_req->id) {
- if (msg)
- add_req->msg = yahoo_string_decode(gc, msg, FALSE);
-
- /* DONE! this is almost exactly the same as what MSN does,
- * this should probably be moved to the core.
- */
- purple_account_request_authorization(purple_connection_get_account(gc), add_req->who, add_req->id,
- NULL, add_req->msg, purple_find_buddy(purple_connection_get_account(gc),add_req->who) != NULL,
+ /* Buddy requested authorization to add us. */
+ else if (pkt->status == 3) {
+ struct yahoo_add_request *add_req;
+ const char *firstname = NULL, *lastname = NULL;
+
+ add_req = g_new0(struct yahoo_add_request, 1);
+ add_req->gc = gc;
+
+ while (l) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+ case 4:
+ add_req->who = g_strdup(pair->value);
+ break;
+ case 5:
+ add_req->id = g_strdup(pair->value);
+ break;
+ case 14:
+ msg = pair->value;
+ break;
+ case 216:
+ firstname = pair->value;
+ break;
+ case 241:
+ add_req->protocol = strtol(pair->value, NULL, 10);
+ break;
+ case 254:
+ lastname = pair->value;
+ break;
+
+ }
+ l = l->next;
+ }
+
+ if (add_req->id) {
+ char *alias = NULL;
+ if (msg)
+ add_req->msg = yahoo_string_decode(gc, msg, FALSE);
+
+ if (firstname && lastname)
+ alias = g_strdup_printf("%s %s", firstname, lastname);
+ else if (firstname)
+ alias = g_strdup(firstname);
+ else if (lastname)
+ alias = g_strdup(lastname);
+
+
+ /* DONE! this is almost exactly the same as what MSN does,
+ * this should probably be moved to the core.
+ */
+ purple_account_request_authorization(purple_connection_get_account(gc), add_req->who, add_req->id,
+ alias, add_req->msg, purple_find_buddy(purple_connection_get_account(gc),add_req->who) != NULL,
yahoo_buddy_add_authorize_cb,
yahoo_buddy_add_deny_reason_cb,
- add_req);
+ add_req);
+ g_free(alias);
+ } else {
+ g_free(add_req->id);
+ g_free(add_req->who);
+ /*g_free(add_req->msg);*/
+ g_free(add_req);
+ }
} else {
- g_free(add_req->id);
- g_free(add_req->who);
- /*g_free(add_req->msg);*/
- g_free(add_req);
+ purple_debug_error("yahoo", "Received authorization of unknown status (%d).\n", pkt->status);
}
}
@@ -1113,13 +1212,12 @@
}
}
-static void yahoo_buddy_denied_our_add(PurpleConnection *gc, struct yahoo_packet *pkt)
+/* I have no idea if this every gets called in version 15 */
+static void yahoo_buddy_denied_our_add_old(PurpleConnection *gc, struct yahoo_packet *pkt)
{
char *who = NULL;
char *msg = NULL;
GSList *l = pkt->hash;
- GString *buf = NULL;
- struct yahoo_data *yd = gc->proto_data;
while (l) {
struct yahoo_pair *pair = l->data;
@@ -1135,22 +1233,7 @@
l = l->next;
}
- if (who) {
- char *msg2;
- buf = g_string_sized_new(0);
- if (!msg) {
- g_string_printf(buf, _("%s has (retroactively) denied your request to add them to your list."), who);
- } else {
- msg2 = yahoo_string_decode(gc, msg, FALSE);
- g_string_printf(buf, _("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2);
- g_free(msg2);
- }
- purple_notify_info(gc, NULL, _("Add buddy rejected"), buf->str);
- g_string_free(buf, TRUE);
- g_hash_table_remove(yd->friends, who);
- purple_prpl_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */
- /* TODO: Shouldn't we remove the buddy from our local list? */
- }
+ yahoo_buddy_denied_our_add(gc, who, msg);
}
static void yahoo_process_contact(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -1163,7 +1246,7 @@
yahoo_buddy_added_us(gc, pkt);
break;
case 7:
- yahoo_buddy_denied_our_add(gc, pkt);
+ yahoo_buddy_denied_our_add_old(gc, pkt);
break;
default:
break;
@@ -2265,7 +2348,7 @@
break;
case YAHOO_SERVICE_AUTH_REQ_15:
yahoo_buddy_auth_req_15(gc, pkt);
- break;
+ break;
case YAHOO_SERVICE_ADDBUDDY:
yahoo_process_addbuddy(gc, pkt);
break;
@@ -2846,7 +2929,7 @@
purple_connection_set_display_name(gc, purple_account_get_username(account));
yd->fd = -1;
- yd->txhandler = -1;
+ yd->txhandler = 0;
/* TODO: Is there a good grow size for the buffer? */
yd->txbuf = purple_circ_buffer_new(0);
yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free);
@@ -3734,7 +3817,7 @@
return;
f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
-
+
if (foo)
group = foo->name;
if (!group) {
@@ -3934,17 +4017,24 @@
static PurpleCmdRet
yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data) {
-
PurpleAccount *account = purple_conversation_get_account(c);
+#ifndef YAHOO_USE_ATTENTION_API
const char *username = purple_account_get_username(account);
+#endif
if (*args && args[0])
return PURPLE_CMD_RET_FAILED;
+#ifdef YAHOO_USE_ATTENTION_API
+ serv_send_attention(account->gc, c->name, YAHOO_BUZZ);
+#else
+
purple_debug(PURPLE_DEBUG_INFO, "yahoo",
"Sending on account %s to buddy %s.\n", username, c->name);
purple_conv_im_send(PURPLE_CONV_IM(c), "");
purple_conversation_write(c, NULL, _("You have just sent a Buzz!"), PURPLE_MESSAGE_SYSTEM, time(NULL));
+#endif
+
return PURPLE_CMD_RET_OK;
}
@@ -3994,6 +4084,40 @@
{
return TRUE;
}
+
+gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type)
+{
+ PurpleConversation *c;
+
+ c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+ username, gc->account);
+
+ g_return_val_if_fail(c != NULL, FALSE);
+
+ purple_debug(PURPLE_DEBUG_INFO, "yahoo",
+ "Sending on account %s to buddy %s.\n", username, c->name);
+ purple_conv_im_send_with_flags(PURPLE_CONV_IM(c), "", PURPLE_MESSAGE_INVISIBLE);
+
+ return TRUE;
+}
+
+GList *yahoo_attention_types(PurpleAccount *account)
+{
+ PurpleAttentionType *attn;
+ static GList *list = NULL;
+
+ if (!list) {
+ /* Yahoo only supports one attention command: the 'buzz'. */
+ /* This is index number YAHOO_BUZZ. */
+ attn = g_new0(PurpleAttentionType, 1);
+ attn->name = _("buzz");
+ attn->incoming_description = _("buzzed");
+ attn->outgoing_description = _("Buzzing");
+ list = g_list_append(list, attn);
+ }
+
+ return list;
+}
/************************** Plugin Initialization ****************************/
static void
@@ -4203,9 +4327,15 @@
NULL, /* send_raw */
NULL, /* roomlist_room_serialize */
- /* padding */
+#ifdef YAHOO_USE_ATTENTION_API
+ yahoo_send_attention,
+ yahoo_attention_types,
+#else
NULL,
NULL,
+#endif
+
+ /* padding */
NULL,
NULL
};
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/yahoo/yahoo.h
--- a/libpurple/protocols/yahoo/yahoo.h Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h Thu Aug 30 00:09:47 2007 +0000
@@ -67,6 +67,13 @@
#define YAHOO_STATUS_TYPE_INVISIBLE "invisible"
#define YAHOO_STATUS_TYPE_MOBILE "mobile"
+#define YAHOO_USE_ATTENTION_API
+
+#ifdef YAHOO_USE_ATTENTION_API
+/* Index into attention types list. */
+#define YAHOO_BUZZ 0
+#endif
+
enum yahoo_status {
YAHOO_STATUS_AVAILABLE = 0,
YAHOO_STATUS_BRB,
@@ -213,4 +220,7 @@
gboolean yahoo_privacy_check
(PurpleConnection *gc, const char *who);
+gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type);
+GList *yahoo_attention_types(PurpleAccount *account);
+
#endif /* _YAHOO_H_ */
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/yahoo/yahoo_packet.c
--- a/libpurple/protocols/yahoo/yahoo_packet.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.c Thu Aug 30 00:09:47 2007 +0000
@@ -294,7 +294,7 @@
if (writelen == 0) {
purple_input_remove(yd->txhandler);
- yd->txhandler = -1;
+ yd->txhandler = 0;
return;
}
@@ -355,7 +355,7 @@
len = yahoo_packet_build(pkt, 0, yd->wm, yd->jp, &data);
yahoo_packet_dump(data, len);
- if (yd->txhandler == -1)
+ if (yd->txhandler == 0)
ret = write(yd->fd, data, len);
else {
ret = -1;
@@ -371,7 +371,7 @@
}
if (ret < len) {
- if (yd->txhandler == -1)
+ if (yd->txhandler == 0)
yd->txhandler = purple_input_add(yd->fd, PURPLE_INPUT_WRITE,
yahoo_packet_send_can_write, yd);
purple_circ_buffer_append(yd->txbuf, data + ret, len - ret);
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/protocols/yahoo/yahoochat.c
--- a/libpurple/protocols/yahoo/yahoochat.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoochat.c Thu Aug 30 00:09:47 2007 +0000
@@ -62,8 +62,9 @@
}
pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE,0);
- yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
- 109, purple_connection_get_display_name(gc), 6, "abcde");
+ yahoo_packet_hash(pkt, "ssss", 1, purple_connection_get_display_name(gc),
+ 109, purple_connection_get_display_name(gc), 6, "abcde",
+ 135, "ym8.1.0.415");
yahoo_packet_send_and_free(pkt, yd);
}
@@ -155,7 +156,7 @@
if (members) {
g_hash_table_replace(components, g_strdup("members"), g_strdup(members->str));
}
- if (!yahoo_privacy_check(gc, who) ||
+ if (!yahoo_privacy_check(gc, who) ||
(purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) {
purple_debug_info("yahoo",
"Invite to conference %s from %s has been dropped.\n", room, who);
@@ -640,7 +641,7 @@
GList *w;
purple_debug_misc("yahoo", "leaving conference %s\n", room);
-
+
pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YAHOO_STATUS_AVAILABLE, 0);
yahoo_packet_hash_str(pkt, 1, dn);
@@ -732,7 +733,7 @@
continue;
yahoo_packet_hash(pkt, "ss", 52, name, 53, name);
}
-
+
yahoo_packet_send_and_free(pkt, yd);
g_free(msg2);
}
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/prpl.c
--- a/libpurple/prpl.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/prpl.c Thu Aug 30 00:09:47 2007 +0000
@@ -199,7 +199,10 @@
if(NULL == status)
continue;
- purple_status_set_active(status, FALSE);
+ if (purple_status_is_active(status)) {
+ purple_status_set_active(status, FALSE);
+ purple_blist_update_buddy_status(buddy, status);
+ }
}
g_slist_free(list);
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/prpl.h
--- a/libpurple/prpl.h Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/prpl.h Thu Aug 30 00:09:47 2007 +0000
@@ -30,6 +30,7 @@
#define _PURPLE_PRPL_H_
typedef struct _PurplePluginProtocolInfo PurplePluginProtocolInfo;
+typedef struct _PurpleAttentionType PurpleAttentionType;
/**************************************************************************/
/** @name Basic Protocol Information */
@@ -91,6 +92,20 @@
gboolean secret;
};
+struct _PurpleAttentionType
+{
+ const char *name; /**< Shown in GUI elements */
+ const char *incoming_description; /**< Shown when sent */
+ const char *outgoing_description; /**< Shown when receied */
+ const char *icon_name; /**< Icon to display (optional) */
+
+ /* Reserved fields for future purposes */
+ gpointer _reserved1;
+ gpointer _reserved2;
+ gpointer _reserved3;
+ gpointer _reserved4;
+};
+
/**
* Protocol options
*
@@ -332,8 +347,10 @@
/* room list serialize */
char *(*roomlist_room_serialize)(PurpleRoomlistRoom *room);
- void (*_purple_reserved1)(void);
- void (*_purple_reserved2)(void);
+ /* Attention API for sending & receiving zaps/nudges/buzzes etc. */
+ gboolean (*send_attention)(PurpleConnection *gc, const char *username, guint type);
+ GList *(*attention_types)(PurpleAccount *acct);
+
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
};
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/request.c
--- a/libpurple/request.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/request.c Thu Aug 30 00:09:47 2007 +0000
@@ -1172,7 +1172,7 @@
void *
purple_request_choice(void *handle, const char *title, const char *primary,
- const char *secondary, unsigned int default_value,
+ const char *secondary, int default_value,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
PurpleAccount *account, const char *who, PurpleConversation *conv,
@@ -1197,7 +1197,7 @@
void *
purple_request_choice_varg(void *handle, const char *title,
const char *primary, const char *secondary,
- unsigned int default_value,
+ int default_value,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
PurpleAccount *account, const char *who, PurpleConversation *conv,
@@ -1233,7 +1233,7 @@
void *
purple_request_action(void *handle, const char *title, const char *primary,
- const char *secondary, unsigned int default_action,
+ const char *secondary, int default_action,
PurpleAccount *account, const char *who, PurpleConversation *conv,
void *user_data, size_t action_count, ...)
{
@@ -1254,7 +1254,7 @@
void *
purple_request_action_varg(void *handle, const char *title,
const char *primary, const char *secondary,
- unsigned int default_action,
+ int default_action,
PurpleAccount *account, const char *who, PurpleConversation *conv,
void *user_data, size_t action_count, va_list actions)
{
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/request.h
--- a/libpurple/request.h Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/request.h Thu Aug 30 00:09:47 2007 +0000
@@ -190,13 +190,13 @@
PurpleAccount *account, const char *who, PurpleConversation *conv,
void *user_data);
void *(*request_choice)(const char *title, const char *primary,
- const char *secondary, unsigned int default_value,
+ const char *secondary, int default_value,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
PurpleAccount *account, const char *who, PurpleConversation *conv,
void *user_data, va_list choices);
void *(*request_action)(const char *title, const char *primary,
- const char *secondary, unsigned int default_action,
+ const char *secondary, int default_action,
PurpleAccount *account, const char *who, PurpleConversation *conv,
void *user_data, size_t action_count,
va_list actions);
@@ -1215,7 +1215,7 @@
* @param cancel_cb The callback for the @c Cancel button.
* @param account The PurpleAccount associated with this request, or NULL if none is
* @param who The username of the buddy assocaited with this request, or NULL if none is
- * @param conv The PurpleConversation associated with this request, or NULL if none is
+ * @param conv The PurpleConversation associated with this request, or NULL if none is
* @param user_data The data to pass to the callback.
* @param ... The choices. This argument list should be
* terminated with a NULL parameter.
@@ -1224,7 +1224,7 @@
*/
void *purple_request_choice(void *handle, const char *title,
const char *primary, const char *secondary,
- unsigned int default_value,
+ int default_value,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
PurpleAccount *account, const char *who, PurpleConversation *conv,
@@ -1246,7 +1246,7 @@
* @param cancel_cb The callback for the @c Cancel button.
* @param account The PurpleAccount associated with this request, or NULL if none is
* @param who The username of the buddy assocaited with this request, or NULL if none is
- * @param conv The PurpleConversation associated with this request, or NULL if none is
+ * @param conv The PurpleConversation associated with this request, or NULL if none is
* @param user_data The data to pass to the callback.
* @param choices The choices. This argument list should be
* terminated with a @c NULL parameter.
@@ -1255,7 +1255,7 @@
*/
void *purple_request_choice_varg(void *handle, const char *title,
const char *primary, const char *secondary,
- unsigned int default_value,
+ int default_value,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
PurpleAccount *account, const char *who, PurpleConversation *conv,
@@ -1275,7 +1275,7 @@
* @param default_action The default value.
* @param account The PurpleAccount associated with this request, or NULL if none is
* @param who The username of the buddy assocaited with this request, or NULL if none is
- * @param conv The PurpleConversation associated with this request, or NULL if none is
+ * @param conv The PurpleConversation associated with this request, or NULL if none is
* @param user_data The data to pass to the callback.
* @param action_count The number of actions.
* @param ... A list of actions. These are pairs of
@@ -1290,7 +1290,7 @@
*/
void *purple_request_action(void *handle, const char *title,
const char *primary, const char *secondary,
- unsigned int default_action,
+ int default_action,
PurpleAccount *account, const char *who, PurpleConversation *conv,
void *user_data, size_t action_count, ...);
@@ -1308,7 +1308,7 @@
* @param default_action The default value.
* @param account The PurpleAccount associated with this request, or NULL if none is
* @param who The username of the buddy assocaited with this request, or NULL if none is
- * @param conv The PurpleConversation associated with this request, or NULL if none is
+ * @param conv The PurpleConversation associated with this request, or NULL if none is
* @param user_data The data to pass to the callback.
* @param action_count The number of actions.
* @param actions A list of actions and callbacks.
@@ -1317,7 +1317,7 @@
*/
void *purple_request_action_varg(void *handle, const char *title,
const char *primary, const char *secondary,
- unsigned int default_action,
+ int default_action,
PurpleAccount *account, const char *who, PurpleConversation *conv,
void *user_data, size_t action_count,
va_list actions);
@@ -1338,7 +1338,7 @@
* @param cancel_cb The callback for the @c Cancel button.
* @param account The PurpleAccount associated with this request, or NULL if none is
* @param who The username of the buddy associated with this request, or NULL if none is
- * @param conv The PurpleConversation associated with this request, or NULL if none is
+ * @param conv The PurpleConversation associated with this request, or NULL if none is
* @param user_data The data to pass to the callback.
*
* @return A UI-specific handle.
@@ -1411,7 +1411,7 @@
* @param cancel_cb The callback for the @c Cancel button.
* @param account The PurpleAccount associated with this request, or NULL if none is
* @param who The username of the buddy assocaited with this request, or NULL if none is
- * @param conv The PurpleConversation associated with this request, or NULL if none is
+ * @param conv The PurpleConversation associated with this request, or NULL if none is
* @param user_data The data to pass to the callback.
*
* @return A UI-specific handle.
@@ -1435,7 +1435,7 @@
* @param cancel_cb The callback for the @c Cancel button.
* @param account The PurpleAccount associated with this request, or NULL if none is
* @param who The username of the buddy assocaited with this request, or NULL if none is
- * @param conv The PurpleConversation associated with this request, or NULL if none is
+ * @param conv The PurpleConversation associated with this request, or NULL if none is
* @param user_data The data to pass to the callback.
*
* @return A UI-specific handle.
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/server.c
--- a/libpurple/server.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/server.c Thu Aug 30 00:09:47 2007 +0000
@@ -242,6 +242,108 @@
}
}
+PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code)
+{
+ PurplePlugin *prpl;
+ PurpleAttentionType* attn;
+ GList *(*get_attention_types)(PurpleAccount *);
+
+ g_return_val_if_fail(account != NULL, NULL);
+
+ prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+
+ /* Lookup the attention type in the protocol's attention_types list, if any. */
+ get_attention_types = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->attention_types;
+ if (get_attention_types) {
+ GList *attention_types;
+
+ attention_types = get_attention_types(account);
+ attn = (PurpleAttentionType *)g_list_nth_data(attention_types, type_code);
+ } else {
+ attn = NULL;
+ }
+
+ return attn;
+}
+
+void
+serv_send_attention(PurpleConnection *gc, const char *who, guint type_code)
+{
+ PurpleAttentionType *attn;
+ PurpleMessageFlags flags;
+ PurplePlugin *prpl;
+ PurpleConversation *conv;
+ gboolean (*send_attention)(PurpleConnection *, const char *, guint);
+
+ gchar *description;
+ time_t mtime;
+
+ g_return_if_fail(gc != NULL);
+ g_return_if_fail(who != NULL);
+
+ prpl = purple_find_prpl(purple_account_get_protocol_id(gc->account));
+ send_attention = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention;
+ g_return_if_fail(send_attention != NULL);
+
+ mtime = time(NULL);
+
+ attn = purple_get_attention_type_from_code(gc->account, type_code);
+
+ if (attn && attn->outgoing_description) {
+ description = g_strdup_printf(_("Attention! %s %s."), attn->outgoing_description, who);
+ } else {
+ description = g_strdup(_("Attention!"));
+ }
+
+ flags = PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM;
+
+ purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n",
+ description, who);
+
+ if (!send_attention(gc, who, type_code))
+ return;
+
+ conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, gc->account, who);
+ purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, description, flags, mtime);
+
+ g_free(description);
+}
+
+void
+serv_got_attention(PurpleConnection *gc, const char *who, guint type_code)
+{
+ PurpleMessageFlags flags;
+ PurpleAttentionType *attn;
+ gchar *description;
+ time_t mtime;
+
+ mtime = time(NULL);
+
+ attn = purple_get_attention_type_from_code(gc->account, type_code);
+
+ /* PURPLE_MESSAGE_NOTIFY is for attention messages. */
+ flags = PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_RECV;
+
+ /* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display
+ * it next to the attention command. And if it is null, display a generic icon. */
+
+ if (attn && attn->incoming_description) {
+ description = g_strdup_printf(_("Attention! You have been %s."), attn->incoming_description);
+ } else {
+ description = g_strdup(_("Attention!"));
+ }
+
+ purple_debug_info("server", "serv_got_attention: got '%s' from %s\n",
+ description, who);
+
+ serv_got_im(gc, who, description, flags, mtime);
+
+ /* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */
+
+ g_free(description);
+}
+
+
/*
* Move a buddy from one group to another on server.
*
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/server.h
--- a/libpurple/server.h Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/server.h Thu Aug 30 00:09:47 2007 +0000
@@ -53,6 +53,35 @@
void serv_move_buddy(PurpleBuddy *, PurpleGroup *, PurpleGroup *);
int serv_send_im(PurpleConnection *, const char *, const char *, PurpleMessageFlags flags);
+
+/** Get information about an account's attention commands, from the prpl.
+ *
+ * @return The attention command numbered 'code' from the prpl's attention_types, or NULL.
+ */
+PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code);
+
+/** Send an attention request message.
+ *
+ * @param gc The connection to send the message on.
+ * @param who Whose attention to request.
+ * @param type An index into the prpl's attention_types list determining the type
+ * of the attention request command to send. 0 if prpl only defines one
+ * (for example, Yahoo and MSN), but some protocols define more (MySpaceIM).
+ *
+ * Note that you can't send arbitrary PurpleAttentionType's, because there is
+ * only a fixed set of attention commands.
+ */
+void serv_send_attention(PurpleConnection *gc, const char *who, guint type_code);
+
+/** Process an incoming attention message.
+ *
+ * @param gc The connection that received the attention message.
+ * @param who Who requested your attention.
+ * @param type An index into the prpl's attention_types list determining the type
+ * of the attention request command to send.
+ */
+void serv_got_attention(PurpleConnection *gc, const char *who, guint type_code);
+
void serv_get_info(PurpleConnection *, const char *);
void serv_set_info(PurpleConnection *, const char *);
@@ -68,6 +97,7 @@
void serv_alias_buddy(PurpleBuddy *);
void serv_got_alias(PurpleConnection *gc, const char *who, const char *alias);
+
/**
* Receive a typing message from a remote user. Either PURPLE_TYPING
* or PURPLE_TYPED. If the user has stopped typing then use
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/sslconn.c
--- a/libpurple/sslconn.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/sslconn.c Thu Aug 30 00:09:47 2007 +0000
@@ -24,6 +24,7 @@
*/
#include "internal.h"
+#include "certificate.h"
#include "debug.h"
#include "sslconn.h"
@@ -117,6 +118,9 @@
gsc->connect_cb = func;
gsc->error_cb = error_func;
+ /* TODO: Move this elsewhere */
+ gsc->verifier = purple_certificate_find_verifier("x509","tls_cached");
+
gsc->connect_data = purple_proxy_connect(NULL, account, host, port, purple_ssl_connect_cb, gsc);
if (gsc->connect_data == NULL)
@@ -151,10 +155,37 @@
gsc->inpa = purple_input_add(gsc->fd, PURPLE_INPUT_READ, recv_cb, gsc);
}
+const gchar *
+purple_ssl_strerror(PurpleSslErrorType error)
+{
+ switch(error) {
+ case PURPLE_SSL_CONNECT_FAILED:
+ return _("SSL Connection Failed");
+ case PURPLE_SSL_HANDSHAKE_FAILED:
+ return _("SSL Handshake Failed");
+ case PURPLE_SSL_CERTIFICATE_INVALID:
+ return _("SSL peer presented an invalid certificate");
+ default:
+ purple_debug_warning("sslconn", "Unknown SSL error code %d\n", error);
+ return _("Unknown SSL error");
+ }
+}
+
PurpleSslConnection *
purple_ssl_connect_fd(PurpleAccount *account, int fd,
PurpleSslInputFunction func,
- PurpleSslErrorFunction error_func, void *data)
+ PurpleSslErrorFunction error_func,
+ void *data)
+{
+ return purple_ssl_connect_with_host_fd(account, fd, func, error_func, NULL, data);
+}
+
+PurpleSslConnection *
+purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd,
+ PurpleSslInputFunction func,
+ PurpleSslErrorFunction error_func,
+ const char *host,
+ void *data)
{
PurpleSslConnection *gsc;
PurpleSslOps *ops;
@@ -175,7 +206,13 @@
gsc->connect_cb = func;
gsc->error_cb = error_func;
gsc->fd = fd;
+ if(host)
+ gsc->host = g_strdup(host);
+ /* TODO: Move this elsewhere */
+ gsc->verifier = purple_certificate_find_verifier("x509","tls_cached");
+
+
ops = purple_ssl_get_ops();
ops->connectfunc(gsc);
@@ -231,6 +268,17 @@
return (ops->write)(gsc, data, len);
}
+GList *
+purple_ssl_get_peer_certificates(PurpleSslConnection *gsc)
+{
+ PurpleSslOps *ops;
+
+ g_return_val_if_fail(gsc != NULL, NULL);
+
+ ops = purple_ssl_get_ops();
+ return (ops->get_peer_certificates)(gsc);
+}
+
void
purple_ssl_set_ops(PurpleSslOps *ops)
{
@@ -246,8 +294,12 @@
void
purple_ssl_init(void)
{
- /* This doesn't do anything at the moment. All the actual init work
- * is handled by purple_ssl_is_supported upon demand. */
+ /* Although purple_ssl_is_supported will do the initialization on
+ command, SSL plugins tend to register CertificateSchemes as well
+ as providing SSL ops. */
+ if (!ssl_init()) {
+ purple_debug_error("sslconn", "Unable to initialize SSL.\n");
+ }
}
void
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/sslconn.h
--- a/libpurple/sslconn.h Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/sslconn.h Thu Aug 30 00:09:47 2007 +0000
@@ -25,6 +25,7 @@
#ifndef _PURPLE_SSLCONN_H_
#define _PURPLE_SSLCONN_H_
+#include "certificate.h"
#include "proxy.h"
#define PURPLE_SSL_DEFAULT_PORT 443
@@ -32,7 +33,8 @@
typedef enum
{
PURPLE_SSL_HANDSHAKE_FAILED = 1,
- PURPLE_SSL_CONNECT_FAILED = 2
+ PURPLE_SSL_CONNECT_FAILED = 2,
+ PURPLE_SSL_CERTIFICATE_INVALID = 3
} PurpleSslErrorType;
typedef struct _PurpleSslConnection PurpleSslConnection;
@@ -69,6 +71,9 @@
/** Internal connection data managed by the SSL backend (GnuTLS/LibNSS/whatever) */
void *private_data;
+
+ /** Verifier to use in authenticating the peer */
+ PurpleCertificateVerifier *verifier;
};
/**
@@ -107,8 +112,17 @@
* @return The number of bytes written (may be less than len) or <0 on error
*/
size_t (*write)(PurpleSslConnection *gsc, const void *data, size_t len);
-
- void (*_purple_reserved1)(void);
+ /** Obtains the certificate chain provided by the peer
+ *
+ * @param gsc Connection context
+ * @return A newly allocated list containing the certificates
+ * the peer provided.
+ * @see PurpleCertificate
+ * @todo Decide whether the ordering of certificates in this
+ * list can be guaranteed.
+ */
+ GList * (* get_peer_certificates)(PurpleSslConnection * gsc);
+
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
@@ -131,6 +145,14 @@
gboolean purple_ssl_is_supported(void);
/**
+ * Returns a human-readable string for an SSL error
+ *
+ * @param error Error code
+ * @return Human-readable error explanation
+ */
+const gchar * purple_ssl_strerror(PurpleSslErrorType error);
+
+/**
* Makes a SSL connection to the specified host and port. The caller
* should keep track of the returned value and use it to cancel the
* connection, if needed.
@@ -154,6 +176,7 @@
/**
* Makes a SSL connection using an already open file descriptor.
+ * DEPRECATED. Use purple_ssl_connect_with_host_fd instead.
*
* @param account The account making the connection.
* @param fd The file descriptor.
@@ -166,7 +189,25 @@
PurpleSslConnection *purple_ssl_connect_fd(PurpleAccount *account, int fd,
PurpleSslInputFunction func,
PurpleSslErrorFunction error_func,
- void *data);
+ void *data);
+
+/**
+ * Makes a SSL connection using an already open file descriptor.
+ *
+ * @param account The account making the connection.
+ * @param fd The file descriptor.
+ * @param func The SSL input handler function.
+ * @param error_func The SSL error handler function.
+ * @param host The hostname of the other peer (to verify the CN)
+ * @param data User-defined data.
+ *
+ * @return The SSL connection handle.
+ */
+PurpleSslConnection *purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd,
+ PurpleSslInputFunction func,
+ PurpleSslErrorFunction error_func,
+ const char *host,
+ void *data);
/**
* Adds an input watcher for the specified SSL connection.
@@ -208,6 +249,16 @@
*/
size_t purple_ssl_write(PurpleSslConnection *gsc, const void *buffer, size_t len);
+/**
+ * Obtains the peer's presented certificates
+ *
+ * @param gsc The SSL connection handle
+ *
+ * @return The peer certificate chain, in the order of certificate, issuer,
+ * issuer's issuer, etc. NULL if no certificates have been provided,
+ */
+GList * purple_ssl_get_peer_certificates(PurpleSslConnection *gsc);
+
/*@}*/
/**************************************************************************/
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/util.c
--- a/libpurple/util.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/util.c Thu Aug 30 00:09:47 2007 +0000
@@ -1374,7 +1374,7 @@
g_string_free(cdata, TRUE);
cdata = NULL;
}
-
+
}
if(tags == tag)
break;
@@ -1425,7 +1425,7 @@
ALLOW_TAG("strong");
ALLOW_TAG("ul");
-
+
/* we skip
because it's not legal in XHTML-IM. However,
* we still want to send something sensible, so we put a
* linebreak in its place.
also needs special handling
@@ -2539,13 +2539,11 @@
* people's settings if there is a problem writing the new values.
*/
gboolean
-purple_util_write_data_to_file(const char *filename, const char *data, size_t size)
+purple_util_write_data_to_file(const char *filename, const char *data, gssize size)
{
const char *user_dir = purple_user_dir();
- gchar *filename_temp, *filename_full;
- FILE *file;
- size_t real_size, byteswritten;
- struct stat st;
+ gchar *filename_full;
+ gboolean ret = FALSE;
g_return_val_if_fail(user_dir != NULL, FALSE);
@@ -2564,6 +2562,25 @@
}
filename_full = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", user_dir, filename);
+
+ ret = purple_util_write_data_to_file_absolute(filename_full,
+ data,size);
+
+ g_free(filename_full);
+ return ret;
+}
+
+gboolean
+purple_util_write_data_to_file_absolute(const char *filename_full, const char *data, size_t size)
+{
+ gchar *filename_temp;
+ FILE *file;
+ size_t real_size, byteswritten;
+ struct stat st;
+
+ purple_debug_info("util", "Writing file %s\n",
+ filename_full);
+
filename_temp = g_strdup_printf("%s.save", filename_full);
/* Remove an old temporary file, if one exists */
@@ -2571,8 +2588,9 @@
{
if (g_unlink(filename_temp) == -1)
{
- purple_debug_error("util", "Error removing old file %s: %s\n",
- filename_temp, strerror(errno));
+ purple_debug_error("util", "Error removing old file "
+ "%s: %s\n",
+ filename_temp, strerror(errno));
}
}
@@ -2580,9 +2598,9 @@
file = g_fopen(filename_temp, "wb");
if (file == NULL)
{
- purple_debug_error("util", "Error opening file %s for writing: %s\n",
- filename_temp, strerror(errno));
- g_free(filename_full);
+ purple_debug_error("util", "Error opening file %s for "
+ "writing: %s\n",
+ filename_temp, strerror(errno));
g_free(filename_temp);
return FALSE;
}
@@ -2595,8 +2613,7 @@
if (fclose(file) != 0)
{
purple_debug_error("util", "Error closing file %s: %s\n",
- filename_temp, strerror(errno));
- g_free(filename_full);
+ filename_temp, strerror(errno));
g_free(filename_temp);
return FALSE;
}
@@ -2604,10 +2621,11 @@
/* Ensure the file is the correct size */
if (byteswritten != real_size)
{
- purple_debug_error("util", "Error writing to file %s: Wrote %" G_GSIZE_FORMAT " bytes "
- "but should have written %" G_GSIZE_FORMAT "; is your disk full?\n",
- filename_temp, byteswritten, real_size);
- g_free(filename_full);
+ purple_debug_error("util", "Error writing to file %s: Wrote %"
+ G_GSIZE_FORMAT " bytes "
+ "but should have written %" G_GSIZE_FORMAT
+ "; is your disk full?\n",
+ filename_temp, byteswritten, real_size);
g_free(filename_temp);
return FALSE;
}
@@ -2615,9 +2633,9 @@
if ((g_stat(filename_temp, &st) == -1) || (st.st_size != real_size))
{
purple_debug_error("util", "Error writing data to file %s: "
- "Incomplete file written; is your disk full?\n",
- filename_temp);
- g_free(filename_full);
+ "Incomplete file written; is your disk "
+ "full?\n",
+ filename_temp);
g_free(filename_temp);
return FALSE;
}
@@ -2635,10 +2653,10 @@
if (g_rename(filename_temp, filename_full) == -1)
{
purple_debug_error("util", "Error renaming %s to %s: %s\n",
- filename_temp, filename_full, strerror(errno));
+ filename_temp, filename_full,
+ strerror(errno));
}
- g_free(filename_full);
g_free(filename_temp);
return TRUE;
@@ -4305,7 +4323,7 @@
}
}
-gboolean purple_message_meify(char *message, size_t len)
+gboolean purple_message_meify(char *message, gssize len)
{
char *c;
gboolean inside_html = FALSE;
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/util.h
--- a/libpurple/util.h Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/util.h Thu Aug 30 00:09:47 2007 +0000
@@ -586,7 +586,27 @@
* @return TRUE if the file was written successfully. FALSE otherwise.
*/
gboolean purple_util_write_data_to_file(const char *filename, const char *data,
- size_t size);
+ gssize size);
+
+/**
+ * Write data to a file using the absolute path.
+ *
+ * This exists for Glib backwards compatibility reasons.
+ *
+ * @param filename_full Filename to write to
+ * @param data A null-terminated string of data to write.
+ * @param size The size of the data to save. If data is
+ * null-terminated you can pass in -1.
+ *
+ * @return TRUE if the file was written successfully. FALSE otherwise.
+ *
+ * @todo Remove this function (use g_file_set_contents instead) when 3.0.0
+ * rolls around.
+ * @see purple_util_write_data_to_file()
+ *
+ */
+gboolean
+purple_util_write_data_to_file_absolute(const char *filename_full, const char *data, size_t size);
/**
* Read the contents of a given file and parse the results into an
@@ -1113,7 +1133,7 @@
* @return TRUE if it starts with "/me ", and it has been removed, otherwise
* FALSE
*/
-gboolean purple_message_meify(char *message, size_t len);
+gboolean purple_message_meify(char *message, gssize len);
/**
* Removes the underscore characters from a string used identify the mnemonic
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/value.h
--- a/libpurple/value.h Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/value.h Thu Aug 30 00:09:47 2007 +0000
@@ -77,7 +77,8 @@
PURPLE_SUBTYPE_SAVEDSTATUS,
PURPLE_SUBTYPE_XMLNODE,
PURPLE_SUBTYPE_USERINFO,
- PURPLE_SUBTYPE_STORED_IMAGE
+ PURPLE_SUBTYPE_STORED_IMAGE,
+ PURPLE_SUBTYPE_CERTIFICATEPOOL
} PurpleSubType;
/**
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/win32/global.mak
--- a/libpurple/win32/global.mak Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/win32/global.mak Thu Aug 30 00:09:47 2007 +0000
@@ -11,7 +11,7 @@
# Locations of our various dependencies
WIN32_DEV_TOP ?= $(PIDGIN_TREE_TOP)/../win32-dev
ASPELL_TOP ?= $(WIN32_DEV_TOP)/aspell-dev-0-50-3-3
-GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.6
+GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.11
GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0
GTK_BIN ?= $(GTK_TOP)/bin
BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK
@@ -20,7 +20,7 @@
NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4
NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.11.4
PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl58
-SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.0.2
+SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.2
TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5
# Where we installing this stuff to?
diff -r 7c84cbb57972 -r a77432fe2f3b libpurple/xmlnode.c
--- a/libpurple/xmlnode.c Wed Aug 29 23:14:46 2007 +0000
+++ b/libpurple/xmlnode.c Thu Aug 30 00:09:47 2007 +0000
@@ -272,6 +272,8 @@
if(NULL != node->parent) {
if(node->parent->child == node) {
node->parent->child = node->next;
+ if (node->parent->lastchild == node)
+ node->parent->lastchild = node->next;
} else {
xmlnode *prev = node->parent->child;
while(prev && prev->next != node) {
@@ -279,6 +281,8 @@
}
if(prev) {
prev->next = node->next;
+ if (node->parent->lastchild == node)
+ node->parent->lastchild = prev;
}
}
}
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/Makefile.am
--- a/pidgin/Makefile.am Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/Makefile.am Thu Aug 30 00:09:47 2007 +0000
@@ -81,6 +81,7 @@
gtkcellrendererprogress.c \
gtkcellview.c \
gtkcellviewmenuitem.c \
+ gtkcertmgr.c \
gtkconn.c \
gtkconv.c \
gtkdebug.c \
@@ -128,6 +129,7 @@
gtkcellviewmenuitem.h \
gtkcellview.h \
gtkcellviewmenuitem.h \
+ gtkcertmgr.h \
pidgincombobox.h \
gtkconn.h \
gtkconv.h \
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/Makefile.mingw
--- a/pidgin/Makefile.mingw Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/Makefile.mingw Thu Aug 30 00:09:47 2007 +0000
@@ -56,10 +56,11 @@
PIDGIN_C_SRC = \
gtkaccount.c \
gtkblist.c \
+ gtkcertmgr.c \
+ gtkcellrendererexpander.c \
+ gtkcellrendererprogress.c \
gtkconn.c \
gtkconv.c \
- gtkcellrendererexpander.c \
- gtkcellrendererprogress.c \
gtkdebug.c \
gtkdialogs.c \
gtkdnd-hints.c \
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkaccount.c
--- a/pidgin/gtkaccount.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkaccount.c Thu Aug 30 00:09:47 2007 +0000
@@ -136,9 +136,6 @@
GtkWidget *proxy_user_entry;
GtkWidget *proxy_pass_entry;
- /* Are we registering? */
- gboolean registering;
-
} AccountPrefsDialog;
typedef struct
@@ -272,7 +269,8 @@
add_user_options(dialog, dialog->top_vbox);
add_protocol_options(dialog, dialog->bottom_vbox);
- if (!dialog->prpl_info || !dialog->prpl_info->register_user) {
+ if (!dialog->prpl_info || !dialog->prpl_info->register_user ||
+ g_object_get_data(G_OBJECT(item), "fake")) {
gtk_widget_hide(dialog->register_button);
} else {
if (dialog->prpl_info != NULL &&
@@ -1146,7 +1144,7 @@
account_win_destroy_cb(NULL, NULL, dialog);
}
-static PurpleAccount*
+static void
ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
{
PurpleProxyInfo *proxy_info = NULL;
@@ -1154,22 +1152,58 @@
const char *value;
char *username;
char *tmp;
- gboolean new = FALSE, icon_change = FALSE;
+ gboolean new_acct = FALSE, icon_change = FALSE;
PurpleAccount *account;
PurplePluginProtocolInfo *prpl_info;
+ /* Build the username string. */
+ username = g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->screenname_entry)));
+
+ if (dialog->prpl_info != NULL)
+ {
+ for (l = dialog->prpl_info->user_splits,
+ l2 = dialog->user_split_entries;
+ l != NULL && l2 != NULL;
+ l = l->next, l2 = l2->next)
+ {
+ PurpleAccountUserSplit *split = l->data;
+ GtkEntry *entry = l2->data;
+ char sep[2] = " ";
+
+ value = gtk_entry_get_text(entry);
+
+ *sep = purple_account_user_split_get_separator(split);
+
+ tmp = g_strconcat(username, sep,
+ (*value ? value :
+ purple_account_user_split_get_default_value(split)),
+ NULL);
+
+ g_free(username);
+ username = tmp;
+ }
+ }
+
if (dialog->account == NULL)
{
- const char *screenname;
+ if (purple_accounts_find(username, dialog->protocol_id) != NULL) {
+ purple_debug_warning("gtkaccount", "Trying to add a duplicate %s account (%s).\n",
+ dialog->protocol_id, username);
+
+ purple_notify_error(NULL, NULL, _("Unable to save new account"),
+ _("An account already exists with the specified criteria."));
+
+ g_free(username);
+ return;
+ }
if (purple_accounts_get_all() == NULL) {
/* We're adding our first account. Be polite and show the buddy list */
purple_blist_set_visible(TRUE);
}
- screenname = gtk_entry_get_text(GTK_ENTRY(dialog->screenname_entry));
- account = purple_account_new(screenname, dialog->protocol_id);
- new = TRUE;
+ account = purple_account_new(username, dialog->protocol_id);
+ new_acct = TRUE;
}
else
{
@@ -1193,7 +1227,7 @@
{
const char *filename;
- if (new || purple_account_get_bool(account, "use-global-buddyicon", TRUE) ==
+ if (new_acct || purple_account_get_bool(account, "use-global-buddyicon", TRUE) ==
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)))
{
icon_change = TRUE;
@@ -1246,40 +1280,11 @@
* the account editor (but has not checked the 'save' box), then we
* don't want to prompt them.
*/
- if ((purple_account_get_remember_password(account) || new) && (*value != '\0'))
+ if ((purple_account_get_remember_password(account) || new_acct) && (*value != '\0'))
purple_account_set_password(account, value);
else
purple_account_set_password(account, NULL);
- /* Build the username string. */
- username =
- g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->screenname_entry)));
-
- if (dialog->prpl_info != NULL)
- {
- for (l = dialog->prpl_info->user_splits,
- l2 = dialog->user_split_entries;
- l != NULL && l2 != NULL;
- l = l->next, l2 = l2->next)
- {
- PurpleAccountUserSplit *split = l->data;
- GtkEntry *entry = l2->data;
- char sep[2] = " ";
-
- value = gtk_entry_get_text(entry);
-
- *sep = purple_account_user_split_get_separator(split);
-
- tmp = g_strconcat(username, sep,
- (*value ? value :
- purple_account_user_split_get_default_value(split)),
- NULL);
-
- g_free(username);
- username = tmp;
- }
- }
-
purple_account_set_username(account, username);
g_free(username);
@@ -1388,13 +1393,15 @@
}
/* If this is a new account, add it to our list */
- if (new)
+ if (new_acct)
purple_accounts_add(account);
else
purple_signal_emit(pidgin_account_get_handle(), "account-modified", account);
/* If this is a new account, then sign on! */
- if (new && !dialog->registering) {
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->register_button))) {
+ purple_account_register(account);
+ } else if (new_acct) {
const PurpleSavedStatus *saved_status;
saved_status = purple_savedstatus_get_current();
@@ -1407,22 +1414,8 @@
/* We no longer need the data from the dialog window */
account_win_destroy_cb(NULL, NULL, dialog);
- return account;
}
-static void
-register_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
-{
- PurpleAccount *account;
-
- dialog->registering = TRUE;
-
- account = ok_account_prefs_cb(NULL, dialog);
-
- purple_account_register(account);
-}
-
-
static const GtkTargetEntry dnd_targets[] = {
{"text/plain", 0, 0},
{"text/uri-list", 0, 1},
@@ -1501,6 +1494,18 @@
add_login_options(dialog, vbox);
add_user_options(dialog, vbox);
+ button = gtk_check_button_new_with_label(_("Create this new account on the server"));
+ gtk_box_pack_start(GTK_BOX(main_vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+ dialog->register_button = button;
+ if (dialog->account == NULL)
+ gtk_widget_set_sensitive(button, FALSE);
+
+ if (!dialog->prpl_info || !dialog->prpl_info->register_user)
+ gtk_widget_hide(button);
+
+
+
/* Setup the page with 'Advanced'. */
dialog->bottom_vbox = dbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
gtk_container_set_border_width(GTK_CONTAINER(dbox), PIDGIN_HIG_BORDER);
@@ -1519,22 +1524,6 @@
gtk_box_pack_end(GTK_BOX(main_vbox), bbox, FALSE, TRUE, 0);
gtk_widget_show(bbox);
- /* Register button */
- button = gtk_button_new_with_label(_("Register"));
- gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
- gtk_widget_show(button);
-
- g_signal_connect(G_OBJECT(button), "clicked",
- G_CALLBACK(register_account_prefs_cb), dialog);
-
- dialog->register_button = button;
-
- if (dialog->account == NULL)
- gtk_widget_set_sensitive(button, FALSE);
-
- if (!dialog->prpl_info || !dialog->prpl_info->register_user)
- gtk_widget_hide(button);
-
/* Cancel button */
button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
@@ -2416,13 +2405,6 @@
g_free(accounts_window);
accounts_window = NULL;
-
- /* See if we're the main window here. */
- if (PIDGIN_BLIST(purple_get_blist())->window == NULL &&
- purple_connections_get_all() == NULL) {
-
- purple_core_quit();
- }
}
static void
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkblist.c
--- a/pidgin/gtkblist.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkblist.c Thu Aug 30 00:09:47 2007 +0000
@@ -42,6 +42,7 @@
#include "gtkaccount.h"
#include "gtkblist.h"
#include "gtkcellrendererexpander.h"
+#include "gtkcertmgr.h"
#include "gtkconv.h"
#include "gtkdebug.h"
#include "gtkdialogs.h"
@@ -331,8 +332,10 @@
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name,
chat->account);
- if (conv != NULL)
+ if (conv != NULL) {
+ pidgin_conv_attach_to_conversation(conv);
purple_conversation_present(conv);
+ }
serv_join_chat(chat->account->gc, chat->components);
g_free(chat_name);
@@ -1538,6 +1541,12 @@
pidgin_clear_cursor(gtkblist->window);
}
+static void pidgin_blist_show_protocol_icons_cb(gpointer data, guint action, GtkWidget *item)
+{
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons",
+ gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)));
+}
+
static void pidgin_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item)
{
pidgin_set_cursor(gtkblist->window, GDK_WATCH);
@@ -2528,7 +2537,8 @@
GValue val;
struct _pidgin_blist_node *gtknode;
- if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y, &path, NULL, NULL, NULL))
+ if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y + (gtkblist->tip_rect.height/2),
+ &path, NULL, NULL, NULL))
return FALSE;
gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
val.g_type = 0;
@@ -2585,7 +2595,8 @@
PurpleBlistNode *node;
GValue val;
- if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y, &path, NULL, NULL, NULL))
+ if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y + (gtkblist->tip_rect.height/2),
+ &path, NULL, NULL, NULL))
return FALSE;
gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
val.g_type = 0;
@@ -2853,10 +2864,12 @@
{ N_("/Buddies/Get User _Info..."), "I", pidgin_dialogs_info, 0, "", PIDGIN_STOCK_TOOLBAR_USER_INFO },
{ N_("/Buddies/View User _Log..."), "L", pidgin_dialogs_log, 0, "- ", NULL },
{ "/Buddies/sep1", NULL, NULL, 0, "", NULL },
- { N_("/Buddies/Show _Offline Buddies"), NULL, pidgin_blist_edit_mode_cb, 1, "", NULL },
- { N_("/Buddies/Show _Empty Groups"), NULL, pidgin_blist_show_empty_groups_cb, 1, "", NULL },
- { N_("/Buddies/Show Buddy _Details"), NULL, pidgin_blist_buddy_details_cb, 1, "", NULL },
- { N_("/Buddies/Show Idle _Times"), NULL, pidgin_blist_show_idle_time_cb, 1, "", NULL },
+ { N_("/Buddies/Show"), NULL, NULL, 0, "", NULL},
+ { N_("/Buddies/Show/Show _Offline Buddies"), NULL, pidgin_blist_edit_mode_cb, 1, "", NULL },
+ { N_("/Buddies/Show/Show _Empty Groups"), NULL, pidgin_blist_show_empty_groups_cb, 1, "", NULL },
+ { N_("/Buddies/Show/Show Buddy _Details"), NULL, pidgin_blist_buddy_details_cb, 1, "", NULL },
+ { N_("/Buddies/Show/Show Idle _Times"), NULL, pidgin_blist_show_idle_time_cb, 1, "", NULL },
+ { N_("/Buddies/Show/Show _Protocol Icons"), NULL, pidgin_blist_show_protocol_icons_cb, 1, "", NULL },
{ N_("/Buddies/_Sort Buddies"), NULL, NULL, 0, "", NULL },
{ "/Buddies/sep2", NULL, NULL, 0, "", NULL },
{ N_("/Buddies/_Add Buddy..."), "B", pidgin_blist_add_buddy_cb, 0, "", GTK_STOCK_ADD },
@@ -2872,6 +2885,7 @@
/* Tools */
{ N_("/_Tools"), NULL, NULL, 0, "", NULL },
{ N_("/Tools/Buddy _Pounces"), NULL, pidgin_pounces_manager_show, 0, "
- ", NULL },
+ { N_("/Tools/_Certificates"), NULL, pidgin_certmgr_show, 0, "
- ", NULL },
{ N_("/Tools/Plu_gins"), "U", pidgin_plugin_dialog_show, 0, "", PIDGIN_STOCK_TOOLBAR_PLUGINS },
{ N_("/Tools/Pr_eferences"), "P", pidgin_prefs_show, 0, "", GTK_STOCK_PREFERENCES },
{ N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "
- ", NULL },
@@ -3141,10 +3155,11 @@
return ret;
}
- if (((struct _pidgin_blist_node*)(node->parent->ui_data))->contact_expanded)
+ if (((struct _pidgin_blist_node*)(node->parent->ui_data))->contact_expanded) {
+ if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"))
+ return NULL;
return pidgin_create_prpl_icon(((PurpleBuddy*)node)->account, PIDGIN_PRPL_ICON_SMALL);
- } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
- return pidgin_create_prpl_icon(((PurpleChat*)node)->account, PIDGIN_PRPL_ICON_SMALL);
+ }
} else {
return NULL;
}
@@ -4403,7 +4418,10 @@
G_TYPE_BOOLEAN, /* Contact expander */
G_TYPE_BOOLEAN, /* Contact expander visible */
GDK_TYPE_PIXBUF, /* Emblem */
- G_TYPE_BOOLEAN); /* Emblem visible */
+ G_TYPE_BOOLEAN, /* Emblem visible */
+ GDK_TYPE_PIXBUF, /* Protocol icon */
+ G_TYPE_BOOLEAN /* Protocol visible */
+ );
gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel));
@@ -4518,6 +4536,17 @@
"visible", EMBLEM_VISIBLE_COLUMN, NULL);
rend = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, rend, FALSE);
+ gtk_tree_view_column_set_attributes(column, rend,
+ "pixbuf", PROTOCOL_ICON_COLUMN,
+ "visible", PROTOCOL_ICON_VISIBLE_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+ "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+ NULL);
+ g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL);
+
+ rend = gtk_cell_renderer_pixbuf_new();
g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
gtk_tree_view_column_pack_start(column, rend, FALSE);
gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN,
@@ -4563,21 +4592,24 @@
/* set the Show Offline Buddies option. must be done
* after the treeview or faceprint gets mad. -Robot101
*/
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Offline Buddies"))),
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show/Show Offline Buddies"))),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies"));
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Empty Groups"))),
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show/Show Empty Groups"))),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups"));
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Tools/Mute Sounds"))),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/sound/mute"));
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Buddy Details"))),
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show/Show Buddy Details"))),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"));
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Idle Times"))),
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show/Show Idle Times"))),
purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time"));
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show/Show Protocol Icons"))),
+ purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"));
+
if(!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), "none"))
gtk_widget_set_sensitive(gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Mute Sounds")), FALSE);
@@ -4607,6 +4639,8 @@
_prefs_change_redo_list, NULL);
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/show_offline_buddies",
_prefs_change_redo_list, NULL);
+ purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/show_protocol_icons",
+ _prefs_change_redo_list, NULL);
/* sorting */
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/sort_type",
@@ -5038,6 +5072,8 @@
BUDDY_ICON_VISIBLE_COLUMN, biglist,
EMBLEM_COLUMN, emblem,
EMBLEM_VISIBLE_COLUMN, emblem,
+ PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(buddy->account, PIDGIN_PRPL_ICON_SMALL),
+ PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"),
BGCOLOR_COLUMN, NULL,
CONTACT_EXPANDER_COLUMN, NULL,
CONTACT_EXPANDER_VISIBLE_COLUMN, expanded,
@@ -5198,6 +5234,8 @@
BUDDY_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"),
EMBLEM_COLUMN, emblem,
EMBLEM_VISIBLE_COLUMN, emblem != NULL,
+ PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL),
+ PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"),
NAME_COLUMN, mark,
GROUP_EXPANDER_VISIBLE_COLUMN, FALSE,
-1);
@@ -6091,6 +6129,7 @@
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups", FALSE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time", TRUE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies", FALSE);
+ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons", FALSE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", FALSE);
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/list_maximized", FALSE);
purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/sort_type", "alphabetical");
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkblist.h
--- a/pidgin/gtkblist.h Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkblist.h Thu Aug 30 00:09:47 2007 +0000
@@ -43,6 +43,8 @@
CONTACT_EXPANDER_VISIBLE_COLUMN,
EMBLEM_COLUMN,
EMBLEM_VISIBLE_COLUMN,
+ PROTOCOL_ICON_COLUMN,
+ PROTOCOL_ICON_VISIBLE_COLUMN,
BLIST_COLUMNS
};
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkcertmgr.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkcertmgr.c Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,679 @@
+/*
+ * @file gtkcertmgr.c GTK+ Certificate Manager API
+ * @ingroup pidgin
+ *
+ * pidgin
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include
+
+#include "core.h"
+#include "internal.h"
+#include "pidgin.h"
+#include "pidginstock.h"
+
+#include "certificate.h"
+#include "debug.h"
+#include "notify.h"
+#include "request.h"
+
+#include "gtkblist.h"
+#include "gtkutils.h"
+
+#include "gtkcertmgr.h"
+
+/*****************************************************************************
+ * X.509 tls_peers management interface *
+ *****************************************************************************/
+
+typedef struct {
+ GtkWidget *mgmt_widget;
+ GtkTreeView *listview;
+ GtkTreeSelection *listselect;
+ GtkWidget *importbutton;
+ GtkWidget *exportbutton;
+ GtkWidget *infobutton;
+ GtkWidget *deletebutton;
+ PurpleCertificatePool *tls_peers;
+} tls_peers_mgmt_data;
+
+tls_peers_mgmt_data *tpm_dat = NULL;
+
+/* Columns
+ See http://developer.gnome.org/doc/API/2.0/gtk/TreeWidget.html */
+enum
+{
+ TPM_HOSTNAME_COLUMN,
+ TPM_N_COLUMNS
+};
+
+static void
+tls_peers_mgmt_destroy(GtkWidget *mgmt_widget, gpointer data)
+{
+ purple_debug_info("certmgr",
+ "tls peers self-destructs\n");
+
+ purple_signals_disconnect_by_handle(tpm_dat);
+ purple_request_close_with_handle(tpm_dat);
+ g_free(tpm_dat); tpm_dat = NULL;
+}
+
+static void
+tls_peers_mgmt_repopulate_list(void)
+{
+ GtkTreeView *listview = tpm_dat->listview;
+ PurpleCertificatePool *tls_peers;
+ GList *idlist, *l;
+
+ GtkListStore *store = GTK_LIST_STORE(
+ gtk_tree_view_get_model(GTK_TREE_VIEW(listview)));
+
+ /* First, delete everything in the list */
+ gtk_list_store_clear(store);
+
+ /* Locate the "tls_peers" pool */
+ tls_peers = purple_certificate_find_pool("x509", "tls_peers");
+ g_return_if_fail(tls_peers);
+
+ /* Grab the loaded certificates */
+ idlist = purple_certificate_pool_get_idlist(tls_peers);
+
+ /* Populate the listview */
+ for (l = idlist; l; l = l->next) {
+ GtkTreeIter iter;
+ gtk_list_store_append(store, &iter);
+
+ gtk_list_store_set(GTK_LIST_STORE(store), &iter,
+ TPM_HOSTNAME_COLUMN, l->data,
+ -1);
+ }
+ purple_certificate_pool_destroy_idlist(idlist);
+}
+
+static void
+tls_peers_mgmt_mod_cb(PurpleCertificatePool *pool, const gchar *id, gpointer data)
+{
+ g_assert (pool == tpm_dat->tls_peers);
+
+ tls_peers_mgmt_repopulate_list();
+}
+
+static void
+tls_peers_mgmt_select_chg_cb(GtkTreeSelection *ignored, gpointer data)
+{
+ GtkTreeSelection *select = tpm_dat->listselect;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ /* See if things are selected */
+ if (gtk_tree_selection_get_selected(select, &model, &iter)) {
+ /* Enable buttons if something is selected */
+ gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat->exportbutton), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat->infobutton), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat->deletebutton), TRUE);
+ } else {
+ /* Otherwise, disable them */
+ gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat->exportbutton), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat->infobutton), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(tpm_dat->deletebutton), FALSE);
+
+ }
+}
+
+static void
+tls_peers_mgmt_import_ok2_cb(gpointer data, const char *result)
+{
+ PurpleCertificate *crt = (PurpleCertificate *) data;
+ const char *id = result;
+
+ /* TODO: Perhaps prompt if you're overwriting a cert? */
+
+ /* Drop the certificate into the pool */
+ purple_certificate_pool_store(tpm_dat->tls_peers, id, crt);
+
+ /* And this certificate is not needed any more */
+ purple_certificate_destroy(crt);
+}
+
+static void
+tls_peers_mgmt_import_cancel2_cb(gpointer data, const char *result)
+{
+ PurpleCertificate *crt = (PurpleCertificate *) data;
+ purple_certificate_destroy(crt);
+}
+
+static void
+tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename)
+{
+ PurpleCertificateScheme *x509;
+ PurpleCertificate *crt;
+
+ /* Load the scheme of our tls_peers pool (ought to be x509) */
+ x509 = purple_certificate_pool_get_scheme(tpm_dat->tls_peers);
+
+ /* Now load the certificate from disk */
+ crt = purple_certificate_import(x509, filename);
+
+ /* Did it work? */
+ if (crt != NULL) {
+ gchar *default_hostname;
+ /* Get name to add to pool as */
+ /* Make a guess about what the hostname should be */
+ default_hostname = purple_certificate_get_subject_name(crt);
+ /* TODO: Find a way to make sure that crt gets destroyed
+ if the window gets closed unusually, such as by handle
+ deletion */
+ /* TODO: Display some more information on the certificate? */
+ purple_request_input(tpm_dat,
+ _("Certificate Import"),
+ _("Specify a hostname"),
+ _("Type the host name this certificate is for."),
+ default_hostname,
+ FALSE, /* Not multiline */
+ FALSE, /* Not masked? */
+ NULL, /* No hints? */
+ _("OK"),
+ G_CALLBACK(tls_peers_mgmt_import_ok2_cb),
+ _("Cancel"),
+ G_CALLBACK(tls_peers_mgmt_import_cancel2_cb),
+ NULL, NULL, NULL, /* No account/who/conv*/
+ crt /* Pass cert instance to callback*/
+ );
+
+ g_free(default_hostname);
+ } else {
+ /* Errors! Oh no! */
+ /* TODO: Perhaps find a way to be specific about what just
+ went wrong? */
+ gchar * secondary;
+
+ secondary = g_strdup_printf(_("File %s could not be imported.\nMake sure that the file is readable and in PEM format.\n"), filename);
+ purple_notify_error(NULL,
+ _("Certificate Import Error"),
+ _("X.509 certificate import failed"),
+ secondary);
+ g_free(secondary);
+ }
+}
+
+static void
+tls_peers_mgmt_import_cb(GtkWidget *button, gpointer data)
+{
+ /* TODO: need to tell the user that we want a .PEM file! */
+ purple_request_file(tpm_dat,
+ _("Select a PEM certificate"),
+ "certificate.pem",
+ FALSE, /* Not a save dialog */
+ G_CALLBACK(tls_peers_mgmt_import_ok_cb),
+ NULL, /* Do nothing if cancelled */
+ NULL, NULL, NULL, NULL );/* No account,conv,etc. */
+}
+
+static void
+tls_peers_mgmt_export_ok_cb(gpointer data, const char *filename)
+{
+ PurpleCertificate *crt = (PurpleCertificate *) data;
+
+ g_assert(filename);
+
+ if (!purple_certificate_export(filename, crt)) {
+ /* Errors! Oh no! */
+ /* TODO: Perhaps find a way to be specific about what just
+ went wrong? */
+ gchar * secondary;
+
+ secondary = g_strdup_printf(_("Export to file %s failed.\nCheck that you have write permission to the target path\n"), filename);
+ purple_notify_error(NULL,
+ _("Certificate Export Error"),
+ _("X.509 certificate export failed"),
+ secondary);
+ g_free(secondary);
+ }
+
+ purple_certificate_destroy(crt);
+}
+
+static void
+tls_peers_mgmt_export_cancel_cb(gpointer data, const char *filename)
+{
+ PurpleCertificate *crt = (PurpleCertificate *) data;
+ /* Pressing cancel just frees the duplicated certificate */
+ purple_certificate_destroy(crt);
+}
+
+static void
+tls_peers_mgmt_export_cb(GtkWidget *button, gpointer data)
+{
+ PurpleCertificate *crt;
+ GtkTreeSelection *select = tpm_dat->listselect;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gchar *id;
+
+ /* See if things are selected */
+ if (!gtk_tree_selection_get_selected(select, &model, &iter)) {
+ purple_debug_warning("gtkcertmgr/tls_peers_mgmt",
+ "Export clicked with no selection?\n");
+ return;
+ }
+
+ /* Retrieve the selected hostname */
+ gtk_tree_model_get(model, &iter, TPM_HOSTNAME_COLUMN, &id, -1);
+
+ /* Extract the certificate from the pool now to make sure it doesn't
+ get deleted out from under us */
+ crt = purple_certificate_pool_retrieve(tpm_dat->tls_peers, id);
+
+ if (NULL == crt) {
+ purple_debug_error("gtkcertmgr/tls_peers_mgmt",
+ "Id %s was not in the peers cache?!\n",
+ id);
+ g_free(id);
+ return;
+ }
+ g_free(id);
+
+
+ /* TODO: inform user that it will be a PEM? */
+ purple_request_file(tpm_dat,
+ _("PEM X.509 Certificate Export"),
+ "certificate.pem",
+ TRUE, /* Is a save dialog */
+ G_CALLBACK(tls_peers_mgmt_export_ok_cb),
+ G_CALLBACK(tls_peers_mgmt_export_cancel_cb),
+ NULL, NULL, NULL, /* No account,conv,etc. */
+ crt); /* Pass the certificate on to the callback */
+}
+
+static void
+tls_peers_mgmt_info_cb(GtkWidget *button, gpointer data)
+{
+ GtkTreeSelection *select = tpm_dat->listselect;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gchar *id;
+ PurpleCertificate *crt;
+ gchar *subject;
+ GByteArray *fpr_sha1;
+ gchar *fpr_sha1_asc;
+ gchar *primary, *secondary;
+
+ /* See if things are selected */
+ if (!gtk_tree_selection_get_selected(select, &model, &iter)) {
+ purple_debug_warning("gtkcertmgr/tls_peers_mgmt",
+ "Info clicked with no selection?\n");
+ return;
+ }
+
+ /* Retrieve the selected hostname */
+ gtk_tree_model_get(model, &iter, TPM_HOSTNAME_COLUMN, &id, -1);
+
+ /* Now retrieve the certificate */
+ crt = purple_certificate_pool_retrieve(tpm_dat->tls_peers, id);
+ g_return_if_fail(crt);
+
+ /* Build a notification thing */
+ /* TODO: This needs a better GUI, but a notification will do for now */
+ primary = g_strdup_printf(_("Certificate for %s"), id);
+
+ fpr_sha1 = purple_certificate_get_fingerprint_sha1(crt);
+ fpr_sha1_asc = purple_base16_encode_chunked(fpr_sha1->data,
+ fpr_sha1->len);
+ subject = purple_certificate_get_subject_name(crt);
+
+ secondary = g_strdup_printf(_("Common name: %s\n\nSHA1 fingerprint:\n%s"), subject, fpr_sha1_asc);
+
+ purple_notify_info(tpm_dat,
+ _("SSL Host Certificate"), primary, secondary );
+
+ g_free(primary);
+ g_free(secondary);
+ g_byte_array_free(fpr_sha1, TRUE);
+ g_free(fpr_sha1_asc);
+ g_free(subject);
+ g_free(id);
+ purple_certificate_destroy(crt);
+}
+
+static void
+tls_peers_mgmt_delete_confirm_cb(gchar *id, gint choice)
+{
+ if (1 == choice) {
+ /* Yes, delete was confirmed */
+ /* Now delete the thing */
+ if (!purple_certificate_pool_delete(tpm_dat->tls_peers, id)) {
+ purple_debug_warning("gtkcertmgr/tls_peers_mgmt",
+ "Deletion failed on id %s\n",
+ id);
+ };
+ }
+
+ g_free(id);
+}
+
+static void
+tls_peers_mgmt_delete_cb(GtkWidget *button, gpointer data)
+{
+ GtkTreeSelection *select = tpm_dat->listselect;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ /* See if things are selected */
+ if (gtk_tree_selection_get_selected(select, &model, &iter)) {
+
+ gchar *id;
+ gchar *primary;
+
+ /* Retrieve the selected hostname */
+ gtk_tree_model_get(model, &iter, TPM_HOSTNAME_COLUMN, &id, -1);
+
+ /* Prompt to confirm deletion */
+ primary = g_strdup_printf(
+ _("Really delete certificate for %s?"), id );
+
+ purple_request_yes_no(tpm_dat, _("Confirm certificate delete"),
+ primary, NULL, /* Can this be NULL? */
+ 2, /* NO is default action */
+ NULL, NULL, NULL,
+ id, /* id ownership passed to callback */
+ tls_peers_mgmt_delete_confirm_cb,
+ tls_peers_mgmt_delete_confirm_cb );
+
+ g_free(primary);
+
+ } else {
+ purple_debug_warning("gtkcertmgr/tls_peers_mgmt",
+ "Delete clicked with no selection?\n");
+ return;
+ }
+}
+
+static GtkWidget *
+tls_peers_mgmt_build(void)
+{
+ GtkWidget *bbox;
+ GtkListStore *store;
+
+ /* This block of variables will end up in tpm_dat */
+ GtkTreeView *listview;
+ GtkTreeSelection *select;
+ GtkWidget *importbutton;
+ GtkWidget *exportbutton;
+ GtkWidget *infobutton;
+ GtkWidget *deletebutton;
+ /** Element to return to the Certmgr window to put in the Notebook */
+ GtkWidget *mgmt_widget;
+
+ /* Create a struct to store context information about this window */
+ tpm_dat = g_new0(tls_peers_mgmt_data, 1);
+
+ tpm_dat->mgmt_widget = mgmt_widget =
+ gtk_hbox_new(FALSE, /* Non-homogeneous */
+ PIDGIN_HIG_BORDER);
+ gtk_widget_show(mgmt_widget);
+
+ /* Ensure that everything gets cleaned up when the dialog box
+ is closed */
+ g_signal_connect(G_OBJECT(mgmt_widget), "destroy",
+ G_CALLBACK(tls_peers_mgmt_destroy), NULL);
+
+ /* List view */
+ store = gtk_list_store_new(TPM_N_COLUMNS, G_TYPE_STRING);
+
+ tpm_dat->listview = listview =
+ GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
+
+ {
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ /* Set up the display columns */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(
+ "Hostname",
+ renderer,
+ "text", TPM_HOSTNAME_COLUMN,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column);
+ }
+
+ /* Get the treeview selector into the struct */
+ tpm_dat->listselect = select =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(listview));
+
+ /* Force the selection mode */
+ gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
+
+ /* Use a callback to enable/disable the buttons based on whether
+ something is selected */
+ g_signal_connect(G_OBJECT(select), "changed",
+ G_CALLBACK(tls_peers_mgmt_select_chg_cb), NULL);
+
+ gtk_box_pack_start(GTK_BOX(mgmt_widget), GTK_WIDGET(listview),
+ TRUE, TRUE, /* Take up lots of space */
+ 0); /* TODO: this padding is wrong */
+ gtk_widget_show(GTK_WIDGET(listview));
+
+ /* Fill the list for the first time */
+ tls_peers_mgmt_repopulate_list();
+
+ /* Right-hand side controls box */
+ bbox = gtk_vbutton_box_new();
+ gtk_box_pack_end(GTK_BOX(mgmt_widget), bbox,
+ FALSE, FALSE, /* Do not take up space */
+ 0); /* TODO: this padding is probably wrong */
+ gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START);
+ gtk_widget_show(bbox);
+
+ /* Import button */
+ /* TODO: This is the wrong stock button */
+ tpm_dat->importbutton = importbutton =
+ gtk_button_new_from_stock(GTK_STOCK_ADD);
+ gtk_box_pack_start(GTK_BOX(bbox), importbutton, FALSE, FALSE, 0);
+ gtk_widget_show(importbutton);
+ g_signal_connect(G_OBJECT(importbutton), "clicked",
+ G_CALLBACK(tls_peers_mgmt_import_cb), NULL);
+
+
+ /* Export button */
+ /* TODO: This is the wrong stock button */
+ tpm_dat->exportbutton = exportbutton =
+ gtk_button_new_from_stock(GTK_STOCK_SAVE);
+ gtk_box_pack_start(GTK_BOX(bbox), exportbutton, FALSE, FALSE, 0);
+ gtk_widget_show(exportbutton);
+ g_signal_connect(G_OBJECT(exportbutton), "clicked",
+ G_CALLBACK(tls_peers_mgmt_export_cb), NULL);
+
+
+ /* Info button */
+ tpm_dat->infobutton = infobutton =
+ gtk_button_new_from_stock(PIDGIN_STOCK_INFO);
+ gtk_box_pack_start(GTK_BOX(bbox), infobutton, FALSE, FALSE, 0);
+ gtk_widget_show(infobutton);
+ g_signal_connect(G_OBJECT(infobutton), "clicked",
+ G_CALLBACK(tls_peers_mgmt_info_cb), NULL);
+
+
+ /* Delete button */
+ tpm_dat->deletebutton = deletebutton =
+ gtk_button_new_from_stock(GTK_STOCK_DELETE);
+ gtk_box_pack_start(GTK_BOX(bbox), deletebutton, FALSE, FALSE, 0);
+ gtk_widget_show(deletebutton);
+ g_signal_connect(G_OBJECT(deletebutton), "clicked",
+ G_CALLBACK(tls_peers_mgmt_delete_cb), NULL);
+
+ /* Call the "selection changed" callback, which will probably disable
+ all the buttons since nothing is selected yet */
+ tls_peers_mgmt_select_chg_cb(select, NULL);
+
+ /* Bind us to the tls_peers pool */
+ tpm_dat->tls_peers = purple_certificate_find_pool("x509", "tls_peers");
+
+ /**** libpurple signals ****/
+ /* Respond to certificate add/remove by just reloading everything */
+ purple_signal_connect(tpm_dat->tls_peers, "certificate-stored",
+ tpm_dat, PURPLE_CALLBACK(tls_peers_mgmt_mod_cb),
+ NULL);
+ purple_signal_connect(tpm_dat->tls_peers, "certificate-deleted",
+ tpm_dat, PURPLE_CALLBACK(tls_peers_mgmt_mod_cb),
+ NULL);
+
+ return mgmt_widget;
+}
+
+PidginCertificateManager tls_peers_mgmt = {
+ tls_peers_mgmt_build, /* Widget creation function */
+ N_("SSL Servers")
+};
+
+/*****************************************************************************
+ * GTK+ main certificate manager *
+ *****************************************************************************/
+typedef struct
+{
+ GtkWidget *window;
+ GtkWidget *notebook;
+
+ GtkWidget *closebutton;
+} CertMgrDialog;
+
+/* If a certificate manager window is open, this will point to it.
+ So if it is set, don't open another one! */
+CertMgrDialog *certmgr_dialog = NULL;
+
+static void
+certmgr_close_cb(GtkWidget *w, CertMgrDialog *dlg)
+{
+ /* TODO: Ignoring the arguments to this function may not be ideal,
+ but there *should* only be "one dialog to rule them all" at a time*/
+ pidgin_certmgr_hide();
+}
+
+void
+pidgin_certmgr_show(void)
+{
+ CertMgrDialog *dlg;
+ GtkWidget *win;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+
+ /* Enumerate all the certificates on file */
+ {
+ GList *idlist, *poollist;
+
+ for ( poollist = purple_certificate_get_pools();
+ poollist;
+ poollist = poollist->next ) {
+ PurpleCertificatePool *pool = poollist->data;
+ GList *l;
+
+ purple_debug_info("gtkcertmgr",
+ "Pool %s found for scheme %s -"
+ "Enumerating certificates:\n",
+ pool->name, pool->scheme_name);
+
+ idlist = purple_certificate_pool_get_idlist(pool);
+
+ for (l=idlist; l; l = l->next) {
+ purple_debug_info("gtkcertmgr",
+ "- %s\n",
+ l->data ? (gchar *) l->data : "(null)");
+ } /* idlist */
+ purple_certificate_pool_destroy_idlist(idlist);
+ } /* poollist */
+ }
+
+
+ /* If the manager is already open, bring it to the front */
+ if (certmgr_dialog != NULL) {
+ gtk_window_present(GTK_WINDOW(certmgr_dialog->window));
+ return;
+ }
+
+ /* Create the dialog, and set certmgr_dialog so we never create
+ more than one at a time */
+ dlg = certmgr_dialog = g_new0(CertMgrDialog, 1);
+
+ win = dlg->window =
+ pidgin_create_window(_("Certificate Manager"),/* Title */
+ PIDGIN_HIG_BORDER, /*Window border*/
+ "certmgr", /* Role */
+ TRUE); /* Allow resizing */
+ g_signal_connect(G_OBJECT(win), "delete_event",
+ G_CALLBACK(certmgr_close_cb), dlg);
+
+
+ /* TODO: Retrieve the user-set window size and use it */
+ gtk_window_set_default_size(GTK_WINDOW(win), 400, 400);
+
+ /* Main vbox */
+ vbox = gtk_vbox_new( FALSE, PIDGIN_HIG_BORDER );
+ gtk_container_add(GTK_CONTAINER(win), vbox);
+ gtk_widget_show(vbox);
+
+ /* Notebook of various certificate managers */
+ dlg->notebook = gtk_notebook_new();
+ gtk_box_pack_start(GTK_BOX(vbox), dlg->notebook,
+ TRUE, TRUE, /* Notebook should take extra space */
+ 0);
+ gtk_widget_show(dlg->notebook);
+
+ /* Box for the close button */
+ bbox = gtk_hbutton_box_new();
+ gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
+ gtk_widget_show(bbox);
+
+ /* Close button */
+ dlg->closebutton = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_box_pack_start(GTK_BOX(bbox), dlg->closebutton, FALSE, FALSE, 0);
+ gtk_widget_show(dlg->closebutton);
+ g_signal_connect(G_OBJECT(dlg->closebutton), "clicked",
+ G_CALLBACK(certmgr_close_cb), dlg);
+
+ /* Add the defined certificate managers */
+ /* TODO: Find a way of determining whether each is shown or not */
+ /* TODO: Implement this correctly */
+ gtk_notebook_append_page(GTK_NOTEBOOK (dlg->notebook),
+ (tls_peers_mgmt.build)(),
+ gtk_label_new(_(tls_peers_mgmt.label)) );
+
+ gtk_widget_show(win);
+}
+
+void
+pidgin_certmgr_hide(void)
+{
+ /* If it isn't open, do nothing */
+ if (certmgr_dialog == NULL) {
+ return;
+ }
+
+ purple_signals_disconnect_by_handle(certmgr_dialog);
+ purple_prefs_disconnect_by_handle(certmgr_dialog);
+
+ gtk_widget_destroy(certmgr_dialog->window);
+ g_free(certmgr_dialog);
+ certmgr_dialog = NULL;
+}
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkcertmgr.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkcertmgr.h Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,62 @@
+/**
+ * @file gtkcertmgr.h GTK+ Certificate Manager API
+ * @ingroup pidgin
+ */
+/*
+ * pidgin
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _PIDGINCERTMGR_H_
+#define _PIDGINCERTMGR_H_
+
+/**************************************************************************
+ * @name Structures *
+ **************************************************************************/
+typedef struct _PidginCertificateManager PidginCertificateManager;
+
+/**
+ * GTK+ Certificate Manager subwidget
+ */
+struct _PidginCertificateManager {
+ /** Create, configure, show, and return the management interface */
+ GtkWidget * (* build)(void);
+ /** Notebook label to use in the CertMgr dialog */
+ gchar *label;
+};
+
+/**************************************************************************/
+/** @name Certificate Manager API */
+/**************************************************************************/
+/*@{*/
+/**
+ * Show the certificate manager window
+ */
+void pidgin_certmgr_show(void);
+
+/**
+ * Hide the certificate manager window
+ */
+void pidgin_certmgr_hide(void);
+
+/*@}*/
+
+#endif /* _PIDGINCERTMGR_H_ */
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkconv.c
--- a/pidgin/gtkconv.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkconv.c Thu Aug 30 00:09:47 2007 +0000
@@ -86,6 +86,7 @@
CONV_ICON_COLUMN,
CONV_TEXT_COLUMN,
CONV_EMBLEM_COLUMN,
+ CONV_PROTOCOL_ICON_COLUMN,
CONV_NUM_COLUMNS
} PidginInfopaneColumns;
@@ -100,36 +101,6 @@
#define LUMINANCE(c) (float)((0.3*(c.red))+(0.59*(c.green))+(0.11*(c.blue)))
-#if 0
-/* These colors come from the default GNOME palette */
-static GdkColor nick_colors[] = {
- {0, 47616, 46336, 43776}, /* Basic 3D Medium */
- {0, 32768, 32000, 29696}, /* Basic 3D Dark */
- {0, 22016, 20992, 18432}, /* 3D Shadow */
- {0, 33536, 42496, 32512}, /* Green Medium */
- {0, 23808, 29952, 21760}, /* Green Dark */
- {0, 17408, 22016, 12800}, /* Green Shadow */
- {0, 57344, 46592, 44800}, /* Red Hilight */
- {0, 49408, 26112, 23040}, /* Red Medium */
- {0, 34816, 17920, 12544}, /* Red Dark */
- {0, 49408, 14336, 8704}, /* Red Shadow */
- {0, 34816, 32512, 41728}, /* Purple Medium */
- {0, 25088, 23296, 33024}, /* Purple Dark */
- {0, 18688, 16384, 26112}, /* Purple Shadow */
- {0, 40192, 47104, 53760}, /* Blue Hilight */
- {0, 29952, 36864, 44544}, /* Blue Medium */
- {0, 57344, 49920, 40448}, /* Face Skin Medium */
- {0, 45824, 37120, 26880}, /* Face skin Dark */
- {0, 33280, 26112, 18176}, /* Face Skin Shadow */
- {0, 57088, 16896, 7680}, /* Accent Red */
- {0, 39168, 0, 0}, /* Accent Red Dark */
- {0, 17920, 40960, 17920}, /* Accent Green */
- {0, 9728, 50944, 9728} /* Accent Green Dark */
-};
-
-#define NUM_NICK_COLORS (sizeof(nick_colors) / sizeof(*nick_colors))
-#endif
-
/* From http://www.w3.org/TR/AERT#color-contrast */
#define MIN_BRIGHTNESS_CONTRAST 75
#define MIN_COLOR_CONTRAST 200
@@ -355,6 +326,7 @@
const char *cmd, char **args, char **error, void *data)
{
clear_conversation_scrollback(conv);
+ purple_conversation_clear_message_history(conv);
return PURPLE_CMD_STATUS_OK;
}
@@ -1328,6 +1300,14 @@
}
static void
+menu_hide_conv_cb(gpointer data, guint action, GtkWidget *widget)
+{
+ PidginWindow *win = data;
+ PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
+ purple_conversation_set_ui_ops(conv, NULL);
+}
+
+static void
menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget)
{
PidginWindow *win = data;
@@ -2301,7 +2281,7 @@
const char *name = NULL;
GdkPixbuf *status = NULL;
PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- const char *icon_size = small_icon ? "pidgin-icon-size-tango-microscopic" : PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL;
+ const char *icon_size = small_icon ? PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC : PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL;
g_return_val_if_fail(conv != NULL, NULL);
account = purple_conversation_get_account(conv);
@@ -2391,6 +2371,12 @@
&(gtkconv->infopane_iter),
CONV_EMBLEM_COLUMN, emblem, -1);
+ if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons")) {
+ gtk_list_store_set(GTK_LIST_STORE(gtkconv->infopane_model),
+ &(gtkconv->infopane_iter),
+ CONV_PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(gtkconv->active_conv->account, PIDGIN_PRPL_ICON_SMALL), -1);
+ }
+
/* XXX seanegan Why do I have to do this? */
gtk_widget_queue_draw(gtkconv->infopane);
@@ -2720,6 +2706,7 @@
pidgin_conv_present_conversation(PurpleConversation *conv)
{
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+ GdkModifierType state;
if(gtkconv->win==hidden_convwin) {
pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
@@ -2727,7 +2714,10 @@
}
pidgin_conv_switch_active_conversation(conv);
- pidgin_conv_window_switch_gtkconv(gtkconv->win, gtkconv);
+ /* Switch the tab only if the user initiated the event by pressing
+ * a button or hitting a key. */
+ if (gtk_get_current_event_state(&state))
+ pidgin_conv_window_switch_gtkconv(gtkconv->win, gtkconv);
gtk_window_present(GTK_WINDOW(gtkconv->win->window));
}
@@ -2753,7 +2743,7 @@
PurpleConversation *conv = (PurpleConversation*)l->data;
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
- if(gtkconv->active_conv != conv)
+ if(gtkconv == NULL || gtkconv->active_conv != conv)
continue;
if (gtkconv->unseen_state >= min_state
@@ -2867,6 +2857,8 @@
{ "/Conversation/sep4", NULL, NULL, 0, "", NULL },
+ { N_("/Conversation/_Hide"), NULL, menu_hide_conv_cb, 0,
+ "", NULL},
{ N_("/Conversation/_Close"), NULL, menu_close_conv_cb, 0,
"", GTK_STOCK_CLOSE },
@@ -4537,7 +4529,7 @@
G_CALLBACK(pidgin_conv_leave_cb), gtkconv);
gtkconv->infopane = gtk_cell_view_new();
- gtkconv->infopane_model = gtk_list_store_new(CONV_NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDK_TYPE_PIXBUF);
+ gtkconv->infopane_model = gtk_list_store_new(CONV_NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF);
gtk_cell_view_set_model(GTK_CELL_VIEW(gtkconv->infopane),
GTK_TREE_MODEL(gtkconv->infopane_model));
gtk_list_store_append(gtkconv->infopane_model, &(gtkconv->infopane_iter));
@@ -4562,6 +4554,12 @@
g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
#endif
+
+ rend = gtk_cell_renderer_pixbuf_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE);
+ gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", CONV_PROTOCOL_ICON_COLUMN, NULL);
+ g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL);
+
rend = gtk_cell_renderer_pixbuf_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", CONV_EMBLEM_COLUMN, NULL);
@@ -5028,6 +5026,10 @@
g_list_foreach(gtkconv->send_history, (GFunc)g_free, NULL);
g_list_free(gtkconv->send_history);
+ if (gtkconv->attach.timer) {
+ g_source_remove(gtkconv->attach.timer);
+ }
+
if (tooltip.gtkconv == gtkconv)
reset_tooltip();
@@ -5244,6 +5246,15 @@
gtkconv = PIDGIN_CONVERSATION(conv);
g_return_if_fail(gtkconv != NULL);
+ if (gtkconv->attach.timer) {
+ /* We are currently in the process of filling up the buffer with the message
+ * history of the conversation. So we do not need to add the message here.
+ * Instead, this message will be added to the message-list, which in turn will
+ * be processed and displayed by the attach-callback.
+ */
+ return;
+ }
+
if (conv != gtkconv->active_conv)
{
if (flags & PURPLE_MESSAGE_ACTIVE_ONLY)
@@ -6918,6 +6929,17 @@
}
static void
+show_protocol_icons_pref_cb(const char *name, PurplePrefType type,
+ gconstpointer value, gpointer data)
+{
+ GList *l;
+ for (l = purple_get_conversations(); l != NULL; l = l->next) {
+ PurpleConversation *conv = l->data;
+ update_tab_icon(conv);
+ }
+}
+
+static void
conv_placement_usetabs_cb(const char *name, PurplePrefType type,
gconstpointer value, gpointer data)
{
@@ -7167,6 +7189,55 @@
pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC);
}
+static gboolean
+add_message_history_to_gtkconv(gpointer data)
+{
+ PidginConversation *gtkconv = data;
+ int count = 0;
+ int timer = gtkconv->attach.timer;
+ gtkconv->attach.timer = 0;
+ while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */
+ PurpleConvMessage *msg = gtkconv->attach.current->data;
+ pidgin_conv_write_conv(gtkconv->active_conv, msg->who, msg->who, msg->what, msg->flags, msg->when);
+ gtkconv->attach.current = gtkconv->attach.current->prev;
+ count++;
+ }
+ gtkconv->attach.timer = timer;
+ if (gtkconv->attach.current)
+ return TRUE;
+
+ g_source_remove(gtkconv->attach.timer);
+ gtkconv->attach.timer = 0;
+ return FALSE;
+}
+
+gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv)
+{
+ GList *list;
+ PidginConversation *gtkconv;
+
+ if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
+ return FALSE;
+
+ purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops());
+ private_gtkconv_new(conv, FALSE);
+ gtkconv = PIDGIN_CONVERSATION(conv);
+
+ list = purple_conversation_get_message_history(conv);
+ if (list) {
+ list = g_list_last(list);
+ gtkconv->attach.current = list;
+ gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv);
+ }
+
+ /* XXX: If this is a chat:
+ * - populate the userlist
+ * - set the topic
+ */
+
+ return TRUE;
+}
+
void *
pidgin_conversations_get_handle(void)
{
@@ -7258,6 +7329,8 @@
animate_buddy_icons_pref_cb, NULL);
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons",
show_buddy_icons_pref_cb, NULL);
+ purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/im/show_protocol_icons",
+ show_protocol_icons_pref_cb, NULL);
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/im/hide_new",
hide_new_pref_cb, NULL);
@@ -8736,7 +8809,7 @@
angle = 270;
#if GTK_CHECK_VERSION(2,6,0)
- if (!angle && pidgin_conv_window_get_gtkconv_count(win) > 1) {
+ if (!angle) {
g_object_set(G_OBJECT(gtkconv->tab_label), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
gtk_label_set_width_chars(GTK_LABEL(gtkconv->tab_label), 4);
} else {
@@ -8801,14 +8874,14 @@
}
gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont,
- !tabs_side && !angle && pidgin_conv_window_get_gtkconv_count(win) > 1,
+ !tabs_side && !angle,
TRUE, GTK_PACK_START);
if (pidgin_conv_window_get_gtkconv_count(win) == 1)
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook),
- !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons") ||
- purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_LEFT ||
- purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_RIGHT);
+ purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs") &&
+ (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons") ||
+ purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") != GTK_POS_TOP));
/* show the widgets */
/* gtk_widget_show(gtkconv->icon); */
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkconv.h
--- a/pidgin/gtkconv.h Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkconv.h Thu Aug 30 00:09:47 2007 +0000
@@ -162,6 +162,13 @@
GtkWidget *infopane;
GtkListStore *infopane_model;
GtkTreeIter infopane_iter;
+
+ /* Used when attaching a PidginConversation to a PurpleConversation
+ * with message history */
+ struct {
+ int timer;
+ GList *current;
+ } attach;
};
/*@}*/
@@ -238,6 +245,8 @@
*/
void pidgin_conv_present_conversation(PurpleConversation *conv);
+gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv);
+
PidginWindow *pidgin_conv_get_window(PidginConversation *gtkconv);
GdkPixbuf *pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon);
void pidgin_conv_new(PurpleConversation *conv);
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkdialogs.c
--- a/pidgin/gtkdialogs.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkdialogs.c Thu Aug 30 00:09:47 2007 +0000
@@ -86,6 +86,7 @@
{"Luke 'LSchiere' Schierer", N_("support"), "lschiere@users.sf.net"},
{"Megan 'Cae' Schneider", N_("support/QA"), NULL},
{"Evan Schoenberg", N_("developer"), NULL},
+ {"Kevin 'SimGuy' Stange", N_("developer & webmaster"), NULL},
{"Stu 'nosnilmot' Tomlinson", N_("developer"), NULL},
{"Nathan 'faceprint' Walp", N_("developer"), NULL},
{NULL, NULL, NULL}
@@ -95,7 +96,10 @@
static struct developer patch_writers[] = {
{"John 'rekkanoryo' Bailey", NULL, NULL},
{"Peter 'Bleeter' Lawler", NULL, NULL},
- {"Kevin 'SimGuy' Stange", NULL, NULL},
+ {"Dennis 'EvilDennisR' Ristuccia", N_("Senior Contributor/QA"), NULL},
+ {"Peter 'Fmoo' Ruibal", NULL, NULL},
+ {"Gabriel 'Nix' Schulhof", NULL, NULL},
+ {"Will 'resiak' Thompson", NULL, NULL},
{NULL, NULL, NULL}
};
@@ -381,7 +385,7 @@
"libpurple which is capable of connecting to "
"AIM, MSN, Yahoo!, XMPP, ICQ, IRC, SILC, SIP/SIMPLE, "
"Novell GroupWise, Lotus Sametime, Bonjour, Zephyr, "
- "Gadu-Gadu, and QQ all at once. "
+ "MySpaceIM, Gadu-Gadu, and QQ all at once. "
"It is written using GTK+.
"
"You may modify and redistribute the program under "
"the terms of the GPL (version 2 or later). A copy of the GPL is "
@@ -765,6 +769,7 @@
if (conv == NULL)
conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username);
+ pidgin_conv_attach_to_conversation(conv);
purple_conversation_present(conv);
}
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkimhtmltoolbar.c
--- a/pidgin/gtkimhtmltoolbar.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c Thu Aug 30 00:09:47 2007 +0000
@@ -70,6 +70,14 @@
}
static void
+do_strikethrough(GtkWidget *strikethrough, GtkIMHtmlToolbar *toolbar)
+{
+ g_return_if_fail(toolbar != NULL);
+ gtk_imhtml_toggle_strike(GTK_IMHTML(toolbar->imhtml));
+ gtk_widget_grab_focus(toolbar->imhtml);
+}
+
+static void
do_small(GtkWidget *smalltb, GtkIMHtmlToolbar *toolbar)
{
g_return_if_fail(toolbar != NULL);
@@ -433,6 +441,17 @@
gtk_widget_grab_focus(toolbar->imhtml);
}
+static void insert_hr_cb(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
+{
+ GtkTextIter iter;
+ GtkTextMark *ins;
+ GtkIMHtmlScalable *hr;
+
+ ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)));
+ gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)), &iter, ins);
+ hr = gtk_imhtml_hr_new();
+ gtk_imhtml_hr_add_to(hr, GTK_IMHTML(toolbar->imhtml), &iter);
+}
static void
do_insert_image_cb(GtkWidget *widget, int response, GtkIMHtmlToolbar *toolbar)
@@ -1027,6 +1046,13 @@
G_CALLBACK(do_underline), toolbar);
toolbar->underline = button;
+
+ /* Strikethrough */
+ button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_STRIKETHROUGH);
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(do_strikethrough), toolbar);
+ toolbar->strikethrough = button;
+
/* Increase font size */
button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_TEXT_LARGER);
g_signal_connect(G_OBJECT(button), "clicked",
@@ -1124,6 +1150,7 @@
{_("_Bold"), &toolbar->bold, TRUE},
{_("_Italic"), &toolbar->italic, TRUE},
{_("_Underline"), &toolbar->underline, TRUE},
+ {_("Strikethrough"), &toolbar->strikethrough, TRUE},
{_("_Larger"), &toolbar->larger_size, TRUE},
#if 0
{_("_Normal"), &toolbar->normal_size, TRUE},
@@ -1230,6 +1257,11 @@
g_signal_connect(G_OBJECT(toolbar->link), "notify::sensitive",
G_CALLBACK(button_sensitiveness_changed), menuitem);
+ menuitem = gtk_menu_item_new_with_mnemonic(_("_Horizontal rule"));
+ g_signal_connect(G_OBJECT(menuitem), "activate" , G_CALLBACK(insert_hr_cb), toolbar);
+ gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
+ toolbar->insert_hr = menuitem;
+
g_signal_connect_swapped(G_OBJECT(insert_button), "button-press-event", G_CALLBACK(gtk_widget_activate), insert_button);
g_signal_connect(G_OBJECT(insert_button), "activate", G_CALLBACK(pidgin_menu_clicked), insert_menu);
g_signal_connect(G_OBJECT(insert_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), insert_button);
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkimhtmltoolbar.h
--- a/pidgin/gtkimhtmltoolbar.h Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.h Thu Aug 30 00:09:47 2007 +0000
@@ -74,6 +74,8 @@
GtkWidget *image_dialog;
char *sml;
+ GtkWidget *strikethrough;
+ GtkWidget *insert_hr;
};
struct _GtkIMHtmlToolbarClass {
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkmain.c
--- a/pidgin/gtkmain.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkmain.c Thu Aug 30 00:09:47 2007 +0000
@@ -768,22 +768,12 @@
purple_set_blist(purple_blist_new());
purple_blist_load();
- /* TODO: Move prefs loading into purple_prefs_init() */
- purple_prefs_load();
- purple_prefs_update_old();
- pidgin_prefs_update_old();
-
/* load plugins we had when we quit */
purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded");
/* TODO: Move pounces loading into purple_pounces_init() */
purple_pounces_load();
- /* Call this early on to try to auto-detect our IP address and
- * hopefully save some time later.
- * TODO: move this (back) into purple_core_init() when purple_prefs_load() is in purple_prefs_init() */
- purple_network_get_my_ip(-1);
-
/* HACK BY SEANEGAN:
* We've renamed prpl-oscar to prpl-aim and prpl-icq, accordingly.
* Let's do that change right here... after everything's loaded, but
@@ -837,10 +827,7 @@
g_free(opt_login_arg);
opt_login_arg = NULL;
}
- }
-
- if (opt_nologin && !opt_login)
- {
+ } else if (opt_nologin) {
/* Set all accounts to "offline" */
PurpleSavedStatus *saved_status;
@@ -854,9 +841,7 @@
/* Set the status for each account */
purple_savedstatus_activate(saved_status);
- }
- else if (!opt_login)
- {
+ } else {
/* Everything is good to go--sign on already */
if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
purple_savedstatus_activate(purple_savedstatus_get_startup());
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtknotify.c
--- a/pidgin/gtknotify.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtknotify.c Thu Aug 30 00:09:47 2007 +0000
@@ -589,16 +589,16 @@
char label_text[2048];
char *linked_text, *primary_esc, *secondary_esc;
- window = pidgin_create_window(title, PIDGIN_HIG_BORDER, NULL, TRUE);
- gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
+ window = gtk_dialog_new();
+ gtk_window_set_title(GTK_WINDOW(window), title);
+ gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
+ gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK(formatted_close_cb), NULL);
/* Setup the main vbox */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(window), vbox);
- gtk_widget_show(vbox);
+ vbox = GTK_DIALOG(window)->vbox;
/* Setup the descriptive label */
primary_esc = g_markup_escape_text(primary, -1);
@@ -630,9 +630,7 @@
gtk_widget_show(frame);
/* Add the Close button. */
- button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
- gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
- gtk_widget_show(button);
+ button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
gtk_widget_grab_focus(button);
g_signal_connect_swapped(G_OBJECT(button), "clicked",
@@ -708,7 +706,6 @@
guint i;
GtkWidget *vbox;
- GtkWidget *button_area;
GtkWidget *label;
GtkWidget *sw;
PidginNotifySearchResultsData *data;
@@ -723,16 +720,16 @@
data->results = results;
/* Create the window */
- window = pidgin_create_window(title ? title :_("Search Results"), PIDGIN_HIG_BORDER, NULL, TRUE);
- gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
+ window = gtk_dialog_new();
+ gtk_window_set_title(GTK_WINDOW(window), title ? title :_("Search Results"));
+ gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
+ gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
g_signal_connect_swapped(G_OBJECT(window), "delete_event",
G_CALLBACK(searchresults_close_cb), data);
/* Setup the main vbox */
- vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(window), vbox);
- gtk_widget_show(vbox);
+ vbox = GTK_DIALOG(window)->vbox;
/* Setup the descriptive label */
primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
@@ -796,13 +793,6 @@
renderer, "text", i, NULL);
}
- /* Setup the button area */
- button_area = gtk_hbutton_box_new();
- gtk_box_pack_start(GTK_BOX(vbox), button_area, FALSE, FALSE, 0);
- gtk_button_box_set_layout(GTK_BUTTON_BOX(button_area), GTK_BUTTONBOX_END);
- gtk_box_set_spacing(GTK_BOX(button_area), PIDGIN_HIG_BORDER);
- gtk_widget_show(button_area);
-
for (i = 0; i < g_list_length(results->buttons); i++) {
PurpleNotifySearchButton *b = g_list_nth_data(results->buttons, i);
GtkWidget *button = NULL;
@@ -815,22 +805,22 @@
}
break;
case PURPLE_NOTIFY_BUTTON_CONTINUE:
- button = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD);
+ button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_GO_FORWARD, GTK_RESPONSE_NONE);
break;
case PURPLE_NOTIFY_BUTTON_ADD:
- button = gtk_button_new_from_stock(GTK_STOCK_ADD);
+ button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_ADD, GTK_RESPONSE_NONE);
break;
case PURPLE_NOTIFY_BUTTON_INFO:
- button = gtk_button_new_from_stock(PIDGIN_STOCK_TOOLBAR_USER_INFO);
+ button = gtk_dialog_add_button(GTK_DIALOG(window), PIDGIN_STOCK_TOOLBAR_USER_INFO, GTK_RESPONSE_NONE);
break;
case PURPLE_NOTIFY_BUTTON_IM:
- button = gtk_button_new_from_stock(PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW);
+ button = gtk_dialog_add_button(GTK_DIALOG(window), PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, GTK_RESPONSE_NONE);
break;
case PURPLE_NOTIFY_BUTTON_JOIN:
- button = gtk_button_new_from_stock(PIDGIN_STOCK_CHAT);
+ button = gtk_dialog_add_button(GTK_DIALOG(window), PIDGIN_STOCK_CHAT, GTK_RESPONSE_NONE);
break;
case PURPLE_NOTIFY_BUTTON_INVITE:
- button = gtk_button_new_from_stock(PIDGIN_STOCK_INVITE);
+ button = gtk_dialog_add_button(GTK_DIALOG(window), PIDGIN_STOCK_INVITE, GTK_RESPONSE_NONE);
break;
default:
purple_debug_warning("gtknotify", "Incorrect button type: %d\n", b->type);
@@ -838,9 +828,6 @@
if (button != NULL) {
PidginNotifySearchResultsButtonData *bd;
- gtk_box_pack_start(GTK_BOX(button_area), button, FALSE, FALSE, 0);
- gtk_widget_show(button);
-
bd = g_new0(PidginNotifySearchResultsButtonData, 1);
bd->button = b;
bd->data = data;
@@ -852,9 +839,7 @@
}
/* Add the Close button */
- close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
- gtk_box_pack_start(GTK_BOX(button_area), close_button, FALSE, FALSE, 0);
- gtk_widget_show(close_button);
+ close_button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
g_signal_connect_swapped(G_OBJECT(close_button), "clicked",
G_CALLBACK(searchresults_close_cb), data);
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkpounce.c
--- a/pidgin/gtkpounce.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkpounce.c Thu Aug 30 00:09:47 2007 +0000
@@ -467,7 +467,6 @@
PidginPounceDialog *dialog;
GtkWidget *window;
GtkWidget *label;
- GtkWidget *bbox;
GtkWidget *vbox1, *vbox2;
GtkWidget *hbox;
GtkWidget *button;
@@ -513,17 +512,16 @@
sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
/* Create the window. */
- dialog->window = window = pidgin_create_window((cur_pounce == NULL ? _("New Buddy Pounce") : _("Edit Buddy Pounce")),
- PIDGIN_HIG_BORDER, "buddy_pounce", FALSE) ;
- gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
+ dialog->window = window = gtk_dialog_new();
+ gtk_window_set_title(GTK_WINDOW(window), (cur_pounce == NULL ? _("New Buddy Pounce") : _("Edit Buddy Pounce")));
+ gtk_window_set_role(GTK_WINDOW(window), "buddy_pounce");
+ gtk_container_set_border_width(GTK_CONTAINER(dialog->window), PIDGIN_HIG_BORDER);
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK(delete_win_cb), dialog);
/* Create the parent vbox for everything. */
- vbox1 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
- gtk_container_add(GTK_CONTAINER(window), vbox1);
- gtk_widget_show(vbox1);
+ vbox1 = GTK_DIALOG(window)->vbox;
/* Create the vbox that will contain all the prefs stuff. */
vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -808,26 +806,13 @@
gtk_widget_show(dialog->on_away);
gtk_widget_show(dialog->save_pounce);
- /* Now the button box! */
- bbox = gtk_hbutton_box_new();
- gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
- gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
- gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, FALSE, 0);
- gtk_widget_show(bbox);
-
/* Cancel button */
- button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
- gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
- gtk_widget_show(button);
-
+ button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(cancel_cb), dialog);
/* Save button */
- dialog->save_button = button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
- gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
- gtk_widget_show(button);
-
+ dialog->save_button = button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_SAVE, GTK_RESPONSE_OK);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(save_pounce_cb), dialog);
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkprefs.c
--- a/pidgin/gtkprefs.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkprefs.c Thu Aug 30 00:09:47 2007 +0000
@@ -240,7 +240,7 @@
if (label != NULL) {
gtk_label_set_mnemonic_widget(GTK_LABEL(label), dropdown);
- pidgin_set_accessible_label (dropdown, label);
+ pidgin_set_accessible_relations (dropdown, label);
}
if (type == PURPLE_PREF_INT)
@@ -887,7 +887,7 @@
ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
-
+
sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
vbox = pidgin_make_frame(ret, _("System Tray Icon"));
@@ -899,7 +899,7 @@
NULL);
gtk_size_group_add_widget(sg, label);
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-
+
vbox = pidgin_make_frame(ret, _("Conversation Window Hiding"));
label = pidgin_prefs_dropdown(vbox, _("_Hide new IM conversations:"),
PURPLE_PREF_STRING, PIDGIN_PREFS_ROOT "/conversations/im/hide_new",
@@ -909,11 +909,11 @@
NULL);
gtk_size_group_add_widget(sg, label);
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-
+
/* All the tab options! */
vbox = pidgin_make_frame(ret, _("Tabs"));
-
+
pidgin_prefs_checkbox(_("Show IMs and chats in _tabbed windows"),
PIDGIN_PREFS_ROOT "/conversations/tabs", vbox);
@@ -944,12 +944,12 @@
NULL);
gtk_size_group_add_widget(sg, label);
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-
+
names = pidgin_conv_placement_get_options();
label = pidgin_prefs_dropdown_from_list(vbox2, _("N_ew conversations:"),
PURPLE_PREF_STRING, PIDGIN_PREFS_ROOT "/conversations/placement", names);
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-
+
gtk_size_group_add_widget(sg, label);
g_list_free(names);
@@ -1620,7 +1620,7 @@
filename = NULL;
purple_request_file(prefs, _("Sound Selection"), filename, FALSE,
- G_CALLBACK(sound_chosen_cb), NULL,
+ G_CALLBACK(sound_chosen_cb), NULL,
NULL, NULL, NULL,
GINT_TO_POINTER(sound_row_sel));
}
@@ -2010,7 +2010,7 @@
return ret;
}
-static int
+static int
prefs_notebook_add_page(const char *text,
GtkWidget *page,
int ind) {
@@ -2164,6 +2164,8 @@
/* Smiley Callbacks */
purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/smileys/theme",
smiley_theme_pref_cb, NULL);
+
+ pidgin_prefs_update_old();
}
void pidgin_prefs_update_old()
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkrequest.c
--- a/pidgin/gtkrequest.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkrequest.c Thu Aug 30 00:09:47 2007 +0000
@@ -442,7 +442,7 @@
static void *
pidgin_request_choice(const char *title, const char *primary,
- const char *secondary, unsigned int default_value,
+ const char *secondary, int default_value,
const char *ok_text, GCallback ok_cb,
const char *cancel_text, GCallback cancel_cb,
PurpleAccount *account, const char *who, PurpleConversation *conv,
@@ -548,7 +548,7 @@
static void *
pidgin_request_action(const char *title, const char *primary,
- const char *secondary, unsigned int default_action,
+ const char *secondary, int default_action,
PurpleAccount *account, const char *who, PurpleConversation *conv,
void *user_data, size_t action_count, va_list actions)
{
@@ -1083,7 +1083,7 @@
data->cbs[0] = ok_cb;
data->cbs[1] = cancel_cb;
-
+
#ifdef _WIN32
data->dialog = win = pidgin_create_window(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
#else /* !_WIN32 */
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtksavedstatuses.c
--- a/pidgin/gtksavedstatuses.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtksavedstatuses.c Thu Aug 30 00:09:47 2007 +0000
@@ -1450,7 +1450,9 @@
GtkWidget *win;
GtkTreeIter iter;
GtkCellRenderer *rend;
- const char *status_id = NULL;
+ char *status_id = NULL;
+ char *message = NULL;
+ gboolean parent_dialog_has_substatus = FALSE;
GList *list;
gboolean select = FALSE;
@@ -1553,25 +1555,29 @@
G_CALLBACK(substatus_editor_ok_cb), dialog);
/* Seed the input widgets with the current values */
- /* TODO: Get the current values from our parent's list store, not the saved_status! */
- if (status_editor->original_title != NULL)
- {
+
+ /* Only look at the saved status if we can't find it in the parent status dialog's substatuses model */
+ gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter,
+ STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &parent_dialog_has_substatus, -1);
+ if (parent_dialog_has_substatus) {
+ gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter,
+ STATUS_EDITOR_COLUMN_STATUS_ID, &status_id,
+ STATUS_EDITOR_COLUMN_STATUS_MESSAGE, &message, -1);
+ } else if (status_editor->original_title != NULL) {
PurpleSavedStatus *saved_status = NULL;
PurpleSavedStatusSub *substatus = NULL;
- saved_status = purple_savedstatus_find(status_editor->original_title);
- if (saved_status != NULL)
- substatus = purple_savedstatus_get_substatus(saved_status, account);
+ if ((saved_status = purple_savedstatus_find(status_editor->original_title)) != NULL) {
+ if ((substatus = purple_savedstatus_get_substatus(saved_status, account)) != NULL) {
+ message = (char *)purple_savedstatus_substatus_get_message(substatus);
+ status_id = (char *)purple_status_type_get_id(purple_savedstatus_substatus_get_type(substatus));
+ }
+ }
+ }
+ /* TODO: Else get the generic status type from our parent */
- if (substatus != NULL)
- {
- gtk_imhtml_append_text(dialog->message,
- purple_savedstatus_substatus_get_message(substatus),
- 0);
- status_id = purple_status_type_get_id(purple_savedstatus_substatus_get_type(substatus));
- }
- /* TODO: Else get the generic status type from our parent */
- }
+ if (message)
+ gtk_imhtml_append_text(dialog->message, message, 0);
for (list = purple_account_get_status_types(account); list; list = list->next)
{
@@ -1607,6 +1613,12 @@
if (!select)
gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
+ if (parent_dialog_has_substatus) {
+ /* These two were gotten from the parent tree model, so they need to be freed */
+ g_free(status_id);
+ g_free(message);
+ }
+
gtk_widget_show_all(win);
}
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtksound.c
--- a/pidgin/gtksound.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtksound.c Thu Aug 30 00:09:47 2007 +0000
@@ -114,7 +114,7 @@
play_conv_event(PurpleConversation *conv, PurpleSoundEventID event)
{
/* If we should not play the sound for some reason, then exit early */
- if (conv != NULL)
+ if (conv != NULL && PIDGIN_IS_PIDGIN_CONVERSATION(conv))
{
PidginConversation *gtkconv;
PidginWindow *win;
@@ -364,14 +364,14 @@
GError *err = NULL;
switch (GST_MESSAGE_TYPE (msg)) {
- case GST_MESSAGE_EOS:
- gst_element_set_state(play, GST_STATE_NULL);
- gst_object_unref(GST_OBJECT(play));
- break;
case GST_MESSAGE_ERROR:
gst_message_parse_error(msg, &err, NULL);
purple_debug_error("gstreamer", "%s\n", err->message);
g_error_free(err);
+ /* fall-through and clean up */
+ case GST_MESSAGE_EOS:
+ gst_element_set_state(play, GST_STATE_NULL);
+ gst_object_unref(GST_OBJECT(play));
break;
case GST_MESSAGE_WARNING:
gst_message_parse_warning(msg, &err, NULL);
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkstatusbox.c
--- a/pidgin/gtkstatusbox.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkstatusbox.c Thu Aug 30 00:09:47 2007 +0000
@@ -1394,7 +1394,7 @@
return;
}
gtk_grab_add (box->popup_window);
- box->popup_in_progress = TRUE;
+// box->popup_in_progress = TRUE;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (box->toggle_button),
TRUE);
@@ -1415,15 +1415,15 @@
}
-static void
-toggled_cb(GtkWidget *widget, PidginStatusBox *box)
+static
+gboolean
+toggled_cb(GtkWidget *widget, GdkEventButton *event, PidginStatusBox *box)
{
- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
if (!box->popup_in_progress)
pidgin_status_box_popup (box);
- } else {
- pidgin_status_box_popdown(box);
- }
+ else
+ pidgin_status_box_popdown(box);
+return TRUE;
}
static void
@@ -1590,14 +1590,15 @@
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (status_box->toggle_button))) {
pidgin_status_box_popdown (status_box);
return TRUE;
+ } else if (ewidget == status_box->toggle_button) {
+ status_box->popup_in_progress = TRUE;
}
/* released outside treeview */
- if (ewidget != status_box->toggle_button)
- {
+ if (ewidget != status_box->toggle_button) {
pidgin_status_box_popdown (status_box);
return TRUE;
- }
+ }
return FALSE;
}
@@ -1773,7 +1774,7 @@
g_signal_connect(G_OBJECT(status_box->toggle_button), "button-release-event",
G_CALLBACK(button_released_cb), status_box);
#endif
- g_signal_connect(G_OBJECT(status_box->toggle_button), "toggled",
+ g_signal_connect(G_OBJECT(status_box->toggle_button), "button-press-event",
G_CALLBACK(toggled_cb), status_box);
g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(imhtml_changed_cb), status_box);
g_signal_connect(G_OBJECT(status_box->imhtml), "format_function_toggle",
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkutils.c
--- a/pidgin/gtkutils.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkutils.c Thu Aug 30 00:09:47 2007 +0000
@@ -1147,12 +1147,30 @@
void
pidgin_set_accessible_label (GtkWidget *w, GtkWidget *l)
{
+ AtkObject *acc;
+ const gchar *label_text;
+ const gchar *existing_name;
+
+ acc = gtk_widget_get_accessible (w);
+
+ /* If this object has no name, set it's name with the label text */
+ existing_name = atk_object_get_name (acc);
+ if (!existing_name) {
+ label_text = gtk_label_get_text (GTK_LABEL(l));
+ if (label_text)
+ atk_object_set_name (acc, label_text);
+ }
+
+ pidgin_set_accessible_relations(w, l);
+}
+
+void
+pidgin_set_accessible_relations (GtkWidget *w, GtkWidget *l)
+{
AtkObject *acc, *label;
AtkObject *rel_obj[1];
AtkRelationSet *set;
AtkRelation *relation;
- const gchar *label_text;
- const gchar *existing_name;
acc = gtk_widget_get_accessible (w);
label = gtk_widget_get_accessible (l);
@@ -1160,14 +1178,6 @@
/* Make sure mnemonics work */
gtk_label_set_mnemonic_widget(GTK_LABEL(l), w);
- /* If this object has no name, set it's name with the label text */
- existing_name = atk_object_get_name (acc);
- if (!existing_name) {
- label_text = gtk_label_get_text (GTK_LABEL(l));
- if (label_text)
- atk_object_set_name (acc, label_text);
- }
-
/* Create the labeled-by relation */
set = atk_object_ref_relation_set (acc);
rel_obj[0] = label;
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/gtkutils.h
--- a/pidgin/gtkutils.h Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/gtkutils.h Thu Aug 30 00:09:47 2007 +0000
@@ -418,6 +418,14 @@
void pidgin_set_accessible_label(GtkWidget *w, GtkWidget *l);
/**
+ * Sets the labelled-by and label-for ATK relationships.
+ *
+ * @param w The widget that we want to label.
+ * @param l A GtkLabel that we want to use as the label for the widget.
+ */
+void pidgin_set_accessible_relations(GtkWidget *w, GtkWidget *l);
+
+/**
* A helper function for GtkMenuPositionFuncs. This ensures the menu will
* be kept on screen if possible.
*
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/pidgin.h
--- a/pidgin/pidgin.h Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/pidgin.h Thu Aug 30 00:09:47 2007 +0000
@@ -26,7 +26,7 @@
#ifndef _PIDGIN_H_
#define _PIDGIN_H_
-#ifndef _WIN32
+#ifdef GDK_WINDOWING_X11
# include
#endif
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/pidginstock.c
--- a/pidgin/pidginstock.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/pidginstock.c Thu Aug 30 00:09:47 2007 +0000
@@ -74,6 +74,11 @@
{ PIDGIN_STOCK_SIGN_OFF, NULL, GTK_STOCK_CLOSE },
{ PIDGIN_STOCK_TYPED, "pidgin", "typed.png" },
{ PIDGIN_STOCK_UPLOAD, NULL, GTK_STOCK_GO_UP },
+#if GTK_CHECK_VERSION(2,8,0)
+ { PIDGIN_STOCK_INFO, NULL, GTK_STOCK_INFO },
+#else
+ { PIDGIN_STOCK_INFO, "buttons", "info.png" },
+#endif
};
static const GtkStockItem stock_items[] =
@@ -105,7 +110,7 @@
{ PIDGIN_STOCK_STATUS_AVAILABLE, "status", "available.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AVAILABLE_I },
{ PIDGIN_STOCK_STATUS_AWAY, "status", "away.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AWAY_I },
{ PIDGIN_STOCK_STATUS_BUSY, "status", "busy.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_BUSY_I },
- { PIDGIN_STOCK_STATUS_CHAT, "status", "chat.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
+ { PIDGIN_STOCK_STATUS_CHAT, "status", "chat.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_STATUS_INVISIBLE,"status", "invisible.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_STATUS_XA, "status", "extended-away.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, PIDGIN_STOCK_STATUS_XA_I },
{ PIDGIN_STOCK_STATUS_LOGIN, "status", "log-in.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
@@ -396,7 +401,7 @@
/* register custom icon sizes */
- microscopic = gtk_icon_size_register("pidgin-icon-size-tango-microscopic", 11, 11);
+ microscopic = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11);
extra_small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16);
small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22);
medium = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MEDIUM, 32, 32);
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/pidginstock.h
--- a/pidgin/pidginstock.h Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/pidginstock.h Thu Aug 30 00:09:47 2007 +0000
@@ -45,6 +45,7 @@
#define PIDGIN_STOCK_FILE_CANCELED "pidgin-file-canceled"
#define PIDGIN_STOCK_FILE_DONE "pidgin-file-done"
#define PIDGIN_STOCK_IGNORE "pidgin-ignore"
+#define PIDGIN_STOCK_INFO "pidgin-info"
#define PIDGIN_STOCK_INVITE "pidgin-invite"
#define PIDGIN_STOCK_MODIFY "pidgin-modify"
#define PIDGIN_STOCK_OPEN_MAIL "pidgin-stock-open-mail"
@@ -145,6 +146,7 @@
/**
* For using icons that aren't one of the default GTK_ICON_SIZEs
*/
+#define PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC "pidgin-icon-size-tango-microscopic"
#define PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL "pidgin-icon-size-tango-extra-small"
#define PIDGIN_ICON_SIZE_TANGO_SMALL "pidgin-icon-size-tango-small"
#define PIDGIN_ICON_SIZE_TANGO_MEDIUM "pidgin-icon-size-tango-medium"
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/pixmaps/Makefile.am
--- a/pidgin/pixmaps/Makefile.am Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/pixmaps/Makefile.am Thu Aug 30 00:09:47 2007 +0000
@@ -2,6 +2,7 @@
EXTRA_DIST = \
edit.png \
+ info.png \
logo.png \
pause.png \
arrow-down.xpm \
@@ -12,7 +13,7 @@
pidgin.ico
pidginbuttonpixdir = $(datadir)/pixmaps/pidgin/buttons
-pidginbuttonpix_DATA = edit.png pause.png
+pidginbuttonpix_DATA = edit.png pause.png info.png
pidgindistpixdir = $(datadir)/pixmaps/pidgin
pidgindistpix_DATA = logo.png arrow-down.xpm arrow-left.xpm arrow-right.xpm arrow-up.xpm
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/pixmaps/info.png
Binary file pidgin/pixmaps/info.png has changed
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/pixmaps/status/11/Makefile.am
--- a/pidgin/pixmaps/status/11/Makefile.am Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/pixmaps/status/11/Makefile.am Thu Aug 30 00:09:47 2007 +0000
@@ -1,4 +1,4 @@
-SUBDIRS = scalable
+SUBDIRS = scalable rtl
EXTRA_DIST = available.png \
away.png \
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/pixmaps/status/11/Makefile.mingw
--- a/pidgin/pixmaps/status/11/Makefile.mingw Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/pixmaps/status/11/Makefile.mingw Thu Aug 30 00:09:47 2007 +0000
@@ -18,3 +18,5 @@
cp $(pidginstatuspix_DATA) $(pidginstatuspixdir); \
fi;
+ $(MAKE) -C rtl -f Makefile.mingw install || exit 1; \
+
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/pixmaps/status/11/rtl/Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/status/11/rtl/Makefile.am Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,6 @@
+EXTRA_DIST = extended-away.png
+
+pidginstatuspixdir = $(datadir)/pixmaps/pidgin/status/11/rtl
+
+pidginstatuspix_DATA = $(EXTRA_DIST)
+
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/pixmaps/status/11/rtl/Makefile.mingw
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/status/11/rtl/Makefile.mingw Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,20 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for win32 (mingw) version of Pidgin pixmaps
+#
+
+PIDGIN_TREE_TOP := ../../../../..
+include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+
+datadir = $(PIDGIN_INSTALL_DIR)
+include ./Makefile.am
+
+.PHONY: install
+
+install:
+ if test '$(pidginstatuspix_DATA)'; then \
+ mkdir -p $(pidginstatuspixdir); \
+ cp $(pidginstatuspix_DATA) $(pidginstatuspixdir); \
+ fi;
+
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/pixmaps/status/11/rtl/extended-away.png
Binary file pidgin/pixmaps/status/11/rtl/extended-away.png has changed
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/plugins/Makefile.am
--- a/pidgin/plugins/Makefile.am Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/plugins/Makefile.am Thu Aug 30 00:09:47 2007 +0000
@@ -16,9 +16,13 @@
PERL_DIR = perl
endif
+if ENABLE_GESTURES
+GESTURE_DIR = gestures
+endif
+
SUBDIRS = \
$(CAP_DIR) \
- gestures \
+ $(GESTURE_DIR) \
$(GEVOLUTION_DIR) \
$(MUSICMESSAGING_DIR) \
$(PERL_DIR) \
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/plugins/musicmessaging/musicmessaging.c
--- a/pidgin/plugins/musicmessaging/musicmessaging.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/plugins/musicmessaging/musicmessaging.c Thu Aug 30 00:09:47 2007 +0000
@@ -71,10 +71,10 @@
/* Globals */
/* List of sessions */
-GList *conversations;
+static GList *conversations;
/* Pointer to this plugin */
-PurplePlugin *plugin_pointer;
+static PurplePlugin *plugin_pointer;
/* Define types needed for DBus */
DBusGConnection *connection;
@@ -350,7 +350,16 @@
static gboolean
intercept_received(PurpleAccount *account, char **sender, char **message, PurpleConversation *conv, int *flags)
{
- MMConversation *mmconv = mmconv_from_conv(conv);
+ MMConversation *mmconv;
+
+ if (conv == NULL) {
+ /* XXX: This is just to avoid a crash (#2726).
+ * We may want to create the conversation instead of returning from here
+ */
+ return FALSE;
+ }
+
+ mmconv = mmconv_from_conv(conv);
purple_debug_misc("purple-musicmessaging", "Intercepted: %s\n", *message);
if (strstr(*message, MUSICMESSAGING_PREFIX))
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/plugins/spellchk.c
--- a/pidgin/plugins/spellchk.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/plugins/spellchk.c Thu Aug 30 00:09:47 2007 +0000
@@ -667,7 +667,7 @@
return;
}
-static int buf_get_line(char *ibuf, char **buf, int *position, int len)
+static int buf_get_line(char *ibuf, char **buf, int *position, gsize len)
{
int pos = *position;
int spos = pos;
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/plugins/win32/transparency/win2ktrans.c
--- a/pidgin/plugins/win32/transparency/win2ktrans.c Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/plugins/win32/transparency/win2ktrans.c Thu Aug 30 00:09:47 2007 +0000
@@ -400,7 +400,7 @@
g_object_get(G_OBJECT(window), "has-toplevel-focus", &has_focus, NULL);
- if (!has_focus)
+ if (!has_focus || !purple_prefs_get_bool(OPT_WINTRANS_IM_ONFOCUS))
set_conv_window_trans(NULL, win);
if (g_signal_handler_find(G_OBJECT(window), G_SIGNAL_MATCH_FUNC,
diff -r 7c84cbb57972 -r a77432fe2f3b pidgin/win32/nsis/pidgin-installer.nsi
--- a/pidgin/win32/nsis/pidgin-installer.nsi Wed Aug 29 23:14:46 2007 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi Thu Aug 30 00:09:47 2007 +0000
@@ -503,8 +503,8 @@
${If} ${IsNT}
${AndIf} ${IsWinNT4}
Delete "$INSTDIR\plugins\libsilc.dll"
- Delete "$INSTDIR\silcclient.dll"
- Delete "$INSTDIR\silc.dll"
+ Delete "$INSTDIR\libsilcclient-1-1-2.dll"
+ Delete "$INSTDIR\libsilc-1-1-2.dll"
${EndIf}
SetOutPath "$INSTDIR"
@@ -554,6 +554,10 @@
Push "msnim"
Call RegisterURIHandler
SectionEnd
+ Section /o "myim:" SecURI_MYIM
+ Push "myim"
+ Call RegisterURIHandler
+ SectionEnd
Section /o "ymsgr:" SecURI_YMSGR
Push "ymsgr"
Call RegisterURIHandler
@@ -688,6 +692,9 @@
DeleteRegValue HKLM "${STARTUP_RUN_KEY}" "Pidgin"
; Remove Language preference info (TODO: check if NSIS removes this)
+ Delete "$INSTDIR\ca-certs\Equifax_Secure_CA.pem"
+ Delete "$INSTDIR\ca-certs\Verisign_RSA_Secure_Server_CA.pem"
+ RMDir "$INSTDIR\ca-certs"
RMDir /r "$INSTDIR\locale"
RMDir /r "$INSTDIR\pixmaps"
RMDir /r "$INSTDIR\perlmod"
@@ -706,6 +713,7 @@
Delete "$INSTDIR\plugins\libicq.dll"
Delete "$INSTDIR\plugins\libirc.dll"
Delete "$INSTDIR\plugins\libmsn.dll"
+ Delete "$INSTDIR\plugins\libmyspace.dll"
Delete "$INSTDIR\plugins\libnapster.dll"
Delete "$INSTDIR\plugins\libnovell.dll"
Delete "$INSTDIR\plugins\libqq.dll"
@@ -756,8 +764,9 @@
Delete "$INSTDIR\pidgin.dll"
Delete "$INSTDIR\plc4.dll"
Delete "$INSTDIR\plds4.dll"
- Delete "$INSTDIR\silc.dll"
- Delete "$INSTDIR\silcclient.dll"
+ Delete "$INSTDIR\libsilc-1-1-2.dll"
+ Delete "$INSTDIR\libsilcclient-1-1-2.dll"
+ Delete "$INSTDIR\smime3.dll"
Delete "$INSTDIR\softokn3.dll"
Delete "$INSTDIR\ssl3.dll"
Delete "$INSTDIR\${PIDGIN_UNINST_EXE}"
diff -r 7c84cbb57972 -r a77432fe2f3b po/pt_BR.po
--- a/po/pt_BR.po Wed Aug 29 23:14:46 2007 +0000
+++ b/po/pt_BR.po Thu Aug 30 00:09:47 2007 +0000
@@ -14207,7 +14207,7 @@
"Favor especificar o que você estava fazendo na hora em que o erro ocorreu\n"
"e enviar o backtrace do arquivo de core. Se você não sabe\n"
"como obter o backtrace, favor ler as instruções em\n"
-"%swiki/GetABackTrace\n"
+"%swiki/GetABacktrace\n"
"\n"
"Se você precisa de assistência adicional, mande uma mensagem instantânea\n"
"para o SeanEgn ou o LSchiere (via AIM e em inglês). Informações de contato\n"
diff -r 7c84cbb57972 -r a77432fe2f3b share/Makefile.am
--- a/share/Makefile.am Wed Aug 29 23:14:46 2007 +0000
+++ b/share/Makefile.am Thu Aug 30 00:09:47 2007 +0000
@@ -1,4 +1,4 @@
-SUBDIRS = sounds
+SUBDIRS = sounds ca-certs
EXTRA_DIST = Makefile.mingw
diff -r 7c84cbb57972 -r a77432fe2f3b share/ca-certs/Equifax_Secure_CA.pem
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/Equifax_Secure_CA.pem Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG
+EwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1
+cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4
+MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgx
+LTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0
+eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2R
+FGiYCh7+2gRvE4RiIcPRfM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO
+/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuv
+K9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAGA1UdHwRp
+MGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEt
+MCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
+MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjAL
+BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gjIBBPM5iQn9Qw
+HQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMBAf8w
+GgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GB
+AFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
+7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2u
+FHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
+-----END CERTIFICATE-----
diff -r 7c84cbb57972 -r a77432fe2f3b share/ca-certs/GTE_CyberTrust_Global_Root.pem
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/GTE_CyberTrust_Global_Root.pem Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgw
+FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRy
+dXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg
+R2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1
+MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYD
+VQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMT
+GkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4usJTQGz0O9pTAipTHBsiQl8i4
+ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcqlHHK6XALn
+ZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8F
+LztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh3
+46B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq
+81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0PlZPvy5TYnh+d
+XIVtx6quTx8itc2VrbqnzPmrC3p/
+-----END CERTIFICATE-----
diff -r 7c84cbb57972 -r a77432fe2f3b share/ca-certs/Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/Makefile.am Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,10 @@
+cacertsdir = $(datadir)/purple/ca-certs
+cacerts_DATA = \
+ Equifax_Secure_CA.pem \
+ GTE_CyberTrust_Global_Root.pem \
+ Verisign_RSA_Secure_Server_CA.pem \
+ Verisign_Class3_Primary_CA.pem
+
+EXTRA_DIST = \
+ $(cacerts_DATA)
+
diff -r 7c84cbb57972 -r a77432fe2f3b share/ca-certs/Makefile.mingw
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/Makefile.mingw Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,21 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for win32 (mingw) version of Pidgin ca-certs
+#
+
+PIDGIN_TREE_TOP := ../..
+include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+
+datadir := $(PIDGIN_INSTALL_DIR)
+include ./Makefile.am
+cacertsdir := $(PIDGIN_INSTALL_DIR)/ca-certs
+
+.PHONY: install
+
+install:
+ if test '$(cacerts_DATA)'; then \
+ mkdir -p $(cacertsdir); \
+ cp $(cacerts_DATA) $(cacertsdir); \
+ fi;
+
diff -r 7c84cbb57972 -r a77432fe2f3b share/ca-certs/Verisign_Class3_Primary_CA.pem
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/Verisign_Class3_Primary_CA.pem Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
+A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
+MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
+BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
+BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
+I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
+CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
+lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
+AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
+-----END CERTIFICATE-----
diff -r 7c84cbb57972 -r a77432fe2f3b share/ca-certs/Verisign_RSA_Secure_Server_CA.pem
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/Verisign_RSA_Secure_Server_CA.pem Thu Aug 30 00:09:47 2007 +0000
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzEL
+MAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMu
+MS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MB4XDTk0MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UE
+BhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD
+VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGb
+MA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6O
+LDfO6zV4ZFQD5YRAUcm/jwjiioII0haGN1XpsSECrXZogZoFokvJSyVmIlZs
+iAeP94FZbYQHZXATcXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJVCxzOmmC
+sZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZIhvcNAQECBQADfgBl3X7hsuyw
+4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3YQO2WxZpO8ZECAyIUwxr
+l0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc1/p3yjkWWW8O6tO1
+g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA==
+-----END CERTIFICATE-----