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