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