updates.
[silc.git] / apps / irssi / src / silc / core / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 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 "module.h"
22 #include "chat-protocols.h"
23 #include "args.h"
24
25 #include "chatnets.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
32
33 #include "signals.h"
34 #include "levels.h"
35 #include "settings.h"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
38 #include "fe-common/core/keyboard.h"
39 #include "fe-common/silc/module-formats.h"
40
41 static void 
42 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
43                                 SilcSocketType conn_type, unsigned char *pk, 
44                                 uint32 pk_len, SilcSKEPKType pk_type,
45                                 SilcVerifyPublicKey completion, void *context);
46
47 void silc_say(SilcClient client, SilcClientConnection conn,
48                      char *msg, ...)
49 {
50   SILC_SERVER_REC *server;
51   va_list va;
52   char *str;
53
54   server = conn == NULL ? NULL : conn->context;
55   
56   va_start(va, msg);
57   str = g_strdup_vprintf(msg, va);
58   printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
59   g_free(str);
60   va_end(va);
61 }
62
63 void silc_say_error(char *msg, ...)
64 {
65   va_list va;
66   char *str;
67
68   va_start(va, msg);
69   str = g_strdup_vprintf(msg, va);
70   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
71
72   g_free(str);
73   va_end(va);
74 }
75
76 /* Message for a channel. The `sender' is the nickname of the sender 
77    received in the packet. The `channel_name' is the name of the channel. */
78
79 void 
80 silc_channel_message(SilcClient client, SilcClientConnection conn,
81                      SilcClientEntry sender, SilcChannelEntry channel,
82                      SilcMessageFlags flags, char *msg)
83 {
84   SILC_SERVER_REC *server;
85   SILC_NICK_REC *nick;
86   SILC_CHANNEL_REC *chanrec;
87   
88   server = conn == NULL ? NULL : conn->context;
89   chanrec = silc_channel_find_entry(server, channel);
90   
91   nick = silc_nicklist_find(chanrec, sender);
92
93   if (flags & SILC_MESSAGE_FLAG_ACTION)
94     printformat_module("fe-common/silc", server, channel->channel_name,
95                        MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, msg);
96   else if (flags & SILC_MESSAGE_FLAG_NOTICE)
97     printformat_module("fe-common/silc", server, channel->channel_name,
98                        MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, msg);
99   else
100     signal_emit("message public", 6, server, msg,
101                 nick == NULL ? "[<unknown>]" : nick->nick,
102                 nick == NULL ? NULL : nick->host,
103                 chanrec->name, nick);
104 }
105
106 /* Private message to the client. The `sender' is the nickname of the
107    sender received in the packet. */
108
109 void 
110 silc_private_message(SilcClient client, SilcClientConnection conn,
111                      SilcClientEntry sender, SilcMessageFlags flags,
112                           char *msg)
113 {
114   SILC_SERVER_REC *server;
115   
116   server = conn == NULL ? NULL : conn->context;
117   signal_emit("message private", 4, server, msg,
118               sender->nickname ? sender->nickname : "[<unknown>]",
119               sender->username ? sender->username : NULL);
120 }
121
122 /* Notify message to the client. The notify arguments are sent in the
123    same order as servers sends them. The arguments are same as received
124    from the server except for ID's.  If ID is received application receives
125    the corresponding entry to the ID. For example, if Client ID is received
126    application receives SilcClientEntry.  Also, if the notify type is
127    for channel the channel entry is sent to application (even if server
128    does not send it). */
129
130 typedef struct {
131   int type;
132   const char *name;
133 } NOTIFY_REC;
134
135 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
136 static NOTIFY_REC notifies[] = {
137   { SILC_NOTIFY_TYPE_NONE,              NULL },
138   { SILC_NOTIFY_TYPE_INVITE,            "invite" },
139   { SILC_NOTIFY_TYPE_JOIN,              "join" },
140   { SILC_NOTIFY_TYPE_LEAVE,             "leave" },
141   { SILC_NOTIFY_TYPE_SIGNOFF,           "signoff" },
142   { SILC_NOTIFY_TYPE_TOPIC_SET,         "topic" },
143   { SILC_NOTIFY_TYPE_NICK_CHANGE,       "nick" },
144   { SILC_NOTIFY_TYPE_CMODE_CHANGE,      "cmode" },
145   { SILC_NOTIFY_TYPE_CUMODE_CHANGE,     "cumode" },
146   { SILC_NOTIFY_TYPE_MOTD,              "motd" },
147   { SILC_NOTIFY_TYPE_CHANNEL_CHANGE,    "channel_change" },
148   { SILC_NOTIFY_TYPE_SERVER_SIGNOFF,    "server_signoff" },
149   { SILC_NOTIFY_TYPE_KICKED,            "kick" },
150   { SILC_NOTIFY_TYPE_KILLED,            "kill" },
151   { SILC_NOTIFY_TYPE_UMODE_CHANGE,      "umode" },
152   { SILC_NOTIFY_TYPE_BAN,               "ban" },
153 };
154
155 void silc_notify(SilcClient client, SilcClientConnection conn,
156                         SilcNotifyType type, ...)
157 {
158   SILC_SERVER_REC *server;
159   va_list va;
160   
161   server = conn == NULL ? NULL : conn->context;
162   va_start(va, type);
163   
164   if (type == SILC_NOTIFY_TYPE_NONE) {
165     /* Some generic notice from server */
166     printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
167   } else if (type < MAX_NOTIFY) {
168     /* Send signal about the notify event */
169     char signal[50];
170     g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
171     signal_emit(signal, 2, server, va);
172   } else {
173     /* Unknown notify */
174     printtext(server, NULL, MSGLEVEL_CRAP, "Unknown notify type %d", type);
175   }
176
177   va_end(va);
178 }
179
180 /* Called to indicate that connection was either successfully established
181    or connecting failed.  This is also the first time application receives
182    the SilcClientConnection objecet which it should save somewhere. */
183
184 void 
185 silc_connect(SilcClient client, SilcClientConnection conn, int success)
186 {
187   SILC_SERVER_REC *server = conn->context;
188
189   if (success) {
190     server->connected = TRUE;
191     signal_emit("event connected", 1, server);
192   } else {
193     server->connection_lost = TRUE;
194     server->conn->context = NULL;
195     server_disconnect(SERVER(server));
196   }
197 }
198
199 /* Called to indicate that connection was disconnected to the server. */
200
201 void 
202 silc_disconnect(SilcClient client, SilcClientConnection conn)
203 {
204   SILC_SERVER_REC *server = conn->context;
205
206   server->conn->context = NULL;
207   server->conn = NULL;
208   server->connection_lost = TRUE;
209   server_disconnect(SERVER(server));
210 }
211
212 /* Command handler. This function is called always in the command function.
213    If error occurs it will be called as well. `conn' is the associated
214    client connection. `cmd_context' is the command context that was
215    originally sent to the command. `success' is FALSE if error occured
216    during command. `command' is the command being processed. It must be
217    noted that this is not reply from server. This is merely called just
218    after application has called the command. Just to tell application
219    that the command really was processed. */
220
221 void 
222 silc_command(SilcClient client, SilcClientConnection conn, 
223              SilcClientCommandContext cmd_context, int success,
224              SilcCommand command)
225 {
226 }
227
228 /* Client info resolving callback when JOIN command reply is received.
229    This will cache all users on the channel. */
230
231 void silc_client_join_get_users(SilcClient client,
232                                 SilcClientConnection conn,
233                                 SilcClientEntry *clients,
234                                 uint32 clients_count,
235                                 void *context)
236 {
237   SilcChannelEntry channel = (SilcChannelEntry)context;
238   SilcChannelUser chu;
239   SILC_SERVER_REC *server = conn->context;
240   SILC_CHANNEL_REC *chanrec;
241   SilcClientEntry founder = NULL;
242   NICK_REC *ownnick;
243
244   if (!clients)
245     return;
246
247   chanrec = silc_channel_find(server, channel->channel_name);
248   if (chanrec == NULL)
249     return;
250
251   silc_list_start(channel->clients);
252   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
253     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
254       founder = chu->client;
255     silc_nicklist_insert(chanrec, chu, FALSE);
256   }
257
258   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
259   nicklist_set_own(CHANNEL(chanrec), ownnick);
260   signal_emit("channel joined", 1, chanrec);
261
262   if (chanrec->topic)
263     printformat_module("fe-common/silc", server, channel->channel_name,
264                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
265                        channel->channel_name, chanrec->topic);
266
267   fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
268
269   if (founder) {
270     if (founder == conn->local_entry)
271       printformat_module("fe-common/silc", 
272                          server, channel->channel_name, MSGLEVEL_CRAP,
273                          SILCTXT_CHANNEL_FOUNDER_YOU,
274                          channel->channel_name);
275     else
276       printformat_module("fe-common/silc", 
277                          server, channel->channel_name, MSGLEVEL_CRAP,
278                          SILCTXT_CHANNEL_FOUNDER,
279                          channel->channel_name, founder->nickname);
280   }
281 }
282
283 /* Command reply handler. This function is called always in the command reply
284    function. If error occurs it will be called as well. Normal scenario
285    is that it will be called after the received command data has been parsed
286    and processed. The function is used to pass the received command data to
287    the application. 
288
289    `conn' is the associated client connection. `cmd_payload' is the command
290    payload data received from server and it can be ignored. It is provided
291    if the application would like to re-parse the received command data,
292    however, it must be noted that the data is parsed already by the library
293    thus the payload can be ignored. `success' is FALSE if error occured.
294    In this case arguments are not sent to the application. `command' is the
295    command reply being processed. The function has variable argument list
296    and each command defines the number and type of arguments it passes to the
297    application (on error they are not sent). */
298
299 void 
300 silc_command_reply(SilcClient client, SilcClientConnection conn,
301                    SilcCommandPayload cmd_payload, int success,
302                    SilcCommand command, SilcCommandStatus status, ...)
303
304 {
305   SILC_SERVER_REC *server = conn->context;
306   SILC_CHANNEL_REC *chanrec;
307   va_list vp;
308
309   va_start(vp, status);
310
311   switch(command) {
312   case SILC_COMMAND_WHOIS:
313     {
314       char buf[1024], *nickname, *username, *realname;
315       uint32 idle, mode;
316       SilcBuffer channels;
317       
318       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
319           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
320         char *tmp;
321         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
322                                          3, NULL);
323         if (tmp)
324           silc_say_error("%s: %s", tmp, 
325                          silc_client_command_status_message(status));
326         else
327           silc_say_error("%s", silc_client_command_status_message(status));
328         break;
329       }
330       
331       if (!success)
332         return;
333       
334       (void)va_arg(vp, SilcClientEntry);
335       nickname = va_arg(vp, char *);
336       username = va_arg(vp, char *);
337       realname = va_arg(vp, char *);
338       channels = va_arg(vp, SilcBuffer);
339       mode = va_arg(vp, uint32);
340       idle = va_arg(vp, uint32);
341       
342       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
343                          SILCTXT_WHOIS_USERINFO, nickname, username, 
344                          realname);
345
346       if (channels) {
347         SilcDList list = silc_channel_payload_parse_list(channels);
348         if (list) {
349           SilcChannelPayload entry;
350           memset(buf, 0, sizeof(buf));
351           silc_dlist_start(list);
352           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
353             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
354             uint32 name_len;
355             char *name = silc_channel_get_name(entry, &name_len);
356             
357             if (m)
358               strncat(buf, m, strlen(m));
359             strncat(buf, name, name_len);
360             strncat(buf, " ", 1);
361             silc_free(m);
362           }
363
364           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
365                              SILCTXT_WHOIS_CHANNELS, buf);
366           silc_channel_payload_list_free(list);
367         }
368       }
369       
370       if (mode) {
371         memset(buf, 0, sizeof(buf));
372
373         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
374             (mode & SILC_UMODE_ROUTER_OPERATOR)) {
375           strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
376                  "Server Operator " :
377                  (mode & SILC_UMODE_ROUTER_OPERATOR) ?
378                  "SILC Operator " : "[Unknown mode] ");
379         }
380         if (mode & SILC_UMODE_GONE)
381           strcat(buf, "away");
382
383         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
384                            SILCTXT_WHOIS_MODES, buf);
385       }
386       
387       if (idle && nickname) {
388         memset(buf, 0, sizeof(buf));
389         snprintf(buf, sizeof(buf) - 1, "%lu %s",
390                  idle > 60 ? (idle / 60) : idle,
391                  idle > 60 ? "minutes" : "seconds");
392
393         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
394                            SILCTXT_WHOIS_IDLE, buf);
395       }
396     }
397     break;
398     
399   case SILC_COMMAND_WHOWAS:
400     {
401       char *nickname, *username, *realname;
402       
403       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
404           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
405         char *tmp;
406         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
407                                          3, NULL);
408         if (tmp)
409           silc_say_error("%s: %s", tmp, 
410                          silc_client_command_status_message(status));
411         else
412           silc_say_error("%s", silc_client_command_status_message(status));
413         break;
414       }
415       
416       if (!success)
417         return;
418       
419       (void)va_arg(vp, SilcClientEntry);
420       nickname = va_arg(vp, char *);
421       username = va_arg(vp, char *);
422       realname = va_arg(vp, char *);
423       
424       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
425                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
426                          realname ? realname : "");
427     }
428     break;
429     
430   case SILC_COMMAND_INVITE:
431     {
432       SilcChannelEntry channel;
433       char *invite_list;
434       
435       if (!success)
436         return;
437       
438       /* XXX should use irssi routines */
439       
440       channel = va_arg(vp, SilcChannelEntry);
441       invite_list = va_arg(vp, char *);
442       
443       if (invite_list)
444         silc_say(client, conn, "%s invite list: %s", channel->channel_name,
445                  invite_list);
446       else
447         silc_say(client, conn, "%s invite list not set", 
448                  channel->channel_name);
449     }
450     break;
451
452   case SILC_COMMAND_JOIN: 
453     {
454       char *channel, *mode, *topic;
455       uint32 modei;
456       SilcChannelEntry channel_entry;
457       SilcBuffer client_id_list;
458       uint32 list_count;
459
460       channel = va_arg(vp, char *);
461       channel_entry = va_arg(vp, SilcChannelEntry);
462       modei = va_arg(vp, uint32);
463       (void)va_arg(vp, uint32);
464       (void)va_arg(vp, unsigned char *);
465       (void)va_arg(vp, unsigned char *);
466       (void)va_arg(vp, unsigned char *);
467       topic = va_arg(vp, char *);
468       (void)va_arg(vp, unsigned char *);
469       list_count = va_arg(vp, uint32);
470       client_id_list = va_arg(vp, SilcBuffer);
471
472       if (!success)
473         return;
474
475       chanrec = silc_channel_find(server, channel);
476       if (chanrec != NULL && !success)
477         channel_destroy(CHANNEL(chanrec));
478       else if (chanrec == NULL && success)
479         chanrec = silc_channel_create(server, channel, TRUE);
480       
481       if (topic) {
482         g_free_not_null(chanrec->topic);
483         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
484         signal_emit("channel topic changed", 1, chanrec);
485       }
486
487       mode = silc_client_chmode(modei, channel_entry);
488       g_free_not_null(chanrec->mode);
489       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
490       signal_emit("channel mode changed", 1, chanrec);
491
492       /* Resolve the client information */
493       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
494                                       silc_client_join_get_users, 
495                                       channel_entry);
496       break;
497     }
498
499   case SILC_COMMAND_NICK: 
500     {
501       SilcClientEntry client = va_arg(vp, SilcClientEntry);
502       char *old;
503       
504       if (!success)
505         return;
506
507       old = g_strdup(server->nick);
508       server_change_nick(SERVER(server), client->nickname);
509       nicklist_rename_unique(SERVER(server),
510                              server->conn->local_entry, server->nick,
511                              client, client->nickname);
512       
513       signal_emit("message own_nick", 4, server, server->nick, old, "");
514       g_free(old);
515       break;
516     }
517     
518   case SILC_COMMAND_LIST:
519     {
520       char *topic, *name;
521       int usercount;
522       char users[20];
523       
524       if (!success)
525         return;
526       
527       (void)va_arg(vp, SilcChannelEntry);
528       name = va_arg(vp, char *);
529       topic = va_arg(vp, char *);
530       usercount = va_arg(vp, int);
531       
532       if (status == SILC_STATUS_LIST_START ||
533           status == SILC_STATUS_OK)
534         printformat_module("fe-common/silc", server, NULL,
535                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
536
537       snprintf(users, sizeof(users) - 1, "%d", usercount);
538       printformat_module("fe-common/silc", server, NULL,
539                          MSGLEVEL_CRAP, SILCTXT_LIST,
540                          name, users, topic ? topic : "");
541     }
542     break;
543     
544   case SILC_COMMAND_UMODE:
545     {
546       uint32 mode;
547       
548       if (!success)
549         return;
550       
551       mode = va_arg(vp, uint32);
552       
553       /* XXX todo */
554     }
555     break;
556     
557   case SILC_COMMAND_OPER:
558     printformat_module("fe-common/silc", server, NULL,
559                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
560     break;
561     
562   case SILC_COMMAND_SILCOPER:
563     printformat_module("fe-common/silc", server, NULL,
564                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
565     break;
566     
567   case SILC_COMMAND_USERS: 
568     {
569       SilcChannelEntry channel;
570       SilcChannelUser chu;
571       
572       if (!success)
573         return;
574       
575       channel = va_arg(vp, SilcChannelEntry);
576       
577       printformat_module("fe-common/silc", server, channel->channel_name,
578                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
579                          channel->channel_name);
580
581       silc_list_start(channel->clients);
582       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
583         SilcClientEntry e = chu->client;
584         char stat[5], *mode;
585         
586         memset(stat, 0, sizeof(stat));
587         mode = silc_client_chumode_char(chu->mode);
588         if (e->mode & SILC_UMODE_GONE)
589           strcat(stat, "G");
590         else
591           strcat(stat, "H");
592         if (mode)
593           strcat(stat, mode);
594
595         printformat_module("fe-common/silc", server, channel->channel_name,
596                            MSGLEVEL_CRAP, SILCTXT_USERS,
597                            e->nickname, stat, e->username, 
598                            e->realname ? e->realname : "");
599         if (mode)
600           silc_free(mode);
601       }
602     }
603     break;
604
605   case SILC_COMMAND_BAN:
606     {
607       SilcChannelEntry channel;
608       char *ban_list;
609       
610       if (!success)
611         return;
612       
613       /* XXX should use irssi routines */
614           
615       channel = va_arg(vp, SilcChannelEntry);
616       ban_list = va_arg(vp, char *);
617       
618       if (ban_list)
619         silc_say(client, conn, "%s ban list: %s", channel->channel_name,
620                  ban_list);
621       else
622         silc_say(client, conn, "%s ban list not set", channel->channel_name);
623     }
624     break;
625     
626   case SILC_COMMAND_GETKEY:
627     {
628       SilcIdType id_type;
629       void *entry;
630       SilcPublicKey public_key;
631       unsigned char *pk;
632       uint32 pk_len;
633       
634       id_type = va_arg(vp, uint32);
635       entry = va_arg(vp, void *);
636       public_key = va_arg(vp, SilcPublicKey);
637       
638       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
639       
640       if (id_type == SILC_ID_CLIENT) {
641         silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
642                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
643                                         NULL, NULL);
644       }
645       
646       silc_free(pk);
647     }
648     
649   case SILC_COMMAND_TOPIC:
650     {
651       SilcChannelEntry channel;
652       char *topic;
653       
654       if (!success)
655         return;
656       
657       channel = va_arg(vp, SilcChannelEntry);
658       topic = va_arg(vp, char *);
659       
660       if (topic) {
661         chanrec = silc_channel_find_entry(server, channel);
662         if (chanrec) {
663           g_free_not_null(chanrec->topic);
664           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
665           signal_emit("channel topic changed", 1, chanrec);
666         }
667         printformat_module("fe-common/silc", server, channel->channel_name,
668                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
669                            channel->channel_name, topic);
670       }
671     }
672     break;
673   }
674
675   va_end(vp);
676 }
677
678 /* Internal routine to verify public key. If the `completion' is provided
679    it will be called to indicate whether public was verified or not. */
680
681 typedef struct {
682   SilcClient client;
683   SilcClientConnection conn;
684   char *filename;
685   char *entity;
686   unsigned char *pk;
687   uint32 pk_len;
688   SilcSKEPKType pk_type;
689   SilcVerifyPublicKey completion;
690   void *context;
691 } *PublicKeyVerify;
692
693 static void verify_public_key_completion(const char *line, void *context)
694 {
695   PublicKeyVerify verify = (PublicKeyVerify)context;
696
697   if (line[0] == 'Y' || line[0] == 'y') {
698     /* Call the completion */
699     if (verify->completion)
700       verify->completion(TRUE, verify->context);
701
702     /* Save the key for future checking */
703     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
704                                    verify->pk_len, SILC_PKCS_FILE_PEM);
705   } else {
706     /* Call the completion */
707     if (verify->completion)
708       verify->completion(FALSE, verify->context);
709
710     silc_say(verify->client, 
711              verify->conn, "Will not accept the %s key", verify->entity);
712   }
713
714   silc_free(verify->filename);
715   silc_free(verify->entity);
716   silc_free(verify);
717 }
718
719 static void 
720 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
721                                 SilcSocketType conn_type, unsigned char *pk, 
722                                 uint32 pk_len, SilcSKEPKType pk_type,
723                                 SilcVerifyPublicKey completion, void *context)
724 {
725   int i;
726   char file[256], filename[256], *fingerprint;
727   struct passwd *pw;
728   struct stat st;
729   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
730                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
731                   "server" : "client");
732   PublicKeyVerify verify;
733
734   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
735     silc_say(client, conn, "We don't support %s public key type %d", 
736              entity, pk_type);
737     if (completion)
738       completion(FALSE, context);
739     return;
740   }
741
742   pw = getpwuid(getuid());
743   if (!pw) {
744     if (completion)
745       completion(FALSE, context);
746     return;
747   }
748
749   memset(filename, 0, sizeof(filename));
750   memset(file, 0, sizeof(file));
751
752   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
753       conn_type == SILC_SOCKET_TYPE_ROUTER) {
754     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
755              conn->sock->hostname, conn->sock->port);
756     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
757              pw->pw_dir, entity, file);
758   } else {
759     /* Replace all whitespaces with `_'. */
760     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
761     for (i = 0; i < strlen(fingerprint); i++)
762       if (fingerprint[i] == ' ')
763         fingerprint[i] = '_';
764     
765     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
766     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
767              pw->pw_dir, entity, file);
768     silc_free(fingerprint);
769   }
770
771   /* Take fingerprint of the public key */
772   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
773
774   verify = silc_calloc(1, sizeof(*verify));
775   verify->client = client;
776   verify->conn = conn;
777   verify->filename = strdup(filename);
778   verify->entity = strdup(entity);
779   verify->pk = pk;
780   verify->pk_len = pk_len;
781   verify->pk_type = pk_type;
782   verify->completion = completion;
783   verify->context = context;
784
785   /* Check whether this key already exists */
786   if (stat(filename, &st) < 0) {
787     /* Key does not exist, ask user to verify the key and save it */
788
789     silc_say(client, conn, "Received %s public key", entity);
790     silc_say(client, conn, "Fingerprint for the %s key is", entity);
791     silc_say(client, conn, "%s", fingerprint);
792
793     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
794                             "Would you like to accept the key (y/n)? ", 0,
795                             verify);
796     silc_free(fingerprint);
797     return;
798   } else {
799     /* The key already exists, verify it. */
800     SilcPublicKey public_key;
801     unsigned char *encpk;
802     uint32 encpk_len;
803
804     /* Load the key file */
805     if (!silc_pkcs_load_public_key(filename, &public_key, 
806                                    SILC_PKCS_FILE_PEM))
807       if (!silc_pkcs_load_public_key(filename, &public_key, 
808                                      SILC_PKCS_FILE_BIN)) {
809         silc_say(client, conn, "Received %s public key", entity);
810         silc_say(client, conn, "Fingerprint for the %s key is", entity);
811         silc_say(client, conn, "%s", fingerprint);
812         silc_say(client, conn, "Could not load your local copy of the %s key",
813                  entity);
814         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
815                                 "Would you like to accept the key "
816                                 "anyway (y/n)? ", 0,
817                                 verify);
818         silc_free(fingerprint);
819         return;
820       }
821   
822     /* Encode the key data */
823     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
824     if (!encpk) {
825       silc_say(client, conn, "Received %s public key", entity);
826       silc_say(client, conn, "Fingerprint for the %s key is", entity);
827       silc_say(client, conn, "%s", fingerprint);
828       silc_say(client, conn, "Your local copy of the %s key is malformed",
829                entity);
830       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
831                               "Would you like to accept the key "
832                               "anyway (y/n)? ", 0,
833                               verify);
834       silc_free(fingerprint);
835       return;
836     }
837
838     /* Compare the keys */
839     if (memcmp(encpk, pk, encpk_len)) {
840       silc_say(client, conn, "Received %s public key", entity);
841       silc_say(client, conn, "Fingerprint for the %s key is", entity);
842       silc_say(client, conn, "%s", fingerprint);
843       silc_say(client, conn, "%s key does not match with your local copy",
844                entity);
845       silc_say(client, conn, 
846                "It is possible that the key has expired or changed");
847       silc_say(client, conn, "It is also possible that some one is performing "
848                        "man-in-the-middle attack");
849
850       /* Ask user to verify the key and save it */
851       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
852                               "Would you like to accept the key "
853                               "anyway (y/n)? ", 0,
854                               verify);
855       silc_free(fingerprint);
856       return;
857     }
858
859     /* Local copy matched */
860     if (completion)
861       completion(TRUE, context);
862     silc_free(fingerprint);
863   }
864 }
865
866 /* Verifies received public key. The `conn_type' indicates which entity
867    (server, client etc.) has sent the public key. If user decides to trust
868    the key may be saved as trusted public key for later use. The 
869    `completion' must be called after the public key has been verified. */
870
871 void 
872 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
873                        SilcSocketType conn_type, unsigned char *pk, 
874                        uint32 pk_len, SilcSKEPKType pk_type,
875                        SilcVerifyPublicKey completion, void *context)
876 {
877   silc_verify_public_key_internal(client, conn, conn_type, pk,
878                                   pk_len, pk_type,
879                                   completion, context);
880 }
881
882 /* Asks passphrase from user on the input line. */
883
884 typedef struct {
885   SilcAskPassphrase completion;
886   void *context;
887 } *AskPassphrase;
888
889 void ask_passphrase_completion(const char *passphrase, void *context)
890 {
891   AskPassphrase p = (AskPassphrase)context;
892   p->completion((unsigned char *)passphrase, 
893                 passphrase ? strlen(passphrase) : 0, p->context);
894   silc_free(p);
895 }
896
897 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
898                                 SilcAskPassphrase completion, void *context)
899 {
900   AskPassphrase p = silc_calloc(1, sizeof(*p));
901   p->completion = completion;
902   p->context = context;
903
904   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
905                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
906 }
907
908 /* Find authentication method and authentication data by hostname and
909    port. The hostname may be IP address as well. The found authentication
910    method and authentication data is returned to `auth_meth', `auth_data'
911    and `auth_data_len'. The function returns TRUE if authentication method
912    is found and FALSE if not. `conn' may be NULL. */
913
914 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
915                          char *hostname, uint16 port,
916                          SilcProtocolAuthMeth *auth_meth,
917                          unsigned char **auth_data,
918                          uint32 *auth_data_len)
919 {
920
921   /* XXX must resolve from configuration whether this connection has
922      any specific authentication data */
923
924   *auth_meth = SILC_AUTH_NONE;
925   *auth_data = NULL;
926   *auth_data_len = 0;
927
928   return TRUE;
929 }
930
931 /* Notifies application that failure packet was received.  This is called
932    if there is some protocol active in the client.  The `protocol' is the
933    protocol context.  The `failure' is opaque pointer to the failure
934    indication.  Note, that the `failure' is protocol dependant and application
935    must explicitly cast it to correct type.  Usually `failure' is 32 bit
936    failure type (see protocol specs for all protocol failure types). */
937
938 void 
939 silc_failure(SilcClient client, SilcClientConnection conn, 
940              SilcProtocol protocol, void *failure)
941 {
942   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
943     SilcSKEStatus status = (SilcSKEStatus)failure;
944     
945     if (status == SILC_SKE_STATUS_BAD_VERSION)
946       silc_say_error("You are running incompatible client version (it may be "
947                      "too old or too new)");
948     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
949       silc_say_error("Server does not support your public key type");
950     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
951       silc_say_error("Server does not support one of your proposed KE group");
952     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
953       silc_say_error("Server does not support one of your proposed cipher");
954     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
955       silc_say_error("Server does not support one of your proposed PKCS");
956     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
957       silc_say_error("Server does not support one of your proposed "
958                      "hash function");
959     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
960       silc_say_error("Server does not support one of your proposed HMAC");
961     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
962       silc_say_error("Incorrect signature");
963   }
964
965   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
966     uint32 err = (uint32)failure;
967
968     if (err == SILC_AUTH_FAILED)
969       silc_say(client, conn, "Authentication failed");
970   }
971 }
972
973 /* Asks whether the user would like to perform the key agreement protocol.
974    This is called after we have received an key agreement packet or an
975    reply to our key agreement packet. This returns TRUE if the user wants
976    the library to perform the key agreement protocol and FALSE if it is not
977    desired (application may start it later by calling the function
978    silc_client_perform_key_agreement). */
979
980 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
981                        SilcClientEntry client_entry, char *hostname,
982                        int port,
983                        SilcKeyAgreementCallback *completion,
984                        void **context)
985 {
986   char portstr[6];
987
988   /* We will just display the info on the screen and return FALSE and user
989      will have to start the key agreement with a command. */
990
991   if (hostname) 
992     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
993
994   if (!hostname)
995     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
996                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
997   else
998     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
999                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1000                        client_entry->nickname, hostname, portstr);
1001
1002   *completion = NULL;
1003   *context = NULL;
1004
1005   return FALSE;
1006 }
1007
1008 /* SILC client operations */
1009 SilcClientOperations ops = {
1010   silc_say,
1011   silc_channel_message,
1012   silc_private_message,
1013   silc_notify,
1014   silc_command,
1015   silc_command_reply,
1016   silc_connect,
1017   silc_disconnect,
1018   silc_get_auth_method,
1019   silc_verify_public_key,
1020   silc_ask_passphrase,
1021   silc_failure,
1022   silc_key_agreement,
1023 };