5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 Pekka Riikonen
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.
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.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Channel Message Send ****************************/
27 /* Sends channel message to `channel'. */
29 SilcBool silc_client_send_channel_message(SilcClient client,
30 SilcClientConnection conn,
31 SilcChannelEntry channel,
32 SilcChannelPrivateKey key,
33 SilcMessageFlags flags,
45 SILC_LOG_DEBUG(("Sending channel message"));
47 if (silc_unlikely(!client || !conn || !channel))
49 if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
51 if (silc_unlikely(conn->internal->disconnected))
54 chu = silc_client_on_channel(channel, conn->local_entry);
55 if (silc_unlikely(!chu)) {
56 client->internal->ops->say(conn->client, conn,
57 SILC_CLIENT_MESSAGE_AUDIT,
58 "Cannot talk to channel: not joined");
62 /* Check if it is allowed to send messages to this channel by us. */
63 if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS &&
66 if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
67 chu->mode & SILC_CHANNEL_UMODE_CHANOP &&
68 !(chu->mode & SILC_CHANNEL_UMODE_CHANFO)))
70 if (silc_unlikely(chu->mode & SILC_CHANNEL_UMODE_QUIET))
73 /* Take the key to be used */
74 if (channel->internal.private_keys) {
76 /* Use key application specified */
79 } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
80 channel->internal.curr_key) {
81 /* Use current private key */
82 cipher = channel->internal.curr_key->cipher;
83 hmac = channel->internal.curr_key->hmac;
84 } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
85 !channel->internal.curr_key &&
86 channel->internal.private_keys) {
87 /* Use just some private key since we don't know what to use
88 and private keys are set. */
89 silc_dlist_start(channel->internal.private_keys);
90 key = silc_dlist_get(channel->internal.private_keys);
94 /* Use this key as current private key */
95 channel->internal.curr_key = key;
97 /* Use normal channel key generated by the server */
98 cipher = channel->internal.send_key;
99 hmac = channel->internal.hmac;
102 /* Use normal channel key generated by the server */
103 cipher = channel->internal.send_key;
104 hmac = channel->internal.hmac;
107 if (silc_unlikely(!cipher || !hmac)) {
108 SILC_LOG_ERROR(("No cipher and HMAC for channel"));
112 /* Encode the message payload. This also encrypts the message payload. */
113 sid.type = SILC_ID_CLIENT;
114 sid.u.client_id = chu->client->id;
115 rid.type = SILC_ID_CHANNEL;
116 rid.u.channel_id = chu->channel->id;
117 buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
118 cipher, hmac, client->rng, NULL,
119 conn->private_key, hash, &sid, &rid,
121 if (silc_unlikely(!buffer)) {
122 SILC_LOG_ERROR(("Error encoding channel message"));
126 /* Send the channel message */
127 ret = silc_packet_send_ext(conn->stream, SILC_PACKET_CHANNEL_MESSAGE, 0,
128 0, NULL, SILC_ID_CHANNEL, &channel->id,
129 silc_buffer_datalen(buffer), NULL, NULL);
131 silc_buffer_free(buffer);
135 /************************* Channel Message Receive **************************/
137 /* Client resolving callback. Continues with the channel message processing */
139 static void silc_client_channel_message_resolved(SilcClient client,
140 SilcClientConnection conn,
145 /* If no client found, ignore the channel message, a silent error */
147 silc_fsm_next(context, silc_client_channel_message_error);
149 /* Continue processing the channel message packet */
150 SILC_FSM_CALL_CONTINUE(context);
153 /* Process received channel message */
155 SILC_FSM_STATE(silc_client_channel_message)
157 SilcClientConnection conn = fsm_context;
158 SilcClient client = conn->client;
159 SilcPacket packet = state_context;
160 SilcBuffer buffer = &packet->buffer;
161 SilcMessagePayload payload = NULL;
162 SilcChannelEntry channel;
163 SilcClientEntry client_entry;
164 SilcClientID remote_id;
165 SilcChannelID channel_id;
166 unsigned char *message;
167 SilcUInt32 message_len;
168 SilcChannelPrivateKey key = NULL;
170 SILC_LOG_DEBUG(("Received channel message"));
172 SILC_LOG_HEXDUMP(("Channel message"), silc_buffer_data(buffer),
173 silc_buffer_len(buffer));
175 if (silc_unlikely(packet->dst_id_type != SILC_ID_CHANNEL)) {
176 /** Invalid packet */
177 silc_fsm_next(fsm, silc_client_channel_message_error);
178 return SILC_FSM_CONTINUE;
181 if (silc_unlikely(!silc_id_str2id(packet->src_id,
182 packet->src_id_len, SILC_ID_CLIENT,
183 &remote_id, sizeof(remote_id)))) {
184 /** Invalid source ID */
185 silc_fsm_next(fsm, silc_client_channel_message_error);
186 return SILC_FSM_CONTINUE;
189 /* Get sender client entry */
190 client_entry = silc_client_get_client_by_id(client, conn, &remote_id);
191 if (!client_entry || !client_entry->internal.valid) {
192 /** Resolve client info */
193 silc_client_unref_client(client, conn, client_entry);
194 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
195 client, conn, &remote_id, NULL,
196 silc_client_channel_message_resolved,
201 if (silc_unlikely(!silc_id_str2id(packet->dst_id, packet->dst_id_len,
202 SILC_ID_CHANNEL, &channel_id,
203 sizeof(channel_id)))) {
204 /** Invalid destination ID */
205 silc_fsm_next(fsm, silc_client_channel_message_error);
206 return SILC_FSM_CONTINUE;
209 /* Find the channel */
210 channel = silc_client_get_channel_by_id(client, conn, &channel_id);
211 if (silc_unlikely(!channel)) {
212 /** Unknown channel */
213 silc_fsm_next(fsm, silc_client_channel_message_error);
214 return SILC_FSM_CONTINUE;
217 /* Check that user is on channel */
218 if (silc_unlikely(!silc_client_on_channel(channel, client_entry))) {
219 /** User not on channel */
220 SILC_LOG_WARNING(("Message from user not on channel, client or "
222 silc_fsm_next(fsm, silc_client_channel_message_error);
223 return SILC_FSM_CONTINUE;
226 /* If there is no channel private key then just decrypt the message
227 with the channel key. If private keys are set then just go through
228 all private keys and check what decrypts correctly. */
229 if (!channel->internal.private_keys) {
230 /* Parse the channel message payload. This also decrypts the payload */
231 payload = silc_message_payload_parse(silc_buffer_data(buffer),
232 silc_buffer_len(buffer), FALSE,
233 FALSE, channel->internal.receive_key,
234 channel->internal.hmac,
235 packet->src_id, packet->src_id_len,
236 packet->dst_id, packet->dst_id_len,
239 /* If decryption failed and we have just performed channel key rekey
240 we will use the old key in decryption. If that fails too then we
241 cannot do more and will drop the packet. */
242 if (silc_unlikely(!payload)) {
246 if (!channel->internal.old_channel_keys ||
247 !silc_dlist_count(channel->internal.old_channel_keys))
250 SILC_LOG_DEBUG(("Attempting to decrypt with old channel key(s)"));
252 silc_dlist_end(channel->internal.old_channel_keys);
253 silc_dlist_end(channel->internal.old_hmacs);
254 while ((cipher = silc_dlist_get(channel->internal.old_channel_keys))) {
255 hmac = silc_dlist_get(channel->internal.old_hmacs);
259 payload = silc_message_payload_parse(silc_buffer_data(buffer),
260 silc_buffer_len(buffer),
261 FALSE, FALSE, cipher, hmac,
274 /* If the private key mode is not set on the channel then try the actual
275 channel key first before trying private keys. */
276 if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
277 payload = silc_message_payload_parse(silc_buffer_data(buffer),
278 silc_buffer_len(buffer),
280 channel->internal.receive_key,
281 channel->internal.hmac,
289 silc_dlist_start(channel->internal.private_keys);
290 while ((key = silc_dlist_get(channel->internal.private_keys))) {
291 /* Parse the message payload. This also decrypts the payload */
292 payload = silc_message_payload_parse(silc_buffer_data(buffer),
293 silc_buffer_len(buffer),
294 FALSE, FALSE, key->cipher,
295 key->hmac, packet->src_id,
303 if (key == SILC_LIST_END)
308 message = silc_message_get_data(payload, &message_len);
310 /* Pass the message to application */
311 client->internal->ops->channel_message(
312 client, conn, client_entry, channel, payload,
313 key, silc_message_get_flags(payload),
314 message, message_len);
317 silc_client_unref_client(client, conn, client_entry);
318 silc_client_unref_channel(client, conn, channel);
320 silc_message_payload_free(payload);
321 return SILC_FSM_FINISH;
324 /* Channel message error. */
326 SILC_FSM_STATE(silc_client_channel_message_error)
328 SilcPacket packet = state_context;
329 silc_packet_free(packet);
330 return SILC_FSM_FINISH;
333 /******************************* Channel Key ********************************/
335 /* Timeout callback that is called after a short period of time after the
336 new channel key has been created. This removes the first channel key
339 SILC_TASK_CALLBACK(silc_client_save_channel_key_rekey)
341 SilcChannelEntry channel = (SilcChannelEntry)context;
345 if (channel->internal.old_channel_keys) {
346 silc_dlist_start(channel->internal.old_channel_keys);
347 key = silc_dlist_get(channel->internal.old_channel_keys);
349 silc_dlist_del(channel->internal.old_channel_keys, key);
350 silc_cipher_free(key);
354 if (channel->internal.old_hmacs) {
355 silc_dlist_start(channel->internal.old_hmacs);
356 hmac = silc_dlist_get(channel->internal.old_hmacs);
358 silc_dlist_del(channel->internal.old_hmacs, hmac);
359 silc_hmac_free(hmac);
364 /* Saves channel key from encoded `key_payload'. This is used when we receive
365 Channel Key Payload and when we are processing JOIN command reply. */
367 SilcBool silc_client_save_channel_key(SilcClient client,
368 SilcClientConnection conn,
369 SilcBuffer key_payload,
370 SilcChannelEntry channel)
372 unsigned char *id_string, *key, *cipher, *hmac, hash[SILC_HASH_MAXLEN];
375 SilcChannelKeyPayload payload;
377 SILC_LOG_DEBUG(("New channel key"));
379 payload = silc_channel_key_payload_parse(silc_buffer_data(key_payload),
380 silc_buffer_len(key_payload));
384 id_string = silc_channel_key_get_id(payload, &tmp_len);
386 silc_channel_key_payload_free(payload);
390 if (!silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL, &id, sizeof(id))) {
391 silc_channel_key_payload_free(payload);
397 channel = silc_client_get_channel_by_id(client, conn, &id);
399 SILC_LOG_DEBUG(("Key for unknown channel"));
400 silc_channel_key_payload_free(payload);
404 silc_client_ref_channel(client, conn, channel);
407 /* Save the old key for a short period of time so that we can decrypt
408 channel message even after the rekey if some client would be sending
409 messages with the old key after the rekey. */
410 if (!channel->internal.old_channel_keys)
411 channel->internal.old_channel_keys = silc_dlist_init();
412 if (!channel->internal.old_hmacs)
413 channel->internal.old_hmacs = silc_dlist_init();
414 if (channel->internal.old_channel_keys && channel->internal.old_hmacs) {
415 silc_dlist_add(channel->internal.old_channel_keys,
416 channel->internal.receive_key);
417 silc_dlist_add(channel->internal.old_hmacs, channel->internal.hmac);
418 silc_schedule_task_add_timeout(client->schedule,
419 silc_client_save_channel_key_rekey,
423 /* Get channel cipher */
424 cipher = silc_channel_key_get_cipher(payload, NULL);
425 if (!silc_cipher_alloc(cipher, &channel->internal.send_key)) {
426 client->internal->ops->say(
428 SILC_CLIENT_MESSAGE_AUDIT,
429 "Cannot talk to channel: unsupported cipher %s",
431 silc_client_unref_channel(client, conn, channel);
432 silc_channel_key_payload_free(payload);
435 if (!silc_cipher_alloc(cipher, &channel->internal.receive_key)) {
436 client->internal->ops->say(
438 SILC_CLIENT_MESSAGE_AUDIT,
439 "Cannot talk to channel: unsupported cipher %s",
441 silc_client_unref_channel(client, conn, channel);
442 silc_channel_key_payload_free(payload);
446 /* Set the cipher key. Both sending and receiving keys are same */
447 key = silc_channel_key_get_key(payload, &tmp_len);
448 silc_cipher_set_key(channel->internal.send_key, key, tmp_len * 8, TRUE);
449 silc_cipher_set_key(channel->internal.receive_key, key, tmp_len * 8, FALSE);
451 /* Get channel HMAC */
452 hmac = (channel->internal.hmac ?
453 (char *)silc_hmac_get_name(channel->internal.hmac) :
455 if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
456 client->internal->ops->say(
458 SILC_CLIENT_MESSAGE_AUDIT,
459 "Cannot talk to channel: unsupported HMAC %s",
461 silc_client_unref_channel(client, conn, channel);
462 silc_channel_key_payload_free(payload);
467 silc_hash_make(silc_hmac_get_hash(channel->internal.hmac), key,
469 silc_hmac_set_key(channel->internal.hmac, hash,
470 silc_hash_len(silc_hmac_get_hash(channel->internal.hmac)));
471 memset(hash, 0, sizeof(hash));
473 silc_client_unref_channel(client, conn, channel);
478 /* Received channel key packet. The key will replace old channel key. */
480 SILC_FSM_STATE(silc_client_channel_key)
482 SilcClientConnection conn = fsm_context;
483 SilcClient client = conn->client;
484 SilcPacket packet = state_context;
486 SILC_LOG_DEBUG(("Received channel key"));
489 silc_client_save_channel_key(client, conn, &packet->buffer, NULL);
490 silc_packet_free(packet);
492 return SILC_FSM_FINISH;
495 /**************************** Channel Private Key ***************************/
497 /* Add new channel private key */
499 SilcBool silc_client_add_channel_private_key(SilcClient client,
500 SilcClientConnection conn,
501 SilcChannelEntry channel,
507 SilcChannelPrivateKey *ret_key)
509 SilcChannelPrivateKey entry;
510 unsigned char hash[SILC_HASH_MAXLEN];
511 SilcSKEKeyMaterial keymat;
513 if (!client || !conn || !channel)
517 cipher = SILC_DEFAULT_CIPHER;
519 hmac = SILC_DEFAULT_HMAC;
521 if (!silc_cipher_is_supported(cipher))
523 if (!silc_hmac_is_supported(hmac))
526 if (!channel->internal.private_keys) {
527 channel->internal.private_keys = silc_dlist_init();
528 if (!channel->internal.private_keys)
532 /* Produce the key material */
533 keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
534 conn->internal->sha1hash);
539 entry = silc_calloc(1, sizeof(*entry));
541 silc_ske_free_key_material(keymat);
544 entry->name = name ? strdup(name) : NULL;
546 /* Allocate the cipher and set the key */
547 if (!silc_cipher_alloc(cipher, &entry->cipher)) {
549 silc_free(entry->name);
550 silc_ske_free_key_material(keymat);
553 silc_cipher_set_key(entry->cipher, keymat->send_enc_key,
554 keymat->enc_key_len, TRUE);
556 /* Generate HMAC key from the channel key data and set it */
557 if (!silc_hmac_alloc(hmac, NULL, &entry->hmac)) {
559 silc_free(entry->name);
560 silc_cipher_free(entry->cipher);
561 silc_ske_free_key_material(keymat);
564 silc_hash_make(silc_hmac_get_hash(entry->hmac), keymat->send_enc_key,
565 keymat->enc_key_len / 8, hash);
566 silc_hmac_set_key(entry->hmac, hash,
567 silc_hash_len(silc_hmac_get_hash(entry->hmac)));
568 memset(hash, 0, sizeof(hash));
570 /* Add to the private keys list */
571 silc_dlist_add(channel->internal.private_keys, entry);
573 if (!channel->internal.curr_key)
574 channel->internal.curr_key = entry;
576 /* Free the key material */
577 silc_ske_free_key_material(keymat);
585 /* Removes all private keys from the `channel'. The old channel key is used
586 after calling this to protect the channel messages. Returns FALSE on
587 on error, TRUE otherwise. */
589 SilcBool silc_client_del_channel_private_keys(SilcClient client,
590 SilcClientConnection conn,
591 SilcChannelEntry channel)
593 SilcChannelPrivateKey entry;
595 if (!client || !conn || !channel)
598 if (!channel->internal.private_keys)
601 silc_dlist_start(channel->internal.private_keys);
602 while ((entry = silc_dlist_get(channel->internal.private_keys))) {
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);
610 channel->internal.curr_key = NULL;
612 silc_dlist_uninit(channel->internal.private_keys);
613 channel->internal.private_keys = NULL;
618 /* Removes and frees private key `key' from the channel `channel'. The `key'
619 is retrieved by calling the function silc_client_list_channel_private_keys.
620 The key is not used after this. If the key was last private key then the
621 old channel key is used hereafter to protect the channel messages. This
622 returns FALSE on error, TRUE otherwise. */
624 SilcBool silc_client_del_channel_private_key(SilcClient client,
625 SilcClientConnection conn,
626 SilcChannelEntry channel,
627 SilcChannelPrivateKey key)
629 SilcChannelPrivateKey entry;
631 if (!client || !conn || !channel)
634 if (!channel->internal.private_keys)
637 silc_dlist_start(channel->internal.private_keys);
638 while ((entry = silc_dlist_get(channel->internal.private_keys))) {
642 if (channel->internal.curr_key == entry)
643 channel->internal.curr_key = NULL;
645 silc_dlist_del(channel->internal.private_keys, entry);
646 silc_free(entry->name);
647 silc_cipher_free(entry->cipher);
648 silc_hmac_free(entry->hmac);
651 if (silc_dlist_count(channel->internal.private_keys) == 0) {
652 silc_dlist_uninit(channel->internal.private_keys);
653 channel->internal.private_keys = NULL;
662 /* Returns array (pointers) of private keys associated to the `channel'.
663 The caller must free the array by calling the function
664 silc_client_free_channel_private_keys. The pointers in the array may be
665 used to delete the specific key by giving the pointer as argument to the
666 function silc_client_del_channel_private_key. */
668 SilcDList silc_client_list_channel_private_keys(SilcClient client,
669 SilcClientConnection conn,
670 SilcChannelEntry channel)
672 SilcChannelPrivateKey entry;
675 if (!client || !conn || !channel)
678 if (!channel->internal.private_keys)
681 list = silc_dlist_init();
685 silc_dlist_start(channel->internal.private_keys);
686 while ((entry = silc_dlist_get(channel->internal.private_keys)))
687 silc_dlist_add(list, entry);
692 /* Sets the `key' to be used as current channel private key on the
693 `channel'. Packet sent after calling this function will be secured
696 void silc_client_current_channel_private_key(SilcClient client,
697 SilcClientConnection conn,
698 SilcChannelEntry channel,
699 SilcChannelPrivateKey key)
703 channel->internal.curr_key = key;
706 /***************************** Utility routines *****************************/
708 /* Returns the SilcChannelUser entry if the `client_entry' is joined on the
709 channel indicated by the `channel'. NULL if client is not joined on
712 SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
713 SilcClientEntry client_entry)
717 if (silc_hash_table_find(channel->user_list, client_entry, NULL,
724 /* Adds client to channel. Returns TRUE if user was added or is already
725 added to the channel, FALSE on error. Must be called with both `channel'
726 and `client_entry' locked. */
728 SilcBool silc_client_add_to_channel(SilcClient client,
729 SilcClientConnection conn,
730 SilcChannelEntry channel,
731 SilcClientEntry client_entry,
736 if (silc_client_on_channel(channel, client_entry))
739 SILC_LOG_DEBUG(("Add client %s to channel", client_entry->nickname));
741 chu = silc_calloc(1, sizeof(*chu));
745 chu->client = client_entry;
746 chu->channel = channel;
749 silc_client_ref_client(client, conn, client_entry);
750 silc_client_ref_channel(client, conn, channel);
752 silc_hash_table_add(channel->user_list, client_entry, chu);
753 silc_hash_table_add(client_entry->channels, channel, chu);
758 /* Removes client from a channel. Returns FALSE if user is not on channel.
759 This handles entry locking internally. */
761 SilcBool silc_client_remove_from_channel(SilcClient client,
762 SilcClientConnection conn,
763 SilcChannelEntry channel,
764 SilcClientEntry client_entry)
768 chu = silc_client_on_channel(channel, client_entry);
772 SILC_LOG_DEBUG(("Remove client %s from channel", client_entry->nickname));
774 silc_rwlock_wrlock(client_entry->internal.lock);
775 silc_rwlock_wrlock(channel->internal.lock);
777 silc_hash_table_del(chu->client->channels, chu->channel);
778 silc_hash_table_del(chu->channel->user_list, chu->client);
781 /* If channel became empty, delete it */
782 if (!silc_hash_table_count(channel->user_list))
783 silc_client_del_channel(client, conn, channel);
785 silc_rwlock_unlock(client_entry->internal.lock);
786 silc_rwlock_unlock(channel->internal.lock);
788 silc_client_unref_client(client, conn, client_entry);
789 silc_client_unref_channel(client, conn, channel);
794 /* Removes a client entry from all channels it has joined. This handles
795 entry locking internally. */
797 void silc_client_remove_from_channels(SilcClient client,
798 SilcClientConnection conn,
799 SilcClientEntry client_entry)
801 SilcHashTableList htl;
804 if (!silc_hash_table_count(client_entry->channels))
807 SILC_LOG_DEBUG(("Remove client from all joined channels"));
809 silc_rwlock_wrlock(client_entry->internal.lock);
811 silc_hash_table_list(client_entry->channels, &htl);
812 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
813 silc_rwlock_wrlock(chu->channel->internal.lock);
815 silc_hash_table_del(chu->client->channels, chu->channel);
816 silc_hash_table_del(chu->channel->user_list, chu->client);
818 /* If channel became empty, delete it */
819 if (!silc_hash_table_count(chu->channel->user_list))
820 silc_client_del_channel(client, conn, chu->channel);
822 silc_rwlock_unlock(chu->channel->internal.lock);
824 silc_client_unref_client(client, conn, chu->client);
825 silc_client_unref_channel(client, conn, chu->channel);
829 silc_rwlock_unlock(client_entry->internal.lock);
831 silc_hash_table_list_reset(&htl);
834 /* Empties channel from users. This handles entry locking internally. */
836 void silc_client_empty_channel(SilcClient client,
837 SilcClientConnection conn,
838 SilcChannelEntry channel)
840 SilcHashTableList htl;
843 silc_rwlock_wrlock(channel->internal.lock);
845 silc_hash_table_list(channel->user_list, &htl);
846 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
847 silc_hash_table_del(chu->client->channels, chu->channel);
848 silc_hash_table_del(chu->channel->user_list, chu->client);
849 silc_client_unref_client(client, conn, chu->client);
850 silc_client_unref_channel(client, conn, chu->channel);
854 silc_rwlock_unlock(channel->internal.lock);
856 silc_hash_table_list_reset(&htl);
859 /* Save public keys to channel public key list. Removes keys that are
860 marked to be removed. Must be called with `channel' locked. */
862 SilcBool silc_client_channel_save_public_keys(SilcChannelEntry channel,
863 unsigned char *chpk_list,
864 SilcUInt32 chpk_list_len)
866 SilcArgumentDecodedList a, b;
870 chpks = silc_argument_list_parse_decoded(chpk_list, chpk_list_len,
871 SILC_ARGUMENT_PUBLIC_KEY);
875 if (!channel->channel_pubkeys) {
876 channel->channel_pubkeys = chpks;
880 silc_dlist_start(chpks);
881 while ((a = silc_dlist_get(chpks))) {
883 silc_dlist_start(channel->channel_pubkeys);
884 while ((b = silc_dlist_get(channel->channel_pubkeys))) {
885 if (silc_pkcs_public_key_compare(a->argument, b->argument)) {
891 if ((a->arg_type == 0x00 || a->arg_type == 0x03) && !found) {
892 silc_dlist_add(channel->channel_pubkeys, a);
893 silc_dlist_del(chpks, a);
894 } else if (a->arg_type == 0x01 && found) {
895 silc_dlist_del(channel->channel_pubkeys, b);
899 silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);