]> git.sesse.net Git - mlt/blob - mlt/src/miracle/miracle_connection.c
6c65e3517b07bb2504dcfbe67f82c0228eb19df9
[mlt] / mlt / src / miracle / miracle_connection.c
1 /*
2  * dvconnection.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 /* Application header files */
38 #include "global_commands.h"
39 #include "dvconnection.h"
40 #include "dvsocket.h"
41 #include "dvserver.h"
42 #include "log.h"
43
44 /** This is a generic replacement for fgets which operates on a file
45    descriptor. Unlike fgets, we can also specify a line terminator. Maximum
46    of (max - 1) chars can be read into buf from fd. If we reach the
47    end-of-file, *eof_chk is set to 1. 
48 */
49
50 int fdgetline( int fd, char *buf, int max, char line_terminator, int *eof_chk )
51 {
52         int count = 0;
53         char tmp [1];
54         *eof_chk = 0;
55         
56         if (fd)
57                 while (count < max - 1) {
58                         if (read (fd, tmp, 1) > 0) {
59                                 if (tmp [0] != line_terminator)
60                                         buf [count++] = tmp [0];
61                                 else
62                                         break;
63
64 /* Is it an EOF character (ctrl-D, i.e. ascii 4)? If so we definitely want
65    to break. */
66
67                                 if (tmp [0] == 4) {
68                                         *eof_chk = 1;
69                                         break;
70                                 }
71                         } else {
72                                 *eof_chk = 1;
73                                 break;
74                         }
75                 }
76                 
77         buf [count] = '\0';
78         
79         return count;
80 }
81
82 static int connection_initiate( int );
83 static int connection_send( int, dv_response );
84 static int connection_read( int, char *, int );
85 static void connection_close( int );
86
87 static int connection_initiate( int fd )
88 {
89         int error = 0;
90         dv_response response = dv_response_init( );
91         dv_response_set_error( response, 100, "VTR Ready" );
92         error = connection_send( fd, response );
93         dv_response_close( response );
94         return error;
95 }
96
97 static int connection_send( int fd, dv_response response )
98 {
99         int error = 0;
100         int index = 0;
101         int code = dv_response_get_error_code( response );
102
103         if ( code != -1 )
104         {
105                 int items = dv_response_count( response );
106
107                 if ( items == 0 )
108                         dv_response_set_error( response, 500, "Unknown error" );
109
110                 if ( code == 200 && items > 2 )
111                         dv_response_set_error( response, 201, "OK" );
112                 else if ( code == 200 && items > 1 )
113                         dv_response_set_error( response, 202, "OK" );
114
115                 code = dv_response_get_error_code( response );
116                 items = dv_response_count( response );
117
118                 for ( index = 0; !error && index < items; index ++ )
119                 {
120                         char *line = dv_response_get_line( response, index );
121                         int length = strlen( line );
122                         if ( length == 0 && index != dv_response_count( response ) - 1 && write( fd, " ", 1 ) != 1 )
123                                 error = -1;
124                         else if ( length > 0 && write( fd, line, length ) != length )
125                                 error = -1;
126                         if ( write( fd, "\r\n", 2 ) != 2 )
127                                 error = -1;                     
128                 }
129
130                 if ( ( code == 201 || code == 500 ) && strcmp( dv_response_get_line( response, items - 1 ), "" ) )
131                         write( fd, "\r\n", 2 );
132         }
133         else
134         {
135                 char *message = "500 Empty Response\r\n\r\n";
136                 write( fd, message, strlen( message ) );
137         }
138
139         return error;
140 }
141
142 static int connection_read( int fd, char *command, int length )
143 {
144         int eof_chk;
145         int nchars = fdgetline( fd, command, length, '\n', &eof_chk );
146         char *cr = strchr( command, '\r');
147         if ( cr != NULL ) 
148                 cr[0] = '\0';
149         if ( eof_chk || strncmp( command, "BYE", 3 ) == 0 ) 
150                 nchars = 0;
151         return nchars;
152 }
153
154 int connection_status( int fd, dv1394_notifier notifier )
155 {
156         int error = 0;
157         int index = 0;
158         dv1394_status_t status;
159         char text[ 10240 ];
160         dv_socket socket = dv_socket_init_fd( fd );
161         
162         for ( index = 0; !error && index < MAX_UNITS; index ++ )
163         {
164                 dv1394_notifier_get( notifier, &status, index );
165                 dv1394_status_serialise( &status, text, sizeof( text ) );
166                 error = dv_socket_write_data( socket, text, strlen( text )  ) != strlen( text );
167         }
168
169         while ( !error )
170         {
171                 if ( dv1394_notifier_wait( notifier, &status ) == 0 )
172                 {
173                         dv1394_status_serialise( &status, text, sizeof( text ) );
174                         error = dv_socket_write_data( socket, text, strlen( text ) ) != strlen( text );
175                 }
176                 else
177                 {
178                         struct timeval tv = { 0, 0 };
179                         fd_set rfds;
180
181                     FD_ZERO( &rfds );
182                     FD_SET( fd, &rfds );
183
184                         if ( select( socket->fd + 1, &rfds, NULL, NULL, &tv ) )
185                                 error = 1;
186                 }
187         }
188
189         dv_socket_close( socket );
190         
191         return error;
192 }
193
194 static void connection_close( int fd )
195 {
196         close( fd );
197 }
198
199 void *parser_thread( void *arg )
200 {
201         struct hostent *he;
202         connection_t *connection = arg;
203         char address[ 512 ];
204         char command[ 1024 ];
205         int fd = connection->fd;
206         dv_parser parser = connection->parser;
207         dv_response response = NULL;
208
209         /* We definitely want to ignore broken pipes. */
210     signal( SIGPIPE, SIG_IGN );
211
212         /* Get the connecting clients ip information */
213         he = gethostbyaddr( (char *) &( connection->sin.sin_addr.s_addr ), sizeof(u_int32_t), AF_INET); 
214         if ( he != NULL )
215                 strcpy( address, he->h_name );
216         else
217                 inet_ntop( AF_INET, &( connection->sin.sin_addr.s_addr), address, 32 );
218
219         dv1394d_log( LOG_NOTICE, "Connection established with %s (%d)", address, fd );
220
221         /* Execute the commands received. */
222         if ( connection_initiate( fd ) == 0 )
223         {
224                 int error = 0;
225
226                 while( !error && connection_read( fd, command, 1024 ) )
227                 {
228                         if ( strncmp( command, "STATUS", 6 ) )
229                         {
230                                 response = dv_parser_execute( parser, command );
231                                 dv1394d_log( LOG_INFO, "%s \"%s\" %d", address, command, dv_response_get_error_code( response ) );
232                                 error = connection_send( fd, response );
233                                 dv_response_close( response );
234                         }
235                         else
236                         {
237                                 error = connection_status( fd, dv_parser_get_notifier( parser ) );
238                         }
239                 }
240         }
241
242         /* Free the resources associated with this connection. */
243         connection_close( fd );
244
245         dv1394d_log( LOG_NOTICE, "Connection with %s (%d) closed", address, fd );
246
247         free( connection );
248
249         return NULL;
250 }