From: Piotr Sawicki Date: Wed, 5 Jul 2017 11:30:43 +0000 (+0200) Subject: Implement client side asynchronous channel X-Git-Tag: submit/tizen/20170727.154157~1^2~41 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=bae242841bf973b5b672a9c33918b14f8ef4b13c;p=platform%2Fcore%2Fsecurity%2Faskuser.git Implement client side asynchronous channel * Implement an abstract channel and async client/server channels * Adjust other modules to this new changes Change-Id: I6788d01a220dfba3eb3ad2226a8a9ee96878153b --- diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 3fafb9f..271273c 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -26,6 +26,7 @@ PKG_CHECK_MODULES(ASKUSER_NOTIFICATION_CLIENT_DEP INCLUDE_DIRECTORIES(SYSTEM ${ASKUSER_NOTIFICATION_CLIENT_DEP_INCLUDE_DIRS}) INCLUDE_DIRECTORIES( ${ASKUSER_PATH}/common + ${ASKUSER_PATH}/ipc-lib ${ASKUSER_NOTIFICATION_CLIENT_PATH}/api ${ASKUSER_NOTIFICATION_CLIENT_PATH}/impl ${ASKUSER_NOTIFICATION_CLIENT_PATH}/include @@ -34,6 +35,7 @@ INCLUDE_DIRECTORIES( SET(ASKUSER_NOTIFICATION_CLIENT_SOURCES ${ASKUSER_NOTIFICATION_CLIENT_PATH}/api/askuser-notification-client.cpp ${ASKUSER_NOTIFICATION_CLIENT_PATH}/impl/ApiInterfaceImpl.cpp + ${ASKUSER_NOTIFICATION_CLIENT_PATH}/impl/ClientCallbacks.cpp ) ADD_LIBRARY( diff --git a/src/client/impl/ApiInterfaceImpl.cpp b/src/client/impl/ApiInterfaceImpl.cpp index ce90206..1a3ec91 100644 --- a/src/client/impl/ApiInterfaceImpl.cpp +++ b/src/client/impl/ApiInterfaceImpl.cpp @@ -21,33 +21,68 @@ * @brief The definition of ApiInterfaceImpl. */ +#include +#include + +#include "ClientCallbacks.h" #include "ApiInterfaceImpl.h" +namespace { + +int eventsToAskUserMask(int events) +{ + return ((events & ASKUSER_READ_EVENT) ? AskUser::Protocol::FdMask::READ : 0) | + ((events & ASKUSER_WRITE_EVENT) ? AskUser::Protocol::FdMask::WRITE : 0); +} + +int askUserMaskToEvents(int mask) +{ + return ((AskUser::Protocol::FdMask::READ & mask) ? ASKUSER_READ_EVENT : 0) | + ((AskUser::Protocol::FdMask::WRITE & mask) ? ASKUSER_WRITE_EVENT : 0); +} + +askuser_popup_result responseToAskUserPopupResult(int response) +{ + switch (response) { + case ASKUSER_ALLOW_FOREVER: + return ASKUSER_POPUP_RESULT_ALLOW_FOREVER; + case ASKUSER_DENY_FOREVER: + return ASKUSER_POPUP_RESULT_DENY_FOREVER; + case ASKUSER_DENY_ONCE: + return ASKUSER_POPUP_RESULT_DENY_ONCE; + } + + return ASKUSER_POPUP_RESULT_DENY_ONCE; +} + +} // namespace + namespace AskUser { namespace Client { ApiInterfaceImpl::ApiInterfaceImpl(const StatusCallbackClosure &statusClosure) : m_statusClosure(statusClosure) +, m_channel(std::move(Protocol::ClientCallbacksPtr(new ClientCallbacks(this)))) { } ApiInterfaceImpl::~ApiInterfaceImpl() { - // TODO + for (const auto &closure : m_callbacks) { + closure.second(closure.first, askuser_call_cause::ASKUSER_CALL_CAUSE_FINALIZE, + askuser_popup_result::ASKUSER_POPUP_RESULT_DENY_ONCE); + } } int ApiInterfaceImpl::process(int fd, int events) { - // TODO - (void) fd; - (void) events; - return 0; + return m_channel.process(fd, eventsToAskUserMask(events)); } askuser_check_result ApiInterfaceImpl::checkPrivilege(const std::string &privilege) { - // TODO + // TODO use PolicyFetchRequest (void) privilege; return ASKUSER_CHECK_RESULT_DENY; @@ -56,11 +91,48 @@ askuser_check_result ApiInterfaceImpl::checkPrivilege(const std::string &privile RequestId ApiInterfaceImpl::popupRequest(const PopupCallbackClosure &closure, const std::string &privilege) { - // TODO - (void) closure; - (void) privilege; + Client::RequestId id = static_cast(m_channel.popupRequest(privilege)); + + auto it = m_callbacks.find(id); + if (it != m_callbacks.end()) { + it->second(it->first, ASKUSER_CALL_CAUSE_ERROR, ASKUSER_POPUP_RESULT_DENY_ONCE); + m_callbacks.erase(it); + } + + m_callbacks.insert({id, closure}); + + return id; +} + +void ApiInterfaceImpl::updateConnection(Protocol::ConnectionFd fd, int mask) +{ + m_statusClosure(fd, askUserMaskToEvents(mask)); + + // remove all pending events + if (mask == ASKUSER_EMPTY_EVENTS) { + for (const auto &closure : m_callbacks) { + closure.second(closure.first, ASKUSER_CALL_CAUSE_ERROR, ASKUSER_POPUP_RESULT_DENY_ONCE); + } + + m_callbacks.clear(); + } +} + +void ApiInterfaceImpl::popupResponse(Protocol::RequestId id, int response) +{ + auto it = m_callbacks.find(id); + if (it == m_callbacks.end()) { + return; + } + + askuser_call_cause cause = ASKUSER_CALL_CAUSE_ANSWER; + if (response == ASKUSER_UNKNOWN_ERROR) { + cause = ASKUSER_CALL_CAUSE_ERROR; + } - return 0; + askuser_popup_result res = responseToAskUserPopupResult(response); + it->second(id, cause, res); + m_callbacks.erase(it); } } // namespace Client diff --git a/src/client/impl/ApiInterfaceImpl.h b/src/client/impl/ApiInterfaceImpl.h index e255ac2..0b45410 100644 --- a/src/client/impl/ApiInterfaceImpl.h +++ b/src/client/impl/ApiInterfaceImpl.h @@ -23,19 +23,23 @@ #pragma once +#include + #include #include #include +#include + #include namespace AskUser { namespace Client { -class ApiInterfaceImpl : public AskUser::Client::ApiInterface { +class ApiInterfaceImpl : public ApiInterface { public: - ApiInterfaceImpl(const AskUser::Client::StatusCallbackClosure &closure); + ApiInterfaceImpl(const StatusCallbackClosure &closure); ApiInterfaceImpl(const ApiInterfaceImpl& orig) = delete; virtual ~ApiInterfaceImpl(); @@ -43,11 +47,16 @@ public: virtual int process(int fd, int events); virtual askuser_check_result checkPrivilege(const std::string &privilege); - virtual RequestId popupRequest(const AskUser::Client::PopupCallbackClosure &closure, + virtual RequestId popupRequest(const PopupCallbackClosure &closure, const std::string &privilege); + void updateConnection(Protocol::ConnectionFd fd, int mask); + void popupResponse(Protocol::RequestId id, int response); + private: - AskUser::Client::StatusCallbackClosure m_statusClosure; + StatusCallbackClosure m_statusClosure; + Protocol::ClientChannel m_channel; + std::map m_callbacks; }; } // namespace Client diff --git a/src/client/impl/ClientCallbacks.cpp b/src/client/impl/ClientCallbacks.cpp new file mode 100644 index 0000000..84705d4 --- /dev/null +++ b/src/client/impl/ClientCallbacks.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 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 ClientCallbacks.cpp + * @author Piotr Sawicki + * @version 1.0 + * @brief The definition of ClientCallbacks. + */ + +#include "ClientCallbacks.h" + +namespace AskUser { + +namespace Client { + +void ClientCallbacks::updateConnection(Protocol::ConnectionFd fd, int mask) +{ + m_api->updateConnection(fd, mask); +} + +void ClientCallbacks::popupResponse(Protocol::RequestId id, int response) +{ + m_api->popupResponse(id, response); +} + +} // namespace Client + +} // namespace AskUser diff --git a/src/client/impl/ClientCallbacks.h b/src/client/impl/ClientCallbacks.h new file mode 100644 index 0000000..440bde1 --- /dev/null +++ b/src/client/impl/ClientCallbacks.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 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 ClientCallbacks.h + * @author Piotr Sawicki + * @version 1.0 + * @brief The declaration of ClientCallbacks. + */ + +#pragma once + +#include + +#include "ApiInterfaceImpl.h" + +namespace AskUser { + +namespace Client { + +class ClientCallbacks : public Protocol::IClientCallbacks { +public: + + ClientCallbacks(ApiInterfaceImpl *api) : m_api(api) + {} + + virtual void updateConnection(Protocol::ConnectionFd fd, int mask); + virtual void popupResponse(Protocol::RequestId id, int response); + +private: + ApiInterfaceImpl *m_api; +}; + +} // namespace Client + +} // namespace AskUser diff --git a/src/ipc-lib/CMakeLists.txt b/src/ipc-lib/CMakeLists.txt index 7a14619..78899a4 100644 --- a/src/ipc-lib/CMakeLists.txt +++ b/src/ipc-lib/CMakeLists.txt @@ -25,12 +25,14 @@ PKG_CHECK_MODULES(ASKUSER_NOTIFICATION_LIB_DEP INCLUDE_DIRECTORIES(SYSTEM ${ASKUSER_NOTIFICATION_LIB_DEP_INCLUDE_DIRS}) INCLUDE_DIRECTORIES( + ${ASKUSER_PATH}/common ${ASKUSER_NOTIFICATION_LIB_PATH} ) SET(ASKUSER_NOTIFICATION_LIB_SOURCES ${ASKUSER_NOTIFICATION_LIB_PATH}/ask-user-channel.cpp ${ASKUSER_NOTIFICATION_LIB_PATH}/ask-user-client-channel.cpp + ${ASKUSER_NOTIFICATION_LIB_PATH}/ask-user-server-channel.cpp ${ASKUSER_NOTIFICATION_LIB_PATH}/sock.cpp ${ASKUSER_NOTIFICATION_LIB_PATH}/ask-user-config.cpp ) @@ -53,7 +55,12 @@ SET_TARGET_PROPERTIES(${TARGET_ASKUSER_NOTIFICATION_LIB} LINK_DIRECTORIES(${ASKUSER_NOTIFICATION_LIB_DEP_LIBRARY_DIRS}) -TARGET_LINK_LIBRARIES(${TARGET_ASKUSER_NOTIFICATION_LIB} ${ASKUSER_NOTIFICATION_LIB_DEP_LIBRARIES}) +TARGET_LINK_LIBRARIES( + ${TARGET_ASKUSER_NOTIFICATION_LIB} + ${TARGET_ASKUSER_COMMON} + ${ASKUSER_NOTIFICATION_LIB_DEP_LIBRARIES} + ) + TARGET_LINK_LIBRARIES(${TARGET_ASKUSER_NOTIFICATION_LIB_TEST} ${TARGET_ASKUSER_NOTIFICATION_LIB}) INSTALL(TARGETS ${TARGET_ASKUSER_NOTIFICATION_LIB} diff --git a/src/ipc-lib/ask-user-channel.cpp b/src/ipc-lib/ask-user-channel.cpp index 5b7c9f5..e59d0bf 100644 --- a/src/ipc-lib/ask-user-channel.cpp +++ b/src/ipc-lib/ask-user-channel.cpp @@ -14,61 +14,35 @@ * limitations under the License */ /** - * @file channel.cpp + * @file ask-user-channel.cpp * @author Bartlomiej Grzelewski - * @brief + * @autor Piotr Sawicki + * @autor Rafal Krypa + * @brief The implementation of Channel. */ -#include -#include -#include - -#include -#include +#include +#include +#include #include -#include namespace AskUser { namespace Protocol { -void Channel::init() { - Sock stream(Sock::SRV_STREAM); - stream.connect(getStreamSocketPath(getuid())); - - int fd = stream.getFd(); - m_sockets[fd] = SockDesc(std::move(stream)); - - m_callbacks->updateConnection(fd, FdMask::READ); -} - -void Channel::parse(const std::string &data, std::vector &parsedData) -{ - size_t begin = 0; - - while (begin < data.size()) { - size_t end = data.find(' ', begin); - - if (end == std::string::npos) { - parsedData.push_back(data.substr(begin)); - return; - } - - parsedData.push_back(data.substr(begin, end - begin)); - begin = end + 1; - } -} - -void Channel::process(int fd, int mask) { +int Channel::process(int fd, int mask) { try { auto it = m_sockets.find(fd); - if (it == m_sockets.end()) - return; + if (it == m_sockets.end()) { + ALOGE("Cannot find file descriptor " << fd); + return -EBADF; + } if (0 == mask) { - m_callbacks->updateConnection(fd, 0); + onClose(fd); m_sockets.erase(it); - return; + ALOGD("Closing connection on file descriptor: " << fd); + return -ENOTCONN; } auto &desc = it->second; @@ -76,42 +50,46 @@ void Channel::process(int fd, int mask) { if (desc.sock.getType() == Sock::SRV_STREAM) { Sock client = desc.sock.accept(); int fd = client.getFd(); - if (fd < 0) - return; + if (fd < 0) { + ALOGE("Cannot accept a connection on file descriptor: " << desc.sock.getFd()); + return -EBADF; + } m_sockets[fd] = SockDesc(std::move(client)); - m_callbacks->newConnection(fd, Credentials()); - m_callbacks->updateConnection(fd, FdMask::READ); - return; + onAccept(fd); + return 0; } if (mask & FdMask::READ) { int ret = desc.sock.recv(desc.input); if (ret <= 0) { - m_callbacks->updateConnection(fd, 0); + onClose(fd); m_sockets.erase(fd); - return; + ALOGE("Cannot read a message from file descriptor: " << fd); + return -ENOTCONN; } - std::vector params; - parse(std::string(desc.input.begin(), desc.input.end()), params); - desc.input.clear(); + // TODO framing + // check that we have the whole message + // only then we can extract and parse it - int command = std::stoi(params[0]); + // TODO assume that the message is limited in size + // A malicious user can try to allocate memory as much as he or she can. + // After detecting such a situation just close a socket. + std::vector message; + parse(std::string(desc.input.begin(), desc.input.end()), message); - switch (command) { - case MSGID_POPUP: - { - std::string &privilege = params[1]; - RequestId id = std::strtol(params[2].c_str(), nullptr, 10); - m_callbacks->popup(fd, id, std::move(privilege)); - break; - } - default : - // TODO log the error - m_callbacks->updateConnection(fd, 0); + // TODO don't clear the buffer, maybe there are some characters + // belonging to the next message + desc.input.clear(); + + int status = onReceive(fd, std::move(message)); + if (status < 0) { + onClose(fd); m_sockets.erase(fd); + ALOGE("Problem while processing received message for file descriptor: " << fd); + return -ENOTCONN; } } @@ -119,53 +97,44 @@ void Channel::process(int fd, int mask) { int size = static_cast(desc.output.size()); int result = desc.sock.send(desc.output); if (result < 0) { - m_callbacks->updateConnection(fd, 0); + onClose(fd); m_sockets.erase(fd); - return; + ALOGE("Cannot send a message on file descritptor: " << fd); + return -ENOTCONN; } if (result == size) { desc.output.clear(); - m_callbacks->updateConnection(fd, FdMask::READ); + onSend(fd); } if (result < size) { desc.output.erase(desc.output.begin(), desc.output.begin()+result); } } - } catch (const std::exception &){ + } catch (const std::exception &e){ // TODO handle error + ALOGE("Unhandled exception occured while processing file descriptor: " << fd); + return -ENOTCONN; } -} -void Channel::popupResponse(ConnectionFd fd, RequestId id, int response) { - try { - auto it = m_sockets.find(fd); - if (it == m_sockets.end()) - return; + return 0; +} - auto &desc = it->second; +void Channel::parse(const std::string &data, std::vector &parsedData) +{ + size_t begin = 0; - std::stringstream ss; - ss << id << " " << response; - std::string o = ss.str(); - std::copy(o.begin(), o.end(), std::back_inserter(desc.output)); - m_callbacks->updateConnection(fd, FdMask::READ | FdMask::WRITE); - } catch (const std::exception &){} -} + while (begin < data.size()) { + size_t end = data.find(' ', begin); -Channel::~Channel() { - for (auto &e : m_sockets) - m_callbacks->updateConnection(e.first, 0); -} + if (end == std::string::npos) { + parsedData.push_back(data.substr(begin)); + return; + } -ChannelPtr createChannel(ServerCallbacksPtr ptr) { - try { - Channel *c = new Channel(std::move(ptr)); - c->init(); - return ChannelPtr(c); - } catch (const std::exception &) { - return ChannelPtr(nullptr); + parsedData.push_back(data.substr(begin, end - begin)); + begin = end + 1; } } diff --git a/src/ipc-lib/ask-user-client-channel.cpp b/src/ipc-lib/ask-user-client-channel.cpp index 61ba65e..a59da5c 100644 --- a/src/ipc-lib/ask-user-client-channel.cpp +++ b/src/ipc-lib/ask-user-client-channel.cpp @@ -14,107 +14,100 @@ * limitations under the License */ /** - * @file ipc-client.cpp + * @file ask-user-client-channel.cpp * @author Bartlomiej Grzelewski - * @brief + * @author Piotr Sawicki + * @brief The implementation of ClientChannel. */ +#include #include -#include -#include -#include +#include #include -#include -#include #include -#define UNUSED __attribute__((unused)) - namespace AskUser { namespace Protocol { -int popup_launch(const std::string &pkgName, - const std::string &appName, - uid_t uid, - const PrivilegeVector &privileges, - int &result) -{ - try { - Sock s(Sock::CLI_STREAM); - if (0 > s.connect(getStreamSocketPath(uid))) - return -1; - - std::stringstream ss; - ss << MSGID_POPUP << " " << pkgName << " " << appName << " " << uid; - for (auto &e : privileges) { - ss << " " << e; - } +ClientChannel::ClientChannel(ClientCallbacksPtr ptr) + : m_callbacks(std::move(ptr)) { +} - std::string str = ss.str(); +ClientChannel::~ClientChannel() { + for (auto &e : m_sockets) { + onClose(e.first); + } +} - if (0 > s.send(RawBuffer(str.begin(), str.end()))) - return -1; +void ClientChannel::init() { + Sock s(Sock::CLI_STREAM); + if (0 > s.connect(getStreamSocketPath(geteuid()))) + throw std::logic_error("Cannot connect to the server"); - RawBuffer resp; - if (0 > s.wait(FdMask::READ)) - return -1; - if (0 > s.recv(resp)) - return -1; + int fd = s.getFd(); + m_sockets[fd] = SockDesc(std::move(s)); +} - std::string input(resp.begin(), resp.end()); - std::stringstream sss(input); - sss >> result; +AskUser::Protocol::RequestId ClientChannel::generateRequestId() { + static RequestId requestId = 0; + return requestId++; +} - return 0; - } catch (const std::exception &) { - return -1; +Protocol::RequestId ClientChannel::popupRequest(const std::string &privilege) { + if (m_sockets.empty()) { + init(); } -} -int popup_runtime(UNUSED const std::string &pkgName, - UNUSED const std::string &appName, - UNUSED uid_t uid, - UNUSED std::string &privilege, - UNUSED int &result) -{ - return -1; -} + std::stringstream ss; + RequestId requestId = generateRequestId(); + // TODO base64 encode privilege, use g_base64_encode() + ss << MSGID_POPUP << " " << privilege << " " << requestId; + // client uses only one active socket + int fd = m_sockets.begin()->first; + std::string str = ss.str(); + std::copy(str.begin(), str.end(), std::back_inserter(m_sockets[fd].output)); + m_callbacks->updateConnection(fd, FdMask::READ | FdMask::WRITE); -int toast_deny(const std::string &pkgName, - const std::string &appName, - uid_t uid, - const std::string &privilege) -{ - try { - Sock s(Sock::CLI_DGRAM); - if (0 > s.connect(getDatagramSocketPath(uid))) - return -1; + return requestId; +} - std::string str = std::to_string(MSGID_TOAST1) + " " + pkgName + " " + appName + " " + std::to_string(uid) + " " + privilege; +void ClientChannel::onAccept(int) { +} - return s.send(RawBuffer(str.begin(), str.end())); - } catch (const std::exception &) { - return -1; - } +void ClientChannel::onClose(int fd) { + m_callbacks->updateConnection(fd, 0); } -int toast_fail_launch(const std::string &pkgName, const std::string &appName, uid_t uid) { - try { - Sock s(Sock::CLI_DGRAM); - if (0 > s.connect(getDatagramSocketPath(uid))) - return -1; +int ClientChannel::onReceive(UNUSED int fd, std::vector &&message) { + if (!message.size()) + return -EINVAL; - std::string str = std::to_string(MSGID_TOAST2) + " " + pkgName + " " + appName + " " + std::to_string(uid); + int command = std::stoi(message[0]); - return s.send(RawBuffer(str.begin(), str.end())); - } catch (const std::exception &) { - return -1; + switch (command) { + case MSGID_POPUP_RESPONSE: + { + if (message.size() != 3) + return -EINVAL; + + RequestId id = std::strtol(message[1].c_str(), nullptr, 10); + int response = std::strtol(message[2].c_str(), nullptr, 10); + m_callbacks->popupResponse(id, response); + break; + } + default : + return -EINVAL; } + + return 0; +} + +void ClientChannel::onSend(int fd) { + m_callbacks->updateConnection(fd, FdMask::READ); } } // namespace Protocol } // namespace AskUser - diff --git a/src/ipc-lib/ask-user-config.cpp b/src/ipc-lib/ask-user-config.cpp index 19f9364..597abe6 100644 --- a/src/ipc-lib/ask-user-config.cpp +++ b/src/ipc-lib/ask-user-config.cpp @@ -16,7 +16,7 @@ /** * @file ask-user-config.cpp * @author Bartlomiej Grzelewski - * @brief + * @brief The implementation of utility functions for getting sockets paths. */ #include diff --git a/src/ipc-lib/ask-user-config.h b/src/ipc-lib/ask-user-config.h index 7b77be4..e6ed815 100644 --- a/src/ipc-lib/ask-user-config.h +++ b/src/ipc-lib/ask-user-config.h @@ -16,18 +16,13 @@ /** * @file ask-user-config.h * @author Bartlomiej Grzelewski - * @brief + * @brief The declaration of utility functions for getting sockets paths. */ #pragma once #include #include -// We want to use it constat expressions (this is the reason why it's not in cpp file -const int MSGID_POPUP = 1; -const int MSGID_TOAST1 = 2; -const int MSGID_TOAST2 = 3; - std::string getDatagramSocketPath(uid_t uid); std::string getStreamSocketPath(uid_t uid); diff --git a/src/ipc-lib/ask-user-server-channel.cpp b/src/ipc-lib/ask-user-server-channel.cpp new file mode 100644 index 0000000..ba72edb --- /dev/null +++ b/src/ipc-lib/ask-user-server-channel.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017 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 ask-user-server-channel.cpp + * @author Bartlomiej Grzelewski + * @author Piotr Sawicki + * @brief The implementation of ServerChannel. + */ +#include +#include + +#include + +#include + +namespace AskUser { +namespace Protocol { + +ServerChannel::ServerChannel(ServerCallbacksPtr ptr) + : m_callbacks(std::move(ptr)) +{ + init(); +} + +ServerChannel::~ServerChannel() { + for (auto &e : m_sockets) { + onClose(e.first); + } +} + +void ServerChannel::init() { + Sock stream(Sock::SRV_STREAM); + stream.connect(getStreamSocketPath(geteuid())); + + int fd = stream.getFd(); + m_sockets[fd] = SockDesc(std::move(stream)); + + m_callbacks->updateConnection(fd, FdMask::READ); +} + +void ServerChannel::popupResponse(ConnectionFd fd, RequestId id, int response) { + try { + auto it = m_sockets.find(fd); + if (it == m_sockets.end()) + return; + + auto &desc = it->second; + + std::stringstream ss; + ss << MSGID_POPUP_RESPONSE << " " << id << " " << response; + std::string o = ss.str(); + std::copy(o.begin(), o.end(), std::back_inserter(desc.output)); + + m_callbacks->updateConnection(fd, FdMask::READ | FdMask::WRITE); + } catch (const std::exception &){} +} + +void ServerChannel::onAccept(int fd) { + m_callbacks->newConnection(fd, Credentials()); + m_callbacks->updateConnection(fd, FdMask::READ); +} + +void ServerChannel::onClose(int fd) { + m_callbacks->updateConnection(fd, 0); +} + +int ServerChannel::onReceive(int fd, std::vector &&message) { + if (!message.size()) + return -1; + + int command = std::stoi(message[0]); + + switch (command) { + case MSGID_POPUP: + { + if (message.size() != 3) + return -1; + + std::string &privilege = message[1]; + RequestId requestId = std::strtol(message[2].c_str(), nullptr, 10); + // TODO base64 decode privilege. Privilege may contain + // any character: \n, space, etc. use g_base64_decode() + m_callbacks->popup(fd, requestId, std::move(privilege)); + break; + } + default : + return -1; + } + + return 0; +} + +void ServerChannel::onSend(int fd) { + m_callbacks->updateConnection(fd, FdMask::READ); +} + +} // namespace Protocol +} // namespace AskUser diff --git a/src/ipc-lib/askuser-notification/ask-user-channel.h b/src/ipc-lib/askuser-notification/ask-user-channel.h new file mode 100644 index 0000000..7bd3658 --- /dev/null +++ b/src/ipc-lib/askuser-notification/ask-user-channel.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017 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 ask-user-channel.h + * @author Bartlomiej Grzelewski + * @author Piotr Sawicki + * @brief The declaration of Channel. + */ +#pragma once + + +#include +#include +#include + +#include +#include + +namespace AskUser { +namespace Protocol { + +/** + * An abstract channel. + */ +class Channel { +public: + + virtual ~Channel() {}; + + /** + * Process function should be called each time some event is reported by poll/select on + * descriptor. + * + * \param[in] fd Number of descriptor. + * \param[in] mask Information about event that is waiting on descriptor + * (FdMask::READ or FdMask::WRITE). If you pass 0 for some reason + * the descriptor will be closed and callback updateFd will be called + * with mask = 0 + * \return 0 on success + * \return a negative value in case of an error (see errno) + */ + virtual int process(int fd, int mask); + + virtual void onAccept(int fd) = 0; + virtual void onClose(int fd) = 0; + virtual int onReceive(int fd, std::vector &¶ms) = 0; + virtual void onSend(int fd) = 0; + +protected: + void parse(const std::string &data, std::vector &parsedData); + + SocketMap m_sockets; +}; + +} // namespace Protocol +} // namespace AskUser diff --git a/src/ipc-lib/askuser-notification/ask-user-client-channel.h b/src/ipc-lib/askuser-notification/ask-user-client-channel.h index a98602f..294bdb2 100644 --- a/src/ipc-lib/askuser-notification/ask-user-client-channel.h +++ b/src/ipc-lib/askuser-notification/ask-user-client-channel.h @@ -16,67 +16,69 @@ /** * @file ask-user-client.h * @author Bartlomiej Grzelewski - * @brief + * @author Piotr Sawicki + * @brief The declaration of IClientCallbacks and ClientChannel. */ #pragma once -#include - +#include #include #include -#include + +#include #include namespace AskUser { namespace Protocol { + /** - * Synchronous request for showing popup. - * - * \param[in] pkgName Application package name. - * \param[in] appName Application name. - * \param[in] uid Information about user that should see popup. - * \param[in] privileges List of privileges that should be shown to user. - * \param[out] result Result returned by ask-user application. - * - * \return Value less that 0 means ipc error. + * IServiceCallbacks defines set of callbacks that must be implemented in client. */ - int popup_launch(const std::string &pkgName, const std::string &appName, uid_t uid, const PrivilegeVector &privileges, int &result); +struct IClientCallbacks { - /** - * Synchronous request for showing popup. - * - * \param[in] pkgName Application package name. - * \param[in] appName Application name. - * \param[in] uid Information about user that should see popup. - * \param[in] privilege Privilege that should be shown to user. - * \param[out] result Result returned by ask-user application. - * - * \return Value less that 0 means ipc error. - */ - int popup_runtime(const std::string &pkgName, const std::string &appName, uid_t uid, std::string &privilege, int &result); + virtual ~IClientCallbacks(){} -/** - * Nonblocking request for showing toast. - * - * \param[in] pkgName Application package name. - * \param[in] appName Application name. - * \param[in] uid Information about user that should see popup. - * \param[in] privilege Name of privilege that was denied. - * - * \return Value less that 0 means ipc error. - */ -int toast_deny(const std::string &pkgName, const std::string &appName, uid_t uid, const std::string &privilege); + /** + * This function gives you number of descriptor that should be watched by poll/select. + * + * \param[in] fd Connection file descriptor + * \param[in] mask Type of acction that is required on this descriptor. + * mask == 0 remove descriptor fd from watched pool + * mask == 1 watch descriptor for READ + * mask == 2 watch descriptor for WRITE + * maks == 3 watch descriptor for READ and WRITE + */ + virtual void updateConnection(ConnectionFd fd, int mask) = 0; -/** - * Nonblocking request for showing toast. - * - * \param[in] pkgName Application package name. - * \param[in] appName Application name. - * \param[in] uid Information about user that should see popup. - * - * \return Value less that 0 means ipc error. - */ -int toast_fail_launch(const std::string &pkgName, const std::string &appName, uid_t uid); + /** + * This function is called when popup response is received. + * + * \param[in] id Request identifier + * \param[in] response Response from popup + */ + virtual void popupResponse(RequestId id, int response) = 0; +}; + +typedef std::unique_ptr ClientCallbacksPtr; + +class ClientChannel : public Channel { +public: + ClientChannel(ClientCallbacksPtr ptr); + virtual ~ClientChannel(); + + virtual Protocol::RequestId popupRequest(const std::string &privilege); + + virtual void onAccept(int fd); + virtual void onClose(int fd); + virtual int onReceive(int fd, std::vector &&message); + virtual void onSend(int fd); + +private: + void init(); + AskUser::Protocol::RequestId generateRequestId(); + + ClientCallbacksPtr m_callbacks; +}; } // namespace Protocol } // namespace AskUser diff --git a/src/ipc-lib/askuser-notification/ask-user-server-channel.h b/src/ipc-lib/askuser-notification/ask-user-server-channel.h index ca55528..c5987ce 100644 --- a/src/ipc-lib/askuser-notification/ask-user-server-channel.h +++ b/src/ipc-lib/askuser-notification/ask-user-server-channel.h @@ -14,43 +14,26 @@ * limitations under the License */ /** - * @file ask-user-service.h + * @file ask-user-server.h * @author Bartlomiej Grzelewski - * @brief + * @author Piotr Sawicki + * @brief The declaration of IServerCallbacks and ServerChannel. */ #pragma once -#include - +#include #include #include -#include #include +#include namespace AskUser { namespace Protocol { -typedef int ConnectionFd; -typedef int RequestId; - -const ConnectionFd INVALID_FD = -1; -const RequestId INVALID_ID = -1; - -enum FdMask { - READ = 1, - WRITE = 2, -}; - -struct Credentials { - std::string label; - uid_t uid; -}; - /** - * IServiceCallbacks defines set of callbacks that must be implemented in service. + * IServiceCallbacks defines set of callbacks that must be implemented in server. */ - struct IServerCallbacks { virtual ~IServerCallbacks(){} @@ -59,6 +42,7 @@ struct IServerCallbacks { * Fd is file descriptor for this new peer connection. * * \param[in] fd Connection file descriptor + * \param[in] creds Credentials taken from the file descriptor */ virtual void newConnection(ConnectionFd fd, const Credentials &creds) = 0; @@ -67,10 +51,10 @@ struct IServerCallbacks { * * \param[in] fd Connection file descriptor * \param[in] mask Type of acction that is required on this descriptor. - * mask == 0 remove descriptor fd from watched pool - * mask == 1 watch descriptor for READ - * mask == 2 watch descriptor for WRITE - * maks == 3 watch descriptor for READ and WRITE + * mask == 0 remove descriptor fd from watched pool + * mask == 1 watch descriptor for READ + * mask == 2 watch descriptor for WRITE + * mask == 3 watch descriptor for READ and WRITE */ virtual void updateConnection(ConnectionFd fd, int mask) = 0; @@ -84,35 +68,25 @@ struct IServerCallbacks { virtual void popup(ConnectionFd fd, RequestId id, std::string &&privilege) = 0; }; -struct IChannel { - virtual ~IChannel(){} +typedef std::unique_ptr ServerCallbacksPtr; + +class ServerChannel : public Channel { +public: + ServerChannel(ServerCallbacksPtr ptr); + virtual ~ServerChannel(); - /** - * Process function should be called each time some event is reported by poll/select on - * descriptor. - * - * \param[in] fd Number of descriptor. - * \param[in] mask Information about event that is waiting on descriptor - * (FdMask::READ or FdMask::WRITE). If you pass 0 for some reason - * the descriptor will be closed and callback updateFd will be called - * with mask = 0 - */ - virtual void process(int fd, int mask) = 0; + virtual void popupResponse(ConnectionFd fd, RequestId id, int response); - /** - * Information about action that was chosen by user. - * - * \param[in] requestId Request number. - * \param[in] response Information about action chosen by user. - */ - virtual void popupResponse(ConnectionFd fd, RequestId id, int response) = 0; -}; + virtual void onAccept(int fd); + virtual void onClose(int fd); + virtual int onReceive(int fd, std::vector &&message); + virtual void onSend(int fd); -typedef std::unique_ptr ChannelPtr; -typedef std::unique_ptr ServerCallbacksPtr; +private: + void init(); -ChannelPtr createChannel(ServerCallbacksPtr ptr); + ServerCallbacksPtr m_callbacks; +}; } // namespace Protocol } // namespace AskUser - diff --git a/src/ipc-lib/askuser-notification/ask-user-types.h b/src/ipc-lib/askuser-notification/ask-user-types.h index 8641df3..71ab50c 100644 --- a/src/ipc-lib/askuser-notification/ask-user-types.h +++ b/src/ipc-lib/askuser-notification/ask-user-types.h @@ -16,10 +16,14 @@ /** * @file ask-user-types.h * @author Bartlomiej Grzelewski - * @brief + * @brief The declaration of various types used by the library. */ #pragma once +#include +#include +#include + #define ASKUSER_NONE 0 #define ASKUSER_DENY_ONCE 1 #define ASKUSER_DENY_FOREVER 2 @@ -30,9 +34,29 @@ namespace AskUser { namespace Protocol { +enum FdMask { + READ = 1, + WRITE = 2, +}; + +struct Credentials { + std::string label; + uid_t uid; +}; + +typedef int ConnectionFd; +typedef int RequestId; typedef std::string Privilege; typedef std::vector PrivilegeVector; +const ConnectionFd INVALID_FD = -1; +const RequestId INVALID_ID = -1; + +// We want to use it constat expressions (this is the reason why it's not in cpp file) +const int MSGID_POPUP = 1; +const int MSGID_TOAST1 = 2; +const int MSGID_TOAST2 = 3; +const int MSGID_POPUP_RESPONSE = 1; + } // namespace Protocol } // AskUser - diff --git a/src/ipc-lib/askuser-notification/sock-desc.h b/src/ipc-lib/askuser-notification/sock-desc.h new file mode 100644 index 0000000..ebdc5cd --- /dev/null +++ b/src/ipc-lib/askuser-notification/sock-desc.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 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 sock-desc.cpp + * @author Bartlomiej Grzelewski + * @autor Piotr Sawicki + * @brief The declaration of SockDesc. + */ + +#pragma once + +#include + +#include +#include + +namespace AskUser { +namespace Protocol { + +struct SockDesc { + SockDesc(Sock psock) + : sock(std::move(psock)) + {} + SockDesc(){} + Sock sock; + RawBuffer input; + RawBuffer output; +}; + +typedef std::map SocketMap; + +} // namespace Protocol +} // namespace AskUser diff --git a/src/ipc-lib/sock.cpp b/src/ipc-lib/sock.cpp index a2ec49a..fccce16 100644 --- a/src/ipc-lib/sock.cpp +++ b/src/ipc-lib/sock.cpp @@ -14,9 +14,9 @@ * limitations under the License */ /** - * @file Sock.cpp + * @file sock.cpp * @author Bartlomiej Grzelewski - * @brief Implementation of Sock methods + * @brief The implementation of Sock. */ #include #include @@ -31,7 +31,7 @@ #include #include -#include +#include #include namespace AskUser { diff --git a/src/ipc-lib/test/main.cpp b/src/ipc-lib/test/main.cpp index b73de55..48f1457 100644 --- a/src/ipc-lib/test/main.cpp +++ b/src/ipc-lib/test/main.cpp @@ -16,15 +16,18 @@ /** * @file main.cpp * @author Bartlomiej Grzelewski + * @author Piotr Sawicki * @brief */ #include #include +#include #include #include #include +#include std::map m_sockets; @@ -32,13 +35,14 @@ std::map m_sockets; using namespace AskUser::Protocol; -struct Callbacks : public IServerCallbacks { - Callbacks() : m_channel(nullptr) {} +struct ServerCallbacks : public IServerCallbacks { + ServerCallbacks() : m_channel(nullptr) {} virtual void newConnection(ConnectionFd fd, const Credentials &creds) { - (void)fd; - (void)creds; + printf("call newConnection fd: %d credentials = { label: %s uid: %d }\n", fd, + creds.label.c_str(), creds.uid); } + virtual void updateConnection(ConnectionFd fd, int mask) { printf("call updateFd %d %d\n", fd, mask); if (mask == 0) { @@ -51,21 +55,21 @@ struct Callbacks : public IServerCallbacks { virtual void popup(ConnectionFd fd, RequestId id, Privilege &&priv) { printf("call popup %s \n", priv.c_str()); if (m_channel) - m_channel->popupResponse(fd, id, 0xdeadbeef); + m_channel->popupResponse(fd, id, rand() % (ASKUSER_ALLOW_FOREVER + 1)); } - void setChannel(IChannel *ptr) { + void setChannel(ServerChannel *ptr) { m_channel = ptr; } private: - IChannel *m_channel; + ServerChannel *m_channel; }; void server(void) { - Callbacks *c = new Callbacks; - ChannelPtr ptr = createChannel(ServerCallbacksPtr(c)); - c->setChannel(ptr.get()); + ServerCallbacks *c = new ServerCallbacks; + ServerChannel *chan = new ServerChannel(ServerCallbacksPtr(c)); + c->setChannel(chan); pollfd fd[100]; @@ -81,26 +85,93 @@ void server(void) { printf("Error in poll. Quit\n"); return; } - for (int i=0; iprocess(fd[i].fd, FdMask::READ); - if (fd[i].revents & POLLOUT) - ptr->process(fd[i].fd, FdMask::WRITE); + for (int i = 0; i < last; ++i) { + if ((fd[i].revents & POLLIN) || (fd[i].revents & POLLOUT)) { + int mask = ((fd[i].revents & POLLIN) ? FdMask::READ : 0) | + ((fd[i].revents & POLLOUT) ? FdMask::WRITE : 0); + chan->process(fd[i].fd, mask); + } } } } -void stream() { - PrivilegeVector vect = {"http://tizen.org/privilege/camera", "http://tizen.org/privilege/contacts"}; - int result; - UNUSED int ret = popup_launch("org.tizen.memo", "org.tizen.memo", getuid(), vect, result); - printf("Sended stream. Result: %x\n", result); -} +struct ClientCallbacks : public IClientCallbacks { + ClientCallbacks() : m_channel(nullptr) {} + + virtual void updateConnection(ConnectionFd fd, int mask) { + printf("call updateFd %d %d\n", fd, mask); + if (mask == 0) { + // remove socket + m_sockets.erase(fd); + return; + } + m_sockets[fd] = mask; + } + + virtual void popupResponse(RequestId id, int response) { + printf("response from popup id: %d response: %d\n", id, response); + } + + void setChannel(ClientChannel *ptr) { + m_channel = ptr; + } + +private: + ClientChannel *m_channel; +}; + +void client() { + ClientCallbacks *c = new ClientCallbacks; + ClientChannel *chan = new ClientChannel(ClientCallbacksPtr(c)); + c->setChannel(chan); + char privilege[4096]; + pollfd fd[100]; + + while(1) { + int last = 0; + for (auto &e : m_sockets) { + fd[last].fd = e.first; + fd[last].revents = 0; + fd[last].events = ((e.second & FdMask::READ) ? POLLIN : 0) | ((e.second & FdMask::WRITE) ? POLLOUT : 0); + printf("poll fd = %d events = %d\n", e.first, fd[last].events); + last++; + } + + // stdin + fd[last].fd = 0; + fd[last].revents = 0; + fd[last].events = POLLIN; + last++; + + if (-1 == poll(fd, last, -1)) { + printf("Error in poll. Quit\n"); + return; + } + + for (int i = 0; i < last; ++i) { + switch (fd[i].fd) { + case 0: + if (fd[i].revents & POLLIN) { + UNUSED int ret = scanf("%s", privilege); + chan->popupRequest(privilege); + } + break; + default: + if ((fd[i].revents & POLLIN) || (fd[i].revents & POLLOUT)) { + int mask = ((fd[i].revents & POLLIN) ? FdMask::READ : 0) | + ((fd[i].revents & POLLOUT) ? FdMask::WRITE : 0); + chan->process(fd[i].fd, mask); + } + break; + } + } + } +} int main(){ int com; - printf("0 - server, 1 - send popup, 2 - send toust1, 3 - send toust2\n>"); + printf("0 - server, 1 - send popup\n>"); UNUSED int ret = scanf("%d", &com); switch(com) { @@ -108,7 +179,7 @@ int main(){ server(); break; case 1: - stream(); + client(); break; } diff --git a/src/notification-daemon/Logic.cpp b/src/notification-daemon/Logic.cpp index 13edb9f..ffd95a0 100644 --- a/src/notification-daemon/Logic.cpp +++ b/src/notification-daemon/Logic.cpp @@ -42,7 +42,7 @@ void Logic::addChannelFd(Protocol::ConnectionFd fd, const Protocol::Credentials auto it = m_connToInfo.find(fd); if (it != m_connToInfo.end()) { ALOGE("Connection with fd : " << fd << " already exists. Closing connection"); - m_clientChannel->process(fd, 0); + m_serverChannel->process(fd, 0); return; } @@ -115,7 +115,7 @@ Eina_Bool Logic::processChannel(int fd, int mask) { processEvents(); } m_fdInfo.erase(fd); - m_clientChannel->process(fd, 0); + m_serverChannel->process(fd, 0); return ECORE_CALLBACK_CANCEL; } case FdChange::CHANGE: @@ -149,16 +149,16 @@ void Logic::popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std:: if (policies.size() != 1) { ALOGE("Something strange happened, more than one policy for (" << it->second.appId << ", " << it->second.user << ", " << privilege << ") exists"); - m_clientChannel->popupResponse(fd, id, ASKUSER_DENY_ONCE); + m_serverChannel->popupResponse(fd, id, ASKUSER_DENY_ONCE); return; } if (policies.front().getLevel() == "Allow") { - m_clientChannel->popupResponse(fd, id, ASKUSER_ALLOW_FOREVER); + m_serverChannel->popupResponse(fd, id, ASKUSER_ALLOW_FOREVER); return; } if (policies.front().getLevel() == "Deny") { - m_clientChannel->popupResponse(fd, id, ASKUSER_DENY_FOREVER); + m_serverChannel->popupResponse(fd, id, ASKUSER_DENY_FOREVER); return; } @@ -222,13 +222,15 @@ Eina_Bool Logic::signalHandler(void *data, Ecore_Fd_Handler *) { } void Logic::prepareChannel() { - m_clientChannel = AskUser::Protocol::createChannel( - std::unique_ptr(new AskUser::Protocol::ServerCallbacks(this, &m_popupper))); + AskUser::Protocol::ServerCallbacksPtr callbacks(new AskUser::Protocol::ServerCallbacks(this, &m_popupper)); + std::unique_ptr channel(new AskUser::Protocol::ServerChannel(std::move(callbacks))); + + m_serverChannel = std::move(channel); } void Logic::updateChannel(int fd, int mask) { // TODO errors? - m_clientChannel->process(fd, mask); + m_serverChannel->process(fd, mask); } void Logic::init() { @@ -290,7 +292,7 @@ void Logic::popupResponse(NResponseType response) { } // TODO create popup ids containing fd and request id - m_clientChannel->popupResponse(m_currentEvent.fd, m_currentEvent.id, clientResponse); + m_serverChannel->popupResponse(m_currentEvent.fd, m_currentEvent.id, clientResponse); finishCurrentEvent(); processEvents(); } diff --git a/src/notification-daemon/Logic.h b/src/notification-daemon/Logic.h index 8c16a93..41712e5 100644 --- a/src/notification-daemon/Logic.h +++ b/src/notification-daemon/Logic.h @@ -79,7 +79,7 @@ private: void finishCurrentEvent(); void popupResponse(NResponseType response); - AskUser::Protocol::ChannelPtr m_clientChannel; + std::unique_ptr m_serverChannel; Popupper m_popupper; struct FdEvent {