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