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