updates.
[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_JOIN:
433       {
434         unsigned int mode;
435         char *topic;
436         SilcBuffer client_id_list;
437         unsigned int list_count;
438         SilcChannelEntry channel;
439
440         if (!success)
441           return;
442
443         app->screen->bottom_line->channel = va_arg(vp, char *);
444         channel = va_arg(vp, SilcChannelEntry);
445         mode = va_arg(vp, unsigned int);
446         (void)va_arg(vp, unsigned int);
447         (void)va_arg(vp, unsigned char *);
448         (void)va_arg(vp, unsigned char *);
449         (void)va_arg(vp, unsigned char *);
450         topic = va_arg(vp, char *);
451         (void)va_arg(vp, unsigned char *);
452         list_count = va_arg(vp, unsigned int);
453         client_id_list = va_arg(vp, SilcBuffer);
454
455         if (topic)
456           client->ops->say(client, conn, "Topic for %s: %s", 
457                            app->screen->bottom_line->channel, topic);
458         
459         app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
460         silc_screen_print_bottom_line(app->screen, 0);
461
462         /* Resolve the client information */
463         silc_client_get_clients_by_list(client, conn, list_count,
464                                         client_id_list,
465                                         silc_client_show_users, channel);
466       }
467       break;
468
469     case SILC_COMMAND_NICK:
470       {
471         SilcClientEntry entry;
472
473         if (!success)
474           return;
475
476         entry = va_arg(vp, SilcClientEntry);
477         silc_say(client, conn, "Your current nickname is %s", entry->nickname);
478         app->screen->bottom_line->nickname = entry->nickname;
479         silc_screen_print_bottom_line(app->screen, 0);
480       }
481       break;
482
483     case SILC_COMMAND_USERS:
484       if (!success)
485         return;
486
487       silc_list_start(conn->current_channel->clients);
488       while ((chu = silc_list_get(conn->current_channel->clients)) 
489              != SILC_LIST_END) {
490         if (chu->client == conn->local_entry) {
491           if (app->screen->bottom_line->mode)
492             silc_free(app->screen->bottom_line->mode);
493           app->screen->bottom_line->mode = 
494             silc_client_chumode_char(chu->mode);
495           silc_screen_print_bottom_line(app->screen, 0);
496           break;
497         }
498       break;
499       }
500     }
501 }
502
503 /* Called to indicate that connection was either successfully established
504    or connecting failed.  This is also the first time application receives
505    the SilcClientConnection objecet which it should save somewhere. */
506
507 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
508 {
509   SilcClientInternal app = (SilcClientInternal)client->application;
510
511   if (success) {
512     app->screen->bottom_line->connection = conn->remote_host;
513     silc_screen_print_bottom_line(app->screen, 0);
514     app->conn = conn;
515   }
516 }
517
518 /* Called to indicate that connection was disconnected to the server. */
519
520 void silc_disconnect(SilcClient client, SilcClientConnection conn)
521 {
522   SilcClientInternal app = (SilcClientInternal)client->application;
523
524   app->screen->bottom_line->connection = NULL;
525   silc_screen_print_bottom_line(app->screen, 0);
526   app->conn = NULL;
527 }
528
529 /* Asks passphrase from user on the input line. */
530
531 unsigned char *silc_ask_passphrase(SilcClient client, 
532                                    SilcClientConnection conn)
533 {
534   SilcClientInternal app = (SilcClientInternal)conn->client->application;
535   char pass1[256], pass2[256];
536   char *ret;
537   int try = 3;
538
539   while(try) {
540
541     /* Print prompt */
542     wattroff(app->screen->input_win, A_INVIS);
543     silc_screen_input_print_prompt(app->screen, "Passphrase: ");
544     wattron(app->screen->input_win, A_INVIS);
545     
546     /* Get string */
547     memset(pass1, 0, sizeof(pass1));
548     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
549     
550     /* Print retype prompt */
551     wattroff(app->screen->input_win, A_INVIS);
552     silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
553     wattron(app->screen->input_win, A_INVIS);
554     
555     /* Get string */
556     memset(pass2, 0, sizeof(pass2));
557     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
558
559     if (!strncmp(pass1, pass2, strlen(pass2)))
560       break;
561
562     try--;
563   }
564
565   ret = silc_calloc(strlen(pass1), sizeof(char));
566   memcpy(ret, pass1, strlen(pass1));
567
568   memset(pass1, 0, sizeof(pass1));
569   memset(pass2, 0, sizeof(pass2));
570
571   wattroff(app->screen->input_win, A_INVIS);
572   silc_screen_input_reset(app->screen);
573
574   return ret;
575 }
576
577 /* Verifies received public key. If user decides to trust the key it is
578    saved as trusted server key for later use. If user does not trust the
579    key this returns FALSE. */
580
581 int silc_verify_server_key(SilcClient client,
582                            SilcClientConnection conn, 
583                            unsigned char *pk, unsigned int pk_len,
584                            SilcSKEPKType pk_type)
585 {
586   SilcSocketConnection sock = conn->sock;
587   char filename[256];
588   char file[256];
589   char *hostname, *fingerprint;
590   struct passwd *pw;
591   struct stat st;
592
593   hostname = sock->hostname ? sock->hostname : sock->ip;
594
595   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
596     silc_say(client, conn, "We don't support server %s key type", hostname);
597     return FALSE;
598   }
599
600   pw = getpwuid(getuid());
601   if (!pw)
602     return FALSE;
603
604   memset(filename, 0, sizeof(filename));
605   memset(file, 0, sizeof(file));
606   snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
607            sock->port);
608   snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
609            pw->pw_dir, file);
610
611   /* Check wheter this key already exists */
612   if (stat(filename, &st) < 0) {
613
614     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
615     silc_say(client, conn, "Received server %s public key", hostname);
616     silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
617     silc_say(client, conn, "%s", fingerprint);
618     silc_free(fingerprint);
619
620     /* Ask user to verify the key and save it */
621     if (silc_client_ask_yes_no(client, 
622        "Would you like to accept the key (y/n)? "))
623       {
624         /* Save the key for future checking */
625         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
626                                        SILC_PKCS_FILE_PEM);
627         return TRUE;
628       }
629   } else {
630     /* The key already exists, verify it. */
631     SilcPublicKey public_key;
632     unsigned char *encpk;
633     unsigned int encpk_len;
634
635     /* Load the key file */
636     if (!silc_pkcs_load_public_key(filename, &public_key, 
637                                    SILC_PKCS_FILE_PEM))
638       if (!silc_pkcs_load_public_key(filename, &public_key, 
639                                      SILC_PKCS_FILE_BIN)) {
640         fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
641         silc_say(client, conn, "Received server %s public key", hostname);
642         silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
643         silc_say(client, conn, "%s", fingerprint);
644         silc_free(fingerprint);
645         silc_say(client, conn, "Could not load your local copy of the server %s key",
646                  hostname);
647         if (silc_client_ask_yes_no(client, 
648            "Would you like to accept the key anyway (y/n)? "))
649           {
650             /* Save the key for future checking */
651             unlink(filename);
652             silc_pkcs_save_public_key_data(filename, pk, pk_len,
653                                            SILC_PKCS_FILE_PEM);
654             return TRUE;
655           }
656         
657         return FALSE;
658       }
659   
660     /* Encode the key data */
661     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
662     if (!encpk) {
663       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
664       silc_say(client, conn, "Received server %s public key", hostname);
665       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
666       silc_say(client, conn, "%s", fingerprint);
667       silc_free(fingerprint);
668       silc_say(client, conn, "Your local copy of the server %s key is malformed",
669                hostname);
670       if (silc_client_ask_yes_no(client, 
671          "Would you like to accept the key anyway (y/n)? "))
672         {
673           /* Save the key for future checking */
674           unlink(filename);
675           silc_pkcs_save_public_key_data(filename, pk, pk_len,
676                                          SILC_PKCS_FILE_PEM);
677           return TRUE;
678         }
679
680       return FALSE;
681     }
682
683     if (memcmp(encpk, pk, encpk_len)) {
684       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
685       silc_say(client, conn, "Received server %s public key", hostname);
686       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
687       silc_say(client, conn, "%s", fingerprint);
688       silc_free(fingerprint);
689       silc_say(client, conn, "Server %s key does not match with your local copy",
690                hostname);
691       silc_say(client, conn, "It is possible that the key has expired or changed");
692       silc_say(client, conn, "It is also possible that some one is performing "
693                        "man-in-the-middle attack");
694       
695       /* Ask user to verify the key and save it */
696       if (silc_client_ask_yes_no(client, 
697          "Would you like to accept the key anyway (y/n)? "))
698         {
699           /* Save the key for future checking */
700           unlink(filename);
701           silc_pkcs_save_public_key_data(filename, pk, pk_len,
702                                          SILC_PKCS_FILE_PEM);
703           return TRUE;
704         }
705
706       silc_say(client, conn, "Will not accept server %s key", hostname);
707       return FALSE;
708     }
709
710     /* Local copy matched */
711     return TRUE;
712   }
713
714   silc_say(client, conn, "Will not accept server %s key", hostname);
715   return FALSE;
716 }
717
718 /* Find authentication method and authentication data by hostname and
719    port. The hostname may be IP address as well. The found authentication
720    method and authentication data is returned to `auth_meth', `auth_data'
721    and `auth_data_len'. The function returns TRUE if authentication method
722    is found and FALSE if not. `conn' may be NULL. */
723
724 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
725                          char *hostname, unsigned short port,
726                          SilcProtocolAuthMeth *auth_meth,
727                          unsigned char **auth_data,
728                          unsigned int *auth_data_len)
729 {
730   SilcClientInternal app = (SilcClientInternal)client->application;
731
732   if (app->config->conns) {
733     SilcClientConfigSectionConnection *conn = NULL;
734
735     /* Check if we find a match from user configured connections */
736     conn = silc_client_config_find_connection(app->config,
737                                               hostname,
738                                               port);
739     if (conn) {
740       /* Match found. Use the configured authentication method */
741       *auth_meth = conn->auth_meth;
742
743       if (conn->auth_data) {
744         *auth_data = strdup(conn->auth_data);
745         *auth_data_len = strlen(conn->auth_data);
746       }
747
748       return TRUE;
749     }
750   }
751
752   return FALSE;
753 }
754
755 /* Notifies application that failure packet was received.  This is called
756    if there is some protocol active in the client.  The `protocol' is the
757    protocol context.  The `failure' is opaque pointer to the failure
758    indication.  Note, that the `failure' is protocol dependant and application
759    must explicitly cast it to correct type.  Usually `failure' is 32 bit
760    failure type (see protocol specs for all protocol failure types). */
761
762 void silc_failure(SilcClient client, SilcClientConnection conn, 
763                   SilcProtocol protocol, void *failure)
764 {
765
766 }
767
768 /* Asks whether the user would like to perform the key agreement protocol.
769    This is called after we have received an key agreement packet or an
770    reply to our key agreement packet. This returns TRUE if the user wants
771    the library to perform the key agreement protocol and FALSE if it is not
772    desired (application may start it later by calling the function
773    silc_client_perform_key_agreement). */
774
775 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
776                        SilcClientEntry client_entry, char *hostname,
777                        int port,
778                        SilcKeyAgreementCallback *completion,
779                        void **context)
780 {
781
782   return FALSE;
783 }
784
785 /* SILC client operations */
786 SilcClientOperations ops = {
787   say:                  silc_say,
788   channel_message:      silc_channel_message,
789   private_message:      silc_private_message,
790   notify:               silc_notify,
791   command:              silc_command,
792   command_reply:        silc_command_reply,
793   connect:              silc_connect,
794   disconnect:           silc_disconnect,
795   get_auth_method:      silc_get_auth_method,
796   verify_server_key:    silc_verify_server_key,
797   ask_passphrase:       silc_ask_passphrase,
798   failure:              silc_failure,
799   key_agreement:        silc_key_agreement,
800 };