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