silc_client_unref_client fixes.
[silc.git] / lib / silcclient / client_prvmsg.c
1 /*
2
3   client_prvmsg.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 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 /************************** Private Message Send ****************************/
26
27 /* Sends private message to remote client. */
28
29 SilcBool silc_client_send_private_message(SilcClient client,
30                                           SilcClientConnection conn,
31                                           SilcClientEntry client_entry,
32                                           SilcMessageFlags flags,
33                                           unsigned char *data,
34                                           SilcUInt32 data_len)
35 {
36   SilcBuffer buffer;
37   SilcBool ret;
38
39   SILC_LOG_DEBUG(("Sending private message"));
40
41   if (!client || !conn || !client_entry)
42     return FALSE;
43
44   /* Encode private message payload */
45   buffer =
46     silc_message_payload_encode(flags, data, data_len,
47                                 (!client_entry->internal.send_key ? FALSE :
48                                  !client_entry->internal.generated),
49                                 TRUE, client_entry->internal.send_key,
50                                 client_entry->internal.hmac_send,
51                                 client->rng, NULL, conn->private_key,
52                                 client->sha1hash, NULL);
53   if (!buffer) {
54     SILC_LOG_ERROR(("Error encoding private message"));
55     return FALSE;
56   }
57
58   /* Send the private message packet */
59   ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE,
60                              client_entry->internal.send_key ?
61                              SILC_PACKET_FLAG_PRIVMSG_KEY : 0,
62                              0, NULL, SILC_ID_CLIENT, &client_entry->id,
63                              silc_buffer_datalen(buffer), NULL, NULL);
64
65   silc_buffer_free(buffer);
66   return ret;
67 }
68
69 /************************* Private Message Receive **************************/
70
71 /* Private message waiting context */
72 typedef struct {
73   SilcMutex wait_lock;
74   SilcCond wait_cond;
75   SilcDList message_queue;
76   unsigned int stopped      : 1;
77 } *SilcClientPrivateMessageWait;
78
79 /* Client resolving callback.  This continues the private message packet
80    processing in the packet processor thread, which is in waiting state
81    (for incoming packets) when we get here.  We can safely continue in
82    the thread and then return back to waiting when we do it synchronously. */
83
84 static void silc_client_private_message_resolved(SilcClient client,
85                                                  SilcClientConnection conn,
86                                                  SilcStatus status,
87                                                  SilcDList clients,
88                                                  void *context)
89 {
90   if (!clients) {
91     silc_packet_free(context);
92     return;
93   }
94
95   /* Continue processing the private message packet */
96   silc_fsm_set_state_context(&conn->internal->packet_thread, context);
97   silc_fsm_next(&conn->internal->packet_thread, silc_client_private_message);
98   silc_fsm_continue_sync(&conn->internal->packet_thread);
99 }
100
101 /* Private message received. */
102
103 SILC_FSM_STATE(silc_client_private_message)
104 {
105   SilcClientConnection conn = fsm_context;
106   SilcClient client = conn->client;
107   SilcPacket packet = state_context;
108   SilcMessagePayload payload = NULL;
109   SilcClientID remote_id;
110   SilcClientEntry remote_client = NULL;
111   SilcMessageFlags flags;
112   unsigned char *message;
113   SilcUInt32 message_len;
114   SilcClientPrivateMessageWait pmw;
115
116   if (packet->src_id_type != SILC_ID_CLIENT)
117     goto out;
118
119   if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
120                       &remote_id, sizeof(remote_id)))
121     goto out;
122
123   /* Check whether we know this client already */
124   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
125   if (!remote_client || !remote_client->nickname[0]) {
126     /* Resolve the client info.  We return back to packet thread to receive
127        other packets while we wait for the resolving to finish. */
128     silc_client_unref_client(client, conn, remote_client);
129     silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
130                                          silc_client_private_message_resolved,
131                                          packet);
132     silc_fsm_next(fsm, silc_client_connection_st_packet);
133     return SILC_FSM_CONTINUE;
134   }
135
136   if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
137       !remote_client->internal.receive_key &&
138       !remote_client->internal.hmac_receive)
139     goto out;
140
141   /* Parse the payload and decrypt it also if private message key is set */
142   payload =
143     silc_message_payload_parse(silc_buffer_datalen(&packet->buffer),
144                                TRUE, !remote_client->internal.generated,
145                                remote_client->internal.receive_key,
146                                remote_client->internal.hmac_receive,
147                                NULL, FALSE, NULL);
148   if (!payload)
149     goto out;
150
151 #if 0 /* We need to rethink this.  This doesn't work with multiple
152          waiters, and performance is suboptimal. */
153   /* Check if some thread is waiting for this private message */
154   silc_mutex_lock(conn->internal->lock);
155   if (conn->internal->privmsg_wait &&
156       silc_hash_table_find_ext(conn->internal->privmsg_wait,
157                                &remote_client->id, NULL, (void **)&pmw,
158                                NULL, NULL, silc_hash_id_compare_full,
159                                SILC_32_TO_PTR(SILC_ID_CLIENT))) {
160     /* Signal that message was received */
161     silc_mutex_unlock(conn->internal->lock);
162     silc_mutex_lock(pmw->wait_lock);
163     if (!pmw->stopped) {
164       silc_dlist_add(pmw->message_queue, payload);
165       silc_cond_broadcast(pmw->wait_cond);
166       silc_mutex_unlock(pmw->wait_lock);
167       silc_packet_free(packet);
168       goto out;
169     }
170     silc_mutex_unlock(pmw->wait_lock);
171   } else
172     silc_mutex_unlock(conn->internal->lock);
173 #endif /* 0 */
174
175   /* Pass the private message to application */
176   flags = silc_message_get_flags(payload);
177   message = silc_message_get_data(payload, &message_len);
178   client->internal->ops->private_message(client, conn, remote_client, payload,
179                                          flags, message, message_len);
180
181   /* See if we are away (gone). If we are away we will reply to the
182      sender with the set away message. */
183   if (conn->internal->away && conn->internal->away->away &&
184       !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
185     /* If it's me, ignore */
186     if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
187       goto out;
188
189     /* Send the away message */
190     silc_client_send_private_message(client, conn, remote_client,
191                                      SILC_MESSAGE_FLAG_AUTOREPLY |
192                                      SILC_MESSAGE_FLAG_NOREPLY,
193                                      conn->internal->away->away,
194                                      strlen(conn->internal->away->away));
195   }
196
197  out:
198   /** Packet processed */
199   silc_client_unref_client(client, conn, remote_client);
200   if (payload)
201     silc_message_payload_free(payload);
202   silc_packet_free(packet);
203   silc_fsm_next(fsm, silc_client_connection_st_packet);
204   return SILC_FSM_CONTINUE;
205 }
206
207 #if 0 /* XXX we need to rethink this */
208 /* Initialize private message waiting in a thread. */
209
210 void *silc_client_private_message_wait_init(SilcClientConnection conn,
211                                             SilcClientEntry client_entry)
212 {
213   SilcClientPrivateMessageWait pmw;
214
215   pmw = silc_calloc(1, sizeof(*pmw));
216   if (!pmw)
217     return NULL;
218
219   pmw->message_queue = silc_dlist_init();
220   if (!pmw->message_queue) {
221     silc_free(pmw);
222     return NULL;
223   }
224
225   /* Allocate mutex and conditional variable */
226   if (!silc_mutex_alloc(&pmw->wait_lock)) {
227     silc_dlist_uninit(pmw->message_queue);
228     silc_free(pmw);
229     return NULL;
230   }
231   if (!silc_cond_alloc(&pmw->wait_cond)) {
232     silc_dlist_uninit(pmw->message_queue);
233     silc_mutex_free(pmw->wait_lock);
234     silc_free(pmw);
235     return NULL;
236   }
237
238   silc_mutex_lock(conn->internal->lock);
239
240   /* Allocate waiting hash table */
241   if (!conn->internal->privmsg_wait) {
242     conn->internal->privmsg_wait =
243       silc_hash_table_alloc(0, silc_hash_id,
244                             SILC_32_TO_PTR(SILC_ID_CLIENT),
245                             silc_hash_id_compare,
246                             SILC_32_TO_PTR(SILC_ID_CLIENT), NULL, NULL, TRUE);
247     if (!conn->internal->privmsg_wait) {
248       silc_mutex_unlock(conn->internal->lock);
249       silc_dlist_uninit(pmw->message_queue);
250       silc_mutex_free(pmw->wait_lock);
251       silc_cond_free(pmw->wait_cond);
252       silc_free(pmw);
253       return NULL;
254     }
255   }
256
257   /* Add to waiting hash table */
258   silc_hash_table_add(conn->internal->privmsg_wait, client_entry->id, pmw);
259
260   silc_mutex_unlock(conn->internal->lock);
261
262   return (void *)pmw;
263 }
264
265 /* Uninitialize private message waiting. */
266
267 void silc_client_private_message_wait_uninit(SilcClientConnection conn,
268                                              SilcClientEntry client_entry,
269                                              void *waiter)
270 {
271   SilcClientPrivateMessageWait pmw = waiter;
272   SilcMessagePayload payload;
273
274   /* Signal any threads to stop waiting */
275   silc_mutex_lock(pmw->wait_lock);
276   pmw->stopped = TRUE;
277   silc_cond_broadcast(pmw->wait_cond);
278   silc_mutex_unlock(pmw->wait_lock);
279
280   /* Re-acquire lock and free resources */
281   silc_mutex_lock(pmw->wait_lock);
282
283   /* Free any remaining message */
284   silc_dlist_start(pmw->message_queue);
285   while ((payload = silc_dlist_get(pmw->message_queue)))
286     silc_message_payload_free(payload);
287
288   silc_dlist_uninit(pmw->message_queue);
289   silc_cond_free(pmw->wait_cond);
290   silc_mutex_unlock(pmw->wait_lock);
291   silc_mutex_free(pmw->wait_lock);
292
293   silc_mutex_lock(conn->internal->lock);
294   silc_hash_table_del_by_context(conn->internal->privmsg_wait,
295                                  client_entry->id, pmw);
296   silc_mutex_unlock(conn->internal->lock);
297
298   silc_free(pmw);
299 }
300
301 /* Blocks the calling process or thread until a private message has been
302    received from the specified client. */
303
304 SilcBool silc_client_private_message_wait(SilcClientConnection conn,
305                                           SilcClientEntry client_entry,
306                                           void *waiter,
307                                           SilcMessagePayload *payload)
308 {
309   SilcClientPrivateMessageWait pmw = waiter;
310   SilcPacket packet;
311
312   silc_mutex_lock(pmw->wait_lock);
313
314   /* Wait here until private message has been received */
315   while (silc_dlist_count(pmw->message_queue) == 0) {
316     if (pmw->stopped) {
317       silc_mutex_unlock(pmw->wait_lock);
318       return FALSE;
319     }
320     silc_cond_wait(pmw->wait_cond, pmw->wait_lock);
321   }
322
323   /* Return message */
324   silc_dlist_start(pmw->message_queue);
325   *payload = silc_dlist_get(pmw->message_queue);
326   silc_dlist_del(pmw->message_queue, *payload);
327
328   silc_mutex_unlock(pmw->wait_lock);
329
330   return TRUE;
331 }
332 #endif /* 0 */
333
334 /*************************** Private Message Key ****************************/
335
336 /* Client resolving callback.  Here we simply mark that we are the responder
337    side of this private message key request.  */
338
339 static void silc_client_private_message_key_cb(SilcClient client,
340                                                SilcClientConnection conn,
341                                                SilcStatus status,
342                                                SilcDList clients,
343                                                void *context)
344 {
345   SilcPacket packet = context;
346   unsigned char *cipher = NULL, *hmac = NULL;
347   SilcClientEntry client_entry;
348   int ret;
349
350   if (!clients) {
351     silc_packet_free(packet);
352     return;
353   }
354
355   /* Parse the private message key payload */
356   ret = silc_buffer_unformat(&packet->buffer,
357                              SILC_STR_UI16_STRING_ALLOC(&cipher),
358                              SILC_STR_UI16_STRING_ALLOC(&hmac),
359                              SILC_STR_END);
360   if (!ret)
361     goto out;
362
363   /* Mark that we are responder */
364   client_entry = silc_dlist_get(clients);
365   client_entry->internal.prv_resp = TRUE;
366
367   /* XXX we should notify application that remote wants to set up the
368      static key */
369
370  out:
371   silc_free(cipher);
372   silc_free(hmac);
373   silc_packet_free(packet);
374 }
375
376 /* Processes incoming Private Message Key payload to indicate that the
377    sender whishes to set up a static private message key. */
378
379 SILC_FSM_STATE(silc_client_private_message_key)
380 {
381   SilcClientConnection conn = fsm_context;
382   SilcClient client = conn->client;
383   SilcPacket packet = state_context;
384   SilcClientID remote_id;
385
386   if (packet->src_id_type != SILC_ID_CLIENT) {
387     silc_packet_free(packet);
388     goto out;
389   }
390
391   if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
392                       &remote_id, sizeof(remote_id))) {
393     silc_packet_free(packet);
394     goto out;
395   }
396
397   /* Always resolve the remote client.  The actual packet is processed
398      in the resolving callback. */
399   silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
400                                        silc_client_private_message_key_cb,
401                                        packet);
402
403  out:
404   silc_fsm_next(fsm, silc_client_connection_st_packet);
405   return SILC_FSM_CONTINUE;
406 }
407
408 /* Adds private message key to the client library. The key will be used to
409    encrypt all private message between the client and the remote client
410    indicated by the `client_entry'. If the `key' is NULL and the boolean
411    value `generate_key' is TRUE the library will generate random key.
412    The `key' maybe for example pre-shared-key, passphrase or similar.
413    The `cipher' and `hmac' MAY be provided but SHOULD be NULL to assure
414    that the requirements of the SILC protocol are met. The API, however,
415    allows to allocate any cipher and HMAC.
416
417    If `responder' is TRUE then the sending and receiving keys will be
418    set according the client being the receiver of the private key.  If
419    FALSE the client is being the sender (or negotiator) of the private
420    key.
421
422    It is not necessary to set key for normal private message usage. If the
423    key is not set then the private messages are encrypted using normal
424    session keys. Setting the private key, however, increases the security.
425
426    Returns FALSE if the key is already set for the `client_entry', TRUE
427    otherwise. */
428
429 SilcBool silc_client_add_private_message_key(SilcClient client,
430                                              SilcClientConnection conn,
431                                              SilcClientEntry client_entry,
432                                              const char *cipher,
433                                              const char *hmac,
434                                              unsigned char *key,
435                                              SilcUInt32 key_len,
436                                              SilcBool generate_key,
437                                              SilcBool responder)
438 {
439   unsigned char private_key[32];
440   SilcUInt32 len;
441   SilcSKEKeyMaterial keymat;
442   SilcBool ret;
443   int i;
444
445   if (!client || !client_entry)
446     return FALSE;
447
448   /* Return FALSE if key already set */
449   if (client_entry->internal.send_key && client_entry->internal.receive_key)
450     return FALSE;
451
452   if (!cipher)
453     cipher = SILC_DEFAULT_CIPHER;
454   if (!hmac)
455     hmac = SILC_DEFAULT_HMAC;
456
457   /* Check the requested cipher and HMAC */
458   if (!silc_cipher_is_supported(cipher))
459     return FALSE;
460   if (!silc_hmac_is_supported(hmac))
461     return FALSE;
462
463   /* Generate key if not provided */
464   if (generate_key == TRUE) {
465     len = 32;
466     for (i = 0; i < len; i++)
467       private_key[i] = silc_rng_get_byte_fast(client->rng);
468     key = private_key;
469     key_len = len;
470   }
471
472   /* Save the key */
473   client_entry->internal.key = silc_memdup(key, key_len);
474   client_entry->internal.key_len = key_len;
475
476   /* Produce the key material as the protocol defines */
477   keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
478                                               client->sha1hash);
479   if (!keymat)
480     return FALSE;
481
482   /* Set the key into use */
483   ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
484                                                 cipher, hmac, keymat,
485                                                 responder);
486
487   if (!generate_key)
488     client_entry->internal.generated = FALSE;
489
490   /* Free the key material */
491   silc_ske_free_key_material(keymat);
492
493   return ret;
494 }
495
496 /* Same as above but takes the key material from the SKE key material
497    structure. This structure is received if the application uses the
498    silc_client_send_key_agreement to negotiate the key material. The
499    `cipher' and `hmac' SHOULD be provided as it is negotiated also in
500    the SKE protocol. */
501
502 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
503                                                  SilcClientConnection conn,
504                                                  SilcClientEntry client_entry,
505                                                  const char *cipher,
506                                                  const char *hmac,
507                                                  SilcSKEKeyMaterial keymat,
508                                                  SilcBool responder)
509 {
510   if (!client || !client_entry)
511     return FALSE;
512
513   /* Return FALSE if key already set */
514   if (client_entry->internal.send_key && client_entry->internal.receive_key)
515     return FALSE;
516
517   if (!cipher)
518     cipher = SILC_DEFAULT_CIPHER;
519   if (!hmac)
520     hmac = SILC_DEFAULT_HMAC;
521
522   /* Check the requested cipher and HMAC */
523   if (!silc_cipher_is_supported(cipher))
524     return FALSE;
525   if (!silc_hmac_is_supported(hmac))
526     return FALSE;
527
528   client_entry->internal.generated = TRUE;
529
530   /* Allocate the cipher and HMAC */
531   if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
532     return FALSE;
533   if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
534     return FALSE;
535   if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
536     return FALSE;
537   if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
538     return FALSE;
539
540   /* Set the keys */
541   if (responder == TRUE) {
542     silc_cipher_set_key(client_entry->internal.send_key,
543                         keymat->receive_enc_key,
544                         keymat->enc_key_len);
545     silc_cipher_set_iv(client_entry->internal.send_key,
546                        keymat->receive_iv);
547     silc_cipher_set_key(client_entry->internal.receive_key,
548                         keymat->send_enc_key,
549                         keymat->enc_key_len);
550     silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
551     silc_hmac_set_key(client_entry->internal.hmac_send,
552                       keymat->receive_hmac_key,
553                       keymat->hmac_key_len);
554     silc_hmac_set_key(client_entry->internal.hmac_receive,
555                       keymat->send_hmac_key,
556                       keymat->hmac_key_len);
557   } else {
558     silc_cipher_set_key(client_entry->internal.send_key,
559                         keymat->send_enc_key,
560                         keymat->enc_key_len);
561     silc_cipher_set_iv(client_entry->internal.send_key,
562                        keymat->send_iv);
563     silc_cipher_set_key(client_entry->internal.receive_key,
564                         keymat->receive_enc_key,
565                         keymat->enc_key_len);
566     silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
567     silc_hmac_set_key(client_entry->internal.hmac_send,
568                       keymat->send_hmac_key,
569                       keymat->hmac_key_len);
570     silc_hmac_set_key(client_entry->internal.hmac_receive,
571                       keymat->receive_hmac_key,
572                       keymat->hmac_key_len);
573   }
574
575   return TRUE;
576 }
577
578 /* Sends private message key indicator.  The sender of this packet is
579    going to be the initiator, if and when, the users set up a static
580    private message key (not Key Agreement). */
581
582 SilcBool
583 silc_client_send_private_message_key_request(SilcClient client,
584                                              SilcClientConnection conn,
585                                              SilcClientEntry client_entry)
586 {
587   SilcBufferStruct buffer;
588   int cipher_len, hmac_len;
589   const char *cipher, *hmac;
590   SilcBool ret;
591
592   if (!client || !conn || !client_entry)
593     return FALSE;
594
595   if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
596     return FALSE;
597
598   SILC_LOG_DEBUG(("Sending private message key indicator"));
599
600   cipher = silc_cipher_get_name(client_entry->internal.send_key);
601   cipher_len = strlen(cipher);
602   hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
603   hmac_len = strlen(hmac);
604
605   /* Create private message key payload */
606   memset(&buffer, 0, sizeof(buffer));
607   if (silc_buffer_format(&buffer,
608                          SILC_STR_UI_SHORT(cipher_len),
609                          SILC_STR_UI_XNSTRING(cipher,
610                                               cipher_len),
611                          SILC_STR_UI_SHORT(hmac_len),
612                          SILC_STR_UI_XNSTRING(hmac,
613                                               hmac_len),
614                          SILC_STR_END) < 0)
615     return FALSE;
616
617   /* Send the packet */
618   ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE_KEY,
619                              0, 0, NULL, SILC_ID_CLIENT, &client_entry->id,
620                              silc_buffer_datalen(&buffer), NULL, NULL);
621   silc_buffer_purge(&buffer);
622
623   return ret;
624 }
625
626 /* Removes the private message from the library. The key won't be used
627    after this to protect the private messages with the remote `client_entry'
628    client. Returns FALSE on error, TRUE otherwise. */
629
630 SilcBool silc_client_del_private_message_key(SilcClient client,
631                                              SilcClientConnection conn,
632                                              SilcClientEntry client_entry)
633 {
634   if (!client || !client_entry)
635     return FALSE;
636
637   if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
638     return FALSE;
639
640   silc_cipher_free(client_entry->internal.send_key);
641   silc_cipher_free(client_entry->internal.receive_key);
642
643   if (client_entry->internal.key) {
644     memset(client_entry->internal.key, 0, client_entry->internal.key_len);
645     silc_free(client_entry->internal.key);
646   }
647
648   client_entry->internal.send_key = NULL;
649   client_entry->internal.receive_key = NULL;
650   client_entry->internal.key = NULL;
651
652   return TRUE;
653 }
654
655 /* Returns array of set private message keys associated to the connection
656    `conn'. Returns allocated SilcPrivateMessageKeys array and the array
657    count to the `key_count' argument. The array must be freed by the caller
658    by calling the silc_client_free_private_message_keys function. Note:
659    the keys returned in the array is in raw format. It might not be desired
660    to show the keys as is. The application might choose not to show the keys
661    at all or to show the fingerprints of the keys. */
662
663 SilcPrivateMessageKeys
664 silc_client_list_private_message_keys(SilcClient client,
665                                       SilcClientConnection conn,
666                                       SilcUInt32 *key_count)
667 {
668   SilcPrivateMessageKeys keys;
669   SilcUInt32 count = 0;
670   SilcList list;
671   SilcIDCacheEntry id_cache;
672   SilcClientEntry entry;
673
674   if (!client || !conn)
675     return NULL;
676
677   if (!silc_idcache_get_all(conn->internal->client_cache, &list))
678     return NULL;
679
680   keys = silc_calloc(silc_list_count(list), sizeof(*keys));
681   if (!keys)
682     return NULL;
683
684   silc_list_start(list);
685   while ((id_cache = silc_list_get(list))) {
686     entry = id_cache->context;
687     if (entry->internal.send_key) {
688       keys[count].client_entry = entry;
689       keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
690                                                         send_key);
691       keys[count].key = (entry->internal.generated == FALSE ?
692                          entry->internal.key : NULL);
693       keys[count].key_len = (entry->internal.generated == FALSE ?
694                              entry->internal.key_len : 0);
695       count++;
696     }
697   }
698
699   if (key_count)
700     *key_count = count;
701
702   return keys;
703 }
704
705 /* Frees the SilcPrivateMessageKeys array returned by the function
706    silc_client_list_private_message_keys. */
707
708 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
709                                            SilcUInt32 key_count)
710 {
711   silc_free(keys);
712 }
713
714 /* Sets away `message'.  The away message may be set when the client's
715    mode is changed to SILC_UMODE_GONE and the client whishes to reply
716    to anyone who sends private message.  The `message' will be sent
717    automatically back to the the client who send private message.  If
718    away message is already set this replaces the old message with the
719    new one.  If `message' is NULL the old away message is removed.
720    The sender may freely free the memory of the `message'. */
721
722 void silc_client_set_away_message(SilcClient client,
723                                   SilcClientConnection conn,
724                                   char *message)
725 {
726   assert(client && conn);
727
728   if (!message && conn->internal->away) {
729     silc_free(conn->internal->away->away);
730     silc_free(conn->internal->away);
731     conn->internal->away = NULL;
732   }
733
734   if (message) {
735     if (!conn->internal->away)
736       conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
737     if (conn->internal->away->away)
738       silc_free(conn->internal->away->away);
739     conn->internal->away->away = strdup(message);
740   }
741 }