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