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