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