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