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