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