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