Fixed channel key setting.
[silc.git] / lib / silcclient / client_register.c
1 /*
2
3   client_register.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 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
20 #include "silc.h"
21 #include "silcclient.h"
22 #include "client_internal.h"
23
24 /************************** Types and definitions ***************************/
25
26 /* Resume session context */
27 typedef struct {
28   SilcClient client;
29   SilcClientConnection conn;
30   SilcBufferStruct detach;
31   char *nickname;
32   SilcUInt32 channel_count;
33 } *SilcClientResumeSession;
34
35 /************************ Static utility functions **************************/
36
37 /* Command callback.  Nothing interesting to do here. */
38
39 static SilcBool
40 silc_client_register_command_called(SilcClient client,
41                                     SilcClientConnection conn,
42                                     SilcCommand command,
43                                     SilcStatus status,
44                                     SilcStatus error,
45                                     void *context,
46                                     va_list ap)
47 {
48   return FALSE;
49 }
50
51 /* Continues resuming after resolving.  Continue after last reply. */
52
53 static SilcBool
54 silc_client_resume_continue(SilcClient client,
55                             SilcClientConnection conn,
56                             SilcCommand command,
57                             SilcStatus status,
58                             SilcStatus error,
59                             void *context,
60                             va_list ap)
61 {
62   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END ||
63       SILC_STATUS_IS_ERROR(status)) {
64     silc_fsm_continue(&conn->internal->event_thread);
65     return FALSE;
66   }
67
68   return TRUE;
69 }
70
71 /* Function used to call command replies back to application in resuming. */
72
73 static void
74 silc_client_resume_command_callback(SilcClient client,
75                                     SilcClientConnection conn,
76                                     SilcCommand command, ...)
77 {
78   va_list ap;
79   va_start(ap, command);
80   client->internal->ops->command_reply(client, conn, command,
81                                        SILC_STATUS_OK, SILC_STATUS_OK, ap);
82   va_end(ap);
83 }
84
85
86 /****************************** NEW_ID packet *******************************/
87
88 /* Received new ID packet from server during registering to SILC network */
89
90 SILC_FSM_STATE(silc_client_new_id)
91 {
92   SilcClientConnection conn = fsm_context;
93   SilcClient client = conn->client;
94   SilcPacket packet = state_context;
95   SilcID id;
96
97   if (conn->local_id)
98     goto out;
99
100   SILC_LOG_DEBUG(("New ID received from server"));
101
102   if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
103                                 silc_buffer_len(&packet->buffer), &id))
104     goto out;
105
106   SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
107                                               SILC_ID_CLIENT)));
108
109   /* Create local client entry */
110   conn->local_entry = silc_client_add_client(client, conn,
111                                              client->username,
112                                              client->username,
113                                              client->realname,
114                                              &id.u.client_id, 0);
115   if (!conn->local_entry)
116     goto out;
117
118   /* Save the ID.  Take reference to conn->local_id. */
119   conn->local_id = &conn->local_entry->id;
120   conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
121
122   /* Save remote ID */
123   if (packet->src_id_len) {
124     conn->internal->remote_idp =
125       silc_id_payload_encode_data(packet->src_id,
126                                   packet->src_id_len,
127                                   packet->src_id_type);
128     if (!conn->internal->remote_idp)
129       goto out;
130     silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
131                              silc_buffer_len(conn->internal->remote_idp),
132                              &conn->remote_id);
133   }
134
135   /* Set IDs to the packet stream */
136   silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
137                       conn->remote_id.type, SILC_ID_GET_ID(conn->remote_id));
138
139   /* Signal connection that new ID was received so it can continue
140      with the registering. */
141   if (conn->internal->registering)
142     silc_fsm_continue_sync(&conn->internal->event_thread);
143
144  out:
145   /** Packet processed */
146   silc_packet_free(packet);
147   SILC_FSM_FINISH;
148 }
149
150
151 /************************ Register to SILC network **************************/
152
153 /* Register to network */
154
155 SILC_FSM_STATE(silc_client_st_register)
156 {
157   SilcClientConnection conn = fsm_context;
158   SilcClient client = conn->client;
159
160   SILC_LOG_DEBUG(("Register to network"));
161
162   /* Send NEW_CLIENT packet to register to network */
163   if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
164                            SILC_STR_UI_SHORT(strlen(client->username)),
165                            SILC_STR_DATA(client->username,
166                                          strlen(client->username)),
167                            SILC_STR_UI_SHORT(strlen(client->realname)),
168                            SILC_STR_DATA(client->realname,
169                                          strlen(client->realname)),
170                            SILC_STR_END)) {
171     /** Error sending packet */
172     silc_fsm_next(fsm, silc_client_st_register_error);
173     SILC_FSM_CONTINUE;
174   }
175
176   /** Wait for new ID */
177   conn->internal->registering = TRUE;
178   silc_fsm_next_later(fsm, silc_client_st_register_complete,
179                       conn->internal->retry_timer, 0);
180   SILC_FSM_WAIT;
181 }
182
183 /* Wait for NEW_ID packet to arrive */
184
185 SILC_FSM_STATE(silc_client_st_register_complete)
186 {
187   SilcClientConnection conn = fsm_context;
188   SilcClient client = conn->client;
189
190   if (conn->internal->disconnected) {
191     /** Disconnected */
192     silc_fsm_next(fsm, silc_client_st_register_error);
193     SILC_FSM_CONTINUE;
194   }
195
196   if (!conn->local_id) {
197     if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) {
198       /** Timeout, ID not received */
199       conn->internal->registering = FALSE;
200       conn->internal->retry_count = 0;
201       conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
202       silc_fsm_next(fsm, silc_client_st_register_error);
203       SILC_FSM_CONTINUE;
204     }
205
206     /** Resend registering packet */
207     silc_fsm_next(fsm, silc_client_st_register);
208     conn->internal->retry_timer = ((conn->internal->retry_timer *
209                                     SILC_CLIENT_RETRY_MUL) +
210                                    (silc_rng_get_rn16(client->rng) %
211                                     SILC_CLIENT_RETRY_RAND));
212     SILC_FSM_CONTINUE;
213   }
214
215   SILC_LOG_DEBUG(("Registered to network"));
216
217   /* Issue IDENTIFY command for itself to get resolved hostname
218      correctly from server. */
219   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
220                            silc_client_register_command_called, NULL,
221                            1, 5, silc_buffer_data(conn->internal->local_idp),
222                            silc_buffer_len(conn->internal->local_idp));
223
224   /* Call NICK command if the nickname was set by the application (and is
225      not same as the username). */
226   if (conn->internal->params.nickname &&
227       !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
228     silc_client_command_call(client, conn, NULL,
229                              "NICK", conn->internal->params.nickname, NULL);
230
231   /* Issue INFO command to fetch the real server name and server
232      information and other stuff. */
233   silc_client_command_send(client, conn, SILC_COMMAND_INFO,
234                            silc_client_register_command_called, NULL,
235                            1, 2, silc_buffer_data(conn->internal->remote_idp),
236                            silc_buffer_len(conn->internal->remote_idp));
237
238   /* Call connection callback.  We are now inside SILC network. */
239   conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
240                  conn->callback_context);
241
242   conn->internal->registering = FALSE;
243   silc_schedule_task_del_by_all(conn->internal->schedule, 0,
244                                 silc_client_connect_timeout, conn);
245
246   SILC_FSM_FINISH;
247 }
248
249 /* Error registering to network */
250
251 SILC_FSM_STATE(silc_client_st_register_error)
252 {
253   SilcClientConnection conn = fsm_context;
254   SilcClient client = conn->client;
255
256   SILC_LOG_DEBUG(("Error registering to network"));
257
258   /* Signal to close connection */
259   if (!conn->internal->disconnected) {
260     conn->internal->disconnected = TRUE;
261     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
262   }
263
264   /* Call connect callback */
265   if (conn->internal->callback_called)
266     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
267                    conn->callback_context);
268   conn->internal->callback_called = TRUE;
269
270   silc_schedule_task_del_by_all(conn->internal->schedule, 0,
271                                 silc_client_connect_timeout, conn);
272
273   SILC_FSM_FINISH;
274 }
275
276 /************************* Resume detached session **************************/
277
278 /* Resume detached session */
279
280 SILC_FSM_STATE(silc_client_st_resume)
281 {
282   SilcClientConnection conn = fsm_context;
283   SilcClient client = conn->client;
284   SilcClientResumeSession resume;
285   SilcBuffer auth;
286   unsigned char *id;
287   SilcUInt16 id_len;
288   SilcClientID client_id;
289   int ret;
290
291   SILC_LOG_DEBUG(("Resuming detached session"));
292
293   resume = silc_calloc(1, sizeof(*resume));
294   if (!resume) {
295     /** Out of memory */
296     silc_fsm_next(fsm, silc_client_st_resume_error);
297     SILC_FSM_CONTINUE;
298   }
299   silc_fsm_set_state_context(fsm, resume);
300
301   silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
302                   conn->internal->params.detach_data_len);
303   SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
304                    silc_buffer_len(&resume->detach));
305
306   /* Take the old client ID from the detachment data */
307   ret = silc_buffer_unformat(&resume->detach,
308                              SILC_STR_ADVANCE,
309                              SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
310                                                          NULL),
311                              SILC_STR_UI16_NSTRING(&id, &id_len),
312                              SILC_STR_UI_INT(NULL),
313                              SILC_STR_UI_INT(&resume->channel_count),
314                              SILC_STR_END);
315   if (ret < 0) {
316     /** Malformed detach data */
317     SILC_LOG_DEBUG(("Malformed detachment data"));
318     silc_fsm_next(fsm, silc_client_st_resume_error);
319     SILC_FSM_CONTINUE;
320   }
321
322   if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &client_id,
323                       sizeof(client_id))) {
324     /** Malformed ID */
325     SILC_LOG_DEBUG(("Malformed ID"));
326     silc_fsm_next(fsm, silc_client_st_resume_error);
327     SILC_FSM_CONTINUE;
328   }
329
330   /* Generate authentication data that server will verify */
331   auth = silc_auth_public_key_auth_generate(conn->public_key,
332                                             conn->private_key,
333                                             client->rng,
334                                             conn->internal->hash,
335                                             &client_id, SILC_ID_CLIENT);
336   if (!auth) {
337     /** Out of memory */
338     silc_fsm_next(fsm, silc_client_st_resume_error);
339     SILC_FSM_CONTINUE;
340   }
341
342   /* Send RESUME_CLIENT packet to resume to network */
343   if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
344                            SILC_STR_UI_SHORT(id_len),
345                            SILC_STR_DATA(id, id_len),
346                            SILC_STR_DATA(silc_buffer_data(auth),
347                                          silc_buffer_len(auth)),
348                            SILC_STR_END)) {
349     /** Error sending packet */
350     SILC_LOG_DEBUG(("Error sending packet"));
351     silc_fsm_next(fsm, silc_client_st_resume_error);
352     SILC_FSM_CONTINUE;
353   }
354
355   /** Wait for new ID */
356   conn->internal->registering = TRUE;
357   silc_fsm_next_later(fsm, silc_client_st_resume_resolve_channels, 15, 0);
358   SILC_FSM_WAIT;
359 }
360
361 /* Resolve the old session information, user mode and joined channels. */
362
363 SILC_FSM_STATE(silc_client_st_resume_resolve_channels)
364 {
365   SilcClientConnection conn = fsm_context;
366   SilcClient client = conn->client;
367   SilcClientResumeSession resume = state_context;
368   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
369   unsigned char **res_argv = NULL;
370   int i;
371
372   if (conn->internal->disconnected) {
373     /** Disconnected */
374     silc_fsm_next(fsm, silc_client_st_resume_error);
375     SILC_FSM_CONTINUE;
376   }
377
378   if (!conn->local_id) {
379     /** Timeout, ID not received */
380     conn->internal->registering = FALSE;
381     silc_fsm_next(fsm, silc_client_st_resume_error);
382     SILC_FSM_CONTINUE;
383   }
384
385   /** Wait for channels */
386   silc_fsm_next(fsm, silc_client_st_resume_resolve_cmodes);
387
388   /* Change our nickname */
389   silc_client_change_nickname(client, conn, conn->local_entry,
390                               resume->nickname, NULL, NULL, 0);
391
392   /* Send UMODE command to get our own user mode in the network */
393   SILC_LOG_DEBUG(("Resolving user mode"));
394   silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
395                            silc_client_register_command_called, NULL,
396                            1, 1, silc_buffer_data(conn->internal->local_idp),
397                            silc_buffer_len(conn->internal->local_idp));
398
399   if (!resume->channel_count)
400     SILC_FSM_YIELD;
401
402   /* Send IDENTIFY command for all channels we know about.  These are the
403      channels we've joined to according our detachment data. */
404   for (i = 0; i < resume->channel_count; i++) {
405     SilcChannelEntry channel;
406     unsigned char *chid;
407     SilcUInt16 chid_len;
408     SilcBuffer idp;
409     SilcChannelID channel_id;
410     char *name;
411
412     if (silc_buffer_unformat(&resume->detach,
413                              SILC_STR_ADVANCE,
414                              SILC_STR_UI16_NSTRING(&name, NULL),
415                              SILC_STR_UI16_NSTRING(&chid, &chid_len),
416                              SILC_STR_UI_INT(NULL),
417                              SILC_STR_END) < 0)
418       continue;
419
420     if (!silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL, &channel_id,
421                         sizeof(channel_id)))
422       continue;
423     idp = silc_id_payload_encode_data(chid, chid_len, SILC_ID_CHANNEL);
424     if (!idp)
425       continue;
426
427     /* Add the channel to cache */
428     channel = silc_client_get_channel_by_id(client, conn, &channel_id);
429     if (!channel)
430       silc_client_add_channel(client, conn, name, 0, &channel_id);
431
432     res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
433     res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
434                                  (res_argc + 1));
435     res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
436                                   (res_argc + 1));
437     res_argv[res_argc] = silc_buffer_steal(idp, &res_argv_lens[res_argc]);
438     res_argv_types[res_argc] = res_argc + 5;
439     res_argc++;
440     silc_buffer_free(idp);
441   }
442
443   /* Send IDENTIFY command */
444   SILC_LOG_DEBUG(("Resolving joined channels"));
445   silc_client_command_send_argv(client, conn, SILC_COMMAND_IDENTIFY,
446                                 silc_client_resume_continue, conn,
447                                 res_argc, res_argv, res_argv_lens,
448                                 res_argv_types);
449
450   for (i = 0; i < resume->channel_count; i++)
451     silc_free(res_argv[i]);
452   silc_free(res_argv);
453   silc_free(res_argv_lens);
454   silc_free(res_argv_types);
455
456   SILC_FSM_WAIT;
457 }
458
459 /* Resolve joined channel modes, users and topics. */
460
461 SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
462 {
463   SilcClientConnection conn = fsm_context;
464   SilcClient client = conn->client;
465   SilcClientResumeSession resume = state_context;
466   SilcIDCacheEntry entry;
467   SilcChannelEntry channel;
468   SilcList channels;
469   SilcBuffer idp;
470
471   if (conn->internal->disconnected) {
472     /** Disconnected */
473     silc_fsm_next(fsm, silc_client_st_resume_error);
474     SILC_FSM_CONTINUE;
475   }
476
477   SILC_LOG_DEBUG(("Resolving channel details"));
478
479   /** Wait for channel modes */
480   silc_fsm_next(fsm, silc_client_st_resume_completed);
481
482   if (!silc_idcache_get_all(conn->internal->channel_cache, &channels))
483     SILC_FSM_YIELD;
484
485   /* Resolve channels' mode, users and topic */
486   resume->channel_count = silc_list_count(channels) * 3;
487   silc_list_start(channels);
488   while ((entry = silc_list_get(channels))) {
489     channel = entry->context;
490     idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
491     if (!idp)
492       continue;
493
494     silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
495                              silc_client_resume_continue, conn, 1,
496                              1, silc_buffer_data(idp),
497                              silc_buffer_len(idp));
498     silc_client_command_send(client, conn, SILC_COMMAND_USERS,
499                              silc_client_resume_continue, conn, 1,
500                              1, silc_buffer_data(idp),
501                              silc_buffer_len(idp));
502     silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
503                              silc_client_resume_continue, conn, 1,
504                              1, silc_buffer_data(idp),
505                              silc_buffer_len(idp));
506     silc_buffer_free(idp);
507   }
508
509   SILC_FSM_WAIT;
510 }
511
512 /* Resuming completed */
513
514 SILC_FSM_STATE(silc_client_st_resume_completed)
515 {
516   SilcClientConnection conn = fsm_context;
517   SilcClient client = conn->client;
518   SilcClientResumeSession resume = state_context;
519   SilcIDCacheEntry entry;
520   SilcChannelEntry channel;
521   SilcList channels;
522
523   if (conn->internal->disconnected) {
524     /** Disconnected */
525     silc_fsm_next(fsm, silc_client_st_resume_error);
526     SILC_FSM_CONTINUE;
527   }
528
529   if (resume->channel_count > 0) {
530     resume->channel_count--;
531     if (resume->channel_count)
532       SILC_FSM_WAIT;
533   }
534
535   SILC_LOG_DEBUG(("Resuming completed"));
536
537   /* Issue IDENTIFY command for itself to get resolved hostname
538      correctly from server. */
539   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
540                            silc_client_register_command_called, NULL,
541                            1, 5, silc_buffer_data(conn->internal->local_idp),
542                            silc_buffer_len(conn->internal->local_idp));
543
544   /* Issue INFO command to fetch the real server name and server
545      information and other stuff. */
546   silc_client_command_send(client, conn, SILC_COMMAND_INFO,
547                            silc_client_register_command_called, NULL,
548                            1, 2, silc_buffer_data(conn->internal->remote_idp),
549                            silc_buffer_len(conn->internal->remote_idp));
550
551   /* Call connection callback.  We have now resumed to SILC network. */
552   conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS_RESUME, 0, NULL,
553                  conn->callback_context);
554
555   /* Call UMODE command reply. */
556   if (conn->local_entry->mode)
557     silc_client_resume_command_callback(client, conn, SILC_COMMAND_UMODE,
558                                         conn->local_entry->mode);
559
560   /* Call NICK command reply. */
561   silc_client_resume_command_callback(client, conn, SILC_COMMAND_NICK,
562                                       conn->local_entry,
563                                       conn->local_entry->nickname,
564                                       &conn->local_entry->id);
565
566   /* Call JOIN command replies for all joined channel */
567   silc_idcache_get_all(conn->internal->channel_cache, &channels);
568   silc_list_start(channels);
569   while ((entry = silc_list_get(channels))) {
570     SilcHashTableList htl;
571     const char *cipher, *hmac;
572
573     channel = entry->context;
574     cipher = (channel->internal.send_key ?
575               silc_cipher_get_name(channel->internal.send_key) : NULL);
576     hmac = (channel->internal.hmac ?
577             silc_hmac_get_name(channel->internal.hmac) : NULL);
578     silc_hash_table_list(channel->user_list, &htl);
579     silc_client_resume_command_callback(client, conn, SILC_COMMAND_JOIN,
580                                         channel->channel_name, channel,
581                                         channel->mode, &htl, channel->topic,
582                                         cipher, hmac, channel->founder_key,
583                                         channel->channel_pubkeys,
584                                         channel->user_limit);
585     silc_hash_table_list_reset(&htl);
586   }
587
588   conn->internal->registering = FALSE;
589   silc_schedule_task_del_by_all(conn->internal->schedule, 0,
590                                 silc_client_connect_timeout, conn);
591   silc_free(resume->nickname);
592   silc_free(resume);
593
594   SILC_FSM_FINISH;
595 }
596
597 /* Error resuming to network */
598
599 SILC_FSM_STATE(silc_client_st_resume_error)
600 {
601   SilcClientConnection conn = fsm_context;
602   SilcClient client = conn->client;
603   SilcClientResumeSession resume = state_context;
604
605   if (conn->internal->disconnected) {
606     if (resume) {
607       silc_free(resume->nickname);
608       silc_free(resume);
609     }
610     SILC_FSM_FINISH;
611   }
612
613   SILC_LOG_DEBUG(("Error resuming to network"));
614
615   /* Signal to close connection */
616   if (!conn->internal->disconnected) {
617     conn->internal->disconnected = TRUE;
618     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
619   }
620
621   /* Call connect callback */
622   if (conn->internal->callback_called)
623     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
624                    conn->callback_context);
625   conn->internal->callback_called = TRUE;
626
627   silc_schedule_task_del_by_all(conn->internal->schedule, 0,
628                                 silc_client_connect_timeout, conn);
629
630   if (resume) {
631     silc_free(resume->nickname);
632     silc_free(resume);
633   }
634
635   SILC_FSM_FINISH;
636 }
637
638 /* Generates the session detachment data. This data can be used later
639    to resume back to the server. */
640
641 SilcBuffer silc_client_get_detach_data(SilcClient client,
642                                        SilcClientConnection conn)
643 {
644   SilcBuffer detach;
645   SilcHashTableList htl;
646   SilcChannelUser chu;
647   unsigned char id[64];
648   SilcUInt32 id_len;
649   int ret, ch_count;
650
651   SILC_LOG_DEBUG(("Creating detachment data"));
652
653   ch_count = silc_hash_table_count(conn->local_entry->channels);
654   silc_id_id2str(conn->local_id, SILC_ID_CLIENT, id, sizeof(id), &id_len);
655
656   /* Save the nickname, Client ID and user mode in SILC network */
657   detach = silc_buffer_alloc(0);
658   if (!detach)
659     return NULL;
660   ret =
661     silc_buffer_format(detach,
662                        SILC_STR_ADVANCE,
663                        SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
664                        SILC_STR_DATA(conn->local_entry->nickname,
665                                      strlen(conn->local_entry->nickname)),
666                        SILC_STR_UI_SHORT(id_len),
667                        SILC_STR_DATA(id, id_len),
668                        SILC_STR_UI_INT(conn->local_entry->mode),
669                        SILC_STR_UI_INT(ch_count),
670                        SILC_STR_END);
671   if (ret < 0) {
672     silc_buffer_free(detach);
673     return NULL;
674   }
675
676   /* Save all joined channels */
677   silc_hash_table_list(conn->local_entry->channels, &htl);
678   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
679     unsigned char chid[32];
680     SilcUInt32 chid_len;
681
682     silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
683                    &chid_len);
684     silc_buffer_format(detach,
685                        SILC_STR_ADVANCE,
686                        SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
687                        SILC_STR_DATA(chu->channel->channel_name,
688                                      strlen(chu->channel->channel_name)),
689                        SILC_STR_UI_SHORT(chid_len),
690                        SILC_STR_DATA(chid, chid_len),
691                        SILC_STR_UI_INT(chu->channel->mode),
692                        SILC_STR_END);
693   }
694   silc_hash_table_list_reset(&htl);
695
696   silc_buffer_start(detach);
697   SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
698                    silc_buffer_len(detach));
699
700   return detach;
701 }