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