Add internal popup implementation 34/50934/16
authorJanusz Kozerski <j.kozerski@samsung.com>
Tue, 3 Nov 2015 13:22:51 +0000 (14:22 +0100)
committerJanusz Kozerski <j.kozerski@samsung.com>
Thu, 17 Dec 2015 14:08:31 +0000 (15:08 +0100)
Add implementation of popups using separate EFL application.
The original askuser process forks, runs popup binary, pass
the data to the popup using pipe, and collect result from popup
(via pipe and exit code).

Verification: run "askuser-test.sh run". All test should pass.

Change-Id: I22b9d172d0a44a2ddb5a875334d9d79088c95cfc

18 files changed:
CMakeLists.txt
packaging/askuser.spec
src/agent/CMakeLists.txt
src/agent/main/Agent.cpp
src/agent/main/main.cpp
src/agent/po/en.po
src/agent/po/pl.po
src/agent/popup-bin/CMakeLists.txt [new file with mode: 0644]
src/agent/popup-bin/popup.cpp [new file with mode: 0644]
src/agent/popup-bin/popup.h [new file with mode: 0644]
src/agent/ui/AskUIInterface.h
src/agent/ui/AskUIPopupBackend.cpp [new file with mode: 0644]
src/agent/ui/AskUIPopupBackend.h [new file with mode: 0644]
src/agent/ui/popup-runner.cpp [new file with mode: 0644]
src/agent/ui/popup-runner.h [new file with mode: 0644]
src/agent/ui/serialization.cpp [new file with mode: 0644]
src/agent/ui/serialization.h [new file with mode: 0644]
systemd/askuser.service

index 251d5d8..69a881e 100644 (file)
@@ -72,6 +72,10 @@ SET(TARGET_ASKUSER_COMMON "askuser-common")
 SET(TARGET_PLUGIN_SERVICE "askuser-plugin-service")
 SET(TARGET_PLUGIN_CLIENT "askuser-plugin-client")
 SET(TARGET_CLIENT "askuser-test-client")
+SET(TARGET_ASKUSER_POPUP "askuser-popup")
+
+# Add definition for popup binary path
+ADD_DEFINITIONS(-DPOPUP_EXEC_PATH="${BIN_INSTALL_DIR}/${TARGET_ASKUSER_POPUP}")
 
 ADD_SUBDIRECTORY(src)
 ADD_SUBDIRECTORY(systemd)
index 2ce08d3..581d5a9 100644 (file)
@@ -15,6 +15,7 @@ BuildRequires: gettext-tools
 BuildRequires: pkgconfig(capi-base-common)
 BuildRequires: pkgconfig(cynara-agent)
 BuildRequires: pkgconfig(cynara-plugin)
+BuildRequires: pkgconfig(elementary)
 BuildRequires: pkgconfig(libsystemd-daemon)
 BuildRequires: pkgconfig(libsystemd-journal)
 BuildRequires: pkgconfig(notification)
@@ -104,6 +105,7 @@ fi
 %manifest %{name}.manifest
 %license LICENSE
 %attr(755,root,root) /usr/bin/%{name}
+%attr(755,root,root) /usr/bin/%{name}-popup
 /usr/lib/systemd/system/%{name}.service
 
 %files -n libaskuser-common
index 3f4c625..137d753 100644 (file)
@@ -32,6 +32,9 @@ SET(ASKUSER_SOURCES
     ${ASKUSER_AGENT_PATH}/main/CynaraTalker.cpp
     ${ASKUSER_AGENT_PATH}/main/main.cpp
     ${ASKUSER_AGENT_PATH}/ui/AskUINotificationBackend.cpp
+    ${ASKUSER_AGENT_PATH}/ui/AskUIPopupBackend.cpp
+    ${ASKUSER_AGENT_PATH}/ui/popup-runner.cpp
+    ${ASKUSER_AGENT_PATH}/ui/serialization.cpp
     )
 
 INCLUDE_DIRECTORIES(
@@ -52,3 +55,4 @@ TARGET_LINK_LIBRARIES(${TARGET_ASKUSER}
 INSTALL(TARGETS ${TARGET_ASKUSER} DESTINATION ${BIN_INSTALL_DIR})
 
 ADD_SUBDIRECTORY(po)
+ADD_SUBDIRECTORY(popup-bin)
index 10f01dc..2629124 100644 (file)
@@ -33,9 +33,9 @@
 #include <types/SupportedTypes.h>
 
 #include <log/alog.h>
-#include <ui/AskUINotificationBackend.h>
-
 #include "Agent.h"
+#include <ui/AskUINotificationBackend.h>
+#include <ui/AskUIPopupBackend.h>
 
 namespace AskUser {
 
@@ -206,7 +206,7 @@ void Agent::processUIResponse(const Response &response) {
 
 bool Agent::startUIForRequest(Request *request) {
     auto data = Translator::Agent::dataToRequest(request->data());
-    AskUIInterfacePtr ui(new AskUINotificationBackend());
+    AskUIInterfacePtr ui(new AskUIPopupBackend());
 
     auto handler = [&](RequestId requestId, UIResponseType resultType) -> void {
                        UIResponseHandler(requestId, resultType);
index aa1f48b..934d06e 100644 (file)
@@ -56,6 +56,12 @@ int main(int argc UNUSED, char **argv UNUSED) {
     char *locale = setlocale(LC_ALL, "");
     ALOGD("Current locale is: <" << locale << ">");
 
+    // Block SIGCHLD signal for any thread - needed for Popup Backend
+    sigset_t set;
+    sigemptyset(&set);
+    sigaddset(&set, SIGCHLD);
+    sigprocmask(SIG_BLOCK, &set, NULL);
+
     try {
         AskUser::Agent::Agent agent;
 
index 7eb103e..4e17b5f 100644 (file)
@@ -21,3 +21,12 @@ msgstr "Yes restart"
 
 msgid "SID_PRIVILEGE_REQUEST_DIALOG_MESSAGE"
 msgstr "Application: %s, ran by user: %s, requested privilege:\n%s\nGrant access to privilege?"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_MESSAGE_NO_NEWLINE"
+msgstr "Application: %s, ran by user: %s, requested privilege: %s. Grant access to privilege?"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_CHECKBOX_REMEMBER_SESSION"
+msgstr "Remember for session"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_CHECKBOX_REMEMBER_LIFE"
+msgstr "Remember until restart"
index 82b865b..6c29d3c 100644 (file)
@@ -22,3 +22,11 @@ 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?"
 
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_MESSAGE_NO_NEWLINE"
+msgstr "Aplikacja: %s, uruchomiona przez użytkownika: %s, zażądała zasobu: %s. Udzielić dostępu?"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_CHECKBOX_REMEMBER_SESSION"
+msgstr "Zapmiętaj do końca sesji"
+
+msgid "SID_PRIVILEGE_REQUEST_DIALOG_CHECKBOX_REMEMBER_LIFE"
+msgstr "Zapamiętaj do restartu"
diff --git a/src/agent/popup-bin/CMakeLists.txt b/src/agent/popup-bin/CMakeLists.txt
new file mode 100644 (file)
index 0000000..abcead6
--- /dev/null
@@ -0,0 +1,60 @@
+# Copyright (c) 2015 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.
+#    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     CMakeLists.txt
+# @author   Janusz Kozerski (j.kozerski@samsung.com)
+#
+# @version  1.0
+#
+
+PKG_CHECK_MODULES(ASKUSER_POPUP_DEP
+    elementary
+    libsystemd-daemon
+    REQUIRED)
+
+set(ASKUSER_AGENT_POPUP_SRC_PATH ${ASKUSER_AGENT_PATH}/popup-bin)
+
+set(ASKUSER_POPUP_SOURCES
+    ${ASKUSER_AGENT_POPUP_SRC_PATH}/popup.cpp
+    ${ASKUSER_AGENT_PATH}/ui/popup-runner.cpp
+    # logs
+    ${ASKUSER_AGENT_PATH}/log/alog.cpp
+    # dpl
+    ${ASKUSER_AGENT_PATH}/ui/serialization.cpp
+)
+
+ADD_DEFINITIONS(${ASKUSER_POPUP_DEP_CFLAGS})
+
+INCLUDE_DIRECTORIES(
+    ${ASKUSER_AGENT_PATH}
+    ${ASKUSER_AGENT_PATH}/ui/popup-bin/
+    ${ASKUSER_AGENT_PATH}/ui/
+)
+
+ADD_EXECUTABLE(${TARGET_ASKUSER_POPUP}
+    ${ASKUSER_POPUP_SOURCES}
+)
+
+SET_TARGET_PROPERTIES(${TARGET_ASKUSER_POPUP} PROPERTIES
+    COMPILE_FLAGS
+    -fpie
+)
+
+TARGET_LINK_LIBRARIES(${TARGET_ASKUSER_POPUP}
+    ${ASKUSER_POPUP_DEP_LIBRARIES}
+    -pie
+)
+
+INSTALL(TARGETS ${TARGET_ASKUSER_POPUP} DESTINATION ${BIN_INSTALL_DIR})
diff --git a/src/agent/popup-bin/popup.cpp b/src/agent/popup-bin/popup.cpp
new file mode 100644 (file)
index 0000000..a17d40f
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2015 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.
+ *    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        popup.cpp
+ * @author      Janusz Kozerski (j.kozerski@samsung.com)
+ * @version     1.0
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <vector>
+#include <libintl.h>
+#include <sys/select.h>
+#include <time.h>
+
+#include <Elementary.h>
+
+#include <log/alog.h>
+#include "popup.h"
+#include "popup-runner.h"
+#include "serialization.h"
+
+using namespace AskUser::Agent;
+
+namespace { // anonymous
+
+void on_done(void) {
+    // Quit the efl-mainloop
+    ALOGD("elm_exit()");
+    elm_exit();
+}
+
+void allow_answer(void *data, Evas_Object * /* obj */, void * /* event_info */) {
+    ALOGD("allow_answer");
+    if (data == NULL) {
+        ALOGE("data is NULL; return");
+        return;
+    }
+    struct cert_checker_popup_data *pdp = static_cast <struct cert_checker_popup_data *> (data);
+    if (elm_check_state_get(pdp->check_life)) {
+        ALOGD("remember_life is set");
+        pdp->result = UIResponseType::URT_YES_LIFE;
+    }
+    else if (elm_check_state_get(pdp->check_session)) {
+        ALOGD("remember_session is set");
+        pdp->result = UIResponseType::URT_YES_SESSION;
+    }
+    else {
+        ALOGD("No flag is set");
+        pdp->result = UIResponseType::URT_YES_ONCE;
+    }
+
+    on_done();
+}
+
+void deny_answer(void *data, Evas_Object * /* obj */, void * /* event_info */) {
+    ALOGD("deny_answer");
+    if (data == NULL) {
+        ALOGE("data is NULL; return");
+        return;
+    }
+    struct cert_checker_popup_data *pdp = static_cast <struct cert_checker_popup_data *> (data);
+    if (elm_check_state_get(pdp->check_life)) {
+        ALOGD("remember_life is set");
+        pdp->result = UIResponseType::URT_NO_LIFE;
+    }
+    else if (elm_check_state_get(pdp->check_session)) {
+        ALOGD("remember_session is set");
+        pdp->result = UIResponseType::URT_NO_SESSION;
+    }
+    else {
+        ALOGD("No flag is set");
+        pdp->result = UIResponseType::URT_NO_ONCE;
+    }
+
+    on_done();
+}
+
+bool show_popup(struct cert_checker_popup_data *pdp) {
+    ALOGD("show_popup");
+
+    if (pdp == NULL) {
+        ALOGE("pdp is NULL; return");
+        return false;
+    }
+
+    pdp->win = elm_win_add(NULL,
+            dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_TITLE"),
+            ELM_WIN_NOTIFICATION);
+
+    elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
+    elm_win_autodel_set(pdp->win, EINA_TRUE);
+    evas_object_show(pdp->win);
+    elm_win_indicator_opacity_set(pdp->win, ELM_WIN_INDICATOR_TRANSLUCENT);
+
+    pdp->popup = elm_popup_add(pdp->win);
+
+    pdp->box = elm_box_add(pdp->popup);
+    evas_object_size_hint_weight_set(pdp->box, EVAS_HINT_EXPAND, 0);
+    evas_object_size_hint_align_set(pdp->box, EVAS_HINT_FILL, 0.0);
+
+    pdp->title = elm_label_add(pdp->popup);
+    elm_object_style_set(pdp->title, "elm.text.title");
+    elm_object_text_set(pdp->title, dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_TITLE"));
+    evas_object_show(pdp->title);
+    elm_box_pack_end(pdp->box, pdp->title);
+
+    pdp->content = elm_label_add(pdp->popup);
+    elm_object_style_set(pdp->content, "elm.swallow.content");
+    elm_label_line_wrap_set(pdp->content, ELM_WRAP_MIXED);
+
+    int ret;
+    char *messageFormat = dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_MESSAGE_NO_NEWLINE");
+    char tmpBuffer[BUFSIZ];
+    ret = std::snprintf(tmpBuffer, sizeof(tmpBuffer), messageFormat,
+            pdp->client.c_str(),
+            pdp->user.c_str(),
+            pdp->privilege.c_str());
+
+    if (ret < 0) {
+        int erryes = errno;
+        ALOGE("sprintf failed with error: <" << strerror(erryes) << ">");
+        return false;
+    }
+
+    elm_object_text_set(pdp->content, tmpBuffer);
+    ALOGD("Popup label: " << tmpBuffer);
+    evas_object_size_hint_weight_set(pdp->content, EVAS_HINT_EXPAND, 0.0);
+    evas_object_size_hint_align_set(pdp->content, EVAS_HINT_FILL, EVAS_HINT_FILL);
+    evas_object_show(pdp->content);
+    elm_box_pack_end(pdp->box, pdp->content);
+
+    elm_object_part_content_set(pdp->popup, "default", pdp->box);
+
+    pdp->keep_button = elm_button_add(pdp->popup);
+    elm_object_style_set(pdp->keep_button, "elm.swallow.content.button1");
+    elm_object_text_set(pdp->keep_button, dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_YES_ONCE"));
+    elm_object_part_content_set(pdp->popup, "button1", pdp->keep_button);
+    evas_object_smart_callback_add(pdp->keep_button, "clicked", allow_answer, pdp);
+
+    pdp->uninstall_button = elm_button_add(pdp->popup);
+    elm_object_style_set(pdp->uninstall_button, "elm.swallow.content.button2");
+    elm_object_text_set(pdp->uninstall_button, dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NO_ONCE"));
+    elm_object_part_content_set(pdp->popup, "button2", pdp->uninstall_button);
+    evas_object_smart_callback_add(pdp->uninstall_button, "clicked", deny_answer, pdp);
+
+    pdp->check_session = elm_check_add(pdp->box);
+    elm_object_style_set(pdp->check_session, "check");
+    elm_object_text_set(pdp->check_session, dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_CHECKBOX_REMEMBER_SESSION"));
+    evas_object_show(pdp->check_session);
+    elm_box_pack_end(pdp->box, pdp->check_session);
+
+    pdp->check_life = elm_check_add(pdp->box);
+    elm_object_style_set(pdp->check_life, "check");
+    elm_object_text_set(pdp->check_life, dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_CHECKBOX_REMEMBER_LIFE"));
+    evas_object_show(pdp->check_life);
+    elm_box_pack_end(pdp->box, pdp->check_life);
+
+    evas_object_show(pdp->popup);
+
+    // Showing the popup window
+    evas_object_show(pdp->win);
+
+    // Run the efl mainloop
+    elm_run();
+
+    // Shutdown elementary
+    ALOGD("elm_shutdown");
+    elm_shutdown();
+
+    return true;
+}
+
+bool wait_for_parent_info(int pipe_in) {
+    // wait for parameters from pipe_in
+    // timeout is set for 10 seconds
+    struct timeval timeout = {10L, 0L};
+    fd_set readfds;
+    FD_ZERO(&readfds);
+    FD_SET(pipe_in, &readfds);
+
+    int sel = select(pipe_in + 1, &readfds, NULL, NULL, &timeout);
+    if (sel == -1) {
+        ALOGE("Cannot get info from parent. Exit popup - ERROR (" << errno << ")");
+        close (pipe_in);
+        return false;
+    } else if (sel == 0) {
+        ALOGE("Timeout reached! Exit popup - ERROR");
+        close (pipe_in);
+        return false;
+    }
+    return true;
+}
+
+bool deserialize(cert_checker_popup_data *pdp, char *line, size_t line_length) {
+    ALOGD("------- Deserialization -------");
+    if (line_length == 0) {
+        ALOGE("Deserialization failed");
+        return false;
+    }
+
+    BinaryStream stream;
+    stream.Write(line_length, static_cast <void *> (line));
+
+    // Deserialization order:
+    // client, user, privilege
+
+    try {
+        AskUser::Deserialization::Deserialize(stream, pdp->client);
+        ALOGD("client : " << pdp->client.c_str());
+
+        AskUser::Deserialization::Deserialize(stream, pdp->user);
+        ALOGD("user : " << pdp->user.c_str());
+
+        AskUser::Deserialization::Deserialize(stream, pdp->privilege);
+        ALOGD("privilege : " << pdp->privilege.c_str());
+    } catch (const std::runtime_error &e) {
+        ALOGE("Cannot serialize data. " << e.what());
+        return false;
+    }
+
+    return true;
+}
+
+} // anonymous
+
+EAPI_MAIN int
+elm_main(int argc, char **argv)
+{
+    // int pipe_in and int pipe_out should be passed to Popup via args.
+
+    // These parameters should be passed to Popup via pipe_in:
+    // std::string    client
+    // std::string    user
+    // std::string    privilege
+
+    struct cert_checker_popup_data pd;
+    struct cert_checker_popup_data *pdp = &pd;
+
+    ALOGD("############################ popup binary ################################");
+
+    setlocale(LC_ALL, "");
+
+    if (argc < 3) {
+        ALOGE("To few args passed in exec to popup-bin - should be at least 3:");
+        ALOGE("(binary-name, pipe_in, pipe_out)");
+        ALOGE("return ERROR");
+        return popup_status::EXIT_ERROR;
+    }
+
+    ALOGD("Passed args: " << argv[0] <<", " << argv[1] << ", " << argv[2]);
+
+    int pipe_in;
+    int pipe_out;
+
+    // Parsing args (pipe_in, pipe_out)
+    if (sscanf(argv[1], "%d", &pipe_in) == 0) {
+        ALOGE("Error while parsing pipe_in; return ERROR");
+        return popup_status::EXIT_ERROR;
+    }
+    if (sscanf(argv[2], "%d", &pipe_out) == 0) {
+        ALOGE("Error while parsing pipe_out; return ERROR");
+        return popup_status::EXIT_ERROR;
+    }
+    ALOGD("Parsed pipes: IN: " << pipe_in <<", OUT: " <<  pipe_out);
+
+    if (!wait_for_parent_info(pipe_in)) {
+        close(pipe_out);
+        return popup_status::EXIT_ERROR;
+    }
+
+    int  buff_size = 1024;
+    char line[buff_size];
+    int tmp;
+    ssize_t count = 0;
+
+    do {
+        tmp = TEMP_FAILURE_RETRY(read(pipe_in, line + count, buff_size - count));
+        if (tmp < 0) {
+            close(pipe_in);
+            close(pipe_out);
+            ALOGE("read returned a negative value (" << count << ")");
+            ALOGE("errno: " << strerror(errno));
+            ALOGE("Exit popup - ERROR");
+            return popup_status::EXIT_ERROR;
+        }
+        if (tmp > 0) {
+            count += tmp;
+            ALOGD("Read " << tmp << " bytes. Total: " << count);
+        }
+    } while (tmp != 0);
+
+    ALOGD("Read bytes in total: " << count);
+    close(pipe_in); // cleanup
+
+    if (!deserialize(pdp, line, count))
+        return popup_status::EXIT_ERROR;
+
+    pdp->result = UIResponseType::URT_ERROR;
+
+    if (!show_popup(pdp)) // Showing popup
+        return popup_status::EXIT_ERROR;
+
+    // sending validation_result to popup-runner
+    BinaryStream stream_out;
+
+    ALOGD("pdp->result : " << pdp->result);
+    AskUser::Serialization::Serialize(stream_out, pdp->result);
+
+    unsigned int begin = 0;
+    while (begin < stream_out.size()) {
+        tmp = TEMP_FAILURE_RETRY(write(pipe_out,
+                stream_out.char_pointer() + begin,
+                stream_out.size() - begin));
+        if (tmp < 0) {
+            ALOGE("Write to pipe failed!");
+            close(pipe_out);
+            return popup_status::EXIT_ERROR;
+        }
+        begin += tmp;
+    }
+
+    close(pipe_out);
+
+    ALOGD("Return: " << popup_status::NO_ERROR);
+    return popup_status::NO_ERROR;
+}
+ELM_MAIN()
diff --git a/src/agent/popup-bin/popup.h b/src/agent/popup-bin/popup.h
new file mode 100644 (file)
index 0000000..f5aeda8
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015 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.
+ *    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        popup.h
+ * @author      Janusz Kozerski (j.kozerski@samsung.com)
+ * @version     1.0
+ */
+
+#include <Elementary.h>
+
+#include <ui/AskUIInterface.h>
+
+struct cert_checker_popup_data {
+    std::string client;
+    std::string user;
+    std::string privilege;
+    AskUser::Agent::UIResponseType result;
+
+    Evas_Object *popup       = NULL;
+    Evas_Object *win         = NULL;
+    Evas_Object *box         = NULL;
+    Evas_Object *title       = NULL;
+    Evas_Object *content     = NULL;
+    Evas_Object *keep_button = NULL;
+    Evas_Object *uninstall_button = NULL;
+    Evas_Object *check_session = NULL;
+    Evas_Object *check_life    = NULL;
+};
index ee23e7f..c13630c 100644 (file)
@@ -31,7 +31,7 @@ namespace AskUser {
 
 namespace Agent {
 
-typedef enum {
+typedef enum : uint8_t {
     URT_NO_ONCE,
     URT_NO_SESSION,
     URT_NO_LIFE,
diff --git a/src/agent/ui/AskUIPopupBackend.cpp b/src/agent/ui/AskUIPopupBackend.cpp
new file mode 100644 (file)
index 0000000..420cf72
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (c) 2015 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.
+ *  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        AskUIPopupBackend.cpp
+ * @author      Adam Malinowski <a.malinowsk2@partner.samsung.com>
+ * @author      Janusz Kozerski <j.kozerski@samsung.com>
+ * @brief       This file implements class for ask user window
+ */
+
+#include <bundle.h>
+#include <cerrno>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <libintl.h>
+#include <privilegemgr/privilege_info.h>
+
+#include <attributes/attributes.h>
+
+#include <log/alog.h>
+#include <ui/AskUIPopupBackend.h>
+
+namespace AskUser {
+
+namespace Agent {
+
+AskUIPopupBackend::AskUIPopupBackend() :
+    m_dismissing(false)
+{}
+
+AskUIPopupBackend::~AskUIPopupBackend(){
+    if (!m_dismissing)
+        dismiss();
+}
+
+bool AskUIPopupBackend::start(const std::string &client, const std::string &user,
+                                     const std::string &privilege, RequestId requestId,
+                                     UIResponseCallback responseCallback) {
+    if (!responseCallback) {
+        ALOGE("Empty response callback is not allowed");
+        return false;
+    }
+    m_responseCallback = responseCallback;
+    m_requestId = requestId;
+
+    char *privilegeDisplayName = NULL;
+    int ret = privilege_info_get_privilege_display_name(privilege.c_str(), &privilegeDisplayName);
+    if (ret != PRVMGR_ERR_NONE) {
+        ALOGE("Unable to get privilege display name, err: [" << ret << "]");
+        privilegeDisplayName = strdup(privilege.c_str());
+    }
+    ALOGD("privilege_info_get_privilege_display_name: [" << ret << "]," " <" << privilegeDisplayName << ">");
+
+    if (!m_popup.run_popup(client, user, std::string(privilegeDisplayName), m_responseTimeout)) {
+        free(privilegeDisplayName);
+        return false;
+    }
+    free(privilegeDisplayName);
+    m_thread = std::thread(&AskUIPopupBackend::run, this);
+    return true;
+}
+
+void AskUIPopupBackend::run() {
+    UIResponseType response = m_popup.wait_for_response();
+    m_responseCallback(m_requestId, response);
+}
+
+bool AskUIPopupBackend::setOutdated() {
+    // There is no possibility to update window
+    return true;
+}
+
+bool AskUIPopupBackend::dismiss() {
+    m_dismissing = true;
+    m_popup.dissmiss();
+    m_thread.join();
+    return true;
+}
+
+} // namespace Agent
+
+} // namespace AskUser
diff --git a/src/agent/ui/AskUIPopupBackend.h b/src/agent/ui/AskUIPopupBackend.h
new file mode 100644 (file)
index 0000000..4043273
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015 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.
+ *    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        AskUIPopupBackend.h
+ * @author      Adam Malinowski <a.malinowsk2@partner.samsung.com>
+ * @author      Janusz Kozerski <j.kozerski@samsung.com>
+ * @brief       This file declares class for ask user window
+ */
+
+#pragma once
+
+#include <atomic>
+#include <thread>
+
+#include <ui/AskUIInterface.h>
+#include <ui/popup-runner.h>
+
+namespace AskUser {
+
+namespace Agent {
+
+class AskUIPopupBackend : public AskUIInterface {
+public:
+    AskUIPopupBackend();
+    virtual ~AskUIPopupBackend();
+
+    virtual bool start(const std::string &client, const std::string &user,
+                       const std::string &privilege, RequestId requestId,
+                       UIResponseCallback responseCallback);
+    virtual bool setOutdated();
+    virtual bool dismiss();
+    virtual bool isDismissing() const {
+        return m_dismissing;
+    }
+
+private:
+    std::thread m_thread;
+    RequestId m_requestId;
+    UIResponseCallback m_responseCallback;
+    static const int m_responseTimeout = 60; // seconds
+    std::atomic<bool> m_dismissing;
+    Popup_runner m_popup;
+
+    void run();
+};
+
+} // namespace Agent
+
+} // namespace AskUser
diff --git a/src/agent/ui/popup-runner.cpp b/src/agent/ui/popup-runner.cpp
new file mode 100644 (file)
index 0000000..c9d6f69
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2015 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.
+ *    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        popup-runner.cpp
+ * @author      Janusz Kozerski (j.kozerski@samsung.com)
+ * @version     1.0
+ */
+
+#include <errno.h>
+#include <iostream>
+#include <signal.h>
+#include <sstream>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <log/alog.h>
+#include <ui/popup-runner.h>
+#include <ui/AskUIPopupBackend.h>
+
+namespace { // anonymous
+
+using namespace AskUser::Agent;
+
+std::string response_to_str(UIResponseType response) {
+    switch (response) {
+    case UIResponseType::URT_NO_ONCE:
+        return "URT_NO_ONCE";
+    case UIResponseType::URT_NO_SESSION:
+        return "URT_NO_SESSION";
+    case UIResponseType::URT_NO_LIFE:
+        return "URT_NO_LIFE";
+    case UIResponseType::URT_YES_ONCE:
+        return "URT_YES_ONCE";
+    case UIResponseType::URT_YES_SESSION:
+        return "URT_YES_SESSION";
+    case UIResponseType::URT_YES_LIFE:
+        return "URT_YES_LIFE";
+    default:
+        return "URT_ERROR";
+    }
+}
+
+void child_process(int fd_send_to_child[2], int fd_send_to_parent[2]) {
+    ALOGD("Child");
+
+    // read data from parent
+    close(fd_send_to_child[1]);
+
+    // send data to parent
+    close(fd_send_to_parent[0]);
+
+    std::stringstream pipe_in_buff;
+    std::stringstream pipe_out_buff;
+    pipe_in_buff  << fd_send_to_parent[1];
+    pipe_out_buff << fd_send_to_child[0];
+    std::string pipe_in  = pipe_in_buff.str();
+    std::string pipe_out = pipe_out_buff.str();
+
+    ALOGD("Passed file descriptors: " << fd_send_to_child[0] << ", "<< fd_send_to_parent[1]);
+
+    if (execl(POPUP_EXEC_PATH, POPUP_EXEC_PATH, pipe_out.c_str(), pipe_in.c_str(), NULL) < 0) {
+        ALOGE("execl FAILED");
+    }
+
+    ALOGE("This should not happen!!!");
+    _exit(UIResponseType::URT_ERROR);
+}
+
+bool send_message_to_child(const BinaryStream &stream, int fd_send_to_child) {
+    ALOGD("Sending message to popup-bin process");
+    unsigned int begin = 0;
+    int tmp;
+    while (begin < stream.size()) {
+        tmp = TEMP_FAILURE_RETRY(write(fd_send_to_child,
+                                       stream.char_pointer() + begin,
+                                       stream.size() - begin));
+        if (tmp == -1) {
+            ALOGE("Write to pipe failed!");
+            return false;
+        }
+        begin += tmp;
+    }
+    ALOGD("Message has been sent");
+    return true;
+}
+
+UIResponseType parse_response(int count, char *data) {
+    ALOGD("RESULT FROM POPUP PIPE (CHILD) : [ " << count << " ]");
+    int response_int;
+    BinaryStream stream_in;
+    stream_in.Write(count, data);
+    try {
+        AskUser::Deserialization::Deserialize(stream_in, response_int);
+    } catch (const std::runtime_error &e) {
+        ALOGE("Cannot deserialize data. " << e.what());
+        return UIResponseType::URT_ERROR;
+    }
+    UIResponseType response = static_cast <UIResponseType> (response_int);
+    ALOGD("response :" << response_to_str(response));
+    return response;
+}
+
+} // anonymous namespace
+
+namespace AskUser {
+namespace Agent {
+
+// BinaryStream class implementation
+void BinaryStream::Read(size_t num, void * bytes) {
+    size_t max_size = m_data.size();
+    if (m_readPosition + num > max_size)
+        throw std::runtime_error("Not enough data to read!");
+    for (size_t i = 0; i < num; ++i)
+        static_cast <unsigned char*> (bytes)[i] = m_data[i + m_readPosition];
+    m_readPosition += num;
+}
+
+void BinaryStream::Write(size_t num, const void * bytes) {
+    for (size_t i = 0; i < num; ++i) {
+        m_data.push_back(static_cast <const unsigned char*> (bytes)[i]);
+    }
+}
+
+BinaryStream::BinaryStream() :
+    m_readPosition(0)
+{}
+
+BinaryStream::~BinaryStream() {}
+
+const unsigned char* BinaryStream::char_pointer() const {
+    return &m_data[0];
+}
+
+size_t BinaryStream::size() const {
+    return m_data.size();
+}
+// BinaryStream
+
+Popup_runner::Popup_runner() :
+    m_popup_pid(-1),
+    m_timeout(60),
+    m_fd_send_to_parent(-1)
+{};
+
+Popup_runner::~Popup_runner() {
+    dissmiss();
+}
+
+bool Popup_runner::wait_for_popup() {
+    int status;
+    int ret;
+
+    sigset_t set;
+    sigemptyset(&set);
+    sigaddset(&set, SIGCHLD);
+    sigprocmask(SIG_BLOCK, &set, NULL);
+    siginfo_t info;
+    struct timespec time = {m_timeout, 0L};
+
+    pid_t popup_pid = m_popup_pid;
+    if (m_popup_pid < 0)
+        return false;
+    else if (m_timeout > 0)
+        ret = TEMP_FAILURE_RETRY(sigtimedwait(&set, &info, &time));
+    else
+        ret = TEMP_FAILURE_RETRY(sigwaitinfo(&set, &info));
+
+    sigprocmask(SIG_UNBLOCK, &set, NULL);
+
+    if  (ret == -1) { // At this point we know, that errno should be equal to EAGAIN
+        ALOGE("POPUP TIMEOUT");
+        goto err;
+    } else if (ret == SIGCHLD && info.si_pid == popup_pid) {
+        m_process_mutex.lock();
+        // call waitpid on the child process to get rid of zombie process
+        waitpid(popup_pid, NULL, 0);
+        m_popup_pid = -1;
+        m_process_mutex.unlock();
+
+        // The proper signal has been caught and its pid matches to popup_pid
+        // Now check the popup exit status
+        status = WEXITSTATUS(info.si_status);
+        ALOGD("STATUS EXIT ON POPUP (CHILD: " << info.si_pid << "): " << status);
+
+        switch (static_cast<popup_status>(status)) {
+        case popup_status::NO_ERROR:
+            ALOGD("NO_ERROR");
+            return true;
+
+        case popup_status::EXIT_ERROR:
+            ALOGD("ERROR");
+            return false;
+
+        default: // Unknown exit status
+           ALOGD("UNKNOWN_ERROR");
+           return false;
+        }
+    }
+    else {
+        ALOGE("Some other signal has been caught (pid: " << info.si_pid << ", signal: " << info.si_signo << ")");
+        goto err;
+    }
+
+err:
+    m_process_mutex.lock();
+    // kill popup process and return error
+    kill(m_popup_pid, SIGKILL);
+    // call waitpid on the child process to get rid of zombie process
+    waitpid(m_popup_pid, NULL, 0);
+    m_popup_pid = -1;
+    m_process_mutex.unlock();
+    return false;
+}
+
+bool Popup_runner::run_popup(const std::string &client,
+        const std::string &user,
+        const std::string &privilege,
+        int timeout) {
+    // serialization
+    BinaryStream stream;
+    AskUser::Serialization::Serialize(stream, client);
+    AskUser::Serialization::Serialize(stream, user);
+    AskUser::Serialization::Serialize(stream, privilege);
+
+    m_timeout = timeout;
+
+    int   fd_send_to_child[2];
+    int   fd_send_to_parent[2];
+
+    if (pipe(fd_send_to_child) != 0) {
+        ALOGE("Cannot create pipes!");
+        return false;
+    }
+    if (pipe(fd_send_to_parent) != 0) {
+        ALOGE("Cannot create pipes!");
+        close(fd_send_to_child[0]);
+        close(fd_send_to_child[1]);
+        return false;
+    }
+
+    // Block SIGCHLD - it's needed to catch the signal in sigtimedwait/sigwaitinfo
+    sigset_t set;
+    sigemptyset(&set);
+    sigaddset(&set, SIGCHLD);
+    sigprocmask(SIG_BLOCK, &set, NULL);
+
+    if ((m_popup_pid = fork()) == -1) {
+        ALOGE("Fork() ERROR");
+        close(fd_send_to_child[0]);
+        close(fd_send_to_parent[1]);
+        goto error;
+    }
+
+    m_fd_send_to_parent = fd_send_to_parent[0];
+
+    if(m_popup_pid == 0) { // Child process
+        child_process (fd_send_to_child, fd_send_to_parent);
+    } else { // Parent process
+        ALOGD("Parent (child pid: " << m_popup_pid << ")");
+
+        // send data to child
+        close(fd_send_to_child[0]);
+
+        // read data from child
+        close(fd_send_to_parent[1]);
+
+        // writing to child
+        if (!send_message_to_child(stream, fd_send_to_child[1])) {
+            // kill popup if sending data has failed
+            kill(m_popup_pid, SIGKILL);
+            // call waitpid on the child process to get rid of zombie process
+            waitpid(m_popup_pid, NULL, 0);
+            m_popup_pid = -1;
+            goto error;
+        }
+        close(fd_send_to_child[1]); /* Reader will see EOF */
+        fd_send_to_child[1] = -1;
+
+        return true;
+    }
+
+    ALOGE("This should not happen!!!");
+error:
+    // cleanup
+    ALOGD("popup-runner: EXIT ERROR");
+    close(fd_send_to_parent[0]);
+    close(fd_send_to_child[1]);
+    return false;
+}
+
+UIResponseType Popup_runner::wait_for_response() {
+    UIResponseType response = UIResponseType::URT_ERROR;
+
+    int buff_size = 1024;
+    char result[buff_size];
+    size_t count = 0;
+
+    // wait for child
+    if (!wait_for_popup())
+        goto error;
+
+    // Read message from popup (child)
+    int tmp;
+    do {
+        tmp = TEMP_FAILURE_RETRY(read(m_fd_send_to_parent, result + count, buff_size - count));
+        if (tmp < 0) {
+            ALOGE("Error while reading popup response, read returned: " << tmp);
+            ALOGE("errno: " << strerror(errno));
+            goto error;
+        }
+        if (tmp > 0) {
+            count += tmp;
+        }
+    } while (tmp != 0);
+
+    // Parsing response from child
+    if (count >= sizeof(UIResponseType))
+        response = parse_response(count, result);
+    else {
+        ALOGD("ERROR, count = " << count);;
+        goto error;
+    }
+
+    ALOGD("popup-runner: EXIT SUCCESS");
+    // cleanup
+error:
+    close(m_fd_send_to_parent);
+    m_fd_send_to_parent = -1;
+    m_popup_pid = -1;
+    return response;
+}
+
+bool Popup_runner::dissmiss() {
+
+    m_process_mutex.lock();
+    if (m_popup_pid > 0) {
+        kill(m_popup_pid, SIGKILL);
+        // wait_for_response() calls wait() internally
+        m_popup_pid = -1;
+    }
+    m_process_mutex.unlock();
+
+    return true;
+}
+
+} // UI
+} // CCHECKER
diff --git a/src/agent/ui/popup-runner.h b/src/agent/ui/popup-runner.h
new file mode 100644 (file)
index 0000000..133d202
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015 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.
+ *    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        popup-runner.h
+ * @author      Janusz Kozerski (j.kozerski@samsung.com)
+ * @version     1.0
+ */
+
+#pragma once
+
+#include <mutex>
+#include <vector>
+
+#include <ui/AskUIInterface.h>
+#include <ui/serialization.h>
+
+namespace AskUser {
+
+namespace Agent {
+
+enum popup_status : int {
+    NO_ERROR   = 0,
+    EXIT_ERROR = 1
+};
+
+class BinaryStream : public AskUser::IStream {
+public:
+    void Read (size_t num,       void * bytes);
+    void Write(size_t num, const void * bytes);
+
+    BinaryStream();
+    ~BinaryStream();
+
+    const unsigned char* char_pointer() const;
+    size_t size() const;
+
+private:
+    std::vector<unsigned char> m_data;
+    size_t m_readPosition;
+};
+
+class Popup_runner {
+public:
+    Popup_runner();
+    virtual ~Popup_runner();
+    bool run_popup(const std::string &client,
+                   const std::string &user,
+                   const std::string &privilege,
+                   int timeout); // zero or negative timeout means infinity
+    UIResponseType wait_for_response();
+    bool dissmiss();
+
+private:
+    std::mutex m_process_mutex;
+    pid_t m_popup_pid;
+    int m_timeout;
+    int m_fd_send_to_parent;
+
+    bool wait_for_popup();
+};
+
+} // Agent
+} // AskUser
diff --git a/src/agent/ui/serialization.cpp b/src/agent/ui/serialization.cpp
new file mode 100644 (file)
index 0000000..89ebaff
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2011 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.
+ *    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        serialization.cpp
+ * @author      Tomasz Swierczek (t.swierczek@samsung.com)
+ * @version     1.0
+ * @brief       This file is the implementation file of data serialization.
+ */
+#include <stddef.h>
+#include "serialization.h"
+
+//
+// Note:
+//
+// The file here is left blank to enable precompilation
+// of templates in corresponding header file.
+// Do not remove this file.
+//
diff --git a/src/agent/ui/serialization.h b/src/agent/ui/serialization.h
new file mode 100644 (file)
index 0000000..222fa4b
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 2011-2015 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.
+ *    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    serialization.h
+ * @author  Tomasz Swierczek (t.swierczek@samsung.com)
+ * @version 1.0
+ * @brief   Interfaces and templates used for data serialization.
+ */
+#ifndef ASKUSER_SERIALIZATION_H
+#define ASKUSER_SERIALIZATION_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <list>
+#include <map>
+#include <memory>
+
+namespace AskUser {
+// Abstract data stream buffer
+class IStream
+{
+  public:
+    virtual void Read(size_t num, void * bytes) = 0;
+    virtual void Write(size_t num, const void * bytes) = 0;
+    virtual ~IStream(){}
+};
+
+// Serializable interface
+class ISerializable
+{
+  public:
+    /*    ISerializable(){};
+     *    ISerializable(IStream&){}; */
+    virtual void Serialize(IStream &) const = 0;
+    virtual ~ISerializable(){}
+};
+
+struct Serialization {
+    // serialization
+    // normal functions
+
+    // ISerializable objects
+    static void Serialize(IStream& stream, const ISerializable& object)
+    {
+        object.Serialize(stream);
+    }
+
+    static void Serialize(IStream& stream, const ISerializable* const object)
+    {
+        object->Serialize(stream);
+    }
+
+    // char
+    static void Serialize(IStream& stream, const char value)
+    {
+        stream.Write(sizeof(value), &value);
+    }
+    static void Serialize(IStream& stream, const char* const value)
+    {
+        stream.Write(sizeof(*value), value);
+    }
+
+    // unsigned char
+    static void Serialize(IStream& stream, const unsigned char value)
+    {
+        stream.Write(sizeof(value), &value);
+    }
+    static void Serialize(IStream& stream, const unsigned char* const value)
+    {
+        stream.Write(sizeof(*value), value);
+    }
+
+    // unsigned int32
+    static void Serialize(IStream& stream, const uint32_t value)
+    {
+        stream.Write(sizeof(value), &value);
+    }
+    static void Serialize(IStream& stream, const uint32_t* const value)
+    {
+        stream.Write(sizeof(*value), value);
+    }
+
+    // int32
+    static void Serialize(IStream& stream, const int32_t value)
+    {
+        stream.Write(sizeof(value), &value);
+    }
+    static void Serialize(IStream& stream, const int32_t* const value)
+    {
+        stream.Write(sizeof(*value), value);
+    }
+
+    // unsigned int64
+    static void Serialize(IStream& stream, const uint64_t value)
+    {
+        stream.Write(sizeof(value), &value);
+    }
+    static void Serialize(IStream& stream, const uint64_t* const value)
+    {
+        stream.Write(sizeof(*value), value);
+    }
+
+    // int64
+    static void Serialize(IStream& stream, const int64_t value)
+    {
+        stream.Write(sizeof(value), &value);
+    }
+    static void Serialize(IStream& stream, const int64_t* const value)
+    {
+        stream.Write(sizeof(*value), value);
+    }
+
+    // bool
+    static void Serialize(IStream& stream, const bool value)
+    {
+        stream.Write(sizeof(value), &value);
+    }
+    static void Serialize(IStream& stream, const bool* const value)
+    {
+        stream.Write(sizeof(*value), value);
+    }
+
+    // std::string
+    template <typename T, typename R, typename A>
+    static void Serialize(IStream& stream, const std::basic_string<T,R,A>& str)
+    {
+        int length = str.size();
+        stream.Write(sizeof(length), &length);
+        stream.Write(length*sizeof(T), str.data());
+    }
+
+    template<typename T, typename R, typename A>
+    static void Serialize(IStream& stream, const std::basic_string<T,R,A>* const str)
+    {
+        int length = str->size();
+        stream.Write(sizeof(length), &length);
+        stream.Write(length*sizeof(T), str->data());
+    }
+
+    // STL templates
+
+    // std::list
+    template <typename T>
+    static void Serialize(IStream& stream, const std::list<T>& list)
+    {
+        int length = list.size();
+        stream.Write(sizeof(length), &length);
+        for (typename std::list<T>::const_iterator list_iter = list.begin();
+             list_iter != list.end(); list_iter++)
+        {
+            Serialize(stream, *list_iter);
+        }
+    }
+    template <typename T>
+    static void Serialize(IStream& stream, const std::list<T>* const list)
+    {
+        Serialize(stream, *list);
+    }
+
+    // RawBuffer
+    template <typename A>
+    static void Serialize(IStream& stream, const std::vector<unsigned char, A>& vec)
+    {
+        int length = vec.size();
+        stream.Write(sizeof(length), &length);
+        stream.Write(length, vec.data());
+    }
+
+    template <typename A>
+    static void Serialize(IStream& stream, const std::vector<unsigned char, A>* const vec)
+    {
+        Serialize(stream, *vec);
+    }
+
+    // std::vector
+    template <typename T, typename A>
+    static void Serialize(IStream& stream, const std::vector<T, A>& vec)
+    {
+        int length = vec.size();
+        stream.Write(sizeof(length), &length);
+        for (const auto &i : vec)
+        {
+            Serialize(stream, i);
+        }
+    }
+    template <typename T, typename A>
+    static void Serialize(IStream& stream, const std::vector<T, A>* const vec)
+    {
+        Serialize(stream, *vec);
+    }
+
+    // std::pair
+    template <typename A, typename B>
+    static void Serialize(IStream& stream, const std::pair<A, B>& p)
+    {
+        Serialize(stream, p.first);
+        Serialize(stream, p.second);
+    }
+    template <typename A, typename B>
+    static void Serialize(IStream& stream, const std::pair<A, B>* const p)
+    {
+        Serialize(stream, *p);
+    }
+
+    // std::map
+    template <typename K, typename T>
+    static void Serialize(IStream& stream, const std::map<K, T>& map)
+    {
+        int length = map.size();
+        stream.Write(sizeof(length), &length);
+        typename std::map<K, T>::const_iterator it;
+        for (it = map.begin(); it != map.end(); ++it) {
+            Serialize(stream, (*it).first);
+            Serialize(stream, (*it).second);
+        }
+    }
+    template <typename K, typename T>
+    static void Serialize(IStream& stream, const std::map<K, T>* const map)
+    {
+        Serialize(stream, *map);
+    }
+
+    // std::unique_ptr
+    template <typename T>
+    static void Serialize(IStream& stream, const std::unique_ptr<T>& p)
+    {
+        Serialize(stream, *p);
+    }
+
+}; // struct Serialization
+
+struct Deserialization {
+    // deserialization
+    // normal functions
+
+    // ISerializable objects
+    // T instead of ISerializable is needed to call proper constructor
+    template <typename T>
+    static void Deserialize(IStream& stream, T& object)
+    {
+        object = T(stream);
+    }
+    template <typename T>
+    static void Deserialize(IStream& stream, T*& object)
+    {
+        object = new T(stream);
+    }
+
+    // char
+    static void Deserialize(IStream& stream, char& value)
+    {
+        stream.Read(sizeof(value), &value);
+    }
+    static void Deserialize(IStream& stream, char*& value)
+    {
+        value = new char;
+        stream.Read(sizeof(*value), value);
+    }
+
+    // unsigned char
+    static void Deserialize(IStream& stream, unsigned char& value)
+    {
+        stream.Read(sizeof(value), &value);
+    }
+    static void Deserialize(IStream& stream, unsigned char*& value)
+    {
+        value = new unsigned char;
+        stream.Read(sizeof(*value), value);
+    }
+
+    // unsigned int32
+    static void Deserialize(IStream& stream, uint32_t& value)
+    {
+        stream.Read(sizeof(value), &value);
+    }
+    static void Deserialize(IStream& stream, uint32_t*& value)
+    {
+        value = new uint32_t;
+        stream.Read(sizeof(*value), value);
+    }
+
+    // int32
+    static void Deserialize(IStream& stream, int32_t& value)
+    {
+        stream.Read(sizeof(value), &value);
+    }
+    static void Deserialize(IStream& stream, int32_t*& value)
+    {
+        value = new int32_t;
+        stream.Read(sizeof(*value), value);
+    }
+
+    // unsigned int64
+    static void Deserialize(IStream& stream, uint64_t& value)
+    {
+        stream.Read(sizeof(value), &value);
+    }
+    static void Deserialize(IStream& stream, uint64_t*& value)
+    {
+        value = new uint64_t;
+        stream.Read(sizeof(*value), value);
+    }
+
+    // int64
+    static void Deserialize(IStream& stream, int64_t& value)
+    {
+        stream.Read(sizeof(value), &value);
+    }
+    static void Deserialize(IStream& stream, int64_t*& value)
+    {
+        value = new int64_t;
+        stream.Read(sizeof(*value), value);
+    }
+
+    // bool
+    static void Deserialize(IStream& stream, bool& value)
+    {
+        stream.Read(sizeof(value), &value);
+    }
+    static void Deserialize(IStream& stream, bool*& value)
+    {
+        value = new bool;
+        stream.Read(sizeof(*value), value);
+    }
+
+    template <typename T, typename R, typename A>
+    static void Deserialize(IStream& stream, std::basic_string<T,R,A>& str)
+    {
+        int length;
+        stream.Read(sizeof(length), &length);
+        std::vector<T> buf(length);
+        stream.Read(length*sizeof(T), buf.data());
+        str = std::basic_string<T,R,A>(buf.data(), buf.data()+length);
+    }
+
+    template <typename T, typename R, typename A>
+    static void Deserialize(IStream& stream, std::basic_string<T,R,A>*& str)
+    {
+        int length;
+        stream.Read(sizeof(length), &length);
+        std::vector<T> buf(length);
+        stream.Read(length*sizeof(T), buf.data());
+        str = new std::basic_string<T,R,A>(buf.data(), buf.data()+length);
+    }
+
+    // STL templates
+
+    // std::list
+    template <typename T>
+    static void Deserialize(IStream& stream, std::list<T>& list)
+    {
+        int length;
+        stream.Read(sizeof(length), &length);
+        for (int i = 0; i < length; ++i) {
+            T obj;
+            Deserialize(stream, obj);
+            list.push_back(std::move(obj));
+        }
+    }
+    template <typename T>
+    static void Deserialize(IStream& stream, std::list<T>*& list)
+    {
+        list = new std::list<T>;
+        Deserialize(stream, *list);
+    }
+
+    // RawBuffer
+    template <typename A>
+    static void Deserialize(IStream& stream, std::vector<unsigned char, A>& vec)
+    {
+        int length;
+        stream.Read(sizeof(length), &length);
+        vec.resize(length);
+        stream.Read(length, vec.data());
+    }
+
+    template <typename A>
+    static void Deserialize(IStream& stream, std::vector<unsigned char, A>*& vec)
+    {
+        vec = new std::vector<unsigned char,A>;
+        Deserialize(stream, *vec);
+    }
+
+    // std::vector
+    template <typename T, typename A>
+    static void Deserialize(IStream& stream, std::vector<T,A>& vec)
+    {
+        int length;
+        stream.Read(sizeof(length), &length);
+        for (int i = 0; i < length; ++i) {
+            T obj;
+            Deserialize(stream, obj);
+            vec.push_back(std::move(obj));
+        }
+    }
+    template <typename T, typename A>
+    static void Deserialize(IStream& stream, std::vector<T,A>*& vec)
+    {
+        vec = new std::vector<T,A>;
+        Deserialize(stream, *vec);
+    }
+
+    // std::pair
+    template <typename A, typename B>
+    static void Deserialize(IStream& stream, std::pair<A, B>& p)
+    {
+        Deserialize(stream, p.first);
+        Deserialize(stream, p.second);
+    }
+    template <typename A, typename B>
+    static void Deserialize(IStream& stream, std::pair<A, B>*& p)
+    {
+        p = new std::pair<A, B>;
+        Deserialize(stream, *p);
+    }
+
+    // std::map
+    template <typename K, typename T>
+    static void Deserialize(IStream& stream, std::map<K, T>& map)
+    {
+        int length;
+        stream.Read(sizeof(length), &length);
+        for (int i = 0; i < length; ++i) {
+            K key;
+            T obj;
+            Deserialize(stream, key);
+            Deserialize(stream, obj);
+            map[key] = std::move(obj);
+        }
+    }
+    template <typename K, typename T>
+    static void Deserialize(IStream& stream, std::map<K, T>*& map)
+    {
+        map = new std::map<K, T>;
+        Deserialize(stream, *map);
+    }
+}; // struct Deserialization
+
+// generic serialization
+template <typename... Args>
+struct Serializer;
+
+template <typename First, typename... Args>
+struct Serializer<First, Args...> : public Serializer<Args...> {
+    static void Serialize(IStream& stream, const First& f, const Args&... args) {
+        Serialization::Serialize(stream, f);
+        Serializer<Args...>::Serialize(stream, args...);
+    }
+};
+
+// end of recursion
+template <>
+struct Serializer<> {
+    static void Serialize(IStream&) {
+        return;
+    }
+};
+
+// generic deserialization
+template <typename... Args>
+struct Deserializer;
+
+template <typename First, typename... Args>
+struct Deserializer<First, Args...> : public Deserializer<Args...> {
+    static void Deserialize(IStream& stream, First& f, Args&... args) {
+        Deserialization::Deserialize(stream, f);
+        Deserializer<Args...>::Deserialize(stream, args...);
+    }
+};
+
+// end of recursion
+template <>
+struct Deserializer<> {
+    static void Deserialize(IStream&) {
+        return;
+    }
+};
+
+} // AskUser
+
+#endif // ASKUSER_SERIALIZATION_H
index 5ee43f8..098372e 100644 (file)
@@ -13,6 +13,9 @@ Restart=always
 UMask=0000
 NoNewPrivileges=true
 
+EnvironmentFile=-/etc/sysconfig/enlightenment
+# Makes popup bigger
+Environment="ELM_SCALE=4.0"
 #Environment="ASKUSER_LOG_LEVEL=LOG_DEBUG"
 
 [Install]