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