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