1 #warning IMPLEMENT POST SUPPORT
2 /*
3 * Copyright (C) 2000-2019 the xine project
4 *
5 * This file is part of xine, a unix video player.
6 *
7 * xine is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * xine is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
20 *
21 * xine network related stuff
22 *
23 */
24 /* required for getsubopt(); the __sun test avoids compilation problems on
25 solaris. On FreeBSD defining this disable BSD functions to be visible
26 and remove INADDR_NONE */
27 #if ! defined (__sun__) && ! defined (__OpenBSD__) && ! defined(__FreeBSD__) && ! defined(__NetBSD__) && ! defined(__APPLE__) && ! defined (__DragonFly__)
28 #define _XOPEN_SOURCE 500
29 #endif
30 /* required for strncasecmp() */
31 #define _BSD_SOURCE 1
32 /* required to enable POSIX variant of getpwuid_r on solaris */
33 #define _POSIX_PTHREAD_SEMANTICS 1
34
35 #include "config.h"
36
37 #ifdef HAVE_READLINE
38
39 #include <stdio.h>
40 #include <stdlib.h>
41
42 #if defined(__hpux)
43 #include <strings.h>
44 #else
45 #include <string.h>
46 #endif
47
48 #include <stdarg.h>
49 #include <ctype.h>
50 #include <dirent.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <unistd.h>
54 #include <time.h>
55 #include <sys/ioctl.h>
56 #include <sys/select.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <signal.h>
60 #include <sys/time.h>
61 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 #include <netdb.h>
65 #include <pthread.h>
66
67 #include <readline/readline.h>
68 #include <readline/history.h>
69
70 #include "common.h"
71
72 #ifndef MSG_NOSIGNAL
73 #define MSG_NOSIGNAL 0
74 #endif
75
76 #define DEFAULT_XINECTL_PORT "6789"
77 #define DEFAULT_SERVER "localhost"
78
79 #define PROGNAME "xine-remote"
80 #define PROGVERSION "0.1.2"
81
82 #define _BUFSIZ 20480
83
84 #define COMMANDS_PREFIX "/\377\200COMMANDS"
85
86 #ifdef NETWORK_CLIENT
87
88 #ifndef INADDR_NONE
89 #define INADDR_NONE ((unsigned long) -1)
90 #endif
91
92 #ifdef HAVE_GETOPT_LONG
93 # include <getopt.h>
94 #else
95 # include "getopt.h"
96 #endif
97
98 /* options args */
99 static const char short_options[] = "?hH:P:ncv";
100 static const struct option long_options[] = {
101 { "help" , no_argument , 0, 'h' },
102 { "host" , required_argument, 0, 'H' },
103 { "port" , required_argument, 0, 'P' },
104 { "command" , no_argument , 0, 'c' },
105 { "noconnect" , no_argument , 0, 'n' },
106 { "version" , no_argument , 0, 'v' },
107 { 0 , no_argument , 0, 0 }
108 };
109
110
111 typedef struct session_s session_t;
112 typedef struct session_commands_s session_commands_t;
113 typedef void (*client_func_t)(session_t *, session_commands_t *, const char *);
114
115 static void client_noop(session_t *, session_commands_t *, const char *);
116 static void client_help(session_t *, session_commands_t *, const char *);
117 static void client_version(session_t *, session_commands_t *, const char *);
118 static void client_open(session_t *, session_commands_t *, const char *);
119 static void client_close(session_t *, session_commands_t *, const char *);
120 static void client_quit(session_t *, session_commands_t *, const char *);
121
122 struct session_s {
123 char host[256];
124 char port[256];
125 int socket;
126 int console;
127 int running;
128 char prompt[544];
129 pthread_t thread;
130 pthread_mutex_t console_mutex;
131 };
132
133 #define ORIGIN_SERVER 1
134 #define ORIGIN_CLIENT 2
135
136 struct session_commands_s {
137 char *command;
138 int origin;
139 int enable;
140 client_func_t function;
141 };
142
143 typedef struct client_commands_s {
144 const char *command;
145 int origin;
146 int enable;
147 client_func_t function;
148 } client_commands_t;
149
150 static session_t session;
151 static const client_commands_t client_commands[] = {
152 { "?", ORIGIN_CLIENT, 1, client_help },
153 { "version", ORIGIN_CLIENT, 1, client_version },
154 { "open", ORIGIN_CLIENT, 1, client_open },
155 { "close", ORIGIN_CLIENT, 1, client_close },
156 { "quit", ORIGIN_CLIENT, 1, client_quit },
157 { NULL, ORIGIN_CLIENT, 1, NULL }
158 };
159
160 static session_commands_t **session_commands = NULL;
161
162 #else /* NETWORK_CLIENT */
163
164 # include <pwd.h>
165 # include <X11/Xlib.h>
166 # include <xine.h>
167 # include <xine/xineutils.h>
168
169 typedef struct commands_s commands_t;
170 typedef struct client_info_s client_info_t;
171 typedef struct passwds_s passwds_t;
172 typedef void (*cmd_func_t)(const commands_t *, client_info_t *);
173
174 static passwds_t **passwds = NULL;
175
176 static void do_commands(const commands_t *, client_info_t *);
177 static void do_help(const commands_t *, client_info_t *);
178 static void do_syntax(const commands_t *, client_info_t *);
179 static void do_auth(const commands_t *, client_info_t *);
180 static void do_mrl(const commands_t *, client_info_t *);
181 static void do_playlist(const commands_t *, client_info_t *);
182 static void do_play(const commands_t *, client_info_t *);
183 static void do_stop(const commands_t *, client_info_t *);
184 static void do_pause(const commands_t *, client_info_t *);
185 static void do_exit(const commands_t *, client_info_t *);
186 static void do_fullscreen(const commands_t *, client_info_t *);
187 #ifdef HAVE_XINERAMA
188 static void do_xinerama_fullscreen(const commands_t *, client_info_t *);
189 #endif
190 static void do_get(const commands_t *, client_info_t *);
191 static void do_set(const commands_t *, client_info_t *);
192 static void do_gui(const commands_t *, client_info_t *);
193 static void do_event(const commands_t *, client_info_t *);
194 static void do_seek(const commands_t *, client_info_t *);
195 static void do_halt(const commands_t *, client_info_t *);
196 static void do_snap(const commands_t *, client_info_t *);
197
198 static void handle_client_command(client_info_t *);
199
200 #define MAX_NAME_LEN 16
201 #define MAX_PASSWD_LEN 16
202
203 #define NO_ARGS 1
204 #define REQUIRE_ARGS 2
205 #define OPTIONAL_ARGS 3
206
207 #define NOT_PUBLIC 0
208 #define PUBLIC 1
209
210 #define AUTH_UNNEEDED 0
211 #define NEED_AUTH 1
212
213 #define PASSWD_ALL_ALLOWED 1
214 #define PASSWD_ALL_DENIED 2
215 #define PASSWD_USER_ALLOWED 3
216 #define PASSWD_USER_DENIED 4
217
218 struct commands_s {
219 const char *command;
220 int argtype;
221 int public;
222 int need_auth;
223 cmd_func_t function;
224 const char *help;
225 const char *syntax;
226 };
227
228 struct client_info_s {
229 int authentified;
230 char name[MAX_NAME_LEN + 1];
231 char passwd[MAX_PASSWD_LEN + 1];
232
233 int finished;
234
235 int socket;
236 union {
237 struct sockaddr_in in;
238 struct sockaddr sa;
239 } fsin;
240 char **actions;
241
242 struct {
243 int execute;
244 char *remain;
245 char *line;
246 char *command;
247 int num_args;
248 char *args[256];
249 } command;
250
251 };
252
253 struct passwds_s {
254 char *ident;
255 char *passwd;
256 };
257
258 typedef struct {
259 FILE *fd;
260 char *ln;
261 char buf[256];
262 } fileobj_t;
263
264 static const commands_t commands[] = {
265 { "commands", NO_ARGS, NOT_PUBLIC, AUTH_UNNEEDED, do_commands,
266 "Display all available commands, tab separated, dot terminated.",
267 "commands"
268 },
269 { "help", OPTIONAL_ARGS, PUBLIC, AUTH_UNNEEDED, do_help,
270 "Display the help text if a command name is specified, otherwise display "
271 "all available commands.",
272 " help [command]"
273 },
274 { "syntax", REQUIRE_ARGS, PUBLIC, AUTH_UNNEEDED, do_syntax,
275 "Display a command syntax.",
276 " syntax [command]"
277 },
278 { "identify", REQUIRE_ARGS, PUBLIC, AUTH_UNNEEDED, do_auth,
279 "Identify client.",
280 "identify <ident> <password>."
281 },
282 { "mrl", REQUIRE_ARGS, PUBLIC, NEED_AUTH, do_mrl,
283 "manage mrls",
284 " mrl add <mrl> <mrl> ...\n"
285 " mrl play <mrl>\n"
286 " mrl next\n"
287 " mrl prev"
288 },
289 { "play", NO_ARGS, PUBLIC, NEED_AUTH, do_play,
290 "start playback",
291 " play"
292 },
293 { "playlist", REQUIRE_ARGS, PUBLIC, NEED_AUTH, do_playlist,
294 "manage playlist",
295 " playlist show\n"
296 " playlist select <num>\n"
297 " playlist delete all|*\n"
298 " playlist delete <num>\n"
299 " playlist first\n"
300 " playlist prev\n"
301 " playlist next\n"
302 " playlist last\n"
303 " playlist stop\n"
304 " playlist continue"
305 },
306 { "stop", NO_ARGS, PUBLIC, NEED_AUTH, do_stop,
307 "stop playback",
308 " stop"
309 },
310 { "pause", NO_ARGS, PUBLIC, NEED_AUTH, do_pause,
311 "pause/resume playback",
312 " pause"
313 },
314 { "exit", NO_ARGS, PUBLIC, AUTH_UNNEEDED, do_exit,
315 "close connection",
316 " exit"
317 },
318 { "fullscreen", NO_ARGS, PUBLIC, NEED_AUTH, do_fullscreen,
319 "fullscreen toggle",
320 " fullscreen"
321 },
322 #ifdef HAVE_XINERAMA
323 { "xineramafull" , NO_ARGS, PUBLIC, NEED_AUTH, do_xinerama_fullscreen,
324 "xinerama fullscreen toggle",
325 " expand display on further screens"
326 },
327 #endif
328 { "get", REQUIRE_ARGS, PUBLIC, NEED_AUTH, do_get,
329 "get values",
330 " get status\n"
331 " get audio channel\n"
332 " get audio lang\n"
333 " get audio volume\n"
334 " get audio mute\n"
335 " get spu channel\n"
336 " get spu lang\n"
337 " get spu offset\n"
338 " get av offset\n"
339 " get speed\n"
340 " get position\n"
341 " get length\n"
342 " get loop"
343 },
344 { "set", REQUIRE_ARGS, PUBLIC, NEED_AUTH, do_set,
345 "set values",
346 " set audio channel <num>\n"
347 " set audio volume <%>\n"
348 " set audio mute <state>\n"
349 " set spu channel <num>\n"
350 " set spu offset <offset>\n"
351 " set av offset <offset>\n"
352 " set speed <XINE_SPEED_PAUSE|XINE_SPEED_SLOW_4|XINE_SPEED_SLOW_2|XINE_SPEED_NORMAL|XINE_SPEED_FAST_2|XINE_SPEED_FAST_4>\n"
353 " < | | /4 | /2 | = | *2 | *4 >\n"
354 " set loop <no | loop | repeat | shuffle | shuffle+>"
355 },
356 { "gui", REQUIRE_ARGS, PUBLIC, NEED_AUTH, do_gui,
357 "manage gui windows",
358 " gui hide\n"
359 " gui output\n"
360 " gui panel\n"
361 " gui playlist\n"
362 " gui control\n"
363 " gui mrl\n"
364 " gui setup\n"
365 " gui vpost\n"
366 " gui help\n"
367 " gui log"
368 },
369 { "event", REQUIRE_ARGS, PUBLIC, NEED_AUTH, do_event,
370 "Send an event to xine",
371 " event menu1\n"
372 " event menu2\n"
373 " event menu3\n"
374 " event menu4\n"
375 " event menu5\n"
376 " event menu6\n"
377 " event menu7\n"
378 " event 0"
379 " event 1"
380 " event 2"
381 " event 3"
382 " event 4"
383 " event 5"
384 " event 6"
385 " event 7"
386 " event 8"
387 " event 9"
388 " event +10"
389 " event up\n"
390 " event down\n"
391 " event left\n"
392 " event right\n"
393 " event next\n"
394 " event previous\n"
395 " event angle next\n"
396 " event angle previous\n"
397 " event select"
398 },
399 { "seek", REQUIRE_ARGS, PUBLIC, NEED_AUTH, do_seek,
400 "Seek in current stream",
401 "seek %[0...100]\n"
402 "seek [+|-][secs]"
403 },
404 { "halt", NO_ARGS, PUBLIC, NEED_AUTH, do_halt,
405 "Stop xine program",
406 "halt"
407 },
408 { "snapshot", NO_ARGS, PUBLIC, NEED_AUTH, do_snap,
409 "Take a snapshot",
410 "snapshot"
411 },
412 { NULL, -1, NOT_PUBLIC, AUTH_UNNEEDED, NULL,
413 NULL,
414 NULL
415 }
416 };
417
418 static const struct {
419 const char *name;
420 int status;
421 } status_struct[] = {
422 { "XINE_STATUS_IDLE" , XINE_STATUS_IDLE },
423 { "XINE_STATUS_PLAY" , XINE_STATUS_PLAY },
424 { "XINE_STATUS_STOP" , XINE_STATUS_STOP },
425 { "XINE_STATUS_QUIT" , XINE_STATUS_QUIT },
426 { NULL , -1 }
427 };
428
429 static const struct {
430 const char *name;
431 int speed;
432 } speeds_struct[] = {
433 { "XINE_SPEED_PAUSE" , XINE_SPEED_PAUSE },
434 { "XINE_SPEED_SLOW_4" , XINE_SPEED_SLOW_4 },
435 { "XINE_SPEED_SLOW_2" , XINE_SPEED_SLOW_2 },
436 { "XINE_SPEED_NORMAL" , XINE_SPEED_NORMAL },
437 { "XINE_SPEED_FAST_2" , XINE_SPEED_FAST_2 },
438 { "XINE_SPEED_FAST_4" , XINE_SPEED_FAST_4 },
439 { NULL , 0 }
440 };
441
442
443 #endif
444
sock_err(const char * error_msg,...)445 static void __attribute__ ((format (printf, 1, 2))) sock_err(const char *error_msg, ...) {
446 va_list args;
447
448 va_start(args, error_msg);
449 vfprintf(stderr, error_msg, args);
450 va_end(args);
451 }
452
sock_create(const char * service,const char * transport,struct sockaddr_in * sin)453 static int sock_create(const char *service, const char *transport, struct sockaddr_in *sin) {
454 struct servent *iservice;
455 struct protoent *itransport;
456 int sock;
457 int type;
458 int proto = 0;
459
460 memset(sin, 0, sizeof(*sin));
461
462 sin->sin_family = AF_INET;
463 sin->sin_port = htons(atoi(service));
464
465 if(!sin->sin_port) {
466 iservice = getservbyname(service, "tcp");
467
468 if(!iservice)
469 sock_err("Service not registered: %s\n", service);
470 else
471 sin->sin_port = iservice->s_port;
472 }
473
474 itransport = getprotobyname(transport);
475
476 if(!itransport)
477 sock_err("Protocol not registered: %s\n", transport);
478 else
479 proto = itransport->p_proto;
480
481 if(!strcmp(transport, "udp"))
482 type = SOCK_DGRAM;
483 else
484 type = SOCK_STREAM;
485
486 sock = socket(AF_INET, type, proto);
487
488 if(sock < 0) {
489 sock_err("Cannot create socket: %s\n", strerror(errno));
490 return -1;
491 }
492
493 if (fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) {
494 sock_err("** socket cannot be made uninheritable (%s)\n", strerror(errno));
495 }
496
497 return sock;
498 }
499
500 /*
501 * Check for socket validity.
502 */
sock_check_opened(int socket)503 static int sock_check_opened(int socket) {
504 fd_set readfds, writefds, exceptfds;
505 int retval;
506 struct timeval timeout;
507
508 for(;;) {
509 FD_ZERO(&readfds);
510 FD_ZERO(&writefds);
511 FD_ZERO(&exceptfds);
512 FD_SET(socket, &exceptfds);
513
514 timeout.tv_sec = 0;
515 timeout.tv_usec = 0;
516
517 retval = select(socket + 1, &readfds, &writefds, &exceptfds, &timeout);
518
519 if(retval == -1 && (errno != EAGAIN && errno != EINTR))
520 return 0;
521
522 if (retval != -1)
523 return 1;
524 }
525
526 return 0;
527 }
528
529 /*
530 * Write to socket.
531 */
_sock_write(int socket,const char * buf,int len)532 static int _sock_write(int socket, const char *buf, int len) {
533 ssize_t size;
534 int wlen = 0;
535
536 if((socket < 0) || (buf == NULL))
537 return -1;
538
539 if(!sock_check_opened(socket))
540 return -1;
541
542 while(len) {
543 size = write(socket, buf, len);
544
545 if(size <= 0)
546 return -1;
547
548 len -= size;
549 wlen += size;
550 buf += size;
551 }
552
553 return wlen;
554 }
555
__sock_write(int socket,int cr,const char * msg,...)556 static int __attribute__ ((format (printf, 3, 4))) __sock_write(int socket, int cr, const char *msg, ...) {
557 char buf[_BUFSIZ];
558 va_list args;
559
560 va_start(args, msg);
561 vsnprintf(buf, _BUFSIZ, msg, args);
562 va_end(args);
563
564 /* Each line sent is '\n' terminated */
565 if(cr) {
566 if((buf[strlen(buf)] == '\0') && (buf[strlen(buf) - 1] != '\n'))
567 strlcat(buf, "\n", sizeof(buf));
568 }
569
570 return _sock_write(socket, buf, strlen(buf));
571 }
572
573 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91)
574 #define sock_write(socket, msg, args...) __sock_write(socket, 1, msg, ##args)
575 #define sock_write_nocr(socket, msg, args...) __sock_write(socket, 0, msg, ##args)
576 #else
577 #define sock_write(socket, ...) __sock_write(socket, 1, __VA_ARGS__)
578 #define sock_write_nocr(socket, ...) __sock_write(socket, 0, __VA_ARGS__)
579 #endif
580
_atoa(char * str)581 static char *_atoa(char *str) {
582 char *pbuf;
583
584 pbuf = str;
585
586 while(*pbuf != '\0') pbuf++;
587
588 if(pbuf > str)
589 pbuf--;
590
591 while((pbuf > str) && (*pbuf == '\r' || *pbuf == '\n')) {
592 *pbuf = '\0';
593 pbuf--;
594 }
595
596 while((pbuf > str) && (*pbuf == ' ')) {
597 *pbuf = '\0';
598 pbuf--;
599 }
600
601 pbuf = str;
602 while(*pbuf == '"' || *pbuf == ' ' || *pbuf == '\t') pbuf++;
603
604 return pbuf;
605 }
606
607 #ifdef NETWORK_CLIENT
sock_client(const char * host,const char * service,const char * transport)608 static int sock_client(const char *host, const char *service, const char *transport) {
609 union {
610 struct sockaddr_in in;
611 struct sockaddr sa;
612 } fsin;
613 struct hostent *ihost;
614 int sock;
615
616 sock = sock_create(service, transport, &fsin.in);
617 if (sock < 0) {
618 return -1;
619 }
620
621 if ((fsin.in.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
622 ihost = gethostbyname(host);
623
624 if(!ihost) {
625 sock_err("Unknown host: %s\n", host);
626 return -1;
627 }
628 memcpy(&fsin.in.sin_addr, ihost->h_addr_list[0], ihost->h_length);
629 }
630
631 if(connect(sock, &fsin.sa, sizeof(fsin.in)) < 0) {
632 int err = errno;
633
634 close(sock);
635 errno = err;
636 sock_err("Unable to connect %s[%s]: %s\n", host, service, strerror(errno));
637 return -1;
638 }
639
640 return sock;
641 }
642
643 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91)
644 #define write_to_console_unlocked(session, msg, args...) __sock_write(session->console, 1, msg, ##args)
645 #define write_to_console_unlocked_nocr(session, msg, args...) __sock_write(session->console, 0, msg, ##args)
646 #else
647 #define write_to_console_unlocked(session, ...) __sock_write(session->console, 1, __VA_ARGS__)
648 #define write_to_console_unlocked_nocr(session, ...) __sock_write(session->console, 0, __VA_ARGS__)
649 #endif
650
write_to_console(session_t * session,const char * msg,...)651 static int __attribute__ ((format (printf, 2, 3))) write_to_console(session_t *session, const char *msg, ...) {
652 char buf[_BUFSIZ];
653 va_list args;
654 int err;
655
656 va_start(args, msg);
657 vsnprintf(buf, _BUFSIZ, msg, args);
658 va_end(args);
659
660 pthread_mutex_lock(&session->console_mutex);
661 err = write_to_console_unlocked(session, "%s", buf);
662 pthread_mutex_unlock(&session->console_mutex);
663
664 return err;
665 }
666
667 #if 0
668 static int __attribute__ ((format (printf, 2, 3)) write_to_console_nocr(session_t *session, const char *msg, ...) {
669 char buf[_BUFSIZ];
670 va_list args;
671 int err;
672
673 va_start(args, msg);
674 vsnprintf(buf, _BUFSIZ, msg, args);
675 va_end(args);
676
677 pthread_mutex_lock(&session->console_mutex);
678 err = write_to_console_unlocked_nocr(session, buf);
679 pthread_mutex_unlock(&session->console_mutex);
680
681 return err;
682 }
683 #endif
684
session_update_prompt(session_t * session)685 static void session_update_prompt(session_t *session) {
686 if(session == NULL)
687 return;
688
689 if(session->socket >= 0)
690 snprintf(session->prompt, sizeof(session->prompt), "[%s:%s]"PROGNAME" >", session->host, session->port);
691 else
692 strlcpy(session->prompt, "[******:****]"PROGNAME" >", sizeof(session->prompt));
693 }
694
session_create_commands(session_t * session)695 static void session_create_commands(session_t *session) {
696 static const size_t num_commands = (sizeof(client_commands) / sizeof(client_commands[0]));
697 int i;
698
699 if(session == NULL)
700 return;
701
702 if(session_commands != NULL) {
703 i = 0;
704 while(session_commands[i]->command != NULL) {
705 free(session_commands[i]->command);
706 free(session_commands[i]);
707 i++;
708 }
709 free(session_commands[i]);
710 free(session_commands);
711 }
712
713 session_commands = (session_commands_t **) calloc(num_commands, sizeof(session_commands_t *));
714 for(i = 0; client_commands[i].command != NULL; i++) {
715 session_commands[i] = (session_commands_t *) malloc(sizeof(session_commands_t));
716 session_commands[i]->command = strdup(client_commands[i].command);
717 session_commands[i]->origin = client_commands[i].origin;
718 session_commands[i]->enable = client_commands[i].enable;
719 session_commands[i]->function = client_commands[i].function;
720 }
721 session_commands[i] = (session_commands_t *) malloc(sizeof(session_commands_t));
722 session_commands[i]->command = NULL;
723 session_commands[i]->origin = ORIGIN_CLIENT;
724 session_commands[i]->enable = 0;
725 session_commands[i]->function = NULL;
726
727 }
728
729 /*
730 * Client commands
731 */
client_noop(session_t * session,session_commands_t * command,const char * cmd)732 static void client_noop(session_t *session, session_commands_t *command, const char *cmd) {
733 }
client_help(session_t * session,session_commands_t * command,const char * cmd)734 static void client_help(session_t *session, session_commands_t *command, const char *cmd) {
735 int i = 0;
736 size_t maxlen = 0, j;
737 int curpos = 0;
738 char buf[_BUFSIZ] = "Available commands are:\n ";
739
740 if((session == NULL) || (command == NULL))
741 return;
742
743 while(session_commands[i]->command != NULL) {
744
745 if(session_commands[i]->enable) {
746 if(strlen(session_commands[i]->command) > maxlen)
747 maxlen = strlen(session_commands[i]->command);
748 }
749
750 i++;
751 }
752
753 maxlen++;
754 i = 0;
755
756 curpos += 7;
757
758 while(session_commands[i]->command != NULL) {
759 if(session_commands[i]->enable) {
760 if((curpos + maxlen) >= 80) {
761 strlcat(buf, "\n ", sizeof(buf));
762 curpos = 7;
763 }
764
765 strlcat(buf, session_commands[i]->command, sizeof(buf));
766 curpos += strlen(session_commands[i]->command);
767
768 for(j = 0; j < (maxlen - strlen(session_commands[i]->command)); j++) {
769 strlcat(buf, " ", sizeof(buf));
770 curpos++;
771 }
772 }
773 i++;
774 }
775
776 write_to_console(session, "%s\n", buf);
777 }
client_version(session_t * session,session_commands_t * command,const char * cmd)778 static void client_version(session_t *session, session_commands_t *command, const char *cmd) {
779
780 if((session == NULL) || (command == NULL))
781 return;
782
783 write_to_console(session, "%s version %s\n\n", PROGNAME, PROGVERSION);
784 }
client_close(session_t * session,session_commands_t * command,const char * cmd)785 static void client_close(session_t *session, session_commands_t *command, const char *cmd) {
786
787 if((session == NULL) || (command == NULL))
788 return;
789
790 if(session->socket >= 0) {
791 int i = 0;
792
793 sock_write(session->socket, "exit");
794 close(session->socket);
795 session->socket = -1;
796 session_update_prompt(session);
797
798 while(session_commands[i]->command != NULL) {
799 if(session_commands[i]->origin == ORIGIN_SERVER)
800 session_commands[i]->enable = 0;
801 i++;
802 }
803 }
804 }
client_open(session_t * session,session_commands_t * command,const char * cmd)805 static void client_open(session_t *session, session_commands_t *command, const char *cmd) {
806 char buf[_BUFSIZ];
807 char *pbuf, *p;
808 char *host = NULL, *port = NULL;
809
810 if((session == NULL) || (command == NULL))
811 return;
812
813 if(session->socket >= 0) {
814 write_to_console(session, "Already connected to '%s:%s'.", session->host, session->port);
815 return;
816 }
817
818 if(cmd) {
819 strlcpy(buf, cmd, sizeof(buf));
820 pbuf = buf;
821 if((p = strchr(pbuf, ' ')) != NULL) {
822 host = _atoa(p);
823 if((port = strrchr(p, ':')) != NULL) {
824 if(strlen(port) > 1) {
825 *port = '\0';
826 port++;
827 }
828 else {
829 *port = '\0';
830 port = NULL;
831 }
832 }
833 }
834
835 if(host != NULL) {
836
837 strlcpy(session->host, host, sizeof(session->host));
838
839 if(port != NULL)
840 strlcpy(session->port, port, sizeof(session->port));
841
842 session_create_commands(session);
843 session->socket = sock_client(session->host, session->port, "tcp");
844 if(session->socket < 0) {
845 write_to_console(session, "opening server '%s' failed: %s.\nExiting.\n",
846 session->host, strerror(errno));
847
848 session->running = 0;
849 }
850 sock_write(session->socket, "commands");
851 session_update_prompt(session);
852 }
853 else {
854 write_to_console(session, "open require arguments (host or host:port)\n");
855 }
856 }
857
858 }
client_quit(session_t * session,session_commands_t * command,const char * cmd)859 static void client_quit(session_t *session, session_commands_t *command, const char *cmd) {
860
861 client_close(session, command, cmd);
862 session->running = 0;
863 }
864 /*
865 * End of client commands
866 */
867
868 /*
869 * completion generator functions.
870 */
command_generator(const char * text,int state)871 static char *command_generator(const char *text, int state) {
872 static int index, len;
873 char *cmd, *retcmd = NULL;
874
875 if(!state) {
876 index = 0;
877 len = strlen(text);
878 }
879
880 if(len) {
881 while((cmd = session_commands[index]->command) != NULL) {
882 index++;
883 if(session_commands[index - 1]->enable) {
884 if(strncasecmp(cmd, text, len) == 0) {
885 retcmd = strdup(cmd);
886 return retcmd;
887 }
888 }
889 }
890 }
891
892 return NULL;
893 }
completion_function(const char * text,int start,int end)894 static char **completion_function(const char *text, int start, int end) {
895 char **cmd = NULL;
896
897 if(start == 0)
898 cmd = rl_completion_matches (text, command_generator);
899
900 return cmd;
901 }
902
signals_handler(int sig)903 static void signals_handler (int sig) {
904 switch(sig) {
905
906 /* Kill the line on C-c */
907 case SIGINT:
908 case SIGTERM:
909 if(rl_prompt) { /* readline is running, otherwise we are in script mode */
910 rl_kill_full_line(1, 1);
911 rl_redisplay();
912 }
913 else {
914 if(session.socket >= 0) {
915 sock_write(session.socket, "exit");
916 close(session.socket);
917 }
918 exit(1);
919 }
920 break;
921
922 }
923 }
924
select_thread(void * data)925 static __attribute__((noreturn)) void *select_thread(void *data) {
926 session_t *session = (session_t *) data;
927 fd_set readfds;
928 struct timeval timeout;
929 char obuffer[_BUFSIZ];
930 int ocount;
931 char buffer[_BUFSIZ];
932 int size;
933 int i;
934 char c;
935 int was_down = 1;
936
937 memset(&buffer, 0, sizeof(buffer));
938 memset(&obuffer, 0, sizeof(obuffer));
939 ocount = 0;
940
941 while(session->running) {
942
943 if(session->socket >= 0) {
944
945 if(was_down) {
946 FD_ZERO(&readfds);
947 FD_SET(session->socket, &readfds);
948 timeout.tv_sec = 0;
949 timeout.tv_usec = 200000;
950
951 select(session->socket + 1, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout);
952 was_down = 0;
953 }
954
955 if(FD_ISSET(session->socket, &readfds)) {
956
957 size = recvfrom(session->socket, buffer, sizeof(buffer), MSG_NOSIGNAL, NULL, NULL);
958
959 if(size > 0) {
960
961 for(i = 0; i < size; i++) {
962 c = buffer[i];
963
964 switch (c) {
965 case '\r':
966 break;
967
968 case '\n':
969 obuffer[ocount++] = c;
970 if(i == (size - 1)) {
971 int pos = (strlen(session->prompt) + rl_end);
972 char *special;
973
974 if((special = strstr(obuffer, COMMANDS_PREFIX)) != NULL) {
975 char *p, *pp;
976 int special_length = strlen(special);
977 size_t i = (sizeof(client_commands) / sizeof(client_commands[0])) - 1;
978
979 pp = special + 11;
980 while((p = xine_strsep(&pp, "\t")) != NULL) {
981 if(strlen(p)) {
982 while(*p == ' ' || *p == '\t') p++;
983 session_commands = (session_commands_t **) realloc(session_commands, (i + 2) * (sizeof(session_commands_t *)));
984 session_commands[i] = (session_commands_t *) malloc(sizeof(session_commands_t));
985 session_commands[i]->command = strdup(p);
986 session_commands[i]->origin = ORIGIN_SERVER;
987 session_commands[i]->enable = 1;
988 session_commands[i]->function = client_noop;
989 i++;
990 }
991 }
992
993 /* remove '.\n' in last grabbed command */
994 session_commands[i - 1]->command[strlen(session_commands[i - 1]->command) - 1] = '\0';
995 session_commands[i - 1]->command[strlen(session_commands[i - 1]->command) - 1] = '\0';
996 session_commands[i] = (session_commands_t *) malloc(sizeof(session_commands_t));
997 session_commands[i]->command = NULL;
998 session_commands[i]->origin = ORIGIN_CLIENT;
999 session_commands[i]->enable = 1;
1000 session_commands[i]->function = NULL;
1001
1002 ocount -= special_length;
1003 obuffer[ocount] = '\0';
1004
1005 }
1006 pthread_mutex_lock(&session->console_mutex);
1007 /* Erase chars til's col0 */
1008 while(pos) {
1009
1010 write_to_console_unlocked_nocr(session, "\b \b");
1011 pos--;
1012 }
1013 write_to_console_unlocked(session, "%s", obuffer);
1014
1015 rl_crlf();
1016 rl_forced_update_display();
1017
1018 pthread_mutex_unlock(&session->console_mutex);
1019
1020 memset(&obuffer, 0, sizeof(obuffer));
1021 ocount = 0;
1022 }
1023 break;
1024
1025 default:
1026 obuffer[ocount++] = c;
1027 break;
1028 }
1029 }
1030 }
1031 }
1032 }
1033 else {
1034 was_down = 1;
1035 }
1036 }
1037
1038 pthread_exit(NULL);
1039 }
1040
client_handle_command(session_t * session,const char * command)1041 static void client_handle_command(session_t *session, const char *command) {
1042 int i, found;
1043 char cmd[_BUFSIZ], *p;
1044
1045 if((command == NULL) || (strlen(command) == 0))
1046 return;
1047
1048 /* Get only first arg of command */
1049 strlcpy(cmd, command, sizeof(cmd));
1050 if((p = strchr(cmd, ' ')) != NULL)
1051 *p = '\0';
1052
1053 i = found = 0;
1054
1055 /* looking for command matching */
1056 while((session_commands[i]->command != NULL) && (found == 0)) {
1057 if(session_commands[i]->enable) {
1058 if(!strncasecmp(cmd, session_commands[i]->command, strlen(cmd))) {
1059 found++;
1060
1061 if(session_commands[i]->origin == ORIGIN_CLIENT) {
1062 if(session_commands[i]->function)
1063 session_commands[i]->function(session, session_commands[i], command);
1064 }
1065 else {
1066 char *p, *pp;
1067 char buf[_BUFSIZ];
1068
1069 /*
1070 * Duplicate single '%' char, vsnprintf() seems
1071 * confused with single one in *format string
1072 */
1073 p = (char *)command;
1074 pp = buf;
1075 while(*p != '\0') {
1076
1077 switch(*p) {
1078 case '%':
1079 if(*(p + 1) != '%') {
1080 *pp++ = '%';
1081 *pp++ = '%';
1082 }
1083 break;
1084
1085 default:
1086 *pp = *p;
1087 pp++;
1088 break;
1089 }
1090
1091 p++;
1092 }
1093
1094 *pp = '\0';
1095
1096 if((sock_write(session->socket, "%s", buf)) == -1) {
1097 session->running = 0;
1098 }
1099 }
1100
1101 }
1102 }
1103 i++;
1104 }
1105
1106 /* Perhaps a ';' separated commands, so send anyway to server */
1107 if(found == 0) {
1108 sock_write(session->socket, "%s", (char *)command);
1109 }
1110
1111 if((!strncasecmp(cmd, "exit", strlen(cmd))) || (!strncasecmp(cmd, "halt", strlen(cmd)))) {
1112 session_create_commands(session);
1113 session->socket = -1;
1114 }
1115
1116 }
1117
session_single_shot(session_t * session,int num_commands,char * commands[])1118 static void session_single_shot(session_t *session, int num_commands, char *commands[]) {
1119 int i;
1120 char buf[_BUFSIZ];
1121
1122 buf[0] = 0;
1123
1124 for(i = 0; i < num_commands; i++) {
1125 if(buf[0])
1126 sprintf(buf+strlen(buf), " %s", commands[i]);
1127 else
1128 strcpy(buf, commands[i]);
1129 }
1130
1131 client_handle_command(session, buf);
1132 usleep(10000);
1133 sock_write(session->socket, "exit");
1134 }
1135
show_version(void)1136 static void show_version(void) {
1137 printf("This is %s - xine's remote control v%s.\n"
1138 "(c) 2000-2019 The xine Team.\n", PROGNAME, PROGVERSION);
1139 }
1140
show_usage(void)1141 static void show_usage(void) {
1142 printf("usage: %s [options]\n", PROGNAME);
1143 printf(" -H, --host <hostname> Connect host <hostname>.\n");
1144 printf(" -P, --port <port> Connect using <port>.\n");
1145 printf(" -c, --command <command> Send <command> to server then quit.\n");
1146 printf(" -n, --noconnect Do not connect default server.\n");
1147 printf(" -v, --version Display version.\n");
1148 printf(" -h, --help Display this help text.\n");
1149 printf("\n");
1150 }
1151
main(int argc,char ** argv)1152 int main(int argc, char **argv) {
1153 int c = '?';
1154 int option_index = 0;
1155 char *grabbed_line;
1156 struct sigaction action;
1157 struct servent *serv_ent;
1158 int port_set = 0;
1159 int auto_connect = 1;
1160 int single_shot = 0;
1161 void *p;
1162
1163 /*
1164 * install sighandler.
1165 */
1166 action.sa_handler = signals_handler;
1167 sigemptyset(&(action.sa_mask));
1168 action.sa_flags = 0;
1169 if(sigaction(SIGHUP, &action, NULL) != 0) {
1170 fprintf(stderr, "sigaction(SIGHUP) failed: %s\n", strerror(errno));
1171 }
1172 action.sa_handler = signals_handler;
1173 sigemptyset(&(action.sa_mask));
1174 action.sa_flags = 0;
1175 if(sigaction(SIGUSR1, &action, NULL) != 0) {
1176 fprintf(stderr, "sigaction(SIGUSR1) failed: %s\n", strerror(errno));
1177 }
1178 action.sa_handler = signals_handler;
1179 sigemptyset(&(action.sa_mask));
1180 action.sa_flags = 0;
1181 if(sigaction(SIGUSR2, &action, NULL) != 0) {
1182 fprintf(stderr, "sigaction(SIGUSR2) failed: %s\n", strerror(errno));
1183 }
1184 action.sa_handler = signals_handler;
1185 sigemptyset(&(action.sa_mask));
1186 action.sa_flags = 0;
1187 if(sigaction(SIGINT, &action, NULL) != 0) {
1188 fprintf(stderr, "sigaction(SIGINT) failed: %s\n", strerror(errno));
1189 }
1190 action.sa_handler = signals_handler;
1191 sigemptyset(&(action.sa_mask));
1192 action.sa_flags = 0;
1193 if(sigaction(SIGTERM, &action, NULL) != 0) {
1194 fprintf(stderr, "sigaction(SIGTERM) failed: %s\n", strerror(errno));
1195 }
1196 action.sa_handler = signals_handler;
1197 sigemptyset(&(action.sa_mask));
1198 action.sa_flags = 0;
1199 if(sigaction(SIGQUIT, &action, NULL) != 0) {
1200 fprintf(stderr, "sigaction(SIGQUIT) failed: %s\n", strerror(errno));
1201 }
1202 action.sa_handler = signals_handler;
1203 sigemptyset(&(action.sa_mask));
1204 action.sa_flags = 0;
1205 if(sigaction(SIGALRM, &action, NULL) != 0) {
1206 fprintf(stderr, "sigaction(SIGALRM) failed: %s\n", strerror(errno));
1207 }
1208
1209 grabbed_line = NULL;
1210
1211 session.socket = -1;
1212 pthread_mutex_init(&session.console_mutex, NULL);
1213 strcpy(session.host, DEFAULT_SERVER);
1214 strcpy(session.port, DEFAULT_XINECTL_PORT);
1215
1216 opterr = 0;
1217 while((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != EOF) {
1218 switch(c) {
1219
1220 case 'H': /* Set host */
1221 if(optarg != NULL)
1222 strlcpy(session.host, optarg, sizeof(session.host));
1223 break;
1224
1225 case 'P': /* Set port */
1226 if(optarg != NULL) {
1227 port_set = 1;
1228 strlcpy(session.port, optarg, sizeof(session.port));
1229 }
1230 break;
1231
1232 case 'c': /* Execute argv[] command, then leave */
1233 single_shot = 1;
1234 break;
1235
1236 case 'n': /* Disable automatic connection */
1237 auto_connect = 0;
1238 break;
1239
1240 case 'v': /* Display version */
1241 show_version();
1242 exit(1);
1243 break;
1244
1245 case 'h': /* Display usage */
1246 case '?':
1247 show_usage();
1248 exit(1);
1249 break;
1250
1251 default:
1252 show_usage();
1253 fprintf(stderr, "invalid argument %d => exit\n", c);
1254 exit(1);
1255 }
1256
1257 }
1258
1259 if(single_shot)
1260 session.console = -1;
1261 else
1262 session.console = STDOUT_FILENO;
1263
1264 /* Few realdline inits */
1265 rl_readline_name = PROGNAME;
1266 rl_set_prompt(session.prompt);
1267 rl_initialize();
1268 rl_attempted_completion_function = completion_function;
1269
1270 signal(SIGPIPE, SIG_IGN);
1271
1272 if(!port_set) {
1273 if((serv_ent = getservbyname("xinectl", "tcp")) != NULL) {
1274 snprintf(session.port, sizeof(session.port), "%u", ntohs(serv_ent->s_port));
1275 }
1276 }
1277
1278 /* Prepare client commands */
1279 session_create_commands(&session);
1280
1281 if(auto_connect) {
1282 session.socket = sock_client(session.host, session.port, "tcp");
1283 if(session.socket < 0) {
1284 fprintf(stderr, "opening server '%s' failed: %s\n", session.host, strerror(errno));
1285 exit(1);
1286 }
1287 /* Ask server for available commands */
1288 sock_write(session.socket, "commands");
1289 }
1290
1291 write_to_console(&session, "? for help.\n");
1292
1293 session.running = 1;
1294 pthread_create(&session.thread, NULL, select_thread, (void *)&session);
1295
1296 if(single_shot) {
1297 session_single_shot(&session, (argc - optind), &argv[optind]);
1298 session.running = 0;
1299 goto __end;
1300 }
1301
1302 while(session.running) {
1303
1304 session_update_prompt(&session);
1305
1306 if((grabbed_line = readline(session.prompt)) == NULL) {
1307 if (errno == 0 || errno == ENOTTY)
1308 exit(0);
1309
1310 fprintf(stderr, "%s(%d): readline() failed: %s\n",
1311 __XINE_FUNCTION__, __LINE__, strerror(errno));
1312 exit(1);
1313 }
1314
1315 if(grabbed_line) {
1316 char *line;
1317
1318 line = _atoa(grabbed_line);
1319
1320 if(strlen(line)) {
1321
1322 add_history(line);
1323 client_handle_command(&session, line);
1324 }
1325 }
1326 SAFE_FREE(grabbed_line);
1327 }
1328
1329 __end:
1330
1331 if(session.socket >= 0)
1332 close(session.socket);
1333
1334 pthread_join(session.thread, &p);
1335 pthread_mutex_destroy(&session.console_mutex);
1336
1337 return 0;
1338 }
1339
1340 #else
1341
sock_serv(const char * service,const char * transport,int queue_length)1342 static int sock_serv(const char *service, const char *transport, int queue_length) {
1343 union {
1344 struct sockaddr_in in;
1345 struct sockaddr sa;
1346 } fsin;
1347 int sock;
1348 int on = 1;
1349
1350 sock = sock_create(service, transport, &fsin.in);
1351 if (sock < 0) {
1352 return -1;
1353 }
1354
1355 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int));
1356
1357 if(bind(sock, &fsin.sa, sizeof(fsin.in)) < 0)
1358 sock_err("Unable to link socket %s: %s\n", service, strerror(errno));
1359
1360 if(strcmp(transport, "udp") && listen(sock, queue_length) < 0)
1361 sock_err("Passive mode impossible on %s: %s\n", service, strerror(errno));
1362
1363 return sock;
1364 }
1365
1366 /*
1367 * Read from socket.
1368 */
sock_read(int socket,char * buf,size_t len)1369 static ssize_t sock_read(int socket, char *buf, size_t len) {
1370 char *pbuf;
1371 ssize_t r, rr;
1372 void *nl;
1373
1374 if((socket < 0) || (buf == NULL) || (len < 1))
1375 return -1;
1376
1377 if(!sock_check_opened(socket))
1378 return -1;
1379
1380 if (--len < 1)
1381 return(-1);
1382
1383 pbuf = buf;
1384
1385 do {
1386
1387 if((r = recv(socket, pbuf, len, MSG_PEEK)) <= 0)
1388 return -1;
1389
1390 if((nl = memchr(pbuf, '\n', r)) != NULL)
1391 r = ((char *) nl) - pbuf + 1;
1392
1393 if((rr = read(socket, pbuf, r)) < 0)
1394 return -1;
1395
1396 pbuf += rr;
1397 len -= rr;
1398
1399 } while((nl == NULL) && len);
1400
1401 *pbuf = '\0';
1402
1403 return (pbuf - buf);
1404 }
1405
1406 /*
1407 * Password related
1408 */
_passwdfile_get_next_line(fileobj_t * fobj)1409 static void _passwdfile_get_next_line(fileobj_t *fobj) {
1410 char *p;
1411
1412 __get_next_line:
1413
1414 fobj->ln = fgets(fobj->buf, sizeof(fobj->buf), fobj->fd);
1415
1416 while(fobj->ln && (*fobj->ln == ' ' || *fobj->ln == '\t')) ++fobj->ln;
1417
1418 if(fobj->ln) {
1419 if((strncmp(fobj->ln, ";", 1) == 0) ||
1420 (strncmp(fobj->ln, "#", 1) == 0) ||
1421 (*fobj->ln == '\0')) {
1422 goto __get_next_line;
1423 }
1424
1425 }
1426
1427 p = fobj->ln;
1428
1429 if(p) {
1430 while(*p != '\0') {
1431 if(*p == '\n' || *p == '\r') {
1432 *p = '\0';
1433 break;
1434 }
1435 p++;
1436 }
1437
1438 while(p > fobj->ln) {
1439 --p;
1440
1441 if(*p == ' ' || *p == '\t')
1442 *p = '\0';
1443 else
1444 break;
1445 }
1446 }
1447
1448 }
_passwdfile_is_entry_valid(char * entry,char * name,char * passwd)1449 static int _passwdfile_is_entry_valid(char *entry, char *name, char *passwd) {
1450 char buf[_BUFSIZ];
1451 char *n, *p;
1452
1453 strlcpy(buf, entry, sizeof(buf));
1454 n = buf;
1455 if((p = strrchr(buf, ':')) != NULL) {
1456 if(strlen(p) > 1) {
1457 *p = '\0';
1458 p++;
1459 }
1460 else {
1461 *p = '\0';
1462 p = NULL;
1463 }
1464 }
1465
1466 if((n != NULL) && (p != NULL)) {
1467 strlcpy(name, n, MAX_NAME_LEN);
1468 strlcpy(passwd, p, MAX_PASSWD_LEN);
1469 return 1;
1470 }
1471
1472 return 0;
1473 }
server_load_passwd(const char * passwdfilename)1474 static int server_load_passwd(const char *passwdfilename) {
1475 fileobj_t fobj;
1476 int entries = 0;
1477 char name[MAX_NAME_LEN];
1478 char passwd[MAX_PASSWD_LEN];
1479
1480
1481 if((fobj.fd = fopen(passwdfilename, "r")) != NULL) {
1482 passwds = (passwds_t **) malloc(sizeof(passwds_t *));
1483
1484 _passwdfile_get_next_line(&fobj);
1485
1486 while(fobj.ln != NULL) {
1487
1488 if(_passwdfile_is_entry_valid(fobj.buf, name, passwd)) {
1489 passwds = (passwds_t **) realloc(passwds, sizeof(passwds_t *) * (entries + 2));
1490 passwds[entries] = (passwds_t *) malloc(sizeof(passwds_t));
1491 passwds[entries]->ident = strdup(name);
1492 passwds[entries]->passwd = strdup(passwd);
1493 entries++;
1494 }
1495
1496 _passwdfile_get_next_line(&fobj);
1497 }
1498
1499 passwds[entries] = (passwds_t *) malloc(sizeof(passwds_t));
1500 passwds[entries]->ident = NULL;
1501 passwds[entries]->passwd = NULL;
1502
1503 fclose(fobj.fd);
1504 }
1505 else {
1506 fprintf(stderr, "fopen() failed: %s\n", strerror(errno));
1507 return 0;
1508 }
1509
1510 if(!entries)
1511 return 0;
1512
1513 return 1;
1514 }
is_user_in_passwds(client_info_t * client_info)1515 static int is_user_in_passwds(client_info_t *client_info) {
1516 int i;
1517
1518 if(client_info == NULL)
1519 return 0;
1520
1521 for(i = 0; passwds[i]->ident != NULL; i++) {
1522 if(!strcasecmp(passwds[i]->ident, client_info->name))
1523 return 1;
1524 }
1525 return 0;
1526 }
is_user_allowed(client_info_t * client_info)1527 static int is_user_allowed(client_info_t *client_info) {
1528 int i;
1529
1530 if((client_info == NULL) || (client_info->passwd == NULL))
1531 return PASSWD_USER_DENIED;
1532
1533 for(i = 0; passwds[i]->ident != NULL; i++) {
1534 if(!strcmp(passwds[i]->ident, client_info->name)) {
1535 if(!strncmp(passwds[i]->passwd, "*", 1))
1536 return PASSWD_USER_DENIED;
1537 else if(!strcmp(passwds[i]->passwd, client_info->passwd))
1538 return PASSWD_USER_ALLOWED;
1539 }
1540 }
1541 return PASSWD_USER_DENIED;
1542 }
is_client_authorized(client_info_t * client_info)1543 static int is_client_authorized(client_info_t *client_info) {
1544 int i;
1545 int all, allowed;
1546
1547 all = allowed = 0;
1548
1549 /* First, find global permissions (ALL/ALLOW|DENY) */
1550 for(i = 0; passwds[i]->ident != NULL; i++) {
1551 if(!strcmp(passwds[i]->ident, "ALL")) {
1552
1553 all = 1;
1554
1555 if(!strcmp(passwds[i]->passwd, "ALLOW"))
1556 allowed = PASSWD_ALL_ALLOWED;
1557 else if(!strcmp(passwds[i]->passwd, "DENY"))
1558 allowed = PASSWD_ALL_DENIED;
1559 else
1560 all = allowed = 0;
1561
1562 }
1563 }
1564
1565 if(all) {
1566 if(allowed == PASSWD_ALL_ALLOWED)
1567 return 1;
1568 /*
1569 * If user in password list, ask him for passwd,
1570 * otherwise don't let him enter.
1571 */
1572 else if(allowed == PASSWD_ALL_DENIED) {
1573
1574 if(!is_user_in_passwds(client_info))
1575 return 0;
1576 else
1577 return 1;
1578
1579 }
1580 }
1581
1582 /*
1583 * No valid ALL rule found, assume everybody are denied and
1584 * check if used is in password file.
1585 */
1586 return (is_user_in_passwds(client_info));
1587 }
1588
1589 /*
1590 * Check access rights.
1591 */
check_client_auth(client_info_t * client_info)1592 static void check_client_auth(client_info_t *client_info) {
1593
1594 client_info->authentified = 0;
1595
1596 if(is_client_authorized(client_info)) {
1597 if((is_user_allowed(client_info)) == PASSWD_USER_ALLOWED) {
1598 client_info->authentified = 1;
1599 sock_write(client_info->socket, "user '%s' has been authentified.\n", client_info->name);
1600 return;
1601 }
1602 }
1603
1604 sock_write(client_info->socket, "user '%s' isn't known/authorized.\n", client_info->name);
1605 }
1606
handle_xine_error(client_info_t * client_info)1607 static void handle_xine_error(client_info_t *client_info) {
1608 gGui_t *gui = gGui;
1609 int err;
1610
1611 err = xine_get_error(gui->stream);
1612
1613 switch(err) {
1614
1615 case XINE_ERROR_NONE:
1616 /* noop */
1617 break;
1618
1619 case XINE_ERROR_NO_INPUT_PLUGIN:
1620 pthread_mutex_lock (&gui->mmk_mutex);
1621 sock_write(client_info->socket,
1622 "xine engine error:\n"
1623 "There is no available input plugin available to handle '%s'.\n",
1624 gui->mmk.mrl);
1625 pthread_mutex_unlock (&gui->mmk_mutex);
1626 break;
1627
1628 case XINE_ERROR_NO_DEMUX_PLUGIN:
1629 pthread_mutex_lock (&gui->mmk_mutex);
1630 sock_write(client_info->socket,
1631 "xine engine error:\n"
1632 "There is no available demuxer plugin to handle '%s'.\n",
1633 gui->mmk.mrl);
1634 pthread_mutex_unlock (&gui->mmk_mutex);
1635 break;
1636
1637 default:
1638 sock_write(client_info->socket, "xine engine error:\n!! Unhandled error !!\n");
1639 break;
1640 }
1641 }
1642
1643 /*
1644 * ************* COMMANDS ************
1645 */
1646 /*
1647 * return argument number.
1648 */
is_args(client_info_t * client_info)1649 static int is_args(client_info_t *client_info) {
1650
1651 if(!client_info)
1652 return -1;
1653
1654 return client_info->command.num_args;
1655 }
1656
1657 /*
1658 * return argument <num>, NULL if there is not argument.
1659 */
get_arg(client_info_t * client_info,int num)1660 static const char *get_arg(client_info_t *client_info, int num) {
1661
1662 if((client_info == NULL) || (num < 1) || (client_info->command.num_args < num))
1663 return NULL;
1664
1665 return(client_info->command.args[num - 1]);
1666 }
1667
1668 /*
1669 * return 1 if *arg match with argument <pos>
1670 */
is_arg_contain(client_info_t * client_info,int pos,const char * arg)1671 static int is_arg_contain(client_info_t *client_info, int pos, const char *arg) {
1672
1673 if(client_info && pos && ((arg != NULL) && (strlen(arg))) && (client_info->command.num_args >= pos)) {
1674 if(!strncmp(client_info->command.args[pos - 1], arg, strlen(client_info->command.args[pos - 1])))
1675 return 1;
1676 }
1677
1678 return 0;
1679 }
1680
1681 /*
1682 * Set current command line from line.
1683 */
set_command_line(client_info_t * client_info,const char * line)1684 static void set_command_line(client_info_t *client_info, const char *line) {
1685
1686 if(client_info && line && strlen(line)) {
1687
1688 client_info->command.line = (char *) realloc(client_info->command.line, strlen(line) + 1);
1689
1690 strcpy(client_info->command.line, line);
1691
1692 }
1693 }
1694
1695 /*
1696 * Display help of given command.
1697 */
command_help(const commands_t * command,client_info_t * client_info)1698 static void command_help(const commands_t *command, client_info_t *client_info) {
1699
1700 if(command) {
1701 if(command->help) {
1702 sock_write(client_info->socket,
1703 "Help of '%s' command:\n %s\n", command->command, command->help);
1704 }
1705 else
1706 sock_write(client_info->socket,
1707 "There is no help text for command '%s'\n", command->command);
1708 }
1709 }
1710
1711 /*
1712 * Display syntax of given command.
1713 */
command_syntax(const commands_t * command,client_info_t * client_info)1714 static void command_syntax(const commands_t *command, client_info_t *client_info) {
1715
1716 if(command) {
1717 if(command->syntax) {
1718 sock_write_nocr(client_info->socket,
1719 "Syntax of '%s' command:\n%s\n", command->command, command->syntax);
1720 }
1721 else
1722 sock_write(client_info->socket,
1723 "There is no syntax definition for command '%s'\n", command->command);
1724 }
1725 }
1726
do_commands(const commands_t * cmd,client_info_t * client_info)1727 static void do_commands(const commands_t *cmd, client_info_t *client_info) {
1728 int i = 0;
1729 char buf[_BUFSIZ];
1730
1731 strcpy(buf, COMMANDS_PREFIX);
1732
1733 while(commands[i].command != NULL) {
1734 if(commands[i].public) {
1735 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\t%s", commands[i].command);
1736 }
1737 i++;
1738 }
1739 strlcat(buf, ".\n", sizeof(buf));
1740 sock_write(client_info->socket, "%s", buf);
1741 }
1742
do_help(const commands_t * cmd,client_info_t * client_info)1743 static void do_help(const commands_t *cmd, client_info_t *client_info) {
1744 char buf[_BUFSIZ] = "Available commands are:\n ";
1745
1746 if(!client_info->command.num_args) {
1747 int i = 0;
1748 size_t maxlen = 0, j;
1749 int curpos = 0;
1750
1751 while(commands[i].command != NULL) {
1752
1753 if(commands[i].public) {
1754 if(strlen(commands[i].command) > maxlen)
1755 maxlen = strlen(commands[i].command);
1756 }
1757
1758 i++;
1759 }
1760
1761 maxlen++;
1762 i = 0;
1763
1764 curpos += 7;
1765
1766 while(commands[i].command != NULL) {
1767 if(commands[i].public) {
1768 if((curpos + maxlen) >= 80) {
1769 strlcat(buf, "\n ", sizeof(buf));
1770 curpos = 7;
1771 }
1772
1773 strlcat(buf, commands[i].command, sizeof(buf));
1774 curpos += strlen(commands[i].command);
1775
1776 for(j = 0; j < (maxlen - strlen(commands[i].command)); j++) {
1777 strlcat(buf, " ", sizeof(buf));
1778 curpos++;
1779 }
1780 }
1781 i++;
1782 }
1783
1784 strlcat(buf, "\n", sizeof(buf));
1785 sock_write(client_info->socket, "%s", buf);
1786 }
1787 else {
1788 int i;
1789
1790 for(i = 0; commands[i].command != NULL; i++) {
1791 if(!strcasecmp((get_arg(client_info, 1)), commands[i].command)) {
1792 command_help(&commands[i], client_info);
1793 return;
1794 }
1795 }
1796
1797 sock_write(client_info->socket, "Unknown command '%s'.\n", (get_arg(client_info, 1)));
1798 }
1799 }
1800
do_syntax(const commands_t * command,client_info_t * client_info)1801 static void do_syntax(const commands_t *command, client_info_t *client_info) {
1802 int i;
1803
1804 for(i = 0; commands[i].command != NULL; i++) {
1805 if(!strcasecmp((get_arg(client_info, 1)), commands[i].command)) {
1806 command_syntax(&commands[i], client_info);
1807 return;
1808 }
1809 }
1810
1811 sock_write(client_info->socket, "Unknown command '%s'.\n", (get_arg(client_info, 1)));
1812 }
1813
do_auth(const commands_t * cmd,client_info_t * client_info)1814 static void do_auth(const commands_t *cmd, client_info_t *client_info) {
1815 int nargs;
1816
1817 nargs = is_args(client_info);
1818 if(nargs) {
1819 if(nargs >= 1) {
1820 char buf[_BUFSIZ];
1821 char *name;
1822 char *passwd;
1823
1824 strlcpy(buf, get_arg(client_info, 1), sizeof(buf));
1825 name = buf;
1826 if((passwd = strrchr(buf, ':')) != NULL) {
1827 if(strlen(passwd) > 1) {
1828 *passwd = '\0';
1829 passwd++;
1830 }
1831 else {
1832 *passwd = '\0';
1833 passwd = NULL;
1834 }
1835 }
1836 if((name != NULL) && (passwd != NULL)) {
1837 memset(&client_info->name, 0, sizeof(client_info->name));
1838 memset(&client_info->passwd, 0, sizeof(client_info->passwd));
1839
1840 strlcpy(client_info->name, name, sizeof(client_info->name));
1841 strlcpy(client_info->passwd, passwd, sizeof(client_info->passwd));
1842
1843 check_client_auth(client_info);
1844 }
1845 else
1846 sock_write(client_info->socket, "use identity:password syntax.\n");
1847 }
1848 }
1849 }
1850
do_mrl(const commands_t * cmd,client_info_t * client_info)1851 static void do_mrl(const commands_t *cmd, client_info_t *client_info) {
1852 gGui_t *gui = gGui;
1853 int nargs;
1854
1855 nargs = is_args(client_info);
1856 if(nargs) {
1857 if(nargs == 1) {
1858 if(is_arg_contain(client_info, 1, "next")) {
1859 gui->ignore_next = 1;
1860 xine_stop (gui->stream);
1861 gui->ignore_next = 0;
1862 gui_playlist_start_next (gui);
1863 }
1864 else if(is_arg_contain(client_info, 1, "prev")) {
1865 gui->ignore_next = 1;
1866 xine_stop (gui->stream);
1867 gui->playlist.cur--;
1868 if ((gui->playlist.cur >= 0) && (gui->playlist.cur < gui->playlist.num)) {
1869 gui_set_current_mmk(mediamark_get_current_mmk());
1870 pthread_mutex_lock (&gui->mmk_mutex);
1871 (void) gui_xine_open_and_play(gui->mmk.mrl, gui->mmk.sub, 0,
1872 gui->mmk.start, gui->mmk.av_offset, gui->mmk.spu_offset, 1);
1873 pthread_mutex_unlock (&gui->mmk_mutex);
1874
1875 }
1876 else {
1877 gui->playlist.cur = 0;
1878 gui_display_logo();
1879 }
1880 gui->ignore_next = 0;
1881 }
1882 }
1883 else if(nargs >= 2) {
1884
1885 if(is_arg_contain(client_info, 1, "add")) {
1886 int argc = 2;
1887
1888 while((get_arg(client_info, argc)) != NULL) {
1889 gui_dndcallback((char *)(get_arg(client_info, argc)));
1890 argc++;
1891 }
1892 }
1893 else if (is_arg_contain(client_info, 1, "play")) {
1894 gui_dndcallback((char *)(get_arg(client_info, 2)));
1895
1896 if((xine_get_status(gui->stream) != XINE_STATUS_STOP)) {
1897 gui->ignore_next = 1;
1898 xine_stop(gui->stream);
1899 gui->ignore_next = 0;
1900 }
1901 pthread_mutex_lock (&gui->mmk_mutex);
1902 gui_set_current_mmk(gui->playlist.mmk[gui->playlist.num - 1]);
1903 if(!(xine_open(gui->stream, gui->mmk.mrl)
1904 && xine_play (gui->stream, 0, gui->mmk.start))) {
1905 handle_xine_error(client_info);
1906 gui_display_logo();
1907 }
1908 else
1909 gui->logo_mode = 0;
1910 pthread_mutex_unlock (&gui->mmk_mutex);
1911 }
1912 }
1913 }
1914 }
1915
do_playlist(const commands_t * cmd,client_info_t * client_info)1916 static void do_playlist(const commands_t *cmd, client_info_t *client_info) {
1917 gGui_t *gui = gGui;
1918 int nargs;
1919
1920 nargs = is_args(client_info);
1921 if(nargs) {
1922 if(nargs == 1) {
1923 int first = 0;
1924
1925 if(is_arg_contain(client_info, 1, "show")) {
1926 int i;
1927
1928 if(gui->playlist.num) {
1929 pthread_mutex_lock (&gui->mmk_mutex);
1930 for(i = 0; i < gui->playlist.num; i++) {
1931 sock_write(client_info->socket, "%2s %5d %s\n",
1932 (i == gui->playlist.cur) ? "*>" : "", i, gui->playlist.mmk[i]->mrl);
1933 }
1934 pthread_mutex_unlock (&gui->mmk_mutex);
1935 }
1936 else
1937 sock_write(client_info->socket, "empty playlist.");
1938
1939 sock_write(client_info->socket, "\n");
1940 }
1941 else if(is_arg_contain(client_info, 1, "next")) { /* Alias of mrl next */
1942 set_command_line(client_info, "mrl next");
1943 handle_client_command(client_info);
1944 }
1945 else if(is_arg_contain(client_info, 1, "prev")) { /* Alias of mrl prev */
1946 set_command_line(client_info, "mrl prev");
1947 handle_client_command(client_info);
1948 }
1949 else if((first = is_arg_contain(client_info, 1, "first")) || is_arg_contain(client_info, 1, "last")) {
1950
1951 if(gui->playlist.num) {
1952 int entry = (first) ? 0 : gui->playlist.num - 1;
1953
1954 if(entry != gui->playlist.cur) {
1955
1956 if(xine_get_status(gui->stream) != XINE_STATUS_STOP) {
1957 gui->ignore_next = 1;
1958 xine_stop (gui->stream);
1959 gui->ignore_next = 0;
1960 }
1961
1962 gui->playlist.cur = entry;
1963 gui_set_current_mmk(mediamark_get_current_mmk());
1964 gui_play (NULL, gui);
1965 }
1966 }
1967 }
1968 else if(is_arg_contain(client_info, 1, "stop")) {
1969 if(xine_get_status(gui->stream) != XINE_STATUS_STOP)
1970 gui->playlist.control |= PLAYLIST_CONTROL_STOP;
1971 }
1972 else if(is_arg_contain(client_info, 1, "continue")) {
1973 if(xine_get_status(gui->stream) != XINE_STATUS_STOP)
1974 gui->playlist.control &= ~PLAYLIST_CONTROL_STOP;
1975 }
1976
1977 }
1978 else if(nargs >= 2) {
1979
1980 if(is_arg_contain(client_info, 1, "select")) {
1981 int j;
1982
1983 j = atoi(get_arg(client_info, 2));
1984
1985 if((j >= 0) && (j <= gui->playlist.num)) {
1986 gui->playlist.cur = j;
1987 gui_set_current_mmk(mediamark_get_current_mmk());
1988
1989 if(xine_get_status(gui->stream) != XINE_STATUS_STOP) {
1990 gui->ignore_next = 1;
1991 xine_stop(gui->stream);
1992 gui->ignore_next = 0;
1993
1994 do_play(cmd, client_info);
1995 }
1996 }
1997 }
1998 else if(is_arg_contain(client_info, 1, "delete")) {
1999 int i;
2000
2001 if((is_arg_contain(client_info, 2, "all")) ||
2002 (is_arg_contain(client_info, 2, "*"))) {
2003
2004 mediamark_free_mediamarks();
2005
2006 if(xine_get_status(gui->stream) != XINE_STATUS_STOP)
2007 gui_stop (NULL, gui);
2008
2009 enable_playback_controls (gui->panel, 0);
2010 }
2011 else {
2012 int j;
2013
2014 j = atoi(get_arg(client_info, 2));
2015
2016 if(j >= 0) {
2017
2018 if((gui->playlist.cur == j) && ((xine_get_status(gui->stream) != XINE_STATUS_STOP)))
2019 gui_stop(NULL, NULL);
2020
2021 mediamark_free_entry(j);
2022
2023 pthread_mutex_lock (&gui->mmk_mutex);
2024 for(i = j; i < gui->playlist.num; i++)
2025 gui->playlist.mmk[i] = gui->playlist.mmk[i + 1];
2026
2027 gui->playlist.mmk = (mediamark_t **) realloc(gui->playlist.mmk, sizeof(mediamark_t *) * (gui->playlist.num + 2));
2028
2029 gui->playlist.mmk[gui->playlist.num + 1] = NULL;
2030
2031 gui->playlist.cur = 0;
2032 pthread_mutex_unlock (&gui->mmk_mutex);
2033 }
2034 }
2035
2036 if(playlist_is_running())
2037 playlist_update_playlist();
2038
2039 if(gui->playlist.num)
2040 gui_set_current_mmk(mediamark_get_current_mmk());
2041 else {
2042
2043 if (is_playback_widgets_enabled (gui->panel))
2044 enable_playback_controls (gui->panel, 0);
2045
2046 if(xine_get_status(gui->stream) != XINE_STATUS_STOP)
2047 gui_stop(NULL, NULL);
2048
2049 gui_set_current_mmk(NULL);
2050 }
2051 }
2052 }
2053 }
2054 }
2055
do_play(const commands_t * cmd,client_info_t * client_info)2056 static void do_play(const commands_t *cmd, client_info_t *client_info) {
2057 gGui_t *gui = gGui;
2058
2059 if (xine_get_status (gui->stream) != XINE_STATUS_PLAY) {
2060 pthread_mutex_lock (&gui->mmk_mutex);
2061 if(!(xine_open(gui->stream, gui->mmk.mrl) && xine_play (gui->stream, 0, gui->mmk.start))) {
2062 handle_xine_error(client_info);
2063 gui_display_logo();
2064 }
2065 else
2066 gui->logo_mode = 0;
2067 pthread_mutex_unlock (&gui->mmk_mutex);
2068 }
2069 else {
2070 xine_set_param(gui->stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL);
2071 }
2072
2073 }
2074
do_stop(const commands_t * cmd,client_info_t * client_info)2075 static void do_stop(const commands_t *cmd, client_info_t *client_info) {
2076 gGui_t *gui = gGui;
2077 gui->ignore_next = 1;
2078 xine_stop(gui->stream);
2079 gui->ignore_next = 0;
2080 gui_display_logo();
2081 }
2082
do_pause(const commands_t * cmd,client_info_t * client_info)2083 static void do_pause(const commands_t *cmd, client_info_t *client_info) {
2084 gGui_t *gui = gGui;
2085
2086 if (xine_get_param (gui->stream, XINE_PARAM_SPEED) != XINE_SPEED_PAUSE)
2087 xine_set_param(gui->stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE);
2088 else
2089 xine_set_param(gui->stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL);
2090 }
2091
do_exit(const commands_t * cmd,client_info_t * client_info)2092 static void do_exit(const commands_t *cmd, client_info_t *client_info) {
2093 if(client_info) {
2094 client_info->finished = 1;
2095 }
2096 }
2097
do_fullscreen(const commands_t * cmd,client_info_t * client_info)2098 static void do_fullscreen(const commands_t *cmd, client_info_t *client_info) {
2099 action_id_t action = ACTID_TOGGLE_FULLSCREEN;
2100
2101 gui_execute_action_id (gGui, action);
2102 }
2103
2104 #ifdef HAVE_XINERAMA
do_xinerama_fullscreen(const commands_t * cmd,client_info_t * client_info)2105 static void do_xinerama_fullscreen(const commands_t *cmd, client_info_t *client_info) {
2106 action_id_t action = ACTID_TOGGLE_XINERAMA_FULLSCREEN;
2107
2108 gui_execute_action_id (gGui, action);
2109 }
2110 #endif
2111
do_get(const commands_t * cmd,client_info_t * client_info)2112 static void do_get(const commands_t *cmd, client_info_t *client_info) {
2113 gGui_t *gui = gGui;
2114 int nargs;
2115
2116 nargs = is_args(client_info);
2117 if(nargs) {
2118 if(nargs == 1) {
2119 if(is_arg_contain(client_info, 1, "status")) {
2120 char buf[64];
2121 int status;
2122
2123 strcpy(buf, "Current status: ");
2124 status = xine_get_status(gui->stream);
2125
2126 if(status <= XINE_STATUS_QUIT)
2127 strlcat(buf, status_struct[status].name, sizeof(buf));
2128 else
2129 strlcat(buf, "*UNKNOWN*", sizeof(buf));
2130
2131 sock_write(client_info->socket, "%s\n", buf);
2132 }
2133 else if(is_arg_contain(client_info, 1, "speed")) {
2134 char buf[64];
2135 int speed = -1;
2136 size_t i;
2137
2138 strcpy(buf, "Current speed: ");
2139 speed = xine_get_param(gui->stream, XINE_PARAM_SPEED);
2140
2141 for(i = 0; speeds_struct[i].name != NULL; i++) {
2142 if(speed == speeds_struct[i].speed)
2143 break;
2144 }
2145
2146 if(i < ((sizeof(speeds_struct) / sizeof(speeds_struct[0])) - 1))
2147 strlcat(buf, speeds_struct[i].name, sizeof(buf));
2148 else
2149 strlcat(buf, "*UNKNOWN*", sizeof(buf));
2150
2151 sock_write(client_info->socket, "%s\n", buf);
2152 }
2153 else if(is_arg_contain(client_info, 1, "position")) {
2154 char buf[64];
2155 int pos_stream;
2156 int pos_time;
2157 int length_time;
2158 xine_get_pos_length(gui->stream,
2159 &pos_stream,
2160 &pos_time,
2161 &length_time);
2162 snprintf(buf, sizeof(buf), "%s: %d\n", "Current position", pos_time);
2163 sock_write(client_info->socket, "%s", buf);
2164 }
2165 else if(is_arg_contain(client_info, 1, "length")) {
2166 char buf[64];
2167 int pos_stream;
2168 int pos_time;
2169 int length_time;
2170 xine_get_pos_length(gui->stream,
2171 &pos_stream,
2172 &pos_time,
2173 &length_time);
2174 snprintf(buf, sizeof(buf), "%s: %d\n", "Current length", length_time);
2175 sock_write(client_info->socket, "%s", buf);
2176 }
2177 else if(is_arg_contain(client_info, 1, "loop")) {
2178 char buf[64] = "Current loop mode is: ";
2179
2180 switch(gui->playlist.loop) {
2181 case PLAYLIST_LOOP_NO_LOOP:
2182 strlcat(buf, "'No Loop'", sizeof(buf));
2183 break;
2184 case PLAYLIST_LOOP_LOOP:
2185 strlcat(buf, "'Loop'", sizeof(buf));
2186 break;
2187 case PLAYLIST_LOOP_REPEAT:
2188 strlcat(buf, "'Repeat'", sizeof(buf));
2189 break;
2190 case PLAYLIST_LOOP_SHUFFLE:
2191 strlcat(buf, "'Shuffle'", sizeof(buf));
2192 break;
2193 case PLAYLIST_LOOP_SHUF_PLUS:
2194 strlcat(buf, "'Shuffle forever'", sizeof(buf));
2195 break;
2196 default:
2197 strlcat(buf, "'!!Unknown!!'", sizeof(buf));
2198 break;
2199 }
2200
2201 sock_write(client_info->socket, "%s.\n", buf);
2202 }
2203 }
2204 else if(nargs >= 2) {
2205 if(is_arg_contain(client_info, 1, "audio")) {
2206 if(is_arg_contain(client_info, 2, "channel")) {
2207 sock_write(client_info->socket, "Current audio channel: %d\n",
2208 (xine_get_param(gui->stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL)));
2209 }
2210 else if(is_arg_contain(client_info, 2, "lang")) {
2211 char buf[XINE_LANG_MAX];
2212 int channel = xine_get_param(gui->stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL);
2213 if (!xine_get_audio_lang(gui->stream, channel, &buf[0])) {
2214 snprintf(buf, sizeof(buf), "%3d", channel);
2215 }
2216 sock_write(client_info->socket, "Current audio language: %s\n", buf);
2217 }
2218 else if(is_arg_contain(client_info, 2, "volume")) {
2219 if(gui->mixer.caps & MIXER_CAP_VOL) {
2220 sock_write(client_info->socket, "Current audio volume: %d\n", gui->mixer.volume_level);
2221 }
2222 else
2223 sock_write(client_info->socket, "Audio is disabled.\n");
2224 }
2225 else if(is_arg_contain(client_info, 2, "mute")) {
2226 if(gui->mixer.caps & MIXER_CAP_MUTE) {
2227 sock_write(client_info->socket, "Current audio mute: %d\n", gui->mixer.mute);
2228 }
2229 else
2230 sock_write(client_info->socket, "Audio is disabled.\n");
2231 }
2232 }
2233 else if(is_arg_contain(client_info, 1, "spu")) {
2234 if(is_arg_contain(client_info, 2, "channel")) {
2235 sock_write(client_info->socket, "Current spu channel: %d\n",
2236 (xine_get_param(gui->stream, XINE_PARAM_SPU_CHANNEL)));
2237 }
2238 else if(is_arg_contain(client_info, 2, "lang")) {
2239 char buf[XINE_LANG_MAX];
2240 int channel = xine_get_param(gui->stream, XINE_PARAM_SPU_CHANNEL);
2241 if (!xine_get_spu_lang (gui->stream, channel, &buf[0])) {
2242 snprintf(buf, sizeof(buf), "%3d", channel);
2243 }
2244 sock_write(client_info->socket, "Current spu language: %s\n", buf);
2245 }
2246 else if(is_arg_contain(client_info, 2, "offset")) {
2247 int offset;
2248
2249 offset = xine_get_param(gui->stream, XINE_PARAM_SPU_OFFSET);
2250 sock_write(client_info->socket, "Current spu offset: %d\n", offset);
2251 }
2252 }
2253 else if(is_arg_contain(client_info, 1, "av")) {
2254 if(is_arg_contain(client_info, 2, "offset")) {
2255 int offset;
2256
2257 offset = xine_get_param(gui->stream, XINE_PARAM_AV_OFFSET);
2258 sock_write(client_info->socket, "Current A/V offset: %d\n", offset);
2259 }
2260 }
2261 }
2262 }
2263 }
2264
do_set(const commands_t * cmd,client_info_t * client_info)2265 static void do_set(const commands_t *cmd, client_info_t *client_info) {
2266 gGui_t *gui = gGui;
2267 int nargs;
2268
2269 nargs = is_args(client_info);
2270 if(nargs) {
2271 if(nargs == 2) {
2272 if(is_arg_contain(client_info, 1, "speed")) {
2273 int speed;
2274
2275 if((is_arg_contain(client_info, 2, "XINE_SPEED_PAUSE")) ||
2276 (is_arg_contain(client_info, 2, "|")))
2277 speed = XINE_SPEED_PAUSE;
2278 else if((is_arg_contain(client_info, 2, "XINE_SPEED_SLOW_4")) ||
2279 (is_arg_contain(client_info, 2, "/4")))
2280 speed = XINE_SPEED_SLOW_4;
2281 else if((is_arg_contain(client_info, 2, "XINE_SPEED_SLOW_2")) ||
2282 (is_arg_contain(client_info, 2, "/2")))
2283 speed = XINE_SPEED_SLOW_2;
2284 else if((is_arg_contain(client_info, 2, "XINE_SPEED_NORMAL")) ||
2285 (is_arg_contain(client_info, 2, "=")))
2286 speed = XINE_SPEED_NORMAL;
2287 else if((is_arg_contain(client_info, 2, "XINE_SPEED_FAST_2")) ||
2288 (is_arg_contain(client_info, 2, "*2")))
2289 speed = XINE_SPEED_FAST_2;
2290 else if((is_arg_contain(client_info, 2, "XINE_SPEED_FAST_4")) ||
2291 (is_arg_contain(client_info, 2, "*4")))
2292 speed = XINE_SPEED_FAST_4;
2293 else
2294 speed = atoi((get_arg(client_info, 2)));
2295
2296 xine_set_param(gui->stream, XINE_PARAM_SPEED, speed);
2297 }
2298 else if(is_arg_contain(client_info, 1, "loop")) {
2299 int i;
2300 static const struct {
2301 const char *mode;
2302 int loop_mode;
2303 } loop_modes[] = {
2304 { "no", PLAYLIST_LOOP_NO_LOOP },
2305 { "loop", PLAYLIST_LOOP_LOOP },
2306 { "repeat", PLAYLIST_LOOP_REPEAT },
2307 { "shuffle", PLAYLIST_LOOP_SHUFFLE },
2308 { "shuffle+", PLAYLIST_LOOP_SHUF_PLUS },
2309 { NULL, -1 }
2310 };
2311 char *arg = (char *) get_arg(client_info, 2);
2312
2313 for(i = 0; loop_modes[i].mode; i++) {
2314 if(!strncmp(arg, loop_modes[i].mode, strlen(arg))) {
2315 gui->playlist.loop = loop_modes[i].loop_mode;
2316 return;
2317 }
2318 }
2319 }
2320 }
2321 else if(nargs >= 3) {
2322 if(is_arg_contain(client_info, 1, "audio")) {
2323 if(is_arg_contain(client_info, 2, "channel")) {
2324 xine_set_param(gui->stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, (atoi(get_arg(client_info, 3))));
2325 }
2326 else if(is_arg_contain(client_info, 2, "volume")) {
2327 if(gui->mixer.caps & MIXER_CAP_VOL) {
2328 int vol = atoi(get_arg(client_info, 3));
2329
2330 if(vol < 0) vol = 0;
2331 if(vol > 100) vol = 100;
2332
2333 gui->mixer.volume_level = vol;
2334 xine_set_param(gui->stream, XINE_PARAM_AUDIO_VOLUME, gui->mixer.volume_level);
2335 }
2336 else
2337 sock_write(client_info->socket, "Audio is disabled.\n");
2338 }
2339 else if(is_arg_contain(client_info, 2, "mute")) {
2340 if(gui->mixer.caps & MIXER_CAP_MUTE) {
2341 gui->mixer.mute = get_bool_value((get_arg(client_info, 3)));
2342 xine_set_param(gui->stream, XINE_PARAM_AUDIO_MUTE, gui->mixer.mute);
2343 }
2344 else
2345 sock_write(client_info->socket, "Audio is disabled.\n");
2346 }
2347 }
2348 else if(is_arg_contain(client_info, 1, "spu")) {
2349 if(is_arg_contain(client_info, 2, "channel")) {
2350 xine_set_param(gui->stream, XINE_PARAM_SPU_CHANNEL, (atoi(get_arg(client_info, 3))));
2351 }
2352 else if(is_arg_contain(client_info, 2, "offset")) {
2353 xine_set_param(gui->stream, XINE_PARAM_SPU_OFFSET, (atoi(get_arg(client_info, 3))));
2354 }
2355 }
2356 else if(is_arg_contain(client_info, 1, "av")) {
2357 if(is_arg_contain(client_info, 2, "offset")) {
2358 xine_set_param(gui->stream, XINE_PARAM_AV_OFFSET, (atoi(get_arg(client_info, 3))));
2359 }
2360 }
2361 }
2362 }
2363 }
2364
do_gui(const commands_t * cmd,client_info_t * client_info)2365 static void do_gui(const commands_t *cmd, client_info_t *client_info) {
2366 gGui_t *gui = gGui;
2367 int nargs;
2368
2369 nargs = is_args(client_info);
2370 if(nargs) {
2371 if(nargs == 1) {
2372 int flushing = 0;
2373
2374 if(is_arg_contain(client_info, 1, "hide")) {
2375 if (panel_is_visible (gui->panel)) {
2376 panel_toggle_visibility (NULL, gui->panel);
2377 flushing++;
2378 }
2379 }
2380 else if(is_arg_contain(client_info, 1, "output")) {
2381 gui_toggle_visibility (NULL, gui);
2382 flushing++;
2383 }
2384 else if(is_arg_contain(client_info, 1, "panel")) {
2385 panel_toggle_visibility (NULL, gui->panel);
2386 flushing++;
2387 }
2388 else if(is_arg_contain(client_info, 1, "playlist")) {
2389 gui_playlist_show(NULL, NULL);
2390 flushing++;
2391 }
2392 else if(is_arg_contain(client_info, 1, "control")) {
2393 gui_control_show(NULL, NULL);
2394 flushing++;
2395 }
2396 else if(is_arg_contain(client_info, 1, "mrl")) {
2397 gui_mrlbrowser_show(NULL, NULL);
2398 flushing++;
2399 }
2400 else if(is_arg_contain(client_info, 1, "setup")) {
2401 gui_setup_show(NULL, NULL);
2402 flushing++;
2403 }
2404 else if(is_arg_contain(client_info, 1, "vpost")) {
2405 gui_vpp_show(NULL, NULL);
2406 flushing++;
2407 }
2408 else if(is_arg_contain(client_info, 1, "apost")) {
2409 gui_app_show(NULL, NULL);
2410 flushing++;
2411 }
2412 else if(is_arg_contain(client_info, 1, "help")) {
2413 gui_help_show(NULL, NULL);
2414 flushing++;
2415 }
2416 else if(is_arg_contain(client_info, 1, "log")) {
2417 gui_viewlog_show(NULL, NULL);
2418 flushing++;
2419 }
2420
2421 /* Flush event when xine !play */
2422 if(flushing && ((xine_get_status(gui->stream) != XINE_STATUS_PLAY))) {
2423 gui->x_lock_display (gui->display);
2424 XSync(gui->display, False);
2425 gui->x_unlock_display (gui->display);
2426 }
2427 }
2428 }
2429 }
2430
do_event(const commands_t * cmd,client_info_t * client_info)2431 static void do_event(const commands_t *cmd, client_info_t *client_info) {
2432 gGui_t *gui = gGui;
2433 xine_event_t xine_event;
2434 int nargs;
2435
2436 nargs = is_args(client_info);
2437 if(nargs) {
2438
2439 xine_event.type = 0;
2440
2441 if(nargs == 1) {
2442 const char *arg = get_arg(client_info, 1);
2443 static const struct {
2444 const char *name;
2445 int type;
2446 } events_struct[] = {
2447 { "menu1" , XINE_EVENT_INPUT_MENU1 },
2448 { "menu2" , XINE_EVENT_INPUT_MENU2 },
2449 { "menu3" , XINE_EVENT_INPUT_MENU3 },
2450 { "menu4" , XINE_EVENT_INPUT_MENU4 },
2451 { "menu5" , XINE_EVENT_INPUT_MENU5 },
2452 { "menu6" , XINE_EVENT_INPUT_MENU6 },
2453 { "menu7" , XINE_EVENT_INPUT_MENU7 },
2454 { "up" , XINE_EVENT_INPUT_UP },
2455 { "down" , XINE_EVENT_INPUT_DOWN },
2456 { "left" , XINE_EVENT_INPUT_LEFT },
2457 { "right" , XINE_EVENT_INPUT_RIGHT },
2458 { "select" , XINE_EVENT_INPUT_SELECT },
2459 { "next" , XINE_EVENT_INPUT_NEXT },
2460 { "previous", XINE_EVENT_INPUT_PREVIOUS },
2461 { NULL , -1 }
2462 };
2463 int i = -1;
2464
2465 if(strlen(arg)) {
2466 i = 0;
2467 while(events_struct[i].name && strncmp(arg, events_struct[i].name, strlen(arg))) i++;
2468 }
2469
2470 if((i >= 0) && ((size_t)i < ((sizeof(events_struct) / sizeof(events_struct[0])) - 1))) {
2471 xine_event.type = events_struct[i].type;
2472 }
2473 else {
2474 size_t value;
2475 int input_numbers[] = {
2476 XINE_EVENT_INPUT_NUMBER_0, XINE_EVENT_INPUT_NUMBER_1, XINE_EVENT_INPUT_NUMBER_2,
2477 XINE_EVENT_INPUT_NUMBER_3, XINE_EVENT_INPUT_NUMBER_4, XINE_EVENT_INPUT_NUMBER_5,
2478 XINE_EVENT_INPUT_NUMBER_6, XINE_EVENT_INPUT_NUMBER_7, XINE_EVENT_INPUT_NUMBER_8,
2479 XINE_EVENT_INPUT_NUMBER_9, XINE_EVENT_INPUT_NUMBER_10_ADD
2480 };
2481
2482
2483 if(((sscanf(arg, "%zu", &value)) == 1) ||
2484 ((sscanf(arg, "+%zu", &value)) == 1)) {
2485
2486 if(value < (sizeof(input_numbers) / sizeof(input_numbers[0])))
2487 xine_event.type = input_numbers[value];
2488 }
2489 }
2490 }
2491 else if(nargs >= 2) {
2492 if(is_arg_contain(client_info, 1, "angle")) {
2493 if(is_arg_contain(client_info, 2, "next")) {
2494 xine_event.type = XINE_EVENT_INPUT_ANGLE_NEXT;
2495 }
2496 else if(is_arg_contain(client_info, 2, "previous")) {
2497 xine_event.type = XINE_EVENT_INPUT_ANGLE_PREVIOUS;
2498 }
2499 }
2500 }
2501
2502 if(xine_event.type != 0) {
2503 xine_event.data_length = 0;
2504 xine_event.data = NULL;
2505 xine_event.stream = gui->stream;
2506 gettimeofday(&xine_event.tv, NULL);
2507
2508 xine_event_send(gui->stream, &xine_event);
2509 }
2510
2511 }
2512 }
2513
do_seek(const commands_t * cmd,client_info_t * client_info)2514 static void do_seek(const commands_t *cmd, client_info_t *client_info) {
2515 gGui_t *gui = gGui;
2516 int nargs;
2517
2518 if((xine_get_stream_info(gui->stream, XINE_STREAM_INFO_SEEKABLE)) == 0)
2519 return;
2520
2521 nargs = is_args(client_info);
2522 if(nargs) {
2523
2524 if(nargs == 1) {
2525 int pos;
2526 char *arg = (char *)get_arg(client_info, 1);
2527
2528 if(sscanf(arg, "%%%d", &pos) == 1) {
2529
2530 if(pos > 100) pos = 100;
2531 if(pos < 0) pos = 0;
2532
2533 gui->ignore_next = 1;
2534 if(!xine_play(gui->stream, ((int) (655.35 * pos)), 0)) {
2535 gui_handle_xine_error(gui->stream, NULL);
2536 gui_display_logo();
2537 }
2538 else
2539 gui->logo_mode = 0;
2540
2541 gui->ignore_next = 0;
2542
2543 }
2544
2545 else if((((arg[0] == '+') || (arg[0] == '-')) && (isdigit(arg[1])))
2546 || (isdigit(arg[0]))) {
2547 int msec;
2548
2549 pos = atoi(arg);
2550
2551 if(((arg[0] == '+') || (arg[0] == '-')) && (isdigit(arg[1]))) {
2552
2553 if(gui_xine_get_pos_length(gui->stream, NULL, &msec, NULL)) {
2554 msec /= 1000;
2555
2556 if((msec + pos) < 0)
2557 msec = 0;
2558 else
2559 msec += pos;
2560 }
2561 else
2562 msec = pos;
2563
2564 msec *= 1000;
2565
2566 gui->ignore_next = 1;
2567 if(!xine_play(gui->stream, 0, msec)) {
2568 gui_handle_xine_error(gui->stream, NULL);
2569 gui_display_logo();
2570 }
2571 else
2572 gui->logo_mode = 0;
2573
2574 gui->ignore_next = 0;
2575 }
2576 }
2577 }
2578 }
2579 }
2580
do_halt(const commands_t * cmd,client_info_t * client_info)2581 static void do_halt(const commands_t *cmd, client_info_t *client_info) {
2582 gui_exit (NULL, gGui);
2583 }
2584
network_messenger(void * data,char * message)2585 static void network_messenger(void *data, char *message) {
2586 int socket = (int)(intptr_t) data;
2587
2588 sock_write(socket, "%s", message);
2589 }
2590
do_snap(const commands_t * cmd,client_info_t * client_info)2591 static void do_snap(const commands_t *cmd, client_info_t *client_info) {
2592 gGui_t *gui = gGui;
2593 pthread_mutex_lock (&gui->mmk_mutex);
2594 create_snapshot(gui->mmk.mrl,
2595 network_messenger, network_messenger, (void *)(intptr_t)client_info->socket);
2596 pthread_mutex_unlock (&gui->mmk_mutex);
2597 }
2598
2599 /*
2600 * *********** COMMANDS ENDS **********
2601 */
2602 /*
2603 *
2604 */
say_hello(client_info_t * client_info)2605 static void say_hello(client_info_t *client_info) {
2606 char buf[256] = "";
2607 char myfqdn[256] = "";
2608 struct hostent *hp = NULL;
2609
2610 if(!gethostname(myfqdn, 255) && (hp = gethostbyname(myfqdn)) != NULL) {
2611 snprintf(buf, sizeof(buf), "%s %s %s %s\n", hp->h_name, PACKAGE, VERSION, "remote server. Nice to meet you.");
2612 }
2613 else {
2614 snprintf(buf, sizeof(buf), "%s %s %s\n", PACKAGE, VERSION, "remote server. Nice to meet you.");
2615 }
2616 sock_write(client_info->socket,"%s", buf);
2617
2618 }
2619
2620 /*
2621 * Push first command in remain queue to current command.
2622 */
parse_destock_remain(client_info_t * client_info)2623 static void parse_destock_remain(client_info_t *client_info) {
2624 char *p, *pp, *c;
2625 char *pbuf;
2626 char commandline[_BUFSIZ];
2627 char remaining[_BUFSIZ];
2628
2629 /*
2630 * Remain and line are already filled, perhaps a forced command line
2631 * happen here: don't handle it here
2632 */
2633
2634 if((client_info->command.remain) && (client_info->command.line))
2635 return;
2636
2637 if((client_info->command.remain == NULL)
2638 && (client_info->command.line && (strchr(client_info->command.line, ';')))) {
2639 client_info->command.remain = strdup(client_info->command.line);
2640 SAFE_FREE(client_info->command.line);
2641 }
2642
2643 if(client_info->command.remain &&
2644 ((client_info->command.line == NULL) ||
2645 ((client_info->command.line != NULL) && (strlen(client_info->command.line) == 0)))) {
2646
2647 memset(&commandline, 0, sizeof(commandline));
2648 memset(&remaining, 0, sizeof(remaining));
2649
2650 if((p = strchr(client_info->command.remain, ';')) != NULL) {
2651
2652 pp = client_info->command.remain;
2653 pbuf = commandline;
2654
2655 while(pp < p) {
2656 *pbuf = *pp;
2657 pp++;
2658 pbuf++;
2659 }
2660 *pbuf = '\0';
2661
2662 if(strlen(commandline)) {
2663 c = &commandline[strlen(commandline)];
2664 while((*c == ';') && (c >= commandline)) {
2665 *c = '\0';
2666 c--;
2667 }
2668 }
2669
2670 c = _atoa(commandline);
2671
2672 if(*p == ';')
2673 p++;
2674
2675 if(p)
2676 strlcpy(remaining, (_atoa(p)), sizeof(remaining));
2677
2678 client_info->command.line = (char *) realloc(client_info->command.line, sizeof(char) * (strlen(c) + 1));
2679
2680 strcpy(client_info->command.line, c);
2681
2682 if(p) {
2683 /* remove last ';' */
2684 if(strchr(remaining, ';')) {
2685 p = &remaining[strlen(remaining)];
2686 while((*p == ';') && (p >= remaining)) {
2687 *p = '\0';
2688 p--;
2689 }
2690 }
2691
2692 if(strlen(remaining)) {
2693 client_info->command.remain = (char *) realloc(client_info->command.remain,
2694 sizeof(char) * (strlen(remaining) + 1));
2695 strcpy(client_info->command.remain, remaining);
2696 }
2697 else {
2698 SAFE_FREE(client_info->command.remain);
2699 }
2700
2701 }
2702 else {
2703 SAFE_FREE(client_info->command.remain);
2704 }
2705
2706 }
2707 else { /* no ';' in remain, copy AS IS remain to line */
2708 client_info->command.line = (char *) realloc(client_info->command.line,
2709 sizeof(char) * (strlen(client_info->command.remain) + 1));
2710
2711 strcpy(client_info->command.line, client_info->command.remain);
2712
2713 SAFE_FREE(client_info->command.remain);
2714 }
2715 }
2716 }
2717
2718 /*
2719 * handle multicommand line.
2720 */
parse_handle_multicommands(client_info_t * client_info)2721 static void parse_handle_multicommands(client_info_t *client_info) {
2722
2723 if(client_info->command.remain) {
2724 fprintf(stderr, "Ooch, Unexpected state, remain isn't empty\n");
2725 }
2726 else {
2727 client_info->command.remain = strdup(client_info->command.line);
2728 parse_destock_remain(client_info);
2729 }
2730 }
2731
2732 /*
2733 * parse command, extract arguments.
2734 */
parse_command(client_info_t * client_info)2735 static void parse_command(client_info_t *client_info) {
2736 char *cmd;
2737 int i = 0;
2738
2739 parse_destock_remain(client_info);
2740
2741 if(strchr(client_info->command.line, ';'))
2742 parse_handle_multicommands(client_info);
2743
2744 cmd = client_info->command.line;
2745
2746 if(client_info->command.num_args) {
2747 for(i = 0; i < client_info->command.num_args; i++)
2748 *client_info->command.args[i] = 0;
2749 }
2750
2751 client_info->command.num_args = 0;
2752
2753 while(*cmd != '\0' && (*cmd != ' ' && *(cmd - 1) != '\\') && *cmd != '\t')
2754 cmd++;
2755
2756 if(cmd >= (client_info->command.line + strlen(client_info->command.line)))
2757 cmd = NULL;
2758
2759 if(cmd) {
2760 client_info->command.command = (char *) realloc(client_info->command.command,
2761 strlen(client_info->command.line) - strlen(cmd) + 1);
2762
2763 *client_info->command.command = 0;
2764 strlcpy(client_info->command.command, client_info->command.line,
2765 (strlen(client_info->command.line) - strlen(cmd)));
2766
2767 cmd = _atoa(cmd);
2768
2769 /*
2770 * Extract and store args
2771 */
2772 if(cmd < client_info->command.line + strlen(client_info->command.line)) {
2773 char *pcmd, *pb, buf[256];
2774 int nargs = 0, get_quote = 0;
2775
2776 pcmd = cmd;
2777 memset(&buf, 0, sizeof(buf));
2778 pb = buf;
2779
2780 while(*(pcmd - 1) != '\0') {
2781
2782 switch(*pcmd) {
2783
2784 case '\'':
2785 if(*(pcmd - 1) != '\\') {
2786 get_quote++;
2787 if(get_quote == 2)
2788 get_quote = 0;
2789 }
2790 else {
2791 pb--;
2792 goto __store_char;
2793 }
2794
2795 break;
2796
2797 case '\t':
2798 case '\0':
2799 goto __end_args;
2800 break;
2801
2802 case ' ':
2803 if((*(pcmd - 1) != '\\') && (get_quote == 0)) {
2804
2805 __end_args:
2806 strcpy(client_info->command.args[nargs], _atoa(buf));
2807 nargs++;
2808 memset(&buf, 0, sizeof(buf));
2809 pb = buf;
2810 }
2811 else {
2812 if(*(pcmd - 1) == '\\')
2813 pb--;
2814 goto __store_char;
2815 }
2816 break;
2817
2818 default:
2819 __store_char:
2820 *pb = *pcmd;
2821 pb++;
2822 break;
2823 }
2824 pcmd++;
2825 }
2826 client_info->command.num_args = nargs;
2827 }
2828 }
2829 else {
2830 client_info->command.command = (char *) realloc(client_info->command.command, strlen(client_info->command.line) + 1);
2831
2832 strcpy(client_info->command.command, client_info->command.line);
2833 }
2834
2835 #if 0
2836 {
2837 int k;
2838 printf("client_info->command = '%s'\n", client_info->command.command);
2839 if(client_info->command.num_args)
2840 for(k = 0; k < client_info->command.num_args; k++)
2841 printf("client_info->command_arg[%d] = '%s'\n", k, client_info->command.args[k]);
2842 }
2843 #endif
2844 }
2845
2846 /*
2847 * Handle user entered commands.
2848 */
handle_client_command(client_info_t * client_info)2849 static void handle_client_command(client_info_t *client_info) {
2850 int i, found;
2851
2852 /* pass command line to the chainsaw */
2853
2854 do {
2855
2856 parse_command(client_info);
2857
2858 if(strlen(client_info->command.line)) {
2859
2860 /*
2861 * Is the command match with one available ?
2862 */
2863 i = found = 0;
2864 while((commands[i].command != NULL) && (found == 0)) {
2865 if(!strncasecmp(client_info->command.command, commands[i].command, strlen(client_info->command.command))) {
2866 if((commands[i].argtype == REQUIRE_ARGS)
2867 && (client_info->command.num_args <= 0)) {
2868 sock_write(client_info->socket,
2869 "Command '%s' require argument(s).\n", commands[i].command);
2870 found++;
2871 }
2872 else if((commands[i].argtype == NO_ARGS) && (client_info->command.num_args > 0)) {
2873 sock_write(client_info->socket,
2874 "Command '%s' doesn't require argument.\n", commands[i].command);
2875 found++;
2876 }
2877 else {
2878 if((commands[i].need_auth == NEED_AUTH) && (client_info->authentified == 0)) {
2879 sock_write(client_info->socket,
2880 "You need to be authentified to use '%s' command, "
2881 "use 'identify'.\n", commands[i].command);
2882 found++;
2883 }
2884 else {
2885 commands[i].function(&commands[i], client_info);
2886 found++;
2887 }
2888 }
2889 }
2890 i++;
2891 }
2892
2893 if(!found)
2894 sock_write(client_info->socket, "unhandled command '%s'.\n", client_info->command.command);
2895
2896 }
2897
2898 SAFE_FREE(client_info->command.line);
2899
2900 } while((client_info->command.remain != NULL));
2901 }
2902
2903 /*
2904 *
2905 */
client_thread(void * data)2906 static __attribute__((noreturn)) void *client_thread(void *data) {
2907 client_info_t *client_info = (client_info_t *) data;
2908 char buffer[_BUFSIZ];
2909 int i;
2910 ssize_t len;
2911 sigset_t sigpipe_mask;
2912
2913 pthread_detach(pthread_self());
2914
2915 sigemptyset(&sigpipe_mask);
2916 sigaddset(&sigpipe_mask, SIGPIPE);
2917 if (pthread_sigmask(SIG_BLOCK, &sigpipe_mask, NULL) == -1)
2918 perror("pthread_sigmask");
2919
2920 client_info->finished = 0;
2921 memset(&client_info->name, 0, sizeof(client_info->name));
2922 memset(&client_info->passwd, 0, sizeof(client_info->passwd));
2923
2924 for(i = 0; i < 256; i++)
2925 client_info->command.args[i] = (char *) malloc(sizeof(char) * 2048);
2926
2927 say_hello(client_info);
2928
2929 do {
2930 len = sock_read(client_info->socket, buffer, sizeof(buffer));
2931
2932 if(len < 0)
2933 client_info->finished = 1;
2934 else {
2935 if(len > 0) {
2936
2937 set_command_line(client_info, _atoa(buffer));
2938 handle_client_command(client_info);
2939 }
2940 }
2941
2942 } while(client_info->finished == 0);
2943
2944 close(client_info->socket);
2945
2946 for(i = 0; i < 256; i++)
2947 SAFE_FREE(client_info->command.args[i]);
2948
2949 free(client_info);
2950 client_info = NULL;
2951
2952 pthread_exit(NULL);
2953 }
2954
2955 /*
2956 *
2957 */
server_thread(void * data)2958 static __attribute__((noreturn)) void *server_thread(void *data) {
2959 gGui_t *gui = gGui;
2960 char *service;
2961 struct servent *serv_ent;
2962 int msock;
2963
2964 if(gui->network_port) {
2965 int len = (int) log10(gui->network_port) + 1;
2966
2967 service = (char *) malloc(len + 1);
2968 sprintf(service, "%u", gui->network_port);
2969 }
2970 else {
2971 /* Search in /etc/services if a xinectl entry exist */
2972 if((serv_ent = getservbyname("xinectl", "tcp")) != NULL) {
2973 service = (char *) malloc(ntohs(serv_ent->s_port));
2974 sprintf(service, "%u", ntohs(serv_ent->s_port));
2975 }
2976 else
2977 service = strdup(DEFAULT_XINECTL_PORT);
2978 }
2979
2980 /* Load passwd file */
2981 /* password file syntax is:
2982 * - one entry per line
2983 * - syntax: 'identifier:password' length limit is 16 chars each
2984 * - a password beginning by an asterisk '*' lock the user.
2985 * - if a line contain 'ALL:ALLOW' (case sensitive),
2986 * all client are allowed to execute all commands.
2987 * - if a line contain 'ALL:DENY' (case sensitive),
2988 * all client aren't allowed to execute a command, except those
2989 * who are specified. ex:
2990 * - rule 'ALL' is optional.
2991 *
2992 * ALL:DENY
2993 * daniel:f1rmb
2994 * .......
2995 * All users are denied, except 'daniel' (if he give good passwd)
2996 *
2997 *
2998 * daniel:f1rmb
2999 * foo:*mypasswd
3000 * .......
3001 * All users are denied, 'daniel' is authorized (with passwd), 'foo'
3002 * is locked.
3003 *
3004 */
3005 {
3006 const char *passwdfile = ".xine/passwd";
3007 char passwdfilename[(strlen((xine_get_homedir())) + strlen(passwdfile)) + 2];
3008 struct stat st;
3009
3010 snprintf(passwdfilename, sizeof(passwdfilename), "%s/%s", (xine_get_homedir()), passwdfile);
3011
3012 if(stat(passwdfilename, &st) < 0) {
3013 fprintf(stderr, "ERROR: there is no password file for network access.!\n");
3014 goto __failed;
3015 }
3016
3017 if(!server_load_passwd(passwdfilename)) {
3018 fprintf(stderr, "ERROR: unable to load network server password file.!\n");
3019 goto __failed;
3020 }
3021
3022 }
3023
3024 while(gui->running) {
3025 client_info_t *client_info;
3026 socklen_t lsin;
3027 pthread_t thread_client;
3028
3029 msock = sock_serv(service, "tcp", 5);
3030 if (msock < 0) {
3031 break;
3032 }
3033
3034 client_info = (client_info_t *) calloc(1, sizeof(client_info_t));
3035 lsin = sizeof(client_info->fsin.in);
3036
3037 client_info->socket = accept(msock, &(client_info->fsin.sa), &lsin);
3038 client_info->authentified = is_client_authorized(client_info);
3039
3040 close(msock);
3041
3042 if(client_info->socket < 0) {
3043
3044 free(client_info);
3045
3046 if(errno == EINTR)
3047 continue;
3048
3049 sock_err("accept: %s\n", strerror(errno));
3050 continue;
3051 }
3052
3053 pthread_create(&thread_client, NULL, client_thread, (void *)client_info);
3054 }
3055
3056 { /* Freeing passwords */
3057 int i;
3058
3059 for(i = 0; passwds[i]->ident != NULL; i++) {
3060 free(passwds[i]->ident);
3061 free(passwds[i]->passwd);
3062 free(passwds[i]);
3063 }
3064 free(passwds);
3065
3066 }
3067
3068 __failed:
3069 free(service);
3070
3071 pthread_exit(NULL);
3072 }
3073
3074 /*
3075 *
3076 */
start_remote_server(void)3077 void start_remote_server(void) {
3078 gGui_t *gui = gGui;
3079 pthread_t thread;
3080
3081 if(gui->network)
3082 pthread_create(&thread, NULL, server_thread, NULL);
3083
3084 }
3085
3086 #endif
3087
3088 #endif /* HAVE_READLINE */
3089