using System; using System.Collections; using System.Collections.Generic; using System.Threading; using System.Text; namespace libsecondlife { public class RegistrationApi { private struct UserInfo { public string FirstName; public string LastName; public string Password; } private struct RegistrationCaps { public Uri CreateUser; public Uri CheckName; public Uri GetLastNames; public Uri GetErrorCodes; } public struct LastName { public int ID; public string Name; } /// /// see https://secure-web6.secondlife.com/developers/third_party_reg/#service_create_user or /// https://wiki.secondlife.com/wiki/RegAPIDoc for description /// public class CreateUserParam { public string FirstName; public LastName LastName; public string Email; public string Password; public DateTime Birthdate; // optional: public Nullable LimitedToEstate; public string StartRegionName; public Nullable StartLocation; public Nullable StartLookAt; } private UserInfo _userInfo; private RegistrationCaps _caps; private int _initializing; private List _lastNames = new List(); private Dictionary _errors = new Dictionary(); public bool Initializing { get { System.Diagnostics.Debug.Assert(_initializing <= 0); return (_initializing < 0); } } public List LastNames { get { lock (_lastNames) { if (_lastNames.Count <= 0) GatherLastNames(); } return _lastNames; } } public RegistrationApi(string firstName, string lastName, string password) { _initializing = -2; _userInfo = new UserInfo(); _userInfo.FirstName = firstName; _userInfo.LastName = lastName; _userInfo.Password = password; GatherCaps(); } public void WaitForInitialization() { while (Initializing) System.Threading.Thread.Sleep(10); } public Uri RegistrationApiCaps { get { return new Uri("https://cap.secondlife.com/get_reg_capabilities"); } } private void GatherCaps() { CapsRequest request = new CapsRequest(RegistrationApiCaps.AbsoluteUri); request.OnCapsResponse += new CapsRequest.CapsResponseCallback(GatherCapsResponse); // build post data byte[] postData = Encoding.ASCII.GetBytes( String.Format("first_name={0}&last_name={1}&password={2}", _userInfo.FirstName, _userInfo.LastName, _userInfo.Password)); // send request.MakeRequest(postData); } private void GatherCapsResponse(object response, HttpRequestState state) { if (response is Hashtable) { Hashtable respTable = (Hashtable)response; // parse _caps = new RegistrationCaps(); _caps.CreateUser = new Uri((string)respTable["create_user"]); _caps.CheckName = new Uri((string)respTable["check_name"]); _caps.GetLastNames = new Uri((string)respTable["get_last_names"]); _caps.GetErrorCodes = new Uri((string)respTable["get_error_codes"]); // finalize _initializing++; GatherErrorMessages(); } } private void GatherErrorMessages() { if (_caps.GetErrorCodes == null) throw new InvalidOperationException("access denied"); // this should work even for not-approved users CapsRequest request = new CapsRequest(_caps.GetErrorCodes.AbsoluteUri); request.OnCapsResponse += new CapsRequest.CapsResponseCallback(GatherErrorMessagesResponse); request.MakeRequest(); } private void GatherErrorMessagesResponse(object response, HttpRequestState state) { if (response is Hashtable) { // parse foreach (ArrayList error in (Hashtable)response) { StringBuilder sb = new StringBuilder(); sb.Append(error[1]); sb.Append(" ("); sb.Append(error[0]); sb.Append("): "); sb.Append(error[2]); _errors.Add((int)error[0], sb.ToString()); } // finalize _initializing++; } } public void GatherLastNames() { if (Initializing) throw new InvalidOperationException("still initializing"); if (_caps.GetLastNames == null) throw new InvalidOperationException("access denied: only approved developers have access to the registration api"); CapsRequest request = new CapsRequest(_caps.GetLastNames.AbsoluteUri); request.OnCapsResponse += new CapsRequest.CapsResponseCallback(GatherLastNamesResponse); request.MakeRequest(); // FIXME: Block } private void GatherLastNamesResponse(object response, HttpRequestState state) { if (response is Hashtable) { Hashtable respTable = (Hashtable)response; _lastNames = new List(respTable.Count); for (IDictionaryEnumerator it = respTable.GetEnumerator(); it.MoveNext(); ) { LastName ln = new LastName(); ln.ID = int.Parse(it.Key.ToString()); ln.Name = it.Value.ToString(); _lastNames.Add(ln); } _lastNames.Sort(new Comparison(delegate(LastName a, LastName b) { return a.Name.CompareTo(b.Name); })); } } public bool CheckName(string firstName, LastName lastName) { if (Initializing) throw new InvalidOperationException("still initializing"); if (_caps.CheckName == null) throw new InvalidOperationException("access denied; only approved developers have access to the registration api"); // Create the POST data Hashtable query = new Hashtable(); query.Add("username", firstName); query.Add("last_name_id", lastName.ID); byte[] postData = LLSD.LLSDSerialize(query); CapsRequest request = new CapsRequest(_caps.CheckName.AbsoluteUri); request.OnCapsResponse += new CapsRequest.CapsResponseCallback(CheckNameResponse); request.MakeRequest(postData); // FIXME: return false; } private void CheckNameResponse(object response, HttpRequestState state) { if (response is bool) { // FIXME: //(bool)response; } else { // FIXME: } } /// /// Returns the new user ID or throws an exception containing the error code /// The error codes can be found here: https://wiki.secondlife.com/wiki/RegAPIError /// /// New user account to create /// The UUID of the new user account public LLUUID CreateUser(CreateUserParam user) { if (Initializing) throw new InvalidOperationException("still initializing"); if (_caps.CreateUser == null) throw new InvalidOperationException("access denied; only approved developers have access to the registration api"); // Create the POST data Hashtable query = new Hashtable(); query.Add("username", user.FirstName); query.Add("last_name_id", user.LastName.ID); query.Add("email", user.Email); query.Add("password", user.Password); query.Add("dob", user.Birthdate.ToString("yyyy-MM-dd")); if (user.LimitedToEstate != null) query.Add("limited_to_estate", user.LimitedToEstate.Value); if (!string.IsNullOrEmpty(user.StartRegionName)) query.Add("start_region_name", user.LimitedToEstate.Value); if (user.StartLocation != null) { query.Add("start_local_x", user.StartLocation.Value.X); query.Add("start_local_y", user.StartLocation.Value.Y); query.Add("start_local_z", user.StartLocation.Value.Z); } if (user.StartLookAt != null) { query.Add("start_look_at_x", user.StartLookAt.Value.X); query.Add("start_look_at_y", user.StartLookAt.Value.Y); query.Add("start_look_at_z", user.StartLookAt.Value.Z); } byte[] postData = LLSD.LLSDSerialize(query); // Make the request CapsRequest request = new CapsRequest(_caps.CreateUser.AbsoluteUri); request.OnCapsResponse += new CapsRequest.CapsResponseCallback(CreateUserResponse); request.MakeRequest(postData); // FIXME: Block return LLUUID.Zero; } private void CreateUserResponse(object response, HttpRequestState state) { if (response is Hashtable) { // everything is okay // FIXME: //return new LLUUID(((Hashtable)response)["agent_id"].ToString()); } else { // an error happened ArrayList al = (ArrayList)response; StringBuilder sb = new StringBuilder(); foreach (int ec in al) { if (sb.Length > 0) sb.Append("; "); sb.Append(_errors[ec]); } // FIXME: //throw new Exception("failed to create user: " + sb.ToString()); } } } }