aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs211
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs134
-rw-r--r--OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs66
3 files changed, 309 insertions, 102 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
index 7094213..1d7889d 100644
--- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
@@ -26,10 +26,8 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.IO;
30using System.Collections; 29using System.Collections;
31using System.Collections.Generic; 30using System.Collections.Generic;
32using System.Xml;
33using System.Reflection; 31using System.Reflection;
34using log4net; 32using log4net;
35using Nini.Config; 33using Nini.Config;
@@ -40,16 +38,16 @@ using OpenSim.Framework.Communications;
40using OpenSim.Region.Framework.Interfaces; 38using OpenSim.Region.Framework.Interfaces;
41using OpenSim.Region.Framework.Scenes; 39using OpenSim.Region.Framework.Scenes;
42using OpenSim.Services.Interfaces; 40using OpenSim.Services.Interfaces;
41using OpenSim.Services.Connectors.Friends;
43using OpenSim.Server.Base; 42using OpenSim.Server.Base;
44using OpenSim.Framework.Servers.HttpServer; 43using OpenSim.Framework.Servers.HttpServer;
45using log4net;
46using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; 44using FriendInfo = OpenSim.Services.Interfaces.FriendInfo;
47using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; 45using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
48using GridRegion = OpenSim.Services.Interfaces.GridRegion; 46using GridRegion = OpenSim.Services.Interfaces.GridRegion;
49 47
50namespace OpenSim.Region.CoreModules.Avatar.Friends 48namespace OpenSim.Region.CoreModules.Avatar.Friends
51{ 49{
52 public class FriendsModule : BaseStreamHandler, ISharedRegionModule, IFriendsModule 50 public class FriendsModule : ISharedRegionModule, IFriendsModule
53 { 51 {
54 protected class UserFriendData 52 protected class UserFriendData
55 { 53 {
@@ -72,12 +70,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
72 70
73 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 71 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
74 72
75 protected int m_Port = 0;
76
77 protected List<Scene> m_Scenes = new List<Scene>(); 73 protected List<Scene> m_Scenes = new List<Scene>();
78 74
79 protected IPresenceService m_PresenceService = null; 75 protected IPresenceService m_PresenceService = null;
80 protected IFriendsService m_FriendsService = null; 76 protected IFriendsService m_FriendsService = null;
77 protected FriendsSimConnector m_FriendsSimConnector;
78
81 protected Dictionary<UUID, UserFriendData> m_Friends = 79 protected Dictionary<UUID, UserFriendData> m_Friends =
82 new Dictionary<UUID, UserFriendData>(); 80 new Dictionary<UUID, UserFriendData>();
83 81
@@ -117,22 +115,23 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
117 } 115 }
118 } 116 }
119 117
120 public FriendsModule()
121 : base("POST", "/friends")
122 {
123 }
124
125 public void Initialise(IConfigSource config) 118 public void Initialise(IConfigSource config)
126 { 119 {
127 IConfig friendsConfig = config.Configs["Friends"]; 120 IConfig friendsConfig = config.Configs["Friends"];
128 if (friendsConfig != null) 121 if (friendsConfig != null)
129 { 122 {
130 m_Port = friendsConfig.GetInt("Port", m_Port); 123 int mPort = friendsConfig.GetInt("Port", 0);
131 124
132 string connector = friendsConfig.GetString("Connector", String.Empty); 125 string connector = friendsConfig.GetString("Connector", String.Empty);
133 Object[] args = new Object[] { config }; 126 Object[] args = new Object[] { config };
134 127
135 m_FriendsService = ServerUtils.LoadPlugin<IFriendsService>(connector, args); 128 m_FriendsService = ServerUtils.LoadPlugin<IFriendsService>(connector, args);
129 m_FriendsSimConnector = new FriendsSimConnector();
130
131 // Instantiate the request handler
132 IHttpServer server = MainServer.GetHttpServer((uint)mPort);
133 server.AddStreamHandler(new FriendsRequestHandler(this));
134
136 } 135 }
137 136
138 if (m_FriendsService == null) 137 if (m_FriendsService == null)
@@ -141,10 +140,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
141 throw new Exception("Connector load error"); 140 throw new Exception("Connector load error");
142 } 141 }
143 142
144 IHttpServer server = MainServer.GetHttpServer((uint)m_Port);
145
146 server.AddStreamHandler(this);
147
148 } 143 }
149 144
150 public void PostInitialise() 145 public void PostInitialise()
@@ -175,41 +170,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
175 m_Scenes.Remove(scene); 170 m_Scenes.Remove(scene);
176 } 171 }
177 172
178 public override byte[] Handle(string path, Stream requestData,
179 OSHttpRequest httpRequest, OSHttpResponse httpResponse)
180 {
181 StreamReader sr = new StreamReader(requestData);
182 string body = sr.ReadToEnd();
183 sr.Close();
184 body = body.Trim();
185
186 m_log.DebugFormat("[XXX]: query String: {0}", body);
187
188 try
189 {
190 Dictionary<string, object> request =
191 ServerUtils.ParseQueryString(body);
192
193 if (!request.ContainsKey("METHOD"))
194 return FailureResult();
195
196 string method = request["METHOD"].ToString();
197 request.Remove("METHOD");
198
199 switch (method)
200 {
201 case "TEST":
202 break;
203 }
204 }
205 catch (Exception e)
206 {
207 m_log.Debug("[FRIENDS]: Exception {0}" + e.ToString());
208 }
209
210 return FailureResult();
211 }
212
213 public string Name 173 public string Name
214 { 174 {
215 get { return "FriendsModule"; } 175 get { return "FriendsModule"; }
@@ -239,49 +199,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
239 return 0; 199 return 0;
240 } 200 }
241 201
242 private byte[] FailureResult()
243 {
244 return BoolResult(false);
245 }
246
247 private byte[] SuccessResult()
248 {
249 return BoolResult(true);
250 }
251
252 private byte[] BoolResult(bool value)
253 {
254 XmlDocument doc = new XmlDocument();
255
256 XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration,
257 "", "");
258
259 doc.AppendChild(xmlnode);
260
261 XmlElement rootElement = doc.CreateElement("", "ServerResponse",
262 "");
263
264 doc.AppendChild(rootElement);
265
266 XmlElement result = doc.CreateElement("", "RESULT", "");
267 result.AppendChild(doc.CreateTextNode(value.ToString()));
268
269 rootElement.AppendChild(result);
270
271 return DocToBytes(doc);
272 }
273
274 private byte[] DocToBytes(XmlDocument doc)
275 {
276 MemoryStream ms = new MemoryStream();
277 XmlTextWriter xw = new XmlTextWriter(ms, null);
278 xw.Formatting = Formatting.Indented;
279 doc.WriteTo(xw);
280 xw.Flush();
281
282 return ms.ToArray();
283 }
284
285 private void OnNewClient(IClientAPI client) 202 private void OnNewClient(IClientAPI client)
286 { 203 {
287 client.OnInstantMessage += OnInstantMessage; 204 client.OnInstantMessage += OnInstantMessage;
@@ -460,8 +377,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
460 UUID principalID = new UUID(im.fromAgentID); 377 UUID principalID = new UUID(im.fromAgentID);
461 UUID friendID = new UUID(im.toAgentID); 378 UUID friendID = new UUID(im.toAgentID);
462 379
463 // this user wants to be friends with the other user 380 // This user wants to be friends with the other user.
381 // Let's add both relations to the DB, but one of them is inactive (-1)
464 FriendsService.StoreFriend(principalID, friendID.ToString(), 1); 382 FriendsService.StoreFriend(principalID, friendID.ToString(), 1);
383 FriendsService.StoreFriend(friendID, principalID.ToString(), -1);
465 384
466 // Now let's ask the other user to be friends with this user 385 // Now let's ask the other user to be friends with this user
467 ForwardFriendshipOffer(principalID, friendID, im); 386 ForwardFriendshipOffer(principalID, friendID, im);
@@ -476,7 +395,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
476 // the prospective friend in this sim as root agent 395 // the prospective friend in this sim as root agent
477 friendClient.SendInstantMessage(im); 396 friendClient.SendInstantMessage(im);
478 // we're done 397 // we're done
479 return; 398 return ;
480 } 399 }
481 400
482 // The prospective friend is not here [as root]. Let's forward. 401 // The prospective friend is not here [as root]. Let's forward.
@@ -485,35 +404,123 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
485 if (friendSession != null) 404 if (friendSession != null)
486 { 405 {
487 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); 406 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
488 // ... 407 m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message);
489 // m_FriendsSimConnector.FriendshipOffered(region, agentID, friemdID, im.message);
490 } 408 }
409
410 // If the prospective friend is not online, he'll get the message upon login.
491 } 411 }
492 412
493 private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders) 413 private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders)
494 { 414 {
495 FriendsService.StoreFriend(agentID, friendID.ToString(), 1); 415 FriendsService.StoreFriend(agentID, friendID.ToString(), 1);
496 416
497 // TODO: Notify the new friend 417 //
418 // Notify the friend
419 //
420
421 IClientAPI friendClient = LocateClientObject(friendID);
422 if (friendClient != null)
423 {
424 // the prospective friend in this sim as root agent
425 GridInstantMessage im = new GridInstantMessage(client.Scene, client.AgentId, client.Name, friendID,
426 (byte)OpenMetaverse.InstantMessageDialog.FriendshipAccepted, client.AgentId.ToString(), false, Vector3.Zero);
427 friendClient.SendInstantMessage(im);
428 // we're done
429 return;
430 }
431
432 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
433 PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions);
434 if (friendSession != null)
435 {
436 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
437 m_FriendsSimConnector.FriendshipApproved(region, agentID, friendID);
438 }
498 } 439 }
499 440
500 private void OnDenyFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders) 441 private void OnDenyFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders)
501 { 442 {
502 // TODO: Notify the friend-wanna-be 443 FriendsService.Delete(agentID, friendID.ToString());
444 FriendsService.Delete(friendID, agentID.ToString());
445
446 //
447 // Notify the friend
448 //
449
450 IClientAPI friendClient = LocateClientObject(friendID);
451 if (friendClient != null)
452 {
453 // the prospective friend in this sim as root agent
454
455 GridInstantMessage im = new GridInstantMessage(client.Scene, client.AgentId, client.Name, friendID,
456 (byte)OpenMetaverse.InstantMessageDialog.FriendshipDeclined, client.AgentId.ToString(), false, Vector3.Zero);
457 friendClient.SendInstantMessage(im);
458 // we're done
459 return;
460 }
461
462 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
463 PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions);
464 if (friendSession != null)
465 {
466 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
467 m_FriendsSimConnector.FriendshipDenied(region, agentID, friendID);
468 }
503 } 469 }
504 470
505 private void OnTerminateFriendship(IClientAPI client, UUID agentID, UUID exfriendID) 471 private void OnTerminateFriendship(IClientAPI client, UUID agentID, UUID exfriendID)
506 { 472 {
507 FriendsService.Delete(agentID, exfriendID.ToString()); 473 FriendsService.Delete(agentID, exfriendID.ToString());
474 FriendsService.Delete(exfriendID, agentID.ToString());
475
476 client.SendTerminateFriend(exfriendID);
508 477
509 // TODO: Notify the exfriend 478 //
479 // Notify the friend
480 //
481
482 IClientAPI friendClient = LocateClientObject(exfriendID);
483 if (friendClient != null)
484 {
485 // the prospective friend in this sim as root agent
486 friendClient.SendTerminateFriend(exfriendID);
487 // we're done
488 return;
489 }
490
491 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { exfriendID.ToString() });
492 PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions);
493 if (friendSession != null)
494 {
495 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
496 m_FriendsSimConnector.FriendshipTerminated(region, agentID, exfriendID);
497 }
510 } 498 }
511 499
512 private void OnGrantUserRights(IClientAPI remoteClient, UUID requester, UUID target, int rights) 500 private void OnGrantUserRights(IClientAPI remoteClient, UUID requester, UUID target, int rights)
513 { 501 {
514 FriendsService.StoreFriend(requester, target.ToString(), rights); 502 FriendsService.StoreFriend(requester, target.ToString(), rights);
515 503
516 // TODO: Notify the friend 504 //
505 // Notify the friend
506 //
507
508 IClientAPI friendClient = LocateClientObject(target);
509 if (friendClient != null)
510 {
511 // the prospective friend in this sim as root agent
512 //friendClient.???;
513 // we're done
514 return;
515 }
516
517 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { target.ToString() });
518 PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions);
519 if (friendSession != null)
520 {
521 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
522 m_FriendsSimConnector.GrantRights(region, requester, target);
523 }
517 } 524 }
518 } 525 }
519} 526}
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs
new file mode 100644
index 0000000..cde54ed
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs
@@ -0,0 +1,134 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32using System.Xml;
33
34using OpenSim.Framework;
35using OpenSim.Server.Base;
36using OpenSim.Framework.Servers.HttpServer;
37
38using OpenMetaverse;
39using log4net;
40
41namespace OpenSim.Region.CoreModules.Avatar.Friends
42{
43 public class FriendsRequestHandler : BaseStreamHandler
44 {
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 private FriendsModule m_FriendsModule;
48
49 public FriendsRequestHandler(FriendsModule fmodule)
50 : base("POST", "/friends")
51 {
52 m_FriendsModule = fmodule;
53 }
54
55 public override byte[] Handle(string path, Stream requestData,
56 OSHttpRequest httpRequest, OSHttpResponse httpResponse)
57 {
58 StreamReader sr = new StreamReader(requestData);
59 string body = sr.ReadToEnd();
60 sr.Close();
61 body = body.Trim();
62
63 m_log.DebugFormat("[XXX]: query String: {0}", body);
64
65 try
66 {
67 Dictionary<string, object> request =
68 ServerUtils.ParseQueryString(body);
69
70 if (!request.ContainsKey("METHOD"))
71 return FailureResult();
72
73 string method = request["METHOD"].ToString();
74 request.Remove("METHOD");
75
76 switch (method)
77 {
78 case "TEST":
79 break;
80 }
81 }
82 catch (Exception e)
83 {
84 m_log.Debug("[FRIENDS]: Exception {0}" + e.ToString());
85 }
86
87 return FailureResult();
88 }
89
90 private byte[] FailureResult()
91 {
92 return BoolResult(false);
93 }
94
95 private byte[] SuccessResult()
96 {
97 return BoolResult(true);
98 }
99
100 private byte[] BoolResult(bool value)
101 {
102 XmlDocument doc = new XmlDocument();
103
104 XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration,
105 "", "");
106
107 doc.AppendChild(xmlnode);
108
109 XmlElement rootElement = doc.CreateElement("", "ServerResponse",
110 "");
111
112 doc.AppendChild(rootElement);
113
114 XmlElement result = doc.CreateElement("", "RESULT", "");
115 result.AppendChild(doc.CreateTextNode(value.ToString()));
116
117 rootElement.AppendChild(result);
118
119 return DocToBytes(doc);
120 }
121
122 private byte[] DocToBytes(XmlDocument doc)
123 {
124 MemoryStream ms = new MemoryStream();
125 XmlTextWriter xw = new XmlTextWriter(ms, null);
126 xw.Formatting = Formatting.Indented;
127 doc.WriteTo(xw);
128 xw.Flush();
129
130 return ms.ToArray();
131 }
132
133 }
134}
diff --git a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs
new file mode 100644
index 0000000..94746ef
--- /dev/null
+++ b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs
@@ -0,0 +1,66 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30
31using OpenSim.Services.Interfaces;
32using GridRegion = OpenSim.Services.Interfaces.GridRegion;
33
34using OpenMetaverse;
35
36namespace OpenSim.Services.Connectors.Friends
37{
38 public class FriendsSimConnector
39 {
40
41 public bool FriendshipOffered(GridRegion region, UUID userID, UUID friendID, string message)
42 {
43 return true;
44 }
45
46 public bool FriendshipApproved(GridRegion region, UUID userID, UUID friendID)
47 {
48 return true;
49 }
50
51 public bool FriendshipDenied(GridRegion region, UUID userID, UUID friendID)
52 {
53 return true;
54 }
55
56 public bool FriendshipTerminated(GridRegion region, UUID userID, UUID friendID)
57 {
58 return true;
59 }
60
61 public bool GrantRights(GridRegion region, UUID requester, UUID target)
62 {
63 return true;
64 }
65 }
66}