62849ce157b41ebd90cb87ecd378759ac01506b9
[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   uint32 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, uint32);
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, uint32);
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   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
325     {
326       SilcClientEntry *clients;
327       uint32 clients_count;
328       int i;
329
330       (void)va_arg(vp, void *);
331       clients = va_arg(vp, SilcClientEntry *);
332       clients_count = va_arg(vp, uint32);
333
334       for (i = 0; i < clients_count; i++) {
335         if (clients[i]->server)
336           snprintf(message, sizeof(message), "Server signoff: %s@%s %s%s%s", 
337                    clients[i]->nickname, clients[i]->server,
338                    tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
339         else
340           snprintf(message, sizeof(message), "Server signoff: %s %s%s%s", 
341                    clients[i]->nickname,
342                    tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
343         silc_print(client, "*** %s", message);
344         memset(message, 0, sizeof(message));
345       }
346       return;
347     }
348
349   default:
350     break;
351   }
352
353   silc_print(client, "*** %s", message);
354 }
355
356 /* Command handler. This function is called always in the command function.
357    If error occurs it will be called as well. `conn' is the associated
358    client connection. `cmd_context' is the command context that was
359    originally sent to the command. `success' is FALSE if error occured
360    during command. `command' is the command being processed. It must be
361    noted that this is not reply from server. This is merely called just
362    after application has called the command. Just to tell application
363    that the command really was processed. */
364
365 void silc_command(SilcClient client, SilcClientConnection conn, 
366                   SilcClientCommandContext cmd_context, int success,
367                   SilcCommand command)
368 {
369   SilcClientInternal app = (SilcClientInternal)client->application;
370
371   if (!success)
372     return;
373
374   switch(command)
375     {
376         
377     case SILC_COMMAND_QUIT:
378       app->screen->bottom_line->channel = NULL;
379       silc_screen_print_bottom_line(app->screen, 0);
380       break;
381
382     case SILC_COMMAND_LEAVE:
383       /* We won't talk anymore on this channel */
384       silc_say(client, conn, "You have left channel %s", 
385                conn->current_channel->channel_name);
386       break;
387
388     }
389 }
390
391 /* We've resolved all clients we don't know about, now just print the
392    users from the channel on the screen. */
393
394 void silc_client_show_users(SilcClient client,
395                             SilcClientConnection conn,
396                             SilcClientEntry *clients,
397                             uint32 clients_count,
398                             void *context)
399 {
400   SilcChannelEntry channel = (SilcChannelEntry)context;
401   SilcChannelUser chu;
402   int k = 0, len1 = 0, len2 = 0;
403   char *name_list = NULL;
404
405   if (!clients)
406     return;
407
408   silc_list_start(channel->clients);
409   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
410     char *m, *n = chu->client->nickname;
411     if (!n)
412       continue;
413
414     len2 = strlen(n);
415     len1 += len2;
416     
417     name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
418     
419     m = silc_client_chumode_char(chu->mode);
420     if (m) {
421       memcpy(name_list + (len1 - len2), m, strlen(m));
422       len1 += strlen(m);
423       silc_free(m);
424     }
425     
426     memcpy(name_list + (len1 - len2), n, len2);
427     name_list[len1] = 0;
428     
429     if (k == silc_list_count(channel->clients) - 1)
430       break;
431     memcpy(name_list + len1, " ", 1);
432     len1++;
433     k++;
434   }
435
436   client->ops->say(client, conn, "Users on %s: %s", channel->channel_name, 
437                    name_list);
438   silc_free(name_list);
439 }
440
441 /* Command reply handler. This function is called always in the command reply
442    function. If error occurs it will be called as well. Normal scenario
443    is that it will be called after the received command data has been parsed
444    and processed. The function is used to pass the received command data to
445    the application. 
446
447    `conn' is the associated client connection. `cmd_payload' is the command
448    payload data received from server and it can be ignored. It is provided
449    if the application would like to re-parse the received command data,
450    however, it must be noted that the data is parsed already by the library
451    thus the payload can be ignored. `success' is FALSE if error occured.
452    In this case arguments are not sent to the application. `command' is the
453    command reply being processed. The function has variable argument list
454    and each command defines the number and type of arguments it passes to the
455    application (on error they are not sent). */
456
457 void silc_command_reply(SilcClient client, SilcClientConnection conn,
458                         SilcCommandPayload cmd_payload, int success,
459                         SilcCommand command, SilcCommandStatus status, ...)
460 {
461   SilcClientInternal app = (SilcClientInternal)client->application;
462   SilcChannelUser chu;
463   va_list vp;
464
465   va_start(vp, status);
466
467   switch(command)
468     {
469     case SILC_COMMAND_WHOIS:
470       {
471         char buf[1024], *nickname, *username, *realname;
472         int len;
473         uint32 idle, mode;
474         SilcBuffer channels;
475
476         if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
477             status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
478           char *tmp;
479           tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
480                                            3, NULL);
481           if (tmp)
482             client->ops->say(client, conn, "%s: %s", tmp,
483                              silc_client_command_status_message(status));
484           else
485             client->ops->say(client, conn, "%s",
486                              silc_client_command_status_message(status));
487           break;
488         }
489
490         if (!success)
491           return;
492
493         (void)va_arg(vp, SilcClientEntry);
494         nickname = va_arg(vp, char *);
495         username = va_arg(vp, char *);
496         realname = va_arg(vp, char *);
497         channels = va_arg(vp, SilcBuffer);
498         mode = va_arg(vp, uint32);
499         idle = va_arg(vp, uint32);
500
501         memset(buf, 0, sizeof(buf));
502
503         if (nickname) {
504           len = strlen(nickname);
505           strncat(buf, nickname, len);
506           strncat(buf, " is ", 4);
507         }
508         
509         if (username) {
510           strncat(buf, username, strlen(username));
511         }
512         
513         if (realname) {
514           strncat(buf, " (", 2);
515           strncat(buf, realname, strlen(realname));
516           strncat(buf, ")", 1);
517         }
518
519         client->ops->say(client, conn, "%s", buf);
520
521         if (channels) {
522           SilcDList list = silc_channel_payload_parse_list(channels);
523           if (list) {
524             SilcChannelPayload entry;
525
526             memset(buf, 0, sizeof(buf));
527             strcat(buf, "on channels: ");
528
529             silc_dlist_start(list);
530             while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
531               char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
532               uint32 name_len;
533               char *name = silc_channel_get_name(entry, &name_len);
534
535               if (m)
536                 strncat(buf, m, strlen(m));
537               strncat(buf, name, name_len);
538               strncat(buf, " ", 1);
539               silc_free(m);
540             }
541
542             client->ops->say(client, conn, "%s", buf);
543             silc_channel_payload_list_free(list);
544           }
545         }
546
547         if (mode) {
548           if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
549               (mode & SILC_UMODE_ROUTER_OPERATOR))
550             client->ops->say(client, conn, "%s is %s", nickname,
551                              (mode & SILC_UMODE_SERVER_OPERATOR) ?
552                              "Server Operator" :
553                              (mode & SILC_UMODE_ROUTER_OPERATOR) ?
554                              "SILC Operator" : "[Unknown mode]");
555
556           if (mode & SILC_UMODE_GONE)
557             client->ops->say(client, conn, "%s is gone", nickname);
558         }
559
560         if (idle && nickname)
561           client->ops->say(client, conn, "%s has been idle %d %s",
562                            nickname,
563                            idle > 60 ? (idle / 60) : idle,
564                            idle > 60 ? "minutes" : "seconds");
565       }
566       break;
567
568     case SILC_COMMAND_WHOWAS:
569       {
570         char buf[1024], *nickname, *username, *realname;
571         int len;
572
573         if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
574             status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
575           char *tmp;
576           tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
577                                            3, NULL);
578           if (tmp)
579             client->ops->say(client, conn, "%s: %s", tmp,
580                              silc_client_command_status_message(status));
581           else
582             client->ops->say(client, conn, "%s",
583                              silc_client_command_status_message(status));
584           break;
585         }
586
587         if (!success)
588           return;
589
590         (void)va_arg(vp, SilcClientEntry);
591         nickname = va_arg(vp, char *);
592         username = va_arg(vp, char *);
593         realname = va_arg(vp, char *);
594
595         memset(buf, 0, sizeof(buf));
596
597         if (nickname) {
598           len = strlen(nickname);
599           strncat(buf, nickname, len);
600           strncat(buf, " was ", 5);
601         }
602         
603         if (username) {
604           strncat(buf, username, strlen(nickname));
605         }
606         
607         if (realname) {
608           strncat(buf, " (", 2);
609           strncat(buf, realname, strlen(realname));
610           strncat(buf, ")", 1);
611         }
612
613         client->ops->say(client, conn, "%s", buf);
614       }
615       break;
616
617     case SILC_COMMAND_INVITE:
618       {
619         SilcChannelEntry channel;
620         char *invite_list;
621
622         if (!success)
623           return;
624         
625         channel = va_arg(vp, SilcChannelEntry);
626         invite_list = va_arg(vp, char *);
627
628         if (invite_list)
629           silc_say(client, conn, "%s invite list: %s", channel->channel_name,
630                    invite_list);
631         else
632           silc_say(client, conn, "%s invite list not set", 
633                    channel->channel_name);
634       }
635       break;
636
637     case SILC_COMMAND_JOIN:
638       {
639         uint32 mode;
640         char *topic;
641         SilcBuffer client_id_list;
642         uint32 list_count;
643         SilcChannelEntry channel;
644
645         if (!success)
646           return;
647
648         app->screen->bottom_line->channel = va_arg(vp, char *);
649         channel = va_arg(vp, SilcChannelEntry);
650         mode = va_arg(vp, uint32);
651         (void)va_arg(vp, uint32);
652         (void)va_arg(vp, unsigned char *);
653         (void)va_arg(vp, unsigned char *);
654         (void)va_arg(vp, unsigned char *);
655         topic = va_arg(vp, char *);
656         (void)va_arg(vp, unsigned char *);
657         list_count = va_arg(vp, uint32);
658         client_id_list = va_arg(vp, SilcBuffer);
659
660         if (topic)
661           client->ops->say(client, conn, "Topic for %s: %s", 
662                            app->screen->bottom_line->channel, topic);
663         
664         app->screen->bottom_line->channel_mode = 
665           silc_client_chmode(mode, channel);
666         silc_screen_print_bottom_line(app->screen, 0);
667
668         /* Resolve the client information */
669         silc_client_get_clients_by_list(client, conn, list_count,
670                                         client_id_list,
671                                         silc_client_show_users, channel);
672       }
673       break;
674
675     case SILC_COMMAND_NICK:
676       {
677         SilcClientEntry entry;
678
679         if (!success)
680           return;
681
682         entry = va_arg(vp, SilcClientEntry);
683         silc_say(client, conn, "Your current nickname is %s", entry->nickname);
684         app->screen->bottom_line->nickname = entry->nickname;
685         silc_screen_print_bottom_line(app->screen, 0);
686       }
687       break;
688
689     case SILC_COMMAND_LIST:
690       {
691         char *topic, *name;
692         int usercount;
693         unsigned char buf[256], tmp[16];
694         int i, len;
695
696         if (!success)
697           return;
698
699         (void)va_arg(vp, SilcChannelEntry);
700         name = va_arg(vp, char *);
701         topic = va_arg(vp, char *);
702         usercount = va_arg(vp, int);
703
704         if (status == SILC_STATUS_LIST_START ||
705             status == SILC_STATUS_OK)
706           silc_say(client, conn, 
707           "  Channel                                  Users     Topic");
708
709         memset(buf, 0, sizeof(buf));
710         strncat(buf, "  ", 2);
711         len = strlen(name);
712         strncat(buf, name, len > 40 ? 40 : len);
713         if (len < 40)
714           for (i = 0; i < 40 - len; i++)
715             strcat(buf, " ");
716         strcat(buf, " ");
717
718         memset(tmp, 0, sizeof(tmp));
719         if (usercount) {
720           snprintf(tmp, sizeof(tmp), "%d", usercount);
721           strcat(buf, tmp);
722         }
723         len = strlen(tmp);
724         if (len < 10)
725           for (i = 0; i < 10 - len; i++)
726             strcat(buf, " ");
727         strcat(buf, " ");
728
729         if (topic) {
730           len = strlen(topic);
731           strncat(buf, topic, len);
732         }
733
734         silc_say(client, conn, "%s", buf);
735       }
736       break;
737
738     case SILC_COMMAND_UMODE:
739       {
740         uint32 mode;
741
742         if (!success)
743           return;
744
745         mode = va_arg(vp, uint32);
746
747         if (!mode && app->screen->bottom_line->umode) {
748           silc_free(app->screen->bottom_line->umode);
749           app->screen->bottom_line->umode = NULL;
750         }
751
752         if (mode & SILC_UMODE_SERVER_OPERATOR) {
753           if (app->screen->bottom_line->umode)
754             silc_free(app->screen->bottom_line->umode);
755           app->screen->bottom_line->umode = strdup("Server Operator");;
756         }
757
758         if (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         }
763
764         silc_screen_print_bottom_line(app->screen, 0);
765       }
766       break;
767
768     case SILC_COMMAND_OPER:
769       if (status == SILC_STATUS_OK) {
770         conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
771         if (app->screen->bottom_line->umode)
772           silc_free(app->screen->bottom_line->umode);
773         app->screen->bottom_line->umode = strdup("Server Operator");;
774         silc_screen_print_bottom_line(app->screen, 0);
775       }
776       break;
777
778     case SILC_COMMAND_SILCOPER:
779       if (status == SILC_STATUS_OK) {
780         conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
781         if (app->screen->bottom_line->umode)
782           silc_free(app->screen->bottom_line->umode);
783         app->screen->bottom_line->umode = strdup("SILC Operator");;
784         silc_screen_print_bottom_line(app->screen, 0);
785       }
786       break;
787
788     case SILC_COMMAND_USERS:
789       {
790         SilcChannelEntry channel;
791         int line_len;
792         char *line;
793         
794         if (!success)
795           return;
796
797         channel = va_arg(vp, SilcChannelEntry);
798
799         /* There are two ways to do this, either parse the list (that
800            the command_reply sends (just take it with va_arg()) or just
801            traverse the channel's client list.  I'll do the latter.  See
802            JOIN command reply for example for the list. */
803
804         silc_say(client, conn, "Users on %s", channel->channel_name);
805         
806         line = silc_calloc(1024, sizeof(*line));
807         line_len = 1024;
808         silc_list_start(channel->clients);
809         while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
810           SilcClientEntry e = chu->client;
811           int i, len1;
812           char *m, tmp[80];
813
814           memset(line, 0, line_len);
815
816           if (chu->client == conn->local_entry) {
817             /* Update status line */
818             if (app->screen->bottom_line->mode)
819               silc_free(app->screen->bottom_line->mode);
820             app->screen->bottom_line->mode = 
821               silc_client_chumode_char(chu->mode);
822             silc_screen_print_bottom_line(app->screen, 0);
823           }
824
825           if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
826             silc_free(line);
827             line_len += strlen(e->nickname) + strlen(e->server) + 100;
828             line = silc_calloc(line_len, sizeof(*line));
829           }
830
831           memset(tmp, 0, sizeof(tmp));
832           m = silc_client_chumode_char(chu->mode);
833
834           strncat(line, " ", 1);
835           strncat(line, e->nickname, strlen(e->nickname));
836           strncat(line, e->server ? "@" : "", 1);
837           
838           len1 = 0;
839           if (e->server)
840             len1 = strlen(e->server);
841           strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
842           
843           len1 = strlen(line);
844           if (len1 >= 30) {
845             memset(&line[29], 0, len1 - 29);
846           } else {
847             for (i = 0; i < 30 - len1 - 1; i++)
848               strcat(line, " ");
849           }
850           
851           if (e->mode & SILC_UMODE_GONE)
852             strcat(line, "  G");
853           else
854             strcat(line, "  H");
855           strcat(tmp, m ? m : "");
856           strncat(line, tmp, strlen(tmp));
857           
858           if (strlen(tmp) < 5)
859             for (i = 0; i < 5 - strlen(tmp); i++)
860               strcat(line, " ");
861           
862           strcat(line, e->username ? e->username : "");
863           
864           silc_say(client, conn, "%s", line);
865           
866           if (m)
867             silc_free(m);
868         }
869         
870         silc_free(line);
871       }
872       break;
873
874     case SILC_COMMAND_BAN:
875       {
876         SilcChannelEntry channel;
877         char *ban_list;
878
879         if (!success)
880           return;
881         
882         channel = va_arg(vp, SilcChannelEntry);
883         ban_list = va_arg(vp, char *);
884
885         if (ban_list)
886           silc_say(client, conn, "%s ban list: %s", channel->channel_name,
887                    ban_list);
888         else
889           silc_say(client, conn, "%s ban list not set", channel->channel_name);
890       }
891       break;
892
893     case SILC_COMMAND_GETKEY:
894       {
895         SilcIdType id_type;
896         void *entry;
897         SilcPublicKey public_key;
898         unsigned char *pk;
899         uint32 pk_len;
900
901         id_type = va_arg(vp, uint32);
902         entry = va_arg(vp, void *);
903         public_key = va_arg(vp, SilcPublicKey);
904
905         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
906
907         if (id_type == SILC_ID_CLIENT) {
908           silc_verify_public_key(client, conn, SILC_SOCKET_TYPE_CLIENT,
909                                  pk, pk_len, SILC_SKE_PK_TYPE_SILC);
910         }
911
912         silc_free(pk);
913       }
914
915     case SILC_COMMAND_TOPIC:
916       {
917         SilcChannelEntry channel;
918         char *topic;
919
920         if (!success)
921           return;
922         
923         channel = va_arg(vp, SilcChannelEntry);
924         topic = va_arg(vp, char *);
925
926         if (topic)
927           silc_say(client, conn, 
928                    "Topic on channel %s: %s", channel->channel_name,
929                    topic);
930       }
931       break;
932
933     default:
934       break;
935     }
936 }
937
938 /* Called to indicate that connection was either successfully established
939    or connecting failed.  This is also the first time application receives
940    the SilcClientConnection objecet which it should save somewhere. */
941
942 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
943 {
944   SilcClientInternal app = (SilcClientInternal)client->application;
945
946   if (success) {
947     app->screen->bottom_line->connection = conn->remote_host;
948     silc_screen_print_bottom_line(app->screen, 0);
949     app->conn = conn;
950   }
951 }
952
953 /* Called to indicate that connection was disconnected to the server. */
954
955 void silc_disconnect(SilcClient client, SilcClientConnection conn)
956 {
957   SilcClientInternal app = (SilcClientInternal)client->application;
958
959   app->screen->bottom_line->connection = NULL;
960   silc_screen_print_bottom_line(app->screen, 0);
961   app->conn = NULL;
962 }
963
964 /* Asks passphrase from user on the input line. */
965
966 unsigned char *silc_ask_passphrase(SilcClient client, 
967                                    SilcClientConnection conn)
968 {
969   SilcClientInternal app = (SilcClientInternal)conn->client->application;
970   char pass1[256], pass2[256];
971   char *ret;
972   int try = 3;
973
974   while(try) {
975
976     /* Print prompt */
977     wattroff(app->screen->input_win, A_INVIS);
978     silc_screen_input_print_prompt(app->screen, "Passphrase: ");
979     wattron(app->screen->input_win, A_INVIS);
980     
981     /* Get string */
982     memset(pass1, 0, sizeof(pass1));
983     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
984     
985     /* Print retype prompt */
986     wattroff(app->screen->input_win, A_INVIS);
987     silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
988     wattron(app->screen->input_win, A_INVIS);
989     
990     /* Get string */
991     memset(pass2, 0, sizeof(pass2));
992     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
993
994     if (!strncmp(pass1, pass2, strlen(pass2)))
995       break;
996
997     try--;
998   }
999
1000   ret = silc_calloc(strlen(pass1), sizeof(char));
1001   memcpy(ret, pass1, strlen(pass1));
1002
1003   memset(pass1, 0, sizeof(pass1));
1004   memset(pass2, 0, sizeof(pass2));
1005
1006   wattroff(app->screen->input_win, A_INVIS);
1007   silc_screen_input_reset(app->screen);
1008
1009   return ret;
1010 }
1011
1012 /* Verifies received public key. If user decides to trust the key it is
1013    saved as public server key for later use. If user does not trust the
1014    key this returns FALSE. */
1015
1016 int silc_verify_public_key(SilcClient client,
1017                            SilcClientConnection conn, 
1018                            SilcSocketType conn_type,
1019                            unsigned char *pk, uint32 pk_len,
1020                            SilcSKEPKType pk_type)
1021 {
1022   int i;
1023   char filename[256];
1024   char file[256];
1025   char *fingerprint;
1026   struct passwd *pw;
1027   struct stat st;
1028   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1029                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1030                   "server" : "client");
1031
1032   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1033     silc_say(client, conn, "We don't support %s public key type %d", 
1034              entity, pk_type);
1035     return FALSE;
1036   }
1037
1038   pw = getpwuid(getuid());
1039   if (!pw)
1040     return FALSE;
1041
1042   memset(filename, 0, sizeof(filename));
1043   memset(file, 0, sizeof(file));
1044
1045   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1046       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1047     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1048              conn->sock->hostname, conn->sock->port);
1049     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
1050              pw->pw_dir, entity, file);
1051   } else {
1052     /* Replace all whitespaces with `_'. */
1053     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1054     for (i = 0; i < strlen(fingerprint); i++)
1055       if (fingerprint[i] == ' ')
1056         fingerprint[i] = '_';
1057     
1058     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1059     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
1060              pw->pw_dir, entity, file);
1061     silc_free(fingerprint);
1062   }
1063
1064   /* Take fingerprint of the public key */
1065   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1066
1067   /* Check whether this key already exists */
1068   if (stat(filename, &st) < 0) {
1069
1070     silc_say(client, conn, "Received %s public key", entity);
1071     silc_say(client, conn, "Fingerprint for the %s key is", entity);
1072     silc_say(client, conn, "%s", fingerprint);
1073
1074     /* Ask user to verify the key and save it */
1075     if (silc_client_ask_yes_no(client, 
1076        "Would you like to accept the key (y/n)? "))
1077       {
1078         /* Save the key for future checking */
1079         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
1080                                        SILC_PKCS_FILE_PEM);
1081         silc_free(fingerprint);
1082         return TRUE;
1083       }
1084   } else {
1085     /* The key already exists, verify it. */
1086     SilcPublicKey public_key;
1087     unsigned char *encpk;
1088     uint32 encpk_len;
1089
1090     /* Load the key file */
1091     if (!silc_pkcs_load_public_key(filename, &public_key, 
1092                                    SILC_PKCS_FILE_PEM))
1093       if (!silc_pkcs_load_public_key(filename, &public_key, 
1094                                      SILC_PKCS_FILE_BIN)) {
1095         silc_say(client, conn, "Received %s public key", entity);
1096         silc_say(client, conn, "Fingerprint for the %s key is", entity);
1097         silc_say(client, conn, "%s", fingerprint);
1098         silc_say(client, conn, "Could not load your local copy of the %s key",
1099                  entity);
1100         if (silc_client_ask_yes_no(client, 
1101            "Would you like to accept the key anyway (y/n)? "))
1102           {
1103             /* Save the key for future checking */
1104             unlink(filename);
1105             silc_pkcs_save_public_key_data(filename, pk, pk_len,
1106                                            SILC_PKCS_FILE_PEM);
1107             silc_free(fingerprint);
1108             return TRUE;
1109           }
1110         
1111         silc_free(fingerprint);
1112         return FALSE;
1113       }
1114   
1115     /* Encode the key data */
1116     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1117     if (!encpk) {
1118       silc_say(client, conn, "Received %s public key", entity);
1119       silc_say(client, conn, "Fingerprint for the %s key is", entity);
1120       silc_say(client, conn, "%s", fingerprint);
1121       silc_say(client, conn, "Your local copy of the %s key is malformed",
1122                entity);
1123       if (silc_client_ask_yes_no(client, 
1124          "Would you like to accept the key anyway (y/n)? "))
1125         {
1126           /* Save the key for future checking */
1127           unlink(filename);
1128           silc_pkcs_save_public_key_data(filename, pk, pk_len,
1129                                          SILC_PKCS_FILE_PEM);
1130           silc_free(fingerprint);
1131           return TRUE;
1132         }
1133
1134       silc_free(fingerprint);
1135       return FALSE;
1136     }
1137
1138     if (memcmp(encpk, pk, encpk_len)) {
1139       silc_say(client, conn, "Received %s public key", entity);
1140       silc_say(client, conn, "Fingerprint for the %s key is", entity);
1141       silc_say(client, conn, "%s", fingerprint);
1142       silc_say(client, conn, "%s key does not match with your local copy",
1143                entity);
1144       silc_say(client, conn, 
1145                "It is possible that the key has expired or changed");
1146       silc_say(client, conn, "It is also possible that some one is performing "
1147                        "man-in-the-middle attack");
1148       
1149       /* Ask user to verify the key and save it */
1150       if (silc_client_ask_yes_no(client, 
1151          "Would you like to accept the key anyway (y/n)? "))
1152         {
1153           /* Save the key for future checking */
1154           unlink(filename);
1155           silc_pkcs_save_public_key_data(filename, pk, pk_len,
1156                                          SILC_PKCS_FILE_PEM);
1157           silc_free(fingerprint);
1158           return TRUE;
1159         }
1160
1161       silc_say(client, conn, "Will not accept the %s key", entity);
1162       silc_free(fingerprint);
1163       return FALSE;
1164     }
1165
1166     /* Local copy matched */
1167     silc_free(fingerprint);
1168     return TRUE;
1169   }
1170
1171   silc_say(client, conn, "Will not accept the %s key", entity);
1172   silc_free(fingerprint);
1173   return FALSE;
1174 }
1175
1176 /* Find authentication method and authentication data by hostname and
1177    port. The hostname may be IP address as well. The found authentication
1178    method and authentication data is returned to `auth_meth', `auth_data'
1179    and `auth_data_len'. The function returns TRUE if authentication method
1180    is found and FALSE if not. `conn' may be NULL. */
1181
1182 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1183                          char *hostname, uint16 port,
1184                          SilcProtocolAuthMeth *auth_meth,
1185                          unsigned char **auth_data,
1186                          uint32 *auth_data_len)
1187 {
1188   SilcClientInternal app = (SilcClientInternal)client->application;
1189
1190   if (app->config && app->config->conns) {
1191     SilcClientConfigSectionConnection *conn = NULL;
1192
1193     /* Check if we find a match from user configured connections */
1194     conn = silc_client_config_find_connection(app->config,
1195                                               hostname,
1196                                               port);
1197     if (conn) {
1198       /* Match found. Use the configured authentication method */
1199       *auth_meth = conn->auth_meth;
1200
1201       if (conn->auth_data) {
1202         *auth_data = strdup(conn->auth_data);
1203         *auth_data_len = strlen(conn->auth_data);
1204       }
1205
1206       return TRUE;
1207     }
1208   }
1209
1210   *auth_meth = SILC_AUTH_NONE;
1211   *auth_data = NULL;
1212   *auth_data_len = 0;
1213
1214   return TRUE;
1215 }
1216
1217 /* Notifies application that failure packet was received.  This is called
1218    if there is some protocol active in the client.  The `protocol' is the
1219    protocol context.  The `failure' is opaque pointer to the failure
1220    indication.  Note, that the `failure' is protocol dependant and application
1221    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1222    failure type (see protocol specs for all protocol failure types). */
1223
1224 void silc_failure(SilcClient client, SilcClientConnection conn, 
1225                   SilcProtocol protocol, void *failure)
1226 {
1227   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1228     SilcSKEStatus status = (SilcSKEStatus)failure;
1229     
1230     if (status == SILC_SKE_STATUS_BAD_VERSION)
1231       silc_say(client, conn, 
1232                "You are running incompatible client version (it may be "
1233                "too old or too new)");
1234     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1235       silc_say(client, conn, "Server does not support your public key type");
1236     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1237       silc_say(client, conn, 
1238                "Server does not support one of your proposed KE group");
1239     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1240       silc_say(client, conn, 
1241                "Server does not support one of your proposed cipher");
1242     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1243       silc_say(client, conn, 
1244                "Server does not support one of your proposed PKCS");
1245     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1246       silc_say(client, conn, 
1247                "Server does not support one of your proposed hash function");
1248     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1249       silc_say(client, conn, 
1250                "Server does not support one of your proposed HMAC");
1251     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1252       silc_say(client, conn, "Incorrect signature");
1253   }
1254
1255   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1256     uint32 err = (uint32)failure;
1257
1258     if (err == SILC_AUTH_FAILED)
1259       silc_say(client, conn, "Authentication failed");
1260   }
1261 }
1262
1263 /* Asks whether the user would like to perform the key agreement protocol.
1264    This is called after we have received an key agreement packet or an
1265    reply to our key agreement packet. This returns TRUE if the user wants
1266    the library to perform the key agreement protocol and FALSE if it is not
1267    desired (application may start it later by calling the function
1268    silc_client_perform_key_agreement). */
1269
1270 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1271                        SilcClientEntry client_entry, char *hostname,
1272                        int port,
1273                        SilcKeyAgreementCallback *completion,
1274                        void **context)
1275 {
1276   char host[256];
1277
1278   /* We will just display the info on the screen and return FALSE and user
1279      will have to start the key agreement with a command. */
1280
1281   if (hostname) {
1282     memset(host, 0, sizeof(host));
1283     snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port); 
1284   }
1285
1286   silc_say(client, conn, "%s wants to perform key agreement %s",
1287            client_entry->nickname, hostname ? host : "");
1288
1289   *completion = NULL;
1290   *context = NULL;
1291
1292   return FALSE;
1293 }
1294
1295 /* SILC client operations */
1296 SilcClientOperations ops = {
1297   silc_say,
1298   silc_channel_message,
1299   silc_private_message,
1300   silc_notify,
1301   silc_command,
1302   silc_command_reply,
1303   silc_connect,
1304   silc_disconnect,
1305   silc_get_auth_method,
1306   silc_verify_public_key,
1307   silc_ask_passphrase,
1308   silc_failure,
1309   silc_key_agreement,
1310 };