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, 
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 }
227
228 /* Client info resolving callback when JOIN command reply is received.
229    This will cache all users on the channel. */
230
231 static 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       channel = va_arg(vp, SilcChannelEntry);
439       invite_list = va_arg(vp, char *);
440       
441       if (invite_list)
442         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
443                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
444                            invite_list);
445       else
446         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
447                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
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       char users[20];
523       
524       if (!success)
525         return;
526       
527       (void)va_arg(vp, SilcChannelEntry);
528       name = va_arg(vp, char *);
529       topic = va_arg(vp, char *);
530       usercount = va_arg(vp, int);
531       
532       if (status == SILC_STATUS_LIST_START ||
533           status == SILC_STATUS_OK)
534         printformat_module("fe-common/silc", server, NULL,
535                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
536
537       snprintf(users, sizeof(users) - 1, "%d", usercount);
538       printformat_module("fe-common/silc", server, NULL,
539                          MSGLEVEL_CRAP, SILCTXT_LIST,
540                          name, users, topic ? topic : "");
541     }
542     break;
543     
544   case SILC_COMMAND_UMODE:
545     {
546       uint32 mode;
547       
548       if (!success)
549         return;
550       
551       mode = va_arg(vp, uint32);
552       
553       if (mode & SILC_UMODE_SERVER_OPERATOR)
554         printformat_module("fe-common/silc", server, NULL,
555                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
556
557       if (mode & SILC_UMODE_ROUTER_OPERATOR)
558         printformat_module("fe-common/silc", server, NULL,
559                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
560     }
561     break;
562     
563   case SILC_COMMAND_OPER:
564     if (!success)
565       return;
566
567     printformat_module("fe-common/silc", server, NULL,
568                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
569     break;
570     
571   case SILC_COMMAND_SILCOPER:
572     if (!success)
573       return;
574
575     printformat_module("fe-common/silc", server, NULL,
576                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
577     break;
578     
579   case SILC_COMMAND_USERS: 
580     {
581       SilcChannelEntry channel;
582       SilcChannelUser chu;
583       
584       if (!success)
585         return;
586       
587       channel = va_arg(vp, SilcChannelEntry);
588       
589       printformat_module("fe-common/silc", server, channel->channel_name,
590                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
591                          channel->channel_name);
592
593       silc_list_start(channel->clients);
594       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
595         SilcClientEntry e = chu->client;
596         char stat[5], *mode;
597         
598         memset(stat, 0, sizeof(stat));
599         mode = silc_client_chumode_char(chu->mode);
600         if (e->mode & SILC_UMODE_GONE)
601           strcat(stat, "G");
602         else
603           strcat(stat, "H");
604         if (mode)
605           strcat(stat, mode);
606
607         printformat_module("fe-common/silc", server, channel->channel_name,
608                            MSGLEVEL_CRAP, SILCTXT_USERS,
609                            e->nickname, stat, e->username, 
610                            e->realname ? e->realname : "");
611         if (mode)
612           silc_free(mode);
613       }
614     }
615     break;
616
617   case SILC_COMMAND_BAN:
618     {
619       SilcChannelEntry channel;
620       char *ban_list;
621       
622       if (!success)
623         return;
624       
625       channel = va_arg(vp, SilcChannelEntry);
626       ban_list = va_arg(vp, char *);
627       
628       if (ban_list)
629         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
630                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
631                            ban_list);
632       else
633         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
634                            SILCTXT_CHANNEL_NO_BAN_LIST, 
635                            channel->channel_name);
636     }
637     break;
638     
639   case SILC_COMMAND_GETKEY:
640     {
641       SilcIdType id_type;
642       void *entry;
643       SilcPublicKey public_key;
644       unsigned char *pk;
645       uint32 pk_len;
646       
647       id_type = va_arg(vp, uint32);
648       entry = va_arg(vp, void *);
649       public_key = va_arg(vp, SilcPublicKey);
650       
651       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
652       
653       if (id_type == SILC_ID_CLIENT)
654         silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
655                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
656                                         NULL, NULL);
657       silc_free(pk);
658     }
659     break;
660     
661   case SILC_COMMAND_TOPIC:
662     {
663       SilcChannelEntry channel;
664       char *topic;
665       
666       if (!success)
667         return;
668       
669       channel = va_arg(vp, SilcChannelEntry);
670       topic = va_arg(vp, char *);
671       
672       if (topic) {
673         chanrec = silc_channel_find_entry(server, channel);
674         if (chanrec) {
675           g_free_not_null(chanrec->topic);
676           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
677           signal_emit("channel topic changed", 1, chanrec);
678         }
679         printformat_module("fe-common/silc", server, channel->channel_name,
680                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
681                            channel->channel_name, topic);
682       } else {
683         printformat_module("fe-common/silc", server, channel->channel_name,
684                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
685                            channel->channel_name);
686       }
687     }
688     break;
689
690   }
691
692   va_end(vp);
693 }
694
695 /* Internal routine to verify public key. If the `completion' is provided
696    it will be called to indicate whether public was verified or not. */
697
698 typedef struct {
699   SilcClient client;
700   SilcClientConnection conn;
701   char *filename;
702   char *entity;
703   unsigned char *pk;
704   uint32 pk_len;
705   SilcSKEPKType pk_type;
706   SilcVerifyPublicKey completion;
707   void *context;
708 } *PublicKeyVerify;
709
710 static void verify_public_key_completion(const char *line, void *context)
711 {
712   PublicKeyVerify verify = (PublicKeyVerify)context;
713
714   if (line[0] == 'Y' || line[0] == 'y') {
715     /* Call the completion */
716     if (verify->completion)
717       verify->completion(TRUE, verify->context);
718
719     /* Save the key for future checking */
720     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
721                                    verify->pk_len, SILC_PKCS_FILE_PEM);
722   } else {
723     /* Call the completion */
724     if (verify->completion)
725       verify->completion(FALSE, verify->context);
726
727     printformat_module("fe-common/silc", NULL, NULL,
728                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
729   }
730
731   silc_free(verify->filename);
732   silc_free(verify->entity);
733   silc_free(verify->pk);
734   silc_free(verify);
735 }
736
737 static void 
738 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
739                                 SilcSocketType conn_type, unsigned char *pk, 
740                                 uint32 pk_len, SilcSKEPKType pk_type,
741                                 SilcVerifyPublicKey completion, void *context)
742 {
743   int i;
744   char file[256], filename[256], *fingerprint, *format;
745   struct passwd *pw;
746   struct stat st;
747   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
748                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
749                   "server" : "client");
750   PublicKeyVerify verify;
751
752   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
753     printformat_module("fe-common/silc", NULL, NULL,
754                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
755                        entity, pk_type);
756     if (completion)
757       completion(FALSE, context);
758     return;
759   }
760
761   pw = getpwuid(getuid());
762   if (!pw) {
763     if (completion)
764       completion(FALSE, context);
765     return;
766   }
767
768   memset(filename, 0, sizeof(filename));
769   memset(file, 0, sizeof(file));
770
771   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
772       conn_type == SILC_SOCKET_TYPE_ROUTER) {
773     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
774              conn->sock->hostname, conn->sock->port);
775     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
776              pw->pw_dir, entity, file);
777   } else {
778     /* Replace all whitespaces with `_'. */
779     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
780     for (i = 0; i < strlen(fingerprint); i++)
781       if (fingerprint[i] == ' ')
782         fingerprint[i] = '_';
783     
784     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
785     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
786              pw->pw_dir, entity, file);
787     silc_free(fingerprint);
788   }
789
790   /* Take fingerprint of the public key */
791   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
792
793   verify = silc_calloc(1, sizeof(*verify));
794   verify->client = client;
795   verify->conn = conn;
796   verify->filename = strdup(filename);
797   verify->entity = strdup(entity);
798   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
799   memcpy(verify->pk, pk, pk_len);
800   verify->pk_len = pk_len;
801   verify->pk_type = pk_type;
802   verify->completion = completion;
803   verify->context = context;
804
805   /* Check whether this key already exists */
806   if (stat(filename, &st) < 0) {
807     /* Key does not exist, ask user to verify the key and save it */
808
809     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
810                        SILCTXT_PUBKEY_RECEIVED, entity);
811     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
812                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
813     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
814                              SILCTXT_PUBKEY_ACCEPT);
815     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
816                             format, 0, verify);
817     g_free(format);
818     silc_free(fingerprint);
819     return;
820   } else {
821     /* The key already exists, verify it. */
822     SilcPublicKey public_key;
823     unsigned char *encpk;
824     uint32 encpk_len;
825
826     /* Load the key file */
827     if (!silc_pkcs_load_public_key(filename, &public_key, 
828                                    SILC_PKCS_FILE_PEM))
829       if (!silc_pkcs_load_public_key(filename, &public_key, 
830                                      SILC_PKCS_FILE_BIN)) {
831         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
832                            SILCTXT_PUBKEY_RECEIVED, entity);
833         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
834                            SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
835         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
836                            SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
837         format = format_get_text("fe-common/silc", NULL, NULL, NULL,
838                                  SILCTXT_PUBKEY_ACCEPT_ANYWAY);
839         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
840                                 format, 0, verify);
841         g_free(format);
842         silc_free(fingerprint);
843         return;
844       }
845   
846     /* Encode the key data */
847     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
848     if (!encpk) {
849       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
850                          SILCTXT_PUBKEY_RECEIVED, entity);
851       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
852                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
853       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
854                          SILCTXT_PUBKEY_MALFORMED, entity);
855       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
856                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
857       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
858                               format, 0, verify);
859       g_free(format);
860       silc_free(fingerprint);
861       return;
862     }
863
864     /* Compare the keys */
865     if (memcmp(encpk, pk, encpk_len)) {
866       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
867                          SILCTXT_PUBKEY_RECEIVED, entity);
868       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
869                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
870       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
871                          SILCTXT_PUBKEY_NO_MATCH, entity);
872       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
873                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
874       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
875                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
876
877       /* Ask user to verify the key and save it */
878       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
879                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
880       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
881                               format, 0, verify);
882       g_free(format);
883       silc_free(fingerprint);
884       return;
885     }
886
887     /* Local copy matched */
888     if (completion)
889       completion(TRUE, context);
890     silc_free(fingerprint);
891   }
892 }
893
894 /* Verifies received public key. The `conn_type' indicates which entity
895    (server, client etc.) has sent the public key. If user decides to trust
896    the key may be saved as trusted public key for later use. The 
897    `completion' must be called after the public key has been verified. */
898
899 void 
900 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
901                        SilcSocketType conn_type, unsigned char *pk, 
902                        uint32 pk_len, SilcSKEPKType pk_type,
903                        SilcVerifyPublicKey completion, void *context)
904 {
905   silc_verify_public_key_internal(client, conn, conn_type, pk,
906                                   pk_len, pk_type,
907                                   completion, context);
908 }
909
910 /* Asks passphrase from user on the input line. */
911
912 typedef struct {
913   SilcAskPassphrase completion;
914   void *context;
915 } *AskPassphrase;
916
917 void ask_passphrase_completion(const char *passphrase, void *context)
918 {
919   AskPassphrase p = (AskPassphrase)context;
920   p->completion((unsigned char *)passphrase, 
921                 passphrase ? strlen(passphrase) : 0, p->context);
922   silc_free(p);
923 }
924
925 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
926                                 SilcAskPassphrase completion, void *context)
927 {
928   AskPassphrase p = silc_calloc(1, sizeof(*p));
929   p->completion = completion;
930   p->context = context;
931
932   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
933                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
934 }
935
936 /* Find authentication method and authentication data by hostname and
937    port. The hostname may be IP address as well. The found authentication
938    method and authentication data is returned to `auth_meth', `auth_data'
939    and `auth_data_len'. The function returns TRUE if authentication method
940    is found and FALSE if not. `conn' may be NULL. */
941
942 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
943                          char *hostname, uint16 port,
944                          SilcProtocolAuthMeth *auth_meth,
945                          unsigned char **auth_data,
946                          uint32 *auth_data_len)
947 {
948   bool ret = TRUE;
949   SILC_SERVER_REC *server = conn ? conn->context : NULL;
950
951   /* XXX must resolve from configuration whether this connection has
952      any specific authentication data */
953
954   *auth_meth = SILC_AUTH_NONE;
955   *auth_data = NULL;
956   *auth_data_len = 0;
957
958   if (ret == FALSE) {
959     printformat_module("fe-common/silc", server, NULL,
960                        MSGLEVEL_MODES, SILCTXT_AUTH_METH_UNRESOLVED);
961   }
962
963   return ret;
964 }
965
966 /* Notifies application that failure packet was received.  This is called
967    if there is some protocol active in the client.  The `protocol' is the
968    protocol context.  The `failure' is opaque pointer to the failure
969    indication.  Note, that the `failure' is protocol dependant and application
970    must explicitly cast it to correct type.  Usually `failure' is 32 bit
971    failure type (see protocol specs for all protocol failure types). */
972
973 void silc_failure(SilcClient client, SilcClientConnection conn, 
974                   SilcProtocol protocol, void *failure)
975 {
976   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
977     SilcSKEStatus status = (SilcSKEStatus)failure;
978     
979     if (status == SILC_SKE_STATUS_BAD_VERSION)
980       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
981                          SILCTXT_KE_BAD_VERSION);
982     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
983       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
984                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
985     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
986       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
987                          SILCTXT_KE_UNKNOWN_GROUP);
988     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
989       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
990                          SILCTXT_KE_UNKNOWN_CIPHER);
991     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
992       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
993                          SILCTXT_KE_UNKNOWN_PKCS);
994     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
995       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
996                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
997     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
998       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
999                          SILCTXT_KE_UNKNOWN_HMAC);
1000     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1001       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1002                          SILCTXT_KE_INCORRECT_SIGNATURE);
1003   }
1004
1005   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1006     uint32 err = (uint32)failure;
1007
1008     if (err == SILC_AUTH_FAILED)
1009       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1010                          SILCTXT_AUTH_FAILED);
1011   }
1012 }
1013
1014 /* Asks whether the user would like to perform the key agreement protocol.
1015    This is called after we have received an key agreement packet or an
1016    reply to our key agreement packet. This returns TRUE if the user wants
1017    the library to perform the key agreement protocol and FALSE if it is not
1018    desired (application may start it later by calling the function
1019    silc_client_perform_key_agreement). */
1020
1021 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1022                        SilcClientEntry client_entry, char *hostname,
1023                        int port,
1024                        SilcKeyAgreementCallback *completion,
1025                        void **context)
1026 {
1027   char portstr[6];
1028
1029   /* We will just display the info on the screen and return FALSE and user
1030      will have to start the key agreement with a command. */
1031
1032   if (hostname) 
1033     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1034
1035   if (!hostname)
1036     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1037                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1038   else
1039     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1040                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1041                        client_entry->nickname, hostname, portstr);
1042
1043   *completion = NULL;
1044   *context = NULL;
1045
1046   return FALSE;
1047 }
1048
1049 /* SILC client operations */
1050 SilcClientOperations ops = {
1051   silc_say,
1052   silc_channel_message,
1053   silc_private_message,
1054   silc_notify,
1055   silc_command,
1056   silc_command_reply,
1057   silc_connect,
1058   silc_disconnect,
1059   silc_get_auth_method,
1060   silc_verify_public_key,
1061   silc_ask_passphrase,
1062   silc_failure,
1063   silc_key_agreement,
1064 };