* @brief This file contains test of CAPI for Privacy Privilege Manager
*/
-#include <glib.h>
+#include <iostream>
+#include <map>
+#include <set>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
+#include <glib.h>
#include <unistd.h>
+#include <attributes/attributes.h>
+#include <askuser-notification/ask-user-client-channel.h>
+#include <askuser-notification/ask-user-server-channel.h>
+#include <askuser-notification/ask-user-types.h>
#include <privacy_privilege_manager.h>
-const char *result_to_str(ppm_popup_result_e result)
+using namespace AskUser::Protocol;
+
+struct ServerPrivilegeRequest {
+
+ ServerPrivilegeRequest(RequestId reqId)
+ : m_id(reqId)
+ {}
+
+ ServerPrivilegeRequest(RequestId reqId, const Privilege &privilege)
+ : m_id(reqId)
+ , m_privilege(privilege)
+ {}
+
+ bool operator<(const ServerPrivilegeRequest &other) const {
+ return m_id < other.m_id;
+ }
+
+ RequestId m_id;
+ Privilege m_privilege;
+};
+
+typedef std::set<ServerPrivilegeRequest> PrivilegeRequestSet;
+
+struct ClientRequest {
+ RequestId m_id;
+ Privilege m_privilege;
+};
+
+struct Connection
+{
+ GIOChannel *gio_channel;
+ GIOCondition condition;
+ guint watch_id;
+};
+
+enum class Mode {
+ CLIENT,
+ SERVER,
+ UNKNOWN
+};
+
+const GIOCondition GIO_ERROR_CONDITION = static_cast<GIOCondition>(G_IO_ERR | G_IO_HUP | G_IO_NVAL);
+
+GIOCondition maskToGIOCondition(int mask)
+{
+ int ret = (mask & FdMask::READ ? G_IO_IN : 0) |
+ (mask & FdMask::WRITE ? G_IO_OUT : 0);
+ return static_cast<GIOCondition>(ret);
+}
+
+GIOCondition makeCondition(GIOCondition condition)
+{
+ return static_cast<GIOCondition>(condition | GIO_ERROR_CONDITION);
+}
+
+bool isErrorCondition(GIOCondition condition)
+{
+ return !!(GIO_ERROR_CONDITION & condition);
+}
+
+void print_prompt(Mode mode)
+{
+ switch (mode) {
+ case Mode::CLIENT:
+ printf("Client ");
+ break;
+ case Mode::SERVER:
+ printf("Server ");
+ break;
+ case Mode::UNKNOWN:
+ printf("Unknown ");
+ break;
+ }
+
+ printf("> ");
+ fflush(stdout);
+}
+
+struct ServerSimulator : public IServerCallbacks {
+
+ ServerSimulator()
+ : m_channel(nullptr)
+ {
+ }
+
+ virtual void newConnection(ConnectionFd fd, const Credentials &creds) {
+ printf("\nnew connection fd: %d credentials = { label: \"%s\" uid: %s }",
+ fd, creds.label.c_str(), creds.uid.c_str());
+ }
+
+ static gboolean server_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
+ {
+ ServerSimulator *handle = static_cast<ServerSimulator *>(data);
+
+ int fd = g_io_channel_unix_get_fd(src);
+ int events = (cond & G_IO_IN ? FdMask::READ : 0) |
+ (cond & G_IO_OUT ? FdMask::WRITE : 0);
+
+ handle->m_channel->process(fd, isErrorCondition(cond) ? 0 : events);
+
+ return TRUE;
+ }
+
+ virtual void updateConnection(ConnectionFd fd, int mask) {
+ if (fd == -1) {
+ printf("ERROR: updateConnection() called with fd == -1\n");
+ return;
+ }
+
+ GIOCondition gio_condition = maskToGIOCondition(mask);
+
+ auto it = m_connections.find(fd);
+
+ if (mask == 0) {
+ if (it == m_connections.end())
+ return;
+
+ auto &connection = it->second;
+
+ g_source_remove(connection.watch_id);
+ g_io_channel_unref(connection.gio_channel);
+
+ m_connections.erase(fd);
+ m_requests.erase(fd);
+
+ printf("Connection for fd: %d has been closed\n", fd);
+ print_prompt(Mode::SERVER);
+ return;
+ }
+
+ if (it == m_connections.end()) {
+ GIOChannel *gio_channel = g_io_channel_unix_new(fd);
+
+ g_io_channel_set_encoding (gio_channel, nullptr, nullptr);
+ g_io_channel_set_close_on_unref(gio_channel, FALSE);
+
+ guint watch_id = g_io_add_watch(gio_channel,
+ makeCondition(gio_condition),
+ server_gio_cb,
+ this);
+
+ m_connections.insert({ fd, Connection{gio_channel, gio_condition, watch_id }});
+
+ return;
+ }
+
+ auto &connection = it->second;
+
+ if (connection.condition != gio_condition) {
+ connection.condition = gio_condition;
+ g_source_remove(connection.watch_id);
+ connection.watch_id = g_io_add_watch(connection.gio_channel,
+ makeCondition(connection.condition),
+ server_gio_cb,
+ this);
+ }
+ }
+
+ virtual void popup(ConnectionFd fd, RequestId id, Privilege &&privilege) {
+ printf("\npopup request fd: %d requestId: %d privilege: \"%s\"\n", fd, id, privilege.c_str());
+ addRequest(fd, id, privilege);
+ print_prompt(Mode::SERVER);
+ }
+
+ void addRequest(ConnectionFd fd, RequestId id, const Privilege &privilege) {
+ auto fdIt = m_requests.find(fd);
+ if (fdIt == m_requests.end()) {
+ m_requests.insert({ fd, PrivilegeRequestSet() });
+ }
+
+ auto &s = m_requests[fd];
+
+ auto reqIt = s.find(id);
+ if (reqIt != s.end()) {
+ printf("ERROR: We already have request id: %d for fd: %d\n", id, fd);
+ return;
+ }
+
+ s.insert(ServerPrivilegeRequest(id, privilege));
+ }
+
+ void sendResponse(ConnectionFd fd, RequestId id, int response) {
+ auto fdIt = m_requests.find(fd);
+ if (fdIt == m_requests.end()) {
+ printf("ERROR: Connection for fd: %d doesn't exist!\n", fd);
+ return;
+ }
+
+ auto &s = fdIt->second;
+
+ auto reqIt = s.find(id);
+ if (reqIt == s.end()) {
+ printf("ERROR: Request %d doesn't exist for fd: %d\n", id, fd);
+ return;
+ }
+
+ if (m_channel) {
+ m_channel->popupResponse(fd, id, response);
+
+ // delete request
+ s.erase(reqIt);
+ if (s.empty()) {
+ m_requests.erase(fdIt);
+ }
+ }
+ }
+
+ void showPrivilegeRequests() {
+ printf("fd requestId privilege\n");
+ for (const auto &con : m_requests) {
+ for (const auto &req : con.second) {
+ printf("%-11d %-11d %-20s\n", con.first, req.m_id, req.m_privilege.c_str());
+ }
+ }
+ }
+
+ void closeConnection(int fd) {
+ auto it = m_connections.find(fd);
+ if (it == m_connections.end()) {
+ printf("ERROR: Connection fd: %d not found\n", fd);
+ print_prompt(Mode::SERVER);
+ return;
+ }
+
+ m_requests.erase(fd);
+ m_channel->process(fd, 0);
+ }
+
+ void showConnections() {
+ printf("fd\n");
+ for (const auto &con : m_connections) {
+ printf("%d\n", con.first);
+ }
+ }
+
+ void setServerChannel(ServerChannel *ptr) {
+ m_channel = ptr;
+ }
+
+private:
+ ServerChannel *m_channel;
+ std::map<ConnectionFd, Connection> m_connections;
+ std::map<ConnectionFd, PrivilegeRequestSet> m_requests;
+};
+
+struct AppContext {
+ AppContext()
+ : m_serverSimulator(nullptr)
+ , m_channel(nullptr)
+ , m_mode(Mode::UNKNOWN)
+ {}
+
+ ~AppContext() {
+ if (m_channel != nullptr) {
+ delete m_channel;
+ }
+ }
+
+ void initialize() {
+ if (m_mode == Mode::SERVER) {
+ m_serverSimulator = new ServerSimulator();
+ m_channel = new ServerChannel(ServerCallbacksPtr(m_serverSimulator));
+ m_serverSimulator->setServerChannel(m_channel);
+ }
+ }
+
+ ServerSimulator *m_serverSimulator;
+ ServerChannel *m_channel;
+ Mode m_mode;
+};
+
+const char *popup_result_to_str(ppm_popup_result_e result)
{
switch (result) {
case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_ALLOW_FOREVER:
void popup_response_cb(ppm_call_cause_e cause, ppm_popup_result_e result, void *user_data)
{
- int id = (int) user_data;
+ ClientRequest *request = static_cast<ClientRequest *>(user_data);
+ if (request == nullptr) {
+ printf("ERROR: User data is nullptr\n");
+ return;
+ }
+
+ printf("localId: %d privilege: \"%s\" ", request->m_id, request->m_privilege.c_str());
- printf("resp_id: %d ", id);
switch (cause) {
case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER:
- printf("popup answer: %s\n", result_to_str(result));
+ printf("popup response ANSWER: %s\n", popup_result_to_str(result));
break;
case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR:
- printf("popup cause PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR\n");
+ printf("popup response ERROR cause: PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR\n");
break;
}
- printf(">");
- fflush(stdout);
+ delete request;
+
+ print_prompt(Mode::CLIENT);
}
-static void print_help()
+void print_client_help()
{
- printf("0 <privilege> - check privilege status\n");
- printf("1 <privilege> - send popup request\n");
- printf("h - print help\n");
- printf("q - quit\n");
- printf(">");
- fflush(stdout);
+ printf("c <privilege> check privilege status\n");
+ printf("r <privilege> send popup request\n");
+ printf("h print help\n");
+ printf("q quit\n");
+ print_prompt(Mode::CLIENT);
}
-static void print_error(ppm_error_e error)
+void print_client_error(ppm_error_e error)
{
switch (error) {
case PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE:
break;
case PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY:
printf("Out of memory\n");
+ break;
case PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN:
printf("Unknown error\n");
break;
}
}
-static void print_check_result(ppm_check_result_e result)
+static void print_client_check_result(ppm_check_result_e result)
{
printf("res: ");
break;
}
- printf(">");
- fflush(stdout);
+ print_prompt(Mode::CLIENT);
}
-gboolean main_loop_cb(GIOChannel *channel, GIOCondition cond, gpointer user_data)
+void handle_client(char *buffer)
{
- int ret;
- char buffer[4096];
ppm_check_result_e result;
- ppm_error_e err;
-
- (void) channel; (void) cond; (void) user_data;
-
- ret = read(0, buffer, sizeof(buffer));
- if (ret < 0) {
- printf("read from stdin failed\n");
- exit(1);
- }
-
- buffer[ret - 1] = '\0';
- int id = rand() % 1024;
+ ppm_error_e err = PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
+ ClientRequest *request = nullptr;
+ char *privilege = nullptr;
+ static RequestId clientRequestId;
switch (buffer[0]) {
- case '0':
+ case 'c':
if (strlen(buffer) < 3) {
- print_help();
+ print_client_help();
break;
}
err = static_cast<ppm_error_e>(ppm_check_privilege(&buffer[2], &result));
if (err == PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
- print_check_result(result);
- }
- else {
- print_error(err);
+ print_client_check_result(result);
}
break;
- case '1':
+ case 'r':
if (strlen(buffer) < 3) {
- print_help();
+ print_client_help();
break;
}
- buffer[ret - 1] = '\0';
- err = static_cast<ppm_error_e>(ppm_popup_request(&buffer[2], popup_response_cb, (void *)id));
- print_error(err);
+
+ buffer[strlen(buffer)] = '\0'; // erase new line
+ privilege = &buffer[2];
+
+ printf("sending localId: %d privilege: \"%s\"\n", clientRequestId, privilege);
+
+ request = new ClientRequest{ clientRequestId++, privilege };
+ err = static_cast<ppm_error_e>(ppm_popup_request(privilege, popup_response_cb, request));
+ if (err != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
+ delete request;
+ }
+
break;
case 'q':
- exit(0);
+ exit(EXIT_SUCCESS);
case 'h':
default:
- print_help();
+ print_client_help();
+ return;
+ }
+
+ print_client_error(err);
+ print_prompt(Mode::CLIENT);
+}
+
+void print_server_help()
+{
+ printf("l list all connections\n");
+ printf("s list all requests\n");
+ printf("ra <fd> <request id> send ALLOW FOREVER response\n");
+ printf("rd <fd> <request id> send DENY FOREVER response\n");
+ printf("ro <fd> <request id> send DENY ONCE response\n");
+ printf("c <fd> close connection\n");
+ printf("h print help\n");
+ printf("q quit\n");
+ print_prompt(Mode::SERVER);
+}
+
+void handle_server(const char *buffer, ServerSimulator *serverSimulator)
+{
+ int res;
+ ConnectionFd fd;
+ RequestId requestId;
+
+ switch (buffer[0]) {
+ case 'l':
+ serverSimulator->showConnections();
+ break;
+ case 's':
+ serverSimulator->showPrivilegeRequests();
+ break;
+ case 'r':
+ res = sscanf(&buffer[2], "%d %d", &fd, &requestId);
+ if (res != 2) {
+ print_server_help();
+ return;
+ }
+ switch (buffer[1]) {
+ case 'a':
+ serverSimulator->sendResponse(fd, requestId, ASKUSER_ALLOW_FOREVER);
+ break;
+ case 'd':
+ serverSimulator->sendResponse(fd, requestId, ASKUSER_DENY_FOREVER);
+ break;
+ case 'o':
+ serverSimulator->sendResponse(fd, requestId, ASKUSER_DENY_ONCE);
+ break;
+ default:
+ print_server_help();
+ return;
+ }
+ break;
+ case 'c':
+ res = sscanf(&buffer[1], "%d", &fd);
+ if (res != 1) {
+ print_server_help();
+ return;
+ }
+ serverSimulator->closeConnection(fd);
+ return;
+ case 'h':
+ print_server_help();
+ return;
+ case 'q':
+ exit(EXIT_SUCCESS);
+ }
+
+ print_prompt(Mode::SERVER);
+}
+
+gboolean main_loop_cb(UNUSED GIOChannel *channel, UNUSED GIOCondition cond, gpointer user_data)
+{
+ char buffer[4096];
+
+ int ret = read(0, buffer, sizeof(buffer));
+ if (ret <= 0) {
+ printf("read from stdin failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ buffer[ret - 1] = '\0';
+
+ AppContext *ctx = static_cast<AppContext *>(user_data);
+ switch (ctx->m_mode) {
+ case Mode::CLIENT:
+ handle_client(buffer);
break;
+ case Mode::SERVER:
+ handle_server(buffer, ctx->m_serverSimulator);
+ break;
+ case Mode::UNKNOWN:
+ default:
+ printf("Mode not set, exiting.\n");
+ exit(EXIT_FAILURE);
}
return TRUE;
}
+void print_help(const char *programName)
+{
+ printf("Usage: %s [OPTIONS]\n", programName);
+ printf("Allows to test CAPI for Privacy Privilege Manager.\n");
+ printf("This program can run in two modes: as a client and as a server.\n");
+ printf("You can use it in two scenarios:\n");
+ printf("1. Run as a client (%s -c) to trigger popups in the askuser-notification daemon.\n", programName);
+ printf("2. Run one part as a client (%s -c) and other one as a server (%s -s).\n",
+ programName, programName);
+ printf("If you want to run this program as a root you need to create /var/run/user_ext/0 folder.\n");
+ printf("To run this program in context of some Tizen's application, type in the console:\n");
+ printf("#echo <SMACK label of the application> > /proc/self/attr/current\n");
+ printf("#su - <user name>\n\n");
+ printf("Options:\n");
+ printf("-s run as a server\n");
+ printf("-c run as a client (uses CAPI)\n");
+ printf("-h display this help end exit\n");
+}
+
int main(int argc, char **argv)
{
- (void) argc; (void) argv;
+ AppContext ctx;
+ int c;
- srand(time(0));
- print_help();
+ opterr = 0;
+ while ((c = getopt (argc, argv, "sch")) != -1) {
+ switch (c) {
+ case 's':
+ ctx.m_mode = Mode::SERVER;
+ print_server_help();
+ break;
+ case 'c':
+ ctx.m_mode = Mode::CLIENT;
+ print_client_help();
+ break;
+ case 'h':
+ default:
+ break;
+ }
+ }
+
+ if (ctx.m_mode == Mode::UNKNOWN) {
+ print_help(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+
+ try {
+ ctx.initialize();
+ }
+ catch (...) {
+ printf("Unknwon exception occured, exiting.\n");
+ exit(EXIT_FAILURE);
+ }
- GMainLoop *mainloop = g_main_loop_new(NULL, FALSE);
+ GMainLoop *mainloop = g_main_loop_new(nullptr, FALSE);
- GIOChannel *channel = g_io_channel_unix_new(0);
- GIOCondition condition = static_cast<GIOCondition>(G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
- g_io_add_watch(channel, condition, main_loop_cb, NULL);
+ GIOChannel *gio_channel = g_io_channel_unix_new(0);
+ g_io_add_watch(gio_channel, static_cast<GIOCondition>(G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+ main_loop_cb, &ctx);
g_main_loop_run(mainloop);