SET(POPUP_SRCS
popup.cpp
+ ecore-mainloop.cpp
)
ADD_EXECUTABLE(${TARGET_TPKP_POPUP} ${POPUP_SRCS})
--- /dev/null
+/*
+ * Copyright (c) 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.
+ * 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.
+ */
+
+#include "ecore-mainloop.h"
+
+#include <utility>
+#include <exception>
+
+#include "tpkp_popup_logger.h"
+
+namespace Csr {
+
+Ecore_Fd_Handler_Flags EcoreMainloop::convertFlags(Mainloop::Event events)
+{
+ uint32_t flags = 0;
+
+ if ((events & Mainloop::Event::READ) != Mainloop::Event::NONE)
+ flags |= ECORE_FD_READ;
+ if ((events & Mainloop::Event::WRITE) != Mainloop::Event::NONE)
+ flags |= ECORE_FD_WRITE;
+
+ return static_cast<Ecore_Fd_Handler_Flags>(flags);
+}
+
+Mainloop::Event EcoreMainloop::convertFlags(Ecore_Fd_Handler *handler)
+{
+ Mainloop::Event flags = Mainloop::Event::NONE;
+
+ if (ecore_main_fd_handler_active_get(handler, ECORE_FD_READ))
+ flags |= Mainloop::Event::READ;
+ if (ecore_main_fd_handler_active_get(handler, ECORE_FD_WRITE))
+ flags |= Mainloop::Event::WRITE;
+ if (ecore_main_fd_handler_active_get(handler, ECORE_FD_ERROR))
+ flags |= Mainloop::Event::CLOSE;
+
+ return flags;
+}
+
+EcoreMainloop::EcoreMainloop() : m_isExitCalled(false), m_timer(nullptr) {}
+
+EcoreMainloop::~EcoreMainloop()
+{
+ if (this->m_timer)
+ ecore_timer_del(this->m_timer);
+
+ if (!this->m_isExitCalled)
+ elm_exit();
+}
+
+void EcoreMainloop::run(int timeout)
+{
+ this->dispatch(timeout);
+}
+
+void EcoreMainloop::dispatch(int timeout)
+{
+ if (timeout >= 0) {
+ this->m_timer = ecore_timer_add(
+ static_cast<double>(timeout), &EcoreMainloop::ecoreTimeoutCb, this);
+
+ if (this->m_timer == nullptr) {
+ SLOGE("Failed to ecore_timer_add()...");
+ }
+ }
+
+ elm_run();
+}
+
+void EcoreMainloop::addEventSource(
+ int fd, Mainloop::Event events, Mainloop::Callback &&callback)
+{
+ auto handler = ecore_main_fd_handler_add(
+ fd, convertFlags(events),
+ &EcoreMainloop::ecoreFdCallback, this, nullptr, nullptr);
+
+ if (handler == nullptr) {
+ SLOGE("failed to handle fd(%d) by ecore_main_fd_handler_add()", fd);
+ return;
+ }
+
+ std::unique_ptr<EventSource> source(new EventSource);
+ source->callback = std::move(callback);
+ source->handler = handler;
+
+ this->m_sources[fd] = std::move(source);
+}
+
+void EcoreMainloop::removeEventSource(int fd)
+{
+ auto it = this->m_sources.find(fd);
+
+ if (it == this->m_sources.end()) {
+ SLOGE("fd(%d) associated source is not found", fd);
+ return;
+ }
+
+ ecore_main_fd_handler_del(it->second->handler);
+
+ this->m_sources.erase(it);
+}
+
+Eina_Bool EcoreMainloop::ecoreFdCallback(void *data, Ecore_Fd_Handler *handler)
+{
+ auto mainloop = static_cast<EcoreMainloop *>(data);
+
+ if (mainloop == nullptr)
+ throw std::invalid_argument("userdata for ecore fd callback is invalid");
+
+ int fd = ecore_main_fd_handler_fd_get(handler);
+
+ auto it = mainloop->m_sources.find(fd);
+ if (it == mainloop->m_sources.end() || it->second == nullptr) {
+ SLOGE("no associated source found with fd: %d", fd);
+ return ECORE_CALLBACK_CANCEL;
+ }
+
+ it->second->callback(convertFlags(handler));
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+Eina_Bool EcoreMainloop::ecoreTimeoutCb(void *data)
+{
+ auto mainloop = static_cast<EcoreMainloop *>(data);
+
+ if (mainloop == nullptr)
+ throw std::invalid_argument("userdata for ecore timeout callback is invalid");
+
+ if (mainloop->m_sources.size() == mainloop->m_domainSourceNum) {
+ SLOGI("There's no alive connection. Only listening socket opened. "
+ "Let's exit the ecore main loop!");
+
+ elm_exit();
+
+ mainloop->m_timer = nullptr;
+ mainloop->m_isExitCalled = true;
+
+ return ECORE_CALLBACK_CANCEL;
+ } else {
+ SLOGI("Time out expired but there's alive connection "
+ "so go ahead to next tick!");
+ return ECORE_CALLBACK_RENEW;
+ }
+}
+
+} // namespace Csr
--- /dev/null
+/*
+ * Copyright (c) 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.
+ * 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.
+ */
+#pragma once
+
+#include <unordered_map>
+#include <memory>
+#include <Elementary.h>
+
+#include "mainloop.h"
+
+namespace Csr {
+
+class EcoreMainloop : public Mainloop {
+public:
+ EcoreMainloop();
+ virtual ~EcoreMainloop();
+
+ virtual void run(int timeout) override;
+
+ virtual void addEventSource(
+ int fd, Mainloop::Event event, Mainloop::Callback &&callback) override;
+ virtual void removeEventSource(int fd) override;
+
+private:
+ struct EventSource {
+ Mainloop::Callback callback;
+ Ecore_Fd_Handler *handler;
+ };
+
+ void dispatch(int timeout);
+
+ static Ecore_Fd_Handler_Flags convertFlags(Mainloop::Event events);
+ static Mainloop::Event convertFlags(Ecore_Fd_Handler *handler);
+
+ static Eina_Bool ecoreFdCallback(void *data, Ecore_Fd_Handler *handler);
+ static Eina_Bool ecoreTimeoutCb(void *data);
+
+ std::unordered_map<int, std::unique_ptr<EventSource>> m_sources;
+
+ bool m_isExitCalled;
+ Ecore_Timer *m_timer;
+};
+
+} // namespace Csr
--- /dev/null
+/*
+ * Copyright (c) 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.
+ * 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.
+ */
+#pragma once
+
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+namespace Csr {
+
+class Mainloop {
+public:
+ enum class Event : uint32_t {
+ NONE = 0,
+ READ = 1 << 0,
+ WRITE = 1 << 1,
+ CLOSE = 1 << 2
+ };
+
+ using Callback = std::function<void(Event event)>;
+
+ Mainloop() : m_domainSourceNum(0) {}
+ virtual ~Mainloop() {}
+
+ Mainloop(const Mainloop &) = delete;
+ Mainloop &operator=(const Mainloop &) = delete;
+ Mainloop(Mainloop &&) = delete;
+ Mainloop &operator=(Mainloop &&) = delete;
+
+ // timeout unit: seconds
+ // if timeout is negative value, no timeout on idle.
+ virtual void run(int timeout) = 0;
+
+ virtual void addEventSource(int fd, Event event, Callback &&callback) = 0;
+ virtual void removeEventSource(int fd) = 0;
+
+ void addDomainEventSource(int fd, Event event, Callback &&callback)
+ {
+ ++this->m_domainSourceNum;
+ this->addEventSource(fd, event, std::move(callback));
+ }
+
+protected:
+ size_t m_domainSourceNum;
+};
+
+template<typename T> using Underlying = typename std::underlying_type<T>::type;
+template<typename T>
+constexpr Underlying<T> underlying(T t) { return Underlying<T>(t); }
+
+inline constexpr Mainloop::Event operator&(Mainloop::Event e1, Mainloop::Event e2)
+{
+ return Mainloop::Event(underlying(e1) & underlying(e2));
+}
+
+inline Mainloop::Event &operator&=(Mainloop::Event &e1, Mainloop::Event e2)
+{
+ return e1 = e1 & e2;
+}
+
+inline constexpr Mainloop::Event operator|(Mainloop::Event e1, Mainloop::Event e2)
+{
+ return Mainloop::Event(underlying(e1) | underlying(e2));
+}
+
+inline Mainloop::Event &operator|=(Mainloop::Event &e1, Mainloop::Event e2)
+{
+ return e1 = e1 | e2;
+}
+
+inline constexpr Mainloop::Event operator~(Mainloop::Event e)
+{
+ return Mainloop::Event(~underlying(e));
+}
+
+} // namespace Csr
* @author Janusz Kozerski (j.kozerski@samsung.com)
* @version 1.0
*/
-#include <unistd.h>
#include <vector>
#include <memory>
#include <string>
#include <functional>
+#include <map>
+#include <unistd.h>
#include <libintl.h>
-#include <poll.h>
#include <sys/un.h>
#include <time.h>
#include <vconf.h>
#include "tpkp_exception.h"
-#include "tpkp_logger.h"
+#include "tpkp_popup_logger.h"
+#include "ecore-mainloop.h"
#include "ui/popup_common.h"
-#ifdef LOG_TAG
-#undef LOG_TAG
-#endif
-#define LOG_TAG "TPKP_POPUP"
-
using namespace TPKP::UI;
namespace {
/* inputs */
std::string hostname;
int timeout;
-
/* internal data fields */
Evas_Object *win;
/* output */
TPKP::UI::Response result;
- TpkpPopup() :
+ int fd;
+
+ TpkpPopup(int fd) :
hostname(),
timeout(-1),
win(nullptr),
- result(TPKP::UI::Response::ERROR) {}
+ result(TPKP::UI::Response::ERROR),
+ fd(fd) {}
};
+std::map<int, std::unique_ptr<TpkpPopup>> g_pdpMap;
+
struct SockRaii {
int sock;
SockRaii() : sock(-1) {}
{
SLOGD("elm_init()");
elm_init(argc, argv);
-
- elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
}
virtual ~ElmRaii()
}
};
+void deserialize(TpkpPopup *pdp, BinaryStream &stream)
+{
+ Deserialization::Deserialize(stream, pdp->hostname);
+ Deserialization::Deserialize(stream, pdp->timeout);
+
+ SLOGD("Params from popup_runner: hostname[%s] timeout[%d]",
+ pdp->hostname.c_str(), pdp->timeout);
+}
+
+BinaryStream serialize(TpkpPopup *pdp)
+{
+ BinaryStream stream;
+ Serialization::Serialize(stream, static_cast<int>(pdp->result));
+
+ return stream;
+}
+
void answerAllowCb(void *data, Evas_Object * /* obj */, void * /* event_info */)
{
SLOGD("allow answer");
}
}
+void onDone(void *data, Evas *, Evas_Object *, void *)
+{
+ TPKP_CHECK_THROW_EXCEPTION(data != nullptr,
+ TPKP_E_INVALID_PARAMETER, "data shouldn't be null on evas callbacks");
+
+ TpkpPopup *pdp = static_cast<TpkpPopup *>(data);
+
+ SLOGD("pdp->result : %d", static_cast<int>(pdp->result));
+
+ /* send result */
+ sendStream(pdp->fd, serialize(pdp));
+}
+
Eina_Bool timeoutCb(void *data)
{
TPKP_CHECK_THROW_EXCEPTION(data != nullptr,
return ECORE_CALLBACK_CANCEL;
}
-CstringPtr getPopupContentString(TpkpPopup *pdp)
+CstringPtr getPopupContentString(const std::string &hostname)
{
char *contentFormat = dgettext(PROJECT_NAME, "SID_CONTENT_PUBLIC_KEY_MISMATCHED");
char *content = nullptr;
- if (asprintf(&content, contentFormat, pdp->hostname.c_str()) == -1)
+ if (asprintf(&content, contentFormat, hostname.c_str()) == -1)
TPKP_THROW_EXCEPTION(TPKP_E_MEMORY,
"Failed to alloc memory for popup text");
evas_object_show(win);
/* create popup */
- auto contentString = getPopupContentString(pdp);
+ auto contentString = getPopupContentString(pdp->hostname);
Evas_Object *popup = elm_popup_add(win);
evas_object_size_hint_weight_set(popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_object_text_set(popup, contentString.get());
evas_object_show(buttonDeny);
if (pdp->timeout > 0)
- ecore_timer_add(pdp->timeout / 1000, timeoutCb, pdp);
+ ecore_timer_add(static_cast<double>(pdp->timeout) / 1000, timeoutCb, pdp);
pdp->win = win;
- SLOGD("elm_run start");
- elm_run();
-}
-
-/*
- * child receive list
- * - std::string hostname
- */
-void deserialize(TpkpPopup *pdp, BinaryStream &stream)
-{
- Deserialization::Deserialize(stream, pdp->hostname);
- Deserialization::Deserialize(stream, pdp->timeout);
-
- SLOGD("Params from popup_runner: hostname[%s] timeout[%d]",
- pdp->hostname.c_str(), pdp->timeout);
-}
-
-/*
- * child send list
- * - TPKP::UI::Response response (int)
- */
-BinaryStream serialize(TpkpPopup *pdp)
-{
- BinaryStream stream;
- Serialization::Serialize(stream, static_cast<int>(pdp->result));
-
- return stream;
+ evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, onDone, pdp);
}
int getSockFromSystemd(void)
TPKP_THROW_EXCEPTION(TPKP_E_IO, "Failed to get sock from systemd.");
}
+void onClose(int fd, Csr::Mainloop *loop)
+{
+ close(fd);
+
+ loop->removeEventSource(fd);
+ g_pdpMap.erase(fd);
+}
+
+void onRequest(int fd)
+{
+ auto it = g_pdpMap.find(fd);
+ if (it == g_pdpMap.end())
+ it = g_pdpMap.insert(std::make_pair(
+ fd, std::unique_ptr<TpkpPopup>(new TpkpPopup(fd)))).first;
+
+ /* receive arguments */
+ auto stream = receiveStream(fd);
+ deserialize(it->second.get(), stream);
+
+ showPopup(it->second.get());
+}
+
+void onAccept(int domainSockFd, Csr::Mainloop *loop)
+{
+ struct sockaddr_un client;
+ memset(&client, 0, sizeof(struct sockaddr));
+ socklen_t len = sizeof(client);
+
+ int fd = accept(domainSockFd, reinterpret_cast<struct sockaddr *>(&client), &len);
+ TPKP_CHECK_THROW_EXCEPTION(fd >= 0, TPKP_E_IO, "error in accept().");
+
+ SLOGD("client accepted with fd: %d", fd);
+
+ loop->addEventSource(fd,
+ Csr::Mainloop::Event::READ | Csr::Mainloop::Event::CLOSE,
+ [&, fd, loop](Csr::Mainloop::Event events) {
+ if ((events & Csr::Mainloop::Event::CLOSE) != Csr::Mainloop::Event::NONE) {
+ onClose(fd, loop);
+ return;
+ }
+
+ onRequest(fd);
+ }
+ );
+}
+
} // namespace anonymous
int main(int argc, char **argv)
setlocale(LC_ALL, vconf_get_str(VCONFKEY_LANGSET));
try {
- struct sockaddr_un clientaddr;
- int client_len = sizeof(clientaddr);
-
- struct pollfd fds[1];
- fds[0].fd = getSockFromSystemd();
- fds[0].events = POLLIN;
-
- SLOGD("server fd from systemd: %d", fds[0].fd);
-
- while (true) {
- /* non blocking poll */
- int ret = poll(fds, 1, 0);
- if (ret < 0) {
- TPKP_THROW_EXCEPTION(TPKP_E_IO, "poll() error. errno: " << errno);
- } else if (ret == 0) {
- SLOGD("tpkp-popup backend service timeout. Let's be deactivated");
- return 0;
- }
-
- /* ready to accept! */
-
- memset(&clientaddr, 0, client_len);
+ std::unique_ptr<Csr::Mainloop> loop(new Csr::EcoreMainloop);
- int clientFd = accept(fds[0].fd, (struct sockaddr *)&clientaddr, (socklen_t *)&client_len);
- TPKP_CHECK_THROW_EXCEPTION(clientFd >= 0, TPKP_E_IO, "Error in func accept()");
- SLOGD("client accepted with fd: %d", clientFd);
+ SockRaii domainSock(getSockFromSystemd());
- SockRaii clientSock(clientFd);
+ loop->addDomainEventSource(
+ domainSock.sock, Csr::Mainloop::Event::READ | Csr::Mainloop::Event::CLOSE,
+ [&](Csr::Mainloop::Event events) {
+ if ((events & Csr::Mainloop::Event::READ) == Csr::Mainloop::Event::NONE)
+ return;
- TpkpPopup pd;
- TpkpPopup *pdp = &pd;
-
- /* receive arguments */
- BinaryStream stream = receiveStream(clientFd);
- deserialize(pdp, stream);
-
- /* get user response */
- showPopup(pdp);
- SLOGD("pdp->result : %d", static_cast<int>(pdp->result));
+ onAccept(domainSock.sock, loop.get());
+ }
+ );
- /* send result */
- stream = serialize(pdp);
- sendStream(clientFd, stream);
+ loop->run(10);
- SLOGD("tpkp-popup done successfully!");
- }
+ SLOGI("turned off by timeout...");
} catch (const TPKP::Exception &e) {
SLOGE("Exception[%d]: %s", e.code(), e.what());
} catch (const std::bad_alloc &e) {
--- /dev/null
+/*
+ * 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 tpkp_popup_logger.h
+ * @author Kyungwook Tak (k.tak@samsung.com)
+ * @version 1.0
+ * @brief Tizen Https Public Key Pinning dlog wrapper for popup service.
+ */
+#pragma once
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "TPKP_POPUP"
+
+#include <dlog.h>
try {
SLOGD("hostname: %s", hostname.c_str());
+ if (timeout <= 0)
+ timeout = -1;
+ else if (timeout < TIMEOUT_MS_MIN)
+ timeout = TIMEOUT_MS_MIN;
+
TpkpPopupParent pd;
TpkpPopupParent *pdp = &pd;
BinaryStream inStream = serialize(pdp);
- if (timeout <= 0)
- timeout = -1;
- else if (timeout < TIMEOUT_MS_MIN)
- timeout = TIMEOUT_MS_MIN;
-
- ServiceConnection connection(SOCK_PATH, timeout);
+ // popup timeout is managed by popup service side also so
+ // have some(1 second) spare to give change to popup service to timed out.
+ ServiceConnection connection(SOCK_PATH,
+ (timeout > 0) ? (timeout + 1000) : timeout);
BinaryStream outStream = connection.processRequest(inStream);
deserialize(pdp, outStream);