diff -r aafdd7bed36d -r e7069bf1de1a libpurple/protocols/silc/ops.c --- a/libpurple/protocols/silc/ops.c Sat Jun 09 16:39:00 2007 +0000 +++ b/libpurple/protocols/silc/ops.c Sat Jun 09 17:31:28 2007 +0000 @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2004 Pekka Riikonen + Copyright (C) 2004 - 2007 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 @@ -17,7 +17,7 @@ */ -#include "silcincludes.h" +#include "silc.h" #include "silcclient.h" #include "silcpurple.h" #include "imgstore.h" @@ -26,14 +26,18 @@ static void silc_channel_message(SilcClient client, SilcClientConnection conn, SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, SilcChannelPrivateKey key, - SilcMessageFlags flags, const unsigned char *message, + SilcMessagePayload payload, + SilcChannelPrivateKey key, SilcMessageFlags flags, + const unsigned char *message, SilcUInt32 message_len); static void silc_private_message(SilcClient client, SilcClientConnection conn, SilcClientEntry sender, SilcMessagePayload payload, SilcMessageFlags flags, const unsigned char *message, SilcUInt32 message_len); +static void +silc_ask_passphrase(SilcClient client, SilcClientConnection conn, + SilcAskPassphrase completion, void *context); /* Message sent to the application by library. `conn' associates the message to a specific connection. `conn', however, may be NULL. @@ -41,23 +45,32 @@ The application can for example filter the message according the type. */ -static void -silc_say(SilcClient client, SilcClientConnection conn, - SilcClientMessageType type, char *msg, ...) +void silc_say(SilcClient client, SilcClientConnection conn, + SilcClientMessageType type, char *msg, ...) { - /* Nothing */ + if (type == SILC_CLIENT_MESSAGE_ERROR) { + char tmp[256]; + va_list va; + + va_start(va, msg); + silc_vsnprintf(tmp, sizeof(tmp), msg, va); + purple_notify_error(NULL, _("Error"), _("Error occurred"), tmp); + + va_end(va); + return; + } } #ifdef HAVE_SILCMIME_H /* Processes incoming MIME message. Can be private message or channel - message. */ + message. Returns TRUE if the message `mime' was displayed. */ -static void +static SilcBool silcpurple_mime_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, SilcChannelPrivateKey key, - SilcMessageFlags flags, SilcMime mime, - gboolean recursive) + SilcClientEntry sender, SilcChannelEntry channel, + SilcMessagePayload payload, SilcChannelPrivateKey key, + SilcMessageFlags flags, SilcMime mime, + gboolean recursive) { PurpleConnection *gc = client->application; SilcPurple sg = gc->proto_data; @@ -66,9 +79,10 @@ SilcUInt32 data_len; PurpleMessageFlags cflags = 0; PurpleConversation *convo = NULL; + SilcBool ret = FALSE; if (!mime) - return; + return FALSE; /* Check for fragmented MIME message */ if (silc_mime_is_partial(mime)) { @@ -79,12 +93,12 @@ mime = silc_mime_assemble(sg->mimeass, mime); if (!mime) /* More fragments to come */ - return; + return FALSE; /* Process the complete message */ - silcpurple_mime_message(client, conn, sender, channel, - payload, key, flags, mime, FALSE); - return; + return silcpurple_mime_message(client, conn, sender, channel, + payload, key, flags, mime, + FALSE); } /* Check for multipart message */ @@ -92,17 +106,33 @@ SilcMime p; const char *mtype; SilcDList parts = silc_mime_get_multiparts(mime, &mtype); + SilcBool ret; - /* Only "mixed" type supported */ - if (strcmp(mtype, "mixed")) - goto out; + if (!strcmp(mtype, "mixed")) { + /* Contains multiple messages */ + silc_dlist_start(parts); + while ((p = silc_dlist_get(parts)) != SILC_LIST_END) { + /* Recursively process parts */ + ret = silcpurple_mime_message(client, conn, sender, channel, + payload, key, flags, p, TRUE); + } + } - silc_dlist_start(parts); - while ((p = silc_dlist_get(parts)) != SILC_LIST_END) { - /* Recursively process parts */ - silcpurple_mime_message(client, conn, sender, channel, - payload, key, flags, p, TRUE); + if (!strcmp(mtype, "alternative")) { + /* Same message in alternative formats. Kopete sends + these. Go in order from last to first. */ + silc_dlist_end(parts); + while ((p = silc_dlist_get(parts)) != SILC_LIST_END) { + /* Go through the alternatives and display the first + one we support. */ + if (silcpurple_mime_message(client, conn, sender, channel, + payload, key, flags, p, TRUE)) { + ret = TRUE; + break; + } + } } + goto out; } @@ -124,13 +154,14 @@ if (channel) silc_channel_message(client, conn, sender, channel, - payload, key, + payload, key, SILC_MESSAGE_FLAG_UTF8, data, data_len); else silc_private_message(client, conn, sender, payload, SILC_MESSAGE_FLAG_UTF8, data, data_len); + ret = TRUE; goto out; } @@ -157,7 +188,7 @@ } if (channel && !convo) convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); + channel->channel_name, sg->account); if (channel && !convo) goto out; @@ -165,11 +196,11 @@ if (imgid) { cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV; g_snprintf(tmp, sizeof(tmp), "", imgid); - + if (channel) serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)), sender->nickname ? - sender->nickname : + sender->nickname : "", cflags, tmp, time(NULL)); else @@ -179,6 +210,7 @@ purple_imgstore_unref_by_id(imgid); cflags = 0; + ret = TRUE; } goto out; } @@ -191,13 +223,15 @@ payload, flags, data, data_len); else silcpurple_wb_receive(client, conn, sender, payload, - flags, data, data_len); + flags, data, data_len); + ret = TRUE; goto out; } out: if (!recursive) silc_mime_free(mime); + return ret; } #endif /* HAVE_SILCMIME_H */ @@ -210,8 +244,9 @@ static void silc_channel_message(SilcClient client, SilcClientConnection conn, SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, SilcChannelPrivateKey key, - SilcMessageFlags flags, const unsigned char *message, + SilcMessagePayload payload, + SilcChannelPrivateKey key, SilcMessageFlags flags, + const unsigned char *message, SilcUInt32 message_len) { PurpleConnection *gc = client->application; @@ -236,7 +271,7 @@ } if (!convo) convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); + channel->channel_name, sg->account); if (!convo) return; @@ -249,9 +284,9 @@ /* Process MIME message */ #ifdef HAVE_SILCMIME_H SilcMime mime; - mime = silc_mime_decode(message, message_len); + mime = silc_mime_decode(NULL, message, message_len); silcpurple_mime_message(client, conn, sender, channel, payload, - key, flags, mime, FALSE); + key, flags, mime, FALSE); #else char type[128], enc[128]; unsigned char *data; @@ -283,9 +318,7 @@ tmp = g_markup_escape_text(msg, -1); /* Send to Purple */ serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)), - sender->nickname ? - sender->nickname : "", 0, - tmp, time(NULL)); + sender->nickname, 0, tmp, time(NULL)); g_free(tmp); g_free(msg); return; @@ -293,9 +326,7 @@ if (flags & SILC_MESSAGE_FLAG_NOTICE) { msg = g_strdup_printf("(notice) %s %s", - sender->nickname ? - sender->nickname : "", - (const char *)message); + sender->nickname, (const char *)message); if (!msg) return; @@ -310,9 +341,7 @@ tmp = g_markup_escape_text((const char *)message, -1); /* Send to Purple */ serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)), - sender->nickname ? - sender->nickname : "", 0, - tmp, time(NULL)); + sender->nickname, 0, tmp, time(NULL)); g_free(tmp); } } @@ -341,7 +370,7 @@ if (sender->nickname) /* XXX - Should this be PURPLE_CONV_TYPE_IM? */ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, - sender->nickname, sg->account); + sender->nickname, sg->account); if (flags & SILC_MESSAGE_FLAG_SIGNED && purple_account_get_bool(sg->account, "sign-verify", FALSE)) { @@ -352,7 +381,7 @@ #ifdef HAVE_SILCMIME_H /* Process MIME message */ SilcMime mime; - mime = silc_mime_decode(message, message_len); + mime = silc_mime_decode(NULL, message, message_len); silcpurple_mime_message(client, conn, sender, NULL, payload, NULL, flags, mime, FALSE); #else @@ -364,7 +393,7 @@ memset(enc, 0, sizeof(enc)); if (!silc_mime_parse(message, message_len, NULL, 0, - type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data, + type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data, &data_len)) return; @@ -383,11 +412,9 @@ if (!msg) return; + /* Send to Purple */ tmp = g_markup_escape_text(msg, -1); - /* Send to Purple */ - serv_got_im(gc, sender->nickname ? - sender->nickname : "", - tmp, 0, time(NULL)); + serv_got_im(gc, sender->nickname, tmp, 0, time(NULL)); g_free(msg); g_free(tmp); return; @@ -395,15 +422,13 @@ if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) { msg = g_strdup_printf("(notice) %s %s", - sender->nickname ? - sender->nickname : "", - (const char *)message); + sender->nickname, (const char *)message); if (!msg) return; /* Send to Purple */ purple_conversation_write(convo, NULL, (const char *)msg, - PURPLE_MESSAGE_SYSTEM, time(NULL)); + PURPLE_MESSAGE_SYSTEM, time(NULL)); g_free(msg); return; } @@ -411,9 +436,7 @@ if (flags & SILC_MESSAGE_FLAG_UTF8) { tmp = g_markup_escape_text((const char *)message, -1); /* Send to Purple */ - serv_got_im(gc, sender->nickname ? - sender->nickname : "", - tmp, 0, time(NULL)); + serv_got_im(gc, sender->nickname, tmp, 0, time(NULL)); g_free(tmp); } } @@ -447,6 +470,7 @@ char buf[512], buf2[512], *tmp, *name; SilcNotifyType notify; PurpleBuddy *b; + SilcDList list; int i; va_start(va, type); @@ -460,7 +484,7 @@ case SILC_NOTIFY_TYPE_INVITE: { GHashTable *components; - va_arg(va, SilcChannelEntry); + (void)va_arg(va, SilcChannelEntry); name = va_arg(va, char *); client_entry = va_arg(va, SilcClientEntry); @@ -479,7 +503,7 @@ break; convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); + channel->channel_name, sg->account); if (!convo) break; @@ -487,7 +511,7 @@ g_snprintf(buf, sizeof(buf), "%s@%s", client_entry->username, client_entry->hostname); purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo), - g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE); + g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE); break; @@ -496,13 +520,13 @@ channel = va_arg(va, SilcChannelEntry); convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); + channel->channel_name, sg->account); if (!convo) break; /* Remove user from channel */ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, NULL); + client_entry->nickname, NULL); break; @@ -510,19 +534,16 @@ client_entry = va_arg(va, SilcClientEntry); tmp = va_arg(va, char *); - if (!client_entry->nickname) - break; - /* Remove from all channels */ silc_hash_table_list(client_entry->channels, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); + chu->channel->channel_name, sg->account); if (!convo) continue; purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, - tmp); + client_entry->nickname, + tmp); } silc_hash_table_list_reset(&htl); @@ -537,7 +558,7 @@ channel = va_arg(va, SilcChannelEntry); convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); + channel->channel_name, sg->account); if (!convo) break; @@ -586,22 +607,22 @@ } case SILC_NOTIFY_TYPE_NICK_CHANGE: client_entry = va_arg(va, SilcClientEntry); - client_entry2 = va_arg(va, SilcClientEntry); + tmp = va_arg(va, char *); /* Old nick */ + name = va_arg(va, char *); /* New nick */ - if (!strcmp(client_entry->nickname, client_entry2->nickname)) + if (!strcmp(tmp, name)) break; /* Change nick on all channels */ - silc_hash_table_list(client_entry2->channels, &htl); + silc_hash_table_list(client_entry->channels, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); + chu->channel->channel_name, sg->account); if (!convo) continue; if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname)) purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, - client_entry2->nickname); + tmp, name); } silc_hash_table_list_reset(&htl); @@ -615,11 +636,11 @@ (void)va_arg(va, char *); (void)va_arg(va, char *); (void)va_arg(va, SilcPublicKey); - (void)va_arg(va, SilcBuffer); + (void)va_arg(va, SilcDList); channel = va_arg(va, SilcChannelEntry); convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); + channel->channel_name, sg->account); if (!convo) break; @@ -643,7 +664,7 @@ channel->channel_name); } purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); + buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); break; case SILC_NOTIFY_TYPE_CUMODE_CHANGE: @@ -656,7 +677,7 @@ channel = va_arg(va, SilcChannelEntry); convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); + channel->channel_name, sg->account); if (!convo) break; @@ -672,19 +693,19 @@ if (mode) { silcpurple_get_chumode_string(mode, buf2, sizeof(buf2)); g_snprintf(buf, sizeof(buf), - _("%s set %s's modes to: %s"), name, - client_entry2->nickname, buf2); + _("%s set %s's modes to: %s"), name, + client_entry2->nickname, buf2); if (mode & SILC_CHANNEL_UMODE_CHANFO) flags |= PURPLE_CBFLAGS_FOUNDER; if (mode & SILC_CHANNEL_UMODE_CHANOP) flags |= PURPLE_CBFLAGS_OP; } else { g_snprintf(buf, sizeof(buf), - _("%s removed all %s's modes"), name, - client_entry2->nickname); + _("%s removed all %s's modes"), name, + client_entry2->nickname); } purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); + buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags); break; } @@ -702,7 +723,7 @@ channel = va_arg(va, SilcChannelEntry); convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); + channel->channel_name, sg->account); if (!convo) break; @@ -713,15 +734,15 @@ channel->channel_name, client_entry2->nickname, tmp ? tmp : ""); purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); + buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo))); } else { /* Remove user from channel */ g_snprintf(buf, sizeof(buf), _("Kicked by %s (%s)"), client_entry2->nickname, tmp ? tmp : ""); purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, - buf); + client_entry->nickname, + buf); } break; @@ -732,9 +753,6 @@ idtype = va_arg(va, int); entry = va_arg(va, SilcClientEntry); - if (!client_entry->nickname) - break; - if (client_entry == conn->local_entry) { if (idtype == SILC_ID_CLIENT) { client_entry2 = (SilcClientEntry)entry; @@ -761,7 +779,7 @@ if (!convo) continue; purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); + buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo))); } silc_hash_table_list_reset(&htl); @@ -792,7 +810,7 @@ if (!convo) continue; purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, tmp); + client_entry->nickname, tmp); } silc_hash_table_list_reset(&htl); } @@ -803,33 +821,23 @@ break; case SILC_NOTIFY_TYPE_SERVER_SIGNOFF: - { - int i; - SilcClientEntry *clients; - SilcUInt32 clients_count; - - (void)va_arg(va, void *); - clients = va_arg(va, SilcClientEntry *); - clients_count = va_arg(va, SilcUInt32); - - for (i = 0; i < clients_count; i++) { - if (!clients[i]->nickname) - break; + (void)va_arg(va, void *); + list = va_arg(va, SilcDList); - /* Remove from all channels */ - silc_hash_table_list(clients[i]->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = - purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - clients[i]->nickname, - _("Server signoff")); - } - silc_hash_table_list_reset(&htl); + silc_dlist_start(list); + while ((client_entry = silc_dlist_get(list))) { + /* Remove from all channels */ + silc_hash_table_list(client_entry->channels, &htl); + while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { + convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, + chu->channel->channel_name, sg->account); + if (!convo) + continue; + purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), + client_entry->nickname, + _("Server signoff")); } + silc_hash_table_list_reset(&htl); } break; @@ -837,8 +845,8 @@ { SilcStatus error = va_arg(va, int); purple_notify_error(gc, "Error Notify", - silc_get_status_message(error), - NULL); + silc_get_status_message(error), + NULL); } break; @@ -909,8 +917,8 @@ } silc_free(b->proto_data); - b->proto_data = silc_memdup(client_entry->id, - sizeof(*client_entry->id)); + b->proto_data = silc_memdup(&client_entry->id, + sizeof(client_entry->id)); if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) { break; } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) { @@ -955,19 +963,20 @@ } -/* Command handler. This function is called always in the command function. - If error occurs it will be called as well. `conn' is the associated - client connection. `cmd_context' is the command context that was - originally sent to the command. `success' is FALSE if error occurred - during command. `command' is the command being processed. It must be - noted that this is not reply from server. This is merely called just - after application has called the command. Just to tell application - that the command really was processed. */ +/* Command handler. This function is called always after application has + called a command. It will be called to indicate that the command + was processed. It will also be called if error occurs while processing + the command. The `success' indicates whether the command was sent + or if error occurred. The `status' indicates the actual error. + The `argc' and `argv' are the command line arguments sent to the + command by application. Note that, this is not reply to the command + from server, this is merely and indication to application that the + command was processed. */ static void silc_command(SilcClient client, SilcClientConnection conn, - SilcClientCommandContext cmd_context, bool success, - SilcCommand command, SilcStatus status) + SilcBool success, SilcCommand command, SilcStatus status, + SilcUInt32 argc, unsigned char **argv) { PurpleConnection *gc = client->application; SilcPurple sg = gc->proto_data; @@ -975,8 +984,7 @@ switch (command) { case SILC_COMMAND_CMODE: - if (cmd_context->argc == 3 && - !strcmp((char *)cmd_context->argv[2], "+C")) + if (argc == 3 && !strcmp((char *)argv[2], "+C")) sg->chpk = TRUE; else sg->chpk = FALSE; @@ -1090,53 +1098,96 @@ } #endif -/* Command reply handler. This function is called always in the command reply - function. If error occurs it will be called as well. Normal scenario - is that it will be called after the received command data has been parsed - and processed. The function is used to pass the received command data to - the application. - `conn' is the associated client connection. `cmd_payload' is the command - payload data received from server and it can be ignored. It is provided - if the application would like to re-parse the received command data, - however, it must be noted that the data is parsed already by the library - thus the payload can be ignored. `success' is FALSE if error occurred. - In this case arguments are not sent to the application. The `status' is - the command reply status server returned. The `command' is the command - reply being processed. The function has variable argument list and each - command defines the number and type of arguments it passes to the - application (on error they are not sent). */ +/* Command reply handler. Delivers a reply to command that was sent + earlier. The `conn' is the associated client connection. The `command' + indicates the command reply type. If the `status' other than + SILC_STATUS_OK an error occurred. In this case the `error' will indicate + the error. It is possible to receive list of command replies and list + of errors. In this case the `status' will indicate it is an list entry + (the `status' is SILC_STATUS_LIST_START, SILC_STATUS_LIST_ITEM and/or + SILC_STATUS_LIST_END). + + The arguments received in `ap' are command specific. See a separate + documentation in the Toolkit Reference Manual for the command reply + arguments. */ static void silc_command_reply(SilcClient client, SilcClientConnection conn, - SilcCommandPayload cmd_payload, bool success, - SilcCommand command, SilcStatus status, ...) + SilcCommand command, SilcStatus status, + SilcStatus error, va_list ap) { PurpleConnection *gc = client->application; SilcPurple sg = gc->proto_data; PurpleConversation *convo; - va_list vp; - - va_start(vp, status); switch (command) { case SILC_COMMAND_JOIN: { - SilcChannelEntry channel_entry; + SilcChannelEntry channel; + PurpleConversation *convo; + SilcHashTableList *user_list; + SilcChannelUser chu; + GList *users = NULL, *flags = NULL; + char tmp[256], *topic; - if (!success) { + if (status != SILC_STATUS_OK) { purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"), - silc_get_status_message(status)); + silc_get_status_message(error)); return; } - (void)va_arg(vp, char *); - channel_entry = va_arg(vp, SilcChannelEntry); + (void)va_arg(ap, char *); + channel = va_arg(ap, SilcChannelEntry); + (void)va_arg(ap, SilcUInt32); + user_list = va_arg(ap, SilcHashTableList *); + topic = va_arg(ap, char *); + + /* 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); + if (!convo) + return; + + /* Add all users to channel */ + while (silc_hash_table_get(user_list, NULL, (void *)&chu)) { + PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE; + 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) + f |= PURPLE_CBFLAGS_OP; + users = g_list_append(users, g_strdup(chu->client->nickname)); + flags = g_list_append(flags, GINT_TO_POINTER(f)); - /* Resolve users on channel */ - silc_client_get_clients_by_channel(client, conn, channel_entry, - silcpurple_chat_join_done, - channel_entry); + if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) { + if (chu->client == conn->local_entry) + g_snprintf(tmp, sizeof(tmp), + _("You are channel founder on %s"), + channel->channel_name); + else + g_snprintf(tmp, sizeof(tmp), + _("Channel founder on %s is %s"), + channel->channel_name, chu->client->nickname); + + purple_conversation_write(convo, NULL, tmp, + PURPLE_MESSAGE_SYSTEM, time(NULL)); + } + } + + purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE); + g_list_free(users); + g_list_free(flags); + + /* Set topic */ + if (topic) + purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, topic); + + /* Set nick */ + purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname); } break; @@ -1148,31 +1199,29 @@ case SILC_COMMAND_WHOIS: { - SilcUInt32 idle, mode; - SilcBuffer channels, user_modes; + SilcUInt32 idle, *user_modes; + SilcDList channels; SilcClientEntry client_entry; char tmp[1024], *tmp2; char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr; PurpleNotifyUserInfo *user_info; - if (!success) { + if (status != SILC_STATUS_OK) { purple_notify_error(gc, _("User Information"), _("Cannot get user information"), - silc_get_status_message(status)); + silc_get_status_message(error)); break; } - client_entry = va_arg(vp, SilcClientEntry); - if (!client_entry->nickname) - break; - (void)va_arg(vp, char *); - (void)va_arg(vp, char *); - (void)va_arg(vp, char *); - channels = va_arg(vp, SilcBuffer); - mode = va_arg(vp, SilcUInt32); - idle = va_arg(vp, SilcUInt32); - (void)va_arg(vp, unsigned char *); - user_modes = va_arg(vp, SilcBuffer); + client_entry = va_arg(ap, SilcClientEntry); + (void)va_arg(ap, char *); + (void)va_arg(ap, char *); + (void)va_arg(ap, char *); + channels = va_arg(ap, SilcDList); + (void)va_arg(ap, SilcUInt32); + idle = va_arg(ap, SilcUInt32); + (void)va_arg(ap, unsigned char *); + user_modes = va_arg(ap, SilcUInt32 *); user_info = purple_notify_user_info_new(); tmp2 = g_markup_escape_text(client_entry->nickname, -1); @@ -1183,22 +1232,20 @@ purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2); g_free(tmp2); } - if (client_entry->username) { - tmp2 = g_markup_escape_text(client_entry->username, -1); - if (client_entry->hostname) { - gchar *tmp3; - tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname); - purple_notify_user_info_add_pair(user_info, _("Username"), tmp3); - g_free(tmp3); - } else - purple_notify_user_info_add_pair(user_info, _("Username"), tmp2); - g_free(tmp2); - } + tmp2 = g_markup_escape_text(client_entry->username, -1); + if (*client_entry->hostname) { + gchar *tmp3; + tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname); + purple_notify_user_info_add_pair(user_info, _("Username"), tmp3); + g_free(tmp3); + } else + purple_notify_user_info_add_pair(user_info, _("Username"), tmp2); + g_free(tmp2); if (client_entry->mode) { memset(tmp, 0, sizeof(tmp)); silcpurple_get_umode_string(client_entry->mode, - tmp, sizeof(tmp) - strlen(tmp)); + tmp, sizeof(tmp) - strlen(tmp)); purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp); } @@ -1240,39 +1287,28 @@ g_free(geostr); } - if (client_entry->server) + if (*client_entry->server) purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server); if (channels && user_modes) { - SilcUInt32 *umodes; - SilcDList list = - silc_channel_payload_parse_list(channels->data, - channels->len); - if (list && silc_get_mode_list(user_modes, - silc_dlist_count(list), - &umodes)) { - SilcChannelPayload entry; - int i = 0; + SilcChannelPayload entry; + int i = 0; - memset(tmp, 0, sizeof(tmp)); - silc_dlist_start(list); - while ((entry = silc_dlist_get(list)) - != SILC_LIST_END) { - SilcUInt32 name_len; - char *m = silc_client_chumode_char(umodes[i++]); - char *name = (char *)silc_channel_get_name(entry, &name_len); - if (m) - silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m)); - silc_strncat(tmp, sizeof(tmp) - 1, name, name_len); - silc_strncat(tmp, sizeof(tmp) - 1, " ", 1); - silc_free(m); - - } - tmp2 = g_markup_escape_text(tmp, -1); - purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2); - g_free(tmp2); - silc_free(umodes); + memset(tmp, 0, sizeof(tmp)); + silc_dlist_start(channels); + while ((entry = silc_dlist_get(channels))) { + SilcUInt32 name_len; + char *m = silc_client_chumode_char(user_modes[i++]); + char *name = (char *)silc_channel_get_name(entry, &name_len); + if (m) + silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m)); + silc_strncat(tmp, sizeof(tmp) - 1, name, name_len); + silc_strncat(tmp, sizeof(tmp) - 1, " ", 1); + silc_free(m); } + tmp2 = g_markup_escape_text(tmp, -1); + purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2); + g_free(tmp2); } if (client_entry->public_key) { @@ -1297,7 +1333,7 @@ _("OK"), G_CALLBACK(silcpurple_whois_more), _("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL); else -#endif +#endif /* 0 */ purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL); purple_notify_user_info_destroy(user_info); } @@ -1309,17 +1345,17 @@ char *nickname, *realname, *username, *tmp; PurpleNotifyUserInfo *user_info; - if (!success) { + if (status != SILC_STATUS_OK) { purple_notify_error(gc, _("User Information"), _("Cannot get user information"), - silc_get_status_message(status)); + silc_get_status_message(error)); break; } - client_entry = va_arg(vp, SilcClientEntry); - nickname = va_arg(vp, char *); - username = va_arg(vp, char *); - realname = va_arg(vp, char *); + client_entry = va_arg(ap, SilcClientEntry); + nickname = va_arg(ap, char *); + username = va_arg(ap, char *); + realname = va_arg(ap, char *); if (!nickname) break; @@ -1334,7 +1370,7 @@ } if (username) { tmp = g_markup_escape_text(username, -1); - if (client_entry && client_entry->hostname) { + if (client_entry && *client_entry->hostname) { gchar *tmp3; tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname); purple_notify_user_info_add_pair(user_info, _("Username"), tmp3); @@ -1343,7 +1379,7 @@ purple_notify_user_info_add_pair(user_info, _("Username"), tmp); g_free(tmp); } - if (client_entry && client_entry->server) + if (client_entry && *client_entry->server) purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server); @@ -1367,10 +1403,23 @@ break; case SILC_COMMAND_DETACH: - if (!success) { - purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"), - silc_get_status_message(status)); - return; + { + const char *file; + SilcBuffer detach_data; + + if (status != SILC_STATUS_OK) { + purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"), + silc_get_status_message(error)); + return; + } + + detach_data = va_arg(ap, SilcBuffer); + + /* Save the detachment data to file. */ + file = silcpurple_session_file(purple_account_get_username(sg->account)); + g_unlink(file); + silc_file_writefile(file, (const char *)silc_buffer_data(detach_data), + silc_buffer_len(detach_data)); } break; @@ -1378,19 +1427,19 @@ { SilcChannelEntry channel; - if (!success) { + if (status != SILC_STATUS_OK) { purple_notify_error(gc, _("Topic"), _("Cannot set topic"), - silc_get_status_message(status)); + silc_get_status_message(error)); return; } - channel = va_arg(vp, SilcChannelEntry); + channel = va_arg(ap, SilcChannelEntry); convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); + channel->channel_name, sg->account); if (!convo) { purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n", - channel->channel_name); + channel->channel_name); break; } @@ -1402,39 +1451,37 @@ case SILC_COMMAND_NICK: { - /* I don't think we should need to do this because the server should - * be sending a SILC_NOTIFY_TYPE_NICK_CHANGE when we change our own - * nick, but it isn't, so we deal with it here instead. Stu. */ SilcClientEntry local_entry; SilcHashTableList htl; SilcChannelUser chu; - const char *oldnick; + const char *oldnick, *newnick; - if (!success) { + if (status != SILC_STATUS_OK) { purple_notify_error(gc, _("Nick"), _("Failed to change nickname"), - silc_get_status_message(status)); + silc_get_status_message(error)); return; } - local_entry = va_arg(vp, SilcClientEntry); + local_entry = va_arg(ap, SilcClientEntry); + newnick = va_arg(ap, char *); /* Change nick on all channels */ silc_hash_table_list(local_entry->channels, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); + chu->channel->channel_name, sg->account); if (!convo) continue; oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo)); - if (strcmp(oldnick, purple_normalize(purple_conversation_get_account(convo), local_entry->nickname))) { + if (strcmp(oldnick, purple_normalize(purple_conversation_get_account(convo), newnick))) { purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo), - oldnick, local_entry->nickname); - purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), local_entry->nickname); + oldnick, newnick); + purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), newnick); } } silc_hash_table_list_reset(&htl); - purple_connection_set_display_name(gc, local_entry->nickname); + purple_connection_set_display_name(gc, newnick); } break; @@ -1447,34 +1494,34 @@ if (sg->roomlist_canceled) break; - if (!success) { + if (error != SILC_STATUS_OK) { purple_notify_error(gc, _("Error"), _("Error retrieving room list"), - silc_get_status_message(status)); + silc_get_status_message(error)); purple_roomlist_set_in_progress(sg->roomlist, FALSE); purple_roomlist_unref(sg->roomlist); sg->roomlist = NULL; return; } - (void)va_arg(vp, SilcChannelEntry); - name = va_arg(vp, char *); + (void)va_arg(ap, SilcChannelEntry); + name = va_arg(ap, char *); if (!name) { purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"), - silc_get_status_message(status)); + _("Network is empty")); purple_roomlist_set_in_progress(sg->roomlist, FALSE); purple_roomlist_unref(sg->roomlist); sg->roomlist = NULL; return; } - topic = va_arg(vp, char *); - usercount = va_arg(vp, int); + topic = va_arg(ap, char *); + usercount = va_arg(ap, int); room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL); purple_roomlist_room_add_field(sg->roomlist, room, name); purple_roomlist_room_add_field(sg->roomlist, room, - SILC_32_TO_PTR(usercount)); + SILC_32_TO_PTR(usercount)); purple_roomlist_room_add_field(sg->roomlist, room, - topic ? topic : ""); + topic ? topic : ""); purple_roomlist_room_add(sg->roomlist, room); if (status == SILC_STATUS_LIST_END || @@ -1490,21 +1537,21 @@ { SilcPublicKey public_key; - if (!success) { + if (status != SILC_STATUS_OK) { purple_notify_error(gc, _("Get Public Key"), - _("Cannot fetch the public key"), - silc_get_status_message(status)); + _("Cannot fetch the public key"), + silc_get_status_message(error)); return; } - (void)va_arg(vp, SilcUInt32); - (void)va_arg(vp, void *); - public_key = va_arg(vp, SilcPublicKey); + (void)va_arg(ap, SilcUInt32); + (void)va_arg(ap, void *); + public_key = va_arg(ap, SilcPublicKey); if (!public_key) purple_notify_error(gc, _("Get Public Key"), - _("Cannot fetch the public key"), - _("No public key was received")); + _("Cannot fetch the public key"), + _("No public key was received")); } break; @@ -1515,16 +1562,16 @@ char *server_info; char tmp[256]; - if (!success) { + if (status != SILC_STATUS_OK) { purple_notify_error(gc, _("Server Information"), - _("Cannot get server information"), - silc_get_status_message(status)); + _("Cannot get server information"), + silc_get_status_message(error)); return; } - (void)va_arg(vp, SilcServerEntry); - server_name = va_arg(vp, char *); - server_info = va_arg(vp, char *); + (void)va_arg(ap, SilcServerEntry); + server_name = va_arg(ap, char *); + server_info = va_arg(ap, char *); if (server_name && server_info) { g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s", @@ -1536,94 +1583,73 @@ case SILC_COMMAND_STATS: { - SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops, - my_router_ops, cell_clients, cell_channels, cell_servers, - clients, channels, servers, routers, server_ops, router_ops; - SilcUInt32 buffer_length; - SilcBufferStruct buf; - - unsigned char *server_stats; + SilcClientStats *stats; char *msg; - if (!success) { + if (status != SILC_STATUS_OK) { purple_notify_error(gc, _("Server Statistics"), - _("Cannot get server statistics"), - silc_get_status_message(status)); + _("Cannot get server statistics"), + silc_get_status_message(error)); return; } - server_stats = va_arg(vp, unsigned char *); - buffer_length = va_arg(vp, SilcUInt32); - if (!server_stats || !buffer_length) { - purple_notify_error(gc, _("Server Statistics"), - _("No server statistics available"), NULL); - break; - } - silc_buffer_set(&buf, server_stats, buffer_length); - silc_buffer_unformat(&buf, - SILC_STR_UI_INT(&starttime), - SILC_STR_UI_INT(&uptime), - SILC_STR_UI_INT(&my_clients), - SILC_STR_UI_INT(&my_channels), - SILC_STR_UI_INT(&my_server_ops), - SILC_STR_UI_INT(&my_router_ops), - SILC_STR_UI_INT(&cell_clients), - SILC_STR_UI_INT(&cell_channels), - SILC_STR_UI_INT(&cell_servers), - SILC_STR_UI_INT(&clients), - SILC_STR_UI_INT(&channels), - SILC_STR_UI_INT(&servers), - SILC_STR_UI_INT(&routers), - SILC_STR_UI_INT(&server_ops), - SILC_STR_UI_INT(&router_ops), - SILC_STR_END); + stats = va_arg(ap, SilcClientStats *); msg = g_strdup_printf(_("Local server start time: %s\n" - "Local server uptime: %s\n" - "Local server clients: %d\n" - "Local server channels: %d\n" - "Local server operators: %d\n" - "Local router operators: %d\n" - "Local cell clients: %d\n" - "Local cell channels: %d\n" - "Local cell servers: %d\n" - "Total clients: %d\n" - "Total channels: %d\n" - "Total servers: %d\n" - "Total routers: %d\n" - "Total server operators: %d\n" - "Total router operators: %d\n"), - silc_get_time(starttime), - purple_str_seconds_to_string((int)uptime), - (int)my_clients, (int)my_channels, (int)my_server_ops, (int)my_router_ops, - (int)cell_clients, (int)cell_channels, (int)cell_servers, - (int)clients, (int)channels, (int)servers, (int)routers, - (int)server_ops, (int)router_ops); + "Local server uptime: %s\n" + "Local server clients: %d\n" + "Local server channels: %d\n" + "Local server operators: %d\n" + "Local router operators: %d\n" + "Local cell clients: %d\n" + "Local cell channels: %d\n" + "Local cell servers: %d\n" + "Total clients: %d\n" + "Total channels: %d\n" + "Total servers: %d\n" + "Total routers: %d\n" + "Total server operators: %d\n" + "Total router operators: %d\n"), + silc_time_string(stats->starttime), + purple_str_seconds_to_string((int)stats->uptime), + (int)stats->my_clients, + (int)stats->my_channels, + (int)stats->my_server_ops, + (int)stats->my_router_ops, + (int)stats->cell_clients, + (int)stats->cell_channels, + (int)stats->cell_servers, + (int)stats->clients, + (int)stats->channels, + (int)stats->servers, + (int)stats->routers, + (int)stats->server_ops, + (int)stats->router_ops); purple_notify_info(gc, NULL, - _("Network Statistics"), msg); + _("Network Statistics"), msg); g_free(msg); } break; case SILC_COMMAND_PING: { - if (!success) { + if (status != SILC_STATUS_OK) { purple_notify_error(gc, _("Ping"), _("Ping failed"), - silc_get_status_message(status)); + silc_get_status_message(error)); return; } purple_notify_info(gc, _("Ping"), _("Ping reply received from server"), - NULL); + NULL); } break; case SILC_COMMAND_KILL: - if (!success) { + if (status != SILC_STATUS_OK) { purple_notify_error(gc, _("Kill User"), - _("Could not kill user"), - silc_get_status_message(status)); + _("Could not kill user"), + silc_get_status_message(error)); return; } break; @@ -1631,188 +1657,108 @@ case SILC_COMMAND_CMODE: { SilcChannelEntry channel_entry; - SilcBuffer channel_pubkeys; + SilcDList channel_pubkeys, list; + SilcArgumentDecodedList e; - if (!success) + if (status != SILC_STATUS_OK) return; - channel_entry = va_arg(vp, SilcChannelEntry); - (void)va_arg(vp, SilcUInt32); - (void)va_arg(vp, SilcPublicKey); - channel_pubkeys = va_arg(vp, SilcBuffer); + channel_entry = va_arg(ap, SilcChannelEntry); + (void)va_arg(ap, SilcUInt32); + (void)va_arg(ap, SilcPublicKey); + channel_pubkeys = va_arg(ap, SilcDList); + + if (!sg->chpk) + break; + + list = silc_dlist_init(); - if (sg->chpk) - silcpurple_chat_chauth_show(sg, channel_entry, channel_pubkeys); + if (channel_pubkeys) { + silc_dlist_start(channel_pubkeys); + while ((e = silc_dlist_get(channel_pubkeys))) { + if (e->arg_type == 0x00 || + e->arg_type == 0x03) + silc_dlist_add(list, silc_pkcs_public_key_copy(e->argument)); + } + } + silcpurple_chat_chauth_show(sg, channel_entry, list); + } + break; + + case SILC_COMMAND_WATCH: + if (status != SILC_STATUS_OK) { + purple_notify_error(gc, _("WATCH"), _("Cannot watch user"), + silc_get_status_message(error)); + return; } break; default: - if (success) + if (status == SILC_STATUS_OK) purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command); else purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command, - silc_get_status_message(status)); + silc_get_status_message(error)); break; } - - va_end(vp); } - -/* Called to indicate that connection was either successfully established - or connecting failed. This is also the first time application receives - the SilcClientConnection object which it should save somewhere. - If the `success' is FALSE the application must always call the function - silc_client_close_connection. */ - -static void -silc_connected(SilcClient client, SilcClientConnection conn, - SilcClientConnectionStatus status) -{ - PurpleConnection *gc = client->application; - SilcPurple sg; - gboolean reject_watch, block_invites, block_ims; - - if (gc == NULL) { - silc_client_close_connection(client, conn); - return; - } - sg = gc->proto_data; - - switch (status) { - case SILC_CLIENT_CONN_SUCCESS: - case SILC_CLIENT_CONN_SUCCESS_RESUME: - purple_connection_set_state(gc, PURPLE_CONNECTED); - - /* Send the server our buddy list */ - silcpurple_send_buddylist(gc); - - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - - /* Send any UMODEs configured for account */ - reject_watch = purple_account_get_bool(sg->account, "reject-watch", FALSE); - block_invites = purple_account_get_bool(sg->account, "block-invites", FALSE); - block_ims = purple_account_get_bool(sg->account, "block-ims", FALSE); - if (reject_watch || block_invites || block_ims) { - char m[5]; - g_snprintf(m, sizeof(m), "+%s%s%s", - reject_watch ? "w" : "", - block_invites ? "I" : "", - block_ims ? "P" : ""); - silc_client_command_call(sg->client, sg->conn, NULL, - "UMODE", m, NULL); - } +/* Generic command reply callback for silc_client_command_send. Simply + calls the default command_reply client operation callback */ - return; - break; - case SILC_CLIENT_CONN_ERROR: - purple_connection_error(gc, _("Error during connecting to SILC Server")); - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - break; - - case SILC_CLIENT_CONN_ERROR_KE: - purple_connection_error(gc, _("Key Exchange failed")); - break; - - case SILC_CLIENT_CONN_ERROR_AUTH: - purple_connection_error(gc, _("Authentication failed")); - break; - - case SILC_CLIENT_CONN_ERROR_RESUME: - purple_connection_error(gc, - _("Resuming detached session failed. " - "Press Reconnect to create new connection.")); - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - break; - - case SILC_CLIENT_CONN_ERROR_TIMEOUT: - purple_connection_error(gc, _("Connection Timeout")); - break; - } - - /* Error */ - sg->conn = NULL; - silc_client_close_connection(client, conn); -} - - -/* Called to indicate that connection was disconnected to the server. - The `status' may tell the reason of the disconnection, and if the - `message' is non-NULL it may include the disconnection message - received from server. */ - -static void -silc_disconnected(SilcClient client, SilcClientConnection conn, - SilcStatus status, const char *message) +SilcBool silcpurple_command_reply(SilcClient client, SilcClientConnection conn, + SilcCommand command, SilcStatus status, + SilcStatus error, void *context, va_list ap) { - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - - if (sg->resuming && !sg->detaching) - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - - sg->conn = NULL; - - /* Close the connection */ - if (!sg->detaching) - purple_connection_error(gc, _("Disconnected by server")); - else - /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */ - purple_account_disconnect(purple_connection_get_account(gc)); + silc_command_reply(client, conn, command, status, error, ap); + return TRUE; } typedef struct { - SilcGetAuthMeth completion; + union { + SilcAskPassphrase ask_pass; + SilcGetAuthMeth get_auth; + } u; void *context; -} *SilcPurpleGetAuthMethod; - -/* Callback called when we've received the authentication method information - from the server after we've requested it. */ - -static void silc_get_auth_method_callback(SilcClient client, - SilcClientConnection conn, - SilcAuthMethod auth_meth, - void *context) -{ - SilcPurpleGetAuthMethod internal = context; +} *SilcPurpleAskPassphrase; - switch (auth_meth) { - case SILC_AUTH_NONE: - /* No authentication required. */ - (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context); - break; +static void +silc_ask_auth_password_cb(const unsigned char *passphrase, + SilcUInt32 passphrase_len, void *context) +{ + SilcPurpleAskPassphrase internal = context; - case SILC_AUTH_PASSWORD: - /* By returning NULL here the library will ask the passphrase from us - by calling the silc_ask_passphrase. */ - (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context); - break; - - case SILC_AUTH_PUBLIC_KEY: - /* Do not get the authentication data now, the library will generate - it using our default key, if we do not provide it here. */ - (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context); - break; - } - + if (!passphrase || !(*passphrase)) + internal->u.get_auth(SILC_AUTH_NONE, NULL, 0, internal->context); + else + internal->u.get_auth(SILC_AUTH_PASSWORD, + (unsigned char *)passphrase, + passphrase_len, internal->context); silc_free(internal); } /* Find authentication method and authentication data by hostname and - port. The hostname may be IP address as well. When the authentication - method has been resolved the `completion' callback with the found - authentication method and authentication data is called. The `conn' - may be NULL. */ + port. The hostname may be IP address as well. The `auth_method' is + the authentication method the remote connection requires. It is + however possible that remote accepts also some other authentication + method. Application should use the method that may have been + configured for this connection. If none has been configured it should + use the required `auth_method'. If the `auth_method' is + SILC_AUTH_NONE, server does not require any authentication or the + required authentication method is not known. The `completion' + callback must be called to deliver the chosen authentication method + and data. The `conn' may be NULL. */ static void silc_get_auth_method(SilcClient client, SilcClientConnection conn, char *hostname, SilcUInt16 port, + SilcAuthMethod auth_method, SilcGetAuthMeth completion, void *context) { PurpleConnection *gc = client->application; SilcPurple sg = gc->proto_data; - SilcPurpleGetAuthMethod internal; + SilcPurpleAskPassphrase internal; const char *password; /* Progress */ @@ -1821,72 +1767,71 @@ else purple_connection_update_progress(gc, _("Authenticating connection"), 4, 5); - /* Check configuration if we have this connection configured. If we - have then return that data immediately, as it's faster way. */ - if (purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) { - completion(TRUE, SILC_AUTH_PUBLIC_KEY, NULL, 0, context); + /* Check configuration if we have this connection configured. */ + if (auth_method == SILC_AUTH_PUBLIC_KEY && + purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) { + completion(SILC_AUTH_PUBLIC_KEY, NULL, 0, context); return; } - password = purple_connection_get_password(gc); - if (password && *password) { - completion(TRUE, SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context); + if (auth_method == SILC_AUTH_PASSWORD) { + password = purple_connection_get_password(gc); + if (password && *password) { + completion(SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context); + return; + } + + /* Ask password from user */ + internal = silc_calloc(1, sizeof(*internal)); + if (!internal) + return; + internal->u.get_auth = completion; + internal->context = context; + silc_ask_passphrase(client, conn, silc_ask_auth_password_cb, + internal); return; } - /* Resolve the authentication method from server, as we may not know it. */ - internal = silc_calloc(1, sizeof(*internal)); - if (!internal) - return; - internal->completion = completion; - internal->context = context; - silc_client_request_authentication_method(client, conn, - silc_get_auth_method_callback, - internal); + completion(SILC_AUTH_NONE, NULL, 0, context); } -/* Verifies received public key. The `conn_type' indicates which entity - (server, client etc.) has sent the public key. If user decides to trust - the application may save the key as trusted public key for later - use. The `completion' must be called after the public key has been - verified. */ +/* Called to verify received public key. The `conn_type' indicates which + entity (server or client) has sent the public key. If user decides to + trust the key the application may save the key as trusted public key for + later use. The `completion' must be called after the public key has + been verified. */ static void silc_verify_public_key(SilcClient client, SilcClientConnection conn, - SilcSocketType conn_type, unsigned char *pk, - SilcUInt32 pk_len, SilcSKEPKType pk_type, + SilcConnectionType conn_type, + SilcPublicKey public_key, SilcVerifyPublicKey completion, void *context) { PurpleConnection *gc = client->application; SilcPurple sg = gc->proto_data; - if (!sg->conn && (conn_type == SILC_SOCKET_TYPE_SERVER || - conn_type == SILC_SOCKET_TYPE_ROUTER)) { + if (!sg->conn && (conn_type == SILC_CONN_SERVER || + conn_type == SILC_CONN_ROUTER)) { /* Progress */ if (sg->resuming) purple_connection_update_progress(gc, _("Resuming session"), 3, 5); else purple_connection_update_progress(gc, _("Verifying server public key"), - 3, 5); + 3, 5); } /* Verify public key */ - silcpurple_verify_public_key(client, conn, NULL, conn_type, pk, - pk_len, pk_type, completion, context); + silcpurple_verify_public_key(client, conn, NULL, conn_type, + public_key, completion, context); } -typedef struct { - SilcAskPassphrase completion; - void *context; -} *SilcPurpleAskPassphrase; - static void silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase) { if (!passphrase || !(*passphrase)) - internal->completion(NULL, 0, internal->context); + internal->u.ask_pass(NULL, 0, internal->context); else - internal->completion((unsigned char *)passphrase, + internal->u.ask_pass((unsigned char *)passphrase, strlen(passphrase), internal->context); silc_free(internal); } @@ -1905,97 +1850,32 @@ if (!internal) return; - internal->completion = completion; + internal->u.ask_pass = completion; internal->context = context; purple_request_input(gc, _("Passphrase"), NULL, - _("Passphrase required"), NULL, FALSE, TRUE, NULL, - _("OK"), G_CALLBACK(silc_ask_passphrase_cb), - _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb), - purple_connection_get_account(gc), NULL, NULL, internal); + _("Passphrase required"), NULL, FALSE, TRUE, NULL, + _("OK"), G_CALLBACK(silc_ask_passphrase_cb), + _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb), + purple_connection_get_account(gc), NULL, NULL, internal); } -/* Notifies application that failure packet was received. This is called - if there is some protocol active in the client. The `protocol' is the - protocol context. The `failure' is opaque pointer to the failure - indication. Note, that the `failure' is protocol dependant and - application must explicitly cast it to correct type. Usually `failure' - is 32 bit failure type (see protocol specs for all protocol failure - types). */ +/* Called to indicate that incoming key agreement request has been + received. If the application wants to perform key agreement it may + call silc_client_perform_key_agreement to initiate key agreement or + silc_client_send_key_agreement to provide connection point to the + remote client in case the `hostname' is NULL. If key agreement is + not desired this request can be ignored. The `protocol' is either + value 0 for TCP or value 1 for UDP. */ static void -silc_failure(SilcClient client, SilcClientConnection conn, - SilcProtocol protocol, void *failure) +silc_key_agreement(SilcClient client, SilcClientConnection conn, + SilcClientEntry client_entry, + const char *hostname, SilcUInt16 protocol, + SilcUInt16 port) { - PurpleConnection *gc = client->application; - char buf[128]; - - memset(buf, 0, sizeof(buf)); - - if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) { - SilcSKEStatus status = (SilcSKEStatus)SILC_PTR_TO_32(failure); - - if (status == SILC_SKE_STATUS_BAD_VERSION) - g_snprintf(buf, sizeof(buf), - _("Failure: Version mismatch, upgrade your client")); - if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not trust/support your public key")); - if (status == SILC_SKE_STATUS_UNKNOWN_GROUP) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not support proposed KE group")); - if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not support proposed cipher")); - if (status == SILC_SKE_STATUS_UNKNOWN_PKCS) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not support proposed PKCS")); - if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not support proposed hash function")); - if (status == SILC_SKE_STATUS_UNKNOWN_HMAC) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not support proposed HMAC")); - if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE) - g_snprintf(buf, sizeof(buf), _("Failure: Incorrect signature")); - if (status == SILC_SKE_STATUS_INVALID_COOKIE) - g_snprintf(buf, sizeof(buf), _("Failure: Invalid cookie")); - - /* Show the error on the progress bar. A more generic error message - is going to be showed to user after this in the silc_connected. */ - purple_connection_update_progress(gc, buf, 2, 5); - } - - if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) { - SilcUInt32 err = SILC_PTR_TO_32(failure); - - if (err == SILC_AUTH_FAILED) - g_snprintf(buf, sizeof(buf), _("Failure: Authentication failed")); - - /* Show the error on the progress bar. A more generic error message - is going to be showed to user after this in the silc_connected. */ - purple_connection_update_progress(gc, buf, 4, 5); - } -} - -/* Asks whether the user would like to perform the key agreement protocol. - This is called after we have received an key agreement packet or an - reply to our key agreement packet. This returns TRUE if the user wants - the library to perform the key agreement protocol and FALSE if it is not - desired (application may start it later by calling the function - silc_client_perform_key_agreement). If TRUE is returned also the - `completion' and `context' arguments must be set by the application. */ - -static bool -silc_key_agreement(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, const char *hostname, - SilcUInt16 port, SilcKeyAgreementCallback *completion, - void **context) -{ - silcpurple_buddy_keyagr_request(client, conn, client_entry, hostname, port); - *completion = NULL; - *context = NULL; - return FALSE; + silcpurple_buddy_keyagr_request(client, conn, client_entry, + hostname, port, protocol); } @@ -2012,39 +1892,7 @@ const char *hostname, SilcUInt16 port) { silcpurple_ftp_request(client, conn, client_entry, session_id, - hostname, port); -} - - -/* Delivers SILC session detachment data indicated by `detach_data' to the - application. If application has issued SILC_COMMAND_DETACH command - the client session in the SILC network is not quit. The client remains - in the network but is detached. The detachment data may be used later - to resume the session in the SILC Network. The appliation is - responsible of saving the `detach_data', to for example in a file. - - The detachment data can be given as argument to the functions - silc_client_connect_to_server, or silc_client_add_connection when - creating connection to remote server, inside SilcClientConnectionParams - structure. If it is provided the client library will attempt to resume - the session in the network. After the connection is created - successfully, the application is responsible of setting the user - interface for user into the same state it was before detaching (showing - same channels, channel modes, etc). It can do this by fetching the - information (like joined channels) from the client library. */ - -static void -silc_detach(SilcClient client, SilcClientConnection conn, - const unsigned char *detach_data, SilcUInt32 detach_data_len) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - const char *file; - - /* Save the detachment data to file. */ - file = silcpurple_session_file(purple_account_get_username(sg->account)); - g_unlink(file); - silc_file_writefile(file, (char *)detach_data, detach_data_len); + hostname, port); } SilcClientOperations ops = { @@ -2054,13 +1902,9 @@ silc_notify, silc_command, silc_command_reply, - silc_connected, - silc_disconnected, silc_get_auth_method, silc_verify_public_key, silc_ask_passphrase, - silc_failure, silc_key_agreement, - silc_ftp, - silc_detach + silc_ftp };