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