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