/* Silky - A GTK+ client for SILC. Copyright (C) 2003,2004 Toni Willberg - Handlers for SILC_NOTIFY_* server messages 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 http://silky.sourceforge.net/ */ #include #ifdef HAVE_CONFIG_H # include #endif #include "ui_buddylist.h" #include "ui_channel.h" #include "ui_console.h" #include "common.h" #include "support.h" #include "gui.h" #include "silc-notify.h" #include "log.h" #include "channel.h" #include "callbacks.h" extern silkyStruct *silky; extern GladeXML *xmlmain; extern gchar *previous_nick; /* my nickname just before sending NICK command */ extern gchar * glade_silky_channel; extern gchar * glade_silky_channel_label; extern SilkyChannel *channels; extern gint lastpong; void silc_notify(SilcClient client, SilcClientConnection conn, SilcNotifyType type, ...) { SilcIdType idtype; void *entry; SilcClientEntry client_entry; SilcClientEntry client_entry2; SilcChannelEntry channel_entry = NULL; SilcClientID *client_id; SilcStatus status; char *str; gchar *str_clientid; gchar *str_fingerprint; GtkNotebook *storednotebook; gchar *str_client_id; gchar *str_client_id2; GtkNotebook *notebook; GtkWidget *stored_query_widget; GtkWidget *wid_tab_label; GladeXML *stored_query_xml; GtkLabel *label_nickname; GtkWidget *wid_channel_topic; SilcUInt32 mode; gchar *changer = NULL; SilcServerEntry serverentry; SilcChannelEntry channelentry; gchar *str_mode; SilcHashTableList souserhtl; SilcChannelUser chu = NULL; SilcBuffer channel_pubkeys; SilcNotifyType notification; va_list va; debug("silc_notify()"); va_start(va, type); lastpong = time(NULL); /* update date of last received data, used in timer.c */ /* This is the part where we receive all kind of different data from the server. silky is only interested in receiving the less important stuff... */ switch(type) { case SILC_NOTIFY_TYPE_NONE: debug_info("SILC_NOTIFY_TYPE_NONE"); str = va_arg(va, char *); printconsole(g_strdup_printf("%s", str)); break; case SILC_NOTIFY_TYPE_MOTD: debug_info("SILC_NOTIFY_TYPE_MOTD"); str = va_arg(va, char *); printconsole(g_strdup_printf("%s", str)); break; case SILC_NOTIFY_TYPE_ERROR: status = va_arg(va, int ); debug_info("SILC_NOTIFY_TYPE_ERROR (%d)", status); switch(status) { /* After sending a private message; There was no such user */ case SILC_STATUS_ERR_NO_SUCH_CLIENT_ID: debug_info("No such client id"); client_id = va_arg(va, SilcClientID *); if (!client_id) { debug("Did not get client_id in _NO_SUCH_CLIENT_ID"); } str_clientid = silc_id_id2str(client_id, SILC_ID_CLIENT); client_entry = silc_client_get_client_by_id(silky->client, silky->conn, client_id); if (!client_entry) { debug("Did not find client_entry"); } else { debug_info("\tnick: '%s'", client_entry->nickname); } printconsole(_("No such user.")); break; default: debug("Unhandled error"); break; } break; case SILC_NOTIFY_TYPE_KICKED: debug_info("SILC_NOTIFY_TYPE_KICKED"); client_entry = va_arg(va, SilcClientEntry); str = va_arg(va, char *); client_entry2 = va_arg(va, SilcClientEntry); channel_entry = va_arg(va, SilcChannelEntry); if (SILC_ID_COMPARE(client_entry->id, conn->local_entry->id, sizeof(SilcClientID) ) ) { /* I GOT KICKED */ debug_info("I have been kicked out from the channel '%s'", channel_entry->channel_name); /* Close the tab in UI */ storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs")); gtk_widget_destroy(get_stored_channel_window(channel_entry->channel_name)); g_object_set_data(G_OBJECT(storednotebook), channel_entry->channel_name, NULL); /* check that all tabs are associated with correct data structures, since their numbers may have changed */ gui_renumber_tabs(); /* remove from ch */ channel_remove_by_entry(channel_entry); printconsole(g_strdup_printf(_("%s kicked me from channel '%s' with reason '%s'."), client_entry2->nickname, channel_entry->channel_name, str)); } else { printchannel(channel_entry->channel_name, g_strdup_printf(_("%s kicked %s from from channel '%s' with reason '%s'."), client_entry2->nickname, /* kicker */ client_entry->nickname, /* target */ channel_entry->channel_name, str)); debug("freeing channel member from list..."); channel_member_remove_by_entry(channel_find_by_entry(channel_entry), client_entry); /* refresh the nicklist */ refresh_gui_nicklist(channel_find_by_entry(channel_entry)); } break; case SILC_NOTIFY_TYPE_JOIN: debug_info("SILC_NOTIFY_TYPE_JOIN"); client_entry = va_arg(va, SilcClientEntry); channel_entry = va_arg(va, SilcChannelEntry); debug_info("'%s' (%s@%s) joined '%s'", client_entry->nickname, client_entry->username, client_entry->hostname, channel_entry->channel_name); printchannel(channel_entry->channel_name, g_strdup_printf(_("%s (%s@%s) has joined %s"), client_entry->nickname, client_entry->username, client_entry->hostname, channel_entry->channel_name)); /* not yet fully implemented, disabled for now silky_print(MSG_MOTION | MSG_PUBLIC, channel_entry, "%s joined %s", client_entry->nickname, channel_entry->channel_name); */ /* Did I join? */ if (!SILC_ID_COMPARE(client_entry->id, conn->local_entry->id, sizeof(SilcClientID) ) ) { debug("Added user to the nicklist."); channel_member_add(channel_find_by_entry(channel_entry), get_channel_user_by_entry(channel_entry, client_entry)); refresh_gui_nicklist(channel_find_by_entry(channel_entry)); } else { debug("I joined. No need to add myself to the userlist."); } break; case SILC_NOTIFY_TYPE_LEAVE: debug_info("SILC_NOTIFY_TYPE_LEAVE"); client_entry = va_arg(va, SilcClientEntry); channel_entry = va_arg(va, SilcChannelEntry); debug_info("'%s' (%s@%s) left '%s'", client_entry->nickname, client_entry->username, client_entry->hostname, channel_entry->channel_name); printchannel(channel_entry->channel_name, g_strdup_printf(_("%s (%s@%s) left %s"), client_entry->nickname, client_entry->username, client_entry->hostname, channel_entry->channel_name)); debug("freeing channel member from list..."); channel_member_remove_by_entry(channel_find_by_entry(channel_entry), client_entry); /* Ask the client library to re-resolve the channel */ refresh_gui_nicklist(channel_find_by_entry(channel_entry)); break; case SILC_NOTIFY_TYPE_NICK_CHANGE: debug("SILC_NOTIFY_TYPE_NICK_CHANGE"); client_entry = va_arg(va, SilcClientEntry); client_entry2 = va_arg(va, SilcClientEntry); str_client_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT); str_client_id2 = silc_id_id2str(client_entry2->id, SILC_ID_CLIENT); debug_info("NICK_CHANGE: '%s' is now known as '%s'", client_entry->nickname, client_entry2->nickname); /* Update the clientid and nickname of the user to the GUI */ /* get notebook widget */ notebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs")); /* get query widget, if any */ stored_query_widget = g_object_get_data(G_OBJECT(notebook), str_client_id); if (stored_query_widget) { /* remove the old clientid */ g_object_set_data(G_OBJECT(notebook), str_client_id, NULL); /* make the new clientid to point to the old query widget */ g_object_set_data(G_OBJECT(notebook), str_client_id2, stored_query_widget); g_object_set_data(G_OBJECT(stored_query_widget), "str_clientid", str_client_id2); g_object_set_data(G_OBJECT(stored_query_widget), "clientid", client_entry2->id); g_object_set_data(G_OBJECT(stored_query_widget), "cliententry", client_entry2); /* get the tab widget for nickname */ wid_tab_label = g_object_get_data(G_OBJECT(stored_query_widget), "tablabel"); if (!wid_tab_label) { debug("Did not find wid_tab_label"); } /* Update the nickname into the top of query window */ stored_query_xml = glade_get_widget_tree(stored_query_widget); label_nickname = GTK_LABEL(glade_xml_get_widget (stored_query_xml, "query_nickname")); gtk_label_set_text(label_nickname, client_entry2->nickname); /* Update the nickname into the tab label */ gtk_label_set_text(GTK_LABEL(wid_tab_label), client_entry2->nickname); } else { debug("Did not get stored_query_widget. This means I have no query open with the user who just changed his nick."); } /* FIXME: update other widgets too */ /* This must be last */ /* Update the nicklist of channel in the GUI */ channels_nick_change(client_entry, client_entry->id, client_entry2); break; case SILC_NOTIFY_TYPE_TOPIC_SET: debug_info("SILC_NOTIFY_TYPE_TOPIC_SET"); /* SilcIdType setter_id_type, void *setter_entry, char *topic, SilcChannelEntry channel */ idtype = va_arg(va, int); entry = va_arg(va, void *); str = va_arg(va, char *); channel_entry = va_arg(va, SilcChannelEntry); if (idtype == SILC_ID_CLIENT) { client_entry = (SilcClientEntry)entry; /* Set new topic to UI */ wid_channel_topic = get_stored_channel_widget(channel_entry->channel_name, "channeltopic"); gtk_entry_set_text(GTK_ENTRY(wid_channel_topic), str); channel_topic_set_by_entry( channel_entry, str ); debug_info(channel_entry->channel_name, g_strdup_printf(_("%s (%s@%s) changed topic for channel %s to '%s'"), client_entry->nickname, client_entry->username, client_entry->hostname, channel_entry->channel_name, str)); printchannel(channel_entry->channel_name, g_strdup_printf(_("%s (%s@%s) changed topic for channel %s to '%s'"), client_entry->nickname, client_entry->username, client_entry->hostname, channel_entry->channel_name, str)); } else { printchannel(channel_entry->channel_name, _("UNHANDLED TOPIC SET EVENT: Please report a bug to Silky development team")); } break; case SILC_NOTIFY_TYPE_CMODE_CHANGE: debug_info("SILC_NOTIFY_TYPE_CMODE_CHANGE:"); /* get attributes from va_list */ idtype = va_arg(va, int); /* changer's id */ entry = va_arg(va, void *); mode = va_arg(va, SilcUInt32); (void)va_arg(va, char *); /* cipher_name */ (void)va_arg(va, char *); /* hmac_name */ (void)va_arg(va, char *); /* passphrase */ (void)va_arg(va, SilcPublicKey); /* founder_key */ channel_pubkeys = va_arg(va, SilcBuffer); channel_entry = va_arg(va, SilcChannelEntry); /* verify we got the channel_entry */ if (channel_entry == NULL) { debug("No channel entry, skipping."); return; } /* Set some UI stuff. */ str_mode = set_cmode_window_widgets(channel_entry->channel_name, mode); /* set modes in the UI */ channel_mode_set_by_entry( channel_entry, mode ); /* look up the type of the changer */ if (idtype == SILC_ID_CLIENT) { client_entry2 = (SilcClientEntry)entry; changer = client_entry2->nickname; printchannel(channel_entry->channel_name, g_strdup_printf(_("%s (%s@%s) changed mode on channel '%s' to %s"), changer, client_entry2->username, client_entry2->hostname, channel_entry->channel_name, str_mode)); } if (idtype == SILC_ID_SERVER) { serverentry = (SilcServerEntry)entry; changer = serverentry->server_name; printchannel(channel_entry->channel_name, g_strdup_printf(_("%s changed mode on channel '%s' to %s"), changer, channel_entry->channel_name, str_mode)); } if (idtype == SILC_ID_CHANNEL) { channelentry = (SilcChannelEntry)entry; changer = channelentry->channel_name; printchannel(channel_entry->channel_name, g_strdup_printf(_("Mode on '%s' is now '%s'"), channel_entry->channel_name, str_mode)); } silky_gui_check_channel_cmodes(channel_entry, str_mode); debug_info("*** '%s' changed mode '%s' on '%s'", changer, str_mode, channel_entry->channel_name); break; case SILC_NOTIFY_TYPE_CUMODE_CHANGE: debug_info("SILC_NOTIFY_TYPE_CUMODE_CHANGE:"); idtype = va_arg(va, int); entry = va_arg(va, void *); mode = va_arg(va, unsigned int); client_entry = va_arg(va, SilcClientEntry); channel_entry = va_arg(va, SilcChannelEntry); /* Refresh UI */ if (!channel_entry) { debug("No channel entry, skipping."); return; } /* Update GUI nicklist */ refresh_gui_nicklist(channel_find_by_entry(channel_entry)); /* look up the type of the changer */ if (idtype == SILC_ID_CLIENT) { client_entry2 = (SilcClientEntry)entry; changer = client_entry2->nickname; } if (idtype == SILC_ID_SERVER) { serverentry = (SilcServerEntry)entry; changer = serverentry->server_name; } if (idtype == SILC_ID_CHANNEL) { channelentry = (SilcChannelEntry)entry; changer = channelentry->channel_name; } /* set all modes cleared */ str_mode = g_strdup(""); /* clear dynamic string */ /* generate mode string for UI */ if (mode & SILC_CHANNEL_UMODE_NONE) { str_mode = g_strdup("none"); } if (mode & SILC_CHANNEL_UMODE_CHANFO) { str_mode = g_strconcat(str_mode, "f", NULL); } /* CHANOP */ if (mode & SILC_CHANNEL_UMODE_CHANOP) { str_mode = g_strconcat(str_mode, "o", NULL); /* if I got CHANOP, let me change the topic... */ if (client_entry == silky->conn->local_entry) { debug("I got @"); /* "change topic" -button */ wid_channel_topic = get_stored_channel_widget(channel_entry->channel_name, "button_change_topic"); gtk_widget_set_sensitive(wid_channel_topic, TRUE); /* text input */ wid_channel_topic = get_stored_channel_widget(channel_entry->channel_name, "channeltopic"); gtk_widget_set_sensitive(wid_channel_topic, TRUE); } } else { /* if I lost CHANOP, don't let me change the topic... */ if (client_entry == silky->conn->local_entry) { debug("I lost @"); /* "change topic" -button */ wid_channel_topic = get_stored_channel_widget(channel_entry->channel_name, "button_change_topic"); gtk_widget_set_sensitive(wid_channel_topic, FALSE); /* text input */ wid_channel_topic = get_stored_channel_widget(channel_entry->channel_name, "channeltopic"); gtk_widget_set_sensitive(wid_channel_topic, FALSE); } } if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) { str_mode = g_strconcat(str_mode, "b", NULL); } if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) { str_mode = g_strconcat(str_mode, "u", NULL); } if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) { str_mode = g_strconcat(str_mode, "r", NULL); } if (mode & SILC_CHANNEL_UMODE_QUIET) { str_mode = g_strconcat(str_mode, "q", NULL); } debug_info("*** '%s' changed mode '%s' for '%s' on '%s'", changer, str_mode, client_entry->nickname, channel_entry->channel_name); printchannel(channel_entry->channel_name, g_strdup_printf(_("%s changed mode for '%s' on '%s' to %s"), changer, client_entry->nickname, channel_entry->channel_name, str_mode)); break; /* INVITE: SilcClientChannel channel, char *channel_name, SilcClientEntry inviter */ case SILC_NOTIFY_TYPE_INVITE: debug_info("SILC_NOTIFY_TYPE_INVITE:"); channel_entry = va_arg(va, SilcChannelEntry); str = va_arg(va, char *); client_entry = va_arg(va, SilcClientEntry); debug_info("I was invited to channel '%s' by '%s (%s@%s)", str, client_entry->nickname, client_entry->username, client_entry->hostname); printconsole(g_strdup_printf(_("%s (%s@%s) invites you to channel %s."), client_entry->nickname, client_entry->username, client_entry->hostname, str)); gtk_label_set_markup(GTK_LABEL(glade_xml_get_widget(xmlmain, "label_recinv_topic")), g_strdup_printf("Join channel '%s'?", str)); gtk_label_set_markup(GTK_LABEL(glade_xml_get_widget(xmlmain, "label_recinv_text")), g_strdup_printf(_("You were invited by %s\n(%s@%s)"), client_entry->nickname, client_entry->username, client_entry->hostname)); gtk_widget_show(glade_xml_get_widget(xmlmain, "dialog_received_invite")); g_signal_connect(glade_xml_get_widget(xmlmain, "button_received_invite_show_user_info"), "activate", G_CALLBACK(on_received_invite_user_info), client_entry); g_signal_connect(glade_xml_get_widget(xmlmain, "button_received_invite_show_user_info"), "released", G_CALLBACK(on_received_invite_user_info), client_entry); /* store channelname, so dialog 'Yes' callback function can access it */ g_object_set_data(G_OBJECT(glade_xml_get_widget(xmlmain, "dialog_received_invite")), "invited_channel", g_strdup(str)); break; /* SIGNOFF */ case SILC_NOTIFY_TYPE_SIGNOFF: debug_info("SILC_NOTIFY_TYPE_SIGNOFF:"); client_entry = va_arg(va, SilcClientEntry); /* user */ str = va_arg(va, char *); /* reason */ if (!client_entry) { debug("Did not get client_entry"); return; } debug_info("User: '%s' quitted with reason '%s'", client_entry->nickname, str); debug("Starting iteration of userchannels"); /* if the user was on some channel we are too */ if (client_entry->channels) { silc_hash_table_list(client_entry->channels, &souserhtl); /* init list */ debug("hash_table initialized"); /* loop through all channels of this user */ while (silc_hash_table_get(&souserhtl, NULL, (void *)&chu )) { debug("'%s' (%s@%s) signed off from '%s' (%s)", client_entry->nickname, client_entry->username, client_entry->hostname, chu->channel->channel_name, str); debug("freeing channel member from list..."); channel_member_remove_by_entry(channel_find_by_entry(chu->channel), client_entry); /* print to the channel tab */ printchannel(chu->channel->channel_name, g_strdup_printf(_("%s (%s@%s) has signed off (%s)"), client_entry->nickname, client_entry->username, client_entry->hostname, str)); /* remove user from the lists, not sure if this is safe?! FIXME */ silc_hash_table_del(chu->client->channels, chu->channel); silc_hash_table_del(chu->channel->user_list, chu->client); /* refresh the UI */ refresh_gui_nicklist(channel_find_by_entry(chu->channel)); } silc_hash_table_list_reset(&souserhtl); /* reset the list */ debug("Iteration done."); } else { debug_message("Got NULL client_entry->channels. This must be a bug in the SILC Client library."); } break; /* WATCH */ case SILC_NOTIFY_TYPE_WATCH: /* SilcClientEntry watched_client, char *new_nickname, SilcUInt32 user_mode, SilcNotifyType notification */ debug_info("SILC_NOTIFY_TYPE_WATCH"); client_entry = va_arg(va, SilcClientEntry); str = va_arg(va, char *); mode = va_arg(va, SilcUInt32); notification = va_arg(va, int); str_fingerprint = silc_fingerprint(client_entry->fingerprint, client_entry->fingerprint_len); buddy_set_clientid_by_fp(str_fingerprint, client_entry->id); switch (notification) { case SILC_NOTIFY_TYPE_NICK_CHANGE: debug_info("SILC_NOTIFY_TYPE_NICK_CHANGE"); debug_info("WATCH '%s' is now known as '%s'", str_fingerprint, client_entry->nickname); if (buddy_set_nick_by_fp(str_fingerprint, client_entry->nickname)) { refresh_gui_buddylist(); } else { debug("fingerprint not found from the buddy list"); } break; case SILC_NOTIFY_TYPE_UMODE_CHANGE: debug_info("SILC_NOTIFY_TYPE_UMODE_CHANGE"); debug_info("WATCH '%s' has changed umode %d", client_entry->nickname, mode); if (buddy_set_mode_by_fp(silc_fingerprint(client_entry->fingerprint, client_entry->fingerprint_len), mode)) { debug("mode set in the buddy list"); refresh_gui_buddylist(); } else { debug("user not found in the buddy list"); } break; default: debug_info("unhandled notification type: %d", notification); break; } break; /* UNHANDLED NOTIFIES */ default: debug_message("unhandled SILC_NOTIFY_TYPE (%d)", type); break; } va_end(va); debug("returning"); }