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
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(
* @brief The definition of ApiInterfaceImpl.
*/
+#include <askuser-notification/ask-user-client-channel.h>
+#include <askuser-notification/ask-user-types.h>
+
+#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;
RequestId ApiInterfaceImpl::popupRequest(const PopupCallbackClosure &closure,
const std::string &privilege)
{
- // TODO
- (void) closure;
- (void) privilege;
+ Client::RequestId id = static_cast<Client::RequestId>(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
#pragma once
+#include <memory>
+
#include <ApiInterface.h>
#include <PopupCallbackClosure.h>
#include <StatusCallbackClosure.h>
+#include <askuser-notification/ask-user-client-channel.h>
+
#include <askuser-notification-client.h>
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();
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<RequestId, PopupCallbackClosure> m_callbacks;
};
} // namespace Client
--- /dev/null
+/*
+ * 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 <p.sawicki2@partner.samsung.com>
+ * @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
--- /dev/null
+/*
+ * 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 <p.sawicki2@partner.samsung.com>
+ * @version 1.0
+ * @brief The declaration of ClientCallbacks.
+ */
+
+#pragma once
+
+#include <askuser-notification/ask-user-client-channel.h>
+
+#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
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
)
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}
* limitations under the License
*/
/**
- * @file channel.cpp
+ * @file ask-user-channel.cpp
* @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
- * @brief
+ * @autor Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @autor Rafal Krypa <r.krypa@samsung.com>
+ * @brief The implementation of Channel.
*/
-#include <cstdlib>
-#include <string>
-#include <sstream>
-
-#include <askuser-notification/ask-user-server-channel.h>
-#include <askuser-notification/sock.h>
+#include <log/alog.h>
+#include <askuser-notification/sock-desc.h>
+#include <askuser-notification/ask-user-channel.h>
#include <ask-user-config.h>
-#include <ask-user-channel.h>
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<std::string> &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;
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<std::string> 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<std::string> 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;
}
}
int size = static_cast<int>(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<std::string> &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;
}
}
* limitations under the License
*/
/**
- * @file ipc-client.cpp
+ * @file ask-user-client-channel.cpp
* @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
- * @brief
+ * @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @brief The implementation of ClientChannel.
*/
+#include <iostream>
#include <sstream>
-#include <string>
-#include <vector>
-#include <memory>
+#include <attributes/attributes.h>
#include <askuser-notification/ask-user-client-channel.h>
-#include <askuser-notification/ask-user-server-channel.h>
-#include <askuser-notification/sock.h>
#include <ask-user-config.h>
-#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<std::string> &&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
-
/**
* @file ask-user-config.cpp
* @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
- * @brief
+ * @brief The implementation of utility functions for getting sockets paths.
*/
#include <ask-user-config.h>
/**
* @file ask-user-config.h
* @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
- * @brief
+ * @brief The declaration of utility functions for getting sockets paths.
*/
#pragma once
#include <string>
#include <sys/types.h>
-// 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);
--- /dev/null
+/*
+ * 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 <b.grzelewski@samsung.com>
+ * @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @brief The implementation of ServerChannel.
+ */
+#include <iostream>
+#include <sstream>
+
+#include <askuser-notification/ask-user-server-channel.h>
+
+#include <ask-user-config.h>
+
+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<std::string> &&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
--- /dev/null
+/*
+ * 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 <b.grzelewski@samsung.com>
+ * @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @brief The declaration of Channel.
+ */
+#pragma once
+
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <askuser-notification/sock-desc.h>
+#include <askuser-notification/ask-user-types.h>
+
+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<std::string> &¶ms) = 0;
+ virtual void onSend(int fd) = 0;
+
+protected:
+ void parse(const std::string &data, std::vector<std::string> &parsedData);
+
+ SocketMap m_sockets;
+};
+
+} // namespace Protocol
+} // namespace AskUser
/**
* @file ask-user-client.h
* @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
- * @brief
+ * @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @brief The declaration of IClientCallbacks and ClientChannel.
*/
#pragma once
-#include <sys/types.h>
-
+#include <memory>
#include <string>
#include <vector>
-#include <memory>
+
+#include <askuser-notification/ask-user-channel.h>
#include <askuser-notification/ask-user-types.h>
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<IClientCallbacks> 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<std::string> &&message);
+ virtual void onSend(int fd);
+
+private:
+ void init();
+ AskUser::Protocol::RequestId generateRequestId();
+
+ ClientCallbacksPtr m_callbacks;
+};
} // namespace Protocol
} // namespace AskUser
* limitations under the License
*/
/**
- * @file ask-user-service.h
+ * @file ask-user-server.h
* @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
- * @brief
+ * @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @brief The declaration of IServerCallbacks and ServerChannel.
*/
#pragma once
-#include <sys/types.h>
-
+#include <memory>
#include <string>
#include <vector>
-#include <memory>
#include <askuser-notification/ask-user-types.h>
+#include <askuser-notification/ask-user-channel.h>
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(){}
* 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;
*
* \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;
virtual void popup(ConnectionFd fd, RequestId id, std::string &&privilege) = 0;
};
-struct IChannel {
- virtual ~IChannel(){}
+typedef std::unique_ptr<IServerCallbacks> 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<std::string> &&message);
+ virtual void onSend(int fd);
-typedef std::unique_ptr<IChannel> ChannelPtr;
-typedef std::unique_ptr<IServerCallbacks> ServerCallbacksPtr;
+private:
+ void init();
-ChannelPtr createChannel(ServerCallbacksPtr ptr);
+ ServerCallbacksPtr m_callbacks;
+};
} // namespace Protocol
} // namespace AskUser
-
/**
* @file ask-user-types.h
* @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
- * @brief
+ * @brief The declaration of various types used by the library.
*/
#pragma once
+#include <map>
+#include <string>
+#include <vector>
+
#define ASKUSER_NONE 0
#define ASKUSER_DENY_ONCE 1
#define ASKUSER_DENY_FOREVER 2
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<Privilege> 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
-
--- /dev/null
+/*
+ * 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 <b.grzelewski@samsung.com>
+ * @autor Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @brief The declaration of SockDesc.
+ */
+
+#pragma once
+
+#include <map>
+
+#include <askuser-notification/sock.h>
+#include <askuser-notification/raw-buffer.h>
+
+namespace AskUser {
+namespace Protocol {
+
+struct SockDesc {
+ SockDesc(Sock psock)
+ : sock(std::move(psock))
+ {}
+ SockDesc(){}
+ Sock sock;
+ RawBuffer input;
+ RawBuffer output;
+};
+
+typedef std::map<int, SockDesc> SocketMap;
+
+} // namespace Protocol
+} // namespace AskUser
* limitations under the License
*/
/**
- * @file Sock.cpp
+ * @file sock.cpp
* @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
- * @brief Implementation of Sock methods
+ * @brief The implementation of Sock.
*/
#include <poll.h>
#include <stdexcept>
#include <string>
#include <vector>
-#include <askuser-notification/ask-user-server-channel.h>
+#include <askuser-notification/ask-user-types.h>
#include <askuser-notification/sock.h>
namespace AskUser {
/**
* @file main.cpp
* @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
+ * @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
* @brief
*/
#include <map>
#include <poll.h>
+#include <stdlib.h>
#include <unistd.h>
#include <askuser-notification/ask-user-client-channel.h>
#include <askuser-notification/ask-user-server-channel.h>
+#include <askuser-notification/ask-user-types.h>
std::map<int, int> 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) {
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];
printf("Error in poll. Quit\n");
return;
}
- for (int i=0; i<last; ++i) {
- if (fd[i].revents & POLLIN)
- ptr->process(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) {
server();
break;
case 1:
- stream();
+ client();
break;
}
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;
}
processEvents();
}
m_fdInfo.erase(fd);
- m_clientChannel->process(fd, 0);
+ m_serverChannel->process(fd, 0);
return ECORE_CALLBACK_CANCEL;
}
case FdChange::CHANGE:
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;
}
}
void Logic::prepareChannel() {
- m_clientChannel = AskUser::Protocol::createChannel(
- std::unique_ptr<AskUser::Protocol::IServerCallbacks>(new AskUser::Protocol::ServerCallbacks(this, &m_popupper)));
+ AskUser::Protocol::ServerCallbacksPtr callbacks(new AskUser::Protocol::ServerCallbacks(this, &m_popupper));
+ std::unique_ptr<AskUser::Protocol::ServerChannel> 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() {
}
// 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();
}
void finishCurrentEvent();
void popupResponse(NResponseType response);
- AskUser::Protocol::ChannelPtr m_clientChannel;
+ std::unique_ptr<AskUser::Protocol::ServerChannel> m_serverChannel;
Popupper m_popupper;
struct FdEvent {