/* Silky - A GTK+ client for SILC. Copyright (C) 2003, 2004, 2005 Toni Willberg - UI callbacks FIXME: move all stuff from this file to other ui_ -files 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 #include #include #include #include #include #ifdef HAVE_CONFIG_H # include #endif #include "ui_buddylist.h" #include "ui_channel.h" #include "ui_console.h" #include "ui_query.h" #include "callbacks.h" #include "support.h" /* silkyStruct() */ #include "cmdhistory.h" /* SilkyCmdHistoryItem() */ #include "buddylist.h" /* buddyAdd/( */ #include "preferences.h" /* prefs_str_to_bool(), prefs_get() */ #include "commands.h" #include "gui.h" #include "log.h" #include "servers.h" /* servers_store_to_xml() */ #include "xmlconfig.h" #include "xmllayout.h" #include "common.h" extern GladeXML *xmlmain; extern silkyStruct *silky; extern gchar *previous_nick; /* my nickname just before sending NICK command */ extern gchar *prefpath; extern gchar *serverspath; extern SilkyChannel *channels; /* from channel.c */ extern SilkyChannel *channel_active; extern SilkyQuery *query_active; extern SilkyCmdHistoryItem *current_cmd_history_entry; /* from cmdhistory.c */ /* called from gtk as callback for nicklist menu FIXME: move this to channel_nicklist.c */ gboolean on_command_buddy_add(GtkMenuItem *menuitem, gpointer clientid) { SilcClientEntry cliententry; gchar *fingerprint_str; SilcBuffer buffer; cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid); if (!cliententry) { debug("client entry not found"); return FALSE; } debug("trying to add buddy to list"); fingerprint_str = silc_fingerprint(cliententry->fingerprint, cliententry->fingerprint_len); debug("nick: %s, fingerprint: %s", cliententry->nickname, fingerprint_str); /* get the key */ debug("sending GETKEY"); buffer = silc_id_payload_encode(cliententry->id, SILC_ID_CLIENT); silc_client_command_send(silky->client, silky->conn, SILC_COMMAND_GETKEY, 0, 1, 1, buffer->data, buffer->len); silc_buffer_free(buffer); // silc_client_command_send(silky->client, silky->conn, SILC_COMMAND_GETKEY, 0, 1, 1, cliententry->id, silc_id_payload_get_len(cliententry->id) ); if (buddy_add(cliententry->nickname, fingerprint_str )) { debug("added, now trying to save the buddy file"); /* save the buddy list file */ save_buddies(); /* refresh the buddy window, it could be visible at this point */ refresh_gui_buddylist(); return TRUE; } else { debug("add failed"); return FALSE; } } gint on_command_whois(GtkMenuItem *menuitem, gpointer clientid) { SilcBuffer idp; SilcClientEntry cliententry; debug("on_command_whois, clientid ptr '%p'", clientid); if (!clientid) { debug("Did not receive clientid in on_nicklist_menu_activate()"); return FALSE; } /* this command requires connection */ if (!silky->conn) { debug("not connected"); return FALSE; } cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid); if (!cliententry) { debug("Did not get cliententry on_send.."); printconsole(_("No such user.")); return FALSE; } else { debug("Sending WHOIS for '%s'", cliententry->nickname); } /* construct WHOIS command, searches with ClientID */ idp = silc_id_payload_encode(clientid, SILC_ID_CLIENT); silc_client_command_send( silky->client, silky->conn, SILC_COMMAND_WHOIS, 0, 4, 1, NULL, NULL, 2, NULL, NULL, 3, NULL, NULL, 4, idp->data, idp->len ); return TRUE; } /* sends WHOIS -pubkey pubkeyfile */ gint on_command_whois_pubkey(GtkMenuItem *menuitem, gpointer pubkeyfile) { debug("sending command: WHOIS -pubkey %s", pubkeyfile); silc_client_command_call(silky->client, silky->conn, NULL, "WHOIS", "-pubkey", pubkeyfile, NULL); return TRUE; } gint on_command_query(GtkMenuItem *menuitem, gpointer clientid) { SilcClientEntry cliententry; debug("on_command_query()"); if (!clientid) { debug("Did not receive clientid in on_nicklist_menu_activate()"); return FALSE; } cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid); if (!cliententry) { debug("Did not get cliententry on_send.."); printconsole(_("No such user.")); return FALSE; } /* user selected himself? */ if (SILC_ID_CLIENT_COMPARE(clientid, silky->conn->local_entry->id)) { debug("Hey, it's me! You don't want to talk to yourself..."); printconsole(_("You do not want to talk to yourself.")); return TRUE; } debug("Opening query tab for: '%s'", cliententry->nickname); /* Open an empty query window for this user */ get_query_window(clientid); debug ("\tquery tab opened"); return TRUE; } /* When user selects KILL from nicklist popup menu */ gint on_command_kill(GtkMenuItem *menuitem, gpointer clientid) { SilcClientEntry cliententry; SilcBuffer idp; SilcBuffer auth = NULL; char *reason = "'Coz I wanted!"; /* FIXME: get from preferences */ debug("on_command_kill()"); if (!clientid) { debug("Did not receive clientid in on_nicklist_menu_activate()"); return FALSE; } cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid); if (!cliententry) { debug("Did not get cliententry at on_nicklist_activated() "); return FALSE; } debug ("FIXME! Sending kill command '%s'", cliententry->nickname); /* FIXME, how to construct this command?! */ /* send kill command */ idp = silc_id_payload_encode(silky->conn->local_id, SILC_ID_CLIENT); auth = silc_auth_public_key_auth_generate(silky->client->public_key, silky->client->private_key, silky->client->rng, silky->client->sha1hash, clientid, SILC_ID_CLIENT); silc_client_command_send( silky->client, silky->conn, SILC_COMMAND_KILL, 0, 3, 1, idp->data, idp->len, 2, reason, strlen(reason), 3, auth, sizeof(auth) ); debug("\tcommand sent"); return FALSE; } /* When user selects KICK from nicklist popup menu */ gint on_command_kick(GtkMenuItem *menuitem, gpointer clientid) { SilcClientEntry cliententry; SilcChannelID *channelid; gchar *channelname; SilcBuffer idp_channel; SilcBuffer idp_client; char *reason = "'I had no reason!"; /* FIXME: ask this from user? */ debug("on_command_kick()"); if (!clientid) { debug("Did not receive clientid()"); return FALSE; } cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid); if (!cliententry) { debug("Did not get cliententry() "); return FALSE; } /* get channel info from GUI */ channelid = get_active_channel_id(); channelname = get_active_channel_name(); if (!channelid) { debug("Did not get channelid for active channel '%s'.", channelname); return FALSE; } debug ("Sending kick command for '%s' on '%s'", cliententry->nickname, channelname); idp_channel = silc_id_payload_encode(channelid, SILC_ID_CHANNEL); idp_client = silc_id_payload_encode(clientid, SILC_ID_CLIENT); silc_client_command_send( silky->client, silky->conn, SILC_COMMAND_KICK, 0, 3, 1, idp_channel->data, idp_channel->len, 2, idp_client->data, idp_client->len, 3, reason, strlen(reason) ); debug("\tcommand sent"); return FALSE; } /* When user selects CUMODE operation from nicklist popup menu */ gint on_command_cumode(GtkMenuItem *menuitem, gpointer clientid) { gchar *cumode; SilcClientEntry cliententry; SilcChannelID *channelid; SilcChannelEntry channelentry; SilcBuffer idp_channel; SilcBuffer idp_client; SilcChannelUser channel_user_entry; SilcUInt32 mode; unsigned char modebuf[4]; debug("on_command_cumode_op()"); if (!clientid) { debug("Did not receive clientid()"); return FALSE; } /* let's see what cumode operation user wanted to do */ cumode = g_object_get_data(G_OBJECT(menuitem), "SILC_COMMAND_CUMODE"); cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid); if (!cliententry) { debug("Did not get cliententry() "); return FALSE; } channelid = get_active_channel_id(); if (!channelid) { debug("Did not get channelid for active channel."); return FALSE; } channelentry = silc_client_get_channel_by_id(silky->client, silky->conn,channelid); if (!channelentry) { debug("Did not find channel entry for this channel"); return FALSE; } debug ("Sending cumode op command for '%s' on '%s'", cliententry->nickname, channelentry->channel_name); /* send cumode command */ idp_channel = silc_id_payload_encode(channelentry->id, SILC_ID_CHANNEL); idp_client = silc_id_payload_encode(cliententry->id, SILC_ID_CLIENT); channel_user_entry = silc_client_on_channel(channelentry, cliententry); if (!channel_user_entry) { debug("Did not get channel_user_entry"); return FALSE; } mode = channel_user_entry->mode; /* Current mode */ debug("\tcurrent mode: %d", mode); /* cumode comes from GUI */ if (cumode == "+o") { /* op */ mode |= SILC_CHANNEL_UMODE_CHANOP; } if (cumode == "-o") { /* deop */ mode &= ~SILC_CHANNEL_UMODE_CHANOP; } if (cumode == "+q") { /* mute */ mode |= SILC_CHANNEL_UMODE_QUIET; } if (cumode == "-q") { /* unmute */ mode &= ~SILC_CHANNEL_UMODE_QUIET; } debug("\taltered mode: %d", mode); SILC_PUT32_MSB(mode, modebuf); silc_client_command_send(silky->client, silky->conn, SILC_COMMAND_CUMODE, 0, 3, 1, idp_channel->data, idp_channel->len, 2, modebuf, sizeof(modebuf), 3, idp_client->data, idp_client->len); debug("\tcommand sent"); return FALSE; } /* SERVER */ void on_server_connect(GtkWidget *widget, gpointer user_data) { GtkEntry *input; gchar *new_server; int ret; GtkWidget *window, *menuwidget, *notebook; /* FIXME! NEED TO CLOSE PREVIOUS CHANNELS/QUERIES WHEN SERVER CHANGES! */ window = glade_xml_get_widget (xmlmain, "dialog_connect_server"); input = GTK_ENTRY(glade_xml_get_widget (xmlmain, "new_servername")); new_server = strdup(gtk_entry_get_text(input)); if ( CONNECTED || CONNECTING ) { errordialog(_("You are already connected or connecting. Disconnect first.")); gtk_widget_grab_focus(window); /* set focus back to the dialog */ return; } if (strlen(new_server) < 1) { errordialog(_("You need to enter the name of the server!")); gtk_widget_grab_focus(window); /* set focus back to the dialog */ return; } /* hide the window */ gtk_widget_hide(glade_xml_get_widget (xmlmain, "dialog_connect_server")); /* disable menu */ menuwidget = glade_xml_get_widget (xmlmain, "menu_server_disconnect"); gtk_widget_set_sensitive(menuwidget, FALSE); menuwidget = glade_xml_get_widget (xmlmain, "menu_server_connect"); gtk_widget_set_sensitive(menuwidget, FALSE); printconsole(g_strdup_printf(_("Connecting to server '%s'..."), new_server)); debug("setting state to STATE_CONNECTING"); silky->state = STATE_CONNECTING; /* close all tabs except console */ if( !(notebook = glade_xml_get_widget(xmlmain, "tabs")) ) { debug("could not retrieve notebook, bailing out"); return; } close_all_tabs(); ret = silc_client_connect_to_server(silky->client, NULL, 706, new_server, silky); if (ret == -1) { printconsole(_("Can not connect to the server.")); debug("can not connect, setting state to STATE_DISCONNECTED"); silky->state = STATE_DISCONNECTED; gtk_widget_grab_focus(window); /* set focus back to the dialog */ /* set menu */ menuwidget = glade_xml_get_widget (xmlmain, "menu_server_connect"); gtk_widget_set_sensitive(menuwidget, TRUE); menuwidget = glade_xml_get_widget (xmlmain, "menu_server_disconnect"); gtk_widget_set_sensitive(menuwidget, FALSE); return; } else { debug("\tConnection established to %s, waiting...", new_server); } } /* Save pressed */ void on_settings_save(GtkWidget *widget, gpointer user_data) { GtkNotebook *storednotebook; gint pages; GtkWidget *active_page; GtkWidget *textview; gint pagenr; GladeXML *tabxml; debug("get main values from gui"); prefs_save_gui(); debug("get server list from gui"); servers_store_to_xml(); debug("saving servers"); /* save servers list */ xml_save_config(CONFIG_SERVERS, serverspath); debug("saving main config"); /* save main config */ xml_save_config(CONFIG_MAIN, prefpath); /* close the config dialog */ gtk_widget_hide(glade_xml_get_widget (xmlmain, "dialog_preferences")); /* iterate open tabs and apply new colors and fonts */ storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs")); pages = gtk_notebook_get_n_pages(storednotebook); debug("iterating pages..."); for (pagenr = 0; pagenr < pages; pagenr++) { debug("#%d", pagenr); active_page = gtk_notebook_get_nth_page(storednotebook, pagenr); tabxml = glade_get_widget_tree(active_page); /* set color for main textview */ textview = glade_xml_get_widget (tabxml, "textviewwidget"); if (textview) { set_textview_colors(GTK_TEXT_VIEW(textview)); } /* set color for nicklist */ textview = glade_xml_get_widget (tabxml, "nicktreeview"); if (textview) { silky_colorize_nicklist(textview); } } debug("returning from on_settings_save()"); } void on_settings_cancel(GtkWidget *widget, gpointer user_data) { /* close the config dialog... */ gtk_widget_hide(glade_xml_get_widget (xmlmain, "dialog_preferences")); /* ...and that's it */ } /* Show key creation dialog */ void on_settings_create_new_key(GtkWidget *widget, gpointer user_data) { debug("on_settings_create_new_key"); // gtk_widget_show (glade_xml_get_widget (xmlmain, "window_setupCreateNewKey")); /* DISABLED FOR NOW */ } /* create the new key and hide the widget*/ void on_settings_create_new_key_create(GtkWidget *widget, gpointer user_data) { GtkEntry *username; GtkEntry *hostname; GtkEntry *name; GtkEntry *email; GtkEntry *passphrase1; GtkEntry *passphrase2; debug ("on_settings_create_new_key_create:"); username = GTK_ENTRY(glade_xml_get_widget (xmlmain, "setupCreateNewKey_username")); hostname = GTK_ENTRY(glade_xml_get_widget (xmlmain, "setupCreateNewKey_hostname")); name = GTK_ENTRY(glade_xml_get_widget (xmlmain, "setupCreateNewKey_name")); email = GTK_ENTRY(glade_xml_get_widget (xmlmain, "setupCreateNewKey_email")); passphrase1 = GTK_ENTRY(glade_xml_get_widget (xmlmain, "setupCreateNewKey_passphrase1")); passphrase2 = GTK_ENTRY(glade_xml_get_widget (xmlmain, "setupCreateNewKey_passphrase2")); if (strcmp (gtk_entry_get_text(passphrase1), gtk_entry_get_text(passphrase2)) == 0) { debug("equal pass"); } else { debug("pass wrong"); } gtk_widget_hide(glade_xml_get_widget (xmlmain, "window_setupCreateNewKey")); } /* When user closes a query tab. */ void on_leave_query(GtkWidget *button, gpointer user_data) { GtkNotebook *storednotebook; GtkWidget *active_page; SilcClientID *clientid; gchar *str_clientid; gint pagenr; debug("on_leave_query()"); storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs")); if (!storednotebook) { debug("didn't get storednotebook"); return; } pagenr = gtk_notebook_get_current_page(storednotebook); debug("pagenr: %d", pagenr); /* remove from internal list */ query_remove_by_pagenr(pagenr); active_page = gtk_notebook_get_nth_page(storednotebook, pagenr); /* remove queried clientid from 'cache' if it's still there */ clientid = g_object_get_data(G_OBJECT(active_page), "clientid"); if (clientid) { debug("clientid found, removing from list"); str_clientid = silc_id_id2str(clientid, SILC_ID_CLIENT); if (str_clientid) { g_object_steal_data(G_OBJECT(storednotebook), str_clientid); } else { debug("invalid str_clientid, ignoring"); } } else { debug("clientid not found, perhaps user already quitted? not removing from list"); } /* remove the query tab, regardles of if we have the clientid or not */ gtk_widget_destroy(active_page); /* check that all tabs are associated with correct data structures, since their numbers may have changed */ gui_renumber_tabs(); } /* Leave the active channel. If not connected, just close the tab of the channel. */ void on_leave_channel(GtkWidget *button, gpointer user_data) { if ( CONNECTED ) { debug("connected, lets call PART"); handle_command("PART"); /* lets part the active channel */ } else { debug("not connected, just closing the tab"); gtk_widget_destroy(get_stored_channel_window(get_active_channel_name())); } /* check that all tabs are associated with correct data structures, since their numbers may have changed */ gui_renumber_tabs(); } void on_show_send_file_dialog(GtkWidget *widget, gpointer user_data) { GtkWidget *file_selector; GtkNotebook *storednotebook; gint apagenr; debug("on_show_send_file_dialog()"); /* see if there's active channel or query tab first */ storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs")); apagenr = gtk_notebook_get_current_page(GTK_NOTEBOOK(storednotebook)); /* test if it was the console tab */ if (apagenr == 0) { printconsole(_("Can not send a message to the console window.")); return; } /* Show an error dialog if there's no connection */ if ( !CONNECTED ) { errordialog(_("Can not send the message. You are not connected to a server.")); return; } /* We use default GTK file selector here! */ file_selector = gtk_file_selection_new (_("Please select a file to send.")); g_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_selector)->ok_button), "clicked", G_CALLBACK (on_send_file_from_dialog), (gpointer) file_selector); /* Ensure that the dialog box is destroyed when the user clicks a button. */ g_signal_connect_swapped (GTK_OBJECT (GTK_FILE_SELECTION (file_selector)->ok_button), "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector); g_signal_connect_swapped (GTK_OBJECT (GTK_FILE_SELECTION (file_selector)->cancel_button), "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector); /* Display that dialog */ gtk_widget_show (file_selector); } /* Show the NEW QUERY dialog */ void on_show_new_query_window(GtkWidget *widget, gpointer user_data) { GtkWidget *widget_vboxforliststore; GtkListStore *liststore; GtkWidget *treeview; GtkCellRenderer *renderer_text; GtkTreeViewColumn *column; debug("on_show_new_query_window()"); if (!CONNECTED) { printconsole(_("You are not connected to server.")); return; } /* container for the treeview from Glade*/ widget_vboxforliststore = glade_xml_get_widget (xmlmain, "vboxforliststore"); if (!widget_vboxforliststore) { debug("didn't get widget_vboxforliststore. Weird!"); return; } else { /* Try to fetch the liststore, as it might be already initialized earlier */ liststore = g_object_get_data(G_OBJECT(widget_vboxforliststore), "new_query_stored_userstore"); } if (!liststore) { debug("Creating the treeview and liststore for the first time. They will be reused."); /* create list store that contains user information */ liststore = gtk_list_store_new ( 7, G_TYPE_STRING, /* nickname */ G_TYPE_STRING, /* server */ G_TYPE_STRING, /* username */ G_TYPE_STRING, /* hostname */ G_TYPE_STRING, /* realname */ G_TYPE_STRING, /* fingerprint */ G_TYPE_POINTER /* ClientID */ ); /* FIXME: check that this doesn't yet exist */ /* save pointer of this liststore for later reference */ g_object_set_data(G_OBJECT(widget_vboxforliststore), "new_query_stored_userstore", liststore); /* CREATE new TREEVIEW */ treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(liststore)); /* ADD columns */ /* create renderer */ renderer_text = gtk_cell_renderer_text_new (); /* nickname */ column = gtk_tree_view_column_new_with_attributes ("Nickname", renderer_text, "text", 0, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column); /* servername */ column = gtk_tree_view_column_new_with_attributes ("Server", renderer_text, "text", 1, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column); /* username */ column = gtk_tree_view_column_new_with_attributes ("Username", renderer_text, "text", 2, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column); /* hostname */ column = gtk_tree_view_column_new_with_attributes ("Host", renderer_text, "text", 3, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column); /* realname */ column = gtk_tree_view_column_new_with_attributes ("Realname", renderer_text, "text", 4, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column); /* fingerprint */ column = gtk_tree_view_column_new_with_attributes ("Fingerprint", renderer_text, "text", 5, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column); /* handler for ACTIVATED signal*/ g_signal_connect (treeview, "row-activated", G_CALLBACK(on_new_query_activated), NULL); /* add the treeview to the container */ gtk_container_add (GTK_CONTAINER(widget_vboxforliststore), treeview); /* And finally mark the treeview visible */ gtk_widget_show(treeview); } else { debug("\ttreeview and list was already initialized, using the old one"); } /* And show the window too */ gtk_widget_show (glade_xml_get_widget (xmlmain, "window_new_query")); /* I18N This is a window title */ gtk_window_set_title(GTK_WINDOW(glade_xml_get_widget (xmlmain, "window_new_query")), g_strdup_printf("Silky v%s : %s", SILKY_VERSION, _("New query"))); /* FIXME? should I free/unref something? */ } /* this is called after user clicks [Search] in the search user -window */ void on_new_query(GtkWidget *widget, gpointer user_data) { const gchar *querynick; debug("on_new_query()"); querynick = gtk_entry_get_text(GTK_ENTRY(glade_xml_get_widget (xmlmain, "new_query_nick"))); if (!querynick) { debug("Did not get anything from input."); return; } /* Let's call WHOIS first, and then the _get_clients thing... Perhaps the client library will get better solution for this some day :/ */ debug("Calling WHOIS '%s'...", querynick); silc_client_command_call(silky->client, silky->conn, NULL, "WHOIS", querynick, NULL); debug("Setting callback for silc_client_get_clients()..."); /* ask client library to resolve this client(s), search by nick@host, we use empty host */ silc_client_get_clients(silky->client, silky->conn, querynick, NULL, on_new_query_resolved, NULL); /* The client library will call on_new_query_resolved after this! */ } /* when user clicks [Clear] on new_query dialog */ void on_new_query_clear(GtkWidget *widget, gpointer user_data) { GtkWidget *widget_vboxforliststore; GtkListStore *liststore; debug("on_new_query_clear()"); widget_vboxforliststore = glade_xml_get_widget (xmlmain, "vboxforliststore"); liststore = g_object_get_data(G_OBJECT(widget_vboxforliststore), "new_query_stored_userstore"); gtk_list_store_clear(liststore); } /* this is the callback function that on_new_query() sets */ void on_new_query_resolved(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, void *context) { GtkWidget *widget_vboxforliststore; GtkListStore *liststore; GtkTreeIter iter; int index; debug("on_new_query_resolved()"); if (clients_count == 0) { debug("\tDid not find anyone"); errordialog(_("No users found.")); return; } widget_vboxforliststore = glade_xml_get_widget (xmlmain, "vboxforliststore"); liststore = g_object_get_data(G_OBJECT(widget_vboxforliststore), "new_query_stored_userstore"); if (!liststore) { debug("Did not get liststore in on_new_query_resolved(). This is OK if user closed the window before this function got called."); return; } if (clients_count) { for (index=0; index < clients_count; index++) { while (!clients[index]->fingerprint) { /* JUST WAIT FOR RESOLVER */ /* FIXME: should check that we won't wait here forever! also run gtk and also check that the widget is still open! */ } debug("\tFound %s@%s (%s@%s) '%s' (%s)", clients[index]->nickname, clients[index]->server, clients[index]->username, clients[index]->hostname, clients[index]->realname, silc_fingerprint(clients[index]->fingerprint, clients[index]->fingerprint_len ) ); /* ADD LINES TO THE GUI */ /* new line */ gtk_list_store_append (GTK_LIST_STORE(liststore), &iter); /* nickname */ gtk_list_store_set (GTK_LIST_STORE(liststore), &iter, 0, clients[index]->nickname , -1); /* server */ gtk_list_store_set (GTK_LIST_STORE(liststore), &iter, 1, clients[index]->server , -1); /* username */ gtk_list_store_set (GTK_LIST_STORE(liststore), &iter, 2, clients[index]->username , -1); /* hostname */ gtk_list_store_set (GTK_LIST_STORE(liststore), &iter, 3, clients[index]->hostname , -1); /* realname */ gtk_list_store_set (GTK_LIST_STORE(liststore), &iter, 4, clients[index]->realname , -1); /* fingerprint */ gtk_list_store_set (GTK_LIST_STORE(liststore), &iter, 5, silc_fingerprint(clients[index]->fingerprint, clients[0]->fingerprint_len ), -1); /* ClientID */ gtk_list_store_set (GTK_LIST_STORE(liststore), &iter, 6, clients[index]->id, -1); } debug("List iterated."); } } /* this is called when user double clicks a name in the new query or clicks [Open query] -list */ void on_new_query_activated(GtkTreeView *treeview, GtkTreePath *treepath, GtkTreeViewColumn *column, gpointer user_data) { GtkTreeIter iter; GtkTreeModel *model; SilcClientID *client_id; SilcClientEntry cliententry; model = gtk_tree_view_get_model(treeview); gtk_tree_model_get_iter(model, &iter, treepath); debug("on_new_query_start()"); /* get client id of activated entry */ gtk_tree_model_get (model, &iter, 6, &client_id, -1); if (!client_id) { debug("Did not get clientid at on_new_query_activated"); return; } if(SILC_ID_CLIENT_COMPARE(client_id, silky->conn->local_entry->id)) { debug("\tI won't talk to myself. Sorry."); printconsole(_("You do not want to talk to yourself.")); return; } /* FIXME! need to check that client really exists, this get's it from the cache */ cliententry = silc_client_get_client_by_id(silky->client, silky->conn, client_id); if (!cliententry) { debug("Did not get cliententry"); return; } /* this opens new query window if no existing is open for the same clientid */ get_query_window(client_id); /* hide the previous window */ gtk_widget_hide (glade_xml_get_widget (xmlmain, "window_new_query")); /* set focus to inputbox of the main window*/ gtk_widget_grab_focus(glade_xml_get_widget (xmlmain, "textviewwidget")); } /* Join to the channel specified in Join to a channel -dialog */ void on_join_channel(GtkWidget *widget, gpointer user_data) { const gchar *channel_name; debug("on_join_channel()"); if (!CONNECTED) { debug("No connection. FIXME, should offer reconnection?"); errordialog(_("You are not connected.")); return; } /* get the channel name */ channel_name = gtk_entry_get_text(GTK_ENTRY(glade_xml_get_widget (xmlmain, "join_channel_name"))); /* require some channel name */ if (strlen(channel_name) < 1) { debug("No channel name given, can not join."); return; } /* hide the dialog */ gtk_widget_hide (glade_xml_get_widget (xmlmain, "dialog_join_channel")); silky_join_channel(channel_name); } void on_join_channel_invited(GtkWidget *widget, gpointer chinvobj) { gchar *str = g_object_get_data(G_OBJECT(glade_xml_get_widget(xmlmain, "dialog_received_invite")), "invited_channel"); debug("on_join_channel_invited()"); if( !str || !strlen(str) ) { debug("Empty channel name passed in dialog, bailing out."); return; } silky_join_channel(str); g_free(str); gtk_widget_hide(glade_xml_get_widget(xmlmain, "dialog_received_invite")); } /* UI */ /* Processes every key press on the main window. Returns FALSE if this function didn't take care of the event. Gtk will process it. Returns TRUE if we did take care. Gtk will not process it further. */ gboolean on_key_press(GtkWidget *widget, GdkEventKey *key, gpointer user_data) { GtkWidget *inputbox = glade_xml_get_widget (xmlmain, "inputbox"); GtkWidget *topicbox = NULL; gchar *newtext; gint pagenr, k = key->keyval; /* if we're not connected and user hits ENTER with no text in inputbox, */ /* display connection dialog */ if( DISCONNECTED && (k == GDK_Return || k == GDK_KP_Enter) && (strlen( gtk_entry_get_text(GTK_ENTRY(inputbox)) ) == 0) ) { /* the ugly number is code for ENTER */ debug("displaying connection dialog"); on_show_server_connect_window( NULL, NULL ); return(FALSE); } /* don't steal focus from topicbox (need to be connected for the check to work, otherwise it segfaults because channel_xml would be not valid...) */ if ( CONNECTED && channel_active && channel_active->glade_xml ) { topicbox = glade_xml_get_widget(channel_active->glade_xml, "channeltopic"); if (topicbox && GTK_WIDGET_HAS_FOCUS(topicbox)) { /* TOO MUCH OUTPUT debug("not stealing focus from the topic box"); */ return(FALSE); } } if( k == GDK_Up ) { /* up arrow */ debug("arrow UP"); newtext = g_strndup( (gchar *)cmd_history_prev(), 255 ); if( newtext && g_utf8_strlen( newtext, -1 ) ) { gtk_entry_set_text(GTK_ENTRY(inputbox), newtext); gtk_window_set_focus(GTK_WINDOW(glade_xml_get_widget (xmlmain, "window_main")), inputbox); } gtk_editable_set_position(GTK_EDITABLE(inputbox), -1); /* focus end of the line */ return(TRUE); } if( k == GDK_Down ) { /* down arrow */ debug("arrow DOWN"); newtext = g_strndup( (gchar *)cmd_history_next(), 255 ); if( newtext && g_utf8_strlen( newtext, -1 ) ) { gtk_entry_set_text(GTK_ENTRY(inputbox), newtext); gtk_window_set_focus(GTK_WINDOW(glade_xml_get_widget (xmlmain, "window_main")), inputbox); } else { gtk_entry_set_text(GTK_ENTRY(inputbox), g_strdup("")); /* clear the line, this happens when we browsed over the last line in the buffer */ } gtk_editable_set_position(GTK_EDITABLE(inputbox), -1); /* focus end of the line. */ return(TRUE); } /* page up */ if ( k == GDK_Page_Up ) { debug("page up key"); /* scroll the page up */ g_signal_emit_by_name (silky_get_active_textview(), "move-cursor", GTK_MOVEMENT_PAGES, -1, FALSE); return (TRUE); } /* page down */ if ( k == GDK_Page_Down ) { debug("page down key"); /* scroll the page up */ g_signal_emit_by_name (silky_get_active_textview(), "move-cursor", GTK_MOVEMENT_PAGES, 1, FALSE); return (TRUE); } /* shift+home*/ if ( key->state & GDK_SHIFT_MASK && k == GDK_Home ) { debug("shift+home key"); return (TRUE); } /* shift+end*/ if ( key->state & GDK_SHIFT_MASK && k == GDK_End ) { debug("shift+end key"); return (TRUE); } /* grab ALT+1..9 and set the give page as active */ if (key->state & GDK_MOD1_MASK && ( k >= '1' && k <= '9') ) { pagenr = (k - 48); /* 48 = '0' */ /* set active page, index starts from 0 */ gtk_notebook_set_current_page(GTK_NOTEBOOK(glade_xml_get_widget(xmlmain, "tabs")), pagenr-1); debug("alt+%d", pagenr); return (TRUE); } /* grab ALT+left/right, and switch notebook pages */ if (key->state & GDK_MOD1_MASK && k == GDK_Left) { debug("alt+left"); pagenr = gtk_notebook_get_current_page(GTK_NOTEBOOK(glade_xml_get_widget(xmlmain, "tabs"))); if (pagenr > 0) { pagenr--; gtk_notebook_set_current_page(GTK_NOTEBOOK(glade_xml_get_widget(xmlmain, "tabs")), pagenr); } return (TRUE); } if (key->state & GDK_MOD1_MASK && k == GDK_Right) { debug("alt+right"); pagenr = gtk_notebook_get_current_page(GTK_NOTEBOOK(glade_xml_get_widget(xmlmain, "tabs"))); pagenr++; gtk_notebook_set_current_page(GTK_NOTEBOOK(glade_xml_get_widget(xmlmain, "tabs")), pagenr); return (TRUE); } /* CTRL-J for join channel dialog */ if( key->state & GDK_CONTROL_MASK && (k == GDK_J || k == GDK_j) ) { debug("CTRL+J pressed, displaying join channel dialog"); on_show_join_channel_window(glade_xml_get_widget(xmlmain, "dialog_join_channel"), NULL); return (TRUE); } /* CTRL-N for nickchange dialog */ if( key->state & GDK_CONTROL_MASK && (k == GDK_N || k == GDK_n) ) { debug("CTRL+N pressed, displaying nickchange dialog"); on_show_nick_change_window(glade_xml_get_widget(xmlmain, "dialog_nick_change"), NULL); return (TRUE); } /* CTRL-W - close current tab (channel or query) */ if( key->state & GDK_CONTROL_MASK && (k == GDK_W || k == GDK_w) ) { if( channel_active ) { debug("CTRL+W pressed, leaving active channel"); silky_leave_channel(channel_active->channel_entry->channel_name); return (TRUE); } else if( query_active ) { debug("CTRL+W pressed, closing active query"); query_remove(query_active); } else debug("No active channel or query, ignoring"); } /* return if it was a control character/key */ if( g_unichar_iscntrl(key->keyval) ) { return(FALSE); } if( !GTK_WIDGET_HAS_FOCUS(inputbox) ) { /* if inputbox didn't have focus, give it to it */ gtk_window_set_focus(GTK_WINDOW(glade_xml_get_widget (xmlmain, "window_main")), inputbox); gtk_editable_set_position(GTK_EDITABLE(inputbox), -1); /* focus end of the line. */ } else if( k == GDK_Tab ) { /* 65289 is code for TAB key */ debug("[tab] pressed"); channel_tab_completion(); gtk_editable_set_position(GTK_EDITABLE(inputbox), -1); /* focus end of the line. */ return(TRUE); } if(!g_unichar_iscntrl(key->keyval)) { channel_reset_tab_completion(); /* if we type anything, reset */ } /* debug("keyval: %d", key->keyval); */ /* we let Gtk process it anyway if we got this far */ return(FALSE); } /* User has clicked [Send] on UI */ void on_ui_send(GtkWidget *widget, gpointer input) { gchar *channel_name; SilcChannelEntry channel; GtkEntry *inmes; gchar *message; SilcMessageFlags sendflags; GtkNotebook *storednotebook; GtkWidget *active_page; SilcClientID *clientid; gchar *str_clientid; SilcClientEntry cliententry; gchar value; /* from config */ debug("on_ui_send()"); /* Get value of the input box */ inmes = GTK_ENTRY(glade_xml_get_widget (xmlmain, "inputbox")); /* Show an error dialog if there's no connection */ if (!CONNECTED) { debug("Cannot send a message.Not connected to a server."); printconsole(_("You are not connected.")); /* select whole text in gui */ gtk_entry_select_region(inmes, 0, -1); return; } storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs")); /* if console is active */ if (gtk_notebook_get_current_page(GTK_NOTEBOOK(storednotebook) ) == 0) { debug("was in the console tab, returning"); printconsole(_("Can not send the message to the console window.")); /* select whole text in gui */ gtk_entry_select_region(inmes, 0, -1); } active_page = gtk_notebook_get_nth_page(storednotebook, gtk_notebook_get_current_page(GTK_NOTEBOOK(storednotebook) ) ); clientid = g_object_get_data(G_OBJECT(active_page), "clientid"); /* get active channel name from UI (support.c) */ channel_name = get_active_channel_name(); debug("\treturned from get_active_channel_name"); /* was it channel message or query message */ if (channel_name) { debug("sending channel message"); /* get channel by name */ channel = silc_client_get_channel(silky->client, silky->conn, channel_name); if (!channel) { debug("can not find channel entry!"); return; } else { debug("got channel"); } /* get a copy of the message, next function wants other type */ message = g_strndup(gtk_entry_get_text(inmes), 255); if (!message || strlen(message) <= 0) { debug("empty message, skipping!"); return; } sendflags = SILC_MESSAGE_FLAG_UTF8; /* set UTF */ value = prefs_str_to_bool(prefs_get("sign_channel_messages")); /* whetter to SIGN message or not */ if (value) { sendflags |= SILC_MESSAGE_FLAG_SIGNED; /* set SIGNED */ } if (silc_client_send_channel_message(silky->client, silky->conn, channel, NULL, sendflags, message, strlen(message), FALSE)) { debug("message sent"); print_mime_to_channel( silky->conn->local_entry, channel->channel_name, 0, g_strdup("text/plain"), (int)sendflags, message); } else { debug("message failed"); } /* Clear the textbox */ gtk_entry_set_text(GTK_ENTRY(inmes), ""); free(message); } else { debug("Was not channel message."); } if (clientid) { debug("sending query/private message"); str_clientid = silc_id_id2str(clientid, SILC_ID_CLIENT); cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid); if (!cliententry) { debug("Did not get cliententry on_send.."); printconsole(_("No such user.")); return; } /* Get value of the input box */ inmes = GTK_ENTRY(glade_xml_get_widget (xmlmain, "inputbox")); /* get a copy of the message, next function wants other type */ message = strdup(gtk_entry_get_text(inmes)); if (strlen(message) <= 0) { debug("empty message, skipped"); return; } sendflags = SILC_MESSAGE_FLAG_UTF8; /* set UTF */ value = prefs_str_to_bool(prefs_get("sign_private_messages")); /* whetter to SIGN message or not */ if (value) { sendflags |= SILC_MESSAGE_FLAG_SIGNED; /* set SIGNED */ } silc_client_send_private_message(silky->client, silky->conn, cliententry, sendflags, message, strlen(message), FALSE); /* force send TRUE */ debug("private message sent to %s.", cliententry->nickname); printquery(clientid, silky->conn->nickname, sendflags, message); /* Clear the textbox */ gtk_entry_set_text(GTK_ENTRY(inmes), ""); } else { debug("Was not private message"); } } /* Send selected file to active channel */ void on_send_file_from_dialog(GtkWidget *widget, gpointer user_data) { GtkWidget *file_selector; const gchar *selected_filename; unsigned int file_size; SilcUInt32 file_handle; gchar file_buffer[64000]; /* FIXME, max size? */ gchar *channel_name; SilcChannelEntry channel; gchar *header; gchar *footer = "\r\n"; gchar *message; gchar *contenttype; /* create a file selector */ file_selector = (GtkWidget *)user_data; selected_filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (file_selector)); if (!selected_filename) { debug("no selected_filename, returning"); return; } debug("file name: '%s'", selected_filename); file_size = silc_file_size(selected_filename); debug("file size is '%d'", file_size); contenttype = mime_types_get_type(g_strdup(selected_filename)); if (!contenttype) { debug("didn't find contentype, using unknown"); contenttype = g_strdup("application/octet-stream"); } debug("contenttype is '%s'", contenttype); /* generate the header */ header = g_strdup_printf("MIME-Version: 1.0\r\nContent-Type: %s\r\nContent-Transfer-Encoding: binary\r\n\r\n", contenttype); /* FIXME: send as multiple messages if too big! */ if (file_size >= sizeof(file_buffer)-1 ) { /* FIXME: max size? OFF BY ONE? */ debug("The file is too big, returning"); printconsole(_("Can not send the file because it is too big.")); return; } /* alloc memory for message */ message = malloc(strlen(header) + file_size + strlen(footer)); /* open the given file */ file_handle = silc_file_open(selected_filename, O_RDONLY); /* read the file */ silc_file_read(file_handle, file_buffer, file_size ); /* get active channel name from UI (support.c) */ channel_name = get_active_channel_name(); /* get channel by name */ channel = silc_client_get_channel(silky->client, silky->conn, channel_name); /* append the file to the END of the message */ strncpy(message, header, strlen(header)); memcpy(message + strlen(header), file_buffer, file_size); strncpy(message + strlen(header) + file_size, footer, strlen(footer) ); debug("message size: %d", strlen(header) + strlen(footer) + file_size); /* send the MIME message */ silc_client_send_channel_message(silky->client, silky->conn, channel, NULL, SILC_MESSAGE_FLAG_DATA, message, strlen(header) + strlen(footer) + 64000 + 1, TRUE); silc_file_close(file_handle); debug("message sent, returning"); printconsole("Message sent."); } /* CMODE */ /* void on_cmode_setui(SilcChannelEntry channel_entry) { gboolean active; GtkWidget *toggle_button; GladeXML *stored_channel_xml; this should be called every time cmode changes also after command_cmode returns, whetter user had privs or not set GUI's togglebuttons to correct position } */ /* this is used in next function only, to invert current channel mode */ #define TOGGLE_FLAG(mode, flag) if (mode & flag) mode &= ~flag; else mode |= flag; gboolean on_cmode_menu(GtkWidget *toggle_button, gpointer user_data) { SilcChannelID *channel_id; SilcChannelEntry channel_entry; SilcUInt32 mode; unsigned char modebuf[4]; SilcBuffer idp_channel; G_CONST_RETURN gchar *wname; /* Get widget/items name from GUI. If you encounter weird problems, make sure nobody has changed names of these menuitems in the glade file! */ wname = gtk_widget_get_name(GTK_WIDGET(toggle_button)); debug("on_cmode(%s)", wname); /* get channel ID, if not on channel tab, give error */ channel_id = get_active_channel_id(); if (!channel_id) { debug("no channel_id found"); printconsole(_("You are not on a channel.")); return FALSE; } channel_entry = silc_client_get_channel_by_id(silky->client, silky->conn, channel_id); if (!channel_entry) { debug("no such channel"); return FALSE; } /* get current mode */ mode = channel_entry->mode; /* mask the mode */ if (! strcmp(wname, "cmode_topic")) { /* name comes from the GUI */ TOGGLE_FLAG(mode, SILC_CHANNEL_MODE_TOPIC); } if (! strcmp(wname, "cmode_invite")) { TOGGLE_FLAG(mode, SILC_CHANNEL_MODE_INVITE); } if (! strcmp(wname, "cmode_private")) { TOGGLE_FLAG(mode, SILC_CHANNEL_MODE_PRIVATE); } if (! strcmp(wname, "cmode_secret")) { TOGGLE_FLAG(mode, SILC_CHANNEL_MODE_SECRET); } if (! strcmp(wname, "cmode_umute")) { TOGGLE_FLAG(mode, SILC_CHANNEL_MODE_SILENCE_USERS); } if (! strcmp(wname, "cmode_omute")) { TOGGLE_FLAG(mode, SILC_CHANNEL_MODE_SILENCE_OPERS); } if (! strcmp(wname, "cmode_founder")) { TOGGLE_FLAG(mode, SILC_CHANNEL_MODE_FOUNDER_AUTH); } debug("mode masked and UI updated"); idp_channel = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL); SILC_PUT32_MSB(mode, modebuf); debug("sending command"); silc_client_command_send(silky->client, silky->conn, SILC_COMMAND_CMODE, 0, 2, 1, idp_channel->data, idp_channel->len, 2, modebuf, sizeof(modebuf) ); debug("\tcommand sent"); return TRUE; /* this tells GTK not to toggle the options, we will do it ourselves later! */ } void notj_msg (void) { debug("no channel joined"); printconsole( g_strdup_printf("%s %s", _("You are not on a channel."), _("Try `/join '.")) ); } void notc_msg (void) { debug("no server connected"); printconsole (_("You are not connected. Try /server []")); } gint on_toggle_channel_settings (GtkWidget *widget, gpointer userdata) { GtkWidget *wid_channel_modes; GtkWidget *wid_channel_keys; GtkWidget *wid_channel_bans; GladeXML *stored_channel_xml; stored_channel_xml = glade_get_widget_tree(widget); wid_channel_modes = glade_xml_get_widget (stored_channel_xml, "channel_settings_modes"); wid_channel_keys = glade_xml_get_widget (stored_channel_xml, "channel_settings_keys"); wid_channel_bans = glade_xml_get_widget (stored_channel_xml, "channel_settings_bans"); if (widget == wid_channel_modes) { debug("click on modes"); gtk_widget_show(wid_channel_modes); gtk_widget_hide(wid_channel_keys); gtk_widget_hide(wid_channel_bans); } if (widget == wid_channel_keys) { debug("click on keys"); gtk_widget_hide(wid_channel_modes); gtk_widget_show(wid_channel_keys); gtk_widget_hide(wid_channel_bans); } if (widget == wid_channel_bans) { debug("click on bans"); gtk_widget_hide(wid_channel_modes); gtk_widget_hide(wid_channel_keys); gtk_widget_show(wid_channel_bans); } return TRUE; } gboolean *on_received_invite_user_info(GtkWidget *widget, SilcClientEntry client_entry) { SilkyUserInfo *user = malloc(sizeof(SilkyUserInfo)); if( !user) { debug("Couldn't allocate memory for SilkyUserInfo, returning."); return FALSE; } debug("starting to fill SilkyUserInfo"); user->nickname = g_strdup_printf("%s@%s", client_entry->nickname, client_entry->server); user->username = g_strdup_printf("%s@%s", client_entry->username, client_entry->hostname); user->realname = g_strdup(client_entry->realname); user->channels = g_strdup(_("N/A")); user->fingerprint = g_strdup(silc_fingerprint(client_entry->fingerprint, 20)); user->attrs = NULL; /* FIXME: request attributes */ silky_show_user_info(user); silky_free_user_info(user); return FALSE; } /* These are not yet used. FIXME: get rid of unnecessary ones! */ void on_favourite_server_add(GtkWidget *widget, gpointer user_data) { } void on_favourite_person_add(GtkWidget *widget, gpointer user_data) { } void on_favourite_channel_add(GtkWidget *widget, gpointer user_data) { } /* UMODE */ void on_umode_away(GtkWidget *widget, gpointer user_data) { } void on_umode_reject_watching(GtkWidget *widget, gpointer user_data) { } void on_umode_block_invites(GtkWidget *widget, gpointer user_data) { } void on_umode_indisposed(GtkWidget *widget, gpointer user_data) { } void on_session_resume(GtkWidget *widget, gpointer user_data) { } void on_session_detach(GtkWidget *widget, gpointer user_data) { } void on_whoami(GtkWidget *widget, gpointer user_data) { } /* UMODE */ void on_umode_block_private_messages(GtkWidget *widget, gpointer user_data) { } void on_umode_await_paging(GtkWidget *widget, gpointer user_data) { } void on_umode_busy(GtkWidget *widget, gpointer user_data) { } void on_umode_hyperactive(GtkWidget *widget, gpointer user_data) { } void on_message_open(GtkWidget *widget, gpointer user_data) { } void on_ui_cut(GtkWidget *widget, gpointer user_data) { } void on_ui_copy(GtkWidget *widget, gpointer user_data) { } void on_ui_paste(GtkWidget *widget, gpointer user_data) { } void on_ui_undo(GtkWidget *widget, gpointer user_data) { } void on_ui_close_tab(GtkWidget *widget, gpointer user_data) {} void on_ui_help(GtkWidget *widget, gpointer user_data) { } void on_ui_find(GtkWidget *widget, gpointer user_data) { } void on_ui_redo(GtkWidget *widget, gpointer user_data) { } void on_ui_clear(GtkWidget *widget, gpointer user_data) { }