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