More client library rewrites (added rekey)
[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   if (!client || !conn || !client_entry)
41     return FALSE;
42   if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
43     return FALSE;
44
45   SILC_LOG_DEBUG(("Sending private message"));
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 /* Sends private message key request.  Sender of this packet is initiator
349    when setting the private message key. */
350
351 static SilcBool
352 silc_client_send_private_message_key_request(SilcClient client,
353                                              SilcClientConnection conn,
354                                              SilcClientEntry client_entry)
355 {
356   const char *cipher, *hmac;
357
358   SILC_LOG_DEBUG(("Sending private message key request"));
359
360   cipher = silc_cipher_get_name(client_entry->internal.send_key);
361   hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
362
363   /* Send the packet */
364   return silc_packet_send_va_ext(conn->stream,
365                                  SILC_PACKET_PRIVATE_MESSAGE_KEY,
366                                  0, 0, NULL, SILC_ID_CLIENT,
367                                  &client_entry->id, NULL, NULL,
368                                  SILC_STR_UI_SHORT(strlen(cipher)),
369                                  SILC_STR_DATA(cipher, strlen(cipher)),
370                                  SILC_STR_UI_SHORT(strlen(hmac)),
371                                  SILC_STR_DATA(hmac, strlen(hmac)),
372                                  SILC_STR_END);
373 }
374
375 /* Client resolving callback.  Here we simply mark that we are the responder
376    side of this private message key request.  */
377
378 static void silc_client_private_message_key_cb(SilcClient client,
379                                                SilcClientConnection conn,
380                                                SilcStatus status,
381                                                SilcDList clients,
382                                                void *context)
383 {
384   SilcPacket packet = context;
385   unsigned char *cipher = NULL, *hmac = NULL;
386   SilcClientEntry client_entry;
387   int ret;
388
389   if (!clients) {
390     silc_packet_free(packet);
391     return;
392   }
393
394   /* Parse the private message key payload */
395   ret = silc_buffer_unformat(&packet->buffer,
396                              SILC_STR_UI16_STRING_ALLOC(&cipher),
397                              SILC_STR_UI16_STRING_ALLOC(&hmac),
398                              SILC_STR_END);
399   if (!ret)
400     goto out;
401
402   /* Mark that we are responder */
403   client_entry = silc_dlist_get(clients);
404   client_entry->internal.prv_resp = TRUE;
405
406   /* XXX we should notify application that remote wants to set up the
407      static key.  And we should tell if we already have key with remote.
408      Application should return status telling whether to delete the key
409      or not. */
410
411  out:
412   silc_free(cipher);
413   silc_free(hmac);
414   silc_packet_free(packet);
415 }
416
417 /* Processes incoming Private Message Key payload to indicate that the
418    sender whishes to set up a static private message key. */
419
420 SILC_FSM_STATE(silc_client_private_message_key)
421 {
422   SilcClientConnection conn = fsm_context;
423   SilcClient client = conn->client;
424   SilcPacket packet = state_context;
425   SilcClientID remote_id;
426
427   if (packet->src_id_type != SILC_ID_CLIENT) {
428     silc_packet_free(packet);
429     return SILC_FSM_FINISH;
430   }
431
432   if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
433                       &remote_id, sizeof(remote_id))) {
434     silc_packet_free(packet);
435     return SILC_FSM_FINISH;
436   }
437
438   /* Always resolve the remote client.  The actual packet is processed
439      in the resolving callback. */
440   SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
441                                        client, conn, &remote_id, NULL,
442                                        silc_client_private_message_key_cb,
443                                        fsm));
444 }
445
446 /* Adds new private message key to `client_entry'.  If we are setting this
447    before receiving request for it from `client_entry' we will send the
448    request to the client.  Otherwise, we are responder side. */
449
450 SilcBool silc_client_add_private_message_key(SilcClient client,
451                                              SilcClientConnection conn,
452                                              SilcClientEntry client_entry,
453                                              const char *cipher,
454                                              const char *hmac,
455                                              unsigned char *key,
456                                              SilcUInt32 key_len)
457 {
458   SilcSKEKeyMaterial keymat;
459   SilcBool ret;
460
461   if (!client || !client_entry)
462     return FALSE;
463
464   /* Return FALSE if key already set */
465   if (client_entry->internal.send_key && client_entry->internal.receive_key)
466     return FALSE;
467
468   if (!cipher)
469     cipher = SILC_DEFAULT_CIPHER;
470   if (!hmac)
471     hmac = SILC_DEFAULT_HMAC;
472
473   /* Check the requested cipher and HMAC */
474   if (!silc_cipher_is_supported(cipher))
475     return FALSE;
476   if (!silc_hmac_is_supported(hmac))
477     return FALSE;
478
479   /* Save the key */
480   client_entry->internal.key = silc_memdup(key, key_len);
481   client_entry->internal.key_len = key_len;
482
483   /* Produce the key material as the protocol defines */
484   keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
485                                               conn->internal->sha1hash);
486   if (!keymat)
487     return FALSE;
488
489   /* Set the key into use */
490   ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
491                                                 cipher, hmac, keymat);
492   client_entry->internal.generated = FALSE;
493
494   /* Free the key material */
495   silc_ske_free_key_material(keymat);
496
497   /* If we are setting the key without a request from the remote client,
498      we will send request to remote. */
499   if (!client_entry->internal.prv_resp)
500     silc_client_send_private_message_key_request(client, conn, client_entry);
501
502   return ret;
503 }
504
505 /* Same as above but takes the key material from the SKE key material
506    structure. */
507
508 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
509                                                  SilcClientConnection conn,
510                                                  SilcClientEntry client_entry,
511                                                  const char *cipher,
512                                                  const char *hmac,
513                                                  SilcSKEKeyMaterial keymat)
514 {
515   if (!client || !client_entry)
516     return FALSE;
517
518   /* Return FALSE if key already set */
519   if (client_entry->internal.send_key && client_entry->internal.receive_key)
520     return FALSE;
521
522   if (!cipher)
523     cipher = SILC_DEFAULT_CIPHER;
524   if (!hmac)
525     hmac = SILC_DEFAULT_HMAC;
526
527   /* Check the requested cipher and HMAC */
528   if (!silc_cipher_is_supported(cipher))
529     return FALSE;
530   if (!silc_hmac_is_supported(hmac))
531     return FALSE;
532
533   client_entry->internal.generated = TRUE;
534
535   /* Allocate the cipher and HMAC */
536   if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
537     return FALSE;
538   if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
539     return FALSE;
540   if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
541     return FALSE;
542   if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
543     return FALSE;
544
545   /* Set the keys */
546   if (client_entry->internal.prv_resp) {
547     silc_cipher_set_key(client_entry->internal.send_key,
548                         keymat->receive_enc_key,
549                         keymat->enc_key_len);
550     silc_cipher_set_iv(client_entry->internal.send_key,
551                        keymat->receive_iv);
552     silc_cipher_set_key(client_entry->internal.receive_key,
553                         keymat->send_enc_key,
554                         keymat->enc_key_len);
555     silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
556     silc_hmac_set_key(client_entry->internal.hmac_send,
557                       keymat->receive_hmac_key,
558                       keymat->hmac_key_len);
559     silc_hmac_set_key(client_entry->internal.hmac_receive,
560                       keymat->send_hmac_key,
561                       keymat->hmac_key_len);
562   } else {
563     silc_cipher_set_key(client_entry->internal.send_key,
564                         keymat->send_enc_key,
565                         keymat->enc_key_len);
566     silc_cipher_set_iv(client_entry->internal.send_key,
567                        keymat->send_iv);
568     silc_cipher_set_key(client_entry->internal.receive_key,
569                         keymat->receive_enc_key,
570                         keymat->enc_key_len);
571     silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
572     silc_hmac_set_key(client_entry->internal.hmac_send,
573                       keymat->send_hmac_key,
574                       keymat->hmac_key_len);
575     silc_hmac_set_key(client_entry->internal.hmac_receive,
576                       keymat->receive_hmac_key,
577                       keymat->hmac_key_len);
578   }
579
580   return TRUE;
581 }
582
583 /* Removes the private message from the library. The key won't be used
584    after this to protect the private messages with the remote `client_entry'
585    client. Returns FALSE on error, TRUE otherwise. */
586
587 SilcBool silc_client_del_private_message_key(SilcClient client,
588                                              SilcClientConnection conn,
589                                              SilcClientEntry client_entry)
590 {
591   if (!client || !client_entry)
592     return FALSE;
593
594   if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
595     return FALSE;
596
597   silc_cipher_free(client_entry->internal.send_key);
598   silc_cipher_free(client_entry->internal.receive_key);
599
600   if (client_entry->internal.key) {
601     memset(client_entry->internal.key, 0, client_entry->internal.key_len);
602     silc_free(client_entry->internal.key);
603   }
604
605   client_entry->internal.send_key = NULL;
606   client_entry->internal.receive_key = NULL;
607   client_entry->internal.key = NULL;
608   client_entry->internal.prv_resp = FALSE;
609
610   return TRUE;
611 }
612
613 /* Returns array of set private message keys associated to the connection
614    `conn'. Returns allocated SilcPrivateMessageKeys array and the array
615    count to the `key_count' argument. The array must be freed by the caller
616    by calling the silc_client_free_private_message_keys function. Note:
617    the keys returned in the array is in raw format. It might not be desired
618    to show the keys as is. The application might choose not to show the keys
619    at all or to show the fingerprints of the keys. */
620
621 SilcPrivateMessageKeys
622 silc_client_list_private_message_keys(SilcClient client,
623                                       SilcClientConnection conn,
624                                       SilcUInt32 *key_count)
625 {
626   SilcPrivateMessageKeys keys;
627   SilcUInt32 count = 0;
628   SilcList list;
629   SilcIDCacheEntry id_cache;
630   SilcClientEntry entry;
631
632   if (!client || !conn)
633     return NULL;
634
635   silc_mutex_lock(conn->internal->lock);
636   if (!silc_idcache_get_all(conn->internal->client_cache, &list)) {
637     silc_mutex_unlock(conn->internal->lock);
638     return NULL;
639   }
640
641   keys = silc_calloc(silc_list_count(list), sizeof(*keys));
642   if (!keys) {
643     silc_mutex_unlock(conn->internal->lock);
644     return NULL;
645   }
646
647   silc_list_start(list);
648   while ((id_cache = silc_list_get(list))) {
649     entry = id_cache->context;
650     if (entry->internal.send_key) {
651       keys[count].client_entry = entry;
652       keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
653                                                         send_key);
654       keys[count].key = (entry->internal.generated == FALSE ?
655                          entry->internal.key : NULL);
656       keys[count].key_len = (entry->internal.generated == FALSE ?
657                              entry->internal.key_len : 0);
658       count++;
659     }
660   }
661
662   silc_mutex_unlock(conn->internal->lock);
663
664   if (key_count)
665     *key_count = count;
666
667   return keys;
668 }
669
670 /* Frees the SilcPrivateMessageKeys array returned by the function
671    silc_client_list_private_message_keys. */
672
673 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
674                                            SilcUInt32 key_count)
675 {
676   silc_free(keys);
677 }
678
679 /* Sets away `message'.  The away message may be set when the client's
680    mode is changed to SILC_UMODE_GONE and the client whishes to reply
681    to anyone who sends private message.  The `message' will be sent
682    automatically back to the the client who send private message.  If
683    away message is already set this replaces the old message with the
684    new one.  If `message' is NULL the old away message is removed.
685    The sender may freely free the memory of the `message'. */
686
687 void silc_client_set_away_message(SilcClient client,
688                                   SilcClientConnection conn,
689                                   char *message)
690 {
691   assert(client && conn);
692
693   if (!message && conn->internal->away) {
694     silc_free(conn->internal->away->away);
695     silc_free(conn->internal->away);
696     conn->internal->away = NULL;
697   }
698
699   if (message) {
700     if (!conn->internal->away)
701       conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
702     if (conn->internal->away->away)
703       silc_free(conn->internal->away->away);
704     conn->internal->away->away = strdup(message);
705   }
706 }