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