Refine popup UI layout
[platform/core/security/pubkey-pinning.git] / src / common / ui / popup-bin / popup.cpp
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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        popup.cpp
18  * @author      Janusz Kozerski (j.kozerski@samsung.com)
19  * @version     1.0
20  */
21 #include <unistd.h>
22 #include <vector>
23 #include <memory>
24 #include <string>
25 #include <libintl.h>
26 #include <poll.h>
27 #include <sys/un.h>
28 #include <time.h>
29
30 #include <Elementary.h>
31 #include <Ecore.h>
32 #include <systemd/sd-daemon.h>
33 #include <vconf.h>
34
35 /*
36  * TODO(k.tak): Separate TPKP::Exception related codes from tpkp_common
37  *              not to include "tpkp_common.h" which have lot of dependencies
38  */
39 #include "tpkp_common.h"
40 #include "tpkp_logger.h"
41 #include "ui/popup_common.h"
42
43 #ifdef LOG_TAG
44 #undef LOG_TAG
45 #endif
46 #define LOG_TAG "TPKP_POPUP"
47
48 #define TPKP_UI_SOCK_ADDR "/tmp/.tpkp-ui-backend.sock"
49
50 using namespace TPKP::UI;
51
52 namespace {
53
54 struct TpkpPopup {
55         /* inputs */
56         std::string hostname;
57         int timeout;
58
59         /* internal data fields */
60         Evas_Object *win;
61
62         /* output */
63         TPKP::UI::Response result;
64
65         TpkpPopup() :
66                 hostname(),
67                 timeout(-1),
68                 win(nullptr),
69                 result(TPKP::UI::Response::ERROR) {}
70 };
71
72 struct SockRaii {
73         int sock;
74         SockRaii() : sock(-1) {}
75         SockRaii(int _sock) : sock(_sock) {}
76         ~SockRaii()
77         {
78                 if (sock != -1)
79                         close(sock);
80         }
81 };
82
83 struct ElmRaii {
84         ElmRaii(int argc, char **argv)
85         {
86                 SLOGD("elm_init()");
87                 elm_init(argc, argv);
88
89                 elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
90         }
91
92         virtual ~ElmRaii()
93         {
94                 SLOGD("elm_shutdown()");
95                 elm_shutdown();
96         }
97 };
98
99 void answerAllowCb(void *data, Evas_Object * /* obj */, void * /* event_info */)
100 {
101         SLOGD("allow answer");
102
103         TPKP_CHECK_THROW_EXCEPTION(data != nullptr,
104                 TPKP_E_INTERNAL, "data shouldn't be null on evas callbacks");
105
106         TpkpPopup *pdp = static_cast<TpkpPopup *>(data);
107         pdp->result = Response::ALLOW;
108
109         evas_object_del(pdp->win);
110 }
111
112 void answerDenyCb(void *data, Evas_Object * /* obj */, void * /* event_info */)
113 {
114         SLOGD("deny answer");
115
116         TPKP_CHECK_THROW_EXCEPTION(data != nullptr,
117                 TPKP_E_INTERNAL, "data shouldn't be null on evas callbacks");
118
119         TpkpPopup *pdp = static_cast<TpkpPopup *>(data);
120         pdp->result = Response::DENY;
121
122         evas_object_del(pdp->win);
123 }
124
125 Eina_Bool timeoutCb(void *data)
126 {
127         TPKP_CHECK_THROW_EXCEPTION(data != nullptr,
128                 TPKP_E_INTERNAL, "data shouldn't be null on timeout callback");
129         TpkpPopup *pdp = static_cast<TpkpPopup *>(data);
130         pdp->result = Response::DENY;
131
132         SLOGI("popup timeout[%d](ms) reached! Let's deny", pdp->timeout);
133
134         evas_object_del(pdp->win);
135
136         return ECORE_CALLBACK_CANCEL;
137 }
138
139 std::unique_ptr<char> getPopupContentString(TpkpPopup *pdp)
140 {
141         char *contentFormat = dgettext(PROJECT_NAME, "SID_CONTENT_PUBLIC_KEY_MISMATCHED");
142         char *content = nullptr;
143
144         if (asprintf(&content, contentFormat, pdp->hostname.c_str()) == -1)
145                 TPKP_THROW_EXCEPTION(TPKP_E_INTERNAL, "Failed to alloc memory for popup text");
146
147         return std::unique_ptr<char>(content);
148 }
149
150 /*
151  *  popup layout
152  *
153  *               window
154  *  --------------------------------
155  *  |                              |
156  *  |            popup             |
157  *  | ---------------------------- |
158  *  | |          title           | |
159  *  | |--------------------------| |
160  *  | |    content (description) | |
161  *  | |                          | |
162  *  | | -----------  ----------- | |
163  *  | | | button1 |  | button2 | | |
164  *  | | -----------  ----------- | |
165  *  | ---------------------------- |
166  *  |                              |
167  *  --------------------------------
168  */
169 void showPopup(TpkpPopup *pdp)
170 {
171         SLOGD("Start to make popup");
172
173         TPKP_CHECK_THROW_EXCEPTION(pdp != nullptr,
174                 TPKP_E_INTERNAL, "pdp shouldn't be null");
175
176         /* create win */
177         Evas_Object *win = elm_win_add(nullptr, "tpkp popup", ELM_WIN_NOTIFICATION);
178         elm_win_autodel_set(win, EINA_TRUE);
179         elm_win_indicator_opacity_set(win, ELM_WIN_INDICATOR_TRANSLUCENT);
180         elm_win_borderless_set(win, EINA_TRUE);
181         elm_win_alpha_set(win, EINA_TRUE);
182         evas_object_show(win);
183
184         /* create popup */
185         auto contentString = getPopupContentString(pdp);
186         Evas_Object *popup = elm_popup_add(win);
187         evas_object_size_hint_weight_set(popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
188         elm_object_text_set(popup, contentString.get());
189         elm_object_part_text_set(popup, "title,text", dgettext(PROJECT_NAME, "SID_TITLE_PUBLIC_KEY_MISMATCHED"));
190         evas_object_show(popup);
191
192         /* create allow button */
193         Evas_Object *buttonAllow = elm_button_add(popup);
194         elm_object_style_set(buttonAllow, "bottom");
195         elm_object_text_set(buttonAllow, dgettext(PROJECT_NAME, "SID_BTN_ALLOW"));
196         elm_object_part_content_set(popup, "button1", buttonAllow);
197         evas_object_smart_callback_add(buttonAllow, "clicked", answerAllowCb, pdp);
198         evas_object_show(buttonAllow);
199
200         /* create deny button */
201         Evas_Object *buttonDeny = elm_button_add(popup);
202         elm_object_style_set(buttonDeny, "bottom");
203         elm_object_text_set(buttonDeny, dgettext(PROJECT_NAME, "SID_BTN_DENY"));
204         elm_object_part_content_set(popup, "button2", buttonDeny);
205         evas_object_smart_callback_add(buttonDeny, "clicked", answerDenyCb, pdp);
206         evas_object_show(buttonDeny);
207
208         if (pdp->timeout > 0) {
209                 ecore_timer_add(pdp->timeout / 1000, timeoutCb, pdp);
210         }
211
212         pdp->win = win;
213
214         SLOGD("elm_run start");
215         elm_run();
216 }
217
218 /*
219  *  child receive list
220  *  - std::string hostname
221  */
222 void deserialize(TpkpPopup *pdp, BinaryStream &stream)
223 {
224         Deserialization::Deserialize(stream, pdp->hostname);
225         Deserialization::Deserialize(stream, pdp->timeout);
226
227         SLOGD("Params from popup_runner: hostname[%s] timeout[%d]",
228                 pdp->hostname.c_str(), pdp->timeout);
229 }
230
231 /*
232  *  child send list
233  *  - TPKP::UI::Response response (int)
234  */
235 BinaryStream serialize(TpkpPopup *pdp)
236 {
237         BinaryStream stream;
238         Serialization::Serialize(stream, static_cast<int>(pdp->result));
239
240         return stream;
241 }
242
243 int getSockFromSystemd(void)
244 {
245         int n = sd_listen_fds(0);
246
247         for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; ++fd) {
248                 if (sd_is_socket_unix(fd, SOCK_STREAM, 1, TPKP_UI_SOCK_ADDR, 0) > 0) {
249                         SLOGD("Get socket from systemd. fd[%d]", fd);
250                         return fd;
251                 }
252         }
253         TPKP_THROW_EXCEPTION(TPKP_E_INTERNAL, "Failed to get sock from systemd.");
254 }
255
256 } // namespace anonymous
257
258 int main(int argc, char **argv)
259 {
260         SLOGI("tpkp popup backend server start!");
261
262         /* init/shutdown elm automatically */
263         ElmRaii elmRaii(argc, argv);
264
265         setlocale(LC_ALL, vconf_get_str(VCONFKEY_LANGSET));
266
267         try {
268                 struct sockaddr_un clientaddr;
269                 int client_len = sizeof(clientaddr);
270
271                 struct pollfd fds[1];
272                 fds[0].fd = getSockFromSystemd();
273                 fds[0].events = POLLIN;
274
275                 SLOGD("server fd from systemd: %d", fds[0].fd);
276
277                 while (true) {
278                         /* non blocking poll */
279                         int ret = poll(fds, 1, 0);
280                         TPKP_CHECK_THROW_EXCEPTION(ret >= 0,
281                                 TPKP_E_INTERNAL, "poll() error. errno: " << errno);
282
283                         if (ret == 0) {
284                                 SLOGD("tpkp-popup backend service timeout. Let's be deactivated");
285                                 return 0;
286                         }
287
288                         /* ready to accept! */
289
290                         memset(&clientaddr, 0, client_len);
291
292                         int clientFd = accept(fds[0].fd, (struct sockaddr *)&clientaddr, (socklen_t *)&client_len);
293                         TPKP_CHECK_THROW_EXCEPTION(clientFd >= 0, TPKP_E_INTERNAL, "Error in func accept()");
294                         SLOGD("client accepted with fd: %d", clientFd);
295
296                         SockRaii clientSock(clientFd);
297
298                         TpkpPopup pd;
299                         TpkpPopup *pdp = &pd;
300
301                         /* receive arguments */
302                         BinaryStream stream = receiveStream(clientFd);
303                         deserialize(pdp, stream);
304
305                         /* get user response */
306                         showPopup(pdp);
307                         SLOGD("pdp->result : %d", static_cast<int>(pdp->result));
308
309                         /* send result */
310                         stream = serialize(pdp);
311                         sendStream(clientFd, stream);
312
313                         SLOGD("tpkp-popup done successfully!");
314                 }
315         } catch (const TPKP::Exception &e) {
316                 SLOGE("Exception[%d]: %s", e.code(), e.what());
317         } catch (const std::bad_alloc &e) {
318                 SLOGE("bad_alloc std exception: %s", e.what());
319         } catch (const std::exception &e) {
320                 SLOGE("std exception: %s", e.what());
321         } catch (...) {
322                 SLOGE("Unhandled exception occured!");
323         }
324
325         return 0;
326 }