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