Shorter timeout for channel joining notify.
[runtime.git] / apps / silcd / command.c
1 /*
2
3   command.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 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  * $Id$
22  * $Log$
23  * Revision 1.2  2000/06/28 05:06:38  priikone
24  *      Shorter timeout for channel joining notify.
25  *
26  * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
27  *      Importet from internal CVS/Added Log headers.
28  *
29  *
30  */
31
32 #include "serverincludes.h"
33 #include "server_internal.h"
34
35 /* Server command list. */
36 SilcServerCommand silc_command_list[] =
37 {
38   SILC_SERVER_CMD(whois, WHOIS, SILC_CF_LAG | SILC_CF_REG),
39   SILC_SERVER_CMD(whowas, WHOWAS, SILC_CF_LAG | SILC_CF_REG),
40   SILC_SERVER_CMD(identify, IDENTIFY, SILC_CF_LAG | SILC_CF_REG),
41   SILC_SERVER_CMD(nick, NICK, SILC_CF_LAG | SILC_CF_REG),
42   SILC_SERVER_CMD(list, LIST, SILC_CF_LAG | SILC_CF_REG),
43   SILC_SERVER_CMD(topic, TOPIC, SILC_CF_LAG | SILC_CF_REG),
44   SILC_SERVER_CMD(invite, INVITE, SILC_CF_LAG | SILC_CF_REG),
45   SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG),
46   SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
47   SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG),
48   SILC_SERVER_CMD(connect, CONNECT, 
49                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
50   SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG),
51   SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
52   SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG | SILC_CF_REG),
53   SILC_SERVER_CMD(motd, MOTD, SILC_CF_LAG | SILC_CF_REG),
54   SILC_SERVER_CMD(umode, UMODE, SILC_CF_LAG | SILC_CF_REG),
55   SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG | SILC_CF_REG),
56   SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG | SILC_CF_REG),
57   SILC_SERVER_CMD(restart, RESTART, 
58                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
59   SILC_SERVER_CMD(close, CLOSE,
60                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
61   SILC_SERVER_CMD(die, DIE, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
62   SILC_SERVER_CMD(silcoper, SILCOPER,
63                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
64   SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG | SILC_CF_REG),
65   SILC_SERVER_CMD(names, NAMES, SILC_CF_LAG | SILC_CF_REG),
66
67   { NULL, 0 },
68 };
69
70 /* List of pending commands. */
71 SilcServerCommandPending *silc_command_pending = NULL;
72
73 /* Add new pending command to the list of pending commands. Currently
74    pending commands are executed from command replies, thus we can
75    execute any command after receiving some specific command reply.
76
77    The argument `reply_cmd' is the command reply from where the callback
78    function is to be called, thus, it IS NOT the command to be executed. */
79
80 void silc_server_command_pending(SilcCommand reply_cmd,
81                                  SilcCommandCb callback,
82                                  void *context)
83 {
84   SilcServerCommandPending *reply, *r;
85
86   reply = silc_calloc(1, sizeof(*reply));
87   reply->reply_cmd = reply_cmd;
88   reply->context = context;
89   reply->callback = callback;
90
91   if (silc_command_pending == NULL) {
92     silc_command_pending = reply;
93     return;
94   }
95
96   for (r = silc_command_pending; r; r = r->next) {
97     if (r->next == NULL) {
98       r->next = reply;
99       break;
100     }
101   }
102 }
103
104 /* Deletes pending command by reply command type. */
105
106 void silc_server_command_pending_del(SilcCommand reply_cmd)
107 {
108   SilcServerCommandPending *r, *tmp;
109   
110   if (silc_command_pending) {
111     if (silc_command_pending->reply_cmd == reply_cmd) {
112       silc_free(silc_command_pending);
113       silc_command_pending = NULL;
114       return;
115     }
116
117     for (r = silc_command_pending; r; r = r->next) {
118       if (r->next && r->next->reply_cmd == reply_cmd) {
119         tmp = r->next;
120         r->next = r->next->next;
121         silc_free(tmp);
122         break;
123       }
124     }
125   }
126 }
127
128 /* Free's the command context allocated before executing the command */
129
130 static void silc_server_command_free(SilcServerCommandContext cmd)
131 {
132   if (cmd) {
133     silc_command_free_payload(cmd->payload);
134     silc_free(cmd);
135   }
136 }
137
138 /* Sends command status message as command reply packet. */
139
140 static void 
141 silc_server_command_send_status_msg(SilcServerCommandContext cmd,
142                                     SilcCommand command,
143                                     SilcCommandStatus status,
144                                     unsigned char *msg,
145                                     unsigned int msg_len)
146 {
147   SilcBuffer sp_buf, buffer;
148
149   SILC_LOG_DEBUG(("Sending command status %d", status));
150
151   sp_buf = silc_command_encode_status_payload(status, msg, msg_len);
152   buffer = silc_command_encode_payload_va(command, 1, 
153                                           sp_buf->data, sp_buf->len);
154   silc_server_packet_send(cmd->server, cmd->sock,
155                           SILC_PACKET_COMMAND_REPLY, 0, 
156                           buffer->data, buffer->len, FALSE);
157   silc_buffer_free(buffer);
158   silc_buffer_free(sp_buf);
159 }
160
161 /* Sends simple status message as command reply packet */
162
163 static void 
164 silc_server_command_send_status_reply(SilcServerCommandContext cmd,
165                                       SilcCommand command,
166                                       SilcCommandStatus status)
167 {
168   SilcBuffer sp_buf, buffer;
169
170   SILC_LOG_DEBUG(("Sending command status %d", status));
171
172   sp_buf = silc_command_encode_status_payload(status, NULL, 0);
173   buffer = silc_command_encode_payload_va(command, 1, 
174                                           sp_buf->data, sp_buf->len);
175   silc_server_packet_send(cmd->server, cmd->sock,
176                           SILC_PACKET_COMMAND_REPLY, 0, 
177                           buffer->data, buffer->len, FALSE);
178   silc_buffer_free(buffer);
179   silc_buffer_free(sp_buf);
180 }
181
182 /* Server side of command WHOIS. Processes user's query and sends found 
183    results as command replies back to the client. */
184
185 SILC_SERVER_CMD_FUNC(whois)
186 {
187   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
188   char *tmp, *nick = NULL, *server = NULL;
189   unsigned int argc, count = 0, len;
190   SilcClientList *entry;
191   SilcBuffer sp_buf, packet;
192   unsigned char *id_string;
193
194   SILC_LOG_DEBUG(("Start"));
195
196   argc = silc_command_get_arg_num(cmd->payload);
197   if (argc < 1) {
198     silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
199                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
200     goto out;
201   }
202   if (argc > 2) {
203     silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
204                                           SILC_STATUS_ERR_TOO_MANY_PARAMS);
205     goto out;
206   }
207
208   /* Get the nickname@server string and parse it. */
209   tmp = silc_command_get_first_arg(cmd->payload, NULL);
210   if (tmp) {
211     if (strchr(tmp, '@')) {
212       len = strcspn(tmp, "@");
213       nick = silc_calloc(len + 1, sizeof(char));
214       memcpy(nick, tmp, len);
215       server = silc_calloc(strlen(tmp) - len, sizeof(char));
216       memcpy(server, tmp + len + 1, strlen(tmp) - len - 1);
217     } else {
218       nick = strdup(tmp);
219     }
220   } else {
221     silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
222                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
223     goto out;
224   }
225
226   /* Get the max count of reply messages allowed */
227   if (argc == 2) {
228     tmp = silc_command_get_next_arg(cmd->payload, NULL);
229     if (!tmp) {
230       silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
231                                             SILC_STATUS_ERR_TOO_MANY_PARAMS);
232       if (nick)
233         silc_free(nick);
234       if (server)
235         silc_free(server);
236       goto out;
237     }
238     count = atoi(tmp);
239   }
240
241   /* Then, make the query from our local client list */
242   entry = silc_idlist_find_client_by_nickname(cmd->server->local_list->clients,
243                                               nick, server);
244   if (!entry) {
245
246     /* If we are normal server and are connected to a router we will
247        make global query from the router. */
248     if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone) {
249
250       goto ok;
251     }
252     
253     /* If we are router then we will check our global list as well. */
254     if (cmd->server->server_type == SILC_ROUTER) {
255       entry =
256         silc_idlist_find_client_by_nickname(cmd->server->global_list->clients,
257                                             nick, server);
258       if (!entry) {
259         silc_server_command_send_status_msg(cmd, SILC_COMMAND_WHOIS,
260                                             SILC_STATUS_ERR_NO_SUCH_NICK,
261                                             tmp, strlen(tmp));
262         goto out;
263       }
264       goto ok;
265     }
266
267     silc_server_command_send_status_msg(cmd, SILC_COMMAND_WHOIS,
268                                         SILC_STATUS_ERR_NO_SUCH_NICK,
269                                         tmp, strlen(tmp));
270     goto out;
271   }
272
273  ok:
274   /* XXX, works only for local server info */
275
276   /* Send WHOIS reply */
277   id_string = silc_id_id2str(entry->id, SILC_ID_CLIENT);
278   tmp = silc_command_get_first_arg(cmd->payload, NULL),
279   sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
280
281   /* XXX */
282   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
283     char nh[256], uh[256];
284     SilcSocketConnection hsock;
285
286     memset(uh, 0, sizeof(uh));
287     memset(nh, 0, sizeof(nh));
288
289     strncat(nh, entry->nickname, strlen(entry->nickname));
290     strncat(nh, "@", 1);
291     len = entry->router ? strlen(entry->router->server_name) :
292       strlen(cmd->server->server_name);
293     strncat(nh, entry->router ? entry->router->server_name :
294             cmd->server->server_name, len);
295
296     strncat(uh, entry->username, strlen(entry->username));
297     strncat(uh, "@", 1);
298     hsock = (SilcSocketConnection)entry->connection;
299     len = hsock->hostname ? strlen(hsock->hostname) : strlen(hsock->ip);
300     strncat(uh, hsock->hostname ? hsock->hostname : hsock->ip, len);
301
302     /* XXX */
303     if (entry->userinfo)
304       packet = 
305         silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 5, 
306                                        sp_buf->data, sp_buf->len,
307                                        id_string, SILC_ID_CLIENT_LEN,
308                                        nh, strlen(nh),
309                                        uh, strlen(uh),
310                                        entry->userinfo, 
311                                        strlen(entry->userinfo));
312     else
313       packet = 
314         silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 4,
315                                        sp_buf->data, sp_buf->len,
316                                        id_string, SILC_ID_CLIENT_LEN,
317                                        nh, strlen(nh),
318                                        uh, strlen(uh));
319
320   } else {
321     /* XXX */
322     packet = 
323       silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 4, 
324                                      sp_buf->data, sp_buf->len,
325                                      id_string, SILC_ID_CLIENT_LEN,
326                                      entry->nickname, strlen(entry->nickname),
327                                      tmp, strlen(tmp)); /* XXX */
328   }
329   silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
330                           0, packet->data, packet->len, FALSE);
331
332   silc_free(id_string);
333   silc_buffer_free(packet);
334   silc_free(sp_buf);
335
336  out:
337   silc_server_command_free(cmd);
338 }
339
340 SILC_SERVER_CMD_FUNC(whowas)
341 {
342 }
343
344 SILC_SERVER_CMD_FUNC(identify)
345 {
346   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
347   char *tmp, *nick = NULL, *server = NULL;
348   unsigned int argc, count = 0, len;
349   SilcClientList *entry;
350   SilcBuffer sp_buf, packet;
351   unsigned char *id_string;
352
353   SILC_LOG_DEBUG(("Start"));
354
355   argc = silc_command_get_arg_num(cmd->payload);
356   if (argc < 1) {
357     silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
358                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
359     goto out;
360   }
361   if (argc > 2) {
362     silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
363                                           SILC_STATUS_ERR_TOO_MANY_PARAMS);
364     goto out;
365   }
366
367   /* Get the nickname@server string and parse it. */
368   tmp = silc_command_get_first_arg(cmd->payload, NULL);
369   if (tmp) {
370     if (strchr(tmp, '@')) {
371       len = strcspn(tmp, "@");
372       nick = silc_calloc(len + 1, sizeof(char));
373       memcpy(nick, tmp, len);
374       server = silc_calloc(strlen(tmp) - len, sizeof(char));
375       memcpy(server, tmp + len + 1, strlen(tmp) - len - 1);
376     } else {
377       nick = strdup(tmp);
378     }
379   } else {
380     silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
381                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
382     goto out;
383   }
384
385   /* Get the max count of reply messages allowed */
386   if (argc == 2) {
387     tmp = silc_command_get_next_arg(cmd->payload, NULL);
388     if (!tmp) {
389       silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
390                                             SILC_STATUS_ERR_TOO_MANY_PARAMS);
391       goto out;
392     }
393     count = atoi(tmp);
394   }
395
396   /* Then, make the query from our local client list */
397   entry = silc_idlist_find_client_by_hash(cmd->server->local_list->clients,
398                                           nick, cmd->server->md5hash);
399   if (!entry) {
400
401     /* If we are normal server and are connected to a router we will
402        make global query from the router. */
403     if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone) {
404       SilcBuffer buffer = cmd->packet->buffer;
405
406       /* Forward the received IDENTIFY command to our router */
407       silc_buffer_push(buffer, buffer->data - buffer->head);
408       silc_server_packet_forward(cmd->server, (SilcSocketConnection)
409                                  cmd->server->id_entry->router->connection,
410                                  buffer->data, buffer->len,
411                                  TRUE);
412       goto out;
413     }
414     
415     /* If we are router then we will check our global list as well. */
416     if (cmd->server->server_type == SILC_ROUTER) {
417       entry = 
418         silc_idlist_find_client_by_hash(cmd->server->global_list->clients,
419                                         nick, cmd->server->md5hash);
420       if (!entry) {
421         silc_server_command_send_status_msg(cmd, SILC_COMMAND_IDENTIFY,
422                                             SILC_STATUS_ERR_NO_SUCH_NICK,
423                                             tmp, strlen(tmp));
424         goto out;
425       }
426       goto ok;
427     }
428
429     silc_server_command_send_status_msg(cmd, SILC_COMMAND_IDENTIFY,
430                                         SILC_STATUS_ERR_NO_SUCH_NICK,
431                                         tmp, strlen(tmp));
432     goto out;
433   }
434
435  ok:
436   /* Send IDENTIFY reply */
437   id_string = silc_id_id2str(entry->id, SILC_ID_CLIENT);
438   tmp = silc_command_get_first_arg(cmd->payload, NULL);
439   sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
440   packet = silc_command_encode_payload_va(SILC_COMMAND_IDENTIFY, 3,
441                                           sp_buf->data, sp_buf->len,
442                                           id_string, SILC_ID_CLIENT_LEN,
443                                           nick, strlen(nick));
444   if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
445     void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
446     silc_server_packet_send_dest(cmd->server, cmd->sock, 
447                                  SILC_PACKET_COMMAND_REPLY, 0,
448                                  id, cmd->packet->src_id_type,
449                                  packet->data, packet->len, FALSE);
450     silc_free(id);
451   } else
452     silc_server_packet_send(cmd->server, cmd->sock, 
453                             SILC_PACKET_COMMAND_REPLY, 0, 
454                             packet->data, packet->len, FALSE);
455
456   silc_free(id_string);
457   silc_buffer_free(packet);
458   silc_free(sp_buf);
459
460  out:
461   if (nick)
462     silc_free(nick);
463   if (server)
464     silc_free(server);
465   silc_server_command_free(cmd);
466 }
467
468 /* Checks string for bad characters and returns TRUE if they are found. */
469
470 static int silc_server_command_bad_chars(char *nick)
471 {
472   if (strchr(nick, '\\')) return TRUE;
473   if (strchr(nick, '\"')) return TRUE;
474   if (strchr(nick, '´')) return TRUE;
475   if (strchr(nick, '`')) return TRUE;
476   if (strchr(nick, '\'')) return TRUE;
477   if (strchr(nick, '*')) return TRUE;
478   if (strchr(nick, '/')) return TRUE;
479   if (strchr(nick, '@')) return TRUE;
480
481   return FALSE;
482 }
483
484 /* Server side of command NICK. Sets nickname for user. Setting
485    nickname causes generation of a new client ID for the client. The
486    new client ID is sent to the client after changing the nickname. */
487
488 SILC_SERVER_CMD_FUNC(nick)
489 {
490   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
491   SilcClientList *id_entry = (SilcClientList *)cmd->sock->user_data;
492   SilcServer server = cmd->server;
493   SilcBuffer packet, sp_buf;
494   SilcClientID *new_id;
495   char *id_string;
496   char *nick;
497
498   SILC_LOG_DEBUG(("Start"));
499
500 #define LCC(x) server->local_list->client_cache[(x) - 32]
501 #define LCCC(x) server->local_list->client_cache_count[(x) - 32]
502
503   /* Check number of arguments */
504   if (silc_command_get_arg_num(cmd->payload) < 1) {
505     silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
506                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
507     goto out;
508   }
509
510   /* Check nickname */
511   nick = silc_command_get_arg_type(cmd->payload, 1, NULL);
512   if (silc_server_command_bad_chars(nick) == TRUE) {
513     silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
514                                           SILC_STATUS_ERR_BAD_NICKNAME);
515     goto out;
516   }
517
518   /* Create new Client ID */
519   silc_id_create_client_id(cmd->server->id, cmd->server->rng, 
520                            cmd->server->md5hash, nick,
521                            &new_id);
522
523   /* Send notify about nickname change to our router. We send the new
524      ID and ask to replace it with the old one. */
525   if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone)
526     silc_server_send_replace_id(server, server->id_entry->router->connection, 
527                                 FALSE, id_entry->id,
528                                 SILC_ID_CLIENT, SILC_ID_CLIENT_LEN,
529                                 new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
530
531   /* If we are router we have to distribute the new Client ID to all 
532      routers in SILC. */
533   if (cmd->server->server_type == SILC_ROUTER && !cmd->server->standalone)
534     silc_server_send_replace_id(server, server->id_entry->router->connection,  
535                                 TRUE, id_entry->id,
536                                 SILC_ID_CLIENT, SILC_ID_CLIENT_LEN,
537                                 new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
538
539   /* Remove old cache entry */
540   silc_idcache_del_by_id(LCC(id_entry->nickname[0]),
541                          LCCC(id_entry->nickname[0]), 
542                          SILC_ID_CLIENT, id_entry->id); 
543   
544   /* Free old ID */
545   if (id_entry->id) {
546     memset(id_entry->id, 0, SILC_ID_CLIENT_LEN);
547     silc_free(id_entry->id);
548   }
549
550   /* Save the nickname as this client is our local client */
551   if (id_entry->nickname)
552     silc_free(id_entry->nickname);
553
554   id_entry->nickname = strdup(nick);
555   id_entry->id = new_id;
556
557   /* Update client cache */
558   LCCC(nick[0]) = silc_idcache_add(&LCC(nick[0]), LCCC(nick[0]),
559                                    id_entry->nickname, SILC_ID_CLIENT, 
560                                    id_entry->id, (void *)id_entry);
561
562   /* Send the new Client ID as reply command back to client */
563   id_string = silc_id_id2str(id_entry->id, SILC_ID_CLIENT);
564   sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
565   packet = silc_command_encode_payload_va(SILC_COMMAND_NICK, 2, 
566                                           sp_buf->data, sp_buf->len,
567                                           id_string, SILC_ID_CLIENT_LEN);
568   silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
569                           0, packet->data, packet->len, FALSE);
570
571   silc_free(id_string);
572   silc_buffer_free(packet);
573   silc_free(sp_buf);
574
575  out:
576   silc_server_command_free(cmd);
577 #undef LCC
578 #undef LCCC
579 }
580
581 SILC_SERVER_CMD_FUNC(list)
582 {
583 }
584
585 SILC_SERVER_CMD_FUNC(topic)
586 {
587 }
588
589 SILC_SERVER_CMD_FUNC(invite)
590 {
591 }
592
593 /* Quits connection to client. This gets called if client won't
594    close the connection even when it has issued QUIT command. */
595
596 SILC_TASK_CALLBACK(silc_server_command_quit_cb)
597 {
598   SilcServer server = (SilcServer)context;
599   SilcSocketConnection sock = server->sockets[fd];
600
601   /* Free all client specific data, such as client entry and entires
602      on channels this client may be on. */
603   silc_server_free_sock_user_data(server, sock);
604
605   /* Close the connection on our side */
606   silc_server_close_connection(server, sock);
607 }
608
609 /* Quits SILC session. This is the normal way to disconnect client. */
610  
611 SILC_SERVER_CMD_FUNC(quit)
612 {
613   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
614   SilcServer server = cmd->server;
615   SilcSocketConnection sock = cmd->sock;
616
617   SILC_LOG_DEBUG(("Start"));
618
619   /* We quit the connection with little timeout */
620   silc_task_register(server->timeout_queue, sock->sock,
621                      silc_server_command_quit_cb, server,
622                      0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
623
624   silc_server_command_free(cmd);
625 }
626
627 SILC_SERVER_CMD_FUNC(kill)
628 {
629 }
630
631 SILC_SERVER_CMD_FUNC(info)
632 {
633 }
634
635 SILC_SERVER_CMD_FUNC(connect)
636 {
637 }
638
639 SILC_SERVER_CMD_FUNC(ping)
640 {
641 }
642
643 SILC_SERVER_CMD_FUNC(oper)
644 {
645 }
646
647 typedef struct {
648   char *channel_name;
649   char *nickname;
650   char *username;
651   char *hostname;
652   SilcChannelList *channel;
653   SilcServer server;
654 } JoinInternalContext;
655
656 SILC_TASK_CALLBACK(silc_server_command_join_notify)
657 {
658   JoinInternalContext *ctx = (JoinInternalContext *)context;
659
660   if (ctx->channel->key && ctx->channel->key_len) {
661     silc_server_send_notify_to_channel(ctx->server, ctx->channel,
662                                        "%s (%s@%s) has joined channel %s",
663                                        ctx->nickname, ctx->username,
664                                        ctx->hostname, ctx->channel_name);
665     silc_free(ctx);
666   } else {
667     silc_task_register(ctx->server->timeout_queue, fd,
668                        silc_server_command_join_notify, context,
669                        0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
670   }
671 }
672
673 /* Server side of command JOIN. Joins client into requested channel. If 
674    the channel does not exist it will be created. */
675
676 SILC_SERVER_CMD_FUNC(join)
677 {
678   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
679   SilcServer server = cmd->server;
680   SilcSocketConnection sock = cmd->sock;
681   SilcBuffer buffer = cmd->packet->buffer;
682   int argc, i, tmp_len;
683   char *tmp, *channel_name = NULL, *cipher = NULL, *id_string = NULL;
684   unsigned char *passphrase;
685   SilcChannelList *channel;
686   SilcServerID *router_id;
687   SilcIDCache *id_cache;
688   SilcBuffer packet, sp_buf;
689   SilcClientList *client;
690
691   SILC_LOG_DEBUG(("Start"));
692
693 #define LCC(x) server->local_list->channel_cache[(x) - 32]
694 #define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
695
696   /* Check number of parameters */
697   argc = silc_command_get_arg_num(cmd->payload);
698   if (argc < 1) {
699     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
700                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
701     goto out;
702   }
703   if (argc > 3) {
704     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
705                                           SILC_STATUS_ERR_TOO_MANY_PARAMS);
706     goto out;
707   }
708
709   /* Get channel name */
710   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
711   if (silc_server_command_bad_chars(tmp) == TRUE) {
712     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
713                                           SILC_STATUS_ERR_BAD_CHANNEL);
714     goto out;
715   }
716   channel_name = strdup(tmp);
717
718   /* Get passphrase */
719   tmp = silc_command_get_arg_type(cmd->payload, 2, &tmp_len);
720   if (tmp) {
721     passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
722     memcpy(passphrase, tmp, tmp_len);
723   }
724   
725   /* Get cipher name */
726   cipher = silc_command_get_arg_type(cmd->payload, 3, NULL);
727
728   /* See if the channel exists */
729   if (silc_idcache_find_by_data(LCC(channel_name[0]), LCCC(channel_name[0]), 
730                                 channel_name, &id_cache) == FALSE) {
731     /* Channel not found */
732     id_cache = NULL;
733
734     /* If we are standalone server we don't have a router, we just create 
735        the channel by  ourselves. */
736     if (server->standalone) {
737       router_id = server->id;
738       channel = silc_server_new_channel(server, router_id, 
739                                         cipher, channel_name);
740       goto join_channel;
741     }
742
743     /* No channel ID found, the channel does not exist on our server.
744        We send JOIN command to our router which will handle the joining
745        procedure (either creates the channel if it doesn't exist or
746        joins the client to it) - if we are normal server. */
747     if (server->server_type == SILC_SERVER) {
748
749       /* Forward the received JOIN command to the router */
750       silc_buffer_push(buffer, buffer->data - buffer->head);
751       silc_server_packet_forward(server, (SilcSocketConnection)
752                                  server->id_entry->router->connection,
753                                  buffer->data, buffer->len,
754                                  TRUE);
755       
756       /* Add the command to be pending. It will be re-executed after
757          router has replied back to us. */
758       cmd->pending = TRUE;
759       silc_server_command_pending(SILC_COMMAND_JOIN, 
760                                   silc_server_command_join, context);
761       return;
762     }
763   }
764
765   /* If we are router and the channel does not exist we will check our
766      global list for the channel. */
767   if (!id_cache && server->server_type == SILC_ROUTER) {
768
769     /* Notify all routers about the new channel in SILC network. */
770     if (!server->standalone) {
771 #if 0
772       silc_server_send_new_id(server, server->id_entry->router->connection, 
773                               TRUE,
774                               xxx, SILC_ID_CHANNEL, SILC_ID_CHANNEL_LEN);
775 #endif
776     }
777
778   }
779
780   channel = (SilcChannelList *)id_cache->context;
781
782  join_channel:
783
784   /* XXX must check whether the client already is on the channel */
785
786   /* Join the client to the channel */
787   i = channel->user_list_count;
788   channel->user_list = silc_realloc(channel->user_list, 
789                                     sizeof(*channel->user_list) * (i + 1));
790   channel->user_list[i].mode = SILC_CHANNEL_UMODE_NONE;
791
792   /* If the JOIN request was forwarded to us we will make a bit slower
793      query to get the client pointer. Otherwise, we get the client pointer
794      real easy. */
795   if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
796     client = (SilcClientList *)sock->user_data;
797     channel->user_list[i].client = client;
798   } else {
799     void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
800     client = silc_idlist_find_client_by_id(server->local_list->clients, id);
801     channel->user_list[i].client = client;
802     silc_free(id);
803   }
804   channel->user_list_count++;
805
806   i = client->channel_count;
807   client->channel = silc_realloc(client->channel, 
808                                  sizeof(*client->channel) * (i + 1));
809   client->channel[i] = channel;
810   client->channel_count++;
811
812   /* Notify router about new user on channel. If we are normal server
813      we send it to our router, if we are router we send it to our
814      primary route. */
815   if (!server->standalone) {
816
817   }
818
819   /* Send Channel ID to the client */
820   if (!cmd->pending) {
821     id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
822     sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
823     if (!channel->topic)
824       packet = 
825         silc_command_encode_payload_va(SILC_COMMAND_JOIN, 3,
826                                        sp_buf->data, sp_buf->len,
827                                        channel_name, strlen(channel_name),
828                                        id_string, SILC_ID_CHANNEL_LEN);
829     else
830       packet = 
831         silc_command_encode_payload_va(SILC_COMMAND_JOIN, 4,
832                                        sp_buf->data, sp_buf->len,
833                                        channel_name, strlen(channel_name),
834                                        id_string, SILC_ID_CHANNEL_LEN,
835                                        channel->topic, 
836                                        strlen(channel->topic));
837
838     if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
839       void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
840       silc_server_packet_send_dest(cmd->server, cmd->sock, 
841                                    SILC_PACKET_COMMAND_REPLY, 0,
842                                    id, cmd->packet->src_id_type,
843                                    packet->data, packet->len, FALSE);
844       silc_free(id);
845     } else
846       silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
847                               packet->data, packet->len, FALSE);
848     
849     silc_buffer_free(packet);
850     silc_free(sp_buf);
851   }
852
853   /* Send channel key to the client. Client cannot start transmitting
854      to the channel until we have sent the key. */
855   if (!cmd->pending) {
856     tmp_len = strlen(channel->channel_key->cipher->name);
857     packet = 
858       silc_channel_key_encode_payload(SILC_ID_CHANNEL_LEN, 
859                                       id_string, tmp_len, 
860                                       channel->channel_key->cipher->name,
861                                       channel->key_len, channel->key);
862     
863     silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
864                             packet->data, packet->len, FALSE);
865     silc_buffer_free(packet);
866   }
867
868   if (id_string)
869     silc_free(id_string);
870
871   /* Finally, send notify message to all clients on the channel about
872      new user on the channel. */
873   if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
874     if (!cmd->pending) {
875       silc_server_send_notify_to_channel(server, channel,
876                                          "%s (%s@%s) has joined channel %s",
877                                          client->nickname, client->username,
878                                          sock->hostname ? sock->hostname :
879                                          sock->ip, channel_name);
880     } else {
881       /* This is pending command request. Send the notify after we have
882          received the key for the channel from the router. */
883       JoinInternalContext *ctx = silc_calloc(1, sizeof(*ctx));
884       ctx->channel_name = channel_name;
885       ctx->nickname = client->nickname;
886       ctx->username = client->username;
887       ctx->hostname = sock->hostname ? sock->hostname : sock->ip;
888       ctx->channel = channel;
889       ctx->server = server;
890       silc_task_register(server->timeout_queue, sock->sock,
891                          silc_server_command_join_notify, ctx,
892                          0, 10000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
893     }
894   }
895
896  out:
897   silc_server_command_free(cmd);
898 #undef LCC
899 #undef LCCC
900 }
901
902 /* Server side of command MOTD. Sends servers current "message of the
903    day" to the client. */
904
905 SILC_SERVER_CMD_FUNC(motd)
906 {
907
908   SILC_LOG_DEBUG(("Start"));
909
910 }
911
912 SILC_SERVER_CMD_FUNC(umode)
913 {
914 }
915
916 SILC_SERVER_CMD_FUNC(cmode)
917 {
918 }
919
920 SILC_SERVER_CMD_FUNC(kick)
921 {
922 }
923
924 SILC_SERVER_CMD_FUNC(restart)
925 {
926 }
927  
928 SILC_SERVER_CMD_FUNC(close)
929 {
930 }
931  
932 SILC_SERVER_CMD_FUNC(die)
933 {
934 }
935  
936 SILC_SERVER_CMD_FUNC(silcoper)
937 {
938 }
939
940 SILC_SERVER_CMD_FUNC(leave)
941 {
942 }
943
944 SILC_SERVER_CMD_FUNC(names)
945 {
946 }