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