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