From: Pekka Riikonen Date: Fri, 19 Apr 2002 15:17:04 +0000 (+0000) Subject: Watcher list support added. X-Git-Tag: silc.toolkit.0.9~37 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=da3a876c26b6d697ee6446ad81a8edfff1828cab Watcher list support added. --- diff --git a/CHANGES b/CHANGES index 5f7ed21c..da7eafb5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,37 @@ +Fri Apr 19 17:35:15 EEST 2002 Pekka Riikonen + + * 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 * Added service support to SILC protocol. Added new command diff --git a/TODO b/TODO index 314939a0..16393d8e 100644 --- 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 and the Attribute Payload to the core library, client and server. diff --git a/apps/irssi/docs/help/in/cumode.in b/apps/irssi/docs/help/in/cumode.in index 03d24965..84d0e208 100644 --- a/apps/irssi/docs/help/in/cumode.in +++ b/apps/irssi/docs/help/in/cumode.in @@ -39,5 +39,21 @@ are available: to the client. This mode can be used to block unwanted messages if desired. + u [@] + + 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 [@] + + 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 index 00000000..387bc2d8 --- /dev/null +++ b/apps/irssi/docs/help/in/detach.in @@ -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 diff --git a/apps/irssi/docs/help/in/umode.in b/apps/irssi/docs/help/in/umode.in index 757fcfc9..65de4ac4 100644 --- a/apps/irssi/docs/help/in/umode.in +++ b/apps/irssi/docs/help/in/umode.in @@ -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 index 00000000..962b3564 --- /dev/null +++ b/apps/irssi/docs/help/in/watch.in @@ -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 diff --git a/apps/irssi/src/fe-common/silc/module-formats.c b/apps/irssi/src/fe-common/silc/module-formats.c index a8861329..fc1d9553 100644 --- a/apps/irssi/src/fe-common/silc/module-formats.c +++ b/apps/irssi/src/fe-common/silc/module-formats.c @@ -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 }, diff --git a/apps/irssi/src/fe-common/silc/module-formats.h b/apps/irssi/src/fe-common/silc/module-formats.h index 42422e4b..b3c613eb 100644 --- a/apps/irssi/src/fe-common/silc/module-formats.h +++ b/apps/irssi/src/fe-common/silc/module-formats.h @@ -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, diff --git a/apps/irssi/src/silc/core/client_ops.c b/apps/irssi/src/silc/core/client_ops.c index 6df6da37..dc6f358d 100644 --- a/apps/irssi/src/silc/core/client_ops.c +++ b/apps/irssi/src/silc/core/client_ops.c @@ -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); diff --git a/apps/irssi/src/silc/core/silc-servers.c b/apps/irssi/src/silc/core/silc-servers.c index 835e20bc..d258c4c7 100644 --- a/apps/irssi/src/silc/core/silc-servers.c +++ b/apps/irssi/src/silc/core/silc-servers.c @@ -349,6 +349,7 @@ char *silc_server_get_channels(SILC_SERVER_REC *server) /* SYNTAX: FILE */ /* SYNTAX: JOIN [] [-cipher ] [-hmac ] [-founder <-pubkey|passwd>] */ /* SYNTAX: DETACH */ +/* SYNTAX: WATCH [<-add | -del> ] */ 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, diff --git a/apps/silcd/command.c b/apps/silcd/command.c index 06e5b96d..25df5edd 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -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; diff --git a/apps/silcd/command.h b/apps/silcd/command.h index 229a3759..464b261b 100644 --- a/apps/silcd/command.h +++ b/apps/silcd/command.h @@ -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); diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index cd93edc7..eb56f929 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -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); +} diff --git a/apps/silcd/command_reply.h b/apps/silcd/command_reply.h index 9156e298..7a06fd6e 100644 --- a/apps/silcd/command_reply.h +++ b/apps/silcd/command_reply.h @@ -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 diff --git a/apps/silcd/idlist.c b/apps/silcd/idlist.c index ab8f7d53..211d59eb 100644 --- a/apps/silcd/idlist.c +++ b/apps/silcd/idlist.c @@ -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; diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index 1d92d4c3..c9fa2fa7 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -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); diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index 428c1f7f..6fcbf601 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -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; diff --git a/apps/silcd/packet_send.c b/apps/silcd/packet_send.c index 9272547b..85e7695d 100644 --- a/apps/silcd/packet_send.c +++ b/apps/silcd/packet_send.c @@ -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, diff --git a/apps/silcd/packet_send.h b/apps/silcd/packet_send.h index 1434ef8a..2c322e20 100644 --- a/apps/silcd/packet_send.h +++ b/apps/silcd/packet_send.h @@ -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, diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 25c719e7..09c54b7d 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -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--; diff --git a/apps/silcd/server_internal.h b/apps/silcd/server_internal.h index 7a6a9253..ea703791 100644 --- a/apps/silcd/server_internal.h +++ b/apps/silcd/server_internal.h @@ -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 diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c index 2fe705f3..b24d07ba 100644 --- a/apps/silcd/server_util.c +++ b/apps/silcd/server_util.c @@ -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; +} diff --git a/apps/silcd/server_util.h b/apps/silcd/server_util.h index 5a531037..6b421f23 100644 --- a/apps/silcd/server_util.h +++ b/apps/silcd/server_util.h @@ -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 */ diff --git a/apps/silcd/serverid.c b/apps/silcd/serverid.c index fdb0cd9d..d9e39bb5 100644 --- a/apps/silcd/serverid.c +++ b/apps/silcd/serverid.c @@ -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); diff --git a/doc/draft-riikonen-silc-commands-03.nroff b/doc/draft-riikonen-silc-commands-03.nroff index 525426a1..470aad30 100644 --- a/doc/draft-riikonen-silc-commands-03.nroff +++ b/doc/draft-riikonen-silc-commands-03.nroff @@ -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 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) + + 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) (2) [] + (3) [] - 21 + This command is used to set up a watch for + 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 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 + The 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) + + 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 diff --git a/doc/draft-riikonen-silc-pp-05.nroff b/doc/draft-riikonen-silc-pp-05.nroff index 818597f5..b433e3a5 100644 --- a/doc/draft-riikonen-silc-pp-05.nroff +++ b/doc/draft-riikonen-silc-pp-05.nroff @@ -1496,11 +1496,42 @@ UTF-8 [RFC2279] encoded. sent currently inside this notify type in [SILC3]. The 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) (2) [] + (3) (4) [] + + The is the user's Client ID which is being watched, + and the is its nickname. If the client just + changed the nickname, then is the new nickname. + The is the user's current user mode. The 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 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 diff --git a/doc/draft-riikonen-silc-spec-05.nroff b/doc/draft-riikonen-silc-spec-05.nroff index 9c62e583..72fa8aee 100644 --- a/doc/draft-riikonen-silc-spec-05.nroff +++ b/doc/draft-riikonen-silc-spec-05.nroff @@ -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 diff --git a/lib/silcclient/client_notify.c b/lib/silcclient/client_notify.c index 12db4c73..9b825954 100644 --- a/lib/silcclient/client_notify.c +++ b/lib/silcclient/client_notify.c @@ -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; } diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index eb9cce3b..48885cce 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -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"); diff --git a/lib/silcclient/command.h b/lib/silcclient/command.h index 46fb3650..a334352e 100644 --- a/lib/silcclient/command.h +++ b/lib/silcclient/command.h @@ -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); diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index 735dcc9d..2e194d9f 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -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; diff --git a/lib/silcclient/command_reply.h b/lib/silcclient/command_reply.h index 3696f748..9811e1c9 100644 --- a/lib/silcclient/command_reply.h +++ b/lib/silcclient/command_reply.h @@ -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); diff --git a/lib/silccore/silccommand.h b/lib/silccore/silccommand.h index 1575a80c..e0357b72 100644 --- a/lib/silccore/silccommand.h +++ b/lib/silccore/silccommand.h @@ -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 diff --git a/lib/silccore/silcid.h b/lib/silccore/silcid.h index 4205ada2..5b890813 100644 --- a/lib/silccore/silcid.h +++ b/lib/silccore/silcid.h @@ -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 */ diff --git a/lib/silccore/silcmode.h b/lib/silccore/silcmode.h index a56d5b4a..2345ef61 100644 --- a/lib/silccore/silcmode.h +++ b/lib/silccore/silcmode.h @@ -68,6 +68,11 @@ #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 diff --git a/lib/silccore/silcnotify.h b/lib/silccore/silcnotify.h index bc6b5035..0c7fb6a8 100644 --- a/lib/silccore/silcnotify.h +++ b/lib/silccore/silcnotify.h @@ -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 */ diff --git a/lib/silccore/silcprivate.h b/lib/silccore/silcprivate.h index 766485de..bede78e2 100644 --- a/lib/silccore/silcprivate.h +++ b/lib/silccore/silcprivate.h @@ -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 * diff --git a/lib/silccore/silcstatus.h b/lib/silccore/silcstatus.h index a11e693b..6accc4a1 100644 --- a/lib/silccore/silcstatus.h +++ b/lib/silccore/silcstatus.h @@ -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 /***/ diff --git a/lib/silcutil/silcdlist.h b/lib/silcutil/silcdlist.h index ef4a9d64..8ea683b8 100644 --- a/lib/silcutil/silcdlist.h +++ b/lib/silcutil/silcdlist.h @@ -36,10 +36,6 @@ 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 diff --git a/lib/silcutil/silchashtable.c b/lib/silcutil/silchashtable.c index d7bca802..84549ad8 100644 --- a/lib/silcutil/silchashtable.c +++ b/lib/silcutil/silchashtable.c @@ -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 diff --git a/lib/silcutil/silchashtable.h b/lib/silcutil/silchashtable.h index 6e04f6b7..22888777 100644 --- a/lib/silcutil/silchashtable.h +++ b/lib/silcutil/silchashtable.h @@ -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 diff --git a/lib/silcutil/silcutil.c b/lib/silcutil/silcutil.c index e3778bb7..f622bed5 100644 --- a/lib/silcutil/silcutil.c +++ b/lib/silcutil/silcutil.c @@ -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); } diff --git a/lib/silcutil/silcutil.h b/lib/silcutil/silcutil.h index ecaf5796..0a01978e 100644 --- a/lib/silcutil/silcutil.h +++ b/lib/silcutil/silcutil.h @@ -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