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 typedef struct {
311   SilcClient client;
312   SilcClientConnection conn;
313   void *entry;
314   SilcIdType id_type;
315   char *fingerprint;
316 } *GetkeyContext;
317
318 void silc_getkey_cb(bool success, void *context)
319 {
320   GetkeyContext getkey = (GetkeyContext)context;
321   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
322   char *name = (getkey->id_type == SILC_ID_CLIENT ? 
323                 ((SilcClientEntry)getkey->entry)->nickname :
324                 ((SilcServerEntry)getkey->entry)->server_name);
325
326   if (success) {
327     printformat_module("fe-common/silc", NULL, NULL,
328                        MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
329   } else {
330     printformat_module("fe-common/silc", NULL, NULL,
331                        MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
332   }
333
334   silc_free(getkey->fingerprint);
335   silc_free(getkey);
336 }
337
338 /* Command reply handler. This function is called always in the command reply
339    function. If error occurs it will be called as well. Normal scenario
340    is that it will be called after the received command data has been parsed
341    and processed. The function is used to pass the received command data to
342    the application. 
343
344    `conn' is the associated client connection. `cmd_payload' is the command
345    payload data received from server and it can be ignored. It is provided
346    if the application would like to re-parse the received command data,
347    however, it must be noted that the data is parsed already by the library
348    thus the payload can be ignored. `success' is FALSE if error occured.
349    In this case arguments are not sent to the application. `command' is the
350    command reply being processed. The function has variable argument list
351    and each command defines the number and type of arguments it passes to the
352    application (on error they are not sent). */
353
354 void 
355 silc_command_reply(SilcClient client, SilcClientConnection conn,
356                    SilcCommandPayload cmd_payload, int success,
357                    SilcCommand command, SilcCommandStatus status, ...)
358
359 {
360   SILC_SERVER_REC *server = conn->context;
361   SILC_CHANNEL_REC *chanrec;
362   va_list vp;
363
364   va_start(vp, status);
365
366   switch(command) {
367   case SILC_COMMAND_WHOIS:
368     {
369       char buf[1024], *nickname, *username, *realname, *nick;
370       uint32 idle, mode;
371       SilcBuffer channels;
372       SilcClientEntry client_entry;
373       
374       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
375           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
376         char *tmp;
377         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
378                                          3, NULL);
379         if (tmp)
380           silc_say_error("%s: %s", tmp, 
381                          silc_client_command_status_message(status));
382         else
383           silc_say_error("%s", silc_client_command_status_message(status));
384         break;
385       }
386       
387       if (!success)
388         return;
389       
390       client_entry = va_arg(vp, SilcClientEntry);
391       nickname = va_arg(vp, char *);
392       username = va_arg(vp, char *);
393       realname = va_arg(vp, char *);
394       channels = va_arg(vp, SilcBuffer);
395       mode = va_arg(vp, uint32);
396       idle = va_arg(vp, uint32);
397       
398       silc_parse_userfqdn(nickname, &nick, NULL);
399       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
400                          SILCTXT_WHOIS_USERINFO, nickname, 
401                          client_entry->username, client_entry->hostname,
402                          nick, client_entry->nickname);
403       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
404                          SILCTXT_WHOIS_REALNAME, realname);
405       silc_free(nick);
406
407       if (channels) {
408         SilcDList list = silc_channel_payload_parse_list(channels);
409         if (list) {
410           SilcChannelPayload entry;
411           memset(buf, 0, sizeof(buf));
412           silc_dlist_start(list);
413           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
414             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
415             uint32 name_len;
416             char *name = silc_channel_get_name(entry, &name_len);
417             
418             if (m)
419               strncat(buf, m, strlen(m));
420             strncat(buf, name, name_len);
421             strncat(buf, " ", 1);
422             silc_free(m);
423           }
424
425           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
426                              SILCTXT_WHOIS_CHANNELS, buf);
427           silc_channel_payload_list_free(list);
428         }
429       }
430       
431       if (mode) {
432         memset(buf, 0, sizeof(buf));
433
434         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
435             (mode & SILC_UMODE_ROUTER_OPERATOR)) {
436           strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
437                  "Server Operator " :
438                  (mode & SILC_UMODE_ROUTER_OPERATOR) ?
439                  "SILC Operator " : "[Unknown mode] ");
440         }
441         if (mode & SILC_UMODE_GONE)
442           strcat(buf, "away");
443
444         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
445                            SILCTXT_WHOIS_MODES, buf);
446       }
447       
448       if (idle && nickname) {
449         memset(buf, 0, sizeof(buf));
450         snprintf(buf, sizeof(buf) - 1, "%lu %s",
451                  idle > 60 ? (idle / 60) : idle,
452                  idle > 60 ? "minutes" : "seconds");
453
454         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
455                            SILCTXT_WHOIS_IDLE, buf);
456       }
457     }
458     break;
459     
460   case SILC_COMMAND_WHOWAS:
461     {
462       char *nickname, *username, *realname;
463       
464       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
465           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
466         char *tmp;
467         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
468                                          3, NULL);
469         if (tmp)
470           silc_say_error("%s: %s", tmp, 
471                          silc_client_command_status_message(status));
472         else
473           silc_say_error("%s", silc_client_command_status_message(status));
474         break;
475       }
476       
477       if (!success)
478         return;
479       
480       (void)va_arg(vp, SilcClientEntry);
481       nickname = va_arg(vp, char *);
482       username = va_arg(vp, char *);
483       realname = va_arg(vp, char *);
484       
485       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
486                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
487                          realname ? realname : "");
488     }
489     break;
490     
491   case SILC_COMMAND_INVITE:
492     {
493       SilcChannelEntry channel;
494       char *invite_list;
495       SilcArgumentPayload args;
496       int argc = 0;
497       
498       if (!success)
499         return;
500       
501       channel = va_arg(vp, SilcChannelEntry);
502       invite_list = va_arg(vp, char *);
503
504       args = silc_command_get_args(cmd_payload);
505       if (args)
506         argc = silc_argument_get_arg_num(args);
507
508       if (invite_list)
509         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
510                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
511                            invite_list);
512       else if (argc == 3)
513         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
514                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
515                            channel->channel_name);
516     }
517     break;
518
519   case SILC_COMMAND_JOIN: 
520     {
521       char *channel, *mode, *topic;
522       uint32 modei;
523       SilcChannelEntry channel_entry;
524       SilcBuffer client_id_list;
525       uint32 list_count;
526
527       if (!success)
528         return;
529
530       channel = va_arg(vp, char *);
531       channel_entry = va_arg(vp, SilcChannelEntry);
532       modei = va_arg(vp, uint32);
533       (void)va_arg(vp, uint32);
534       (void)va_arg(vp, unsigned char *);
535       (void)va_arg(vp, unsigned char *);
536       (void)va_arg(vp, unsigned char *);
537       topic = va_arg(vp, char *);
538       (void)va_arg(vp, unsigned char *);
539       list_count = va_arg(vp, uint32);
540       client_id_list = va_arg(vp, SilcBuffer);
541
542       chanrec = silc_channel_find(server, channel);
543       if (!chanrec)
544         chanrec = silc_channel_create(server, channel, TRUE);
545
546       if (topic) {
547         g_free_not_null(chanrec->topic);
548         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
549         signal_emit("channel topic changed", 1, chanrec);
550       }
551
552       mode = silc_client_chmode(modei, 
553                                 channel_entry->channel_key ? 
554                                 channel_entry->channel_key->cipher->name : "",
555                                 channel_entry->hmac ? 
556                                 channel_entry->hmac->hmac->name : "");
557       g_free_not_null(chanrec->mode);
558       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
559       signal_emit("channel mode changed", 1, chanrec);
560
561       /* Resolve the client information */
562       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
563                                       silc_client_join_get_users, 
564                                       channel_entry);
565       break;
566     }
567
568   case SILC_COMMAND_NICK: 
569     {
570       SilcClientEntry client = va_arg(vp, SilcClientEntry);
571       char *old;
572       
573       if (!success)
574         return;
575
576       old = g_strdup(server->nick);
577       server_change_nick(SERVER(server), client->nickname);
578       nicklist_rename_unique(SERVER(server),
579                              server->conn->local_entry, server->nick,
580                              client, client->nickname);
581       
582       signal_emit("message own_nick", 4, server, server->nick, old, "");
583       g_free(old);
584       break;
585     }
586     
587   case SILC_COMMAND_LIST:
588     {
589       char *topic, *name;
590       int usercount;
591       char users[20];
592       
593       if (!success)
594         return;
595       
596       (void)va_arg(vp, SilcChannelEntry);
597       name = va_arg(vp, char *);
598       topic = va_arg(vp, char *);
599       usercount = va_arg(vp, int);
600       
601       if (status == SILC_STATUS_LIST_START ||
602           status == SILC_STATUS_OK)
603         printformat_module("fe-common/silc", server, NULL,
604                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
605
606       snprintf(users, sizeof(users) - 1, "%d", usercount);
607       printformat_module("fe-common/silc", server, NULL,
608                          MSGLEVEL_CRAP, SILCTXT_LIST,
609                          name, users, topic ? topic : "");
610     }
611     break;
612     
613   case SILC_COMMAND_UMODE:
614     {
615       uint32 mode;
616       
617       if (!success)
618         return;
619       
620       mode = va_arg(vp, uint32);
621       
622       if (mode & SILC_UMODE_SERVER_OPERATOR)
623         printformat_module("fe-common/silc", server, NULL,
624                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
625
626       if (mode & SILC_UMODE_ROUTER_OPERATOR)
627         printformat_module("fe-common/silc", server, NULL,
628                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
629     }
630     break;
631     
632   case SILC_COMMAND_OPER:
633     if (!success)
634       return;
635
636     printformat_module("fe-common/silc", server, NULL,
637                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
638     break;
639     
640   case SILC_COMMAND_SILCOPER:
641     if (!success)
642       return;
643
644     printformat_module("fe-common/silc", server, NULL,
645                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
646     break;
647     
648   case SILC_COMMAND_USERS: 
649     {
650       SilcChannelEntry channel;
651       SilcChannelUser chu;
652       
653       if (!success)
654         return;
655       
656       channel = va_arg(vp, SilcChannelEntry);
657       
658       printformat_module("fe-common/silc", server, channel->channel_name,
659                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
660                          channel->channel_name);
661
662       silc_list_start(channel->clients);
663       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
664         SilcClientEntry e = chu->client;
665         char stat[5], *mode;
666         
667         memset(stat, 0, sizeof(stat));
668         mode = silc_client_chumode_char(chu->mode);
669         if (e->mode & SILC_UMODE_GONE)
670           strcat(stat, "G");
671         else
672           strcat(stat, "H");
673         if (mode)
674           strcat(stat, mode);
675
676         printformat_module("fe-common/silc", server, channel->channel_name,
677                            MSGLEVEL_CRAP, SILCTXT_USERS,
678                            e->nickname, stat, e->username, 
679                            e->hostname, e->realname ? e->realname : "");
680         if (mode)
681           silc_free(mode);
682       }
683     }
684     break;
685
686   case SILC_COMMAND_BAN:
687     {
688       SilcChannelEntry channel;
689       char *ban_list;
690       
691       if (!success)
692         return;
693       
694       channel = va_arg(vp, SilcChannelEntry);
695       ban_list = va_arg(vp, char *);
696       
697       if (ban_list)
698         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
699                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
700                            ban_list);
701       else
702         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
703                            SILCTXT_CHANNEL_NO_BAN_LIST, 
704                            channel->channel_name);
705     }
706     break;
707     
708   case SILC_COMMAND_GETKEY:
709     {
710       SilcIdType id_type;
711       void *entry;
712       SilcPublicKey public_key;
713       unsigned char *pk;
714       uint32 pk_len;
715       GetkeyContext getkey;
716       
717       if (!success)
718         return;
719       
720       id_type = va_arg(vp, uint32);
721       entry = va_arg(vp, void *);
722       public_key = va_arg(vp, SilcPublicKey);
723
724       if (public_key) {
725         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
726
727         getkey = silc_calloc(1, sizeof(*getkey));
728         getkey->entry = entry;
729         getkey->id_type = id_type;
730         getkey->client = client;
731         getkey->conn = conn;
732         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
733         
734         silc_verify_public_key_internal(client, conn, 
735                                         (id_type == SILC_ID_CLIENT ?
736                                          SILC_SOCKET_TYPE_CLIENT :
737                                          SILC_SOCKET_TYPE_SERVER),
738                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
739                                         silc_getkey_cb, getkey);
740         silc_free(pk);
741       } else {
742         printformat_module("fe-common/silc", server, NULL,
743                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
744       }
745     }
746     break;
747     
748   case SILC_COMMAND_TOPIC:
749     {
750       SilcChannelEntry channel;
751       char *topic;
752       
753       if (!success)
754         return;
755       
756       channel = va_arg(vp, SilcChannelEntry);
757       topic = va_arg(vp, char *);
758       
759       if (topic) {
760         chanrec = silc_channel_find_entry(server, channel);
761         if (chanrec) {
762           g_free_not_null(chanrec->topic);
763           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
764           signal_emit("channel topic changed", 1, chanrec);
765         }
766         printformat_module("fe-common/silc", server, channel->channel_name,
767                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
768                            channel->channel_name, topic);
769       } else {
770         printformat_module("fe-common/silc", server, channel->channel_name,
771                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
772                            channel->channel_name);
773       }
774     }
775     break;
776
777   }
778
779   va_end(vp);
780 }
781
782 /* Internal routine to verify public key. If the `completion' is provided
783    it will be called to indicate whether public was verified or not. */
784
785 typedef struct {
786   SilcClient client;
787   SilcClientConnection conn;
788   char *filename;
789   char *entity;
790   unsigned char *pk;
791   uint32 pk_len;
792   SilcSKEPKType pk_type;
793   SilcVerifyPublicKey completion;
794   void *context;
795 } *PublicKeyVerify;
796
797 static void verify_public_key_completion(const char *line, void *context)
798 {
799   PublicKeyVerify verify = (PublicKeyVerify)context;
800
801   if (line[0] == 'Y' || line[0] == 'y') {
802     /* Call the completion */
803     if (verify->completion)
804       verify->completion(TRUE, verify->context);
805
806     /* Save the key for future checking */
807     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
808                                    verify->pk_len, SILC_PKCS_FILE_PEM);
809   } else {
810     /* Call the completion */
811     if (verify->completion)
812       verify->completion(FALSE, verify->context);
813
814     printformat_module("fe-common/silc", NULL, NULL,
815                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
816   }
817
818   silc_free(verify->filename);
819   silc_free(verify->entity);
820   silc_free(verify->pk);
821   silc_free(verify);
822 }
823
824 static void 
825 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
826                                 SilcSocketType conn_type, unsigned char *pk, 
827                                 uint32 pk_len, SilcSKEPKType pk_type,
828                                 SilcVerifyPublicKey completion, void *context)
829 {
830   int i;
831   char file[256], filename[256], *fingerprint, *format;
832   struct passwd *pw;
833   struct stat st;
834   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
835                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
836                   "server" : "client");
837   PublicKeyVerify verify;
838
839   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
840     printformat_module("fe-common/silc", NULL, NULL,
841                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
842                        entity, pk_type);
843     if (completion)
844       completion(FALSE, context);
845     return;
846   }
847
848   pw = getpwuid(getuid());
849   if (!pw) {
850     if (completion)
851       completion(FALSE, context);
852     return;
853   }
854
855   memset(filename, 0, sizeof(filename));
856   memset(file, 0, sizeof(file));
857
858   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
859       conn_type == SILC_SOCKET_TYPE_ROUTER) {
860     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
861              conn->sock->hostname, conn->sock->port);
862     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
863              pw->pw_dir, entity, file);
864   } else {
865     /* Replace all whitespaces with `_'. */
866     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
867     for (i = 0; i < strlen(fingerprint); i++)
868       if (fingerprint[i] == ' ')
869         fingerprint[i] = '_';
870     
871     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
872     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
873              pw->pw_dir, entity, file);
874     silc_free(fingerprint);
875   }
876
877   /* Take fingerprint of the public key */
878   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
879
880   verify = silc_calloc(1, sizeof(*verify));
881   verify->client = client;
882   verify->conn = conn;
883   verify->filename = strdup(filename);
884   verify->entity = strdup(entity);
885   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
886   memcpy(verify->pk, pk, pk_len);
887   verify->pk_len = pk_len;
888   verify->pk_type = pk_type;
889   verify->completion = completion;
890   verify->context = context;
891
892   /* Check whether this key already exists */
893   if (stat(filename, &st) < 0) {
894     /* Key does not exist, ask user to verify the key and save it */
895
896     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
897                        SILCTXT_PUBKEY_RECEIVED, entity);
898     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
899                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
900     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
901                              SILCTXT_PUBKEY_ACCEPT);
902     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
903                             format, 0, verify);
904     g_free(format);
905     silc_free(fingerprint);
906     return;
907   } else {
908     /* The key already exists, verify it. */
909     SilcPublicKey public_key;
910     unsigned char *encpk;
911     uint32 encpk_len;
912
913     /* Load the key file */
914     if (!silc_pkcs_load_public_key(filename, &public_key, 
915                                    SILC_PKCS_FILE_PEM))
916       if (!silc_pkcs_load_public_key(filename, &public_key, 
917                                      SILC_PKCS_FILE_BIN)) {
918         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
919                            SILCTXT_PUBKEY_RECEIVED, entity);
920         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
921                            SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
922         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
923                            SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
924         format = format_get_text("fe-common/silc", NULL, NULL, NULL,
925                                  SILCTXT_PUBKEY_ACCEPT_ANYWAY);
926         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
927                                 format, 0, verify);
928         g_free(format);
929         silc_free(fingerprint);
930         return;
931       }
932   
933     /* Encode the key data */
934     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
935     if (!encpk) {
936       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
937                          SILCTXT_PUBKEY_RECEIVED, entity);
938       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
939                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
940       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
941                          SILCTXT_PUBKEY_MALFORMED, entity);
942       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
943                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
944       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
945                               format, 0, verify);
946       g_free(format);
947       silc_free(fingerprint);
948       return;
949     }
950
951     /* Compare the keys */
952     if (memcmp(encpk, pk, encpk_len)) {
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_NO_MATCH, entity);
959       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
960                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
961       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
962                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
963
964       /* Ask user to verify the key and save it */
965       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
966                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
967       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
968                               format, 0, verify);
969       g_free(format);
970       silc_free(fingerprint);
971       return;
972     }
973
974     /* Local copy matched */
975     if (completion)
976       completion(TRUE, context);
977     silc_free(fingerprint);
978   }
979 }
980
981 /* Verifies received public key. The `conn_type' indicates which entity
982    (server, client etc.) has sent the public key. If user decides to trust
983    the key may be saved as trusted public key for later use. The 
984    `completion' must be called after the public key has been verified. */
985
986 void 
987 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
988                        SilcSocketType conn_type, unsigned char *pk, 
989                        uint32 pk_len, SilcSKEPKType pk_type,
990                        SilcVerifyPublicKey completion, void *context)
991 {
992   silc_verify_public_key_internal(client, conn, conn_type, pk,
993                                   pk_len, pk_type,
994                                   completion, context);
995 }
996
997 /* Asks passphrase from user on the input line. */
998
999 typedef struct {
1000   SilcAskPassphrase completion;
1001   void *context;
1002 } *AskPassphrase;
1003
1004 void ask_passphrase_completion(const char *passphrase, void *context)
1005 {
1006   AskPassphrase p = (AskPassphrase)context;
1007   p->completion((unsigned char *)passphrase, 
1008                 passphrase ? strlen(passphrase) : 0, p->context);
1009   silc_free(p);
1010 }
1011
1012 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1013                          SilcAskPassphrase completion, void *context)
1014 {
1015   AskPassphrase p = silc_calloc(1, sizeof(*p));
1016   p->completion = completion;
1017   p->context = context;
1018
1019   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1020                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1021 }
1022
1023 typedef struct {
1024   SilcGetAuthMeth completion;
1025   void *context;
1026 } *InternalGetAuthMethod;
1027
1028 /* Callback called when we've received the authentication method information
1029    from the server after we've requested it. This will get the authentication
1030    data from the user if needed. */
1031
1032 static void silc_get_auth_method_callback(SilcClient client,
1033                                           SilcClientConnection conn,
1034                                           SilcAuthMethod auth_meth,
1035                                           void *context)
1036 {
1037   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1038
1039   switch (auth_meth) {
1040   case SILC_AUTH_NONE:
1041     /* No authentication required. */
1042     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1043     break;
1044   case SILC_AUTH_PASSWORD:
1045     /* Do not ask the passphrase from user, the library will ask it if
1046        we do not provide it here. */
1047     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1048     break;
1049   case SILC_AUTH_PUBLIC_KEY:
1050     /* Do not get the authentication data now, the library will generate
1051        it using our default key, if we do not provide it here. */
1052     /* XXX In the future when we support multiple local keys and multiple
1053        local certificates we will need to ask from user which one to use. */
1054     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1055     break;
1056   }
1057
1058   silc_free(internal);
1059 }
1060
1061 /* Find authentication method and authentication data by hostname and
1062    port. The hostname may be IP address as well. The found authentication
1063    method and authentication data is returned to `auth_meth', `auth_data'
1064    and `auth_data_len'. The function returns TRUE if authentication method
1065    is found and FALSE if not. `conn' may be NULL. */
1066
1067 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1068                           char *hostname, uint16 port,
1069                           SilcGetAuthMeth completion, void *context)
1070 {
1071   InternalGetAuthMethod internal;
1072
1073   /* XXX must resolve from configuration whether this connection has
1074      any specific authentication data */
1075
1076   /* If we do not have this connection configured by the user in a
1077      configuration file then resolve the authentication method from the
1078      server for this session. */
1079   internal = silc_calloc(1, sizeof(*internal));
1080   internal->completion = completion;
1081   internal->context = context;
1082
1083   silc_client_request_authentication_method(client, conn, 
1084                                             silc_get_auth_method_callback,
1085                                             internal);
1086 }
1087
1088 /* Notifies application that failure packet was received.  This is called
1089    if there is some protocol active in the client.  The `protocol' is the
1090    protocol context.  The `failure' is opaque pointer to the failure
1091    indication.  Note, that the `failure' is protocol dependant and application
1092    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1093    failure type (see protocol specs for all protocol failure types). */
1094
1095 void silc_failure(SilcClient client, SilcClientConnection conn, 
1096                   SilcProtocol protocol, void *failure)
1097 {
1098   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1099     SilcSKEStatus status = (SilcSKEStatus)failure;
1100     
1101     if (status == SILC_SKE_STATUS_BAD_VERSION)
1102       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1103                          SILCTXT_KE_BAD_VERSION);
1104     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1105       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1106                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1107     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1108       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1109                          SILCTXT_KE_UNKNOWN_GROUP);
1110     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1111       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1112                          SILCTXT_KE_UNKNOWN_CIPHER);
1113     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1114       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1115                          SILCTXT_KE_UNKNOWN_PKCS);
1116     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1117       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1118                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1119     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1120       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1121                          SILCTXT_KE_UNKNOWN_HMAC);
1122     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1123       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1124                          SILCTXT_KE_INCORRECT_SIGNATURE);
1125     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1126       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1127                          SILCTXT_KE_INVALID_COOKIE);
1128   }
1129
1130   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1131     uint32 err = (uint32)failure;
1132
1133     if (err == SILC_AUTH_FAILED)
1134       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1135                          SILCTXT_AUTH_FAILED);
1136   }
1137 }
1138
1139 /* Asks whether the user would like to perform the key agreement protocol.
1140    This is called after we have received an key agreement packet or an
1141    reply to our key agreement packet. This returns TRUE if the user wants
1142    the library to perform the key agreement protocol and FALSE if it is not
1143    desired (application may start it later by calling the function
1144    silc_client_perform_key_agreement). */
1145
1146 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1147                        SilcClientEntry client_entry, char *hostname,
1148                        int port,
1149                        SilcKeyAgreementCallback *completion,
1150                        void **context)
1151 {
1152   char portstr[12];
1153
1154   /* We will just display the info on the screen and return FALSE and user
1155      will have to start the key agreement with a command. */
1156
1157   if (hostname) 
1158     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1159
1160   if (!hostname)
1161     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1162                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1163   else
1164     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1165                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1166                        client_entry->nickname, hostname, portstr);
1167
1168   *completion = NULL;
1169   *context = NULL;
1170
1171   return FALSE;
1172 }
1173
1174 /* SILC client operations */
1175 SilcClientOperations ops = {
1176   silc_say,
1177   silc_channel_message,
1178   silc_private_message,
1179   silc_notify,
1180   silc_command,
1181   silc_command_reply,
1182   silc_connect,
1183   silc_disconnect,
1184   silc_get_auth_method,
1185   silc_verify_public_key,
1186   silc_ask_passphrase,
1187   silc_failure,
1188   silc_key_agreement,
1189 };