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