static void detach_signals(PurpleConversation *conv) { PidginConversation *gtkconv = NULL; PidginWindow *gtkwin = NULL; GSList *ids = NULL, *l; gtkconv = PIDGIN_CONVERSATION(conv); if (!gtkconv) return; gtkwin = gtkconv->win; ids = purple_conversation_get_data(conv, "notify-imhtml-signals"); for (l = ids; l != NULL; l = l->next) g_signal_handler_disconnect(gtkconv->imhtml, GPOINTER_TO_INT(l->data)); g_slist_free(ids); ids = purple_conversation_get_data(conv, "notify-entry-signals"); for (l = ids; l != NULL; l = l->next) g_signal_handler_disconnect(gtkconv->entry, GPOINTER_TO_INT(l->data)); g_slist_free(ids); purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0)); purple_conversation_set_data(conv, "notify-imhtml-signals", NULL); purple_conversation_set_data(conv, "notify-entry-signals", NULL); }
static void hide_conv(PidginConversation *gtkconv, gboolean closetimer) { GList *list; purple_signal_emit(pidgin_conversations_get_handle(), "conversation-hiding", gtkconv); for (list = g_list_copy(gtkconv->convs); list; list = g_list_delete_link(list, list)) { PurpleConversation *conv = list->data; if (closetimer) { guint timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer")); if (timer) purple_timeout_remove(timer); timer = purple_timeout_add_seconds(CLOSE_CONV_TIMEOUT_SECS, close_already, conv); purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(timer)); } #if 0 /* I will miss you */ purple_conversation_set_ui_ops(conv, NULL); #else pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv); pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv); #endif } }
static int unnotify_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv) { if (GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count")) != 0) unnotify(conv, TRUE); return 0; }
static void twitter_conv_icon_add_conv(TwitterConvIcon * conv_icon, PurpleConversation * conv) { GList **conv_conv_icons = purple_conversation_get_data(conv, PLUGIN_ID "-conv-icons"); g_return_if_fail(conv_conv_icons != NULL); if (!g_list_find(conv_icon->convs, conv)) { conv_icon->convs = g_list_prepend(conv_icon->convs, conv); *conv_conv_icons = g_list_prepend(*conv_conv_icons, conv_icon); } }
static void twitter_conv_icon_deleting_conversation_cb(PurpleConversation * conv, PurpleAccount * account) { GList **conv_icons; if (purple_conversation_get_account(conv) != account) return; twitter_conv_icon_remove_conversation_conv_icons(conv); conv_icons = purple_conversation_get_data(conv, PLUGIN_ID "-conv-icons"); g_free(conv_icons); }
static void twitter_conv_icon_displayed_chat_cb(PurpleAccount * account, const char *who, char *message, PurpleConversation * conv, PurpleMessageFlags flags, void *account_signal) { GtkIMHtml *imhtml; GtkTextBuffer *text_buffer; GtkTextIter insertion_point; gint linenumber; TwitterConvIcon *conv_icon; PurpleConnection *gc; TwitterConnectionData *twitter; if (account != account_signal) return; gc = purple_account_get_connection(account); if (!gc) { return; } twitter = gc->proto_data; purple_debug_info(PLUGIN_ID, "%s\n", G_STRFUNC); /* insert icon */ imhtml = GTK_IMHTML(PIDGIN_CONVERSATION(conv)->imhtml); text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)); /* get GtkTextIter in the target line */ linenumber = GPOINTER_TO_INT(purple_conversation_get_data(conv, PLUGIN_ID "-icon-ln")); gtk_text_buffer_get_iter_at_line(text_buffer, &insertion_point, linenumber); conv_icon = twitter_conv_icon_find(account, who); /* if we don't have the icon for this user, put a mark instead and * request the icon */ if (!conv_icon) { conv_icon = twitter_conv_icon_new(account, who); twitter_conv_icon_add_conv(conv_icon, conv); g_hash_table_insert(twitter->icons, g_strdup(purple_normalize(account, who)), conv_icon); mark_icon_for_user(gtk_text_buffer_create_mark(text_buffer, NULL, &insertion_point, FALSE), conv_icon); return; } twitter_conv_icon_add_conv(conv_icon, conv); /* if we have the icon for this user, insert the icon immediately */ if (TRUE) { if (conv_icon->pixbuf) { gtk_text_buffer_insert_pixbuf(text_buffer, &insertion_point, conv_icon->pixbuf); } else { mark_icon_for_user(gtk_text_buffer_create_mark(text_buffer, NULL, &insertion_point, FALSE), conv_icon); } } purple_debug_info(PLUGIN_ID, "end %s\n", G_STRFUNC); }
std::string PurpleLine::conv_attachment_add(PurpleConversation *conv, line::ContentType::type type, std::string id) { auto atts = (std::vector<Attachment> *)purple_conversation_get_data(conv, "line-attachments"); if (!atts) { atts = new std::vector<Attachment>(); purple_conversation_set_data(conv, "line-attachments", atts); } atts->emplace_back(type, id); return std::to_string(atts->size()); }
PurpleLine::Attachment *PurpleLine::conv_attachment_get(PurpleConversation *conv, std::string token) { int index; try { index = std::stoi(token); } catch (...) { return nullptr; } auto atts = (std::vector<Attachment> *)purple_conversation_get_data(conv, "line-attachments"); return (atts && index <= (int)atts->size()) ? &(*atts)[index - 1] : nullptr; }
static guint count_messages(PidginWindow *purplewin) { guint count = 0; GList *convs = NULL, *l; for (convs = purplewin->gtkconvs; convs != NULL; convs = convs->next) { PidginConversation *conv = convs->data; for (l = conv->convs; l != NULL; l = l->next) { count += GPOINTER_TO_INT(purple_conversation_get_data(l->data, "notify-message-count")); } } return count; }
void PurpleLine::signal_deleting_conversation(PurpleConversation *conv) { if (purple_conversation_get_account(conv) != acct) return; auto queue = (std::vector<line::Message> *) purple_conversation_get_data(conv, "line-message-queue"); if (queue) { purple_conversation_set_data(conv, "line-message-queue", nullptr); delete queue; } int64_t *end_seq_p = (int64_t *)purple_conversation_get_data(conv, "line-end-seq"); if (end_seq_p) { purple_conversation_set_data(conv, "line-end-seq", nullptr); delete end_seq_p; } auto atts = (std::vector<Attachment> *)purple_conversation_get_data(conv, "line-attachments"); if (atts) { purple_conversation_set_data(conv, "line-attachments", nullptr); delete atts; } }
static void twitter_conv_icon_remove_conversation_conv_icons(PurpleConversation * conv) { GList **conv_icons; GList *l; g_return_if_fail(conv != NULL); purple_debug_info(PLUGIN_ID, "%s conv %s\n", G_STRFUNC, purple_conversation_get_name(conv)); conv_icons = purple_conversation_get_data(conv, PLUGIN_ID "-conv-icons"); if (conv_icons) { for (l = *conv_icons; l; l = l->next) { TwitterConvIcon *conv_icon = l->data; twitter_conv_icon_remove_conv(conv_icon, conv); } g_list_free(*conv_icons); *conv_icons = NULL; } }
static void apply_method() { GList *convs; PidginWindow *purplewin = NULL; for (convs = purple_get_conversations(); convs != NULL; convs = convs->next) { PurpleConversation *conv = (PurpleConversation *)convs->data; /* remove notifications */ unnotify(conv, FALSE); purplewin = PIDGIN_CONVERSATION(conv)->win; if (GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count")) != 0) /* reattach appropriate notifications */ notify(conv, FALSE); } }
static gboolean timestamp_displaying_conv_msg(PurpleAccount *account, const char *who, char **buffer, PurpleConversation *conv, PurpleMessageFlags flags, void *data) { time_t now = time(NULL) / interval * interval; time_t then; if (!g_list_find(purple_get_conversations(), conv)) return FALSE; then = GPOINTER_TO_INT(purple_conversation_get_data( conv, "timestamp-last")); if (now - then >= interval) { timestamp_display(conv, then, now); purple_conversation_set_data( conv, "timestamp-last", GINT_TO_POINTER(now)); } return FALSE; }
static int notify(PurpleConversation *conv, gboolean increment) { gint count; gboolean has_focus; PidginWindow *purplewin = NULL; if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL) return 0; /* We want to remove the notifications, but not reset the counter */ unnotify(conv, FALSE); purplewin = PIDGIN_CONVERSATION(conv)->win; /* If we aren't doing notifications for this type of conversation, return */ if (((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) && !purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im")) || ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) && !purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat"))) return 0; g_object_get(G_OBJECT(purplewin->window), "has-toplevel-focus", &has_focus, NULL); if (purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused") || !has_focus) { if (increment) { count = GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count")); count++; purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(count)); } notify_win(purplewin, conv); } return 0; }
int purple_conversation_get_unseen(PurpleConversation *conv) { return GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")); }
void PurpleLine::fetch_conversation_history(PurpleConversation *conv, int count, bool requested) { PurpleConversationType type = conv->type; std::string name(purple_conversation_get_name(conv)); int64_t end_seq = -1; int64_t *end_seq_p = (int64_t *)purple_conversation_get_data(conv, "line-end-seq"); if (end_seq_p) end_seq = *end_seq_p; if (end_seq != -1) c_out->send_getPreviousMessages(name, end_seq - 1, count); else c_out->send_getRecentMessages(name, count); c_out->send([this, requested, type, name, end_seq]() { int64_t new_end_seq = end_seq; std::vector<line::Message> recent_msgs; if (end_seq != -1) c_out->recv_getPreviousMessages(recent_msgs); else c_out->recv_getRecentMessages(recent_msgs); PurpleConversation *conv = purple_find_conversation_with_account(type, name.c_str(), acct); if (!conv) return; // Conversation died while fetching messages auto *queue = (std::vector<line::Message> *) purple_conversation_get_data(conv, "line-message-queue"); purple_conversation_set_data(conv, "line-message-queue", nullptr); // Find least seq value from messages for future history queries for (line::Message &msg: recent_msgs) { if (msg.contentMetadata.count("seq")) { try { int64_t seq = std::stoll(msg.contentMetadata["seq"]); if (new_end_seq == -1 || seq < new_end_seq) new_end_seq = seq; } catch (...) { /* ignore parse error */ } } } if (queue) { // If there's a message queue, remove any already-queued messages in the recent message // list to prevent them showing up twice. recent_msgs.erase( std::remove_if( recent_msgs.begin(), recent_msgs.end(), [&queue](line::Message &rm) { auto r = find_if( queue->begin(), queue->end(), [&rm](line::Message &qm) { return qm.id == rm.id; }); return (r != queue->end()); }), recent_msgs.end()); } if (recent_msgs.size()) { purple_conversation_write( conv, "", "<strong>Message history</strong>", (PurpleMessageFlags)PURPLE_MESSAGE_RAW, time(NULL)); for (auto msgi = recent_msgs.rbegin(); msgi != recent_msgs.rend(); msgi++) write_message(*msgi, true); purple_conversation_write( conv, "", "<hr>", (PurpleMessageFlags)PURPLE_MESSAGE_RAW, time(NULL)); } else { if (requested) { // If history was requested by the user and there is none, let the user know purple_conversation_write( conv, "", "<strong>No more history</strong>", (PurpleMessageFlags)PURPLE_MESSAGE_RAW, time(NULL)); } } // If there's a message queue, play it back now if (queue) { for (line::Message &msg: *queue) write_message(msg, false); delete queue; } int64_t *end_seq_p = (int64_t *)purple_conversation_get_data(conv, "line-end-seq"); if (end_seq_p) delete end_seq_p; purple_conversation_set_data(conv, "line-end-seq", new int64_t(new_end_seq)); }); }
void PurpleLine::write_message(line::Message &msg, bool replay) { std::string text; int flags = 0; time_t mtime = (time_t)(msg.createdTime / 1000); bool sent = (msg.from == profile.mid); if (std::find(recent_messages.cbegin(), recent_messages.cend(), msg.id) != recent_messages.cend()) { // We already processed this message. User is probably talking with himself. return; } // Hack if (msg.from == msg.to) push_recent_message(msg.id); PurpleConversation *conv = purple_find_conversation_with_account( (msg.toType == line::MIDType::USER ? PURPLE_CONV_TYPE_IM : PURPLE_CONV_TYPE_CHAT), ((!sent && msg.toType == line::MIDType::USER) ? msg.from.c_str() : msg.to.c_str()), acct); // If this is a new received IM, create the conversation if it doesn't exist if (!conv && !sent && msg.toType == line::MIDType::USER) conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, msg.from.c_str()); // If this is a new conversation, we're not replaying history and history hasn't been fetched // yet, queue the message instead of showing it. if (conv && !replay) { auto *queue = (std::vector<line::Message> *) purple_conversation_get_data(conv, "line-message-queue"); if (queue) { queue->push_back(msg); return; } } // Replaying messages from history // Unfortunately Pidgin displays messages with this flag with odd formatting and no username. // Disable for now. //if (replay) // flags |= PURPLE_MESSAGE_NO_LOG; switch (msg.contentType) { case line::ContentType::NONE: // actually text case line::ContentType::LOCATION: if (msg.__isset.location) { line::Location &loc = msg.location; text = markup_escape(loc.title) + " | <a href=\"https://maps.google.com/?q=" + url_encode(loc.address) + "&ll=" + std::to_string(loc.latitude) + "," + std::to_string(loc.longitude) + "\">" + (loc.address.size() ? markup_escape(loc.address) : "(no address)") + "</a>"; } else { text = markup_escape(msg.text); } break; case line::ContentType::STICKER: { std::string id = get_sticker_id(msg); if (id == "") { text = "<em>[Broken sticker]</em>"; purple_debug_warning("line", "Got a broken sticker.\n"); } else { text = id; if (conv && purple_conv_custom_smiley_add(conv, id.c_str(), "id", id.c_str(), TRUE)) { http.request(get_sticker_url(msg), [this, id, conv](int status, const guchar *data, gsize len) { if (status == 200 && data && len > 0) { purple_conv_custom_smiley_write( conv, id.c_str(), data, len); } else { purple_debug_warning( "line", "Couldn't download sticker. Status: %d\n", status); } purple_conv_custom_smiley_close(conv, id.c_str()); }); } } } break; case line::ContentType::IMAGE: case line::ContentType::VIDEO: // Videos could really benefit from streaming... { std::string type_std = line::_ContentType_VALUES_TO_NAMES.at(msg.contentType); std::string id = "[LINE " + type_std + " " + msg.id + "]"; text = id; if (conv) { text += " <font color=\"#888888\">/open " + conv_attachment_add(conv, msg.contentType, msg.id) + "</font>"; } if (!conv || !purple_conv_custom_smiley_add(conv, id.c_str(), "id", id.c_str(), TRUE)) { break; } if (msg.contentPreview.size() > 0) { purple_conv_custom_smiley_write( conv, id.c_str(), (const guchar *)msg.contentPreview.c_str(), msg.contentPreview.size()); purple_conv_custom_smiley_close(conv, id.c_str()); } else { std::string preview_url = msg.contentMetadata.count("PREVIEW_URL") ? msg.contentMetadata["PREVIEW_URL"] : std::string(LINE_OS_URL) + "os/m/" + msg.id + "/preview"; http.request(preview_url, HTTPFlag::AUTH | HTTPFlag::LARGE, [this, id, conv](int status, const guchar *data, gsize len) { if (status == 200 && data && len > 0) { purple_conv_custom_smiley_write( conv, id.c_str(), data, len); } else { purple_debug_warning( "line", "Couldn't download image message. Status: %d\n", status); } purple_conv_custom_smiley_close(conv, id.c_str()); }); } } break; case line::ContentType::AUDIO: { text = "[Audio message"; if (msg.contentMetadata.count("AUDLEN")) { int len = 0; try { len = std::stoi(msg.contentMetadata["AUDLEN"]); } catch(...) { /* ignore */ } if (len > 0) { text += " " + std::to_string(len / 1000) + "." + std::to_string((len % 1000) / 100) + "s"; } } text += "]"; if (conv) { text += " <font color=\"#888888\">/open " + conv_attachment_add(conv, msg.contentType, msg.id) + "</font>"; } } break; // TODO: other content types default: text = "<em>[Not implemented: "; text += line::_ContentType_VALUES_TO_NAMES.at(msg.contentType); text += " message]</em>"; break; } if (sent) { // Messages sent by user (sync from other devices) write_message(conv, msg.from, text, mtime, flags | PURPLE_MESSAGE_SEND); } else { // Messages received from other users flags |= PURPLE_MESSAGE_RECV; if (replay) { // Write replayed messages instead of serv_got_* to avoid Pidgin's IM sound write_message(conv, msg.from, text, mtime, flags); } else { if (msg.toType == line::MIDType::USER) { serv_got_im( conn, msg.from.c_str(), text.c_str(), (PurpleMessageFlags)flags, mtime); } else if (msg.toType == line::MIDType::GROUP || msg.toType == line::MIDType::ROOM) { serv_got_chat_in( conn, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), msg.from.c_str(), (PurpleMessageFlags)flags, text.c_str(), mtime); } } } }
void irc_msg_names(struct irc_conn *irc, const char *name, const char *from, char **args) { char *names, *cur, *end, *tmp, *msg; PurpleConversation *convo; if (!strcmp(name, "366")) { convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, args[1], irc->account); if (!convo) { purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got a NAMES list for %s, which doesn't exist\n", args[1]); g_string_free(irc->names, TRUE); irc->names = NULL; return; } names = cur = g_string_free(irc->names, FALSE); irc->names = NULL; if (purple_conversation_get_data(convo, IRC_NAMES_FLAG)) { msg = g_strdup_printf(_("Users on %s: %s"), args[1], names ? names : ""); if (purple_conversation_get_type(convo) == PURPLE_CONV_TYPE_CHAT) purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL)); else purple_conv_im_write(PURPLE_CONV_IM(convo), "", msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL)); g_free(msg); } else { GList *users = NULL; GList *flags = NULL; while (*cur) { PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE; end = strchr(cur, ' '); if (!end) end = cur + strlen(cur); if (*cur == '@') { f = PURPLE_CBFLAGS_OP; cur++; } else if (*cur == '%') { f = PURPLE_CBFLAGS_HALFOP; cur++; } else if(*cur == '+') { f = PURPLE_CBFLAGS_VOICE; cur++; } else if(irc->mode_chars && strchr(irc->mode_chars, *cur)) { if (*cur == '~') f = PURPLE_CBFLAGS_FOUNDER; cur++; } tmp = g_strndup(cur, end - cur); users = g_list_prepend(users, tmp); flags = g_list_prepend(flags, GINT_TO_POINTER(f)); cur = end; if (*cur) cur++; } if (users != NULL) { GList *l; purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE); for (l = users; l != NULL; l = l->next) g_free(l->data); g_list_free(users); g_list_free(flags); } purple_conversation_set_data(convo, IRC_NAMES_FLAG, GINT_TO_POINTER(TRUE)); } g_free(names); } else { if (!irc->names) irc->names = g_string_new(""); if (irc->names->len && irc->names->str[irc->names->len - 1] != ' ') irc->names = g_string_append_c(irc->names, ' '); irc->names = g_string_append(irc->names, args[3]); } }