Add notification daemon
[platform/core/security/askuser.git] / src / agent / main / NotificationTalker.cpp
1 /*
2  *  Copyright (c) 2016 Samsung Electronics Co.
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License
15  */
16 /**
17  * @file        src/daemon/NotificationTalker.cpp
18  * @author      Oskar Ĺšwitalski <o.switalski@samsung.com>
19  * @brief       Definition of NotificationTalker class
20  */
21
22 #include "NotificationTalker.h"
23
24 #include <algorithm>
25 #include <cstring>
26 #include <cynara-creds-socket.h>
27
28 #include <exception/ErrnoException.h>
29 #include <exception/CynaraException.h>
30 #include <log/alog.h>
31 #include <socket/Socket.h>
32 #include <translator/Translator.h>
33 #include <config/Path.h>
34 #include <types/Protocol.h>
35
36 namespace AskUser {
37
38 namespace Agent {
39
40 NotificationTalker::NotificationTalker()
41 {
42     m_stopflag = false;
43     m_select.setTimeout(100);
44     m_sockfd = Socket::listen(Path::getSocketPath().c_str());
45 }
46
47 void NotificationTalker::parseRequest(RequestType type, NotificationRequest request)
48 {
49     switch (type) {
50     case RequestType::RT_Close:
51         ALOGD("Close service");
52         stop();
53         return;
54     case RequestType::RT_Action:
55         ALOGD("Add request: " << request.id);
56         addRequest(std::move(request));
57         return;
58     case RequestType::RT_Cancel:
59         ALOGD("Cancel request: " << request.id);
60         removeRequest(request.id);
61         return;
62     default:
63         return;
64     }
65 }
66
67 void NotificationTalker::addRequest(NotificationRequest &&request)
68 {
69     std::lock_guard<std::mutex> lock(m_mutex);
70
71     auto &queue = m_requests[request.data.user];
72     auto it = std::find_if(queue.begin(), queue.end(),
73             [&request](const NotificationRequest &req){return req.id == request.id;}
74         );
75
76     if (it == queue.end()) {
77         queue.emplace_back(std::move(request));
78     } else {
79         ALOGD("Cynara request already exists");
80     }
81 }
82
83 void NotificationTalker::removeRequest(RequestId id)
84 {
85     std::lock_guard<std::mutex> lock(m_mutex);
86
87     for (auto &pair : m_requests) {
88         auto &queue = std::get<1>(pair);
89         auto it = std::find_if(queue.begin(), queue.end(),
90                 [&id](const NotificationRequest &req){return req.id == id;}
91             );
92         if (it == queue.end()) {
93             ALOGW("Removing non-existent request");
94             return;
95         }
96         if (it == queue.begin()) {
97             auto user = std::get<0>(pair);
98             auto it2 = m_userToFd.find(user);
99             if (it2 != m_userToFd.end())
100                 sendDismiss(std::get<1>(*it2));
101         }
102
103         queue.erase(it);
104     }
105 }
106
107 void NotificationTalker::setResponseHandler(ResponseHandler responseHandler)
108 {
109     m_responseHandler = responseHandler;
110 }
111
112 void NotificationTalker::stop()
113 {
114     m_stopflag = true;
115
116     for (auto& pair : m_fdStatus) {
117         int fd = std::get<0>(pair);
118         Socket::close(fd);
119     }
120
121     m_fdStatus.clear();
122     m_fdToUser.clear();
123     m_userToFd.clear();
124
125     Socket::close(m_sockfd);
126     m_sockfd = 0;
127 }
128
129 NotificationTalker::~NotificationTalker()
130 {
131     for (auto& pair : m_fdStatus) {
132         int fd = std::get<0>(pair);
133         Socket::close(fd);
134     }
135
136     Socket::close(m_sockfd);
137 }
138
139 void NotificationTalker::sendRequest(int fd, const NotificationRequest &request)
140 {
141     m_fdStatus[fd] = false;
142
143     std::string data = Translator::Gui::notificationRequestToData(request.id,
144                                                                   request.data.client,
145                                                                   request.data.privilege);
146     auto size = data.size();
147
148     if (!Socket::send(fd, &size, sizeof(size))) {
149         remove(fd);
150         return;
151     }
152
153     if (!Socket::send(fd, data.c_str(), size)) {
154         remove(fd);
155         return;
156     }
157 }
158
159 void NotificationTalker::sendDismiss(int fd)
160 {
161     if (!m_fdStatus[fd]) {
162         if (!Socket::send(fd, &Protocol::dissmisCode, sizeof(Protocol::dissmisCode))) {
163             remove(fd);
164             return;
165         }
166         m_fdStatus[fd] = true;
167     }
168 }
169
170 void NotificationTalker::parseResponse(NotificationResponse response, int fd)
171 {
172     auto &queue = m_requests[m_fdToUser[fd]];
173     if (queue.empty()) {
174         ALOGD("Request canceled");
175         m_fdStatus[fd] = true;
176         return;
177     }
178
179     NotificationRequest request = queue.front();
180     if (request.id != response.id) {
181         ALOGD("Request canceled");
182         m_fdStatus[fd] = true;
183         return;
184     }
185
186     queue.pop_front();
187     ALOGD("For user: <" << request.data.user
188           << "> client: <" << request.data.client
189           << "> privilege: <" << request.data.privilege
190           << "> received: <" << Translator::Gui::responseToString(response.response) << ">");
191
192     m_responseHandler(response);
193
194     if (!Socket::send(fd, &Protocol::ackCode, sizeof(Protocol::ackCode))) {
195         remove(fd);
196         return;
197     }
198
199     m_fdStatus[fd] = true;
200 }
201
202 void NotificationTalker::recvResponses(int &rv)
203 {
204     for (auto pair : m_userToFd) {
205         if (!rv) break;
206         int fd = std::get<1>(pair);
207
208         if (m_select.isSet(fd)) {
209             --rv;
210
211             NotificationResponse response;
212             if (Socket::recv(fd, &response, sizeof(response))) {
213                 parseResponse(response, fd);
214             } else {
215                 remove(fd);
216             }
217         }
218     }
219 }
220
221 void NotificationTalker::newConnection(int &rv)
222 {
223     if (m_select.isSet(m_sockfd)) {
224         --rv;
225         int fd = Socket::accept(m_sockfd);
226         try {
227             char *user_c = nullptr;
228
229             int ret = cynara_creds_socket_get_user(fd, USER_METHOD_DEFAULT,&user_c);
230
231             std::unique_ptr<char[]> userPtr(user_c);
232
233             if (ret != CYNARA_API_SUCCESS) {
234                 throw CynaraException("cynara_creds_socket_get_user", ret);
235             }
236             std::string user = user_c;
237
238             auto it = m_userToFd.find(user);
239             if (it != m_userToFd.end())
240                 remove(std::get<1>(*it));
241
242             m_userToFd[user] = fd;
243             m_fdToUser[fd] = user;
244             m_fdStatus[fd] = true;
245
246             ALOGD("Accepted new conection for user: " << user);
247         } catch (...) {
248             Socket::close(fd);
249             throw;
250         }
251     }
252 }
253
254 void NotificationTalker::remove(int fd)
255 {
256     Socket::close(fd);
257     auto user = m_fdToUser[fd];
258     m_fdToUser.erase(fd);
259     m_userToFd.erase(user);
260     m_fdStatus.erase(fd);
261 }
262
263 void NotificationTalker::run()
264 {
265     try {
266         ALOGD("Notification loop started");
267         while (!m_stopflag) {
268             m_select.add(m_sockfd);
269
270             for (auto pair : m_userToFd)
271                 m_select.add(std::get<1>(pair));
272
273             int rv = m_select.exec();
274
275             if (m_stopflag)
276                 break;
277
278             if (rv) {
279                 newConnection(rv);
280                 recvResponses(rv);
281             } else {
282                 // timeout
283             }
284
285             /* lock_guard */
286             {
287                 std::lock_guard<std::mutex> lock(m_mutex);
288                 for (auto pair : m_fdStatus ) {
289                     int fd = std::get<0>(pair);
290                     bool b = std::get<1>(pair);
291                     auto &queue = m_requests[m_fdToUser[fd]];
292                     if (b && !queue.empty()) {
293                         NotificationRequest request = queue.front();
294                         sendRequest(fd, request);
295                     }
296                 }
297             } /* lock_guard */
298         }
299         ALOGD("NotificationTalker loop ended");
300     } catch (const std::exception &e) {
301         ALOGE("NotificationTalker: " << e.what());
302     } catch (...) {
303         ALOGE("NotificationTalker: unknown error");
304     }
305 }
306
307 } /* namespace Agent */
308
309 } /* namespace AskUser */