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