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