HGGroupsService.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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 bool 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. {
  124. return RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID);
  125. }
  126. else
  127. {
  128. m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]);
  129. return false;
  130. }
  131. }
  132. else
  133. {
  134. m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", AgentID);
  135. return false;
  136. }
  137. }
  138. public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string groupName, string token)
  139. {
  140. // check the token
  141. if (!VerifyToken(GroupID, RequestingAgentID, token))
  142. return null;
  143. ExtendedGroupRecord grec;
  144. if (GroupID == UUID.Zero)
  145. grec = GetGroupRecord(RequestingAgentID, groupName);
  146. else
  147. grec = GetGroupRecord(RequestingAgentID, GroupID);
  148. if (grec != null)
  149. FillFounderUUI(grec);
  150. return grec;
  151. }
  152. public List<ExtendedGroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID, string token)
  153. {
  154. if (!VerifyToken(GroupID, RequestingAgentID, token))
  155. return new List<ExtendedGroupMembersData>();
  156. List<ExtendedGroupMembersData> members = GetGroupMembers(RequestingAgentID, GroupID);
  157. // convert UUIDs to UUIs
  158. members.ForEach(delegate (ExtendedGroupMembersData m)
  159. {
  160. if (m.AgentID.ToString().Length == 36) // UUID
  161. {
  162. UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.AgentID));
  163. if (account != null)
  164. m.AgentID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
  165. }
  166. });
  167. return members;
  168. }
  169. public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID, string token)
  170. {
  171. if (!VerifyToken(GroupID, RequestingAgentID, token))
  172. return new List<GroupRolesData>();
  173. return GetGroupRoles(RequestingAgentID, GroupID);
  174. }
  175. public List<ExtendedGroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token)
  176. {
  177. if (!VerifyToken(GroupID, RequestingAgentID, token))
  178. return new List<ExtendedGroupRoleMembersData>();
  179. List<ExtendedGroupRoleMembersData> rolemembers = GetGroupRoleMembers(RequestingAgentID, GroupID);
  180. // convert UUIDs to UUIs
  181. rolemembers.ForEach(delegate(ExtendedGroupRoleMembersData m)
  182. {
  183. if (m.MemberID.ToString().Length == 36) // UUID
  184. {
  185. UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.MemberID));
  186. if (account != null)
  187. m.MemberID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
  188. }
  189. });
  190. return rolemembers;
  191. }
  192. public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
  193. bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
  194. {
  195. // check that the group proxy exists
  196. ExtendedGroupRecord grec = GetGroupRecord(RequestingAgentID, groupID);
  197. if (grec == null)
  198. {
  199. m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to non-existent group proxy");
  200. return false;
  201. }
  202. // check that the group is remote
  203. if (grec.ServiceLocation == string.Empty)
  204. {
  205. m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to local (non-proxy) group");
  206. return false;
  207. }
  208. // check that there isn't already a notice with the same ID
  209. if (GetGroupNotice(RequestingAgentID, noticeID) != null)
  210. {
  211. m_log.DebugFormat("[Groups.HGGroupsService]: a notice with the same ID already exists", grec.ServiceLocation);
  212. return false;
  213. }
  214. // This has good intentions (security) but it will potentially DDS the origin...
  215. // We'll need to send a proof along with the message. Maybe encrypt the message
  216. // using key pairs
  217. //
  218. //// check that the notice actually exists in the origin
  219. //GroupsServiceHGConnector c = new GroupsServiceHGConnector(grec.ServiceLocation);
  220. //if (!c.VerifyNotice(noticeID, groupID))
  221. //{
  222. // m_log.DebugFormat("[Groups.HGGroupsService]: notice does not exist at origin {0}", grec.ServiceLocation);
  223. // return false;
  224. //}
  225. // ok, we're good!
  226. return _AddNotice(groupID, noticeID, fromName, subject, message, hasAttachment, attType, attName, attItemID, attOwnerID);
  227. }
  228. public bool VerifyNotice(UUID noticeID, UUID groupID)
  229. {
  230. GroupNoticeInfo notice = GetGroupNotice(string.Empty, noticeID);
  231. if (notice == null)
  232. return false;
  233. if (notice.GroupID != groupID)
  234. return false;
  235. return true;
  236. }
  237. #endregion
  238. private void InviteToGroup(string fromName, UUID groupID, UUID invitedAgentID, string groupName)
  239. {
  240. // Todo: Security check, probably also want to send some kind of notification
  241. UUID InviteID = UUID.Random();
  242. if (AddAgentToGroupInvite(InviteID, groupID, invitedAgentID.ToString()))
  243. {
  244. Guid inviteUUID = InviteID.Guid;
  245. GridInstantMessage msg = new GridInstantMessage();
  246. msg.imSessionID = inviteUUID;
  247. // msg.fromAgentID = agentID.Guid;
  248. msg.fromAgentID = groupID.Guid;
  249. msg.toAgentID = invitedAgentID.Guid;
  250. //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
  251. msg.timestamp = 0;
  252. msg.fromAgentName = fromName;
  253. msg.message = string.Format("Please confirm your acceptance to join group {0}.", groupName);
  254. msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
  255. msg.fromGroup = true;
  256. msg.offline = (byte)0;
  257. msg.ParentEstateID = 0;
  258. msg.Position = Vector3.Zero;
  259. msg.RegionID = UUID.Zero.Guid;
  260. msg.binaryBucket = new byte[20];
  261. string reason = string.Empty;
  262. m_OfflineIM.StoreMessage(msg, out reason);
  263. }
  264. }
  265. private bool AddAgentToGroupInvite(UUID inviteID, UUID groupID, string agentID)
  266. {
  267. // Check whether the invitee is already a member of the group
  268. MembershipData m = m_Database.RetrieveMember(groupID, agentID);
  269. if (m != null)
  270. return false;
  271. // Check whether there are pending invitations and delete them
  272. InvitationData invite = m_Database.RetrieveInvitation(groupID, agentID);
  273. if (invite != null)
  274. m_Database.DeleteInvite(invite.InviteID);
  275. invite = new InvitationData();
  276. invite.InviteID = inviteID;
  277. invite.PrincipalID = agentID;
  278. invite.GroupID = groupID;
  279. invite.RoleID = UUID.Zero;
  280. invite.Data = new Dictionary<string, string>();
  281. return m_Database.StoreInvitation(invite);
  282. }
  283. private void FillFounderUUI(ExtendedGroupRecord grec)
  284. {
  285. UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, grec.FounderID);
  286. if (account != null)
  287. grec.FounderUUI = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
  288. }
  289. private bool VerifyToken(UUID groupID, string agentID, string token)
  290. {
  291. // check the token
  292. MembershipData membership = m_Database.RetrieveMember(groupID, agentID);
  293. if (membership != null)
  294. {
  295. if (token != string.Empty && token.Equals(membership.Data["AccessToken"]))
  296. return true;
  297. else
  298. m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]);
  299. }
  300. else
  301. m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", agentID);
  302. return false;
  303. }
  304. }
  305. }