updates.
[silc.git] / apps / silc / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2000 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20
21 #include "clientincludes.h"
22
23 /* 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 /* Command reply handler. This function is called always in the command reply
298    function. If error occurs it will be called as well. Normal scenario
299    is that it will be called after the received command data has been parsed
300    and processed. The function is used to pass the received command data to
301    the application. 
302
303    `conn' is the associated client connection. `cmd_payload' is the command
304    payload data received from server and it can be ignored. It is provided
305    if the application would like to re-parse the received command data,
306    however, it must be noted that the data is parsed already by the library
307    thus the payload can be ignored. `success' is FALSE if error occured.
308    In this case arguments are not sent to the application. `command' is the
309    command reply being processed. The function has variable argument list
310    and each command defines the number and type of arguments it passes to the
311    application (on error they are not sent). */
312
313 void silc_command_reply(SilcClient client, SilcClientConnection conn,
314                         SilcCommandPayload cmd_payload, int success,
315                         SilcCommand command, SilcCommandStatus status, ...)
316 {
317   SilcClientInternal app = (SilcClientInternal)client->application;
318   SilcChannelUser chu;
319   va_list vp;
320
321   if (!success)
322     return;
323
324   va_start(vp, status);
325
326   switch(command)
327     {
328
329     case SILC_COMMAND_JOIN:
330       {
331         unsigned int mode;
332
333         app->screen->bottom_line->channel = va_arg(vp, char *);
334         (void)va_arg(vp, void *);
335         mode = va_arg(vp, unsigned int);
336         app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
337         silc_screen_print_bottom_line(app->screen, 0);
338       }
339       break;
340
341     case SILC_COMMAND_NICK:
342       {
343         SilcClientEntry entry;
344
345         entry = va_arg(vp, SilcClientEntry);
346         silc_say(client, conn, "Your current nickname is %s", entry->nickname);
347         app->screen->bottom_line->nickname = entry->nickname;
348         silc_screen_print_bottom_line(app->screen, 0);
349       }
350       break;
351
352     case SILC_COMMAND_USERS:
353       silc_list_start(conn->current_channel->clients);
354       while ((chu = silc_list_get(conn->current_channel->clients)) 
355              != SILC_LIST_END) {
356         if (chu->client == conn->local_entry) {
357           if (app->screen->bottom_line->mode)
358             silc_free(app->screen->bottom_line->mode);
359           app->screen->bottom_line->mode = 
360             silc_client_chumode_char(chu->mode);
361           silc_screen_print_bottom_line(app->screen, 0);
362           break;
363         }
364       break;
365       }
366     }
367 }
368
369 /* Called to indicate that connection was either successfully established
370    or connecting failed.  This is also the first time application receives
371    the SilcClientConnection objecet which it should save somewhere. */
372
373 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
374 {
375   SilcClientInternal app = (SilcClientInternal)client->application;
376
377   if (success) {
378     app->screen->bottom_line->connection = conn->remote_host;
379     silc_screen_print_bottom_line(app->screen, 0);
380     app->conn = conn;
381   }
382 }
383
384 /* Called to indicate that connection was disconnected to the server. */
385
386 void silc_disconnect(SilcClient client, SilcClientConnection conn)
387 {
388   SilcClientInternal app = (SilcClientInternal)client->application;
389
390   app->screen->bottom_line->connection = NULL;
391   silc_screen_print_bottom_line(app->screen, 0);
392   app->conn = NULL;
393 }
394
395 /* Asks passphrase from user on the input line. */
396
397 unsigned char *silc_ask_passphrase(SilcClient client, 
398                                    SilcClientConnection conn)
399 {
400   SilcClientInternal app = (SilcClientInternal)conn->client->application;
401   char pass1[256], pass2[256];
402   char *ret;
403   int try = 3;
404
405   while(try) {
406
407     /* Print prompt */
408     wattroff(app->screen->input_win, A_INVIS);
409     silc_screen_input_print_prompt(app->screen, "Passphrase: ");
410     wattron(app->screen->input_win, A_INVIS);
411     
412     /* Get string */
413     memset(pass1, 0, sizeof(pass1));
414     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
415     
416     /* Print retype prompt */
417     wattroff(app->screen->input_win, A_INVIS);
418     silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
419     wattron(app->screen->input_win, A_INVIS);
420     
421     /* Get string */
422     memset(pass2, 0, sizeof(pass2));
423     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
424
425     if (!strncmp(pass1, pass2, strlen(pass2)))
426       break;
427
428     try--;
429   }
430
431   ret = silc_calloc(strlen(pass1), sizeof(char));
432   memcpy(ret, pass1, strlen(pass1));
433
434   memset(pass1, 0, sizeof(pass1));
435   memset(pass2, 0, sizeof(pass2));
436
437   wattroff(app->screen->input_win, A_INVIS);
438   silc_screen_input_reset(app->screen);
439
440   return ret;
441 }
442
443 /* Verifies received public key. If user decides to trust the key it is
444    saved as trusted server key for later use. If user does not trust the
445    key this returns FALSE. */
446
447 int silc_verify_server_key(SilcClient client,
448                            SilcClientConnection conn, 
449                            unsigned char *pk, unsigned int pk_len,
450                            SilcSKEPKType pk_type)
451 {
452   SilcSocketConnection sock = conn->sock;
453   char filename[256];
454   char file[256];
455   char *hostname, *fingerprint;
456   struct passwd *pw;
457   struct stat st;
458
459   hostname = sock->hostname ? sock->hostname : sock->ip;
460
461   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
462     silc_say(client, conn, "We don't support server %s key type", hostname);
463     return FALSE;
464   }
465
466   pw = getpwuid(getuid());
467   if (!pw)
468     return FALSE;
469
470   memset(filename, 0, sizeof(filename));
471   memset(file, 0, sizeof(file));
472   snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
473            sock->port);
474   snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
475            pw->pw_dir, file);
476
477   /* Check wheter this key already exists */
478   if (stat(filename, &st) < 0) {
479
480     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
481     silc_say(client, conn, "Received server %s public key", hostname);
482     silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
483     silc_say(client, conn, "%s", fingerprint);
484     silc_free(fingerprint);
485
486     /* Ask user to verify the key and save it */
487     if (silc_client_ask_yes_no(client, 
488        "Would you like to accept the key (y/n)? "))
489       {
490         /* Save the key for future checking */
491         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
492                                        SILC_PKCS_FILE_PEM);
493         return TRUE;
494       }
495   } else {
496     /* The key already exists, verify it. */
497     SilcPublicKey public_key;
498     unsigned char *encpk;
499     unsigned int encpk_len;
500
501     /* Load the key file */
502     if (!silc_pkcs_load_public_key(filename, &public_key, 
503                                    SILC_PKCS_FILE_PEM))
504       if (!silc_pkcs_load_public_key(filename, &public_key, 
505                                      SILC_PKCS_FILE_BIN)) {
506         fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
507         silc_say(client, conn, "Received server %s public key", hostname);
508         silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
509         silc_say(client, conn, "%s", fingerprint);
510         silc_free(fingerprint);
511         silc_say(client, conn, "Could not load your local copy of the server %s key",
512                  hostname);
513         if (silc_client_ask_yes_no(client, 
514            "Would you like to accept the key anyway (y/n)? "))
515           {
516             /* Save the key for future checking */
517             unlink(filename);
518             silc_pkcs_save_public_key_data(filename, pk, pk_len,
519                                            SILC_PKCS_FILE_PEM);
520             return TRUE;
521           }
522         
523         return FALSE;
524       }
525   
526     /* Encode the key data */
527     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
528     if (!encpk) {
529       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
530       silc_say(client, conn, "Received server %s public key", hostname);
531       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
532       silc_say(client, conn, "%s", fingerprint);
533       silc_free(fingerprint);
534       silc_say(client, conn, "Your local copy of the server %s key is malformed",
535                hostname);
536       if (silc_client_ask_yes_no(client, 
537          "Would you like to accept the key anyway (y/n)? "))
538         {
539           /* Save the key for future checking */
540           unlink(filename);
541           silc_pkcs_save_public_key_data(filename, pk, pk_len,
542                                          SILC_PKCS_FILE_PEM);
543           return TRUE;
544         }
545
546       return FALSE;
547     }
548
549     if (memcmp(encpk, pk, encpk_len)) {
550       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
551       silc_say(client, conn, "Received server %s public key", hostname);
552       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
553       silc_say(client, conn, "%s", fingerprint);
554       silc_free(fingerprint);
555       silc_say(client, conn, "Server %s key does not match with your local copy",
556                hostname);
557       silc_say(client, conn, "It is possible that the key has expired or changed");
558       silc_say(client, conn, "It is also possible that some one is performing "
559                        "man-in-the-middle attack");
560       
561       /* Ask user to verify the key and save it */
562       if (silc_client_ask_yes_no(client, 
563          "Would you like to accept the key anyway (y/n)? "))
564         {
565           /* Save the key for future checking */
566           unlink(filename);
567           silc_pkcs_save_public_key_data(filename, pk, pk_len,
568                                          SILC_PKCS_FILE_PEM);
569           return TRUE;
570         }
571
572       silc_say(client, conn, "Will not accept server %s key", hostname);
573       return FALSE;
574     }
575
576     /* Local copy matched */
577     return TRUE;
578   }
579
580   silc_say(client, conn, "Will not accept server %s key", hostname);
581   return FALSE;
582 }
583
584 /* Find authentication method and authentication data by hostname and
585    port. The hostname may be IP address as well. The found authentication
586    method and authentication data is returned to `auth_meth', `auth_data'
587    and `auth_data_len'. The function returns TRUE if authentication method
588    is found and FALSE if not. `conn' may be NULL. */
589
590 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
591                          char *hostname, unsigned short port,
592                          SilcProtocolAuthMeth *auth_meth,
593                          unsigned char **auth_data,
594                          unsigned int *auth_data_len)
595 {
596   SilcClientInternal app = (SilcClientInternal)client->application;
597
598   if (app->config->conns) {
599     SilcClientConfigSectionConnection *conn = NULL;
600
601     /* Check if we find a match from user configured connections */
602     conn = silc_client_config_find_connection(app->config,
603                                               hostname,
604                                               port);
605     if (conn) {
606       /* Match found. Use the configured authentication method */
607       *auth_meth = conn->auth_meth;
608
609       if (conn->auth_data) {
610         *auth_data = strdup(conn->auth_data);
611         *auth_data_len = strlen(conn->auth_data);
612       }
613
614       return TRUE;
615     }
616   }
617
618   return FALSE;
619 }
620
621 /* Notifies application that failure packet was received.  This is called
622    if there is some protocol active in the client.  The `protocol' is the
623    protocol context.  The `failure' is opaque pointer to the failure
624    indication.  Note, that the `failure' is protocol dependant and application
625    must explicitly cast it to correct type.  Usually `failure' is 32 bit
626    failure type (see protocol specs for all protocol failure types). */
627
628 void silc_failure(SilcClient client, SilcClientConnection conn, 
629                   SilcProtocol protocol, void *failure)
630 {
631   SilcClientInternal app = (SilcClientInternal)client->application;
632
633 }
634
635 /* Asks whether the user would like to perform the key agreement protocol.
636    This is called after we have received an key agreement packet or an
637    reply to our key agreement packet. This returns TRUE if the user wants
638    the library to perform the key agreement protocol and FALSE if it is not
639    desired (application may start it later by calling the function
640    silc_client_perform_key_agreement). */
641
642 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
643                        SilcClientEntry client_entry, char *hostname,
644                        int port,
645                        SilcKeyAgreementCallback *completion,
646                        void **context)
647 {
648
649   return FALSE;
650 }
651
652 /* SILC client operations */
653 SilcClientOperations ops = {
654   say:                  silc_say,
655   channel_message:      silc_channel_message,
656   private_message:      silc_private_message,
657   notify:               silc_notify,
658   command:              silc_command,
659   command_reply:        silc_command_reply,
660   connect:              silc_connect,
661   disconnect:           silc_disconnect,
662   get_auth_method:      silc_get_auth_method,
663   verify_server_key:    silc_verify_server_key,
664   ask_passphrase:       silc_ask_passphrase,
665   failure:              silc_failure,
666   key_agreement:        silc_key_agreement,
667 };