Added WHOWAS command to server and client.
[silc.git] / apps / silc / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2000 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20
21 #include "clientincludes.h"
22
23 /* Prints a message with three star (*) sign before the actual message
24    on the current output window. This is used to print command outputs
25    and error messages. */
26
27 void silc_say(SilcClient client, SilcClientConnection conn, 
28               char *msg, ...)
29 {
30   va_list vp;
31   char message[2048];
32   SilcClientInternal app = (SilcClientInternal)client->application;
33
34   memset(message, 0, sizeof(message));
35   strncat(message, "\n***  ", 5);
36
37   va_start(vp, msg);
38   vsprintf(message + 5, msg, vp);
39   va_end(vp);
40   
41   /* Print the message */
42   silc_print_to_window(app->screen->output_win[0], message);
43 }
44
45 /* Message for a channel. The `sender' is the nickname of the sender 
46    received in the packet. The `channel_name' is the name of the channel. */
47
48 void silc_channel_message(SilcClient client, SilcClientConnection conn,
49                           SilcClientEntry sender, SilcChannelEntry channel
50                           , char *msg)
51 {
52   /* Message from client */
53   if (conn && !strcmp(conn->current_channel->channel_name, 
54                       channel->channel_name))
55     silc_print(client, "<%s> %s", sender ? sender->nickname : "[<unknown>]", 
56                msg);
57   else
58     silc_print(client, "<%s:%s> %s", sender ? sender->nickname : "[<unknown>]",
59                channel->channel_name, msg);
60 }
61
62 /* Private message to the client. The `sender' is the nickname of the
63    sender received in the packet. */
64
65 void silc_private_message(SilcClient client, SilcClientConnection conn,
66                           SilcClientEntry sender, char *msg)
67 {
68   silc_print(client, "*%s* %s", sender->nickname, msg);
69 }
70
71
72 /* Notify message to the client. The notify arguments are sent in the
73    same order as servers sends them. The arguments are same as received
74    from the server except for ID's.  If ID is received application receives
75    the corresponding entry to the ID. For example, if Client ID is received
76    application receives SilcClientEntry.  Also, if the notify type is
77    for channel the channel entry is sent to application (even if server
78    does not send it). */
79
80 void silc_notify(SilcClient client, SilcClientConnection conn, 
81                  SilcNotifyType type, ...)
82 {
83   SilcClientInternal app = (SilcClientInternal)client->application;
84   va_list vp;
85   char message[4096];
86   SilcClientEntry client_entry, client_entry2;
87   SilcChannelEntry channel_entry;
88   char *tmp = NULL;
89   unsigned int tmp_int;
90
91   va_start(vp, type);
92
93   memset(message, 0, sizeof(message));
94
95   /* Get arguments (defined by protocol in silc-pp-01 -draft) */
96   switch(type) {
97   case SILC_NOTIFY_TYPE_NONE:
98     tmp = va_arg(vp, char *);
99     if (!tmp)
100       return;
101     strcpy(message, tmp);
102     break;
103
104   case SILC_NOTIFY_TYPE_INVITE:
105     client_entry = va_arg(vp, SilcClientEntry);
106     channel_entry = va_arg(vp, SilcChannelEntry);
107     snprintf(message, sizeof(message), "%s invites you to channel %s", 
108              client_entry->nickname, channel_entry->channel_name);
109     break;
110
111   case SILC_NOTIFY_TYPE_JOIN:
112     client_entry = va_arg(vp, SilcClientEntry);
113     channel_entry = va_arg(vp, SilcChannelEntry);
114     snprintf(message, sizeof(message), "%s (%s) has joined channel %s", 
115              client_entry->nickname, client_entry->username, 
116              channel_entry->channel_name);
117     break;
118
119   case SILC_NOTIFY_TYPE_LEAVE:
120     client_entry = va_arg(vp, SilcClientEntry);
121     channel_entry = va_arg(vp, SilcChannelEntry);
122     if (client_entry->server)
123       snprintf(message, sizeof(message), "%s@%s has left channel %s", 
124                client_entry->nickname, client_entry->server, 
125                channel_entry->channel_name);
126     else
127       snprintf(message, sizeof(message), "%s has left channel %s", 
128                client_entry->nickname, channel_entry->channel_name);
129     break;
130
131   case SILC_NOTIFY_TYPE_SIGNOFF:
132     client_entry = va_arg(vp, SilcClientEntry);
133     tmp = va_arg(vp, char *);
134     if (client_entry->server)
135       snprintf(message, sizeof(message), "Signoff: %s@%s %s%s%s", 
136                client_entry->nickname, client_entry->server,
137                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
138     else
139       snprintf(message, sizeof(message), "Signoff: %s %s%s%s", 
140                client_entry->nickname,
141                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
142     break;
143
144   case SILC_NOTIFY_TYPE_TOPIC_SET:
145     client_entry = va_arg(vp, SilcClientEntry);
146     tmp = va_arg(vp, char *);
147     channel_entry = va_arg(vp, SilcChannelEntry);
148     if (client_entry->server)
149       snprintf(message, sizeof(message), "%s@%s set topic on %s: %s", 
150                client_entry->nickname, client_entry->server,
151                channel_entry->channel_name, tmp);
152     else
153       snprintf(message, sizeof(message), "%s set topic on %s: %s", 
154                client_entry->nickname, channel_entry->channel_name, tmp);
155     break;
156
157   case SILC_NOTIFY_TYPE_NICK_CHANGE:
158     client_entry = va_arg(vp, SilcClientEntry);
159     client_entry2 = va_arg(vp, SilcClientEntry);
160     if (client_entry->server && client_entry2->server)
161       snprintf(message, sizeof(message), "%s@%s is known as %s@%s", 
162                client_entry->nickname, client_entry->server,
163                client_entry2->nickname, client_entry2->server);
164     else
165       snprintf(message, sizeof(message), "%s is known as %s", 
166                client_entry->nickname, client_entry2->nickname);
167     break;
168
169   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
170     client_entry = va_arg(vp, SilcClientEntry);
171     tmp = silc_client_chmode(va_arg(vp, unsigned int));
172     channel_entry = va_arg(vp, SilcChannelEntry);
173     if (tmp)
174       snprintf(message, sizeof(message), "%s changed channel mode to +%s", 
175                client_entry->nickname, tmp);
176     else
177       snprintf(message, sizeof(message), "%s removed all channel modes", 
178                client_entry->nickname);
179     if (app->screen->bottom_line->channel_mode)
180       silc_free(app->screen->bottom_line->channel_mode);
181     app->screen->bottom_line->channel_mode = tmp;
182     silc_screen_print_bottom_line(app->screen, 0);
183     break;
184
185   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
186     client_entry = va_arg(vp, SilcClientEntry);
187     tmp_int = va_arg(vp, unsigned int);
188     tmp = silc_client_chumode(tmp_int);
189     client_entry2 = va_arg(vp, SilcClientEntry);
190     channel_entry = va_arg(vp, SilcChannelEntry);
191     if (tmp)
192       snprintf(message, sizeof(message), "%s changed %s's mode to +%s", 
193                client_entry->nickname, client_entry2->nickname, tmp);
194     else
195       snprintf(message, sizeof(message), "%s removed %s's modes", 
196                client_entry->nickname, client_entry2->nickname);
197     if (client_entry2 == conn->local_entry) {
198       if (app->screen->bottom_line->mode)
199         silc_free(app->screen->bottom_line->mode);
200       app->screen->bottom_line->mode = silc_client_chumode_char(tmp_int);
201       silc_screen_print_bottom_line(app->screen, 0);
202     }
203     silc_free(tmp);
204     break;
205
206   case SILC_NOTIFY_TYPE_MOTD:
207     {
208       char line[256];
209       int i;
210       tmp = va_arg(vp, unsigned char *);
211
212       i = 0;
213       while(tmp[i] != 0) {
214         if (tmp[i++] == '\n') {
215           memset(line, 0, sizeof(line));
216           strncat(line, tmp, i - 1);
217           tmp += i;
218           
219           silc_say(client, conn, "%s", line);
220           
221           if (!strlen(tmp))
222             break;
223           i = 0;
224         }
225       }
226     }
227     return;
228
229   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
230     break;
231
232   case SILC_NOTIFY_TYPE_KICKED:
233     client_entry = va_arg(vp, SilcClientEntry);
234     tmp = va_arg(vp, char *);
235     channel_entry = va_arg(vp, SilcChannelEntry);
236
237     if (client_entry == conn->local_entry) {
238       snprintf(message, sizeof(message), 
239                "You have been kicked off channel %s %s%s%s", 
240                conn->current_channel->channel_name,
241                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
242     } else {
243       snprintf(message, sizeof(message), 
244                "%s%s%s has been kicked off channel %s %s%s%s", 
245                client_entry->nickname, 
246                client_entry->server ? "@" : "",
247                client_entry->server ? client_entry->server : "",
248                conn->current_channel->channel_name,
249                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
250     }
251     break;
252
253   default:
254     break;
255   }
256
257   silc_print(client, "*** %s", message);
258 }
259
260 /* Command handler. This function is called always in the command function.
261    If error occurs it will be called as well. `conn' is the associated
262    client connection. `cmd_context' is the command context that was
263    originally sent to the command. `success' is FALSE if error occured
264    during command. `command' is the command being processed. It must be
265    noted that this is not reply from server. This is merely called just
266    after application has called the command. Just to tell application
267    that the command really was processed. */
268
269 void silc_command(SilcClient client, SilcClientConnection conn, 
270                   SilcClientCommandContext cmd_context, int success,
271                   SilcCommand command)
272 {
273   SilcClientInternal app = (SilcClientInternal)client->application;
274
275   if (!success)
276     return;
277
278   switch(command)
279     {
280         
281     case SILC_COMMAND_QUIT:
282       app->screen->bottom_line->channel = NULL;
283       silc_screen_print_bottom_line(app->screen, 0);
284       break;
285
286     case SILC_COMMAND_LEAVE:
287 #if 0
288       if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
289         app->screen->bottom_line->channel = NULL;
290         silc_screen_print_bottom_line(app->screen, 0);
291       }
292 #endif
293       break;
294
295     }
296 }
297
298 /* We've resolved all clients we don't know about, now just print the
299    users from the channel on the screen. */
300
301 void silc_client_show_users(SilcClient client,
302                             SilcClientConnection conn,
303                             SilcClientEntry *clients,
304                             unsigned int clients_count,
305                             void *context)
306 {
307   SilcChannelEntry channel = (SilcChannelEntry)context;
308   SilcChannelUser chu;
309   int k = 0, len1 = 0, len2 = 0;
310   char *name_list = NULL;
311
312   if (!clients)
313     return;
314
315   silc_list_start(channel->clients);
316   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
317     char *m, *n = chu->client->nickname;
318     if (!n)
319       continue;
320
321     len2 = strlen(n);
322     len1 += len2;
323     
324     name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
325     
326     m = silc_client_chumode_char(chu->mode);
327     if (m) {
328       memcpy(name_list + (len1 - len2), m, strlen(m));
329       len1 += strlen(m);
330       silc_free(m);
331     }
332     
333     memcpy(name_list + (len1 - len2), n, len2);
334     name_list[len1] = 0;
335     
336     if (k == silc_list_count(channel->clients) - 1)
337       break;
338     memcpy(name_list + len1, " ", 1);
339     len1++;
340     k++;
341   }
342
343   client->ops->say(client, conn, "Users on %s: %s", channel->channel_name, 
344                    name_list);
345   silc_free(name_list);
346 }
347
348 /* Command reply handler. This function is called always in the command reply
349    function. If error occurs it will be called as well. Normal scenario
350    is that it will be called after the received command data has been parsed
351    and processed. The function is used to pass the received command data to
352    the application. 
353
354    `conn' is the associated client connection. `cmd_payload' is the command
355    payload data received from server and it can be ignored. It is provided
356    if the application would like to re-parse the received command data,
357    however, it must be noted that the data is parsed already by the library
358    thus the payload can be ignored. `success' is FALSE if error occured.
359    In this case arguments are not sent to the application. `command' is the
360    command reply being processed. The function has variable argument list
361    and each command defines the number and type of arguments it passes to the
362    application (on error they are not sent). */
363
364 void silc_command_reply(SilcClient client, SilcClientConnection conn,
365                         SilcCommandPayload cmd_payload, int success,
366                         SilcCommand command, SilcCommandStatus status, ...)
367 {
368   SilcClientInternal app = (SilcClientInternal)client->application;
369   SilcChannelUser chu;
370   va_list vp;
371
372   va_start(vp, status);
373
374   switch(command)
375     {
376     case SILC_COMMAND_WHOIS:
377       {
378         char buf[1024], *nickname, *username, *realname;
379         int len;
380         unsigned int idle;
381
382         if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
383           char *tmp;
384           tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
385                                            3, NULL);
386           if (tmp)
387             client->ops->say(client, conn, "%s: %s", tmp,
388                              silc_client_command_status_message(status));
389           else
390             client->ops->say(client, conn, "%s",
391                              silc_client_command_status_message(status));
392           break;
393         }
394
395         if (!success)
396           return;
397
398         (void)va_arg(vp, SilcClientEntry);
399         nickname = va_arg(vp, char *);
400         username = va_arg(vp, char *);
401         realname = va_arg(vp, char *);
402         (void)va_arg(vp, void *);
403         idle = va_arg(vp, unsigned int);
404
405         memset(buf, 0, sizeof(buf));
406
407         if (nickname) {
408           len = strlen(nickname);
409           strncat(buf, nickname, len);
410           strncat(buf, " is ", 4);
411         }
412         
413         if (username) {
414           strncat(buf, username, strlen(nickname));
415         }
416         
417         if (realname) {
418           strncat(buf, " (", 2);
419           strncat(buf, realname, strlen(realname));
420           strncat(buf, ")", 1);
421         }
422
423         client->ops->say(client, conn, "%s", buf);
424         if (idle && nickname)
425           client->ops->say(client, conn, "%s has been idle %d %s",
426                            nickname,
427                            idle > 60 ? (idle / 60) : idle,
428                            idle > 60 ? "minutes" : "seconds");
429       }
430       break;
431
432     case SILC_COMMAND_WHOWAS:
433       {
434         char buf[1024], *nickname, *username, *realname;
435         int len;
436
437         if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
438           char *tmp;
439           tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
440                                            3, NULL);
441           if (tmp)
442             client->ops->say(client, conn, "%s: %s", tmp,
443                              silc_client_command_status_message(status));
444           else
445             client->ops->say(client, conn, "%s",
446                              silc_client_command_status_message(status));
447           break;
448         }
449
450         if (!success)
451           return;
452
453         (void)va_arg(vp, SilcClientEntry);
454         nickname = va_arg(vp, char *);
455         username = va_arg(vp, char *);
456         realname = va_arg(vp, char *);
457
458         memset(buf, 0, sizeof(buf));
459
460         if (nickname) {
461           len = strlen(nickname);
462           strncat(buf, nickname, len);
463           strncat(buf, " was ", 5);
464         }
465         
466         if (username) {
467           strncat(buf, username, strlen(nickname));
468         }
469         
470         if (realname) {
471           strncat(buf, " (", 2);
472           strncat(buf, realname, strlen(realname));
473           strncat(buf, ")", 1);
474         }
475
476         client->ops->say(client, conn, "%s", buf);
477       }
478       break;
479
480     case SILC_COMMAND_JOIN:
481       {
482         unsigned int mode;
483         char *topic;
484         SilcBuffer client_id_list;
485         unsigned int list_count;
486         SilcChannelEntry channel;
487
488         if (!success)
489           return;
490
491         app->screen->bottom_line->channel = va_arg(vp, char *);
492         channel = va_arg(vp, SilcChannelEntry);
493         mode = va_arg(vp, unsigned int);
494         (void)va_arg(vp, unsigned int);
495         (void)va_arg(vp, unsigned char *);
496         (void)va_arg(vp, unsigned char *);
497         (void)va_arg(vp, unsigned char *);
498         topic = va_arg(vp, char *);
499         (void)va_arg(vp, unsigned char *);
500         list_count = va_arg(vp, unsigned int);
501         client_id_list = va_arg(vp, SilcBuffer);
502
503         if (topic)
504           client->ops->say(client, conn, "Topic for %s: %s", 
505                            app->screen->bottom_line->channel, topic);
506         
507         app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
508         silc_screen_print_bottom_line(app->screen, 0);
509
510         /* Resolve the client information */
511         silc_client_get_clients_by_list(client, conn, list_count,
512                                         client_id_list,
513                                         silc_client_show_users, channel);
514       }
515       break;
516
517     case SILC_COMMAND_NICK:
518       {
519         SilcClientEntry entry;
520
521         if (!success)
522           return;
523
524         entry = va_arg(vp, SilcClientEntry);
525         silc_say(client, conn, "Your current nickname is %s", entry->nickname);
526         app->screen->bottom_line->nickname = entry->nickname;
527         silc_screen_print_bottom_line(app->screen, 0);
528       }
529       break;
530
531     case SILC_COMMAND_USERS:
532       if (!success)
533         return;
534
535       silc_list_start(conn->current_channel->clients);
536       while ((chu = silc_list_get(conn->current_channel->clients)) 
537              != SILC_LIST_END) {
538         if (chu->client == conn->local_entry) {
539           if (app->screen->bottom_line->mode)
540             silc_free(app->screen->bottom_line->mode);
541           app->screen->bottom_line->mode = 
542             silc_client_chumode_char(chu->mode);
543           silc_screen_print_bottom_line(app->screen, 0);
544           break;
545         }
546       break;
547       }
548     }
549 }
550
551 /* Called to indicate that connection was either successfully established
552    or connecting failed.  This is also the first time application receives
553    the SilcClientConnection objecet which it should save somewhere. */
554
555 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
556 {
557   SilcClientInternal app = (SilcClientInternal)client->application;
558
559   if (success) {
560     app->screen->bottom_line->connection = conn->remote_host;
561     silc_screen_print_bottom_line(app->screen, 0);
562     app->conn = conn;
563   }
564 }
565
566 /* Called to indicate that connection was disconnected to the server. */
567
568 void silc_disconnect(SilcClient client, SilcClientConnection conn)
569 {
570   SilcClientInternal app = (SilcClientInternal)client->application;
571
572   app->screen->bottom_line->connection = NULL;
573   silc_screen_print_bottom_line(app->screen, 0);
574   app->conn = NULL;
575 }
576
577 /* Asks passphrase from user on the input line. */
578
579 unsigned char *silc_ask_passphrase(SilcClient client, 
580                                    SilcClientConnection conn)
581 {
582   SilcClientInternal app = (SilcClientInternal)conn->client->application;
583   char pass1[256], pass2[256];
584   char *ret;
585   int try = 3;
586
587   while(try) {
588
589     /* Print prompt */
590     wattroff(app->screen->input_win, A_INVIS);
591     silc_screen_input_print_prompt(app->screen, "Passphrase: ");
592     wattron(app->screen->input_win, A_INVIS);
593     
594     /* Get string */
595     memset(pass1, 0, sizeof(pass1));
596     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
597     
598     /* Print retype prompt */
599     wattroff(app->screen->input_win, A_INVIS);
600     silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
601     wattron(app->screen->input_win, A_INVIS);
602     
603     /* Get string */
604     memset(pass2, 0, sizeof(pass2));
605     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
606
607     if (!strncmp(pass1, pass2, strlen(pass2)))
608       break;
609
610     try--;
611   }
612
613   ret = silc_calloc(strlen(pass1), sizeof(char));
614   memcpy(ret, pass1, strlen(pass1));
615
616   memset(pass1, 0, sizeof(pass1));
617   memset(pass2, 0, sizeof(pass2));
618
619   wattroff(app->screen->input_win, A_INVIS);
620   silc_screen_input_reset(app->screen);
621
622   return ret;
623 }
624
625 /* Verifies received public key. If user decides to trust the key it is
626    saved as trusted server key for later use. If user does not trust the
627    key this returns FALSE. */
628
629 int silc_verify_server_key(SilcClient client,
630                            SilcClientConnection conn, 
631                            unsigned char *pk, unsigned int pk_len,
632                            SilcSKEPKType pk_type)
633 {
634   SilcSocketConnection sock = conn->sock;
635   char filename[256];
636   char file[256];
637   char *hostname, *fingerprint;
638   struct passwd *pw;
639   struct stat st;
640
641   hostname = sock->hostname ? sock->hostname : sock->ip;
642
643   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
644     silc_say(client, conn, "We don't support server %s key type", hostname);
645     return FALSE;
646   }
647
648   pw = getpwuid(getuid());
649   if (!pw)
650     return FALSE;
651
652   memset(filename, 0, sizeof(filename));
653   memset(file, 0, sizeof(file));
654   snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
655            sock->port);
656   snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
657            pw->pw_dir, file);
658
659   /* Check wheter this key already exists */
660   if (stat(filename, &st) < 0) {
661
662     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
663     silc_say(client, conn, "Received server %s public key", hostname);
664     silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
665     silc_say(client, conn, "%s", fingerprint);
666     silc_free(fingerprint);
667
668     /* Ask user to verify the key and save it */
669     if (silc_client_ask_yes_no(client, 
670        "Would you like to accept the key (y/n)? "))
671       {
672         /* Save the key for future checking */
673         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
674                                        SILC_PKCS_FILE_PEM);
675         return TRUE;
676       }
677   } else {
678     /* The key already exists, verify it. */
679     SilcPublicKey public_key;
680     unsigned char *encpk;
681     unsigned int encpk_len;
682
683     /* Load the key file */
684     if (!silc_pkcs_load_public_key(filename, &public_key, 
685                                    SILC_PKCS_FILE_PEM))
686       if (!silc_pkcs_load_public_key(filename, &public_key, 
687                                      SILC_PKCS_FILE_BIN)) {
688         fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
689         silc_say(client, conn, "Received server %s public key", hostname);
690         silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
691         silc_say(client, conn, "%s", fingerprint);
692         silc_free(fingerprint);
693         silc_say(client, conn, "Could not load your local copy of the server %s key",
694                  hostname);
695         if (silc_client_ask_yes_no(client, 
696            "Would you like to accept the key anyway (y/n)? "))
697           {
698             /* Save the key for future checking */
699             unlink(filename);
700             silc_pkcs_save_public_key_data(filename, pk, pk_len,
701                                            SILC_PKCS_FILE_PEM);
702             return TRUE;
703           }
704         
705         return FALSE;
706       }
707   
708     /* Encode the key data */
709     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
710     if (!encpk) {
711       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
712       silc_say(client, conn, "Received server %s public key", hostname);
713       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
714       silc_say(client, conn, "%s", fingerprint);
715       silc_free(fingerprint);
716       silc_say(client, conn, "Your local copy of the server %s key is malformed",
717                hostname);
718       if (silc_client_ask_yes_no(client, 
719          "Would you like to accept the key anyway (y/n)? "))
720         {
721           /* Save the key for future checking */
722           unlink(filename);
723           silc_pkcs_save_public_key_data(filename, pk, pk_len,
724                                          SILC_PKCS_FILE_PEM);
725           return TRUE;
726         }
727
728       return FALSE;
729     }
730
731     if (memcmp(encpk, pk, encpk_len)) {
732       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
733       silc_say(client, conn, "Received server %s public key", hostname);
734       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
735       silc_say(client, conn, "%s", fingerprint);
736       silc_free(fingerprint);
737       silc_say(client, conn, "Server %s key does not match with your local copy",
738                hostname);
739       silc_say(client, conn, "It is possible that the key has expired or changed");
740       silc_say(client, conn, "It is also possible that some one is performing "
741                        "man-in-the-middle attack");
742       
743       /* Ask user to verify the key and save it */
744       if (silc_client_ask_yes_no(client, 
745          "Would you like to accept the key anyway (y/n)? "))
746         {
747           /* Save the key for future checking */
748           unlink(filename);
749           silc_pkcs_save_public_key_data(filename, pk, pk_len,
750                                          SILC_PKCS_FILE_PEM);
751           return TRUE;
752         }
753
754       silc_say(client, conn, "Will not accept server %s key", hostname);
755       return FALSE;
756     }
757
758     /* Local copy matched */
759     return TRUE;
760   }
761
762   silc_say(client, conn, "Will not accept server %s key", hostname);
763   return FALSE;
764 }
765
766 /* Find authentication method and authentication data by hostname and
767    port. The hostname may be IP address as well. The found authentication
768    method and authentication data is returned to `auth_meth', `auth_data'
769    and `auth_data_len'. The function returns TRUE if authentication method
770    is found and FALSE if not. `conn' may be NULL. */
771
772 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
773                          char *hostname, unsigned short port,
774                          SilcProtocolAuthMeth *auth_meth,
775                          unsigned char **auth_data,
776                          unsigned int *auth_data_len)
777 {
778   SilcClientInternal app = (SilcClientInternal)client->application;
779
780   if (app->config->conns) {
781     SilcClientConfigSectionConnection *conn = NULL;
782
783     /* Check if we find a match from user configured connections */
784     conn = silc_client_config_find_connection(app->config,
785                                               hostname,
786                                               port);
787     if (conn) {
788       /* Match found. Use the configured authentication method */
789       *auth_meth = conn->auth_meth;
790
791       if (conn->auth_data) {
792         *auth_data = strdup(conn->auth_data);
793         *auth_data_len = strlen(conn->auth_data);
794       }
795
796       return TRUE;
797     }
798   }
799
800   return FALSE;
801 }
802
803 /* Notifies application that failure packet was received.  This is called
804    if there is some protocol active in the client.  The `protocol' is the
805    protocol context.  The `failure' is opaque pointer to the failure
806    indication.  Note, that the `failure' is protocol dependant and application
807    must explicitly cast it to correct type.  Usually `failure' is 32 bit
808    failure type (see protocol specs for all protocol failure types). */
809
810 void silc_failure(SilcClient client, SilcClientConnection conn, 
811                   SilcProtocol protocol, void *failure)
812 {
813
814 }
815
816 /* Asks whether the user would like to perform the key agreement protocol.
817    This is called after we have received an key agreement packet or an
818    reply to our key agreement packet. This returns TRUE if the user wants
819    the library to perform the key agreement protocol and FALSE if it is not
820    desired (application may start it later by calling the function
821    silc_client_perform_key_agreement). */
822
823 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
824                        SilcClientEntry client_entry, char *hostname,
825                        int port,
826                        SilcKeyAgreementCallback *completion,
827                        void **context)
828 {
829
830   return FALSE;
831 }
832
833 /* SILC client operations */
834 SilcClientOperations ops = {
835   say:                  silc_say,
836   channel_message:      silc_channel_message,
837   private_message:      silc_private_message,
838   notify:               silc_notify,
839   command:              silc_command,
840   command_reply:        silc_command_reply,
841   connect:              silc_connect,
842   disconnect:           silc_disconnect,
843   get_auth_method:      silc_get_auth_method,
844   verify_server_key:    silc_verify_server_key,
845   ask_passphrase:       silc_ask_passphrase,
846   failure:              silc_failure,
847   key_agreement:        silc_key_agreement,
848 };