1 /*
2  * Carla REST API Server
3  * Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * For a full copy of the GNU General Public License see the doc/GPL.txt file.
16  */
17 
18 /* NOTE
19  * Even though Carla is GPL, restbed if AGPL.
20  * As such, the resulting binary will be AGPL.
21  * Take this into consideration before deploying it to any servers.
22  */
23 
24 #include "common.hpp"
25 
26 #include "carla-host.cpp"
27 #include "carla-utils.cpp"
28 
29 #include "CarlaMutex.hpp"
30 #include "CarlaStringList.hpp"
31 
32 // -------------------------------------------------------------------------------------------------------------------
33 
34 #include <map>
35 #include <restbed>
36 #include <system_error>
37 #include <openssl/sha.h>
38 #include <openssl/hmac.h>
39 #include <openssl/evp.h>
40 #include <openssl/bio.h>
41 #include <openssl/buffer.h>
42 
43 using namespace std;
44 using namespace restbed;
45 using namespace std::chrono;
46 
47 // std::vector<std::shared_ptr<Session>> gSessions;
48 
49 CarlaStringList gSessionMessages;
50 CarlaMutex gSessionMessagesMutex;
51 
52 std::map< string, shared_ptr< WebSocket > > sockets = { };
53 
54 // -------------------------------------------------------------------------------------------------------------------
55 
send_server_side_message(const char * const message)56 void send_server_side_message(const char* const message)
57 {
58     const CarlaMutexLocker cml(gSessionMessagesMutex);
59 
60     gSessionMessages.append(message);
61 }
62 
63 // -------------------------------------------------------------------------------------------------------------------
64 
event_stream_handler(void)65 static void event_stream_handler(void)
66 {
67     static bool firstInit = true;
68 
69     if (firstInit)
70     {
71         firstInit = false;
72         carla_stdout("Carla REST-API Server started");
73     }
74 
75     const bool running = carla_is_engine_running();
76 
77     if (running)
78         carla_engine_idle();
79 
80     CarlaStringList messages;
81 
82     {
83         const CarlaMutexLocker cml(gSessionMessagesMutex);
84 
85         if (gSessionMessages.count() > 0)
86             gSessionMessages.moveTo(messages);
87     }
88 
89     for (auto message : messages)
90     {
91         for (auto entry : sockets)
92         {
93             auto socket = entry.second;
94 
95             if (socket->is_open())
96                 socket->send(message);
97         }
98     }
99 
100     if (running)
101     {
102         if (const uint count = carla_get_current_plugin_count())
103         {
104             char msgBuf[1024];
105             float* peaks;
106 
107             for (uint i=0; i<count; ++i)
108             {
109                 peaks = carla_get_peak_values(i);
110                 CARLA_SAFE_ASSERT_BREAK(peaks != nullptr);
111 
112                 std::snprintf(msgBuf, 1023, "Peaks: %u %f %f %f %f", i, peaks[0], peaks[1], peaks[2], peaks[3]);
113                 msgBuf[1023] = '\0';
114 
115                 for (auto entry : sockets)
116                 {
117                     auto socket = entry.second;
118 
119                     if (socket->is_open())
120                         socket->send(msgBuf);
121                 }
122             }
123         }
124     }
125 
126     for (auto entry : sockets)
127     {
128         auto socket = entry.second;
129 
130         if (socket->is_open())
131             socket->send("Keep-Alive");
132     }
133 }
134 
135 
136 // -------------------------------------------------------------------------------------------------------------------
137 
base64_encode(const unsigned char * input,int length)138 string base64_encode( const unsigned char* input, int length )
139 {
140     BIO* bmem, *b64;
141     BUF_MEM* bptr;
142 
143     b64 = BIO_new( BIO_f_base64( ) );
144     bmem = BIO_new( BIO_s_mem( ) );
145     b64 = BIO_push( b64, bmem );
146     BIO_write( b64, input, length );
147     ( void ) BIO_flush( b64 );
148     BIO_get_mem_ptr( b64, &bptr );
149 
150     char* buff = ( char* )malloc( bptr->length );
151     memcpy( buff, bptr->data, bptr->length - 1 );
152     buff[ bptr->length - 1 ] = 0;
153 
154     BIO_free_all( b64 );
155 
156     return buff;
157 }
158 
build_websocket_handshake_response_headers(const shared_ptr<const Request> & request)159 multimap< string, string > build_websocket_handshake_response_headers( const shared_ptr< const Request >& request )
160 {
161     auto key = request->get_header( "Sec-WebSocket-Key" );
162     key.append( "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" );
163 
164     Byte hash[ SHA_DIGEST_LENGTH ];
165     SHA1( reinterpret_cast< const unsigned char* >( key.data( ) ), key.length( ), hash );
166 
167     multimap< string, string > headers;
168     headers.insert( make_pair( "Upgrade", "websocket" ) );
169     headers.insert( make_pair( "Connection", "Upgrade" ) );
170     headers.insert( make_pair( "Sec-WebSocket-Accept", base64_encode( hash, SHA_DIGEST_LENGTH ) ) );
171 
172     return headers;
173 }
174 
close_handler(const shared_ptr<WebSocket> socket)175 void close_handler( const shared_ptr< WebSocket > socket )
176 {
177     carla_stdout("CLOSE %i", __LINE__);
178 
179     if ( socket->is_open( ) )
180     {
181         auto response = make_shared< WebSocketMessage >( WebSocketMessage::CONNECTION_CLOSE_FRAME, Bytes( { 10, 00 } ) );
182         socket->send( response );
183     }
184     carla_stdout("CLOSE %i", __LINE__);
185 
186     const auto key = socket->get_key( );
187     sockets.erase( key );
188 
189     fprintf( stderr, "Closed connection to %s.\n", key.data( ) );
190 }
191 
error_handler(const shared_ptr<WebSocket> socket,const error_code error)192 void error_handler( const shared_ptr< WebSocket > socket, const error_code error )
193 {
194     const auto key = socket->get_key( );
195     fprintf( stderr, "WebSocket Errored '%s' for %s.\n", error.message( ).data( ), key.data( ) );
196 }
197 
message_handler(const shared_ptr<WebSocket> source,const shared_ptr<WebSocketMessage> message)198 void message_handler( const shared_ptr< WebSocket > source, const shared_ptr< WebSocketMessage > message )
199 {
200     const auto opcode = message->get_opcode( );
201 
202     if ( opcode == WebSocketMessage::PING_FRAME )
203     {
204         auto response = make_shared< WebSocketMessage >( WebSocketMessage::PONG_FRAME, message->get_data( ) );
205         source->send( response );
206     }
207     else if ( opcode == WebSocketMessage::PONG_FRAME )
208     {
209         //Ignore PONG_FRAME.
210         //
211         //Every time the ping_handler is scheduled to run, it fires off a PING_FRAME to each
212         //WebSocket. The client, if behaving correctly, will respond with a PONG_FRAME.
213         //
214         //On each occasion the underlying TCP socket sees any packet data transfer, whether
215         //a PING, PONG, TEXT, or BINARY... frame. It will automatically reset the timeout counter
216         //leaving the connection active; see also Settings::set_connection_timeout.
217         return;
218     }
219     else if ( opcode == WebSocketMessage::CONNECTION_CLOSE_FRAME )
220     {
221         source->close( );
222     }
223     else if ( opcode == WebSocketMessage::BINARY_FRAME )
224     {
225         //We don't support binary data.
226         auto response = make_shared< WebSocketMessage >( WebSocketMessage::CONNECTION_CLOSE_FRAME, Bytes( { 10, 03 } ) );
227         source->send( response );
228     }
229     else if ( opcode == WebSocketMessage::TEXT_FRAME )
230     {
231         auto response = make_shared< WebSocketMessage >( *message );
232         response->set_mask( 0 );
233 
234         for ( auto socket : sockets )
235         {
236             auto destination = socket.second;
237             destination->send( response );
238         }
239 
240         const auto key = source->get_key( );
241         const auto data = String::format( "Received message '%.*s' from %s\n", message->get_data( ).size( ), message->get_data( ).data( ), key.data( ) );
242         fprintf( stderr, "%s", data.data( ) );
243     }
244 }
245 
get_method_handler(const shared_ptr<Session> session)246 void get_method_handler(const shared_ptr<Session> session)
247 {
248     carla_stdout("HERE %i", __LINE__);
249     const auto request = session->get_request();
250     const auto connection_header = request->get_header("connection", String::lowercase);
251     carla_stdout("HERE %i", __LINE__);
252 
253     if ( connection_header.find( "upgrade" ) not_eq string::npos )
254     {
255         if ( request->get_header( "upgrade", String::lowercase ) == "websocket" )
256         {
257             const auto headers = build_websocket_handshake_response_headers( request );
258 
259             session->upgrade( SWITCHING_PROTOCOLS, headers, [ ]( const shared_ptr< WebSocket > socket )
260             {
261                 if ( socket->is_open( ) )
262                 {
263                     socket->set_close_handler( close_handler );
264                     socket->set_error_handler( error_handler );
265                     socket->set_message_handler( message_handler );
266 
267                     socket->send("Welcome to Corvusoft Chat!");
268 
269                     auto key = socket->get_key( );
270                     sockets[key] = socket;
271                 }
272                 else
273                 {
274                     fprintf( stderr, "WebSocket Negotiation Failed: Client closed connection.\n" );
275                 }
276             } );
277 
278             return;
279         }
280     }
281 
282     session->close( BAD_REQUEST );
283 }
284 
ping_handler(void)285 void ping_handler( void )
286 {
287     for ( auto entry : sockets )
288     {
289         auto key = entry.first;
290         auto socket = entry.second;
291 
292         if ( socket->is_open( ) )
293         {
294             socket->send( WebSocketMessage::PING_FRAME );
295         }
296         else
297         {
298             socket->close( );
299         }
300     }
301 }
302 
303 // -------------------------------------------------------------------------------------------------------------------
304 
make_resource(Service & service,const char * const path,const std::function<void (const std::shared_ptr<Session>)> & callback)305 static void make_resource(Service& service,
306                    const char* const path,
307                    const std::function<void (const std::shared_ptr<Session>)>& callback)
308 {
309     std::shared_ptr<Resource> resource = std::make_shared<Resource>();
310     resource->set_path(path);
311     resource->set_method_handler("GET", callback);
312     service.publish(resource);
313 }
314 
315 // -------------------------------------------------------------------------------------------------------------------
316 
main(int,const char **)317 int main(int, const char**)
318 {
319     Service service;
320 
321     // websocket
322     {
323         std::shared_ptr<Resource> resource = std::make_shared<Resource>();
324         resource->set_path("/ws");
325         resource->set_method_handler("GET", get_method_handler);
326         service.publish(resource);
327     }
328 
329     // carla-host
330     make_resource(service, "/get_engine_driver_count", handle_carla_get_engine_driver_count);
331     make_resource(service, "/get_engine_driver_name", handle_carla_get_engine_driver_name);
332     make_resource(service, "/get_engine_driver_device_names", handle_carla_get_engine_driver_device_names);
333     make_resource(service, "/get_engine_driver_device_info", handle_carla_get_engine_driver_device_info);
334 
335     make_resource(service, "/engine_init", handle_carla_engine_init);
336     make_resource(service, "/engine_close", handle_carla_engine_close);
337     make_resource(service, "/is_engine_running", handle_carla_is_engine_running);
338     make_resource(service, "/set_engine_about_to_close", handle_carla_set_engine_about_to_close);
339 
340     make_resource(service, "/set_engine_option", handle_carla_set_engine_option);
341     make_resource(service, "/load_file", handle_carla_load_file);
342     make_resource(service, "/load_project", handle_carla_load_project);
343     make_resource(service, "/save_project", handle_carla_save_project);
344 
345     make_resource(service, "/patchbay_connect", handle_carla_patchbay_connect);
346     make_resource(service, "/patchbay_disconnect", handle_carla_patchbay_disconnect);
347     make_resource(service, "/patchbay_refresh", handle_carla_patchbay_refresh);
348 
349     make_resource(service, "/transport_play", handle_carla_transport_play);
350     make_resource(service, "/transport_pause", handle_carla_transport_pause);
351     make_resource(service, "/transport_bpm", handle_carla_transport_bpm);
352     make_resource(service, "/transport_relocate", handle_carla_transport_relocate);
353     make_resource(service, "/get_current_transport_frame", handle_carla_get_current_transport_frame);
354     make_resource(service, "/get_transport_info", handle_carla_get_transport_info);
355 
356     make_resource(service, "/get_current_plugin_count", handle_carla_get_current_plugin_count);
357     make_resource(service, "/get_max_plugin_number", handle_carla_get_max_plugin_number);
358     make_resource(service, "/add_plugin", handle_carla_add_plugin);
359     make_resource(service, "/remove_plugin", handle_carla_remove_plugin);
360     make_resource(service, "/remove_all_plugins", handle_carla_remove_all_plugins);
361 
362     make_resource(service, "/rename_plugin", handle_carla_rename_plugin);
363     make_resource(service, "/clone_plugin", handle_carla_clone_plugin);
364     make_resource(service, "/replace_plugin", handle_carla_replace_plugin);
365     make_resource(service, "/switch_plugins", handle_carla_switch_plugins);
366 
367     make_resource(service, "/load_plugin_state", handle_carla_load_plugin_state);
368     make_resource(service, "/save_plugin_state", handle_carla_save_plugin_state);
369     make_resource(service, "/export_plugin_lv2", handle_carla_export_plugin_lv2);
370 
371     make_resource(service, "/get_plugin_info", handle_carla_get_plugin_info);
372     make_resource(service, "/get_audio_port_count_info", handle_carla_get_audio_port_count_info);
373     make_resource(service, "/get_midi_port_count_info", handle_carla_get_midi_port_count_info);
374     make_resource(service, "/get_parameter_count_info", handle_carla_get_parameter_count_info);
375     make_resource(service, "/get_parameter_info", handle_carla_get_parameter_info);
376     make_resource(service, "/get_parameter_scalepoint_info", handle_carla_get_parameter_scalepoint_info);
377 
378     make_resource(service, "/get_parameter_data", handle_carla_get_parameter_data);
379     make_resource(service, "/get_parameter_ranges", handle_carla_get_parameter_ranges);
380     make_resource(service, "/get_midi_program_data", handle_carla_get_midi_program_data);
381     make_resource(service, "/get_custom_data", handle_carla_get_custom_data);
382     make_resource(service, "/get_custom_data_value", handle_carla_get_custom_data_value);
383     make_resource(service, "/get_chunk_data", handle_carla_get_chunk_data);
384 
385     make_resource(service, "/get_parameter_count", handle_carla_get_parameter_count);
386     make_resource(service, "/get_program_count", handle_carla_get_program_count);
387     make_resource(service, "/get_midi_program_count", handle_carla_get_midi_program_count);
388     make_resource(service, "/get_custom_data_count", handle_carla_get_custom_data_count);
389 
390     make_resource(service, "/get_parameter_text", handle_carla_get_parameter_text);
391     make_resource(service, "/get_program_name", handle_carla_get_program_name);
392     make_resource(service, "/get_midi_program_name", handle_carla_get_midi_program_name);
393     make_resource(service, "/get_real_plugin_name", handle_carla_get_real_plugin_name);
394 
395     make_resource(service, "/get_current_program_index", handle_carla_get_current_program_index);
396     make_resource(service, "/get_current_midi_program_index", handle_carla_get_current_midi_program_index);
397 
398     make_resource(service, "/get_default_parameter_value", handle_carla_get_default_parameter_value);
399     make_resource(service, "/get_current_parameter_value", handle_carla_get_current_parameter_value);
400     make_resource(service, "/get_internal_parameter_value", handle_carla_get_internal_parameter_value);
401     make_resource(service, "/get_input_peak_value", handle_carla_get_input_peak_value);
402     make_resource(service, "/get_output_peak_value", handle_carla_get_output_peak_value);
403 
404     make_resource(service, "/set_active", handle_carla_set_active);
405     make_resource(service, "/set_drywet", handle_carla_set_drywet);
406     make_resource(service, "/set_volume", handle_carla_set_volume);
407     make_resource(service, "/set_balance_left", handle_carla_set_balance_left);
408     make_resource(service, "/set_balance_right", handle_carla_set_balance_right);
409     make_resource(service, "/set_panning", handle_carla_set_panning);
410     make_resource(service, "/set_ctrl_channel", handle_carla_set_ctrl_channel);
411     make_resource(service, "/set_option", handle_carla_set_option);
412 
413     make_resource(service, "/set_parameter_value", handle_carla_set_parameter_value);
414     make_resource(service, "/set_parameter_midi_channel", handle_carla_set_parameter_midi_channel);
415     make_resource(service, "/set_parameter_midi_cc", handle_carla_set_parameter_midi_cc);
416     make_resource(service, "/set_program", handle_carla_set_program);
417     make_resource(service, "/set_midi_program", handle_carla_set_midi_program);
418     make_resource(service, "/set_custom_data", handle_carla_set_custom_data);
419     make_resource(service, "/set_chunk_data", handle_carla_set_chunk_data);
420 
421     make_resource(service, "/prepare_for_save", handle_carla_prepare_for_save);
422     make_resource(service, "/reset_parameters", handle_carla_reset_parameters);
423     make_resource(service, "/randomize_parameters", handle_carla_randomize_parameters);
424     make_resource(service, "/send_midi_note", handle_carla_send_midi_note);
425 
426     make_resource(service, "/get_buffer_size", handle_carla_get_buffer_size);
427     make_resource(service, "/get_sample_rate", handle_carla_get_sample_rate);
428     make_resource(service, "/get_last_error", handle_carla_get_last_error);
429     make_resource(service, "/get_host_osc_url_tcp", handle_carla_get_host_osc_url_tcp);
430     make_resource(service, "/get_host_osc_url_udp", handle_carla_get_host_osc_url_udp);
431 
432     // carla-utils
433     make_resource(service, "/get_complete_license_text", handle_carla_get_complete_license_text);
434     make_resource(service, "/get_supported_file_extensions", handle_carla_get_supported_file_extensions);
435     make_resource(service, "/get_supported_features", handle_carla_get_supported_features);
436     make_resource(service, "/get_cached_plugin_count", handle_carla_get_cached_plugin_count);
437     make_resource(service, "/get_cached_plugin_info", handle_carla_get_cached_plugin_info);
438 
439     // schedule events
440     service.schedule(event_stream_handler, std::chrono::milliseconds(33));
441     service.schedule(ping_handler, milliseconds(5000));
442 
443     std::shared_ptr<Settings> settings = std::make_shared<Settings>();
444     settings->set_port(2228);
445     settings->set_default_header("Connection", "close");
446 
447     service.start(settings);
448     return 0;
449 }
450 
451 // -------------------------------------------------------------------------------------------------------------------
452