Added KILL command.
[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   case SILC_NOTIFY_TYPE_KILLED:
254     client_entry = va_arg(vp, SilcClientEntry);
255     tmp = va_arg(vp, char *);
256     channel_entry = va_arg(vp, SilcChannelEntry);
257
258     if (client_entry == conn->local_entry) {
259       snprintf(message, sizeof(message), 
260                "You have been killed from the SILC Network %s%s%s", 
261                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
262     } else {
263       snprintf(message, sizeof(message), 
264                "%s%s%s has been killed from the SILC Network %s%s%s", 
265                client_entry->nickname, 
266                client_entry->server ? "@" : "",
267                client_entry->server ? client_entry->server : "",
268                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
269     }
270     break;
271
272   default:
273     break;
274   }
275
276   silc_print(client, "*** %s", message);
277 }
278
279 /* Command handler. This function is called always in the command function.
280    If error occurs it will be called as well. `conn' is the associated
281    client connection. `cmd_context' is the command context that was
282    originally sent to the command. `success' is FALSE if error occured
283    during command. `command' is the command being processed. It must be
284    noted that this is not reply from server. This is merely called just
285    after application has called the command. Just to tell application
286    that the command really was processed. */
287
288 void silc_command(SilcClient client, SilcClientConnection conn, 
289                   SilcClientCommandContext cmd_context, int success,
290                   SilcCommand command)
291 {
292   SilcClientInternal app = (SilcClientInternal)client->application;
293
294   if (!success)
295     return;
296
297   switch(command)
298     {
299         
300     case SILC_COMMAND_QUIT:
301       app->screen->bottom_line->channel = NULL;
302       silc_screen_print_bottom_line(app->screen, 0);
303       break;
304
305     case SILC_COMMAND_LEAVE:
306 #if 0
307       if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
308         app->screen->bottom_line->channel = NULL;
309         silc_screen_print_bottom_line(app->screen, 0);
310       }
311 #endif
312       break;
313
314     }
315 }
316
317 /* We've resolved all clients we don't know about, now just print the
318    users from the channel on the screen. */
319
320 void silc_client_show_users(SilcClient client,
321                             SilcClientConnection conn,
322                             SilcClientEntry *clients,
323                             unsigned int clients_count,
324                             void *context)
325 {
326   SilcChannelEntry channel = (SilcChannelEntry)context;
327   SilcChannelUser chu;
328   int k = 0, len1 = 0, len2 = 0;
329   char *name_list = NULL;
330
331   if (!clients)
332     return;
333
334   silc_list_start(channel->clients);
335   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
336     char *m, *n = chu->client->nickname;
337     if (!n)
338       continue;
339
340     len2 = strlen(n);
341     len1 += len2;
342     
343     name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
344     
345     m = silc_client_chumode_char(chu->mode);
346     if (m) {
347       memcpy(name_list + (len1 - len2), m, strlen(m));
348       len1 += strlen(m);
349       silc_free(m);
350     }
351     
352     memcpy(name_list + (len1 - len2), n, len2);
353     name_list[len1] = 0;
354     
355     if (k == silc_list_count(channel->clients) - 1)
356       break;
357     memcpy(name_list + len1, " ", 1);
358     len1++;
359     k++;
360   }
361
362   client->ops->say(client, conn, "Users on %s: %s", channel->channel_name, 
363                    name_list);
364   silc_free(name_list);
365 }
366
367 /* Command reply handler. This function is called always in the command reply
368    function. If error occurs it will be called as well. Normal scenario
369    is that it will be called after the received command data has been parsed
370    and processed. The function is used to pass the received command data to
371    the application. 
372
373    `conn' is the associated client connection. `cmd_payload' is the command
374    payload data received from server and it can be ignored. It is provided
375    if the application would like to re-parse the received command data,
376    however, it must be noted that the data is parsed already by the library
377    thus the payload can be ignored. `success' is FALSE if error occured.
378    In this case arguments are not sent to the application. `command' is the
379    command reply being processed. The function has variable argument list
380    and each command defines the number and type of arguments it passes to the
381    application (on error they are not sent). */
382
383 void silc_command_reply(SilcClient client, SilcClientConnection conn,
384                         SilcCommandPayload cmd_payload, int success,
385                         SilcCommand command, SilcCommandStatus status, ...)
386 {
387   SilcClientInternal app = (SilcClientInternal)client->application;
388   SilcChannelUser chu;
389   va_list vp;
390
391   va_start(vp, status);
392
393   switch(command)
394     {
395     case SILC_COMMAND_WHOIS:
396       {
397         char buf[1024], *nickname, *username, *realname;
398         int len;
399         unsigned int idle;
400
401         if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
402           char *tmp;
403           tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
404                                            3, NULL);
405           if (tmp)
406             client->ops->say(client, conn, "%s: %s", tmp,
407                              silc_client_command_status_message(status));
408           else
409             client->ops->say(client, conn, "%s",
410                              silc_client_command_status_message(status));
411           break;
412         }
413
414         if (!success)
415           return;
416
417         (void)va_arg(vp, SilcClientEntry);
418         nickname = va_arg(vp, char *);
419         username = va_arg(vp, char *);
420         realname = va_arg(vp, char *);
421         (void)va_arg(vp, void *);
422         idle = va_arg(vp, unsigned int);
423
424         memset(buf, 0, sizeof(buf));
425
426         if (nickname) {
427           len = strlen(nickname);
428           strncat(buf, nickname, len);
429           strncat(buf, " is ", 4);
430         }
431         
432         if (username) {
433           strncat(buf, username, strlen(username));
434         }
435         
436         if (realname) {
437           strncat(buf, " (", 2);
438           strncat(buf, realname, strlen(realname));
439           strncat(buf, ")", 1);
440         }
441
442         client->ops->say(client, conn, "%s", buf);
443         if (idle && nickname)
444           client->ops->say(client, conn, "%s has been idle %d %s",
445                            nickname,
446                            idle > 60 ? (idle / 60) : idle,
447                            idle > 60 ? "minutes" : "seconds");
448       }
449       break;
450
451     case SILC_COMMAND_WHOWAS:
452       {
453         char buf[1024], *nickname, *username, *realname;
454         int len;
455
456         if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
457           char *tmp;
458           tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
459                                            3, NULL);
460           if (tmp)
461             client->ops->say(client, conn, "%s: %s", tmp,
462                              silc_client_command_status_message(status));
463           else
464             client->ops->say(client, conn, "%s",
465                              silc_client_command_status_message(status));
466           break;
467         }
468
469         if (!success)
470           return;
471
472         (void)va_arg(vp, SilcClientEntry);
473         nickname = va_arg(vp, char *);
474         username = va_arg(vp, char *);
475         realname = va_arg(vp, char *);
476
477         memset(buf, 0, sizeof(buf));
478
479         if (nickname) {
480           len = strlen(nickname);
481           strncat(buf, nickname, len);
482           strncat(buf, " was ", 5);
483         }
484         
485         if (username) {
486           strncat(buf, username, strlen(nickname));
487         }
488         
489         if (realname) {
490           strncat(buf, " (", 2);
491           strncat(buf, realname, strlen(realname));
492           strncat(buf, ")", 1);
493         }
494
495         client->ops->say(client, conn, "%s", buf);
496       }
497       break;
498
499     case SILC_COMMAND_JOIN:
500       {
501         unsigned int mode;
502         char *topic;
503         SilcBuffer client_id_list;
504         unsigned int list_count;
505         SilcChannelEntry channel;
506
507         if (!success)
508           return;
509
510         app->screen->bottom_line->channel = va_arg(vp, char *);
511         channel = va_arg(vp, SilcChannelEntry);
512         mode = va_arg(vp, unsigned int);
513         (void)va_arg(vp, unsigned int);
514         (void)va_arg(vp, unsigned char *);
515         (void)va_arg(vp, unsigned char *);
516         (void)va_arg(vp, unsigned char *);
517         topic = va_arg(vp, char *);
518         (void)va_arg(vp, unsigned char *);
519         list_count = va_arg(vp, unsigned int);
520         client_id_list = va_arg(vp, SilcBuffer);
521
522         if (topic)
523           client->ops->say(client, conn, "Topic for %s: %s", 
524                            app->screen->bottom_line->channel, topic);
525         
526         app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
527         silc_screen_print_bottom_line(app->screen, 0);
528
529         /* Resolve the client information */
530         silc_client_get_clients_by_list(client, conn, list_count,
531                                         client_id_list,
532                                         silc_client_show_users, channel);
533       }
534       break;
535
536     case SILC_COMMAND_NICK:
537       {
538         SilcClientEntry entry;
539
540         if (!success)
541           return;
542
543         entry = va_arg(vp, SilcClientEntry);
544         silc_say(client, conn, "Your current nickname is %s", entry->nickname);
545         app->screen->bottom_line->nickname = entry->nickname;
546         silc_screen_print_bottom_line(app->screen, 0);
547       }
548       break;
549
550     case SILC_COMMAND_USERS:
551       if (!success)
552         return;
553
554       silc_list_start(conn->current_channel->clients);
555       while ((chu = silc_list_get(conn->current_channel->clients)) 
556              != SILC_LIST_END) {
557         if (chu->client == conn->local_entry) {
558           if (app->screen->bottom_line->mode)
559             silc_free(app->screen->bottom_line->mode);
560           app->screen->bottom_line->mode = 
561             silc_client_chumode_char(chu->mode);
562           silc_screen_print_bottom_line(app->screen, 0);
563           break;
564         }
565       break;
566       }
567     }
568 }
569
570 /* Called to indicate that connection was either successfully established
571    or connecting failed.  This is also the first time application receives
572    the SilcClientConnection objecet which it should save somewhere. */
573
574 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
575 {
576   SilcClientInternal app = (SilcClientInternal)client->application;
577
578   if (success) {
579     app->screen->bottom_line->connection = conn->remote_host;
580     silc_screen_print_bottom_line(app->screen, 0);
581     app->conn = conn;
582   }
583 }
584
585 /* Called to indicate that connection was disconnected to the server. */
586
587 void silc_disconnect(SilcClient client, SilcClientConnection conn)
588 {
589   SilcClientInternal app = (SilcClientInternal)client->application;
590
591   app->screen->bottom_line->connection = NULL;
592   silc_screen_print_bottom_line(app->screen, 0);
593   app->conn = NULL;
594 }
595
596 /* Asks passphrase from user on the input line. */
597
598 unsigned char *silc_ask_passphrase(SilcClient client, 
599                                    SilcClientConnection conn)
600 {
601   SilcClientInternal app = (SilcClientInternal)conn->client->application;
602   char pass1[256], pass2[256];
603   char *ret;
604   int try = 3;
605
606   while(try) {
607
608     /* Print prompt */
609     wattroff(app->screen->input_win, A_INVIS);
610     silc_screen_input_print_prompt(app->screen, "Passphrase: ");
611     wattron(app->screen->input_win, A_INVIS);
612     
613     /* Get string */
614     memset(pass1, 0, sizeof(pass1));
615     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
616     
617     /* Print retype prompt */
618     wattroff(app->screen->input_win, A_INVIS);
619     silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
620     wattron(app->screen->input_win, A_INVIS);
621     
622     /* Get string */
623     memset(pass2, 0, sizeof(pass2));
624     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
625
626     if (!strncmp(pass1, pass2, strlen(pass2)))
627       break;
628
629     try--;
630   }
631
632   ret = silc_calloc(strlen(pass1), sizeof(char));
633   memcpy(ret, pass1, strlen(pass1));
634
635   memset(pass1, 0, sizeof(pass1));
636   memset(pass2, 0, sizeof(pass2));
637
638   wattroff(app->screen->input_win, A_INVIS);
639   silc_screen_input_reset(app->screen);
640
641   return ret;
642 }
643
644 /* Verifies received public key. If user decides to trust the key it is
645    saved as trusted server key for later use. If user does not trust the
646    key this returns FALSE. */
647
648 int silc_verify_server_key(SilcClient client,
649                            SilcClientConnection conn, 
650                            unsigned char *pk, unsigned int pk_len,
651                            SilcSKEPKType pk_type)
652 {
653   SilcSocketConnection sock = conn->sock;
654   char filename[256];
655   char file[256];
656   char *hostname, *fingerprint;
657   struct passwd *pw;
658   struct stat st;
659
660   hostname = sock->hostname ? sock->hostname : sock->ip;
661
662   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
663     silc_say(client, conn, "We don't support server %s key type", hostname);
664     return FALSE;
665   }
666
667   pw = getpwuid(getuid());
668   if (!pw)
669     return FALSE;
670
671   memset(filename, 0, sizeof(filename));
672   memset(file, 0, sizeof(file));
673   snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
674            sock->port);
675   snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
676            pw->pw_dir, file);
677
678   /* Check wheter this key already exists */
679   if (stat(filename, &st) < 0) {
680
681     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
682     silc_say(client, conn, "Received server %s public key", hostname);
683     silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
684     silc_say(client, conn, "%s", fingerprint);
685     silc_free(fingerprint);
686
687     /* Ask user to verify the key and save it */
688     if (silc_client_ask_yes_no(client, 
689        "Would you like to accept the key (y/n)? "))
690       {
691         /* Save the key for future checking */
692         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
693                                        SILC_PKCS_FILE_PEM);
694         return TRUE;
695       }
696   } else {
697     /* The key already exists, verify it. */
698     SilcPublicKey public_key;
699     unsigned char *encpk;
700     unsigned int encpk_len;
701
702     /* Load the key file */
703     if (!silc_pkcs_load_public_key(filename, &public_key, 
704                                    SILC_PKCS_FILE_PEM))
705       if (!silc_pkcs_load_public_key(filename, &public_key, 
706                                      SILC_PKCS_FILE_BIN)) {
707         fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
708         silc_say(client, conn, "Received server %s public key", hostname);
709         silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
710         silc_say(client, conn, "%s", fingerprint);
711         silc_free(fingerprint);
712         silc_say(client, conn, "Could not load your local copy of the server %s key",
713                  hostname);
714         if (silc_client_ask_yes_no(client, 
715            "Would you like to accept the key anyway (y/n)? "))
716           {
717             /* Save the key for future checking */
718             unlink(filename);
719             silc_pkcs_save_public_key_data(filename, pk, pk_len,
720                                            SILC_PKCS_FILE_PEM);
721             return TRUE;
722           }
723         
724         return FALSE;
725       }
726   
727     /* Encode the key data */
728     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
729     if (!encpk) {
730       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
731       silc_say(client, conn, "Received server %s public key", hostname);
732       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
733       silc_say(client, conn, "%s", fingerprint);
734       silc_free(fingerprint);
735       silc_say(client, conn, "Your local copy of the server %s key is malformed",
736                hostname);
737       if (silc_client_ask_yes_no(client, 
738          "Would you like to accept the key anyway (y/n)? "))
739         {
740           /* Save the key for future checking */
741           unlink(filename);
742           silc_pkcs_save_public_key_data(filename, pk, pk_len,
743                                          SILC_PKCS_FILE_PEM);
744           return TRUE;
745         }
746
747       return FALSE;
748     }
749
750     if (memcmp(encpk, pk, encpk_len)) {
751       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
752       silc_say(client, conn, "Received server %s public key", hostname);
753       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
754       silc_say(client, conn, "%s", fingerprint);
755       silc_free(fingerprint);
756       silc_say(client, conn, "Server %s key does not match with your local copy",
757                hostname);
758       silc_say(client, conn, "It is possible that the key has expired or changed");
759       silc_say(client, conn, "It is also possible that some one is performing "
760                        "man-in-the-middle attack");
761       
762       /* Ask user to verify the key and save it */
763       if (silc_client_ask_yes_no(client, 
764          "Would you like to accept the key anyway (y/n)? "))
765         {
766           /* Save the key for future checking */
767           unlink(filename);
768           silc_pkcs_save_public_key_data(filename, pk, pk_len,
769                                          SILC_PKCS_FILE_PEM);
770           return TRUE;
771         }
772
773       silc_say(client, conn, "Will not accept server %s key", hostname);
774       return FALSE;
775     }
776
777     /* Local copy matched */
778     return TRUE;
779   }
780
781   silc_say(client, conn, "Will not accept server %s key", hostname);
782   return FALSE;
783 }
784
785 /* Find authentication method and authentication data by hostname and
786    port. The hostname may be IP address as well. The found authentication
787    method and authentication data is returned to `auth_meth', `auth_data'
788    and `auth_data_len'. The function returns TRUE if authentication method
789    is found and FALSE if not. `conn' may be NULL. */
790
791 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
792                          char *hostname, unsigned short port,
793                          SilcProtocolAuthMeth *auth_meth,
794                          unsigned char **auth_data,
795                          unsigned int *auth_data_len)
796 {
797   SilcClientInternal app = (SilcClientInternal)client->application;
798
799   if (app->config->conns) {
800     SilcClientConfigSectionConnection *conn = NULL;
801
802     /* Check if we find a match from user configured connections */
803     conn = silc_client_config_find_connection(app->config,
804                                               hostname,
805                                               port);
806     if (conn) {
807       /* Match found. Use the configured authentication method */
808       *auth_meth = conn->auth_meth;
809
810       if (conn->auth_data) {
811         *auth_data = strdup(conn->auth_data);
812         *auth_data_len = strlen(conn->auth_data);
813       }
814
815       return TRUE;
816     }
817   }
818
819   return FALSE;
820 }
821
822 /* Notifies application that failure packet was received.  This is called
823    if there is some protocol active in the client.  The `protocol' is the
824    protocol context.  The `failure' is opaque pointer to the failure
825    indication.  Note, that the `failure' is protocol dependant and application
826    must explicitly cast it to correct type.  Usually `failure' is 32 bit
827    failure type (see protocol specs for all protocol failure types). */
828
829 void silc_failure(SilcClient client, SilcClientConnection conn, 
830                   SilcProtocol protocol, void *failure)
831 {
832
833 }
834
835 /* Asks whether the user would like to perform the key agreement protocol.
836    This is called after we have received an key agreement packet or an
837    reply to our key agreement packet. This returns TRUE if the user wants
838    the library to perform the key agreement protocol and FALSE if it is not
839    desired (application may start it later by calling the function
840    silc_client_perform_key_agreement). */
841
842 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
843                        SilcClientEntry client_entry, char *hostname,
844                        int port,
845                        SilcKeyAgreementCallback *completion,
846                        void **context)
847 {
848
849   return FALSE;
850 }
851
852 /* SILC client operations */
853 SilcClientOperations ops = {
854   say:                  silc_say,
855   channel_message:      silc_channel_message,
856   private_message:      silc_private_message,
857   notify:               silc_notify,
858   command:              silc_command,
859   command_reply:        silc_command_reply,
860   connect:              silc_connect,
861   disconnect:           silc_disconnect,
862   get_auth_method:      silc_get_auth_method,
863   verify_server_key:    silc_verify_server_key,
864   ask_passphrase:       silc_ask_passphrase,
865   failure:              silc_failure,
866   key_agreement:        silc_key_agreement,
867 };