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       {
768         SilcChannelEntry channel;
769         int line_len;
770         char *line;
771         
772         if (!success)
773           return;
774
775         channel = va_arg(vp, SilcChannelEntry);
776
777         /* There are two ways to do this, either parse the list (that
778            the command_reply sends (just take it with va_arg())) or just
779            traverse the channel'c client list.  I'll do the latter.  See
780            JOIN command reply for example for the list. */
781
782         silc_say(client, conn, "Users on %s", channel->channel_name);
783         
784         line = silc_calloc(1024, sizeof(*line));
785         line_len = 1024;
786         silc_list_start(channel->clients);
787         while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
788           SilcClientEntry e = chu->client;
789           int i, len1;
790           char *m, tmp[80];
791
792           memset(line, 0, line_len);
793
794           if (chu->client == conn->local_entry) {
795             /* Update status line */
796             if (app->screen->bottom_line->mode)
797               silc_free(app->screen->bottom_line->mode);
798             app->screen->bottom_line->mode = 
799               silc_client_chumode_char(chu->mode);
800             silc_screen_print_bottom_line(app->screen, 0);
801           }
802
803           if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
804             silc_free(line);
805             line_len += strlen(e->nickname) + strlen(e->server) + 100;
806             line = silc_calloc(line_len, sizeof(*line));
807           }
808
809           memset(tmp, 0, sizeof(tmp));
810           m = silc_client_chumode_char(chu->mode);
811
812           strncat(line, " ", 1);
813           strncat(line, e->nickname, strlen(e->nickname));
814           strncat(line, e->server ? "@" : "", 1);
815           
816           len1 = 0;
817           if (e->server)
818             len1 = strlen(e->server);
819           strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
820           
821           len1 = strlen(line);
822           if (len1 >= 30) {
823             memset(&line[29], 0, len1 - 29);
824           } else {
825             for (i = 0; i < 30 - len1 - 1; i++)
826               strcat(line, " ");
827           }
828           
829           if (e->mode & SILC_UMODE_GONE)
830             strcat(line, "  G");
831           else
832             strcat(line, "  H");
833           strcat(tmp, m ? m : "");
834           strncat(line, tmp, strlen(tmp));
835           
836           if (strlen(tmp) < 5)
837             for (i = 0; i < 5 - strlen(tmp); i++)
838               strcat(line, " ");
839           
840           strcat(line, e->username ? e->username : "");
841           
842           silc_say(client, conn, "%s", line);
843           
844           if (m)
845             silc_free(m);
846         }
847         
848         silc_free(line);
849       }
850       break;
851
852     case SILC_COMMAND_BAN:
853       {
854         SilcChannelEntry channel;
855         char *ban_list;
856
857         if (!success)
858           return;
859         
860         channel = va_arg(vp, SilcChannelEntry);
861         ban_list = va_arg(vp, char *);
862
863         if (ban_list)
864           silc_say(client, conn, "%s ban list: %s", channel->channel_name,
865                    ban_list);
866         else
867           silc_say(client, conn, "%s ban list not set", channel->channel_name);
868       }
869       break;
870
871     default:
872       break;
873     }
874 }
875
876 /* Called to indicate that connection was either successfully established
877    or connecting failed.  This is also the first time application receives
878    the SilcClientConnection objecet which it should save somewhere. */
879
880 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
881 {
882   SilcClientInternal app = (SilcClientInternal)client->application;
883
884   if (success) {
885     app->screen->bottom_line->connection = conn->remote_host;
886     silc_screen_print_bottom_line(app->screen, 0);
887     app->conn = conn;
888   }
889 }
890
891 /* Called to indicate that connection was disconnected to the server. */
892
893 void silc_disconnect(SilcClient client, SilcClientConnection conn)
894 {
895   SilcClientInternal app = (SilcClientInternal)client->application;
896
897   app->screen->bottom_line->connection = NULL;
898   silc_screen_print_bottom_line(app->screen, 0);
899   app->conn = NULL;
900 }
901
902 /* Asks passphrase from user on the input line. */
903
904 unsigned char *silc_ask_passphrase(SilcClient client, 
905                                    SilcClientConnection conn)
906 {
907   SilcClientInternal app = (SilcClientInternal)conn->client->application;
908   char pass1[256], pass2[256];
909   char *ret;
910   int try = 3;
911
912   while(try) {
913
914     /* Print prompt */
915     wattroff(app->screen->input_win, A_INVIS);
916     silc_screen_input_print_prompt(app->screen, "Passphrase: ");
917     wattron(app->screen->input_win, A_INVIS);
918     
919     /* Get string */
920     memset(pass1, 0, sizeof(pass1));
921     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
922     
923     /* Print retype prompt */
924     wattroff(app->screen->input_win, A_INVIS);
925     silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
926     wattron(app->screen->input_win, A_INVIS);
927     
928     /* Get string */
929     memset(pass2, 0, sizeof(pass2));
930     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
931
932     if (!strncmp(pass1, pass2, strlen(pass2)))
933       break;
934
935     try--;
936   }
937
938   ret = silc_calloc(strlen(pass1), sizeof(char));
939   memcpy(ret, pass1, strlen(pass1));
940
941   memset(pass1, 0, sizeof(pass1));
942   memset(pass2, 0, sizeof(pass2));
943
944   wattroff(app->screen->input_win, A_INVIS);
945   silc_screen_input_reset(app->screen);
946
947   return ret;
948 }
949
950 /* Verifies received public key. If user decides to trust the key it is
951    saved as public server key for later use. If user does not trust the
952    key this returns FALSE. */
953
954 int silc_verify_public_key(SilcClient client,
955                            SilcClientConnection conn, 
956                            SilcSocketType conn_type,
957                            unsigned char *pk, unsigned int pk_len,
958                            SilcSKEPKType pk_type)
959 {
960   SilcSocketConnection sock = conn->sock;
961   char filename[256];
962   char file[256];
963   char *hostname, *fingerprint;
964   struct passwd *pw;
965   struct stat st;
966   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
967                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
968                   "server" : "client");
969
970   hostname = sock->hostname ? sock->hostname : sock->ip;
971
972   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
973     silc_say(client, conn, "We don't support %s %s key type", 
974              entity, hostname);
975     return FALSE;
976   }
977
978   pw = getpwuid(getuid());
979   if (!pw)
980     return FALSE;
981
982   memset(filename, 0, sizeof(filename));
983   memset(file, 0, sizeof(file));
984   snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, hostname,
985            sock->port);
986   snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
987            pw->pw_dir, entity, file);
988
989   /* Check wheter this key already exists */
990   if (stat(filename, &st) < 0) {
991
992     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
993     silc_say(client, conn, "Received %s %s public key", entity, hostname);
994     silc_say(client, conn, "Fingerprint for the %s %s key is", entity, 
995              hostname);
996     silc_say(client, conn, "%s", fingerprint);
997     silc_free(fingerprint);
998
999     /* Ask user to verify the key and save it */
1000     if (silc_client_ask_yes_no(client, 
1001        "Would you like to accept the key (y/n)? "))
1002       {
1003         /* Save the key for future checking */
1004         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
1005                                        SILC_PKCS_FILE_PEM);
1006         return TRUE;
1007       }
1008   } else {
1009     /* The key already exists, verify it. */
1010     SilcPublicKey public_key;
1011     unsigned char *encpk;
1012     unsigned int encpk_len;
1013
1014     /* Load the key file */
1015     if (!silc_pkcs_load_public_key(filename, &public_key, 
1016                                    SILC_PKCS_FILE_PEM))
1017       if (!silc_pkcs_load_public_key(filename, &public_key, 
1018                                      SILC_PKCS_FILE_BIN)) {
1019         fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1020         silc_say(client, conn, "Received %s %s public key", entity, hostname);
1021         silc_say(client, conn, "Fingerprint for the %s %s key is", 
1022                  entity, hostname);
1023         silc_say(client, conn, "%s", fingerprint);
1024         silc_free(fingerprint);
1025         silc_say(client, conn, "Could not load your local copy of the %s %s key",
1026                  entity, hostname);
1027         if (silc_client_ask_yes_no(client, 
1028            "Would you like to accept the key anyway (y/n)? "))
1029           {
1030             /* Save the key for future checking */
1031             unlink(filename);
1032             silc_pkcs_save_public_key_data(filename, pk, pk_len,
1033                                            SILC_PKCS_FILE_PEM);
1034             return TRUE;
1035           }
1036         
1037         return FALSE;
1038       }
1039   
1040     /* Encode the key data */
1041     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1042     if (!encpk) {
1043       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1044       silc_say(client, conn, "Received %s %s public key", entity, hostname);
1045       silc_say(client, conn, "Fingerprint for the %s %s key is", 
1046                entity, hostname);
1047       silc_say(client, conn, "%s", fingerprint);
1048       silc_free(fingerprint);
1049       silc_say(client, conn, "Your local copy of the %s %s key is malformed",
1050                entity, hostname);
1051       if (silc_client_ask_yes_no(client, 
1052          "Would you like to accept the key anyway (y/n)? "))
1053         {
1054           /* Save the key for future checking */
1055           unlink(filename);
1056           silc_pkcs_save_public_key_data(filename, pk, pk_len,
1057                                          SILC_PKCS_FILE_PEM);
1058           return TRUE;
1059         }
1060
1061       return FALSE;
1062     }
1063
1064     if (memcmp(encpk, pk, encpk_len)) {
1065       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1066       silc_say(client, conn, "Received %s %s public key", entity, hostname);
1067       silc_say(client, conn, "Fingerprint for the %s %s key is", 
1068                entity, hostname);
1069       silc_say(client, conn, "%s", fingerprint);
1070       silc_free(fingerprint);
1071       silc_say(client, conn, "%s %s key does not match with your local copy",
1072                entity, hostname);
1073       silc_say(client, conn, "It is possible that the key has expired or changed");
1074       silc_say(client, conn, "It is also possible that some one is performing "
1075                        "man-in-the-middle attack");
1076       
1077       /* Ask user to verify the key and save it */
1078       if (silc_client_ask_yes_no(client, 
1079          "Would you like to accept the key anyway (y/n)? "))
1080         {
1081           /* Save the key for future checking */
1082           unlink(filename);
1083           silc_pkcs_save_public_key_data(filename, pk, pk_len,
1084                                          SILC_PKCS_FILE_PEM);
1085           return TRUE;
1086         }
1087
1088       silc_say(client, conn, "Will not accept %s %s key", entity, hostname);
1089       return FALSE;
1090     }
1091
1092     /* Local copy matched */
1093     return TRUE;
1094   }
1095
1096   silc_say(client, conn, "Will not accept %s %s key", entity, hostname);
1097   return FALSE;
1098 }
1099
1100 /* Find authentication method and authentication data by hostname and
1101    port. The hostname may be IP address as well. The found authentication
1102    method and authentication data is returned to `auth_meth', `auth_data'
1103    and `auth_data_len'. The function returns TRUE if authentication method
1104    is found and FALSE if not. `conn' may be NULL. */
1105
1106 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1107                          char *hostname, unsigned short port,
1108                          SilcProtocolAuthMeth *auth_meth,
1109                          unsigned char **auth_data,
1110                          unsigned int *auth_data_len)
1111 {
1112   SilcClientInternal app = (SilcClientInternal)client->application;
1113
1114   if (app->config->conns) {
1115     SilcClientConfigSectionConnection *conn = NULL;
1116
1117     /* Check if we find a match from user configured connections */
1118     conn = silc_client_config_find_connection(app->config,
1119                                               hostname,
1120                                               port);
1121     if (conn) {
1122       /* Match found. Use the configured authentication method */
1123       *auth_meth = conn->auth_meth;
1124
1125       if (conn->auth_data) {
1126         *auth_data = strdup(conn->auth_data);
1127         *auth_data_len = strlen(conn->auth_data);
1128       }
1129
1130       return TRUE;
1131     }
1132   }
1133
1134   *auth_meth = SILC_AUTH_NONE;
1135   *auth_data = NULL;
1136   *auth_data_len = 0;
1137
1138   return TRUE;
1139 }
1140
1141 /* Notifies application that failure packet was received.  This is called
1142    if there is some protocol active in the client.  The `protocol' is the
1143    protocol context.  The `failure' is opaque pointer to the failure
1144    indication.  Note, that the `failure' is protocol dependant and application
1145    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1146    failure type (see protocol specs for all protocol failure types). */
1147
1148 void silc_failure(SilcClient client, SilcClientConnection conn, 
1149                   SilcProtocol protocol, void *failure)
1150 {
1151
1152 }
1153
1154 /* Asks whether the user would like to perform the key agreement protocol.
1155    This is called after we have received an key agreement packet or an
1156    reply to our key agreement packet. This returns TRUE if the user wants
1157    the library to perform the key agreement protocol and FALSE if it is not
1158    desired (application may start it later by calling the function
1159    silc_client_perform_key_agreement). */
1160
1161 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1162                        SilcClientEntry client_entry, char *hostname,
1163                        int port,
1164                        SilcKeyAgreementCallback *completion,
1165                        void **context)
1166 {
1167   char host[256];
1168
1169   /* We will just display the info on the screen and return FALSE and user
1170      will have to start the key agreement with a command. */
1171
1172   if (hostname) {
1173     memset(host, 0, sizeof(host));
1174     snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port); 
1175   }
1176
1177   silc_say(client, conn, "%s wants to perform key agreement %s",
1178            client_entry->nickname, hostname ? host : "");
1179
1180   *completion = NULL;
1181   *context = NULL;
1182
1183   return FALSE;
1184 }
1185
1186 /* SILC client operations */
1187 SilcClientOperations ops = {
1188   say:                  silc_say,
1189   channel_message:      silc_channel_message,
1190   private_message:      silc_private_message,
1191   notify:               silc_notify,
1192   command:              silc_command,
1193   command_reply:        silc_command_reply,
1194   connect:              silc_connect,
1195   disconnect:           silc_disconnect,
1196   get_auth_method:      silc_get_auth_method,
1197   verify_public_key:    silc_verify_public_key,
1198   ask_passphrase:       silc_ask_passphrase,
1199   failure:              silc_failure,
1200   key_agreement:        silc_key_agreement,
1201 };