More client library rewrites.
[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 TRUE;
53 }
54
55 /****************************** NEW_ID packet *******************************/
56
57 /* Received new ID packet from server during registering to SILC network */
58
59 SILC_FSM_STATE(silc_client_new_id)
60 {
61   SilcClientConnection conn = fsm_context;
62   SilcClient client = conn->client;
63   SilcPacket packet = state_context;
64   SilcID id;
65
66   if (conn->local_id)
67     goto out;
68
69   SILC_LOG_DEBUG(("New ID received from server"));
70
71   if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
72                                 silc_buffer_len(&packet->buffer), &id))
73     goto out;
74
75   SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
76                                               SILC_ID_CLIENT)));
77
78   /* Create local client entry */
79   conn->local_entry = silc_client_add_client(client, conn,
80                                              client->username,
81                                              client->username,
82                                              client->realname,
83                                              &id.u.client_id, 0);
84   if (!conn->local_entry)
85     goto out;
86
87   /* Save the ID */
88   conn->local_id = &conn->local_entry->id;
89   conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
90
91   /* Save cache entry */
92   silc_mutex_lock(conn->internal->lock);
93   if (!silc_idcache_find_by_id_one(conn->internal->client_cache,
94                                    conn->local_id,
95                                    &conn->internal->local_entry)) {
96     silc_mutex_unlock(conn->internal->lock);
97     goto out;
98   }
99   silc_mutex_unlock(conn->internal->lock);
100
101   /* Save remote ID */
102   if (packet->src_id_len) {
103     conn->internal->remote_idp =
104       silc_id_payload_encode_data(packet->src_id,
105                                   packet->src_id_len,
106                                   packet->src_id_type);
107     if (!conn->internal->remote_idp)
108       goto out;
109     silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
110                              silc_buffer_len(conn->internal->remote_idp),
111                              &conn->remote_id);
112   }
113
114   /* Set IDs to the packet stream */
115   silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
116                       conn->remote_id.type, SILC_ID_GET_ID(conn->remote_id));
117
118   /* Signal connection that new ID was received so it can continue
119      with the registering. */
120   if (conn->internal->registering)
121     silc_fsm_continue_sync(&conn->internal->event_thread);
122
123  out:
124   /** Packet processed */
125   silc_packet_free(packet);
126   return SILC_FSM_FINISH;
127 }
128
129
130 /************************ Register to SILC network **************************/
131
132 /* Register to network */
133
134 SILC_FSM_STATE(silc_client_st_register)
135 {
136   SilcClientConnection conn = fsm_context;
137   SilcClient client = conn->client;
138
139   SILC_LOG_DEBUG(("Register to network"));
140
141   /* Send NEW_CLIENT packet to register to network */
142   if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
143                            SILC_STR_UI_SHORT(strlen(client->username)),
144                            SILC_STR_DATA(client->username,
145                                          strlen(client->username)),
146                            SILC_STR_UI_SHORT(strlen(client->realname)),
147                            SILC_STR_DATA(client->realname,
148                                          strlen(client->realname)),
149                            SILC_STR_END)) {
150     /** Error sending packet */
151     silc_fsm_next(fsm, silc_client_st_register_error);
152     return SILC_FSM_CONTINUE;
153   }
154
155   /** Wait for new ID */
156   conn->internal->registering = TRUE;
157   silc_fsm_next_later(fsm, silc_client_st_register_complete,
158                       conn->internal->retry_timer, 0);
159   return SILC_FSM_WAIT;
160 }
161
162 /* Wait for NEW_ID packet to arrive */
163
164 SILC_FSM_STATE(silc_client_st_register_complete)
165 {
166   SilcClientConnection conn = fsm_context;
167   SilcClient client = conn->client;
168
169   if (!conn->local_id) {
170     if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) {
171       /** Timeout, ID not received */
172       conn->internal->registering = FALSE;
173       conn->internal->retry_count = 0;
174       conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
175       silc_fsm_next(fsm, silc_client_st_register_error);
176       return SILC_FSM_CONTINUE;
177     }
178
179     /** Resend registering packet */
180     silc_fsm_next(fsm, silc_client_st_register);
181     conn->internal->retry_timer = ((conn->internal->retry_timer *
182                                    SILC_CLIENT_RETRY_MUL) +
183                                    (silc_rng_get_rn16(client->rng) %
184                                     SILC_CLIENT_RETRY_RAND));
185     return SILC_FSM_CONTINUE;
186   }
187
188   SILC_LOG_DEBUG(("Registered to network"));
189
190   /* Issue IDENTIFY command for itself to get resolved hostname
191      correctly from server. */
192   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
193                            silc_client_register_command_called, NULL,
194                            1, 5, silc_buffer_data(conn->internal->local_idp),
195                            silc_buffer_len(conn->internal->local_idp));
196
197   /* Call NICK command if the nickname was set by the application (and is
198      not same as the username). */
199   if (conn->internal->params.nickname &&
200       !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
201     silc_client_command_call(client, conn, NULL,
202                              "NICK", conn->internal->params.nickname, NULL);
203
204   /* Issue INFO command to fetch the real server name and server
205      information and other stuff. */
206   silc_client_command_send(client, conn, SILC_COMMAND_INFO,
207                            silc_client_register_command_called, NULL,
208                            1, 2, silc_buffer_data(conn->internal->remote_idp),
209                            silc_buffer_len(conn->internal->remote_idp));
210
211   /* Call connection callback.  We are now inside SILC network. */
212   conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
213                  conn->callback_context);
214
215   conn->internal->registering = FALSE;
216   return SILC_FSM_FINISH;
217 }
218
219 /* Error registering to network */
220
221 SILC_FSM_STATE(silc_client_st_register_error)
222 {
223   SilcClientConnection conn = fsm_context;
224   SilcClient client = conn->client;
225
226   SILC_LOG_DEBUG(("Error registering to network"));
227
228   /* Signal to close connection */
229   conn->internal->disconnected = TRUE;
230   SILC_FSM_SEMA_POST(&conn->internal->wait_event);
231
232   /* Call connect callback */
233   conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
234                  conn->callback_context);
235
236   return SILC_FSM_FINISH;
237 }
238
239
240 /************************* Resume detached session **************************/
241
242 /* Resume detached session */
243
244 SILC_FSM_STATE(silc_client_st_resume)
245 {
246   SilcClientConnection conn = fsm_context;
247   SilcClient client = conn->client;
248   SilcClientResumeSession resume;
249   SilcBuffer auth;
250   unsigned char *id;
251   SilcUInt16 id_len;
252   int ret;
253
254   SILC_LOG_DEBUG(("Resuming detached session"));
255
256   resume = silc_calloc(1, sizeof(*resume));
257   if (!resume) {
258     /** Out of memory */
259     silc_fsm_next(fsm, silc_client_st_resume_error);
260     return SILC_FSM_CONTINUE;
261   }
262   silc_fsm_set_state_context(fsm, resume);
263
264   silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
265                   conn->internal->params.detach_data_len);
266   SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
267                    silc_buffer_len(&resume->detach));
268
269   /* Take the old client ID from the detachment data */
270   ret = silc_buffer_unformat(&resume->detach,
271                              SILC_STR_ADVANCE,
272                              SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
273                                                          NULL),
274                              SILC_STR_UI16_NSTRING(&id, &id_len),
275                              SILC_STR_UI_INT(NULL),
276                              SILC_STR_UI_INT(&resume->channel_count),
277                              SILC_STR_END);
278   if (ret < 0) {
279     /** Malformed detach data */
280     silc_fsm_next(fsm, silc_client_st_resume_error);
281     return SILC_FSM_CONTINUE;
282   }
283
284   if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &resume->client_id,
285                       sizeof(resume->client_id))) {
286     /** Malformed ID */
287     silc_fsm_next(fsm, silc_client_st_resume_error);
288     return SILC_FSM_CONTINUE;
289   }
290
291   /* Generate authentication data that server will verify */
292   auth = silc_auth_public_key_auth_generate(conn->public_key,
293                                             conn->private_key,
294                                             client->rng,
295                                             conn->internal->hash,
296                                             &resume->client_id,
297                                             SILC_ID_CLIENT);
298   if (!auth) {
299     /** Out of memory */
300     silc_fsm_next(fsm, silc_client_st_resume_error);
301     return SILC_FSM_CONTINUE;
302   }
303
304   /* Send RESUME_CLIENT packet to resume to network */
305   if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
306                            SILC_STR_UI_SHORT(id_len),
307                            SILC_STR_UI_XNSTRING(id, id_len),
308                            SILC_STR_UI_XNSTRING(silc_buffer_data(auth),
309                                                 silc_buffer_len(auth)),
310                            SILC_STR_END)) {
311     /** Error sending packet */
312     silc_fsm_next(fsm, silc_client_st_resume_error);
313     return SILC_FSM_CONTINUE;
314   }
315
316   /** Wait for new ID */
317   conn->internal->registering = TRUE;
318   silc_fsm_next_later(fsm, silc_client_st_resume_resolve, 15, 0);
319   return SILC_FSM_WAIT;
320 }
321
322 /* Resolve the old session information */
323
324 SILC_FSM_STATE(silc_client_st_resume_resolve)
325 {
326 #if 0
327   SilcClientConnection conn = fsm_context;
328   SilcClientResumeSession resume = state_context;
329
330   if (!conn->local_id) {
331     /** Timeout, ID not received */
332     conn->internal->registering = FALSE;
333     silc_fsm_next(fsm, silc_client_st_resume_error);
334     return SILC_FSM_CONTINUE;
335   }
336
337
338   for (i = 0; i < ch_count; i++) {
339     char *channel;
340     unsigned char *chid;
341     SilcUInt16 chid_len;
342     SilcUInt32 ch_mode;
343     SilcChannelID *channel_id;
344     SilcChannelEntry channel_entry;
345
346     len = silc_buffer_unformat(&detach,
347                                SILC_STR_UI16_NSTRING_ALLOC(&channel, NULL),
348                                SILC_STR_UI16_NSTRING(&chid, &chid_len),
349                                SILC_STR_UI_INT(&ch_mode),
350                                SILC_STR_END);
351     if (len == -1)
352       return FALSE;
353
354     /* Add new channel */
355     channel_id = silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL);
356     channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
357     if (!channel_entry) {
358       channel_entry = silc_client_add_channel(client, conn, channel, ch_mode,
359                                               channel_id);
360     } else {
361       silc_free(channel);
362       silc_free(channel_id);
363     }
364
365     silc_buffer_pull(&detach, len);
366   }
367 #endif /* 0 */
368
369   return SILC_FSM_FINISH;
370 }
371
372 SILC_FSM_STATE(silc_client_st_resume_error)
373 {
374   /* XXX */
375   /* Close connection */
376
377   return SILC_FSM_FINISH;
378 }
379
380 /* Generates the session detachment data. This data can be used later
381    to resume back to the server. */
382
383 SilcBuffer silc_client_get_detach_data(SilcClient client,
384                                        SilcClientConnection conn)
385 {
386   SilcBuffer detach;
387   SilcHashTableList htl;
388   SilcChannelUser chu;
389   int ret, ch_count;
390
391   SILC_LOG_DEBUG(("Creating detachment data"));
392
393   ch_count = silc_hash_table_count(conn->local_entry->channels);
394
395   /* Save the nickname, Client ID and user mode in SILC network */
396   detach = silc_buffer_alloc(0);
397   if (!detach)
398     return NULL;
399   ret =
400     silc_buffer_format(detach,
401                        SILC_STR_ADVANCE,
402                        SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
403                        SILC_STR_DATA(conn->local_entry->nickname,
404                                      strlen(conn->local_entry->nickname)),
405                        SILC_STR_UI_SHORT(silc_buffer_len(conn->internal->
406                                                          local_idp)),
407                        SILC_STR_DATA(silc_buffer_data(conn->internal->
408                                                       local_idp),
409                                      silc_buffer_len(conn->internal->
410                                                      local_idp)),
411                        SILC_STR_UI_INT(conn->local_entry->mode),
412                        SILC_STR_UI_INT(ch_count),
413                        SILC_STR_END);
414   if (ret < 0) {
415     silc_buffer_free(detach);
416     return NULL;
417   }
418
419   /* Save all joined channels */
420   silc_hash_table_list(conn->local_entry->channels, &htl);
421   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
422     unsigned char chid[32];
423     SilcUInt32 chid_len;
424
425     silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
426                    &chid_len);
427     silc_buffer_format(detach,
428                        SILC_STR_ADVANCE,
429                        SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
430                        SILC_STR_DATA(chu->channel->channel_name,
431                                      strlen(chu->channel->channel_name)),
432                        SILC_STR_UI_SHORT(chid_len),
433                        SILC_STR_DATA(chid, chid_len),
434                        SILC_STR_UI_INT(chu->channel->mode),
435                        SILC_STR_END);
436     silc_free(chid);
437   }
438   silc_hash_table_list_reset(&htl);
439
440   silc_buffer_start(detach);
441   SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
442                    silc_buffer_len(detach));
443
444   return detach;
445 }