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