dffb4c8f0bf0c05f0e39fcb26d88b1280295e6fb
[platform/core/appfw/wgt-backend.git] / src / wgt / step / pkgmgr / step_generate_xml.cc
1 /* 2014, Copyright © Intel Coporation, license APACHE-2.0, see LICENSE file */
2 // Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3 // Use of this source code is governed by a apache 2.0 license that can be
4 // found in the LICENSE file.
5
6 #include "wgt/step/pkgmgr/step_generate_xml.h"
7
8 #include <boost/filesystem/path.hpp>
9 #include <boost/system/error_code.hpp>
10
11 #include <common/utils/file_util.h>
12 #include <common/utils/glist_range.h>
13 #include <common/privileges.h>
14
15 #include <libxml/parser.h>
16 #include <libxml/xmlreader.h>
17 #include <pkgmgr-info.h>
18 #include <pkgmgr_parser.h>
19 #include <tzplatform_config.h>
20 #include <unistd.h>
21
22 #include <cassert>
23 #include <cstring>
24 #include <string>
25
26 #include "wgt/wgt_backend_data.h"
27
28 namespace bs = boost::system;
29 namespace bf = boost::filesystem;
30
31 namespace {
32
33 const char kResWgt[] = "res/wgt";
34 const char kSharedRes[] = "shared/res";
35
36 void WriteUIApplicationAttributes(
37     xmlTextWriterPtr writer, application_x *app) {
38   if (app->taskmanage)
39     xmlTextWriterWriteAttribute(writer, BAD_CAST "taskmanage",
40         BAD_CAST app->taskmanage);
41   if (app->nodisplay)
42     xmlTextWriterWriteAttribute(writer, BAD_CAST "nodisplay",
43         BAD_CAST app->nodisplay);
44   if (app->multiple)
45     xmlTextWriterWriteAttribute(writer, BAD_CAST "multiple",
46         BAD_CAST app->multiple);
47   if (app->launch_mode && strlen(app->launch_mode))
48     xmlTextWriterWriteAttribute(writer, BAD_CAST "launch_mode",
49         BAD_CAST app->launch_mode);
50   if (app->ui_gadget && strlen(app->ui_gadget))
51     xmlTextWriterWriteAttribute(writer, BAD_CAST "ui-gadget",
52         BAD_CAST app->ui_gadget);
53   if (app->submode && strlen(app->submode))
54     xmlTextWriterWriteAttribute(writer, BAD_CAST "submode",
55         BAD_CAST app->submode);
56   if (app->submode_mainid && strlen(app->submode_mainid))
57     xmlTextWriterWriteAttribute(writer, BAD_CAST "submode-mainid",
58         BAD_CAST app->submode_mainid);
59   if (app->indicatordisplay && strlen(app->indicatordisplay))
60     xmlTextWriterWriteAttribute(writer, BAD_CAST "indicatordisplay",
61         BAD_CAST app->indicatordisplay);
62   if (app->portraitimg && strlen(app->portraitimg))
63     xmlTextWriterWriteAttribute(writer, BAD_CAST "portrait-effectimage",
64         BAD_CAST app->portraitimg);
65   if (app->landscapeimg && strlen(app->landscapeimg))
66     xmlTextWriterWriteAttribute(writer, BAD_CAST "landscape-effectimage",
67         BAD_CAST app->landscapeimg);
68   if (app->effectimage_type && strlen(app->effectimage_type))
69     xmlTextWriterWriteAttribute(writer, BAD_CAST "effectimage-type",
70         BAD_CAST app->effectimage_type);
71   if (app->hwacceleration && strlen(app->hwacceleration))
72     xmlTextWriterWriteAttribute(writer, BAD_CAST "hwacceleration",
73         BAD_CAST app->hwacceleration);
74 }
75
76 void WriteServiceApplicationAttributes(
77     xmlTextWriterPtr writer, application_x *app) {
78   xmlTextWriterWriteAttribute(writer, BAD_CAST "auto-restart",
79       BAD_CAST(app->autorestart ? app->autorestart : "false"));
80   xmlTextWriterWriteAttribute(writer, BAD_CAST "on-boot",
81       BAD_CAST(app->onboot ? app->onboot : "false"));
82   if (app->taskmanage)
83       xmlTextWriterWriteAttribute(writer, BAD_CAST "taskmanage",
84          BAD_CAST app->taskmanage);
85 }
86
87 bool WriteWidgetApplicationAttributesAndElements(
88     xmlTextWriterPtr writer, application_x *app,
89     const wgt::parse::AppWidgetInfo& widget_info,
90     const bf::path& shared_path) {
91   if (app->nodisplay)
92     xmlTextWriterWriteAttribute(writer, BAD_CAST "nodisplay",
93         BAD_CAST app->nodisplay);
94   if (app->multiple)
95     xmlTextWriterWriteAttribute(writer, BAD_CAST "multiple",
96         BAD_CAST app->multiple);
97
98   // Generate attributes and elements not covered in manifest.xsd
99   auto& appwidgets = widget_info.app_widgets();
100   const auto& appwidget = std::find_if(appwidgets.begin(), appwidgets.end(),
101                                  [app](const wgt::parse::AppWidget& widget) {
102                                     return widget.id == app->appid;
103                                  });
104   if (appwidget == appwidgets.end()) {
105     LOG(ERROR) << "Failed to generate appwidget extra elements";
106     return false;
107   }
108
109   xmlTextWriterWriteAttribute(writer, BAD_CAST "main",
110       BAD_CAST (appwidget->primary ? "true" : "false"));  // NOLINT
111   if (!appwidget->update_period.empty()) {
112         xmlTextWriterWriteAttribute(writer, BAD_CAST "update-period", BAD_CAST
113             std::to_string(static_cast<int>(
114                     appwidget->update_period.front())).c_str());
115   }
116
117   for (auto& size : appwidget->content_size) {
118     xmlTextWriterStartElement(writer, BAD_CAST "support-size");
119
120     std::string type = wgt::parse::AppWidgetSizeTypeToString(size.type);
121     if (!size.preview.empty()) {
122       std::string icon_name = shared_path.string() + "/"
123           + appwidget->id + "." + type + "." + "preview" +
124           bf::path(size.preview).extension().string();
125       xmlTextWriterWriteAttribute(writer, BAD_CAST "preview",
126           BAD_CAST icon_name.c_str());  // NOLINT
127     }
128
129     xmlTextWriterWriteAttribute(writer, BAD_CAST "frame",
130                                 BAD_CAST "true");
131     xmlTextWriterWriteString(writer,
132         BAD_CAST type.c_str());
133     xmlTextWriterEndElement(writer);
134   }
135   return true;
136 }
137
138 void WriteWatchApplicationAttributes(
139     xmlTextWriterPtr writer, application_x* app) {
140   if (app->ambient_support)
141     xmlTextWriterWriteAttribute(writer, BAD_CAST "ambient-support",
142         BAD_CAST app->ambient_support);
143 }
144
145 }  // namespace
146
147 namespace wgt {
148 namespace pkgmgr {
149
150 common_installer::Step::Status StepGenerateXml::GenerateApplicationCommonXml(
151     application_x* app, xmlTextWriterPtr writer, AppCompType type) {
152   xmlTextWriterWriteAttribute(writer, BAD_CAST "appid", BAD_CAST app->appid);
153
154   // binary is a symbolic link named <appid> and is located in <pkgid>/<appid>
155   bf::path exec_path = context_->pkg_path.get()
156       / bf::path("bin") / bf::path(app->appid);
157   xmlTextWriterWriteAttribute(writer, BAD_CAST "exec",
158                               BAD_CAST exec_path.string().c_str());
159   if (app->type)
160     xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST app->type);
161   else
162     xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST "capp");
163
164   if (app->process_pool && strlen(app->process_pool))
165     xmlTextWriterWriteAttribute(writer, BAD_CAST "process-pool",
166                                 BAD_CAST app->process_pool);
167   // app-specific attributes
168   switch (type) {
169   case AppCompType::UIAPP:
170     WriteUIApplicationAttributes(writer, app);
171     break;
172   case AppCompType::SVCAPP:
173     WriteServiceApplicationAttributes(writer, app);
174     break;
175   case AppCompType::WIDGETAPP:
176     if (!WriteWidgetApplicationAttributesAndElements(writer, app,
177         static_cast<WgtBackendData*>(
178             context_->backend_data.get())->appwidgets.get(),
179         context_->pkg_path.get() / "shared" / "res"))
180       return Status::MANIFEST_ERROR;
181     break;
182   case AppCompType::WATCHAPP:
183     WriteWatchApplicationAttributes(writer, app);
184     break;
185   }
186
187   for (label_x* label : GListRange<label_x*>(app->label)) {
188     xmlTextWriterStartElement(writer, BAD_CAST "label");
189     if (label->lang && strcmp(DEFAULT_LOCALE, label->lang) != 0) {
190       xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
191                                   BAD_CAST label->lang);
192     }
193     xmlTextWriterWriteString(writer, BAD_CAST label->name);
194     xmlTextWriterEndElement(writer);
195   }
196
197   if (app->icon) {
198     icon_x* iconx = reinterpret_cast<icon_x*>(app->icon->data);
199     xmlTextWriterWriteFormatElement(
200         writer, BAD_CAST "icon", "%s", BAD_CAST iconx->text);
201   } else {
202     // Default icon setting is role of the platform
203     LOG(DEBUG) << "Icon was not found in application";
204   }
205
206   for (image_x* image : GListRange<image_x*>(app->image)) {
207     xmlTextWriterStartElement(writer, BAD_CAST "image");
208     if (image->lang && strcmp(DEFAULT_LOCALE, image->lang) != 0) {
209       xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
210
211                                   BAD_CAST image->lang);
212     }
213     if (image->section)
214       xmlTextWriterWriteAttribute(writer, BAD_CAST "section",
215                                   BAD_CAST image->section);
216     xmlTextWriterEndElement(writer);
217   }
218
219   for (appcontrol_x* appc : GListRange<appcontrol_x*>(app->appcontrol)) {
220     xmlTextWriterStartElement(writer, BAD_CAST "app-control");
221
222     if (appc->operation) {
223       xmlTextWriterStartElement(writer, BAD_CAST "operation");
224       xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
225           BAD_CAST appc->operation);
226       xmlTextWriterEndElement(writer);
227     }
228
229     if (appc->uri) {
230       xmlTextWriterStartElement(writer, BAD_CAST "uri");
231       xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
232           BAD_CAST appc->uri);
233       xmlTextWriterEndElement(writer);
234     }
235
236     if (appc->mime) {
237       xmlTextWriterStartElement(writer, BAD_CAST "mime");
238       xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
239           BAD_CAST appc->mime);
240       xmlTextWriterEndElement(writer);
241     }
242
243     xmlTextWriterEndElement(writer);
244   }
245
246   for (datacontrol_x* datacontrol :
247        GListRange<datacontrol_x*>(app->datacontrol)) {
248     xmlTextWriterStartElement(writer, BAD_CAST "datacontrol");
249     if (datacontrol->access) {
250       xmlTextWriterWriteAttribute(writer, BAD_CAST "access",
251           BAD_CAST datacontrol->access);
252     }
253     if (datacontrol->providerid) {
254       xmlTextWriterWriteAttribute(writer, BAD_CAST "providerid",
255           BAD_CAST datacontrol->providerid);
256     }
257     if (datacontrol->type) {
258       xmlTextWriterWriteAttribute(writer, BAD_CAST "type",
259           BAD_CAST datacontrol->type);
260     }
261     xmlTextWriterEndElement(writer);
262   }
263
264   for (metadata_x* meta : GListRange<metadata_x*>(app->metadata)) {
265     xmlTextWriterStartElement(writer, BAD_CAST "metadata");
266     xmlTextWriterWriteAttribute(writer, BAD_CAST "key",
267         BAD_CAST meta->key);
268     if (meta->value)
269       xmlTextWriterWriteAttribute(writer, BAD_CAST "value",
270           BAD_CAST meta->value);
271     xmlTextWriterEndElement(writer);
272   }
273
274   for (const char* category : GListRange<char*>(app->category)) {
275     xmlTextWriterStartElement(writer, BAD_CAST "category");
276     xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST category);
277     xmlTextWriterEndElement(writer);
278   }
279
280   for (const char* background_category : GListRange<char*>(
281       app->background_category)) {
282     xmlTextWriterStartElement(writer, BAD_CAST "background-category");
283     xmlTextWriterWriteAttribute(writer, BAD_CAST "value",
284         BAD_CAST background_category);
285     xmlTextWriterEndElement(writer);
286   }
287
288   return Step::Status::OK;
289 }
290
291 common_installer::Step::Status StepGenerateXml::undo() {
292   bs::error_code error;
293   if (bf::exists(context_->xml_path.get()))
294     bf::remove_all(context_->xml_path.get(), error);
295   return Status::OK;
296 }
297
298 common_installer::Step::Status StepGenerateXml::precheck() {
299   if (!context_->manifest_data.get()) {
300     LOG(ERROR) << "manifest_data attribute is empty";
301     return Step::Status::INVALID_VALUE;
302   }
303   if (context_->pkgid.get().empty()) {
304     LOG(ERROR) << "pkgid attribute is empty";
305     return Step::Status::PACKAGE_NOT_FOUND;
306   }
307
308   if (!context_->manifest_data.get()->application) {
309     LOG(ERROR) << "No application in package";
310     return Step::Status::INVALID_VALUE;
311   }
312   // TODO(p.sikorski) check context_->uid.get()
313
314   return Step::Status::OK;
315 }
316
317 common_installer::Step::Status StepGenerateXml::process() {
318   bf::path xml_path =
319       bf::path(getUserManifestPath(context_->uid.get(), false))
320       / bf::path(context_->pkgid.get());
321   xml_path += ".xml";
322   context_->xml_path.set(xml_path.string());
323
324   bs::error_code error;
325   if (!bf::exists(xml_path.parent_path(), error)) {
326     if (!common_installer::CreateDir(xml_path.parent_path())) {
327       LOG(ERROR) <<
328           "Directory for manifest xml is missing and cannot be created";
329       return Status::MANIFEST_ERROR;
330     }
331   }
332
333   xmlTextWriterPtr writer;
334   writer = xmlNewTextWriterFilename(context_->xml_path.get().c_str(), 0);
335   if (!writer) {
336     LOG(ERROR) << "Failed to create new file";
337     return Step::Status::MANIFEST_ERROR;
338   }
339
340   xmlTextWriterStartDocument(writer, nullptr, nullptr, nullptr);
341   xmlTextWriterSetIndent(writer, 1);
342
343   Status status = GenerateManifestElement(writer);
344   if (status != Status::OK) {
345     return status;
346   }
347
348   xmlTextWriterEndDocument(writer);
349   xmlFreeTextWriter(writer);
350
351   if (pkgmgr_parser_check_manifest_validation(
352       context_->xml_path.get().c_str()) != 0) {
353     LOG(ERROR) << "Manifest is not valid";
354     return Step::Status::MANIFEST_ERROR;
355   }
356
357   LOG(DEBUG) << "Successfully create manifest xml "
358       << context_->xml_path.get();
359   return Status::OK;
360 }
361
362 common_installer::Step::Status StepGenerateXml::GenerateManifestElement(
363         xmlTextWriterPtr writer) {
364   xmlTextWriterStartElement(writer, BAD_CAST "manifest");
365
366   GenerateManifestElementAttributes(writer);
367   GenerateLangLabels(writer);
368   GenerateAuthor(writer);
369   GenerateDescription(writer);
370   Status status = GenerateApplications(writer);
371   if (status != Status::OK) {
372     return status;
373   }
374   GeneratePrivilege(writer);
375   GenerateAccount(writer);
376   GenerateIme(writer);
377   GenerateProfiles(writer);
378   GenerateShortcuts(writer);
379
380   xmlTextWriterEndElement(writer);
381   return Status::OK;
382 }
383
384 void StepGenerateXml::GenerateManifestElementAttributes(
385         xmlTextWriterPtr writer) {
386   xmlTextWriterWriteAttribute(writer, BAD_CAST "xmlns",
387       BAD_CAST "http://tizen.org/ns/packages");
388   xmlTextWriterWriteAttribute(writer, BAD_CAST "package",
389       BAD_CAST context_->manifest_data.get()->package);
390   xmlTextWriterWriteAttribute(writer, BAD_CAST "type",
391       BAD_CAST context_->manifest_data.get()->type);
392   xmlTextWriterWriteAttribute(writer, BAD_CAST "version",
393       BAD_CAST context_->manifest_data.get()->version);
394   xmlTextWriterWriteAttribute(writer, BAD_CAST "api-version",
395       BAD_CAST context_->manifest_data.get()->api_version);
396   xmlTextWriterWriteAttribute(writer, BAD_CAST "nodisplay-setting",
397       BAD_CAST context_->manifest_data.get()->nodisplay_setting);
398 }
399
400 void StepGenerateXml::GenerateLangLabels(xmlTextWriterPtr writer) {
401   for (label_x* label :
402        GListRange<label_x*>(context_->manifest_data.get()->label)) {
403     xmlTextWriterStartElement(writer, BAD_CAST "label");
404     if (label->lang && strcmp(DEFAULT_LOCALE, label->lang) != 0) {
405       xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
406                                   BAD_CAST label->lang);
407     }
408     xmlTextWriterWriteString(writer, BAD_CAST label->name);
409     xmlTextWriterEndElement(writer);
410   }
411 }
412
413 void StepGenerateXml::GenerateAuthor(xmlTextWriterPtr writer) {
414   for (author_x* author :
415        GListRange<author_x*>(context_->manifest_data.get()->author)) {
416     xmlTextWriterStartElement(writer, BAD_CAST "author");
417     if (author->email && strlen(author->email)) {
418       xmlTextWriterWriteAttribute(writer, BAD_CAST "email",
419                                   BAD_CAST author->email);
420     }
421     if (author->href && strlen(author->href)) {
422       xmlTextWriterWriteAttribute(writer, BAD_CAST "href",
423                                   BAD_CAST author->href);
424     }
425     xmlTextWriterWriteString(writer, BAD_CAST author->text);
426     xmlTextWriterEndElement(writer);
427   }
428 }
429
430 void StepGenerateXml::GenerateDescription(xmlTextWriterPtr writer) {
431   for (description_x* description :
432        GListRange<description_x*>(context_->manifest_data.get()->description)) {
433     xmlTextWriterStartElement(writer, BAD_CAST "description");
434     if (description->lang && strcmp(DEFAULT_LOCALE, description->lang) != 0) {
435       xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
436                                   BAD_CAST description->lang);
437     }
438     xmlTextWriterWriteString(writer, BAD_CAST description->text);
439     xmlTextWriterEndElement(writer);
440   }
441 }
442
443 common_installer::Step::Status StepGenerateXml::GenerateApplications(
444         xmlTextWriterPtr writer) {
445   for (application_x* app :
446        GListRange<application_x*>(context_->manifest_data.get()->application)) {
447     AppCompType type;
448     if (strcmp(app->component_type, "uiapp") == 0) {
449       type = AppCompType::UIAPP;
450       xmlTextWriterStartElement(writer, BAD_CAST "ui-application");
451     } else if (strcmp(app->component_type, "svcapp") == 0) {
452       type = AppCompType::SVCAPP;
453       xmlTextWriterStartElement(writer, BAD_CAST "service-application");
454     } else if (strcmp(app->component_type, "widgetapp") == 0) {
455       type = AppCompType::WIDGETAPP;
456       xmlTextWriterStartElement(writer, BAD_CAST "widget-application");
457     } else if (strcmp(app->component_type, "watchapp") == 0) {
458       type = AppCompType::WATCHAPP;
459       xmlTextWriterStartElement(writer, BAD_CAST "watch-application");
460     } else {
461       LOG(ERROR) << "Unknown application component_type";
462       xmlFreeTextWriter(writer);
463       return Status::ERROR;
464     }
465     Status status = GenerateApplicationCommonXml(app, writer, type);
466     if (status != Status::OK) {
467       xmlFreeTextWriter(writer);
468       return status;
469     }
470     xmlTextWriterEndElement(writer);
471   }
472   return Status::OK;
473 }
474
475 void StepGenerateXml::GeneratePrivilege(xmlTextWriterPtr writer) {
476     if (context_->manifest_data.get()->privileges) {
477     xmlTextWriterStartElement(writer, BAD_CAST "privileges");
478     for (const char* priv :
479          GListRange<char*>(context_->manifest_data.get()->privileges)) {
480       xmlTextWriterWriteFormatElement(writer, BAD_CAST "privilege",
481         "%s", BAD_CAST priv);
482     }
483
484     xmlTextWriterEndElement(writer);
485   }
486 }
487
488 void StepGenerateXml::GenerateAccount(xmlTextWriterPtr writer) {
489   const auto& accounts =
490       context_->manifest_plugins_data.get().account_info.get().accounts();
491   if (!accounts.empty()) {
492     xmlTextWriterStartElement(writer, BAD_CAST "account");
493     // add account info
494     for (auto& account : accounts) {
495       xmlTextWriterStartElement(writer, BAD_CAST "account-provider");
496
497       xmlTextWriterWriteAttribute(writer, BAD_CAST "appid",
498                                   BAD_CAST account.appid.c_str());
499
500       if (!account.providerid.empty())
501         xmlTextWriterWriteAttribute(writer, BAD_CAST "providerid",
502                                     BAD_CAST account.providerid.c_str());
503
504       if (account.multiple_account_support)
505         xmlTextWriterWriteAttribute(writer,
506                                     BAD_CAST "multiple-accounts-support",
507                                     BAD_CAST "true");
508       else
509         xmlTextWriterWriteAttribute(writer,
510                                     BAD_CAST "multiple-accounts-support",
511                                     BAD_CAST "false");
512       for (auto& icon_pair : account.icon_paths) {
513         xmlTextWriterStartElement(writer, BAD_CAST "icon");
514         if (icon_pair.first == "AccountSmall")
515           xmlTextWriterWriteAttribute(writer, BAD_CAST "section",
516                                       BAD_CAST "account-small");
517         else
518           xmlTextWriterWriteAttribute(writer, BAD_CAST "section",
519                                       BAD_CAST "account");
520         xmlTextWriterWriteString(writer, BAD_CAST icon_pair.second.c_str());
521         xmlTextWriterEndElement(writer);
522       }
523
524       for (auto& name_pair : account.names) {
525         xmlTextWriterStartElement(writer, BAD_CAST "label");
526         if (!name_pair.second.empty())
527           xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
528                                       BAD_CAST name_pair.second.c_str());
529         xmlTextWriterWriteString(writer, BAD_CAST name_pair.first.c_str());
530         xmlTextWriterEndElement(writer);
531       }
532
533       for (auto& capability : account.capabilities) {
534         xmlTextWriterWriteFormatElement(writer, BAD_CAST "capability",
535           "%s", BAD_CAST capability.c_str());
536       }
537
538       xmlTextWriterEndElement(writer);
539     }
540     xmlTextWriterEndElement(writer);
541   }
542 }
543
544 void StepGenerateXml::GenerateIme(xmlTextWriterPtr writer) {
545   const auto &ime = context_->manifest_plugins_data.get().ime_info.get();
546   const auto ime_uuid = ime.uuid();
547   if (!ime_uuid.empty()) {
548     xmlTextWriterStartElement(writer, BAD_CAST "ime");
549
550     GListRange<application_x*> app_range(
551         context_->manifest_data.get()->application);
552     if (!app_range.Empty()) {
553       // wgt app have ui-application as first application element.
554       // there may be service-applications but not as first element.
555       application_x* app = *app_range.begin();
556       xmlTextWriterWriteAttribute(writer, BAD_CAST "appid",
557                                   BAD_CAST app->appid);
558     }
559
560     xmlTextWriterStartElement(writer, BAD_CAST "uuid");
561     xmlTextWriterWriteString(writer, BAD_CAST ime_uuid.c_str());
562     xmlTextWriterEndElement(writer);
563
564     xmlTextWriterStartElement(writer, BAD_CAST "languages");
565
566     for (auto it = ime.LanguagesBegin(); it != ime.LanguagesEnd(); ++it) {
567       xmlTextWriterStartElement(writer, BAD_CAST "language");
568       xmlTextWriterWriteString(writer, BAD_CAST it->c_str());
569       xmlTextWriterEndElement(writer);
570     }
571
572     xmlTextWriterEndElement(writer);
573
574     xmlTextWriterEndElement(writer);
575   }
576 }
577
578 void StepGenerateXml::GenerateProfiles(xmlTextWriterPtr writer) {
579   for (const char* profile :
580        GListRange<char*>(context_->manifest_data.get()->deviceprofile)) {
581     xmlTextWriterStartElement(writer, BAD_CAST "profile");
582     xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
583                                 BAD_CAST profile);
584     xmlTextWriterEndElement(writer);
585   }
586 }
587
588 void StepGenerateXml::GenerateShortcuts(xmlTextWriterPtr writer) {
589   const auto& shortcuts =
590       context_->manifest_plugins_data.get().shortcut_info.get();
591   if (!shortcuts.empty()) {
592     xmlTextWriterStartElement(writer, BAD_CAST "shortcut-list");
593     for (auto& shortcut : shortcuts) {
594       xmlTextWriterStartElement(writer, BAD_CAST "shortcut");
595       if (!shortcut.app_id.empty())
596         xmlTextWriterWriteAttribute(writer, BAD_CAST "appid",
597                                     BAD_CAST shortcut.app_id.c_str());
598       if (!shortcut.app_id.empty())
599         xmlTextWriterWriteAttribute(writer, BAD_CAST "extra_data",
600                                     BAD_CAST shortcut.extra_data.c_str());
601       if (!shortcut.app_id.empty())
602         xmlTextWriterWriteAttribute(writer, BAD_CAST "extra_key",
603                                     BAD_CAST shortcut.extra_key.c_str());
604       if (!shortcut.icon.empty()) {
605         xmlTextWriterStartElement(writer, BAD_CAST "icon");
606         xmlTextWriterWriteString(writer, BAD_CAST shortcut.icon.c_str());
607         xmlTextWriterEndElement(writer);
608       }
609       for (auto& label : shortcut.labels) {
610         xmlTextWriterStartElement(writer, BAD_CAST "label");
611         if (!label.first.empty())
612           xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
613                                       BAD_CAST label.first.c_str());
614         xmlTextWriterWriteString(writer, BAD_CAST label.second.c_str());
615         xmlTextWriterEndElement(writer);
616       }
617       xmlTextWriterEndElement(writer);
618     }
619     xmlTextWriterEndElement(writer);
620   }
621 }
622
623 }  // namespace pkgmgr
624 }  // namespace wgt