Source code formating unification
[platform/framework/web/wrt.git] / src / view / webkit / view_logic_web_notification_support.cpp
1 /*
2  * Copyright (c) 2011 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    view_logic_web_notification_support.cpp
18  * @author  Jihoon Chung (jihoon.chung@samsung.com)
19  * @brief   Implementation file of web Notification API used by ViewLogic
20  */
21
22 #include "view_logic_web_notification_support.h"
23 #include <string>
24 #include <dpl/log/log.h>
25 #include <dpl/string.h>
26 #include <wrt-commons/security-origin-dao/security_origin_dao_types.h>
27 #include <wrt-commons/security-origin-dao/security_origin_dao.h>
28 #include <common/view_logic_security_origin_support.h>
29
30 #include <notification.h>
31 #include <pcrecpp.h>
32 #include <sstream>
33 #include <curl/curl.h>
34 #include <widget_model.h>
35 #include <EWebKit2.h>
36
37 namespace {
38 const char* PATTERN_CHECK_EXTERNAL = "^http(s)?://\\w+.*$";
39 const char* const WEB_NOTIFICATION_ASK_BODY_PREFIX = "Do you want to allow ";
40 const char* const WEB_NOTIFICATION_ASK_BODY_POSTFIX =
41     " to use web notificaion?";
42 const char* const WEB_NOTIFICATION_ASK_CHECK = "Don't ask again";
43 }
44
45 namespace ViewModule {
46 namespace WebNotification {
47 using namespace SecurityOriginDB;
48 using namespace ViewModule::SecurityOriginSupportUtil;
49
50 class WebNotificationPermissionData :
51     public ViewModule::SecurityOriginSupportUtil::PermissionData
52 {
53   public:
54     Ewk_Context* m_ewkContext;
55
56     WebNotificationPermissionData(
57         SecurityOriginDB::SecurityOriginDAO* originDao,
58         SecurityOriginDB::SecurityOriginData originData,
59         Ewk_Context* ewkContext,
60         void* data) :
61         ViewModule::SecurityOriginSupportUtil::PermissionData(
62             originDao,
63             originData,
64             data),
65         m_ewkContext(ewkContext)
66     {}
67 };
68
69 bool notificationShow(WebNotificationDataPtr notiData);
70 bool notificationHide(WebNotificationDataPtr notiData);
71 bool isExternalUri(const std::string &pattern, const std::string &uri);
72 bool downloadImage(WebNotificationDataPtr notiData);
73 size_t curlWriteData(void *ptr, size_t size, size_t nmemb, FILE *stream);
74 void askUserForWebNotificationPermission(
75     Evas_Object* window,
76     WebNotificationPermissionData* data);
77 static void popupCallback(void* data, Evas_Object* obj, void* eventInfo);
78
79 WebNotificationData::WebNotificationData(WidgetModel* widgetModel,
80                                          uint64_t id) :
81     m_widgetModel(widgetModel),
82     m_id(id)
83 {
84     Assert(m_widgetModel);
85 }
86
87 WebNotificationData::~WebNotificationData()
88 {}
89
90 void webNotificationPermissionRequest(
91     Evas_Object* window,
92     SecurityOriginDAO* securityOriginDAO,
93     Ewk_Context* ewkContext,
94     void* data)
95 {
96     LogDebug("permissionRequest called");
97     Assert(securityOriginDAO);
98     Assert(data);
99     Ewk_Notification_Permission_Request* request =
100         static_cast<Ewk_Notification_Permission_Request*>(data);
101     const Ewk_Security_Origin* ewkOrigin =
102         ewk_notification_permission_request_origin_get(request);
103     Assert(ewkOrigin);
104
105     SecurityOriginData securityOriginData(
106         FEATURE_WEB_NOTIFICATION,
107         Origin(
108             DPL::FromUTF8String(ewk_security_origin_protocol_get(ewkOrigin)),
109             DPL::FromUTF8String(ewk_security_origin_host_get(ewkOrigin)),
110             ewk_security_origin_port_get(ewkOrigin)));
111
112     // check cache database
113     Result result = securityOriginDAO->getResult(securityOriginData);
114     if (RESULT_ALLOW_ONCE == result || RESULT_ALLOW_ALWAYS == result) {
115         LogDebug("allow");
116         ewk_notification_permission_request_response(ewkContext,
117                                                      request,
118                                                      EINA_TRUE);
119         return;
120     } else if (RESULT_DENY_ONCE == result || RESULT_DENY_ALWAYS == result) {
121         LogDebug("deny");
122         ewk_notification_permission_request_response(ewkContext,
123                                                      request,
124                                                      EINA_FALSE);
125         return;
126     }
127
128     // ask to user
129     WebNotificationPermissionData* permissionData =
130         new WebNotificationPermissionData(securityOriginDAO,
131                                           securityOriginData,
132                                           ewkContext,
133                                           data);
134
135     // suspend notification
136     ewk_notification_permission_request_suspend(request);
137     askUserForWebNotificationPermission(window, permissionData);
138     return;
139 }
140
141 bool showWebNotification(WebNotificationDataPtr notiData)
142 {
143     LogInfo("showWebNotification called");
144
145     /* TODO : register notification to database */
146     return notificationShow(notiData);
147 }
148
149 bool notificationShow(WebNotificationDataPtr notiData)
150 {
151     LogDebug("notificationShow called");
152     Assert(notiData);
153     notification_h noti_h = NULL;
154     notification_error_e error = NOTIFICATION_ERROR_NONE;
155
156     Try {
157         // create notification
158         noti_h = notification_new(
159                 NOTIFICATION_TYPE_NOTI,
160                 NOTIFICATION_GROUP_ID_NONE,
161                 notiData->m_id);
162         if (!noti_h) {
163             LogError("Fail to notification_new");
164             return false;
165         }
166
167         // set notification title
168         error = notification_set_text(
169                 noti_h,
170                 NOTIFICATION_TEXT_TYPE_TITLE,
171                 notiData->m_title.c_str(),
172                 NULL,
173                 NOTIFICATION_VARIABLE_TYPE_NONE);
174         if (error != NOTIFICATION_ERROR_NONE) {
175             ThrowMsg(Exception::NotificationShowError, "Fail to set title");
176         }
177
178         // set notification content
179         error = notification_set_text(
180                 noti_h,
181                 NOTIFICATION_TEXT_TYPE_CONTENT,
182                 notiData->m_body.c_str(),
183                 NULL,
184                 NOTIFICATION_VARIABLE_TYPE_NONE);
185         if (error != NOTIFICATION_ERROR_NONE) {
186             ThrowMsg(Exception::NotificationShowError,
187                      "Fail to set content:" << error);
188         }
189
190         //check uri is "http", https" or not
191         bool validIconURL = true;
192         LogInfo("url path is " << notiData->m_iconURL);
193         if (isExternalUri(PATTERN_CHECK_EXTERNAL, notiData->m_iconURL)) {
194             //download image from url
195             validIconURL = downloadImage(notiData);
196         }
197
198         //set image
199         // If fail to download external image, skip to set image.
200         // In this case, set to default package image.
201         if (true == validIconURL) {
202             error = notification_set_image(
203                     noti_h,
204                     NOTIFICATION_IMAGE_TYPE_ICON,
205                     notiData->m_iconURL.c_str());
206             if (error != NOTIFICATION_ERROR_NONE) {
207                 ThrowMsg(Exception::NotificationShowError,
208                          "Fail to free notification: " << error);
209             }
210         }
211
212         // insert notification
213         int priv_id = NOTIFICATION_PRIV_ID_NONE;
214         error = notification_insert(noti_h, &priv_id);
215         if (error != NOTIFICATION_ERROR_NONE) {
216             ThrowMsg(Exception::NotificationShowError,
217                      "Fail to insert notification: " << error);
218         }
219
220         LogInfo("Notification is inserted!");
221         LogInfo("noti id =[" << notiData->m_id << "] " <<
222                 "priv_id =[" << priv_id << "]");
223
224         if (noti_h) {
225             error = notification_free(noti_h);
226             if (error != NOTIFICATION_ERROR_NONE) {
227                 ThrowMsg(Exception::NotificationShowError,
228                          "Fail to free notification: " << error);
229             }
230             noti_h = NULL;
231         }
232         return true;
233     } Catch(Exception::NotificationShowError) {
234         LogError(_rethrown_exception.GetMessage());
235         notification_free(noti_h);
236         noti_h = NULL;
237         return false;
238     } Catch(DPL::Exception) {
239         LogError(_rethrown_exception.GetMessage());
240         return false;
241     }
242 }
243
244 bool notificationHide(WebNotificationDataPtr notiData)
245 {
246     LogInfo("notificationHide called");
247
248     Assert(notiData);
249     return true;
250 }
251
252 bool isExternalUri(const std::string &pattern, const std::string &uri)
253 {
254     LogInfo("isExternalUri called");
255
256     pcrecpp::RE_Options pcreOpt;
257     pcreOpt.set_caseless(true);
258     pcrecpp::RE re(pattern, pcreOpt);
259
260     return re.FullMatch(uri);
261 }
262
263 bool downloadImage(WebNotificationDataPtr notiData)
264 {
265     LogInfo("downloadImage called");
266
267     Assert(notiData);
268     // download path is
269     // /opt/apps/tizen_id/data + '/' + filename
270     std::string downloadPath =
271         DPL::ToUTF8String(
272             notiData->m_widgetModel->PersistentStoragePath.Get()) + "/";
273     LogDebug("downloadPath " << downloadPath);
274
275     // Make valid filename
276     // If there is already exist filename, rename to "filename_%d"
277     std::string fileName =
278         notiData->m_iconURL.substr(notiData->m_iconURL.rfind('/') + 1);
279     LogDebug("fileName from URL: " << fileName);
280     std::string rename = fileName;
281     unsigned int renameSuffixNb = 0;
282     while (0 == access((downloadPath + rename).c_str(), F_OK)) {
283         std::ostringstream suffixOstr;
284         suffixOstr.str("");
285         suffixOstr << fileName << "_" << renameSuffixNb++;
286
287         rename = fileName;
288         rename.insert(rename.find('.'), suffixOstr.str());
289     }
290
291     downloadPath += rename;
292     LogDebug("Valid file path : " << downloadPath);
293
294     // Download image by curl
295     FILE *fp = NULL;
296     CURL *curl = NULL;
297
298     Try {
299         curl = curl_easy_init();
300         if (NULL == curl) {
301             ThrowMsg(Exception::InitError, "fail to curl_easy_init");
302         }
303
304         fp = fopen(downloadPath.c_str(), "wb");
305         if (fp == NULL) {
306             ThrowMsg(Exception::InitError, "fail to open");
307         }
308
309         curl_easy_setopt(curl, CURLOPT_URL, notiData->m_iconURL.c_str());
310         curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
311         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlWriteData);
312
313         CURLcode err = curl_easy_perform(curl);
314         if (0 != err) {
315             ThrowMsg(Exception::DownloadImageError,
316                      "fail to curl_easy_perform: " << err);
317         }
318         fclose(fp);
319         curl_easy_cleanup(curl);
320     } Catch(DPL::Exception) {
321         LogError(_rethrown_exception.GetMessage());
322         fclose(fp);
323         curl_easy_cleanup(curl);
324         return false;
325     }
326
327     LogDebug("Download success.. downloadedImgPath: " << downloadPath);
328     notiData->m_iconURL = downloadPath;
329     return true;
330 }
331
332 size_t curlWriteData(void *ptr, size_t size, size_t nmemb, FILE *stream)
333 {
334     size_t written = fwrite(ptr, size, nmemb, stream);
335     return written;
336 }
337
338 void askUserForWebNotificationPermission(
339     Evas_Object* window,
340     WebNotificationPermissionData* data)
341 {
342     LogDebug("askUserForWebNotificationPermission called");
343     std::string body =
344         WEB_NOTIFICATION_ASK_BODY_PREFIX +
345         DPL::ToUTF8String(data->m_originData.origin.host) +
346         WEB_NOTIFICATION_ASK_BODY_POSTFIX;
347     Evas_Object* popup = createPopup(window,
348                                      body.c_str(),
349                                      WEB_NOTIFICATION_ASK_CHECK,
350                                      popupCallback,
351                                      data);
352     if (popup == NULL) {
353         LogError("Fail to create popup object");
354         delete data;
355         return;
356     } else {
357         evas_object_show(popup);
358     }
359 }
360
361 void popupCallback(void* data, Evas_Object* obj, void* /*eventInfo*/)
362 {
363     LogDebug("popupCallback");
364     Assert(data);
365     WebNotificationPermissionData* permData =
366         static_cast<WebNotificationPermissionData*>(data);
367     Ewk_Notification_Permission_Request* request =
368         static_cast<Ewk_Notification_Permission_Request*>(permData->m_data);
369
370     Evas_Object* popup = getPopup(obj);
371     Result result = getResult(obj);
372
373     if (result != RESULT_UNKNOWN) {
374         permData->m_originDao->setSecurityOriginData(permData->m_originData,
375                                                      result);
376     }
377     Eina_Bool ret =
378         (result == RESULT_ALLOW_ALWAYS || result == RESULT_ALLOW_ONCE) ?
379         EINA_TRUE : EINA_FALSE;
380     ewk_notification_permission_request_response(permData->m_ewkContext,
381                                                  request,
382                                                  ret);
383     delete permData;
384     evas_object_hide(popup);
385     evas_object_del(popup);
386 }
387 } // namespace WebNotification
388 } //namespace ViewModule