Merge branch 'topic/mm-fixes' of git://208.110.73.182/silc into silc.1.1.branch
[silc.git] / lib / silcclient / client.c
1 /*
2
3   client.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 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_int32(&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   /* Nothing */
153 }
154
155 /* Packet stream callbacks */
156 static SilcPacketCallbacks silc_client_stream_cbs =
157 {
158   silc_client_packet_receive,
159   silc_client_packet_eos,
160   silc_client_packet_error
161 };
162
163 /* FSM destructor */
164
165 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
166                                 void *destructor_context)
167 {
168   silc_fsm_free(fsm);
169 }
170
171 /* Connect abort operation */
172
173 static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
174 {
175   SilcClientConnection conn = context;
176
177   SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
178
179   /* Connection callback will not be called after user aborted connecting */
180   conn->callback = NULL;
181   conn->internal->cop = NULL;
182
183   /* Signal to close connection */
184   if (!conn->internal->disconnected) {
185     conn->internal->disconnected = TRUE;
186
187     /* If user aborts before connection machine is even up yet, then don't
188        send signal yet.  It will process this event when it comes up. */
189     if (silc_fsm_is_started(&conn->internal->fsm))
190       SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
191   }
192 }
193
194 /************************** Connection's machine ****************************/
195
196 /* Start the connection's state machine.  If threads are in use the machine
197    is always executed in a real thread. */
198
199 SILC_FSM_STATE(silc_client_connection_st_start)
200 {
201   SilcClientConnection conn = fsm_context;
202   SilcFSM connfsm;
203
204   /* Take scheduler for connection */
205   conn->internal->schedule = silc_fsm_get_schedule(fsm);
206
207   /*** Run connection machine */
208   connfsm = &conn->internal->fsm;
209   silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
210                 fsm, conn->internal->schedule);
211   silc_fsm_event_init(&conn->internal->wait_event, connfsm);
212   silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
213
214   /* Schedule any events possibly set in initialization */
215   if (conn->internal->disconnected)
216     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
217   if (conn->internal->connect)
218     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
219   if (conn->internal->key_exchange)
220     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
221
222   /* Wait until this thread is terminated from the machine destructor */
223   return SILC_FSM_WAIT;
224 }
225
226 /* Connection machine main state.  This handles various connection related
227    events, but not packet processing.  It's done in dedicated packet
228    processing FSM thread. */
229
230 SILC_FSM_STATE(silc_client_connection_st_run)
231 {
232   SilcClientConnection conn = fsm_context;
233   SilcFSMThread thread;
234
235   /* Wait for events */
236   SILC_FSM_EVENT_WAIT(&conn->internal->wait_event);
237
238   /* Process events */
239   thread = &conn->internal->event_thread;
240
241   if (conn->internal->disconnected) {
242     /** Event: disconnected */
243     SILC_LOG_DEBUG(("Event: disconnected"));
244     silc_fsm_next(fsm, silc_client_connection_st_close);
245     return SILC_FSM_YIELD;
246   }
247
248   if (conn->internal->connect) {
249     SILC_LOG_DEBUG(("Event: connect"));
250     conn->internal->connect = FALSE;
251     SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
252
253     /*** Event: connect */
254     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
255                          NULL, NULL, FALSE);
256     silc_fsm_start_sync(thread, silc_client_st_connect);
257     return SILC_FSM_CONTINUE;
258   }
259
260   if (conn->internal->key_exchange) {
261     SILC_LOG_DEBUG(("Event: key exchange"));
262     conn->internal->key_exchange = FALSE;
263     SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
264
265     /*** Event: key exchange */
266     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
267                          NULL, NULL, FALSE);
268     silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
269     return SILC_FSM_CONTINUE;
270   }
271
272   if (conn->internal->rekeying) {
273     SILC_LOG_DEBUG(("Event: rekey"));
274     conn->internal->rekeying = FALSE;
275     SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
276
277     /*** Event: rekey */
278     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
279                          NULL, NULL, FALSE);
280     silc_fsm_start_sync(thread, silc_client_st_rekey);
281     return SILC_FSM_CONTINUE;
282   }
283
284   /* NOT REACHED */
285   SILC_ASSERT(FALSE);
286   return SILC_FSM_CONTINUE;
287 }
288
289 /* Packet processor thread.  Each incoming packet is processed in FSM
290    thread in this state.  The thread is run in the connection machine. */
291
292 SILC_FSM_STATE(silc_client_connection_st_packet)
293 {
294   SilcClientConnection conn = fsm_context;
295   SilcPacket packet = state_context;
296
297   SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
298
299   switch (packet->type) {
300
301   case SILC_PACKET_PRIVATE_MESSAGE:
302     /** Private message */
303     silc_fsm_next(fsm, silc_client_private_message);
304     break;
305
306   case SILC_PACKET_CHANNEL_MESSAGE:
307     /** Channel message */
308     silc_fsm_next(fsm, silc_client_channel_message);
309     break;
310
311   case SILC_PACKET_FTP:
312     /* File transfer packet */
313     silc_fsm_next(fsm, silc_client_ftp);
314     break;
315
316   case SILC_PACKET_CHANNEL_KEY:
317     /** Channel key */
318     silc_fsm_next(fsm, silc_client_channel_key);
319     break;
320
321   case SILC_PACKET_COMMAND_REPLY:
322     /** Command reply */
323     silc_fsm_next(fsm, silc_client_command_reply);
324     break;
325
326   case SILC_PACKET_NOTIFY:
327     /** Notify */
328     silc_fsm_next(fsm, silc_client_notify);
329     break;
330
331   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
332     /* Private message key indicator */
333     silc_fsm_next(fsm, silc_client_private_message_key);
334     break;
335
336   case SILC_PACKET_DISCONNECT:
337     /** Disconnect */
338     silc_fsm_next(fsm, silc_client_disconnect);
339     break;
340
341   case SILC_PACKET_ERROR:
342     /* Error by server */
343     silc_fsm_next(fsm, silc_client_error);
344     break;
345
346   case SILC_PACKET_KEY_AGREEMENT:
347     /** Key agreement */
348     silc_fsm_next(fsm, silc_client_key_agreement);
349     break;
350
351   case SILC_PACKET_COMMAND:
352     /** Command packet */
353     silc_fsm_next(fsm, silc_client_command);
354     break;
355
356   case SILC_PACKET_NEW_ID:
357     /** New ID */
358     silc_fsm_next(fsm, silc_client_new_id);
359     break;
360
361   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
362     /** Connection auth resolve reply */
363     silc_fsm_next(fsm, silc_client_connect_auth_request);
364     break;
365
366   case SILC_PACKET_REKEY:
367     /* Signal to start rekey */
368     conn->internal->rekey_responder = TRUE;
369     conn->internal->rekeying = TRUE;
370     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
371
372     silc_packet_free(packet);
373     return SILC_FSM_FINISH;
374     break;
375
376   default:
377     silc_packet_free(packet);
378     return SILC_FSM_FINISH;
379     break;
380   }
381
382   return SILC_FSM_CONTINUE;
383 }
384
385 /* Disconnection event to close remote connection.  We close the connection
386    and finish the connection machine in this state.  The connection context
387    is deleted in the machine destructor.  The connection callback is called
388    in this state if it is set. */
389
390 SILC_FSM_STATE(silc_client_connection_st_close)
391 {
392   SilcClientConnection conn = fsm_context;
393   SilcClientCommandContext cmd;
394
395   /* Finish running command threads.  This will also finish waiting packet
396      thread, as they are always waiting for some command.  If any thread is
397      waiting something else than command, they must be finished explicitly. */
398   if (silc_list_count(conn->internal->pending_commands)) {
399     SILC_LOG_DEBUG(("Finish pending commands"));
400     silc_list_start(conn->internal->pending_commands);
401     while ((cmd = silc_list_get(conn->internal->pending_commands))) {
402       if (silc_fsm_is_started(&cmd->thread)) {
403         cmd->verbose = FALSE;
404         silc_fsm_continue_sync(&cmd->thread);
405       }
406     }
407
408     /* Give threads time to finish */
409     return SILC_FSM_YIELD;
410   }
411
412   /* Abort ongoing event */
413   if (conn->internal->op) {
414     SILC_LOG_DEBUG(("Abort event"));
415     silc_async_abort(conn->internal->op, NULL, NULL);
416     conn->internal->op = NULL;
417   }
418
419   /* If event thread is running, finish it. */
420   if (silc_fsm_is_started(&conn->internal->event_thread)) {
421     SILC_LOG_DEBUG(("Finish event thread"));
422     silc_fsm_continue_sync(&conn->internal->event_thread);
423     return SILC_FSM_YIELD;
424   }
425
426   /* Call the connection callback */
427   if (conn->callback)
428     conn->callback(conn->client, conn, conn->internal->status,
429                    conn->internal->error, conn->internal->disconnect_message,
430                    conn->callback_context);
431   silc_free(conn->internal->disconnect_message);
432
433   SILC_LOG_DEBUG(("Closing remote connection"));
434
435   /* Close connection. */
436   if (conn->stream)
437     silc_packet_stream_destroy(conn->stream);
438
439   SILC_LOG_DEBUG(("Finishing connection machine"));
440   return SILC_FSM_FINISH;
441 }
442
443 /* Received error packet from server.  Send it to application. */
444
445 SILC_FSM_STATE(silc_client_error)
446 {
447   SilcClientConnection conn = fsm_context;
448   SilcClient client = conn->client;
449   SilcPacket packet = state_context;
450   char *msg;
451
452   msg = silc_memdup(silc_buffer_data(&packet->buffer),
453                     silc_buffer_len(&packet->buffer));
454   if (msg)
455     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
456
457   silc_free(msg);
458   silc_packet_free(packet);
459
460   return SILC_FSM_FINISH;
461 }
462
463 /* Received disconnect packet from server.  We close the connection and
464    send the disconnect message to application. */
465
466 SILC_FSM_STATE(silc_client_disconnect)
467 {
468   SilcClientConnection conn = fsm_context;
469   SilcPacket packet = state_context;
470   SilcStatus status;
471   char *message = NULL;
472
473   SILC_LOG_DEBUG(("Server disconnected"));
474
475   if (silc_buffer_len(&packet->buffer) < 1) {
476     silc_packet_free(packet);
477     return SILC_FSM_FINISH;
478   }
479
480   status = (SilcStatus)packet->buffer.data[0];
481
482   silc_buffer_pull(&packet->buffer, 1);
483   if (silc_buffer_len(&packet->buffer) > 1 &&
484       silc_utf8_valid(silc_buffer_data(&packet->buffer),
485                       silc_buffer_len(&packet->buffer)))
486     message = silc_memdup(silc_buffer_data(&packet->buffer),
487                           silc_buffer_len(&packet->buffer));
488
489   /* Call connection callback */
490   conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
491   conn->internal->error = status;
492   conn->internal->disconnect_message = message;
493
494   /* Signal to close connection */
495   if (!conn->internal->disconnected) {
496     conn->internal->disconnected = TRUE;
497     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
498   }
499
500   silc_packet_free(packet);
501
502   return SILC_FSM_FINISH;
503 }
504
505 /*************************** Main client machine ****************************/
506
507 /* The client's main state where we wait for various events */
508
509 SILC_FSM_STATE(silc_client_st_run)
510 {
511   SilcClient client = fsm_context;
512
513   /* Wait for events */
514   SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
515
516   /* Process events */
517
518   if (client->internal->run_callback) {
519     /* Call running callbcak back to application */
520     client->internal->run_callback = FALSE;
521     if (client->internal->running) {
522       SILC_LOG_DEBUG(("We are up, call running callback"));
523       client->internal->running(client, client->internal->running_context);
524     }
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_int32(&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_int32(&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_int32(&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   if (client->internal->run_callback) {
776     SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
777                     "callback has not been called yet."));
778     return NULL;
779   }
780
781   /* Add new connection */
782   conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
783                                     public_key, private_key, remote_host,
784                                     port, callback, context);
785   if (!conn) {
786     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
787     return NULL;
788   }
789
790   client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
791                              "Connecting to port %d of server %s",
792                              port, remote_host);
793
794   /* Signal connection machine to start connecting */
795   conn->internal->connect = TRUE;
796   return conn->internal->cop;
797 }
798
799 /* Connects to remote client.  Performs key exchange also.  Returns the
800    connection context to the connection callback. */
801
802 SilcAsyncOperation
803 silc_client_connect_to_client(SilcClient client,
804                               SilcClientConnectionParams *params,
805                               SilcPublicKey public_key,
806                               SilcPrivateKey private_key,
807                               char *remote_host, int port,
808                               SilcClientConnectCallback callback,
809                               void *context)
810 {
811   SilcClientConnection conn;
812
813   SILC_LOG_DEBUG(("Connecting to client"));
814
815   if (!client || !remote_host)
816     return NULL;
817
818   if (client->internal->run_callback) {
819     SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
820                     "callback has not been called yet."));
821     return NULL;
822   }
823
824   if (params)
825     params->no_authentication = TRUE;
826
827   /* Add new connection */
828   conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
829                                     public_key, private_key, remote_host,
830                                     port, callback, context);
831   if (!conn) {
832     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
833     return NULL;
834   }
835
836   /* Signal connection machine to start connecting */
837   conn->internal->connect = TRUE;
838   return conn->internal->cop;
839 }
840
841 /* Starts key exchange in the remote stream indicated by `stream'.  This
842    creates the connection context and returns it in the connection callback. */
843
844 SilcAsyncOperation
845 silc_client_key_exchange(SilcClient client,
846                          SilcClientConnectionParams *params,
847                          SilcPublicKey public_key,
848                          SilcPrivateKey private_key,
849                          SilcStream stream,
850                          SilcConnectionType conn_type,
851                          SilcClientConnectCallback callback,
852                          void *context)
853 {
854   SilcClientConnection conn;
855   const char *host;
856   SilcUInt16 port;
857
858   SILC_LOG_DEBUG(("Performing key exchange"));
859
860   if (!client || !stream)
861     return NULL;
862
863   if (client->internal->run_callback) {
864     SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
865                     "callback has not been called yet."));
866     return NULL;
867   }
868
869   if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
870     SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
871     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
872     return NULL;
873   }
874
875   /* Add new connection */
876   conn = silc_client_add_connection(client, conn_type, TRUE, params,
877                                     public_key, private_key,
878                                     (char *)host, port, callback, context);
879   if (!conn) {
880     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
881     return NULL;
882   }
883   conn->internal->user_stream = stream;
884
885   /* Signal connection to start key exchange */
886   conn->internal->key_exchange = TRUE;
887   return conn->internal->cop;
888 }
889
890 /* Closes remote connection */
891
892 void silc_client_close_connection(SilcClient client,
893                                   SilcClientConnection conn)
894 {
895   SILC_LOG_DEBUG(("Closing connection %p", conn));
896
897   /* Signal to close connection */
898   conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
899   if (!conn->internal->disconnected) {
900     conn->internal->disconnected = TRUE;
901     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
902   }
903 }
904
905 /* Allocates new client object. This has to be done before client may
906    work. After calling this one must call silc_client_init to initialize
907    the client. The `application' is application specific user data pointer
908    and caller must free it. */
909
910 SilcClient silc_client_alloc(SilcClientOperations *ops,
911                              SilcClientParams *params,
912                              void *application,
913                              const char *version_string)
914 {
915   SilcClient new_client;
916
917   new_client = silc_calloc(1, sizeof(*new_client));
918   if (!new_client)
919     return NULL;
920   new_client->application = application;
921
922   new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
923   if (!new_client->internal) {
924     silc_free(new_client);
925     return NULL;
926   }
927   new_client->internal->ops = ops;
928   new_client->internal->params =
929     silc_calloc(1, sizeof(*new_client->internal->params));
930   if (!version_string)
931     version_string = silc_version_string;
932   new_client->internal->silc_client_version = strdup(version_string);
933
934   if (params)
935     memcpy(new_client->internal->params, params, sizeof(*params));
936
937   new_client->internal->params->
938     nickname_format[sizeof(new_client->internal->
939                            params->nickname_format) - 1] = 0;
940
941   silc_atomic_init32(&new_client->internal->conns, 0);
942
943   return new_client;
944 }
945
946 /* Frees client object and its internals. */
947
948 void silc_client_free(SilcClient client)
949 {
950   if (client->schedule)
951     silc_schedule_uninit(client->schedule);
952
953   if (client->rng)
954     silc_rng_free(client->rng);
955
956   if (!client->internal->params->dont_register_crypto_library) {
957     silc_cipher_unregister_all();
958     silc_pkcs_unregister_all();
959     silc_hash_unregister_all();
960     silc_hmac_unregister_all();
961   }
962
963   if (client->internal->packet_engine)
964     silc_packet_engine_stop(client->internal->packet_engine);
965   if (client->internal->ftp_sessions)
966     silc_dlist_uninit(client->internal->ftp_sessions);
967   if (client->internal->lock)
968     silc_mutex_free(client->internal->lock);
969   silc_atomic_uninit32(&client->internal->conns);
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.  Also, we will not be overriding something
1033        application might have registered earlier. */
1034     silc_cipher_register_default();
1035     silc_pkcs_register_default();
1036     silc_hash_register_default();
1037     silc_hmac_register_default();
1038   }
1039
1040   /* Initialize random number generator */
1041   client->rng = silc_rng_alloc();
1042   if (!client->rng)
1043     return FALSE;
1044   silc_rng_init(client->rng);
1045   silc_rng_global_init(client->rng);
1046
1047   /* Initialize the scheduler */
1048   client->schedule = silc_schedule_init(0, client);
1049   if (!client->schedule)
1050     return FALSE;
1051
1052   /* Allocate client lock */
1053   silc_mutex_alloc(&client->internal->lock);
1054
1055   /* Register commands */
1056   silc_client_commands_register(client);
1057
1058   /* Start packet engine */
1059   client->internal->packet_engine =
1060     silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1061                              client);
1062   if (!client->internal->packet_engine)
1063     return FALSE;
1064
1065   /* Initialize and start the client FSM */
1066   client->internal->running = running;
1067   client->internal->running_context = context;
1068   silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1069   silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1070   silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1071
1072   /* Signal the application when we are running */
1073   client->internal->run_callback = TRUE;
1074   SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1075
1076   return TRUE;
1077 }
1078
1079 /* Starts the SILC client FSM machine and blocks here.  When this returns
1080    the client has ended. */
1081
1082 void silc_client_run(SilcClient client)
1083 {
1084   SILC_LOG_DEBUG(("Starting SILC client"));
1085
1086   /* Run the scheduler */
1087   silc_schedule(client->schedule);
1088 }
1089
1090 /* Call scheduler one iteration and return. */
1091
1092 void silc_client_run_one(SilcClient client)
1093 {
1094   if (silc_fsm_is_started(&client->internal->fsm))
1095     silc_schedule_one(client->schedule, 0);
1096 }
1097
1098 /* Stops the client. This is called to stop the client and thus to stop
1099    the program. */
1100
1101 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1102                       void *context)
1103 {
1104   SILC_LOG_DEBUG(("Stopping client"));
1105
1106   if (!silc_fsm_is_started(&client->internal->fsm)) {
1107     SILC_LOG_WARNING(("Attempting to stop client library before it has been "
1108                       "started (silc_client_init not called)"));
1109     return;
1110   }
1111
1112   client->internal->running = (SilcClientRunning)stopped;
1113   client->internal->running_context = context;
1114
1115   /* Signal to stop */
1116   client->internal->stop = TRUE;
1117   SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1118 }