Temporary fix for asking geolocation permission popup crash.
[platform/framework/web/wrt.git] / src / view / common / scheme_action_map.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       scheme_action_map.cpp
18  * @author     Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
19  * @version    1.0
20  */
21
22 #include "scheme_action_map.h"
23 #include <scheme.h>
24 #include <dpl/log/log.h>
25 #include <dpl/event/controller.h>
26 #include <application_launcher.h>
27 #include <appsvc.h>
28 #include <pcrecpp.h>
29 #include <memory>
30 #include <curl/curl.h>
31 #include "scheme_action_map_data.h"
32
33 namespace ViewModule {
34 namespace {
35 /*
36  * Lazy construction pattern.
37  * TODO Make it more general. Use variadic template/tuples/lambdas or sth. Move
38  * to DPL.
39  */
40 template <typename T, typename Arg1>
41 class Lazy
42 {
43   public:
44     explicit Lazy(const Arg1& arg1) :
45         m_arg1(arg1),
46         m_object(new std::unique_ptr<T>())
47     {}
48     Lazy(const Lazy<T, Arg1>& other) :
49         m_arg1(other.m_arg1),
50         m_object(other.m_object)
51     {}
52
53     T& operator*()
54     {
55         return GetObject();
56     }
57     const T& operator*() const
58     {
59         return GetObject();
60     }
61     const T* operator->() const
62     {
63         return &GetObject();
64     }
65     T* operator->()
66     {
67         return &GetObject();
68     }
69
70     Lazy<T, Arg1>& operator=(const Lazy<T, Arg1>& other)
71     {
72         m_arg1 = other.m_arg1;
73         m_object = other.m_object;
74         return *this;
75     }
76
77   private:
78     T& GetObject() const
79     {
80         if (!(*m_object)) {
81             (*m_object).reset(new T(m_arg1));
82         }
83         return **m_object;
84     }
85
86     Arg1 m_arg1;
87     // single unique_ptr shared among many Lazy copies
88     mutable std::shared_ptr<std::unique_ptr<T> > m_object;
89 };
90
91 /*
92  * Struct defining conversion of scheme for given appsvc key for example:
93  * sms:5551212?body=expected%20text => APPSVC_DATA_TEXT + expected%20text
94  */
95 struct AppSvcConversion {
96     AppSvcConversion(char const * const keyValue,
97                      const std::string& regexStr) :
98         key(keyValue),
99         regex(regexStr) {}
100     char const * const key;
101     Lazy<pcrecpp::RE, std::string> regex;
102 };
103
104 /*
105  * Struct defining an appsvc operation and a list of scheme conversions used to
106  * fill in additional appsvc data.
107  */
108 struct ServiceOperation {
109     const char* operation;
110     const char* fakeUri;
111     const char* mime;
112     std::list<AppSvcConversion> conversions;
113 };
114
115 typedef std::map<Scheme::Type, ServiceOperation> ServiceOperationMap;
116
117 // Regular expressions used to extract appsvc data from scheme
118 // TODO what about multiple recipients?
119 char const * const REGEX_BODY =         ".*[?&]body=([^&]+).*";
120 char const * const REGEX_SMS =           "sms:([^&]+).*";
121 char const * const REGEX_SMSTO =        "smsto:([^&]+).*";
122 char const * const REGEX_MMSTO =        "mmsto:([^&]+).*";
123 char const * const REGEX_MAILTO =       "mailto:([^&]+).*";
124 char const * const REGEX_TO =           ".*[?&]to=([^&]+).*";
125 char const * const REGEX_CC =           ".*[?&]cc=([^&]+).*";
126 char const * const REGEX_BCC =          ".*[?&]bcc=([^&]+).*";
127 char const * const REGEX_SUBJECT =      ".*[?&]subject=([^&]+).*";
128 char const * const REGEX_DATA_CONTEXT = ".*;phone-context=([^:]+).*";
129
130 ServiceOperationMap initializeAppSvcOperations()
131 {
132     ServiceOperationMap ret;
133
134     // FILE, HTTP & HTTPS
135     ServiceOperation viewOp;
136     viewOp.operation = SERVICE_OPERATION_VIEW;
137     viewOp.fakeUri = NULL;
138     viewOp.mime = NULL;
139     // no additional data
140     ret.insert(std::make_pair(Scheme::FILE, viewOp));
141     ret.insert(std::make_pair(Scheme::HTTP, viewOp));
142     ret.insert(std::make_pair(Scheme::HTTPS, viewOp));
143
144     // SMS
145     ServiceOperation smsOp;
146     smsOp.operation = SERVICE_OPERATION_COMPOSE;
147     smsOp.fakeUri = NULL;
148     smsOp.mime = "*/*";
149     smsOp.conversions.push_back(AppSvcConversion(SERVICE_DATA_TO, REGEX_SMS));
150     smsOp.conversions.push_back(AppSvcConversion(SERVICE_DATA_TEXT, REGEX_BODY));
151     ret.insert(std::make_pair(Scheme::SMS, smsOp));
152
153     // SMSTO
154     ServiceOperation smstoOp;
155     smstoOp.operation = SERVICE_OPERATION_COMPOSE;
156     smstoOp.fakeUri = "sms";
157     smstoOp.mime = "*/*";
158     smstoOp.conversions.push_back(AppSvcConversion(SERVICE_DATA_TO, REGEX_SMSTO));
159     smstoOp.conversions.push_back(AppSvcConversion(SERVICE_DATA_TEXT, REGEX_BODY));
160     ret.insert(std::make_pair(Scheme::SMSTO, smstoOp));
161
162
163     // MMSTO & MAILTO
164     ServiceOperation sendOp;
165     sendOp.operation = SERVICE_OPERATION_COMPOSE;
166     sendOp.fakeUri = NULL;
167     sendOp.mime = NULL;
168     sendOp.conversions.push_back(AppSvcConversion(SERVICE_DATA_TO, REGEX_MMSTO));
169     sendOp.conversions.push_back(AppSvcConversion(SERVICE_DATA_TO, REGEX_MAILTO));
170     sendOp.conversions.push_back(AppSvcConversion(SERVICE_DATA_CC, REGEX_CC));
171     sendOp.conversions.push_back(
172         AppSvcConversion(SERVICE_DATA_BCC, REGEX_BCC));
173     sendOp.conversions.push_back(
174         AppSvcConversion(SERVICE_DATA_SUBJECT, REGEX_SUBJECT));
175     sendOp.conversions.push_back(
176         AppSvcConversion(SERVICE_DATA_TEXT, REGEX_BODY));
177     ret.insert(std::make_pair(Scheme::MAILTO, sendOp));
178     sendOp.mime = "*/*";
179     ret.insert(std::make_pair(Scheme::MMSTO, sendOp));
180
181     // TODO what about DATA?
182
183     // TEL
184     ServiceOperation telOp;
185     telOp.operation = SERVICE_OPERATION_CALL;
186     telOp.fakeUri = NULL;
187     telOp.mime = NULL;
188     ret.insert(std::make_pair(Scheme::TEL, telOp));
189
190     return ret;
191 }
192
193 ServiceOperationMap g_serviceOperationMap = initializeAppSvcOperations();
194
195 void handleTizenServiceScheme(const char* uri)
196 {
197     // <a href="tizen-service:AppID=com.samsung.myfile; key=key1, value=value1; end">Tizen Service</a>
198     std::string parameter = std::string(uri);
199     std::string appId;
200     size_t start, end = 0;
201
202     if (parameter.find("AppID=") != std::string::npos) {
203         start = parameter.find("AppID=") + strlen("AppID=");
204         end = parameter.find(";", start);
205         appId = parameter.substr(start, end-start);
206     } else {
207         LogError("parameter doesn't contain appID");
208         return;
209     }
210
211     service_h handle = NULL;
212     if (service_create(&handle) != SERVICE_ERROR_NONE) {
213         LogError("Fail to create service handle");
214         return;
215     }
216
217     if (service_set_app_id(handle, appId.c_str()) < 0) {
218         LogError("Fail to service_set_app_id");
219         service_destroy(handle);
220         return;
221     }
222
223     const char* KEY_KEY = "key=";
224     const char* KEY_VALUE = "value=";
225
226     char* buf = strdup(parameter.c_str());
227     const char* ptr = strtok(buf,";");
228     while (ptr != NULL) {
229         std::string string = ptr;
230         ptr = strtok (NULL, ";");
231
232         size_t devide = string.find(',');
233         if (devide == std::string::npos) {
234             continue;
235         }
236         size_t keyPos = string.find(KEY_KEY);
237         if (keyPos == std::string::npos) {
238             continue;
239         }
240         size_t valuePos = string.find(KEY_VALUE);
241         if (valuePos == std::string::npos) {
242             continue;
243         }
244
245         std::string key =
246             string.substr(keyPos + std::string(KEY_KEY).size(),
247                           devide - (keyPos + std::string(KEY_KEY).size()));
248         std::string value =
249             string.substr(valuePos + std::string(KEY_VALUE).size());
250
251         if (service_add_extra_data(handle, key.c_str(), value.c_str())) {
252             LogError("service_add_extra_data is failed.");
253             service_destroy(handle);
254             free(buf);
255             return;
256         }
257     }
258     free(buf);
259
260     CONTROLLER_POST_EVENT(
261         ApplicationLauncher,
262         ApplicationLauncherEvents::LaunchApplicationByAppService(
263             handle,
264             NULL,
265             NULL));
266 }
267
268 void handleUnknownScheme(const char* scheme, const char* uri)
269 {
270     LogError("Invalid scheme: " << scheme);
271     // case of unknown scheme, send to app-control
272     // This is temporary soultion. "invalid" scheme should be handled by
273     // scheme map data
274
275     if (!strcmp(scheme, "tizen-service")) {
276         handleTizenServiceScheme(uri);
277         return;
278     } else {
279         // create service
280         service_h serviceHandle = NULL;
281         if (SERVICE_ERROR_NONE != service_create(&serviceHandle)) {
282             service_set_operation(serviceHandle, SERVICE_OPERATION_VIEW);
283             service_set_uri(serviceHandle, uri);
284             CONTROLLER_POST_EVENT(
285                 ApplicationLauncher,
286                 ApplicationLauncherEvents::LaunchApplicationByAppService(
287                     serviceHandle,
288                     NULL,
289                     NULL));
290         }
291     }
292 }
293
294
295 } // namespace
296
297 namespace SchemeActionMap {
298 bool HandleUri(const char* uri, NavigationContext context)
299 {
300     if (!uri) {
301         LogError("wrong arguments passed");
302         return false;
303     }
304     LogDebug("Uri being checked: " << uri);
305
306     const char *end = strstr(uri, ":");
307     if (!end) {
308         LogError("Lack of scheme - ignoring");
309         return false;
310     }
311     std::string name(uri, end);
312     Scheme scheme(name);
313     LogDebug("Scheme: " << name);
314
315     Scheme::Type type = scheme.GetType();
316     if (type < Scheme::FILE || type >= Scheme::COUNT) {
317         LogError("Invalid scheme: " << name);
318         handleUnknownScheme(name.c_str(), uri);
319         return false;
320     }
321
322     LogDebug("Scheme type: " << type);
323     LogDebug("Navigation context: " << context);
324
325     UriAction action = g_tizenActionMap[type][context];
326
327     LogDebug("Uri action: " << action);
328
329     // execute action if necessary
330     switch (action) {
331     case URI_ACTION_APPSVC:
332     {
333         // find AppSvcOperation for given scheme type
334         auto it = g_serviceOperationMap.find(type);
335         if (it == g_serviceOperationMap.end()) {
336             LogError("No entry for scheme: " << name);
337             return false;
338         }
339
340         // prepare appsvc bundle
341         service_h serviceHandle = NULL;
342         service_create(&serviceHandle);
343         LogDebug("appsvc operation " << it->second.operation);
344         service_set_operation(serviceHandle, it->second.operation);
345         if (it->second.fakeUri) {
346             size_t size = strlen(it->second.fakeUri) + strlen(uri) + 1;
347             char *newUri = new char[size];
348             strcpy(newUri, it->second.fakeUri);
349             const char* uriArgs = strstr(uri, ":");
350             strcpy(newUri + strlen(it->second.fakeUri), uriArgs);
351             service_set_uri(serviceHandle, newUri);
352             delete [] newUri;
353         }
354         else {
355             service_set_uri(serviceHandle, uri);
356         }
357         if (it->second.mime) {
358             service_set_mime(serviceHandle, it->second.mime);
359         }
360
361         // this is safe as there are no other threads
362         CURL* curl = curl_easy_init();
363         // unescape the url
364         int outLength = 0;
365         char* unescaped = curl_easy_unescape(curl, uri, 0, &outLength);
366         std::string uUri(unescaped, outLength);
367         curl_free(unescaped);
368         curl_easy_cleanup(curl);
369         LogDebug("unescaped " << uUri);
370
371         // setup additional appsvc data
372         FOREACH(cit, it->second.conversions) {
373             LogDebug("extracting data for key " << cit->key);
374
375             std::string match;
376             pcrecpp::StringPiece input(uUri);
377
378             // convert scheme text to appsvc format
379             while (cit->regex->Consume(&input, &match)) {
380                 LogDebug("Adding apssvc data: " << cit->key << " " << match);
381                 service_add_extra_data(serviceHandle, cit->key, match.c_str());
382             }
383         }
384
385         // TODO do we need a callback?
386         CONTROLLER_POST_EVENT(
387             ApplicationLauncher,
388             ApplicationLauncherEvents::LaunchApplicationByAppService(
389                 serviceHandle,
390                 NULL,
391                 NULL));
392         break;
393     }
394
395     case URI_ACTION_VIDEO:
396         CONTROLLER_POST_EVENT(
397             ApplicationLauncher,
398             ApplicationLauncherEvents::LaunchApplicationByPkgname(
399                 ApplicationLauncherPkgname::PKG_NAME_VIDEO_PLAYER,
400                 name,
401                 uri,
402                 "null"));
403         break;
404     default:
405         break;
406     }
407     return (action == URI_ACTION_WRT);
408 }
409 } // namespace SchemeActionMap
410 } /* namespace ViewModule */