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);
443         if (list) {
444           SilcChannelPayload entry;
445           memset(buf, 0, sizeof(buf));
446           silc_dlist_start(list);
447           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
448             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
449             uint32 name_len;
450             char *name = silc_channel_get_name(entry, &name_len);
451             
452             if (m)
453               strncat(buf, m, strlen(m));
454             strncat(buf, name, name_len);
455             strncat(buf, " ", 1);
456             silc_free(m);
457           }
458
459           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
460                              SILCTXT_WHOIS_CHANNELS, buf);
461           silc_channel_payload_list_free(list);
462         }
463       }
464       
465       if (mode) {
466         memset(buf, 0, sizeof(buf));
467
468         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
469             (mode & SILC_UMODE_ROUTER_OPERATOR)) {
470           strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
471                  "Server Operator " :
472                  (mode & SILC_UMODE_ROUTER_OPERATOR) ?
473                  "SILC Operator " : "[Unknown mode] ");
474         }
475         if (mode & SILC_UMODE_GONE)
476           strcat(buf, "away");
477
478         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
479                            SILCTXT_WHOIS_MODES, buf);
480       }
481       
482       if (idle && nickname) {
483         memset(buf, 0, sizeof(buf));
484         snprintf(buf, sizeof(buf) - 1, "%lu %s",
485                  idle > 60 ? (idle / 60) : idle,
486                  idle > 60 ? "minutes" : "seconds");
487
488         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
489                            SILCTXT_WHOIS_IDLE, buf);
490       }
491
492       if (fingerprint) {
493         fingerprint = silc_fingerprint(fingerprint, 20);
494         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
495                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
496         silc_free(fingerprint);
497       }
498     }
499     break;
500     
501   case SILC_COMMAND_WHOWAS:
502     {
503       char *nickname, *username, *realname;
504       
505       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
506           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
507         char *tmp;
508         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
509                                          3, NULL);
510         if (tmp)
511           silc_say_error("%s: %s", tmp, 
512                          silc_client_command_status_message(status));
513         break;
514       }
515       
516       if (!success)
517         return;
518       
519       (void)va_arg(vp, SilcClientEntry);
520       nickname = va_arg(vp, char *);
521       username = va_arg(vp, char *);
522       realname = va_arg(vp, char *);
523       
524       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
525                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
526                          realname ? realname : "");
527     }
528     break;
529     
530   case SILC_COMMAND_INVITE:
531     {
532       SilcChannelEntry channel;
533       char *invite_list;
534       SilcArgumentPayload args;
535       int argc = 0;
536       
537       if (!success)
538         return;
539       
540       channel = va_arg(vp, SilcChannelEntry);
541       invite_list = va_arg(vp, char *);
542
543       args = silc_command_get_args(cmd_payload);
544       if (args)
545         argc = silc_argument_get_arg_num(args);
546
547       if (invite_list)
548         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
549                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
550                            invite_list);
551       else if (argc == 3)
552         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
553                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
554                            channel->channel_name);
555     }
556     break;
557
558   case SILC_COMMAND_JOIN: 
559     {
560       char *channel, *mode, *topic;
561       uint32 modei;
562       SilcChannelEntry channel_entry;
563       SilcBuffer client_id_list;
564       uint32 list_count;
565
566       if (!success)
567         return;
568
569       channel = va_arg(vp, char *);
570       channel_entry = va_arg(vp, SilcChannelEntry);
571       modei = va_arg(vp, uint32);
572       (void)va_arg(vp, uint32);
573       (void)va_arg(vp, unsigned char *);
574       (void)va_arg(vp, unsigned char *);
575       (void)va_arg(vp, unsigned char *);
576       topic = va_arg(vp, char *);
577       (void)va_arg(vp, unsigned char *);
578       list_count = va_arg(vp, uint32);
579       client_id_list = va_arg(vp, SilcBuffer);
580
581       chanrec = silc_channel_find(server, channel);
582       if (!chanrec)
583         chanrec = silc_channel_create(server, channel, TRUE);
584
585       if (topic) {
586         g_free_not_null(chanrec->topic);
587         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
588         signal_emit("channel topic changed", 1, chanrec);
589       }
590
591       mode = silc_client_chmode(modei, 
592                                 channel_entry->channel_key ? 
593                                 channel_entry->channel_key->cipher->name : "",
594                                 channel_entry->hmac ? 
595                                 silc_hmac_get_name(channel_entry->hmac) : "");
596       g_free_not_null(chanrec->mode);
597       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
598       signal_emit("channel mode changed", 1, chanrec);
599
600       /* Resolve the client information */
601       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
602                                       silc_client_join_get_users, 
603                                       channel_entry);
604       break;
605     }
606
607   case SILC_COMMAND_NICK: 
608     {
609       SilcClientEntry client = va_arg(vp, SilcClientEntry);
610       char *old;
611       
612       if (!success)
613         return;
614
615       old = g_strdup(server->nick);
616       server_change_nick(SERVER(server), client->nickname);
617       nicklist_rename_unique(SERVER(server),
618                              server->conn->local_entry, server->nick,
619                              client, client->nickname);
620       
621       signal_emit("message own_nick", 4, server, server->nick, old, "");
622       g_free(old);
623       break;
624     }
625     
626   case SILC_COMMAND_LIST:
627     {
628       char *topic, *name;
629       int usercount;
630       char users[20];
631       
632       if (!success)
633         return;
634       
635       (void)va_arg(vp, SilcChannelEntry);
636       name = va_arg(vp, char *);
637       topic = va_arg(vp, char *);
638       usercount = va_arg(vp, int);
639       
640       if (status == SILC_STATUS_LIST_START ||
641           status == SILC_STATUS_OK)
642         printformat_module("fe-common/silc", server, NULL,
643                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
644
645       if (!usercount)
646         snprintf(users, sizeof(users) - 1, "N/A");
647       else
648         snprintf(users, sizeof(users) - 1, "%d", usercount);
649       printformat_module("fe-common/silc", server, NULL,
650                          MSGLEVEL_CRAP, SILCTXT_LIST,
651                          name, users, topic ? topic : "");
652     }
653     break;
654     
655   case SILC_COMMAND_UMODE:
656     {
657       uint32 mode;
658       
659       if (!success)
660         return;
661       
662       mode = va_arg(vp, uint32);
663       
664       if (mode & SILC_UMODE_SERVER_OPERATOR)
665         printformat_module("fe-common/silc", server, NULL,
666                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
667
668       if (mode & SILC_UMODE_ROUTER_OPERATOR)
669         printformat_module("fe-common/silc", server, NULL,
670                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
671     }
672     break;
673     
674   case SILC_COMMAND_OPER:
675     if (!success)
676       return;
677
678     printformat_module("fe-common/silc", server, NULL,
679                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
680     break;
681     
682   case SILC_COMMAND_SILCOPER:
683     if (!success)
684       return;
685
686     printformat_module("fe-common/silc", server, NULL,
687                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
688     break;
689     
690   case SILC_COMMAND_USERS: 
691     {
692       SilcChannelEntry channel;
693       SilcChannelUser chu;
694       
695       if (!success)
696         return;
697       
698       channel = va_arg(vp, SilcChannelEntry);
699       
700       printformat_module("fe-common/silc", server, channel->channel_name,
701                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
702                          channel->channel_name);
703
704       silc_list_start(channel->clients);
705       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
706         SilcClientEntry e = chu->client;
707         char stat[5], *mode;
708
709         if (!e->nickname)
710           continue;
711         
712         memset(stat, 0, sizeof(stat));
713         mode = silc_client_chumode_char(chu->mode);
714         if (e->mode & SILC_UMODE_GONE)
715           strcat(stat, "G");
716         else
717           strcat(stat, "H");
718         if (mode)
719           strcat(stat, mode);
720
721         printformat_module("fe-common/silc", server, channel->channel_name,
722                            MSGLEVEL_CRAP, SILCTXT_USERS,
723                            e->nickname, stat, 
724                            e->username ? e->username : "",
725                            e->hostname ? e->hostname : "",
726                            e->realname ? e->realname : "");
727         if (mode)
728           silc_free(mode);
729       }
730     }
731     break;
732
733   case SILC_COMMAND_BAN:
734     {
735       SilcChannelEntry channel;
736       char *ban_list;
737       
738       if (!success)
739         return;
740       
741       channel = va_arg(vp, SilcChannelEntry);
742       ban_list = va_arg(vp, char *);
743       
744       if (ban_list)
745         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
746                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
747                            ban_list);
748       else
749         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
750                            SILCTXT_CHANNEL_NO_BAN_LIST, 
751                            channel->channel_name);
752     }
753     break;
754     
755   case SILC_COMMAND_GETKEY:
756     {
757       SilcIdType id_type;
758       void *entry;
759       SilcPublicKey public_key;
760       unsigned char *pk;
761       uint32 pk_len;
762       GetkeyContext getkey;
763       
764       if (!success)
765         return;
766       
767       id_type = va_arg(vp, uint32);
768       entry = va_arg(vp, void *);
769       public_key = va_arg(vp, SilcPublicKey);
770
771       if (public_key) {
772         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
773
774         getkey = silc_calloc(1, sizeof(*getkey));
775         getkey->entry = entry;
776         getkey->id_type = id_type;
777         getkey->client = client;
778         getkey->conn = conn;
779         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
780         
781         silc_verify_public_key_internal(client, conn, 
782                                         (id_type == SILC_ID_CLIENT ?
783                                          SILC_SOCKET_TYPE_CLIENT :
784                                          SILC_SOCKET_TYPE_SERVER),
785                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
786                                         silc_getkey_cb, getkey);
787         silc_free(pk);
788       } else {
789         printformat_module("fe-common/silc", server, NULL,
790                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
791       }
792     }
793     break;
794
795   case SILC_COMMAND_INFO:
796     {
797       SilcServerEntry server_entry;
798       char *server_name;
799       char *server_info;
800
801       if (!success)
802         return;
803       
804       server_entry = va_arg(vp, SilcServerEntry);
805       server_name = va_arg(vp, char *);
806       server_info = va_arg(vp, char *);
807
808       if (server_name && server_info )
809         {
810           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
811           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
812         }
813     }
814     break;
815     
816   case SILC_COMMAND_TOPIC:
817     {
818       SilcChannelEntry channel;
819       char *topic;
820       
821       if (!success)
822         return;
823       
824       channel = va_arg(vp, SilcChannelEntry);
825       topic = va_arg(vp, char *);
826       
827       if (topic) {
828         chanrec = silc_channel_find_entry(server, channel);
829         if (chanrec) {
830           g_free_not_null(chanrec->topic);
831           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
832           signal_emit("channel topic changed", 1, chanrec);
833         }
834         printformat_module("fe-common/silc", server, channel->channel_name,
835                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
836                            channel->channel_name, topic);
837       } else {
838         printformat_module("fe-common/silc", server, channel->channel_name,
839                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
840                            channel->channel_name);
841       }
842     }
843     break;
844
845   }
846
847   va_end(vp);
848 }
849
850 /* Internal routine to verify public key. If the `completion' is provided
851    it will be called to indicate whether public was verified or not. */
852
853 typedef struct {
854   SilcClient client;
855   SilcClientConnection conn;
856   char *filename;
857   char *entity;
858   unsigned char *pk;
859   uint32 pk_len;
860   SilcSKEPKType pk_type;
861   SilcVerifyPublicKey completion;
862   void *context;
863 } *PublicKeyVerify;
864
865 static void verify_public_key_completion(const char *line, void *context)
866 {
867   PublicKeyVerify verify = (PublicKeyVerify)context;
868
869   if (line[0] == 'Y' || line[0] == 'y') {
870     /* Call the completion */
871     if (verify->completion)
872       verify->completion(TRUE, verify->context);
873
874     /* Save the key for future checking */
875     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
876                                    verify->pk_len, SILC_PKCS_FILE_PEM);
877   } else {
878     /* Call the completion */
879     if (verify->completion)
880       verify->completion(FALSE, verify->context);
881
882     printformat_module("fe-common/silc", NULL, NULL,
883                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
884   }
885
886   silc_free(verify->filename);
887   silc_free(verify->entity);
888   silc_free(verify->pk);
889   silc_free(verify);
890 }
891
892 static void 
893 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
894                                 SilcSocketType conn_type, unsigned char *pk, 
895                                 uint32 pk_len, SilcSKEPKType pk_type,
896                                 SilcVerifyPublicKey completion, void *context)
897 {
898   int i;
899   char file[256], filename[256], *fingerprint, *babbleprint, *format;
900   struct passwd *pw;
901   struct stat st;
902   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
903                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
904                   "server" : "client");
905   PublicKeyVerify verify;
906
907   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
908     printformat_module("fe-common/silc", NULL, NULL,
909                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
910                        entity, pk_type);
911     if (completion)
912       completion(FALSE, context);
913     return;
914   }
915
916   pw = getpwuid(getuid());
917   if (!pw) {
918     if (completion)
919       completion(FALSE, context);
920     return;
921   }
922
923   memset(filename, 0, sizeof(filename));
924   memset(file, 0, sizeof(file));
925
926   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
927       conn_type == SILC_SOCKET_TYPE_ROUTER) {
928     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
929              conn->sock->ip, conn->sock->port);
930     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
931              pw->pw_dir, entity, file);
932   } else {
933     /* Replace all whitespaces with `_'. */
934     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
935     for (i = 0; i < strlen(fingerprint); i++)
936       if (fingerprint[i] == ' ')
937         fingerprint[i] = '_';
938     
939     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
940     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
941              pw->pw_dir, entity, file);
942     silc_free(fingerprint);
943   }
944
945   /* Take fingerprint of the public key */
946   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
947   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
948
949   verify = silc_calloc(1, sizeof(*verify));
950   verify->client = client;
951   verify->conn = conn;
952   verify->filename = strdup(filename);
953   verify->entity = strdup(entity);
954   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
955   memcpy(verify->pk, pk, pk_len);
956   verify->pk_len = pk_len;
957   verify->pk_type = pk_type;
958   verify->completion = completion;
959   verify->context = context;
960
961   /* Check whether this key already exists */
962   if (stat(filename, &st) < 0) {
963     /* Key does not exist, ask user to verify the key and save it */
964
965     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
966                        SILCTXT_PUBKEY_RECEIVED, entity);
967     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
968                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
969     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
970                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
971     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
972                              SILCTXT_PUBKEY_ACCEPT);
973     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
974                             format, 0, verify);
975     g_free(format);
976     silc_free(fingerprint);
977     return;
978   } else {
979     /* The key already exists, verify it. */
980     SilcPublicKey public_key;
981     unsigned char *encpk;
982     uint32 encpk_len;
983
984     /* Load the key file */
985     if (!silc_pkcs_load_public_key(filename, &public_key, 
986                                    SILC_PKCS_FILE_PEM))
987       if (!silc_pkcs_load_public_key(filename, &public_key, 
988                                      SILC_PKCS_FILE_BIN)) {
989         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
990                            SILCTXT_PUBKEY_RECEIVED, entity);
991         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
992                            SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
993         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
994                            SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
995         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
996                            SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
997         format = format_get_text("fe-common/silc", NULL, NULL, NULL,
998                                  SILCTXT_PUBKEY_ACCEPT_ANYWAY);
999         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1000                                 format, 0, verify);
1001         g_free(format);
1002         silc_free(fingerprint);
1003         return;
1004       }
1005   
1006     /* Encode the key data */
1007     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1008     if (!encpk) {
1009       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1010                          SILCTXT_PUBKEY_RECEIVED, entity);
1011       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1012                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1013       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1014                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1015       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1016                          SILCTXT_PUBKEY_MALFORMED, entity);
1017       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1018                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1019       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1020                               format, 0, verify);
1021       g_free(format);
1022       silc_free(fingerprint);
1023       return;
1024     }
1025
1026     /* Compare the keys */
1027     if (memcmp(encpk, pk, encpk_len)) {
1028       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1029                          SILCTXT_PUBKEY_RECEIVED, entity);
1030       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1031                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1032       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1033                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1034       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1035                          SILCTXT_PUBKEY_NO_MATCH, entity);
1036       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1037                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1038       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1039                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1040
1041       /* Ask user to verify the key and save it */
1042       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1043                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1044       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1045                               format, 0, verify);
1046       g_free(format);
1047       silc_free(fingerprint);
1048       return;
1049     }
1050
1051     /* Local copy matched */
1052     if (completion)
1053       completion(TRUE, context);
1054     silc_free(fingerprint);
1055   }
1056 }
1057
1058 /* Verifies received public key. The `conn_type' indicates which entity
1059    (server, client etc.) has sent the public key. If user decides to trust
1060    the key may be saved as trusted public key for later use. The 
1061    `completion' must be called after the public key has been verified. */
1062
1063 void 
1064 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1065                        SilcSocketType conn_type, unsigned char *pk, 
1066                        uint32 pk_len, SilcSKEPKType pk_type,
1067                        SilcVerifyPublicKey completion, void *context)
1068 {
1069   silc_verify_public_key_internal(client, conn, conn_type, pk,
1070                                   pk_len, pk_type,
1071                                   completion, context);
1072 }
1073
1074 /* Asks passphrase from user on the input line. */
1075
1076 typedef struct {
1077   SilcAskPassphrase completion;
1078   void *context;
1079 } *AskPassphrase;
1080
1081 void ask_passphrase_completion(const char *passphrase, void *context)
1082 {
1083   AskPassphrase p = (AskPassphrase)context;
1084   p->completion((unsigned char *)passphrase, 
1085                 passphrase ? strlen(passphrase) : 0, p->context);
1086   silc_free(p);
1087 }
1088
1089 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1090                          SilcAskPassphrase completion, void *context)
1091 {
1092   AskPassphrase p = silc_calloc(1, sizeof(*p));
1093   p->completion = completion;
1094   p->context = context;
1095
1096   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1097                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1098 }
1099
1100 typedef struct {
1101   SilcGetAuthMeth completion;
1102   void *context;
1103 } *InternalGetAuthMethod;
1104
1105 /* Callback called when we've received the authentication method information
1106    from the server after we've requested it. This will get the authentication
1107    data from the user if needed. */
1108
1109 static void silc_get_auth_method_callback(SilcClient client,
1110                                           SilcClientConnection conn,
1111                                           SilcAuthMethod auth_meth,
1112                                           void *context)
1113 {
1114   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1115
1116   SILC_LOG_DEBUG(("Start"));
1117
1118   switch (auth_meth) {
1119   case SILC_AUTH_NONE:
1120     /* No authentication required. */
1121     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1122     break;
1123   case SILC_AUTH_PASSWORD:
1124     /* Do not ask the passphrase from user, the library will ask it if
1125        we do not provide it here. */
1126     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1127     break;
1128   case SILC_AUTH_PUBLIC_KEY:
1129     /* Do not get the authentication data now, the library will generate
1130        it using our default key, if we do not provide it here. */
1131     /* XXX In the future when we support multiple local keys and multiple
1132        local certificates we will need to ask from user which one to use. */
1133     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1134     break;
1135   }
1136
1137   silc_free(internal);
1138 }
1139
1140 /* Find authentication method and authentication data by hostname and
1141    port. The hostname may be IP address as well. The found authentication
1142    method and authentication data is returned to `auth_meth', `auth_data'
1143    and `auth_data_len'. The function returns TRUE if authentication method
1144    is found and FALSE if not. `conn' may be NULL. */
1145
1146 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1147                           char *hostname, uint16 port,
1148                           SilcGetAuthMeth completion, void *context)
1149 {
1150   InternalGetAuthMethod internal;
1151
1152   SILC_LOG_DEBUG(("Start"));
1153
1154   /* XXX must resolve from configuration whether this connection has
1155      any specific authentication data */
1156
1157   /* If we do not have this connection configured by the user in a
1158      configuration file then resolve the authentication method from the
1159      server for this session. */
1160   internal = silc_calloc(1, sizeof(*internal));
1161   internal->completion = completion;
1162   internal->context = context;
1163
1164   silc_client_request_authentication_method(client, conn, 
1165                                             silc_get_auth_method_callback,
1166                                             internal);
1167 }
1168
1169 /* Notifies application that failure packet was received.  This is called
1170    if there is some protocol active in the client.  The `protocol' is the
1171    protocol context.  The `failure' is opaque pointer to the failure
1172    indication.  Note, that the `failure' is protocol dependant and application
1173    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1174    failure type (see protocol specs for all protocol failure types). */
1175
1176 void silc_failure(SilcClient client, SilcClientConnection conn, 
1177                   SilcProtocol protocol, void *failure)
1178 {
1179   SILC_LOG_DEBUG(("Start"));
1180
1181   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1182     SilcSKEStatus status = (SilcSKEStatus)failure;
1183     
1184     if (status == SILC_SKE_STATUS_BAD_VERSION)
1185       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1186                          SILCTXT_KE_BAD_VERSION);
1187     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1188       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1189                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1190     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1191       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1192                          SILCTXT_KE_UNKNOWN_GROUP);
1193     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1194       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1195                          SILCTXT_KE_UNKNOWN_CIPHER);
1196     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1197       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1198                          SILCTXT_KE_UNKNOWN_PKCS);
1199     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1200       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1201                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1202     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1203       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1204                          SILCTXT_KE_UNKNOWN_HMAC);
1205     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1206       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1207                          SILCTXT_KE_INCORRECT_SIGNATURE);
1208     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1209       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1210                          SILCTXT_KE_INVALID_COOKIE);
1211   }
1212
1213   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1214     uint32 err = (uint32)failure;
1215
1216     if (err == SILC_AUTH_FAILED)
1217       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1218                          SILCTXT_AUTH_FAILED);
1219   }
1220 }
1221
1222 /* Asks whether the user would like to perform the key agreement protocol.
1223    This is called after we have received an key agreement packet or an
1224    reply to our key agreement packet. This returns TRUE if the user wants
1225    the library to perform the key agreement protocol and FALSE if it is not
1226    desired (application may start it later by calling the function
1227    silc_client_perform_key_agreement). */
1228
1229 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1230                        SilcClientEntry client_entry, const char *hostname,
1231                        uint16 port, SilcKeyAgreementCallback *completion,
1232                        void **context)
1233 {
1234   char portstr[12];
1235
1236   SILC_LOG_DEBUG(("Start"));
1237
1238   /* We will just display the info on the screen and return FALSE and user
1239      will have to start the key agreement with a command. */
1240
1241   if (hostname) 
1242     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1243
1244   if (!hostname)
1245     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1246                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1247   else
1248     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1249                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1250                        client_entry->nickname, hostname, portstr);
1251
1252   *completion = NULL;
1253   *context = NULL;
1254
1255   return FALSE;
1256 }
1257
1258 void silc_ftp(SilcClient client, SilcClientConnection conn,
1259               SilcClientEntry client_entry, uint32 session_id,
1260               const char *hostname, uint16 port)
1261 {
1262   SILC_SERVER_REC *server;
1263   char portstr[12];
1264   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1265
1266   SILC_LOG_DEBUG(("Start"));
1267
1268   server = conn->context;
1269
1270   ftp->client_entry = client_entry;
1271   ftp->session_id = session_id;
1272   ftp->send = FALSE;
1273   ftp->conn = conn;
1274   silc_dlist_add(server->ftp_sessions, ftp);
1275   server->current_session = ftp;
1276
1277   if (hostname) 
1278     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1279
1280   if (!hostname)
1281     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1282                        SILCTXT_FILE_REQUEST, client_entry->nickname);
1283   else
1284     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1285                        SILCTXT_FILE_REQUEST_HOST, 
1286                        client_entry->nickname, hostname, portstr);
1287 }
1288
1289 /* SILC client operations */
1290 SilcClientOperations ops = {
1291   silc_say,
1292   silc_channel_message,
1293   silc_private_message,
1294   silc_notify,
1295   silc_command,
1296   silc_command_reply,
1297   silc_connect,
1298   silc_disconnect,
1299   silc_get_auth_method,
1300   silc_verify_public_key,
1301   silc_ask_passphrase,
1302   silc_failure,
1303   silc_key_agreement,
1304   silc_ftp,
1305 };