/*
* Copyright (c) 2006-2014, openmetaverse.org
* All rights reserved.
*
* - Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Neither the name of the openmetaverse.org nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Http;
namespace OpenMetaverse
{
public class RegistrationApi
{
const int REQUEST_TIMEOUT = 1000 * 100;
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()
{
// build post data
byte[] postData = Encoding.ASCII.GetBytes(
String.Format("first_name={0}&last_name={1}&password={2}", _userInfo.FirstName, _userInfo.LastName,
_userInfo.Password));
CapsClient request = new CapsClient(RegistrationApiCaps);
request.OnComplete += new CapsClient.CompleteCallback(GatherCapsResponse);
request.BeginGetResponse(postData, "application/x-www-form-urlencoded", REQUEST_TIMEOUT);
}
private void GatherCapsResponse(CapsClient client, OSD response, Exception error)
{
if (response is OSDMap)
{
OSDMap respTable = (OSDMap)response;
// parse
_caps = new RegistrationCaps();
_caps.CreateUser = respTable["create_user"].AsUri();
_caps.CheckName = respTable["check_name"].AsUri();
_caps.GetLastNames = respTable["get_last_names"].AsUri();
_caps.GetErrorCodes = respTable["get_error_codes"].AsUri();
// finalize
_initializing++;
GatherErrorMessages();
}
}
private void GatherErrorMessages()
{
if (_caps.GetErrorCodes == null)
throw new InvalidOperationException("access denied"); // this should work even for not-approved users
CapsClient request = new CapsClient(_caps.GetErrorCodes);
request.OnComplete += new CapsClient.CompleteCallback(GatherErrorMessagesResponse);
request.BeginGetResponse(REQUEST_TIMEOUT);
}
private void GatherErrorMessagesResponse(CapsClient client, OSD response, Exception error)
{
if (response is OSDMap)
{
// parse
//FIXME: wtf?
//foreach (KeyValuePair error in (Dictionary)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");
CapsClient request = new CapsClient(_caps.GetLastNames);
request.OnComplete += new CapsClient.CompleteCallback(GatherLastNamesResponse);
request.BeginGetResponse(REQUEST_TIMEOUT);
// FIXME: Block
}
private void GatherLastNamesResponse(CapsClient client, OSD response, Exception error)
{
if (response is OSDMap)
{
//LLSDMap respTable = (LLSDMap)response;
//FIXME:
//_lastNames = new List(respTable.Count);
//for (Dictionary.Enumerator it = respTable.GetEnumerator(); it.MoveNext(); )
//{
// LastName ln = new LastName();
// ln.ID = int.Parse(it.Current.Key.ToString());
// ln.Name = it.Current.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
OSDMap query = new OSDMap();
query.Add("username", OSD.FromString(firstName));
query.Add("last_name_id", OSD.FromInteger(lastName.ID));
//byte[] postData = OSDParser.SerializeXmlBytes(query);
CapsClient request = new CapsClient(_caps.CheckName);
request.OnComplete += new CapsClient.CompleteCallback(CheckNameResponse);
request.BeginGetResponse(REQUEST_TIMEOUT);
// FIXME:
return false;
}
private void CheckNameResponse(CapsClient client, OSD response, Exception error)
{
if (response.Type == OSDType.Boolean)
{
// 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 UUID 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
OSDMap query = new OSDMap();
query.Add("username", OSD.FromString(user.FirstName));
query.Add("last_name_id", OSD.FromInteger(user.LastName.ID));
query.Add("email", OSD.FromString(user.Email));
query.Add("password", OSD.FromString(user.Password));
query.Add("dob", OSD.FromString(user.Birthdate.ToString("yyyy-MM-dd")));
if (user.LimitedToEstate != null)
query.Add("limited_to_estate", OSD.FromInteger(user.LimitedToEstate.Value));
if (!string.IsNullOrEmpty(user.StartRegionName))
query.Add("start_region_name", OSD.FromInteger(user.LimitedToEstate.Value));
if (user.StartLocation != null)
{
query.Add("start_local_x", OSD.FromReal(user.StartLocation.Value.X));
query.Add("start_local_y", OSD.FromReal(user.StartLocation.Value.Y));
query.Add("start_local_z", OSD.FromReal(user.StartLocation.Value.Z));
}
if (user.StartLookAt != null)
{
query.Add("start_look_at_x", OSD.FromReal(user.StartLookAt.Value.X));
query.Add("start_look_at_y", OSD.FromReal(user.StartLookAt.Value.Y));
query.Add("start_look_at_z", OSD.FromReal(user.StartLookAt.Value.Z));
}
//byte[] postData = OSDParser.SerializeXmlBytes(query);
// Make the request
CapsClient request = new CapsClient(_caps.CreateUser);
request.OnComplete += new CapsClient.CompleteCallback(CreateUserResponse);
request.BeginGetResponse(REQUEST_TIMEOUT);
// FIXME: Block
return UUID.Zero;
}
private void CreateUserResponse(CapsClient client, OSD response, Exception error)
{
if (response is OSDMap)
{
// everything is okay
// FIXME:
//return new UUID(((Dictionary)response)["agent_id"].ToString());
}
else
{
// an error happened
OSDArray al = (OSDArray)response;
StringBuilder sb = new StringBuilder();
foreach (OSD ec in al)
{
if (sb.Length > 0)
sb.Append("; ");
sb.Append(_errors[ec.AsInteger()]);
}
// FIXME:
//throw new Exception("failed to create user: " + sb.ToString());
}
}
}
}