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     printformat_module("fe-common/silc", server, NULL,
561                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
562     break;
563     
564   case SILC_COMMAND_SILCOPER:
565     printformat_module("fe-common/silc", server, NULL,
566                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
567     break;
568     
569   case SILC_COMMAND_USERS: 
570     {
571       SilcChannelEntry channel;
572       SilcChannelUser chu;
573       
574       if (!success)
575         return;
576       
577       channel = va_arg(vp, SilcChannelEntry);
578       
579       printformat_module("fe-common/silc", server, channel->channel_name,
580                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
581                          channel->channel_name);
582
583       silc_list_start(channel->clients);
584       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
585         SilcClientEntry e = chu->client;
586         char stat[5], *mode;
587         
588         memset(stat, 0, sizeof(stat));
589         mode = silc_client_chumode_char(chu->mode);
590         if (e->mode & SILC_UMODE_GONE)
591           strcat(stat, "G");
592         else
593           strcat(stat, "H");
594         if (mode)
595           strcat(stat, mode);
596
597         printformat_module("fe-common/silc", server, channel->channel_name,
598                            MSGLEVEL_CRAP, SILCTXT_USERS,
599                            e->nickname, stat, e->username, 
600                            e->realname ? e->realname : "");
601         if (mode)
602           silc_free(mode);
603       }
604     }
605     break;
606
607   case SILC_COMMAND_BAN:
608     {
609       SilcChannelEntry channel;
610       char *ban_list;
611       
612       if (!success)
613         return;
614       
615       channel = va_arg(vp, SilcChannelEntry);
616       ban_list = va_arg(vp, char *);
617       
618       if (ban_list)
619         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
620                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
621                            ban_list);
622       else
623         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
624                            SILCTXT_CHANNEL_NO_BAN_LIST, 
625                            channel->channel_name);
626     }
627     break;
628     
629   case SILC_COMMAND_GETKEY:
630     {
631       SilcIdType id_type;
632       void *entry;
633       SilcPublicKey public_key;
634       unsigned char *pk;
635       uint32 pk_len;
636       
637       id_type = va_arg(vp, uint32);
638       entry = va_arg(vp, void *);
639       public_key = va_arg(vp, SilcPublicKey);
640       
641       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
642       
643       if (id_type == SILC_ID_CLIENT)
644         silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
645                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
646                                         NULL, NULL);
647       silc_free(pk);
648     }
649     
650   case SILC_COMMAND_TOPIC:
651     {
652       SilcChannelEntry channel;
653       char *topic;
654       
655       if (!success)
656         return;
657       
658       channel = va_arg(vp, SilcChannelEntry);
659       topic = va_arg(vp, char *);
660       
661       if (topic) {
662         chanrec = silc_channel_find_entry(server, channel);
663         if (chanrec) {
664           g_free_not_null(chanrec->topic);
665           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
666           signal_emit("channel topic changed", 1, chanrec);
667         }
668         printformat_module("fe-common/silc", server, channel->channel_name,
669                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
670                            channel->channel_name, topic);
671       }
672     }
673     break;
674   }
675
676   va_end(vp);
677 }
678
679 /* Internal routine to verify public key. If the `completion' is provided
680    it will be called to indicate whether public was verified or not. */
681
682 typedef struct {
683   SilcClient client;
684   SilcClientConnection conn;
685   char *filename;
686   char *entity;
687   unsigned char *pk;
688   uint32 pk_len;
689   SilcSKEPKType pk_type;
690   SilcVerifyPublicKey completion;
691   void *context;
692 } *PublicKeyVerify;
693
694 static void verify_public_key_completion(const char *line, void *context)
695 {
696   PublicKeyVerify verify = (PublicKeyVerify)context;
697
698   if (line[0] == 'Y' || line[0] == 'y') {
699     /* Call the completion */
700     if (verify->completion)
701       verify->completion(TRUE, verify->context);
702
703     /* Save the key for future checking */
704     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
705                                    verify->pk_len, SILC_PKCS_FILE_PEM);
706   } else {
707     /* Call the completion */
708     if (verify->completion)
709       verify->completion(FALSE, verify->context);
710
711     printformat_module("fe-common/silc", NULL, NULL,
712                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
713   }
714
715   silc_free(verify->filename);
716   silc_free(verify->entity);
717   silc_free(verify->pk);
718   silc_free(verify);
719 }
720
721 static void 
722 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
723                                 SilcSocketType conn_type, unsigned char *pk, 
724                                 uint32 pk_len, SilcSKEPKType pk_type,
725                                 SilcVerifyPublicKey completion, void *context)
726 {
727   int i;
728   char file[256], filename[256], *fingerprint, *format;
729   struct passwd *pw;
730   struct stat st;
731   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
732                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
733                   "server" : "client");
734   PublicKeyVerify verify;
735
736   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
737     printformat_module("fe-common/silc", NULL, NULL,
738                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
739                        entity, pk_type);
740     if (completion)
741       completion(FALSE, context);
742     return;
743   }
744
745   pw = getpwuid(getuid());
746   if (!pw) {
747     if (completion)
748       completion(FALSE, context);
749     return;
750   }
751
752   memset(filename, 0, sizeof(filename));
753   memset(file, 0, sizeof(file));
754
755   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
756       conn_type == SILC_SOCKET_TYPE_ROUTER) {
757     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
758              conn->sock->hostname, conn->sock->port);
759     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
760              pw->pw_dir, entity, file);
761   } else {
762     /* Replace all whitespaces with `_'. */
763     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
764     for (i = 0; i < strlen(fingerprint); i++)
765       if (fingerprint[i] == ' ')
766         fingerprint[i] = '_';
767     
768     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
769     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
770              pw->pw_dir, entity, file);
771     silc_free(fingerprint);
772   }
773
774   /* Take fingerprint of the public key */
775   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
776
777   verify = silc_calloc(1, sizeof(*verify));
778   verify->client = client;
779   verify->conn = conn;
780   verify->filename = strdup(filename);
781   verify->entity = strdup(entity);
782   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
783   memcpy(verify->pk, pk, pk_len);
784   verify->pk_len = pk_len;
785   verify->pk_type = pk_type;
786   verify->completion = completion;
787   verify->context = context;
788
789   /* Check whether this key already exists */
790   if (stat(filename, &st) < 0) {
791     /* Key does not exist, ask user to verify the key and save it */
792
793     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
794                        SILCTXT_PUBKEY_RECEIVED, entity);
795     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
796                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
797     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
798                              SILCTXT_PUBKEY_ACCEPT);
799     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
800                             format, 0, verify);
801     g_free(format);
802     silc_free(fingerprint);
803     return;
804   } else {
805     /* The key already exists, verify it. */
806     SilcPublicKey public_key;
807     unsigned char *encpk;
808     uint32 encpk_len;
809
810     /* Load the key file */
811     if (!silc_pkcs_load_public_key(filename, &public_key, 
812                                    SILC_PKCS_FILE_PEM))
813       if (!silc_pkcs_load_public_key(filename, &public_key, 
814                                      SILC_PKCS_FILE_BIN)) {
815         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
816                            SILCTXT_PUBKEY_RECEIVED, entity);
817         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
818                            SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
819         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
820                            SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
821         format = format_get_text("fe-common/silc", NULL, NULL, NULL,
822                                  SILCTXT_PUBKEY_ACCEPT_ANYWAY);
823         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
824                                 format, 0, verify);
825         g_free(format);
826         silc_free(fingerprint);
827         return;
828       }
829   
830     /* Encode the key data */
831     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
832     if (!encpk) {
833       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
834                          SILCTXT_PUBKEY_RECEIVED, entity);
835       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
836                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
837       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
838                          SILCTXT_PUBKEY_MALFORMED, entity);
839       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
840                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
841       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
842                               format, 0, verify);
843       g_free(format);
844       silc_free(fingerprint);
845       return;
846     }
847
848     /* Compare the keys */
849     if (memcmp(encpk, pk, encpk_len)) {
850       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
851                          SILCTXT_PUBKEY_RECEIVED, entity);
852       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
853                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
854       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
855                          SILCTXT_PUBKEY_NO_MATCH, entity);
856       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
857                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
858       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
859                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
860
861       /* Ask user to verify the key and save it */
862       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
863                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
864       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
865                               format, 0, verify);
866       g_free(format);
867       silc_free(fingerprint);
868       return;
869     }
870
871     /* Local copy matched */
872     if (completion)
873       completion(TRUE, context);
874     silc_free(fingerprint);
875   }
876 }
877
878 /* Verifies received public key. The `conn_type' indicates which entity
879    (server, client etc.) has sent the public key. If user decides to trust
880    the key may be saved as trusted public key for later use. The 
881    `completion' must be called after the public key has been verified. */
882
883 void 
884 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
885                        SilcSocketType conn_type, unsigned char *pk, 
886                        uint32 pk_len, SilcSKEPKType pk_type,
887                        SilcVerifyPublicKey completion, void *context)
888 {
889   silc_verify_public_key_internal(client, conn, conn_type, pk,
890                                   pk_len, pk_type,
891                                   completion, context);
892 }
893
894 /* Asks passphrase from user on the input line. */
895
896 typedef struct {
897   SilcAskPassphrase completion;
898   void *context;
899 } *AskPassphrase;
900
901 void ask_passphrase_completion(const char *passphrase, void *context)
902 {
903   AskPassphrase p = (AskPassphrase)context;
904   p->completion((unsigned char *)passphrase, 
905                 passphrase ? strlen(passphrase) : 0, p->context);
906   silc_free(p);
907 }
908
909 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
910                                 SilcAskPassphrase completion, void *context)
911 {
912   AskPassphrase p = silc_calloc(1, sizeof(*p));
913   p->completion = completion;
914   p->context = context;
915
916   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
917                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
918 }
919
920 /* Find authentication method and authentication data by hostname and
921    port. The hostname may be IP address as well. The found authentication
922    method and authentication data is returned to `auth_meth', `auth_data'
923    and `auth_data_len'. The function returns TRUE if authentication method
924    is found and FALSE if not. `conn' may be NULL. */
925
926 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
927                          char *hostname, uint16 port,
928                          SilcProtocolAuthMeth *auth_meth,
929                          unsigned char **auth_data,
930                          uint32 *auth_data_len)
931 {
932
933   /* XXX must resolve from configuration whether this connection has
934      any specific authentication data */
935
936   *auth_meth = SILC_AUTH_NONE;
937   *auth_data = NULL;
938   *auth_data_len = 0;
939
940   return TRUE;
941 }
942
943 /* Notifies application that failure packet was received.  This is called
944    if there is some protocol active in the client.  The `protocol' is the
945    protocol context.  The `failure' is opaque pointer to the failure
946    indication.  Note, that the `failure' is protocol dependant and application
947    must explicitly cast it to correct type.  Usually `failure' is 32 bit
948    failure type (see protocol specs for all protocol failure types). */
949
950 void silc_failure(SilcClient client, SilcClientConnection conn, 
951                   SilcProtocol protocol, void *failure)
952 {
953   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
954     SilcSKEStatus status = (SilcSKEStatus)failure;
955     
956     if (status == SILC_SKE_STATUS_BAD_VERSION)
957       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
958                          SILCTXT_KE_BAD_VERSION);
959     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
960       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
961                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
962     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
963       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
964                          SILCTXT_KE_UNKNOWN_GROUP);
965     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
966       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
967                          SILCTXT_KE_UNKNOWN_CIPHER);
968     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
969       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
970                          SILCTXT_KE_UNKNOWN_PKCS);
971     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
972       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
973                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
974     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
975       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
976                          SILCTXT_KE_UNKNOWN_HMAC);
977     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
978       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
979                          SILCTXT_KE_INCORRECT_SIGNATURE);
980   }
981
982   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
983     uint32 err = (uint32)failure;
984
985     if (err == SILC_AUTH_FAILED)
986       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
987                          SILCTXT_AUTH_FAILED);
988   }
989 }
990
991 /* Asks whether the user would like to perform the key agreement protocol.
992    This is called after we have received an key agreement packet or an
993    reply to our key agreement packet. This returns TRUE if the user wants
994    the library to perform the key agreement protocol and FALSE if it is not
995    desired (application may start it later by calling the function
996    silc_client_perform_key_agreement). */
997
998 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
999                        SilcClientEntry client_entry, char *hostname,
1000                        int port,
1001                        SilcKeyAgreementCallback *completion,
1002                        void **context)
1003 {
1004   char portstr[6];
1005
1006   /* We will just display the info on the screen and return FALSE and user
1007      will have to start the key agreement with a command. */
1008
1009   if (hostname) 
1010     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1011
1012   if (!hostname)
1013     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1014                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1015   else
1016     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1017                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1018                        client_entry->nickname, hostname, portstr);
1019
1020   *completion = NULL;
1021   *context = NULL;
1022
1023   return FALSE;
1024 }
1025
1026 /* SILC client operations */
1027 SilcClientOperations ops = {
1028   silc_say,
1029   silc_channel_message,
1030   silc_private_message,
1031   silc_notify,
1032   silc_command,
1033   silc_command_reply,
1034   silc_connect,
1035   silc_disconnect,
1036   silc_get_auth_method,
1037   silc_verify_public_key,
1038   silc_ask_passphrase,
1039   silc_failure,
1040   silc_key_agreement,
1041 };