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