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