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