updates.
[silc.git] / lib / silcclient / 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 /* $Id$ */
21
22 #include "clientlibincludes.h"
23
24 /* Client command list. */
25 SilcClientCommand silc_command_list[] =
26 {
27   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
28   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
29   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
30                   SILC_CF_LAG | SILC_CF_REG, 3),
31   SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
32   SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
33   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
34   SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
35   SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
36   SILC_CLIENT_CMD(kill, KILL, "KILL", 
37                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
38   SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
39   SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
40                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
41   SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
42   SILC_CLIENT_CMD(oper, OPER, "OPER",
43                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
44   SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 4),
45   SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
46   SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
47   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
48   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
49   SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
50   SILC_CLIENT_CMD(restart, RESTART, "RESTART",
51                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
52   SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
53                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
54   SILC_CLIENT_CMD(die, DIE, "DIE",
55                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
56   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
57                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
58   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
59   SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
60
61   { NULL, 0, NULL, 0, 0 },
62 };
63
64 #define SILC_NOT_CONNECTED(x, c) \
65   x->ops->say((x), (c), \
66            "You are not connected to a server, use /SERVER to connect");
67
68 /* Command operation that is called at the end of all commands. 
69    Usage: COMMAND; */
70 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
71   cmd, TRUE, cmd->command->cmd)
72
73 /* Error to application. Usage: COMMAND_ERROR; */
74 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
75   cmd, FALSE, cmd->command->cmd)
76
77 /* Generic function to send any command. The arguments must be sent already
78    encoded into correct form in correct order. */
79
80 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
81                               SilcCommand command, unsigned short ident,
82                               unsigned int argc, ...)
83 {
84   SilcBuffer packet;
85   va_list ap;
86
87   va_start(ap, argc);
88
89   packet = silc_command_payload_encode_vap(command, ident, argc, ap);
90   silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
91                           NULL, 0, NULL, NULL, packet->data, 
92                           packet->len, TRUE);
93   silc_buffer_free(packet);
94 }
95
96 /* Finds and returns a pointer to the command list. Return NULL if the
97    command is not found. */
98
99 SilcClientCommand *silc_client_command_find(const char *name)
100 {
101   SilcClientCommand *cmd;
102
103   for (cmd = silc_command_list; cmd->name; cmd++) {
104     if (!strcmp(cmd->name, name))
105       return cmd;
106   }
107
108   return NULL;
109 }
110
111 /* Add new pending command to be executed when reply to a command has been
112    received.  The `reply_cmd' is the command that will call the `callback'
113    with `context' when reply has been received.  If `ident is non-zero
114    the `callback' will be executed when received reply with command 
115    identifier `ident'. */
116
117 void silc_client_command_pending(SilcClientConnection conn,
118                                  SilcCommand reply_cmd,
119                                  unsigned short ident,
120                                  SilcClientPendingDestructor destructor,
121                                  SilcCommandCb callback,
122                                  void *context)
123 {
124   SilcClientCommandPending *reply;
125
126   reply = silc_calloc(1, sizeof(*reply));
127   reply->reply_cmd = reply_cmd;
128   reply->ident = ident;
129   reply->context = context;
130   reply->callback = callback;
131   reply->destructor = destructor;
132   silc_dlist_add(conn->pending_commands, reply);
133 }
134
135 /* Deletes pending command by reply command type. */
136
137 void silc_client_command_pending_del(SilcClientConnection conn,
138                                      SilcCommand reply_cmd,
139                                      unsigned short ident)
140 {
141   SilcClientCommandPending *r;
142
143   silc_dlist_start(conn->pending_commands);
144   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
145     if (r->reply_cmd == reply_cmd && r->ident == ident) {
146       silc_dlist_del(conn->pending_commands, r);
147       break;
148     }
149   }
150 }
151
152 /* Checks for pending commands and marks callbacks to be called from
153    the command reply function. Returns TRUE if there were pending command. */
154
155 int silc_client_command_pending_check(SilcClientConnection conn,
156                                       SilcClientCommandReplyContext ctx,
157                                       SilcCommand command, 
158                                       unsigned short ident)
159 {
160   SilcClientCommandPending *r;
161
162   silc_dlist_start(conn->pending_commands);
163   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
164     if (r->reply_cmd == command && r->ident == ident) {
165       ctx->context = r->context;
166       ctx->callback = r->callback;
167       ctx->destructor = r->destructor;
168       ctx->ident = ident;
169       return TRUE;
170     }
171   }
172
173   return FALSE;
174 }
175
176 /* Allocate Command Context */
177
178 SilcClientCommandContext silc_client_command_alloc()
179 {
180   SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
181   ctx->users++;
182   return ctx;
183 }
184
185 /* Free command context and its internals */
186
187 void silc_client_command_free(SilcClientCommandContext ctx)
188 {
189   ctx->users--;
190   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
191                   ctx->users));
192   if (ctx->users < 1) {
193     int i;
194
195     for (i = 0; i < ctx->argc; i++)
196       silc_free(ctx->argv[i]);
197     silc_free(ctx);
198   }
199 }
200
201 /* Duplicate Command Context by adding reference counter. The context won't
202    be free'd untill it hits zero. */
203
204 SilcClientCommandContext 
205 silc_client_command_dup(SilcClientCommandContext ctx)
206 {
207   ctx->users++;
208   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
209                   ctx->users));
210   return ctx;
211 }
212
213 /* Pending command destructor. */
214
215 static void silc_client_command_destructor(void *context)
216 {
217   silc_client_command_free((SilcClientCommandContext)context);
218 }
219
220 /* Command WHOIS. This command is used to query information about 
221    specific user. */
222
223 SILC_CLIENT_CMD_FUNC(whois)
224 {
225   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
226   SilcClientConnection conn = cmd->conn;
227   SilcBuffer buffer;
228
229   if (!cmd->conn) {
230     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
231     COMMAND_ERROR;
232     goto out;
233   }
234
235   if (cmd->argc < 2 || cmd->argc > 3) {
236     cmd->client->ops->say(cmd->client, conn, 
237              "Usage: /WHOIS <nickname>[@<server>] [<count>]");
238     COMMAND_ERROR;
239     goto out;
240   }
241
242   buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
243                                        cmd->argc - 1, ++cmd->argv,
244                                        ++cmd->argv_lens, ++cmd->argv_types,
245                                        0);
246   silc_client_packet_send(cmd->client, cmd->conn->sock,
247                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
248                           buffer->data, buffer->len, TRUE);
249   silc_buffer_free(buffer);
250   cmd->argv--;
251   cmd->argv_lens--;
252   cmd->argv_types--;
253
254   /* Notify application */
255   COMMAND;
256
257  out:
258   silc_client_command_free(cmd);
259 }
260
261 SILC_CLIENT_CMD_FUNC(whowas)
262 {
263 }
264
265 /* Command IDENTIFY. This command is used to query information about 
266    specific user, especially ID's. */
267
268 SILC_CLIENT_CMD_FUNC(identify)
269 {
270   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
271   SilcClientConnection conn = cmd->conn;
272   SilcBuffer buffer;
273
274   if (!cmd->conn) {
275     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
276     COMMAND_ERROR;
277     goto out;
278   }
279
280   if (cmd->argc < 2 || cmd->argc > 3) {
281     cmd->client->ops->say(cmd->client, conn,
282              "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
283     COMMAND_ERROR;
284     goto out;
285   }
286
287   buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
288                                        cmd->argc - 1, ++cmd->argv,
289                                        ++cmd->argv_lens, ++cmd->argv_types,
290                                        0);
291   silc_client_packet_send(cmd->client, cmd->conn->sock,
292                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
293                           buffer->data, buffer->len, TRUE);
294   silc_buffer_free(buffer);
295   cmd->argv--;
296   cmd->argv_lens--;
297   cmd->argv_types--;
298
299   /* Notify application */
300   COMMAND;
301
302  out:
303   silc_client_command_free(cmd);
304 }
305
306 /* Command NICK. Shows current nickname/sets new nickname on current
307    window. */
308
309 SILC_CLIENT_CMD_FUNC(nick)
310 {
311   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
312   SilcClientConnection conn = cmd->conn;
313   SilcBuffer buffer;
314
315   if (!cmd->conn) {
316     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
317     COMMAND_ERROR;
318     goto out;
319   }
320
321   if (!strcmp(conn->nickname, cmd->argv[1]))
322     goto out;
323
324   /* Show current nickname */
325   if (cmd->argc < 2) {
326     if (cmd->conn) {
327       cmd->client->ops->say(cmd->client, conn, 
328                             "Your nickname is %s on server %s", 
329                             conn->nickname, conn->remote_host);
330     } else {
331       cmd->client->ops->say(cmd->client, conn, 
332                             "Your nickname is %s", conn->nickname);
333     }
334
335     /* XXX Notify application */
336     COMMAND;
337     goto out;
338   }
339
340   /* Set new nickname */
341   buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
342                                        cmd->argc - 1, ++cmd->argv,
343                                        ++cmd->argv_lens, ++cmd->argv_types,
344                                        0);
345   silc_client_packet_send(cmd->client, cmd->conn->sock,
346                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
347                           buffer->data, buffer->len, TRUE);
348   silc_buffer_free(buffer);
349   cmd->argv--;
350   cmd->argv_lens--;
351   cmd->argv_types--;
352   if (conn->nickname)
353     silc_free(conn->nickname);
354   conn->nickname = strdup(cmd->argv[1]);
355
356   /* Notify application */
357   COMMAND;
358
359  out:
360   silc_client_command_free(cmd);
361 }
362
363 SILC_CLIENT_CMD_FUNC(list)
364 {
365 }
366
367 /* Command TOPIC. Sets/shows topic on a channel. */
368
369 SILC_CLIENT_CMD_FUNC(topic)
370 {
371   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
372   SilcClientConnection conn = cmd->conn;
373   SilcIDCacheEntry id_cache = NULL;
374   SilcChannelEntry channel;
375   SilcBuffer buffer, idp;
376   char *name;
377
378   if (!cmd->conn) {
379     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
380     COMMAND_ERROR;
381     goto out;
382   }
383
384   if (cmd->argc < 2 || cmd->argc > 3) {
385     cmd->client->ops->say(cmd->client, conn,
386                           "Usage: /TOPIC <channel> [<topic>]");
387     COMMAND_ERROR;
388     goto out;
389   }
390
391   if (cmd->argv[1][0] == '*') {
392     if (!conn->current_channel) {
393       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
394       COMMAND_ERROR;
395       goto out;
396     }
397     name = conn->current_channel->channel_name;
398   } else {
399     name = cmd->argv[1];
400   }
401
402   if (!conn->current_channel) {
403     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
404     COMMAND_ERROR;
405     goto out;
406   }
407
408   /* Get the Channel ID of the channel */
409   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
410     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
411     COMMAND_ERROR;
412     goto out;
413   }
414
415   channel = (SilcChannelEntry)id_cache->context;
416
417   /* Send TOPIC command to the server */
418   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
419   if (cmd->argc > 2)
420     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2, 
421                                             1, idp->data, idp->len,
422                                             2, cmd->argv[2], 
423                                             strlen(cmd->argv[2]));
424   else
425     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1, 
426                                             1, idp->data, idp->len,
427                                             0);
428   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
429                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
430   silc_buffer_free(buffer);
431   silc_buffer_free(idp);
432
433   /* Notify application */
434   COMMAND;
435
436  out:
437   silc_client_command_free(cmd);
438 }
439
440 /* Command INVITE. Invites specific client to join a channel. */
441
442 SILC_CLIENT_CMD_FUNC(invite)
443 {
444   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
445   SilcClient client = cmd->client;
446   SilcClientConnection conn = cmd->conn;
447   SilcClientEntry client_entry;
448   SilcChannelEntry channel_entry;
449   SilcBuffer buffer, clidp, chidp;
450   unsigned int num = 0;
451   char *nickname = NULL, *server = NULL;
452
453   if (!cmd->conn) {
454     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
455     COMMAND_ERROR;
456     goto out;
457   }
458
459   if (cmd->argc != 3) {
460     cmd->client->ops->say(cmd->client, conn,
461                           "Usage: /INVITE <nickname>[@<server>] <channel>");
462     COMMAND_ERROR;
463     goto out;
464   }
465
466   /* Parse the typed nickname. */
467   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
468     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
469     COMMAND_ERROR;
470     goto out;
471   }
472
473   /* Find client entry */
474   client_entry = silc_idlist_get_client(client, conn, nickname, server, num);
475   if (!client_entry) {
476     if (nickname)
477       silc_free(nickname);
478     if (server)
479       silc_free(server);
480
481     /* Client entry not found, it was requested thus mark this to be
482        pending command. */
483     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 0,
484                                 silc_client_command_destructor,
485                                 silc_client_command_invite, 
486                                 silc_client_command_dup(cmd));
487     cmd->pending = 1;
488     return;
489   }
490
491   /* Find channel entry */
492   channel_entry = silc_idlist_get_channel(client, conn, cmd->argv[2]);
493   if (!channel_entry) {
494     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
495     COMMAND_ERROR;
496     goto out;
497   }
498
499   /* Send command */
500   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
501   chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
502   buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
503                                           1, clidp->data, clidp->len,
504                                           2, chidp->data, chidp->len);
505   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
506                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
507   silc_buffer_free(buffer);
508   silc_buffer_free(clidp);
509   silc_buffer_free(chidp);
510
511   cmd->client->ops->say(cmd->client, conn, 
512                         "Inviting %s to channel %s", cmd->argv[1], 
513                         cmd->argv[2]);
514
515   /* Notify application */
516   COMMAND;
517
518  out:
519   silc_client_command_free(cmd);
520 }
521
522 typedef struct {
523   SilcClient client;
524   SilcClientConnection conn;
525 } *QuitInternal;
526
527 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
528 {
529   QuitInternal q = (QuitInternal)context;
530
531   /* Close connection */
532   q->client->ops->disconnect(q->client, q->conn);
533   silc_client_close_connection(q->client, q->conn->sock);
534
535   silc_free(q);
536 }
537
538 /* Command QUIT. Closes connection with current server. */
539  
540 SILC_CLIENT_CMD_FUNC(quit)
541 {
542   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
543   SilcBuffer buffer;
544   QuitInternal q;
545
546   if (!cmd->conn) {
547     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
548     COMMAND_ERROR;
549     goto out;
550   }
551
552   if (cmd->argc > 1)
553     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, 
554                                          &cmd->argv[1], &cmd->argv_lens[1],
555                                          &cmd->argv_types[1], 0);
556   else
557     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
558                                          NULL, NULL, NULL, 0);
559   silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
560                           NULL, 0, NULL, NULL, 
561                           buffer->data, buffer->len, TRUE);
562   silc_buffer_free(buffer);
563
564   q = silc_calloc(1, sizeof(*q));
565   q->client = cmd->client;
566   q->conn = cmd->conn;
567
568   /* We quit the connection with little timeout */
569   silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
570                      silc_client_command_quit_cb, (void *)q,
571                      1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
572
573   /* Notify application */
574   COMMAND;
575
576  out:
577   silc_client_command_free(cmd);
578 }
579
580 SILC_CLIENT_CMD_FUNC(kill)
581 {
582 }
583
584 /* Command INFO. Request information about specific server. If specific
585    server is not provided the current server is used. */
586
587 SILC_CLIENT_CMD_FUNC(info)
588 {
589   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
590   SilcClientConnection conn = cmd->conn;
591   SilcBuffer buffer;
592   char *name;
593
594   if (!cmd->conn) {
595     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
596     COMMAND_ERROR;
597     goto out;
598   }
599
600   if (cmd->argc < 2)
601     name = strdup(conn->remote_host);
602   else
603     name = strdup(cmd->argv[1]);
604
605   /* Send the command */
606   buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
607                                           1, name, strlen(name));
608   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
609                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
610   silc_buffer_free(buffer);
611
612   /* Notify application */
613   COMMAND;
614
615  out:
616   silc_client_command_free(cmd);
617 }
618
619 SILC_CLIENT_CMD_FUNC(connect)
620 {
621 }
622
623 /* Command PING. Sends ping to server. This is used to test the 
624    communication channel. */
625
626 SILC_CLIENT_CMD_FUNC(ping)
627 {
628   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
629   SilcClientConnection conn = cmd->conn;
630   SilcBuffer buffer;
631   void *id;
632   int i;
633   char *name = NULL;
634
635   if (!cmd->conn) {
636     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
637     COMMAND_ERROR;
638     goto out;
639   }
640
641   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
642     name = strdup(conn->remote_host);
643
644   /* Send the command */
645   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
646                                           1, conn->remote_id_data, 
647                                           SILC_ID_SERVER_LEN);
648   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
649                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
650   silc_buffer_free(buffer);
651
652   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
653                       SILC_ID_SERVER);
654   if (!id) {
655     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
656     COMMAND_ERROR;
657     goto out;
658   }
659
660   /* Start counting time */
661   for (i = 0; i < conn->ping_count; i++) {
662     if (conn->ping[i].dest_id == NULL) {
663       conn->ping[i].start_time = time(NULL);
664       conn->ping[i].dest_id = id;
665       conn->ping[i].dest_name = name;
666       conn->ping_count++;
667       break;
668     }
669   }
670   if (i >= conn->ping_count) {
671     i = conn->ping_count;
672     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
673     conn->ping[i].start_time = time(NULL);
674     conn->ping[i].dest_id = id;
675     conn->ping[i].dest_name = name;
676     conn->ping_count++;
677   }
678   
679   /* Notify application */
680   COMMAND;
681
682  out:
683   silc_client_command_free(cmd);
684 }
685
686 SILC_CLIENT_CMD_FUNC(oper)
687 {
688 }
689
690 SILC_CLIENT_CMD_FUNC(trace)
691 {
692 }
693
694 SILC_CLIENT_CMD_FUNC(notice)
695 {
696 }
697
698 /* Command JOIN. Joins to a channel. */
699
700 SILC_CLIENT_CMD_FUNC(join)
701 {
702   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
703   SilcClientConnection conn = cmd->conn;
704   SilcIDCacheEntry id_cache = NULL;
705   SilcBuffer buffer, idp;
706
707   if (!cmd->conn) {
708     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
709     COMMAND_ERROR;
710     goto out;
711   }
712
713   if (cmd->argc < 2) {
714     /* Show channels currently joined to */
715
716     goto out;
717   }
718
719   /* See if we have joined to the requested channel already */
720   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
721                                     &id_cache)) {
722     cmd->client->ops->say(cmd->client, conn, 
723                           "You are talking to channel %s", cmd->argv[1]);
724     conn->current_channel = (SilcChannelEntry)id_cache->context;
725 #if 0
726     cmd->client->screen->bottom_line->channel = cmd->argv[1];
727     silc_screen_print_bottom_line(cmd->client->screen, 0);
728 #endif
729     goto out;
730   }
731
732   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
733
734   /* Send JOIN command to the server */
735   if (cmd->argc == 2)
736     buffer = 
737       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
738                                      1, cmd->argv[1], cmd->argv_lens[1],
739                                      2, idp->data, idp->len);
740   else if (cmd->argc == 3)
741     /* XXX Buggy */
742     buffer = 
743       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
744                                      1, cmd->argv[1], cmd->argv_lens[1],
745                                      2, idp->data, idp->len,
746                                      3, cmd->argv[2], cmd->argv_lens[2]);
747   else
748     buffer = 
749       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
750                                      1, cmd->argv[1], cmd->argv_lens[1],
751                                      2, idp->data, idp->len,
752                                      3, cmd->argv[2], cmd->argv_lens[2],
753                                      4, cmd->argv[3], cmd->argv_lens[3]);
754
755   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
756                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
757   silc_buffer_free(buffer);
758   silc_buffer_free(idp);
759
760   /* Notify application */
761   COMMAND;
762
763  out:
764   silc_client_command_free(cmd);
765 }
766
767 /* MOTD command. Requests motd from server. */
768
769 SILC_CLIENT_CMD_FUNC(motd)
770 {
771   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
772   SilcClientConnection conn = cmd->conn;
773   SilcBuffer buffer;
774
775   if (!cmd->conn) {
776     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
777     COMMAND_ERROR;
778     goto out;
779   }
780
781   if (cmd->argc < 1 || cmd->argc > 1) {
782     cmd->client->ops->say(cmd->client, conn,
783                           "Usage: /MOTD");
784     COMMAND_ERROR;
785     goto out;
786   }
787
788   /* Send TOPIC command to the server */
789   buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
790                                           2, conn->remote_host, 
791                                           strlen(conn->remote_host));
792   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
793                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
794   silc_buffer_free(buffer);
795
796   /* Notify application */
797   COMMAND;
798
799  out:
800   silc_client_command_free(cmd);
801 }
802
803 /* UMODE. Set user mode in SILC. */
804
805 SILC_CLIENT_CMD_FUNC(umode)
806 {
807
808 }
809
810 /* CMODE command. Sets channel mode. Modes that does not require any arguments
811    can be set several at once. Those modes that require argument must be set
812    separately (unless set with modes that does not require arguments). */
813
814 SILC_CLIENT_CMD_FUNC(cmode)
815 {
816   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
817   SilcClientConnection conn = cmd->conn;
818   SilcChannelEntry channel;
819   SilcBuffer buffer, chidp;
820   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
821   unsigned int mode, add, type, len, arg_len = 0;
822   int i;
823
824   if (!cmd->conn) {
825     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
826     COMMAND_ERROR;
827     goto out;
828   }
829
830   if (cmd->argc < 3) {
831     cmd->client->ops->say(cmd->client, conn, 
832                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
833     COMMAND_ERROR;
834     goto out;
835   }
836
837   if (cmd->argv[1][0] == '*') {
838     if (!conn->current_channel) {
839       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
840       COMMAND_ERROR;
841       goto out;
842     }
843
844     channel = conn->current_channel;
845   } else {
846     name = cmd->argv[1];
847
848     channel = silc_idlist_get_channel(cmd->client, conn, name);
849     if (!channel) {
850       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
851       COMMAND_ERROR;
852       goto out;
853     }
854   }
855
856   mode = channel->mode;
857
858   /* Are we adding or removing mode */
859   if (cmd->argv[2][0] == '-')
860     add = FALSE;
861   else
862     add = TRUE;
863
864   /* Argument type to be sent to server */
865   type = 0;
866
867   /* Parse mode */
868   cp = cmd->argv[2] + 1;
869   len = strlen(cp);
870   for (i = 0; i < len; i++) {
871     switch(cp[i]) {
872     case 'p':
873       if (add)
874         mode |= SILC_CHANNEL_MODE_PRIVATE;
875       else
876         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
877       break;
878     case 's':
879       if (add)
880         mode |= SILC_CHANNEL_MODE_SECRET;
881       else
882         mode &= ~SILC_CHANNEL_MODE_SECRET;
883       break;
884     case 'k':
885       if (add)
886         mode |= SILC_CHANNEL_MODE_PRIVKEY;
887       else
888         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
889       break;
890     case 'i':
891       if (add)
892         mode |= SILC_CHANNEL_MODE_INVITE;
893       else
894         mode &= ~SILC_CHANNEL_MODE_INVITE;
895       break;
896     case 't':
897       if (add)
898         mode |= SILC_CHANNEL_MODE_TOPIC;
899       else
900         mode &= ~SILC_CHANNEL_MODE_TOPIC;
901       break;
902     case 'l':
903       if (add) {
904         int ll;
905         mode |= SILC_CHANNEL_MODE_ULIMIT;
906         type = 3;
907         ll = atoi(cmd->argv[3]);
908         SILC_PUT32_MSB(ll, tmp);
909         arg = tmp;
910         arg_len = 4;
911       } else {
912         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
913       }
914       break;
915     case 'a':
916       if (add) {
917         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
918         type = 4;
919         arg = cmd->argv[3];
920         arg_len = cmd->argv_lens[3];
921       } else {
922         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
923       }
924       break;
925     case 'b':
926       if (add) {
927         mode |= SILC_CHANNEL_MODE_BAN;
928         type = 5;
929         arg = cmd->argv[3];
930         arg_len = cmd->argv_lens[3];
931       } else {
932         mode &= ~SILC_CHANNEL_MODE_BAN;
933       }
934       break;
935     case 'I':
936       if (add) {
937         mode |= SILC_CHANNEL_MODE_INVITE_LIST;
938         type = 6;
939         arg = cmd->argv[3];
940         arg_len = cmd->argv_lens[3];
941       } else {
942         mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
943       }
944       break;
945     case 'c':
946       if (add) {
947         mode |= SILC_CHANNEL_MODE_CIPHER;
948         type = 8;
949         arg = cmd->argv[3];
950         arg_len = cmd->argv_lens[3];
951       } else {
952         mode &= ~SILC_CHANNEL_MODE_CIPHER;
953       }
954       break;
955     default:
956       COMMAND_ERROR;
957       goto out;
958       break;
959     }
960   }
961
962   if (type && cmd->argc < 3) {
963     COMMAND_ERROR;
964     goto out;
965   }
966
967   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
968   SILC_PUT32_MSB(mode, modebuf);
969
970   /* Send the command packet. We support sending only one mode at once
971      that requires an argument. */
972   if (type && arg) {
973     buffer = 
974       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
975                                      1, chidp->data, chidp->len, 
976                                      2, modebuf, sizeof(modebuf),
977                                      type, arg, arg_len);
978   } else {
979     buffer = 
980       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
981                                      1, chidp->data, chidp->len, 
982                                      2, modebuf, sizeof(modebuf));
983   }
984
985   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
986                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
987   silc_buffer_free(buffer);
988   silc_buffer_free(chidp);
989
990   /* Notify application */
991   COMMAND;
992
993  out:
994   silc_client_command_free(cmd);
995 }
996
997 /* CUMODE command. Changes client's mode on a channel. */
998
999 SILC_CLIENT_CMD_FUNC(cumode)
1000 {
1001   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1002   SilcClientConnection conn = cmd->conn;
1003   SilcChannelEntry channel;
1004   SilcChannelUser chu;
1005   SilcClientEntry client_entry;
1006   SilcBuffer buffer, clidp, chidp;
1007   unsigned char *name, *cp, modebuf[4];
1008   unsigned int mode = 0, add, len;
1009   char *nickname = NULL, *server = NULL;
1010   unsigned int num = 0;
1011   int i;
1012
1013   if (!cmd->conn) {
1014     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1015     COMMAND_ERROR;
1016     goto out;
1017   }
1018
1019   if (cmd->argc < 4) {
1020     cmd->client->ops->say(cmd->client, conn, 
1021                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1022     COMMAND_ERROR;
1023     goto out;
1024   }
1025
1026   if (cmd->argv[1][0] == '*') {
1027     if (!conn->current_channel) {
1028       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1029       COMMAND_ERROR;
1030       goto out;
1031     }
1032
1033     channel = conn->current_channel;
1034   } else {
1035     name = cmd->argv[1];
1036
1037     channel = silc_idlist_get_channel(cmd->client, conn, name);
1038     if (!channel) {
1039       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1040       COMMAND_ERROR;
1041       goto out;
1042     }
1043   }
1044
1045   /* Parse the typed nickname. */
1046   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1047     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1048     COMMAND_ERROR;
1049     goto out;
1050   }
1051
1052   /* Find client entry */
1053   client_entry = silc_idlist_get_client(cmd->client, conn, 
1054                                         nickname, server, num);
1055   if (!client_entry) {
1056     /* Client entry not found, it was requested thus mark this to be
1057        pending command. */
1058     silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,  
1059                                 silc_client_command_destructor,
1060                                 silc_client_command_cumode, 
1061                                 silc_client_command_dup(cmd));
1062     cmd->pending = 1;
1063     return;
1064   }
1065   
1066   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1067     if (chu->client == client_entry) {
1068       chu->mode = mode;
1069       break;
1070     }
1071   }
1072
1073   /* Are we adding or removing mode */
1074   if (cmd->argv[2][0] == '-')
1075     add = FALSE;
1076   else
1077     add = TRUE;
1078
1079   /* Parse mode */
1080   cp = cmd->argv[2] + 1;
1081   len = strlen(cp);
1082   for (i = 0; i < len; i++) {
1083     switch(cp[i]) {
1084     case 'a':
1085       if (add) {
1086         mode |= SILC_CHANNEL_UMODE_CHANFO;
1087         mode |= SILC_CHANNEL_UMODE_CHANOP;
1088       } else {
1089         mode = SILC_CHANNEL_UMODE_NONE;
1090       }
1091       break;
1092     case 'f':
1093       if (add)
1094         mode |= SILC_CHANNEL_UMODE_CHANFO;
1095       else
1096         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1097       break;
1098     case 'o':
1099       if (add)
1100         mode |= SILC_CHANNEL_UMODE_CHANOP;
1101       else
1102         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1103       break;
1104     default:
1105       COMMAND_ERROR;
1106       goto out;
1107       break;
1108     }
1109   }
1110
1111   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1112   SILC_PUT32_MSB(mode, modebuf);
1113   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1114
1115   /* Send the command packet. We support sending only one mode at once
1116      that requires an argument. */
1117   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1118                                           1, chidp->data, chidp->len, 
1119                                           2, modebuf, 4,
1120                                           3, clidp->data, clidp->len);
1121
1122   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1123                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1124   silc_buffer_free(buffer);
1125   silc_buffer_free(chidp);
1126   silc_buffer_free(clidp);
1127   
1128   /* Notify application */
1129   COMMAND;
1130
1131  out:
1132   silc_client_command_free(cmd);
1133 }
1134
1135 /* KICK command. Kicks a client out of channel. */
1136
1137 SILC_CLIENT_CMD_FUNC(kick)
1138 {
1139   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1140   SilcClientConnection conn = cmd->conn;
1141
1142 }
1143
1144 SILC_CLIENT_CMD_FUNC(restart)
1145 {
1146 }
1147  
1148 SILC_CLIENT_CMD_FUNC(close)
1149 {
1150 }
1151  
1152 SILC_CLIENT_CMD_FUNC(die)
1153 {
1154 }
1155  
1156 SILC_CLIENT_CMD_FUNC(silcoper)
1157 {
1158 }
1159
1160 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1161
1162 SILC_CLIENT_CMD_FUNC(leave)
1163 {
1164   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1165   SilcClientConnection conn = cmd->conn;
1166   SilcIDCacheEntry id_cache = NULL;
1167   SilcChannelEntry channel;
1168   SilcBuffer buffer, idp;
1169   char *name;
1170
1171   if (!cmd->conn) {
1172     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1173     COMMAND_ERROR;
1174     goto out;
1175   }
1176
1177   if (cmd->argc != 2) {
1178     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1179     COMMAND_ERROR;
1180     goto out;
1181   }
1182
1183   if (cmd->argv[1][0] == '*') {
1184     if (!conn->current_channel) {
1185       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1186       COMMAND_ERROR;
1187       goto out;
1188     }
1189     name = conn->current_channel->channel_name;
1190   } else {
1191     name = cmd->argv[1];
1192   }
1193
1194   if (!conn->current_channel) {
1195     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1196     COMMAND_ERROR;
1197     goto out;
1198   }
1199
1200   /* Get the Channel ID of the channel */
1201   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1202     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1203     COMMAND_ERROR;
1204     goto out;
1205   }
1206
1207   channel = (SilcChannelEntry)id_cache->context;
1208
1209   /* Send LEAVE command to the server */
1210   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1211   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1212                                           1, idp->data, idp->len);
1213   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1214                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1215   silc_buffer_free(buffer);
1216   silc_buffer_free(idp);
1217
1218   /* We won't talk anymore on this channel */
1219   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1220
1221   conn->current_channel = NULL;
1222
1223   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1224   silc_free(channel->channel_name);
1225   silc_free(channel->id);
1226   silc_free(channel->key);
1227   silc_cipher_free(channel->channel_key);
1228   silc_free(channel);
1229
1230   /* Notify application */
1231   COMMAND;
1232
1233  out:
1234   silc_client_command_free(cmd);
1235 }
1236
1237 /* Command USERS. Requests the USERS of the clients joined on requested
1238    channel. */
1239
1240 SILC_CLIENT_CMD_FUNC(users)
1241 {
1242   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1243   SilcClientConnection conn = cmd->conn;
1244   SilcIDCacheEntry id_cache = NULL;
1245   SilcChannelEntry channel;
1246   SilcBuffer buffer, idp;
1247   char *name, *line = NULL;
1248   unsigned int line_len = 0;
1249
1250   if (!cmd->conn) {
1251     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1252     COMMAND_ERROR;
1253     goto out;
1254   }
1255
1256   if (cmd->argc != 2) {
1257     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1258     COMMAND_ERROR;
1259     goto out;
1260   }
1261
1262   if (cmd->argv[1][0] == '*') {
1263     if (!conn->current_channel) {
1264       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1265       COMMAND_ERROR;
1266       goto out;
1267     }
1268     name = conn->current_channel->channel_name;
1269   } else {
1270     name = cmd->argv[1];
1271   }
1272
1273   if (!conn->current_channel) {
1274     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1275     COMMAND_ERROR;
1276     goto out;
1277   }
1278
1279   /* Get the Channel ID of the channel */
1280   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1281     /* XXX should resolve the channel ID; LIST command */
1282     cmd->client->ops->say(cmd->client, conn, 
1283                           "You are not on that channel", name);
1284     COMMAND_ERROR;
1285     goto out;
1286   }
1287
1288   channel = (SilcChannelEntry)id_cache->context;
1289
1290   if (!cmd->pending) {
1291     /* Send USERS command to the server */
1292     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1293     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1, 
1294                                             1, idp->data, idp->len);
1295     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1296                             NULL, 0, NULL, NULL, buffer->data, 
1297                             buffer->len, TRUE);
1298     silc_buffer_free(buffer);
1299     silc_buffer_free(idp);
1300
1301     /* Register pending callback which will recall this command callback with
1302        same context and reprocesses the command. When reprocessing we actually
1303        display the information on the screen. */
1304     silc_client_command_pending(conn, SILC_COMMAND_USERS, 0, 
1305                                 silc_client_command_destructor,
1306                                 silc_client_command_users, 
1307                                 silc_client_command_dup(cmd));
1308     cmd->pending = TRUE;
1309     return;
1310   }
1311
1312   if (cmd->pending) {
1313     /* Pending command. Now we've resolved the information from server and
1314        we are ready to display the information on screen. */
1315     int i;
1316     SilcChannelUser chu;
1317
1318     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1319                           channel->channel_name);
1320
1321     line = silc_calloc(4096, sizeof(*line));
1322     line_len = 4096;
1323     silc_list_start(channel->clients);
1324     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1325       SilcClientEntry e = chu->client;
1326       char *m, tmp[80], len1;
1327
1328       memset(line, 0, sizeof(line_len));
1329
1330       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1331         silc_free(line);
1332         line_len += strlen(e->nickname) + strlen(e->server) + 100;
1333         line = silc_calloc(line_len, sizeof(*line));
1334       }
1335
1336       memset(tmp, 0, sizeof(tmp));
1337       m = silc_client_chumode_char(chu->mode);
1338
1339       strncat(line, " ", 1);
1340       strncat(line, e->nickname, strlen(e->nickname));
1341       strncat(line, e->server ? "@" : "", 1);
1342
1343       len1 = 0;
1344       if (e->server)
1345         len1 = strlen(e->server);
1346       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1347
1348       len1 = strlen(line);
1349       if (len1 >= 30) {
1350         memset(&line[29], 0, len1 - 29);
1351       } else {
1352         for (i = 0; i < 30 - len1 - 1; i++)
1353           strcat(line, " ");
1354       }
1355
1356       strncat(line, "  H", 3);
1357       strcat(tmp, m ? m : "");
1358       strncat(line, tmp, strlen(tmp));
1359
1360       if (strlen(tmp) < 5)
1361         for (i = 0; i < 5 - strlen(tmp); i++)
1362           strcat(line, " ");
1363
1364       strcat(line, e->username ? e->username : "");
1365
1366       cmd->client->ops->say(cmd->client, conn, "%s", line);
1367
1368       if (m)
1369         silc_free(m);
1370     }
1371   }
1372
1373   if (line)
1374     silc_free(line);
1375
1376   /* Notify application */
1377   COMMAND;
1378
1379  out:
1380   silc_client_command_free(cmd);
1381 }