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