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.
6 #include "wgt/step/pkgmgr/step_generate_xml.h"
8 #include <boost/filesystem/path.hpp>
9 #include <boost/system/error_code.hpp>
11 #include <common/utils/file_util.h>
12 #include <common/utils/glist_range.h>
13 #include <common/privileges.h>
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>
26 #include "wgt/wgt_backend_data.h"
28 namespace bs = boost::system;
29 namespace bf = boost::filesystem;
33 const char kResWgt[] = "res/wgt";
34 const char kSharedRes[] = "shared/res";
36 void WriteUIApplicationAttributes(
37 xmlTextWriterPtr writer, application_x *app) {
39 xmlTextWriterWriteAttribute(writer, BAD_CAST "taskmanage",
40 BAD_CAST app->taskmanage);
42 xmlTextWriterWriteAttribute(writer, BAD_CAST "nodisplay",
43 BAD_CAST app->nodisplay);
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);
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"));
83 xmlTextWriterWriteAttribute(writer, BAD_CAST "taskmanage",
84 BAD_CAST app->taskmanage);
87 bool WriteWidgetApplicationAttributesAndElements(
88 xmlTextWriterPtr writer, application_x *app,
89 const wgt::parse::AppWidgetInfo& widget_info,
90 const bf::path& shared_path) {
92 xmlTextWriterWriteAttribute(writer, BAD_CAST "nodisplay",
93 BAD_CAST app->nodisplay);
95 xmlTextWriterWriteAttribute(writer, BAD_CAST "multiple",
96 BAD_CAST app->multiple);
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;
104 if (appwidget == appwidgets.end()) {
105 LOG(ERROR) << "Failed to generate appwidget extra elements";
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());
117 for (auto& size : appwidget->content_size) {
118 xmlTextWriterStartElement(writer, BAD_CAST "support-size");
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
129 xmlTextWriterWriteAttribute(writer, BAD_CAST "frame",
131 xmlTextWriterWriteString(writer,
132 BAD_CAST type.c_str());
133 xmlTextWriterEndElement(writer);
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);
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);
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());
160 xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST app->type);
162 xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST "capp");
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
169 case AppCompType::UIAPP:
170 WriteUIApplicationAttributes(writer, app);
172 case AppCompType::SVCAPP:
173 WriteServiceApplicationAttributes(writer, app);
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;
182 case AppCompType::WATCHAPP:
183 WriteWatchApplicationAttributes(writer, app);
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);
193 xmlTextWriterWriteString(writer, BAD_CAST label->name);
194 xmlTextWriterEndElement(writer);
198 icon_x* iconx = reinterpret_cast<icon_x*>(app->icon->data);
199 xmlTextWriterWriteFormatElement(
200 writer, BAD_CAST "icon", "%s", BAD_CAST iconx->text);
202 // Default icon setting is role of the platform
203 LOG(DEBUG) << "Icon was not found in application";
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",
211 BAD_CAST image->lang);
214 xmlTextWriterWriteAttribute(writer, BAD_CAST "section",
215 BAD_CAST image->section);
216 xmlTextWriterEndElement(writer);
219 for (appcontrol_x* appc : GListRange<appcontrol_x*>(app->appcontrol)) {
220 xmlTextWriterStartElement(writer, BAD_CAST "app-control");
222 if (appc->operation) {
223 xmlTextWriterStartElement(writer, BAD_CAST "operation");
224 xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
225 BAD_CAST appc->operation);
226 xmlTextWriterEndElement(writer);
230 xmlTextWriterStartElement(writer, BAD_CAST "uri");
231 xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
233 xmlTextWriterEndElement(writer);
237 xmlTextWriterStartElement(writer, BAD_CAST "mime");
238 xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
239 BAD_CAST appc->mime);
240 xmlTextWriterEndElement(writer);
243 xmlTextWriterEndElement(writer);
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);
253 if (datacontrol->providerid) {
254 xmlTextWriterWriteAttribute(writer, BAD_CAST "providerid",
255 BAD_CAST datacontrol->providerid);
257 if (datacontrol->type) {
258 xmlTextWriterWriteAttribute(writer, BAD_CAST "type",
259 BAD_CAST datacontrol->type);
261 xmlTextWriterEndElement(writer);
264 for (metadata_x* meta : GListRange<metadata_x*>(app->metadata)) {
265 xmlTextWriterStartElement(writer, BAD_CAST "metadata");
266 xmlTextWriterWriteAttribute(writer, BAD_CAST "key",
269 xmlTextWriterWriteAttribute(writer, BAD_CAST "value",
270 BAD_CAST meta->value);
271 xmlTextWriterEndElement(writer);
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);
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);
288 return Step::Status::OK;
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);
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;
303 if (context_->pkgid.get().empty()) {
304 LOG(ERROR) << "pkgid attribute is empty";
305 return Step::Status::PACKAGE_NOT_FOUND;
308 if (!context_->manifest_data.get()->application) {
309 LOG(ERROR) << "No application in package";
310 return Step::Status::INVALID_VALUE;
312 // TODO(p.sikorski) check context_->uid.get()
314 return Step::Status::OK;
317 common_installer::Step::Status StepGenerateXml::process() {
319 bf::path(getUserManifestPath(context_->uid.get(), false))
320 / bf::path(context_->pkgid.get());
322 context_->xml_path.set(xml_path.string());
324 bs::error_code error;
325 if (!bf::exists(xml_path.parent_path(), error)) {
326 if (!common_installer::CreateDir(xml_path.parent_path())) {
328 "Directory for manifest xml is missing and cannot be created";
329 return Status::MANIFEST_ERROR;
333 xmlTextWriterPtr writer;
334 writer = xmlNewTextWriterFilename(context_->xml_path.get().c_str(), 0);
336 LOG(ERROR) << "Failed to create new file";
337 return Step::Status::MANIFEST_ERROR;
340 xmlTextWriterStartDocument(writer, nullptr, nullptr, nullptr);
341 xmlTextWriterSetIndent(writer, 1);
343 Status status = GenerateManifestElement(writer);
344 if (status != Status::OK) {
348 xmlTextWriterEndDocument(writer);
349 xmlFreeTextWriter(writer);
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;
357 LOG(DEBUG) << "Successfully create manifest xml "
358 << context_->xml_path.get();
362 common_installer::Step::Status StepGenerateXml::GenerateManifestElement(
363 xmlTextWriterPtr writer) {
364 xmlTextWriterStartElement(writer, BAD_CAST "manifest");
366 GenerateManifestElementAttributes(writer);
367 GenerateLangLabels(writer);
368 GenerateAuthor(writer);
369 GenerateDescription(writer);
370 Status status = GenerateApplications(writer);
371 if (status != Status::OK) {
374 GeneratePrivilege(writer);
375 GenerateAccount(writer);
377 GenerateProfiles(writer);
378 GenerateShortcuts(writer);
380 xmlTextWriterEndElement(writer);
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);
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);
408 xmlTextWriterWriteString(writer, BAD_CAST label->name);
409 xmlTextWriterEndElement(writer);
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);
421 if (author->href && strlen(author->href)) {
422 xmlTextWriterWriteAttribute(writer, BAD_CAST "href",
423 BAD_CAST author->href);
425 xmlTextWriterWriteString(writer, BAD_CAST author->text);
426 xmlTextWriterEndElement(writer);
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);
438 xmlTextWriterWriteString(writer, BAD_CAST description->text);
439 xmlTextWriterEndElement(writer);
443 common_installer::Step::Status StepGenerateXml::GenerateApplications(
444 xmlTextWriterPtr writer) {
445 for (application_x* app :
446 GListRange<application_x*>(context_->manifest_data.get()->application)) {
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");
461 LOG(ERROR) << "Unknown application component_type";
462 xmlFreeTextWriter(writer);
463 return Status::ERROR;
465 Status status = GenerateApplicationCommonXml(app, writer, type);
466 if (status != Status::OK) {
467 xmlFreeTextWriter(writer);
470 xmlTextWriterEndElement(writer);
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);
484 xmlTextWriterEndElement(writer);
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");
494 for (auto& account : accounts) {
495 xmlTextWriterStartElement(writer, BAD_CAST "account-provider");
497 xmlTextWriterWriteAttribute(writer, BAD_CAST "appid",
498 BAD_CAST account.appid.c_str());
500 if (!account.providerid.empty())
501 xmlTextWriterWriteAttribute(writer, BAD_CAST "providerid",
502 BAD_CAST account.providerid.c_str());
504 if (account.multiple_account_support)
505 xmlTextWriterWriteAttribute(writer,
506 BAD_CAST "multiple-accounts-support",
509 xmlTextWriterWriteAttribute(writer,
510 BAD_CAST "multiple-accounts-support",
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");
518 xmlTextWriterWriteAttribute(writer, BAD_CAST "section",
520 xmlTextWriterWriteString(writer, BAD_CAST icon_pair.second.c_str());
521 xmlTextWriterEndElement(writer);
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);
533 for (auto& capability : account.capabilities) {
534 xmlTextWriterWriteFormatElement(writer, BAD_CAST "capability",
535 "%s", BAD_CAST capability.c_str());
538 xmlTextWriterEndElement(writer);
540 xmlTextWriterEndElement(writer);
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");
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);
560 xmlTextWriterStartElement(writer, BAD_CAST "uuid");
561 xmlTextWriterWriteString(writer, BAD_CAST ime_uuid.c_str());
562 xmlTextWriterEndElement(writer);
564 xmlTextWriterStartElement(writer, BAD_CAST "languages");
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);
572 xmlTextWriterEndElement(writer);
574 xmlTextWriterEndElement(writer);
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",
584 xmlTextWriterEndElement(writer);
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);
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);
617 xmlTextWriterEndElement(writer);
619 xmlTextWriterEndElement(writer);
623 } // namespace pkgmgr