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