]> git.sesse.net Git - mlt/blob - src/miracle/miracle_connection.c
7f287e34194ef49bde4eb51247cc56385133184d
[mlt] / src / miracle / miracle_connection.c
1 /*
2  * miracle_connection.c -- DV Connection Handler
3  * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 /* System header files */
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <signal.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <netdb.h>
34 #include <sys/socket.h> 
35 #include <arpa/inet.h>
36
37 #include <valerie/valerie_socket.h>
38
39 /* Application header files */
40 #include "miracle_commands.h"
41 #include "miracle_connection.h"
42 #include "miracle_server.h"
43 #include "miracle_log.h"
44
45 /** This is a generic replacement for fgets which operates on a file
46    descriptor. Unlike fgets, we can also specify a line terminator. Maximum
47    of (max - 1) chars can be read into buf from fd. If we reach the
48    end-of-file, *eof_chk is set to 1. 
49 */
50
51 int fdgetline( int fd, char *buf, int max, char line_terminator, int *eof_chk )
52 {
53         int count = 0;
54         char tmp [1];
55         *eof_chk = 0;
56         
57         if (fd)
58                 while (count < max - 1) {
59                         if (read (fd, tmp, 1) > 0) {
60                                 if (tmp [0] != line_terminator)
61                                         buf [count++] = tmp [0];
62                                 else
63                                         break;
64
65 /* Is it an EOF character (ctrl-D, i.e. ascii 4)? If so we definitely want
66    to break. */
67
68                                 if (tmp [0] == 4) {
69                                         *eof_chk = 1;
70                                         break;
71                                 }
72                         } else {
73                                 *eof_chk = 1;
74                                 break;
75                         }
76                 }
77                 
78         buf [count] = '\0';
79         
80         return count;
81 }
82
83 static int connection_initiate( int );
84 static int connection_send( int, valerie_response );
85 static int connection_read( int, char *, int );
86 static void connection_close( int );
87
88 static int connection_initiate( int fd )
89 {
90         int error = 0;
91         valerie_response response = valerie_response_init( );
92         valerie_response_set_error( response, 100, "VTR Ready" );
93         error = connection_send( fd, response );
94         valerie_response_close( response );
95         return error;
96 }
97
98 static int connection_send( int fd, valerie_response response )
99 {
100         int error = 0;
101         int index = 0;
102         int code = valerie_response_get_error_code( response );
103
104         if ( code != -1 )
105         {
106                 int items = valerie_response_count( response );
107
108                 if ( items == 0 )
109                         valerie_response_set_error( response, 500, "Unknown error" );
110
111                 if ( code == 200 && items > 2 )
112                         valerie_response_set_error( response, 201, "OK" );
113                 else if ( code == 200 && items > 1 )
114                         valerie_response_set_error( response, 202, "OK" );
115
116                 code = valerie_response_get_error_code( response );
117                 items = valerie_response_count( response );
118
119                 for ( index = 0; !error && index < items; index ++ )
120                 {
121                         char *line = valerie_response_get_line( response, index );
122                         int length = strlen( line );
123                         if ( length == 0 && index != valerie_response_count( response ) - 1 && write( fd, " ", 1 ) != 1 )
124                                 error = -1;
125                         else if ( length > 0 && write( fd, line, length ) != length )
126                                 error = -1;
127                         if ( write( fd, "\r\n", 2 ) != 2 )
128                                 error = -1;                     
129                 }
130
131                 if ( ( code == 201 || code == 500 ) && strcmp( valerie_response_get_line( response, items - 1 ), "" ) )
132                         if ( write( fd, "\r\n", 2 ) != 2 )
133                                 miracle_log( LOG_ERR, "write(\"\\r\\n\") failed!" );
134         }
135         else
136         {
137                 const char *message = "500 Empty Response\r\n\r\n";
138                 if ( write( fd, message, strlen( message ) ) != strlen( message ))
139                         miracle_log( LOG_ERR, "write(%s) failed!", message );
140         }
141
142         return error;
143 }
144
145 static int connection_read( int fd, char *command, int length )
146 {
147         int eof_chk;
148         int nchars = fdgetline( fd, command, length, '\n', &eof_chk );
149         char *cr = strchr( command, '\r');
150         if ( cr != NULL ) 
151                 cr[0] = '\0';
152         if ( eof_chk || strncasecmp( command, "BYE", 3 ) == 0 ) 
153                 nchars = 0;
154         return nchars;
155 }
156
157 int connection_status( int fd, valerie_notifier notifier )
158 {
159         int error = 0;
160         int index = 0;
161         valerie_status_t status;
162         char text[ 10240 ];
163         valerie_socket socket = valerie_socket_init_fd( fd );
164         
165         for ( index = 0; !error && index < MAX_UNITS; index ++ )
166         {
167                 valerie_notifier_get( notifier, &status, index );
168                 valerie_status_serialise( &status, text, sizeof( text ) );
169                 error = valerie_socket_write_data( socket, text, strlen( text )  ) != strlen( text );
170         }
171
172         while ( !error )
173         {
174                 if ( valerie_notifier_wait( notifier, &status ) == 0 )
175                 {
176                         valerie_status_serialise( &status, text, sizeof( text ) );
177                         error = valerie_socket_write_data( socket, text, strlen( text ) ) != strlen( text );
178                 }
179                 else
180                 {
181                         struct timeval tv = { 0, 0 };
182                         fd_set rfds;
183
184                     FD_ZERO( &rfds );
185                     FD_SET( fd, &rfds );
186
187                         if ( select( socket->fd + 1, &rfds, NULL, NULL, &tv ) )
188                                 error = 1;
189                 }
190         }
191
192         valerie_socket_close( socket );
193         
194         return error;
195 }
196
197 static void connection_close( int fd )
198 {
199         close( fd );
200 }
201
202 void *parser_thread( void *arg )
203 {
204         struct hostent *he;
205         connection_t *connection = arg;
206         mlt_properties owner = connection->owner;
207         char address[ 512 ];
208         char command[ 1024 ];
209         int fd = connection->fd;
210         valerie_parser parser = connection->parser;
211         valerie_response response = NULL;
212
213         /* Get the connecting clients ip information */
214         he = gethostbyaddr( (char *) &( connection->sin.sin_addr.s_addr ), sizeof(u_int32_t), AF_INET); 
215         if ( he != NULL )
216                 strcpy( address, he->h_name );
217         else
218                 inet_ntop( AF_INET, &( connection->sin.sin_addr.s_addr), address, 32 );
219
220         miracle_log( LOG_NOTICE, "Connection established with %s (%d)", address, fd );
221
222         /* Execute the commands received. */
223         if ( connection_initiate( fd ) == 0 )
224         {
225                 int error = 0;
226
227                 while( !error && connection_read( fd, command, 1024 ) )
228                 {
229                         response = NULL;
230
231                         if ( !strncmp( command, "PUSH ", 5 ) )
232                         {
233                                 char temp[ 20 ];
234                                 int bytes;
235                                 char *buffer = NULL;
236                                 int total = 0;
237                                 mlt_service service = NULL;
238
239                                 connection_read( fd, temp, 20 );
240                                 bytes = atoi( temp );
241                                 buffer = malloc( bytes + 1 );
242                                 while ( total < bytes )
243                                 {
244                                         int count = read( fd, buffer + total, bytes - total );
245                                         if ( count >= 0 )
246                                                 total += count;
247                                         else
248                                                 break;
249                                 }
250                                 buffer[ bytes ] = '\0';
251                                 if ( bytes > 0 && total == bytes )
252                                 {
253                                         if ( mlt_properties_get( owner, "push-parser-off" ) == 0 )
254                                         {
255                                                 service = ( mlt_service )mlt_factory_producer( NULL, "westley-xml", buffer );
256                                                 mlt_events_fire( owner, "push-received", &response, command, service, NULL );
257                                                 if ( response == NULL )
258                                                         response = valerie_parser_push( parser, command, service );
259                                         }
260                                         else
261                                         {
262                                                 response = valerie_parser_received( parser, command, buffer );
263                                         }
264                                 }
265                                 error = connection_send( fd, response );
266                                 valerie_response_close( response );
267                                 mlt_service_close( service );
268                                 free( buffer );
269                         }
270                         else if ( strncmp( command, "STATUS", 6 ) )
271                         {
272                                 mlt_events_fire( owner, "command-received", &response, command, NULL );
273                                 if ( response == NULL )
274                                         response = valerie_parser_execute( parser, command );
275                                 miracle_log( LOG_INFO, "%s \"%s\" %d", address, command, valerie_response_get_error_code( response ) );
276                                 error = connection_send( fd, response );
277                                 valerie_response_close( response );
278                         }
279                         else
280                         {
281                                 error = connection_status( fd, valerie_parser_get_notifier( parser ) );
282                         }
283                 }
284         }
285
286         /* Free the resources associated with this connection. */
287         connection_close( fd );
288
289         miracle_log( LOG_NOTICE, "Connection with %s (%d) closed", address, fd );
290
291         free( connection );
292
293         return NULL;
294 }