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