Merge "fix: use EINA_* booleans instread of TRUE/FALSE" into tizen
[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 "view_logic_web_notification_data.h"
24
25 #include <stdint.h>
26 #include <string>
27 #include <map>
28 #include <unistd.h>
29 #include <dpl/log/secure_log.h>
30 #include <dpl/assert.h>
31 #include <dpl/exception.h>
32 #include <dpl/wrt-dao-ro/widget_config.h>
33
34 #include <notification.h>
35 #include <pcrecpp.h>
36 #include <sstream>
37 #include <curl/curl.h>
38
39 namespace ViewModule {
40 namespace {
41 const char* PATTERN_CHECK_EXTERNAL = "^http(s)?://\\w+.*$";
42
43 // Function declare
44 bool isExternalUri(const std::string &uri);
45 size_t curlWriteData(void* ptr, size_t size, size_t nmemb, FILE* stream);
46
47 bool isExternalUri(const std::string &uri)
48 {
49     pcrecpp::RE_Options pcreOpt;
50     pcreOpt.set_caseless(true);
51     pcrecpp::RE re(PATTERN_CHECK_EXTERNAL, pcreOpt);
52
53     return re.FullMatch(uri);
54 }
55
56 size_t curlWriteData(void* ptr, size_t size, size_t nmemb, FILE* stream)
57 {
58     size_t written = fwrite(ptr, size, nmemb, stream);
59     return written;
60 }
61 } // anonymous namespace
62
63 // Implementation class
64 class WebNotificationSupportImplementation
65 {
66   private:
67     class Exception
68     {
69       public:
70         DECLARE_EXCEPTION_TYPE(DPL::Exception, Base)
71         DECLARE_EXCEPTION_TYPE(Base, InitError)
72         DECLARE_EXCEPTION_TYPE(Base, NotificationShowError)
73         DECLARE_EXCEPTION_TYPE(Base, DownloadImageError)
74     };
75
76     bool m_initialized;
77     std::string m_persistentPath;
78
79     typedef std::map<uint64_t, WebNotificationDataPtr> NotiMap;
80     typedef std::map<uint64_t, WebNotificationDataPtr>::iterator NotiMapIt;
81     NotiMap m_notiDataMap;
82
83     std::string createDownloadPath(const std::string& iconUrl)
84     {
85         // Make valid filename
86         // If there is already exist filename, rename to "filename_%d"
87
88         std::string downloadPath = m_persistentPath;
89         std::string fileName = iconUrl.substr(iconUrl.rfind('/') + 1);
90         _D("fileName from URL: %s", fileName.c_str());
91         std::string rename = fileName;
92         unsigned int renameSuffixNb = 0;
93         while (0 == access((m_persistentPath + rename).c_str(), F_OK)) {
94             std::ostringstream suffixOstr;
95             suffixOstr.str("");
96             suffixOstr << fileName << "_" << renameSuffixNb++;
97
98             rename = fileName;
99             rename.insert(rename.find('.'), suffixOstr.str());
100         }
101
102         downloadPath += rename;
103         _D("Valid file path : %s", downloadPath.c_str());
104         return downloadPath;
105     }
106
107     std::string downloadImage(const std::string& iconUrl)
108     {
109         if (iconUrl.empty()) {
110             _W("Icon url is empty");
111             return std::string();
112         }
113         std::string downloadPath = createDownloadPath(iconUrl);
114
115         // Download image by curl
116         FILE *fp = NULL;
117         CURL *curl = NULL;
118
119         Try {
120             curl = curl_easy_init();
121             if (NULL == curl) {
122                 _W("fail to curl_easy_init");
123                 Throw(Exception::InitError);
124             }
125             fp = fopen(downloadPath.c_str(), "wb");
126             if (fp == NULL) {
127                 _W("fail to open");
128                 Throw(Exception::InitError);
129             }
130
131             curl_easy_setopt(curl, CURLOPT_URL, iconUrl.c_str());
132             curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
133             curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlWriteData);
134
135             CURLcode err = curl_easy_perform(curl);
136             if (0 != err) {
137                 _W("fail to curl_easy_perform: %d", err);
138                 Throw(Exception::DownloadImageError);
139             }
140             fclose(fp);
141             curl_easy_cleanup(curl);
142         } Catch(DPL::Exception) {
143             fclose(fp);
144             curl_easy_cleanup(curl);
145             return std::string();
146         }
147         _D("Download success");
148         return downloadPath;
149     }
150
151     int showNotification(WebNotificationDataPtr notiData)
152     {
153         Assert(notiData);
154         notification_h noti_h = NULL;
155         notification_error_e error = NOTIFICATION_ERROR_NONE;
156
157         Try {
158             // create notification
159             noti_h = notification_new(
160                     NOTIFICATION_TYPE_NOTI,
161                     NOTIFICATION_GROUP_ID_DEFAULT,
162                     NOTIFICATION_PRIV_ID_NONE);
163             if (!noti_h) {
164                 _E("Fail to notification_new");
165                 Throw(Exception::NotificationShowError);
166             }
167
168             // set notification title
169             error = notification_set_text(
170                     noti_h,
171                     NOTIFICATION_TEXT_TYPE_TITLE,
172                     notiData->getTitle(),
173                     NULL,
174                     NOTIFICATION_VARIABLE_TYPE_NONE);
175             if (error != NOTIFICATION_ERROR_NONE) {
176                 _E("Fail to set title");
177                 Throw(Exception::NotificationShowError);
178             }
179
180             // set notification content
181             error = notification_set_text(
182                     noti_h,
183                     NOTIFICATION_TEXT_TYPE_CONTENT,
184                     notiData->getBody(),
185                     NULL,
186                     NOTIFICATION_VARIABLE_TYPE_NONE);
187             if (error != NOTIFICATION_ERROR_NONE) {
188                 _E("Fail to set content: %d", error);
189                 Throw(Exception::NotificationShowError);
190             }
191
192             //check uri is "http", https" or not
193             if (notiData->getIconUrl()) {
194                 std::string iconPath;
195                 if (isExternalUri(notiData->getIconUrl())) {
196                     //download image from url
197                     iconPath = downloadImage(notiData->getIconUrl());
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 (!iconPath.empty()) {
204                     error = notification_set_image(
205                             noti_h,
206                             NOTIFICATION_IMAGE_TYPE_ICON,
207                             iconPath.c_str());
208                     if (error != NOTIFICATION_ERROR_NONE) {
209                         _E("Fail to free notification: %d", error);
210                         Throw(Exception::NotificationShowError);
211                     }
212                 }
213             }
214
215             // insert notification
216             int privId = NOTIFICATION_PRIV_ID_NONE;
217             error = notification_insert(noti_h, &privId);
218             if (error != NOTIFICATION_ERROR_NONE) {
219                 _E("Fail to insert notification : %d", error);
220                 Throw(Exception::NotificationShowError);
221             }
222             _D("ewkId=[%u], notiId=[%d]", notiData->getEwkNotiId(), privId);
223
224             if (noti_h) {
225                 error = notification_free(noti_h);
226                 if (error != NOTIFICATION_ERROR_NONE) {
227                     _E("Fail to free notification : %d", error);
228                     Throw(Exception::NotificationShowError);
229                 }
230                 noti_h = NULL;
231             }
232             notiData->setNotiId(privId);
233         } Catch(Exception::NotificationShowError) {
234             notification_free(noti_h);
235             noti_h = NULL;
236             return false;
237         } Catch(DPL::Exception) {
238             _E("Fail to show notification");
239             return false;
240         }
241         return true;
242     }
243
244     bool hideNotification(uint64_t ewkNotiId)
245     {
246         notification_error_e error = NOTIFICATION_ERROR_NONE;
247         NotiMapIt it = m_notiDataMap.find(ewkNotiId);
248         error =
249             notification_delete_by_priv_id(NULL,
250                                            NOTIFICATION_TYPE_NOTI,
251                                            it->second->getNotiId());
252         if (error == NOTIFICATION_ERROR_NONE) {
253             _D("Success to hide");
254             return true;
255         } else if (error == NOTIFICATION_ERROR_NOT_EXIST_ID) {
256             _D("Success to hide. Not exist noti");
257             return true;
258         } else {
259             _W("Error to hide : %d", error);
260             return false;
261         }
262     }
263
264     // manage noti data
265     bool isExistData(uint64_t ewkNotiId)
266     {
267         if (m_notiDataMap.find(ewkNotiId) == m_notiDataMap.end()) {
268             return false;
269         }
270         return true;
271     }
272
273     void insertData(WebNotificationDataPtr notiData)
274     {
275         m_notiDataMap[notiData->getEwkNotiId()] = notiData;
276     }
277
278     void removeData(uint64_t ewkNotiId)
279     {
280         m_notiDataMap.erase(ewkNotiId);
281     }
282
283     WebNotificationDataPtr getData(uint64_t ewkNotiId)
284     {
285         return m_notiDataMap.find(ewkNotiId)->second;
286     }
287
288   public:
289     WebNotificationSupportImplementation() :
290         m_initialized(false)
291     {
292     }
293
294     void initialize(WrtDB::TizenPkgId pkgId)
295     {
296         // icon download path
297         // /opt/apps/tizen_id/data + '/' + filename
298         m_persistentPath =
299             WrtDB::WidgetConfig::GetWidgetPersistentStoragePath(pkgId) + '/';
300         _D("path %s", m_persistentPath.c_str());
301         m_initialized = true;
302     }
303
304     void deinitialize(void)
305     {
306         _D("called");
307         m_initialized = false;
308     }
309
310     bool show(WebNotificationDataPtr notiData)
311     {
312         Assert(m_initialized);
313         if (isExistData(notiData->getEwkNotiId())) {
314             // Web Notifications (http://www.w3.org/TR/notifications/)
315             // 4.7 Replacing a notification
316             //   3. If old is in the list of pending notifications, queue a
317             //      task to replace old with new, in the same position, in the
318             //      list of pending notifications, and fire an event named
319             //      close on old.
320             //   4. Otherwise, queue a task to replace old with new, in the
321             //      same position, in the list of active notifications, fire
322             //      an event named close on old, and fire an event named show
323             //      on new.
324             hideNotification(notiData->getEwkNotiId());
325             removeData(notiData->getEwkNotiId());
326         }
327         if (showNotification(notiData)) {
328             insertData(notiData);
329             return true;
330         }
331         return false;
332     }
333
334     void* hide(uint64_t ewkNotiId)
335     {
336         Assert(m_initialized);
337         if (!isExistData(ewkNotiId)) {
338             _W("Noti isn't exist");
339             return NULL;
340         }
341         if (!hideNotification(ewkNotiId)) {
342             return NULL;
343         }
344         WebNotificationDataPtr data = getData(ewkNotiId);
345         removeData(ewkNotiId);
346         return static_cast<void*>(data->getData());
347     }
348 };
349
350 WebNotificationSupport::WebNotificationSupport() :
351     m_impl(new WebNotificationSupportImplementation())
352 {
353 }
354
355 WebNotificationSupport::~WebNotificationSupport()
356 {
357 }
358
359 void WebNotificationSupport::initialize(WrtDB::TizenAppId appId)
360 {
361     m_impl->initialize(appId);
362 }
363
364 void WebNotificationSupport::deinitialize(void)
365 {
366     m_impl->deinitialize();
367 }
368
369 bool WebNotificationSupport::show(WebNotificationDataPtr notiData)
370 {
371     return m_impl->show(notiData);
372 }
373
374 void* WebNotificationSupport::hide(uint64_t ewkNotiId)
375 {
376     return m_impl->hide(ewkNotiId);
377 }
378 } //namespace ViewModule