The SILC Project

source navigation ]
identifier search ]
freetext search ]
file search ]

silc/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 *);
<