Add unknown scheme handler
[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 AppSvcOperation {
109     const char* operation;
110     bool needUri;
111     std::list<AppSvcConversion> conversions;
112 };
113
114 typedef std::map<Scheme::Type, AppSvcOperation> AppSvcOperationMap;
115
116 // Regular expressions used to extract appsvc data from scheme
117 // TODO what about multiple recipients?
118 char const * const REGEX_BODY = ".*[?&]body=([^&]+).*";
119 char const * const REGEX_SMS = "sms:([^&]+).*";
120 char const * const REGEX_MMSTO = "mmsto:([^&]+).*";
121 char const * const REGEX_MAILTO = "mailto:([^&]+).*";
122 char const * const REGEX_TO = ".*[?&]to=([^&]+).*";
123 char const * const REGEX_CC = ".*[?&]cc=([^&]+).*";
124 char const * const REGEX_BCC = ".*[?&]bcc=([^&]+).*";
125 char const * const REGEX_SUBJECT = ".*[?&]subject=([^&]+).*";
126 char const * const REGEX_DATA_CONTEXT = ".*;phone-context=([^:]+).*";
127
128 AppSvcOperationMap initializeAppSvcOperations()
129 {
130     AppSvcOperationMap ret;
131
132     // FILE, HTTP & HTTPS
133     AppSvcOperation viewOp;
134     viewOp.operation = APPSVC_OPERATION_VIEW;
135     viewOp.needUri = true;
136     // no additional data
137     ret.insert(std::make_pair(Scheme::FILE, viewOp));
138     ret.insert(std::make_pair(Scheme::HTTP, viewOp));
139     ret.insert(std::make_pair(Scheme::HTTPS, viewOp));
140
141     // SMS
142     AppSvcOperation smsOp;
143     smsOp.operation = APPSVC_OPERATION_SEND_TEXT;
144     smsOp.needUri = false;
145     smsOp.conversions.push_back(AppSvcConversion(APPSVC_DATA_TO, REGEX_SMS));
146     smsOp.conversions.push_back(AppSvcConversion(APPSVC_DATA_TEXT, REGEX_BODY));
147     ret.insert(std::make_pair(Scheme::SMS, smsOp));
148
149     // MMSTO & MAILTO
150     AppSvcOperation sendOp;
151     sendOp.operation = APPSVC_OPERATION_SEND;
152     sendOp.needUri = true;
153     sendOp.conversions.push_back(AppSvcConversion(APPSVC_DATA_TO, REGEX_MMSTO));
154     sendOp.conversions.push_back(AppSvcConversion(APPSVC_DATA_TO, REGEX_MAILTO));
155     sendOp.conversions.push_back(AppSvcConversion(APPSVC_DATA_CC, REGEX_CC));
156     sendOp.conversions.push_back(
157         AppSvcConversion(APPSVC_DATA_BCC, REGEX_BCC));
158     sendOp.conversions.push_back(
159         AppSvcConversion(APPSVC_DATA_SUBJECT, REGEX_SUBJECT));
160     sendOp.conversions.push_back(
161         AppSvcConversion(APPSVC_DATA_TEXT, REGEX_BODY));
162     ret.insert(std::make_pair(Scheme::MMSTO, sendOp));
163     ret.insert(std::make_pair(Scheme::MAILTO, sendOp));
164
165     // TODO what about DATA?
166
167     // TEL
168     AppSvcOperation telOp;
169     telOp.operation = APPSVC_OPERATION_CALL;
170     telOp.needUri = true;
171     ret.insert(std::make_pair(Scheme::TEL, telOp));
172
173     return ret;
174 }
175
176 AppSvcOperationMap g_appSvcOperationMap = initializeAppSvcOperations();
177 } // namespace
178
179 namespace SchemeActionMap {
180 bool HandleUri(const char* uri,
181                NavigationContext context,
182                WrtDB::AppType appType)
183 {
184     if (!uri) {
185         LogError("wrong arguments passed");
186         return false;
187     }
188     LogDebug("Uri being checked: " << uri);
189
190     const char *end = strstr(uri, ":");
191     if (!end) {
192         LogError("Lack of scheme - ignoring");
193         return false;
194     }
195     std::string name(uri, end);
196     Scheme scheme(name);
197     LogDebug("Scheme: " << name);
198
199     Scheme::Type type = scheme.GetType();
200     if (type < Scheme::FILE || type >= Scheme::COUNT) {
201         LogError("Invalid scheme: " << name);
202         // case of unknown scheme, send to app-control
203         // This is temporary soultion. "invalid" scheme should be handled by
204         // scheme map data
205         bundle* bundleData = bundle_create();
206         appsvc_set_operation(bundleData, APPSVC_OPERATION_VIEW);
207         appsvc_set_uri(bundleData, uri);
208         CONTROLLER_POST_EVENT(
209             ApplicationLauncher,
210             ApplicationLauncherEvents::LaunchApplicationByAppService(
211                 bundleData,
212                 NULL,
213                 NULL));
214         return false;
215     }
216
217     LogDebug("Scheme type: " << type);
218     LogDebug("Navigation context: " << context);
219     LogDebug("Application type: " << appType);
220
221     UriAction action;
222     switch (appType) {
223     case WrtDB::APP_TYPE_WAC20:
224         action = g_wacActionMap[type][context];
225         break;
226     case WrtDB::APP_TYPE_TIZENWEBAPP:
227         action = g_tizenActionMap[type][context];
228         break;
229     default:
230         LogError("Unsupported application type: " << type);
231         return false;
232     }
233
234     LogDebug("Uri action: " << action);
235
236     // execute action if necessary
237     switch (action) {
238     case URI_ACTION_APPSVC:
239     {
240         // find AppSvcOperation for given scheme type
241         auto it = g_appSvcOperationMap.find(type);
242         if (it == g_appSvcOperationMap.end()) {
243             LogError("No entry for scheme: " << name);
244             return false;
245         }
246
247         // prepare appsvc bundle
248         bundle* bundleData = bundle_create();
249         LogDebug("appsvc operation " << it->second.operation);
250         appsvc_set_operation(bundleData, it->second.operation);
251         if (it->second.needUri) {
252             appsvc_set_uri(bundleData, uri);
253         }
254
255         // this is safe as there are no other threads
256         CURL* curl = curl_easy_init();
257         // unescape the url
258         int outLength = 0;
259         char* unescaped = curl_easy_unescape(curl, uri, 0, &outLength);
260         std::string uUri(unescaped, outLength);
261         curl_free(unescaped);
262         curl_easy_cleanup(curl);
263         LogDebug("unescaped " << uUri);
264
265         // setup additional appsvc data
266         FOREACH(cit, it->second.conversions) {
267             LogDebug("extracting data for key " << cit->key);
268
269             std::string match;
270             pcrecpp::StringPiece input(uUri);
271
272             // convert scheme text to appsvc format
273             while (cit->regex->Consume(&input, &match)) {
274                 LogInfo("Adding apssvc data: " << cit->key << " " << match);
275                 appsvc_add_data(bundleData, cit->key, match.c_str());
276             }
277         }
278
279         // TODO do we need a callback?
280         CONTROLLER_POST_EVENT(
281             ApplicationLauncher,
282             ApplicationLauncherEvents::LaunchApplicationByAppService(
283                 bundleData,
284                 NULL,
285                 NULL));
286         break;
287     }
288
289     case URI_ACTION_VIDEO:
290         CONTROLLER_POST_EVENT(
291             ApplicationLauncher,
292             ApplicationLauncherEvents::LaunchApplicationByPkgname(
293                 ApplicationLauncherPkgname::PKG_NAME_VIDEO_PLAYER,
294                 name,
295                 uri,
296                 "null"));
297         break;
298     default:
299         break;
300     }
301     return (action == URI_ACTION_WRT);
302 }
303 } // namespace SchemeActionMap
304 } /* namespace ViewModule */