silcclient: Fix error path memory freeing
[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 (!client || !callback || !remote_host)
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->remote_host);
628     silc_free(conn);
629     return NULL;
630   }
631   conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
632   silc_mutex_alloc(&conn->internal->lock);
633   silc_atomic_init16(&conn->internal->cmd_ident, 0);
634
635   if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
636     silc_free(conn->remote_host);
637     silc_free(conn->internal);
638     silc_free(conn);
639     return NULL;
640   }
641
642   /* Set parameters */
643   if (params) {
644     conn->internal->params = *params;
645     conn->context = params->context;
646   }
647   if (!conn->internal->params.rekey_secs)
648     conn->internal->params.rekey_secs = 3600;
649 #ifndef SILC_DIST_INPLACE
650   if (conn->internal->params.rekey_secs < 300)
651     conn->internal->params.rekey_secs = 300;
652 #endif /* SILC_DIST_INPLACE */
653
654   conn->internal->verbose = TRUE;
655   silc_list_init(conn->internal->pending_commands,
656                  struct SilcClientCommandContextStruct, next);
657   silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
658
659   /* Allocate client, channel and serve caches */
660   if (conn_type != SILC_CONN_CLIENT) {
661     conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
662                                                       NULL, NULL);
663     conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
664                                                        NULL, NULL);
665     conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
666                                                       NULL, NULL);
667     if (!conn->internal->client_cache || !conn->internal->channel_cache ||
668         !conn->internal->server_cache) {
669       silc_client_del_connection(client, conn);
670       return NULL;
671     }
672   }
673
674   if (connect) {
675     /* Initialize our async operation so that application may abort us
676        while we're connecting. */
677     conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
678                                            NULL, conn);
679     if (!conn->internal->cop) {
680       silc_client_del_connection(client, conn);
681       return NULL;
682     }
683   }
684
685   /* Run the connection state machine.  If threads are in use the connection
686      machine is always run in a real thread. */
687   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
688                                  silc_client_connection_finished, NULL,
689                                  client->internal->params->threads);
690   if (!thread) {
691     silc_client_del_connection(client, conn);
692     return NULL;
693   }
694   silc_fsm_set_state_context(thread, client);
695   silc_fsm_start(thread, silc_client_connection_st_start);
696
697   SILC_LOG_DEBUG(("New connection %p", conn));
698   silc_atomic_add_int32(&client->internal->conns, 1);
699
700   return conn;
701 }
702
703 /* Deletes connection.  This is always called from the connection machine
704    destructor.  Do not call this directly other places. */
705
706 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
707 {
708   SilcList list;
709   SilcIDCacheEntry entry;
710   SilcFSMThread thread;
711
712   SILC_LOG_DEBUG(("Freeing connection %p", conn));
713
714   silc_schedule_task_del_by_context(conn->internal->schedule, conn);
715
716   /* Free all cache entries */
717   if (conn->internal->server_cache) {
718     if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
719       silc_list_start(list);
720       while ((entry = silc_list_get(list)))
721         silc_client_del_server(client, conn, entry->context);
722     }
723   }
724   if (conn->internal->channel_cache) {
725     if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
726       silc_list_start(list);
727       while ((entry = silc_list_get(list))) {
728         silc_client_empty_channel(client, conn, entry->context);
729         silc_client_del_channel(client, conn, entry->context);
730       }
731     }
732   }
733   if (conn->internal->client_cache) {
734     if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
735       silc_list_start(list);
736       while ((entry = silc_list_get(list)))
737         silc_client_del_client(client, conn, entry->context);
738     }
739   }
740
741   /* Free ID caches */
742   if (conn->internal->client_cache)
743     silc_idcache_free(conn->internal->client_cache);
744   if (conn->internal->channel_cache)
745     silc_idcache_free(conn->internal->channel_cache);
746   if (conn->internal->server_cache)
747     silc_idcache_free(conn->internal->server_cache);
748
749   /* Free thread pool */
750   silc_list_start(conn->internal->thread_pool);
751   while ((thread = silc_list_get(conn->internal->thread_pool)))
752     silc_fsm_free(thread);
753
754   silc_free(conn->remote_host);
755   silc_buffer_free(conn->internal->local_idp);
756   silc_buffer_free(conn->internal->remote_idp);
757   silc_mutex_free(conn->internal->lock);
758   if (conn->internal->hash)
759     silc_hash_free(conn->internal->hash);
760   if (conn->internal->sha1hash)
761     silc_hash_free(conn->internal->sha1hash);
762   silc_atomic_uninit16(&conn->internal->cmd_ident);
763   silc_free(conn->internal->away_message);
764   if (conn->internal->rekey)
765     silc_ske_free_rekey_material(conn->internal->rekey);
766   if (conn->internal->cop)
767     silc_async_free(conn->internal->cop);
768
769   silc_free(conn->internal);
770   memset(conn, 'F', sizeof(*conn));
771   silc_free(conn);
772 }
773
774 /******************************* Client API *********************************/
775
776 /* Connects to remote server.  This is the main routine used to connect
777    to remote SILC server.  Performs key exchange also.  Returns the
778    connection context to the connection callback. */
779
780 SilcAsyncOperation
781 silc_client_connect_to_server(SilcClient client,
782                               SilcClientConnectionParams *params,
783                               SilcPublicKey public_key,
784                               SilcPrivateKey private_key,
785                               char *remote_host, int port,
786                               SilcClientConnectCallback callback,
787                               void *context)
788 {
789   SilcClientConnection conn;
790
791   SILC_LOG_DEBUG(("Connecting to server"));
792
793   if (!client || !remote_host || !callback)
794     return NULL;
795
796   if (client->internal->run_callback) {
797     SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
798                     "callback has not been called yet."));
799     return NULL;
800   }
801
802   /* Add new connection */
803   conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
804                                     public_key, private_key, remote_host,
805                                     port, callback, context);
806   if (!conn) {
807     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
808     return NULL;
809   }
810
811   client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
812                              "Connecting to port %d of server %s",
813                              port, remote_host);
814
815   /* Signal connection machine to start connecting */
816   conn->internal->connect = TRUE;
817   return conn->internal->cop;
818 }
819
820 /* Connects to remote client.  Performs key exchange also.  Returns the
821    connection context to the connection callback. */
822
823 SilcAsyncOperation
824 silc_client_connect_to_client(SilcClient client,
825                               SilcClientConnectionParams *params,
826                               SilcPublicKey public_key,
827                               SilcPrivateKey private_key,
828                               char *remote_host, int port,
829                               SilcClientConnectCallback callback,
830                               void *context)
831 {
832   SilcClientConnection conn;
833
834   SILC_LOG_DEBUG(("Connecting to client"));
835
836   if (!client || !remote_host || !callback)
837     return NULL;
838
839   if (client->internal->run_callback) {
840     SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
841                     "callback has not been called yet."));
842     return NULL;
843   }
844
845   if (params)
846     params->no_authentication = TRUE;
847
848   /* Add new connection */
849   conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
850                                     public_key, private_key, remote_host,
851                                     port, callback, context);
852   if (!conn) {
853     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
854     return NULL;
855   }
856
857   /* Signal connection machine to start connecting */
858   conn->internal->connect = TRUE;
859   return conn->internal->cop;
860 }
861
862 /* Starts key exchange in the remote stream indicated by `stream'.  This
863    creates the connection context and returns it in the connection callback. */
864
865 SilcAsyncOperation
866 silc_client_key_exchange(SilcClient client,
867                          SilcClientConnectionParams *params,
868                          SilcPublicKey public_key,
869                          SilcPrivateKey private_key,
870                          SilcStream stream,
871                          SilcConnectionType conn_type,
872                          SilcClientConnectCallback callback,
873                          void *context)
874 {
875   SilcClientConnection conn;
876   const char *host;
877   SilcUInt16 port;
878
879   SILC_LOG_DEBUG(("Performing key exchange"));
880
881   if (!client || !stream || !callback)
882     return NULL;
883
884   if (client->internal->run_callback) {
885     SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
886                     "callback has not been called yet."));
887     return NULL;
888   }
889
890   if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
891     SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
892     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
893     return NULL;
894   }
895
896   /* Add new connection */
897   conn = silc_client_add_connection(client, conn_type, TRUE, params,
898                                     public_key, private_key,
899                                     (char *)host, port, callback, context);
900   if (!conn) {
901     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
902     return NULL;
903   }
904   conn->internal->user_stream = stream;
905
906   /* Signal connection to start key exchange */
907   conn->internal->key_exchange = TRUE;
908   return conn->internal->cop;
909 }
910
911 /* Closes remote connection */
912
913 void silc_client_close_connection(SilcClient client,
914                                   SilcClientConnection conn)
915 {
916   SILC_LOG_DEBUG(("Closing connection %p", conn));
917
918   /* Signal to close connection */
919   conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
920   if (!conn->internal->disconnected) {
921     conn->internal->disconnected = TRUE;
922     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
923   }
924 }
925
926 /* Allocates new client object. This has to be done before client may
927    work. After calling this one must call silc_client_init to initialize
928    the client. The `application' is application specific user data pointer
929    and caller must free it. */
930
931 SilcClient silc_client_alloc(SilcClientOperations *ops,
932                              SilcClientParams *params,
933                              void *application,
934                              const char *version_string)
935 {
936   SilcClient new_client;
937
938   new_client = silc_calloc(1, sizeof(*new_client));
939   if (!new_client)
940     return NULL;
941   new_client->application = application;
942
943   new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
944   if (!new_client->internal) {
945     silc_free(new_client);
946     return NULL;
947   }
948   new_client->internal->ops = ops;
949   new_client->internal->params =
950     silc_calloc(1, sizeof(*new_client->internal->params));
951   if (!version_string)
952     version_string = silc_version_string;
953   new_client->internal->silc_client_version = strdup(version_string);
954
955   if (params)
956     memcpy(new_client->internal->params, params, sizeof(*params));
957
958   new_client->internal->params->
959     nickname_format[sizeof(new_client->internal->
960                            params->nickname_format) - 1] = 0;
961
962   silc_atomic_init32(&new_client->internal->conns, 0);
963
964   return new_client;
965 }
966
967 /* Frees client object and its internals. */
968
969 void silc_client_free(SilcClient client)
970 {
971   if (client->schedule)
972     silc_schedule_uninit(client->schedule);
973
974   if (client->rng)
975     silc_rng_free(client->rng);
976
977   if (!client->internal->params->dont_register_crypto_library) {
978     silc_cipher_unregister_all();
979     silc_pkcs_unregister_all();
980     silc_hash_unregister_all();
981     silc_hmac_unregister_all();
982   }
983
984   if (client->internal->packet_engine)
985     silc_packet_engine_stop(client->internal->packet_engine);
986   if (client->internal->ftp_sessions)
987     silc_dlist_uninit(client->internal->ftp_sessions);
988   if (client->internal->lock)
989     silc_mutex_free(client->internal->lock);
990   silc_atomic_uninit32(&client->internal->conns);
991   silc_free(client->username);
992   silc_free(client->hostname);
993   silc_free(client->realname);
994   silc_free(client->internal->params);
995   silc_free(client->internal->silc_client_version);
996   silc_free(client->internal);
997   silc_free(client);
998 }
999
1000 /* Initializes the client. This makes all the necessary steps to make
1001    the client ready to be run. One must call silc_client_run to run the
1002    client. Returns FALSE if error occured, TRUE otherwise. */
1003
1004 SilcBool silc_client_init(SilcClient client, const char *username,
1005                           const char *hostname, const char *realname,
1006                           SilcClientRunning running, void *context)
1007 {
1008   SILC_LOG_DEBUG(("Initializing client"));
1009
1010   if (!client)
1011     return FALSE;
1012
1013   if (!username || !hostname) {
1014     SILC_LOG_ERROR(("Username and hostname must be given to "
1015                     "silc_client_init"));
1016     return FALSE;
1017   }
1018   if (!realname)
1019     realname = username;
1020
1021   /* Validate essential strings */
1022   if (!silc_identifier_verify(username, strlen(username),
1023                               SILC_STRING_UTF8, 128)) {
1024     SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1025                     username));
1026     return FALSE;
1027   }
1028   if (!silc_identifier_verify(hostname, strlen(hostname),
1029                               SILC_STRING_UTF8, 256)) {
1030     SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1031                     hostname));
1032     return FALSE;
1033   }
1034   if (!silc_utf8_valid(realname, strlen(realname))) {
1035     SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1036                     realname));
1037     return FALSE;
1038   }
1039
1040   /* Take the name strings */
1041   client->username = strdup(username);
1042   client->hostname = strdup(hostname);
1043   client->realname = strdup(realname);
1044   if (!username || !hostname || !realname)
1045     return FALSE;
1046
1047   client->internal->ftp_sessions = silc_dlist_init();
1048   if (!client->internal->ftp_sessions)
1049     return FALSE;
1050
1051   if (!client->internal->params->dont_register_crypto_library) {
1052     /* Initialize the crypto library.  If application has done this already
1053        this has no effect.  Also, we will not be overriding something
1054        application might have registered earlier. */
1055     silc_cipher_register_default();
1056     silc_pkcs_register_default();
1057     silc_hash_register_default();
1058     silc_hmac_register_default();
1059   }
1060
1061   /* Initialize random number generator */
1062   client->rng = silc_rng_alloc();
1063   if (!client->rng)
1064     return FALSE;
1065   silc_rng_init(client->rng);
1066   silc_rng_global_init(client->rng);
1067
1068   /* Initialize the scheduler */
1069   client->schedule = silc_schedule_init(0, client);
1070   if (!client->schedule)
1071     return FALSE;
1072
1073   /* Allocate client lock */
1074   silc_mutex_alloc(&client->internal->lock);
1075
1076   /* Register commands */
1077   silc_client_commands_register(client);
1078
1079   /* Start packet engine */
1080   client->internal->packet_engine =
1081     silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1082                              client);
1083   if (!client->internal->packet_engine)
1084     return FALSE;
1085
1086   /* Initialize and start the client FSM */
1087   client->internal->running = running;
1088   client->internal->running_context = context;
1089   silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1090   silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1091   silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1092
1093   /* Signal the application when we are running */
1094   client->internal->run_callback = TRUE;
1095   SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1096
1097   return TRUE;
1098 }
1099
1100 /* Starts the SILC client FSM machine and blocks here.  When this returns
1101    the client has ended. */
1102
1103 void silc_client_run(SilcClient client)
1104 {
1105   SILC_LOG_DEBUG(("Starting SILC client"));
1106
1107   /* Run the scheduler */
1108   silc_schedule(client->schedule);
1109 }
1110
1111 /* Call scheduler one iteration and return. */
1112
1113 void silc_client_run_one(SilcClient client)
1114 {
1115   if (silc_fsm_is_started(&client->internal->fsm))
1116     silc_schedule_one(client->schedule, 0);
1117 }
1118
1119 /* Stops the client. This is called to stop the client and thus to stop
1120    the program. */
1121
1122 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1123                       void *context)
1124 {
1125   SILC_LOG_DEBUG(("Stopping client"));
1126
1127   if (!silc_fsm_is_started(&client->internal->fsm)) {
1128     SILC_LOG_WARNING(("Attempting to stop client library before it has been "
1129                       "started (silc_client_init not called)"));
1130     return;
1131   }
1132
1133   client->internal->running = (SilcClientRunning)stopped;
1134   client->internal->running_context = context;
1135
1136   /* Signal to stop */
1137   client->internal->stop = TRUE;
1138   SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1139 }