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