-# Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+# Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#
# @file CMakeLists.txt
# @author Adam Malinowski <a.malinowsk2@partner.samsung.com>
+# @author Oskar Switalski <o.switalski@samsung.com>
#
############################# Check minimum CMake version #####################
SET(TARGET_PLUGIN_SERVICE "askuser-plugin-service")
SET(TARGET_PLUGIN_CLIENT "askuser-plugin-client")
SET(TARGET_CLIENT "askuser-test-client")
+SET(TARGET_ASKUSER_NOTIFICATION "askuser-notification")
ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(systemd)
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_" />
+ </request>
+</manifest>
Source1002: libaskuser-common.manifest
Source1003: askuser-plugins.manifest
Source1004: askuser-test.manifest
+Source1005: askuser-notification.manifest
BuildRequires: cmake
BuildRequires: libwayland-egl
BuildRequires: gettext-tools
BuildRequires: pkgconfig(cynara-agent)
+BuildRequires: pkgconfig(cynara-creds-socket)
BuildRequires: pkgconfig(cynara-plugin)
+BuildRequires: pkgconfig(elementary)
BuildRequires: pkgconfig(libsystemd-daemon)
BuildRequires: pkgconfig(libsystemd-journal)
+BuildRequires: pkgconfig(security-manager)
BuildRequires: pkgconfig(security-privilege-manager)
BuildRequires: coregl
%{?systemd_requires}
%description -n libaskuser-common
Askuser common library with common functionalities
+%package -n askuser-notification
+Summary: User daemon which shows popup with privilege request
+
+%description -n askuser-notification
+User daemon which shows popup with privilege request
+
%package -n askuser-plugins
Requires: cynara
Requires: libcynara-client
cp -a %{SOURCE1002} .
cp -a %{SOURCE1003} .
cp -a %{SOURCE1004} .
+cp -a %{SOURCE1005} .
%build
%if 0%{?sec_build_binary_debug_enable}
%find_lang %{name}
%post
+# todo properly use systemd --user
+ln -s /lib/systemd/user/askuser-notification.service \
+/usr/lib/systemd/user/default.target.wants/askuser-notification.service 2> /dev/null
+
systemctl daemon-reload
if [ $1 = 1 ]; then
%attr(755, root, root) /usr/bin/askuser
/usr/lib/systemd/system/askuser.service
+%files -n askuser-notification
+%manifest askuser-notification.manifest
+%license LICENSE
+%attr(755,root,root) /usr/bin/askuser-notification
+/usr/lib/systemd/user/askuser-notification.service
+/usr/share/locale/en/LC_MESSAGES/askuser.mo
+/usr/share/locale/pl/LC_MESSAGES/askuser.mo
+
%files -n libaskuser-common
%manifest libaskuser-common.manifest
%license LICENSE
REQUIRED
cynara-agent
cynara-plugin
+ cynara-creds-socket
libsystemd-daemon
security-privilege-manager
)
${ASKUSER_AGENT_PATH}/main/Agent.cpp
${ASKUSER_AGENT_PATH}/main/CynaraTalker.cpp
${ASKUSER_AGENT_PATH}/main/main.cpp
+ ${ASKUSER_AGENT_PATH}/main/NotificationTalker.cpp
)
INCLUDE_DIRECTORIES(
INSTALL(TARGETS ${TARGET_ASKUSER} DESTINATION ${BIN_INSTALL_DIR})
-ADD_SUBDIRECTORY(po)
+ADD_SUBDIRECTORY(notification-daemon)
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/daemon/NotificationTalker.cpp
+ * @author Oskar Świtalski <o.switalski@samsung.com>
+ * @brief Definition of NotificationTalker class
+ */
+
+#include "NotificationTalker.h"
+
+#include <algorithm>
+#include <cstring>
+#include <cynara-creds-socket.h>
+
+#include <exception/ErrnoException.h>
+#include <exception/CynaraException.h>
+#include <log/alog.h>
+#include <socket/Socket.h>
+#include <translator/Translator.h>
+#include <config/Path.h>
+#include <types/Protocol.h>
+
+namespace AskUser {
+
+namespace Agent {
+
+NotificationTalker::NotificationTalker()
+{
+ m_stopflag = false;
+ m_select.setTimeout(100);
+ m_sockfd = Socket::listen(Path::getSocketPath().c_str());
+}
+
+void NotificationTalker::parseRequest(RequestType type, NotificationRequest request)
+{
+ switch (type) {
+ case RequestType::RT_Close:
+ ALOGD("Close service");
+ stop();
+ return;
+ case RequestType::RT_Action:
+ ALOGD("Add request: " << request.id);
+ addRequest(std::move(request));
+ return;
+ case RequestType::RT_Cancel:
+ ALOGD("Cancel request: " << request.id);
+ removeRequest(request.id);
+ return;
+ default:
+ return;
+ }
+}
+
+void NotificationTalker::addRequest(NotificationRequest &&request)
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ auto &queue = m_requests[request.data.user];
+ auto it = std::find_if(queue.begin(), queue.end(),
+ [&request](const NotificationRequest &req){return req.id == request.id;}
+ );
+
+ if (it == queue.end()) {
+ queue.emplace_back(std::move(request));
+ } else {
+ ALOGD("Cynara request already exists");
+ }
+}
+
+void NotificationTalker::removeRequest(RequestId id)
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ for (auto &pair : m_requests) {
+ auto &queue = std::get<1>(pair);
+ auto it = std::find_if(queue.begin(), queue.end(),
+ [&id](const NotificationRequest &req){return req.id == id;}
+ );
+ if (it == queue.end()) {
+ ALOGW("Removing non-existent request");
+ return;
+ }
+ if (it == queue.begin()) {
+ auto user = std::get<0>(pair);
+ auto it2 = m_userToFd.find(user);
+ if (it2 != m_userToFd.end())
+ sendDismiss(std::get<1>(*it2));
+ }
+
+ queue.erase(it);
+ }
+}
+
+void NotificationTalker::setResponseHandler(ResponseHandler responseHandler)
+{
+ m_responseHandler = responseHandler;
+}
+
+void NotificationTalker::stop()
+{
+ m_stopflag = true;
+
+ for (auto& pair : m_fdStatus) {
+ int fd = std::get<0>(pair);
+ Socket::close(fd);
+ }
+
+ m_fdStatus.clear();
+ m_fdToUser.clear();
+ m_userToFd.clear();
+
+ Socket::close(m_sockfd);
+ m_sockfd = 0;
+}
+
+NotificationTalker::~NotificationTalker()
+{
+ for (auto& pair : m_fdStatus) {
+ int fd = std::get<0>(pair);
+ Socket::close(fd);
+ }
+
+ Socket::close(m_sockfd);
+}
+
+void NotificationTalker::sendRequest(int fd, const NotificationRequest &request)
+{
+ m_fdStatus[fd] = false;
+
+ std::string data = Translator::Gui::notificationRequestToData(request.id,
+ request.data.client,
+ request.data.privilege);
+ auto size = data.size();
+
+ if (!Socket::send(fd, &size, sizeof(size))) {
+ remove(fd);
+ return;
+ }
+
+ if (!Socket::send(fd, data.c_str(), size)) {
+ remove(fd);
+ return;
+ }
+}
+
+void NotificationTalker::sendDismiss(int fd)
+{
+ if (!m_fdStatus[fd]) {
+ if (!Socket::send(fd, &Protocol::dissmisCode, sizeof(Protocol::dissmisCode))) {
+ remove(fd);
+ return;
+ }
+ m_fdStatus[fd] = true;
+ }
+}
+
+void NotificationTalker::parseResponse(NotificationResponse response, int fd)
+{
+ auto &queue = m_requests[m_fdToUser[fd]];
+ if (queue.empty()) {
+ ALOGD("Request canceled");
+ m_fdStatus[fd] = true;
+ return;
+ }
+
+ NotificationRequest request = queue.front();
+ if (request.id != response.id) {
+ ALOGD("Request canceled");
+ m_fdStatus[fd] = true;
+ return;
+ }
+
+ queue.pop_front();
+ ALOGD("For user: <" << request.data.user
+ << "> client: <" << request.data.client
+ << "> privilege: <" << request.data.privilege
+ << "> received: <" << Translator::Gui::responseToString(response.response) << ">");
+
+ m_responseHandler(response);
+
+ if (!Socket::send(fd, &Protocol::ackCode, sizeof(Protocol::ackCode))) {
+ remove(fd);
+ return;
+ }
+
+ m_fdStatus[fd] = true;
+}
+
+void NotificationTalker::recvResponses(int &rv)
+{
+ for (auto pair : m_userToFd) {
+ if (!rv) break;
+ int fd = std::get<1>(pair);
+
+ if (m_select.isSet(fd)) {
+ --rv;
+
+ NotificationResponse response;
+ if (Socket::recv(fd, &response, sizeof(response))) {
+ parseResponse(response, fd);
+ } else {
+ remove(fd);
+ }
+ }
+ }
+}
+
+void NotificationTalker::newConnection(int &rv)
+{
+ if (m_select.isSet(m_sockfd)) {
+ --rv;
+ int fd = Socket::accept(m_sockfd);
+ try {
+ char *user_c = nullptr;
+
+ int ret = cynara_creds_socket_get_user(fd, USER_METHOD_DEFAULT,&user_c);
+
+ std::unique_ptr<char[]> userPtr(user_c);
+
+ if (ret != CYNARA_API_SUCCESS) {
+ throw CynaraException("cynara_creds_socket_get_user", ret);
+ }
+ std::string user = user_c;
+
+ auto it = m_userToFd.find(user);
+ if (it != m_userToFd.end())
+ remove(std::get<1>(*it));
+
+ m_userToFd[user] = fd;
+ m_fdToUser[fd] = user;
+ m_fdStatus[fd] = true;
+
+ ALOGD("Accepted new conection for user: " << user);
+ } catch (...) {
+ Socket::close(fd);
+ throw;
+ }
+ }
+}
+
+void NotificationTalker::remove(int fd)
+{
+ Socket::close(fd);
+ auto user = m_fdToUser[fd];
+ m_fdToUser.erase(fd);
+ m_userToFd.erase(user);
+ m_fdStatus.erase(fd);
+}
+
+void NotificationTalker::run()
+{
+ try {
+ ALOGD("Notification loop started");
+ while (!m_stopflag) {
+ m_select.add(m_sockfd);
+
+ for (auto pair : m_userToFd)
+ m_select.add(std::get<1>(pair));
+
+ int rv = m_select.exec();
+
+ if (m_stopflag)
+ break;
+
+ if (rv) {
+ newConnection(rv);
+ recvResponses(rv);
+ } else {
+ // timeout
+ }
+
+ /* lock_guard */
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ for (auto pair : m_fdStatus ) {
+ int fd = std::get<0>(pair);
+ bool b = std::get<1>(pair);
+ auto &queue = m_requests[m_fdToUser[fd]];
+ if (b && !queue.empty()) {
+ NotificationRequest request = queue.front();
+ sendRequest(fd, request);
+ }
+ }
+ } /* lock_guard */
+ }
+ ALOGD("NotificationTalker loop ended");
+ } catch (const std::exception &e) {
+ ALOGE("NotificationTalker: " << e.what());
+ } catch (...) {
+ ALOGE("NotificationTalker: unknown error");
+ }
+}
+
+} /* namespace Agent */
+
+} /* namespace AskUser */
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/daemon/NotificationTalker.cpp
+ * @author Oskar Świtalski <o.switalski@samsung.com>
+ * @brief Declaration of NotificationTalker class
+ */
+
+#pragma once
+
+#include <deque>
+#include <functional>
+#include <map>
+#include <mutex>
+#include <string>
+
+#include <socket/SelectRead.h>
+#include <types/RequestId.h>
+#include <types/NotificationResponse.h>
+#include <types/NotificationRequest.h>
+
+#include <main/Request.h>
+
+namespace AskUser {
+
+namespace Agent {
+
+typedef std::pair<std::string, int> UserToFdPair;
+typedef std::map<std::string, int> UserToFdMap;
+typedef std::map<int, std::string> FdToUserMap;
+typedef std::map<int, bool> FdStatus;
+
+typedef std::map<std::string, std::deque<NotificationRequest>> RequestsQueue;
+
+typedef std::function<void(NotificationResponse)> ResponseHandler;
+
+class NotificationTalker
+{
+public:
+ NotificationTalker();
+
+ void parseRequest(RequestType type, NotificationRequest request);
+ void run();
+ void setResponseHandler(ResponseHandler responseHandler);
+ virtual void stop();
+
+ ~NotificationTalker();
+
+protected:
+ ResponseHandler m_responseHandler;
+
+ UserToFdMap m_userToFd;
+ FdToUserMap m_fdToUser;
+ FdStatus m_fdStatus;
+ Socket::SelectRead m_select;
+ int m_sockfd = 0;
+
+ RequestsQueue m_requests;
+ std::mutex m_mutex;
+
+ bool m_stopflag;
+
+ void parseResponse(NotificationResponse response, int fd);
+ void recvResponses(int &rv);
+
+ void newConnection(int &rv);
+ void remove(int fd);
+
+ virtual void addRequest(NotificationRequest &&request);
+ virtual void removeRequest(RequestId id);
+ virtual void sendRequest(int fd, const NotificationRequest &request);
+ virtual void sendDismiss(int fd);
+};
+
+} /* namespace Agent */
+
+} /* namespace AskUser */
+
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/notification-daemon/GuiRunner.h
+ * @author Oskar Świtalski <o.switalski@samsung.com>
+ * @brief Definition of AskUserTalker class
+ */
+
+#include "AskUserTalker.h"
+
+#include <iostream>
+#include <string>
+
+#include <socket/Socket.h>
+#include <socket/SelectRead.h>
+#include <types/NotificationResponse.h>
+#include <types/Protocol.h>
+#include <types/NotificationRequest.h>
+#include <exception/ErrnoException.h>
+#include <exception/Exception.h>
+#include <translator/Translator.h>
+#include <config/Path.h>
+
+#include <security-manager.h>
+
+namespace AskUser {
+
+namespace Notification {
+
+namespace {
+
+inline void throwOnSecurityPrivilegeError(std::string err, int ret)
+{
+ if (ret != SECURITY_MANAGER_SUCCESS)
+ throw Exception(err + " : " + std::to_string(ret));
+}
+
+inline const char *dropPrefix(const char* app)
+{
+ constexpr char prefix[] = "User::App::";
+ constexpr size_t prefixSize = sizeof(prefix) - 1;
+ return strncmp(app, prefix, prefixSize) ? app : app + prefixSize;
+}
+
+void setSecurityLevel(const std::string &app, const std::string &perm, const std::string &level)
+{
+ int ret;
+
+ policy_update_req *policyUpdateRequest = nullptr;
+ policy_entry *policyEntry = nullptr;
+
+ try {
+ if (level != "Allow" && level != "Deny")
+ throw std::invalid_argument("Not allowed security level <" + level + ">");
+
+ ALOGD("SecurityManager: Setting security level to " << level);
+
+ ret = security_manager_policy_update_req_new(&policyUpdateRequest);
+ throwOnSecurityPrivilegeError("security_manager_policy_update_req_new", ret);
+
+ ret = security_manager_policy_entry_new(&policyEntry);
+ throwOnSecurityPrivilegeError("security_manager_policy_entry_new", ret);
+
+ ret = security_manager_policy_entry_set_application(policyEntry,
+ dropPrefix(app.c_str()));
+ throwOnSecurityPrivilegeError("security_manager_policy_entry_set_application", ret);
+
+ ret = security_manager_policy_entry_set_privilege(policyEntry, perm.c_str());
+ throwOnSecurityPrivilegeError("security_manager_policy_entry_set_privilege", ret);
+
+ ret = security_manager_policy_entry_set_level(policyEntry, level.c_str());
+ throwOnSecurityPrivilegeError("security_manager_policy_entry_admin_set_level", ret);
+
+ ret = security_manager_policy_update_req_add_entry(policyUpdateRequest, policyEntry);
+ throwOnSecurityPrivilegeError("security_manager_policy_update_req_add_entry", ret);
+
+ ret = security_manager_policy_update_send(policyUpdateRequest);
+ throwOnSecurityPrivilegeError("security_manager_policy_update_send", ret);
+
+ ALOGD("SecurityManager: Setting level succeeded");
+ } catch (std::exception &e) {
+ ALOGE("SecurityManager: Failed <" << e.what() << ">");
+ }
+
+ security_manager_policy_entry_free(policyEntry);
+ security_manager_policy_update_req_free(policyUpdateRequest);
+}
+
+} /* namespace */
+
+
+AskUserTalker::AskUserTalker(GuiRunner *gui) : m_gui(gui) {
+ m_gui->setDropHandler([&](){return this->shouldDismiss();});
+}
+
+AskUserTalker::~AskUserTalker()
+{
+ try {
+ Socket::close(sockfd);
+ } catch (const std::exception &e) {
+ ALOGE(std::string("~AskUserTalker") + e.what());
+ } catch (...) {
+ ALOGE("~AskUserTalker: Unknow error");
+ }
+}
+
+void AskUserTalker::run()
+{
+ sockfd = Socket::connect(Path::getSocketPath());
+
+ while (!stopFlag) {
+ size_t size;
+ char *buf;
+ NotificationResponse response;
+
+ ALOGD("Waiting for request...");
+
+ if (!Socket::recv(sockfd, &size, sizeof(size))) {
+ ALOGI("Askuserd closed connection, closing...");
+ break;
+ }
+
+ buf = new char[size];
+
+ if (!Socket::recv(sockfd, buf, size)) {
+ ALOGI("Askuserd closed connection, closing...");
+ break;
+ }
+
+ NotificationRequest request = Translator::Gui::dataToNotificationRequest(buf);
+ delete[] buf;
+ ALOGD("Recieved data " << request.data.client << " " << request.data.privilege);
+
+ response.response = m_gui->popupRun(request.data.client, request.data.privilege);
+ response.id = request.id;
+
+ if (response.response == NResponseType::None) {
+ continue;
+ }
+
+ if (!Socket::send(sockfd, &response, sizeof(response))) {
+ ALOGI("Askuserd closed connection, closing...");
+ break;
+ }
+
+ uint8_t ack = 0x00;
+ if (!Socket::recv(sockfd, &ack, sizeof(ack))) {
+ ALOGI("Askuserd closed connection, closing...");
+ break;
+ }
+
+ if (ack != Protocol::ackCode)
+ throw Exception("Incorrect ack");
+
+ switch (response.response) {
+ case NResponseType::Error:
+ throw Exception(m_gui->getErrorMsg());
+ case NResponseType::Allow:
+ case NResponseType::Never:
+ setSecurityLevel(request.data.client, request.data.privilege,
+ Translator::Gui::responseToString(response.response));
+ default:
+ break;
+ }
+ }
+}
+
+void AskUserTalker::stop()
+{
+ m_gui->stop();
+ Socket::close(sockfd);
+}
+
+bool AskUserTalker::shouldDismiss()
+{
+ Socket::SelectRead select;
+ select.add(sockfd);
+ if (select.exec() == 0)
+ return false;
+
+ uint8_t a = 0x00;
+ Socket::recv(sockfd, &a, sizeof(a));
+
+ if (a != Protocol::dissmisCode)
+ throw Exception("Incorrect dismiss flag");
+
+ return true;
+}
+
+} /* namespace Notification */
+
+} /* namespace AskUser */
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/notification-daemon/GuiRunner.h
+ * @author Oskar Świtalski <o.switalski@samsung.com>
+ * @brief Declaration of AskUserTalker class
+ */
+
+#pragma once
+
+#include <functional>
+#include <queue>
+#include <memory>
+#include <mutex>
+
+#include "GuiRunner.h"
+
+namespace AskUser {
+
+namespace Notification {
+
+class AskUserTalker
+{
+public:
+ AskUserTalker(GuiRunner *gui);
+ ~AskUserTalker();
+
+ void run();
+ void stop();
+
+ bool shouldDismiss();
+
+private:
+ GuiRunner *m_gui;
+ int sockfd = 0;
+ bool stopFlag = false;
+};
+
+} /* namespace Notification */
+
+} /* namespace AskUser */
--- /dev/null
+SET(NOTIF_PATH ${PROJECT_SOURCE_DIR}/src/agent/notification-daemon/)
+
+PKG_CHECK_MODULES(ASKUSER_NOTIFICATION_DEP
+ REQUIRED
+ elementary
+ cynara-agent
+ libsystemd-daemon
+ security-manager
+ security-privilege-manager
+)
+
+INCLUDE_DIRECTORIES(SYSTEM
+ ${ASKUSER_NOTIFICATION_DEP_INCLUDE_DIRS}
+)
+INCLUDE_DIRECTORIES(
+ ${ASKUSER_PATH}
+ ${ASKUSER_PATH}/common
+)
+
+SET(ASKUSER_NOTIFICATION_SOURCES
+ ${NOTIF_PATH}/main.cpp
+ ${NOTIF_PATH}/GuiRunner.cpp
+ ${NOTIF_PATH}/AskUserTalker.cpp
+ )
+
+
+ADD_EXECUTABLE(${TARGET_ASKUSER_NOTIFICATION} ${ASKUSER_NOTIFICATION_SOURCES})
+
+SET_TARGET_PROPERTIES(${TARGET_ASKUSER_NOTIFICATION} PROPERTIES
+ COMPILE_FLAGS
+ -fpie
+)
+
+TARGET_LINK_LIBRARIES(${TARGET_ASKUSER_NOTIFICATION}
+ ${ASKUSER_NOTIFICATION_DEP_LIBRARIES}
+ ${TARGET_ASKUSER_COMMON}
+ -pie
+)
+
+INSTALL(TARGETS ${TARGET_ASKUSER_NOTIFICATION} DESTINATION ${BIN_INSTALL_DIR})
+
+ADD_SUBDIRECTORY(po)
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/notification-daemon/GuiRunner.cpp
+ * @author Oskar Świtalski <o.switalski@samsung.com>
+ * @brief Definition of GuiRunner class
+ */
+
+#include "GuiRunner.h"
+
+#include <exception/ErrnoException.h>
+#include <exception/Exception.h>
+#include <translator/Translator.h>
+#include <libintl.h>
+#include <privilegemgr/privilege_info.h>
+
+namespace AskUser {
+
+namespace Notification {
+
+namespace {
+
+bool should_raise = false;
+
+void unfocused(void *data, Evas_Object *, void *)
+{
+ if (data == NULL)
+ return;
+
+ PopupData *res = static_cast<PopupData*>(data);
+
+ if (should_raise)
+ elm_win_raise(res->win);
+ else
+ elm_exit();
+}
+
+void inline win_close(Evas_Object *win) {
+ should_raise = false;
+ elm_win_lower(win);
+}
+
+void inline answer(void *data, NResponseType response)
+{
+ ALOGD("User selected: " + Translator::Gui::responseToString(response));
+
+ if (data == NULL)
+ return;
+
+ PopupData *res = static_cast<PopupData*>(data);
+ res->type = response;
+ win_close(res->win);
+}
+
+void allow_answer(void *data, Evas_Object *, void *)
+{
+ answer(data, NResponseType::Allow);
+}
+
+void deny_answer(void *data, Evas_Object *, void *)
+{
+ answer(data, NResponseType::Deny);
+}
+
+void never_answer(void *data, Evas_Object *, void *)
+{
+ answer(data, NResponseType::Never);
+}
+
+Eina_Bool timeout_answer(void *data) {
+ if (!data)
+ return ECORE_CALLBACK_RENEW;
+
+ drop *d = static_cast<drop*>(data);
+
+ if (d->handle()) {
+ win_close(d->popup->win);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+std::string friendlyPrivilegeName(const std::string &privilege)
+{
+ char *name = nullptr;
+ int res = privilege_info_get_privilege_display_name(privilege.c_str(), &name);
+ if (res != PRVMGR_ERR_NONE || !name) {
+ ALOGE("Unable to get privilege display name for: <" << privilege << ">, err: <" << res << ">");
+ return privilege;
+ }
+ std::string ret(name);
+ free(name);
+ return ret;
+}
+
+} /* namespace */
+
+GuiRunner::GuiRunner()
+{
+ m_popupData = new PopupData({NResponseType::Deny, nullptr});
+}
+
+void GuiRunner::initialize()
+{
+ elm_init(0, NULL);
+
+ //placeholder
+ m_win = elm_win_add(NULL, dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_TITLE"),
+ ELM_WIN_DOCK);
+ elm_win_autodel_set(m_win, EINA_TRUE);
+ elm_win_override_set(m_win, EINA_TRUE);
+ elm_win_alpha_set(m_win, EINA_TRUE);
+
+ // popup
+ m_popup = elm_popup_add(m_win);
+ elm_object_part_text_set(m_popup, "title,text", dgettext(PROJECT_NAME,
+ "SID_PRIVILEGE_REQUEST_DIALOG_TITLE"));
+
+ // box
+ m_box = elm_box_add(m_popup);
+ evas_object_size_hint_weight_set(m_box, EVAS_HINT_EXPAND, 0);
+ evas_object_size_hint_align_set(m_box, EVAS_HINT_FILL, 0.0);
+
+ // content
+ m_content = elm_label_add(m_popup);
+ elm_object_style_set(m_content, "elm.swallow.content");
+ elm_label_line_wrap_set(m_content, ELM_WRAP_MIXED);
+ evas_object_size_hint_weight_set(m_content, EVAS_HINT_EXPAND, 0.0);
+ evas_object_size_hint_align_set(m_content, EVAS_HINT_FILL, EVAS_HINT_FILL);
+
+ evas_object_show(m_content);
+ elm_box_pack_end(m_box, m_content);
+ elm_object_part_content_set(m_popup, "default", m_box);
+
+ // buttons
+ m_allowButton = elm_button_add(m_popup);
+ elm_object_part_content_set(m_popup, "button1", m_allowButton);
+ elm_object_text_set(m_allowButton, dgettext(PROJECT_NAME,
+ "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_ALLOW"));
+
+ m_neverButton = elm_button_add(m_popup);
+ elm_object_text_set(m_neverButton, dgettext(PROJECT_NAME,
+ "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NEVER"));
+ elm_object_part_content_set(m_popup, "button2", m_neverButton);
+
+ m_denyButton = elm_button_add(m_popup);
+ elm_object_text_set(m_denyButton, dgettext(PROJECT_NAME,
+ "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_DENY"));
+ elm_object_part_content_set(m_popup, "button3", m_denyButton);
+
+ // callbacks
+ evas_object_smart_callback_add(m_win, "unfocused", unfocused, m_popupData);
+ evas_object_smart_callback_add(m_allowButton, "clicked", allow_answer, m_popupData);
+ evas_object_smart_callback_add(m_neverButton, "clicked", never_answer, m_popupData);
+ evas_object_smart_callback_add(m_denyButton, "clicked", deny_answer, m_popupData);
+
+ m_popupData->win = m_win;
+ m_initialized = true;
+
+}
+
+NResponseType GuiRunner::popupRun(const std::string &app, const std::string &perm)
+{
+
+ try {
+ if (!m_dropHandler)
+ throw Exception("DropHandler was not initialized");
+
+ if (!m_initialized)
+ initialize();
+
+ m_running = true;
+ drop *Drop = new drop({m_dropHandler, m_popupData});
+ m_timer = ecore_timer_add(0.1, timeout_answer, Drop);
+
+ // create message
+ char *messageFormat = dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_MESSAGE");
+ char buf[BUFSIZ];
+ int ret = std::snprintf(buf, sizeof(buf), messageFormat,
+ app.c_str(),
+ friendlyPrivilegeName(perm).c_str());
+
+ if (ret < 0)
+ throw ErrnoException("snprintf failed", errno);
+
+ m_popupData->type = NResponseType::None;
+
+ should_raise = true;
+
+ elm_object_text_set(m_content, buf);
+
+ evas_object_show(m_popup);
+ evas_object_show(m_win);
+
+ elm_win_raise(m_win);
+ elm_run();
+
+ ecore_timer_del(m_timer);
+ m_running = false;
+ should_raise = false;
+ } catch (const std::exception &e) {
+ m_popupData->type = NResponseType::Error;
+ m_errorMsg = std::move(e.what());
+ }
+
+ return m_popupData->type;
+}
+
+void GuiRunner::setDropHandler(DropHandler dropHandler)
+{
+ m_dropHandler = dropHandler;
+}
+
+void GuiRunner::stop()
+{
+ if (m_running) {
+ evas_object_hide(m_win);
+ elm_exit();
+ }
+
+ elm_shutdown();
+}
+
+} /* namespace Notification */
+
+} /* namespace AskUser */
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/notification-daemon/GuiRunner.h
+ * @author Oskar Świtalski <o.switalski@samsung.com>
+ * @brief Declaration of GuiRunner class
+ */
+
+#pragma once
+
+#include <Elementary.h>
+#include <functional>
+#include <string>
+
+#include <types/NotificationResponse.h>
+#include <log/alog.h>
+
+namespace AskUser {
+
+namespace Notification {
+
+typedef std::function<bool()> DropHandler;
+
+struct PopupData {
+ NResponseType type;
+ Evas_Object *win;
+};
+
+struct drop {
+ DropHandler handle;
+ PopupData *popup;
+};
+
+class GuiRunner {
+public:
+ GuiRunner();
+
+ NResponseType popupRun(const std::string &app, const std::string &perm);
+
+ void setDropHandler(DropHandler dropHandler);
+ void stop();
+
+ std::string getErrorMsg() { return m_errorMsg; }
+
+private:
+ PopupData *m_popupData;
+ DropHandler m_dropHandler;
+
+ Evas_Object *m_win;
+ Evas_Object *m_popup;
+ Evas_Object *m_box;
+ Evas_Object *m_content;
+ Evas_Object *m_allowButton;
+ Evas_Object *m_neverButton;
+ Evas_Object *m_denyButton;
+ Ecore_Timer *m_timer;
+
+ bool m_running = false;
+ bool m_initialized = false;
+
+ std::string m_errorMsg;
+
+ void initialize();
+};
+
+} /* namespace Notification */
+
+} /* namespace AskUser */
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/notification-daemon/main.cpp
+ * @author Oskar Świtalski <o.switalski@samsung.com>
+ * @brief Main askuser notification daemon file
+ */
+
+#include <clocale>
+#include <csignal>
+#include <cstdlib>
+#include <string>
+#include <systemd/sd-daemon.h>
+#include <thread>
+#include <unistd.h>
+
+#include <exception/Exception.h>
+#include <log/alog.h>
+
+#include "GuiRunner.h"
+#include "AskUserTalker.h"
+
+int main()
+{
+ using namespace AskUser::Notification;
+ init_agent_log();
+
+ char *locale = setlocale(LC_ALL, "");
+ ALOGD("Current locale is: <" << locale << ">");
+
+ try {
+ GuiRunner gui;
+ AskUserTalker askUserTalker(&gui);
+
+ int ret = sd_notify(0, "READY=1");
+ if (ret == 0) {
+ ALOGW("Agent was not configured to notify its status");
+ } else if (ret < 0) {
+ ALOGE("sd_notify failed: [" << ret << "]");
+ }
+
+ askUserTalker.run();
+
+ } catch (std::exception &e) {
+ ALOGE("Askuser-notification stopped because of: <" << e.what() << ">.");
+ } catch (...) {
+ ALOGE("Askuser-notification stopped because of unknown unhandled exception.");
+ }
+
+ return 0;
+}
-# Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+# Copyright (c) 2015-2016 Samsung Electronics Co., Ltd All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
--- /dev/null
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_TITLE"
+msgstr "Privilege request"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NEVER"
+msgstr "Never"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_DENY"
+msgstr "Deny"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_ALLOW"
+msgstr "Always"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_MESSAGE"
+msgstr "Application <b>%s</b> requested privilege for <b>%s</b>."
--- /dev/null
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_TITLE"
+msgstr "Żądanie dostępu"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NEVER"
+msgstr "Nigdy"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_DENY"
+msgstr "Odmów"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_ALLOW"
+msgstr "Zawsze"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_MESSAGE"
+msgstr "Aplikacja <b>%s</b> zażądała przywileju do <b>%s</b>."
+++ /dev/null
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_TITLE"
-msgstr "Privilege request"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NO_ONCE"
-msgstr "No"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NO_SESSION"
-msgstr "No session"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NO_LIFE"
-msgstr "No restart"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_YES_ONCE"
-msgstr "Yes"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_YES_SESSION"
-msgstr "Yes session"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_YES_LIFE"
-msgstr "Yes restart"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_MESSAGE"
-msgstr "Application: %s, ran by user: %s, requested privilege:\n%s\nGrant access to privilege?"
+++ /dev/null
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_TITLE"
-msgstr "Żądanie dostępu"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NO_ONCE"
-msgstr "Nie"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NO_SESSION"
-msgstr "Nie sesja"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NO_LIFE"
-msgstr "Nie restart"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_YES_ONCE"
-msgstr "Tak"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_YES_SESSION"
-msgstr "Tak sesja"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_YES_LIFE"
-msgstr "Tak restart"
-
-msgid "SID_PRIVILEGE_REQUEST_DIALOG_MESSAGE"
-msgstr "Aplikacja: %s, uruchomiona przez użytkownika: %s, zażądała zasobu:\n %s\nUdzielić dostępu?"
-
lib/systemd/system
)
+INSTALL(FILES
+ ${CMAKE_SOURCE_DIR}/systemd/askuser-notification.service
+ DESTINATION
+ lib/systemd/user
+)
--- /dev/null
+[Unit]
+Description=Ask user notification
+
+[Service]
+ExecStart=/usr/bin/askuser-notification
+
+Type=notify
+
+KillMode=process
+TimeoutStopSec=10
+TimeoutStartSec=10
+RestartSec=5
+Restart=always
+
+NoNewPrivileges=true
+
+EnvironmentFile=-/run/tizen-system-env
+
+[Install]
+WantedBy=multi-user.target