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