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