kill mallinfo
[paraslash.git] / command.c
1 /*
2 * Copyright (C) 1997-2006 Andre Noll <maan@systemlinux.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
17 */
18
19 /** \file command.c does client authentication and executes server commands */
20
21 #include <sys/time.h> /* gettimeofday */
22 #include "crypt.h"
23 #include "server.cmdline.h"
24 #include "db.h"
25 #include "server.h"
26 #include "afs.h"
27 #include "send.h"
28 #include "rc4.h"
29 #include <openssl/rc4.h>
30 #include "error.h"
31 #include "net.h"
32 #include "daemon.h"
33 #include "string.h"
34
35 void (*crypt_function_recv)(unsigned long len, const unsigned char *indata,
36 unsigned char *outdata) = NULL;
37 void (*crypt_function_send)(unsigned long len, const unsigned char *indata,
38 unsigned char *outdata) = NULL;
39 static RC4_KEY rc4_recv_key;
40 static RC4_KEY rc4_send_key;
41 static unsigned char rc4_buf[2 * RC4_KEY_LEN];
42
43 extern const char *status_item_list[NUM_STAT_ITEMS];
44 extern struct misc_meta_data *mmd;
45 extern struct gengetopt_args_info conf;
46 extern struct audio_file_selector selectors[];
47 extern struct audio_format afl[];
48 extern struct sender senders[];
49 extern char *user_list;
50 struct sockaddr_in *in_addr;
51
52 static int com_si(int, int, char **);
53 static int com_version(int, int, char **);
54 static int com_sb(int, int, char **);
55 static int com_sc(int, int, char **);
56 static int com_stat(int, int, char **);
57 static int com_help(int, int, char **);
58 static int com_hup(int, int, char **);
59 static int com_term(int, int, char **);
60 static int com_play(int, int, char **);
61 static int com_stop(int, int, char **);
62 static int com_pause(int, int, char **);
63 static int com_next(int, int, char **);
64 static int com_nomore(int, int, char **);
65 static int com_chs(int, int, char **);
66 static int com_ff(int, int, char **);
67 static int com_jmp(int, int, char **);
68 static int com_sender(int, int, char **);
69
70
71 /* commands that are handled by the server itself */
72 static struct server_command cmd_struct[] = {
73 {
74 .name = "chs",
75 .handler = com_chs,
76 .perms = DB_READ | DB_WRITE,
77 .description = "change the current audio file selector",
78 .synopsis = "chs [new_selector]",
79 .help =
80 "Shutdown the current selector and activate new_selector. If no\n"
81 "argument was given, print the name of the current selector.\n"
82 },
83
84 {
85 .name = "ff",
86 .handler = com_ff,
87 .perms = AFS_READ | AFS_WRITE,
88 .description = "jmp amount of time forwards or backwards "
89 "in current audio file",
90 .synopsis = "ff n[-]",
91 .help =
92
93 "\tSet the 'R' (reposition request) bit of the afs status flags\n"
94 "\tand enqueue a request to jump n seconds forwards or backwards\n"
95 "\tin the current audio file.\n"
96 "\n"
97 "EXAMPLE\n"
98 "\n"
99 "\t\tff 30-\n"
100 "\n"
101 "\tjumps 30 seconds backwards.\n"
102
103 },
104
105 {
106 .name = "help",
107 .handler = com_help,
108 .perms = 0,
109 .description = "print help text",
110 .synopsis = "help [command]",
111 .help =
112
113 "Without any arguments, help prints a list of availible commands. When\n"
114 "issued with a command name as first argument, print out a description\n"
115 "for that command.\n"
116
117 },
118
119 {
120 .name = "hup",
121 .handler = com_hup,
122 .perms = AFS_WRITE,
123 .description = "force reload of config file and log file",
124 .synopsis = "hup",
125 .help =
126
127 "After rereading the config file, a signal is sent to all children\n"
128 "which forces them to close/reopen the log file.\n"
129
130 },
131
132 {
133 .name = "jmp",
134 .handler = com_jmp,
135 .perms = AFS_READ | AFS_WRITE,
136 .description = "jmp to given position in current audio file",
137 .synopsis = "jmp [n]",
138 .help =
139
140 "\tSet the 'R' (reposition request) bit of the afs status flags\n"
141 "\tand enqueue a request to jump to n% of the current audio file,\n"
142 "\twhere 0 <= n <= 100.\n"
143
144 },
145
146 {
147 .name = "next",
148 .handler = com_next,
149 .perms = AFS_READ | AFS_WRITE,
150 .description = "skip rest of current audio file",
151 .synopsis = "next",
152 .help =
153
154 "\tSet the 'N' (next audio file) bit of the afs status flags. When\n"
155 "\tplaying, change audio file immediately. Equivalent to stop\n"
156 "\tif paused, NOP if stopped.\n"
157
158
159 },
160
161 {
162 .name = "nomore",
163 .handler = com_nomore,
164 .perms = AFS_READ | AFS_WRITE,
165 .description = "stop playing after current audio file",
166 .synopsis = "nomore",
167 .help =
168
169 "Set the 'O' (no more) bit of the afs status flags. This instructs\n"
170 "para_server to clear the 'P' (playing) bit as soon as it encounters\n"
171 "the 'N' (next audio file) bit being set.\n"
172 "\n"
173 "Use this command instead of stop if you don't like\n"
174 "sudden endings.\n"
175
176 },
177
178 {
179 .name ="pause",
180 .handler = com_pause,
181 .perms = AFS_READ | AFS_WRITE,
182 .description = "pause current audio file",
183 .synopsis = "pause",
184 .help =
185
186 "\tClear the 'P' (playing) bit of the afs status flags.\n"
187
188 },
189
190 {
191 .name = "play",
192 .handler = com_play,
193 .perms = AFS_READ | AFS_WRITE,
194 .description = "start playing or resume playing when paused",
195 .synopsis = "play",
196 .help =
197
198 "\tSet the 'P' (playing) bit of the afs status flags. This\n"
199 "\tresults in starting/continuing to stream.\n"
200
201 },
202
203 {
204 .name = "sb",
205 .handler = com_sb,
206 .perms = AFS_READ,
207 .description = "print status bar for current audio file",
208 .synopsis = "sb [n]",
209 .help =
210
211 "Without any arguments, sb continuously prints a status bar of the form\n"
212 "\n"
213 " 12:34 [56:12] (56%) filename\n"
214 "\n"
215 "indicating playing time, remaining time, percentage and the name of\n"
216 "the file beeing streamed. Use the optional number n to let stat exit\n"
217 "after having displayed the status bar n times.\n"
218
219 },
220 {
221 .name = "sc",
222 .handler = com_sc,
223 .perms = AFS_READ,
224 .description = "print name of audio file whenever it changes",
225 .synopsis = "sc [n]",
226 .help =
227
228 "\tsc prints exactly one line (the filename of the audio file\n"
229 "\tbeing played) whenever the audio file changes. Stops after\n"
230 "\tn iterations, or never if n is not specified.\n"
231
232 },
233 {
234 .name = "sender",
235 .handler = com_sender,
236 .perms = AFS_READ | AFS_WRITE,
237 .description = "control paraslash internal senders",
238 .synopsis = "sender [s cmd [arguments]]",
239 .help =
240
241 "send command cmd to sender s. cmd may be one of the following:\n"
242 "help, on, off, add, delete, allow, or deny. Note that not all senders\n"
243 "support each command. Try e.g. 'para_client sender http help' for\n"
244 "more information about the http sender. If no argument is given,\n"
245 "print out a list of all senders that are compiled in.\n"
246
247 },
248 {
249 .name = "si",
250 .handler = com_si,
251 .perms = 0,
252 .description = "print server info",
253 .synopsis = "si",
254 .help =
255 "Print server uptime and other information.\n"
256 },
257
258 {
259 .name = "stat",
260 .handler = com_stat,
261 .perms = AFS_READ,
262 .description = "print status info for current audio file",
263 .synopsis = "stat [n]",
264 .help =
265
266 "\tWithout any arguments, stat continuously prints status messages\n"
267 "\tof the audio file being streamed. Use the optional number n\n"
268 "\tto let stat exit after having displayed status n times.\n"
269
270 },
271
272 {
273 .name = "stop",
274 .handler = com_stop,
275 .perms = AFS_READ | AFS_WRITE,
276 .description = "stop playing",
277 .synopsis = "stop",
278 .help =
279
280 "\tClear the 'P' (play) bit and set the 'N' bit of the afs status\n"
281 "\tflags.\n"
282
283 },
284 {
285 .name = "term",
286 .handler = com_term,
287 .perms = AFS_READ | AFS_WRITE,
288 .description = "terminate para_server",
289 .synopsis = "term",
290 .help =
291
292 "Shuts down the server. Instead of this command, you can also send\n"
293 "SIGINT or SIGTERM. It should never be necessary to send SIGKILL.\n"
294
295 },
296 {
297 .name = "version",
298 .handler = com_version,
299 .perms = 0,
300 .description = "print server's version",
301 .synopsis = "version",
302 .help =
303 "Show version and other info\n"
304 },
305 /* this indicates the end of the list. Do not touch. */
306 {
307 .name = NULL,
308 }
309 };
310
311 static void dummy(__a_unused int s)
312 {}
313
314 static void mmd_dup(struct misc_meta_data *new_mmd)
315 {
316 mmd_lock();
317 *new_mmd = *mmd;
318 mmd_unlock();
319 }
320
321 /*
322 * compute human readable string containing
323 * afs_status for given integer value
324 */
325 static char *afs_status_tohuman(unsigned int flags)
326 {
327 if (flags & AFS_PLAYING)
328 return para_strdup("playing");
329 else if (flags & AFS_NEXT)
330 return para_strdup("stopped");
331 else
332 return para_strdup("paused");
333 }
334
335 /*
336 * return human readable permission string. Never returns NULL.
337 */
338 char *cmd_perms_itohuman(unsigned int perms)
339 {
340 char *msg = para_malloc(7 * sizeof(char));
341
342 msg[0] = perms & DB_READ? 'd' : '-';
343 msg[1] = perms & DB_WRITE? 'D' : '-';
344 msg[2] = perms & AFS_READ? 'a' : '-';
345 msg[3] = perms & AFS_WRITE? 'A' : '-';
346 msg[4] = '\0';
347 return msg;
348 }
349
350 /*
351 * Never returns NULL.
352 */
353 static char *afs_get_status_flags(unsigned int flags)
354 {
355 char *msg = para_malloc(5 * sizeof(char));
356
357 msg[0] = (flags & AFS_PLAYING)? 'P' : '_';
358 msg[1] = (flags & AFS_NOMORE)? 'O' : '_';
359 msg[2] = (flags & AFS_NEXT)? 'N' : '_';
360 msg[3] = (flags & AFS_REPOS)? 'R' : '_';
361 msg[4] = '\0';
362 return msg;
363 }
364
365 /*
366 * compute status bar string. Never returns NULL
367 */
368 char *get_sb_string(struct misc_meta_data *nmmd)
369 {
370 char *base, *ret;
371 long long unsigned secs = 0, rsecs = 0, percent = 0;
372
373 base = para_basename(nmmd->filename);
374 if (!base)
375 return para_strdup("");
376 if (!base[0])
377 return base;
378 if (nmmd->chunks_total) {
379 secs = (long long) nmmd->seconds_total * nmmd->chunks_sent
380 / nmmd->chunks_total;
381 rsecs = (long long) nmmd->seconds_total *
382 (nmmd->chunks_total - nmmd->chunks_sent)
383 / nmmd->chunks_total;
384 percent = 100 * ((nmmd->chunks_sent + 5) / 10)
385 / ((nmmd->chunks_total + 5) / 10);
386 }
387 ret = make_message("%llu:%02llu [%llu:%02llu] (%llu%%) %s",
388 secs / 60, secs % 60,
389 rsecs / 60, rsecs % 60,
390 percent,
391 base
392 );
393 free(base);
394 return ret;
395 }
396
397 static char *get_status(struct misc_meta_data *nmmd)
398 {
399 char *bar, *ret, mtime[30] = "";
400 char *status, *flags; /* afs status info */
401 char *ut = uptime_str();
402 long offset = (nmmd->offset + 500) / 1000;
403 struct timeval now;
404 struct tm mtime_tm;
405
406 if (nmmd->audio_format >= 0) {
407 localtime_r(&nmmd->mtime, &mtime_tm);
408 strftime(mtime, 29, "%a %b %d %Y", &mtime_tm);
409 }
410 /* report real status */
411 status = afs_status_tohuman(nmmd->afs_status_flags);
412 flags = afs_get_status_flags(nmmd->afs_status_flags);
413 bar = para_basename(nmmd->filename);
414 gettimeofday(&now, NULL);
415 ret = make_message(
416 "%s:%lu\n" "%s:%s\n" "%s:%i\n" "%s:%u\n"
417 "%s:%s\n" "%s:%s\n" "%s:%s\n" "%s:%s\n"
418 "%s:%li\n" "%s:%s\n" "%s" "%s"
419 "%s:%s\n" "%s:%lu.%lu\n" "%s:%lu.%lu\n",
420 status_item_list[SI_FILE_SIZE], nmmd->size / 1024,
421 status_item_list[SI_MTIME], mtime,
422 status_item_list[SI_LENGTH], nmmd->seconds_total,
423 status_item_list[SI_NUM_PLAYED], nmmd->num_played,
424
425 status_item_list[SI_STATUS_BAR], bar ? bar : "(none)",
426 status_item_list[SI_STATUS], status,
427 status_item_list[SI_STATUS_FLAGS], flags,
428 status_item_list[SI_SELECTOR], selectors[nmmd->selector_num].name,
429
430 status_item_list[SI_OFFSET], offset,
431 status_item_list[SI_FORMAT], audio_format_name(nmmd->audio_format),
432 nmmd->selector_info,
433 nmmd->audio_file_info,
434
435 status_item_list[SI_UPTIME], ut,
436 status_item_list[SI_STREAM_START], nmmd->stream_start.tv_sec,
437 nmmd->stream_start.tv_usec,
438 status_item_list[SI_CURRENT_TIME], now.tv_sec, now.tv_usec
439
440 );
441 free(bar);
442 free(flags);
443 free(status);
444 free(ut);
445 return ret;
446 }
447
448 static int check_sender_args(int argc, char **argv, struct sender_command_data *scd)
449 {
450 int i;
451 /* this has to match sender.h */
452 const char *subcmds[] = {"add", "delete", "allow", "deny", "on", "off", NULL};
453
454 scd->sender_num = -1;
455 if (argc < 2)
456 return -E_COMMAND_SYNTAX;
457 for (i = 0; senders[i].name; i++)
458 if (!strcmp(senders[i].name, argv[1]))
459 break;
460 PARA_DEBUG_LOG("%d:%s\n", argc, argv[1]);
461 if (!senders[i].name)
462 return -E_COMMAND_SYNTAX;
463 scd->sender_num = i;
464 for (i = 0; subcmds[i]; i++)
465 if (!strcmp(subcmds[i], argv[2]))
466 break;
467 if (!subcmds[i])
468 return -E_COMMAND_SYNTAX;
469 scd->cmd_num = i;
470 mmd_lock();
471 if (!senders[scd->sender_num].client_cmds[scd->cmd_num]) {
472 mmd_unlock();
473 return -E_SENDER_CMD;
474 }
475 mmd_unlock();
476 switch (scd->cmd_num) {
477 case SENDER_ON:
478 case SENDER_OFF:
479 if (argc != 3)
480 return -E_COMMAND_SYNTAX;
481 break;
482 case SENDER_DENY:
483 case SENDER_ALLOW:
484 if (argc != 4 && argc != 5)
485 return -E_COMMAND_SYNTAX;
486 if (!inet_aton(argv[3], &scd->addr))
487 return -E_COMMAND_SYNTAX;
488 scd->netmask = 32;
489 if (argc == 5) {
490 scd->netmask = atoi(argv[4]);
491 if (scd->netmask < 0 || scd->netmask > 32)
492 return -E_COMMAND_SYNTAX;
493 }
494 break;
495 case SENDER_ADD:
496 case SENDER_DELETE:
497 if (argc != 4 && argc != 5)
498 return -E_COMMAND_SYNTAX;
499 if (!inet_aton(argv[3], &scd->addr))
500 return -E_COMMAND_SYNTAX;
501 scd->port = -1;
502 if (argc == 5) {
503 scd->port = atoi(argv[4]);
504 if (scd->port < 0 || scd->port > 65535)
505 return -E_COMMAND_SYNTAX;
506 }
507 break;
508 default:
509 return -E_COMMAND_SYNTAX;
510 }
511 return 1;
512 }
513
514 static int com_sender(int fd, int argc, char **argv)
515 {
516 int i, ret;
517 struct sender_command_data scd;
518
519 if (argc < 2) {
520 char *msg = NULL;
521 for (i = 0; senders[i].name; i++) {
522 char *tmp = make_message("%s%s\n",
523 msg? msg : "", senders[i].name);
524 free(msg);
525 msg = tmp;
526 }
527 ret = send_buffer(fd, msg);
528 free(msg);
529 return ret;
530 }
531 ret = check_sender_args(argc, argv, &scd);
532 if (ret < 0) {
533 char *msg;
534 if (scd.sender_num < 0)
535 return ret;
536 msg = senders[scd.sender_num].help();
537 send_buffer(fd, msg);
538 free(msg);
539 return 1;
540 }
541 for (i = 0; i < 10; i++) {
542 mmd_lock();
543 if (mmd->sender_cmd_data.cmd_num >= 0) {
544 mmd_unlock();
545 usleep(100 * 1000);
546 continue;
547 }
548 mmd->sender_cmd_data = scd;
549 mmd_unlock();
550 break;
551 }
552 return (i < 10)? 1 : -E_LOCK;
553 }
554
555 /* server info */
556 static int com_si(int fd, int argc, __a_unused char **argv)
557 {
558 int i, ret;
559 char *ut;
560 char *selector_string = NULL, *sender_info = NULL, *sender_list = NULL;
561
562 if (argc != 1)
563 return -E_COMMAND_SYNTAX;
564 mmd_lock();
565 for (i = 0; selectors[i].name; i++) {
566 selector_string = para_strcat(selector_string, selectors[i].name);
567 selector_string = para_strcat(selector_string, " ");
568 }
569 for (i = 0; senders[i].name; i++) {
570 char *info = senders[i].info();
571 sender_info = para_strcat(sender_info, info);
572 free(info);
573 sender_list = para_strcat(sender_list, senders[i].name);
574 sender_list = para_strcat(sender_list, " ");
575 }
576 ut = uptime_str();
577 ret = send_va_buffer(fd, "up: %s\nplayed: %u\n"
578 "pid: %d\n"
579 "connections (active/accepted/total): %u/%u/%u\n"
580 "current loglevel: %i\n"
581 "supported audio file selectors: %s\n"
582 "supported audio formats: %s\n"
583 "supported senders: %s\n"
584 "%s",
585 ut, mmd->num_played,
586 getppid(),
587 mmd->active_connections,
588 mmd->num_commands,
589 mmd->num_connects,
590 conf.loglevel_arg,
591 selector_string,
592 SUPPORTED_AUDIO_FORMATS,
593 sender_list,
594 sender_info
595 );
596 mmd_unlock();
597 free(ut);
598 free(selector_string);
599 free(sender_list);
600 free(sender_info);
601 return ret;
602 }
603
604 /* version */
605 static int com_version(int socket_fd, int argc, __a_unused char **argv)
606 {
607 if (argc != 1)
608 return -E_COMMAND_SYNTAX;
609 return send_buffer(socket_fd, "para_server-" VERSION ", \"" CODENAME "\"\n"
610 COPYRIGHT "\n"
611 "built: " BUILD_DATE "\n"
612 SYSTEM ", " CC_VERSION "\n"
613 );
614 }
615
616 /* sc */
617 static int com_sc(int socket_fd, int argc, char **argv)
618 {
619 char *name = NULL;
620 int ret, old = 0, count = -1; /* print af change forever */
621
622 if (argc > 1)
623 count = atoi(argv[1]);
624 repeat:
625 mmd_lock();
626 if (old != mmd->num_played) {
627 old = mmd->num_played;
628 name = para_strdup(mmd->filename);
629 }
630 mmd_unlock();
631 if (name) {
632 ret = send_va_buffer(socket_fd, "%s\n", name);
633 free(name);
634 name = NULL;
635 if (ret < 0)
636 return ret;
637 if (argc > 1 && !--count)
638 return 1;
639 }
640 usleep(500000);
641 goto repeat;
642 }
643
644 /* sb */
645 static int com_sb(int socket_fd, int argc, char **argv)
646 {
647 char *sb;
648 int ret, nr = -1; /* status bar will be printed that many
649 * times. Negative value means: print
650 * forever
651 */
652 if (argc > 1)
653 nr = atoi(argv[1]);
654 while (nr) {
655 mmd_lock();
656 sb = get_sb_string(mmd);
657 mmd_unlock();
658 ret = send_va_buffer(socket_fd, "%s\n", sb);
659 free(sb);
660 if (ret < 0)
661 return ret;
662 if (nr == 1)
663 return 1;
664 usleep(500000);
665 if (nr > 0)
666 nr--;
667 }
668 return 1;
669 }
670
671 /* stat */
672 static int com_stat(int socket_fd, int argc, char **argv)
673 {
674 int ret, num = 0;/* status will be printed that many
675 * times. num <= 0 means: print forever
676 */
677 struct misc_meta_data tmp, *nmmd = &tmp;
678 char *s;
679
680 signal(SIGUSR1, dummy);
681
682 if (argc > 1)
683 num = atoi(argv[1]);
684 for (;;) {
685
686 mmd_dup(nmmd);
687 s = get_status(nmmd);
688 ret = send_buffer(socket_fd, s);
689 free(s);
690 if (ret < 0)
691 goto out;
692 ret = 1;
693 if (num == 1)
694 goto out;
695 usleep(500000 * 100);
696 }
697 out:
698 return ret;
699 }
700
701 static int send_description(int fd, struct server_command *cmd, const char *handler, int num)
702 {
703 int ret, i;
704
705 for (i = 1; cmd->name && (!num || i <= num); cmd++, i++) {
706 char *perms = cmd_perms_itohuman(cmd->perms);
707 ret = send_va_buffer(fd, "%s\t%s\t%s\t%s\n", cmd->name,
708 handler,
709 perms,
710 cmd->description);
711 free(perms);
712 if (ret < 0)
713 return ret;
714 }
715 return 1;
716 }
717
718 /* always returns string that must be freed by the caller in handler */
719 static struct server_command *get_cmd_ptr(char *name, char **handler)
720 {
721 struct server_command *cmd = cmd_struct;
722
723 for (cmd = cmd_struct; cmd->name; cmd++)
724 if (!strcmp(cmd->name, name)) {
725 if (handler)
726 *handler = para_strdup("para_server"); /* server commands */
727 return cmd;
728 }
729 /* not found, look for commands supported by the current selector */
730 mmd_lock();
731 if (handler)
732 *handler = make_message("the %s selector",
733 selectors[mmd->selector_num].name);
734 cmd = selectors[mmd->selector_num].cmd_list;
735 mmd_unlock();
736 for (; cmd->name; cmd++)
737 if (!strcmp(cmd->name, name))
738 return cmd;
739 return NULL;
740 }
741
742 /* help */
743 static int com_help(int fd, int argc, char **argv)
744 {
745 struct server_command *cmd;
746 char *perms, *handler;
747 int ret;
748
749 if (argc < 2) {
750 /* no argument given, print list of commands */
751 if ((ret = send_description(fd, cmd_struct, "server", 0)) < 0)
752 return ret;
753 mmd_lock();
754 handler = para_strdup(selectors[mmd->selector_num].name);
755 cmd = selectors[mmd->selector_num].cmd_list;
756 mmd_unlock();
757 ret = send_description(fd, cmd, handler, 0);
758 free(handler);
759 return ret;
760 }
761 /* argument given for help */
762 cmd = get_cmd_ptr(argv[1], &handler);
763 if (!cmd) {
764 free(handler);
765 return -E_BAD_CMD;
766 }
767 perms = cmd_perms_itohuman(cmd->perms);
768 ret = send_va_buffer(fd,
769 "NAME\n\t%s - %s\n"
770 "SYNOPSIS\n\t para_client %s\n"
771 "DESCRIPTION\n%s\n"
772 "HANDLER\n"
773 "This command is handled by %s.\n\n"
774 "PERMISSIONS\n"
775 "Needed privileges for %s: %s\n",
776 argv[1],
777 cmd->description,
778 cmd->synopsis,
779 cmd->help,
780 handler,
781 argv[1],
782 perms
783 );
784 free(perms);
785 free(handler);
786 return ret;
787 }
788
789 /* hup */
790 static int com_hup(__a_unused int socket_fd, int argc, __a_unused char **argv)
791 {
792 if (argc != 1)
793 return -E_COMMAND_SYNTAX;
794 kill(getppid(), SIGHUP);
795 return 1;
796 }
797
798 /* term */
799 static int com_term(__a_unused int socket_fd, int argc, __a_unused char **argv)
800 {
801 if (argc != 1)
802 return -E_COMMAND_SYNTAX;
803 kill(getppid(), SIGTERM);
804 return 1;
805 }
806
807 static int com_play(__a_unused int socket_fd, int argc, __a_unused char **argv)
808 {
809 if (argc != 1)
810 return -E_COMMAND_SYNTAX;
811 mmd_lock();
812 mmd->new_afs_status_flags |= AFS_PLAYING;
813 mmd->new_afs_status_flags &= ~AFS_NOMORE;
814 mmd_unlock();
815 return 1;
816
817 }
818
819 /* stop */
820 static int com_stop(__a_unused int socket_fd, int argc, __a_unused char **argv)
821 {
822 if (argc != 1)
823 return -E_COMMAND_SYNTAX;
824 mmd_lock();
825 mmd->new_afs_status_flags &= ~AFS_PLAYING;
826 mmd->new_afs_status_flags &= ~AFS_REPOS;
827 mmd->new_afs_status_flags |= AFS_NEXT;
828 mmd_unlock();
829 return 1;
830 }
831
832 /* pause */
833 static int com_pause(__a_unused int socket_fd, int argc, __a_unused char **argv)
834 {
835 if (argc != 1)
836 return -E_COMMAND_SYNTAX;
837 mmd_lock();
838 if (!afs_paused())
839 mmd->events++;
840 mmd->new_afs_status_flags &= ~AFS_PLAYING;
841 mmd->new_afs_status_flags &= ~AFS_NEXT;
842 mmd_unlock();
843 return 1;
844 }
845
846 static int com_chs(int fd, int argc, char **argv)
847 {
848 int i, ret;
849
850 if (argc == 1) {
851 char *selector;
852 mmd_lock();
853 selector = para_strdup(selectors[mmd->selector_num].name);
854 mmd_unlock();
855 ret = send_va_buffer(fd, "%s\n", selector);
856 free(selector);
857 return ret;
858 }
859 for (i = 0; selectors[i].name; i++) {
860 if (strcmp(selectors[i].name, argv[1]))
861 continue;
862 mmd_lock();
863 mmd->selector_change = i;
864 mmd->events++;
865 mmd_unlock();
866 return 1;
867 }
868 return -E_BAD_SELECTOR;
869 }
870
871 /* next */
872 static int com_next(__a_unused int socket_fd, int argc, __a_unused char **argv)
873 {
874 if (argc != 1)
875 return -E_COMMAND_SYNTAX;
876 mmd_lock();
877 mmd->events++;
878 mmd->new_afs_status_flags |= AFS_NEXT;
879 mmd_unlock();
880 return 1;
881 }
882
883 /* nomore */
884 static int com_nomore(__a_unused int socket_fd, int argc, __a_unused char **argv)
885 {
886 if (argc != 1)
887 return -E_COMMAND_SYNTAX;
888 mmd_lock();
889 if (afs_playing() || afs_paused())
890 mmd->new_afs_status_flags |= AFS_NOMORE;
891 mmd_unlock();
892 return 1;
893 }
894
895 /* ff */
896 static int com_ff(__a_unused int socket_fd, int argc, char **argv)
897 {
898 long promille;
899 int ret, backwards = 0;
900 unsigned i;
901 char c;
902
903 if (argc != 2)
904 return -E_COMMAND_SYNTAX;
905 if (!(ret = sscanf(argv[1], "%u%c", &i, &c)))
906 return -E_COMMAND_SYNTAX;
907 if (ret > 1 && c == '-')
908 backwards = 1; /* jmp backwards */
909 mmd_lock();
910 ret = -E_NO_AUDIO_FILE;
911 if (!mmd->chunks_total || !mmd->seconds_total)
912 goto out;
913 promille = (1000 * mmd->current_chunk) / mmd->chunks_total;
914 if (backwards)
915 promille -= 1000 * i / mmd->seconds_total;
916 else
917 promille += 1000 * i / mmd->seconds_total;
918 if (promille < 0)
919 promille = 0;
920 if (promille > 1000) {
921 mmd->new_afs_status_flags |= AFS_NEXT;
922 goto out;
923 }
924 mmd->repos_request = (mmd->chunks_total * promille) / 1000;
925 mmd->new_afs_status_flags |= AFS_REPOS;
926 mmd->new_afs_status_flags &= ~AFS_NEXT;
927 mmd->events++;
928 ret = 1;
929 out:
930 mmd_unlock();
931 return ret;
932 }
933
934 /* jmp */
935 static int com_jmp(__a_unused int socket_fd, int argc, char **argv)
936 {
937 long unsigned int i;
938 int ret;
939
940 if (argc != 2)
941 return -E_COMMAND_SYNTAX;
942 if (sscanf(argv[1], "%lu", &i) <= 0)
943 return -E_COMMAND_SYNTAX;
944 mmd_lock();
945 ret = -E_NO_AUDIO_FILE;
946 if (!mmd->chunks_total)
947 goto out;
948 if (i > 100)
949 i = 100;
950 PARA_INFO_LOG("jumping to %lu%%\n", i);
951 mmd->repos_request = (mmd->chunks_total * i + 50)/ 100;
952 PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n",
953 mmd->chunks_sent, mmd->offset);
954 mmd->new_afs_status_flags |= AFS_REPOS;
955 mmd->new_afs_status_flags &= ~AFS_NEXT;
956 ret = 1;
957 mmd->events++;
958 out:
959 mmd_unlock();
960 return ret;
961 }
962
963 /*
964 * check if perms are sufficient to exec a command having perms cmd_perms.
965 * Returns 0 if perms are sufficient, -E_PERM otherwise.
966 */
967 static int check_perms(unsigned int perms, struct server_command *cmd_ptr)
968 {
969 PARA_DEBUG_LOG("%s", "checking permissions\n");
970 return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0;
971 }
972
973 /*
974 * Parse first string from *cmd and lookup in table of valid commands.
975 * On error, NULL is returned.
976 */
977 static struct server_command *parse_cmd(const char *cmdstr)
978 {
979 char buf[255];
980 int n = 0;
981
982 sscanf(cmdstr, "%200s%n", buf, &n);
983 if (!n)
984 return NULL;
985 buf[n] = '\0';
986 return get_cmd_ptr(buf, NULL);
987 }
988
989 long int para_rand(long unsigned max)
990 {
991 return (long int) ((max + 0.0) * (random() / (RAND_MAX + 1.0)));
992 }
993
994 /* Open user_list file, returns pointer to opened file on success,
995 * NULL on errors
996 */
997 static FILE *open_user_list(char *file)
998 {
999 PARA_DEBUG_LOG("opening user list %s\n", file);
1000 return fopen(file, "r");
1001 }
1002
1003 /*
1004 * lookup user in user_list file. Fills in a user struct containing
1005 * filename of the user's public key as well as the permissions of that user.
1006 * Returns 1 on success, 0 if user does not exist and < 0 on errors.
1007 */
1008 static int get_user(struct user *user) {
1009 FILE *file_ptr;
1010 char *char_ptr;
1011 char line[MAXLINE];
1012 /* keyword, user, key, perms */
1013 char w[MAXLINE], n[MAXLINE], k[MAXLINE], p[MAXLINE], tmp[4][MAXLINE];
1014 int num;
1015
1016 file_ptr = open_user_list(user_list);
1017 if (!file_ptr)
1018 return -E_USERLIST;
1019 while (fgets(line, MAXLINE, file_ptr)) {
1020 // PARA_DEBUG_LOG("%s: Read line (%i bytes) "
1021 // "from config file\n", __func__, strlen(line));
1022 if (sscanf(line,"%200s %200s %200s %200s", w, n, k, p) < 3)
1023 continue;
1024 if (!strcmp(w, "user") && !strcmp(user->name, n)) {
1025 PARA_DEBUG_LOG("found entry for %s\n", n);
1026 strcpy(user->name, n);
1027 strcpy(user->pubkey_file, k);
1028 user->perms = 0;
1029 char_ptr = p;
1030 num = sscanf(char_ptr, "%200[A-Z_],%200[A-Z_],%200[A-Z_],%200[A-Z_]",
1031 tmp[0], tmp[1], tmp[2], tmp[3]);
1032 PARA_DEBUG_LOG("found %i perm entries\n",
1033 num);
1034 user->perms = 0;
1035 while (num > 0) {
1036 num--;
1037 //PARA_DEBUG_LOG("%s: tmp[%i]=%s\n", __func__,
1038 // num, tmp[num]);
1039 if (!strcmp(tmp[num], "AFS_READ"))
1040 user->perms =
1041 user->perms | AFS_READ;
1042 else if (!strcmp(tmp[num], "AFS_WRITE"))
1043 user->perms =
1044 user->perms | AFS_WRITE;
1045 else if (!strcmp(tmp[num], "DB_READ"))
1046 user->perms = user->perms | DB_READ;
1047 else if (!strcmp(tmp[num], "DB_WRITE"))
1048 user->perms = user->perms | DB_WRITE;
1049 else /* unknown permission */
1050 PARA_WARNING_LOG("unknown permission:"
1051 "%s\n", tmp[num]);
1052 }
1053 fclose(file_ptr);
1054 return 1;
1055 }
1056 }
1057 fclose(file_ptr);
1058 return 0;
1059 }
1060
1061 static void init_rc4_keys(void)
1062 {
1063 int i;
1064
1065 for (i = 0; i < 2 * RC4_KEY_LEN; i++)
1066 rc4_buf[i] = para_rand(256);
1067 PARA_DEBUG_LOG("rc4 keys initialized (%u:%u)\n",
1068 (unsigned char) rc4_buf[0],
1069 (unsigned char) rc4_buf[RC4_KEY_LEN]);
1070 RC4_set_key(&rc4_recv_key, RC4_KEY_LEN, rc4_buf);
1071 RC4_set_key(&rc4_send_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN);
1072 }
1073
1074 static void rc4_recv(unsigned long len, const unsigned char *indata, unsigned char *outdata)
1075 {
1076 RC4(&rc4_recv_key, len, indata, outdata);
1077 }
1078
1079 static void rc4_send(unsigned long len, const unsigned char *indata, unsigned char *outdata)
1080 {
1081 RC4(&rc4_send_key, len, indata, outdata);
1082 }
1083
1084 int handle_connect(int fd, struct sockaddr_in *addr)
1085 {
1086 int numbytes, ret, argc, use_rc4 = 0;
1087 char buf[STRINGSIZE];
1088 unsigned char crypt_buf[MAXLINE];
1089 struct user u;
1090 struct server_command *cmd = NULL;
1091 long unsigned challenge_nr, chall_response;
1092 char **argv = NULL;
1093 char *p, *command = NULL;
1094
1095 signal(SIGCHLD, SIG_IGN);
1096 signal(SIGINT, SIG_DFL);
1097 signal(SIGTERM, SIG_DFL);
1098 signal(SIGHUP, SIG_DFL);
1099 signal(SIGUSR1, SIG_IGN);
1100
1101 in_addr = addr;
1102 challenge_nr = random();
1103 /* send Welcome message */
1104 ret = send_va_buffer(fd, "This is para_server, version " VERSION ".\n" );
1105 if (ret < 0)
1106 goto err_out;
1107 /* recv auth request line */
1108 ret = recv_buffer(fd, buf, sizeof(buf));
1109 if (ret < 0)
1110 goto err_out;
1111 if (ret <= 6) {
1112 ret = -E_AUTH;
1113 goto err_out;
1114 }
1115 numbytes = ret;
1116 ret = -E_AUTH;
1117 if (strncmp(buf, "auth ", 5))
1118 goto err_out;
1119
1120 if (numbytes < 9 || strncmp(buf, "auth rc4 ", 9))
1121 strcpy(u.name, buf + 5); /* client version < 0.2.6 */
1122 else {
1123 strcpy(u.name, buf + 9); /* client version >= 0.2.6 */
1124 use_rc4 = 1;
1125 }
1126 // strcpy(u.name, buf + 5); /* ok, but ugly */
1127 PARA_DEBUG_LOG("received %s request for user %s\n",
1128 use_rc4? "rc4" : "auth", u.name);
1129 /* lookup user in list file */
1130 if ((ret = get_user(&u)) < 0)
1131 goto err_out;
1132 if (!ret) { /* user not found */
1133 PARA_WARNING_LOG("auth request for unknown user %s\n", u.name);
1134 ret = -E_BAD_USER;
1135 goto err_out;
1136 }
1137 ret = para_encrypt_challenge(u.pubkey_file, challenge_nr, crypt_buf);
1138 if (ret <= 0)
1139 goto err_out;
1140 numbytes = ret;
1141 PARA_DEBUG_LOG("sending %d byte challenge\n", numbytes);
1142 /* We can't use send_buffer here since buf may contain null bytes */
1143 ret = send_bin_buffer(fd,(char *) crypt_buf, numbytes);
1144 if (ret < 0)
1145 goto err_out;
1146 /* recv decrypted number */
1147 numbytes = recv_buffer(fd, buf, sizeof(buf));
1148 ret = numbytes;
1149 if (ret < 0)
1150 goto err_out;
1151 ret = -E_AUTH;
1152 if (!numbytes)
1153 goto err_out;
1154 if (sscanf(buf, CHALLENGE_RESPONSE_MSG "%lu", &chall_response) < 1
1155 || chall_response != challenge_nr)
1156 goto err_out;
1157 /* auth successful. Send 'Proceed' message */
1158 PARA_INFO_LOG("good auth for %s (%lu)\n", u.name, challenge_nr);
1159 sprintf(buf, "%s", PROCEED_MSG);
1160 if (use_rc4) {
1161 init_rc4_keys();
1162 ret = para_encrypt_buffer(u.pubkey_file, rc4_buf, 2 * RC4_KEY_LEN,
1163 (unsigned char *)buf + PROCEED_MSG_LEN + 1);
1164 if (ret <= 0)
1165 goto err_out;
1166 numbytes = ret + strlen(PROCEED_MSG) + 1;
1167 } else
1168 numbytes = strlen(buf);
1169 ret = send_bin_buffer(fd, buf, numbytes);
1170 if (ret < 0)
1171 goto err_out;
1172 if (use_rc4) {
1173 crypt_function_recv = rc4_recv;
1174 crypt_function_send = rc4_send;
1175 PARA_INFO_LOG("%s", "rc4 encrytion activated\n");
1176 }
1177 /* read command */
1178 while ((numbytes = recv_buffer(fd, buf, sizeof(buf))) > 0) {
1179 // PARA_INFO_LOG("recvd: %s (%d)\n", buf, numbytes);
1180 ret = -E_COMMAND_SYNTAX;
1181 if (command && numbytes + strlen(command) > STRINGSIZE) /* DOS */
1182 goto err_out;
1183 command = para_strcat(command, buf);
1184 if ((p = strstr(command, EOC_MSG))) {
1185 *p = '\0';
1186 break;
1187 }
1188 }
1189 ret = numbytes;
1190 if (ret < 0)
1191 goto err_out;
1192 ret = -E_BAD_CMD;
1193 /* parse command */
1194 if (!(cmd = parse_cmd(command)))
1195 goto err_out;
1196 /* valid command, check permissions */
1197 ret = check_perms(u.perms, cmd);
1198 if (ret < 0)
1199 goto err_out;
1200 /* valid command and sufficient perms */
1201 alarm(0);
1202 argc = split_args(command, &argv, "\n");
1203 mmd_lock();
1204 mmd->num_commands++;
1205 mmd_unlock();
1206 PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name, u.name,
1207 inet_ntoa(addr->sin_addr));
1208 ret = cmd->handler(fd, argc, argv);
1209 if (ret >= 0) {
1210 ret = EXIT_SUCCESS;
1211 goto out;
1212 }
1213 err_out:
1214 if (ret != -E_SEND && ret != -E_RECV) {
1215 PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-ret));
1216 send_va_buffer(fd, "%s\n", PARA_STRERROR(-ret));
1217 }
1218 ret = EXIT_FAILURE;
1219 out:
1220 free(command);
1221 free(argv);
1222 mmd_lock();
1223 if (cmd && (cmd->perms & DB_WRITE) && ret >= 0)
1224 mmd->events++;
1225 mmd->active_connections--;
1226 mmd_unlock();
1227 return ret;
1228 }