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