updates.
authorPekka Riikonen <priikone@silcnet.org>
Sun, 27 May 2001 11:04:55 +0000 (11:04 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sun, 27 May 2001 11:04:55 +0000 (11:04 +0000)
apps/irssi/src/fe-common/silc/module-formats.c
apps/irssi/src/fe-common/silc/module-formats.h
apps/irssi/src/silc/core/client_ops.c [new file with mode: 0644]
apps/irssi/src/silc/core/silc-servers.c

index c70ff98d66b79f8defdaebced1c417e8c08e3091..4e591058a8a6fb68150000206fda546e9eae2665 100644 (file)
@@ -47,6 +47,24 @@ FORMAT_REC fecommon_silc_formats[] = {
        { "whois_idle", " idle     : $0", 1, { 0 } },
        { "whowas", "{nick $0} was {nickhost $1} ($2)", 3, { 0, 0, 0 } },
 
+       /* Key management and key agreement */
+       { NULL, "Key Management And Key Agreement", 0 },
+
+       { "channel_private_key_add", "Private key set to channel {channel $0}", 1, { 0 } },
+       { "channel_private_key_nomode", "Private key mode is not set on channel {channel $0}", 1, { 0 } },
+       { "channel_private_key_error", "Could not add private key to channel {channel $0}", 1, { 0 } },
+       { "channel_private_key_list", "Channel {channel $0} private keys%:  Cipher           Hmac             Key", 1, { 0 } },
+       { "private_key_list", "Private message keys%:  Client                         Cipher         Key", 0, { 0 } },
+       { "private_key_list_nick", "Private message keys with {nick $0}%:  Client                         Cipher         Key", 1, { 0 } },
+       { "key_agreement", "Requesting key agreement with {nick $0}", 1, { 0 } },
+       { "key_agreement_request", "{nick $0} wants to perform key agreement", 1, { 0 } },
+       { "key_agreement_request_host", "{nick $0} wants to perform key agreement on {nickhost $1} port {hilight $2}", 3, { 0, 0, 0 } },
+       { "key_agreement_negotiate", "Starting key agreement with {nick $0}", 1, { 0 } },
+       { "key_agreement_privmsg", "The private messages with the {nick $0} are now protected with the private key", 1, { 0 } },
+       { "key_agreement_ok", "Key agreement completed successfully with {nick $0}", 1, { 0 } },
+       { "key_agreement_error", "Error occurred during key agreement with {nick $0}", 1, { 0 } },
+       { "key_agreement_failure", "Key agreement failed with {nick $0}", 1, { 0 } },
+       { "key_agreement_timeout", "Timeout during key agreement. The key agreement was not performed with {nick $0}", 1, { 0 } },
+
        { NULL, NULL, 0 }
 };
-
index f2c5a1fcb424a9a68b9a5d4e11e4b178c74899b9..c0e9e86c668294cc7116f7730f8a6c4dc99e4a02 100644 (file)
@@ -42,6 +42,25 @@ enum {
   SILCTXT_WHOIS_MODES,
   SILCTXT_WHOIS_IDLE,
   SILCTXT_WHOWAS_USERINFO,
+
+  SILCTXT_FILL_3,
+
+  SILCTXT_CH_PRIVATE_KEY_ADD,
+  SILCTXT_CH_PRIVATE_KEY_NOMODE,
+  SILCTXT_CH_PRIVATE_KEY_ERROR,
+  SILCTXT_CH_PRIVATE_KEY_LIST,
+  SILCTXT_PRIVATE_KEY_LIST,
+  SILCTXT_PRIVATE_KEY_LIST_NICK,
+  SILCTXT_KEY_AGREEMENT,
+  SILCTXT_KEY_AGREEMENT_REQUEST,
+  SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
+  SILCTXT_KEY_AGREEMENT_NEGOTIATE,
+  SILCTXT_KEY_AGREEMENT_PRIVMSG,
+  SILCTXT_KEY_AGREEMENT_OK,
+  SILCTXT_KEY_AGREEMENT_ERROR,
+  SILCTXT_KEY_AGREEMENT_FAILURE,
+  SILCTXT_KEY_AGREEMENT_TIMEOUT,
+
 };
 
 extern FORMAT_REC fecommon_silc_formats[];
diff --git a/apps/irssi/src/silc/core/client_ops.c b/apps/irssi/src/silc/core/client_ops.c
new file mode 100644 (file)
index 0000000..ca4f643
--- /dev/null
@@ -0,0 +1,1086 @@
+/*
+
+  client_ops.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2001 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; 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.
+
+*/
+
+#include "module.h"
+#include "chat-protocols.h"
+#include "args.h"
+
+#include "chatnets.h"
+#include "servers-setup.h"
+#include "channels-setup.h"
+#include "silc-servers.h"
+#include "silc-channels.h"
+#include "silc-queries.h"
+#include "silc-nicklist.h"
+
+#include "signals.h"
+#include "levels.h"
+#include "settings.h"
+#include "fe-common/core/printtext.h"
+#include "fe-common/core/fe-channels.h"
+#include "fe-common/core/keyboard.h"
+#include "fe-common/silc/module-formats.h"
+
+static void 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               SilcSocketType conn_type, unsigned char *pk, 
+                               uint32 pk_len, SilcSKEPKType pk_type,
+                               SilcVerifyPublicKey completion, void *context);
+
+void silc_say(SilcClient client, SilcClientConnection conn,
+                    char *msg, ...)
+{
+  SILC_SERVER_REC *server;
+  va_list va;
+  char *str;
+
+  server = conn == NULL ? NULL : conn->context;
+  
+  va_start(va, msg);
+  str = g_strdup_vprintf(msg, va);
+  printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
+  g_free(str);
+  va_end(va);
+}
+
+void silc_say_error(char *msg, ...)
+{
+  va_list va;
+  char *str;
+
+  va_start(va, msg);
+  str = g_strdup_vprintf(msg, va);
+  printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
+
+  g_free(str);
+  va_end(va);
+}
+
+/* Message for a channel. The `sender' is the nickname of the sender 
+   received in the packet. The `channel_name' is the name of the channel. */
+
+void 
+silc_channel_message(SilcClient client, SilcClientConnection conn,
+                    SilcClientEntry sender, SilcChannelEntry channel,
+                    SilcMessageFlags flags, char *msg)
+{
+  SILC_SERVER_REC *server;
+  SILC_NICK_REC *nick;
+  SILC_CHANNEL_REC *chanrec;
+  
+  server = conn == NULL ? NULL : conn->context;
+  chanrec = silc_channel_find_entry(server, channel);
+  
+  nick = silc_nicklist_find(chanrec, sender);
+
+  if (flags & SILC_MESSAGE_FLAG_ACTION)
+    printformat_module("fe-common/silc", server, channel->channel_name,
+                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, msg);
+  else if (flags & SILC_MESSAGE_FLAG_NOTICE)
+    printformat_module("fe-common/silc", server, channel->channel_name,
+                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, msg);
+  else
+    signal_emit("message public", 6, server, msg,
+               nick == NULL ? "[<unknown>]" : nick->nick,
+               nick == NULL ? NULL : nick->host,
+               chanrec->name, nick);
+}
+
+/* Private message to the client. The `sender' is the nickname of the
+   sender received in the packet. */
+
+void 
+silc_private_message(SilcClient client, SilcClientConnection conn,
+                    SilcClientEntry sender, SilcMessageFlags flags,
+                         char *msg)
+{
+  SILC_SERVER_REC *server;
+  
+  server = conn == NULL ? NULL : conn->context;
+  signal_emit("message private", 4, server, msg,
+             sender->nickname ? sender->nickname : "[<unknown>]",
+             sender->username ? sender->username : NULL);
+}
+
+/* Notify message to the client. The notify arguments are sent in the
+   same order as servers sends them. The arguments are same as received
+   from the server except for ID's.  If ID is received application receives
+   the corresponding entry to the ID. For example, if Client ID is received
+   application receives SilcClientEntry.  Also, if the notify type is
+   for channel the channel entry is sent to application (even if server
+   does not send it). */
+
+typedef struct {
+  int type;
+  const char *name;
+} NOTIFY_REC;
+
+#define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
+static NOTIFY_REC notifies[] = {
+  { SILC_NOTIFY_TYPE_NONE,             NULL },
+  { SILC_NOTIFY_TYPE_INVITE,           "invite" },
+  { SILC_NOTIFY_TYPE_JOIN,             "join" },
+  { SILC_NOTIFY_TYPE_LEAVE,            "leave" },
+  { SILC_NOTIFY_TYPE_SIGNOFF,          "signoff" },
+  { SILC_NOTIFY_TYPE_TOPIC_SET,                "topic" },
+  { SILC_NOTIFY_TYPE_NICK_CHANGE,      "nick" },
+  { SILC_NOTIFY_TYPE_CMODE_CHANGE,     "cmode" },
+  { SILC_NOTIFY_TYPE_CUMODE_CHANGE,    "cumode" },
+  { SILC_NOTIFY_TYPE_MOTD,             "motd" },
+  { SILC_NOTIFY_TYPE_CHANNEL_CHANGE,   "channel_change" },
+  { SILC_NOTIFY_TYPE_SERVER_SIGNOFF,   "server_signoff" },
+  { SILC_NOTIFY_TYPE_KICKED,           "kick" },
+  { SILC_NOTIFY_TYPE_KILLED,           "kill" },
+  { SILC_NOTIFY_TYPE_UMODE_CHANGE,      "umode" },
+  { SILC_NOTIFY_TYPE_BAN,               "ban" },
+};
+
+void silc_notify(SilcClient client, SilcClientConnection conn,
+                       SilcNotifyType type, ...)
+{
+  SILC_SERVER_REC *server;
+  va_list va;
+  
+  server = conn == NULL ? NULL : conn->context;
+  va_start(va, type);
+  
+  if (type == SILC_NOTIFY_TYPE_NONE) {
+    /* Some generic notice from server */
+    printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
+  } else if (type < MAX_NOTIFY) {
+    /* Send signal about the notify event */
+    char signal[50];
+    g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
+    signal_emit(signal, 2, server, va);
+  } else {
+    /* Unknown notify */
+    printtext(server, NULL, MSGLEVEL_CRAP, "Unknown notify type %d", type);
+  }
+
+  va_end(va);
+}
+
+/* Called to indicate that connection was either successfully established
+   or connecting failed.  This is also the first time application receives
+   the SilcClientConnection objecet which it should save somewhere. */
+
+void 
+silc_connect(SilcClient client, SilcClientConnection conn, int success)
+{
+  SILC_SERVER_REC *server = conn->context;
+
+  if (success) {
+    server->connected = TRUE;
+    signal_emit("event connected", 1, server);
+  } else {
+    server->connection_lost = TRUE;
+    server->conn->context = NULL;
+    server_disconnect(SERVER(server));
+  }
+}
+
+/* Called to indicate that connection was disconnected to the server. */
+
+void 
+silc_disconnect(SilcClient client, SilcClientConnection conn)
+{
+  SILC_SERVER_REC *server = conn->context;
+
+  server->conn->context = NULL;
+  server->conn = NULL;
+  server->connection_lost = TRUE;
+  server_disconnect(SERVER(server));
+}
+
+/* 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 occured
+   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. */
+
+void 
+silc_command(SilcClient client, SilcClientConnection conn, 
+            SilcClientCommandContext cmd_context, int success,
+            SilcCommand command)
+{
+}
+
+/* Client info resolving callback when JOIN command reply is received.
+   This will cache all users on the channel. */
+
+void silc_client_join_get_users(SilcClient client,
+                               SilcClientConnection conn,
+                               SilcClientEntry *clients,
+                               uint32 clients_count,
+                               void *context)
+{
+  SilcChannelEntry channel = (SilcChannelEntry)context;
+  SilcChannelUser chu;
+  SILC_SERVER_REC *server = conn->context;
+  SILC_CHANNEL_REC *chanrec;
+  SilcClientEntry founder = NULL;
+  NICK_REC *ownnick;
+
+  if (!clients)
+    return;
+
+  chanrec = silc_channel_find(server, channel->channel_name);
+  if (chanrec == NULL)
+    return;
+
+  silc_list_start(channel->clients);
+  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+    if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
+      founder = chu->client;
+    silc_nicklist_insert(chanrec, chu, FALSE);
+  }
+
+  ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
+  nicklist_set_own(CHANNEL(chanrec), ownnick);
+  signal_emit("channel joined", 1, chanrec);
+
+  if (chanrec->topic)
+    printformat_module("fe-common/silc", server, channel->channel_name,
+                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
+                      channel->channel_name, chanrec->topic);
+
+  fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
+
+  if (founder) {
+    if (founder == conn->local_entry)
+      printformat_module("fe-common/silc", 
+                        server, channel->channel_name, MSGLEVEL_CRAP,
+                        SILCTXT_CHANNEL_FOUNDER_YOU,
+                        channel->channel_name);
+    else
+      printformat_module("fe-common/silc", 
+                        server, channel->channel_name, MSGLEVEL_CRAP,
+                        SILCTXT_CHANNEL_FOUNDER,
+                        channel->channel_name, founder->nickname);
+  }
+}
+
+/* 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 occured.
+   In this case arguments are not sent to the application. `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). */
+
+void 
+silc_command_reply(SilcClient client, SilcClientConnection conn,
+                  SilcCommandPayload cmd_payload, int success,
+                  SilcCommand command, SilcCommandStatus status, ...)
+
+{
+  SILC_SERVER_REC *server = conn->context;
+  SILC_CHANNEL_REC *chanrec;
+  va_list vp;
+
+  va_start(vp, status);
+
+  switch(command) {
+  case SILC_COMMAND_WHOIS:
+    {
+      char buf[1024], *nickname, *username, *realname;
+      uint32 idle, mode;
+      SilcBuffer channels;
+      
+      if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
+         status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+       char *tmp;
+       tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                        3, NULL);
+       if (tmp)
+         silc_say_error("%s: %s", tmp, 
+                        silc_client_command_status_message(status));
+       else
+         silc_say_error("%s", silc_client_command_status_message(status));
+       break;
+      }
+      
+      if (!success)
+       return;
+      
+      (void)va_arg(vp, SilcClientEntry);
+      nickname = va_arg(vp, char *);
+      username = va_arg(vp, char *);
+      realname = va_arg(vp, char *);
+      channels = va_arg(vp, SilcBuffer);
+      mode = va_arg(vp, uint32);
+      idle = va_arg(vp, uint32);
+      
+      printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_WHOIS_USERINFO, nickname, username, 
+                        realname);
+
+      if (channels) {
+       SilcDList list = silc_channel_payload_parse_list(channels);
+       if (list) {
+         SilcChannelPayload entry;
+         memset(buf, 0, sizeof(buf));
+         silc_dlist_start(list);
+         while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
+           char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
+           uint32 name_len;
+           char *name = silc_channel_get_name(entry, &name_len);
+           
+           if (m)
+             strncat(buf, m, strlen(m));
+           strncat(buf, name, name_len);
+           strncat(buf, " ", 1);
+           silc_free(m);
+         }
+
+         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                            SILCTXT_WHOIS_CHANNELS, buf);
+         silc_channel_payload_list_free(list);
+       }
+      }
+      
+      if (mode) {
+       memset(buf, 0, sizeof(buf));
+
+       if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
+           (mode & SILC_UMODE_ROUTER_OPERATOR)) {
+         strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
+                "Server Operator " :
+                (mode & SILC_UMODE_ROUTER_OPERATOR) ?
+                "SILC Operator " : "[Unknown mode] ");
+       }
+       if (mode & SILC_UMODE_GONE)
+         strcat(buf, "away");
+
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_WHOIS_MODES, buf);
+      }
+      
+      if (idle && nickname) {
+       memset(buf, 0, sizeof(buf));
+       snprintf(buf, sizeof(buf) - 1, "%lu %s",
+                idle > 60 ? (idle / 60) : idle,
+                idle > 60 ? "minutes" : "seconds");
+
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_WHOIS_IDLE, buf);
+      }
+    }
+    break;
+    
+  case SILC_COMMAND_WHOWAS:
+    {
+      char *nickname, *username, *realname;
+      
+      if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
+         status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+       char *tmp;
+       tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                        3, NULL);
+       if (tmp)
+         silc_say_error("%s: %s", tmp, 
+                        silc_client_command_status_message(status));
+       else
+         silc_say_error("%s", silc_client_command_status_message(status));
+       break;
+      }
+      
+      if (!success)
+       return;
+      
+      (void)va_arg(vp, SilcClientEntry);
+      nickname = va_arg(vp, char *);
+      username = va_arg(vp, char *);
+      realname = va_arg(vp, char *);
+      
+      printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_WHOWAS_USERINFO, nickname, username, 
+                        realname ? realname : "");
+    }
+    break;
+    
+  case SILC_COMMAND_INVITE:
+    {
+      SilcChannelEntry channel;
+      char *invite_list;
+      
+      if (!success)
+       return;
+      
+      /* XXX should use irssi routines */
+      
+      channel = va_arg(vp, SilcChannelEntry);
+      invite_list = va_arg(vp, char *);
+      
+      if (invite_list)
+       silc_say(client, conn, "%s invite list: %s", channel->channel_name,
+                invite_list);
+      else
+       silc_say(client, conn, "%s invite list not set", 
+                channel->channel_name);
+    }
+    break;
+
+  case SILC_COMMAND_JOIN: 
+    {
+      char *channel, *mode, *topic;
+      uint32 modei;
+      SilcChannelEntry channel_entry;
+      SilcBuffer client_id_list;
+      uint32 list_count;
+
+      channel = va_arg(vp, char *);
+      channel_entry = va_arg(vp, SilcChannelEntry);
+      modei = va_arg(vp, uint32);
+      (void)va_arg(vp, uint32);
+      (void)va_arg(vp, unsigned char *);
+      (void)va_arg(vp, unsigned char *);
+      (void)va_arg(vp, unsigned char *);
+      topic = va_arg(vp, char *);
+      (void)va_arg(vp, unsigned char *);
+      list_count = va_arg(vp, uint32);
+      client_id_list = va_arg(vp, SilcBuffer);
+
+      if (!success)
+       return;
+
+      chanrec = silc_channel_find(server, channel);
+      if (chanrec != NULL && !success)
+       channel_destroy(CHANNEL(chanrec));
+      else if (chanrec == NULL && success)
+       chanrec = silc_channel_create(server, channel, TRUE);
+      
+      if (topic) {
+       g_free_not_null(chanrec->topic);
+       chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
+       signal_emit("channel topic changed", 1, chanrec);
+      }
+
+      mode = silc_client_chmode(modei, channel_entry);
+      g_free_not_null(chanrec->mode);
+      chanrec->mode = g_strdup(mode == NULL ? "" : mode);
+      signal_emit("channel mode changed", 1, chanrec);
+
+      /* Resolve the client information */
+      silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
+                                     silc_client_join_get_users, 
+                                     channel_entry);
+      break;
+    }
+
+  case SILC_COMMAND_NICK: 
+    {
+      SilcClientEntry client = va_arg(vp, SilcClientEntry);
+      char *old;
+      
+      if (!success)
+       return;
+
+      old = g_strdup(server->nick);
+      server_change_nick(SERVER(server), client->nickname);
+      nicklist_rename_unique(SERVER(server),
+                            server->conn->local_entry, server->nick,
+                            client, client->nickname);
+      
+      signal_emit("message own_nick", 4, server, server->nick, old, "");
+      g_free(old);
+      break;
+    }
+    
+  case SILC_COMMAND_LIST:
+    {
+      char *topic, *name;
+      int usercount;
+      unsigned char buf[256], tmp[16];
+      int i, len;
+      
+      if (!success)
+       return;
+      
+      /* XXX should use irssi routines */
+      
+      (void)va_arg(vp, SilcChannelEntry);
+      name = va_arg(vp, char *);
+      topic = va_arg(vp, char *);
+      usercount = va_arg(vp, int);
+      
+      if (status == SILC_STATUS_LIST_START ||
+         status == SILC_STATUS_OK)
+       silc_say(client, conn, 
+                "  Channel                                  Users     Topic");
+      
+      memset(buf, 0, sizeof(buf));
+      strncat(buf, "  ", 2);
+      len = strlen(name);
+      strncat(buf, name, len > 40 ? 40 : len);
+      if (len < 40)
+       for (i = 0; i < 40 - len; i++)
+         strcat(buf, " ");
+      strcat(buf, " ");
+      
+      memset(tmp, 0, sizeof(tmp));
+      if (usercount) {
+       snprintf(tmp, sizeof(tmp), "%d", usercount);
+       strcat(buf, tmp);
+      }
+      len = strlen(tmp);
+      if (len < 10)
+       for (i = 0; i < 10 - len; i++)
+         strcat(buf, " ");
+      strcat(buf, " ");
+      
+      if (topic) {
+       len = strlen(topic);
+       strncat(buf, topic, len);
+      }
+      
+      silc_say(client, conn, "%s", buf);
+    }
+    break;
+    
+  case SILC_COMMAND_UMODE:
+    {
+      uint32 mode;
+      
+      if (!success)
+       return;
+      
+      mode = va_arg(vp, uint32);
+      
+      /* XXX todo */
+    }
+    break;
+    
+  case SILC_COMMAND_OPER:
+    silc_say(client, conn, "You are now server operator");
+    break;
+    
+  case SILC_COMMAND_SILCOPER:
+    silc_say(client, conn, "You are now SILC operator");
+    break;
+    
+  case SILC_COMMAND_USERS: 
+    {
+      SilcChannelEntry channel;
+      SilcChannelUser chu;
+      int line_len;
+      char *line;
+      
+      if (!success)
+       return;
+      
+      channel = va_arg(vp, SilcChannelEntry);
+      
+      /* There are two ways to do this, either parse the list (that
+        the command_reply sends (just take it with va_arg()) or just
+        traverse the channel's client list.  I'll do the latter.  See
+        JOIN command reply for example for the list. */
+      
+      silc_say(client, conn, "Users on %s", channel->channel_name);
+       
+      line = silc_calloc(1024, sizeof(*line));
+      line_len = 1024;
+      silc_list_start(channel->clients);
+      while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+       SilcClientEntry e = chu->client;
+       int i, len1;
+       char *m, tmp[80];
+       
+       memset(line, 0, line_len);
+       
+       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
+         silc_free(line);
+         line_len += strlen(e->nickname) + strlen(e->server) + 100;
+         line = silc_calloc(line_len, sizeof(*line));
+       }
+       
+       memset(tmp, 0, sizeof(tmp));
+       m = silc_client_chumode_char(chu->mode);
+       
+       strncat(line, " ", 1);
+       strncat(line, e->nickname, strlen(e->nickname));
+       strncat(line, e->server ? "@" : "", 1);
+       
+       len1 = 0;
+       if (e->server)
+         len1 = strlen(e->server);
+       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
+       
+       len1 = strlen(line);
+       if (len1 >= 30) {
+         memset(&line[29], 0, len1 - 29);
+       } else {
+         for (i = 0; i < 30 - len1 - 1; i++)
+           strcat(line, " ");
+       }
+       
+       if (e->mode & SILC_UMODE_GONE)
+         strcat(line, "  G");
+       else
+         strcat(line, "  H");
+       strcat(tmp, m ? m : "");
+       strncat(line, tmp, strlen(tmp));
+       
+       if (strlen(tmp) < 5)
+         for (i = 0; i < 5 - strlen(tmp); i++)
+           strcat(line, " ");
+       
+       strcat(line, e->username ? e->username : "");
+       
+       silc_say(client, conn, "%s", line);
+       
+       if (m)
+         silc_free(m);
+      }
+      
+      silc_free(line);
+    }
+    break;
+
+  case SILC_COMMAND_BAN:
+    {
+      SilcChannelEntry channel;
+      char *ban_list;
+      
+      if (!success)
+       return;
+      
+      /* XXX should use irssi routines */
+          
+      channel = va_arg(vp, SilcChannelEntry);
+      ban_list = va_arg(vp, char *);
+      
+      if (ban_list)
+       silc_say(client, conn, "%s ban list: %s", channel->channel_name,
+                ban_list);
+      else
+       silc_say(client, conn, "%s ban list not set", channel->channel_name);
+    }
+    break;
+    
+  case SILC_COMMAND_GETKEY:
+    {
+      SilcIdType id_type;
+      void *entry;
+      SilcPublicKey public_key;
+      unsigned char *pk;
+      uint32 pk_len;
+      
+      id_type = va_arg(vp, uint32);
+      entry = va_arg(vp, void *);
+      public_key = va_arg(vp, SilcPublicKey);
+      
+      pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+      
+      if (id_type == SILC_ID_CLIENT) {
+       silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
+                                       pk, pk_len, SILC_SKE_PK_TYPE_SILC,
+                                       NULL, NULL);
+      }
+      
+      silc_free(pk);
+    }
+    
+  case SILC_COMMAND_TOPIC:
+    {
+      SilcChannelEntry channel;
+      char *topic;
+      
+      if (!success)
+       return;
+      
+      channel = va_arg(vp, SilcChannelEntry);
+      topic = va_arg(vp, char *);
+      
+      if (topic) {
+       chanrec = silc_channel_find_entry(server, channel);
+       if (chanrec) {
+         g_free_not_null(chanrec->topic);
+         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
+         signal_emit("channel topic changed", 1, chanrec);
+       }
+       printformat_module("fe-common/silc", server, channel->channel_name,
+                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
+                          channel->channel_name, topic);
+      }
+    }
+    break;
+  }
+
+  va_end(vp);
+}
+
+/* Internal routine to verify public key. If the `completion' is provided
+   it will be called to indicate whether public was verified or not. */
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  char *filename;
+  char *entity;
+  unsigned char *pk;
+  uint32 pk_len;
+  SilcSKEPKType pk_type;
+  SilcVerifyPublicKey completion;
+  void *context;
+} *PublicKeyVerify;
+
+static void verify_public_key_completion(const char *line, void *context)
+{
+  PublicKeyVerify verify = (PublicKeyVerify)context;
+
+  if (line[0] == 'Y' || line[0] == 'y') {
+    /* Call the completion */
+    if (verify->completion)
+      verify->completion(TRUE, verify->context);
+
+    /* Save the key for future checking */
+    silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
+                                  verify->pk_len, SILC_PKCS_FILE_PEM);
+  } else {
+    /* Call the completion */
+    if (verify->completion)
+      verify->completion(FALSE, verify->context);
+
+    silc_say(verify->client, 
+            verify->conn, "Will not accept the %s key", verify->entity);
+  }
+
+  silc_free(verify->filename);
+  silc_free(verify->entity);
+  silc_free(verify);
+}
+
+static void 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               SilcSocketType conn_type, unsigned char *pk, 
+                               uint32 pk_len, SilcSKEPKType pk_type,
+                               SilcVerifyPublicKey completion, void *context)
+{
+  int i;
+  char file[256], filename[256], *fingerprint;
+  struct passwd *pw;
+  struct stat st;
+  char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
+                  conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
+                 "server" : "client");
+  PublicKeyVerify verify;
+
+  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+    silc_say(client, conn, "We don't support %s public key type %d", 
+            entity, pk_type);
+    if (completion)
+      completion(FALSE, context);
+    return;
+  }
+
+  pw = getpwuid(getuid());
+  if (!pw) {
+    if (completion)
+      completion(FALSE, context);
+    return;
+  }
+
+  memset(filename, 0, sizeof(filename));
+  memset(file, 0, sizeof(file));
+
+  if (conn_type == SILC_SOCKET_TYPE_SERVER ||
+      conn_type == SILC_SOCKET_TYPE_ROUTER) {
+    snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
+            conn->sock->hostname, conn->sock->port);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+  } else {
+    /* Replace all whitespaces with `_'. */
+    fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+    for (i = 0; i < strlen(fingerprint); i++)
+      if (fingerprint[i] == ' ')
+       fingerprint[i] = '_';
+    
+    snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+    silc_free(fingerprint);
+  }
+
+  /* Take fingerprint of the public key */
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+
+  verify = silc_calloc(1, sizeof(*verify));
+  verify->client = client;
+  verify->conn = conn;
+  verify->filename = strdup(filename);
+  verify->entity = strdup(entity);
+  verify->pk = pk;
+  verify->pk_len = pk_len;
+  verify->pk_type = pk_type;
+  verify->completion = completion;
+  verify->context = context;
+
+  /* Check whether this key already exists */
+  if (stat(filename, &st) < 0) {
+    /* Key does not exist, ask user to verify the key and save it */
+
+    silc_say(client, conn, "Received %s public key", entity);
+    silc_say(client, conn, "Fingerprint for the %s key is", entity);
+    silc_say(client, conn, "%s", fingerprint);
+
+    keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                           "Would you like to accept the key (y/n)? ", 0,
+                           verify);
+    silc_free(fingerprint);
+    return;
+  } else {
+    /* The key already exists, verify it. */
+    SilcPublicKey public_key;
+    unsigned char *encpk;
+    uint32 encpk_len;
+
+    /* Load the key file */
+    if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                  SILC_PKCS_FILE_PEM))
+      if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                    SILC_PKCS_FILE_BIN)) {
+       silc_say(client, conn, "Received %s public key", entity);
+       silc_say(client, conn, "Fingerprint for the %s key is", entity);
+       silc_say(client, conn, "%s", fingerprint);
+       silc_say(client, conn, "Could not load your local copy of the %s key",
+                entity);
+       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                               "Would you like to accept the key "
+                               "anyway (y/n)? ", 0,
+                               verify);
+       silc_free(fingerprint);
+       return;
+      }
+  
+    /* Encode the key data */
+    encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
+    if (!encpk) {
+      silc_say(client, conn, "Received %s public key", entity);
+      silc_say(client, conn, "Fingerprint for the %s key is", entity);
+      silc_say(client, conn, "%s", fingerprint);
+      silc_say(client, conn, "Your local copy of the %s key is malformed",
+              entity);
+      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                             "Would you like to accept the key "
+                             "anyway (y/n)? ", 0,
+                             verify);
+      silc_free(fingerprint);
+      return;
+    }
+
+    /* Compare the keys */
+    if (memcmp(encpk, pk, encpk_len)) {
+      silc_say(client, conn, "Received %s public key", entity);
+      silc_say(client, conn, "Fingerprint for the %s key is", entity);
+      silc_say(client, conn, "%s", fingerprint);
+      silc_say(client, conn, "%s key does not match with your local copy",
+              entity);
+      silc_say(client, conn, 
+              "It is possible that the key has expired or changed");
+      silc_say(client, conn, "It is also possible that some one is performing "
+                      "man-in-the-middle attack");
+
+      /* Ask user to verify the key and save it */
+      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                             "Would you like to accept the key "
+                             "anyway (y/n)? ", 0,
+                             verify);
+      silc_free(fingerprint);
+      return;
+    }
+
+    /* Local copy matched */
+    if (completion)
+      completion(TRUE, context);
+    silc_free(fingerprint);
+  }
+}
+
+/* Verifies received public key. The `conn_type' indicates which entity
+   (server, client etc.) has sent the public key. If user decides to trust
+   the key may be saved as trusted public key for later use. The 
+   `completion' must be called after the public key has been verified. */
+
+void 
+silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                      SilcSocketType conn_type, unsigned char *pk, 
+                      uint32 pk_len, SilcSKEPKType pk_type,
+                      SilcVerifyPublicKey completion, void *context)
+{
+  silc_verify_public_key_internal(client, conn, conn_type, pk,
+                                 pk_len, pk_type,
+                                 completion, context);
+}
+
+/* Asks passphrase from user on the input line. */
+
+typedef struct {
+  SilcAskPassphrase completion;
+  void *context;
+} *AskPassphrase;
+
+void ask_passphrase_completion(const char *passphrase, void *context)
+{
+  AskPassphrase p = (AskPassphrase)context;
+  p->completion((unsigned char *)passphrase, 
+               passphrase ? strlen(passphrase) : 0, p->context);
+  silc_free(p);
+}
+
+void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                               SilcAskPassphrase completion, void *context)
+{
+  AskPassphrase p = silc_calloc(1, sizeof(*p));
+  p->completion = completion;
+  p->context = context;
+
+  keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
+                         "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
+}
+
+/* Find authentication method and authentication data by hostname and
+   port. The hostname may be IP address as well. The found authentication
+   method and authentication data is returned to `auth_meth', `auth_data'
+   and `auth_data_len'. The function returns TRUE if authentication method
+   is found and FALSE if not. `conn' may be NULL. */
+
+int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
+                        char *hostname, uint16 port,
+                        SilcProtocolAuthMeth *auth_meth,
+                        unsigned char **auth_data,
+                        uint32 *auth_data_len)
+{
+
+  /* XXX must resolve from configuration whether this connection has
+     any specific authentication data */
+
+  *auth_meth = SILC_AUTH_NONE;
+  *auth_data = NULL;
+  *auth_data_len = 0;
+
+  return TRUE;
+}
+
+/* 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). */
+
+void 
+silc_failure(SilcClient client, SilcClientConnection conn, 
+            SilcProtocol protocol, void *failure)
+{
+  if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
+    SilcSKEStatus status = (SilcSKEStatus)failure;
+    
+    if (status == SILC_SKE_STATUS_BAD_VERSION)
+      silc_say_error("You are running incompatible client version (it may be "
+                    "too old or too new)");
+    if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
+      silc_say_error("Server does not support your public key type");
+    if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
+      silc_say_error("Server does not support one of your proposed KE group");
+    if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
+      silc_say_error("Server does not support one of your proposed cipher");
+    if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
+      silc_say_error("Server does not support one of your proposed PKCS");
+    if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
+      silc_say_error("Server does not support one of your proposed "
+                    "hash function");
+    if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
+      silc_say_error("Server does not support one of your proposed HMAC");
+    if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
+      silc_say_error("Incorrect signature");
+  }
+
+  if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
+    uint32 err = (uint32)failure;
+
+    if (err == SILC_AUTH_FAILED)
+      silc_say(client, conn, "Authentication failed");
+  }
+}
+
+/* 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). */
+
+int silc_key_agreement(SilcClient client, SilcClientConnection conn,
+                      SilcClientEntry client_entry, char *hostname,
+                      int port,
+                      SilcKeyAgreementCallback *completion,
+                      void **context)
+{
+  char portstr[6];
+
+  /* We will just display the info on the screen and return FALSE and user
+     will have to start the key agreement with a command. */
+
+  if (hostname) 
+    snprintf(portstr, sizeof(portstr) - 1, "%d", port);
+
+  if (!hostname)
+    printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
+                      SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
+  else
+    printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
+                      SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
+                      client_entry->nickname, hostname, portstr);
+
+  *completion = NULL;
+  *context = NULL;
+
+  return FALSE;
+}
+
+/* SILC client operations */
+SilcClientOperations ops = {
+  silc_say,
+  silc_channel_message,
+  silc_private_message,
+  silc_notify,
+  silc_command,
+  silc_command_reply,
+  silc_connect,
+  silc_disconnect,
+  silc_get_auth_method,
+  silc_verify_public_key,
+  silc_ask_passphrase,
+  silc_failure,
+  silc_key_agreement,
+};
index 062b6049fb221fc66095b5e939a0160a002d3018..50233995048e60bd1ed2302fcf3d7876f6fcceae 100644 (file)
@@ -62,6 +62,9 @@ typedef struct {
   char *msg;
 } PRIVMSG_REC;
 
+/* Callback function that sends the private message if the client was
+   resolved from the server. */
+
 static void silc_send_msg_clients(SilcClient client,
                                  SilcClientConnection conn,
                                  SilcClientEntry *clients,
@@ -89,13 +92,32 @@ static void silc_send_msg_clients(SilcClient client,
 static void silc_send_msg(SILC_SERVER_REC *server, char *nick, char *msg)
 {
   PRIVMSG_REC *rec;
+  SilcClientEntry client_entry;
+  uint32 num = 0;
+  char *nickname = NULL, *serv = NULL;
   
-  rec = g_new0(PRIVMSG_REC, 1);
-  rec->nick = g_strdup(nick);
-  rec->msg = g_strdup(msg);
-  
-  silc_client_get_clients(silc_client, server->conn,
-                         nick, "", silc_send_msg_clients, rec);
+  if (!silc_parse_nickname(nick, &nickname, &serv, &num)) {
+    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Bad nickname: %s", nick);
+    return;
+  }
+
+  /* Find client entry */
+  client_entry = silc_idlist_get_client(silc_client, server->conn, 
+                                       nickname, serv, num, FALSE);
+  if (!client_entry) {
+    rec = g_new0(PRIVMSG_REC, 1);
+    rec->nick = g_strdup(nick);
+    rec->msg = g_strdup(msg);
+
+    /* Could not find client with that nick, resolve it from server. */
+    silc_client_get_clients(silc_client, server->conn,
+                           nickname, serv, silc_send_msg_clients, rec);
+    return;
+  }
+
+  /* Send the private message directly */
+  silc_client_send_private_message(silc_client, server->conn, client_entry, 0,
+                                  msg, strlen(msg), TRUE);
 }
 
 static int isnickflag_func(char flag)
@@ -263,12 +285,6 @@ void silc_command_exec(SILC_SERVER_REC *server,
   (*cmd->cb)(ctx);
 }
 
-static void command_users(const char *data, SILC_SERVER_REC *server,
-                         WI_ITEM_REC *item)
-{
-  signal_emit("command names", 3, data, server, item);
-}
-
 static void command_self(const char *data, SILC_SERVER_REC *server)
 {
   if (!IS_SILC_SERVER(server) || !server->connected) {