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