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