5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2000 Pekka Riikonen
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.
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.
21 #include "clientincludes.h"
23 /* Prints a message with three star (*) sign before the actual message
24 on the current output window. This is used to print command outputs
25 and error messages. */
27 void silc_say(SilcClient client, SilcClientConnection conn,
32 SilcClientInternal app = (SilcClientInternal)client->application;
34 memset(message, 0, sizeof(message));
35 strncat(message, "\n*** ", 5);
38 vsprintf(message + 5, msg, vp);
41 /* Print the message */
42 silc_print_to_window(app->screen->output_win[0], message);
45 /* Message for a channel. The `sender' is the nickname of the sender
46 received in the packet. The `channel_name' is the name of the channel. */
48 void silc_channel_message(SilcClient client, SilcClientConnection conn,
49 SilcClientEntry sender, SilcChannelEntry channel
52 /* Message from client */
53 if (conn && !strcmp(conn->current_channel->channel_name,
54 channel->channel_name))
55 silc_print(client, "<%s> %s", sender ? sender->nickname : "[<unknown>]",
58 silc_print(client, "<%s:%s> %s", sender ? sender->nickname : "[<unknown>]",
59 channel->channel_name, msg);
62 /* Private message to the client. The `sender' is the nickname of the
63 sender received in the packet. */
65 void silc_private_message(SilcClient client, SilcClientConnection conn,
66 SilcClientEntry sender, char *msg)
68 silc_print(client, "*%s* %s", sender->nickname, msg);
72 /* Notify message to the client. The notify arguments are sent in the
73 same order as servers sends them. The arguments are same as received
74 from the server except for ID's. If ID is received application receives
75 the corresponding entry to the ID. For example, if Client ID is received
76 application receives SilcClientEntry. Also, if the notify type is
77 for channel the channel entry is sent to application (even if server
80 void silc_notify(SilcClient client, SilcClientConnection conn,
81 SilcNotifyType type, ...)
83 SilcClientInternal app = (SilcClientInternal)client->application;
86 SilcClientEntry client_entry, client_entry2;
87 SilcChannelEntry channel_entry;
93 memset(message, 0, sizeof(message));
95 /* Get arguments (defined by protocol in silc-pp-01 -draft) */
97 case SILC_NOTIFY_TYPE_NONE:
98 tmp = va_arg(vp, char *);
101 strcpy(message, tmp);
104 case SILC_NOTIFY_TYPE_INVITE:
105 client_entry = va_arg(vp, SilcClientEntry);
106 channel_entry = va_arg(vp, SilcChannelEntry);
107 snprintf(message, sizeof(message), "%s invites you to channel %s",
108 client_entry->nickname, channel_entry->channel_name);
111 case SILC_NOTIFY_TYPE_JOIN:
112 client_entry = va_arg(vp, SilcClientEntry);
113 channel_entry = va_arg(vp, SilcChannelEntry);
114 snprintf(message, sizeof(message), "%s (%s) has joined channel %s",
115 client_entry->nickname, client_entry->username,
116 channel_entry->channel_name);
119 case SILC_NOTIFY_TYPE_LEAVE:
120 client_entry = va_arg(vp, SilcClientEntry);
121 channel_entry = va_arg(vp, SilcChannelEntry);
122 if (client_entry->server)
123 snprintf(message, sizeof(message), "%s@%s has left channel %s",
124 client_entry->nickname, client_entry->server,
125 channel_entry->channel_name);
127 snprintf(message, sizeof(message), "%s has left channel %s",
128 client_entry->nickname, channel_entry->channel_name);
131 case SILC_NOTIFY_TYPE_SIGNOFF:
132 client_entry = va_arg(vp, SilcClientEntry);
133 tmp = va_arg(vp, char *);
134 if (client_entry->server)
135 snprintf(message, sizeof(message), "Signoff: %s@%s %s%s%s",
136 client_entry->nickname, client_entry->server,
137 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
139 snprintf(message, sizeof(message), "Signoff: %s %s%s%s",
140 client_entry->nickname,
141 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
144 case SILC_NOTIFY_TYPE_TOPIC_SET:
145 client_entry = va_arg(vp, SilcClientEntry);
146 tmp = va_arg(vp, char *);
147 channel_entry = va_arg(vp, SilcChannelEntry);
148 if (client_entry->server)
149 snprintf(message, sizeof(message), "%s@%s set topic on %s: %s",
150 client_entry->nickname, client_entry->server,
151 channel_entry->channel_name, tmp);
153 snprintf(message, sizeof(message), "%s set topic on %s: %s",
154 client_entry->nickname, channel_entry->channel_name, tmp);
157 case SILC_NOTIFY_TYPE_NICK_CHANGE:
158 client_entry = va_arg(vp, SilcClientEntry);
159 client_entry2 = va_arg(vp, SilcClientEntry);
160 if (client_entry->server && client_entry2->server)
161 snprintf(message, sizeof(message), "%s@%s is known as %s@%s",
162 client_entry->nickname, client_entry->server,
163 client_entry2->nickname, client_entry2->server);
165 snprintf(message, sizeof(message), "%s is known as %s",
166 client_entry->nickname, client_entry2->nickname);
169 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
170 client_entry = va_arg(vp, SilcClientEntry);
171 tmp = silc_client_chmode(va_arg(vp, unsigned int));
172 channel_entry = va_arg(vp, SilcChannelEntry);
174 snprintf(message, sizeof(message), "%s changed channel mode to +%s",
175 client_entry->nickname, tmp);
177 snprintf(message, sizeof(message), "%s removed all channel modes",
178 client_entry->nickname);
179 if (app->screen->bottom_line->channel_mode)
180 silc_free(app->screen->bottom_line->channel_mode);
181 app->screen->bottom_line->channel_mode = tmp;
182 silc_screen_print_bottom_line(app->screen, 0);
185 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
186 client_entry = va_arg(vp, SilcClientEntry);
187 tmp_int = va_arg(vp, unsigned int);
188 tmp = silc_client_chumode(tmp_int);
189 client_entry2 = va_arg(vp, SilcClientEntry);
190 channel_entry = va_arg(vp, SilcChannelEntry);
192 snprintf(message, sizeof(message), "%s changed %s's mode to +%s",
193 client_entry->nickname, client_entry2->nickname, tmp);
195 snprintf(message, sizeof(message), "%s removed %s's modes",
196 client_entry->nickname, client_entry2->nickname);
197 if (client_entry2 == conn->local_entry) {
198 if (app->screen->bottom_line->mode)
199 silc_free(app->screen->bottom_line->mode);
200 app->screen->bottom_line->mode = silc_client_chumode_char(tmp_int);
201 silc_screen_print_bottom_line(app->screen, 0);
206 case SILC_NOTIFY_TYPE_MOTD:
210 tmp = va_arg(vp, unsigned char *);
214 if (tmp[i++] == '\n') {
215 memset(line, 0, sizeof(line));
216 strncat(line, tmp, i - 1);
219 silc_say(client, conn, "%s", line);
229 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
232 case SILC_NOTIFY_TYPE_KICKED:
233 client_entry = va_arg(vp, SilcClientEntry);
234 tmp = va_arg(vp, char *);
235 channel_entry = va_arg(vp, SilcChannelEntry);
237 if (client_entry == conn->local_entry) {
238 snprintf(message, sizeof(message),
239 "You have been kicked off channel %s %s%s%s",
240 conn->current_channel->channel_name,
241 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
243 snprintf(message, sizeof(message),
244 "%s%s%s has been kicked off channel %s %s%s%s",
245 client_entry->nickname,
246 client_entry->server ? "@" : "",
247 client_entry->server ? client_entry->server : "",
248 conn->current_channel->channel_name,
249 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
253 case SILC_NOTIFY_TYPE_KILLED:
254 client_entry = va_arg(vp, SilcClientEntry);
255 tmp = va_arg(vp, char *);
256 channel_entry = va_arg(vp, SilcChannelEntry);
258 if (client_entry == conn->local_entry) {
259 snprintf(message, sizeof(message),
260 "You have been killed from the SILC Network %s%s%s",
261 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
263 snprintf(message, sizeof(message),
264 "%s%s%s has been killed from the SILC Network %s%s%s",
265 client_entry->nickname,
266 client_entry->server ? "@" : "",
267 client_entry->server ? client_entry->server : "",
268 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
276 silc_print(client, "*** %s", message);
279 /* Command handler. This function is called always in the command function.
280 If error occurs it will be called as well. `conn' is the associated
281 client connection. `cmd_context' is the command context that was
282 originally sent to the command. `success' is FALSE if error occured
283 during command. `command' is the command being processed. It must be
284 noted that this is not reply from server. This is merely called just
285 after application has called the command. Just to tell application
286 that the command really was processed. */
288 void silc_command(SilcClient client, SilcClientConnection conn,
289 SilcClientCommandContext cmd_context, int success,
292 SilcClientInternal app = (SilcClientInternal)client->application;
300 case SILC_COMMAND_QUIT:
301 app->screen->bottom_line->channel = NULL;
302 silc_screen_print_bottom_line(app->screen, 0);
305 case SILC_COMMAND_LEAVE:
307 if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
308 app->screen->bottom_line->channel = NULL;
309 silc_screen_print_bottom_line(app->screen, 0);
317 /* We've resolved all clients we don't know about, now just print the
318 users from the channel on the screen. */
320 void silc_client_show_users(SilcClient client,
321 SilcClientConnection conn,
322 SilcClientEntry *clients,
323 unsigned int clients_count,
326 SilcChannelEntry channel = (SilcChannelEntry)context;
328 int k = 0, len1 = 0, len2 = 0;
329 char *name_list = NULL;
334 silc_list_start(channel->clients);
335 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
336 char *m, *n = chu->client->nickname;
343 name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
345 m = silc_client_chumode_char(chu->mode);
347 memcpy(name_list + (len1 - len2), m, strlen(m));
352 memcpy(name_list + (len1 - len2), n, len2);
355 if (k == silc_list_count(channel->clients) - 1)
357 memcpy(name_list + len1, " ", 1);
362 client->ops->say(client, conn, "Users on %s: %s", channel->channel_name,
364 silc_free(name_list);
367 /* Command reply handler. This function is called always in the command reply
368 function. If error occurs it will be called as well. Normal scenario
369 is that it will be called after the received command data has been parsed
370 and processed. The function is used to pass the received command data to
373 `conn' is the associated client connection. `cmd_payload' is the command
374 payload data received from server and it can be ignored. It is provided
375 if the application would like to re-parse the received command data,
376 however, it must be noted that the data is parsed already by the library
377 thus the payload can be ignored. `success' is FALSE if error occured.
378 In this case arguments are not sent to the application. `command' is the
379 command reply being processed. The function has variable argument list
380 and each command defines the number and type of arguments it passes to the
381 application (on error they are not sent). */
383 void silc_command_reply(SilcClient client, SilcClientConnection conn,
384 SilcCommandPayload cmd_payload, int success,
385 SilcCommand command, SilcCommandStatus status, ...)
387 SilcClientInternal app = (SilcClientInternal)client->application;
391 va_start(vp, status);
395 case SILC_COMMAND_WHOIS:
397 char buf[1024], *nickname, *username, *realname;
401 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
403 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
406 client->ops->say(client, conn, "%s: %s", tmp,
407 silc_client_command_status_message(status));
409 client->ops->say(client, conn, "%s",
410 silc_client_command_status_message(status));
417 (void)va_arg(vp, SilcClientEntry);
418 nickname = va_arg(vp, char *);
419 username = va_arg(vp, char *);
420 realname = va_arg(vp, char *);
421 (void)va_arg(vp, void *);
422 idle = va_arg(vp, unsigned int);
424 memset(buf, 0, sizeof(buf));
427 len = strlen(nickname);
428 strncat(buf, nickname, len);
429 strncat(buf, " is ", 4);
433 strncat(buf, username, strlen(username));
437 strncat(buf, " (", 2);
438 strncat(buf, realname, strlen(realname));
439 strncat(buf, ")", 1);
442 client->ops->say(client, conn, "%s", buf);
443 if (idle && nickname)
444 client->ops->say(client, conn, "%s has been idle %d %s",
446 idle > 60 ? (idle / 60) : idle,
447 idle > 60 ? "minutes" : "seconds");
451 case SILC_COMMAND_WHOWAS:
453 char buf[1024], *nickname, *username, *realname;
456 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
458 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
461 client->ops->say(client, conn, "%s: %s", tmp,
462 silc_client_command_status_message(status));
464 client->ops->say(client, conn, "%s",
465 silc_client_command_status_message(status));
472 (void)va_arg(vp, SilcClientEntry);
473 nickname = va_arg(vp, char *);
474 username = va_arg(vp, char *);
475 realname = va_arg(vp, char *);
477 memset(buf, 0, sizeof(buf));
480 len = strlen(nickname);
481 strncat(buf, nickname, len);
482 strncat(buf, " was ", 5);
486 strncat(buf, username, strlen(nickname));
490 strncat(buf, " (", 2);
491 strncat(buf, realname, strlen(realname));
492 strncat(buf, ")", 1);
495 client->ops->say(client, conn, "%s", buf);
499 case SILC_COMMAND_JOIN:
503 SilcBuffer client_id_list;
504 unsigned int list_count;
505 SilcChannelEntry channel;
510 app->screen->bottom_line->channel = va_arg(vp, char *);
511 channel = va_arg(vp, SilcChannelEntry);
512 mode = va_arg(vp, unsigned int);
513 (void)va_arg(vp, unsigned int);
514 (void)va_arg(vp, unsigned char *);
515 (void)va_arg(vp, unsigned char *);
516 (void)va_arg(vp, unsigned char *);
517 topic = va_arg(vp, char *);
518 (void)va_arg(vp, unsigned char *);
519 list_count = va_arg(vp, unsigned int);
520 client_id_list = va_arg(vp, SilcBuffer);
523 client->ops->say(client, conn, "Topic for %s: %s",
524 app->screen->bottom_line->channel, topic);
526 app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
527 silc_screen_print_bottom_line(app->screen, 0);
529 /* Resolve the client information */
530 silc_client_get_clients_by_list(client, conn, list_count,
532 silc_client_show_users, channel);
536 case SILC_COMMAND_NICK:
538 SilcClientEntry entry;
543 entry = va_arg(vp, SilcClientEntry);
544 silc_say(client, conn, "Your current nickname is %s", entry->nickname);
545 app->screen->bottom_line->nickname = entry->nickname;
546 silc_screen_print_bottom_line(app->screen, 0);
550 case SILC_COMMAND_USERS:
554 silc_list_start(conn->current_channel->clients);
555 while ((chu = silc_list_get(conn->current_channel->clients))
557 if (chu->client == conn->local_entry) {
558 if (app->screen->bottom_line->mode)
559 silc_free(app->screen->bottom_line->mode);
560 app->screen->bottom_line->mode =
561 silc_client_chumode_char(chu->mode);
562 silc_screen_print_bottom_line(app->screen, 0);
570 /* Called to indicate that connection was either successfully established
571 or connecting failed. This is also the first time application receives
572 the SilcClientConnection objecet which it should save somewhere. */
574 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
576 SilcClientInternal app = (SilcClientInternal)client->application;
579 app->screen->bottom_line->connection = conn->remote_host;
580 silc_screen_print_bottom_line(app->screen, 0);
585 /* Called to indicate that connection was disconnected to the server. */
587 void silc_disconnect(SilcClient client, SilcClientConnection conn)
589 SilcClientInternal app = (SilcClientInternal)client->application;
591 app->screen->bottom_line->connection = NULL;
592 silc_screen_print_bottom_line(app->screen, 0);
596 /* Asks passphrase from user on the input line. */
598 unsigned char *silc_ask_passphrase(SilcClient client,
599 SilcClientConnection conn)
601 SilcClientInternal app = (SilcClientInternal)conn->client->application;
602 char pass1[256], pass2[256];
609 wattroff(app->screen->input_win, A_INVIS);
610 silc_screen_input_print_prompt(app->screen, "Passphrase: ");
611 wattron(app->screen->input_win, A_INVIS);
614 memset(pass1, 0, sizeof(pass1));
615 wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
617 /* Print retype prompt */
618 wattroff(app->screen->input_win, A_INVIS);
619 silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
620 wattron(app->screen->input_win, A_INVIS);
623 memset(pass2, 0, sizeof(pass2));
624 wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
626 if (!strncmp(pass1, pass2, strlen(pass2)))
632 ret = silc_calloc(strlen(pass1), sizeof(char));
633 memcpy(ret, pass1, strlen(pass1));
635 memset(pass1, 0, sizeof(pass1));
636 memset(pass2, 0, sizeof(pass2));
638 wattroff(app->screen->input_win, A_INVIS);
639 silc_screen_input_reset(app->screen);
644 /* Verifies received public key. If user decides to trust the key it is
645 saved as trusted server key for later use. If user does not trust the
646 key this returns FALSE. */
648 int silc_verify_server_key(SilcClient client,
649 SilcClientConnection conn,
650 unsigned char *pk, unsigned int pk_len,
651 SilcSKEPKType pk_type)
653 SilcSocketConnection sock = conn->sock;
656 char *hostname, *fingerprint;
660 hostname = sock->hostname ? sock->hostname : sock->ip;
662 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
663 silc_say(client, conn, "We don't support server %s key type", hostname);
667 pw = getpwuid(getuid());
671 memset(filename, 0, sizeof(filename));
672 memset(file, 0, sizeof(file));
673 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
675 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
678 /* Check wheter this key already exists */
679 if (stat(filename, &st) < 0) {
681 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
682 silc_say(client, conn, "Received server %s public key", hostname);
683 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
684 silc_say(client, conn, "%s", fingerprint);
685 silc_free(fingerprint);
687 /* Ask user to verify the key and save it */
688 if (silc_client_ask_yes_no(client,
689 "Would you like to accept the key (y/n)? "))
691 /* Save the key for future checking */
692 silc_pkcs_save_public_key_data(filename, pk, pk_len,
697 /* The key already exists, verify it. */
698 SilcPublicKey public_key;
699 unsigned char *encpk;
700 unsigned int encpk_len;
702 /* Load the key file */
703 if (!silc_pkcs_load_public_key(filename, &public_key,
705 if (!silc_pkcs_load_public_key(filename, &public_key,
706 SILC_PKCS_FILE_BIN)) {
707 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
708 silc_say(client, conn, "Received server %s public key", hostname);
709 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
710 silc_say(client, conn, "%s", fingerprint);
711 silc_free(fingerprint);
712 silc_say(client, conn, "Could not load your local copy of the server %s key",
714 if (silc_client_ask_yes_no(client,
715 "Would you like to accept the key anyway (y/n)? "))
717 /* Save the key for future checking */
719 silc_pkcs_save_public_key_data(filename, pk, pk_len,
727 /* Encode the key data */
728 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
730 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
731 silc_say(client, conn, "Received server %s public key", hostname);
732 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
733 silc_say(client, conn, "%s", fingerprint);
734 silc_free(fingerprint);
735 silc_say(client, conn, "Your local copy of the server %s key is malformed",
737 if (silc_client_ask_yes_no(client,
738 "Would you like to accept the key anyway (y/n)? "))
740 /* Save the key for future checking */
742 silc_pkcs_save_public_key_data(filename, pk, pk_len,
750 if (memcmp(encpk, pk, encpk_len)) {
751 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
752 silc_say(client, conn, "Received server %s public key", hostname);
753 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
754 silc_say(client, conn, "%s", fingerprint);
755 silc_free(fingerprint);
756 silc_say(client, conn, "Server %s key does not match with your local copy",
758 silc_say(client, conn, "It is possible that the key has expired or changed");
759 silc_say(client, conn, "It is also possible that some one is performing "
760 "man-in-the-middle attack");
762 /* Ask user to verify the key and save it */
763 if (silc_client_ask_yes_no(client,
764 "Would you like to accept the key anyway (y/n)? "))
766 /* Save the key for future checking */
768 silc_pkcs_save_public_key_data(filename, pk, pk_len,
773 silc_say(client, conn, "Will not accept server %s key", hostname);
777 /* Local copy matched */
781 silc_say(client, conn, "Will not accept server %s key", hostname);
785 /* Find authentication method and authentication data by hostname and
786 port. The hostname may be IP address as well. The found authentication
787 method and authentication data is returned to `auth_meth', `auth_data'
788 and `auth_data_len'. The function returns TRUE if authentication method
789 is found and FALSE if not. `conn' may be NULL. */
791 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
792 char *hostname, unsigned short port,
793 SilcProtocolAuthMeth *auth_meth,
794 unsigned char **auth_data,
795 unsigned int *auth_data_len)
797 SilcClientInternal app = (SilcClientInternal)client->application;
799 if (app->config->conns) {
800 SilcClientConfigSectionConnection *conn = NULL;
802 /* Check if we find a match from user configured connections */
803 conn = silc_client_config_find_connection(app->config,
807 /* Match found. Use the configured authentication method */
808 *auth_meth = conn->auth_meth;
810 if (conn->auth_data) {
811 *auth_data = strdup(conn->auth_data);
812 *auth_data_len = strlen(conn->auth_data);
822 /* Notifies application that failure packet was received. This is called
823 if there is some protocol active in the client. The `protocol' is the
824 protocol context. The `failure' is opaque pointer to the failure
825 indication. Note, that the `failure' is protocol dependant and application
826 must explicitly cast it to correct type. Usually `failure' is 32 bit
827 failure type (see protocol specs for all protocol failure types). */
829 void silc_failure(SilcClient client, SilcClientConnection conn,
830 SilcProtocol protocol, void *failure)
835 /* Asks whether the user would like to perform the key agreement protocol.
836 This is called after we have received an key agreement packet or an
837 reply to our key agreement packet. This returns TRUE if the user wants
838 the library to perform the key agreement protocol and FALSE if it is not
839 desired (application may start it later by calling the function
840 silc_client_perform_key_agreement). */
842 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
843 SilcClientEntry client_entry, char *hostname,
845 SilcKeyAgreementCallback *completion,
852 /* SILC client operations */
853 SilcClientOperations ops = {
855 channel_message: silc_channel_message,
856 private_message: silc_private_message,
858 command: silc_command,
859 command_reply: silc_command_reply,
860 connect: silc_connect,
861 disconnect: silc_disconnect,
862 get_auth_method: silc_get_auth_method,
863 verify_server_key: silc_verify_server_key,
864 ask_passphrase: silc_ask_passphrase,
865 failure: silc_failure,
866 key_agreement: silc_key_agreement,