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