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