/*
Silky - A GTK+ client for SILC.
Copyright (C) 2003, 2004, 2005 Toni Willberg
- Various auxiliary functions
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 <silcincludes.h>
#include <silcclient.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <glib/gprintf.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "keys.h"
#include "log.h"
#include "commands.h"
#include "support.h"
#include "preferences.h"
#include "gui.h"
#include "ui_console.h"
#include "ui_channel.h"
#include "common.h"
GHashTable *mimetable; /* parsed mime.types */
extern GladeXML *xmlmain;
extern silkyStruct *silky;
extern gchar *homedir;
extern SilkyChannel *channel_active;
extern SilkyQuery *query_active;
/* Returns HH:MM */
gchar * utf_timestamp (void) {
time_t curtime;
struct tm *loctime;
gchar *utf_time;
curtime = time(0);
loctime = localtime (&curtime);
utf_time = g_strdup_printf("%02d:%02d", loctime->tm_hour, loctime->tm_min);
return utf_time;
}
/* marks active the channel tab of given channel ch,
called usually when some text was printed there and
user needs to be notified about activity in given channel struct
*/
void hilight_channel_window_by_ch(SilkyChannel *ch, gint type) {
if (ch == channel_active) {
debug("channel is active, doing nothing");
return;
}
switch (type) {
case HILIGHT_MOTION: /* wtf? */
debug("HILIGHT_MOTION");
gtk_label_set_markup( (gpointer)ch->label_text, g_strconcat("<b>", CHANNEL_NAME(ch), "</b>", NULL));
gtk_image_set_from_stock( (gpointer)ch->label_image, GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON );
break;
case HILIGHT_SPEECH: /* any public message */
debug("HILIGHT_SPEECH");
gtk_label_set_markup( (gpointer)ch->label_text, g_strconcat("<b>", CHANNEL_NAME(ch), "</b>", NULL));
gtk_image_set_from_stock( (gpointer)ch->label_image, GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON );
break;
case HILIGHT_WORD: /* wordlist match */
debug("HILIGHT_WORD");
gtk_label_set_markup( (gpointer)ch->label_text, g_strconcat("<b>", CHANNEL_NAME(ch), "</b>", NULL));
gtk_image_set_from_stock( (gpointer)ch->label_image, GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_BUTTON );
break;
case HILIGHT_NICK: /* my nick match */
debug("HILIGHT_NICK");
gtk_label_set_markup( (gpointer)ch->label_text, g_strconcat("<b>", CHANNEL_NAME(ch), "</b>", NULL));
gtk_image_set_from_stock( (gpointer)ch->label_image, GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_BUTTON );
break;
default:
debug("default hilight, this should not happen?");
break;
}
}
/* marks active the query tab of given channel ch,
called usually when some text was printed there and
user needs to be notified about activity in given channel struct
*/
void hilight_query_window_by_q(SilkyQuery *q, gint type) {
if (q == query_active) {
debug("query is active, doing nothing");
return;
}
switch (type) {
case HILIGHT_MOTION: /* wtf? */
debug("HILIGHT_MOTION");
gtk_label_set_markup( (gpointer)q->label_text, g_strconcat("<b>", q->client_entry->nickname, "</b>", NULL));
gtk_image_set_from_stock( (gpointer)q->label_image, GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON );
break;
case HILIGHT_SPEECH: /* any public message */
debug("HILIGHT_SPEECH");
gtk_label_set_markup( (gpointer)q->label_text, g_strconcat("<b>", q->client_entry->nickname, "</b>", NULL));
gtk_image_set_from_stock( (gpointer)q->label_image, GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON );
break;
case HILIGHT_WORD: /* wordlist match */
debug("HILIGHT_WORD");
gtk_label_set_markup( (gpointer)q->label_text, g_strconcat("<b>", q->client_entry->nickname, "</b>", NULL));
gtk_image_set_from_stock( (gpointer)q->label_image, GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_BUTTON );
break;
case HILIGHT_NICK: /* my nick match */
debug("HILIGHT_NICK");
gtk_label_set_markup( (gpointer)q->label_text, g_strconcat("<b>", q->client_entry->nickname, "</b>", NULL));
gtk_image_set_from_stock( (gpointer)q->label_image, GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_BUTTON );
break;
default:
debug("default hilight, this should not happen?");
break;
}
}
void add_user_to_nicklist (GtkListStore *liststore, SilcChannelEntry channel, SilcChannelUser user) {
GtkTreeIter iter, i;
gchar *nick = NULL;
int x = 0, pos = 0;
if (!liststore) {
debug("Missing liststore");
return;
}
if (!channel) {
debug("Missing channel");
return;
}
if (!user) {
debug("Missing user");
return;
}
/* new line, sort alphabetically */
if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &i) ) {
do {
gtk_tree_model_get(GTK_TREE_MODEL(liststore), &i, 2, &nick, -1);
if( g_utf8_collate( g_utf8_casefold(user->client->nickname, 256), g_utf8_casefold(nick, 256) ) <= 0) {
g_free(nick);
gtk_list_store_insert_before(liststore, &iter, &i);
x++;
} else pos++;
} while ( gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &i) && !x );
}
if (!x) gtk_list_store_append(liststore, &iter);
debug("Added new line to liststore");
debug("Adding nick '%s'", user->client->nickname);
channel_member_add(channel_find_by_entry(channel), user);
/* founder column*/
if (user->mode & SILC_CHANNEL_UMODE_CHANFO) {
gtk_list_store_set (liststore, &iter,
0, "*", -1);
}
/* muted? using same column as founder can not be muted :) */
if (user->mode & SILC_CHANNEL_UMODE_QUIET) {
gtk_list_store_set (liststore, &iter,
0, "&", -1);
}
/* chanop column */
if (user->mode & SILC_CHANNEL_UMODE_CHANOP) {
gtk_list_store_set (liststore, &iter,
1, "@", -1);
}
/* nickname column */
gtk_list_store_set (liststore, &iter,
2, user->client->nickname, -1);
/* clientid column */
gtk_list_store_set (liststore, &iter,
3 , user->client->id, -1);
debug("--------------");
}
/* delete the list, call this just before exiting Silky */
void mime_types_list_destroy (void) {
debug("mime_types_list_destroy()");
if (!mimetable) {
return;
}
g_hash_table_destroy(mimetable);
}
gchar *mime_types_get_type(gchar *filename) {
gchar *extension;
debug ("mime_types_get_type()");
if (!mimetable || !filename) {
debug ("No mimetable or filename specified");
return NULL;
}
extension = strrchr(filename, '.');
if (!extension || *(extension+1) == 0) {
debug("unknown extension");
return NULL;
}
extension++;
return g_hash_table_lookup(mimetable, extension);
}
/* read given mime.types file and initialize the hash */
GHashTable *mime_init(gchar *file) {
FILE *f1;
gchar str[4096];
GHashTable *mimetable;
debug("mime_init()");
f1 = fopen(file, "r");
if (!f1) {
debug("file open failed for '%s'", file);
return NULL;
}
/* init the table */
mimetable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
if (!mimetable) {
debug("was not able to initialize mimetable. out of mem?");
return NULL;
}
/* parse the mime.types file */
while (fgets(str, sizeof(str), f1)) {
gchar *key, *value;
gchar *tmp;
// gchar buf[4096];
// ignore lines starting with '#'
if (str[0] == '#') {
continue;
}
// ignore empty lines
if (strlen(str) < 3) {
continue;
}
str[strlen(str) - 1] = 0; /* nullify the end of the string */
tmp = strrchr(str, '\t'); /* ?? */
if (!tmp) {
continue;
}
*tmp = 0;
tmp++;
if (*tmp == 0) {
continue;
}
key = tmp;
tmp = strchr(str, '\t'); /* ?? */
if (!tmp) {
continue;
}
*tmp = 0;
value = str;
tmp = strtok(key, " ");
if (!tmp) {
g_hash_table_insert(mimetable, strdup(key), strdup(value));
} else {
g_hash_table_insert(mimetable, strdup(tmp), strdup(value));
while ( ( tmp = strtok(NULL, " ") )) {
g_hash_table_insert(mimetable, strdup(tmp), strdup(value));
}
}
}
return mimetable;
}
/* called when an event was triggered for a <nickname> in the text view */
gboolean context_nick_event(GtkTextTag *texttag, GObject *widget, GdkEvent *event, GtkTextIter *iter, gpointer user_data) {
GtkMenu *menu;
GtkWidget *menuitem;
SilcClientID *clientid;
gchar *target_nick = NULL;
GtkTextIter *start_iter, *end_iter;
GtkTextBuffer *text_buffer;
if (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS) {
/* Get the nickname from the text */
start_iter = gtk_text_iter_copy (iter);
end_iter = gtk_text_iter_copy (iter);
while (!gtk_text_iter_begins_tag (start_iter, texttag)) {
gtk_text_iter_backward_char (start_iter);
}
while (!gtk_text_iter_ends_tag (end_iter, texttag)) {
gtk_text_iter_forward_char (end_iter);
}
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
target_nick = gtk_text_buffer_get_text (text_buffer, start_iter, end_iter, FALSE);
if (!target_nick) {
debug("no tagtext found?!");
}
gtk_text_iter_free (start_iter);
gtk_text_iter_free (end_iter);
}
/* single right-click */
if (target_nick && event->type == GDK_BUTTON_PRESS && event->button.button == 3 ) {
debug ("right-clicked on '%s'", target_nick );
// temp, should get one from the tags
clientid = silky->conn->local_entry->id;
/* create the menu */
menu = GTK_MENU(gtk_menu_new());
/* ADD MENU items and signal handlers*/
/* WHOIS */
menuitem = gtk_menu_item_new_with_label(_("Who is"));
g_signal_connect(menuitem, "activate", G_CALLBACK(handle_command_callback), g_strdup_printf("WHOIS %s", target_nick));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show (menuitem);
/* send a message */
menuitem = gtk_menu_item_new_with_label(_("Send message"));
g_signal_connect(menuitem, "activate", G_CALLBACK(handle_command_callback), g_strdup_printf("QUERY %s", target_nick));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show (menuitem);
/* a separator here */
menuitem = gtk_separator_menu_item_new();
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show(menuitem);
/* cumode +o user */
menuitem = gtk_menu_item_new_with_label(_("Op"));
g_signal_connect(menuitem, "activate", G_CALLBACK(handle_command_callback), g_strdup_printf("OP %s", target_nick));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show(menuitem);
/* cumode -o user */
menuitem = gtk_menu_item_new_with_label(_("Deop"));
g_signal_connect(menuitem, "activate", G_CALLBACK(handle_command_callback), g_strdup_printf("DEOP %s", target_nick));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show(menuitem);
/* cumode +q user */
menuitem = gtk_menu_item_new_with_label(_("Mute"));
g_signal_connect(menuitem, "activate", G_CALLBACK(handle_command_callback), g_strdup_printf("MUTE %s", target_nick));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show(menuitem);
/* cumode -q user */
menuitem = gtk_menu_item_new_with_label(_("Unmute"));
g_signal_connect(menuitem, "activate", G_CALLBACK(handle_command_callback), g_strdup_printf("UNMUTE %s", target_nick));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show(menuitem);
/* a separator here */
menuitem = gtk_separator_menu_item_new();
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show(menuitem);
/* kick user */
menuitem = gtk_menu_item_new_with_label(_("Kick"));
g_signal_connect(menuitem, "activate", G_CALLBACK(handle_command_callback), g_strdup_printf("KICK %s", target_nick));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show(menuitem);
/* kill user */
/* FIXME, the on_command_kill needs some work
menuitem = gtk_menu_item_new_with_label(_("Kill"));
g_signal_connect(menuitem, "activate", G_CALLBACK(on_command_kill), clientid);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show(menuitem);
*/
/* show the menu */
gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
event->button.button, event->button.time);
}
/* right button released, should hide a context sensitive menu if it was visible */
if (event->type == GDK_BUTTON_RELEASE && event->button.button == 3 ) {
debug ("Released right-click");
}
/* double left-click */
if (target_nick && event->type == GDK_2BUTTON_PRESS && event->button.button == 1 ) {
debug ("Double left-clicked on '%s'", target_nick);
handle_command(g_strdup_printf("QUERY %s", target_nick));
}
g_free(target_nick);
return TRUE;
}
SilcChannelUser get_channel_user_by_entry(SilcChannelEntry channel, SilcClientEntry client) {
SilcChannelUser chu;
SilcHashTableList htl;
if( !channel ) {
debug("channel == NULL");
return NULL;
}
if (!channel->user_list) {
debug("no ulist, returning");
return NULL;
}
debug("calling silc_hash_table_list()");
silc_hash_table_list(channel->user_list, &htl);
debug("looping");
while(silc_hash_table_get(&htl, NULL, (void *)&chu)) {
if( client == chu->client ) return chu;
}
return NULL;
}
/*
Tries to initialize mimetable.
*/
gboolean silky_read_mimetable() {
gint i;
/*
First NULL is reserved for later use..
*/
gchar *mimefiles[] = {
NULL,
"/etc/mime.types", /* default */
"/usr/pkg/etc/mime.types", /* pkgsrc */
"/usr/lib/mime.types", /* IRIX 6.5 */
"/usr/local/etc/mime.types", /* default? */
"mime.types", /* Current dir */
NULL
};
if (!homedir) {
g_error("No homedir\n");
}
#ifdef SILKY_WIN32
mimefiles[0] = g_strconcat(homedir, "\\mime.types", NULL);
#else
mimefiles[0] = g_strconcat(homedir, "/.mime.types", NULL);
#endif
/* Check all paths from mimefiles to find working "mime.types" file, stops on first one */
for (i=0; mimefiles[i]; i++) {
debug("trying %d: '%s'", i, mimefiles[i]);
mimetable = mime_init(mimefiles[i]);
if (mimetable) {
break;
}
}
/* We didn't find any mime.table: give error and quit */
if (!mimetable) {
debug("mime.types not found at all");
return FALSE;
}
else {
debug("found mime.types. Hurray!");
return TRUE;
}
}
gchar *silky_get_homedir() {
gchar *homedir;
homedir = g_strdup(g_get_home_dir());
if (!homedir) {
debug("homedir not found, can not continue");
return NULL;
}
else {
return homedir;
}
}
/*
returns full path to user's silkydir
*/
gchar *silky_get_silkydir(gchar *homedir) {
#ifdef SILKY_WIN32
return g_strconcat(homedir, "\\silky", NULL);
#else
return g_strconcat(homedir, "/.silky/", NULL);
#endif
}
/*
Create silkydir.
*/
gboolean silky_create_silkydir(gchar *silkydir) {
gint ret;
if( g_file_test(silkydir, G_FILE_TEST_EXISTS) ) {
if( !g_file_test(silkydir, G_FILE_TEST_IS_DIR) ) {
debug("Error: '%s' exists, but is NOT a directory. Aborting", silkydir);
return FALSE;
}
debug("'%s' exists and is a directory, continuing", silkydir);
return TRUE;
}
debug("Directory '%s' does not exist, trying to create it", silkydir);
#ifdef SILKY_WIN32
ret = mkdir(silkydir);
#else
ret = mkdir(silkydir, 0700);
#endif
if (ret == -1) {
debug("error creating '%s' (error %d: %s)", silkydir, errno, strerror(errno));
if (errno == EEXIST) {
debug("'%s' exists, using it", silkydir);
}
else {
debug("Can not create '%s'", silkydir);
return FALSE;
}
}
else {
debug ("'%s' created", silkydir);
}
return TRUE;
}
/*
creates serverkeydir
*/
gboolean silky_create_serverkeydir(gchar *serverkeydir) {
gint ret;
debug("trying to create '%s'", serverkeydir);
if (!serverkeydir) return FALSE;
#ifdef SILKY_WIN32
ret = mkdir(serverkeydir);
#else
ret = mkdir(serverkeydir, 0700);
#endif
if (ret == -1) {
debug("error creating '%s' (error %d: %s)", serverkeydir, errno, strerror(errno));
if (errno == EEXIST) {
debug("'%s' exists, using it", serverkeydir);
}
else {
g_error("Can not create '%s'\n", serverkeydir);
return FALSE;
}
}
else {
debug ("'%s' created", serverkeydir);
}
return TRUE;
}
/* fixme: is this, and why, needed? */
void silky_nickname_parse(const char *nickname, char **ret_nickname) {
// strncpy (&ret_nickname, nickname, sizeof(&ret_nickname)-1);
debug("got '%s'", nickname);
*ret_nickname = strdup(nickname);
}
/* Returns a random positive number between 'min' and 'max-1' */
unsigned int get_random_number(const unsigned int min,
const unsigned int max) {
unsigned int range, ret;
SilcUInt32 random;
/* Largish random number */
random = silc_rng_get_rn32(silky->rng);
/* Random number in range of given values */
range = max - min;
ret = random % range;
return ret + min;
}
/* inputs null, returns active textview widget */
void * silky_get_active_textview () {
GtkTextView *textview = NULL;
GtkNotebook *storednotebook = NULL;
GladeXML *stored_xml;
GtkWidget *active_page = NULL;
storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs"));
active_page = gtk_notebook_get_nth_page(storednotebook, gtk_notebook_get_current_page(GTK_NOTEBOOK(storednotebook) ) ); /* active page from stored g_object*/
stored_xml = glade_get_widget_tree(active_page);
textview = GTK_TEXT_VIEW(glade_xml_get_widget (stored_xml, "textviewwidget"));
return textview;
}
int verify_message_signature(SilcClientEntry sender, SilcMessageSignedPayload sig, SilcMessagePayload message) {
SilcPublicKey pk, cached_pk = NULL;
const gchar *filename;
gchar *fingerprint, *fingerprint2;
unsigned char *pk_data;
SilcUInt32 pk_datalen;
struct stat st;
int ret = SILC_MSG_SIGNED_VERIFIED;
if( !sig ) return SILC_MSG_SIGNED_UNKNOWN;
/* get public key from the signature payload and compare it with the
one stored in the client entry */
pk = silc_message_signed_get_public_key(sig, &pk_data, &pk_datalen);
if( pk ) {
fingerprint = silc_fingerprint(pk_data, pk_datalen);
if (sender->fingerprint) {
fingerprint2 = silc_fingerprint(sender->fingerprint, sender->fingerprint_len);
if (strcmp(fingerprint, fingerprint2)) {
/* since the public key differs from the senders public key, the
verification _failed_ */
silc_pkcs_public_key_free(pk);
silc_free(fingerprint);
ret = SILC_MSG_SIGNED_UNKNOWN;
}
silc_free(fingerprint2);
}
} else if( sender->fingerprint ) {
fingerprint = silc_fingerprint(sender->fingerprint, sender->fingerprint_len);
} else
/* no idea, who or what signed that message ... */
return SILC_MSG_SIGNED_UNKNOWN;
/* search our local client key cache */
filename = gen_keyfile_path(fingerprint, "client");
silc_free(fingerprint);
if( stat(filename, &st) < 0 )
/* we don't have the public key cached ... use the one from the sig */
ret = SILC_MSG_SIGNED_UNKNOWN;
else {
/* try to load the file */
if (!silc_pkcs_load_public_key(filename, &cached_pk, SILC_PKCS_FILE_PEM) &&
!silc_pkcs_load_public_key(filename, &cached_pk,
SILC_PKCS_FILE_BIN)) {
debug("Couldn't load client key.");
if( !pk ) return SILC_MSG_SIGNED_UNKNOWN;
else ret = SILC_MSG_SIGNED_UNKNOWN;
}
if( cached_pk ) {
if( pk ) silc_pkcs_public_key_free(pk);
pk = cached_pk;
}
}
/* the public key is now in pk, our "level of trust" in ret */
if( (pk) && silc_message_signed_verify(sig, message, pk, silky->client->sha1hash)!= SILC_AUTH_OK )
ret = SILC_MSG_SIGNED_FAILED;
if( pk )
silc_pkcs_public_key_free(pk);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1