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