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
677   va_end(vp);
678 }
679
680 /* Internal routine to verify public key. If the `completion' is provided
681    it will be called to indicate whether public was verified or not. */
682
683 typedef struct {
684   SilcClient client;
685   SilcClientConnection conn;
686   char *filename;
687   char *entity;
688   unsigned char *pk;
689   uint32 pk_len;
690   SilcSKEPKType pk_type;
691   SilcVerifyPublicKey completion;
692   void *context;
693 } *PublicKeyVerify;
694
695 static void verify_public_key_completion(const char *line, void *context)
696 {
697   PublicKeyVerify verify = (PublicKeyVerify)context;
698
699   if (line[0] == 'Y' || line[0] == 'y') {
700     /* Call the completion */
701     if (verify->completion)
702       verify->completion(TRUE, verify->context);
703
704     /* Save the key for future checking */
705     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
706                                    verify->pk_len, SILC_PKCS_FILE_PEM);
707   } else {
708     /* Call the completion */
709     if (verify->completion)
710       verify->completion(FALSE, verify->context);
711
712     printformat_module("fe-common/silc", NULL, NULL,
713                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
714   }
715
716   silc_free(verify->filename);
717   silc_free(verify->entity);
718   silc_free(verify->pk);
719   silc_free(verify);
720 }
721
722 static void 
723 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
724                                 SilcSocketType conn_type, unsigned char *pk, 
725                                 uint32 pk_len, SilcSKEPKType pk_type,
726                                 SilcVerifyPublicKey completion, void *context)
727 {
728   int i;
729   char file[256], filename[256], *fingerprint, *format;
730   struct passwd *pw;
731   struct stat st;
732   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
733                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
734                   "server" : "client");
735   PublicKeyVerify verify;
736
737   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
738     printformat_module("fe-common/silc", NULL, NULL,
739                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
740                        entity, pk_type);
741     if (completion)
742       completion(FALSE, context);
743     return;
744   }
745
746   pw = getpwuid(getuid());
747   if (!pw) {
748     if (completion)
749       completion(FALSE, context);
750     return;
751   }
752
753   memset(filename, 0, sizeof(filename));
754   memset(file, 0, sizeof(file));
755
756   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
757       conn_type == SILC_SOCKET_TYPE_ROUTER) {
758     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
759              conn->sock->hostname, conn->sock->port);
760     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
761              pw->pw_dir, entity, file);
762   } else {
763     /* Replace all whitespaces with `_'. */
764     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
765     for (i = 0; i < strlen(fingerprint); i++)
766       if (fingerprint[i] == ' ')
767         fingerprint[i] = '_';
768     
769     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
770     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
771              pw->pw_dir, entity, file);
772     silc_free(fingerprint);
773   }
774
775   /* Take fingerprint of the public key */
776   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
777
778   verify = silc_calloc(1, sizeof(*verify));
779   verify->client = client;
780   verify->conn = conn;
781   verify->filename = strdup(filename);
782   verify->entity = strdup(entity);
783   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
784   memcpy(verify->pk, pk, pk_len);
785   verify->pk_len = pk_len;
786   verify->pk_type = pk_type;
787   verify->completion = completion;
788   verify->context = context;
789
790   /* Check whether this key already exists */
791   if (stat(filename, &st) < 0) {
792     /* Key does not exist, ask user to verify the key and save it */
793
794     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
795                        SILCTXT_PUBKEY_RECEIVED, entity);
796     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
797                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
798     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
799                              SILCTXT_PUBKEY_ACCEPT);
800     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
801                             format, 0, verify);
802     g_free(format);
803     silc_free(fingerprint);
804     return;
805   } else {
806     /* The key already exists, verify it. */
807     SilcPublicKey public_key;
808     unsigned char *encpk;
809     uint32 encpk_len;
810
811     /* Load the key file */
812     if (!silc_pkcs_load_public_key(filename, &public_key, 
813                                    SILC_PKCS_FILE_PEM))
814       if (!silc_pkcs_load_public_key(filename, &public_key, 
815                                      SILC_PKCS_FILE_BIN)) {
816         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
817                            SILCTXT_PUBKEY_RECEIVED, entity);
818         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
819                            SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
820         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
821                            SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
822         format = format_get_text("fe-common/silc", NULL, NULL, NULL,
823                                  SILCTXT_PUBKEY_ACCEPT_ANYWAY);
824         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
825                                 format, 0, verify);
826         g_free(format);
827         silc_free(fingerprint);
828         return;
829       }
830   
831     /* Encode the key data */
832     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
833     if (!encpk) {
834       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
835                          SILCTXT_PUBKEY_RECEIVED, entity);
836       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
837                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
838       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
839                          SILCTXT_PUBKEY_MALFORMED, entity);
840       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
841                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
842       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
843                               format, 0, verify);
844       g_free(format);
845       silc_free(fingerprint);
846       return;
847     }
848
849     /* Compare the keys */
850     if (memcmp(encpk, pk, encpk_len)) {
851       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
852                          SILCTXT_PUBKEY_RECEIVED, entity);
853       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
854                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
855       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
856                          SILCTXT_PUBKEY_NO_MATCH, entity);
857       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
858                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
859       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
860                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
861
862       /* Ask user to verify the key and save it */
863       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
864                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
865       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
866                               format, 0, verify);
867       g_free(format);
868       silc_free(fingerprint);
869       return;
870     }
871
872     /* Local copy matched */
873     if (completion)
874       completion(TRUE, context);
875     silc_free(fingerprint);
876   }
877 }
878
879 /* Verifies received public key. The `conn_type' indicates which entity
880    (server, client etc.) has sent the public key. If user decides to trust
881    the key may be saved as trusted public key for later use. The 
882    `completion' must be called after the public key has been verified. */
883
884 void 
885 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
886                        SilcSocketType conn_type, unsigned char *pk, 
887                        uint32 pk_len, SilcSKEPKType pk_type,
888                        SilcVerifyPublicKey completion, void *context)
889 {
890   silc_verify_public_key_internal(client, conn, conn_type, pk,
891                                   pk_len, pk_type,
892                                   completion, context);
893 }
894
895 /* Asks passphrase from user on the input line. */
896
897 typedef struct {
898   SilcAskPassphrase completion;
899   void *context;
900 } *AskPassphrase;
901
902 void ask_passphrase_completion(const char *passphrase, void *context)
903 {
904   AskPassphrase p = (AskPassphrase)context;
905   p->completion((unsigned char *)passphrase, 
906                 passphrase ? strlen(passphrase) : 0, p->context);
907   silc_free(p);
908 }
909
910 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
911                                 SilcAskPassphrase completion, void *context)
912 {
913   AskPassphrase p = silc_calloc(1, sizeof(*p));
914   p->completion = completion;
915   p->context = context;
916
917   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
918                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
919 }
920
921 /* Find authentication method and authentication data by hostname and
922    port. The hostname may be IP address as well. The found authentication
923    method and authentication data is returned to `auth_meth', `auth_data'
924    and `auth_data_len'. The function returns TRUE if authentication method
925    is found and FALSE if not. `conn' may be NULL. */
926
927 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
928                          char *hostname, uint16 port,
929                          SilcProtocolAuthMeth *auth_meth,
930                          unsigned char **auth_data,
931                          uint32 *auth_data_len)
932 {
933   bool ret = TRUE;
934   SILC_SERVER_REC *server = conn ? conn->context : NULL;
935
936   /* XXX must resolve from configuration whether this connection has
937      any specific authentication data */
938
939   *auth_meth = SILC_AUTH_NONE;
940   *auth_data = NULL;
941   *auth_data_len = 0;
942
943   if (ret == FALSE) {
944     printformat_module("fe-common/silc", server, NULL,
945                        MSGLEVEL_MODES, SILCTXT_AUTH_METH_UNRESOLVED);
946   }
947
948   return ret;
949 }
950
951 /* Notifies application that failure packet was received.  This is called
952    if there is some protocol active in the client.  The `protocol' is the
953    protocol context.  The `failure' is opaque pointer to the failure
954    indication.  Note, that the `failure' is protocol dependant and application
955    must explicitly cast it to correct type.  Usually `failure' is 32 bit
956    failure type (see protocol specs for all protocol failure types). */
957
958 void silc_failure(SilcClient client, SilcClientConnection conn, 
959                   SilcProtocol protocol, void *failure)
960 {
961   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
962     SilcSKEStatus status = (SilcSKEStatus)failure;
963     
964     if (status == SILC_SKE_STATUS_BAD_VERSION)
965       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
966                          SILCTXT_KE_BAD_VERSION);
967     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
968       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
969                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
970     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
971       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
972                          SILCTXT_KE_UNKNOWN_GROUP);
973     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
974       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
975                          SILCTXT_KE_UNKNOWN_CIPHER);
976     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
977       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
978                          SILCTXT_KE_UNKNOWN_PKCS);
979     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
980       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
981                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
982     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
983       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
984                          SILCTXT_KE_UNKNOWN_HMAC);
985     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
986       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
987                          SILCTXT_KE_INCORRECT_SIGNATURE);
988   }
989
990   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
991     uint32 err = (uint32)failure;
992
993     if (err == SILC_AUTH_FAILED)
994       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
995                          SILCTXT_AUTH_FAILED);
996   }
997 }
998
999 /* Asks whether the user would like to perform the key agreement protocol.
1000    This is called after we have received an key agreement packet or an
1001    reply to our key agreement packet. This returns TRUE if the user wants
1002    the library to perform the key agreement protocol and FALSE if it is not
1003    desired (application may start it later by calling the function
1004    silc_client_perform_key_agreement). */
1005
1006 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1007                        SilcClientEntry client_entry, char *hostname,
1008                        int port,
1009                        SilcKeyAgreementCallback *completion,
1010                        void **context)
1011 {
1012   char portstr[6];
1013
1014   /* We will just display the info on the screen and return FALSE and user
1015      will have to start the key agreement with a command. */
1016
1017   if (hostname) 
1018     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1019
1020   if (!hostname)
1021     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1022                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1023   else
1024     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1025                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1026                        client_entry->nickname, hostname, portstr);
1027
1028   *completion = NULL;
1029   *context = NULL;
1030
1031   return FALSE;
1032 }
1033
1034 /* SILC client operations */
1035 SilcClientOperations ops = {
1036   silc_say,
1037   silc_channel_message,
1038   silc_private_message,
1039   silc_notify,
1040   silc_command,
1041   silc_command_reply,
1042   silc_connect,
1043   silc_disconnect,
1044   silc_get_auth_method,
1045   silc_verify_public_key,
1046   silc_ask_passphrase,
1047   silc_failure,
1048   silc_key_agreement,
1049 };