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     if (client_entry == conn->local_entry) {
118       SilcChannelUser chu;
119
120       silc_list_start(channel_entry->clients);
121       while ((chu = silc_list_get(channel_entry->clients)) != SILC_LIST_END) {
122         if (chu->client == client_entry) {
123           if (app->screen->bottom_line->mode)
124             silc_free(app->screen->bottom_line->mode);
125           app->screen->bottom_line->mode = silc_client_chumode_char(chu->mode);
126           silc_screen_print_bottom_line(app->screen, 0);
127           break;
128         }
129       }
130     }
131     break;
132
133   case SILC_NOTIFY_TYPE_LEAVE:
134     client_entry = va_arg(vp, SilcClientEntry);
135     channel_entry = va_arg(vp, SilcChannelEntry);
136     if (client_entry->server)
137       snprintf(message, sizeof(message), "%s@%s has left channel %s", 
138                client_entry->nickname, client_entry->server, 
139                channel_entry->channel_name);
140     else
141       snprintf(message, sizeof(message), "%s has left channel %s", 
142                client_entry->nickname, channel_entry->channel_name);
143     break;
144
145   case SILC_NOTIFY_TYPE_SIGNOFF:
146     client_entry = va_arg(vp, SilcClientEntry);
147     tmp = va_arg(vp, char *);
148     if (client_entry->server)
149       snprintf(message, sizeof(message), "Signoff: %s@%s %s%s%s", 
150                client_entry->nickname, client_entry->server,
151                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
152     else
153       snprintf(message, sizeof(message), "Signoff: %s %s%s%s", 
154                client_entry->nickname,
155                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
156     break;
157
158   case SILC_NOTIFY_TYPE_TOPIC_SET:
159     client_entry = va_arg(vp, SilcClientEntry);
160     tmp = va_arg(vp, char *);
161     channel_entry = va_arg(vp, SilcChannelEntry);
162     if (client_entry->server)
163       snprintf(message, sizeof(message), "%s@%s set topic on %s: %s", 
164                client_entry->nickname, client_entry->server,
165                channel_entry->channel_name, tmp);
166     else
167       snprintf(message, sizeof(message), "%s set topic on %s: %s", 
168                client_entry->nickname, channel_entry->channel_name, tmp);
169     break;
170
171   case SILC_NOTIFY_TYPE_NICK_CHANGE:
172     client_entry = va_arg(vp, SilcClientEntry);
173     client_entry2 = va_arg(vp, SilcClientEntry);
174     if (client_entry->server && client_entry2->server)
175       snprintf(message, sizeof(message), "%s@%s is known as %s@%s", 
176                client_entry->nickname, client_entry->server,
177                client_entry2->nickname, client_entry2->server);
178     else
179       snprintf(message, sizeof(message), "%s is known as %s", 
180                client_entry->nickname, client_entry2->nickname);
181     break;
182
183   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
184     client_entry = va_arg(vp, SilcClientEntry);
185     tmp = silc_client_chmode(va_arg(vp, unsigned int));
186     channel_entry = va_arg(vp, SilcChannelEntry);
187     if (tmp)
188       snprintf(message, sizeof(message), "%s changed channel mode to +%s", 
189                client_entry->nickname, tmp);
190     else
191       snprintf(message, sizeof(message), "%s removed all channel modes", 
192                client_entry->nickname);
193     if (app->screen->bottom_line->channel_mode)
194       silc_free(app->screen->bottom_line->channel_mode);
195     app->screen->bottom_line->channel_mode = tmp;
196     silc_screen_print_bottom_line(app->screen, 0);
197     break;
198
199   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
200     client_entry = va_arg(vp, SilcClientEntry);
201     tmp_int = va_arg(vp, unsigned int);
202     tmp = silc_client_chumode(tmp_int);
203     client_entry2 = va_arg(vp, SilcClientEntry);
204     channel_entry = va_arg(vp, SilcChannelEntry);
205     if (tmp)
206       snprintf(message, sizeof(message), "%s changed %s's mode to +%s", 
207                client_entry->nickname, client_entry2->nickname, tmp);
208     else
209       snprintf(message, sizeof(message), "%s removed %s's modes", 
210                client_entry->nickname, client_entry2->nickname);
211     if (client_entry2 == conn->local_entry) {
212       if (app->screen->bottom_line->mode)
213         silc_free(app->screen->bottom_line->mode);
214       app->screen->bottom_line->mode = silc_client_chumode_char(tmp_int);
215       silc_screen_print_bottom_line(app->screen, 0);
216     }
217     silc_free(tmp);
218     break;
219
220   case SILC_NOTIFY_TYPE_MOTD:
221     {
222       char line[256];
223       int i;
224       tmp = va_arg(vp, unsigned char *);
225
226       i = 0;
227       while(tmp[i] != 0) {
228         if (tmp[i++] == '\n') {
229           memset(line, 0, sizeof(line));
230           strncat(line, tmp, i - 1);
231           tmp += i;
232           
233           silc_say(client, conn, "%s", line);
234           
235           if (!strlen(tmp))
236             break;
237           i = 0;
238         }
239       }
240     }
241     return;
242
243   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
244     break;
245
246   case SILC_NOTIFY_TYPE_KICKED:
247     client_entry = va_arg(vp, SilcClientEntry);
248     tmp = va_arg(vp, char *);
249     channel_entry = va_arg(vp, SilcChannelEntry);
250
251     if (client_entry == conn->local_entry) {
252       snprintf(message, sizeof(message), 
253                "You have been kicked off channel %s %s%s%s", 
254                conn->current_channel->channel_name,
255                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
256     } else {
257       snprintf(message, sizeof(message), 
258                "%s%s%s has been kicked off channel %s %s%s%s", 
259                client_entry->nickname, 
260                client_entry->server ? "@" : "",
261                client_entry->server ? client_entry->server : "",
262                conn->current_channel->channel_name,
263                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
264     }
265     break;
266
267   case SILC_NOTIFY_TYPE_KILLED:
268     client_entry = va_arg(vp, SilcClientEntry);
269     tmp = va_arg(vp, char *);
270     channel_entry = va_arg(vp, SilcChannelEntry);
271
272     if (client_entry == conn->local_entry) {
273       snprintf(message, sizeof(message), 
274                "You have been killed from the SILC Network %s%s%s", 
275                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
276     } else {
277       snprintf(message, sizeof(message), 
278                "%s%s%s has been killed from the SILC Network %s%s%s", 
279                client_entry->nickname, 
280                client_entry->server ? "@" : "",
281                client_entry->server ? client_entry->server : "",
282                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
283     }
284     break;
285
286   default:
287     break;
288   }
289
290   silc_print(client, "*** %s", message);
291 }
292
293 /* Command handler. This function is called always in the command function.
294    If error occurs it will be called as well. `conn' is the associated
295    client connection. `cmd_context' is the command context that was
296    originally sent to the command. `success' is FALSE if error occured
297    during command. `command' is the command being processed. It must be
298    noted that this is not reply from server. This is merely called just
299    after application has called the command. Just to tell application
300    that the command really was processed. */
301
302 void silc_command(SilcClient client, SilcClientConnection conn, 
303                   SilcClientCommandContext cmd_context, int success,
304                   SilcCommand command)
305 {
306   SilcClientInternal app = (SilcClientInternal)client->application;
307
308   if (!success)
309     return;
310
311   switch(command)
312     {
313         
314     case SILC_COMMAND_QUIT:
315       app->screen->bottom_line->channel = NULL;
316       silc_screen_print_bottom_line(app->screen, 0);
317       break;
318
319     case SILC_COMMAND_LEAVE:
320 #if 0
321       if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
322         app->screen->bottom_line->channel = NULL;
323         silc_screen_print_bottom_line(app->screen, 0);
324       }
325 #endif
326       break;
327
328     }
329 }
330
331 /* We've resolved all clients we don't know about, now just print the
332    users from the channel on the screen. */
333
334 void silc_client_show_users(SilcClient client,
335                             SilcClientConnection conn,
336                             SilcClientEntry *clients,
337                             unsigned int clients_count,
338                             void *context)
339 {
340   SilcChannelEntry channel = (SilcChannelEntry)context;
341   SilcChannelUser chu;
342   int k = 0, len1 = 0, len2 = 0;
343   char *name_list = NULL;
344
345   if (!clients)
346     return;
347
348   silc_list_start(channel->clients);
349   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
350     char *m, *n = chu->client->nickname;
351     if (!n)
352       continue;
353
354     len2 = strlen(n);
355     len1 += len2;
356     
357     name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
358     
359     m = silc_client_chumode_char(chu->mode);
360     if (m) {
361       memcpy(name_list + (len1 - len2), m, strlen(m));
362       len1 += strlen(m);
363       silc_free(m);
364     }
365     
366     memcpy(name_list + (len1 - len2), n, len2);
367     name_list[len1] = 0;
368     
369     if (k == silc_list_count(channel->clients) - 1)
370       break;
371     memcpy(name_list + len1, " ", 1);
372     len1++;
373     k++;
374   }
375
376   client->ops->say(client, conn, "Users on %s: %s", channel->channel_name, 
377                    name_list);
378   silc_free(name_list);
379 }
380
381 /* Command reply handler. This function is called always in the command reply
382    function. If error occurs it will be called as well. Normal scenario
383    is that it will be called after the received command data has been parsed
384    and processed. The function is used to pass the received command data to
385    the application. 
386
387    `conn' is the associated client connection. `cmd_payload' is the command
388    payload data received from server and it can be ignored. It is provided
389    if the application would like to re-parse the received command data,
390    however, it must be noted that the data is parsed already by the library
391    thus the payload can be ignored. `success' is FALSE if error occured.
392    In this case arguments are not sent to the application. `command' is the
393    command reply being processed. The function has variable argument list
394    and each command defines the number and type of arguments it passes to the
395    application (on error they are not sent). */
396
397 void silc_command_reply(SilcClient client, SilcClientConnection conn,
398                         SilcCommandPayload cmd_payload, int success,
399                         SilcCommand command, SilcCommandStatus status, ...)
400 {
401   SilcClientInternal app = (SilcClientInternal)client->application;
402   SilcChannelUser chu;
403   va_list vp;
404
405   va_start(vp, status);
406
407   switch(command)
408     {
409     case SILC_COMMAND_WHOIS:
410       {
411         char buf[1024], *nickname, *username, *realname;
412         int len;
413         unsigned int idle, mode;
414         SilcBuffer channels;
415
416         if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
417             status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
418           char *tmp;
419           tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
420                                            3, NULL);
421           if (tmp)
422             client->ops->say(client, conn, "%s: %s", tmp,
423                              silc_client_command_status_message(status));
424           else
425             client->ops->say(client, conn, "%s",
426                              silc_client_command_status_message(status));
427           break;
428         }
429
430         if (!success)
431           return;
432
433         (void)va_arg(vp, SilcClientEntry);
434         nickname = va_arg(vp, char *);
435         username = va_arg(vp, char *);
436         realname = va_arg(vp, char *);
437         channels = va_arg(vp, SilcBuffer);
438         mode = va_arg(vp, unsigned int);
439         idle = va_arg(vp, unsigned int);
440
441         memset(buf, 0, sizeof(buf));
442
443         if (nickname) {
444           len = strlen(nickname);
445           strncat(buf, nickname, len);
446           strncat(buf, " is ", 4);
447         }
448         
449         if (username) {
450           strncat(buf, username, strlen(username));
451         }
452         
453         if (realname) {
454           strncat(buf, " (", 2);
455           strncat(buf, realname, strlen(realname));
456           strncat(buf, ")", 1);
457         }
458
459         client->ops->say(client, conn, "%s", buf);
460
461         if (channels) {
462           SilcDList list = silc_channel_payload_parse_list(channels);
463           if (list) {
464             SilcChannelPayload entry;
465
466             memset(buf, 0, sizeof(buf));
467             strcat(buf, "on channels: ");
468
469             silc_dlist_start(list);
470             while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
471               char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
472               unsigned int name_len;
473               char *name = silc_channel_get_name(entry, &name_len);
474
475               if (m)
476                 strncat(buf, m, strlen(m));
477               strncat(buf, name, name_len);
478               strncat(buf, " ", 1);
479               silc_free(m);
480             }
481
482             client->ops->say(client, conn, "%s", buf);
483             silc_channel_payload_list_free(list);
484           }
485         }
486
487         if (mode)
488           client->ops->say(client, conn, "%s is %s", nickname,
489                            (mode & SILC_UMODE_SERVER_OPERATOR) ?
490                            "Server Operator" :
491                            (mode & SILC_UMODE_ROUTER_OPERATOR) ?
492                            "SILC Operator" : "[Unknown mode]");
493
494         if (idle && nickname)
495           client->ops->say(client, conn, "%s has been idle %d %s",
496                            nickname,
497                            idle > 60 ? (idle / 60) : idle,
498                            idle > 60 ? "minutes" : "seconds");
499       }
500       break;
501
502     case SILC_COMMAND_WHOWAS:
503       {
504         char buf[1024], *nickname, *username, *realname;
505         int len;
506
507         if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
508             status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
509           char *tmp;
510           tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
511                                            3, NULL);
512           if (tmp)
513             client->ops->say(client, conn, "%s: %s", tmp,
514                              silc_client_command_status_message(status));
515           else
516             client->ops->say(client, conn, "%s",
517                              silc_client_command_status_message(status));
518           break;
519         }
520
521         if (!success)
522           return;
523
524         (void)va_arg(vp, SilcClientEntry);
525         nickname = va_arg(vp, char *);
526         username = va_arg(vp, char *);
527         realname = va_arg(vp, char *);
528
529         memset(buf, 0, sizeof(buf));
530
531         if (nickname) {
532           len = strlen(nickname);
533           strncat(buf, nickname, len);
534           strncat(buf, " was ", 5);
535         }
536         
537         if (username) {
538           strncat(buf, username, strlen(nickname));
539         }
540         
541         if (realname) {
542           strncat(buf, " (", 2);
543           strncat(buf, realname, strlen(realname));
544           strncat(buf, ")", 1);
545         }
546
547         client->ops->say(client, conn, "%s", buf);
548       }
549       break;
550
551     case SILC_COMMAND_JOIN:
552       {
553         unsigned int mode;
554         char *topic;
555         SilcBuffer client_id_list;
556         unsigned int list_count;
557         SilcChannelEntry channel;
558
559         if (!success)
560           return;
561
562         app->screen->bottom_line->channel = va_arg(vp, char *);
563         channel = va_arg(vp, SilcChannelEntry);
564         mode = va_arg(vp, unsigned int);
565         (void)va_arg(vp, unsigned int);
566         (void)va_arg(vp, unsigned char *);
567         (void)va_arg(vp, unsigned char *);
568         (void)va_arg(vp, unsigned char *);
569         topic = va_arg(vp, char *);
570         (void)va_arg(vp, unsigned char *);
571         list_count = va_arg(vp, unsigned int);
572         client_id_list = va_arg(vp, SilcBuffer);
573
574         if (topic)
575           client->ops->say(client, conn, "Topic for %s: %s", 
576                            app->screen->bottom_line->channel, topic);
577         
578         app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
579         silc_screen_print_bottom_line(app->screen, 0);
580
581         /* Resolve the client information */
582         silc_client_get_clients_by_list(client, conn, list_count,
583                                         client_id_list,
584                                         silc_client_show_users, channel);
585       }
586       break;
587
588     case SILC_COMMAND_NICK:
589       {
590         SilcClientEntry entry;
591
592         if (!success)
593           return;
594
595         entry = va_arg(vp, SilcClientEntry);
596         silc_say(client, conn, "Your current nickname is %s", entry->nickname);
597         app->screen->bottom_line->nickname = entry->nickname;
598         silc_screen_print_bottom_line(app->screen, 0);
599       }
600       break;
601
602     case SILC_COMMAND_UMODE:
603       {
604         unsigned int mode;
605
606         if (!success)
607           return;
608
609         mode = va_arg(vp, unsigned int);
610
611         if (!mode && app->screen->bottom_line->umode) {
612           silc_free(app->screen->bottom_line->umode);
613           app->screen->bottom_line->umode = NULL;
614         }
615
616         if (mode & SILC_UMODE_SERVER_OPERATOR) {
617           if (app->screen->bottom_line->umode)
618             silc_free(app->screen->bottom_line->umode);
619           app->screen->bottom_line->umode = strdup("Server Operator");;
620         }
621
622         if (mode & SILC_UMODE_ROUTER_OPERATOR) {
623           if (app->screen->bottom_line->umode)
624             silc_free(app->screen->bottom_line->umode);
625           app->screen->bottom_line->umode = strdup("SILC Operator");;
626         }
627
628         silc_screen_print_bottom_line(app->screen, 0);
629       }
630       break;
631
632     case SILC_COMMAND_OPER:
633       if (status == SILC_STATUS_OK) {
634         conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
635         if (app->screen->bottom_line->umode)
636           silc_free(app->screen->bottom_line->umode);
637         app->screen->bottom_line->umode = strdup("Server Operator");;
638         silc_screen_print_bottom_line(app->screen, 0);
639       }
640       break;
641
642     case SILC_COMMAND_SILCOPER:
643       if (status == SILC_STATUS_OK) {
644         conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
645         if (app->screen->bottom_line->umode)
646           silc_free(app->screen->bottom_line->umode);
647         app->screen->bottom_line->umode = strdup("SILC Operator");;
648         silc_screen_print_bottom_line(app->screen, 0);
649       }
650       break;
651
652     case SILC_COMMAND_USERS:
653       if (!success)
654         return;
655
656       silc_list_start(conn->current_channel->clients);
657       while ((chu = silc_list_get(conn->current_channel->clients)) 
658              != SILC_LIST_END) {
659         if (chu->client == conn->local_entry) {
660           if (app->screen->bottom_line->mode)
661             silc_free(app->screen->bottom_line->mode);
662           app->screen->bottom_line->mode = silc_client_chumode_char(chu->mode);
663           silc_screen_print_bottom_line(app->screen, 0);
664           break;
665         }
666       break;
667       }
668     }
669 }
670
671 /* Called to indicate that connection was either successfully established
672    or connecting failed.  This is also the first time application receives
673    the SilcClientConnection objecet which it should save somewhere. */
674
675 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
676 {
677   SilcClientInternal app = (SilcClientInternal)client->application;
678
679   if (success) {
680     app->screen->bottom_line->connection = conn->remote_host;
681     silc_screen_print_bottom_line(app->screen, 0);
682     app->conn = conn;
683   }
684 }
685
686 /* Called to indicate that connection was disconnected to the server. */
687
688 void silc_disconnect(SilcClient client, SilcClientConnection conn)
689 {
690   SilcClientInternal app = (SilcClientInternal)client->application;
691
692   app->screen->bottom_line->connection = NULL;
693   silc_screen_print_bottom_line(app->screen, 0);
694   app->conn = NULL;
695 }
696
697 /* Asks passphrase from user on the input line. */
698
699 unsigned char *silc_ask_passphrase(SilcClient client, 
700                                    SilcClientConnection conn)
701 {
702   SilcClientInternal app = (SilcClientInternal)conn->client->application;
703   char pass1[256], pass2[256];
704   char *ret;
705   int try = 3;
706
707   while(try) {
708
709     /* Print prompt */
710     wattroff(app->screen->input_win, A_INVIS);
711     silc_screen_input_print_prompt(app->screen, "Passphrase: ");
712     wattron(app->screen->input_win, A_INVIS);
713     
714     /* Get string */
715     memset(pass1, 0, sizeof(pass1));
716     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
717     
718     /* Print retype prompt */
719     wattroff(app->screen->input_win, A_INVIS);
720     silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
721     wattron(app->screen->input_win, A_INVIS);
722     
723     /* Get string */
724     memset(pass2, 0, sizeof(pass2));
725     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
726
727     if (!strncmp(pass1, pass2, strlen(pass2)))
728       break;
729
730     try--;
731   }
732
733   ret = silc_calloc(strlen(pass1), sizeof(char));
734   memcpy(ret, pass1, strlen(pass1));
735
736   memset(pass1, 0, sizeof(pass1));
737   memset(pass2, 0, sizeof(pass2));
738
739   wattroff(app->screen->input_win, A_INVIS);
740   silc_screen_input_reset(app->screen);
741
742   return ret;
743 }
744
745 /* Verifies received public key. If user decides to trust the key it is
746    saved as trusted server key for later use. If user does not trust the
747    key this returns FALSE. */
748
749 int silc_verify_server_key(SilcClient client,
750                            SilcClientConnection conn, 
751                            unsigned char *pk, unsigned int pk_len,
752                            SilcSKEPKType pk_type)
753 {
754   SilcSocketConnection sock = conn->sock;
755   char filename[256];
756   char file[256];
757   char *hostname, *fingerprint;
758   struct passwd *pw;
759   struct stat st;
760
761   hostname = sock->hostname ? sock->hostname : sock->ip;
762
763   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
764     silc_say(client, conn, "We don't support server %s key type", hostname);
765     return FALSE;
766   }
767
768   pw = getpwuid(getuid());
769   if (!pw)
770     return FALSE;
771
772   memset(filename, 0, sizeof(filename));
773   memset(file, 0, sizeof(file));
774   snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
775            sock->port);
776   snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
777            pw->pw_dir, file);
778
779   /* Check wheter this key already exists */
780   if (stat(filename, &st) < 0) {
781
782     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
783     silc_say(client, conn, "Received server %s public key", hostname);
784     silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
785     silc_say(client, conn, "%s", fingerprint);
786     silc_free(fingerprint);
787
788     /* Ask user to verify the key and save it */
789     if (silc_client_ask_yes_no(client, 
790        "Would you like to accept the key (y/n)? "))
791       {
792         /* Save the key for future checking */
793         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
794                                        SILC_PKCS_FILE_PEM);
795         return TRUE;
796       }
797   } else {
798     /* The key already exists, verify it. */
799     SilcPublicKey public_key;
800     unsigned char *encpk;
801     unsigned int encpk_len;
802
803     /* Load the key file */
804     if (!silc_pkcs_load_public_key(filename, &public_key, 
805                                    SILC_PKCS_FILE_PEM))
806       if (!silc_pkcs_load_public_key(filename, &public_key, 
807                                      SILC_PKCS_FILE_BIN)) {
808         fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
809         silc_say(client, conn, "Received server %s public key", hostname);
810         silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
811         silc_say(client, conn, "%s", fingerprint);
812         silc_free(fingerprint);
813         silc_say(client, conn, "Could not load your local copy of the server %s key",
814                  hostname);
815         if (silc_client_ask_yes_no(client, 
816            "Would you like to accept the key anyway (y/n)? "))
817           {
818             /* Save the key for future checking */
819             unlink(filename);
820             silc_pkcs_save_public_key_data(filename, pk, pk_len,
821                                            SILC_PKCS_FILE_PEM);
822             return TRUE;
823           }
824         
825         return FALSE;
826       }
827   
828     /* Encode the key data */
829     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
830     if (!encpk) {
831       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
832       silc_say(client, conn, "Received server %s public key", hostname);
833       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
834       silc_say(client, conn, "%s", fingerprint);
835       silc_free(fingerprint);
836       silc_say(client, conn, "Your local copy of the server %s key is malformed",
837                hostname);
838       if (silc_client_ask_yes_no(client, 
839          "Would you like to accept the key anyway (y/n)? "))
840         {
841           /* Save the key for future checking */
842           unlink(filename);
843           silc_pkcs_save_public_key_data(filename, pk, pk_len,
844                                          SILC_PKCS_FILE_PEM);
845           return TRUE;
846         }
847
848       return FALSE;
849     }
850
851     if (memcmp(encpk, pk, encpk_len)) {
852       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
853       silc_say(client, conn, "Received server %s public key", hostname);
854       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
855       silc_say(client, conn, "%s", fingerprint);
856       silc_free(fingerprint);
857       silc_say(client, conn, "Server %s key does not match with your local copy",
858                hostname);
859       silc_say(client, conn, "It is possible that the key has expired or changed");
860       silc_say(client, conn, "It is also possible that some one is performing "
861                        "man-in-the-middle attack");
862       
863       /* Ask user to verify the key and save it */
864       if (silc_client_ask_yes_no(client, 
865          "Would you like to accept the key anyway (y/n)? "))
866         {
867           /* Save the key for future checking */
868           unlink(filename);
869           silc_pkcs_save_public_key_data(filename, pk, pk_len,
870                                          SILC_PKCS_FILE_PEM);
871           return TRUE;
872         }
873
874       silc_say(client, conn, "Will not accept server %s key", hostname);
875       return FALSE;
876     }
877
878     /* Local copy matched */
879     return TRUE;
880   }
881
882   silc_say(client, conn, "Will not accept server %s key", hostname);
883   return FALSE;
884 }
885
886 /* Find authentication method and authentication data by hostname and
887    port. The hostname may be IP address as well. The found authentication
888    method and authentication data is returned to `auth_meth', `auth_data'
889    and `auth_data_len'. The function returns TRUE if authentication method
890    is found and FALSE if not. `conn' may be NULL. */
891
892 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
893                          char *hostname, unsigned short port,
894                          SilcProtocolAuthMeth *auth_meth,
895                          unsigned char **auth_data,
896                          unsigned int *auth_data_len)
897 {
898   SilcClientInternal app = (SilcClientInternal)client->application;
899
900   if (app->config->conns) {
901     SilcClientConfigSectionConnection *conn = NULL;
902
903     /* Check if we find a match from user configured connections */
904     conn = silc_client_config_find_connection(app->config,
905                                               hostname,
906                                               port);
907     if (conn) {
908       /* Match found. Use the configured authentication method */
909       *auth_meth = conn->auth_meth;
910
911       if (conn->auth_data) {
912         *auth_data = strdup(conn->auth_data);
913         *auth_data_len = strlen(conn->auth_data);
914       }
915
916       return TRUE;
917     }
918   }
919
920   return FALSE;
921 }
922
923 /* Notifies application that failure packet was received.  This is called
924    if there is some protocol active in the client.  The `protocol' is the
925    protocol context.  The `failure' is opaque pointer to the failure
926    indication.  Note, that the `failure' is protocol dependant and application
927    must explicitly cast it to correct type.  Usually `failure' is 32 bit
928    failure type (see protocol specs for all protocol failure types). */
929
930 void silc_failure(SilcClient client, SilcClientConnection conn, 
931                   SilcProtocol protocol, void *failure)
932 {
933
934 }
935
936 /* Asks whether the user would like to perform the key agreement protocol.
937    This is called after we have received an key agreement packet or an
938    reply to our key agreement packet. This returns TRUE if the user wants
939    the library to perform the key agreement protocol and FALSE if it is not
940    desired (application may start it later by calling the function
941    silc_client_perform_key_agreement). */
942
943 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
944                        SilcClientEntry client_entry, char *hostname,
945                        int port,
946                        SilcKeyAgreementCallback *completion,
947                        void **context)
948 {
949
950   return FALSE;
951 }
952
953 /* SILC client operations */
954 SilcClientOperations ops = {
955   say:                  silc_say,
956   channel_message:      silc_channel_message,
957   private_message:      silc_private_message,
958   notify:               silc_notify,
959   command:              silc_command,
960   command_reply:        silc_command_reply,
961   connect:              silc_connect,
962   disconnect:           silc_disconnect,
963   get_auth_method:      silc_get_auth_method,
964   verify_server_key:    silc_verify_server_key,
965   ask_passphrase:       silc_ask_passphrase,
966   failure:              silc_failure,
967   key_agreement:        silc_key_agreement,
968 };