/* 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 #include #include #include #include #include "common.h" #include "gui.h" #ifdef HAVE_CONFIG_H # include #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); }