Fix invalid licenses
[platform/framework/web/crosswalk-tizen.git] / src / extension / extension_server.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 #include "extension/extension_server.h"
18
19 #include <glob.h>
20 #include <glib.h>
21 #include <glib-unix.h>
22
23 #include <string>
24 #include <vector>
25
26 #include "common/logger.h"
27 #include "common/constants.h"
28 #include "common/file_utils.h"
29 #include "common/string_utils.h"
30 #include "common/command_line.h"
31 #include "extension/extension.h"
32
33 namespace wrt {
34
35 namespace {
36
37 const char kExtensionPrefix[] = "lib";
38 const char kExtensionSuffix[] = ".so";
39
40 const char kDBusIntrospectionXML[] =
41   "<node>"
42   "  <interface name='org.tizen.wrt.Extension'>"
43   "    <method name='GetExtensions'>"
44   "      <arg name='extensions' type='a(ssas)' direction='out' />"
45   "    </method>"
46   "    <method name='CreateInstance'>"
47   "      <arg name='extension_name' type='s' direction='in' />"
48   "      <arg name='instance_id' type='s' direction='out' />"
49   "    </method>"
50   "    <method name='DestroyInstance'>"
51   "      <arg name='instance_id' type='s' direction='in' />"
52   "      <arg name='instance_id' type='s' direction='out' />"
53   "    </method>"
54   "    <method name='PostMessage'>"
55   "      <arg name='instance_id' type='s' direction='in' />"
56   "      <arg name='msg' type='s' direction='in' />"
57   "    </method>"
58   "    <method name='SendSyncMessage'>"
59   "      <arg name='instance_id' type='s' direction='in' />"
60   "      <arg name='msg' type='s' direction='in' />"
61   "      <arg name='reply' type='s' direction='out' />"
62   "    </method>"
63   "    <signal name='OnMessageToJS'>"
64   "      <arg name='instance_id' type='s' />"
65   "      <arg name='msg' type='s' />"
66   "    </signal>"
67   "  </interface>"
68   "</node>";
69
70 }  // namespace
71
72 ExtensionServer::ExtensionServer(const std::string& uuid)
73     : app_uuid_(uuid) {
74 }
75
76 ExtensionServer::~ExtensionServer() {
77 }
78
79 bool ExtensionServer::Start() {
80   return Start(StringVector());
81 }
82
83 bool ExtensionServer::Start(const StringVector& paths) {
84   // Connect to DBusServer for Application of Runtime
85   if (!dbus_application_client_.ConnectByName(
86           app_uuid_ + "." + std::string(kDBusNameForApplication))) {
87     LOGGER(ERROR) << "Failed to connect to the dbus server for Application.";
88     return false;
89   }
90
91   // Register system extensions to support Tizen Device APIs
92   RegisterSystemExtensions();
93
94   // Register user extensions
95   for (auto it = paths.begin(); it != paths.end(); ++it) {
96     if (utils::Exists(*it)) {
97       RegisterExtension(*it);
98     }
99   }
100
101   // Start DBusServer
102   using std::placeholders::_1;
103   using std::placeholders::_2;
104   using std::placeholders::_3;
105   using std::placeholders::_4;
106   dbus_server_.SetIntrospectionXML(kDBusIntrospectionXML);
107   dbus_server_.SetMethodCallback(
108       kDBusInterfaceNameForExtension,
109       std::bind(&ExtensionServer::HandleDBusMethod, this, _1, _2, _3, _4));
110   dbus_server_.Start(app_uuid_ + "." + std::string(kDBusNameForExtension));
111
112   // Send 'ready' signal to Injected Bundle.
113   NotifyEPCreatedToApplication();
114
115   return true;
116 }
117
118 void ExtensionServer::RegisterExtension(const std::string& path) {
119   Extension* ext = new Extension(path, this);
120   if (!ext->Initialize() || !RegisterSymbols(ext)) {
121     delete ext;
122     return;
123   }
124   extensions_[ext->name()] = ext;
125   LOGGER(DEBUG) << ext->name() << " is registered.";
126 }
127
128 void ExtensionServer::RegisterSystemExtensions() {
129 #ifdef EXTENSION_PATH
130   std::string extension_path(EXTENSION_PATH);
131 #else
132   #error EXTENSION_PATH is not set.
133 #endif
134   extension_path.append("/");
135   extension_path.append(kExtensionPrefix);
136   extension_path.append("*");
137   extension_path.append(kExtensionSuffix);
138
139   glob_t glob_result;
140   glob(extension_path.c_str(), GLOB_TILDE, NULL, &glob_result);
141   for (unsigned int i = 0; i < glob_result.gl_pathc; ++i) {
142     RegisterExtension(glob_result.gl_pathv[i]);
143   }
144 }
145
146 bool ExtensionServer::RegisterSymbols(Extension* extension) {
147   std::string name = extension->name();
148
149   if (extension_symbols_.find(name) != extension_symbols_.end()) {
150     LOGGER(WARN) << "Ignoring extension with name already registred. '"
151                  << name << "'";
152     return false;
153   }
154
155   Extension::StringVector entry_points = extension->entry_points();
156   for (auto it = entry_points.begin(); it != entry_points.end(); ++it) {
157     if (extension_symbols_.find(*it) != extension_symbols_.end()) {
158       LOGGER(WARN) << "Ignoring extension with entry_point already registred. '"
159                    << (*it) << "'";
160       return false;
161     }
162   }
163
164   for (auto it = entry_points.begin(); it != entry_points.end(); ++it) {
165     extension_symbols_.insert(*it);
166   }
167
168   extension_symbols_.insert(name);
169
170   return true;
171 }
172
173 void ExtensionServer::GetRuntimeVariable(const char* key, char* value,
174     size_t value_len) {
175   GVariant* ret = dbus_application_client_.Call(
176       kDBusInterfaceNameForApplication, kMethodGetRuntimeVariable,
177       g_variant_new("(s)", key), G_VARIANT_TYPE("(s)"));
178
179   if (!ret) {
180     LOGGER(ERROR) << "Failed to get runtime variable from Application. ("
181                   << key << ")";
182     return;
183   }
184
185   gchar* v;
186   g_variant_get(ret, "(&s)", &v);
187   strncpy(value, v, value_len);
188
189   g_variant_unref(ret);
190 }
191
192 void ExtensionServer::NotifyEPCreatedToApplication() {
193   dbus_application_client_.Call(
194       kDBusInterfaceNameForApplication, kMethodNotifyEPCreated,
195       g_variant_new("(s)", dbus_server_.GetClientAddress().c_str()),
196       NULL);
197 }
198
199 void ExtensionServer::HandleDBusMethod(GDBusConnection* connection,
200                                        const std::string& method_name,
201                                        GVariant* parameters,
202                                        GDBusMethodInvocation* invocation) {
203   if (method_name == kMethodGetExtensions) {
204     OnGetExtensions(invocation);
205   } else if (method_name == kMethodCreateInstance) {
206     gchar* extension_name;
207     g_variant_get(parameters, "(&s)", &extension_name);
208     OnCreateInstance(connection, extension_name, invocation);
209   } else if (method_name == kMethodDestroyInstance) {
210     gchar* instance_id;
211     g_variant_get(parameters, "(&s)", &instance_id);
212     OnDestroyInstance(instance_id, invocation);
213   } else if (method_name == kMethodSendSyncMessage) {
214     gchar* instance_id;
215     gchar* msg;
216     g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
217     OnSendSyncMessage(instance_id, msg, invocation);
218   } else if (method_name == kMethodPostMessage) {
219     gchar* instance_id;
220     gchar* msg;
221     g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
222     OnPostMessage(instance_id, msg);
223   }
224 }
225
226 void ExtensionServer::OnGetExtensions(GDBusMethodInvocation* invocation) {
227   GVariantBuilder builder;
228
229   g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
230
231   // build an array of extensions
232   auto it = extensions_.begin();
233   for ( ; it != extensions_.end(); ++it) {
234     Extension* ext = it->second;
235     // open container for extension
236     g_variant_builder_open(&builder, G_VARIANT_TYPE("(ssas)"));
237     g_variant_builder_add(&builder, "s", ext->name().c_str());
238     g_variant_builder_add(&builder, "s", ext->javascript_api().c_str());
239     // open container for entry_point
240     g_variant_builder_open(&builder, G_VARIANT_TYPE("as"));
241     auto it_entry = ext->entry_points().begin();
242     for ( ; it_entry != ext->entry_points().end(); ++it_entry) {
243       g_variant_builder_add(&builder, "s", (*it_entry).c_str());
244     }
245     // close container('as') for entry_point
246     g_variant_builder_close(&builder);
247     // close container('(ssas)') for extension
248     g_variant_builder_close(&builder);
249   }
250
251   GVariant* reply = NULL;
252   if (extensions_.size() == 0) {
253     reply = g_variant_new_array(G_VARIANT_TYPE("(ssas)"), NULL, 0);
254   } else {
255     reply = g_variant_builder_end(&builder);
256   }
257
258   g_dbus_method_invocation_return_value(
259       invocation, g_variant_new_tuple(&reply, 1));
260 }
261
262 void ExtensionServer::OnCreateInstance(
263     GDBusConnection* connection, const std::string& extension_name,
264     GDBusMethodInvocation* invocation) {
265   std::string instance_id = utils::GenerateUUID();
266
267   // find extension with given the extension name
268   auto it = extensions_.find(extension_name);
269   if (it == extensions_.end()) {
270     LOGGER(ERROR) << "Failed to find extension '" << extension_name << "'";
271     g_dbus_method_invocation_return_error(
272         invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
273         "Not found extension %s", extension_name.c_str());
274     return;
275   }
276
277   // create instance
278   ExtensionInstance* instance = it->second->CreateInstance();
279   if (!instance) {
280     LOGGER(ERROR) << "Failed to create instance of extension '"
281                   << extension_name << "'";
282     g_dbus_method_invocation_return_error(
283         invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
284         "Failed to create instance of extension %s", extension_name.c_str());
285     return;
286   }
287
288   // set callbacks
289   using std::placeholders::_1;
290   instance->SetPostMessageCallback(
291       std::bind(&ExtensionServer::PostMessageToJSCallback,
292                 this, connection, instance_id, _1));
293
294   instances_[instance_id] = instance;
295   g_dbus_method_invocation_return_value(
296       invocation, g_variant_new("(s)", instance_id.c_str()));
297 }
298
299 void ExtensionServer::OnDestroyInstance(
300     const std::string& instance_id, GDBusMethodInvocation* invocation) {
301   // find instance with the given instance id
302   auto it = instances_.find(instance_id);
303   if (it == instances_.end()) {
304     LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
305     g_dbus_method_invocation_return_error(
306         invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
307         "Not found instance %s", instance_id.c_str());
308     return;
309   }
310
311   // destroy the instance
312   ExtensionInstance* instance = it->second;
313   delete instance;
314
315   instances_.erase(it);
316
317   g_dbus_method_invocation_return_value(
318       invocation, g_variant_new("(s)", instance_id.c_str()));
319 }
320
321 void ExtensionServer::OnSendSyncMessage(
322     const std::string& instance_id, const std::string& msg,
323     GDBusMethodInvocation* invocation) {
324   // find instance with the given instance id
325   auto it = instances_.find(instance_id);
326   if (it == instances_.end()) {
327     LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
328     g_dbus_method_invocation_return_error(
329         invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
330         "Not found instance %s", instance_id.c_str());
331     return;
332   }
333
334   ExtensionInstance* instance = it->second;
335
336   using std::placeholders::_1;
337   instance->SetSendSyncReplyCallback(
338       std::bind(&ExtensionServer::SyncReplyCallback, this, _1, invocation));
339
340   instance->HandleSyncMessage(msg);
341
342   // reponse will be sent by SyncReplyCallback()
343 }
344
345 // async
346 void ExtensionServer::OnPostMessage(
347     const std::string& instance_id, const std::string& msg) {
348   auto it = instances_.find(instance_id);
349   if (it == instances_.end()) {
350     LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
351     return;
352   }
353
354   ExtensionInstance* instance = it->second;
355   instance->HandleMessage(msg);
356 }
357
358 void ExtensionServer::SyncReplyCallback(
359     const std::string& reply, GDBusMethodInvocation* invocation) {
360   g_dbus_method_invocation_return_value(
361       invocation, g_variant_new("(s)", reply.c_str()));
362 }
363
364 void ExtensionServer::PostMessageToJSCallback(
365     GDBusConnection* connection, const std::string& instance_id,
366     const std::string& msg) {
367   if (!connection || g_dbus_connection_is_closed(connection)) {
368     LOGGER(ERROR) << "Client connection is closed already.";
369     return;
370   }
371
372   dbus_server_.SendSignal(connection,
373                           kDBusInterfaceNameForExtension,
374                           kSignalOnMessageToJS,
375                           g_variant_new("(ss)",
376                                         instance_id.c_str(),
377                                         msg.c_str()));
378 }
379
380 // static
381 bool ExtensionServer::StartExtensionProcess() {
382   GMainLoop* loop;
383
384   loop = g_main_loop_new(NULL, FALSE);
385
386   // Register Quit Signal Handlers
387   auto quit_callback = [](gpointer data) -> gboolean {
388     GMainLoop* loop = reinterpret_cast<GMainLoop*>(data);
389     g_main_loop_quit(loop);
390     return false;
391   };
392   g_unix_signal_add(SIGINT, quit_callback, loop);
393   g_unix_signal_add(SIGTERM, quit_callback, loop);
394
395   CommandLine* cmd = CommandLine::ForCurrentProcess();
396
397   // TODO(wy80.choi): Receive extension paths for user defined extensions.
398
399   // Receive AppID from arguments.
400   if (cmd->arguments().size() < 1) {
401     LOGGER(ERROR) << "uuid is required.";
402     return false;
403   }
404   std::string uuid = cmd->arguments()[0];
405
406   // Start ExtensionServer
407   ExtensionServer server(uuid);
408   if (!server.Start()) {
409     LOGGER(ERROR) << "Failed to start extension server.";
410     return false;
411   }
412
413   LOGGER(INFO) << "extension process has been started.";
414
415   g_main_loop_run(loop);
416
417   LOGGER(INFO) << "extension process is exiting.";
418
419   g_main_loop_unref(loop);
420
421   return true;
422 }
423
424 }  // namespace wrt