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