Handle command reply lists in threads.
[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   SilcClientID client_id;
33   SilcUInt32 channel_count;
34   SilcUInt32 *cmd_idents;
35   SilcUInt32 cmd_idents_count;
36   SilcBool success;
37 } *SilcClientResumeSession;
38
39 /************************ Static utility functions **************************/
40
41 /* Command callback.  Nothing interesting to do here. */
42
43 static SilcBool
44 silc_client_register_command_called(SilcClient client,
45                                     SilcClientConnection conn,
46                                     SilcCommand command,
47                                     SilcStatus status,
48                                     SilcStatus error,
49                                     void *context,
50                                     va_list ap)
51 {
52   return FALSE;
53 }
54
55 /* Continues resuming after resolving.  Continue after last reply. */
56
57 static SilcBool
58 silc_client_resume_continue(SilcClient client,
59                             SilcClientConnection conn,
60                             SilcCommand command,
61                             SilcStatus status,
62                             SilcStatus error,
63                             void *context,
64                             va_list ap)
65 {
66   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END ||
67       SILC_STATUS_IS_ERROR(status)) {
68     silc_fsm_continue(&conn->internal->event_thread);
69     return FALSE;
70   }
71
72   return TRUE;
73 }
74
75 /****************************** NEW_ID packet *******************************/
76
77 /* Received new ID packet from server during registering to SILC network */
78
79 SILC_FSM_STATE(silc_client_new_id)
80 {
81   SilcClientConnection conn = fsm_context;
82   SilcClient client = conn->client;
83   SilcPacket packet = state_context;
84   SilcID id;
85
86   if (conn->local_id)
87     goto out;
88
89   SILC_LOG_DEBUG(("New ID received from server"));
90
91   if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
92                                 silc_buffer_len(&packet->buffer), &id))
93     goto out;
94
95   SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
96                                               SILC_ID_CLIENT)));
97
98   /* Create local client entry */
99   conn->local_entry = silc_client_add_client(client, conn,
100                                              client->username,
101                                              client->username,
102                                              client->realname,
103                                              &id.u.client_id, 0);
104   if (!conn->local_entry)
105     goto out;
106
107   /* Save the ID */
108   conn->local_id = &conn->local_entry->id;
109   conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
110
111   /* Save cache entry */
112   silc_mutex_lock(conn->internal->lock);
113   if (!silc_idcache_find_by_id_one(conn->internal->client_cache,
114                                    conn->local_id,
115                                    &conn->internal->local_entry)) {
116     silc_mutex_unlock(conn->internal->lock);
117     goto out;
118   }
119   silc_mutex_unlock(conn->internal->lock);
120
121   /* Save remote ID */
122   if (packet->src_id_len) {
123     conn->internal->remote_idp =
124       silc_id_payload_encode_data(packet->src_id,
125                                   packet->src_id_len,
126                                   packet->src_id_type);
127     if (!conn->internal->remote_idp)
128       goto out;
129     silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
130                              silc_buffer_len(conn->internal->remote_idp),
131                              &conn->remote_id);
132   }
133
134   /* Set IDs to the packet stream */
135   silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
136                       conn->remote_id.type, SILC_ID_GET_ID(conn->remote_id));
137
138   /* Signal connection that new ID was received so it can continue
139      with the registering. */
140   if (conn->internal->registering)
141     silc_fsm_continue_sync(&conn->internal->event_thread);
142
143  out:
144   /** Packet processed */
145   silc_packet_free(packet);
146   return SILC_FSM_FINISH;
147 }
148
149
150 /************************ Register to SILC network **************************/
151
152 /* Register to network */
153
154 SILC_FSM_STATE(silc_client_st_register)
155 {
156   SilcClientConnection conn = fsm_context;
157   SilcClient client = conn->client;
158
159   SILC_LOG_DEBUG(("Register to network"));
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_END)) {
170     /** Error sending packet */
171     silc_fsm_next(fsm, silc_client_st_register_error);
172     return SILC_FSM_CONTINUE;
173   }
174
175   /** Wait for new ID */
176   conn->internal->registering = TRUE;
177   silc_fsm_next_later(fsm, silc_client_st_register_complete,
178                       conn->internal->retry_timer, 0);
179   return SILC_FSM_WAIT;
180 }
181
182 /* Wait for NEW_ID packet to arrive */
183
184 SILC_FSM_STATE(silc_client_st_register_complete)
185 {
186   SilcClientConnection conn = fsm_context;
187   SilcClient client = conn->client;
188
189   if (conn->internal->disconnected) {
190     /** Disconnected */
191     silc_fsm_next(fsm, silc_client_st_register_error);
192     return SILC_FSM_CONTINUE;
193   }
194
195   if (!conn->local_id) {
196     if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) {
197       /** Timeout, ID not received */
198       conn->internal->registering = FALSE;
199       conn->internal->retry_count = 0;
200       conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
201       silc_fsm_next(fsm, silc_client_st_register_error);
202       return SILC_FSM_CONTINUE;
203     }
204
205     /** Resend registering packet */
206     silc_fsm_next(fsm, silc_client_st_register);
207     conn->internal->retry_timer = ((conn->internal->retry_timer *
208                                     SILC_CLIENT_RETRY_MUL) +
209                                    (silc_rng_get_rn16(client->rng) %
210                                     SILC_CLIENT_RETRY_RAND));
211     return SILC_FSM_CONTINUE;
212   }
213
214   SILC_LOG_DEBUG(("Registered to network"));
215
216   /* Issue IDENTIFY command for itself to get resolved hostname
217      correctly from server. */
218   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
219                            silc_client_register_command_called, NULL,
220                            1, 5, silc_buffer_data(conn->internal->local_idp),
221                            silc_buffer_len(conn->internal->local_idp));
222
223   /* Call NICK command if the nickname was set by the application (and is
224      not same as the username). */
225   if (conn->internal->params.nickname &&
226       !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
227     silc_client_command_call(client, conn, NULL,
228                              "NICK", conn->internal->params.nickname, NULL);
229
230   /* Issue INFO command to fetch the real server name and server
231      information and other stuff. */
232   silc_client_command_send(client, conn, SILC_COMMAND_INFO,
233                            silc_client_register_command_called, NULL,
234                            1, 2, silc_buffer_data(conn->internal->remote_idp),
235                            silc_buffer_len(conn->internal->remote_idp));
236
237   /* Call connection callback.  We are now inside SILC network. */
238   conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
239                  conn->callback_context);
240
241   conn->internal->registering = FALSE;
242   silc_schedule_task_del_by_all(conn->internal->schedule, 0,
243                                 silc_client_connect_timeout, conn);
244
245   return SILC_FSM_FINISH;
246 }
247
248 /* Error registering to network */
249
250 SILC_FSM_STATE(silc_client_st_register_error)
251 {
252   SilcClientConnection conn = fsm_context;
253   SilcClient client = conn->client;
254
255   SILC_LOG_DEBUG(("Error registering to network"));
256
257   /* Signal to close connection */
258   if (!conn->internal->disconnected) {
259     conn->internal->disconnected = TRUE;
260     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
261   }
262
263   /* Call connect callback */
264   if (conn->internal->callback_called)
265     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
266                    conn->callback_context);
267   conn->internal->callback_called = TRUE;
268
269   silc_schedule_task_del_by_all(conn->internal->schedule, 0,
270                                 silc_client_connect_timeout, conn);
271
272   return SILC_FSM_FINISH;
273 }
274
275 /************************* Resume detached session **************************/
276
277 /* Resume detached session */
278
279 SILC_FSM_STATE(silc_client_st_resume)
280 {
281   SilcClientConnection conn = fsm_context;
282   SilcClient client = conn->client;
283   SilcClientResumeSession resume;
284   SilcBuffer auth;
285   unsigned char *id;
286   SilcUInt16 id_len;
287   int ret;
288
289   SILC_LOG_DEBUG(("Resuming detached session"));
290
291   resume = silc_calloc(1, sizeof(*resume));
292   if (!resume) {
293     /** Out of memory */
294     silc_fsm_next(fsm, silc_client_st_resume_error);
295     return SILC_FSM_CONTINUE;
296   }
297   silc_fsm_set_state_context(fsm, resume);
298
299   silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
300                   conn->internal->params.detach_data_len);
301   SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
302                    silc_buffer_len(&resume->detach));
303
304   /* Take the old client ID from the detachment data */
305   ret = silc_buffer_unformat(&resume->detach,
306                              SILC_STR_ADVANCE,
307                              SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
308                                                          NULL),
309                              SILC_STR_UI16_NSTRING(&id, &id_len),
310                              SILC_STR_UI_INT(NULL),
311                              SILC_STR_UI_INT(&resume->channel_count),
312                              SILC_STR_END);
313   if (ret < 0) {
314     /** Malformed detach 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, &resume->client_id,
320                       sizeof(resume->client_id))) {
321     /** Malformed ID */
322     silc_fsm_next(fsm, silc_client_st_resume_error);
323     return SILC_FSM_CONTINUE;
324   }
325
326   /* Generate authentication data that server will verify */
327   auth = silc_auth_public_key_auth_generate(conn->public_key,
328                                             conn->private_key,
329                                             client->rng,
330                                             conn->internal->hash,
331                                             &resume->client_id,
332                                             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_fsm_next(fsm, silc_client_st_resume_error);
348     return SILC_FSM_CONTINUE;
349   }
350
351   /** Wait for new ID */
352   conn->internal->registering = TRUE;
353   silc_fsm_next_later(fsm, silc_client_st_resume_resolve_channels, 15, 0);
354   return SILC_FSM_WAIT;
355 }
356
357 /* Resolve the old session information, user mode and joined channels. */
358
359 SILC_FSM_STATE(silc_client_st_resume_resolve_channels)
360 {
361   SilcClientConnection conn = fsm_context;
362   SilcClient client = conn->client;
363   SilcClientResumeSession resume = state_context;
364   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
365   unsigned char **res_argv = NULL;
366   int i;
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   /* First, send UMODE command to get our own user mode in the network */
376   SILC_LOG_DEBUG(("Resolving user mode"));
377   silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
378                            silc_client_register_command_called, NULL,
379                            1, 1, silc_buffer_data(conn->internal->local_idp),
380                            silc_buffer_len(conn->internal->local_idp));
381
382   /* Second, send IDENTIFY command for all channels we know about.  These
383      are the channels we've joined to according our detachment data. */
384   for (i = 0; i < resume->channel_count; i++) {
385     SilcChannelID channel_id;
386     unsigned char *chid;
387     SilcUInt16 chid_len;
388     SilcBuffer idp;
389
390     if (silc_buffer_unformat(&resume->detach,
391                              SILC_STR_ADVANCE,
392                              SILC_STR_UI16_NSTRING(NULL, NULL),
393                              SILC_STR_UI16_NSTRING(&chid, &chid_len),
394                              SILC_STR_UI_INT(NULL),
395                              SILC_STR_END) < 0)
396       continue;
397
398     idp = silc_id_payload_encode_data(chid, chid_len, SILC_ID_CHANNEL);
399     if (!idp)
400       continue;
401     res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
402     res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
403                                  (res_argc + 1));
404     res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
405                                   (res_argc + 1));
406     res_argv[res_argc] = silc_buffer_steal(idp, &res_argv_lens[res_argc]);
407     res_argv_types[res_argc] = res_argc + 5;
408     res_argc++;
409     silc_buffer_free(idp);
410   }
411
412   /* Send IDENTIFY command */
413   SILC_LOG_DEBUG(("Resolving joined channels"));
414   silc_client_command_send_argv(client, conn, SILC_COMMAND_IDENTIFY,
415                                 silc_client_resume_continue, conn,
416                                 res_argc, res_argv, res_argv_lens,
417                                 res_argv_types);
418
419   for (i = 0; i < resume->channel_count; i++)
420     silc_free(res_argv[i]);
421   silc_free(res_argv);
422   silc_free(res_argv_lens);
423   silc_free(res_argv_types);
424
425   /** Wait for channels */
426   silc_fsm_next(fsm, silc_client_st_resume_resolve_cmodes);
427   return SILC_FSM_WAIT;
428 }
429
430 /* Resolve joined channel modes. */
431
432 SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
433 {
434   SilcClientConnection conn = fsm_context;
435   SilcClientResumeSession resume = state_context;
436   SilcHashTableList htl;
437   SilcChannelUser chu;
438
439   SILC_LOG_DEBUG(("Resolving joined channel modes"));
440
441   silc_hash_table_list(conn->local_entry->channels, &htl);
442   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
443
444   }
445   silc_hash_table_list_reset(&htl);
446
447   return SILC_FSM_FINISH;
448 }
449
450 SILC_FSM_STATE(silc_client_st_resume_error)
451 {
452   /* XXX */
453   /* Close connection */
454
455   return SILC_FSM_FINISH;
456 }
457
458 /* Generates the session detachment data. This data can be used later
459    to resume back to the server. */
460
461 SilcBuffer silc_client_get_detach_data(SilcClient client,
462                                        SilcClientConnection conn)
463 {
464   SilcBuffer detach;
465   SilcHashTableList htl;
466   SilcChannelUser chu;
467   int ret, ch_count;
468
469   SILC_LOG_DEBUG(("Creating detachment data"));
470
471   ch_count = silc_hash_table_count(conn->local_entry->channels);
472
473   /* Save the nickname, Client ID and user mode in SILC network */
474   detach = silc_buffer_alloc(0);
475   if (!detach)
476     return NULL;
477   ret =
478     silc_buffer_format(detach,
479                        SILC_STR_ADVANCE,
480                        SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
481                        SILC_STR_DATA(conn->local_entry->nickname,
482                                      strlen(conn->local_entry->nickname)),
483                        SILC_STR_UI_SHORT(silc_buffer_len(conn->internal->
484                                                          local_idp)),
485                        SILC_STR_DATA(silc_buffer_data(conn->internal->
486                                                       local_idp),
487                                      silc_buffer_len(conn->internal->
488                                                      local_idp)),
489                        SILC_STR_UI_INT(conn->local_entry->mode),
490                        SILC_STR_UI_INT(ch_count),
491                        SILC_STR_END);
492   if (ret < 0) {
493     silc_buffer_free(detach);
494     return NULL;
495   }
496
497   /* Save all joined channels */
498   silc_hash_table_list(conn->local_entry->channels, &htl);
499   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
500     unsigned char chid[32];
501     SilcUInt32 chid_len;
502
503     silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
504                    &chid_len);
505     silc_buffer_format(detach,
506                        SILC_STR_ADVANCE,
507                        SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
508                        SILC_STR_DATA(chu->channel->channel_name,
509                                      strlen(chu->channel->channel_name)),
510                        SILC_STR_UI_SHORT(chid_len),
511                        SILC_STR_DATA(chid, chid_len),
512                        SILC_STR_UI_INT(chu->channel->mode),
513                        SILC_STR_END);
514     silc_free(chid);
515   }
516   silc_hash_table_list_reset(&htl);
517
518   silc_buffer_start(detach);
519   SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
520                    silc_buffer_len(detach));
521
522   return detach;
523 }