More client library rewrites.
[silc.git] / lib / silcclient / client.c
1 /*
2
3   client.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 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 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /************************** Types and definitions ***************************/
26
27
28 /************************ Static utility functions **************************/
29
30 /* Connection machine FSM destructor.  This will finish the thread where
31    the machine was running and deletes the connection context. */
32
33 static void silc_client_connection_destructor(SilcFSM fsm,
34                                               void *fsm_context,
35                                               void *destructor_context)
36 {
37   SilcClientConnection conn = fsm_context;
38   SilcFSMThread thread = destructor_context;
39
40   /* Delete connection */
41   silc_client_del_connection(conn->client, conn);
42
43   /* Finish the thread were this machine was running */
44   silc_fsm_finish(thread);
45 }
46
47 /* Packet FSM thread destructor */
48
49 static void silc_client_packet_destructor(SilcFSMThread thread,
50                                           void *thread_context,
51                                           void *destructor_context)
52 {
53   SilcClientConnection conn = thread_context;
54
55   /* Add thread back to thread pool */
56   silc_list_add(conn->internal->thread_pool, thread);
57   if (silc_list_count(conn->internal->thread_pool) == 1)
58     silc_list_start(conn->internal->thread_pool);
59 }
60
61 /* Packet engine callback to receive a packet */
62
63 static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
64                                            SilcPacketStream stream,
65                                            SilcPacket packet,
66                                            void *callback_context,
67                                            void *stream_context)
68 {
69   SilcClientConnection conn = stream_context;
70   SilcFSMThread thread;
71
72   /* Packets we do not handle */
73   switch (packet->type) {
74   case SILC_PACKET_HEARTBEAT:
75   case SILC_PACKET_SUCCESS:
76   case SILC_PACKET_FAILURE:
77   case SILC_PACKET_REJECT:
78   case SILC_PACKET_KEY_EXCHANGE:
79   case SILC_PACKET_KEY_EXCHANGE_1:
80   case SILC_PACKET_KEY_EXCHANGE_2:
81   case SILC_PACKET_REKEY:
82   case SILC_PACKET_REKEY_DONE:
83   case SILC_PACKET_CONNECTION_AUTH:
84   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
85     return FALSE;
86     break;
87   }
88
89   /* Get packet processing thread */
90   thread = silc_list_get(conn->internal->thread_pool);
91   if (!thread) {
92     thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
93                                    silc_client_packet_destructor, NULL, FALSE);
94     if (!thread)
95       return FALSE;
96   } else {
97     silc_list_del(conn->internal->thread_pool, thread);
98     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
99                          silc_client_packet_destructor, NULL, FALSE);
100   }
101
102   /* Process packet in thread */
103   silc_fsm_set_state_context(thread, packet);
104   silc_fsm_start_sync(thread, silc_client_connection_st_packet);
105
106   return TRUE;
107 }
108
109 /* Packet engine callback to indicate end of stream */
110
111 static void silc_client_packet_eos(SilcPacketEngine engine,
112                                    SilcPacketStream stream,
113                                    void *callback_context,
114                                    void *stream_context)
115 {
116   SILC_LOG_DEBUG(("End of stream received"));
117 }
118
119 /* Packet engine callback to indicate error */
120
121 static void silc_client_packet_error(SilcPacketEngine engine,
122                                      SilcPacketStream stream,
123                                      SilcPacketError error,
124                                      void *callback_context,
125                                      void *stream_context)
126 {
127
128 }
129
130 /* Packet stream callbacks */
131 static SilcPacketCallbacks silc_client_stream_cbs =
132 {
133   silc_client_packet_receive,
134   silc_client_packet_eos,
135   silc_client_packet_error
136 };
137
138 /* FSM destructor */
139
140 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
141                                 void *destructor_context)
142 {
143   silc_fsm_free(fsm);
144 }
145
146
147 /************************** Connection's machine ****************************/
148
149 /* Start the connection's state machine.  If threads are in use the machine
150    is always executed in a real thread. */
151
152 SILC_FSM_STATE(silc_client_connection_st_start)
153 {
154   SilcClientConnection conn = fsm_context;
155   SilcFSM connfsm;
156
157   /* Take scheduler for connection */
158   conn->internal->schedule = silc_fsm_get_schedule(fsm);
159
160   /*** Run connection machine */
161   connfsm = &conn->internal->fsm;
162   silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
163                 fsm, conn->internal->schedule);
164   silc_fsm_sema_init(&conn->internal->wait_event, connfsm, 0);
165   silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
166
167   /* Schedule any events set in initialization */
168   if (conn->internal->connect)
169     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
170   if (conn->internal->key_exchange)
171     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
172
173   /* Wait until this thread is terminated from the machine destructor */
174   return SILC_FSM_WAIT;
175 }
176
177 /* Connection machine main state.  This handles various connection related
178    events, but not packet processing.  It's done in dedicated packet
179    processing FSM thread. */
180
181 SILC_FSM_STATE(silc_client_connection_st_run)
182 {
183   SilcClientConnection conn = fsm_context;
184   SilcFSMThread thread;
185
186   /* Wait for events */
187   SILC_FSM_SEMA_WAIT(&conn->internal->wait_event);
188
189   /* Process events */
190   thread = &conn->internal->event_thread;
191
192   if (conn->internal->connect) {
193     SILC_LOG_DEBUG(("Event: connect"));
194     conn->internal->connect = FALSE;
195
196     /*** Event: connect */
197     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
198                          NULL, NULL, FALSE);
199     silc_fsm_start_sync(thread, silc_client_st_connect);
200     return SILC_FSM_CONTINUE;
201   }
202
203   if (conn->internal->key_exchange) {
204     SILC_LOG_DEBUG(("Event: key exchange"));
205     conn->internal->key_exchange = FALSE;
206
207     /*** Event: key exchange */
208     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
209                          NULL, NULL, FALSE);
210     silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
211     return SILC_FSM_CONTINUE;
212   }
213
214   if (conn->internal->disconnected) {
215     /** Event: disconnected */
216     SILC_LOG_DEBUG(("Event: disconnected"));
217     conn->internal->disconnected = FALSE;
218     silc_fsm_next(fsm, silc_client_connection_st_close);
219     return SILC_FSM_CONTINUE;
220   }
221
222   /* NOT REACHED */
223   SILC_ASSERT(FALSE);
224   return SILC_FSM_CONTINUE;
225 }
226
227 /* Packet processor thread.  Each incoming packet is processed in FSM
228    thread in this state.  The thread is run in the connection machine. */
229
230 SILC_FSM_STATE(silc_client_connection_st_packet)
231 {
232   SilcPacket packet = state_context;
233
234   SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
235
236   switch (packet->type) {
237
238   case SILC_PACKET_PRIVATE_MESSAGE:
239     /** Private message */
240     silc_fsm_next(fsm, silc_client_private_message);
241     break;
242
243   case SILC_PACKET_CHANNEL_MESSAGE:
244     /** Channel message */
245     silc_fsm_next(fsm, silc_client_channel_message);
246     break;
247
248   case SILC_PACKET_FTP:
249     /* File transfer packet */
250     //    silc_client_ftp(client, conn, packet);
251     break;
252
253   case SILC_PACKET_CHANNEL_KEY:
254     /** Channel key */
255     silc_fsm_next(fsm, silc_client_channel_key);
256     break;
257
258   case SILC_PACKET_COMMAND_REPLY:
259     /** Command reply */
260     silc_fsm_next(fsm, silc_client_command_reply);
261     break;
262
263   case SILC_PACKET_NOTIFY:
264     /** Notify */
265     silc_fsm_next(fsm, silc_client_notify);
266     break;
267
268   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
269     /* Private message key indicator */
270     silc_fsm_next(fsm, silc_client_private_message_key);
271     break;
272
273   case SILC_PACKET_DISCONNECT:
274     /** Disconnect */
275     silc_fsm_next(fsm, silc_client_disconnect);
276     break;
277
278   case SILC_PACKET_ERROR:
279     /* Error by server */
280     silc_fsm_next(fsm, silc_client_error);
281     break;
282
283   case SILC_PACKET_KEY_AGREEMENT:
284     /* Key agreement */
285     //    silc_client_key_agreement(client, conn, packet);
286     break;
287
288   case SILC_PACKET_COMMAND:
289     /** Command packet */
290     silc_fsm_next(fsm, silc_client_command);
291     break;
292
293   case SILC_PACKET_NEW_ID:
294     /** New ID */
295     silc_fsm_next(fsm, silc_client_new_id);
296     break;
297
298   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
299     /* Reply to connection authentication request to resolve authentication
300        method from server. */
301     //    silc_client_connection_auth_request(client, conn, packet);
302     break;
303
304   default:
305     silc_packet_free(packet);
306     return SILC_FSM_FINISH;
307     break;
308   }
309
310   return SILC_FSM_CONTINUE;
311 }
312
313 /* Disconnection even to close remote connection.  We close the connection
314    and finish the connection machine in this state.  The connection context
315    is deleted in the machine destructor.  The connection callback must be
316    already called back to application before getting here. */
317
318 SILC_FSM_STATE(silc_client_connection_st_close)
319 {
320   SilcClientConnection conn = fsm_context;
321
322   SILC_LOG_DEBUG(("Closing remote connection"));
323
324   /* Abort ongoing events */
325   if (conn->internal->op)
326     silc_async_abort(conn->internal->op, NULL, NULL);
327
328   /* Close connection */
329   silc_packet_stream_destroy(conn->stream);
330
331   SILC_LOG_DEBUG(("Finishing connection machine"));
332
333   return SILC_FSM_FINISH;
334 }
335
336 /* Received error packet from server.  Send it to application. */
337
338 SILC_FSM_STATE(silc_client_error)
339 {
340   SilcClientConnection conn = fsm_context;
341   SilcClient client = conn->client;
342   SilcPacket packet = state_context;
343   char *msg;
344
345   msg = silc_memdup(silc_buffer_data(&packet->buffer),
346                     silc_buffer_len(&packet->buffer));
347   if (msg)
348     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
349
350   silc_free(msg);
351   silc_packet_free(packet);
352
353   return SILC_FSM_FINISH;
354 }
355
356 /* Received disconnect packet from server.  We close the connection and
357    send the disconnect message to application. */
358
359 SILC_FSM_STATE(silc_client_disconnect)
360 {
361   SilcClientConnection conn = fsm_context;
362   SilcClient client = conn->client;
363   SilcPacket packet = state_context;
364   SilcStatus status;
365   char *message = NULL;
366
367   SILC_LOG_DEBUG(("Server disconnected"));
368
369   if (silc_buffer_len(&packet->buffer) < 1) {
370     silc_packet_free(packet);
371     return SILC_FSM_FINISH;
372   }
373
374   status = (SilcStatus)packet->buffer.data[0];
375
376   silc_buffer_pull(&packet->buffer, 1);
377   if (silc_buffer_len(&packet->buffer) > 1 &&
378       silc_utf8_valid(silc_buffer_data(&packet->buffer),
379                       silc_buffer_len(&packet->buffer)))
380     message = silc_memdup(silc_buffer_data(&packet->buffer),
381                           silc_buffer_len(&packet->buffer));
382
383   /* Call connection callback */
384   conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
385                  message, conn->callback_context);
386
387   silc_free(message);
388   silc_packet_free(packet);
389
390   /* Signal to close connection */
391   conn->internal->disconnected = TRUE;
392   SILC_FSM_SEMA_POST(&conn->internal->wait_event);
393
394   return SILC_FSM_FINISH;
395 }
396
397 /*************************** Main client machine ****************************/
398
399 /* The client's main state where we wait for various events */
400
401 SILC_FSM_STATE(silc_client_st_run)
402 {
403   SilcClient client = fsm_context;
404
405   /* Wait for events */
406   SILC_FSM_SEMA_WAIT(&client->internal->wait_event);
407
408   /* Process events */
409
410   if (client->internal->run_callback && client->internal->ops->running) {
411     /* Call running callbcak back to application */
412     SILC_LOG_DEBUG(("We are running, call running callback"));
413     client->internal->run_callback = FALSE;
414     client->internal->ops->running(client, client->application);
415     return SILC_FSM_CONTINUE;
416   }
417
418   /* NOT REACHED */
419   SILC_ASSERT(FALSE);
420   return SILC_FSM_CONTINUE;
421 }
422
423 /******************************* Private API ********************************/
424
425 /* Adds new connection.  Creates the connection context and returns it. */
426
427 static SilcClientConnection
428 silc_client_add_connection(SilcClient client,
429                            SilcConnectionType conn_type,
430                            SilcClientConnectionParams *params,
431                            SilcPublicKey public_key,
432                            SilcPrivateKey private_key,
433                            char *remote_host, int port,
434                            SilcClientConnectCallback callback,
435                            void *context)
436 {
437   SilcClientConnection conn;
438   SilcFSMThread thread;
439
440   if (!callback)
441     return NULL;
442
443   SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
444
445   conn = silc_calloc(1, sizeof(*conn));
446   if (!conn)
447     return NULL;
448
449   conn->client = client;
450   conn->public_key = public_key;
451   conn->private_key = private_key;
452   conn->remote_host = strdup(remote_host);
453   conn->remote_port = port ? port : 706;
454   conn->type = conn_type;
455   conn->callback = callback;
456   conn->callback_context = context;
457
458   conn->internal = silc_calloc(1, sizeof(*conn->internal));
459   if (!conn->internal) {
460     silc_free(conn);
461     return NULL;
462   }
463   conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
464   silc_mutex_alloc(&conn->internal->lock);
465   silc_atomic_init16(&conn->internal->cmd_ident, 0);
466
467   if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
468     silc_free(conn);
469     silc_free(conn->internal);
470     return NULL;
471   }
472   if (params)
473     conn->internal->params = *params;
474   conn->internal->verbose = TRUE;
475   silc_list_init(conn->internal->pending_commands,
476                  struct SilcClientCommandContextStruct, next);
477   silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
478
479   conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
480                                                     NULL, NULL);
481   conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
482                                                      NULL, NULL);
483   conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
484                                                     NULL, NULL);
485   if (!conn->internal->client_cache || !conn->internal->channel_cache ||
486       !conn->internal->server_cache) {
487     silc_client_del_connection(client, conn);
488     return NULL;
489   }
490
491   conn->internal->ftp_sessions = silc_dlist_init();
492
493   /* Run the connection state machine.  If threads are in use the machine
494      is always run in a real thread. */
495   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
496                                  silc_client_fsm_destructor, NULL,
497                                  client->internal->params->threads);
498   if (!thread) {
499     silc_client_del_connection(client, conn);
500     return NULL;
501   }
502   silc_fsm_start(thread, silc_client_connection_st_start);
503
504   return conn;
505 }
506
507 /* Deletes connection.  This is always called from the connection machine
508    destructor.  Do not call this directly other places. */
509
510 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
511 {
512   SilcList list;
513   SilcIDCacheEntry entry;
514   SilcFSMThread thread;
515   SilcClientCommandContext cmd;
516
517   SILC_LOG_DEBUG(("Freeing connection %p", conn));
518
519   /* Free all cache entries */
520   if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
521     silc_list_start(list);
522     while ((entry = silc_list_get(list)))
523       silc_client_del_client(client, conn, entry->context);
524   }
525   if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
526     silc_list_start(list);
527     while ((entry = silc_list_get(list)))
528       silc_client_del_channel(client, conn, entry->context);
529   }
530   if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
531     silc_list_start(list);
532     while ((entry = silc_list_get(list)))
533       silc_client_del_server(client, conn, entry->context);
534   }
535
536   /* Free ID caches */
537   if (conn->internal->client_cache)
538     silc_idcache_free(conn->internal->client_cache);
539   if (conn->internal->channel_cache)
540     silc_idcache_free(conn->internal->channel_cache);
541   if (conn->internal->server_cache)
542     silc_idcache_free(conn->internal->server_cache);
543
544   /* Free thread pool */
545   silc_list_start(conn->internal->thread_pool);
546   while ((thread = silc_list_get(conn->internal->thread_pool)))
547     silc_fsm_free(thread);
548
549   /* Free pending commands */
550   silc_list_start(conn->internal->pending_commands);
551   while ((cmd = silc_list_get(conn->internal->pending_commands)))
552     silc_client_command_free(cmd);
553
554   silc_free(conn->remote_host);
555   silc_buffer_free(conn->internal->local_idp);
556   silc_buffer_free(conn->internal->remote_idp);
557   silc_mutex_free(conn->internal->lock);
558   if (conn->internal->hash)
559     silc_hash_free(conn->internal->hash);
560   if (conn->internal->sha1hash)
561     silc_hash_free(conn->internal->sha1hash);
562   silc_atomic_uninit16(&conn->internal->cmd_ident);
563
564   silc_free(conn->internal);
565   memset(conn, 'F', sizeof(*conn));
566   silc_free(conn);
567 }
568
569
570 /******************************* Client API *********************************/
571
572 /* Connects to remote server.  This is the main routine used to connect
573    to remote SILC server.  Performs key exchange also.  Returns the
574    connection context to the connection callback. */
575
576 SilcBool silc_client_connect_to_server(SilcClient client,
577                                        SilcClientConnectionParams *params,
578                                        SilcPublicKey public_key,
579                                        SilcPrivateKey private_key,
580                                        char *remote_host, int port,
581                                        SilcClientConnectCallback callback,
582                                        void *context)
583 {
584   SilcClientConnection conn;
585
586   if (!client || !remote_host)
587     return FALSE;
588
589   /* Add new connection */
590   conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
591                                     public_key, private_key, remote_host,
592                                     port, callback, context);
593   if (!conn) {
594     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
595     return FALSE;
596   }
597
598   client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
599                              "Connecting to port %d of server %s",
600                              port, remote_host);
601
602   /* Signal connection machine to start connecting */
603   conn->internal->connect = TRUE;
604   return TRUE;
605 }
606
607 /* Connects to remote client.  Performs key exchange also.  Returns the
608    connection context to the connection callback. */
609
610 SilcBool silc_client_connect_to_client(SilcClient client,
611                                        SilcClientConnectionParams *params,
612                                        SilcPublicKey public_key,
613                                        SilcPrivateKey private_key,
614                                        char *remote_host, int port,
615                                        SilcClientConnectCallback callback,
616                                        void *context)
617 {
618   SilcClientConnection conn;
619
620   if (!client || !remote_host)
621     return FALSE;
622
623   /* Add new connection */
624   conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
625                                     public_key, private_key, remote_host,
626                                     port, callback, context);
627   if (!conn) {
628     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
629     return FALSE;
630   }
631
632   /* Signal connection machine to start connecting */
633   conn->internal->connect = TRUE;
634   return TRUE;
635 }
636
637 /* Starts key exchange in the remote stream indicated by `stream'.  This
638    creates the connection context and returns it in the connection callback. */
639
640 SilcBool silc_client_key_exchange(SilcClient client,
641                                   SilcClientConnectionParams *params,
642                                   SilcPublicKey public_key,
643                                   SilcPrivateKey private_key,
644                                   SilcStream stream,
645                                   SilcConnectionType conn_type,
646                                   SilcClientConnectCallback callback,
647                                   void *context)
648 {
649   SilcClientConnection conn;
650   const char *host;
651   SilcUInt16 port;
652
653   if (!client || !stream)
654     return FALSE;
655
656   if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
657     SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
658     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
659     return FALSE;
660   }
661
662   /* Add new connection */
663   conn = silc_client_add_connection(client, conn_type, params,
664                                     public_key, private_key,
665                                     (char *)host, port, callback, context);
666   if (!conn) {
667     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
668     return FALSE;
669   }
670   conn->stream = (void *)stream;
671
672   /* Signal connection to start key exchange */
673   conn->internal->key_exchange = TRUE;
674   return TRUE;
675 }
676
677 /* Closes remote connection */
678
679 void silc_client_close_connection(SilcClient client,
680                                   SilcClientConnection conn)
681 {
682   SILC_LOG_DEBUG(("Closing connection %p", conn));
683
684   /* Signal to close connection */
685   conn->internal->disconnected = TRUE;
686   SILC_FSM_SEMA_POST(&conn->internal->wait_event);
687 }
688
689 #if 0
690 /* Finalizes the connection to the remote SILC server. This is called
691    after authentication protocol has been completed. This send our
692    user information to the server to receive our client ID from
693    server. */
694
695 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
696 {
697   SilcProtocol protocol = (SilcProtocol)context;
698   SilcClientConnAuthInternalContext *ctx =
699     (SilcClientConnAuthInternalContext *)protocol->context;
700   SilcClient client = (SilcClient)ctx->client;
701   SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
702   SilcBuffer packet;
703
704   SILC_LOG_DEBUG(("Start"));
705
706   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
707       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
708     /* Error occured during protocol */
709     SILC_LOG_DEBUG(("Error during authentication protocol"));
710     ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
711     goto err;
712   }
713
714   if (conn->internal->params.detach_data) {
715     /* Send RESUME_CLIENT packet to the server, which is used to resume
716        old detached session back. */
717     SilcBuffer auth;
718     SilcClientID *old_client_id;
719     unsigned char *old_id;
720     SilcUInt16 old_id_len;
721
722     if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
723       ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
724       goto err;
725     }
726
727     old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
728     if (!old_client_id) {
729       silc_free(old_id);
730       ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
731       goto err;
732     }
733
734     /* Generate authentication data that server will verify */
735     auth = silc_auth_public_key_auth_generate(client->public_key,
736                                               client->private_key,
737                                               client->rng,
738                                               conn->internal->hash,
739                                               old_client_id, SILC_ID_CLIENT);
740     if (!auth) {
741       silc_free(old_client_id);
742       silc_free(old_id);
743       ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
744       goto err;
745     }
746
747     packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
748     silc_buffer_format(packet,
749                        SILC_STR_UI_SHORT(old_id_len),
750                        SILC_STR_UI_XNSTRING(old_id, old_id_len),
751                        SILC_STR_UI_XNSTRING(auth->data, auth->len),
752                        SILC_STR_END);
753
754     /* Send the packet */
755     silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
756                             NULL, 0, NULL, NULL,
757                             packet->data, packet->len, TRUE);
758     silc_buffer_free(packet);
759     silc_buffer_free(auth);
760     silc_free(old_client_id);
761     silc_free(old_id);
762   } else {
763     /* Send NEW_CLIENT packet to the server. We will become registered
764        to the SILC network after sending this packet and we will receive
765        client ID from the server. */
766     packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
767                                strlen(client->realname));
768     silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
769     silc_buffer_format(packet,
770                        SILC_STR_UI_SHORT(strlen(client->username)),
771                        SILC_STR_UI_XNSTRING(client->username,
772                                             strlen(client->username)),
773                        SILC_STR_UI_SHORT(strlen(client->realname)),
774                        SILC_STR_UI_XNSTRING(client->realname,
775                                             strlen(client->realname)),
776                        SILC_STR_END);
777
778     /* Send the packet */
779     silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
780                             NULL, 0, NULL, NULL,
781                             packet->data, packet->len, TRUE);
782     silc_buffer_free(packet);
783   }
784
785   /* Save remote ID. */
786   conn->remote_id = ctx->dest_id;
787   conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
788   conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
789
790   /* Register re-key timeout */
791   conn->internal->rekey->timeout = client->internal->params->rekey_secs;
792   conn->internal->rekey->context = (void *)client;
793   silc_schedule_task_add(client->schedule, conn->sock->sock,
794                          silc_client_rekey_callback,
795                          (void *)conn->sock, conn->internal->rekey->timeout, 0,
796                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
797
798   silc_protocol_free(protocol);
799   silc_free(ctx->auth_data);
800   if (ctx->ske)
801     silc_ske_free(ctx->ske);
802   silc_socket_free(ctx->sock);
803   silc_free(ctx);
804   conn->sock->protocol = NULL;
805   return;
806
807  err:
808   silc_protocol_free(protocol);
809   silc_free(ctx->auth_data);
810   silc_free(ctx->dest_id);
811   if (ctx->ske)
812     silc_ske_free(ctx->ske);
813   conn->sock->protocol = NULL;
814   silc_socket_free(ctx->sock);
815
816   /* Notify application of failure */
817   silc_schedule_task_add(client->schedule, ctx->sock->sock,
818                          silc_client_connect_failure_auth, ctx,
819                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
820 }
821
822 /* Client session resuming callback.  If the session was resumed
823    this callback is called after the resuming is completed.  This
824    will call the `connect' client operation to the application
825    since it has not been called yet. */
826
827 static void silc_client_resume_session_cb(SilcClient client,
828                                           SilcClientConnection conn,
829                                           SilcBool success,
830                                           void *context)
831 {
832   SilcBuffer sidp;
833
834   /* Notify application that connection is created to server */
835   client->internal->ops->connected(client, conn, success ?
836                                    SILC_CLIENT_CONN_SUCCESS_RESUME :
837                                    SILC_CLIENT_CONN_ERROR_RESUME);
838
839   if (success) {
840     /* Issue INFO command to fetch the real server name and server
841        information and other stuff. */
842     silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
843                                  silc_client_command_reply_info_i, 0,
844                                  ++conn->cmd_ident);
845     sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
846     silc_client_command_send(client, conn, SILC_COMMAND_INFO,
847                              conn->cmd_ident, 1, 2, sidp->data, sidp->len);
848     silc_buffer_free(sidp);
849   }
850 }
851
852 /* Processes incoming connection authentication method request packet.
853    It is a reply to our previously sent request. The packet can be used
854    to resolve the authentication method for the current session if the
855    client does not know it beforehand. */
856
857 void silc_client_connection_auth_request(SilcClient client,
858                                          SilcClientConnection conn,
859                                          SilcPacketContext *packet)
860 {
861   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
862   SilcUInt16 conn_type, auth_meth;
863   int ret;
864
865   /* If we haven't send our request then ignore this one. */
866   if (!conn->internal->connauth)
867     return;
868
869   /* Parse the payload */
870   ret = silc_buffer_unformat(packet->buffer,
871                              SILC_STR_UI_SHORT(&conn_type),
872                              SILC_STR_UI_SHORT(&auth_meth),
873                              SILC_STR_END);
874   if (ret == -1)
875     auth_meth = SILC_AUTH_NONE;
876
877   /* Call the request callback to notify application for received
878      authentication method information. */
879   if (conn->internal->connauth->callback)
880     (*conn->internal->connauth->callback)(client, conn, auth_meth,
881                                           conn->internal->connauth->context);
882
883   silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
884
885   silc_free(conn->internal->connauth);
886   conn->internal->connauth = NULL;
887 }
888
889 /* Timeout task callback called if the server does not reply to our
890    connection authentication method request in the specified time interval. */
891
892 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
893 {
894   SilcClientConnection conn = (SilcClientConnection)context;
895   SilcClient client = conn->client;
896
897   if (!conn->internal->connauth)
898     return;
899
900   /* Call the request callback to notify application */
901   if (conn->internal->connauth->callback)
902     (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
903                                           conn->internal->connauth->context);
904
905   silc_free(conn->internal->connauth);
906   conn->internal->connauth = NULL;
907 }
908
909 /* This function can be used to request the current authentication method
910    from the server. This may be called when connecting to the server
911    and the client library requests the authentication data from the
912    application. If the application does not know the current authentication
913    method it can request it from the server using this function.
914    The `callback' with `context' will be called after the server has
915    replied back with the current authentication method. */
916
917 void
918 silc_client_request_authentication_method(SilcClient client,
919                                           SilcClientConnection conn,
920                                           SilcConnectionAuthRequest callback,
921                                           void *context)
922 {
923   SilcClientConnAuthRequest connauth;
924   SilcBuffer packet;
925
926   assert(client && conn);
927   connauth = silc_calloc(1, sizeof(*connauth));
928   connauth->callback = callback;
929   connauth->context = context;
930
931   if (conn->internal->connauth)
932     silc_free(conn->internal->connauth);
933
934   conn->internal->connauth = connauth;
935
936   /* Assemble the request packet and send it to the server */
937   packet = silc_buffer_alloc(4);
938   silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
939   silc_buffer_format(packet,
940                      SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
941                      SILC_STR_UI_SHORT(SILC_AUTH_NONE),
942                      SILC_STR_END);
943   silc_client_packet_send(client, conn->sock,
944                           SILC_PACKET_CONNECTION_AUTH_REQUEST,
945                           NULL, 0, NULL, NULL,
946                           packet->data, packet->len, FALSE);
947   silc_buffer_free(packet);
948
949   /* Register a timeout in case server does not reply anything back. */
950   connauth->timeout =
951     silc_schedule_task_add(client->schedule, conn->sock->sock,
952                            silc_client_request_authentication_method_timeout,
953                            conn,
954                            client->internal->params->connauth_request_secs, 0,
955                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
956 }
957 #endif /* 0 */
958
959
960 /* Allocates new client object. This has to be done before client may
961    work. After calling this one must call silc_client_init to initialize
962    the client. The `application' is application specific user data pointer
963    and caller must free it. */
964
965 SilcClient silc_client_alloc(SilcClientOperations *ops,
966                              SilcClientParams *params,
967                              void *application,
968                              const char *version_string)
969 {
970   SilcClient new_client;
971
972   new_client = silc_calloc(1, sizeof(*new_client));
973   if (!new_client)
974     return NULL;
975   new_client->application = application;
976
977   new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
978   if (!new_client->internal) {
979     silc_free(new_client);
980     return NULL;
981   }
982   new_client->internal->ops = ops;
983   new_client->internal->params =
984     silc_calloc(1, sizeof(*new_client->internal->params));
985   if (!version_string)
986     version_string = silc_version_string;
987   new_client->internal->silc_client_version = strdup(version_string);
988
989   if (params)
990     memcpy(new_client->internal->params, params, sizeof(*params));
991
992   if (!new_client->internal->params->rekey_secs)
993     new_client->internal->params->rekey_secs = 3600;
994
995   if (!new_client->internal->params->connauth_request_secs)
996     new_client->internal->params->connauth_request_secs = 2;
997
998   new_client->internal->params->
999     nickname_format[sizeof(new_client->internal->
1000                            params->nickname_format) - 1] = 0;
1001
1002   return new_client;
1003 }
1004
1005 /* Frees client object and its internals. */
1006
1007 void silc_client_free(SilcClient client)
1008 {
1009   if (client->rng)
1010     silc_rng_free(client->rng);
1011
1012   if (!client->internal->params->dont_register_crypto_library) {
1013     silc_cipher_unregister_all();
1014     silc_pkcs_unregister_all();
1015     silc_hash_unregister_all();
1016     silc_hmac_unregister_all();
1017   }
1018
1019   silc_free(client->username);
1020   silc_free(client->hostname);
1021   silc_free(client->realname);
1022   silc_free(client->internal->params);
1023   silc_free(client->internal->silc_client_version);
1024   silc_free(client->internal);
1025   silc_free(client);
1026 }
1027
1028 /* Initializes the client. This makes all the necessary steps to make
1029    the client ready to be run. One must call silc_client_run to run the
1030    client. Returns FALSE if error occured, TRUE otherwise. */
1031
1032 SilcBool silc_client_init(SilcClient client, const char *username,
1033                           const char *hostname, const char *realname)
1034 {
1035   SILC_LOG_DEBUG(("Initializing client"));
1036
1037   if (!client)
1038     return FALSE;
1039
1040   if (!username || !hostname) {
1041     SILC_LOG_ERROR(("Username, hostname and realname must be given to "
1042                     "silc_client_init"));
1043     return FALSE;
1044   }
1045   if (!realname)
1046     realname = username;
1047
1048   /* Validate essential strings */
1049   if (!silc_identifier_verify(username, strlen(username),
1050                               SILC_STRING_UTF8, 128)) {
1051     SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1052                     client->username));
1053     return FALSE;
1054   }
1055   if (!silc_identifier_verify(hostname, strlen(hostname),
1056                               SILC_STRING_UTF8, 256)) {
1057     SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1058                     client->hostname));
1059     return FALSE;
1060   }
1061   if (!silc_utf8_valid(realname, strlen(realname))) {
1062     SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1063                     client->realname));
1064     return FALSE;
1065   }
1066
1067   /* Take the name strings */
1068   client->username = strdup(username);
1069   client->hostname = strdup(hostname);
1070   client->realname = strdup(realname);
1071   if (!username || !hostname || !realname)
1072     return FALSE;
1073
1074   if (!client->internal->params->dont_register_crypto_library) {
1075     /* Initialize the crypto library.  If application has done this already
1076        this has no effect.  Also, we will not be overriding something
1077        application might have registered earlier. */
1078     silc_cipher_register_default();
1079     silc_pkcs_register_default();
1080     silc_hash_register_default();
1081     silc_hmac_register_default();
1082   }
1083
1084   /* Initialize random number generator */
1085   client->rng = silc_rng_alloc();
1086   if (!client->rng)
1087     return FALSE;
1088   silc_rng_init(client->rng);
1089   silc_rng_global_init(client->rng);
1090
1091   /* Initialize the scheduler */
1092   client->schedule = silc_schedule_init(0, client);
1093   if (!client->schedule)
1094     return FALSE;
1095
1096   /* Start packet engine */
1097   client->internal->packet_engine =
1098     silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1099                              client);
1100   if (!client->internal->packet_engine)
1101     return FALSE;
1102
1103   /* Allocate client lock */
1104   silc_mutex_alloc(&client->internal->lock);
1105
1106   /* Register commands */
1107   silc_client_commands_register(client);
1108
1109   /* Initialize and start the client FSM */
1110   silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1111   silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1112   silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1113
1114   /* Signal the application when we are running */
1115   client->internal->run_callback = TRUE;
1116   SILC_FSM_SEMA_POST(&client->internal->wait_event);
1117
1118   return TRUE;
1119 }
1120
1121 /* Stops the client. This is called to stop the client and thus to stop
1122    the program. */
1123
1124 void silc_client_stop(SilcClient client)
1125 {
1126   SILC_LOG_DEBUG(("Stopping client"));
1127
1128   silc_schedule_stop(client->schedule);
1129   silc_schedule_uninit(client->schedule);
1130   silc_client_commands_unregister(client);
1131
1132   SILC_LOG_DEBUG(("Client stopped"));
1133 }
1134
1135 /* Starts the SILC client FSM machine and blocks here.  When this returns
1136    the client has ended. */
1137
1138 void silc_client_run(SilcClient client)
1139 {
1140   SILC_LOG_DEBUG(("Starting SILC client"));
1141
1142   /* Run the scheduler */
1143   silc_schedule(client->schedule);
1144 }
1145
1146 /* Call scheduler one iteration and return.  This cannot be called if threads
1147    are in use. */
1148
1149 void silc_client_run_one(SilcClient client)
1150 {
1151   silc_schedule_one(client->schedule, 0);
1152 }