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