Handle command reply lists in threads.
[silc.git] / lib / silcclient / client_channel.c
1 /*
2
3   client_channel.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2004, 2006 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /************************** Channel Message Send ****************************/
26
27 /* Sends channel message to `channel'. */
28
29 SilcBool silc_client_send_channel_message(SilcClient client,
30                                           SilcClientConnection conn,
31                                           SilcChannelEntry channel,
32                                           SilcChannelPrivateKey key,
33                                           SilcMessageFlags flags,
34                                           SilcHash hash,
35                                           unsigned char *data,
36                                           SilcUInt32 data_len)
37 {
38   SilcChannelUser chu;
39   SilcBuffer buffer;
40   SilcCipher cipher;
41   SilcHmac hmac;
42   SilcBool ret;
43
44   SILC_LOG_DEBUG(("Sending channel message"));
45
46   if (!client || !conn || !channel)
47     return FALSE;
48   if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
49     return FALSE;
50   if (conn->internal->disconnected)
51     return FALSE;
52
53   chu = silc_client_on_channel(channel, conn->local_entry);
54   if (!chu) {
55     client->internal->ops->say(conn->client, conn,
56                                SILC_CLIENT_MESSAGE_AUDIT,
57                                "Cannot talk to channel: not joined");
58     return FALSE;
59   }
60
61   /* Check if it is allowed to send messages to this channel by us. */
62   if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && !chu->mode)
63     return FALSE;
64   if (channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
65       chu->mode & SILC_CHANNEL_UMODE_CHANOP &&
66       !(chu->mode & SILC_CHANNEL_UMODE_CHANFO))
67     return FALSE;
68   if (chu->mode & SILC_CHANNEL_UMODE_QUIET)
69     return FALSE;
70
71   /* Take the key to be used */
72   if (channel->internal.private_keys) {
73     if (key) {
74       /* Use key application specified */
75       cipher = key->cipher;
76       hmac = key->hmac;
77     } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
78                channel->internal.curr_key) {
79       /* Use current private key */
80       cipher = channel->internal.curr_key->cipher;
81       hmac = channel->internal.curr_key->hmac;
82     } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
83                !channel->internal.curr_key &&
84                channel->internal.private_keys) {
85       /* Use just some private key since we don't know what to use
86          and private keys are set. */
87       silc_dlist_start(channel->internal.private_keys);
88       key = silc_dlist_get(channel->internal.private_keys);
89       cipher = key->cipher;
90       hmac = key->hmac;
91
92       /* Use this key as current private key */
93       channel->internal.curr_key = key;
94     } else {
95       /* Use normal channel key generated by the server */
96       cipher = channel->internal.channel_key;
97       hmac = channel->internal.hmac;
98     }
99   } else {
100     /* Use normal channel key generated by the server */
101     cipher = channel->internal.channel_key;
102     hmac = channel->internal.hmac;
103   }
104
105   if (!cipher || !hmac) {
106     SILC_LOG_ERROR(("No cipher and HMAC for channel"));
107     return FALSE;
108   }
109
110   /* Encode the message payload. This also encrypts the message payload. */
111   buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
112                                        cipher, hmac, client->rng, NULL,
113                                        conn->private_key, hash, NULL);
114   if (!buffer) {
115     SILC_LOG_ERROR(("Error encoding channel message"));
116     return FALSE;
117   }
118
119   /* Send the channel message */
120   ret = silc_packet_send_ext(conn->stream, SILC_PACKET_CHANNEL_MESSAGE, 0,
121                              0, NULL, SILC_ID_CHANNEL, &channel->id,
122                              silc_buffer_datalen(buffer), NULL, NULL);
123
124   silc_buffer_free(buffer);
125   return ret;
126 }
127
128 /************************* Channel Message Receive **************************/
129
130 /* Client resolving callback.  Continues with the channel message processing */
131
132 static void silc_client_channel_message_resolved(SilcClient client,
133                                                  SilcClientConnection conn,
134                                                  SilcStatus status,
135                                                  SilcDList clients,
136                                                  void *context)
137 {
138   /* If no client found, ignore the channel message, a silent error */
139   if (!clients)
140     silc_fsm_next(context, silc_client_channel_message_error);
141
142   /* Continue processing the channel message packet */
143   SILC_FSM_CALL_CONTINUE(context);
144 }
145
146 /* Process received channel message */
147
148 SILC_FSM_STATE(silc_client_channel_message)
149 {
150   SilcClientConnection conn = fsm_context;
151   SilcClient client = conn->client;
152   SilcPacket packet = state_context;
153   SilcBuffer buffer = &packet->buffer;
154   SilcMessagePayload payload = NULL;
155   SilcChannelEntry channel;
156   SilcClientEntry client_entry;
157   SilcClientID remote_id;
158   SilcChannelID channel_id;
159   unsigned char *message;
160   SilcUInt32 message_len;
161   SilcChannelPrivateKey key = NULL;
162
163   SILC_LOG_DEBUG(("Received channel message"));
164
165   if (packet->dst_id_type != SILC_ID_CHANNEL) {
166     /** Invalid packet */
167     silc_fsm_next(fsm, silc_client_channel_message_error);
168     return SILC_FSM_CONTINUE;
169   }
170
171   if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
172                       &remote_id, sizeof(remote_id))) {
173     /** Invalid source ID */
174     silc_fsm_next(fsm, silc_client_channel_message_error);
175     return SILC_FSM_CONTINUE;
176   }
177
178   /* Get sender client entry */
179   client_entry = silc_client_get_client_by_id(client, conn, &remote_id);
180   if (!client_entry || !client_entry->nickname[0]) {
181     /** Resolve client info */
182     silc_client_unref_client(client, conn, client_entry);
183     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
184                                          client, conn, &remote_id, NULL,
185                                          silc_client_channel_message_resolved,
186                                          fsm));
187     /* NOT REACHED */
188   }
189
190   if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
191                       &channel_id, sizeof(channel_id))) {
192     /** Invalid destination ID */
193     silc_fsm_next(fsm, silc_client_channel_message_error);
194     return SILC_FSM_CONTINUE;
195   }
196
197   /* Find the channel */
198   channel = silc_client_get_channel_by_id(client, conn, &channel_id);
199   if (!channel) {
200     /** Unknown channel */
201     silc_fsm_next(fsm, silc_client_channel_message_error);
202     return SILC_FSM_CONTINUE;
203   }
204
205   /* Check that user is on channel */
206   if (!silc_client_on_channel(channel, client_entry)) {
207     /** User not on channel */
208     silc_fsm_next(fsm, silc_client_channel_message_error);
209     return SILC_FSM_CONTINUE;
210   }
211
212   /* If there is no channel private key then just decrypt the message
213      with the channel key. If private keys are set then just go through
214      all private keys and check what decrypts correctly. */
215   if (!channel->internal.private_keys) {
216     /* Parse the channel message payload. This also decrypts the payload */
217     payload = silc_message_payload_parse(silc_buffer_data(buffer),
218                                          silc_buffer_len(buffer), FALSE,
219                                          FALSE, channel->internal.channel_key,
220                                          channel->internal.hmac, NULL,
221                                          FALSE, NULL);
222
223     /* If decryption failed and we have just performed channel key rekey
224        we will use the old key in decryption. If that fails too then we
225        cannot do more and will drop the packet. */
226     if (!payload) {
227       SilcCipher cipher;
228       SilcHmac hmac;
229
230       if (!channel->internal.old_channel_keys ||
231           !silc_dlist_count(channel->internal.old_channel_keys))
232         goto out;
233
234       SILC_LOG_DEBUG(("Attempting to decrypt with old channel key(s)"));
235
236       silc_dlist_end(channel->internal.old_channel_keys);
237       silc_dlist_end(channel->internal.old_hmacs);
238       while ((cipher = silc_dlist_get(channel->internal.old_channel_keys))) {
239         hmac = silc_dlist_get(channel->internal.old_hmacs);
240         if (!hmac)
241           break;
242
243         payload = silc_message_payload_parse(silc_buffer_data(buffer),
244                                              silc_buffer_len(buffer),
245                                              FALSE, FALSE, cipher, hmac,
246                                              NULL, FALSE, NULL);
247         if (payload)
248           break;
249       }
250       if (!payload)
251         goto out;
252     }
253   } else {
254     /* If the private key mode is not set on the channel then try the actual
255        channel key first before trying private keys. */
256     if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
257       payload = silc_message_payload_parse(silc_buffer_data(buffer),
258                                            silc_buffer_len(buffer),
259                                            FALSE, FALSE,
260                                            channel->internal.channel_key,
261                                            channel->internal.hmac, NULL,
262                                            FALSE, NULL);
263
264     if (!payload) {
265       silc_dlist_start(channel->internal.private_keys);
266       while ((key = silc_dlist_get(channel->internal.private_keys))) {
267         /* Parse the message payload. This also decrypts the payload */
268         payload = silc_message_payload_parse(silc_buffer_data(buffer),
269                                              silc_buffer_len(buffer),
270                                              FALSE, FALSE, key->cipher,
271                                              key->hmac, NULL, FALSE, NULL);
272         if (payload)
273           break;
274       }
275       if (key == SILC_LIST_END)
276         goto out;
277     }
278   }
279
280   message = silc_message_get_data(payload, &message_len);
281
282   /* Pass the message to application */
283   client->internal->ops->channel_message(
284                              client, conn, client_entry, channel, payload,
285                              key, silc_message_get_flags(payload),
286                              message, message_len);
287
288  out:
289   silc_client_unref_client(client, conn, client_entry);
290   silc_client_unref_channel(client, conn, channel);
291   if (payload)
292     silc_message_payload_free(payload);
293   return SILC_FSM_FINISH;
294 }
295
296 /* Channel message error. */
297
298 SILC_FSM_STATE(silc_client_channel_message_error)
299 {
300   SilcPacket packet = state_context;
301   silc_packet_free(packet);
302   return SILC_FSM_FINISH;
303 }
304
305 /******************************* Channel Key ********************************/
306
307 /* Timeout callback that is called after a short period of time after the
308    new channel key has been created.  This removes the first channel key
309    in the list. */
310
311 SILC_TASK_CALLBACK(silc_client_save_channel_key_rekey)
312 {
313   SilcChannelEntry channel = (SilcChannelEntry)context;
314   SilcCipher key;
315   SilcHmac hmac;
316
317   if (channel->internal.old_channel_keys) {
318     silc_dlist_start(channel->internal.old_channel_keys);
319     key = silc_dlist_get(channel->internal.old_channel_keys);
320     if (key) {
321       silc_dlist_del(channel->internal.old_channel_keys, key);
322       silc_cipher_free(key);
323     }
324   }
325
326   if (channel->internal.old_hmacs) {
327     silc_dlist_start(channel->internal.old_hmacs);
328     hmac = silc_dlist_get(channel->internal.old_hmacs);
329     if (hmac) {
330       silc_dlist_del(channel->internal.old_hmacs, hmac);
331       silc_hmac_free(hmac);
332     }
333   }
334 }
335
336 /* Saves channel key from encoded `key_payload'. This is used when we receive
337    Channel Key Payload and when we are processing JOIN command reply. */
338
339 SilcBool silc_client_save_channel_key(SilcClient client,
340                                       SilcClientConnection conn,
341                                       SilcBuffer key_payload,
342                                       SilcChannelEntry channel)
343 {
344   unsigned char *id_string, *key, *cipher, *hmac, hash[SILC_HASH_MAXLEN];
345   SilcUInt32 tmp_len;
346   SilcChannelID id;
347   SilcChannelKeyPayload payload;
348
349   payload = silc_channel_key_payload_parse(silc_buffer_data(key_payload),
350                                            silc_buffer_len(key_payload));
351   if (!payload)
352     return FALSE;
353
354   id_string = silc_channel_key_get_id(payload, &tmp_len);
355   if (!id_string) {
356     silc_channel_key_payload_free(payload);
357     return FALSE;
358   }
359
360   if (!silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL, &id, sizeof(id))) {
361     silc_channel_key_payload_free(payload);
362     return FALSE;
363   }
364
365   /* Find channel. */
366   if (!channel) {
367     channel = silc_client_get_channel_by_id(client, conn, &id);
368     if (!channel) {
369       silc_channel_key_payload_free(payload);
370       return FALSE;
371     }
372   } else {
373     silc_client_ref_channel(client, conn, channel);
374   }
375
376   /* Save the old key for a short period of time so that we can decrypt
377      channel message even after the rekey if some client would be sending
378      messages with the old key after the rekey. */
379   if (!channel->internal.old_channel_keys)
380     channel->internal.old_channel_keys = silc_dlist_init();
381   if (!channel->internal.old_hmacs)
382     channel->internal.old_hmacs = silc_dlist_init();
383   if (channel->internal.old_channel_keys && channel->internal.old_hmacs) {
384     silc_dlist_add(channel->internal.old_channel_keys,
385                    channel->internal.channel_key);
386     silc_dlist_add(channel->internal.old_hmacs, channel->internal.hmac);
387     silc_schedule_task_add_timeout(client->schedule,
388                                    silc_client_save_channel_key_rekey,
389                                    channel, 15, 0);
390   }
391
392   /* Get channel cipher */
393   cipher = silc_channel_key_get_cipher(payload, NULL);
394   if (!silc_cipher_alloc(cipher, &channel->internal.channel_key)) {
395     client->internal->ops->say(
396                            conn->client, conn,
397                            SILC_CLIENT_MESSAGE_AUDIT,
398                            "Cannot talk to channel: unsupported cipher %s",
399                            cipher);
400     silc_client_unref_channel(client, conn, channel);
401     silc_channel_key_payload_free(payload);
402     return FALSE;
403   }
404
405   /* Set the cipher key */
406   key = silc_channel_key_get_key(payload, &tmp_len);
407   silc_cipher_set_key(channel->internal.channel_key, key, tmp_len * 8);
408
409   /* Get channel HMAC */
410   hmac = (channel->internal.hmac ?
411           (char *)silc_hmac_get_name(channel->internal.hmac) :
412           SILC_DEFAULT_HMAC);
413   if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
414     client->internal->ops->say(
415                            conn->client, conn,
416                            SILC_CLIENT_MESSAGE_AUDIT,
417                            "Cannot talk to channel: unsupported HMAC %s",
418                            hmac);
419     silc_client_unref_channel(client, conn, channel);
420     silc_channel_key_payload_free(payload);
421     return FALSE;
422   }
423
424   /* Set HMAC key */
425   silc_hash_make(silc_hmac_get_hash(channel->internal.hmac), key,
426                  tmp_len, hash);
427   silc_hmac_set_key(channel->internal.hmac, hash,
428                     silc_hash_len(silc_hmac_get_hash(channel->internal.hmac)));
429   memset(hash, 0, sizeof(hash));
430
431   silc_client_unref_channel(client, conn, channel);
432
433   return TRUE;
434 }
435
436 /* Received channel key packet.  The key will replace old channel key. */
437
438 SILC_FSM_STATE(silc_client_channel_key)
439 {
440   SilcClientConnection conn = fsm_context;
441   SilcClient client = conn->client;
442   SilcPacket packet = state_context;
443
444   SILC_LOG_DEBUG(("Received channel key"));
445
446   /* Save the key */
447   silc_client_save_channel_key(client, conn, &packet->buffer, NULL);
448   silc_packet_free(packet);
449
450   return SILC_FSM_FINISH;
451 }
452
453 /**************************** Channel Private Key ***************************/
454
455 /* Add new channel private key */
456
457 SilcBool silc_client_add_channel_private_key(SilcClient client,
458                                              SilcClientConnection conn,
459                                              SilcChannelEntry channel,
460                                              const char *name,
461                                              char *cipher,
462                                              char *hmac,
463                                              unsigned char *key,
464                                              SilcUInt32 key_len,
465                                              SilcChannelPrivateKey *ret_key)
466 {
467   SilcChannelPrivateKey entry;
468   unsigned char hash[SILC_HASH_MAXLEN];
469   SilcSKEKeyMaterial keymat;
470
471   if (!client || !conn || !channel)
472     return FALSE;
473
474   if (!cipher)
475     cipher = SILC_DEFAULT_CIPHER;
476   if (!hmac)
477     hmac = SILC_DEFAULT_HMAC;
478
479   if (!silc_cipher_is_supported(cipher))
480     return FALSE;
481   if (!silc_hmac_is_supported(hmac))
482     return FALSE;
483
484   if (!channel->internal.private_keys) {
485     channel->internal.private_keys = silc_dlist_init();
486     if (!channel->internal.private_keys)
487       return FALSE;
488   }
489
490   /* Produce the key material */
491   keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
492                                               conn->internal->sha1hash);
493   if (!keymat)
494     return FALSE;
495
496   /* Save the key */
497   entry = silc_calloc(1, sizeof(*entry));
498   if (!entry) {
499     silc_ske_free_key_material(keymat);
500     return FALSE;
501   }
502   entry->name = name ? strdup(name) : NULL;
503
504   /* Allocate the cipher and set the key*/
505   if (!silc_cipher_alloc(cipher, &entry->cipher)) {
506     silc_free(entry);
507     silc_free(entry->name);
508     silc_ske_free_key_material(keymat);
509     return FALSE;
510   }
511   silc_cipher_set_key(entry->cipher, keymat->send_enc_key,
512                       keymat->enc_key_len);
513
514   /* Generate HMAC key from the channel key data and set it */
515   if (!silc_hmac_alloc(hmac, NULL, &entry->hmac)) {
516     silc_free(entry);
517     silc_free(entry->name);
518     silc_cipher_free(entry->cipher);
519     silc_ske_free_key_material(keymat);
520     return FALSE;
521   }
522   silc_hash_make(silc_hmac_get_hash(entry->hmac), keymat->send_enc_key,
523                  keymat->enc_key_len / 8, hash);
524   silc_hmac_set_key(entry->hmac, hash,
525                     silc_hash_len(silc_hmac_get_hash(entry->hmac)));
526   memset(hash, 0, sizeof(hash));
527
528   /* Add to the private keys list */
529   silc_dlist_add(channel->internal.private_keys, entry);
530
531   if (!channel->internal.curr_key)
532     channel->internal.curr_key = entry;
533
534   /* Free the key material */
535   silc_ske_free_key_material(keymat);
536
537   if (ret_key)
538     *ret_key = entry;
539
540   return TRUE;
541 }
542
543 /* Removes all private keys from the `channel'. The old channel key is used
544    after calling this to protect the channel messages. Returns FALSE on
545    on error, TRUE otherwise. */
546
547 SilcBool silc_client_del_channel_private_keys(SilcClient client,
548                                               SilcClientConnection conn,
549                                               SilcChannelEntry channel)
550 {
551   SilcChannelPrivateKey entry;
552
553   if (!client || !conn || !channel)
554     return FALSE;
555
556   if (!channel->internal.private_keys)
557     return FALSE;
558
559   silc_dlist_start(channel->internal.private_keys);
560   while ((entry = silc_dlist_get(channel->internal.private_keys))) {
561     silc_dlist_del(channel->internal.private_keys, entry);
562     silc_free(entry->name);
563     silc_cipher_free(entry->cipher);
564     silc_hmac_free(entry->hmac);
565     silc_free(entry);
566   }
567
568   channel->internal.curr_key = NULL;
569
570   silc_dlist_uninit(channel->internal.private_keys);
571   channel->internal.private_keys = NULL;
572
573   return TRUE;
574 }
575
576 /* Removes and frees private key `key' from the channel `channel'. The `key'
577    is retrieved by calling the function silc_client_list_channel_private_keys.
578    The key is not used after this. If the key was last private key then the
579    old channel key is used hereafter to protect the channel messages. This
580    returns FALSE on error, TRUE otherwise. */
581
582 SilcBool silc_client_del_channel_private_key(SilcClient client,
583                                              SilcClientConnection conn,
584                                              SilcChannelEntry channel,
585                                              SilcChannelPrivateKey key)
586 {
587   SilcChannelPrivateKey entry;
588
589   if (!client || !conn || !channel)
590     return FALSE;
591
592   if (!channel->internal.private_keys)
593     return FALSE;
594
595   silc_dlist_start(channel->internal.private_keys);
596   while ((entry = silc_dlist_get(channel->internal.private_keys))) {
597     if (entry != key)
598       continue;
599
600     if (channel->internal.curr_key == entry)
601       channel->internal.curr_key = NULL;
602
603     silc_dlist_del(channel->internal.private_keys, entry);
604     silc_free(entry->name);
605     silc_cipher_free(entry->cipher);
606     silc_hmac_free(entry->hmac);
607     silc_free(entry);
608
609     if (silc_dlist_count(channel->internal.private_keys) == 0) {
610       silc_dlist_uninit(channel->internal.private_keys);
611       channel->internal.private_keys = NULL;
612     }
613
614     return TRUE;
615   }
616
617   return FALSE;
618 }
619
620 /* Returns array (pointers) of private keys associated to the `channel'.
621    The caller must free the array by calling the function
622    silc_client_free_channel_private_keys. The pointers in the array may be
623    used to delete the specific key by giving the pointer as argument to the
624    function silc_client_del_channel_private_key. */
625
626 SilcDList silc_client_list_channel_private_keys(SilcClient client,
627                                                 SilcClientConnection conn,
628                                                 SilcChannelEntry channel)
629 {
630   SilcChannelPrivateKey entry;
631   SilcDList list;
632
633   if (!client || !conn || !channel)
634     return FALSE;
635
636   if (!channel->internal.private_keys)
637     return NULL;
638
639   list = silc_dlist_init();
640   if (!list)
641     return NULL;
642
643   silc_dlist_start(channel->internal.private_keys);
644   while ((entry = silc_dlist_get(channel->internal.private_keys)))
645     silc_dlist_add(list, entry);
646
647   return list;
648 }
649
650 /* Sets the `key' to be used as current channel private key on the
651    `channel'.  Packet sent after calling this function will be secured
652    with `key'. */
653
654 void silc_client_current_channel_private_key(SilcClient client,
655                                              SilcClientConnection conn,
656                                              SilcChannelEntry channel,
657                                              SilcChannelPrivateKey key)
658 {
659   if (!channel)
660     return;
661   channel->internal.curr_key = key;
662 }
663
664 /***************************** Utility routines *****************************/
665
666 /* Returns the SilcChannelUser entry if the `client_entry' is joined on the
667    channel indicated by the `channel'. NULL if client is not joined on
668    the channel. */
669
670 SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
671                                        SilcClientEntry client_entry)
672 {
673   SilcChannelUser chu;
674
675   if (silc_hash_table_find(channel->user_list, client_entry, NULL,
676                            (void *)&chu))
677     return chu;
678
679   return NULL;
680 }
681
682 /* Adds client to channel.  Returns TRUE if user was added or is already
683    added to the channel, FALSE on error. */
684
685 SilcBool silc_client_add_to_channel(SilcClient client,
686                                     SilcClientConnection conn,
687                                     SilcChannelEntry channel,
688                                     SilcClientEntry client_entry,
689                                     SilcUInt32 cumode)
690 {
691   SilcChannelUser chu;
692
693   if (silc_client_on_channel(channel, client_entry))
694     return TRUE;
695
696   chu = silc_calloc(1, sizeof(*chu));
697   if (!chu)
698     return FALSE;
699
700   chu->client = client_entry;
701   chu->channel = channel;
702   chu->mode = cumode;
703
704   silc_client_ref_client(client, conn, client_entry);
705   silc_client_ref_channel(client, conn, channel);
706
707   silc_hash_table_add(channel->user_list, client_entry, chu);
708   silc_hash_table_add(client_entry->channels, channel, chu);
709
710   return TRUE;
711 }
712
713 /* Removes client from a channel */
714
715 SilcBool silc_client_remove_from_channel(SilcClient client,
716                                          SilcClientConnection conn,
717                                          SilcChannelEntry channel,
718                                          SilcClientEntry client_entry)
719 {
720   SilcChannelUser chu;
721
722   chu = silc_client_on_channel(channel, client_entry);
723   if (!chu)
724     return FALSE;
725
726   silc_hash_table_del(chu->client->channels, chu->channel);
727   silc_hash_table_del(chu->channel->user_list, chu->client);
728   silc_free(chu);
729
730   silc_client_unref_client(client, conn, client_entry);
731   silc_client_unref_channel(client, conn, channel);
732
733   return TRUE;
734 }
735
736 /* Removes a client entry from all channels it has joined. */
737
738 void silc_client_remove_from_channels(SilcClient client,
739                                       SilcClientConnection conn,
740                                       SilcClientEntry client_entry)
741 {
742   SilcHashTableList htl;
743   SilcChannelUser chu;
744
745   silc_hash_table_list(client_entry->channels, &htl);
746   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
747     silc_hash_table_del(chu->client->channels, chu->channel);
748     silc_hash_table_del(chu->channel->user_list, chu->client);
749     silc_client_unref_client(client, conn, chu->client);
750     silc_client_unref_channel(client, conn, chu->channel);
751     silc_free(chu);
752   }
753
754   silc_hash_table_list_reset(&htl);
755 }
756
757 /* Empties channel from users. */
758
759 void silc_client_empty_channel(SilcClient client,
760                                SilcClientConnection conn,
761                                SilcChannelEntry channel)
762 {
763   SilcHashTableList htl;
764   SilcChannelUser chu;
765
766   silc_hash_table_list(channel->user_list, &htl);
767   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
768     silc_hash_table_del(chu->client->channels, chu->channel);
769     silc_hash_table_del(chu->channel->user_list, chu->client);
770     silc_client_unref_client(client, conn, chu->client);
771     silc_client_unref_channel(client, conn, chu->channel);
772     silc_free(chu);
773   }
774   silc_hash_table_list_reset(&htl);
775 }