silcclient: Add generic client entry operation context
[silc.git] / lib / silcclient / client.c
1 /*
2
3   client.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2014 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   SilcList list;
395   SilcIDCacheEntry entry;
396   SilcClientEntry client_entry;
397
398   /* Finish running command threads.  This will also finish waiting packet
399      thread, as they are always waiting for some command.  If any thread is
400      waiting something else than command, they must be finished explicitly. */
401   if (silc_list_count(conn->internal->pending_commands)) {
402     SILC_LOG_DEBUG(("Finish pending commands"));
403     silc_list_start(conn->internal->pending_commands);
404     while ((cmd = silc_list_get(conn->internal->pending_commands))) {
405       if (silc_fsm_is_started(&cmd->thread)) {
406         cmd->verbose = FALSE;
407         silc_fsm_continue_sync(&cmd->thread);
408       }
409     }
410
411     /* Give threads time to finish */
412     return SILC_FSM_YIELD;
413   }
414
415   /* Abort ongoing event */
416   if (conn->internal->op) {
417     SILC_LOG_DEBUG(("Abort event"));
418     silc_async_abort(conn->internal->op, NULL, NULL);
419     conn->internal->op = NULL;
420   }
421
422   /* Abort ongoing client entry operations */
423   if (conn->internal->client_cache) {
424     if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
425       silc_list_start(list);
426       while ((entry = silc_list_get(list))) {
427         client_entry = entry->context;
428         if (client_entry->internal.op) {
429           silc_async_abort(client_entry->internal.op, NULL, NULL);
430           client_entry->internal.op = NULL;
431         }
432       }
433     }
434   }
435
436   /* If event thread is running, finish it. */
437   if (silc_fsm_is_started(&conn->internal->event_thread)) {
438     SILC_LOG_DEBUG(("Finish event thread"));
439     silc_fsm_continue_sync(&conn->internal->event_thread);
440     return SILC_FSM_YIELD;
441   }
442
443   /* Call the connection callback */
444   if (conn->callback)
445     conn->callback(conn->client, conn, conn->internal->status,
446                    conn->internal->error, conn->internal->disconnect_message,
447                    conn->callback_context);
448   silc_free(conn->internal->disconnect_message);
449
450   SILC_LOG_DEBUG(("Closing remote connection"));
451
452   /* Close connection. */
453   if (conn->stream)
454     silc_packet_stream_destroy(conn->stream);
455
456   SILC_LOG_DEBUG(("Finishing connection machine"));
457   return SILC_FSM_FINISH;
458 }
459
460 /* Received error packet from server.  Send it to application. */
461
462 SILC_FSM_STATE(silc_client_error)
463 {
464   SilcClientConnection conn = fsm_context;
465   SilcClient client = conn->client;
466   SilcPacket packet = state_context;
467   char *msg;
468
469   msg = silc_memdup(silc_buffer_data(&packet->buffer),
470                     silc_buffer_len(&packet->buffer));
471   if (msg)
472     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, msg);
473
474   silc_free(msg);
475   silc_packet_free(packet);
476
477   return SILC_FSM_FINISH;
478 }
479
480 /* Received disconnect packet from server.  We close the connection and
481    send the disconnect message to application. */
482
483 SILC_FSM_STATE(silc_client_disconnect)
484 {
485   SilcClientConnection conn = fsm_context;
486   SilcPacket packet = state_context;
487   SilcStatus status;
488   char *message = NULL;
489
490   SILC_LOG_DEBUG(("Server disconnected"));
491
492   if (silc_buffer_len(&packet->buffer) < 1) {
493     silc_packet_free(packet);
494     return SILC_FSM_FINISH;
495   }
496
497   status = (SilcStatus)packet->buffer.data[0];
498
499   silc_buffer_pull(&packet->buffer, 1);
500   if (silc_buffer_len(&packet->buffer) > 1 &&
501       silc_utf8_valid(silc_buffer_data(&packet->buffer),
502                       silc_buffer_len(&packet->buffer)))
503     message = silc_memdup(silc_buffer_data(&packet->buffer),
504                           silc_buffer_len(&packet->buffer));
505
506   /* Call connection callback */
507   conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
508   conn->internal->error = status;
509   conn->internal->disconnect_message = message;
510
511   /* Signal to close connection */
512   if (!conn->internal->disconnected) {
513     conn->internal->disconnected = TRUE;
514     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
515   }
516
517   silc_packet_free(packet);
518
519   return SILC_FSM_FINISH;
520 }
521
522 /*************************** Main client machine ****************************/
523
524 /* The client's main state where we wait for various events */
525
526 SILC_FSM_STATE(silc_client_st_run)
527 {
528   SilcClient client = fsm_context;
529
530   /* Wait for events */
531   SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
532
533   /* Process events */
534
535   if (client->internal->run_callback) {
536     /* Call running callbcak back to application */
537     client->internal->run_callback = FALSE;
538     if (client->internal->running) {
539       SILC_LOG_DEBUG(("We are up, call running callback"));
540       client->internal->running(client, client->internal->running_context);
541     }
542     return SILC_FSM_CONTINUE;
543   }
544
545   if (client->internal->connection_closed) {
546     /* A connection finished */
547     SILC_LOG_DEBUG(("Event: connection closed"));
548     client->internal->connection_closed = FALSE;
549     if (silc_atomic_get_int32(&client->internal->conns) == 0 &&
550         client->internal->stop)
551       SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
552     return SILC_FSM_CONTINUE;
553   }
554
555   if (client->internal->stop) {
556     /* Stop client libarry.  If we have running connections, wait until
557        they finish first. */
558     if (silc_atomic_get_int32(&client->internal->conns) == 0) {
559       SILC_LOG_DEBUG(("Event: stop"));
560       silc_fsm_next(fsm, silc_client_st_stop);
561     }
562     return SILC_FSM_CONTINUE;
563   }
564
565   /* NOT REACHED */
566   SILC_ASSERT(FALSE);
567   return SILC_FSM_CONTINUE;
568 }
569
570 /* Stop event.  Stops the client library. */
571
572 SILC_FSM_STATE(silc_client_st_stop)
573 {
574   SilcClient client = fsm_context;
575
576   SILC_LOG_DEBUG(("Client stopped"));
577
578   /* Stop scheduler */
579   silc_schedule_stop(client->schedule);
580   silc_client_commands_unregister(client);
581
582   /* Call stopped callback to application */
583   if (client->internal->running)
584     client->internal->running(client, client->internal->running_context);
585
586   return SILC_FSM_FINISH;
587 }
588
589 /******************************* Private API ********************************/
590
591 /* Adds new connection.  Creates the connection context and returns it. */
592
593 SilcClientConnection
594 silc_client_add_connection(SilcClient client,
595                            SilcConnectionType conn_type,
596                            SilcBool connect,
597                            SilcClientConnectionParams *params,
598                            SilcPublicKey public_key,
599                            SilcPrivateKey private_key,
600                            char *remote_host, int port,
601                            SilcClientConnectCallback callback,
602                            void *context)
603 {
604   SilcClientConnection conn;
605   SilcFSMThread thread;
606
607   if (!callback)
608     return NULL;
609
610   SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
611
612   conn = silc_calloc(1, sizeof(*conn));
613   if (!conn)
614     return NULL;
615
616   conn->client = client;
617   conn->public_key = public_key;
618   conn->private_key = private_key;
619   conn->remote_host = strdup(remote_host);
620   conn->remote_port = port ? port : 706;
621   conn->type = conn_type;
622   conn->callback = callback;
623   conn->callback_context = context;
624
625   conn->internal = silc_calloc(1, sizeof(*conn->internal));
626   if (!conn->internal) {
627     silc_free(conn);
628     return NULL;
629   }
630   conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
631   silc_mutex_alloc(&conn->internal->lock);
632   silc_atomic_init16(&conn->internal->cmd_ident, 0);
633
634   if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
635     silc_free(conn);
636     silc_free(conn->internal);
637     return NULL;
638   }
639
640   /* Set parameters */
641   if (params) {
642     conn->internal->params = *params;
643     conn->context = params->context;
644   }
645   if (!conn->internal->params.rekey_secs)
646     conn->internal->params.rekey_secs = 3600;
647 #ifndef SILC_DIST_INPLACE
648   if (conn->internal->params.rekey_secs < 300)
649     conn->internal->params.rekey_secs = 300;
650 #endif /* SILC_DIST_INPLACE */
651
652   conn->internal->verbose = TRUE;
653   silc_list_init(conn->internal->pending_commands,
654                  struct SilcClientCommandContextStruct, next);
655   silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
656
657   /* Allocate client, channel and serve caches */
658   if (conn_type != SILC_CONN_CLIENT) {
659     conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
660                                                       NULL, NULL);
661     conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
662                                                        NULL, NULL);
663     conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
664                                                       NULL, NULL);
665     if (!conn->internal->client_cache || !conn->internal->channel_cache ||
666         !conn->internal->server_cache) {
667       silc_client_del_connection(client, conn);
668       return NULL;
669     }
670   }
671
672   if (connect) {
673     /* Initialize our async operation so that application may abort us
674        while we're connecting. */
675     conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
676                                            NULL, conn);
677     if (!conn->internal->cop) {
678       silc_client_del_connection(client, conn);
679       return NULL;
680     }
681   }
682
683   /* Run the connection state machine.  If threads are in use the connection
684      machine is always run in a real thread. */
685   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
686                                  silc_client_connection_finished, NULL,
687                                  client->internal->params->threads);
688   if (!thread) {
689     silc_client_del_connection(client, conn);
690     return NULL;
691   }
692   silc_fsm_set_state_context(thread, client);
693   silc_fsm_start(thread, silc_client_connection_st_start);
694
695   SILC_LOG_DEBUG(("New connection %p", conn));
696   silc_atomic_add_int32(&client->internal->conns, 1);
697
698   return conn;
699 }
700
701 /* Deletes connection.  This is always called from the connection machine
702    destructor.  Do not call this directly other places. */
703
704 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
705 {
706   SilcList list;
707   SilcIDCacheEntry entry;
708   SilcFSMThread thread;
709
710   SILC_LOG_DEBUG(("Freeing connection %p", conn));
711
712   silc_schedule_task_del_by_context(conn->internal->schedule, conn);
713
714   /* Free all cache entries */
715   if (conn->internal->server_cache) {
716     if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
717       silc_list_start(list);
718       while ((entry = silc_list_get(list)))
719         silc_client_del_server(client, conn, entry->context);
720     }
721   }
722   if (conn->internal->channel_cache) {
723     if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
724       silc_list_start(list);
725       while ((entry = silc_list_get(list))) {
726         silc_client_empty_channel(client, conn, entry->context);
727         silc_client_del_channel(client, conn, entry->context);
728       }
729     }
730   }
731   if (conn->internal->client_cache) {
732     if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
733       silc_list_start(list);
734       while ((entry = silc_list_get(list)))
735         silc_client_del_client(client, conn, entry->context);
736     }
737   }
738
739   /* Free ID caches */
740   if (conn->internal->client_cache)
741     silc_idcache_free(conn->internal->client_cache);
742   if (conn->internal->channel_cache)
743     silc_idcache_free(conn->internal->channel_cache);
744   if (conn->internal->server_cache)
745     silc_idcache_free(conn->internal->server_cache);
746
747   /* Free thread pool */
748   silc_list_start(conn->internal->thread_pool);
749   while ((thread = silc_list_get(conn->internal->thread_pool)))
750     silc_fsm_free(thread);
751
752   silc_free(conn->remote_host);
753   silc_buffer_free(conn->internal->local_idp);
754   silc_buffer_free(conn->internal->remote_idp);
755   silc_mutex_free(conn->internal->lock);
756   if (conn->internal->hash)
757     silc_hash_free(conn->internal->hash);
758   if (conn->internal->sha1hash)
759     silc_hash_free(conn->internal->sha1hash);
760   silc_atomic_uninit16(&conn->internal->cmd_ident);
761   silc_free(conn->internal->away_message);
762   if (conn->internal->rekey)
763     silc_ske_free_rekey_material(conn->internal->rekey);
764   if (conn->internal->cop)
765     silc_async_free(conn->internal->cop);
766
767   silc_free(conn->internal);
768   memset(conn, 'F', sizeof(*conn));
769   silc_free(conn);
770 }
771
772 /******************************* Client API *********************************/
773
774 /* Connects to remote server.  This is the main routine used to connect
775    to remote SILC server.  Performs key exchange also.  Returns the
776    connection context to the connection callback. */
777
778 SilcAsyncOperation
779 silc_client_connect_to_server(SilcClient client,
780                               SilcClientConnectionParams *params,
781                               SilcPublicKey public_key,
782                               SilcPrivateKey private_key,
783                               char *remote_host, int port,
784                               SilcClientConnectCallback callback,
785                               void *context)
786 {
787   SilcClientConnection conn;
788
789   SILC_LOG_DEBUG(("Connecting to server"));
790
791   if (!client || !remote_host || !callback)
792     return NULL;
793
794   if (client->internal->run_callback) {
795     SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
796                     "callback has not been called yet."));
797     return NULL;
798   }
799
800   /* Add new connection */
801   conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
802                                     public_key, private_key, remote_host,
803                                     port, callback, context);
804   if (!conn) {
805     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
806     return NULL;
807   }
808
809   client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
810                              "Connecting to port %d of server %s",
811                              port, remote_host);
812
813   /* Signal connection machine to start connecting */
814   conn->internal->connect = TRUE;
815   return conn->internal->cop;
816 }
817
818 /* Connects to remote client.  Performs key exchange also.  Returns the
819    connection context to the connection callback. */
820
821 SilcAsyncOperation
822 silc_client_connect_to_client(SilcClient client,
823                               SilcClientConnectionParams *params,
824                               SilcPublicKey public_key,
825                               SilcPrivateKey private_key,
826                               char *remote_host, int port,
827                               SilcClientConnectCallback callback,
828                               void *context)
829 {
830   SilcClientConnection conn;
831
832   SILC_LOG_DEBUG(("Connecting to client"));
833
834   if (!client || !remote_host || !callback)
835     return NULL;
836
837   if (client->internal->run_callback) {
838     SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
839                     "callback has not been called yet."));
840     return NULL;
841   }
842
843   if (params)
844     params->no_authentication = TRUE;
845
846   /* Add new connection */
847   conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
848                                     public_key, private_key, remote_host,
849                                     port, callback, context);
850   if (!conn) {
851     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
852     return NULL;
853   }
854
855   /* Signal connection machine to start connecting */
856   conn->internal->connect = TRUE;
857   return conn->internal->cop;
858 }
859
860 /* Starts key exchange in the remote stream indicated by `stream'.  This
861    creates the connection context and returns it in the connection callback. */
862
863 SilcAsyncOperation
864 silc_client_key_exchange(SilcClient client,
865                          SilcClientConnectionParams *params,
866                          SilcPublicKey public_key,
867                          SilcPrivateKey private_key,
868                          SilcStream stream,
869                          SilcConnectionType conn_type,
870                          SilcClientConnectCallback callback,
871                          void *context)
872 {
873   SilcClientConnection conn;
874   const char *host;
875   SilcUInt16 port;
876
877   SILC_LOG_DEBUG(("Performing key exchange"));
878
879   if (!client || !stream || !callback)
880     return NULL;
881
882   if (client->internal->run_callback) {
883     SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
884                     "callback has not been called yet."));
885     return NULL;
886   }
887
888   if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
889     SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
890     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
891     return NULL;
892   }
893
894   /* Add new connection */
895   conn = silc_client_add_connection(client, conn_type, TRUE, params,
896                                     public_key, private_key,
897                                     (char *)host, port, callback, context);
898   if (!conn) {
899     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
900     return NULL;
901   }
902   conn->internal->user_stream = stream;
903
904   /* Signal connection to start key exchange */
905   conn->internal->key_exchange = TRUE;
906   return conn->internal->cop;
907 }
908
909 /* Closes remote connection */
910
911 void silc_client_close_connection(SilcClient client,
912                                   SilcClientConnection conn)
913 {
914   SILC_LOG_DEBUG(("Closing connection %p", conn));
915
916   /* Signal to close connection */
917   conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
918   if (!conn->internal->disconnected) {
919     conn->internal->disconnected = TRUE;
920     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
921   }
922 }
923
924 /* Allocates new client object. This has to be done before client may
925    work. After calling this one must call silc_client_init to initialize
926    the client. The `application' is application specific user data pointer
927    and caller must free it. */
928
929 SilcClient silc_client_alloc(SilcClientOperations *ops,
930                              SilcClientParams *params,
931                              void *application,
932                              const char *version_string)
933 {
934   SilcClient new_client;
935
936   new_client = silc_calloc(1, sizeof(*new_client));
937   if (!new_client)
938     return NULL;
939   new_client->application = application;
940
941   new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
942   if (!new_client->internal) {
943     silc_free(new_client);
944     return NULL;
945   }
946   new_client->internal->ops = ops;
947   new_client->internal->params =
948     silc_calloc(1, sizeof(*new_client->internal->params));
949   if (!version_string)
950     version_string = silc_version_string;
951   new_client->internal->silc_client_version = strdup(version_string);
952
953   if (params)
954     memcpy(new_client->internal->params, params, sizeof(*params));
955
956   new_client->internal->params->
957     nickname_format[sizeof(new_client->internal->
958                            params->nickname_format) - 1] = 0;
959
960   silc_atomic_init32(&new_client->internal->conns, 0);
961
962   return new_client;
963 }
964
965 /* Frees client object and its internals. */
966
967 void silc_client_free(SilcClient client)
968 {
969   if (client->schedule)
970     silc_schedule_uninit(client->schedule);
971
972   if (client->rng)
973     silc_rng_free(client->rng);
974
975   if (!client->internal->params->dont_register_crypto_library) {
976     silc_cipher_unregister_all();
977     silc_pkcs_unregister_all();
978     silc_hash_unregister_all();
979     silc_hmac_unregister_all();
980   }
981
982   if (client->internal->packet_engine)
983     silc_packet_engine_stop(client->internal->packet_engine);
984   if (client->internal->ftp_sessions)
985     silc_dlist_uninit(client->internal->ftp_sessions);
986   if (client->internal->lock)
987     silc_mutex_free(client->internal->lock);
988   silc_atomic_uninit32(&client->internal->conns);
989   silc_free(client->username);
990   silc_free(client->hostname);
991   silc_free(client->realname);
992   silc_free(client->internal->params);
993   silc_free(client->internal->silc_client_version);
994   silc_free(client->internal);
995   silc_free(client);
996 }
997
998 /* Initializes the client. This makes all the necessary steps to make
999    the client ready to be run. One must call silc_client_run to run the
1000    client. Returns FALSE if error occured, TRUE otherwise. */
1001
1002 SilcBool silc_client_init(SilcClient client, const char *username,
1003                           const char *hostname, const char *realname,
1004                           SilcClientRunning running, void *context)
1005 {
1006   SILC_LOG_DEBUG(("Initializing client"));
1007
1008   if (!client)
1009     return FALSE;
1010
1011   if (!username || !hostname) {
1012     SILC_LOG_ERROR(("Username and hostname must be given to "
1013                     "silc_client_init"));
1014     return FALSE;
1015   }
1016   if (!realname)
1017     realname = username;
1018
1019   /* Validate essential strings */
1020   if (!silc_identifier_verify(username, strlen(username),
1021                               SILC_STRING_UTF8, 128)) {
1022     SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1023                     client->username));
1024     return FALSE;
1025   }
1026   if (!silc_identifier_verify(hostname, strlen(hostname),
1027                               SILC_STRING_UTF8, 256)) {
1028     SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1029                     client->hostname));
1030     return FALSE;
1031   }
1032   if (!silc_utf8_valid(realname, strlen(realname))) {
1033     SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1034                     client->realname));
1035     return FALSE;
1036   }
1037
1038   /* Take the name strings */
1039   client->username = strdup(username);
1040   client->hostname = strdup(hostname);
1041   client->realname = strdup(realname);
1042   if (!username || !hostname || !realname)
1043     return FALSE;
1044
1045   client->internal->ftp_sessions = silc_dlist_init();
1046   if (!client->internal->ftp_sessions)
1047     return FALSE;
1048
1049   if (!client->internal->params->dont_register_crypto_library) {
1050     /* Initialize the crypto library.  If application has done this already
1051        this has no effect.  Also, we will not be overriding something
1052        application might have registered earlier. */
1053     silc_cipher_register_default();
1054     silc_pkcs_register_default();
1055     silc_hash_register_default();
1056     silc_hmac_register_default();
1057   }
1058
1059   /* Initialize random number generator */
1060   client->rng = silc_rng_alloc();
1061   if (!client->rng)
1062     return FALSE;
1063   silc_rng_init(client->rng);
1064   silc_rng_global_init(client->rng);
1065
1066   /* Initialize the scheduler */
1067   client->schedule = silc_schedule_init(0, client);
1068   if (!client->schedule)
1069     return FALSE;
1070
1071   /* Allocate client lock */
1072   silc_mutex_alloc(&client->internal->lock);
1073
1074   /* Register commands */
1075   silc_client_commands_register(client);
1076
1077   /* Start packet engine */
1078   client->internal->packet_engine =
1079     silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1080                              client);
1081   if (!client->internal->packet_engine)
1082     return FALSE;
1083
1084   /* Initialize and start the client FSM */
1085   client->internal->running = running;
1086   client->internal->running_context = context;
1087   silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1088   silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1089   silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1090
1091   /* Signal the application when we are running */
1092   client->internal->run_callback = TRUE;
1093   SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1094
1095   return TRUE;
1096 }
1097
1098 /* Starts the SILC client FSM machine and blocks here.  When this returns
1099    the client has ended. */
1100
1101 void silc_client_run(SilcClient client)
1102 {
1103   SILC_LOG_DEBUG(("Starting SILC client"));
1104
1105   /* Run the scheduler */
1106   silc_schedule(client->schedule);
1107 }
1108
1109 /* Call scheduler one iteration and return. */
1110
1111 void silc_client_run_one(SilcClient client)
1112 {
1113   if (silc_fsm_is_started(&client->internal->fsm))
1114     silc_schedule_one(client->schedule, 0);
1115 }
1116
1117 /* Stops the client. This is called to stop the client and thus to stop
1118    the program. */
1119
1120 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1121                       void *context)
1122 {
1123   SILC_LOG_DEBUG(("Stopping client"));
1124
1125   if (!silc_fsm_is_started(&client->internal->fsm)) {
1126     SILC_LOG_WARNING(("Attempting to stop client library before it has been "
1127                       "started (silc_client_init not called)"));
1128     return;
1129   }
1130
1131   client->internal->running = (SilcClientRunning)stopped;
1132   client->internal->running_context = context;
1133
1134   /* Signal to stop */
1135   client->internal->stop = TRUE;
1136   SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1137 }