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