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