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