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           char *tmp;
418           tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
419                                            3, NULL);
420           if (tmp)
421             client->ops->say(client, conn, "%s: %s", tmp,
422                              silc_client_command_status_message(status));
423           else
424             client->ops->say(client, conn, "%s",
425                              silc_client_command_status_message(status));
426           break;
427         }
428
429         if (!success)
430           return;
431
432         (void)va_arg(vp, SilcClientEntry);
433         nickname = va_arg(vp, char *);
434         username = va_arg(vp, char *);
435         realname = va_arg(vp, char *);
436         channels = va_arg(vp, SilcBuffer);
437         mode = va_arg(vp, unsigned int);
438         idle = va_arg(vp, unsigned int);
439
440         memset(buf, 0, sizeof(buf));
441
442         if (nickname) {
443           len = strlen(nickname);
444           strncat(buf, nickname, len);
445           strncat(buf, " is ", 4);
446         }
447         
448         if (username) {
449           strncat(buf, username, strlen(username));
450         }
451         
452         if (realname) {
453           strncat(buf, " (", 2);
454           strncat(buf, realname, strlen(realname));
455           strncat(buf, ")", 1);
456         }
457
458         client->ops->say(client, conn, "%s", buf);
459
460         if (channels) {
461           SilcDList list = silc_channel_payload_parse_list(channels);
462           if (list) {
463             SilcChannelPayload entry;
464
465             memset(buf, 0, sizeof(buf));
466             strcat(buf, "on channels: ");
467
468             silc_dlist_start(list);
469             while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
470               char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
471               unsigned int name_len;
472               char *name = silc_channel_get_name(entry, &name_len);
473
474               if (m)
475                 strncat(buf, m, strlen(m));
476               strncat(buf, name, name_len);
477               strncat(buf, " ", 1);
478               silc_free(m);
479             }
480
481             client->ops->say(client, conn, "%s", buf);
482             silc_channel_payload_list_free(list);
483           }
484         }
485
486         if (mode)
487           client->ops->say(client, conn, "%s is %s", nickname,
488                            (mode & SILC_UMODE_SERVER_OPERATOR) ?
489                            "Server Operator" :
490                            (mode & SILC_UMODE_ROUTER_OPERATOR) ?
491                            "SILC Operator" : "[Unknown mode]");
492
493         if (idle && nickname)
494           client->ops->say(client, conn, "%s has been idle %d %s",
495                            nickname,
496                            idle > 60 ? (idle / 60) : idle,
497                            idle > 60 ? "minutes" : "seconds");
498       }
499       break;
500
501     case SILC_COMMAND_WHOWAS:
502       {
503         char buf[1024], *nickname, *username, *realname;
504         int len;
505
506         if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
507           char *tmp;
508           tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
509                                            3, NULL);
510           if (tmp)
511             client->ops->say(client, conn, "%s: %s", tmp,
512                              silc_client_command_status_message(status));
513           else
514             client->ops->say(client, conn, "%s",
515                              silc_client_command_status_message(status));
516           break;
517         }
518
519         if (!success)
520           return;
521
522         (void)va_arg(vp, SilcClientEntry);
523         nickname = va_arg(vp, char *);
524         username = va_arg(vp, char *);
525         realname = va_arg(vp, char *);
526
527         memset(buf, 0, sizeof(buf));
528
529         if (nickname) {
530           len = strlen(nickname);
531           strncat(buf, nickname, len);
532           strncat(buf, " was ", 5);
533         }
534         
535         if (username) {
536           strncat(buf, username, strlen(nickname));
537         }
538         
539         if (realname) {
540           strncat(buf, " (", 2);
541           strncat(buf, realname, strlen(realname));
542           strncat(buf, ")", 1);
543         }
544
545         client->ops->say(client, conn, "%s", buf);
546       }
547       break;
548
549     case SILC_COMMAND_JOIN:
550       {
551         unsigned int mode;
552         char *topic;
553         SilcBuffer client_id_list;
554         unsigned int list_count;
555         SilcChannelEntry channel;
556
557         if (!success)
558           return;
559
560         app->screen->bottom_line->channel = va_arg(vp, char *);
561         channel = va_arg(vp, SilcChannelEntry);
562         mode = va_arg(vp, unsigned int);
563         (void)va_arg(vp, unsigned int);
564         (void)va_arg(vp, unsigned char *);
565         (void)va_arg(vp, unsigned char *);
566         (void)va_arg(vp, unsigned char *);
567         topic = va_arg(vp, char *);
568         (void)va_arg(vp, unsigned char *);
569         list_count = va_arg(vp, unsigned int);
570         client_id_list = va_arg(vp, SilcBuffer);
571
572         if (topic)
573           client->ops->say(client, conn, "Topic for %s: %s", 
574                            app->screen->bottom_line->channel, topic);
575         
576         app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
577         silc_screen_print_bottom_line(app->screen, 0);
578
579         /* Resolve the client information */
580         silc_client_get_clients_by_list(client, conn, list_count,
581                                         client_id_list,
582                                         silc_client_show_users, channel);
583       }
584       break;
585
586     case SILC_COMMAND_NICK:
587       {
588         SilcClientEntry entry;
589
590         if (!success)
591           return;
592
593         entry = va_arg(vp, SilcClientEntry);
594         silc_say(client, conn, "Your current nickname is %s", entry->nickname);
595         app->screen->bottom_line->nickname = entry->nickname;
596         silc_screen_print_bottom_line(app->screen, 0);
597       }
598       break;
599
600     case SILC_COMMAND_UMODE:
601       {
602         unsigned int mode;
603
604         if (!success)
605           return;
606
607         mode = va_arg(vp, unsigned int);
608
609         if (!mode && app->screen->bottom_line->umode) {
610           silc_free(app->screen->bottom_line->umode);
611           app->screen->bottom_line->umode = NULL;
612         }
613
614         if (mode & SILC_UMODE_SERVER_OPERATOR) {
615           if (app->screen->bottom_line->umode)
616             silc_free(app->screen->bottom_line->umode);
617           app->screen->bottom_line->umode = strdup("Server Operator");;
618         }
619
620         if (mode & SILC_UMODE_ROUTER_OPERATOR) {
621           if (app->screen->bottom_line->umode)
622             silc_free(app->screen->bottom_line->umode);
623           app->screen->bottom_line->umode = strdup("SILC Operator");;
624         }
625
626         silc_screen_print_bottom_line(app->screen, 0);
627       }
628       break;
629
630     case SILC_COMMAND_OPER:
631       if (status == SILC_STATUS_OK) {
632         conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
633         if (app->screen->bottom_line->umode)
634           silc_free(app->screen->bottom_line->umode);
635         app->screen->bottom_line->umode = strdup("Server Operator");;
636         silc_screen_print_bottom_line(app->screen, 0);
637       }
638       break;
639
640     case SILC_COMMAND_SILCOPER:
641       if (status == SILC_STATUS_OK) {
642         conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
643         if (app->screen->bottom_line->umode)
644           silc_free(app->screen->bottom_line->umode);
645         app->screen->bottom_line->umode = strdup("SILC Operator");;
646         silc_screen_print_bottom_line(app->screen, 0);
647       }
648       break;
649
650     case SILC_COMMAND_USERS:
651       if (!success)
652         return;
653
654       silc_list_start(conn->current_channel->clients);
655       while ((chu = silc_list_get(conn->current_channel->clients)) 
656              != SILC_LIST_END) {
657         if (chu->client == conn->local_entry) {
658           if (app->screen->bottom_line->mode)
659             silc_free(app->screen->bottom_line->mode);
660           app->screen->bottom_line->mode = silc_client_chumode_char(chu->mode);
661           silc_screen_print_bottom_line(app->screen, 0);
662           break;
663         }
664       break;
665       }
666     }
667 }
668
669 /* Called to indicate that connection was either successfully established
670    or connecting failed.  This is also the first time application receives
671    the SilcClientConnection objecet which it should save somewhere. */
672
673 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
674 {
675   SilcClientInternal app = (SilcClientInternal)client->application;
676
677   if (success) {
678     app->screen->bottom_line->connection = conn->remote_host;
679     silc_screen_print_bottom_line(app->screen, 0);
680     app->conn = conn;
681   }
682 }
683
684 /* Called to indicate that connection was disconnected to the server. */
685
686 void silc_disconnect(SilcClient client, SilcClientConnection conn)
687 {
688   SilcClientInternal app = (SilcClientInternal)client->application;
689
690   app->screen->bottom_line->connection = NULL;
691   silc_screen_print_bottom_line(app->screen, 0);
692   app->conn = NULL;
693 }
694
695 /* Asks passphrase from user on the input line. */
696
697 unsigned char *silc_ask_passphrase(SilcClient client, 
698                                    SilcClientConnection conn)
699 {
700   SilcClientInternal app = (SilcClientInternal)conn->client->application;
701   char pass1[256], pass2[256];
702   char *ret;
703   int try = 3;
704
705   while(try) {
706
707     /* Print prompt */
708     wattroff(app->screen->input_win, A_INVIS);
709     silc_screen_input_print_prompt(app->screen, "Passphrase: ");
710     wattron(app->screen->input_win, A_INVIS);
711     
712     /* Get string */
713     memset(pass1, 0, sizeof(pass1));
714     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
715     
716     /* Print retype prompt */
717     wattroff(app->screen->input_win, A_INVIS);
718     silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
719     wattron(app->screen->input_win, A_INVIS);
720     
721     /* Get string */
722     memset(pass2, 0, sizeof(pass2));
723     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
724
725     if (!strncmp(pass1, pass2, strlen(pass2)))
726       break;
727
728     try--;
729   }
730
731   ret = silc_calloc(strlen(pass1), sizeof(char));
732   memcpy(ret, pass1, strlen(pass1));
733
734   memset(pass1, 0, sizeof(pass1));
735   memset(pass2, 0, sizeof(pass2));
736
737   wattroff(app->screen->input_win, A_INVIS);
738   silc_screen_input_reset(app->screen);
739
740   return ret;
741 }
742
743 /* Verifies received public key. If user decides to trust the key it is
744    saved as trusted server key for later use. If user does not trust the
745    key this returns FALSE. */
746
747 int silc_verify_server_key(SilcClient client,
748                            SilcClientConnection conn, 
749                            unsigned char *pk, unsigned int pk_len,
750                            SilcSKEPKType pk_type)
751 {
752   SilcSocketConnection sock = conn->sock;
753   char filename[256];
754   char file[256];
755   char *hostname, *fingerprint;
756   struct passwd *pw;
757   struct stat st;
758
759   hostname = sock->hostname ? sock->hostname : sock->ip;
760
761   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
762     silc_say(client, conn, "We don't support server %s key type", hostname);
763     return FALSE;
764   }
765
766   pw = getpwuid(getuid());
767   if (!pw)
768     return FALSE;
769
770   memset(filename, 0, sizeof(filename));
771   memset(file, 0, sizeof(file));
772   snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
773            sock->port);
774   snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
775            pw->pw_dir, file);
776
777   /* Check wheter this key already exists */
778   if (stat(filename, &st) < 0) {
779
780     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
781     silc_say(client, conn, "Received server %s public key", hostname);
782     silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
783     silc_say(client, conn, "%s", fingerprint);
784     silc_free(fingerprint);
785
786     /* Ask user to verify the key and save it */
787     if (silc_client_ask_yes_no(client, 
788        "Would you like to accept the key (y/n)? "))
789       {
790         /* Save the key for future checking */
791         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
792                                        SILC_PKCS_FILE_PEM);
793         return TRUE;
794       }
795   } else {
796     /* The key already exists, verify it. */
797     SilcPublicKey public_key;
798     unsigned char *encpk;
799     unsigned int encpk_len;
800
801     /* Load the key file */
802     if (!silc_pkcs_load_public_key(filename, &public_key, 
803                                    SILC_PKCS_FILE_PEM))
804       if (!silc_pkcs_load_public_key(filename, &public_key, 
805                                      SILC_PKCS_FILE_BIN)) {
806         fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
807         silc_say(client, conn, "Received server %s public key", hostname);
808         silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
809         silc_say(client, conn, "%s", fingerprint);
810         silc_free(fingerprint);
811         silc_say(client, conn, "Could not load your local copy of the server %s key",
812                  hostname);
813         if (silc_client_ask_yes_no(client, 
814            "Would you like to accept the key anyway (y/n)? "))
815           {
816             /* Save the key for future checking */
817             unlink(filename);
818             silc_pkcs_save_public_key_data(filename, pk, pk_len,
819                                            SILC_PKCS_FILE_PEM);
820             return TRUE;
821           }
822         
823         return FALSE;
824       }
825   
826     /* Encode the key data */
827     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
828     if (!encpk) {
829       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
830       silc_say(client, conn, "Received server %s public key", hostname);
831       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
832       silc_say(client, conn, "%s", fingerprint);
833       silc_free(fingerprint);
834       silc_say(client, conn, "Your local copy of the server %s key is malformed",
835                hostname);
836       if (silc_client_ask_yes_no(client, 
837          "Would you like to accept the key anyway (y/n)? "))
838         {
839           /* Save the key for future checking */
840           unlink(filename);
841           silc_pkcs_save_public_key_data(filename, pk, pk_len,
842                                          SILC_PKCS_FILE_PEM);
843           return TRUE;
844         }
845
846       return FALSE;
847     }
848
849     if (memcmp(encpk, pk, encpk_len)) {
850       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
851       silc_say(client, conn, "Received server %s public key", hostname);
852       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
853       silc_say(client, conn, "%s", fingerprint);
854       silc_free(fingerprint);
855       silc_say(client, conn, "Server %s key does not match with your local copy",
856                hostname);
857       silc_say(client, conn, "It is possible that the key has expired or changed");
858       silc_say(client, conn, "It is also possible that some one is performing "
859                        "man-in-the-middle attack");
860       
861       /* Ask user to verify the key and save it */
862       if (silc_client_ask_yes_no(client, 
863          "Would you like to accept the key anyway (y/n)? "))
864         {
865           /* Save the key for future checking */
866           unlink(filename);
867           silc_pkcs_save_public_key_data(filename, pk, pk_len,
868                                          SILC_PKCS_FILE_PEM);
869           return TRUE;
870         }
871
872       silc_say(client, conn, "Will not accept server %s key", hostname);
873       return FALSE;
874     }
875
876     /* Local copy matched */
877     return TRUE;
878   }
879
880   silc_say(client, conn, "Will not accept server %s key", hostname);
881   return FALSE;
882 }
883
884 /* Find authentication method and authentication data by hostname and
885    port. The hostname may be IP address as well. The found authentication
886    method and authentication data is returned to `auth_meth', `auth_data'
887    and `auth_data_len'. The function returns TRUE if authentication method
888    is found and FALSE if not. `conn' may be NULL. */
889
890 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
891                          char *hostname, unsigned short port,
892                          SilcProtocolAuthMeth *auth_meth,
893                          unsigned char **auth_data,
894                          unsigned int *auth_data_len)
895 {
896   SilcClientInternal app = (SilcClientInternal)client->application;
897
898   if (app->config->conns) {
899     SilcClientConfigSectionConnection *conn = NULL;
900
901     /* Check if we find a match from user configured connections */
902     conn = silc_client_config_find_connection(app->config,
903                                               hostname,
904                                               port);
905     if (conn) {
906       /* Match found. Use the configured authentication method */
907       *auth_meth = conn->auth_meth;
908
909       if (conn->auth_data) {
910         *auth_data = strdup(conn->auth_data);
911         *auth_data_len = strlen(conn->auth_data);
912       }
913
914       return TRUE;
915     }
916   }
917
918   return FALSE;
919 }
920
921 /* Notifies application that failure packet was received.  This is called
922    if there is some protocol active in the client.  The `protocol' is the
923    protocol context.  The `failure' is opaque pointer to the failure
924    indication.  Note, that the `failure' is protocol dependant and application
925    must explicitly cast it to correct type.  Usually `failure' is 32 bit
926    failure type (see protocol specs for all protocol failure types). */
927
928 void silc_failure(SilcClient client, SilcClientConnection conn, 
929                   SilcProtocol protocol, void *failure)
930 {
931
932 }
933
934 /* Asks whether the user would like to perform the key agreement protocol.
935    This is called after we have received an key agreement packet or an
936    reply to our key agreement packet. This returns TRUE if the user wants
937    the library to perform the key agreement protocol and FALSE if it is not
938    desired (application may start it later by calling the function
939    silc_client_perform_key_agreement). */
940
941 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
942                        SilcClientEntry client_entry, char *hostname,
943                        int port,
944                        SilcKeyAgreementCallback *completion,
945                        void **context)
946 {
947
948   return FALSE;
949 }
950
951 /* SILC client operations */
952 SilcClientOperations ops = {
953   say:                  silc_say,
954   channel_message:      silc_channel_message,
955   private_message:      silc_private_message,
956   notify:               silc_notify,
957   command:              silc_command,
958   command_reply:        silc_command_reply,
959   connect:              silc_connect,
960   disconnect:           silc_disconnect,
961   get_auth_method:      silc_get_auth_method,
962   verify_server_key:    silc_verify_server_key,
963   ask_passphrase:       silc_ask_passphrase,
964   failure:              silc_failure,
965   key_agreement:        silc_key_agreement,
966 };