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