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