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