/*
Silky - A GTK+ client for SILC.
Copyright (C) 2003, 2004, 2005 Toni Willberg
- Channel list management functions
- Channel member list management functions
- Nickname TAB-completion functions
- Generic SILC channel 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 <glade/glade.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <silcincludes.h>
#include <silcclient.h>
#include "common.h"
#include "gui.h"
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ui_channel.h" /* for refresh_gui_nicklist() */
#include "support.h"
#include "log.h"
extern silkyStruct *silky;
SilkyChannel *channels = NULL;
SilkyChannel *channel_active = NULL;
/* channel_find_*() */
SilkyChannel *channel_find_by_name( gchar *name ) {
SilkyChannel *ch;
if (name == NULL) {
return (NULL);
}
for( ch = channels; ch; ch = ch->next ) {
if( name && !strcmp(CHANNEL_NAME(ch), name) ) {
debug("Channel found.");
return(ch);
}
}
debug("Channel not found, returning NULL.");
return(NULL);
}
SilkyChannel *channel_find_by_pagenr( gint pagenr ) {
SilkyChannel *ch;
for( ch = channels; ch; ch = ch->next ) {
if( ch->pagenr == pagenr ) {
debug("found channel, page %d", ch->pagenr);
return(ch);
}
}
debug("Channel not found on page %d.", pagenr);
return(NULL);
}
SilkyChannel *channel_find_by_entry( SilcChannelEntry entry ) {
SilkyChannel *ch;
if (!entry) {
debug("entry == NULL !!");
return(NULL);
}
for( ch = channels; ch; ch = ch->next ) {
if( ch->channel_entry == entry ) return(ch);
}
debug("channel NOT found");
return(NULL);
}
/* creates a new channel struct in memory */
SilkyChannel *channel_add( SilcChannelEntry entry ) {
SilkyChannel *ch, *newch;
if( channel_find_by_entry(entry) ) {
debug("channel '%s' already exists", entry->channel_name);
return(NULL);
}
debug("creating channel struct for '%s'", entry->channel_name);
newch = malloc( sizeof( SilkyChannel ) );
newch->next = NULL;
newch->channel_entry = entry;
newch->members = NULL;
newch->tab_member = NULL;
newch->tab_prefix = NULL;
newch->tab_text = NULL;
newch->pagenr = -1; /* gint - will be set later */
newch->label_text = NULL; /* GtkLabel */
newch->label_image = NULL; /* GtkImage */
if( !channels ) {
debug("first channel, creating linked list");
channels = newch;
} else {
debug("there are already some channels, appending to linked list");
for( ch = channels; ch->next; ch = ch->next ) {}
ch->next = newch;
}
return( newch );
}
/* channel_remove_*() */
gboolean channel_remove_by_name( gchar *name ) {
SilkyChannel *ch, *prevch;
gint found = 0;
debug("trying to remove channel '%s'", name);
if( !channels ) {
debug("No channels, returning");
return FALSE;
}
for( ch = channels, prevch = channels; ch; ch = ch->next ) {
if (CHANNEL_NAME(ch) == NULL) {
debug("WARNING: Channel without a name found, this should not be!");
continue;
}
if ( !g_ascii_strcasecmp(CHANNEL_NAME(ch), name) ) {
debug("found it");
found++;
break;
}
prevch = ch;
}
if( !found ) {
debug("could not find channel struct, returning");
return FALSE;
}
if( channels == ch ) {
debug("it is first channel");
channels = channels->next;
} else {
debug("not first channel");
prevch->next = ch->next;
}
debug("removing '%s'", CHANNEL_NAME(ch));
g_free(ch);
return TRUE;
}
gboolean channel_remove_by_entry( SilcChannelEntry entry ) {
SilkyChannel *ch, *prevch;
gint found = 0;
if (!entry) {
debug("entry == NULL !!");
return FALSE;
}
debug("trying to remove channel '%s'", entry->channel_name);
for( ch = channels, prevch = channels; ch; ch = ch->next ) {
if ( ch->channel_entry == entry ) {
debug("found it");
found++;
break;
}
prevch = ch;
}
if( !found ) {
debug("could not find channel struct, returning");
return FALSE;
}
if( channels == ch ) {
debug("it is first channel");
channels = channels->next;
} else {
debug("not first channel");
prevch->next = ch->next;
}
channel_member_free_all(ch);
debug("removing '%s'", CHANNEL_NAME(ch));
g_free(ch);
return TRUE;
}
/* channel_topic_set_*() */
void channel_topic_set_by_name( gchar *name, gchar *topic ) {
SilkyChannel *ch = channel_find_by_name(name);
if(ch) {
debug("setting topic for '%s'", CHANNEL_NAME(ch));
g_snprintf(ch->topic, 256, "%s", topic);
}
}
void channel_topic_set_by_entry( SilcChannelEntry entry, gchar *topic ) {
SilkyChannel *ch;
if (!entry) {
debug("entry == NULL !!");
return;
}
ch = channel_find_by_entry( entry );
if(ch) {
debug("setting topic for '%s'", CHANNEL_NAME(ch));
strncpy(ch->topic, topic, 256);
}
}
/* channel_pagenr_set_*() */
void channel_pagenr_set_by_name( gchar *name, gint pagenr ) {
SilkyChannel *ch = channel_find_by_name(name);
if(ch) {
debug("setting pagenr for '%s'", CHANNEL_NAME(ch));
ch->pagenr = pagenr;
}
}
void channel_pagenr_set_by_entry( SilcChannelEntry entry, gint pagenr ) {
SilkyChannel *ch;
if (!entry) {
debug("entry == NULL !!");
return;
}
ch = channel_find_by_entry( entry );
if(ch) {
debug("setting pagenr for '%s'", CHANNEL_NAME(ch));
ch->pagenr = pagenr;
}
}
/* channel_mode_set_*() */
void channel_mode_set_by_name( gchar *name, SilcUInt32 cmode ) {
SilkyChannel *ch = channel_find_by_name( name );
if(ch) {
debug("setting cmode for '%s'", CHANNEL_NAME(ch));
ch->channel_entry->mode = cmode;
} else {
debug("no channel found");
}
}
void channel_mode_set_by_entry( SilcChannelEntry entry, SilcUInt32 cmode ) {
SilkyChannel *ch;
if (!entry) {
debug("entry == NULL !!");
return;
}
ch = channel_find_by_entry( entry );
if(ch) {
debug("setting cmode for '%s'", CHANNEL_NAME(ch));
ch->channel_entry->mode = cmode;
} else {
debug("no channel found");
}
}
/* channel_find_*() */
SilkyChannelMember *channel_member_find_by_nick( SilkyChannel *channel, gchar *name ) {
SilkyChannelMember *ch;
if( !channel ) {
debug("channel == NULL");
return(NULL);
}
for( ch = channel->members; ch; ch = ch->next ) {
if( !strcmp(CHMEMBER_NAME(ch), name) ) {
debug("found channel member '%s'", CHMEMBER_NAME(ch));
return(ch);
}
}
debug("channel member '%s' NOT found in channel %s", name, CHANNEL_NAME(channel));
return(NULL);
}
SilkyChannelMember *channel_member_find_by_entry( SilkyChannel *channel, SilcClientEntry entry ) {
SilkyChannelMember *ch;
if( !channel || !(channel->members)) {
debug("channel == NULL");
return(NULL);
}
for( ch = channel->members; ch; ch = ch->next ) {
if( ch->chuser->client == entry ) {
debug("found channel member '%s'", CHMEMBER_NAME(ch));
return(ch);
}
}
debug("channel member NOT found in channel %s", CHANNEL_NAME(channel));
return(NULL);
}
/* creates a new channel member struct in memory and links it to the position
in the list indicated by pos (starting from 0)
(if pos < 0, simply append) */
SilkyChannelMember *channel_member_add( SilkyChannel *channel, SilcChannelUser chuser ) {
SilkyChannelMember *ch = NULL, *prevch, *newch;
if( !channel ) {
debug("channel == NULL");
return(NULL);
}
/* first we create the struct for new member */
newch = malloc( sizeof( SilkyChannelMember ) );
newch->chuser = chuser;
newch->client = chuser->client;
if( !channel->members ) {
debug("first channel member, creating linked list");
channel->members = newch;
newch->next = NULL;
} else {
debug("there are already some channel members, adding into linked list");
prevch = NULL;
for( ch = channel->members; ch->next; ch = ch->next ) {
if( g_utf8_collate( g_utf8_casefold(CHMEMBER_NAME(newch), -1), g_utf8_casefold(CHMEMBER_NAME(ch), -1)) <= 0 ) {
if( !prevch ) {
debug("inserting '%s' to the beginning of list", CHMEMBER_NAME(newch));
channel->members = newch;
newch->next = ch;
} else {
debug("inserting '%s' before '%s' in list", CHMEMBER_NAME(newch), CHMEMBER_NAME(ch));
prevch->next = newch;
newch->next = ch;
}
return(newch);
}
prevch = ch;
}
debug("adding '%s' to the end of list", CHMEMBER_NAME(newch));
ch->next = newch;
newch->next = NULL;
return(newch);
}
return(newch);
}
/* channel_member_remove_by_*() */
gint channel_member_remove_by_entry( SilkyChannel *channel, SilcClientEntry client ) {
SilkyChannelMember *ch, *prevch;
gint found = 0;
if( !channel || !(channel->members) ) {
debug("channel or channel->members is NULL!");
return(0);
}
if( !client ) {
debug("client == NULL");
return(0);
}
for( ch = channel->members, prevch = channel->members; ch; ch = ch->next ) {
if ( ch->client == client ) {
debug("found it");
found++;
break;
}
prevch = ch;
}
if( !found ) {
debug("could not find channel member struct, returning");
return(0);
}
if( channel->members == ch ) channel->members = channel->members->next;
else prevch->next = ch->next;
debug("removing '%s' from channel %s", CHMEMBER_NAME(ch), CHANNEL_NAME(channel));
free(ch);
return(1);
}
void channel_member_change_client_entry(SilkyChannel *channel, SilcClientEntry oldclient, SilcClientEntry newclient) {
SilkyChannelMember *ch;
if( !channel ) {
debug("channel == NULL");
return;
}
if( !newclient || !oldclient ) {
debug("newclient or oldclient is NULL");
return;
}
for( ch = channel->members; ch; ch = ch->next )
if( ch->client == oldclient ) ch->client = newclient;
}
void channel_free_all( void ) {
SilkyChannel *ch, *prevch = NULL;
for( ch = channels; ch; ch = ch->next ) {
if(prevch) {
debug("freeing channel struct '%s'", CHANNEL_NAME(prevch));
channel_member_free_all(prevch);
g_free(prevch);
}
prevch = ch;
if( ch && !ch->next ) {
debug("freeing last channel struct '%s'", CHANNEL_NAME(ch));
channel_member_free_all(ch);
g_free(ch);
break;
}
}
channels = NULL;
channel_active = NULL;
}
void channel_member_free_all( SilkyChannel *channel ) {
SilkyChannelMember *ch, *prevch = NULL;
if( !channel ) {
debug("channel == NULL");
return;
}
for( ch = channel->members; ch; ch = ch->next ) {
if(prevch) {
debug("freeing channel member struct '%s'", CHMEMBER_NAME(prevch));
g_free(prevch);
}
prevch = ch;
if( ch && !ch->next ) {
debug("freeing last channel member struct '%s'", CHMEMBER_NAME(ch));
g_free(ch);
break;
}
}
channel->members = NULL;
channel->tab_member = NULL;
}
/*
called when a user's nickname changes
informs all channels' UI about the change
*/
void channels_nick_change (SilcClientEntry old_entry, SilcClientID *old_client_id, SilcClientEntry new_client_entry) {
SilcHashTableList userhtl;
SilcChannelEntry userchannel;
gchar *old_nickname = old_entry->nickname;
SilkyChannel *channel;
/* channels of the nick */
silc_hash_table_list(new_client_entry->channels, &userhtl);
while (silc_hash_table_get(&userhtl, (void *)&userchannel, NULL)) {
debug("\tchannel: '%s'", userchannel->channel_name);
/* Show nick change in all channel windows */
/* I18N This is a nickname change */
printchannel(userchannel->channel_name, g_strdup_printf(_("'%s' is now known as '%s'."), old_nickname, new_client_entry->nickname));
channel = channel_find_by_entry(userchannel);
/* Update nicklist of the channel */
channel_member_change_client_entry(channel, old_entry, new_client_entry);
channel_member_remove_by_entry(channel, new_client_entry);
channel_member_add(channel, get_channel_user_by_entry(channel->channel_entry, new_client_entry));
refresh_gui_nicklist(channel);
}
silc_hash_table_list_reset(&userhtl);
debug("Iteration done. Returning.");
/* free? */
return;
}
/* Call this whenever something changes in the channel. This will resolve
the channel from the server and refresh the UI nicklist */
void refresh_nicklist (SilcChannelEntry channel_entry) {
SilcJoinResolve *r;
SilkyChannelMember *ch;
SilkyChannel *cha;
debug("refresh_nicklist");
if (!channel_entry) {
debug("Did not get channel_entry in refresh_nicklist()");
}
r = silc_calloc(1, sizeof(*r));
r->channel = channel_entry;
r->retry = 0;
debug("channel: '%s'", channel_entry->channel_name);
debug("freeing list of members, so we can recreate it");
channel_member_free_all(cha = channel_find_by_entry(channel_entry));
silc_client_get_clients_by_channel(silky->client, silky->conn,
channel_entry, refresh_nicklist_resolved, r);
debug("now displaying contents of channel members linked list:");
for( ch = cha->members; ch; ch = ch->next )
debug(">>> %s", CHMEMBER_NAME(ch));
debug("refresh_nicklist returning");
}
/* this gets called when the userlist is resolved */
void refresh_nicklist_resolved(SilcClient client,
SilcClientConnection conn,
SilcClientEntry *clients,
SilcUInt32 clients_count,
void *context) {
SilcJoinResolve *r = context;
SilcChannelEntry channel = r->channel;
SilcHashTableList htl;
SilcChannelUser chu;
gint usercount;
SilkyChannel *cha = channel_find_by_entry(channel);
debug("refresh_nicklist_resolved");
/* check that the resolving was complete */
debug("channel: '%s'", channel->channel_name);
if (!clients && r->retry < 1) {
/* not, retry */
r->retry++;
silc_client_get_clients_by_channel(client, conn, channel,
refresh_nicklist_resolved, context);
return;
}
debug("Starting to list users...");
/* iterate through the list */
usercount = 0;
silc_hash_table_list(channel->user_list, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
/* this client has no nickname. (WHY?) */
if (!chu->client->nickname) {
continue; /* skip */
}
usercount++;
debug("\tnick: '%s' realname: '%s' ", chu->client->nickname,
chu->client->realname);
channel_member_add(cha, chu);
}
/* end of while */
if( usercount == 1) debug("1 user listed");
else debug("%d users listed", usercount);
/* now we update gui nicklist from channel struct */
refresh_gui_nicklist(channel_find_by_entry(channel));
/* This needs to be in the end of this function */
silc_hash_table_list_reset(&htl);
debug("returning...");
}
void silky_join_channel(const gchar *channel_name) {
SilcBuffer idp = silc_id_payload_encode(silky->conn->local_id, SILC_ID_CLIENT);
if( !idp ) {
debug("Couldn't get idp in silc invite handler, cannot join.");
return;
}
silc_client_command_send(silky->client, silky->conn, SILC_COMMAND_JOIN, 0, 2,
1, channel_name, strlen(channel_name), 2, idp->data, idp->len);
debug("Trying to join channel '%s'.", channel_name);
}
void silky_leave_channel(const gchar *channel_name) {
if( !channel_name || !strlen(channel_name) ) {
debug("No channel name given, can not leave.");
return;
}
if( !CONNECTED ) return;
debug("Leaving channel '%s'", channel_name);
printconsole(g_strdup_printf(_("Leaving channel '%s'."), channel_name));
silc_client_command_call(silky->client, silky->conn, NULL, "LEAVE", channel_name, NULL);
}
syntax highlighted by Code2HTML, v. 0.9.1