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