llavataractions.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. /**
  2. * @file llavataractions.cpp
  3. * @brief avatar-related actions (IM, teleporting, etc)
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. *
  7. * Copyright (c) 2001-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "llavataractions.h"
  34. #include "llcachename.h"
  35. #include "llnotifications.h"
  36. #include "lltrans.h"
  37. #include "roles_constants.h" // For GP_LAND_ADMIN
  38. #include "llagent.h"
  39. #include "llcommandhandler.h"
  40. #include "llfloateravatarinfo.h"
  41. #include "llfloaterfriends.h"
  42. #include "llfloaterinspect.h"
  43. #include "llfloatermute.h"
  44. #include "llfloaterpay.h"
  45. #include "llinventorymodel.h"
  46. #include "llimmgr.h"
  47. //MK
  48. #include "mkrlinterface.h"
  49. //mk
  50. #include "llviewermessage.h" // send_improved_im() handle_lure() give_money()
  51. #include "llviewerobjectlist.h"
  52. #include "llviewerparcelmgr.h"
  53. #include "llviewerregion.h"
  54. #include "llvoavatar.h"
  55. //-----------------------------------------------------------------------------
  56. // Command handler
  57. //-----------------------------------------------------------------------------
  58. static void on_name_cache_mute(const LLUUID& agent_id, const std::string& name,
  59. bool is_group, bool mute_it)
  60. {
  61. LLMute mute(agent_id, name, LLMute::AGENT);
  62. if (LLMuteList::isMuted(agent_id, name))
  63. {
  64. if (!mute_it)
  65. {
  66. LLMuteList::remove(mute);
  67. }
  68. }
  69. else
  70. {
  71. if (mute_it)
  72. {
  73. LLMuteList::add(mute);
  74. }
  75. LLFloaterMute::selectMute(agent_id);
  76. }
  77. }
  78. class LLAgentHandler final : public LLCommandHandler
  79. {
  80. public:
  81. LLAgentHandler()
  82. : LLCommandHandler("agent", UNTRUSTED_THROTTLE)
  83. {
  84. }
  85. bool canHandleUntrusted(const LLSD& params, const LLSD&,
  86. LLMediaCtrl*, const std::string& nav_type) override
  87. {
  88. if (params.size() < 2)
  89. {
  90. return true; // Do not block; it will fail later in handle()
  91. }
  92. if (nav_type == "clicked" || nav_type == "external")
  93. {
  94. return true;
  95. }
  96. std::string verb = params[1].asString();
  97. return verb == "about" || verb == "inspect" || verb == "username" ||
  98. verb == "displayname" || verb == "completename";
  99. }
  100. bool handle(const LLSD& params, const LLSD&, LLMediaCtrl*) override
  101. {
  102. if (params.size() < 2) return false;
  103. LLUUID agent_id;
  104. if (!agent_id.set(params[0], false))
  105. {
  106. return false;
  107. }
  108. const std::string verb = params[1].asString();
  109. if (verb == "about" || verb == "username" || verb == "displayname" ||
  110. verb == "completename")
  111. {
  112. LLFloaterAvatarInfo::show(agent_id);
  113. }
  114. else if (verb == "inspect")
  115. {
  116. HBFloaterInspectAvatar::show(agent_id);
  117. }
  118. else if (verb == "pay")
  119. {
  120. LLAvatarActions::pay(agent_id);
  121. }
  122. else if (verb == "offerteleport")
  123. {
  124. LLAvatarActions::offerTeleport(agent_id);
  125. }
  126. else if (verb == "im")
  127. {
  128. LLAvatarActions::startIM(agent_id);
  129. }
  130. else if (verb == "requestfriend")
  131. {
  132. LLAvatarActions::requestFriendshipDialog(agent_id);
  133. }
  134. else if (verb == "mute" || verb == "unmute" ||
  135. verb == "block" || verb == "unblock")
  136. {
  137. if (!gCacheNamep) return false; // Paranoia
  138. gCacheNamep->get(agent_id, false,
  139. boost::bind(&on_name_cache_mute,
  140. _1, _2, _3,
  141. verb == "mute" || verb == "block"));
  142. }
  143. else
  144. {
  145. return false;
  146. }
  147. return true;
  148. }
  149. };
  150. LLAgentHandler gAgentHandler;
  151. ///////////////////////////////////////////////////////////////////////////////
  152. // LLAvatarActions class
  153. ///////////////////////////////////////////////////////////////////////////////
  154. static void on_avatar_name_friendship(const LLUUID& id,
  155. const LLAvatarName& av_name)
  156. {
  157. std::string fullname;
  158. if (!LLAvatarName::sLegacyNamesForFriends &&
  159. LLAvatarNameCache::useDisplayNames())
  160. {
  161. if (LLAvatarNameCache::useDisplayNames() == 2)
  162. {
  163. fullname = av_name.mDisplayName;
  164. }
  165. else
  166. {
  167. fullname = av_name.getNames();
  168. }
  169. }
  170. else
  171. {
  172. fullname = av_name.getLegacyName();
  173. }
  174. LLAvatarActions::requestFriendshipDialog(id, fullname);
  175. }
  176. //static
  177. void LLAvatarActions::requestFriendshipDialog(const LLUUID& id)
  178. {
  179. if (id.isNull() || !gCacheNamep)
  180. {
  181. return;
  182. }
  183. std::string fullname;
  184. if (gCacheNamep->getFullName(id, fullname) &&
  185. (LLAvatarName::sLegacyNamesForFriends ||
  186. !LLAvatarNameCache::useDisplayNames()))
  187. {
  188. requestFriendshipDialog(id, fullname);
  189. return;
  190. }
  191. LLAvatarNameCache::get(id,
  192. boost::bind(&on_avatar_name_friendship, _1, _2));
  193. }
  194. static bool callback_add_friend(const LLSD& notification,
  195. const LLSD& response)
  196. {
  197. if (LLNotification::getSelectedOption(notification, response) == 0)
  198. {
  199. LLUUID id = notification["payload"]["id"].asUUID();
  200. std::string message = response["message"].asString();
  201. //MK
  202. if (gRLenabled && !gRLInterface.canSendIM(id))
  203. {
  204. message = "(Hidden)";
  205. }
  206. //mk
  207. std::string name = notification["payload"]["name"].asString();
  208. LLAvatarActions::requestFriendship(id, name, message);
  209. }
  210. return false;
  211. }
  212. //static
  213. void LLAvatarActions::requestFriendshipDialog(const LLUUID& id,
  214. const std::string& name)
  215. {
  216. if (id == gAgentID)
  217. {
  218. gNotifications.add("AddSelfFriend");
  219. return;
  220. }
  221. LLSD args;
  222. args["NAME"] = name;
  223. LLSD payload;
  224. payload["id"] = id;
  225. payload["name"] = name;
  226. gNotifications.add("AddFriendWithMessage", args, payload,
  227. callback_add_friend);
  228. }
  229. //static
  230. void LLAvatarActions::requestFriendship(const LLUUID& id,
  231. const std::string& name,
  232. const std::string& message)
  233. {
  234. const LLUUID& folder_id =
  235. gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD);
  236. send_improved_im(id, name, message, IM_ONLINE, IM_FRIENDSHIP_OFFERED,
  237. folder_id);
  238. }
  239. //static
  240. void LLAvatarActions::offerTeleport(const LLUUID& id)
  241. {
  242. if (id.isNull())
  243. {
  244. llwarns << "Null avatar UUID, aborted." << llendl;
  245. }
  246. else if (id == gAgentID)
  247. {
  248. llwarns << "Cannot teleport self !" << llendl;
  249. }
  250. else
  251. {
  252. uuid_vec_t ids;
  253. ids.push_back(id);
  254. handle_lure(ids);
  255. }
  256. }
  257. //static
  258. void LLAvatarActions::offerTeleport(const uuid_vec_t& ids)
  259. {
  260. if (ids.size() > 0)
  261. {
  262. handle_lure(ids);
  263. }
  264. else
  265. {
  266. llwarns << "Tried to offer teleport to an empty list of avatars"
  267. << llendl;
  268. }
  269. }
  270. static void teleport_request_callback(const LLSD& notification,
  271. const LLSD& response)
  272. {
  273. S32 option = 0;
  274. if (response.isInteger())
  275. {
  276. option = response.asInteger();
  277. }
  278. else
  279. {
  280. option = LLNotification::getSelectedOption(notification, response);
  281. }
  282. if (option == 0)
  283. {
  284. LLMessageSystem* msg = gMessageSystemp;
  285. msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
  286. msg->nextBlockFast(_PREHASH_AgentData);
  287. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  288. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  289. msg->nextBlockFast(_PREHASH_MessageBlock);
  290. msg->addBoolFast(_PREHASH_FromGroup, false);
  291. LLUUID target_id = notification["substitutions"]["uuid"].asUUID();
  292. msg->addUUIDFast(_PREHASH_ToAgentID, target_id);
  293. msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
  294. msg->addU8Fast(_PREHASH_Dialog, IM_TELEPORT_REQUEST);
  295. msg->addUUIDFast(_PREHASH_ID, LLUUID::null);
  296. // no timestamp necessary
  297. msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP);
  298. std::string name;
  299. gAgent.buildFullname(name);
  300. msg->addStringFast(_PREHASH_FromAgentName, name);
  301. //MK
  302. if (gRLenabled && !gRLInterface.canSendIM(target_id))
  303. {
  304. msg->addStringFast(_PREHASH_Message, "(Hidden)");
  305. }
  306. else
  307. //mk
  308. {
  309. msg->addStringFast(_PREHASH_Message, response["message"]);
  310. }
  311. msg->addU32Fast(_PREHASH_ParentEstateID, 0);
  312. msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
  313. msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
  314. msg->addBinaryDataFast(_PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET,
  315. EMPTY_BINARY_BUCKET_SIZE);
  316. gAgent.sendReliableMessage();
  317. }
  318. }
  319. //static
  320. void LLAvatarActions::teleportRequest(const LLUUID& id)
  321. {
  322. if (id.isNull())
  323. {
  324. llwarns << "Null avatar UUID, aborted." << llendl;
  325. }
  326. else if (id == gAgentID)
  327. {
  328. llwarns << "Cannot request a teleport to self !" << llendl;
  329. }
  330. else
  331. {
  332. LLAvatarName av_name;
  333. if (LLAvatarNameCache::get(id, &av_name))
  334. {
  335. LLSD notification;
  336. notification["uuid"] = id;
  337. notification["NAME"] = av_name.getNames();
  338. LLSD payload;
  339. gNotifications.add("TeleportRequestPrompt", notification, payload,
  340. teleport_request_callback);
  341. }
  342. else // Unlikely ... they just picked this name from somewhere...
  343. {
  344. // Re-invoke this very method after the name resolves
  345. LLAvatarNameCache::get(id, boost::bind(&teleportRequest, id));
  346. }
  347. }
  348. }
  349. static void on_avatar_name_cache_start_im(const LLUUID& agent_id,
  350. const LLAvatarName& av_name)
  351. {
  352. if (gIMMgrp)
  353. {
  354. gIMMgrp->setFloaterOpen(true);
  355. gIMMgrp->addSession(av_name.getLegacyName(), IM_NOTHING_SPECIAL,
  356. agent_id);
  357. make_ui_sound("UISndStartIM");
  358. }
  359. }
  360. //static
  361. void LLAvatarActions::startIM(const LLUUID& id)
  362. {
  363. if (id.isNull())
  364. {
  365. llwarns << "Null avatar UUID, aborted." << llendl;
  366. }
  367. else if (id == gAgentID)
  368. {
  369. llwarns << "Cannot IM to self !" << llendl;
  370. }
  371. else
  372. {
  373. LLAvatarNameCache::get(id, boost::bind(&on_avatar_name_cache_start_im,
  374. _1, _2));
  375. }
  376. }
  377. //static
  378. void LLAvatarActions::startIM(const uuid_vec_t& ids, bool friends)
  379. {
  380. if (!gIMMgrp) return;
  381. S32 count = ids.size();
  382. if (count > 1)
  383. {
  384. // Group IM
  385. LLUUID session_id;
  386. session_id.generate();
  387. gIMMgrp->setFloaterOpen(true);
  388. // *TODO: translate
  389. gIMMgrp->addSession(friends ? "Friends Conference"
  390. : "Avatars Conference",
  391. IM_SESSION_CONFERENCE_START, ids[0], ids);
  392. make_ui_sound("UISndStartIM");
  393. }
  394. else if (count == 1)
  395. {
  396. // Single avatar
  397. LLUUID agent_id = ids[0];
  398. startIM(agent_id);
  399. }
  400. else
  401. {
  402. llwarns << "Tried to initiate an IM conference with an empty list of participants"
  403. << llendl;
  404. }
  405. }
  406. //static
  407. void LLAvatarActions::pay(const LLUUID& id)
  408. {
  409. if (id.isNull())
  410. {
  411. llwarns << "Null avatar UUID, aborted." << llendl;
  412. }
  413. else
  414. {
  415. LLFloaterPay::payDirectly(&give_money, id, false);
  416. }
  417. }
  418. //static
  419. void LLAvatarActions::buildAvatarsList(std::vector<LLAvatarName> avatar_names,
  420. std::string& avatars, bool force_legacy,
  421. const std::string& separator)
  422. {
  423. U32 name_usage = force_legacy ? 0 : LLAvatarNameCache::useDisplayNames();
  424. std::sort(avatar_names.begin(), avatar_names.end());
  425. for (std::vector<LLAvatarName>::const_iterator it = avatar_names.begin(),
  426. end = avatar_names.end();
  427. it != end; ++it)
  428. {
  429. if (!avatars.empty())
  430. {
  431. avatars.append(separator);
  432. }
  433. switch (name_usage)
  434. {
  435. case 2:
  436. avatars.append(it->mDisplayName);
  437. break;
  438. case 1:
  439. avatars.append(it->getNames());
  440. break;
  441. default:
  442. avatars.append(it->getLegacyName());
  443. }
  444. }
  445. }
  446. //static
  447. LLViewerRegion* LLAvatarActions::canEjectOrFreeze(const LLUUID& avatar_id)
  448. {
  449. LLVOAvatar* avatarp = gObjectList.findAvatar(avatar_id);
  450. if (!avatarp)
  451. {
  452. return NULL;
  453. }
  454. LLViewerRegion* regionp = avatarp->getRegion();
  455. if (!regionp)
  456. {
  457. return NULL;
  458. }
  459. const LLVector3& pos = avatarp->getPositionRegion();
  460. bool can_do = regionp->isOwnedSelf(pos);
  461. const LLVector3d& pos_global = avatarp->getPositionGlobal();
  462. LLParcel* parcelp =
  463. gViewerParcelMgr.selectParcelAt(pos_global)->getParcel();
  464. if (parcelp && (!can_do || regionp->isOwnedGroup(pos)))
  465. {
  466. can_do = gViewerParcelMgr.isParcelOwnedByAgent(parcelp, GP_LAND_ADMIN);
  467. }
  468. return regionp;
  469. }
  470. //static
  471. bool LLAvatarActions::sendEject(const LLUUID& avatar_id, bool ban)
  472. {
  473. LLViewerRegion* regionp = canEjectOrFreeze(avatar_id);
  474. if (!regionp)
  475. {
  476. return false;
  477. }
  478. LLMessageSystem* msg = gMessageSystemp;
  479. if (!msg) return false; // Paranoia
  480. msg->newMessage(_PREHASH_EjectUser);
  481. msg->nextBlock(_PREHASH_AgentData);
  482. msg->addUUID(_PREHASH_AgentID, gAgentID);
  483. msg->addUUID(_PREHASH_SessionID, gAgentSessionID);
  484. msg->nextBlock(_PREHASH_Data);
  485. msg->addUUID(_PREHASH_TargetID, avatar_id);
  486. msg->addU32(_PREHASH_Flags, ban ? 0x1 : 0x0);
  487. msg->sendReliable(regionp->getHost());
  488. return true;
  489. }
  490. //static
  491. bool LLAvatarActions::sendFreeze(const LLUUID& avatar_id, bool freeze)
  492. {
  493. LLViewerRegion* regionp = canEjectOrFreeze(avatar_id);
  494. if (!regionp)
  495. {
  496. return false;
  497. }
  498. LLMessageSystem* msg = gMessageSystemp;
  499. if (!msg) return false; // Paranoia
  500. msg->newMessage(_PREHASH_FreezeUser);
  501. msg->nextBlock(_PREHASH_AgentData);
  502. msg->addUUID(_PREHASH_AgentID, gAgentID);
  503. msg->addUUID(_PREHASH_SessionID, gAgentSessionID);
  504. msg->nextBlock(_PREHASH_Data);
  505. msg->addUUID(_PREHASH_TargetID, avatar_id);
  506. msg->addU32(_PREHASH_Flags, freeze ? 0x0 : 0x1);
  507. msg->sendReliable(regionp->getHost());
  508. return true;
  509. }
  510. static bool god_finish_kick(const LLSD& notification, const LLSD& response)
  511. {
  512. if (LLNotification::getSelectedOption(notification, response) == 0)
  513. {
  514. LLMessageSystem* msg = gMessageSystemp;
  515. if (!msg) return false; // Paranoia
  516. LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID();
  517. U32 flags = notification["payload"]["flags"].asInteger();
  518. msg->newMessageFast(_PREHASH_GodKickUser);
  519. msg->nextBlockFast(_PREHASH_UserInfo);
  520. msg->addUUIDFast(_PREHASH_GodID, gAgentID);
  521. msg->addUUIDFast(_PREHASH_GodSessionID, gAgentSessionID);
  522. msg->addUUIDFast(_PREHASH_AgentID, avatar_id);
  523. msg->addU32(_PREHASH_KickFlags, flags);
  524. msg->addStringFast(_PREHASH_Reason, response["message"].asString());
  525. gAgent.sendReliableMessage();
  526. }
  527. return false;
  528. }
  529. static bool user_finish_eject(const LLSD& notification, const LLSD& response)
  530. {
  531. if (LLNotification::getSelectedOption(notification, response) == 0)
  532. {
  533. LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID();
  534. LLAvatarActions::sendEject(avatar_id, false);
  535. }
  536. return false;
  537. }
  538. static bool user_finish_freeze(const LLSD& notification, const LLSD& response)
  539. {
  540. if (LLNotification::getSelectedOption(notification, response) == 0)
  541. {
  542. LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID();
  543. bool freeze = notification["payload"]["freeze"].asBoolean();
  544. LLAvatarActions::sendFreeze(avatar_id, freeze);
  545. }
  546. return false;
  547. }
  548. //static
  549. void LLAvatarActions::kick(const LLUUID& avatar_id)
  550. {
  551. LLSD payload;
  552. payload["avatar_id"] = avatar_id;
  553. LLSD args;
  554. std::string fullname;
  555. if (gCacheNamep && gCacheNamep->getFullName(avatar_id, fullname))
  556. {
  557. //MK
  558. if (gRLenabled &&
  559. (gRLInterface.mContainsShownames ||
  560. gRLInterface.mContainsShownametags))
  561. {
  562. fullname = gRLInterface.getDummyName(fullname);
  563. }
  564. //mk
  565. args["AVATAR_NAME"] = fullname;
  566. }
  567. else
  568. {
  569. args["AVATAR_NAME"] = LLTrans::getString("this_resident");
  570. }
  571. if (gAgent.isGodlikeWithoutAdminMenuFakery())
  572. {
  573. payload["flags"] = S32(KICK_FLAGS_DEFAULT);
  574. gNotifications.add("KickUser", args, payload, god_finish_kick);
  575. }
  576. else if (canEjectOrFreeze(avatar_id))
  577. {
  578. gNotifications.add("EjectUserNoMessage", args, payload,
  579. user_finish_eject);
  580. }
  581. }
  582. //static
  583. void LLAvatarActions::freeze(const LLUUID& avatar_id, bool freeze)
  584. {
  585. LLSD payload;
  586. payload["avatar_id"] = avatar_id;
  587. LLSD args;
  588. std::string fullname;
  589. if (gCacheNamep && gCacheNamep->getFullName(avatar_id, fullname))
  590. {
  591. //MK
  592. if (gRLenabled &&
  593. (gRLInterface.mContainsShownames ||
  594. gRLInterface.mContainsShownametags))
  595. {
  596. fullname = gRLInterface.getDummyName(fullname);
  597. }
  598. //mk
  599. args["AVATAR_NAME"] = fullname;
  600. }
  601. else
  602. {
  603. args["AVATAR_NAME"] = LLTrans::getString("this_resident");
  604. }
  605. if (gAgent.isGodlikeWithoutAdminMenuFakery())
  606. {
  607. payload["flags"] = S32(freeze ? KICK_FLAGS_FREEZE
  608. : KICK_FLAGS_UNFREEZE);
  609. gNotifications.add("FreezeUser", args, payload, god_finish_kick);
  610. }
  611. else if (canEjectOrFreeze(avatar_id))
  612. {
  613. payload["freeze"] = freeze;
  614. gNotifications.add("FreezeUserNoMessage", args, payload,
  615. user_finish_freeze);
  616. }
  617. }