aff49a2362f63d4d0318b5f72026dd2917e46a58
[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 - 2002 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 /* This file includes the private message sending and receiving routines
21    and private message key handling routines. */
22
23 #include "silcincludes.h"
24 #include "silcclient.h"
25 #include "client_internal.h"
26
27 /* Sends private message to remote client. If private message key has
28    not been set with this client then the message will be encrypted using
29    normal session keys. Private messages are special packets in SILC
30    network hence we need this own function for them. This is similiar
31    to silc_client_packet_send_to_channel except that we send private
32    message. The `data' is the private message. If the `force_send' is
33    TRUE the packet is sent immediately. */
34
35 void silc_client_send_private_message(SilcClient client,
36                                       SilcClientConnection conn,
37                                       SilcClientEntry client_entry,
38                                       SilcMessageFlags flags,
39                                       unsigned char *data, 
40                                       SilcUInt32 data_len, 
41                                       bool force_send)
42 {
43   SilcSocketConnection sock;
44   SilcBuffer buffer;
45   SilcPacketContext packetdata;
46   const SilcBufferStruct packet;
47   SilcCipher cipher;
48   SilcHmac hmac;
49   int block_len;
50
51   assert(client && conn && client_entry);
52   sock = conn->sock;
53   SILC_LOG_DEBUG(("Sending private message"));
54
55   /* Encode private message payload */
56   buffer = silc_message_payload_encode(flags, data, data_len,
57                                        !client_entry->send_key ? FALSE :
58                                        !client_entry->generated,
59                                        TRUE, client_entry->send_key,
60                                        client_entry->hmac_send,
61                                        client->rng);
62
63   /* If we don't have private message specific key then private messages
64      are just as any normal packet thus call normal packet sending.  If
65      the key exist then the encryption process is a bit different and
66      will be done in the rest of this function. */
67   if (!client_entry->send_key) {
68     silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
69                             client_entry->id, SILC_ID_CLIENT, NULL, NULL,
70                             buffer->data, buffer->len, force_send);
71     goto out;
72   }
73
74   /* We have private message specific key */
75
76   /* Get data used in the encryption */
77   cipher = conn->internal->send_key;
78   hmac = conn->internal->hmac_send;
79   block_len = silc_cipher_get_block_len(cipher);
80
81   /* Set the packet context pointers. */
82   data = buffer->data;
83   data_len = buffer->len;
84   packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
85   packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
86   packetdata.src_id = conn->local_id_data;
87   packetdata.src_id_len = silc_id_get_len(conn->local_id, SILC_ID_CLIENT);
88   packetdata.src_id_type = SILC_ID_CLIENT;
89   packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
90   packetdata.dst_id_len = silc_id_get_len(client_entry->id, SILC_ID_CLIENT);
91   packetdata.dst_id_type = SILC_ID_CLIENT;
92   data_len = SILC_PACKET_DATALEN(data_len, SILC_PACKET_HEADER_LEN +
93                                  packetdata.src_id_len +
94                                  packetdata.dst_id_len);
95   packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
96     packetdata.src_id_len + packetdata.dst_id_len;
97   SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
98                       packetdata.src_id_len +
99                       packetdata.dst_id_len), block_len, packetdata.padlen);
100
101   /* Create the outgoing packet */
102   if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock, 
103                             data, data_len, (const SilcBuffer)&packet)) {
104     SILC_LOG_ERROR(("Error assembling packet"));
105     goto out;
106   }
107
108   /* Encrypt the header and padding of the packet. */
109   silc_packet_encrypt(cipher, hmac, conn->internal->psn_send++,
110                       (SilcBuffer)&packet, SILC_PACKET_HEADER_LEN +
111                       packetdata.src_id_len + packetdata.dst_id_len +
112                       packetdata.padlen);
113
114   SILC_LOG_HEXDUMP(("Private message packet, len %d", packet.len),
115                    packet.data, packet.len);
116
117   /* Now actually send the packet */
118   silc_client_packet_send_real(client, sock, force_send);
119
120   /* Check for mandatory rekey */
121   if (conn->internal->psn_send == SILC_CLIENT_REKEY_THRESHOLD)
122     silc_schedule_task_add(client->schedule, sock->sock,
123                            silc_client_rekey_callback, sock, 0, 1,
124                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
125
126   silc_free(packetdata.dst_id);
127
128  out:
129   silc_buffer_free(buffer);
130 }     
131
132 static void silc_client_private_message_cb(SilcClient client,
133                                            SilcClientConnection conn,
134                                            SilcClientEntry *clients,
135                                            SilcUInt32 clients_count,
136                                            void *context)
137 {
138   SilcPacketContext *packet = (SilcPacketContext *)context;
139
140   if (!clients) {
141     silc_packet_context_free(packet);
142     return;
143   }
144
145   silc_client_private_message(client, conn->sock, packet);
146   silc_packet_context_free(packet);
147 }
148
149 /* Private message received. This processes the private message and
150    finally displays it on the screen. */
151
152 void silc_client_private_message(SilcClient client, 
153                                  SilcSocketConnection sock, 
154                                  SilcPacketContext *packet)
155 {
156   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
157   SilcMessagePayload payload = NULL;
158   SilcClientID *remote_id = NULL;
159   SilcClientEntry remote_client;
160   SilcMessageFlags flags;
161   unsigned char *message;
162   SilcUInt32 message_len;
163   SilcCipher cipher = NULL;
164   SilcHmac hmac = NULL;
165
166   if (packet->src_id_type != SILC_ID_CLIENT)
167     goto out;
168
169   remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
170                              SILC_ID_CLIENT);
171   if (!remote_id)
172     goto out;
173
174   /* Check whether we know this client already */
175   remote_client = silc_client_get_client_by_id(client, conn, remote_id);
176   if (!remote_client || !remote_client->nickname) {
177     if (remote_client) {
178       if (remote_client->status & SILC_CLIENT_STATUS_RESOLVING) {
179         remote_client->status &= ~SILC_CLIENT_STATUS_RESOLVING;
180         goto out;
181       }
182       remote_client->status |= SILC_CLIENT_STATUS_RESOLVING;
183       remote_client->resolve_cmd_ident = conn->cmd_ident + 1;
184     }
185
186     /* Resolve the client info */
187     silc_client_get_client_by_id_resolve(client, conn, remote_id, NULL,
188                                          silc_client_private_message_cb,
189                                          silc_packet_context_dup(packet));
190     return;
191   }
192
193   cipher = remote_client->receive_key;
194   hmac = remote_client->hmac_receive;
195   if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY && !cipher && !hmac) {
196     silc_free(remote_id);
197     return;
198   }
199
200   /* Parse the payload and decrypt it also if private message key is set */
201   payload = silc_message_payload_parse(packet->buffer->data,
202                                        packet->buffer->len, TRUE,
203                                        !remote_client->generated,
204                                        cipher, hmac);
205   if (!payload) {
206     silc_free(remote_id);
207     return;
208   }
209
210   flags = silc_message_get_flags(payload);
211
212   /* Pass the private message to application */
213   message = silc_message_get_data(payload, &message_len);
214   client->internal->ops->private_message(client, conn, remote_client, flags,
215                                          message, message_len);
216
217   /* See if we are away (gone). If we are away we will reply to the
218      sender with the set away message. */
219   if (conn->internal->away && conn->internal->away->away &&
220       !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
221     /* If it's me, ignore */
222     if (SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
223       goto out;
224
225     /* Send the away message */
226     silc_client_send_private_message(client, conn, remote_client,
227                                      SILC_MESSAGE_FLAG_AUTOREPLY |
228                                      SILC_MESSAGE_FLAG_NOREPLY,
229                                      conn->internal->away->away,
230                                      strlen(conn->internal->away->away), TRUE);
231   }
232
233  out:
234   if (payload)
235     silc_message_payload_free(payload);
236   silc_free(remote_id);
237 }
238
239 /* Function that actually employes the received private message key */
240
241 static void silc_client_private_message_key_cb(SilcClient client,
242                                                SilcClientConnection conn,
243                                                SilcClientEntry *clients,
244                                                SilcUInt32 clients_count,
245                                                void *context)
246 {
247   SilcPacketContext *packet = (SilcPacketContext *)context;
248   unsigned char *key;
249   SilcUInt16 key_len;
250   unsigned char *cipher = NULL, *hmac = NULL;
251   int ret;
252
253   if (!clients)
254     goto out;
255
256   /* Parse the private message key payload */
257   ret = silc_buffer_unformat(packet->buffer,
258                              SILC_STR_UI16_NSTRING(&key, &key_len),
259                              SILC_STR_UI16_STRING_ALLOC(&cipher),
260                              SILC_STR_UI16_STRING_ALLOC(&hmac),
261                              SILC_STR_END);
262   if (!ret)
263     goto out;
264
265   if (key_len > packet->buffer->len)
266     goto out;
267
268   /* Now take the key in use */
269   if (!silc_client_add_private_message_key(client, conn, clients[0],
270                                            cipher, hmac, key, key_len,
271                                            FALSE, TRUE))
272     goto out;
273
274   /* Print some info for application */
275   client->internal->ops->say(
276                      client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
277                      "Received private message key from %s%s%s %s%s%s", 
278                      clients[0]->nickname,
279                      clients[0]->server ? "@" : "",
280                      clients[0]->server ? clients[0]->server : "",
281                      clients[0]->username ? "(" : "",
282                      clients[0]->username ? clients[0]->username : "",
283                      clients[0]->username ? ")" : "");
284
285  out:
286   silc_free(cipher);
287   silc_free(hmac);
288   silc_packet_context_free(packet);
289 }
290
291 /* Processes incoming Private Message Key payload. The libary always
292    accepts the key and takes it into use. */
293
294 void silc_client_private_message_key(SilcClient client,
295                                      SilcSocketConnection sock,
296                                      SilcPacketContext *packet)
297 {
298   SilcClientID *remote_id;
299
300   if (packet->src_id_type != SILC_ID_CLIENT)
301     return;
302
303   remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
304                              SILC_ID_CLIENT);
305   if (!remote_id)
306     return;
307
308   silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
309                                        NULL,
310                                        silc_client_private_message_key_cb,
311                                        silc_packet_context_dup(packet));
312   silc_free(remote_id);
313 }
314
315 /* Adds private message key to the client library. The key will be used to
316    encrypt all private message between the client and the remote client
317    indicated by the `client_entry'. If the `key' is NULL and the boolean
318    value `generate_key' is TRUE the library will generate random key.
319    The `key' maybe for example pre-shared-key, passphrase or similar.
320    The `cipher' and `hmac' MAY be provided but SHOULD be NULL to assure
321    that the requirements of the SILC protocol are met. The API, however,
322    allows to allocate any cipher and HMAC.
323
324    If `responder' is TRUE then the sending and receiving keys will be
325    set according the client being the receiver of the private key.  If
326    FALSE the client is being the sender (or negotiator) of the private
327    key.
328
329    It is not necessary to set key for normal private message usage. If the
330    key is not set then the private messages are encrypted using normal
331    session keys. Setting the private key, however, increases the security. 
332
333    Returns FALSE if the key is already set for the `client_entry', TRUE
334    otherwise. */
335
336 bool silc_client_add_private_message_key(SilcClient client,
337                                          SilcClientConnection conn,
338                                          SilcClientEntry client_entry,
339                                          const char *cipher,
340                                          const char *hmac,
341                                          unsigned char *key,
342                                          SilcUInt32 key_len,
343                                          bool generate_key,
344                                          bool responder)
345 {
346   unsigned char private_key[32];
347   SilcUInt32 len;
348   int i;
349   SilcSKEKeyMaterial *keymat;
350
351   assert(client && client_entry);
352
353   /* Return FALSE if key already set */
354   if (client_entry->send_key && client_entry->receive_key)
355     return FALSE;
356
357   if (!cipher)
358     cipher = SILC_DEFAULT_CIPHER;
359   if (!hmac)
360     hmac = SILC_DEFAULT_HMAC;
361
362   /* Check the requested cipher and HMAC */
363   if (!silc_cipher_is_supported(cipher))
364     return FALSE;
365   if (!silc_hmac_is_supported(hmac))
366     return FALSE;
367
368   /* Generate key if not provided */
369   if (generate_key == TRUE) {
370     len = 32;
371     for (i = 0; i < len; i++)
372       private_key[i] = silc_rng_get_byte_fast(client->rng);
373     key = private_key;
374     key_len = len;
375     client_entry->generated = TRUE;
376   }
377
378   /* Save the key */
379   client_entry->key = silc_memdup(key, key_len);
380   client_entry->key_len = key_len;
381
382   /* Produce the key material as the protocol defines */
383   keymat = silc_calloc(1, sizeof(*keymat));
384   if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
385                                          client->md5hash, keymat) 
386       != SILC_SKE_STATUS_OK)
387     return FALSE;
388
389   /* Allocate the cipher and HMAC */
390   silc_cipher_alloc(cipher, &client_entry->send_key);
391   silc_cipher_alloc(cipher, &client_entry->receive_key);
392   silc_hmac_alloc(hmac, NULL, &client_entry->hmac_send);
393   silc_hmac_alloc(hmac, NULL, &client_entry->hmac_receive);
394
395   /* Set the keys */
396   if (responder == TRUE) {
397     silc_cipher_set_key(client_entry->send_key, keymat->receive_enc_key,
398                         keymat->enc_key_len);
399     silc_cipher_set_iv(client_entry->send_key, keymat->receive_iv);
400     silc_cipher_set_key(client_entry->receive_key, keymat->send_enc_key,
401                         keymat->enc_key_len);
402     silc_cipher_set_iv(client_entry->receive_key, keymat->send_iv);
403     silc_hmac_set_key(client_entry->hmac_send, keymat->receive_hmac_key,
404                       keymat->hmac_key_len);
405     silc_hmac_set_key(client_entry->hmac_receive, keymat->send_hmac_key,
406                       keymat->hmac_key_len);
407   } else {
408     silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
409                         keymat->enc_key_len);
410     silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
411     silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
412                         keymat->enc_key_len);
413     silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
414     silc_hmac_set_key(client_entry->hmac_send, keymat->send_hmac_key,
415                       keymat->hmac_key_len);
416     silc_hmac_set_key(client_entry->hmac_receive, keymat->receive_hmac_key,
417                       keymat->hmac_key_len);
418   }
419
420   /* Free the key material */
421   silc_ske_free_key_material(keymat);
422
423   return TRUE;
424 }
425
426 /* Same as above but takes the key material from the SKE key material
427    structure. This structure is received if the application uses the
428    silc_client_send_key_agreement to negotiate the key material. The
429    `cipher' and `hmac' SHOULD be provided as it is negotiated also in
430    the SKE protocol. */
431
432 bool silc_client_add_private_message_key_ske(SilcClient client,
433                                              SilcClientConnection conn,
434                                              SilcClientEntry client_entry,
435                                              const char *cipher,
436                                              const char *hmac,
437                                              SilcSKEKeyMaterial *key,
438                                              bool responder)
439 {
440   assert(client && client_entry);
441
442   /* Return FALSE if key already set */
443   if (client_entry->send_key && client_entry->receive_key)
444     return FALSE;
445
446   if (!cipher)
447     cipher = SILC_DEFAULT_CIPHER;
448   if (!hmac)
449     hmac = SILC_DEFAULT_HMAC;
450
451   /* Check the requested cipher and HMAC */
452   if (!silc_cipher_is_supported(cipher))
453     return FALSE;
454   if (!silc_hmac_is_supported(hmac))
455     return FALSE;
456
457   client_entry->generated = TRUE;
458
459   /* Allocate the cipher and HMAC */
460   silc_cipher_alloc(cipher, &client_entry->send_key);
461   silc_cipher_alloc(cipher, &client_entry->receive_key);
462   silc_hmac_alloc(hmac, NULL, &client_entry->hmac_send);
463   silc_hmac_alloc(hmac, NULL, &client_entry->hmac_receive);
464
465   /* Set the keys */
466   if (responder == TRUE) {
467     silc_cipher_set_key(client_entry->send_key, key->receive_enc_key,
468                         key->enc_key_len);
469     silc_cipher_set_iv(client_entry->send_key, key->receive_iv);
470     silc_cipher_set_key(client_entry->receive_key, key->send_enc_key,
471                         key->enc_key_len);
472     silc_cipher_set_iv(client_entry->receive_key, key->send_iv);
473     silc_hmac_set_key(client_entry->hmac_send, key->receive_hmac_key,
474                       key->hmac_key_len);
475     silc_hmac_set_key(client_entry->hmac_receive, key->send_hmac_key,
476                       key->hmac_key_len);
477   } else {
478     silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
479                         key->enc_key_len);
480     silc_cipher_set_iv(client_entry->send_key, key->send_iv);
481     silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
482                         key->enc_key_len);
483     silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
484     silc_hmac_set_key(client_entry->hmac_send, key->send_hmac_key,
485                       key->hmac_key_len);
486     silc_hmac_set_key(client_entry->hmac_receive, key->receive_hmac_key,
487                       key->hmac_key_len);
488   }
489
490   return TRUE;
491 }
492
493 /* Sends private message key payload to the remote client indicated by
494    the `client_entry'. If the `force_send' is TRUE the packet is sent
495    immediately. Returns FALSE if error occurs, TRUE otherwise. The
496    application should call this function after setting the key to the
497    client.
498
499    Note that the key sent using this function is sent to the remote client
500    through the SILC network. The packet is protected using normal session
501    keys. */
502
503 bool silc_client_send_private_message_key(SilcClient client,
504                                          SilcClientConnection conn,
505                                          SilcClientEntry client_entry,
506                                          bool force_send)
507 {
508   SilcSocketConnection sock;
509   SilcBuffer buffer;
510   int cipher_len, hmac_len;
511   const char *cipher, *hmac;
512
513   assert(client && conn && client_entry);
514
515   sock = conn->sock;
516   if (!client_entry->send_key || !client_entry->key)
517     return FALSE;
518
519   SILC_LOG_DEBUG(("Sending private message key"));
520
521   cipher = silc_cipher_get_name(client_entry->send_key);
522   cipher_len = strlen(cipher);
523   hmac = silc_hmac_get_name(client_entry->hmac_send);
524   hmac_len = strlen(hmac);
525
526   /* Create private message key payload */
527   buffer = silc_buffer_alloc(2 + client_entry->key_len);
528   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
529   silc_buffer_format(buffer,
530                      SILC_STR_UI_SHORT(client_entry->key_len),
531                      SILC_STR_UI_XNSTRING(client_entry->key, 
532                                           client_entry->key_len),
533                      SILC_STR_UI_SHORT(cipher_len),
534                      SILC_STR_UI_XNSTRING(cipher,
535                                           cipher_len),
536                      SILC_STR_UI_SHORT(hmac_len),
537                      SILC_STR_UI_XNSTRING(hmac,
538                                           hmac_len),
539                      SILC_STR_END);
540
541   /* Send the packet */
542   silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
543                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
544                           buffer->data, buffer->len, force_send);
545   silc_free(buffer);
546
547   return TRUE;
548 }
549
550 /* Removes the private message from the library. The key won't be used
551    after this to protect the private messages with the remote `client_entry'
552    client. Returns FALSE on error, TRUE otherwise. */
553
554 bool silc_client_del_private_message_key(SilcClient client,
555                                         SilcClientConnection conn,
556                                         SilcClientEntry client_entry)
557 {
558   assert(client && client_entry);
559
560   if (!client_entry->send_key && !client_entry->receive_key)
561     return FALSE;
562
563   silc_cipher_free(client_entry->send_key);
564   silc_cipher_free(client_entry->receive_key);
565
566   if (client_entry->key) {
567     memset(client_entry->key, 0, client_entry->key_len);
568     silc_free(client_entry->key);
569   }
570
571   client_entry->send_key = NULL;
572   client_entry->receive_key = NULL;
573   client_entry->key = NULL;
574
575   return TRUE;
576 }
577
578 /* Returns array of set private message keys associated to the connection
579    `conn'. Returns allocated SilcPrivateMessageKeys array and the array
580    count to the `key_count' argument. The array must be freed by the caller
581    by calling the silc_client_free_private_message_keys function. Note: 
582    the keys returned in the array is in raw format. It might not be desired
583    to show the keys as is. The application might choose not to show the keys
584    at all or to show the fingerprints of the keys. */
585
586 SilcPrivateMessageKeys
587 silc_client_list_private_message_keys(SilcClient client,
588                                       SilcClientConnection conn,
589                                       SilcUInt32 *key_count)
590 {
591   SilcPrivateMessageKeys keys;
592   SilcUInt32 count = 0;
593   SilcIDCacheEntry id_cache;
594   SilcIDCacheList list;
595   SilcClientEntry entry;
596
597   assert(client && conn);
598
599   if (!silc_idcache_get_all(conn->internal->client_cache, &list))
600     return NULL;
601
602   if (!silc_idcache_list_count(list)) {
603     silc_idcache_list_free(list);
604     return NULL;
605   }
606
607   keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
608
609   silc_idcache_list_first(list, &id_cache);
610   while (id_cache) {
611     entry = (SilcClientEntry)id_cache->context;
612
613     if (entry->send_key) {
614       keys[count].client_entry = entry;
615       keys[count].cipher = (char *)silc_cipher_get_name(entry->send_key);
616       keys[count].key = entry->generated == FALSE ? entry->key : NULL;
617       keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
618       count++;
619     }
620
621     if (!silc_idcache_list_next(list, &id_cache))
622       break;
623   }
624
625   if (key_count)
626     *key_count = count;
627
628   return keys;
629 }
630
631 /* Frees the SilcPrivateMessageKeys array returned by the function
632    silc_client_list_private_message_keys. */
633
634 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
635                                            SilcUInt32 key_count)
636 {
637   silc_free(keys);
638 }
639
640 /* Sets away `message'.  The away message may be set when the client's
641    mode is changed to SILC_UMODE_GONE and the client whishes to reply
642    to anyone who sends private message.  The `message' will be sent
643    automatically back to the the client who send private message.  If
644    away message is already set this replaces the old message with the
645    new one.  If `message' is NULL the old away message is removed. 
646    The sender may freely free the memory of the `message'. */
647
648 void silc_client_set_away_message(SilcClient client,
649                                   SilcClientConnection conn,
650                                   char *message)
651 {
652   assert(client && conn);
653
654   if (!message && conn->internal->away) {
655     silc_free(conn->internal->away->away);
656     silc_free(conn->internal->away);
657     conn->internal->away = NULL;
658   }
659
660   if (message) {
661     if (!conn->internal->away)
662       conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
663     if (conn->internal->away->away)
664       silc_free(conn->internal->away->away);
665     conn->internal->away->away = strdup(message);
666   }
667 }