HGGroupsService.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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. using System;
  28. using System.Collections.Generic;
  29. using System.Reflection;
  30. using System.Timers;
  31. using log4net;
  32. using Nini.Config;
  33. using OpenMetaverse;
  34. using OpenSim.Data;
  35. using OpenSim.Framework;
  36. using OpenSim.Services.Interfaces;
  37. namespace OpenSim.Groups
  38. {
  39. public class HGGroupsService : GroupsService
  40. {
  41. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  42. private IOfflineIMService m_OfflineIM;
  43. private IUserAccountService m_UserAccounts;
  44. private string m_HomeURI;
  45. public HGGroupsService(IConfigSource config, IOfflineIMService im, IUserAccountService users, string homeURI)
  46. : base(config, string.Empty)
  47. {
  48. m_OfflineIM = im;
  49. m_UserAccounts = users;
  50. m_HomeURI = homeURI;
  51. if (!m_HomeURI.EndsWith("/"))
  52. m_HomeURI += "/";
  53. }
  54. #region HG specific operations
  55. public bool CreateGroupProxy(string RequestingAgentID, string agentID, string accessToken, UUID groupID, string serviceLocation, string name, out string reason)
  56. {
  57. reason = string.Empty;
  58. Uri uri = null;
  59. try
  60. {
  61. uri = new Uri(serviceLocation);
  62. }
  63. catch (UriFormatException)
  64. {
  65. reason = "Bad location for group proxy";
  66. return false;
  67. }
  68. // Check if it already exists
  69. GroupData grec = m_Database.RetrieveGroup(groupID);
  70. if (grec == null ||
  71. (grec != null && grec.Data["Location"] != string.Empty && grec.Data["Location"].ToLower() != serviceLocation.ToLower()))
  72. {
  73. // Create the group
  74. grec = new GroupData();
  75. grec.GroupID = groupID;
  76. grec.Data = new Dictionary<string, string>();
  77. grec.Data["Name"] = name + " @ " + uri.Authority;
  78. grec.Data["Location"] = serviceLocation;
  79. grec.Data["Charter"] = string.Empty;
  80. grec.Data["InsigniaID"] = UUID.Zero.ToString();
  81. grec.Data["FounderID"] = UUID.Zero.ToString();
  82. grec.Data["MembershipFee"] = "0";
  83. grec.Data["OpenEnrollment"] = "0";
  84. grec.Data["ShowInList"] = "0";
  85. grec.Data["AllowPublish"] = "0";
  86. grec.Data["MaturePublish"] = "0";
  87. grec.Data["OwnerRoleID"] = UUID.Zero.ToString();
  88. if (!m_Database.StoreGroup(grec))
  89. return false;
  90. }
  91. if (grec.Data["Location"] == string.Empty)
  92. {
  93. reason = "Cannot add proxy membership to non-proxy group";
  94. return false;
  95. }
  96. UUID uid = UUID.Zero;
  97. string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
  98. Util.ParseUniversalUserIdentifier(RequestingAgentID, out uid, out url, out first, out last, out tmp);
  99. string fromName = first + "." + last + "@" + url;
  100. // Invite to group again
  101. InviteToGroup(fromName, groupID, new UUID(agentID), grec.Data["Name"]);
  102. // Stick the proxy membership in the DB already
  103. // we'll delete it if the agent declines the invitation
  104. MembershipData membership = new MembershipData();
  105. membership.PrincipalID = agentID;
  106. membership.GroupID = groupID;
  107. membership.Data = new Dictionary<string, string>();
  108. membership.Data["SelectedRoleID"] = UUID.Zero.ToString();
  109. membership.Data["Contribution"] = "0";
  110. membership.Data["ListInProfile"] = "1";
  111. membership.Data["AcceptNotices"] = "1";
  112. membership.Data["AccessToken"] = accessToken;
  113. m_Database.StoreMember(membership);
  114. return true;
  115. }
  116. public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID, string token)
  117. {
  118. // check the token
  119. MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID);
  120. if (membership != null)
  121. {
  122. if (token != string.Empty && token.Equals(membership.Data["AccessToken"]))
  123. RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID);
  124. else
  125. m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]);
  126. }
  127. else
  128. m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", AgentID);
  129. }
  130. public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string groupName, string token)
  131. {
  132. // check the token
  133. if (!VerifyToken(GroupID, RequestingAgentID, token))
  134. return null;
  135. ExtendedGroupRecord grec;
  136. if (GroupID == UUID.Zero)
  137. grec = GetGroupRecord(RequestingAgentID, groupName);
  138. else
  139. grec = GetGroupRecord(RequestingAgentID, GroupID);
  140. if (grec != null)
  141. FillFounderUUI(grec);
  142. return grec;
  143. }
  144. public List<ExtendedGroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID, string token)
  145. {
  146. if (!VerifyToken(GroupID, RequestingAgentID, token))
  147. return new List<ExtendedGroupMembersData>();
  148. List<ExtendedGroupMembersData> members = GetGroupMembers(RequestingAgentID, GroupID);
  149. // convert UUIDs to UUIs
  150. members.ForEach(delegate (ExtendedGroupMembersData m)
  151. {
  152. if (m.AgentID.ToString().Length == 36) // UUID
  153. {
  154. UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.AgentID));
  155. if (account != null)
  156. m.AgentID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
  157. }
  158. });
  159. return members;
  160. }
  161. public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID, string token)
  162. {
  163. if (!VerifyToken(GroupID, RequestingAgentID, token))
  164. return new List<GroupRolesData>();
  165. return GetGroupRoles(RequestingAgentID, GroupID);
  166. }
  167. public List<ExtendedGroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token)
  168. {
  169. if (!VerifyToken(GroupID, RequestingAgentID, token))
  170. return new List<ExtendedGroupRoleMembersData>();
  171. List<ExtendedGroupRoleMembersData> rolemembers = GetGroupRoleMembers(RequestingAgentID, GroupID);
  172. // convert UUIDs to UUIs
  173. rolemembers.ForEach(delegate(ExtendedGroupRoleMembersData m)
  174. {
  175. if (m.MemberID.ToString().Length == 36) // UUID
  176. {
  177. UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.MemberID));
  178. if (account != null)
  179. m.MemberID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
  180. }
  181. });
  182. return rolemembers;
  183. }
  184. public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
  185. bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
  186. {
  187. // check that the group proxy exists
  188. ExtendedGroupRecord grec = GetGroupRecord(RequestingAgentID, groupID);
  189. if (grec == null)
  190. {
  191. m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to non-existent group proxy");
  192. return false;
  193. }
  194. // check that the group is remote
  195. if (grec.ServiceLocation == string.Empty)
  196. {
  197. m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to local (non-proxy) group");
  198. return false;
  199. }
  200. // check that there isn't already a notice with the same ID
  201. if (GetGroupNotice(RequestingAgentID, noticeID) != null)
  202. {
  203. m_log.DebugFormat("[Groups.HGGroupsService]: a notice with the same ID already exists", grec.ServiceLocation);
  204. return false;
  205. }
  206. // This has good intentions (security) but it will potentially DDS the origin...
  207. // We'll need to send a proof along with the message. Maybe encrypt the message
  208. // using key pairs
  209. //
  210. //// check that the notice actually exists in the origin
  211. //GroupsServiceHGConnector c = new GroupsServiceHGConnector(grec.ServiceLocation);
  212. //if (!c.VerifyNotice(noticeID, groupID))
  213. //{
  214. // m_log.DebugFormat("[Groups.HGGroupsService]: notice does not exist at origin {0}", grec.ServiceLocation);
  215. // return false;
  216. //}
  217. // ok, we're good!
  218. return _AddNotice(groupID, noticeID, fromName, subject, message, hasAttachment, attType, attName, attItemID, attOwnerID);
  219. }
  220. public bool VerifyNotice(UUID noticeID, UUID groupID)
  221. {
  222. GroupNoticeInfo notice = GetGroupNotice(string.Empty, noticeID);
  223. if (notice == null)
  224. return false;
  225. if (notice.GroupID != groupID)
  226. return false;
  227. return true;
  228. }
  229. #endregion
  230. private void InviteToGroup(string fromName, UUID groupID, UUID invitedAgentID, string groupName)
  231. {
  232. // Todo: Security check, probably also want to send some kind of notification
  233. UUID InviteID = UUID.Random();
  234. if (AddAgentToGroupInvite(InviteID, groupID, invitedAgentID.ToString()))
  235. {
  236. Guid inviteUUID = InviteID.Guid;
  237. GridInstantMessage msg = new GridInstantMessage();
  238. msg.imSessionID = inviteUUID;
  239. // msg.fromAgentID = agentID.Guid;
  240. msg.fromAgentID = groupID.Guid;
  241. msg.toAgentID = invitedAgentID.Guid;
  242. //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
  243. msg.timestamp = 0;
  244. msg.fromAgentName = fromName;
  245. msg.message = string.Format("Please confirm your acceptance to join group {0}.", groupName);
  246. msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
  247. msg.fromGroup = true;
  248. msg.offline = (byte)0;
  249. msg.ParentEstateID = 0;
  250. msg.Position = Vector3.Zero;
  251. msg.RegionID = UUID.Zero.Guid;
  252. msg.binaryBucket = new byte[20];
  253. string reason = string.Empty;
  254. m_OfflineIM.StoreMessage(msg, out reason);
  255. }
  256. }
  257. private bool AddAgentToGroupInvite(UUID inviteID, UUID groupID, string agentID)
  258. {
  259. // Check whether the invitee is already a member of the group
  260. MembershipData m = m_Database.RetrieveMember(groupID, agentID);
  261. if (m != null)
  262. return false;
  263. // Check whether there are pending invitations and delete them
  264. InvitationData invite = m_Database.RetrieveInvitation(groupID, agentID);
  265. if (invite != null)
  266. m_Database.DeleteInvite(invite.InviteID);
  267. invite = new InvitationData();
  268. invite.InviteID = inviteID;
  269. invite.PrincipalID = agentID;
  270. invite.GroupID = groupID;
  271. invite.RoleID = UUID.Zero;
  272. invite.Data = new Dictionary<string, string>();
  273. return m_Database.StoreInvitation(invite);
  274. }
  275. private void FillFounderUUI(ExtendedGroupRecord grec)
  276. {
  277. UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, grec.FounderID);
  278. if (account != null)
  279. grec.FounderUUI = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
  280. }
  281. private bool VerifyToken(UUID groupID, string agentID, string token)
  282. {
  283. // check the token
  284. MembershipData membership = m_Database.RetrieveMember(groupID, agentID);
  285. if (membership != null)
  286. {
  287. if (token != string.Empty && token.Equals(membership.Data["AccessToken"]))
  288. return true;
  289. else
  290. m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]);
  291. }
  292. else
  293. m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", agentID);
  294. return false;
  295. }
  296. }
  297. }