ab9d3355e5d5022eb0695fb77c5d2e4add7b6cf7
[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         Evas_Object *popup;
62         Evas_Object *box;
63         Evas_Object *title;
64         Evas_Object *content;
65         Evas_Object *buttonAllow;
66         Evas_Object *buttonDeny;
67
68         /* output */
69         TPKP::UI::Response result;
70
71         TpkpPopup() :
72                 hostname(),
73                 timeout(-1),
74                 win(nullptr),
75                 popup(nullptr),
76                 box(nullptr),
77                 title(nullptr),
78                 content(nullptr),
79                 buttonAllow(nullptr),
80                 buttonDeny(nullptr),
81                 result(TPKP::UI::Response::ERROR) {}
82 };
83
84 struct SockRaii {
85         int sock;
86         SockRaii() : sock(-1) {}
87         SockRaii(int _sock) : sock(_sock) {}
88         ~SockRaii()
89         {
90                 if (sock != -1)
91                         close(sock);
92         }
93 };
94
95 struct ElmRaii {
96         ElmRaii(int argc, char **argv)
97         {
98                 SLOGD("elm_init()");
99                 elm_init(argc, argv);
100
101                 elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
102         }
103
104         virtual ~ElmRaii()
105         {
106                 SLOGD("elm_shutdown()");
107                 elm_shutdown();
108         }
109 };
110
111 void answerAllowCb(void *data, Evas_Object * /* obj */, void * /* event_info */)
112 {
113         SLOGD("allow answer");
114
115         TPKP_CHECK_THROW_EXCEPTION(data != nullptr,
116                 TPKP_E_INTERNAL, "data shouldn't be null on evas callbacks");
117
118         TpkpPopup *pdp = static_cast<TpkpPopup *>(data);
119         pdp->result = Response::ALLOW;
120
121         evas_object_del(pdp->win);
122 }
123
124 void answerDenyCb(void *data, Evas_Object * /* obj */, void * /* event_info */)
125 {
126         SLOGD("deny answer");
127
128         TPKP_CHECK_THROW_EXCEPTION(data != nullptr,
129                 TPKP_E_INTERNAL, "data shouldn't be null on evas callbacks");
130
131         TpkpPopup *pdp = static_cast<TpkpPopup *>(data);
132         pdp->result = Response::DENY;
133
134         evas_object_del(pdp->win);
135 }
136
137 Eina_Bool timeoutCb(void *data)
138 {
139         TPKP_CHECK_THROW_EXCEPTION(data != nullptr,
140                 TPKP_E_INTERNAL, "data shouldn't be null on timeout callback");
141         TpkpPopup *pdp = static_cast<TpkpPopup *>(data);
142         pdp->result = Response::DENY;
143
144         SLOGI("popup timeout[%d](ms) reached! Let's deny", pdp->timeout);
145
146         evas_object_del(pdp->win);
147
148         return ECORE_CALLBACK_CANCEL;
149 }
150
151 /*
152  *  popup layout
153  *
154  *               window
155  *  --------------------------------
156  *  |                              |
157  *  |            popup             |
158  *  | ---------------------------- |
159  *  | |    content (description) | |
160  *  | |                          | |
161  *  | |                          | |
162  *  | | -----------  ----------- | |
163  *  | | | button1 |  | button2 | | |
164  *  | | -----------  ----------- | |
165  *  | |                          | |
166  *  | ---------------------------- |
167  *  --------------------------------
168  */
169 /* TODO(k.tak): UI layout refinement */
170 void showPopup(TpkpPopup *pdp)
171 {
172         SLOGD("Start to make popup");
173
174         TPKP_CHECK_THROW_EXCEPTION(pdp != nullptr,
175                 TPKP_E_INTERNAL, "pdp shouldn't be null");
176
177         pdp->win = elm_win_add(nullptr, "tpkp popup", ELM_WIN_NOTIFICATION);
178
179         elm_win_autodel_set(pdp->win, EINA_TRUE);
180         elm_win_indicator_opacity_set(pdp->win, ELM_WIN_INDICATOR_TRANSLUCENT);
181         elm_win_alpha_set(pdp->win, true);
182         evas_object_show(pdp->win);
183
184         pdp->popup = elm_popup_add(pdp->win);
185         evas_object_show(pdp->popup);
186
187         pdp->box = elm_box_add(pdp->popup);
188         evas_object_size_hint_weight_set(pdp->box, EVAS_HINT_EXPAND, 0);
189         evas_object_size_hint_align_set(pdp->box, EVAS_HINT_FILL, 0.0);
190         evas_object_show(pdp->box);
191
192         pdp->title = elm_label_add(pdp->popup);
193         elm_object_style_set(pdp->title, "elm.text.title");
194         elm_object_text_set(pdp->title, dgettext(PROJECT_NAME, "SID_TITLE_PUBLIC_KEY_MISMATCHED"));
195         evas_object_show(pdp->title);
196         elm_box_pack_end(pdp->box, pdp->title);
197
198         pdp->content = elm_label_add(pdp->popup);
199         elm_object_style_set(pdp->content, "elm.swallow.content");
200         elm_label_line_wrap_set(pdp->content, ELM_WRAP_MIXED);
201         char *contentFormat = dgettext(PROJECT_NAME, "SID_CONTENT_PUBLIC_KEY_MISMATCHED");
202         char *content = nullptr;
203         if (asprintf(&content, contentFormat, pdp->hostname.c_str()) == -1) {
204                 SLOGE("Failed to alloc memory for popup text. Just go for it.");
205                 elm_object_text_set(pdp->content, contentFormat);
206         } else {
207                 elm_object_text_set(pdp->content, content);
208                 free(content);
209         }
210
211         evas_object_size_hint_weight_set(pdp->content, EVAS_HINT_EXPAND, 0.0);
212         evas_object_size_hint_align_set(pdp->content, EVAS_HINT_FILL, EVAS_HINT_FILL);
213         evas_object_show(pdp->content);
214         elm_box_pack_end(pdp->box, pdp->content);
215
216         elm_object_part_content_set(pdp->popup, "default", pdp->box);
217
218         pdp->buttonAllow = elm_button_add(pdp->popup);
219         elm_object_style_set(pdp->buttonAllow, "elm.swallow.content.button1");
220         elm_object_text_set(pdp->buttonAllow, dgettext(PROJECT_NAME, "SID_BTN_ALLOW"));
221         elm_object_part_content_set(pdp->popup, "button1", pdp->buttonAllow);
222         evas_object_smart_callback_add(pdp->buttonAllow, "clicked", answerAllowCb, pdp);
223         evas_object_show(pdp->buttonAllow);
224
225         pdp->buttonDeny = elm_button_add(pdp->popup);
226         elm_object_style_set(pdp->buttonDeny, "elm.swallow.content.button2");
227         elm_object_text_set(pdp->buttonDeny, dgettext(PROJECT_NAME, "SID_BTN_DENY"));
228         elm_object_part_content_set(pdp->popup, "button2  ", pdp->buttonDeny);
229         evas_object_smart_callback_add(pdp->buttonDeny, "clicked", answerDenyCb, pdp);
230         evas_object_show(pdp->buttonDeny);
231
232         if (pdp->timeout > 0) {
233                 ecore_timer_add(pdp->timeout / 1000, timeoutCb, pdp);
234         }
235
236         SLOGD("elm_run start");
237         elm_run();
238 }
239
240 /*
241  *  child receive list
242  *  - std::string hostname
243  */
244 void deserialize(TpkpPopup *pdp, BinaryStream &stream)
245 {
246         Deserialization::Deserialize(stream, pdp->hostname);
247         Deserialization::Deserialize(stream, pdp->timeout);
248
249         SLOGD("Params from popup_runner: hostname[%s] timeout[%d]",
250                 pdp->hostname.c_str(), pdp->timeout);
251 }
252
253 /*
254  *  child send list
255  *  - TPKP::UI::Response response (int)
256  */
257 BinaryStream serialize(TpkpPopup *pdp)
258 {
259         BinaryStream stream;
260         Serialization::Serialize(stream, static_cast<int>(pdp->result));
261
262         return stream;
263 }
264
265 int getSockFromSystemd(void)
266 {
267         int n = sd_listen_fds(0);
268
269         for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; ++fd) {
270                 if (sd_is_socket_unix(fd, SOCK_STREAM, 1, TPKP_UI_SOCK_ADDR, 0) > 0) {
271                         SLOGD("Get socket from systemd. fd[%d]", fd);
272                         return fd;
273                 }
274         }
275         TPKP_THROW_EXCEPTION(TPKP_E_INTERNAL, "Failed to get sock from systemd.");
276 }
277
278 } // namespace anonymous
279
280 int main(int argc, char **argv)
281 {
282         SLOGI("tpkp popup backend server start!");
283
284         /* init/shutdown elm automatically */
285         ElmRaii elmRaii(argc, argv);
286
287         setlocale(LC_ALL, vconf_get_str(VCONFKEY_LANGSET));
288
289         try {
290                 struct sockaddr_un clientaddr;
291                 int client_len = sizeof(clientaddr);
292
293                 struct pollfd fds[1];
294                 fds[0].fd = getSockFromSystemd();
295                 fds[0].events = POLLIN;
296
297                 SLOGD("server fd from systemd: %d", fds[0].fd);
298
299                 while (true) {
300                         /* non blocking poll */
301                         int ret = poll(fds, 1, 0);
302                         TPKP_CHECK_THROW_EXCEPTION(ret >= 0,
303                                 TPKP_E_INTERNAL, "poll() error. errno: " << errno);
304
305                         if (ret == 0) {
306                                 SLOGD("tpkp-popup backend service timeout. Let's be deactivated");
307                                 return 0;
308                         }
309
310                         /* ready to accept! */
311
312                         memset(&clientaddr, 0, client_len);
313
314                         int clientFd = accept(fds[0].fd, (struct sockaddr *)&clientaddr, (socklen_t *)&client_len);
315                         TPKP_CHECK_THROW_EXCEPTION(clientFd >= 0, TPKP_E_INTERNAL, "Error in func accept()");
316                         SLOGD("client accepted with fd: %d", clientFd);
317
318                         SockRaii clientSock(clientFd);
319
320                         TpkpPopup pd;
321                         TpkpPopup *pdp = &pd;
322
323                         /* receive arguments */
324                         BinaryStream stream = receiveStream(clientFd);
325                         deserialize(pdp, stream);
326
327                         /* get user response */
328                         showPopup(pdp);
329                         SLOGD("pdp->result : %d", static_cast<int>(pdp->result));
330
331                         /* send result */
332                         stream = serialize(pdp);
333                         sendStream(clientFd, stream);
334
335                         SLOGD("tpkp-popup done successfully!");
336                 }
337         } catch (const TPKP::Exception &e) {
338                 SLOGE("Exception[%d]: %s", e.code(), e.what());
339         } catch (const std::bad_alloc &e) {
340                 SLOGE("bad_alloc std exception: %s", e.what());
341         } catch (const std::exception &e) {
342                 SLOGE("std exception: %s", e.what());
343         } catch (...) {
344                 SLOGE("Unhandled exception occured!");
345         }
346
347         return 0;
348 }