update.
[silc.git] / apps / silc / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2000 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20
21 #include "clientincludes.h"
22
23 /* 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. */
26
27 void silc_say(SilcClient client, SilcClientConnection conn, 
28               char *msg, ...)
29 {
30   va_list vp;
31   char message[2048];
32   SilcClientInternal app = (SilcClientInternal)client->application;
33
34   memset(message, 0, sizeof(message));
35   strncat(message, "\n***  ", 5);
36
37   va_start(vp, msg);
38   vsprintf(message + 5, msg, vp);
39   va_end(vp);
40   
41   /* Print the message */
42   silc_print_to_window(app->screen->output_win[0], message);
43 }
44
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. */
47
48 void silc_channel_message(SilcClient client, SilcClientConnection conn,
49                           SilcClientEntry sender, SilcChannelEntry channel
50                           , char *msg)
51 {
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>]", 
56                msg);
57   else
58     silc_print(client, "<%s:%s> %s", sender ? sender->nickname : "[<unknown>]",
59                channel->channel_name, msg);
60 }
61
62 /* Private message to the client. The `sender' is the nickname of the
63    sender received in the packet. */
64
65 void silc_private_message(SilcClient client, SilcClientConnection conn,
66                           SilcClientEntry sender, char *msg)
67 {
68   silc_print(client, "*%s* %s", sender->nickname, msg);
69 }
70
71
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
78    does not send it). */
79
80 void silc_notify(SilcClient client, SilcClientConnection conn, 
81                  SilcNotifyType type, ...)
82 {
83   SilcClientInternal app = (SilcClientInternal)client->application;
84   va_list vp;
85   char message[4096];
86   SilcClientEntry client_entry, client_entry2;
87   SilcChannelEntry channel_entry;
88   char *tmp = NULL;
89   unsigned int tmp_int;
90
91   va_start(vp, type);
92
93   memset(message, 0, sizeof(message));
94
95   /* Get arguments (defined by protocol in silc-pp-01 -draft) */
96   switch(type) {
97   case SILC_NOTIFY_TYPE_NONE:
98     tmp = va_arg(vp, char *);
99     if (!tmp)
100       return;
101     strcpy(message, tmp);
102     break;
103
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);
109     break;
110
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);
117     break;
118
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);
126     else
127       snprintf(message, sizeof(message), "%s has left channel %s", 
128                client_entry->nickname, channel_entry->channel_name);
129     break;
130
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 ? ")" : "");
138     else
139       snprintf(message, sizeof(message), "Signoff: %s %s%s%s", 
140                client_entry->nickname,
141                tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
142     break;
143
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);
152     else
153       snprintf(message, sizeof(message), "%s set topic on %s: %s", 
154                client_entry->nickname, channel_entry->channel_name, tmp);
155     break;
156
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);
164     else
165       snprintf(message, sizeof(message), "%s is known as %s", 
166                client_entry->nickname, client_entry2->nickname);
167     break;
168
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);
173     if (tmp)
174       snprintf(message, sizeof(message), "%s changed channel mode to +%s", 
175                client_entry->nickname, tmp);
176     else
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);
183     break;
184
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);
191     if (tmp)
192       snprintf(message, sizeof(message), "%s changed %s's mode to +%s", 
193                client_entry->nickname, client_entry2->nickname, tmp);
194     else
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);
202     }
203     silc_free(tmp);
204     break;
205
206   case SILC_NOTIFY_TYPE_MOTD:
207     {
208       char line[256];
209       int i;
210       tmp = va_arg(vp, unsigned char *);
211
212       i = 0;
213       while(tmp[i] != 0) {
214         if (tmp[i++] == '\n') {
215           memset(line, 0, sizeof(line));
216           strncat(line, tmp, i - 1);
217           tmp += i;
218           
219           silc_say(client, conn, "%s", line);
220           
221           if (!strlen(tmp))
222             break;
223           i = 0;
224         }
225       }
226     }
227     return;
228
229   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
230     break;
231
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);
236
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 ? ")" : "");
242     } else {
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 ? ")" : "");
250     }
251     break;
252
253   default:
254     break;
255   }
256
257   silc_print(client, "*** %s", message);
258 }
259
260 /* Command handler. This function is called always in the command function.
261    If error occurs it will be called as well. `conn' is the associated
262    client connection. `cmd_context' is the command context that was
263    originally sent to the command. `success' is FALSE if error occured
264    during command. `command' is the command being processed. It must be
265    noted that this is not reply from server. This is merely called just
266    after application has called the command. Just to tell application
267    that the command really was processed. */
268
269 void silc_command(SilcClient client, SilcClientConnection conn, 
270                   SilcClientCommandContext cmd_context, int success,
271                   SilcCommand command)
272 {
273   SilcClientInternal app = (SilcClientInternal)client->application;
274
275   if (!success)
276     return;
277
278   switch(command)
279     {
280     case SILC_COMMAND_QUIT:
281       app->screen->bottom_line->channel = NULL;
282       silc_screen_print_bottom_line(app->screen, 0);
283       break;
284
285     case SILC_COMMAND_LEAVE:
286 #if 0
287       if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
288         app->screen->bottom_line->channel = NULL;
289         silc_screen_print_bottom_line(app->screen, 0);
290       }
291 #endif
292       break;
293
294     }
295 }
296
297 /* We've resolved all clients we don't know about, now just print the
298    users from the channel on the screen. */
299
300 void silc_client_show_users(SilcClient client,
301                             SilcClientConnection conn,
302                             SilcClientEntry *clients,
303                             unsigned int clients_count,
304                             void *context)
305 {
306   SilcChannelEntry channel = (SilcChannelEntry)context;
307   SilcChannelUser chu;
308   int k = 0, len1 = 0, len2 = 0;
309   char *name_list = NULL;
310
311   if (!clients)
312     return;
313
314   silc_list_start(channel->clients);
315   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
316     char *m, *n = chu->client->nickname;
317     len2 = strlen(n);
318     len1 += len2;
319     
320     name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
321     
322     m = silc_client_chumode_char(chu->mode);
323     if (m) {
324       memcpy(name_list + (len1 - len2), m, strlen(m));
325       len1 += strlen(m);
326       silc_free(m);
327     }
328     
329     memcpy(name_list + (len1 - len2), n, len2);
330     name_list[len1] = 0;
331     
332     if (k == silc_list_count(channel->clients) - 1)
333       break;
334     memcpy(name_list + len1, " ", 1);
335     len1++;
336     k++;
337   }
338
339   client->ops->say(client, conn, "Users on %s: %s", channel->channel_name, 
340                    name_list);
341   silc_free(name_list);
342 }
343
344 /* Command reply handler. This function is called always in the command reply
345    function. If error occurs it will be called as well. Normal scenario
346    is that it will be called after the received command data has been parsed
347    and processed. The function is used to pass the received command data to
348    the application. 
349
350    `conn' is the associated client connection. `cmd_payload' is the command
351    payload data received from server and it can be ignored. It is provided
352    if the application would like to re-parse the received command data,
353    however, it must be noted that the data is parsed already by the library
354    thus the payload can be ignored. `success' is FALSE if error occured.
355    In this case arguments are not sent to the application. `command' is the
356    command reply being processed. The function has variable argument list
357    and each command defines the number and type of arguments it passes to the
358    application (on error they are not sent). */
359
360 void silc_command_reply(SilcClient client, SilcClientConnection conn,
361                         SilcCommandPayload cmd_payload, int success,
362                         SilcCommand command, SilcCommandStatus status, ...)
363 {
364   SilcClientInternal app = (SilcClientInternal)client->application;
365   SilcChannelUser chu;
366   va_list vp;
367
368   if (!success)
369     return;
370
371   va_start(vp, status);
372
373   switch(command)
374     {
375
376     case SILC_COMMAND_JOIN:
377       {
378         unsigned int mode;
379         char *topic;
380         SilcBuffer client_id_list;
381         unsigned int list_count;
382         SilcChannelEntry channel;
383
384         app->screen->bottom_line->channel = va_arg(vp, char *);
385         channel = va_arg(vp, SilcChannelEntry);
386         mode = va_arg(vp, unsigned int);
387         (void)va_arg(vp, unsigned int);
388         (void)va_arg(vp, unsigned char *);
389         (void)va_arg(vp, unsigned char *);
390         (void)va_arg(vp, unsigned char *);
391         topic = va_arg(vp, char *);
392         (void)va_arg(vp, unsigned char *);
393         list_count = va_arg(vp, unsigned int);
394         client_id_list = va_arg(vp, SilcBuffer);
395
396         if (topic)
397           client->ops->say(client, conn, "Topic for %s: %s", 
398                            app->screen->bottom_line->channel, topic);
399         
400         app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
401         silc_screen_print_bottom_line(app->screen, 0);
402
403         /* Resolve the client information */
404         silc_client_get_clients_by_list(client, conn, list_count,
405                                         client_id_list,
406                                         silc_client_show_users, channel);
407       }
408       break;
409
410     case SILC_COMMAND_NICK:
411       {
412         SilcClientEntry entry;
413
414         entry = va_arg(vp, SilcClientEntry);
415         silc_say(client, conn, "Your current nickname is %s", entry->nickname);
416         app->screen->bottom_line->nickname = entry->nickname;
417         silc_screen_print_bottom_line(app->screen, 0);
418       }
419       break;
420
421     case SILC_COMMAND_USERS:
422       silc_list_start(conn->current_channel->clients);
423       while ((chu = silc_list_get(conn->current_channel->clients)) 
424              != SILC_LIST_END) {
425         if (chu->client == conn->local_entry) {
426           if (app->screen->bottom_line->mode)
427             silc_free(app->screen->bottom_line->mode);
428           app->screen->bottom_line->mode = 
429             silc_client_chumode_char(chu->mode);
430           silc_screen_print_bottom_line(app->screen, 0);
431           break;
432         }
433       break;
434       }
435     }
436 }
437
438 /* Called to indicate that connection was either successfully established
439    or connecting failed.  This is also the first time application receives
440    the SilcClientConnection objecet which it should save somewhere. */
441
442 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
443 {
444   SilcClientInternal app = (SilcClientInternal)client->application;
445
446   if (success) {
447     app->screen->bottom_line->connection = conn->remote_host;
448     silc_screen_print_bottom_line(app->screen, 0);
449     app->conn = conn;
450   }
451 }
452
453 /* Called to indicate that connection was disconnected to the server. */
454
455 void silc_disconnect(SilcClient client, SilcClientConnection conn)
456 {
457   SilcClientInternal app = (SilcClientInternal)client->application;
458
459   app->screen->bottom_line->connection = NULL;
460   silc_screen_print_bottom_line(app->screen, 0);
461   app->conn = NULL;
462 }
463
464 /* Asks passphrase from user on the input line. */
465
466 unsigned char *silc_ask_passphrase(SilcClient client, 
467                                    SilcClientConnection conn)
468 {
469   SilcClientInternal app = (SilcClientInternal)conn->client->application;
470   char pass1[256], pass2[256];
471   char *ret;
472   int try = 3;
473
474   while(try) {
475
476     /* Print prompt */
477     wattroff(app->screen->input_win, A_INVIS);
478     silc_screen_input_print_prompt(app->screen, "Passphrase: ");
479     wattron(app->screen->input_win, A_INVIS);
480     
481     /* Get string */
482     memset(pass1, 0, sizeof(pass1));
483     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
484     
485     /* Print retype prompt */
486     wattroff(app->screen->input_win, A_INVIS);
487     silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
488     wattron(app->screen->input_win, A_INVIS);
489     
490     /* Get string */
491     memset(pass2, 0, sizeof(pass2));
492     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
493
494     if (!strncmp(pass1, pass2, strlen(pass2)))
495       break;
496
497     try--;
498   }
499
500   ret = silc_calloc(strlen(pass1), sizeof(char));
501   memcpy(ret, pass1, strlen(pass1));
502
503   memset(pass1, 0, sizeof(pass1));
504   memset(pass2, 0, sizeof(pass2));
505
506   wattroff(app->screen->input_win, A_INVIS);
507   silc_screen_input_reset(app->screen);
508
509   return ret;
510 }
511
512 /* Verifies received public key. If user decides to trust the key it is
513    saved as trusted server key for later use. If user does not trust the
514    key this returns FALSE. */
515
516 int silc_verify_server_key(SilcClient client,
517                            SilcClientConnection conn, 
518                            unsigned char *pk, unsigned int pk_len,
519                            SilcSKEPKType pk_type)
520 {
521   SilcSocketConnection sock = conn->sock;
522   char filename[256];
523   char file[256];
524   char *hostname, *fingerprint;
525   struct passwd *pw;
526   struct stat st;
527
528   hostname = sock->hostname ? sock->hostname : sock->ip;
529
530   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
531     silc_say(client, conn, "We don't support server %s key type", hostname);
532     return FALSE;
533   }
534
535   pw = getpwuid(getuid());
536   if (!pw)
537     return FALSE;
538
539   memset(filename, 0, sizeof(filename));
540   memset(file, 0, sizeof(file));
541   snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
542            sock->port);
543   snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
544            pw->pw_dir, file);
545
546   /* Check wheter this key already exists */
547   if (stat(filename, &st) < 0) {
548
549     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
550     silc_say(client, conn, "Received server %s public key", hostname);
551     silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
552     silc_say(client, conn, "%s", fingerprint);
553     silc_free(fingerprint);
554
555     /* Ask user to verify the key and save it */
556     if (silc_client_ask_yes_no(client, 
557        "Would you like to accept the key (y/n)? "))
558       {
559         /* Save the key for future checking */
560         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
561                                        SILC_PKCS_FILE_PEM);
562         return TRUE;
563       }
564   } else {
565     /* The key already exists, verify it. */
566     SilcPublicKey public_key;
567     unsigned char *encpk;
568     unsigned int encpk_len;
569
570     /* Load the key file */
571     if (!silc_pkcs_load_public_key(filename, &public_key, 
572                                    SILC_PKCS_FILE_PEM))
573       if (!silc_pkcs_load_public_key(filename, &public_key, 
574                                      SILC_PKCS_FILE_BIN)) {
575         fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
576         silc_say(client, conn, "Received server %s public key", hostname);
577         silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
578         silc_say(client, conn, "%s", fingerprint);
579         silc_free(fingerprint);
580         silc_say(client, conn, "Could not load your local copy of the server %s key",
581                  hostname);
582         if (silc_client_ask_yes_no(client, 
583            "Would you like to accept the key anyway (y/n)? "))
584           {
585             /* Save the key for future checking */
586             unlink(filename);
587             silc_pkcs_save_public_key_data(filename, pk, pk_len,
588                                            SILC_PKCS_FILE_PEM);
589             return TRUE;
590           }
591         
592         return FALSE;
593       }
594   
595     /* Encode the key data */
596     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
597     if (!encpk) {
598       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
599       silc_say(client, conn, "Received server %s public key", hostname);
600       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
601       silc_say(client, conn, "%s", fingerprint);
602       silc_free(fingerprint);
603       silc_say(client, conn, "Your local copy of the server %s key is malformed",
604                hostname);
605       if (silc_client_ask_yes_no(client, 
606          "Would you like to accept the key anyway (y/n)? "))
607         {
608           /* Save the key for future checking */
609           unlink(filename);
610           silc_pkcs_save_public_key_data(filename, pk, pk_len,
611                                          SILC_PKCS_FILE_PEM);
612           return TRUE;
613         }
614
615       return FALSE;
616     }
617
618     if (memcmp(encpk, pk, encpk_len)) {
619       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
620       silc_say(client, conn, "Received server %s public key", hostname);
621       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
622       silc_say(client, conn, "%s", fingerprint);
623       silc_free(fingerprint);
624       silc_say(client, conn, "Server %s key does not match with your local copy",
625                hostname);
626       silc_say(client, conn, "It is possible that the key has expired or changed");
627       silc_say(client, conn, "It is also possible that some one is performing "
628                        "man-in-the-middle attack");
629       
630       /* Ask user to verify the key and save it */
631       if (silc_client_ask_yes_no(client, 
632          "Would you like to accept the key anyway (y/n)? "))
633         {
634           /* Save the key for future checking */
635           unlink(filename);
636           silc_pkcs_save_public_key_data(filename, pk, pk_len,
637                                          SILC_PKCS_FILE_PEM);
638           return TRUE;
639         }
640
641       silc_say(client, conn, "Will not accept server %s key", hostname);
642       return FALSE;
643     }
644
645     /* Local copy matched */
646     return TRUE;
647   }
648
649   silc_say(client, conn, "Will not accept server %s key", hostname);
650   return FALSE;
651 }
652
653 /* Find authentication method and authentication data by hostname and
654    port. The hostname may be IP address as well. The found authentication
655    method and authentication data is returned to `auth_meth', `auth_data'
656    and `auth_data_len'. The function returns TRUE if authentication method
657    is found and FALSE if not. `conn' may be NULL. */
658
659 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
660                          char *hostname, unsigned short port,
661                          SilcProtocolAuthMeth *auth_meth,
662                          unsigned char **auth_data,
663                          unsigned int *auth_data_len)
664 {
665   SilcClientInternal app = (SilcClientInternal)client->application;
666
667   if (app->config->conns) {
668     SilcClientConfigSectionConnection *conn = NULL;
669
670     /* Check if we find a match from user configured connections */
671     conn = silc_client_config_find_connection(app->config,
672                                               hostname,
673                                               port);
674     if (conn) {
675       /* Match found. Use the configured authentication method */
676       *auth_meth = conn->auth_meth;
677
678       if (conn->auth_data) {
679         *auth_data = strdup(conn->auth_data);
680         *auth_data_len = strlen(conn->auth_data);
681       }
682
683       return TRUE;
684     }
685   }
686
687   return FALSE;
688 }
689
690 /* Notifies application that failure packet was received.  This is called
691    if there is some protocol active in the client.  The `protocol' is the
692    protocol context.  The `failure' is opaque pointer to the failure
693    indication.  Note, that the `failure' is protocol dependant and application
694    must explicitly cast it to correct type.  Usually `failure' is 32 bit
695    failure type (see protocol specs for all protocol failure types). */
696
697 void silc_failure(SilcClient client, SilcClientConnection conn, 
698                   SilcProtocol protocol, void *failure)
699 {
700
701 }
702
703 /* Asks whether the user would like to perform the key agreement protocol.
704    This is called after we have received an key agreement packet or an
705    reply to our key agreement packet. This returns TRUE if the user wants
706    the library to perform the key agreement protocol and FALSE if it is not
707    desired (application may start it later by calling the function
708    silc_client_perform_key_agreement). */
709
710 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
711                        SilcClientEntry client_entry, char *hostname,
712                        int port,
713                        SilcKeyAgreementCallback *completion,
714                        void **context)
715 {
716
717   return FALSE;
718 }
719
720 /* SILC client operations */
721 SilcClientOperations ops = {
722   say:                  silc_say,
723   channel_message:      silc_channel_message,
724   private_message:      silc_private_message,
725   notify:               silc_notify,
726   command:              silc_command,
727   command_reply:        silc_command_reply,
728   connect:              silc_connect,
729   disconnect:           silc_disconnect,
730   get_auth_method:      silc_get_auth_method,
731   verify_server_key:    silc_verify_server_key,
732   ask_passphrase:       silc_ask_passphrase,
733   failure:              silc_failure,
734   key_agreement:        silc_key_agreement,
735 };