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