+
+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 (!context)
+ return;
+
+ 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 :
+ (const char *)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(("Checking watcher list %s",
+ client->nickname ? client->nickname : (unsigned char *)""));
+
+ /* 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);
+
+ if (client->id)
+ 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;
+}
+
+/* Force the client indicated by `chl' to change the channel user mode
+ on channel indicated by `channel' to `forced_mode'. */
+
+bool silc_server_force_cumode_change(SilcServer server,
+ SilcSocketConnection sock,
+ SilcChannelEntry channel,
+ SilcChannelClientEntry chl,
+ SilcUInt32 forced_mode)
+{
+ SilcBuffer idp1, idp2;
+ unsigned char cumode[4];
+
+ SILC_LOG_DEBUG(("Enforcing sender to change mode"));
+
+ if (sock)
+ silc_server_send_notify_cumode(server, sock, FALSE, channel, forced_mode,
+ server->id, SILC_ID_SERVER,
+ chl->client->id, NULL);
+
+ idp1 = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+ idp2 = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
+ SILC_PUT32_MSB(forced_mode, cumode);
+ silc_server_send_notify_to_channel(server, sock, channel, FALSE,
+ SILC_NOTIFY_TYPE_CUMODE_CHANGE,
+ 3, idp1->data, idp1->len,
+ cumode, sizeof(cumode),
+ idp2->data, idp2->len);
+ silc_buffer_free(idp1);
+ silc_buffer_free(idp2);
+
+ return TRUE;
+}
+
+/* Find active socket connection by the IP address and port indicated by
+ `ip' and `port', and socket connection type of `type'. */
+
+SilcSocketConnection
+silc_server_find_socket_by_host(SilcServer server,
+ SilcSocketType type,
+ const char *ip, SilcUInt16 port)
+{
+ int i;
+
+ for (i = 0; i < server->config->param.connections_max; i++) {
+ if (!server->sockets[i])
+ continue;
+ if (!strcmp(server->sockets[i]->ip, ip) &&
+ (!port || server->sockets[i]->port == port) &&
+ server->sockets[i]->type == type)
+ return server->sockets[i];
+ }
+
+ return NULL;
+}
+
+/* This function can be used to match the invite and ban lists. */
+
+bool silc_server_inviteban_match(SilcServer server, SilcHashTable list,
+ SilcUInt8 type, void *check)
+{
+ unsigned char *tmp = NULL;
+ SilcUInt32 len = 0, t;
+ SilcHashTableList htl;
+ SilcBuffer entry, idp = NULL;
+ bool ret = FALSE;
+
+ if (type < 1 || type > 3 || !check)
+ return FALSE;
+
+ if (type == 1) {
+ tmp = strdup((char *)check);
+ if (!tmp)
+ return FALSE;
+ }
+ if (type == 2) {
+ tmp = silc_pkcs_public_key_encode(check, &len);
+ if (!tmp)
+ return FALSE;
+ }
+ if (type == 3) {
+ idp = silc_id_payload_encode(check, SILC_ID_CLIENT);
+ if (!idp)
+ return FALSE;
+ tmp = idp->data;
+ len = idp->len;
+ }
+
+ /* Compare the list */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void **)&t, (void **)&entry)) {
+ if (type == t) {
+ if (type == 1) {
+ if (silc_string_match((char *)entry, tmp)) {
+ ret = TRUE;
+ break;
+ }
+ } else if (!memcmp(entry->data, tmp, len)) {
+ ret = TRUE;
+ break;
+ }
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ if (!idp)
+ silc_free(tmp);
+ silc_buffer_free(idp);
+ return ret;
+}
+
+static void silc_server_inviteban_dummy_dest(void *key, void *context,
+ void *user_context)
+{
+ /* Nothing */
+}
+
+/* Process invite or ban information */
+
+void silc_server_inviteban_process(SilcServer server, SilcHashTable list,
+ SilcUInt8 action, SilcArgumentPayload args)
+{
+ unsigned char *tmp;
+ SilcUInt32 type, len;
+ SilcBuffer tmp2;
+ SilcHashTableList htl;
+
+ SILC_LOG_DEBUG(("Processing invite/ban for %s action",
+ action == 0x00 ? "ADD" : "DEL"));
+
+ /* Add the information to invite list */
+ if (action == 0x00) {
+ /* Traverse all arguments and add to the hash table according to
+ their type. */
+ tmp = silc_argument_get_first_arg(args, &type, &len);
+ while (tmp) {
+ if (type == 1) {
+ /* Invite string. Get the old invite string from hash table
+ and append this at the end of the existing one. */
+ char *string = NULL;
+ silc_hash_table_find(list, (void *)1,
+ NULL, (void **)&string);
+ silc_hash_table_del_ext(list, (void *)1, NULL, NULL, NULL, NULL,
+ silc_server_inviteban_dummy_dest, NULL);
+ if (!string)
+ string = silc_calloc(len + 2, sizeof(*string));
+ else
+ string = silc_realloc(string, sizeof(*string) *
+ (strlen(string) + len + 2));
+ memset(string + strlen(string), 0, len + 2);
+ if (tmp[len - 1] == ',')
+ tmp[len - 1] = '\0';
+ strncat(string, tmp, len);
+ strncat(string, ",", 1);
+
+ /* Add new invite string to invite list */
+ silc_hash_table_add(list, (void *)1, string);
+
+ } else if (type == 2) {
+ /* Public key. Check first if the public key is already on the
+ list and ignore it if it is, otherwise, add it to hash table. */
+
+ /* Check if the public key is in the list already */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) {
+ if (type == 2 && !memcmp(tmp2->data, tmp, len)) {
+ tmp = NULL;
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ /* Add new public key to invite list */
+ if (tmp) {
+ tmp2 = silc_buffer_alloc_size(len);
+ silc_buffer_put(tmp2, tmp, len);
+ silc_hash_table_add(list, (void *)2, tmp2);
+ }
+
+ } else if (type == 3) {
+ /* Client ID */
+
+ /* Check if the ID is in the list already */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) {
+ if (type == 3 && !memcmp(tmp2->data, tmp, len)) {
+ tmp = NULL;
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ /* Add new Client ID to invite list */
+ if (tmp) {
+ tmp2 = silc_buffer_alloc_size(len);
+ silc_buffer_put(tmp2, tmp, len);
+ silc_hash_table_add(list, (void *)3, tmp2);
+ }
+ }
+
+ tmp = silc_argument_get_next_arg(args, &type, &len);
+ }
+ }
+
+ /* Delete information to invite list */
+ if (action == 0x01 && list) {
+ /* Now delete the arguments from invite list */
+ tmp = silc_argument_get_first_arg(args, &type, &len);
+ while (tmp) {
+ if (type == 1) {
+ /* Invite string. Get the old string from hash table and delete
+ the requested string. */
+ char *string = NULL, *start, *end, *n;
+
+ if (silc_hash_table_find(list, (void *)1, NULL, (void **)&string)) {
+ if (!strncmp(string, tmp, strlen(string) - 1)) {
+ silc_hash_table_del(list, (void *)1);
+ string = NULL;
+ } else {
+ start = strstr(string, tmp);
+ if (start && strlen(start) >= len) {
+ end = start + len;
+ n = silc_calloc(strlen(string) - len, sizeof(*n));
+ strncat(n, string, start - string);
+ strncat(n, end + 1, ((string + strlen(string)) - end) - 1);
+ silc_hash_table_del(list, (void *)1);
+ string = n;
+ }
+ }
+
+ /* Add new invite string to invite list */
+ if (string)
+ silc_hash_table_add(list, (void *)1, string);
+ }
+
+ } else if (type == 2) {
+ /* Public key. */
+
+ /* Delete from the invite list */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) {
+ if (type == 2 && !memcmp(tmp2->data, tmp, len)) {
+ silc_hash_table_del_by_context(list, (void *)2, tmp2);
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ } else if (type == 3) {
+ /* Client ID */
+
+ /* Delete from the invite list */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) {
+ if (type == 3 && !memcmp(tmp2->data, tmp, len)) {
+ silc_hash_table_del_by_context(list, (void *)3, tmp2);
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+ }
+
+ tmp = silc_argument_get_next_arg(args, &type, &len);
+ }
+ }
+}
+
+/* Destructor for invite or ban list entrys */
+
+void silc_server_inviteban_destruct(void *key, void *context,
+ void *user_context)
+{
+ switch ((SilcUInt32)key) {
+ case 1:
+ /* Invite string */
+ silc_free(context);
+ break;
+ case 2:
+ case 3:
+ /* Public key/Channel ID SilcBuffer */
+ silc_buffer_free(context);
+ break;
+ default:
+ break;
+ }
+}