Author: Pekka Riikonen <priikone@silcnet.org> Copyright (C) 2004 Pekka Riikonen 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; version 2 of the License. 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. #include "silcincludes.h" /***************************** Channel Routines ******************************/ GList *silcpurple_chat_info(PurpleConnection *gc) struct proto_chat_entry *pce; pce = g_new0(struct proto_chat_entry, 1); pce->label = _("_Channel:"); pce->identifier = "channel"; ci = g_list_append(ci, pce); pce = g_new0(struct proto_chat_entry, 1); pce->label = _("_Passphrase:"); pce->identifier = "passphrase"; ci = g_list_append(ci, pce); GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name) defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); g_hash_table_insert(defaults, "channel", g_strdup(chat_name)); silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components); silcpurple_chat_getinfo_res(SilcClient client, SilcClientConnection conn, SilcChannelEntry *channels, SilcUInt32 channels_count, GHashTable *components = context; PurpleConnection *gc = client->application; chname = g_hash_table_lookup(components, "channel"); g_snprintf(tmp, sizeof(tmp), _("Channel %s does not exist in the network"), chname); purple_notify_error(gc, _("Channel Information"), _("Cannot get channel information"), tmp); silcpurple_chat_getinfo(gc, components); silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components) SilcPurple sg = gc->proto_data; char *buf, tmp[256], *tmp2; SilcChannelEntry channel; chname = g_hash_table_lookup(components, "channel"); channel = silc_client_get_channel(sg->client, sg->conn, silc_client_get_channel_resolve(sg->client, sg->conn, silcpurple_chat_getinfo_res, tmp2 = g_markup_escape_text(channel->channel_name, -1); g_string_append_printf(s, _("<b>Channel Name:</b> %s"), tmp2); if (channel->user_list && silc_hash_table_count(channel->user_list)) g_string_append_printf(s, _("<br><b>User Count:</b> %d"), (int)silc_hash_table_count(channel->user_list)); silc_hash_table_list(channel->user_list, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) { tmp2 = g_markup_escape_text(chu->client->nickname, -1); g_string_append_printf(s, _("<br><b>Channel Founder:</b> %s"), silc_hash_table_list_reset(&htl); if (channel->channel_key) g_string_append_printf(s, _("<br><b>Channel Cipher:</b> %s"), silc_cipher_get_name(channel->channel_key)); /* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */ g_string_append_printf(s, _("<br><b>Channel HMAC:</b> %s"), silc_hmac_get_name(channel->hmac)); tmp2 = g_markup_escape_text(channel->topic, -1); g_string_append_printf(s, _("<br><b>Channel Topic:</b><br>%s"), tmp2); g_string_append_printf(s, _("<br><b>Channel Modes:</b> ")); silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp)); if (channel->founder_key) { char *fingerprint, *babbleprint; pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len); fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); g_string_append_printf(s, _("<br><b>Founder Key Fingerprint:</b><br>%s"), fingerprint); g_string_append_printf(s, _("<br><b>Founder Key Babbleprint:</b><br>%s"), babbleprint); buf = g_string_free(s, FALSE); purple_notify_formatted(gc, NULL, _("Channel Information"), NULL, buf, NULL, NULL); silcpurple_chat_getinfo_menu(PurpleBlistNode *node, gpointer data) PurpleChat *chat = (PurpleChat *)node; silcpurple_chat_getinfo(chat->account->gc, chat->components); #if 0 /* XXX For now these are not implemented. We need better listview dialog from Purple for these. */ /************************** Channel Invite List ******************************/ silcpurple_chat_invitelist(PurpleBlistNode *node, gpointer data); /**************************** Channel Ban List *******************************/ silcpurple_chat_banlist(PurpleBlistNode *node, gpointer data); /************************* Channel Authentication ****************************/ SilcChannelEntry channel; silcpurple_chat_chpk_add(void *user_data, const char *name) SilcPurpleChauth sgc = (SilcPurpleChauth)user_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcPublicKey public_key; SilcBuffer chpks, pk, chidp; /* Load the public key */ if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) && !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) { silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys); silc_buffer_free(sgc->pubkeys); purple_notify_error(client->application, _("Add Channel Public Key"), _("Could not load public key"), NULL); pk = silc_pkcs_public_key_payload_encode(public_key); chpks = silc_buffer_alloc_size(2); SILC_PUT16_MSB(1, chpks->head); chpks = silc_argument_payload_encode_one(chpks, pk->data, m |= SILC_CHANNEL_MODE_CHANNEL_AUTH; chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL); silc_client_command_send(client, conn, SILC_COMMAND_CMODE, 1, chidp->data, chidp->len, 9, chpks->data, chpks->len); silc_buffer_free(sgc->pubkeys); silcpurple_chat_chpk_cancel(void *user_data, const char *name) SilcPurpleChauth sgc = (SilcPurpleChauth)user_data; silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys); silc_buffer_free(sgc->pubkeys); silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields) SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcPublicKey public_key; SilcBuffer chpks, pk, chidp; f = purple_request_fields_get_field(fields, "list"); if (!purple_request_field_list_get_selected(f)) { purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE, G_CALLBACK(silcpurple_chat_chpk_add), G_CALLBACK(silcpurple_chat_chpk_cancel), purple_connection_get_account(sg->gc), NULL, NULL, sgc); list = purple_request_field_list_get_items(f); chpks = silc_buffer_alloc_size(2); for (ct = 0; list; list = list->next, ct++) { public_key = purple_request_field_list_get_data(f, list->data); if (purple_request_field_list_is_selected(f, list->data)) { /* Delete this public key */ pk = silc_pkcs_public_key_payload_encode(public_key); chpks = silc_argument_payload_encode_one(chpks, pk->data, silc_pkcs_public_key_free(public_key); SILC_PUT16_MSB(c, chpks->head); m &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH; chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL); silc_client_command_send(client, conn, SILC_COMMAND_CMODE, 1, chidp->data, chidp->len, 9, chpks->data, chpks->len); silc_buffer_free(sgc->pubkeys); silcpurple_chat_chauth_ok(SilcPurpleChauth sgc, PurpleRequestFields *fields) const char *curpass, *val; f = purple_request_fields_get_field(fields, "passphrase"); val = purple_request_field_string_get_value(f); curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase"); else if (val && !curpass) else if (val && curpass && strcmp(val, curpass)) silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", sgc->channel->channel_name, "+a", val, NULL); purple_blist_node_set_string((PurpleBlistNode *)sgc->c, "passphrase", val); silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", sgc->channel->channel_name, "-a", NULL); purple_blist_node_remove_setting((PurpleBlistNode *)sgc->c, "passphrase"); silc_buffer_free(sgc->pubkeys); void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel, SilcBuffer channel_pubkeys) SilcArgumentPayload chpks; char *fingerprint, *babbleprint; SilcPublicKeyIdentifier ident; PurpleRequestFields *fields; PurpleRequestFieldGroup *g; const char *curpass = NULL; sgc = silc_calloc(1, sizeof(*sgc)); fields = purple_request_fields_new(); curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase"); g = purple_request_field_group_new(NULL); f = purple_request_field_string_new("passphrase", _("Channel Passphrase"), purple_request_field_string_set_masked(f, TRUE); purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); g = purple_request_field_group_new(NULL); f = purple_request_field_label_new("l1", _("Channel Public Keys List")); purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); _("Channel authentication is used to secure the channel from " "unauthorized access. The authentication may be based on " "passphrase and digital signatures. If passphrase is set, it " "is required to be able to join. If channel public keys are set " "then only users whose public keys are listed are able to join.")); f = purple_request_field_list_new("list", NULL); purple_request_field_group_add_field(g, f); purple_request_fields(sg->gc, _("Channel Authentication"), _("Channel Authentication"), t, fields, _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb), _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok), purple_connection_get_account(sg->gc), NULL, NULL, sgc); sgc->pubkeys = silc_buffer_copy(channel_pubkeys); g = purple_request_field_group_new(NULL); f = purple_request_field_list_new("list", NULL); purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); SILC_GET16_MSB(argc, channel_pubkeys->data); chpks = silc_argument_payload_parse(channel_pubkeys->data + 2, channel_pubkeys->len - 2, argc); pk = silc_argument_get_first_arg(chpks, &type, &pk_len); fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4); babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4); silc_pkcs_public_key_payload_decode(pk, pk_len, &pubkey); ident = silc_pkcs_decode_identifier(pubkey->identifier); g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s", ident->realname ? ident->realname : ident->username ? ident->username : "", fingerprint, babbleprint); purple_request_field_list_add_icon(f, tmp2, NULL, pubkey); silc_pkcs_free_identifier(ident); pk = silc_argument_get_next_arg(chpks, &type, &pk_len); purple_request_field_list_set_multi_select(f, FALSE); purple_request_fields(sg->gc, _("Channel Authentication"), _("Channel Authentication"), t, fields, _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb), _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok), purple_connection_get_account(sg->gc), NULL, NULL, sgc); silc_argument_payload_free(chpks); silcpurple_chat_chauth(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", g_hash_table_lookup(chat->components, "channel"), /************************** Channel Private Groups **************************/ /* Private groups are "virtual" channels. They are groups inside a channel. This is implemented by using channel private keys. By knowing a channel private key user becomes part of that group and is able to talk on that group. Other users, on the same channel, won't be able to see the messages of that group. It is possible to have multiple groups inside a channel - and thus having multiple private keys on the channel. */ silcpurple_chat_prv_add(SilcPurpleCharPrv p, PurpleRequestFields *fields) const char *name, *passphrase, *alias; f = purple_request_fields_get_field(fields, "name"); name = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "passphrase"); passphrase = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "alias"); alias = purple_request_field_string_get_value(f); /* Add private group to buddy list */ g_snprintf(tmp, sizeof(tmp), "%s [Private Group]", name); comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); g_hash_table_replace(comp, g_strdup("channel"), g_strdup(tmp)); g_hash_table_replace(comp, g_strdup("passphrase"), g_strdup(passphrase)); cn = purple_chat_new(sg->account, alias, comp); g = (PurpleGroup *)p->c->node.parent; purple_blist_add_chat(cn, g, (PurpleBlistNode *)p->c); /* Associate to a real channel */ purple_blist_node_set_string((PurpleBlistNode *)cn, "parentch", p->channel); silcpurple_chat_join(sg->gc, comp); silcpurple_chat_prv_cancel(SilcPurpleCharPrv p, PurpleRequestFields *fields) silcpurple_chat_prv(PurpleBlistNode *node, gpointer data) PurpleRequestFields *fields; PurpleRequestFieldGroup *g; g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); p = silc_calloc(1, sizeof(*p)); p->channel = g_hash_table_lookup(chat->components, "channel"); p->c = purple_blist_find_chat(sg->account, p->channel); fields = purple_request_fields_new(); g = purple_request_field_group_new(NULL); f = purple_request_field_string_new("name", _("Group Name"), purple_request_field_group_add_field(g, f); f = purple_request_field_string_new("passphrase", _("Passphrase"), purple_request_field_string_set_masked(f, TRUE); purple_request_field_group_add_field(g, f); f = purple_request_field_string_new("alias", _("Alias"), purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); g_snprintf(tmp, sizeof(tmp), _("Please enter the %s channel private group name and passphrase."), purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields, _("Add"), G_CALLBACK(silcpurple_chat_prv_add), _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel), purple_connection_get_account(gc), NULL, NULL, p); /****************************** Channel Modes ********************************/ silcpurple_chat_permanent_reset(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", g_hash_table_lookup(chat->components, "channel"), silcpurple_chat_permanent(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); /* XXX we should have ability to define which founder key to use. Now we use the user's own public key channel = g_hash_table_lookup(chat->components, "channel"); silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel, silcpurple_chat_ulimit_cb(SilcPurpleChatInput s, const char *limit) SilcChannelEntry channel; channel = silc_client_get_channel(s->sg->client, s->sg->conn, if (!limit || !(*limit) || *limit == '0') { if (limit && ulimit == channel->user_limit) { silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE", if (ulimit == channel->user_limit) { silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE", s->channel, "+l", limit, NULL); silcpurple_chat_ulimit(PurpleBlistNode *node, gpointer data) SilcChannelEntry channel; g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); ch = g_strdup(g_hash_table_lookup(chat->components, "channel")); channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch); s = silc_calloc(1, sizeof(*s)); g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit); purple_request_input(gc, _("User Limit"), NULL, _("Set user limit on channel. Set to zero to reset user limit."), _("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb), _("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb), purple_connection_get_account(gc), NULL, NULL, s); silcpurple_chat_resettopic(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", g_hash_table_lookup(chat->components, "channel"), silcpurple_chat_settopic(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", g_hash_table_lookup(chat->components, "channel"), silcpurple_chat_resetprivate(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", g_hash_table_lookup(chat->components, "channel"), silcpurple_chat_setprivate(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", g_hash_table_lookup(chat->components, "channel"), silcpurple_chat_resetsecret(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", g_hash_table_lookup(chat->components, "channel"), silcpurple_chat_setsecret(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); chat = (PurpleChat *) node; gc = purple_account_get_connection(chat->account); silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", g_hash_table_lookup(chat->components, "channel"), SilcChannelEntry channel; silcpurple_chat_wb(PurpleBlistNode *node, gpointer data) SilcPurpleChatWb wb = data; silcpurple_wb_init_ch(wb->sg, wb->channel); GList *silcpurple_chat_menu(PurpleChat *chat) GHashTable *components = chat->components; PurpleConnection *gc = purple_account_get_connection(chat->account); SilcPurple sg = gc->proto_data; SilcClientConnection conn = sg->conn; const char *chname = NULL; SilcChannelEntry channel = NULL; SilcChannelUser chu = NULL; chname = g_hash_table_lookup(components, "channel"); channel = silc_client_get_channel(sg->client, sg->conn, chu = silc_client_on_channel(channel, conn->local_entry); if (strstr(chname, "[Private Group]")) act = purple_menu_action_new(_("Get Info"), PURPLE_CALLBACK(silcpurple_chat_getinfo_menu), m = g_list_append(m, act); #if 0 /* XXX For now these are not implemented. We need better listview dialog from Purple for these. */ if (mode & SILC_CHANNEL_UMODE_CHANOP) { act = purple_menu_action_new(_("Invite List"), PURPLE_CALLBACK(silcpurple_chat_invitelist), m = g_list_append(m, act); act = purple_menu_action_new(_("Ban List"), PURPLE_CALLBACK(silcpurple_chat_banlist), m = g_list_append(m, act); act = purple_menu_action_new(_("Add Private Group"), PURPLE_CALLBACK(silcpurple_chat_prv), m = g_list_append(m, act); if (mode & SILC_CHANNEL_UMODE_CHANFO) { act = purple_menu_action_new(_("Channel Authentication"), PURPLE_CALLBACK(silcpurple_chat_chauth), m = g_list_append(m, act); if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) { act = purple_menu_action_new(_("Reset Permanent"), PURPLE_CALLBACK(silcpurple_chat_permanent_reset), m = g_list_append(m, act); act = purple_menu_action_new(_("Set Permanent"), PURPLE_CALLBACK(silcpurple_chat_permanent), m = g_list_append(m, act); if (mode & SILC_CHANNEL_UMODE_CHANOP) { act = purple_menu_action_new(_("Set User Limit"), PURPLE_CALLBACK(silcpurple_chat_ulimit), m = g_list_append(m, act); if (channel->mode & SILC_CHANNEL_MODE_TOPIC) { act = purple_menu_action_new(_("Reset Topic Restriction"), PURPLE_CALLBACK(silcpurple_chat_resettopic), m = g_list_append(m, act); act = purple_menu_action_new(_("Set Topic Restriction"), PURPLE_CALLBACK(silcpurple_chat_settopic), m = g_list_append(m, act); if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) { act = purple_menu_action_new(_("Reset Private Channel"), PURPLE_CALLBACK(silcpurple_chat_resetprivate), m = g_list_append(m, act); act = purple_menu_action_new(_("Set Private Channel"), PURPLE_CALLBACK(silcpurple_chat_setprivate), m = g_list_append(m, act); if (channel->mode & SILC_CHANNEL_MODE_SECRET) { act = purple_menu_action_new(_("Reset Secret Channel"), PURPLE_CALLBACK(silcpurple_chat_resetsecret), m = g_list_append(m, act); act = purple_menu_action_new(_("Set Secret Channel"), PURPLE_CALLBACK(silcpurple_chat_setsecret), m = g_list_append(m, act); wb = silc_calloc(1, sizeof(*wb)); act = purple_menu_action_new(_("Draw On Whiteboard"), PURPLE_CALLBACK(silcpurple_chat_wb), m = g_list_append(m, act); /******************************* Joining Etc. ********************************/ void silcpurple_chat_join_done(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, PurpleConnection *gc = client->application; SilcPurple sg = gc->proto_data; SilcChannelEntry channel = context; PurpleConversation *convo; SilcUInt32 retry = SILC_PTR_TO_32(channel->context); GList *users = NULL, *flags = NULL; if (!clients && retry < 1) { /* Resolving users failed, try again. */ channel->context = SILC_32_TO_PTR(retry + 1); silc_client_get_clients_by_channel(client, conn, channel, silcpurple_chat_join_done, channel); /* Add channel to Purple */ channel->context = SILC_32_TO_PTR(++sg->channel_ids); serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name); convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, channel->channel_name, sg->account); /* Add all users to channel */ silc_hash_table_list(channel->user_list, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE; if (!chu->client->nickname) chu->context = SILC_32_TO_PTR(sg->channel_ids); if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) f |= PURPLE_CBFLAGS_FOUNDER; if (chu->mode & SILC_CHANNEL_UMODE_CHANOP) users = g_list_append(users, g_strdup(chu->client->nickname)); flags = g_list_append(flags, GINT_TO_POINTER(f)); if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) { if (chu->client == conn->local_entry) g_snprintf(tmp, sizeof(tmp), _("You are channel founder on <I>%s</I>"), g_snprintf(tmp, sizeof(tmp), _("Channel founder on <I>%s</I> is <I>%s</I>"), channel->channel_name, chu->client->nickname); purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); silc_hash_table_list_reset(&htl); purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE); purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic); purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname); char *silcpurple_get_chat_name(GHashTable *data) return g_strdup(g_hash_table_lookup(data, "channel")); void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; const char *channel, *passphrase, *parentch; channel = g_hash_table_lookup(data, "channel"); passphrase = g_hash_table_lookup(data, "passphrase"); /* Check if we are joining a private group. Handle it purely locally as it's not a real channel */ if (strstr(channel, "[Private Group]")) { SilcChannelEntry channel_entry; SilcChannelPrivateKey key; c = purple_blist_find_chat(sg->account, channel); parentch = purple_blist_node_get_string((PurpleBlistNode *)c, "parentch"); channel_entry = silc_client_get_channel(sg->client, sg->conn, !silc_client_on_channel(channel_entry, sg->conn->local_entry)) { g_snprintf(tmp, sizeof(tmp), _("You have to join the %s channel before you are " "able to join the private group"), parentch); purple_notify_error(gc, _("Join Private Group"), _("Cannot join private group"), tmp); /* Add channel private key */ if (!silc_client_add_channel_private_key(client, conn, (unsigned char *)passphrase, strlen(passphrase), &key)) grp = silc_calloc(1, sizeof(*grp)); grp->id = ++sg->channel_ids + SILCPURPLE_PRVGRP; grp->chid = SILC_PTR_TO_32(channel_entry->context); grp->parentch = parentch; sg->grps = g_list_append(sg->grps, grp); serv_got_joined_chat(gc, grp->id, channel); /* XXX We should have other properties here as well: 1. whether to try to authenticate to the channel 2. whether to try to authenticate to become founder. Since now such variety is not possible in the join dialog we always use -founder and -auth options, which try to do both 1 and 2 with default keys. */ if ((passphrase != NULL) && (*passphrase != '\0')) silc_client_command_call(client, conn, NULL, "JOIN", channel, passphrase, "-auth", "-founder", NULL); silc_client_command_call(client, conn, NULL, "JOIN", channel, "-auth", "-founder", NULL); void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg, SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; /* See if we are inviting on a private group. Invite if (id > SILCPURPLE_PRVGRP) { for (l = sg->grps; l; l = l->next) if (((SilcPurplePrvgrp)l->data)->id == id) silc_hash_table_list(conn->local_entry->channels, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { if (SILC_PTR_TO_32(chu->channel->context) == id ) { silc_hash_table_list_reset(&htl); silc_client_command_call(client, conn, NULL, "INVITE", chu->channel->channel_name, void silcpurple_chat_leave(PurpleConnection *gc, int id) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; /* See if we are leaving a private group */ if (id > SILCPURPLE_PRVGRP) { SilcChannelEntry channel; for (l = sg->grps; l; l = l->next) if (((SilcPurplePrvgrp)l->data)->id == id) channel = silc_client_get_channel(sg->client, sg->conn, silc_client_del_channel_private_key(client, conn, sg->grps = g_list_remove(sg->grps, prv); serv_got_chat_left(gc, id); silc_hash_table_list(conn->local_entry->channels, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { if (SILC_PTR_TO_32(chu->channel->context) == id ) { silc_hash_table_list_reset(&htl); silc_client_command_call(client, conn, NULL, "LEAVE", chu->channel->channel_name, NULL); serv_got_chat_left(gc, id); /* Leave from private groups on this channel as well */ for (l = sg->grps; l; l = l->next) if (((SilcPurplePrvgrp)l->data)->chid == id) { silc_client_del_channel_private_key(client, conn, serv_got_chat_left(gc, prv->id); sg->grps = g_list_remove(sg->grps, prv); int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags msgflags) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcChannelEntry channel = NULL; SilcChannelPrivateKey key = NULL; gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE); flags = SILC_MESSAGE_FLAG_UTF8; tmp = msg2 = purple_unescape_html(msg); if (!g_ascii_strncasecmp(msg2, "/me ", 4)) flags |= SILC_MESSAGE_FLAG_ACTION; } else if (strlen(msg) > 1 && msg[0] == '/') { if (!silc_client_command_call(client, conn, msg + 1)) purple_notify_error(gc, _("Call Command"), _("Cannot call command"), flags |= SILC_MESSAGE_FLAG_SIGNED; /* Get the channel private key if we are sending on if (id > SILCPURPLE_PRVGRP) { for (l = sg->grps; l; l = l->next) if (((SilcPurplePrvgrp)l->data)->id == id) channel = silc_client_get_channel(sg->client, sg->conn, silc_hash_table_list(conn->local_entry->channels, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { if (SILC_PTR_TO_32(chu->channel->context) == id ) { silc_hash_table_list_reset(&htl); /* Send channel message */ ret = silc_client_send_channel_message(client, conn, channel, key, flags, (unsigned char *)msg2, serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; /* See if setting topic on private group. Set it if (id > SILCPURPLE_PRVGRP) { for (l = sg->grps; l; l = l->next) if (((SilcPurplePrvgrp)l->data)->id == id) silc_hash_table_list(conn->local_entry->channels, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { if (SILC_PTR_TO_32(chu->channel->context) == id ) { silc_hash_table_list_reset(&htl); silc_client_command_call(client, conn, NULL, "TOPIC", chu->channel->channel_name, topic, NULL); PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; purple_roomlist_unref(sg->roomlist); sg->roomlist_cancelled = FALSE; sg->roomlist = purple_roomlist_new(purple_connection_get_account(gc)); f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "channel", TRUE); fields = g_list_append(fields, f); f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT, _("Users"), "users", FALSE); fields = g_list_append(fields, f); f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Topic"), "topic", FALSE); fields = g_list_append(fields, f); purple_roomlist_set_fields(sg->roomlist, fields); silc_client_command_call(client, conn, "LIST"); purple_roomlist_set_in_progress(sg->roomlist, TRUE); void silcpurple_roomlist_cancel(PurpleRoomlist *list) PurpleConnection *gc = purple_account_get_connection(list->account); purple_roomlist_set_in_progress(list, FALSE); if (sg->roomlist == list) { purple_roomlist_unref(sg->roomlist); sg->roomlist_cancelled = TRUE;