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