More 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->callback_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     SILC_LOG_DEBUG(("We are running, call running callback"));
410     client->internal->run_callback = FALSE;
411     client->internal->ops->running(client, client->application);
412     return SILC_FSM_CONTINUE;
413   }
414
415   /* NOT REACHED */
416   SILC_ASSERT(FALSE);
417   return SILC_FSM_CONTINUE;
418 }
419
420 /******************************* Private API ********************************/
421
422 /* Adds new connection.  Creates the connection context and returns it. */
423
424 static SilcClientConnection
425 silc_client_add_connection(SilcClient client,
426                            SilcConnectionType conn_type,
427                            SilcClientConnectionParams *params,
428                            SilcPublicKey public_key,
429                            SilcPrivateKey private_key,
430                            char *remote_host, int port,
431                            SilcClientConnectCallback callback,
432                            void *context)
433 {
434   SilcClientConnection conn;
435   SilcFSMThread thread;
436
437   if (!callback)
438     return NULL;
439
440   SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
441
442   conn = silc_calloc(1, sizeof(*conn));
443   if (!conn)
444     return NULL;
445
446   conn->client = client;
447   conn->public_key = public_key;
448   conn->private_key = private_key;
449   conn->remote_host = strdup(remote_host);
450   conn->remote_port = port ? port : 706;
451   conn->type = conn_type;
452   conn->callback = callback;
453   conn->callback_context = context;
454
455   conn->internal = silc_calloc(1, sizeof(*conn->internal));
456   if (!conn->internal) {
457     silc_free(conn);
458     return NULL;
459   }
460
461   if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
462     silc_free(conn);
463     silc_free(conn->internal);
464     return NULL;
465   }
466   if (params)
467     conn->internal->params = *params;
468   conn->internal->verbose = TRUE;
469   silc_list_init(conn->internal->pending_commands,
470                  struct SilcClientCommandContextStruct, next);
471   silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
472
473   conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
474                                                     NULL, NULL);
475   conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
476                                                      NULL, NULL);
477   conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
478                                                     NULL, NULL);
479   if (!conn->internal->client_cache || !conn->internal->channel_cache ||
480       !conn->internal->server_cache) {
481     silc_client_del_connection(client, conn);
482     return NULL;
483   }
484
485   conn->internal->ftp_sessions = silc_dlist_init();
486
487   /* Run the connection state machine.  If threads are in use the machine
488      is always run in a real thread. */
489   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
490                                  silc_client_fsm_destructor, NULL,
491                                  client->internal->params->threads);
492   if (!thread) {
493     silc_client_del_connection(client, conn);
494     return NULL;
495   }
496   silc_fsm_start(thread, silc_client_connection_st_start);
497
498   return conn;
499 }
500
501 /* Removes connection from client. Frees all memory. */
502
503 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
504 {
505 #if 0
506   SilcClientConnection c;
507   SilcIDCacheList list;
508   SilcIDCacheEntry entry;
509   SilcClientCommandPending *r;
510   SilcBool ret;
511
512   silc_dlist_start(client->internal->conns);
513   while ((c = silc_dlist_get(client->internal->conns)) != SILC_LIST_END) {
514     if (c != conn)
515       continue;
516
517     /* Free all cache entries */
518     if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
519       ret = silc_idcache_list_first(list, &entry);
520       while (ret) {
521         silc_client_del_client(client, conn, entry->context);
522         ret = silc_idcache_list_next(list, &entry);
523       }
524       silc_idcache_list_free(list);
525     }
526
527     if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
528       ret = silc_idcache_list_first(list, &entry);
529       while (ret) {
530         silc_client_del_channel(client, conn, entry->context);
531         ret = silc_idcache_list_next(list, &entry);
532       }
533       silc_idcache_list_free(list);
534     }
535
536     if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
537       ret = silc_idcache_list_first(list, &entry);
538       while (ret) {
539         silc_client_del_server(client, conn, entry->context);
540         ret = silc_idcache_list_next(list, &entry);
541       }
542       silc_idcache_list_free(list);
543     }
544
545     /* Clear ID caches */
546     if (conn->internal->client_cache)
547       silc_idcache_free(conn->internal->client_cache);
548     if (conn->internal->channel_cache)
549       silc_idcache_free(conn->internal->channel_cache);
550     if (conn->internal->server_cache)
551       silc_idcache_free(conn->internal->server_cache);
552
553     /* Free data (my ID is freed in above silc_client_del_client).
554        conn->nickname is freed when freeing the local_entry->nickname. */
555     silc_free(conn->remote_host);
556     silc_free(conn->local_id_data);
557     if (conn->internal->send_key)
558       silc_cipher_free(conn->internal->send_key);
559     if (conn->internal->receive_key)
560       silc_cipher_free(conn->internal->receive_key);
561     if (conn->internal->hmac_send)
562       silc_hmac_free(conn->internal->hmac_send);
563     if (conn->internal->hmac_receive)
564       silc_hmac_free(conn->internal->hmac_receive);
565     silc_free(conn->internal->rekey);
566
567     if (conn->internal->active_session) {
568       if (conn->sock)
569         conn->sock->user_data = NULL;
570       silc_client_ftp_session_free(conn->internal->active_session);
571       conn->internal->active_session = NULL;
572     }
573
574     silc_client_ftp_free_sessions(client, conn);
575
576     if (conn->internal->pending_commands) {
577       silc_dlist_start(conn->internal->pending_commands);
578       while ((r = silc_dlist_get(conn->internal->pending_commands))
579              != SILC_LIST_END)
580         silc_dlist_del(conn->internal->pending_commands, r);
581       silc_dlist_uninit(conn->internal->pending_commands);
582     }
583
584     silc_free(conn->internal);
585     memset(conn, 0, sizeof(*conn));
586     silc_free(conn);
587
588     silc_dlist_del(client->internal->conns, conn);
589   }
590 #endif /* 0 */
591 }
592
593
594 /******************************* Client API *********************************/
595
596 /* Connects to remote server.  This is the main routine used to connect
597    to remote SILC server.  Performs key exchange also.  Returns the
598    connection context to the connection callback. */
599
600 SilcBool silc_client_connect_to_server(SilcClient client,
601                                        SilcClientConnectionParams *params,
602                                        SilcPublicKey public_key,
603                                        SilcPrivateKey private_key,
604                                        char *remote_host, int port,
605                                        SilcClientConnectCallback callback,
606                                        void *context)
607 {
608   SilcClientConnection conn;
609
610   if (!client || !remote_host)
611     return FALSE;
612
613   /* Add new connection */
614   conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
615                                     public_key, private_key, remote_host,
616                                     port, callback, context);
617   if (!conn) {
618     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
619     return FALSE;
620   }
621
622   client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
623                              "Connecting to port %d of server %s",
624                              port, remote_host);
625
626   /* Signal connection machine to start connecting */
627   conn->internal->connect = TRUE;
628   return TRUE;
629 }
630
631 /* Connects to remote client.  Performs key exchange also.  Returns the
632    connection context to the connection callback. */
633
634 SilcBool silc_client_connect_to_client(SilcClient client,
635                                        SilcClientConnectionParams *params,
636                                        SilcPublicKey public_key,
637                                        SilcPrivateKey private_key,
638                                        char *remote_host, int port,
639                                        SilcClientConnectCallback callback,
640                                        void *context)
641 {
642   SilcClientConnection conn;
643
644   if (!client || !remote_host)
645     return FALSE;
646
647   /* Add new connection */
648   conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
649                                     public_key, private_key, remote_host,
650                                     port, callback, context);
651   if (!conn) {
652     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
653     return FALSE;
654   }
655
656   client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
657                              "Connecting to port %d of client host %s",
658                              port, remote_host);
659
660   /* Signal connection machine to start connecting */
661   conn->internal->connect = TRUE;
662   return TRUE;
663 }
664
665 /* Starts key exchange in the remote stream indicated by `stream'.  This
666    creates the connection context and returns it in the connection callback. */
667
668 SilcBool silc_client_key_exchange(SilcClient client,
669                                   SilcClientConnectionParams *params,
670                                   SilcPublicKey public_key,
671                                   SilcPrivateKey private_key,
672                                   SilcStream stream,
673                                   SilcConnectionType conn_type,
674                                   SilcClientConnectCallback callback,
675                                   void *context)
676 {
677   SilcClientConnection conn;
678   const char *host;
679   SilcUInt16 port;
680
681   if (!client || !stream)
682     return FALSE;
683
684   if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port))
685     return FALSE;
686
687   /* Add new connection */
688   conn = silc_client_add_connection(client, conn_type, params,
689                                     public_key, private_key,
690                                     (char *)host, port, callback, context);
691   if (!conn) {
692     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
693     return FALSE;
694   }
695   conn->stream = (void *)stream;
696
697   /* Signal connection to start key exchange */
698   conn->internal->key_exchange = TRUE;
699   return TRUE;
700 }
701
702 /* Closes remote connection */
703
704 void silc_client_close_connection(SilcClient client,
705                                   SilcClientConnection conn)
706 {
707
708 }
709
710 #if 0
711 /* Finalizes the connection to the remote SILC server. This is called
712    after authentication protocol has been completed. This send our
713    user information to the server to receive our client ID from
714    server. */
715
716 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
717 {
718   SilcProtocol protocol = (SilcProtocol)context;
719   SilcClientConnAuthInternalContext *ctx =
720     (SilcClientConnAuthInternalContext *)protocol->context;
721   SilcClient client = (SilcClient)ctx->client;
722   SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
723   SilcBuffer packet;
724
725   SILC_LOG_DEBUG(("Start"));
726
727   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
728       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
729     /* Error occured during protocol */
730     SILC_LOG_DEBUG(("Error during authentication protocol"));
731     ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
732     goto err;
733   }
734
735   if (conn->internal->params.detach_data) {
736     /* Send RESUME_CLIENT packet to the server, which is used to resume
737        old detached session back. */
738     SilcBuffer auth;
739     SilcClientID *old_client_id;
740     unsigned char *old_id;
741     SilcUInt16 old_id_len;
742
743     if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
744       ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
745       goto err;
746     }
747
748     old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
749     if (!old_client_id) {
750       silc_free(old_id);
751       ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
752       goto err;
753     }
754
755     /* Generate authentication data that server will verify */
756     auth = silc_auth_public_key_auth_generate(client->public_key,
757                                               client->private_key,
758                                               client->rng,
759                                               conn->internal->hash,
760                                               old_client_id, SILC_ID_CLIENT);
761     if (!auth) {
762       silc_free(old_client_id);
763       silc_free(old_id);
764       ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
765       goto err;
766     }
767
768     packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
769     silc_buffer_format(packet,
770                        SILC_STR_UI_SHORT(old_id_len),
771                        SILC_STR_UI_XNSTRING(old_id, old_id_len),
772                        SILC_STR_UI_XNSTRING(auth->data, auth->len),
773                        SILC_STR_END);
774
775     /* Send the packet */
776     silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
777                             NULL, 0, NULL, NULL,
778                             packet->data, packet->len, TRUE);
779     silc_buffer_free(packet);
780     silc_buffer_free(auth);
781     silc_free(old_client_id);
782     silc_free(old_id);
783   } else {
784     /* Send NEW_CLIENT packet to the server. We will become registered
785        to the SILC network after sending this packet and we will receive
786        client ID from the server. */
787     packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
788                                strlen(client->realname));
789     silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
790     silc_buffer_format(packet,
791                        SILC_STR_UI_SHORT(strlen(client->username)),
792                        SILC_STR_UI_XNSTRING(client->username,
793                                             strlen(client->username)),
794                        SILC_STR_UI_SHORT(strlen(client->realname)),
795                        SILC_STR_UI_XNSTRING(client->realname,
796                                             strlen(client->realname)),
797                        SILC_STR_END);
798
799     /* Send the packet */
800     silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
801                             NULL, 0, NULL, NULL,
802                             packet->data, packet->len, TRUE);
803     silc_buffer_free(packet);
804   }
805
806   /* Save remote ID. */
807   conn->remote_id = ctx->dest_id;
808   conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
809   conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
810
811   /* Register re-key timeout */
812   conn->internal->rekey->timeout = client->internal->params->rekey_secs;
813   conn->internal->rekey->context = (void *)client;
814   silc_schedule_task_add(client->schedule, conn->sock->sock,
815                          silc_client_rekey_callback,
816                          (void *)conn->sock, conn->internal->rekey->timeout, 0,
817                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
818
819   silc_protocol_free(protocol);
820   silc_free(ctx->auth_data);
821   if (ctx->ske)
822     silc_ske_free(ctx->ske);
823   silc_socket_free(ctx->sock);
824   silc_free(ctx);
825   conn->sock->protocol = NULL;
826   return;
827
828  err:
829   silc_protocol_free(protocol);
830   silc_free(ctx->auth_data);
831   silc_free(ctx->dest_id);
832   if (ctx->ske)
833     silc_ske_free(ctx->ske);
834   conn->sock->protocol = NULL;
835   silc_socket_free(ctx->sock);
836
837   /* Notify application of failure */
838   silc_schedule_task_add(client->schedule, ctx->sock->sock,
839                          silc_client_connect_failure_auth, ctx,
840                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
841 }
842
843 /* Client session resuming callback.  If the session was resumed
844    this callback is called after the resuming is completed.  This
845    will call the `connect' client operation to the application
846    since it has not been called yet. */
847
848 static void silc_client_resume_session_cb(SilcClient client,
849                                           SilcClientConnection conn,
850                                           SilcBool success,
851                                           void *context)
852 {
853   SilcBuffer sidp;
854
855   /* Notify application that connection is created to server */
856   client->internal->ops->connected(client, conn, success ?
857                                    SILC_CLIENT_CONN_SUCCESS_RESUME :
858                                    SILC_CLIENT_CONN_ERROR_RESUME);
859
860   if (success) {
861     /* Issue INFO command to fetch the real server name and server
862        information and other stuff. */
863     silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
864                                  silc_client_command_reply_info_i, 0,
865                                  ++conn->cmd_ident);
866     sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
867     silc_client_command_send(client, conn, SILC_COMMAND_INFO,
868                              conn->cmd_ident, 1, 2, sidp->data, sidp->len);
869     silc_buffer_free(sidp);
870   }
871 }
872
873 /* Processes incoming connection authentication method request packet.
874    It is a reply to our previously sent request. The packet can be used
875    to resolve the authentication method for the current session if the
876    client does not know it beforehand. */
877
878 void silc_client_connection_auth_request(SilcClient client,
879                                          SilcClientConnection conn,
880                                          SilcPacketContext *packet)
881 {
882   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
883   SilcUInt16 conn_type, auth_meth;
884   int ret;
885
886   /* If we haven't send our request then ignore this one. */
887   if (!conn->internal->connauth)
888     return;
889
890   /* Parse the payload */
891   ret = silc_buffer_unformat(packet->buffer,
892                              SILC_STR_UI_SHORT(&conn_type),
893                              SILC_STR_UI_SHORT(&auth_meth),
894                              SILC_STR_END);
895   if (ret == -1)
896     auth_meth = SILC_AUTH_NONE;
897
898   /* Call the request callback to notify application for received
899      authentication method information. */
900   if (conn->internal->connauth->callback)
901     (*conn->internal->connauth->callback)(client, conn, auth_meth,
902                                           conn->internal->connauth->context);
903
904   silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
905
906   silc_free(conn->internal->connauth);
907   conn->internal->connauth = NULL;
908 }
909
910 /* Timeout task callback called if the server does not reply to our
911    connection authentication method request in the specified time interval. */
912
913 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
914 {
915   SilcClientConnection conn = (SilcClientConnection)context;
916   SilcClient client = conn->client;
917
918   if (!conn->internal->connauth)
919     return;
920
921   /* Call the request callback to notify application */
922   if (conn->internal->connauth->callback)
923     (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
924                                           conn->internal->connauth->context);
925
926   silc_free(conn->internal->connauth);
927   conn->internal->connauth = NULL;
928 }
929
930 /* This function can be used to request the current authentication method
931    from the server. This may be called when connecting to the server
932    and the client library requests the authentication data from the
933    application. If the application does not know the current authentication
934    method it can request it from the server using this function.
935    The `callback' with `context' will be called after the server has
936    replied back with the current authentication method. */
937
938 void
939 silc_client_request_authentication_method(SilcClient client,
940                                           SilcClientConnection conn,
941                                           SilcConnectionAuthRequest callback,
942                                           void *context)
943 {
944   SilcClientConnAuthRequest connauth;
945   SilcBuffer packet;
946
947   assert(client && conn);
948   connauth = silc_calloc(1, sizeof(*connauth));
949   connauth->callback = callback;
950   connauth->context = context;
951
952   if (conn->internal->connauth)
953     silc_free(conn->internal->connauth);
954
955   conn->internal->connauth = connauth;
956
957   /* Assemble the request packet and send it to the server */
958   packet = silc_buffer_alloc(4);
959   silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
960   silc_buffer_format(packet,
961                      SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
962                      SILC_STR_UI_SHORT(SILC_AUTH_NONE),
963                      SILC_STR_END);
964   silc_client_packet_send(client, conn->sock,
965                           SILC_PACKET_CONNECTION_AUTH_REQUEST,
966                           NULL, 0, NULL, NULL,
967                           packet->data, packet->len, FALSE);
968   silc_buffer_free(packet);
969
970   /* Register a timeout in case server does not reply anything back. */
971   connauth->timeout =
972     silc_schedule_task_add(client->schedule, conn->sock->sock,
973                            silc_client_request_authentication_method_timeout,
974                            conn,
975                            client->internal->params->connauth_request_secs, 0,
976                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
977 }
978 #endif /* 0 */
979
980
981 /* Allocates new client object. This has to be done before client may
982    work. After calling this one must call silc_client_init to initialize
983    the client. The `application' is application specific user data pointer
984    and caller must free it. */
985
986 SilcClient silc_client_alloc(SilcClientOperations *ops,
987                              SilcClientParams *params,
988                              void *application,
989                              const char *version_string)
990 {
991   SilcClient new_client;
992
993   new_client = silc_calloc(1, sizeof(*new_client));
994   if (!new_client)
995     return NULL;
996   new_client->application = application;
997
998   new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
999   if (!new_client->internal) {
1000     silc_free(new_client);
1001     return NULL;
1002   }
1003   new_client->internal->ops = ops;
1004   new_client->internal->params =
1005     silc_calloc(1, sizeof(*new_client->internal->params));
1006   if (!version_string)
1007     version_string = silc_version_string;
1008   new_client->internal->silc_client_version = strdup(version_string);
1009
1010   if (params)
1011     memcpy(new_client->internal->params, params, sizeof(*params));
1012
1013   if (!new_client->internal->params->task_max)
1014     new_client->internal->params->task_max = 200;
1015
1016   if (!new_client->internal->params->rekey_secs)
1017     new_client->internal->params->rekey_secs = 3600;
1018
1019   if (!new_client->internal->params->connauth_request_secs)
1020     new_client->internal->params->connauth_request_secs = 2;
1021
1022   new_client->internal->params->
1023     nickname_format[sizeof(new_client->internal->
1024                            params->nickname_format) - 1] = 0;
1025
1026   return new_client;
1027 }
1028
1029 /* Frees client object and its internals. */
1030
1031 void silc_client_free(SilcClient client)
1032 {
1033   if (client) {
1034     if (client->rng)
1035       silc_rng_free(client->rng);
1036
1037     if (!client->internal->params->dont_register_crypto_library) {
1038       silc_cipher_unregister_all();
1039       silc_pkcs_unregister_all();
1040       silc_hash_unregister_all();
1041       silc_hmac_unregister_all();
1042     }
1043
1044     silc_free(client->internal->params);
1045     silc_free(client->internal->silc_client_version);
1046     silc_free(client->internal);
1047     silc_free(client);
1048   }
1049 }
1050
1051 /* Initializes the client. This makes all the necessary steps to make
1052    the client ready to be run. One must call silc_client_run to run the
1053    client. Returns FALSE if error occured, TRUE otherwise. */
1054
1055 SilcBool silc_client_init(SilcClient client, const char *username,
1056                           const char *hostname, const char *realname)
1057 {
1058   SILC_LOG_DEBUG(("Initializing client"));
1059
1060   if (!client)
1061     return FALSE;
1062
1063   if (!username || !hostname || !realname) {
1064     SILC_LOG_ERROR(("Username, hostname and realname must be given to "
1065                     "silc_client_init"));
1066     return FALSE;
1067   }
1068
1069   /* Validate essential strings */
1070   if (!silc_identifier_verify(username, strlen(username),
1071                               SILC_STRING_UTF8, 128)) {
1072     SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1073                     client->username));
1074     return FALSE;
1075   }
1076   if (!silc_identifier_verify(hostname, strlen(hostname),
1077                               SILC_STRING_UTF8, 256)) {
1078     SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1079                     client->hostname));
1080     return FALSE;
1081   }
1082   if (!silc_utf8_valid(realname, strlen(realname))) {
1083     SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1084                     client->realname));
1085     return FALSE;
1086   }
1087
1088   /* Take the name strings */
1089   client->username = strdup(username);
1090   client->hostname = strdup(hostname);
1091   client->realname = strdup(realname);
1092   if (!username || !hostname || !realname)
1093     return FALSE;
1094
1095   if (!client->internal->params->dont_register_crypto_library) {
1096     /* Initialize the crypto library.  If application has done this already
1097        this has no effect.  Also, we will not be overriding something
1098        application might have registered earlier. */
1099     silc_cipher_register_default();
1100     silc_pkcs_register_default();
1101     silc_hash_register_default();
1102     silc_hmac_register_default();
1103   }
1104
1105   /* Initialize random number generator */
1106   client->rng = silc_rng_alloc();
1107   silc_rng_init(client->rng);
1108   silc_rng_global_init(client->rng);
1109
1110   /* Initialize the scheduler */
1111   client->schedule =
1112     silc_schedule_init(client->internal->params->task_max ?
1113                        client->internal->params->task_max : 0, client);
1114   if (!client->schedule)
1115     return FALSE;
1116
1117   /* Start packet engine */
1118   client->internal->packet_engine =
1119     silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1120                              client);
1121   if (!client->internal->packet_engine)
1122     return FALSE;
1123
1124   /* Initialize FSM */
1125   if (!silc_fsm_init(&client->internal->fsm, client, NULL, NULL,
1126                      client->schedule))
1127     return FALSE;
1128   silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1129
1130   /* Allocate client lock */
1131   silc_mutex_alloc(&client->internal->lock);
1132
1133   /* Register commands */
1134   silc_client_commands_register(client);
1135
1136   /* Start the client machine */
1137   silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1138
1139   /* Signal the application when we are running */
1140   client->internal->run_callback = TRUE;
1141   SILC_FSM_SEMA_POST(&client->internal->wait_event);
1142
1143   return TRUE;
1144 }
1145
1146 /* Stops the client. This is called to stop the client and thus to stop
1147    the program. */
1148
1149 void silc_client_stop(SilcClient client)
1150 {
1151   SILC_LOG_DEBUG(("Stopping client"));
1152
1153   silc_schedule_stop(client->schedule);
1154   silc_schedule_uninit(client->schedule);
1155   silc_client_commands_unregister(client);
1156
1157   SILC_LOG_DEBUG(("Client stopped"));
1158 }
1159
1160 /* Starts the SILC client FSM machine and blocks here.  When this returns
1161    the client has ended. */
1162
1163 void silc_client_run(SilcClient client)
1164 {
1165   SILC_LOG_DEBUG(("Starting SILC client"));
1166
1167   /* Run the scheduler */
1168   silc_schedule(client->schedule);
1169 }
1170
1171 /* Call scheduler one iteration and return.  This cannot be called if threads
1172    are in use. */
1173
1174 void silc_client_run_one(SilcClient client)
1175 {
1176   silc_schedule_one(client->schedule, 0);
1177 }