allow build mvcp client without mlt framework
[melted] / src / mvcp / mvcp_remote.c
1 /*
2 * mvcp_remote.c -- Remote Parser
3 * Copyright (C) 2002-2009 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /* System header files */
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <signal.h>
26 #include <errno.h>
27 #include <pthread.h>
28
29 /* Application header files */
30 #ifndef MVCP_EMBEDDED
31 #include <framework/mlt.h>
32 #else
33 #define mlt_service void *
34 #endif
35 #include "mvcp_remote.h"
36 #include "mvcp_socket.h"
37 #include "mvcp_tokeniser.h"
38 #include "mvcp_util.h"
39
40 /** Private mvcp_remote structure.
41 */
42
43 typedef struct
44 {
45 int terminated;
46 char *server;
47 int port;
48 mvcp_socket socket;
49 mvcp_socket status;
50 pthread_t thread;
51 mvcp_parser parser;
52 pthread_mutex_t mutex;
53 int connected;
54 }
55 *mvcp_remote, mvcp_remote_t;
56
57 /** Forward declarations.
58 */
59
60 static mvcp_response mvcp_remote_connect( mvcp_remote );
61 static mvcp_response mvcp_remote_execute( mvcp_remote, char * );
62 static mvcp_response mvcp_remote_receive( mvcp_remote, char *, char * );
63 static mvcp_response mvcp_remote_push( mvcp_remote, char *, mlt_service );
64 static void mvcp_remote_close( mvcp_remote );
65 static int mvcp_remote_read_response( mvcp_socket, mvcp_response );
66
67 /** MVCP Parser constructor.
68 */
69
70 mvcp_parser mvcp_parser_init_remote( char *server, int port )
71 {
72 mvcp_parser parser = calloc( 1, sizeof( mvcp_parser_t ) );
73 mvcp_remote remote = calloc( 1, sizeof( mvcp_remote_t ) );
74
75 if ( parser != NULL )
76 {
77 parser->connect = (parser_connect)mvcp_remote_connect;
78 parser->execute = (parser_execute)mvcp_remote_execute;
79 parser->push = (parser_push)mvcp_remote_push;
80 parser->received = (parser_received)mvcp_remote_receive;
81 parser->close = (parser_close)mvcp_remote_close;
82 parser->real = remote;
83
84 if ( remote != NULL )
85 {
86 remote->parser = parser;
87 remote->server = strdup( server );
88 remote->port = port;
89 pthread_mutex_init( &remote->mutex, NULL );
90 }
91 }
92 return parser;
93 }
94
95 /** Thread for receiving and distributing the status information.
96 */
97
98 static void *mvcp_remote_status_thread( void *arg )
99 {
100 mvcp_remote remote = arg;
101 char temp[ 10240 ];
102 int length = 0;
103 int offset = 0;
104 mvcp_tokeniser tokeniser = mvcp_tokeniser_init( );
105 mvcp_notifier notifier = mvcp_parser_get_notifier( remote->parser );
106 mvcp_status_t status;
107 int index = 0;
108
109 mvcp_socket_write_data( remote->status, "STATUS\r\n", 8 );
110
111 while ( !remote->terminated &&
112 ( length = mvcp_socket_read_data( remote->status, temp + offset, sizeof( temp ) ) ) >= 0 )
113 {
114 if ( strchr( temp, '\n' ) == NULL )
115 {
116 offset = length;
117 continue;
118 }
119 offset = 0;
120 mvcp_tokeniser_parse_new( tokeniser, temp, "\n" );
121 for ( index = 0; index < mvcp_tokeniser_count( tokeniser ); index ++ )
122 {
123 char *line = mvcp_tokeniser_get_string( tokeniser, index );
124 if ( line[ strlen( line ) - 1 ] == '\r' )
125 {
126 mvcp_util_chomp( line );
127 mvcp_status_parse( &status, line );
128 mvcp_notifier_put( notifier, &status );
129 }
130 else
131 {
132 strcpy( temp, line );
133 offset = strlen( temp );
134 }
135 }
136 }
137
138 mvcp_notifier_disconnected( notifier );
139 mvcp_tokeniser_close( tokeniser );
140 remote->terminated = 1;
141
142 return NULL;
143 }
144
145 /** Forward reference.
146 */
147
148 static void mvcp_remote_disconnect( mvcp_remote remote );
149
150 /** Connect to the server.
151 */
152
153 static mvcp_response mvcp_remote_connect( mvcp_remote remote )
154 {
155 mvcp_response response = NULL;
156
157 mvcp_remote_disconnect( remote );
158
159 if ( !remote->connected )
160 {
161 signal( SIGPIPE, SIG_IGN );
162
163 remote->socket = mvcp_socket_init( remote->server, remote->port );
164 remote->status = mvcp_socket_init( remote->server, remote->port );
165
166 if ( mvcp_socket_connect( remote->socket ) == 0 )
167 {
168 response = mvcp_response_init( );
169 mvcp_remote_read_response( remote->socket, response );
170 }
171
172 if ( response != NULL && mvcp_socket_connect( remote->status ) == 0 )
173 {
174 mvcp_response status_response = mvcp_response_init( );
175 mvcp_remote_read_response( remote->status, status_response );
176 if ( mvcp_response_get_error_code( status_response ) == 100 )
177 pthread_create( &remote->thread, NULL, mvcp_remote_status_thread, remote );
178 mvcp_response_close( status_response );
179 remote->connected = 1;
180 }
181 }
182
183 return response;
184 }
185
186 /** Execute the command.
187 */
188
189 static mvcp_response mvcp_remote_execute( mvcp_remote remote, char *command )
190 {
191 mvcp_response response = NULL;
192 pthread_mutex_lock( &remote->mutex );
193 if ( mvcp_socket_write_data( remote->socket, command, strlen( command ) ) == strlen( command ) )
194 {
195 response = mvcp_response_init( );
196 mvcp_socket_write_data( remote->socket, "\r\n", 2 );
197 mvcp_remote_read_response( remote->socket, response );
198 }
199 pthread_mutex_unlock( &remote->mutex );
200 return response;
201 }
202
203 /** Push a MLT XML document to the server.
204 */
205
206 static mvcp_response mvcp_remote_receive( mvcp_remote remote, char *command, char *buffer )
207 {
208 mvcp_response response = NULL;
209 pthread_mutex_lock( &remote->mutex );
210 if ( mvcp_socket_write_data( remote->socket, command, strlen( command ) ) == strlen( command ) )
211 {
212 char temp[ 20 ];
213 int length = strlen( buffer );
214 response = mvcp_response_init( );
215 mvcp_socket_write_data( remote->socket, "\r\n", 2 );
216 sprintf( temp, "%d", length );
217 mvcp_socket_write_data( remote->socket, temp, strlen( temp ) );
218 mvcp_socket_write_data( remote->socket, "\r\n", 2 );
219 mvcp_socket_write_data( remote->socket, buffer, length );
220 mvcp_socket_write_data( remote->socket, "\r\n", 2 );
221 mvcp_remote_read_response( remote->socket, response );
222 }
223 pthread_mutex_unlock( &remote->mutex );
224 return response;
225 }
226
227 /** Push a producer to the server.
228 */
229
230 static mvcp_response mvcp_remote_push( mvcp_remote remote, char *command, mlt_service service )
231 {
232 mvcp_response response = NULL;
233 #ifndef MVCP_EMBEDDED
234 if ( service != NULL )
235 {
236 mlt_consumer consumer = mlt_factory_consumer( NULL, "xml", "buffer" );
237 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
238 char *buffer = NULL;
239 // Temporary hack
240 mlt_properties_set( properties, "store", "nle_" );
241 mlt_consumer_connect( consumer, service );
242 mlt_consumer_start( consumer );
243 buffer = mlt_properties_get( properties, "buffer" );
244 response = mvcp_remote_receive( remote, command, buffer );
245 mlt_consumer_close( consumer );
246 }
247 #endif
248 return response;
249 }
250
251 /** Disconnect.
252 */
253
254 static void mvcp_remote_disconnect( mvcp_remote remote )
255 {
256 if ( remote != NULL && remote->terminated )
257 {
258 if ( remote->connected )
259 pthread_join( remote->thread, NULL );
260 mvcp_socket_close( remote->status );
261 mvcp_socket_close( remote->socket );
262 remote->connected = 0;
263 remote->terminated = 0;
264 }
265 }
266
267 /** Close the parser.
268 */
269
270 static void mvcp_remote_close( mvcp_remote remote )
271 {
272 if ( remote != NULL )
273 {
274 remote->terminated = 1;
275 mvcp_remote_disconnect( remote );
276 pthread_mutex_destroy( &remote->mutex );
277 free( remote->server );
278 free( remote );
279 }
280 }
281
282 /** Read response.
283 */
284
285 static int mvcp_remote_read_response( mvcp_socket socket, mvcp_response response )
286 {
287 char temp[ 10240 ];
288 int length;
289 int terminated = 0;
290
291 while ( !terminated && ( length = mvcp_socket_read_data( socket, temp, 10240 ) ) >= 0 )
292 {
293 int position = 0;
294 temp[ length ] = '\0';
295 mvcp_response_write( response, temp, length );
296 position = mvcp_response_count( response ) - 1;
297 if ( position < 0 || temp[ strlen( temp ) - 1 ] != '\n' )
298 continue;
299 switch( mvcp_response_get_error_code( response ) )
300 {
301 case 201:
302 case 500:
303 terminated = !strcmp( mvcp_response_get_line( response, position ), "" );
304 break;
305 case 202:
306 terminated = mvcp_response_count( response ) >= 2;
307 break;
308 default:
309 terminated = 1;
310 break;
311 }
312 }
313
314 return 0;
315 }