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