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