Watcher list support added.
authorPekka Riikonen <priikone@silcnet.org>
Fri, 19 Apr 2002 15:17:04 +0000 (15:17 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Fri, 19 Apr 2002 15:17:04 +0000 (15:17 +0000)
43 files changed:
CHANGES
TODO
apps/irssi/docs/help/in/cumode.in
apps/irssi/docs/help/in/detach.in [new file with mode: 0644]
apps/irssi/docs/help/in/umode.in
apps/irssi/docs/help/in/watch.in [new file with mode: 0644]
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
apps/irssi/src/silc/core/silc-servers.c
apps/silcd/command.c
apps/silcd/command.h
apps/silcd/command_reply.c
apps/silcd/command_reply.h
apps/silcd/idlist.c
apps/silcd/idlist.h
apps/silcd/packet_receive.c
apps/silcd/packet_send.c
apps/silcd/packet_send.h
apps/silcd/server.c
apps/silcd/server_internal.h
apps/silcd/server_util.c
apps/silcd/server_util.h
apps/silcd/serverid.c
doc/draft-riikonen-silc-commands-03.nroff
doc/draft-riikonen-silc-pp-05.nroff
doc/draft-riikonen-silc-spec-05.nroff
lib/silcclient/client_notify.c
lib/silcclient/command.c
lib/silcclient/command.h
lib/silcclient/command_reply.c
lib/silcclient/command_reply.h
lib/silccore/silccommand.h
lib/silccore/silcid.h
lib/silccore/silcmode.h
lib/silccore/silcnotify.h
lib/silccore/silcprivate.h
lib/silccore/silcstatus.h
lib/silcutil/silcdlist.h
lib/silcutil/silchashtable.c
lib/silcutil/silchashtable.h
lib/silcutil/silcutil.c
lib/silcutil/silcutil.h

diff --git a/CHANGES b/CHANGES
index 5f7ed21c8119e357e4b95934586aa8ac7afed30d..da7eafb53809be1b42ee4847d8a7bd47597e97fd 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,37 @@
+Fri Apr 19 17:35:15 EEST 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Defined that the nickname hash in Client ID MUST be from
+         lowercase nickname.  This effectively changes nicknames in
+         SILC to case-insensitive.  Updated the protocol specs and
+          the code.  Affected files are lib/silcutil/silcutil.[ch],
+          silcd/serverid.c, and silcd/idlist.c.
+
+       * Added new channel user modes BLOCK_MESSAGES_USERS and
+         BLOCK_MESSAGES_ROBOTS.  Updated the protocol specs and the
+         code.  Affected files are lib/silccore/silcmode.h,
+         lib/silcclient/command.c, and silcd/packet_send.c.
+
+       * Added new error status ERR_RESOURCE_LIMIT.  Updated protocol
+         specs and code.  Affected file lib/silccore/silcstatus.h.
+
+       * Added support for watch list.  It is possible to add nicknames
+         to be watched, and when they come to network, leave network
+         or user mode changes the watcher will be notified of this
+         change.  Added SILC_COMMAND_WATCH command, added new
+         notify type SILC_NOTIFY_TYPE_WATCH to deliver the watch
+         notifications.  Updated the protocol specs and implemented
+         this to library, client and server.  Protocol TODO #21.
+         Affected files are lib/silccore/silccomand.h,
+         lib/silccore/silcnotify.h, lib/silcclient/command[_reply].[ch],
+         silcd/command[_reply].[ch], lib/silcclient/client_notify.c,
+         silcd/packet_send.[ch], silcd/packet_receive.c, and
+         irssi/src/silc/core/client_ops.c.
+
+       * Added user mode SILC_UMODE_REJECT_WATCHING to reject
+         somebody watching you.  Updated the protocol specs and the
+         code.  Affected files are lib/silccore/silcmode.h, and
+         lib/silcclient/command.c.
+
 Fri Apr 19 09:02:20 CEST 2002  Pekka Riikonen <priikone@silcnet.org>
 
        * Added service support to SILC protocol.  Added new command
diff --git a/TODO b/TODO
index 314939a08f346d823c0dcbc30a9485c9994a5d6e..16393d8e027fdb5872faca26fa8f1c9a3ded128b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -112,8 +112,6 @@ TODO in SILC Protocol
  17. Cell wide channel founder support, and permanent channels when
      founder mode set.
 
- 21. Subscription/IRC's notify kind support?
-
  24. Implement the <Requested Attributes> and the Attribute Payload to
      the core library, client and server.
 
index 03d249654368a891cda476045e8a6f13cffedab0..84d0e2083c57863eb64e655345321c2ac0adbe91 100644 (file)
@@ -39,5 +39,21 @@ are available:
         to the client.  This mode can be used to block
         unwanted messages if desired.
 
+    u <nickname>[@<server>]
+
+        Set/unset channel message blocking for messages
+        sent by normal users.  When set the server will
+        only deliver messages sent by channel founder or
+        channel operator.  Client may set this mode only
+        to itself.
+
+    r <nickname>[@<server>]
+
+        Set/unset channel message blocking for messages
+        sent by robots.  When set the server will not
+        deliver message from users that has been marked
+        as robots.  Client may set this mode only to
+        itself.
+
 
 See also: CMODE, UMODE
diff --git a/apps/irssi/docs/help/in/detach.in b/apps/irssi/docs/help/in/detach.in
new file mode 100644 (file)
index 0000000..387bc2d
--- /dev/null
@@ -0,0 +1,18 @@
+
+@SYNTAX:detach@
+
+This command is used to detach from the SILC Network without actually
+loosing the SILC session in the network.  This means that the network
+connection to the server is closed but the client remains in the network.
+After giving this command the connection to the server is closed, and
+a resume data is saved in ~/.silc/ directory.  Next time connection
+is created into a server (which may be different from the original server)
+the resume data is used to resume back to the network.
+
+When this command is called the server sets a detached mode to your
+client.  If other users give WHOIS to your nickname they will see that
+you are detached from the network.  Also, all messages that are sent
+to you will be lost since server cannot deliver them to you.
+
+
+See also: UMODE, QUIT, DISCONNECT
index 757fcfc9cf12868c23a548daf19e0617c69570ca..65de4ac443857e8b2eadf2ca289aaa3f20db6155 100644 (file)
@@ -22,5 +22,6 @@ modes are available:
              private messages server automatically discards.
              This can be used to block unwanted private
              messages.
+    w        Set/unset to reject anyone from watching you
 
 See also: CMODE, CUMODE, AWAY
diff --git a/apps/irssi/docs/help/in/watch.in b/apps/irssi/docs/help/in/watch.in
new file mode 100644 (file)
index 0000000..962b356
--- /dev/null
@@ -0,0 +1,20 @@
+
+@SYNTAX:watch@
+
+This command is used to add nicknames to your watch list in the server.
+When the watched nickname appears in the network, leaves the network
+or its user mode is changed you will be notified for this change.  This
+same command can be used also to remove nicknames from being watched.
+
+Note that some users may have a user mode set that rejects you from
+receiving notifications about them.  Also note that since nicknames are
+not unique it is possible that same nickname matches several users in
+the network.  In this case you will receive notifications about all
+of those users.
+
+Examples:
+
+    /WATCH -add foobar
+    /WATCH -del foobar
+
+See also: WHOIS
index a886132904715ac1ede6f8100a27073f2d4c1cbb..fc1d95530bfc8660381c52f7f583ac70ff66069a 100644 (file)
@@ -119,6 +119,11 @@ FORMAT_REC fecommon_silc_formats[] = {
        { "set_away", "You have been marked as being away (away message: {hilight $0})", 1, { 0 } },
        { "unset_away", "You are no longer marked as being away", 0 },
        { "auth_meth_unresolved", "Could not resolve authentication method to use, assume no authentication", 0 },
+       { "watch_present", "Watch: {nick $0} is now present in the network", 1, { 0 } },
+       { "watch_signoff", "Watch: {nick $0} left the network", 1, { 0 } },
+       { "watch_killed", "Watch: {nick $0} was killed from the network", 1, { 0 } },
+       { "watch_umode_change", "Watch: {nick $0} is now {hilight $1}", 2, { 0, 0 } },
+       { "watch_nick_change", "Watch: {nick $0} changed nickname to {nick $1}", 2, { 0, 0 } },
 
        /* File transfer messages */
        { NULL, "FileTransfer", 0 },
index 42422e4bd4616d1b206bb06e22c86ed23e45d3b5..b3c613eb0fbbdf7ea0f507037f188f634b6c2ce9 100644 (file)
@@ -113,6 +113,11 @@ enum {
   SILCTXT_SET_AWAY,
   SILCTXT_UNSET_AWAY,
   SILCTXT_AUTH_METH_UNRESOLVED,
+  SILCTXT_WATCH_PRESENT,
+  SILCTXT_WATCH_SIGNOFF,
+  SILCTXT_WATCH_KILLED,
+  SILCTXT_WATCH_UMODE_CHANGE,
+  SILCTXT_WATCH_NICK_CHANGE,
 
   SILCTXT_FILL_5,
 
index 6df6da378b7971ff3595c7dd2ce7da8a0d531dfe..dc6f358d89ea9e926613e751f7bdee81c2e1cbcb 100644 (file)
@@ -47,6 +47,36 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
                                SilcSKEPKType pk_type,
                                SilcVerifyPublicKey completion, void *context);
 
+static void silc_get_umode_string(SilcUInt32 mode, char *buf, 
+                                 SilcUInt32 buf_size)
+{
+  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]");
+  if (mode & SILC_UMODE_INDISPOSED)
+    strcat(buf, " [indisposed]");
+  if (mode & SILC_UMODE_BUSY)
+    strcat(buf, " [busy]");
+  if (mode & SILC_UMODE_PAGE)
+    strcat(buf, " [page to reach]");
+  if (mode & SILC_UMODE_HYPER)
+    strcat(buf, " [hyper active]");
+  if (mode & SILC_UMODE_ROBOT)
+    strcat(buf, " [robot]");
+  if (mode & SILC_UMODE_ANONYMOUS)
+    strcat(buf, " [anonymous]");
+  if (mode & SILC_UMODE_BLOCK_PRIVMSG)
+    strcat(buf, " [blocks private messages]");
+  if (mode & SILC_UMODE_DETACHED)
+    strcat(buf, " [detached]");
+}
+
 void silc_say(SilcClient client, SilcClientConnection conn,
              SilcClientMessageType type, char *msg, ...)
 {
@@ -212,7 +242,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
   SilcIdType idtype;
   void *entry;
   SilcUInt32 mode;
-  char userhost[512];
+  char buf[512];
   char *name, *tmp;
   GSList *list1, *list_tmp;
 
@@ -239,11 +269,11 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     name = va_arg(va, char *);
     client_entry = va_arg(va, SilcClientEntry);
 
-    memset(userhost, 0, sizeof(userhost));
-    snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+    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, userhost);
+               name, client_entry->nickname, buf);
     break;
 
   case SILC_NOTIFY_TYPE_JOIN:
@@ -270,13 +300,13 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
       }
     }
     
-    memset(userhost, 0, sizeof(userhost));
+    memset(buf, 0, sizeof(buf));
     if (client_entry->username)
-    snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+    snprintf(buf, sizeof(buf) - 1, "%s@%s",
             client_entry->username, client_entry->hostname);
     signal_emit("message join", 4, server, channel->channel_name,
                client_entry->nickname,
-               client_entry->username == NULL ? "" : userhost);
+               client_entry->username == NULL ? "" : buf);
     break;
 
   case SILC_NOTIFY_TYPE_LEAVE:
@@ -289,13 +319,13 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     client_entry = va_arg(va, SilcClientEntry);
     channel = va_arg(va, SilcChannelEntry);
     
-    memset(userhost, 0, sizeof(userhost));
+    memset(buf, 0, sizeof(buf));
     if (client_entry->username)
-      snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+      snprintf(buf, sizeof(buf) - 1, "%s@%s",
               client_entry->username, client_entry->hostname);
     signal_emit("message part", 5, server, channel->channel_name,
                client_entry->nickname,  client_entry->username ? 
-               userhost : "", client_entry->nickname);
+               buf : "", client_entry->nickname);
     
     chanrec = silc_channel_find_entry(server, channel);
     if (chanrec != NULL) {
@@ -317,12 +347,12 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     
     silc_server_free_ftp(server, client_entry);
     
-    memset(userhost, 0, sizeof(userhost));
+    memset(buf, 0, sizeof(buf));
     if (client_entry->username)
-      snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+      snprintf(buf, sizeof(buf) - 1, "%s@%s",
               client_entry->username, client_entry->hostname);
     signal_emit("message quit", 4, server, client_entry->nickname,
-               client_entry->username ? userhost : "", 
+               client_entry->username ? buf : "", 
                tmp ? tmp : "");
     
     list1 = nicklist_get_same_unique(SERVER(server), client_entry);
@@ -356,11 +386,11 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     
     if (idtype == SILC_ID_CLIENT) {
       client_entry = (SilcClientEntry)entry;
-      memset(userhost, 0, sizeof(userhost));
-      snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+      memset(buf, 0, sizeof(buf));
+      snprintf(buf, sizeof(buf) - 1, "%s@%s",
               client_entry->username, client_entry->hostname);
       signal_emit("message topic", 5, server, channel->channel_name,
-                 tmp, client_entry->nickname, userhost);
+                 tmp, client_entry->nickname, buf);
     } else if (idtype == SILC_ID_SERVER) {
       server_entry = (SilcServerEntry)entry;
       signal_emit("message topic", 5, server, channel->channel_name,
@@ -386,14 +416,14 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     if (!strcmp(client_entry->nickname, client_entry2->nickname))
       break;
     
-    memset(userhost, 0, sizeof(userhost));
-    snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+    memset(buf, 0, sizeof(buf));
+    snprintf(buf, sizeof(buf) - 1, "%s@%s",
             client_entry2->username, client_entry2->hostname);
     nicklist_rename_unique(SERVER(server),
                           client_entry, client_entry->nickname,
                           client_entry2, client_entry2->nickname);
     signal_emit("message nick", 4, server, client_entry2->nickname, 
-               client_entry->nickname, userhost);
+               client_entry->nickname, buf);
     break;
 
   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
@@ -640,12 +670,12 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
       clients_count = va_arg(va, SilcUInt32);
   
       for (i = 0; i < clients_count; i++) {
-       memset(userhost, 0, sizeof(userhost));
+       memset(buf, 0, sizeof(buf));
        if (clients[i]->username)
-         snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+         snprintf(buf, sizeof(buf) - 1, "%s@%s",
                   clients[i]->username, clients[i]->hostname);
        signal_emit("message quit", 4, server, clients[i]->nickname,
-                   clients[i]->username ? userhost : "", 
+                   clients[i]->username ? buf : "", 
                    "server signoff");
 
        silc_server_free_ftp(server, clients[i]);
@@ -670,6 +700,64 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     }
     break;
 
+  case SILC_NOTIFY_TYPE_WATCH:
+    {
+      SilcNotifyType notify;
+
+      client_entry = va_arg(va, SilcClientEntry);
+      name = va_arg(va, char *);          /* Maybe NULL */
+      mode = va_arg(va, SilcUInt32);
+      notify = va_arg(va, int);
+
+      if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
+       if (name)
+         printformat_module("fe-common/silc", server, NULL,
+                            MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
+                            client_entry->nickname, name);
+       else
+         printformat_module("fe-common/silc", server, NULL,
+                            MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
+                            client_entry->nickname);
+      } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
+       /* See if client was away and is now present */
+       if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
+                     SILC_UMODE_BUSY | SILC_UMODE_PAGE |
+                     SILC_UMODE_DETACHED)) &&
+           (client_entry->mode & SILC_UMODE_GONE ||
+            client_entry->mode & SILC_UMODE_INDISPOSED ||
+            client_entry->mode & SILC_UMODE_BUSY ||
+            client_entry->mode & SILC_UMODE_PAGE ||
+            client_entry->mode & SILC_UMODE_DETACHED)) {
+         printformat_module("fe-common/silc", server, NULL,
+                            MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
+                            client_entry->nickname);
+       }
+
+       if (mode) {
+         memset(buf, 0, sizeof(buf));
+         silc_get_umode_string(mode, buf, sizeof(buf) - 1);
+         printformat_module("fe-common/silc", server, NULL,
+                            MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
+                            client_entry->nickname, buf);
+       }
+      } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
+                          client_entry->nickname);
+      } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
+                notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
+                          client_entry->nickname);
+      } else if (notify == SILC_NOTIFY_TYPE_NONE) {
+       /* Client logged in to the network */
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
+                          client_entry->nickname);
+      }
+    }
+    break;
+
   default:
     /* Unknown notify */
     printformat_module("fe-common/silc", server, NULL,
@@ -1003,33 +1091,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       
       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]");
-       if (mode & SILC_UMODE_INDISPOSED)
-         strcat(buf, " [indisposed]");
-       if (mode & SILC_UMODE_BUSY)
-         strcat(buf, " [busy]");
-       if (mode & SILC_UMODE_PAGE)
-         strcat(buf, " [page to reach]");
-       if (mode & SILC_UMODE_HYPER)
-         strcat(buf, " [hyper active]");
-       if (mode & SILC_UMODE_ROBOT)
-         strcat(buf, " [robot]");
-       if (mode & SILC_UMODE_ANONYMOUS)
-         strcat(buf, " [anonymous]");
-       if (mode & SILC_UMODE_BLOCK_PRIVMSG)
-         strcat(buf, " [blocks private messages]");
-       if (mode & SILC_UMODE_DETACHED)
-         strcat(buf, " [detached]");
-
+       silc_get_umode_string(mode, buf, sizeof(buf - 1));
        printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
                           SILCTXT_WHOIS_MODES, buf);
       }
@@ -1465,6 +1527,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
     }
     break;
 
+  case SILC_COMMAND_WATCH:
+    break;
   }
 
   va_end(vp);
index 835e20bc579345fdb42603f05ccc69eec238a2f4..d258c4c7802eec282e83b1a2521e20cc470fd9ce 100644 (file)
@@ -349,6 +349,7 @@ char *silc_server_get_channels(SILC_SERVER_REC *server)
 /* SYNTAX: FILE */
 /* SYNTAX: JOIN <channel> [<passphrase>] [-cipher <cipher>] [-hmac <hmac>] [-founder <-pubkey|passwd>] */
 /* SYNTAX: DETACH */
+/* SYNTAX: WATCH [<-add | -del> <nickname>] */
 
 void silc_command_exec(SILC_SERVER_REC *server,
                       const char *command, const char *args)
@@ -899,6 +900,7 @@ void silc_server_init(void)
   command_bind_silc("sconnect", MODULE_NAME, (SIGNAL_FUNC) command_sconnect);
   command_bind_silc("file", MODULE_NAME, (SIGNAL_FUNC) command_file);
   command_bind_silc("detach", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind_silc("watch", MODULE_NAME, (SIGNAL_FUNC) command_self);
 
   command_set_options("connect", "+silcnet");
 }
@@ -933,6 +935,7 @@ void silc_server_deinit(void)
   command_unbind("sconnect", (SIGNAL_FUNC) command_sconnect);
   command_unbind("file", (SIGNAL_FUNC) command_file);
   command_unbind("detach", (SIGNAL_FUNC) command_self);
+  command_unbind("watch", (SIGNAL_FUNC) command_self);
 }
 
 void silc_server_free_ftp(SILC_SERVER_REC *server,
index 06e5b96d7717fed78c6d947714f6f5bb70300415..25df5eddead10cca6f40826c906f0d7fe4b7efe0 100644 (file)
@@ -67,6 +67,7 @@ SilcServerCommand silc_command_list[] =
   SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(ban, BAN, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(detach, DETACH, SILC_CF_LAG_STRICT | SILC_CF_REG),
+  SILC_SERVER_CMD(watch, WATCH, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(silcoper, SILCOPER,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
   SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
@@ -2046,19 +2047,21 @@ SILC_SERVER_CMD_FUNC(nick)
                                        FALSE : TRUE, client->id,
                                        new_id, nick);
 
+  /* Check if anyone is watching the old nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, nick,
+                                  SILC_NOTIFY_TYPE_NICK_CHANGE);
+
   oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
   /* Remove old cache entry */
   silc_idcache_del_by_context(server->local_list->clients, client);
 
-  /* Free old ID */
   silc_free(client->id);
+  client->id = new_id;
 
-  /* Save the nickname as this client is our local client */
   silc_free(client->nickname);
-
   client->nickname = strdup(nick);
-  client->id = new_id;
 
   /* Update client cache */
   silc_idcache_add(server->local_list->clients, client->nickname, 
@@ -2074,6 +2077,11 @@ SILC_SERVER_CMD_FUNC(nick)
                                      client->nickname, 
                                      strlen(client->nickname));
 
+  /* Check if anyone is watching the new nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_NICK_CHANGE);
+
  send_reply:
   /* Send the new Client ID as reply command back to client */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK, 
@@ -2345,8 +2353,9 @@ SILC_SERVER_CMD_FUNC(topic)
       goto out;
     }
 
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE && 
-       channel->mode & SILC_CHANNEL_MODE_TOPIC) {
+    if (channel->mode & SILC_CHANNEL_MODE_TOPIC &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
                                            SILC_STATUS_ERR_NO_CHANNEL_PRIV);
       goto out;
@@ -2451,8 +2460,9 @@ SILC_SERVER_CMD_FUNC(invite)
 
   /* Check whether the channel is invite-only channel. If yes then the
      sender of this command must be at least channel operator. */
-  if (chl->mode == SILC_CHANNEL_UMODE_NONE &&
-      channel->mode & SILC_CHANNEL_MODE_INVITE) {
+  if (channel->mode & SILC_CHANNEL_MODE_INVITE &&
+      !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+      !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                            SILC_STATUS_ERR_NO_CHANNEL_PRIV);
     goto out;
@@ -2750,6 +2760,11 @@ SILC_SERVER_CMD_FUNC(kill)
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
                                        SILC_STATUS_OK);
 
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_KILLED);
+
   /* Now do the killing */
   silc_server_kill_client(server, remote_client, comment, client->id,
                          SILC_ID_CLIENT);
@@ -3759,6 +3774,11 @@ SILC_SERVER_CMD_FUNC(umode)
     if (!server->standalone)
       silc_server_send_notify_umode(server, server->router->connection, TRUE,
                                    client->id, client->mode);
+
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_UMODE_CHANGE);
   }
 
   /* Send command reply to sender */
@@ -3838,10 +3858,11 @@ SILC_SERVER_CMD_FUNC(cmode)
   /* Check that client has rights to change any requested channel modes */
   if (set_mask && !silc_server_check_cmode_rights(server, channel, chl, 
                                                  mode_mask)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         (chl->mode == 0 ? 
-                                          SILC_STATUS_ERR_NO_CHANNEL_PRIV :
-                                          SILC_STATUS_ERR_NO_CHANNEL_FOPRIV));
+    silc_server_command_send_status_reply(
+                            cmd, SILC_COMMAND_CMODE,
+                            (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) ? 
+                             SILC_STATUS_ERR_NO_CHANNEL_PRIV :
+                             SILC_STATUS_ERR_NO_CHANNEL_FOPRIV));
     goto out;
   }
 
@@ -4394,6 +4415,53 @@ SILC_SERVER_CMD_FUNC(cumode)
     }
   }
 
+  if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NOT_YOU);
+      goto out;
+    }
+
+    if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)) {
+      chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) {
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
+      }
+
+      chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+      notify = TRUE;
+    }
+  }
+
+  if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NOT_YOU);
+      goto out;
+    }
+
+    if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)) {
+      chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) {
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
+      }
+
+      chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+      notify = TRUE;
+    }
+  }
 
   idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
   tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
@@ -4487,7 +4555,8 @@ SILC_SERVER_CMD_FUNC(kick)
   }
 
   /* Check that the kicker is channel operator or channel founder */
-  if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+  if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+      !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CHANNEL_PRIV);
     goto out;
@@ -4663,6 +4732,11 @@ SILC_SERVER_CMD_FUNC(oper)
     silc_server_send_notify_umode(server, server->router->connection, TRUE,
                                  client->id, client->mode);
 
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_UMODE_CHANGE);
+
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
                                        SILC_STATUS_OK);
@@ -4731,6 +4805,11 @@ SILC_SERVER_CMD_FUNC(detach)
                                  server->server_type == SILC_SERVER ?
                                  FALSE : TRUE, client->id, client->mode);
 
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_UMODE_CHANGE);
+
   q = silc_calloc(1, sizeof(*q));
   q->server = server;
   q->sock = cmd->sock;
@@ -4755,6 +4834,165 @@ SILC_SERVER_CMD_FUNC(detach)
   silc_server_command_free(cmd);
 }
 
+/* Server side of WATCH command. */
+
+SILC_SERVER_CMD_FUNC(watch)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  char *add_nick, *del_nick;
+  SilcUInt32 add_nick_len, del_nick_len, tmp_len;
+  char nick[128 + 1];
+  unsigned char hash[16], *tmp;
+  SilcClientEntry client;
+  SilcClientID *client_id = NULL;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WATCH, cmd, 1, 3);
+
+  if (server->server_type == SILC_SERVER && !server->standalone) {
+    if (!cmd->pending) {
+      /* Send the command to router */
+      SilcBuffer tmpbuf;
+      SilcUInt16 old_ident;
+
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+      silc_server_packet_send(server, server->router->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_WATCH,
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_watch,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+    } else if (context2) {
+      /* Received reply from router, just send same data to the client. */
+      SilcServerCommandReplyContext reply = context2;
+      SilcStatus status;
+      silc_command_get_status(reply->payload, &status, NULL);
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, status);
+    }
+
+    goto out;
+  }
+
+  /* We are router and keep the watch list for local cell */
+
+  /* Get the client ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+    goto out;
+  }
+
+  /* Get the client entry which must be in local list */
+  client = silc_idlist_find_client_by_id(server->local_list, 
+                                        client_id, TRUE, NULL);
+  if (!client) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+    goto out;
+  }
+
+  /* Take nickname */
+  add_nick = silc_argument_get_arg_type(cmd->args, 2, &add_nick_len);
+  del_nick = silc_argument_get_arg_type(cmd->args, 3, &del_nick_len);
+  if (!add_nick && !del_nick) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  if (add_nick && add_nick_len > 128)
+    add_nick[128] = '\0';
+  if (del_nick && del_nick_len > 128)
+    del_nick[128] = '\0';
+
+  memset(nick, 0, sizeof(nick));
+
+  /* Add new nickname to be watched in our cell */
+  if (add_nick) {
+    if (silc_server_name_bad_chars(add_nick, strlen(add_nick)) == TRUE) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_BAD_NICKNAME);
+      goto out;
+    }
+
+    /* Hash the nick, we have the hash saved, not nicks because we can
+       do one to one mapping to the nick from Client ID hash this way. */
+    silc_to_lower(add_nick, nick, sizeof(nick) - 1);
+    silc_hash_make(server->md5hash, nick, strlen(nick), hash);
+
+    /* Check whether this client is already watching this nickname */
+    if (silc_hash_table_find_by_context(server->watcher_list, hash, 
+                                       client, NULL)) {
+      /* Nickname is alredy being watched for this client */
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_NICKNAME_IN_USE);
+      goto out;
+    }
+
+    /* Get the nickname from the watcher list and use the same key in
+       new entries as well.  If key doesn't exist then create it. */
+    if (!silc_hash_table_find(server->watcher_list, hash, (void **)&tmp, NULL))
+      tmp = silc_memdup(hash, CLIENTID_HASH_LEN);
+
+    /* Add the client to the watcher list with the specified nickname hash. */
+    silc_hash_table_add(server->watcher_list, tmp, client);
+  }
+
+  /* Delete nickname from watch list */
+  if (del_nick) {
+    if (silc_server_name_bad_chars(del_nick, strlen(del_nick)) == TRUE) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_BAD_NICKNAME);
+      goto out;
+    }
+
+    /* Hash the nick, we have the hash saved, not nicks because we can
+       do one to one mapping to the nick from Client ID hash this way. */
+    silc_to_lower(del_nick, nick, sizeof(nick) - 1);
+    silc_hash_make(server->md5hash, nick, strlen(nick), hash);
+
+    /* Check that this client is watching for this nickname */
+    if (!silc_hash_table_find_by_context(server->watcher_list, hash, 
+                                        client, (void **)&tmp)) {
+      /* Nickname is alredy being watched for this client */
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_NO_SUCH_NICK);
+      goto out;
+    }
+
+    /* Delete the nickname from the watcher list. */
+    silc_hash_table_del_by_context(server->watcher_list, hash, client);
+
+    /* Now check whether there still exists entries with this key, if not
+       then free the key to not leak memory. */
+    if (!silc_hash_table_find(server->watcher_list, hash, NULL, NULL))
+      silc_free(tmp);
+  }
+
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_free(client_id);
+  silc_server_command_free(cmd);
+}
+
 /* Server side of SILCOPER command. Client uses this comand to obtain router
    operator privileges to this router. */
 
@@ -4845,6 +5083,11 @@ SILC_SERVER_CMD_FUNC(silcoper)
     silc_server_send_notify_umode(server, server->router->connection, TRUE,
                                  client->id, client->mode);
 
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_UMODE_CHANGE);
+
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
                                        SILC_STATUS_OK);
@@ -5404,7 +5647,8 @@ SILC_SERVER_CMD_FUNC(connect)
     goto out;
 
   /* Check whether client has the permissions. */
-  if (client->mode == SILC_UMODE_NONE) {
+  if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+      !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
                                          SILC_STATUS_ERR_NO_SERVER_PRIV);
     goto out;
@@ -5461,7 +5705,8 @@ SILC_SERVER_CMD_FUNC(close)
     goto out;
 
   /* Check whether client has the permissions. */
-  if (client->mode == SILC_UMODE_NONE) {
+  if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+      !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
                                          SILC_STATUS_ERR_NO_SERVER_PRIV);
     goto out;
@@ -5528,7 +5773,8 @@ SILC_SERVER_CMD_FUNC(shutdown)
     goto out;
 
   /* Check whether client has the permission. */
-  if (client->mode == SILC_UMODE_NONE) {
+  if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+      !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
                                          SILC_STATUS_ERR_NO_SERVER_PRIV);
     goto out;
index 229a37597f81fab7f037398662907e168c37df97..464b261b7f8efacc8d881d945a2831b42b8b47f6 100644 (file)
@@ -141,6 +141,7 @@ SILC_SERVER_CMD_FUNC(cumode);
 SILC_SERVER_CMD_FUNC(kick);
 SILC_SERVER_CMD_FUNC(ban);
 SILC_SERVER_CMD_FUNC(detach);
+SILC_SERVER_CMD_FUNC(watch);
 SILC_SERVER_CMD_FUNC(silcoper);
 SILC_SERVER_CMD_FUNC(leave);
 SILC_SERVER_CMD_FUNC(users);
index cd93edc7a21287ca8898407eb820bef86d9a7dbe..eb56f929e90d11fe527305ce7052959e99593c8d 100644 (file)
@@ -1262,3 +1262,15 @@ SILC_SERVER_CMD_REPLY_FUNC(list)
   silc_free(channel_id);
   silc_server_command_reply_free(cmd);
 }
+
+SILC_SERVER_CMD_REPLY_FUNC(watch)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcStatus status, error;
+
+  COMMAND_CHECK_STATUS;
+
+ out:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
+  silc_server_command_reply_free(cmd);
+}
index 9156e298413da14a0da6f0a48371e69ff040b118..7a06fd6e590ee8f2f2952af203dcc1b42911214b 100644 (file)
@@ -76,5 +76,6 @@ SILC_SERVER_CMD_REPLY_FUNC(stats);
 SILC_SERVER_CMD_REPLY_FUNC(users);
 SILC_SERVER_CMD_REPLY_FUNC(getkey);
 SILC_SERVER_CMD_REPLY_FUNC(list);
+SILC_SERVER_CMD_REPLY_FUNC(watch);
 
 #endif
index ab8f7d53af5abb6b9b0773020d25001fb2e4526d..211d59eb0bae2a81d999fd65e19258b6d74e2a07 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "serverincludes.h"
 #include "idlist.h"
+#include "server_internal.h"
 
 /******************************************************************************
 
@@ -420,10 +421,13 @@ int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
   SilcIDCacheEntry id_cache = NULL;
   unsigned char hash[32];
   SilcClientID client_id;
+  char nick[128 + 1];
 
   SILC_LOG_DEBUG(("Start"));
 
-  silc_hash_make(md5hash, nickname, strlen(nickname), hash);
+  memset(nick, 0, sizeof(nick));
+  silc_to_lower(nickname, nick, sizeof(nick) - 1);
+  silc_hash_make(md5hash, nick, strlen(nick), hash);
 
   /* As the Client ID is hashed in the ID cache by hashing only the hash
      from the Client ID, we can do a lookup with only the hash not the
@@ -493,7 +497,8 @@ silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
 /* Replaces old Client ID with new one */
 
 SilcClientEntry
-silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
+silc_idlist_replace_client_id(SilcServer server,
+                             SilcIDList id_list, SilcClientID *old_id,
                              SilcClientID *new_id, const char *nickname)
 {
   SilcIDCacheEntry id_cache = NULL;
@@ -521,6 +526,11 @@ silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
   if (!silc_idcache_del_by_context(id_list->clients, client))
     return NULL;
 
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, nickname,
+                                  SILC_NOTIFY_TYPE_NICK_CHANGE);
+
   silc_free(client->id);
   silc_free(client->nickname);
   client->id = new_id;
index 1d92d4c388687738aa09e816386bcd04e35f54f0..c9fa2fa76bb095cd1d515c7f595cdc0e5913dfa3 100644 (file)
@@ -598,7 +598,8 @@ SilcClientEntry
 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
                              bool registered, SilcIDCacheEntry *ret_entry);
 SilcClientEntry
-silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
+silc_idlist_replace_client_id(SilcServer server,
+                             SilcIDList id_list, SilcClientID *old_id,
                              SilcClientID *new_id, const char *nickname);
 void silc_idlist_client_destructor(SilcIDCache cache,
                                   SilcIDCacheEntry entry);
index 428c1f7f9f47d94744424e609a116e0537e6e5ff..6fcbf60132326f6d577278455c7075692f9d53bd 100644 (file)
@@ -338,6 +338,11 @@ void silc_server_notify(SilcServer server,
     /* Remove the client from all channels. */
     silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
 
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_SIGNOFF);
+
     client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
     cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
     break;
@@ -402,8 +407,9 @@ void silc_server_notify(SilcServer server,
     /* Get user's channel entry and check that topic set is allowed. */
     if (!silc_server_client_on_channel(client, channel, &chl))
       goto out;
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE && 
-       channel->mode & SILC_CHANNEL_MODE_TOPIC) {
+    if (channel->mode & SILC_CHANNEL_MODE_TOPIC &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       SILC_LOG_DEBUG(("Topic change is not allowed"));
       goto out;
     }
@@ -455,10 +461,12 @@ void silc_server_notify(SilcServer server,
       nickname = silc_argument_get_arg_type(args, 3, &nickname_len);;
 
       /* Replace the Client ID */
-      client = silc_idlist_replace_client_id(server->global_list, client_id,
+      client = silc_idlist_replace_client_id(server,
+                                            server->global_list, client_id,
                                             client_id2, nickname);
       if (!client)
-       client = silc_idlist_replace_client_id(server->local_list, client_id, 
+       client = silc_idlist_replace_client_id(server,
+                                              server->local_list, client_id, 
                                               client_id2, nickname);
 
       if (client) {
@@ -583,8 +591,7 @@ void silc_server_notify(SilcServer server,
       /* Set the HMAC key out of current channel key. The client must do
         this locally. */
       silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
-                    channel->key_len / 8, 
-                    hash);
+                    channel->key_len / 8, hash);
       silc_hmac_set_key(channel->hmac, hash, 
                        silc_hash_len(silc_hmac_get_hash(channel->hmac)));
       memset(hash, 0, sizeof(hash));
@@ -688,7 +695,8 @@ void silc_server_notify(SilcServer server,
        
        if (client != client2) {
          /* Sender must be operator */
-         if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+         if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+             !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
            SILC_LOG_DEBUG(("CUMODE change is not allowed"));
            goto out;
          }
@@ -828,8 +836,9 @@ void silc_server_notify(SilcServer server,
     /* Get user's channel entry and check that inviting is allowed. */
     if (!silc_server_client_on_channel(client, channel, &chl))
       goto out;
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE && 
-       channel->mode & SILC_CHANNEL_MODE_INVITE) {
+    if (channel->mode & SILC_CHANNEL_MODE_INVITE &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       SILC_LOG_DEBUG(("Inviting is not allowed"));
       goto out;
     }
@@ -1047,6 +1056,15 @@ void silc_server_notify(SilcServer server,
            silc_server_remove_from_channels(server, NULL, client, 
                                             TRUE, NULL, FALSE);
 
+           /* Check if anyone is watching this nickname */
+           if (server->server_type == SILC_ROUTER)
+             silc_server_check_watcher_list(server, client, NULL,
+                                            SILC_NOTIFY_TYPE_SERVER_SIGNOFF);
+
+           /* Remove this client from watcher list if it is */
+           if (local)
+             silc_server_del_from_watcher_list(server, client);
+
            /* Remove the client */
            silc_idlist_del_client(local ? server->local_list :
                                   server->global_list, client);
@@ -1148,7 +1166,8 @@ void silc_server_notify(SilcServer server,
       /* Kicker must be operator on channel */
       if (!silc_server_client_on_channel(client2, channel, &chl))
        goto out;
-      if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+      if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+         !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
        SILC_LOG_DEBUG(("Kicking is not allowed"));
        goto out;
       }
@@ -1249,6 +1268,11 @@ void silc_server_notify(SilcServer server,
       silc_server_remove_from_channels(server, NULL, client, FALSE, NULL, 
                                       FALSE);
 
+      /* Check if anyone is watching this nickname */
+      if (server->server_type == SILC_ROUTER)
+       silc_server_check_watcher_list(server, client, NULL,
+                                      SILC_NOTIFY_TYPE_KILLED);
+
       break;
     }
 
@@ -1299,6 +1323,11 @@ void silc_server_notify(SilcServer server,
     /* Change the mode */
     client->mode = mode;
 
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_UMODE_CHANGE);
+
     break;
 
   case SILC_NOTIFY_TYPE_BAN:
@@ -1736,7 +1765,9 @@ void silc_server_channel_message(SilcServer server,
 
     /* If channel is moderated check that client is allowed to send
        messages. */
-    if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && !chl->mode) {
+    if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && 
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       SILC_LOG_DEBUG(("Channel is silenced from normal users"));
       goto out;
     }
@@ -1995,6 +2026,10 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   /* Send some nice info to the client */
   silc_server_send_connect_notifys(server, sock, client);
 
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL, 0);
+
   return client;
 }
 
@@ -2269,6 +2304,10 @@ static void silc_server_new_id_real(SilcServer server,
       if (sock->type == SILC_SOCKET_TYPE_SERVER)
        server->stat.cell_clients++;
       server->stat.clients++;
+
+      /* Check if anyone is watching this nickname */
+      if (server->server_type == SILC_ROUTER && id_list == server->local_list)
+       silc_server_check_watcher_list(server, entry, NULL, 0);
     }
     break;
 
index 9272547baa0f36593ef6d46533f0c91ec7f649db..85e7695d70f267c814bd9f6852f1104e49cdc33e 100644 (file)
@@ -721,7 +721,7 @@ void silc_server_packet_relay_to_channel(SilcServer server,
   SilcPacketContext packetdata;
   SilcClientEntry client = NULL;
   SilcServerEntry *routed = NULL;
-  SilcChannelClientEntry chl;
+  SilcChannelClientEntry chl, chl_sender;
   SilcUInt32 routed_count = 0;
   SilcIDListData idata;
   SilcHashTableList htl;
@@ -730,6 +730,9 @@ void silc_server_packet_relay_to_channel(SilcServer server,
 
   SILC_LOG_DEBUG(("Relaying packet to channel"));
 
+  if (!silc_server_client_on_channel(sender_entry, channel, &chl_sender))
+    return;
+
   /* This encrypts the packet, if needed. It will be encrypted if
      it came from the router thus it needs to be encrypted with the
      channel key. If the channel key does not exist, then we know we
@@ -784,8 +787,18 @@ void silc_server_packet_relay_to_channel(SilcServer server,
   silc_hash_table_list(channel->user_list, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     client = chl->client;
-    if (!client || client == sender_entry || 
-       chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
+    if (!client || client == sender_entry)
+      continue;
+
+    /* Check whether message sending is blocked */
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
+      continue;
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS &&
+       !(chl_sender->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl_sender->mode & SILC_CHANNEL_UMODE_CHANFO))
+      continue;
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS &&
+       sender_entry->mode & SILC_UMODE_ROBOT)
       continue;
 
     /* If the client has set router it means that it is not locally
@@ -1378,6 +1391,32 @@ void silc_server_send_notify_invite(SilcServer server,
   silc_buffer_free(idp2);
 }
 
+/* Sends WATCH notify type. This tells that the `client' was watched and
+   its status in the network has changed. */
+
+void silc_server_send_notify_watch(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  SilcClientEntry watcher,
+                                  SilcClientEntry client,
+                                  const char *nickname,
+                                  SilcNotifyType type)
+{
+  SilcBuffer idp;
+  unsigned char mode[4], n[2];
+
+  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  SILC_PUT16_MSB(type, n);
+  SILC_PUT32_MSB(client->mode, mode);
+  silc_server_send_notify_dest(server, sock, FALSE, watcher->id,
+                              SILC_ID_CLIENT, SILC_NOTIFY_TYPE_WATCH,
+                              4, idp->data, idp->len,
+                              nickname, strlen(nickname),
+                              mode, sizeof(mode), 
+                              type != SILC_NOTIFY_TYPE_NONE ?
+                              n : NULL, sizeof(n));
+  silc_buffer_free(idp);
+}
+
 /* Sends notify message destined to specific entity. */
 
 void silc_server_send_notify_dest(SilcServer server,
index 1434ef8a3d40ac69d51cec4efdbd40b4685cb0a3..2c322e200f23d55d54c8eb6106e41afecfb71e46 100644 (file)
@@ -190,6 +190,12 @@ void silc_server_send_notify_invite(SilcServer server,
                                    SilcChannelEntry channel,
                                    SilcClientID *client_id,
                                    char *add, char *del);
+void silc_server_send_notify_watch(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  SilcClientEntry watcher,
+                                  SilcClientEntry client,
+                                  const char *nickname,
+                                  SilcNotifyType type);
 void silc_server_send_notify_dest(SilcServer server,
                                  SilcSocketConnection sock,
                                  bool broadcast,
index 25c719e7777c01b2be7cc3e5db2b52358c4fd317..09c54b7d9af231dd659546308b98d75e04fafdaa 100644 (file)
@@ -108,6 +108,7 @@ void silc_server_free(SilcServer server)
     silc_idcache_free(server->global_list->clients);
     silc_idcache_free(server->global_list->servers);
     silc_idcache_free(server->global_list->channels);
+    silc_hash_table_free(server->watcher_list);
 
     silc_free(server->sockets);
     silc_free(server);
@@ -208,6 +209,14 @@ bool silc_server_init(SilcServer server)
   server->global_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
   server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
 
+  /* Init watcher list */
+  server->watcher_list = 
+    silc_hash_table_alloc(1, silc_hash_client_id_hash, NULL,
+                         silc_hash_data_compare, (void *)CLIENTID_HASH_LEN,
+                         NULL, NULL, TRUE);
+  if (!server->watcher_list)
+    goto err;
+
   /* Create a listening server */
   if (!silc_server_listen(server, &sock))
     goto err;
@@ -2506,6 +2515,14 @@ void silc_server_free_client_data(SilcServer server,
     silc_server_remove_from_channels(server, NULL, client,
                                     FALSE, NULL, FALSE);
 
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_SIGNOFF);
+
+  /* Remove this client from watcher list if it is */
+  silc_server_del_from_watcher_list(server, client);
+
   /* Update statistics */
   server->stat.my_clients--;
   server->stat.clients--;
index 7a6a9253c6bcb7b02b23a03696bdea81cc64ef09..ea703791c46b942b1aa974951c65e563f3c9b57c 100644 (file)
@@ -94,6 +94,7 @@ struct SilcServerStruct {
   /* ID lists. */
   SilcIDList local_list;
   SilcIDList global_list;
+  SilcHashTable watcher_list;
 
   /* Table of connected sockets */
   SilcSocketConnection *sockets;
@@ -208,5 +209,7 @@ do {                                                \
 
 /* Prototypes */
 SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final);
+void silc_server_watcher_list_destroy(void *key, void *context,
+                                     void *user_context);
 
 #endif
index 2fe705f32b5c10064f8f541f15207b2a56552e49..b24d07bac76385baebe9d6cecb8d5c37ce762ebb 100644 (file)
@@ -230,6 +230,9 @@ bool silc_server_remove_clients_by_server(SilcServer server,
          id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
        } else {
          silc_idlist_del_client(server->local_list, client);
+
+         /* Remove this client from watcher list if it is */
+         silc_server_del_from_watcher_list(server, client);
        }
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -1251,10 +1254,119 @@ void silc_server_kill_client(SilcServer server,
     SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
 
     /* Remove remote client */
-    if (!silc_idlist_del_client(server->global_list, remote_client))
+    if (!silc_idlist_del_client(server->global_list, remote_client)) {
+      /* Remove this client from watcher list if it is */
+      silc_server_del_from_watcher_list(server, remote_client);
+
       silc_idlist_del_client(server->local_list, remote_client);  
-}
+    }
+  }
 
   silc_buffer_free(killer);
   silc_buffer_free(killed);
 }
+
+typedef struct {
+  SilcServer server;
+  SilcClientEntry client;
+  SilcNotifyType notify;
+  const char *new_nick;
+} WatcherNotifyContext;
+
+static void 
+silc_server_check_watcher_list_foreach(void *key, void *context, 
+                                      void *user_context)
+{
+  WatcherNotifyContext *notify = user_context;
+  SilcClientEntry entry = context;
+  SilcSocketConnection sock;
+
+  if (entry == notify->client)
+    return;
+
+  sock = silc_server_get_client_route(notify->server, NULL, 0, entry->id,
+                                     NULL, NULL);
+  if (sock) {
+    SILC_LOG_DEBUG(("Sending WATCH notify to %s",
+                   silc_id_render(entry->id, SILC_ID_CLIENT)));
+
+    /* Send the WATCH notify */
+    silc_server_send_notify_watch(notify->server, sock, entry, 
+                                 notify->client, 
+                                 notify->new_nick ? notify->new_nick :
+                                 notify->client->nickname, notify->notify);
+  }
+}
+
+/* This function checks whether the `client' nickname is being watched
+   by someone, and notifies the watcher of the notify change of notify
+   type indicated by `notify'. */
+
+bool silc_server_check_watcher_list(SilcServer server,
+                                   SilcClientEntry client,
+                                   const char *new_nick,
+                                   SilcNotifyType notify)
+{
+  unsigned char hash[16];
+  WatcherNotifyContext n;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* If the watching is rejected by the client do nothing */
+  if (client->mode & SILC_UMODE_REJECT_WATCHING)
+    return FALSE;
+
+  /* Make hash from the nick, or take it from Client ID */
+  if (client->nickname) {
+    char nick[128 + 1];
+    memset(nick, 0, sizeof(nick));
+    silc_to_lower(client->nickname, nick, sizeof(nick) - 1);
+    silc_hash_make(server->md5hash, nick, strlen(nick), hash);
+  } else {
+    memset(hash, 0, sizeof(hash));
+    memcpy(hash, client->id->hash, sizeof(client->id->hash));
+  }
+
+  n.server = server;
+  n.client = client;
+  n.new_nick = new_nick;
+  n.notify = notify;
+
+  /* Send notify to all watchers */
+  silc_hash_table_find_foreach(server->watcher_list, hash,
+                              silc_server_check_watcher_list_foreach, &n);
+
+  return TRUE;
+}
+
+/* Remove the `client' from watcher list. After calling this the `client'
+   is not watching any nicknames. */
+
+bool silc_server_del_from_watcher_list(SilcServer server,
+                                      SilcClientEntry client)
+{
+  SilcHashTableList htl;
+  void *key;
+  SilcClientEntry entry;
+  bool found = FALSE;
+
+  silc_hash_table_list(server->watcher_list, &htl);
+  while (silc_hash_table_get(&htl, &key, (void **)&entry)) {
+    if (entry == client) {
+      silc_hash_table_del_by_context(server->watcher_list, key, client);
+
+      SILC_LOG_DEBUG(("Removing %s from WATCH list",
+                     silc_id_render(client->id, SILC_ID_CLIENT)));
+
+      /* Now check whether there still exists entries with this key, if not
+        then free the key to not leak memory. */
+      if (!silc_hash_table_find(server->watcher_list, key, NULL, NULL))
+       silc_free(key);
+
+      found = TRUE;
+    }
+  }
+  silc_hash_table_list_reset(&htl);
+
+  return found;
+}
index 5a531037f7e6dfddba77861227531daee756e702..6b421f238d0e86b3f1c8a9f477981a62b4c99eb3 100644 (file)
@@ -149,4 +149,17 @@ void silc_server_kill_client(SilcServer server,
                             void *killer_id,
                             SilcIdType killer_id_type);
 
+/* This function checks whether the `client' nickname is being watched
+   by someone, and notifies the watcher of the notify change of notify
+   type indicated by `notify'. */
+bool silc_server_check_watcher_list(SilcServer server,
+                                   SilcClientEntry client,
+                                   const char *new_nick,
+                                   SilcNotifyType notify);
+
+/* Remove the `client' from watcher list. After calling this the `client'
+   is not watching any nicknames. */
+bool silc_server_del_from_watcher_list(SilcServer server,
+                                      SilcClientEntry client);
+
 #endif /* SERVER_UTIL_H */
index fdb0cd9df21dfe85f629b58c5be3e437f72f2a49..d9e39bb5350c35d0b5eacbbf346d82580297c231 100644 (file)
@@ -61,13 +61,16 @@ bool silc_id_create_client_id(SilcServer server,
 {
   unsigned char hash[16];
   bool finding = FALSE;
+  char nick[128 + 1];
 
   SILC_LOG_DEBUG(("Creating new Client ID"));
 
   *new_id = silc_calloc(1, sizeof(**new_id));
 
   /* Create hash of the nickanem */
-  silc_hash_make(md5hash, nickname, strlen(nickname), hash);
+  memset(nick, 0, sizeof(nick));
+  silc_to_lower(nickname, nick, sizeof(nick) - 1);
+  silc_hash_make(md5hash, nick, strlen(nick), hash);
 
   /* Create the ID */
   memcpy((*new_id)->ip.data, server_id->ip.data, server_id->ip.data_len);
index 525426a1462afb5f01b42cb6ee71e82a8e91c1c8..470aad30e2defd99d7a1fa2d36944fb3a35c0ebd 100644 (file)
@@ -462,9 +462,7 @@ List of all defined commands in SILC follows.
 
         Set/change nickname.  This command is used to set nickname for
         user.  Nickname MUST NOT include any spaces (` '), non-printable
-        characters, commas (`,') and any wildcard characters.  Note that
-        nicknames in SILC are case-sensitive which must be taken into
-        account when searching clients by nickname.
+        characters, commas (`,') and any wildcard characters.
 
         When nickname is changed new Client ID is generated.  Server MUST
         distribute SILC_NOTIFY_TYPE_NICK_CHANGE to local clients on the
@@ -637,6 +635,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NOT_ON_CHANNEL
             SILC_STATUS_ERR_USER_ON_CHANNEL
             SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_RESOURCE_LIMIT
 
 
    8    SILC_COMMAND_QUIT
@@ -1082,7 +1081,9 @@ List of all defined commands in SILC follows.
               Message Key flag is set in the SILC packet header.
               If this mode is set server MUST NOT deliver private
               messages to the client without the Private Message
-              Key flag being set.
+              Key flag being set.  The Private Message Key flag set
+              indicates that the private message is protected with
+              a key shared between the sender and the recipient.
 
               A separate service could provide additional filtering
               features for accepting private messages from certain
@@ -1111,6 +1112,21 @@ List of all defined commands in SILC follows.
               by knowing that the network connection is not active.
               In this case the default case is to discard the packet.
 
+
+           0x00000800    SILC_UMODE_REJECT_WATCHING
+
+              Marks that the client rejects that it could be watched
+              by someone else.  If this mode is set notifications about
+              this client is not send, even if someone is watching the
+              same nickname this client has.  Client MAY set and unset
+              this mode.  Any changes for this client MUST NOT be
+              notified to any watcher when this mode is set.
+
+              A separate service could provide additional filtering
+              features for rejecting and accepting the watching from
+              certain users.  However, this document does not specify
+              such service.
+
         If the <client mode mask> was not provided this command merely
         returns the mode mask to the client.
 
@@ -1444,6 +1460,33 @@ List of all defined commands in SILC follows.
               service.
 
 
+           0x00000008    SILC_CUMODE_BLOCK_MESSAGES_USERS
+
+              Marks that the client wishes not to receive any channel
+              messages sent from normal users.  Only messages sent by
+              channel founder or channel operator is accepted.  Client
+              MAY set and unset this mode to itself.  Client MUST NOT
+              set it to anyone else.  When this mode is set server MUST
+              NOT deliver channel messages that are sent by normal users
+              to this client.
+
+              A separate service could provide additional filtering
+              features for accepting channel messages from certain
+              sender.  However, this document does not specify such
+              service.
+
+
+           0x00000010    SILC_CUMODE_BLOCK_MESSAGES_ROBOTS
+
+              Marks that the client wishes not to receive any channel
+              messages sent from robots.  Messages sent by Users with
+              the SILC_UMODE_ROBOT user mode set are not delivered.
+              Client MAY set and unset this mode to itself.  Client MUST
+              NOT set it to anyone else.  When this mode is set server
+              MUST NOT deliver channel messages that are sent by robots
+              to this client.
+
+
         Reply messages to the command:
 
         Max Arguments:  4
@@ -1552,12 +1595,89 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_CHANNEL_ID
             SILC_STATUS_ERR_NOT_ON_CHANNEL
             SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_RESOURCE_LIMIT
+
+
+   21   SILC_COMMAND_DETACH
+
+        Max Arguments:  0
+            Arguments:
+
+        This command is used to detach from the network.  Client can
+        send this command to its server to indicate that it will be
+        detached.  By detaching the client remains in the network but
+        the actual network connection to the server is closed.  The
+        client may then later resume the old session back.
+
+        When this command is received the server MUST check that the
+        client is locally connected client, and set the user mode
+        SILC_UMODE_DETACHED flag.  The SILC_NOTIFY_TYPE_UMODE_CHANGE
+        MUST be also sent to routers.  The server then sends command
+        reply to this command and closes the network connection.
+        The server MUST NOT remove the client from its lists, or send
+        any signoff notifications for this client.  See the [SILC1]
+        for detailed information about detaching.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with the status indication.
 
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+
+
+   22   SILC_COMMAND_WATCH
+
+        Max Arguments:  3
+            Arguments:  (1) <Client ID>       (2) [<add nickname>]
+                        (3) [<del nickname>]
 
-   21   <deprecated command>
+        This command is used to set up a watch for <add nickname>
+        nickname.  When a user in the network appears with the
+        nickname, or signoffs the network or user's mode is changed
+        the client which set up the watch will be notified about
+        this change.  This can be used to watch for certain nicknames
+        in the network and receive notifications when for example a
+        friend appears in the network or leaves the network.
 
+        The <del nickname> is a nickname that has been previously
+        added to watch list and is now removed from it.  Notifications
+        for that nickname will not be delivered anymore.
 
-   22   <deprecated command>
+        The <Client ID> is the Client ID of the sender of this command.
+
+        The nickname set to watch MUST NOT include any wildcards.
+        Note also that a nickname may match several users since
+        nicknames are not unique.  Implementations MAY set limits
+        for how many nicknames client can watch.
+
+        When normal server receives this command from client it
+        MUST send it to its router.  Router will process the command
+        and actually keeps the watch list.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with the status indication.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_BAD_NICKNAME
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_RESOURCE_LIMIT
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NICKNAME_IN_USE
 
 
    23   SILC_COMMAND_SILCOPER
@@ -2103,6 +2223,11 @@ List of all defined status types:
         The unknown Server ID MUST be provided as next argument
         in the reply.
 
+   48   SILC_STATUS_ERR_RESOURCE_LIMIT
+
+        "No more resources available".  This can mean that server cannot
+        or will not accept something due to resource limitations.
+
    49   SILC_STATUS_ERR_NO_SUCH_SERVICE
 
         "Service does not exist".  Requested service identifier is
index 818597f5762e630aabd52d2048c2c7aea09ac4ee..b433e3a5d014ae9a9090b9c3836ab55bad930589 100644 (file)
@@ -1496,11 +1496,42 @@ UTF-8 [RFC2279] encoded.
       sent currently inside this notify type in [SILC3].  The <Status
       Type> is of size of 1 byte.
 
+
+17    SILC_NOTIFY_TYPE_WATCH
+
+      Sent to indicate change in a watched user.  Client can set
+      nicknames to be watched with SILC_COMMAND_WATCH command, and
+      receive notifications when they login to network, signoff from
+      the network or their user mode is changed.  This notify type
+      is used to deliver these notifications.  The notify type is
+      sent directly to the watching client.
+
+      Max Arguments:  4
+          Arguments:  (1) <Client ID>        (2) [<nickname>]
+                      (3) <user mode>        (4) [<Notify Type>]
+
+      The <Client ID> is the user's Client ID which is being watched,
+      and the <nickname> is its nickname.  If the client just
+      changed the nickname, then <nickname> is the new nickname.
+      The <user mode> is the user's current user mode.  The <Notify
+      Type> can be same as the Notify Payload's Notify Type, and is
+      16 bit MSB first order value.  If provided it may indicate the
+      notify that occurred for the client.  If client logged in to the
+      network the <Notify Type> MUST NOT be present.
 .in 3
 
 Notify types starting from 16384 are reserved for private notify
 message types.
 
+Router server which receives SILC_NOTIFY_TYPE_SIGNOFF, 
+SILC_NOTIFY_TYPE_SERVER_SIGNOFF, SILC_NOTIFY_TYPE_KILLED,
+SILC_NOTIFY_TYPE_NICK_CHANGE and SILC_NOTIFY_TYPE_UMODE_CHANGE
+MUST chech whether someone in the local cell is watching the nickname
+the client has, and send the SILC_NOTIFY_TYPE_WATCH notify to the
+watcher, unless the client in case has the SILC_UMODE_REJECT_WATCHING
+user mode set.  If the watcher client and the client that was
+watched is same the notify SHOULD NOT be sent.
+
 
 .ti 0
 2.3.8 Error Payload
index 9c62e5839f51f726634babafac1794d167a3a8a3..72fa8aee8ec13c51b651983290ab8109294a32dc 100644 (file)
@@ -490,10 +490,11 @@ o Random number or counter - Random number to further
   possible to have 2^8 same nicknames from the same
   server IP address.
 
-o MD5 hash - MD5 hash value of the nickname is truncated
-  taking 88 bits from the start of the hash value.  This
-  hash value is used to search the user's Client ID from
-  the ID lists.
+o MD5 hash - MD5 hash value of the lowercase nickname is
+  truncated taking 88 bits from the start of the hash value.
+  This hash value is used to search the user's Client ID
+  from the ID lists.  Note that the nickname MUST be in
+  lowercase format.
 
 .in 3
 Collisions could occur when more than 2^8 clients using same nickname
@@ -1775,6 +1776,10 @@ Server MUST also distribute the information about newly registered
 client to its router (or if the server is router, to all routers in
 the SILC network).  More information about this in [SILC2].
 
+Router server MUST also check whether some client in the local cell
+is watching for the nickname this new client has, and send the 
+SILC_NOTIFY_TYPE_WATCH to the watcher.
+
 
 .ti 0
 4.2 Creating Server Connection
@@ -2123,6 +2128,11 @@ type SILC_NOTIFY_TYPE_SERVER_SIGNOFF to its primary router and to all
 local clients that are joined on the same channels with the remote 
 server's or router's clients.
 
+Router server MUST also check whether some client in the local cell
+is watching for the nickname this client has, and send the 
+SILC_NOTIFY_TYPE_WATCH to the watcher, unless the client which left
+the network has the SILC_UMODE_REJECT_WATCHING user mode set.
+
 
 .ti 0
 4.11 Detaching and Resuming a Session
index 12db4c73a2f08c3983da2085178811ae9cf9232a..9b825954f2deb2f9618be991fc39f478a84eaa33 100644 (file)
@@ -1063,6 +1063,77 @@ void silc_client_notify_by_server(SilcClient client,
     }
     break;
 
+  case SILC_NOTIFY_TYPE_WATCH:
+    {
+      /*
+       * Received notify about some client we are watching
+       */
+      SilcNotifyType notify = 0;
+
+      SILC_LOG_DEBUG(("Notify: WATCH"));
+
+      /* Get sender Client ID */
+      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+      if (!tmp)
+       goto out;
+      client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+      if (!client_id)
+       goto out;
+
+      /* Find Client entry and if not found query it */
+      client_entry = silc_client_get_client_by_id(client, conn, client_id);
+      if (!client_entry) {
+       silc_client_notify_by_server_resolve(client, conn, packet, 
+                                            SILC_ID_CLIENT, client_id);
+       goto out;
+      }
+
+      /* Get user mode */
+      tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+      if (!tmp || tmp_len != 4)
+       goto out;
+      SILC_GET32_MSB(mode, tmp);
+
+      /* Get notify type */
+      tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+      if (tmp && tmp_len != 2)
+       goto out;
+      if (tmp)
+       SILC_GET16_MSB(notify, tmp);
+
+      /* Get nickname */
+      tmp = silc_argument_get_arg_type(args, 2, NULL);
+      if (tmp) {
+       char *tmp_nick = NULL;
+
+       if (client->internal->params->nickname_parse)
+         client->internal->params->nickname_parse(client_entry->nickname,
+                                                  &tmp_nick);
+       else
+         tmp_nick = strdup(tmp);
+
+       /* If same nick, the client was new to us and has become "present"
+          to network.  Send NULL as nick to application. */
+       if (!strcmp(tmp, tmp_nick))
+         tmp = NULL;
+
+       silc_free(tmp_nick);
+      }
+
+      /* Notify application. */
+      client->internal->ops->notify(client, conn, type, client_entry,
+                                   tmp, mode, notify);
+
+      client_entry->mode = mode;
+
+      /* If nickname was changed, remove the client entry unless the
+        client is on some channel */
+      if (tmp && notify == SILC_NOTIFY_TYPE_NICK_CHANGE &&
+         !silc_hash_table_count(client_entry->channels))
+       silc_client_del_client(client, conn, client_entry);
+    }
+    break;
+
   default:
     break;
   }
index eb9cce3b0a5451d724f79b371f55c409d2956b3e..48885cce3d36de9e65a3a2da75d74fa0a9fdbc72 100644 (file)
@@ -1166,6 +1166,14 @@ SILC_CLIENT_CMD_FUNC(umode)
        mode = 0;
        mode |= SILC_UMODE_SERVER_OPERATOR;
        mode |= SILC_UMODE_ROUTER_OPERATOR;
+       mode |= SILC_UMODE_GONE;
+       mode |= SILC_UMODE_INDISPOSED;
+       mode |= SILC_UMODE_BUSY;
+       mode |= SILC_UMODE_PAGE;
+       mode |= SILC_UMODE_HYPER;
+       mode |= SILC_UMODE_ROBOT;
+       mode |= SILC_UMODE_BLOCK_PRIVMSG;
+       mode |= SILC_UMODE_REJECT_WATCHING;
       } else {
        mode = SILC_UMODE_NONE;
       }
@@ -1224,6 +1232,12 @@ SILC_CLIENT_CMD_FUNC(umode)
       else
        mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
       break;
+    case 'w':
+      if (add)
+       mode |= SILC_UMODE_REJECT_WATCHING;
+      else
+       mode &= ~SILC_UMODE_REJECT_WATCHING;
+      break;
     default:
       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
       goto out;
@@ -1590,6 +1604,8 @@ SILC_CLIENT_CMD_FUNC(cumode)
        mode |= SILC_CHANNEL_UMODE_CHANFO;
        mode |= SILC_CHANNEL_UMODE_CHANOP;
        mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+       mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+       mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
       } else {
        mode = SILC_CHANNEL_UMODE_NONE;
       }
@@ -1626,6 +1642,18 @@ SILC_CLIENT_CMD_FUNC(cumode)
       else
        mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
       break;
+    case 'u':
+      if (add)
+       mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+      else
+       mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+      break;
+    case 'r':
+      if (add)
+       mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+      else
+       mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+      break;
     default:
       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
       goto out;
@@ -2001,6 +2029,55 @@ SILC_CLIENT_CMD_FUNC(detach)
   silc_client_command_free(cmd);
 }
 
+/* Command WATCH. */
+
+SILC_CLIENT_CMD_FUNC(watch)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, idp = NULL;
+  int type = 0;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
+    goto out;
+  }
+
+  if (cmd->argc < 3) {
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
+
+  if (!strcasecmp(cmd->argv[1], "-add")) {
+    type = 2;
+  } else if (!strcasecmp(cmd->argv[1], "-del")) {
+    type = 3;
+  } else {
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH, 
+                                         ++conn->cmd_ident, 2,
+                                         1, idp->data, idp->len,
+                                         type, cmd->argv[2],
+                                         cmd->argv_lens[2]);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+  /* Notify application */
+  COMMAND(SILC_STATUS_OK);
+
+ out:
+  if (idp)
+    silc_buffer_free(idp);
+  silc_client_command_free(cmd);
+}
+
 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
 
 SILC_CLIENT_CMD_FUNC(leave)
@@ -2452,6 +2529,7 @@ void silc_client_commands_register(SilcClient client)
   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
+  SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
@@ -2486,6 +2564,7 @@ void silc_client_commands_unregister(SilcClient client)
   SILC_CLIENT_CMDU(kick, KICK, "KICK");
   SILC_CLIENT_CMDU(ban, BAN, "BAN");
   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
+  SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
   SILC_CLIENT_CMDU(users, USERS, "USERS");
index 46fb36509bc830781800e32bae61be9ad425e17f..a334352ed9c691c9959aff0f2cadc757d19aef29 100644 (file)
@@ -139,6 +139,7 @@ SILC_CLIENT_CMD_FUNC(cumode);
 SILC_CLIENT_CMD_FUNC(kick);
 SILC_CLIENT_CMD_FUNC(ban);
 SILC_CLIENT_CMD_FUNC(detach);
+SILC_CLIENT_CMD_FUNC(watch);
 SILC_CLIENT_CMD_FUNC(silcoper);
 SILC_CLIENT_CMD_FUNC(leave);
 SILC_CLIENT_CMD_FUNC(users);
index 735dcc9d9ad3c3a594c6d69e3033dd517d6af84d..2e194d9ffdc2ee1d59ef23a4836e7e605f151686 100644 (file)
@@ -1398,6 +1398,26 @@ SILC_CLIENT_CMD_REPLY_FUNC(detach)
   silc_client_command_reply_free(cmd);
 }
 
+SILC_CLIENT_CMD_REPLY_FUNC(watch)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+
+  if (cmd->error != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_status_message(cmd->error));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
+  silc_client_command_reply_free(cmd);
+}
+
 SILC_CLIENT_CMD_REPLY_FUNC(ban)
 {
   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
index 3696f748226e23a1f0d21c874ce3cc290360a7e1..9811e1c9ae5679be8739c2f50cee7b9dc559ec8f 100644 (file)
@@ -102,6 +102,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode);
 SILC_CLIENT_CMD_REPLY_FUNC(kick);
 SILC_CLIENT_CMD_REPLY_FUNC(ban);
 SILC_CLIENT_CMD_REPLY_FUNC(detach);
+SILC_CLIENT_CMD_REPLY_FUNC(watch);
 SILC_CLIENT_CMD_REPLY_FUNC(silcoper);
 SILC_CLIENT_CMD_REPLY_FUNC(leave);
 SILC_CLIENT_CMD_REPLY_FUNC(users);
index 1575a80ca11c4d208cf5e414c9ec7b2eba0f9159..e0357b723b942a252be36a867e378228e46b835a 100644 (file)
@@ -145,6 +145,7 @@ typedef unsigned char SilcCommand;
 #define SILC_COMMAND_KICK              19
 #define SILC_COMMAND_BAN               20
 #define SILC_COMMAND_DETACH            21
+#define SILC_COMMAND_WATCH             22
 #define SILC_COMMAND_SILCOPER          23
 #define SILC_COMMAND_LEAVE             24
 #define SILC_COMMAND_USERS             25
index 4205ada288dc048dac48aa56884de5d3437afcb8..5b89081351b5f8f4b072b771608a7b418a35809c 100644 (file)
@@ -289,7 +289,7 @@ typedef struct {
  *
  *      n bit ServerID IP address [bits 1-32 or bits 1-128]
  *      8 bit random number
- *     88 bit hash value from nickname
+ *     88 bit hash value from lowercase nickname
  *
  * SOURCE
  */
index a56d5b4a5241024d4741d447f209f85acda12c58..2345ef611029c9df0b94db6ae313a7874554e353 100644 (file)
 #define SILC_CHANNEL_UMODE_CHANFO          0x00000001 /* channel founder */
 #define SILC_CHANNEL_UMODE_CHANOP          0x00000002 /* channel operator */
 #define SILC_CHANNEL_UMODE_BLOCK_MESSAGES  0x00000004 /* messages blocked */
+#define SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS  0x00000008 /* Block messages
+                                                              from normal
+                                                              users */
+#define SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS 0x00000010 /* Block messages
+                                                              from robots */
 /***/
 
 /****d* silccore/Modes/SilcUserMode
@@ -91,6 +96,7 @@
 #define SILC_UMODE_ANONYMOUS         0x00000100 /* Client is anonymous */
 #define SILC_UMODE_BLOCK_PRIVMSG     0x00000200 /* Client blocks privmsgs */
 #define SILC_UMODE_DETACHED          0x00000400 /* Client is detached */
+#define SILC_UMODE_REJECT_WATCHING   0x00000800 /* Client rejects watching */
 /***/
 
 #endif
index bc6b5035f8cd52c0a1110b9f10bb7ea74a44952e..0c7fb6a89489df2d1bc702cbb1dbcc0059b26a4a 100644 (file)
@@ -80,6 +80,7 @@ typedef SilcUInt16 SilcNotifyType;
 #define SILC_NOTIFY_TYPE_UMODE_CHANGE    14 /* user mode was changed */
 #define SILC_NOTIFY_TYPE_BAN             15 /* ban list change */
 #define SILC_NOTIFY_TYPE_ERROR           16 /* error notify */
+#define SILC_NOTIFY_TYPE_WATCH           17 /* watch notify */
 /***/
 
 /* Prototypes */
index 766485defbdbaff23f04bf648323387950661c1f..bede78e2f3dd23d388175672223fa48be2c8fae7 100644 (file)
@@ -130,8 +130,8 @@ silc_private_message_get_flags(SilcPrivateMessagePayload payload);
  * SYNOPSIS
  *
  *    unsigned char *
- *    silc_private_message_get_nickname(SilcPrivateMessagePayload payload,
- *                                      SilcUInt32 *nickname_len);
+ *    silc_private_message_get_message(SilcPrivateMessagePayload payload,
+ *                                     SilcUInt32 *message_len);
  *
  * DESCRIPTION
  *
index a11e693bce5a5199e55150edd551e5979e38c905..6accc4a1a672cbb3bb981e55f9f0a6441e07e624 100644 (file)
@@ -93,6 +93,7 @@ typedef SilcUInt8 SilcStatus;
 #define SILC_STATUS_ERR_AUTH_FAILED         45
 #define SILC_STATUS_ERR_UNKNOWN_ALGORITHM   46
 #define SILC_STATUS_ERR_NO_SUCH_SERVER_ID   47
+#define SILC_STATUS_ERR_RESOURCE_LIMIT      48
 #define SILC_STATUS_ERR_NO_SUCH_SERVICE     49
 /***/
 
index ef4a9d64913e611ba3fc2019c8a685f3343afb9a..8ea683b8a41ac7c2e2f158b30099124b5add98e7 100644 (file)
    allocated already and the new list entry requires one additional memory 
    allocation.  The memory allocation and free'ing is done automatically in
    the API and does not show to the caller.
-
-   I left sorting functions out because I don't know whether we need them.
-   If needed, just copy them from silclist.h
-
 */
 
 /* SilcDList object. This is the actual SilcDList object that is used by
index d7bca802f7cd99575a29cd98797433ce5ddeb5fc..84549ad8add4fbe025937988d67e15f6c6780908 100644 (file)
@@ -136,7 +136,7 @@ silc_hash_table_find_internal(SilcHashTable ht, void *key,
 }
 
 /* Internal routine to find entry in the hash table by `key' and `context'.
-   Returns the previous entry (if exists) as well. */
+   Returns the previous entry (if exists) as well to `prev_entry'. */
 
 static inline SilcHashTableEntry *
 silc_hash_table_find_internal_context(SilcHashTable ht, void *key,
@@ -170,7 +170,8 @@ silc_hash_table_find_internal_context(SilcHashTable ht, void *key,
     }
   }
 
-  *prev_entry = prev;
+  if (prev_entry)
+    *prev_entry = prev;
   return entry;
 }
 
@@ -683,6 +684,27 @@ bool silc_hash_table_find_ext(SilcHashTable ht, void *key,
   return TRUE;
 }
 
+/* Same as silc_hash_table_find but finds with specific context. */
+
+bool silc_hash_table_find_by_context(SilcHashTable ht, void *key,
+                                    void *context, void **ret_key)
+{
+  SilcHashTableEntry *entry;
+  
+  entry = silc_hash_table_find_internal_context(ht, key, context, NULL,
+                                               ht->hash, 
+                                               ht->hash_user_context,
+                                               ht->compare,
+                                               ht->compare_user_context);
+  if (!entry || !(*entry))
+    return FALSE;
+
+  if (ret_key)
+    *ret_key = (*entry)->key;
+
+  return TRUE;
+}
+
 /* As the hash table is collision resistant it is possible to save duplicate
    keys to the hash table. This function can be used to find all keys
    and contexts from the hash table that are found using the `key'. The
index 6e04f6b75103d712d00835421f961b9ce64bd63e..228887775a990655d5a1c8dbfa0bd34a4eaeef8c 100644 (file)
@@ -324,6 +324,27 @@ bool silc_hash_table_del_by_context(SilcHashTable ht, void *key,
 bool silc_hash_table_find(SilcHashTable ht, void *key,
                          void **ret_key, void **ret_context);
 
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_find_by_context
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hash_table_find_by_context(SilcHashTable ht, void *key,
+ *                                         void *context, void **ret_key);
+ *
+ * DESCRIPTION
+ *
+ *    Finds the entry in the hash table by the provided `key' and
+ *    `context' as fast as possible.  This is handy function when there
+ *    can be multiple same keys in the hash table.  By using this function
+ *    the specific key with specific context can be found.  Return
+ *    TRUE if the entry with the key and context was found and FALSE
+ *    otherwise.  The function returns only the key to `ret_key' since
+ *    the caller already knows the context.
+ *
+ ***/
+bool silc_hash_table_find_by_context(SilcHashTable ht, void *key,
+                                    void *context, void **ret_key);
+
 /****f* silcutil/SilcHashTableAPI/silc_hash_table_find_foreach
  *
  * SYNOPSIS
index e3778bb7b2f8b78ef25112a90f46ccac7cb0a73b..f622bed5eb341807ead387dc4463b032edefb2b7 100644 (file)
@@ -94,15 +94,32 @@ char *silc_get_time()
 
 /* Converts string to capital characters. */
 
-char *silc_to_upper(char *string)
+bool silc_to_upper(const char *string, char *dest, SilcUInt32 dest_size)
 {
   int i;
-  char *ret = silc_calloc(strlen(string) + 1, sizeof(char));
+
+  if (strlen(string) > dest_size)
+    return FALSE;
 
   for (i = 0; i < strlen(string); i++)
-    ret[i] = toupper(string[i]);
+    dest[i] = toupper(string[i]);
 
-  return ret;
+  return TRUE;
+}
+
+/* Converts string to lower letter characters. */
+
+bool silc_to_lower(const char *string, char *dest, SilcUInt32 dest_size)
+{
+  int i;
+
+  if (strlen(string) > dest_size)
+    return FALSE;
+
+  for (i = 0; i < strlen(string); i++)
+    dest[i] = tolower(string[i]);
+
+  return TRUE;
 }
 
 /* Parse userfqdn string which is in user@fqdn format. */
@@ -162,7 +179,10 @@ void silc_parse_command_line(unsigned char *buffer,
 
   /* Get the command first */
   len = strcspn(cp, " ");
-  tmp = silc_to_upper((char *)cp);
+  tmp = silc_calloc(strlen(cp) + 1, sizeof(*tmp));
+  if (!tmp)
+    return;
+  silc_to_upper(cp, tmp, strlen(cp));
   (*parsed)[0] = silc_calloc(len + 1, sizeof(char));
   memcpy((*parsed)[0], tmp, len);
   silc_free(tmp);
@@ -468,22 +488,12 @@ SilcUInt32 silc_hash_id(void *key, void *user_context)
   case SILC_ID_CLIENT:
     {
       SilcClientID *id = (SilcClientID *)key;
-      SilcUInt32 g;
 
       /* The client ID is hashed by hashing the hash of the ID
         (which is a truncated MD5 hash of the nickname) so that we
         can access the entry from the cache with both Client ID but
         with just a hash from the ID as well. */
-
-      for (i = 0; i < sizeof(id->hash); i++) {
-       h = (h << 4) + id->hash[i];
-       if ((g = h & 0xf0000000)) {
-         h = h ^ (g >> 24);
-         h = h ^ g;
-       }
-      }
-
-      return h;
+      return silc_hash_client_id_hash(id->hash, NULL);
     }
     break;
   case SILC_ID_SERVER:
@@ -515,6 +525,25 @@ SilcUInt32 silc_hash_id(void *key, void *user_context)
   return h;
 }
 
+/* Hash Client ID's hash. */
+
+SilcUInt32 silc_hash_client_id_hash(void *key, void *user_context)
+{
+  int i;
+  unsigned char *hash = key;
+  SilcUInt32 h = 0, g;
+
+  for (i = 0; i < CLIENTID_HASH_LEN; i++) {
+    h = (h << 4) + hash[i];
+    if ((g = h & 0xf0000000)) {
+      h = h ^ (g >> 24);
+      h = h ^ g;
+    }
+  }
+
+  return h;
+}
+
 /* Hash binary data. The `user_context' is the data length. */
 
 SilcUInt32 silc_hash_data(void *key, void *user_context)
@@ -655,6 +684,12 @@ char *silc_client_chumode(SilcUInt32 mode)
   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
     strncat(string, "b", 1);
 
+  if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
+    strncat(string, "u", 1);
+
+  if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
+    strncat(string, "r", 1);
+
   return strdup(string);
 }
 
index ecaf5796e326315291d3134fcf1ade643fe14383..0a01978e0ea7821ba6f0321b11d3da659aa55754 100644 (file)
@@ -76,14 +76,27 @@ char *silc_get_time();
  *
  * SYNOPSIS
  *
- *    char *silc_to_upper(char *string);
+ *    bool silc_to_upper(const char *string, char *dest, SilcUInt32 dest_size);
  *
  * DESCRIPTION
  *
  *    Converts string to capital characters.
  *
  ***/
-char *silc_to_upper(char *string);
+bool silc_to_upper(const char *string, char *dest, SilcUInt32 dest_size);
+
+/****f* silcutil/SilcUtilAPI/silc_to_lower
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_to_lower(const char *string, char *dest, SilcUInt32 dest_size);
+ *
+ * DESCRIPTION
+ *
+ *    Converts string to capital characters.
+ *
+ ***/
+bool silc_to_lower(const char *string, char *dest, SilcUInt32 dest_size);
 
 /****f* silcutil/SilcUtilAPI/silc_parse_userfqdn
  *
@@ -220,6 +233,19 @@ SilcUInt32 silc_hash_ptr(void *key, void *user_context);
  ***/
 SilcUInt32 silc_hash_id(void *key, void *user_context);
 
+/****f* silcutil/SilcUtilAPI/silc_hash_client_id_hash
+ *
+ * SYNOPSIS
+ *
+ *    SilcUInt32 silc_hash_client_id_hash(void *key, void *user_context)
+ *
+ * DESCRIPTION
+ *
+ *    Hash Client ID's hash.
+ *
+ ***/
+SilcUInt32 silc_hash_client_id_hash(void *key, void *user_context);
+
 /****f* silcutil/SilcUtilAPI/silc_hash_data
  *
  * SYNOPSIS