Fix crash on expired keyboard prompts
[silc.git] / apps / irssi / src / silc / core / client_ops.c
index 4ac23150b16fc2c0c84d842ac5761b66969e321a..2de86781ee02c2339e3728f42678ac95e0e36e01 100644 (file)
@@ -30,6 +30,7 @@
 #include "silc-queries.h"
 #include "silc-nicklist.h"
 #include "silc-cmdqueue.h"
+#include "clientutil.h"
 
 #include "signals.h"
 #include "levels.h"
@@ -729,11 +730,10 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     name = va_arg(va, char *);
     client_entry = va_arg(va, SilcClientEntry);
 
-    memset(buf, 0, sizeof(buf));
-    snprintf(buf, sizeof(buf) - 1, "%s@%s",
-            client_entry->username, client_entry->hostname);
-    signal_emit("message invite", 4, server, channel ? channel->channel_name :
-               name, client_entry->nickname, buf);
+    silc_snprintf(buf, sizeof(buf) - 1, "%s@%s",
+                 client_entry->username, client_entry->hostname);
+    signal_emit("message invite", 4, server, name,
+               client_entry->nickname, buf);
     break;
 
   case SILC_NOTIFY_TYPE_JOIN:
@@ -843,10 +843,13 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
       if (silc_client_on_channel(channel, client_entry2)) {
        silc_snprintf(buf, sizeof(buf), "%s", client_entry2->nickname);
        silc_client_nickname_format(client, conn, client_entry2, TRUE);
-       if (!silc_utf8_strcasecmp(buf, client_entry2->nickname))
+       if (!silc_utf8_strcasecmp(buf, client_entry2->nickname)) {
+         nicklist_rename_unique(SERVER(server), client_entry2, buf,
+                                client_entry2, client_entry2->nickname);
          printformat_module("fe-common/silc", server, channel->channel_name,
                             MSGLEVEL_CRAP, SILCTXT_CHANNEL_USER_APPEARS,
                             buf, client_entry2->nickname);
+       }
       }
       silc_client_list_free(client, conn, clients);
       silc_free(name);
@@ -899,10 +902,13 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
       if (silc_client_on_channel(channel, client_entry2)) {
        silc_snprintf(buf, sizeof(buf), "%s", client_entry2->nickname);
        silc_client_nickname_format(client, conn, client_entry2, TRUE);
-       if (!silc_utf8_strcasecmp(buf, client_entry2->nickname))
+       if (!silc_utf8_strcasecmp(buf, client_entry2->nickname)) {
+         nicklist_rename_unique(SERVER(server), client_entry2, buf,
+                                client_entry2, client_entry2->nickname);
          printformat_module("fe-common/silc", server, channel->channel_name,
                             MSGLEVEL_CRAP, SILCTXT_CHANNEL_USER_APPEARS,
                             buf, client_entry2->nickname);
+       }
       }
       silc_client_list_free(client, conn, clients);
       silc_free(name);
@@ -1433,6 +1439,20 @@ void silc_getkey_cb(bool success, void *context)
                       entity, name);
   }
 
+  /*
+       * Drop our references as need be.
+       */
+  switch (getkey->id_type)
+  {
+    case SILC_ID_CLIENT:
+          silc_client_unref_client(getkey->client, getkey->conn, (SilcClientEntry)getkey->entry);
+          break;
+
+    case SILC_ID_SERVER:
+          silc_client_unref_server(getkey->client, getkey->conn, (SilcServerEntry)getkey->entry);
+               break;
+  }
+
   silc_free(getkey);
 }
 
@@ -1731,6 +1751,20 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
       NICK_REC *ownnick;
 
       if (SILC_STATUS_IS_ERROR(status)) {
+       if (status == SILC_STATUS_ERR_NO_SUCH_SERVER) {
+         char *tmp = va_arg(vp, char *);
+         if (tmp)
+           silc_say_error("JOIN: %s: %s", tmp,
+                          silc_get_status_message(status));
+         return;
+       }
+       if (status == SILC_STATUS_ERR_NO_SUCH_CHANNEL) {
+         char *tmp = va_arg(vp, char *);
+         if (tmp)
+           silc_say_error("JOIN: %s: %s", tmp,
+                          silc_get_status_message(status));
+         return;
+       }
        silc_say_error("JOIN: %s", silc_get_status_message(status));
        return;
       }
@@ -1788,8 +1822,8 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
       if (!ownnick)
        break;
       nicklist_set_own(CHANNEL(chanrec), ownnick);
-      signal_emit("channel joined", 1, chanrec);
       chanrec->entry = channel_entry;
+      signal_emit("channel joined", 1, chanrec);
 
       if (chanrec->topic)
        printformat_module("fe-common/silc", server,
@@ -2078,6 +2112,19 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
                ((SilcClientEntry)entry)->nickname :
                ((SilcServerEntry)entry)->server_name);
 
+       switch (id_type)
+       {
+               case SILC_ID_CLIENT:
+                       name = ((SilcClientEntry)entry)->nickname;
+                       silc_client_ref_client(client, conn, (SilcClientEntry)entry);
+                       break;
+
+               case SILC_ID_SERVER:
+                       name = ((SilcServerEntry)entry)->server_name;
+                       silc_client_ref_server(client, conn, (SilcServerEntry)entry);
+                       break;
+       }
+
        silc_verify_public_key_internal(client, conn, name,
                                        (id_type == SILC_ID_CLIENT ?
                                         SILC_CONN_CLIENT :
@@ -2347,11 +2394,13 @@ typedef struct {
   void *context;
 } *PublicKeyVerify;
 
-static void verify_public_key_completion(const char *line, void *context)
+static void verify_public_key_completion(const char *line, void *context,
+               SilcKeyboardPromptStatus reason)
 {
   PublicKeyVerify verify = (PublicKeyVerify)context;
+  bool success = (reason == KeyboardCompletionSuccess);
 
-  if (line[0] == 'Y' || line[0] == 'y') {
+  if (success && (line[0] == 'Y' || line[0] == 'y')) {
     /* Call the completion */
     if (verify->completion)
       verify->completion(TRUE, verify->context);
@@ -2370,6 +2419,30 @@ static void verify_public_key_completion(const char *line, void *context)
                       verify->entity);
   }
 
+  /*
+   * If we were not called due to a failure to begin the callback, then we
+   * shall zero the async context block in the server record.  If we were
+   * called due to a failure to begin the callback, then it is possible that
+   * we failed due to an overlapping callback, in which case we shouldn't
+   * overwrite the async context block pointer.
+   */
+
+  if (reason != KeyboardCompletionFailed)
+  {
+    /*
+         * Null out the completion context in the server record as this operation
+         * is done as far as we are concerned.  The underlying keyboard library
+         * routine will take care of freeing the async context memory when the
+         * actual callback is called by irssi in the abort case.  In the success
+         * case, it will free the async context memory after we return from this
+         * routine.
+         */
+
+     SILC_SERVER_REC *server = (SILC_SERVER_REC*)(verify->conn->context);
+
+         server->prompt_op = NULL;
+  }
+
   silc_free(verify->filename);
   silc_free(verify->entity);
   silc_free(verify->entity_name);
@@ -2394,6 +2467,7 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
   SilcPublicKey local_pubkey;
   SilcSILCPublicKey silc_pubkey;
   SilcUInt16 port;
+  SILC_SERVER_REC *server;
   const char *hostname, *ip;
   unsigned char *pk;
   SilcUInt32 pk_len;
@@ -2404,6 +2478,51 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
                  "server" : "client");
   int i;
 
+  server = (SILC_SERVER_REC*)conn->context;
+
+  /*
+       * If we don't have a context yet, then we'll set it up based on the
+       * stream context associated with the SilcPacketStream that is attached
+       * to the SilcClientConnection.  This is a bit ugly, but we need to have a
+       * per-connection context value to perform the public key verify operation,
+       * and the public API was not designed to let us have this in a particularly
+       * straightforward fashion.
+       */
+
+  if (!server) {
+    SilcPacketStream packet_stream;
+        SilcStream       stream;
+
+    packet_stream = conn->stream;
+
+        if (!packet_stream)
+    {
+      if (completion)
+        completion(FALSE, context);
+      return;
+    }
+
+    stream        = silc_packet_stream_get_stream(packet_stream);
+
+    if (!stream)
+    {
+      if (completion)
+        completion(FALSE, context);
+      return;
+    }
+
+    server        = (SILC_SERVER_REC*)(silc_socket_stream_get_context(stream));
+
+    if (!server)
+    {
+      if (completion)
+        completion(FALSE, context);
+      return;
+    }
+
+    conn->context = (void *)server;
+  }
+
   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
     printformat_module("fe-common/silc", NULL, NULL,
                       MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
@@ -2512,8 +2631,8 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
                       SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
                             SILCTXT_PUBKEY_ACCEPT);
-    keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
-                           format, 0, verify);
+    silc_keyboard_entry_redirect(verify_public_key_completion,
+                           format, 0, verify, &server->prompt_op);
     g_free(format);
     silc_free(fingerprint);
     silc_free(babbleprint);
@@ -2545,8 +2664,8 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
                         SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
                               SILCTXT_PUBKEY_ACCEPT_ANYWAY);
-      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
-                             format, 0, verify);
+      silc_keyboard_entry_redirect(verify_public_key_completion,
+                             format, 0, verify, &server->prompt_op);
       g_free(format);
       silc_free(fingerprint);
       silc_free(babbleprint);
@@ -2575,8 +2694,8 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
                         SILCTXT_PUBKEY_MALFORMED, entity);
       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
                               SILCTXT_PUBKEY_ACCEPT_ANYWAY);
-      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
-                             format, 0, verify);
+      silc_keyboard_entry_redirect(verify_public_key_completion,
+                             format, 0, verify, &server->prompt_op);
       g_free(format);
       silc_free(fingerprint);
       silc_free(babbleprint);
@@ -2611,8 +2730,8 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
       /* Ask user to verify the key and save it */
       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
                               SILCTXT_PUBKEY_ACCEPT_ANYWAY);
-      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
-                             format, 0, verify);
+      silc_keyboard_entry_redirect(verify_public_key_completion,
+                             format, 0, verify, &server->prompt_op);
       g_free(format);
       silc_free(fingerprint);
       silc_free(babbleprint);
@@ -2654,28 +2773,41 @@ silc_verify_public_key(SilcClient client, SilcClientConnection conn,
 
 typedef struct {
   SilcAskPassphrase completion;
+  SilcClientConnection conn;
   void *context;
 } *AskPassphrase;
 
-void ask_passphrase_completion(const char *passphrase, void *context)
+void ask_passphrase_completion(const char *passphrase, void *context,
+               SilcKeyboardPromptStatus reason)
 {
   AskPassphrase p = (AskPassphrase)context;
   if (passphrase && passphrase[0] == '\0')
     passphrase = NULL;
   p->completion((unsigned char *)passphrase,
                passphrase ? strlen(passphrase) : 0, p->context);
+
+  if (reason != KeyboardCompletionFailed)
+  {
+    SILC_SERVER_REC *server = (SILC_SERVER_REC *)(p->conn->context);
+
+        server->prompt_op = NULL;
+  }
+
   silc_free(p);
 }
 
 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
                         SilcAskPassphrase completion, void *context)
 {
+  SILC_SERVER_REC *server = (SILC_SERVER_REC*)(conn->context);
   AskPassphrase p = silc_calloc(1, sizeof(*p));
   p->completion = completion;
-  p->context = context;
+  p->conn       = conn;
+  p->context    = context;
 
-  keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
-                         "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
+  silc_keyboard_entry_redirect(ask_passphrase_completion,
+                         "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p, &server->prompt_op);
 }
 
 typedef struct {
@@ -2683,7 +2815,7 @@ typedef struct {
   void *context;
 } *GetAuthMethod;
 
-static void silc_get_auth_ask_passphrase(unsigned char *passphrase,
+static void silc_get_auth_ask_passphrase(const unsigned char *passphrase,
                                         SilcUInt32 passphrase_len,
                                         void *context)
 {