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