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