Release version 0.5.85
[platform/core/api/notification.git] / notification-ex / dbus_event_listener.cc
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd.
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 <dlog.h>
18 #include <glib.h>
19 #include <unistd.h>
20
21 #include <string>
22 #include <map>
23 #include <list>
24
25 #include "notification-ex/dbus_connection_manager.h"
26 #include "notification-ex/dbus_event_listener.h"
27 #include "notification-ex/dbus_event_listener_implementation.h"
28 #include "notification-ex/exception.h"
29 #include "notification-ex/ex_util.h"
30 #include "notification-ex/event_info_internal.h"
31
32 #ifdef LOG_TAG
33 #undef LOG_TAG
34 #endif
35
36 #define LOG_TAG "NOTIFICATION_EX"
37 #define MAX_PACKAGE_STR_SIZE 512
38 #define NORMAL_UID_BASE 5000
39 #define MAX_CACHE_COUNT 20
40
41 #define DBUS_NAME "org.freedesktop.DBus"
42 #define DBUS_OBJECT_PATH "/org/freedesktop/DBus"
43 #define DBUS_INTERFACE_NAME "org.freedesktop.DBus"
44
45 using namespace std;
46 using namespace tizen_base;
47
48 namespace notification {
49
50 DBusEventListener::DBusEventListener(string path)
51     : impl_(new Impl(this, path)) {
52   LOGW("Created (%s)", path.c_str());
53 }
54
55 DBusEventListener::~DBusEventListener() {
56   LOGW("Destroyed");
57 }
58
59 DBusEventListener::Impl::~Impl() = default;
60
61 DBusEventListener::Impl::Impl(DBusEventListener* parent, string path)
62     : subscribe_id_(0), registration_id_(0), path_(path), parent_(parent) {
63   LOGI("Dbus_event_listener impl created");
64 }
65
66 uid_t DBusEventListener::Impl::GetSenderUid(GDBusConnection* connection,
67                 const char* sender_name) {
68   static std::map<std::string, uid_t> cache;
69   auto cp = cache.find(sender_name);
70   if (cp != cache.end())
71     return cp->second;
72
73   GDBusMessage* msg = nullptr;
74   GDBusMessage* reply = nullptr;
75   GError* err = nullptr;
76   GVariant* body;
77   uid_t uid = 0;
78
79   msg = g_dbus_message_new_method_call(DBUS_NAME, DBUS_OBJECT_PATH,
80       DBUS_INTERFACE_NAME, "GetConnectionUnixUser");
81   if (!msg) {
82     LOGE("Failed to alloc new method call");
83     goto out;
84   }
85
86   g_dbus_message_set_body(msg, g_variant_new("(s)", sender_name));
87   reply = g_dbus_connection_send_message_with_reply_sync(connection, msg,
88       G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, nullptr, nullptr, &err);
89
90   if (!reply) {
91     if (err != nullptr) {
92       LOGE("Failed to get uid [%s]", err->message);
93       g_error_free(err);
94     }
95     goto out;
96   }
97
98   body = g_dbus_message_get_body(reply);
99   g_variant_get(body, "(u)", &uid);
100
101   if (cache.size() >= MAX_CACHE_COUNT)
102     cache.clear();
103
104   cache[sender_name] = uid;
105
106 out:
107   if (msg)
108     g_object_unref(msg);
109   if (reply)
110     g_object_unref(reply);
111
112   return uid;
113 }
114
115 pid_t DBusEventListener::Impl::GetSenderPid(GDBusConnection* connection,
116                 const char* sender_name) {
117   static std::map<std::string, pid_t> cache;
118   auto cp = cache.find(sender_name);
119   if (cp != cache.end())
120     return cp->second;
121
122   GDBusMessage* msg = nullptr;
123   GDBusMessage* reply = nullptr;
124   GError* err = nullptr;
125   GVariant* body;
126   pid_t pid = 0;
127
128   msg = g_dbus_message_new_method_call(DBUS_NAME, DBUS_OBJECT_PATH,
129       DBUS_INTERFACE_NAME, "GetConnectionUnixProcessID");
130   if (!msg) {
131     LOGE("Failed to alloc new method call");
132     goto out;
133   }
134
135   g_dbus_message_set_body(msg, g_variant_new("(s)", sender_name));
136   reply = g_dbus_connection_send_message_with_reply_sync(connection, msg,
137       G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, nullptr, nullptr, &err);
138
139   if (!reply) {
140     if (err != nullptr) {
141       LOGE("Failed to get uid [%s]", err->message);
142       g_error_free(err);
143     }
144     goto out;
145   }
146
147   body = g_dbus_message_get_body(reply);
148   g_variant_get(body, "(u)", &pid);
149
150   if (cache.size() >= MAX_CACHE_COUNT)
151     cache.clear();
152
153   cache[sender_name] = pid;
154
155 out:
156   if (msg)
157     g_object_unref(msg);
158   if (reply)
159     g_object_unref(reply);
160
161   return pid;
162 }
163
164 void DBusEventListener::Impl::SignalCb(GDBusConnection* connection,
165                       const gchar* sender_name,
166                       const gchar* object_path,
167                       const gchar* interface_name,
168                       const gchar* signal_name,
169                       GVariant* parameters,
170                       void* user_data) {
171   DBusEventListener::Impl* dl = static_cast<DBusEventListener::Impl*>(user_data);
172   char* appid = nullptr;
173   GVariantIter *iter = nullptr;
174   char* event_info_raw = nullptr;
175   g_variant_get(parameters, "(&s&sa(s))", &appid, &event_info_raw, &iter);
176
177   LOGI("signal callback!! (%s)", appid);
178   string sender_appid = string(appid);
179   string cur_appid = util::GetAppId();
180   if (sender_appid == cur_appid) {
181     g_variant_iter_free(iter);
182     return;
183   }
184   if ((!DBusConnectionManager::GetInst().IsDataProviderMaster(cur_appid)
185     && !DBusConnectionManager::GetInst().IsDataProviderMaster(sender_appid))
186     || (cur_appid == sender_appid)) {
187     g_variant_iter_free(iter);
188     return;
189   }
190
191   LOGD("%s : %s", cur_appid.c_str(), sender_appid.c_str());
192
193   try {
194     char* raw = nullptr;
195     list<Bundle> ret_list;
196     while (g_variant_iter_loop(iter, "(&s)", &raw) && raw != nullptr) {
197       Bundle ret(raw);
198       ret_list.emplace_back(ret);
199     }
200
201     g_variant_iter_free(iter);
202     Bundle b(event_info_raw);
203     EventInfo info(b);
204     if (info.GetEventType() == EventInfo::Post
205            || info.GetEventType() == EventInfo::Update) {
206       uid_t uid = GetSenderUid(connection, sender_name);
207       if (uid >= NORMAL_UID_BASE) {
208         pid_t pid = GetSenderPid(connection, sender_name);
209         info.SetValidatedOwner(util::GetAppId(pid));
210         info.SetValidatedUid(uid);
211       }
212     }
213     dl->parent_->NotifyObserver(info, ret_list);
214   } catch(Exception &ex) {
215     LOGE("%s %d", ex.what(), ex.GetErrorCode());
216   }
217 }
218
219 void DBusEventListener::Impl::MethodCallHandler(GVariant* parameters,
220             DBusEventListener::Impl* dl, pid_t pid, uid_t uid) {
221   char* appid = nullptr;
222   GVariantIter* iter = nullptr;
223   char* event_info_raw = nullptr;
224
225   g_variant_get(parameters, "(&s&sa(s))", &appid, &event_info_raw, &iter);
226
227   string sender_appid = string(appid);
228   string cur_appid = util::GetAppId();
229   LOGI("MethodCallHandler!! appid(%s), sender(%s), cur(%s)", appid,
230     sender_appid.c_str(), cur_appid.c_str());
231
232   if (sender_appid == cur_appid) {
233     g_variant_iter_free(iter);
234     return;
235   }
236
237   if ((!DBusConnectionManager::GetInst().IsDataProviderMaster(cur_appid)
238     && !DBusConnectionManager::GetInst().IsDataProviderMaster(sender_appid))
239     || (cur_appid == sender_appid)) {
240     g_variant_iter_free(iter);
241     return;
242   }
243
244   char* raw = nullptr;
245   list<Bundle> ret_list;
246   while (g_variant_iter_loop(iter, "(&s)", &raw) && raw != nullptr) {
247     Bundle ret(raw);
248     ret_list.emplace_back(ret);
249   }
250
251   g_variant_iter_free(iter);
252   Bundle b(event_info_raw);
253   EventInfo info(b);
254
255   if (info.GetEventType() == EventInfo::Post
256         || info.GetEventType() == EventInfo::Update) {
257     if (uid >= NORMAL_UID_BASE) {
258       info.SetValidatedOwner(util::GetAppId(pid));
259       info.SetValidatedUid(uid);
260     }
261   }
262
263   dl->parent_->NotifyObserver(info, ret_list);
264 }
265
266 void DBusEventListener::Impl::OnMethodCall(
267     GDBusConnection* conn, const gchar* sender, const gchar* object_path,
268     const gchar* iface_name, const gchar* method_name,
269     GVariant* parameters, GDBusMethodInvocation* invocation,
270     gpointer user_data) {
271   LOGI("method_name[%s] sender[%s]", method_name, sender);
272   DBusEventListener::Impl* dl = static_cast<DBusEventListener::Impl*>(user_data);
273   GVariant* reply_body = NULL;
274   try {
275     char* appid = NULL;
276     char* serialized = NULL;
277
278     uid_t uid = GetSenderUid(conn, sender);
279     pid_t pid = GetSenderPid(conn, sender);
280
281     if (g_strcmp0(method_name, "Post") == 0
282            || g_strcmp0(method_name, "Update") == 0
283            || g_strcmp0(method_name, "Delete") == 0
284            || g_strcmp0(method_name, "Error") == 0
285            || g_strcmp0(method_name, "DeleteAll") == 0
286            || g_strcmp0(method_name, "Register") == 0
287            || g_strcmp0(method_name, "Unregister") == 0) {
288       parameters = g_variant_ref(parameters);
289       g_dbus_method_invocation_return_value(invocation, g_variant_new("(i)", 0));
290       MethodCallHandler(parameters, dl, pid, uid);
291       g_variant_unref(parameters);
292       return;
293     } else if (g_strcmp0(method_name, "Count") == 0) {
294       g_variant_get(parameters, "(&s&s)", &appid, &serialized);
295
296       Bundle b(serialized);
297       EventInfo info(b);
298       int num = dl->parent_->NotifyNumberRequest(info);
299       reply_body = g_variant_new("(i)", num);
300     } else if (g_strcmp0(method_name, "Get") == 0) {
301       g_variant_get(parameters, "(&s&s)", &appid, &serialized);
302
303       Bundle b(serialized);
304       EventInfo info(b);
305       list<Bundle> result = dl->parent_->NotifyObserver(info);
306       GVariantBuilder* builder = g_variant_builder_new(G_VARIANT_TYPE("a(s)"));
307       for (auto& i : result) {
308         g_variant_builder_add(builder, "(s)",
309           reinterpret_cast<char*>(i.ToRaw().first.get()));
310       }
311       reply_body = g_variant_new("(a(s))", builder);
312       g_variant_builder_unref(builder);
313     } else {
314       g_dbus_method_invocation_return_error(invocation, noti_ex_error_quark(),
315             ERROR_IO_ERROR, "invalid operation");
316       return;
317     }
318
319     g_dbus_method_invocation_return_value(invocation, reply_body);
320
321   } catch (Exception &ex) {
322     LOGE("%s %d", ex.what(), ex.GetErrorCode());
323     g_dbus_method_invocation_return_error(invocation, noti_ex_error_quark(),
324             ERROR_IO_ERROR, "OnMethodCall fail");
325   }
326 }
327
328 int DBusEventListener::Impl::RegisterGDBusInterface() {
329   static const GDBusInterfaceVTable interface_vtable = {
330     OnMethodCall,
331     nullptr,
332     nullptr
333   };
334   static gchar introspection_xml[] =
335     "  <node>"
336     "  <interface name='org.tizen.notification_ex'>"
337     "        <method name='Post'>"
338     "          <arg type='s' name='appid' direction='in'/>"
339     "          <arg type='s' name='info' direction='in'/>"
340     "          <arg type='a(s)' name='serialized' direction='in'/>"
341     "          <arg type='i' name='ret' direction='out'/>"
342     "        </method>"
343     "        <method name='Update'>"
344     "          <arg type='s' name='appid' direction='in'/>"
345     "          <arg type='s' name='info' direction='in'/>"
346     "          <arg type='a(s)' name='serialized' direction='in'/>"
347     "          <arg type='i' name='ret' direction='out'/>"
348     "        </method>"
349     "        <method name='Delete'>"
350     "          <arg type='s' name='appid' direction='in'/>"
351     "          <arg type='s' name='info' direction='in'/>"
352     "          <arg type='a(s)' name='serialized' direction='in'/>"
353     "          <arg type='i' name='ret' direction='out'/>"
354     "        </method>"
355     "        <method name='Get'>"
356     "          <arg type='s' name='appid' direction='in'/>"
357     "          <arg type='s' name='serialized' direction='in'/>"
358     "          <arg type='a(s)' name='noti_arr' direction='out'/>"
359     "        </method>"
360     "        <method name='Error'>"
361     "          <arg type='s' name='appid' direction='in'/>"
362     "          <arg type='s' name='info' direction='in'/>"
363     "          <arg type='a(s)' name='serialized' direction='in'/>"
364     "          <arg type='i' name='ret' direction='out'/>"
365     "        </method>"
366     "        <method name='Count'>"
367     "          <arg type='s' name='appid' direction='in'/>"
368     "          <arg type='s' name='serialized' direction='in'/>"
369     "          <arg type='i' name='num' direction='out'/>"
370     "        </method>"
371     "        <method name='DeleteAll'>"
372     "          <arg type='s' name='appid' direction='in'/>"
373     "          <arg type='s' name='info' direction='in'/>"
374     "          <arg type='a(s)' name='serialized' direction='in'/>"
375     "          <arg type='i' name='ret' direction='out'/>"
376     "        </method>"
377     "        <method name='Register'>"
378     "          <arg type='s' name='appid' direction='in'/>"
379     "          <arg type='s' name='info' direction='in'/>"
380     "          <arg type='a(s)' name='serialized' direction='in'/>"
381     "          <arg type='i' name='ret' direction='out'/>"
382     "        </method>"
383     "        <method name='Unregister'>"
384     "          <arg type='s' name='appid' direction='in'/>"
385     "          <arg type='s' name='info' direction='in'/>"
386     "          <arg type='a(s)' name='serialized' direction='in'/>"
387     "          <arg type='i' name='ret' direction='out'/>"
388     "        </method>"
389     "  </interface>"
390     "  </node>";
391   GError *error = NULL;
392   GDBusNodeInfo *introspection_data =
393       g_dbus_node_info_new_for_xml(introspection_xml, &error);
394   if (!introspection_data) {
395     LOGE("g_dbus_node_info_new_for_xml is failed.");
396     if (error != NULL) {
397       LOGE("g_dbus_node_info_new_for_xml err [%s]", error->message);
398       g_error_free(error);
399     }
400     return ERROR_IO_ERROR;
401   }
402
403   registration_id_ = g_dbus_connection_register_object(
404       DBusConnectionManager::GetInst().GetConnection(),
405       path_.c_str(), introspection_data->interfaces[0],
406       &interface_vtable, this, NULL, NULL);
407   g_dbus_node_info_unref(introspection_data);
408   if (registration_id_ == 0) {
409     LOGE("register object fail");
410     return ERROR_IO_ERROR;
411   }
412
413   LOGI("RegisterGDBusInterface success");
414   return ERROR_NONE;
415 }
416
417 void DBusEventListener::Impl::UnRegisterGDBusInterface() {
418   g_dbus_connection_unregister_object(
419         DBusConnectionManager::GetInst().GetConnection(),
420         registration_id_);
421 }
422
423 bool DBusEventListener::Impl::SubscribeSignal() {
424   subscribe_id_ = g_dbus_connection_signal_subscribe(
425                     DBusConnectionManager::GetInst().GetConnection(),
426                     NULL,
427                     DBusConnectionManager::GetInst().GetInterfaceName().c_str(),
428                     NULL,
429                     path_.c_str(),
430                     NULL,
431                     G_DBUS_SIGNAL_FLAGS_NONE,
432                     SignalCb,
433                     this,
434                     NULL);
435   return subscribe_id_ > 0;
436 }
437
438 void DBusEventListener::Impl::UnSubscribeSignal() {
439   g_dbus_connection_signal_unsubscribe(
440       DBusConnectionManager::GetInst().GetConnection(), subscribe_id_);
441   subscribe_id_ = 0;
442 }
443
444 void DBusEventListener::RegisterObserver(IEventObserver* observer) {
445   if (impl_->subscribe_id_ == 0) {
446     impl_->SubscribeSignal();
447   } else {
448     impl_->UnSubscribeSignal();
449     impl_->SubscribeSignal();
450   }
451
452   if (impl_->registration_id_ == 0) {
453     impl_->RegisterGDBusInterface();
454   } else {
455     impl_->UnRegisterGDBusInterface();
456     impl_->RegisterGDBusInterface();
457   }
458   impl_->observer_ = observer;
459 }
460
461 void DBusEventListener::UnRegisterObserver(IEventObserver* observer) {
462   impl_->UnSubscribeSignal();
463   impl_->observer_ = NULL;
464 }
465
466 void DBusEventListener::NotifyObserver(
467     const IEventInfo& info, list<Bundle> serialized) {
468   impl_->observer_->OnEvent(info, serialized);
469 }
470
471 list<Bundle> DBusEventListener::NotifyObserver(const IEventInfo& info) {
472   return impl_->observer_->OnRequest(info);
473 }
474
475 int DBusEventListener::NotifyNumberRequest(const IEventInfo& info) {
476   return impl_->observer_->OnRequestNumber(info);
477 }
478
479 }  // namespace notification