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