00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include <stdlib.h>
00029
00030 #include <vlc/vlc.h>
00031 #include <vlc/intf.h>
00032
00033 #include <vlc/input.h>
00034
00035 #include <sys/stat.h>
00036
00037 #include <errno.h>
00038 #include <fcntl.h>
00039
00040 #ifdef HAVE_SYS_TIME_H
00041 # include <sys/time.h>
00042 #endif
00043
00044 #ifdef HAVE_UNISTD_H
00045 # include <unistd.h>
00046 #endif
00047
00048 #if defined( UNDER_CE )
00049 # include <winsock.h>
00050 #elif defined( WIN32 )
00051 # include <winsock2.h>
00052 #else
00053 # include <sys/socket.h>
00054 #endif
00055
00056 #include "network.h"
00057
00058 #include "vlc_vlm.h"
00059
00060 #define READ_MODE_PWD 1
00061 #define READ_MODE_CMD 2
00062 #define WRITE_MODE_PWD 3 // when we write the word "Password:"
00063 #define WRITE_MODE_CMD 4
00064
00065
00066 #define TEL_WILL 251
00067 #define TEL_WONT 252
00068 #define TEL_DO 253
00069 #define TEL_DONT 254
00070 #define TEL_IAC 255
00071 #define TEL_ECHO 1
00072
00073
00074
00075
00076 static int Open ( vlc_object_t * );
00077 static void Close( vlc_object_t * );
00078
00079 #define TELNETHOST_TEXT N_( "Telnet Interface host" )
00080 #define TELNETHOST_LONGTEXT N_( "Default to listen on all network interfaces" )
00081 #define TELNETPORT_TEXT N_( "Telnet Interface port" )
00082 #define TELNETPORT_LONGTEXT N_( "Default to 4212" )
00083 #define TELNETPORT_DEFAULT 4212
00084 #define TELNETPWD_TEXT N_( "Telnet Interface password" )
00085 #define TELNETPWD_LONGTEXT N_( "Default to admin" )
00086 #define TELNETPWD_DEFAULT "admin"
00087
00088 vlc_module_begin();
00089 set_shortname( "Telnet" );
00090 set_category( CAT_INTERFACE );
00091 set_subcategory( SUBCAT_INTERFACE_GENERAL );
00092 add_string( "telnet-host", "", NULL, TELNETHOST_TEXT,
00093 TELNETHOST_LONGTEXT, VLC_TRUE );
00094 add_integer( "telnet-port", TELNETPORT_DEFAULT, NULL, TELNETPORT_TEXT,
00095 TELNETPORT_LONGTEXT, VLC_TRUE );
00096 add_string( "telnet-password", TELNETPWD_DEFAULT, NULL, TELNETPWD_TEXT,
00097 TELNETPWD_LONGTEXT, VLC_TRUE );
00098 set_description( _("VLM remote control interface") );
00099 add_category_hint( "VLM", NULL, VLC_FALSE );
00100 set_capability( "interface", 0 );
00101 set_callbacks( Open , Close );
00102 vlc_module_end();
00103
00104
00105
00106
00107 static void Run( intf_thread_t * );
00108
00109 typedef struct
00110 {
00111 int i_mode;
00112 int fd;
00113 char buffer_read[1000];
00114 char *buffer_write;
00115 char *p_buffer_read;
00116 char *p_buffer_write;
00117 int i_buffer_write;
00118 int i_tel_cmd;
00119
00120 } telnet_client_t;
00121
00122 static char *MessageToString( vlm_message_t *, int );
00123 static void Write_message( telnet_client_t *, vlm_message_t *, char *, int );
00124
00125 struct intf_sys_t
00126 {
00127 telnet_client_t **clients;
00128 int i_clients;
00129 int *pi_fd;
00130 vlm_t *mediatheque;
00131 };
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141 static int getPort(intf_thread_t *p_intf, vlc_url_t url, int i_port)
00142 {
00143
00144 if (url.i_port != 0 &&
00145 i_port != TELNETPORT_DEFAULT &&
00146 url.i_port != i_port )
00147 {
00148 msg_Err( p_intf, "ignoring port %d and using %d", url.i_port,
00149 i_port);
00150 }
00151 if (i_port != TELNETPORT_DEFAULT)
00152 {
00153 return i_port;
00154 }
00155 if (url.i_port != 0)
00156 {
00157 return url.i_port;
00158 }
00159
00160 return i_port;
00161 }
00162
00163
00164
00165
00166 static int Open( vlc_object_t *p_this )
00167 {
00168 intf_thread_t *p_intf = (intf_thread_t*) p_this;
00169 vlm_t *mediatheque;
00170 char *psz_address;
00171 vlc_url_t url;
00172 int i_telnetport;
00173
00174 if( !(mediatheque = vlm_New( p_intf )) )
00175 {
00176 msg_Err( p_intf, "cannot start VLM" );
00177 return VLC_EGENERIC;
00178 }
00179
00180 msg_Info( p_intf, "Using the VLM interface plugin..." );
00181
00182 i_telnetport = config_GetInt( p_intf, "telnet-port" );
00183 psz_address = config_GetPsz( p_intf, "telnet-host" );
00184
00185 vlc_UrlParse(&url, psz_address, 0);
00186
00187
00188
00189 url.i_port = getPort(p_intf, url, i_telnetport);
00190
00191 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
00192 if( ( p_intf->p_sys->pi_fd = net_ListenTCP( p_intf, url.psz_host, url.i_port ) )
00193 == NULL )
00194 {
00195 msg_Err( p_intf, "cannot listen for telnet" );
00196 free( p_intf->p_sys );
00197 return VLC_EGENERIC;
00198 }
00199 msg_Info( p_intf,
00200 "Telnet interface started on interface %s %d",
00201 url.psz_host, url.i_port );
00202
00203 p_intf->p_sys->i_clients = 0;
00204 p_intf->p_sys->clients = NULL;
00205 p_intf->p_sys->mediatheque = mediatheque;
00206 p_intf->pf_run = Run;
00207
00208 return VLC_SUCCESS;
00209 }
00210
00211
00212
00213
00214 static void Close( vlc_object_t *p_this )
00215 {
00216 intf_thread_t *p_intf = (intf_thread_t*)p_this;
00217 intf_sys_t *p_sys = p_intf->p_sys;
00218 int i;
00219
00220 for( i = 0; i < p_sys->i_clients; i++ )
00221 {
00222 telnet_client_t *cl = p_sys->clients[i];
00223
00224 net_Close( cl->fd );
00225 free( cl );
00226 }
00227 if( p_sys->clients != NULL ) free( p_sys->clients );
00228
00229 net_ListenClose( p_sys->pi_fd );
00230
00231 vlm_Delete( p_sys->mediatheque );
00232
00233 free( p_sys );
00234 }
00235
00236
00237
00238
00239 static void Run( intf_thread_t *p_intf )
00240 {
00241 intf_sys_t *p_sys = p_intf->p_sys;
00242 struct timeval timeout;
00243 char *psz_password;
00244
00245 psz_password = config_GetPsz( p_intf, "telnet-password" );
00246
00247 while( !p_intf->b_die )
00248 {
00249 fd_set fds_read, fds_write;
00250 int i_handle_max = 0;
00251 int i_ret, i_len, fd, i;
00252
00253
00254 fd = net_Accept( p_intf, p_sys->pi_fd, p_sys->i_clients > 0 ? 0 : -1 );
00255 if( fd > 0 )
00256 {
00257 telnet_client_t *cl;
00258
00259
00260 #if defined( WIN32 ) || defined( UNDER_CE )
00261 {
00262 unsigned long i_dummy = 1;
00263 ioctlsocket( fd, FIONBIO, &i_dummy );
00264 }
00265 #else
00266 fcntl( fd, F_SETFL, O_NONBLOCK );
00267 #endif
00268 cl = malloc( sizeof( telnet_client_t ));
00269 cl->i_tel_cmd = 0;
00270 cl->fd = fd;
00271 cl->buffer_write = NULL;
00272 cl->p_buffer_write = cl->buffer_write;
00273 Write_message( cl, NULL, "Password:\xff\xfb\x01", WRITE_MODE_PWD );
00274
00275 TAB_APPEND( p_sys->i_clients, p_sys->clients, cl );
00276 }
00277
00278
00279 FD_ZERO( &fds_read );
00280 FD_ZERO( &fds_write );
00281
00282 for( i = 0 ; i < p_sys->i_clients ; i++ )
00283 {
00284 telnet_client_t *cl = p_sys->clients[i];
00285
00286 if( cl->i_mode == WRITE_MODE_PWD || cl->i_mode == WRITE_MODE_CMD )
00287 {
00288 FD_SET( cl->fd , &fds_write );
00289 }
00290 else
00291 {
00292 FD_SET( cl->fd , &fds_read );
00293 }
00294 i_handle_max = __MAX( i_handle_max, cl->fd );
00295 }
00296
00297 timeout.tv_sec = 0;
00298 timeout.tv_usec = 500*1000;
00299
00300 i_ret = select( i_handle_max + 1, &fds_read, &fds_write, 0, &timeout );
00301 if( i_ret == -1 && errno != EINTR )
00302 {
00303 msg_Warn( p_intf, "cannot select sockets" );
00304 msleep( 1000 );
00305 continue;
00306 }
00307 else if( i_ret <= 0 )
00308 {
00309 continue;
00310 }
00311
00312
00313 for( i = 0 ; i < p_sys->i_clients ; i++ )
00314 {
00315 telnet_client_t *cl = p_sys->clients[i];
00316
00317 if( FD_ISSET(cl->fd , &fds_write) && cl->i_buffer_write > 0 )
00318 {
00319 i_len = send( cl->fd , cl->p_buffer_write ,
00320 cl->i_buffer_write , 0 );
00321 if( i_len > 0 )
00322 {
00323 cl->p_buffer_write += i_len;
00324 cl->i_buffer_write -= i_len;
00325 }
00326 }
00327 else if( FD_ISSET( cl->fd, &fds_read) )
00328 {
00329 int i_end = 0;
00330 int i_recv;
00331
00332 while( (i_recv=recv( cl->fd, cl->p_buffer_read, 1, 0 )) > 0 &&
00333 cl->p_buffer_read - cl->buffer_read < 999 )
00334 {
00335 switch( cl->i_tel_cmd )
00336 {
00337 case 0:
00338 switch( *(uint8_t *)cl->p_buffer_read )
00339 {
00340 case '\r':
00341 break;
00342 case '\n':
00343 *cl->p_buffer_read = '\n';
00344 i_end = 1;
00345 break;
00346 case TEL_IAC:
00347 cl->i_tel_cmd = 1;
00348 cl->p_buffer_read++;
00349 break;
00350 default:
00351 cl->p_buffer_read++;
00352 break;
00353 }
00354 break;
00355 case 1:
00356 switch( *(uint8_t *)cl->p_buffer_read )
00357 {
00358 case TEL_WILL: case TEL_WONT:
00359 case TEL_DO: case TEL_DONT:
00360 cl->i_tel_cmd++;
00361 cl->p_buffer_read++;
00362 break;
00363 default:
00364 cl->i_tel_cmd = 0;
00365 cl->p_buffer_read--;
00366 break;
00367 }
00368 break;
00369 case 2:
00370 cl->i_tel_cmd = 0;
00371 cl->p_buffer_read -= 2;
00372 break;
00373 }
00374
00375 if( i_end != 0 ) break;
00376 }
00377
00378 if( cl->p_buffer_read - cl->buffer_read == 999 )
00379 {
00380 Write_message( cl, NULL, "Line too long\r\n",
00381 cl->i_mode + 2 );
00382 }
00383
00384 if (i_recv == 0)
00385 {
00386 net_Close( cl->fd );
00387 TAB_REMOVE( p_intf->p_sys->i_clients ,
00388 p_intf->p_sys->clients , cl );
00389 free( cl );
00390 }
00391 }
00392 }
00393
00394
00395 for( i = 0 ; i < p_sys->i_clients ; i++ )
00396 {
00397 telnet_client_t *cl = p_sys->clients[i];
00398
00399 if( cl->i_mode >= WRITE_MODE_PWD && cl->i_buffer_write == 0 )
00400 {
00401
00402 cl->i_mode -= 2;
00403 }
00404 else if( cl->i_mode == READ_MODE_PWD &&
00405 *cl->p_buffer_read == '\n' )
00406 {
00407 *cl->p_buffer_read = '\0';
00408 if( strcmp( psz_password, cl->buffer_read ) == 0 )
00409 {
00410 Write_message( cl, NULL, "\xff\xfc\x01\r\nWelcome, "
00411 "Master\r\n> ", WRITE_MODE_CMD );
00412 }
00413 else
00414 {
00415
00416 Write_message( cl, NULL, "\r\nWrong password. ",
00417 WRITE_MODE_PWD );
00418 }
00419 }
00420 else if( cl->i_mode == READ_MODE_CMD &&
00421 *cl->p_buffer_read == '\n' )
00422 {
00423
00424 if( !strncmp( cl->buffer_read, "logout", 6 ) ||
00425 !strncmp( cl->buffer_read, "quit", 4 ) ||
00426 !strncmp( cl->buffer_read, "exit", 4 ) )
00427 {
00428 net_Close( cl->fd );
00429 TAB_REMOVE( p_intf->p_sys->i_clients ,
00430 p_intf->p_sys->clients , cl );
00431 free( cl );
00432 }
00433 else if( !strncmp( cl->buffer_read, "shutdown", 8 ) )
00434 {
00435 msg_Err( p_intf, "shutdown requested" );
00436 p_intf->p_vlc->b_die = VLC_TRUE;
00437 }
00438 else
00439 {
00440 vlm_message_t *message;
00441
00442
00443 *cl->p_buffer_read = '\0';
00444
00445 vlm_ExecuteCommand( p_sys->mediatheque, cl->buffer_read,
00446 &message );
00447 Write_message( cl, message, NULL, WRITE_MODE_CMD );
00448 vlm_MessageDelete( message );
00449 }
00450 }
00451 }
00452 }
00453 }
00454
00455 static void Write_message( telnet_client_t *client, vlm_message_t *message,
00456 char *string_message, int i_mode )
00457 {
00458 char *psz_message;
00459
00460 client->p_buffer_read = client->buffer_read;
00461 (client->p_buffer_read)[0] = 0;
00462 if( client->buffer_write ) free( client->buffer_write );
00463
00464
00465 if( message )
00466 {
00467
00468 psz_message = MessageToString( message, 0 );
00469 }
00470 else
00471 {
00472
00473 psz_message = strdup( string_message );
00474 }
00475
00476 client->buffer_write = client->p_buffer_write = psz_message;
00477 client->i_buffer_write = strlen( psz_message );
00478 client->i_mode = i_mode;
00479 }
00480
00481
00482
00483 static char *MessageToString( vlm_message_t *message, int i_level )
00484 {
00485 #define STRING_CR "\r\n"
00486 #define STRING_TAIL "> "
00487
00488 char *psz_message;
00489 int i, i_message = sizeof( STRING_TAIL );
00490
00491 if( !message || !message->psz_name )
00492 {
00493 return strdup( STRING_CR STRING_TAIL );
00494 }
00495 else if( !i_level && !message->i_child && !message->psz_value )
00496 {
00497
00498 return strdup( STRING_CR STRING_TAIL );
00499 }
00500
00501 i_message += strlen( message->psz_name ) + i_level * sizeof( " " ) + 1;
00502 psz_message = malloc( i_message ); *psz_message = 0;
00503 for( i = 0; i < i_level; i++ ) strcat( psz_message, " " );
00504 strcat( psz_message, message->psz_name );
00505
00506 if( message->psz_value )
00507 {
00508 i_message += sizeof( " : " ) + strlen( message->psz_value ) +
00509 sizeof( STRING_CR );
00510 psz_message = realloc( psz_message, i_message );
00511 strcat( psz_message, " : " );
00512 strcat( psz_message, message->psz_value );
00513 strcat( psz_message, STRING_CR );
00514 }
00515 else
00516 {
00517 i_message += sizeof( STRING_CR );
00518 psz_message = realloc( psz_message, i_message );
00519 strcat( psz_message, STRING_CR );
00520 }
00521
00522 for( i = 0; i < message->i_child; i++ )
00523 {
00524 char *child_message =
00525 MessageToString( message->child[i], i_level + 1 );
00526
00527 i_message += strlen( child_message );
00528 psz_message = realloc( psz_message, i_message );
00529 strcat( psz_message, child_message );
00530 free( child_message );
00531 }
00532
00533 if( i_level == 0 ) strcat( psz_message, STRING_TAIL );
00534
00535 return psz_message;
00536 }