commit e7ad569246864a264ef1684edf61db86ac39fdc9 Author: XlXi Date: Sun Apr 16 13:03:43 2023 -0400 Publication of source code. diff --git a/SharedCode/Autoloader.php b/SharedCode/Autoloader.php new file mode 100644 index 0000000..1c38f45 --- /dev/null +++ b/SharedCode/Autoloader.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/SharedCode/Global.php b/SharedCode/Global.php new file mode 100644 index 0000000..0db7921 --- /dev/null +++ b/SharedCode/Global.php @@ -0,0 +1,4 @@ +cacheSettings = $settings; + $this->entityName = $entityName; + $this->boolValue = $boolValue; + } + + function CacheSettings() { + return $cacheSettings; + } + + function EntityName() { + return $entityName; + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Caching/CacheabilitySettings.php b/SharedCode/Roblox/Caching/CacheabilitySettings.php new file mode 100644 index 0000000..99a2d93 --- /dev/null +++ b/SharedCode/Roblox/Caching/CacheabilitySettings.php @@ -0,0 +1,27 @@ + $value) { + if (isset($this->$name)) { + // TODO: This makes potential private classes modifiable from outside the class + $this->$name = $value; + }else { + // One of the given cacheability settings doesn't exist + throw new \Exception("Invalid cacheability setting: \"$name\""); + } + } + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Caching/LocalCache.php b/SharedCode/Roblox/Caching/LocalCache.php new file mode 100644 index 0000000..bd7b9f4 --- /dev/null +++ b/SharedCode/Roblox/Caching/LocalCache.php @@ -0,0 +1,20 @@ +, , >*/(/*ICacheInfo*/ $cacheInfo, /*String*/ $entityIdLookup, /*Get`1*/ $getter) { + // Call the $getter function + call_user_func($getter, $entityIdLookup); + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Data/DBWireup/BIZ.cs b/SharedCode/Roblox/Data/DBWireup/BIZ.cs new file mode 100644 index 0000000..e1b2992 --- /dev/null +++ b/SharedCode/Roblox/Data/DBWireup/BIZ.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using Roblox; +using Roblox.Caching; +using Roblox.Common; +using Roblox.Data.Interfaces; + +namespace +{ + internal class : IRobloxEntity<, > + { + private _EntityDAL; + + public ID + { + get { return _EntityDAL.ID; } + } + + + + public () + { + _EntityDAL = new (); + } + + internal void Delete() + { + EntityHelper.DeleteEntity( + this, + _EntityDAL.Delete + ); + } + internal void Save() + { + EntityHelper.SaveEntity( + this, + () => + { + _EntityDAL.Created = DateTime.Now; + _EntityDAL.Updated = _EntityDAL.Created; + _EntityDAL.Insert(); + }, + () => + { + _EntityDAL.Updated = DateTime.Now; + _EntityDAL.Update(); + } + ); + } + + private static CreateNew() + { + var entity = new (); + + entity.Save(); + + return entity; + } + internal static Get( id) + { + return EntityHelper.GetEntity<, , >( + EntityCacheInfo, + id, + () => .Get(id) + ); + } + + #$@#@ - Unique ID used to find this section, don't change it. 599W#%2 + internal static ICollection<> GetsBy( ) + { + string collectionId = string.Format("GetsBy_:{0}", ); + return EntityHelper.GetEntityCollection<, >( + EntityCacheInfo, + new CacheManager.CachePolicy( + CacheManager.CacheScopeFilter.Qualified, + string.Format(":{0}", ) + ), + collectionId, + () => DAL.GetIDsBy(), + Get + ); + } + #$@#@2 - Unique ID used to find this section, don't change it. 599W#%44 + + public void Construct( dal) + { + _EntityDAL = dal; + } + + public CacheInfo CacheInfo + { + get { return EntityCacheInfo; } + } + + public static CacheInfo EntityCacheInfo = new CacheInfo( + new CacheabilitySettings(collectionsAreCacheable: true, countsAreCacheable: true, entityIsCacheable: true, idLookupsAreCacheable: true, hasUnqualifiedCollections: true), + typeof().ToString(), + true + ); + + public IEnumerable BuildEntityIDLookups() + { + yield break; + } + + public IEnumerable BuildStateTokenCollection() + { + %^^^^yield return new StateToken(string.Format(":{0}", ));^^^^% + yield break; + } + + + } +} \ No newline at end of file diff --git a/SharedCode/Roblox/Data/DBWireup/DAL.cs b/SharedCode/Roblox/Data/DBWireup/DAL.cs new file mode 100644 index 0000000..ae4b786 --- /dev/null +++ b/SharedCode/Roblox/Data/DBWireup/DAL.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using Roblox.Common; +using Roblox.Common.Persistence; + +namespace +{ + internal class + { + private _ID = default(); + + internal ID + { + get { return _ID; } + set { _ID = value; } + } + + private static string + { + get { return ; } + } + + internal () + { + } + + internal void Delete() + { + if (_ID == default()) + throw new ApplicationException("Required value not specified: ID."); + + var queryParameters = new List + { + new SqlParameter("@ID", _ID) + }; + + var dbInfo = new dbInfo( + , + "", + queryParameters + ); + + EntityHelper.DoEntityDALDelete(dbInfo); + } + internal void Insert() + { + var queryParameters = new List + { + + }; + + var dbInfo = new dbInfo( + , + "", + new SqlParameter("@ID", ), + queryParameters + ); + + _ID = EntityHelper.DoEntityDALInsert<>(dbInfo); + } + internal void Update() + { + var queryParameters = new List + { + new SqlParameter("@ID", _ID), + + }; + + var dbInfo = new dbInfo( + , + "", + queryParameters + ); + + EntityHelper.DoEntityDALUpdate(dbInfo); + } + + private static BuildDAL(SqlDataReader reader) + { + var dal = new (); + + while (reader.Read()) + { + dal.ID = ()reader["ID"]; + + } + + if (dal.ID == default()) + return null; + + return dal; + } + + internal static Get( id) + { + if (id == default()) + return null; + + var queryParameters = new List + { + new SqlParameter("@ID", id) + }; + + var dbInfo = new dbInfo( + , + "", + queryParameters + ); + + return EntityHelper.GetEntityDAL(dbInfo, BuildDAL); + } + #$@#@ - Unique ID used to find this section, don't change it. 599W#%2 + internal static ICollection<> GetIDsBy( ) + { + if ( == default()) + throw new ApplicationException("Required value not specified: ."); + + var queryParameters = new List(); + queryParameters.Add(new SqlParameter("@", )); + + return EntityHelper.GetDataEntityIDCollection<>( + new dbInfo( + , + "s_GetIDsBy", + queryParameters + ) + ); + } + #$@#@2 - Unique ID used to find this section, don't change it. 599W#%44 + } +} diff --git a/SharedCode/Roblox/Data/DBWireup/SQL.txt b/SharedCode/Roblox/Data/DBWireup/SQL.txt new file mode 100644 index 0000000..4e822a3 --- /dev/null +++ b/SharedCode/Roblox/Data/DBWireup/SQL.txt @@ -0,0 +1,169 @@ +/* Standard Insertion */ +IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[]') AND type in (N'P', N'PC')) +BEGIN + EXEC('CREATE PROCEDURE [dbo].[] AS BEGIN SET NOCOUNT ON; END') +END + +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER OFF +GO + +ALTER PROCEDURE [dbo].[] +( + @ID OUTPUT + +) +AS + +SET NOCOUNT ON + +INSERT INTO + [] +( + +) +VALUES +( + +) + +SET @ID = SCOPE_IDENTITY(); + +SET NOCOUNT OFF + +RETURN + +GO + + +/* Standard Update */ +IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[]') AND type in (N'P', N'PC')) +BEGIN + EXEC('CREATE PROCEDURE [dbo].[] AS BEGIN SET NOCOUNT ON; END') +END + +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER OFF +GO + +ALTER PROCEDURE [dbo].[] +( + @ID + +) +AS + +SET NOCOUNT ON + +UPDATE + [] +SET + +WHERE + ([ID] = @ID) + +SET NOCOUNT OFF + +RETURN + +GO + +/* Standard Get-By-ID */ +IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[]') AND type in (N'P', N'PC')) +BEGIN + EXEC('CREATE PROCEDURE [dbo].[] AS BEGIN SET NOCOUNT ON; END') +END +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + + +ALTER PROCEDURE [dbo].[] +( + @ID +) +AS + +SET NOCOUNT ON + +SELECT + [ID] + , +FROM + [] +WHERE + ([ID] = @ID) + +SET NOCOUNT OFF + +RETURN +GO + + +/* Standard Delete */ +IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[]') AND type in (N'P', N'PC')) +BEGIN + EXEC('CREATE PROCEDURE [dbo].[] AS BEGIN SET NOCOUNT ON; END') +END + +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + + +ALTER PROCEDURE [dbo].[] +( + @ID +) +AS + +SET NOCOUNT ON + +DELETE FROM + [] +WHERE + ([ID] = @ID) + +SET NOCOUNT OFF + +RETURN +GO + + +#$@#@ +/* FK Lookup */ +/* Don't use this. It's garbage. */ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +CREATE PROCEDURE [dbo].[s_GetIDsBy] +( + @ +) +AS + +SET NOCOUNT ON + +SELECT + [ID] +FROM + [s] +WHERE + ([] = @) + +SET NOCOUNT OFF + +RETURN + +599W#%44 \ No newline at end of file diff --git a/SharedCode/Roblox/Data/Entities/Entity.php b/SharedCode/Roblox/Data/Entities/Entity.php new file mode 100644 index 0000000..a0e1de5 --- /dev/null +++ b/SharedCode/Roblox/Data/Entities/Entity.php @@ -0,0 +1,33 @@ +CreateNew() not implemented"); + } + + function Delete() { + throw new \Exception(__CLASS__ . "->Delete() not implemented"); + } + function Insert() { + throw new \Exception(__CLASS__ . "->Insert() not implemented"); + } + function Update() { + throw new \Exception(__CLASS__ . "->Update() not implemented"); + } + static function Get($id) { + throw new \Exception(__CLASS__ . "->Get() not implemented"); + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Data/Entities/EntityDAL.php b/SharedCode/Roblox/Data/Entities/EntityDAL.php new file mode 100644 index 0000000..91bc468 --- /dev/null +++ b/SharedCode/Roblox/Data/Entities/EntityDAL.php @@ -0,0 +1,39 @@ +, // the string used to connect to the DB + "", // the name of the procedure + $queryParameters // the parameters used in the procedure defenition + ); + + \Roblox\Common\EntityHelper::DoEntityDALDelete($dbInfo); + } + function Insert() { + throw new \Exception(__CLASS__ . "->Insert() not implemented"); + } + function Update() { + throw new \Exception(__CLASS__ . "->Update() not implemented"); + } + static function Get($id) { + throw new \Exception(__CLASS__ . "->Get() not implemented"); + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Data/Entities/EntityHelper.php b/SharedCode/Roblox/Data/Entities/EntityHelper.php new file mode 100644 index 0000000..cc45939 --- /dev/null +++ b/SharedCode/Roblox/Data/Entities/EntityHelper.php @@ -0,0 +1,16 @@ +{key($vars)} = current($vars); + }while (!(next($vars) === FALSE)); + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Data/Entities/RobloxEntity.php b/SharedCode/Roblox/Data/Entities/RobloxEntity.php new file mode 100644 index 0000000..ff9e735 --- /dev/null +++ b/SharedCode/Roblox/Data/Entities/RobloxEntity.php @@ -0,0 +1,97 @@ +_EntityClassName = get_class($this)."Entity"; + + $this->Construct($this->Get($id)); + if ($this->_EntityDAL !== null) { + // $this <-- $_EntityDAL + EntityHelper::SyncProperties($this->_EntityDAL, $this); + }else { + $this->Construct(new $this->_EntityClassName()); + } + } + + function Delete() { + $this->_EntityDAL->ID = $this->ID; + return $this->_EntityDAL->Delete(); + } + + function Save() { + // $this --> $this->_EntityDAL + EntityHelper::SyncProperties($this, $this->_EntityDAL); + + // TODO: move this check to EntityHelper.SaveEntity + // The check for $this->ID here checks if we've loaded the entity from the database. + // If the entity doesn't already exist in the database, then create it. + $dateTime = new \DateTime(); + if ($this->ID !== null) { + $this->_EntityDAL->Created = $dateTime->getTimestamp(); + $this->_EntityDAL->Updated = $this->_EntityDAL->Created; + + $this->_EntityDAL->Insert(); + }else { + $this->_EntityDAL->Updated = $dateTime->getTimestamp(); + + $this->_EntityDAL->Update(); + } + } + + protected static function CreateNew(...$params){ + // Generate the entity class path + $entityClassPath = self::$_entityBasePath . substr(get_called_class(), 7); + + $entity = new $entityClassPath(); + $entity->CreateNew(...$params); + + return $entity; + } + + static function Get(int $id) { + if ($id == null) { + return null; + } + // Generate the entity class path + $entityClassPath = self::$_entityBasePath . substr(get_called_class(), 7); + return \Roblox\Common\EntityHelper::GetEntity( + self::CacheInfo(), // EntityCacheInfo, + $id, // id, + $entityClassPath."::Get" // () => .Get(id) + ); + } + + function Construct($dal) { + $this->_EntityDAL = $dal; + } + + private static $_cacheSettings = [ + "collectionsAreCacheable" => true, + "countsAreCacheable" => true, + "entityIsCacheable" => true, + "idLookupsAreCacheable" => true, + "hasUnqualifiedCollections" => true + ]; + + public static function CacheInfo() + { + // Generate the entity cache info on the spot. + // We can't create it outside of this function. + $EntityCacheInfo = new \Roblox\Caching\CacheInfo( + new \Roblox\Caching\CacheabilitySettings(self::$_cacheSettings), + get_called_class(), + true // Not sure what this does + ); + return $EntityCacheInfo; + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Entities/Mssql/RobloxDataAccessPatternExtensions.php b/SharedCode/Roblox/Entities/Mssql/RobloxDataAccessPatternExtensions.php new file mode 100644 index 0000000..8b4dfd1 --- /dev/null +++ b/SharedCode/Roblox/Entities/Mssql/RobloxDataAccessPatternExtensions.php @@ -0,0 +1,20 @@ +CreateNew() not implemented"); + } + + function Delete() { + throw new \Exception(__CLASS__ . "->Delete() not implemented"); + } + function Insert() { + throw new \Exception(__CLASS__ . "->Insert() not implemented"); + } + function Update() { + throw new \Exception(__CLASS__ . "->Update() not implemented"); + } + static function Get($id) { + throw new \Exception(__CLASS__ . "->Get() not implemented"); + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Grid/Rcc/Job.php b/SharedCode/Roblox/Grid/Rcc/Job.php new file mode 100644 index 0000000..83cafb2 --- /dev/null +++ b/SharedCode/Roblox/Grid/Rcc/Job.php @@ -0,0 +1,20 @@ +id = $id; + $this->expirationInSeconds = $expirationInSeconds; + $this->category = $category; + $this->cores = $cores; + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Grid/Rcc/LuaType.php b/SharedCode/Roblox/Grid/Rcc/LuaType.php new file mode 100644 index 0000000..2e755b2 --- /dev/null +++ b/SharedCode/Roblox/Grid/Rcc/LuaType.php @@ -0,0 +1,29 @@ + LuaType::LUA_TNIL, + 'boolean' => LuaType::LUA_TBOOLEAN, + 'integer' => LuaType::LUA_TNUMBER, + 'double' => LuaType::LUA_TNUMBER, + 'string' => LuaType::LUA_TSTRING, + 'array' => LuaType::LUA_TTABLE, + 'object' => LuaType::LUA_TNIL + ]; + return $luaTypeConversions[gettype($value)]; + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Grid/Rcc/LuaValue.php b/SharedCode/Roblox/Grid/Rcc/LuaValue.php new file mode 100644 index 0000000..af8eaa6 --- /dev/null +++ b/SharedCode/Roblox/Grid/Rcc/LuaValue.php @@ -0,0 +1,73 @@ + $child) { + $this->{$name} = $child; + } + } + } + + // This function serializes the given PHP variable into a Lua value. + static function serializeValue($phpValue) { + $luaValue = new LuaValue(null); // A really dumb hack + $luaValue->type = LuaType::castToLuaType($phpValue); + if (is_array($phpValue)) { + // TODO: Make this an empty array by default to allow for easy table creation? + $luaValue->table = []; + foreach ($phpValue as $value) { + array_push($luaValue->table, new LuaValue($value)); + } + }else { + $luaValue->value = $phpValue; + } + return $luaValue; + } + + // This function deserializes the given Lua value into a normal PHP variable. + static function deserializeValue($luaValue) { + if (is_array($luaValue)) { + $phpValue = []; + foreach ($luaValue as $value) { + array_push($phpValue, LuaValue::deserializeValue($value)); + } + }else { + if ($luaValue->type == LuaType::LUA_TTABLE && isset($luaValue->table->LuaValue)) { + $phpValue = []; + if (is_array($luaValue->table->LuaValue)) { + $value = $luaValue->table->LuaValue; + }else { + $value = $luaValue->table; + } + foreach ($value as $value) { + array_push($phpValue, $value->deserialize()); + } + }elseif ($luaValue->type == LuaType::LUA_TNIL) { + // Null value + $phpValue = null; + }else { + // Direct read from LuaValue's value + $phpValue = $luaValue->value; + } + } + return $phpValue; + } + + // This function deserializes the current Lua value into a normal PHP variable. + function deserialize() { + return LuaValue::deserializeValue($this); + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Grid/Rcc/RCCService.wsdl b/SharedCode/Roblox/Grid/Rcc/RCCService.wsdl new file mode 100644 index 0000000..4fb100d --- /dev/null +++ b/SharedCode/Roblox/Grid/Rcc/RCCService.wsdl @@ -0,0 +1,800 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SharedCode/Roblox/Grid/Rcc/RCCServiceSoap.php b/SharedCode/Roblox/Grid/Rcc/RCCServiceSoap.php new file mode 100644 index 0000000..ca2f5ad --- /dev/null +++ b/SharedCode/Roblox/Grid/Rcc/RCCServiceSoap.php @@ -0,0 +1,264 @@ + "Roblox\Grid\Rcc\Status", + "Job" => "Roblox\Grid\Rcc\Job", + "ScriptExecution" => "Roblox\Grid\Rcc\ScriptExecution", + "LuaValue" => "Roblox\Grid\Rcc\LuaValue", + "LuaType" => "Roblox\Grid\Rcc\LuaType" + ]; + + // The specification of a URL and port should always be done during production, though the defaults can be used when testing. + function __construct($url = "127.0.0.1", $port = 64989) { + $this->SoapClient = new \SoapClient(__DIR__."\RCCService.wsdl", ["location" => "http://".$url.":".$port, "uri" => "http://roblox.com/", "classmap" => $this->classmap, "exceptions" => false]); + } + + // Begin function handlers + // Use the HelloWorld function as a template for all future functions. + // NOTE: Please use is_soap_fault() when checking if functions failed. + + function callToService($name, $arguments = []) { + $result = $this->SoapClient->{$name}($arguments); + return (!is_soap_fault($result) ? (/*is_soap_fault($result) ||*/ !isset($result->{$name."Result"}) ? null : $result->{$name."Result"}) : $result); + } + + private static function parseJobResult($value) { + if ($value !== new \stdClass() && isset($value->LuaValue)) { + // Our job result isn't empty, so let's deserialize it + $result = LuaValue::deserializeValue($value->LuaValue); + }else { + // Something went wrong :( + $result = null; + } + return $result; + } + + /** + * Name: Hello World + * Description: This function calls a simple HelloWorld function from RCCService. The expected HelloWorldResponse is "Hello World". + * Parameters: [] + */ + function HelloWorld() { + return $this->callToService(__FUNCTION__); + } + + /** + * Name: Get Version + * Description: This function fetches the version of RCCService. + * Parameters: [] + */ + function GetVersion() { + return $this->callToService(__FUNCTION__); + } + + /** + * Name: Open Job + * Description: This function opens a job in accordance with the given arguments. Though this function is deprecated on ROBLOX's end, we'll still use it here and OpenJobEx will be called instead. + * Parameters: [ + * "job" => "This function opens a job in accordance with the given arguments. It returns the value that's returned by the Lua script.", + * "script" => "The ScriptExecution class that's going to be executed in the job. This contains values such as name, script, and arguments." + * ] + */ + function OpenJob($job, $script = null) { + return $this->OpenJobEx($job, $script); + } + + /** + * Name: Open Job Ex + * Description: This function opens a job in accordance with the given arguments. It returns the value that's returned by the Lua script. Feel free to use the other version of this function. + * Parameters: [ + * "job" => "The Job class that's going to be serialized and pushed to the service.", + * "script" => "The ScriptExecution class that's going to be executed in the job. This contains values such as name, script, and arguments." + * ] + */ + function OpenJobEx($job, $script = null) { + $result = $this->callToService(__FUNCTION__, ["job" => $job, "script" => $script]); + return RCCServiceSoap::parseJobResult($result); + } + + /** + * Name: Batch Job + * Description: This function runs a batch job in accordance with the given arguments. Though this function is deprecated on ROBLOX's end, we'll still use it here and BatchJobEx will be called instead. + * Parameters: [ + * "job" => "The Job class that's going to be serialized and pushed to the service.", + * "script" => "The ScriptExecution class that's going to be executed in the job. This contains values such as name, script, and arguments." + * ] + */ + function BatchJob($job, $script) { + return $this->BatchJobEx($job, $script); + } + + /** + * Name: Batch Job Ex + * Description: This function runs a batch job in accordance with the given arguments. Feel free to use the other version of this function. + * Parameters: [ + * "job" => "The Job class that's going to be serialized and pushed to the service.", + * "script" => "The ScriptExecution class that's going to be executed in the job. This contains values such as name, script, and arguments." + * ] + */ + function BatchJobEx($job, $script) { + $result = $this->callToService(__FUNCTION__, ["job" => $job, "script" => $script]); + return RCCServiceSoap::parseJobResult($result); + } + + /** + * Name: Renew Lease + * Description: This function changes the expirationInSeconds of a job based on the jobID. It essentially allows you to set the expiration time of a currently opened job. + * Parameters: [ + * "jobID" => "The ID of the job who's expiration is going to be renewed.", + * "expirationInSeconds" => "The new expiration time for the job." + * ] + */ + function RenewLease($jobID, $expirationInSeconds) { + return $this->callToService(__FUNCTION__, ["jobID" => $jobID, "expirationInSeconds" => $expirationInSeconds]); + } + + /** + * Name: Execute + * Description: This function uses the given arguments to execute a script inside an existing job. Though this function is deprecated on ROBLOX's end, we'll still use it here and ExecuteEx will be called instead. + * Parameters: [ + * "jobID" => "The ID of the job in which the script is executed.", + * "script" => "The script that's going to be executed." + * ] + */ + function Execute($jobID, $script) { + return $this->ExecuteEx($jobID, $script); + } + + /** + * Name: Execute Ex + * Description: This function uses the given arguments to execute a script inside an existing job. + * Parameters: [ + * "jobID" => "The ID of the job in which the script is executed.", + * "script" => "The script that's going to be executed." + * ] + */ + function ExecuteEx($jobID, $script) { + return $this->callToService(__FUNCTION__, ["jobID" => $jobID, "script" => $script]); + } + + /** + * Name: Close Job + * Description: This function closes an existing job using the given job ID. + * Parameters: [ + * "jobID" => "The ID of the job that's going to be closed." + * ] + */ + function CloseJob($jobID) { + return $this->callToService(__FUNCTION__, ["jobID" => $jobID]); + } + + /** + * Name: Get Expiration + * Description: This function fetches and returns the expirationInSeconds of a job using the given job ID. + * Parameters: [ + * "jobID" => "The ID of the job." + * ] + */ + function GetExpiration($jobID) { + return $this->callToService(__FUNCTION__, ["jobID" => $jobID]); + } + + /** + * Name: Diag + * Description: This function returns various types of diagnostic information from RCCService. Though this function is deprecated on ROBLOX's end, we'll still use it here and DiagEx will be called instead. + * Parameters: [ + * "type" => "The diagnostic type to retrieve.", + * "jobID" => "The id of the job to retrieve the diagnostic from." + * ] + */ + function Diag($type, $jobID) { + return $this->DiagEx($type, $jobID); + } + + /** + * Name: Diag Ex + * Description: This function returns various types of diagnostic information from RCCService. + * Parameters: [ + * "type" => "The diagnostic type to retrieve.", + * "jobID" => "The id of the job to retrieve the diagnostic from." + * ] + */ + /* This is the format of the Diag data: + + type == 0 + DataModel Count in this process + PerfCounter data + Task Scheduler + (obsolete entry) + double threadAffinity + double numQueuedJobs + double numScheduledJobs + double numRunningJobs + long threadPoolSize + double messageRate + double messagePumpDutyCycle + DataModel Jobs Info + Machine configuration + Memory Leak Detection + type & 1 + leak dump + type & 2 + attempt to allocate 500k. if success, then true else false + type & 4 + DataModel dutyCycles + */ + function DiagEx($type, $jobID) { + return $this->callToService(__FUNCTION__, ["type" => $type, "jobID" => $jobID]); + } + + /** + * Name: Get Status + * Description: This function fetches the status information from RCCService. The returned Status class contains a version string and an environmentCount int. + * Parameters: [] + */ + function GetStatus() { + return $this->callToService(__FUNCTION__); + } + + /** + * Name: Get All Jobs + * Description: This function fetches an array of every job that's currently open on RCCService. Though this function is deprecated on ROBLOX's end, we'll still use it here and GetAllJobsEx will be called instead. + * Parameters: [] + */ + function GetAllJobs() { + // GetAllJobs is deprecated. + return $this->GetAllJobsEx(); + } + + /** + * Name: Get All Jobs Ex + * Description: This function fetches an array of every job that's currently open on RCCService. + * Parameters: [] + */ + function GetAllJobsEx() { + return $this->callToService(__FUNCTION__); + } + + /** + * Name: Close Expired Jobs + * Description: This function closes all currently open and expired jobs on RCCService. This returns the amount of jobs that were closed. + * Parameters: [] + */ + function CloseExpiredJobs() { + return $this->callToService(__FUNCTION__); + } + + /** + * Name: Close All Jobs + * Description: This function closes all currently open jobs on RCCService. This returns the amount of jobs that were closed. + * Parameters: [] + */ + function CloseAllJobs() { + return $this->callToService(__FUNCTION__); + } +} + +// EOF diff --git a/SharedCode/Roblox/Grid/Rcc/ScriptExecution.php b/SharedCode/Roblox/Grid/Rcc/ScriptExecution.php new file mode 100644 index 0000000..182f852 --- /dev/null +++ b/SharedCode/Roblox/Grid/Rcc/ScriptExecution.php @@ -0,0 +1,20 @@ +name = $name; + $this->script = $script; + foreach ($arguments as $arg) { + array_push($this->arguments, new LuaValue($arg)); + } + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Grid/Rcc/Status.php b/SharedCode/Roblox/Grid/Rcc/Status.php new file mode 100644 index 0000000..7cb9bec --- /dev/null +++ b/SharedCode/Roblox/Grid/Rcc/Status.php @@ -0,0 +1,16 @@ +version = $version; + $this->environmentCount = $environmentCount; + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Logging/Gatherer.php b/SharedCode/Roblox/Logging/Gatherer.php new file mode 100644 index 0000000..a8fe1dd --- /dev/null +++ b/SharedCode/Roblox/Logging/Gatherer.php @@ -0,0 +1,45 @@ +format("Y-m-d").".log"; + } + + private static function buildLogPath($machineIp, $shardType) { + return Gatherer::buildLogFileLocation($shardType).Gatherer::buildLogFileName($machineIp); + } + + private static function buildLogEntry($text) { + $date = new \DateTime(); + return $date->format("h:i:s")." \n"; + } + + static function logEntry($shardType, $machineIp, $entry) { + // ShardType check + if ($shardType !== ShardType::Server) { + $shardType = ShardType::Client; + } + $path = Gatherer::buildLogPath($machineIp, $shardType); + file_put_contents($path, Gatherer::buildLogEntry($entry), FILE_APPEND); + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Logging/LogEntry b/SharedCode/Roblox/Logging/LogEntry new file mode 100644 index 0000000..bdd6988 --- /dev/null +++ b/SharedCode/Roblox/Logging/LogEntry @@ -0,0 +1,22 @@ +shard = $shard; + $this->entry = $entry; + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Logging/ShardType.php b/SharedCode/Roblox/Logging/ShardType.php new file mode 100644 index 0000000..e1d58aa --- /dev/null +++ b/SharedCode/Roblox/Logging/ShardType.php @@ -0,0 +1,12 @@ +c__DisplayClass2_0.b__0() in C:\teamcity-agent\work\a6371342c4f9b6ec\Assemblies\Mssql\Roblox.Mssql\GuardedDatabase.cs:line 21. + +namespace Roblox\Mssql; + +class GuardedDatabase { + + function __construct() { + } + + function Execute(/*CommandType*/ $commandType, /*String*/ $commandText, /*SqlParameter[]*/ $sqlParameters, /*Action`1*/ $action, /*Nullable`1*/ $commandTimeout, /*Nullable`1*/ $applicationIntent) { + /* + at Roblox.Sentinels.ExecutionCircuitBreakerBase.Execute(Action action) + at Roblox.Mssql.GuardedDatabase.Execute(CommandType commandType, String commandText, SqlParameter[] sqlParameters, Action`1 action, Nullable`1 commandTimeout, Nullable`1 applicationIntent) in C:\teamcity-agent\work\a6371342c4f9b6ec\Assemblies\Mssql\Roblox.Mssql\GuardedDatabase.cs:line 22 + */ + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Mysql/Helpers/ParameterHelper.php b/SharedCode/Roblox/Mysql/Helpers/ParameterHelper.php new file mode 100644 index 0000000..db17633 --- /dev/null +++ b/SharedCode/Roblox/Mysql/Helpers/ParameterHelper.php @@ -0,0 +1,32 @@ + [ + "boolean" => "i", + "integer" => "i", + "double" => "d", + "string" => "s" + ]; + + static function getSqlType($type) { + if (isset($phpToSqlType[$type])) { + return $phpToSqlType[$type]; + } else { + throw new \Exception('Type "'.$type.'" not supported'); + } + } + + static function getSqlValue($value) { + if (gettype($value) == "boolean") { + // Cast bool to int for storage via SQL + $value = (int)$value + } + return $value; + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Mysql/SqlParameter.php b/SharedCode/Roblox/Mysql/SqlParameter.php new file mode 100644 index 0000000..be7adbf --- /dev/null +++ b/SharedCode/Roblox/Mysql/SqlParameter.php @@ -0,0 +1,20 @@ +name = $name; + $this->type = Helpers\ParameterHelper::getSqlType(gettype($value)); + $this->value = Helpers\ParameterHelper::getSqlValue($value); + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Platform/AssetOwnership/UserAsset.php b/SharedCode/Roblox/Platform/AssetOwnership/UserAsset.php new file mode 100644 index 0000000..1c71baa --- /dev/null +++ b/SharedCode/Roblox/Platform/AssetOwnership/UserAsset.php @@ -0,0 +1,21 @@ +userAssetId = $userAssetId; + // TODO: Load other values + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Platform/Assets/AssetType.php b/SharedCode/Roblox/Platform/Assets/AssetType.php new file mode 100644 index 0000000..b25a782 --- /dev/null +++ b/SharedCode/Roblox/Platform/Assets/AssetType.php @@ -0,0 +1,65 @@ +userId = $userId; + // TODO: Load other values + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Platform/Throttling/Entities/NamespaceDAL.php b/SharedCode/Roblox/Platform/Throttling/Entities/NamespaceDAL.php new file mode 100644 index 0000000..fa3959a --- /dev/null +++ b/SharedCode/Roblox/Platform/Throttling/Entities/NamespaceDAL.php @@ -0,0 +1,20 @@ + + /// This static function (that is intended to be called from script emitted + /// on the server) + /// + var a = $find(componentID); + if (a != null || a != undefined) { + a._onUpdate(); + } +}; + +Roblox.Thumbs.AssetImage.mediaPlayer = null; +Roblox.Thumbs.AssetImage.isInitialized = false; +Roblox.Thumbs.AssetImage.currentlyPlayingAsset = null; +Roblox.Thumbs.AssetImage.InitMediaPlayer = function () { + if (typeof jQuery !== "undefined" && !Roblox.Thumbs.AssetImage.isInitialized) { + Roblox.Thumbs.AssetImage.isInitialized = true; + $(function () { + $(document).on("click", "div.MediaPlayerIcon", function (e) { + var assetButton = $(e.target); + assetButton.mediaUrl = assetButton.attr("data-mediathumb-url"); + + /// + /// Determines if the asset button and the given asset button are the same asset. + /// + assetButton.hasSameMediaAs = function (other) { + return assetButton.mediaUrl === other.mediaUrl; + }; + + /// + /// Plays or resumes the asset button's media and updates the button's visuals. + /// + assetButton.play = function () { + if (Roblox.Thumbs.AssetImage.currentlyPlayingAsset === null || + !Roblox.Thumbs.AssetImage.currentlyPlayingAsset.hasSameMediaAs(assetButton)) { + + // If another asset is playing then pause it first. + if (Roblox.Thumbs.AssetImage.currentlyPlayingAsset != null) { + Roblox.Thumbs.AssetImage.currentlyPlayingAsset.stop(); + } + + Roblox.Thumbs.AssetImage.mediaPlayer.jPlayer("setMedia", { mp3: assetButton.mediaUrl }); + Roblox.Thumbs.AssetImage.currentlyPlayingAsset = assetButton; + + Roblox.Thumbs.AssetImage.mediaPlayer.on($.jPlayer.event.ended, assetButton.onJPlayerEnded); + Roblox.Thumbs.AssetImage.mediaPlayer.on($.jPlayer.event.error, assetButton.onJPlayerError); + } + + Roblox.Thumbs.AssetImage.mediaPlayer.jPlayer("play"); + assetButton.removeClass("Play").addClass("Pause"); + }; + + /// + /// Stops the asset button's media and resets the button. + /// + assetButton.stop = function () { + if (Roblox.Thumbs.AssetImage.currentlyPlayingAsset.hasSameMediaAs(assetButton)) { + + Roblox.Thumbs.AssetImage.currentlyPlayingAsset = null; + Roblox.Thumbs.AssetImage.mediaPlayer.jPlayer("clearMedia"); + Roblox.Thumbs.AssetImage.mediaPlayer.off($.jPlayer.event.ended); + Roblox.Thumbs.AssetImage.mediaPlayer.off($.jPlayer.event.error); + + assetButton.removeClass("Pause").addClass("Play"); + } + }; + + /// + /// Pauses the asset button's media and changes the button's visuals. + /// + assetButton.pause = function () { + if (Roblox.Thumbs.AssetImage.currentlyPlayingAsset.hasSameMediaAs(assetButton)) { + Roblox.Thumbs.AssetImage.mediaPlayer.jPlayer("pause"); + assetButton.removeClass("Pause").addClass("Play"); + } + } + + /// + /// Handles the jPlayer error event. + /// + assetButton.onJPlayerError = function () { + assetButton.stop(); + }; + + /// + /// Handles the jPlayer ended event. + /// + assetButton.onJPlayerEnded = function () { + assetButton.stop(); + }; + + // If the media player isn't yet initialized then + // initialize it and begin playing when it's ready. + if (Roblox.Thumbs.AssetImage.mediaPlayer == null) { + Roblox.Thumbs.AssetImage.mediaPlayer = $("
").appendTo("body").jPlayer({ + swfPath: '/js/jPlayer/2.9.2/jquery.jplayer.swf', + solution: "html, flash", + supplied: "mp3", + wmode: "transparent", + errorAlerts: false, + warningAlerts: false, + ready: function (event) { + assetButton.play(); + } + }); + } + + else { // Otherwise, play or pause normally. + if (assetButton.hasClass("Pause")) { + assetButton.pause(); + } + else { + assetButton.play(); + } + } + }); + }); + } +} \ No newline at end of file diff --git a/SharedCode/Roblox/Thumbs/AssetImage.php b/SharedCode/Roblox/Thumbs/AssetImage.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Thumbs/Avatar.php b/SharedCode/Roblox/Thumbs/Avatar.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Thumbs/AvatarImage.js b/SharedCode/Roblox/Thumbs/AvatarImage.js new file mode 100644 index 0000000..c8988be --- /dev/null +++ b/SharedCode/Roblox/Thumbs/AvatarImage.js @@ -0,0 +1,14 @@ +// Register the namespace for the control. +Type.registerNamespace('Roblox.Thumbs'); + +// +// Definte the control properties. +// +Roblox.Thumbs.AvatarImage = function(element) { + Roblox.Thumbs.AvatarImage.initializeBase(this, [element]); + this._userID = 0; +} + +// +// Create the prototype for the control. +// \ No newline at end of file diff --git a/SharedCode/Roblox/Thumbs/AvatarImage.php b/SharedCode/Roblox/Thumbs/AvatarImage.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Thumbs/Buckets.php b/SharedCode/Roblox/Thumbs/Buckets.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Thumbs/Dispatcher.php b/SharedCode/Roblox/Thumbs/Dispatcher.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Thumbs/GridRequest.php b/SharedCode/Roblox/Thumbs/GridRequest.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Thumbs/Image.js b/SharedCode/Roblox/Thumbs/Image.js new file mode 100644 index 0000000..6b4bd90 --- /dev/null +++ b/SharedCode/Roblox/Thumbs/Image.js @@ -0,0 +1,144 @@ +// Image.Js. Register the namespace for the control. +Type.registerNamespace('Roblox.Thumbs'); + +// +// Define the control properties. +// +Roblox.Thumbs.Image = function (element) { + Roblox.Thumbs.Image.initializeBase(this, [element]); + this._fileExtension = null; + this._spinnerUrl = null; + this._pollTime = 3000; + this._waitTime = 0; + this._webService = null; + this._requestThumbnail = null; + this._updateTimeout = null; + this._spinner = null; +}; + +// +// Create the prototype for the control. +// +Roblox.Thumbs.Image.prototype = { + + initialize: function () { + Roblox.Thumbs.Image.callBaseMethod(this, 'initialize'); + }, + + dispose: function () { + if (typeof (this._updateTimeout) !== 'undefined') + window.clearTimeout(this._updateTimeout); + Roblox.Thumbs.Image.callBaseMethod(this, 'dispose'); + }, + + _showSpinner: function () { + if (this._spinner !== null) + return; + + this.get_element().style.position = "relative"; + this._spinner = document.createElement("img"); + this._spinner.style.position = "absolute"; + this._spinner.style.left = "0px"; + this._spinner.style.top = "0px"; + this._spinner.style.height = "16px"; + this._spinner.style.width = "16px"; + this._spinner.style.border = 0; + this._spinner.src = this._spinnerUrl; + this.get_element().appendChild(this._spinner); + }, + + _hideSpinner: function () { + if (!this._spinner) + return; + var e = this.get_element(); + if (e) { + e.removeChild(this._spinner); + } + this._spinner = null; + }, + + _onUpdate: function () { + + if (!this._webService) { + this._hideSpinner(); + return; + } + this._showSpinner(); + this._requestThumbnail(); + }, + + _onUrl: function (result) { + + if (!this.get_element()) { + this._hideSpinner(); + return; + } + + Roblox.Controls.Image.SetImageSrc(this.get_element(), result.url); + + if ((!result.final || result.url.indexOf("unavail") > 0) && this._waitTime <= 40) // Give up after 40 times. + { + // Try again later + this._waitTime = parseInt(this._waitTime) + parseInt(1); + this._updateTimeout = window.setTimeout(Function.createDelegate(this, this._onUpdate), this._pollTime); + } + else + this._hideSpinner(); + }, + + _onError: function (result) { + this._hideSpinner(); + }, + + get_thumbnailFormatID: function () { + return this._thumbnailFormatID; + }, + + set_thumbnailFormatID: function (value) { + if (this._thumbnailFormatID !== value) { + this._thumbnailFormatID = value; + this.raisePropertyChanged('thumbnailFormatID'); + } + }, + + get_pollTime: function () { + return this._pollTime.value; + }, + + set_pollTime: function (value) { + if (this._pollTime !== value) { + this._pollTime = value; + this.raisePropertyChanged('pollTime'); + } + }, + + get_fileExtension: function () { + return this._fileExtension; + }, + + set_fileExtension: function (value) { + if (this._fileExtension !== value) { + this._fileExtension = value; + this.raisePropertyChanged('fileExtension'); + } + }, + + get_spinnerUrl: function () { + return this._spinnerUrl; + }, + + set_spinnerUrl: function (value) { + if (this._spinnerUrl !== value) { + this._spinnerUrl = value; + this.raisePropertyChanged('spinnerUrl'); + } + } +}; + +// Optional descriptor for JSON serialization. +Roblox.Thumbs.Image.descriptor = { + properties: [] +}; + +// Register the class as a type that inherits from Sys.UI.Control. +Roblox.Thumbs.Image.registerClass('Roblox.Thumbs.Image', Sys.UI.Control); \ No newline at end of file diff --git a/SharedCode/Roblox/Thumbs/ImageCompression.php b/SharedCode/Roblox/Thumbs/ImageCompression.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Thumbs/ImageParameters.php b/SharedCode/Roblox/Thumbs/ImageParameters.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Thumbs/Settings.php b/SharedCode/Roblox/Thumbs/Settings.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Thumbs/ThumbnailImage.php b/SharedCode/Roblox/Thumbs/ThumbnailImage.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Thumbs/ThumbnailRequest.php b/SharedCode/Roblox/Thumbs/ThumbnailRequest.php new file mode 100644 index 0000000..e69de29 diff --git a/SharedCode/Roblox/Web/AspNet/TreeView.php b/SharedCode/Roblox/Web/AspNet/TreeView.php new file mode 100644 index 0000000..f281b3d --- /dev/null +++ b/SharedCode/Roblox/Web/AspNet/TreeView.php @@ -0,0 +1,41 @@ + $item) { + $href = $item[0]; + $selected = $href == $pageUrl; + if (isset($item[1]) && $item[1] !== null && $item[1] !== []) { + // If the element contains children, push them to the tree as well + TreeView::generateBulletTree($item[1]); + } + // Push the text and link to the element on the tree + array_push($tree, ["text" => $key, "a_attr" => ["href" => $href == "" ? "/Default.aspx" : $href], "children" => $item[1] ?? [], "state" => ["selected" => $selected]]); + } + unset($item); + TreeView::stampBulletTree($tree); + } +} + +// EOF \ No newline at end of file diff --git a/SharedCode/Roblox/Web/Catalog/NoPriceStatus.php b/SharedCode/Roblox/Web/Catalog/NoPriceStatus.php new file mode 100644 index 0000000..c6ae960 --- /dev/null +++ b/SharedCode/Roblox/Web/Catalog/NoPriceStatus.php @@ -0,0 +1,13 @@ + +* +* More info, bugs or requests, contact info@kingsquare.nl +* +* Latest version and demo: http://www.kingsquare.nl/phppsdreader +* +* TODO +* ---- +* - read color values for "multichannel data" PSD files +* - find and implement (hunter)lab to RGB algorithm +* - fix 32 bit colors... has something to do with gamma and exposure available since CS2, but dunno how to read them... +*/ + + +class PhpPsdReader { + var $infoArray; + var $fp; + var $fileName; + var $tempFileName; + var $colorBytesLength; + + function PhpPsdReader($fileName) { + set_time_limit(0); + $this->infoArray = array(); + $this->fileName = $fileName; + $this->fp = fopen($this->fileName,'r'); + + if (fread($this->fp,4)=='8BPS') { + $this->infoArray['version id'] = $this->_getInteger(2); + fseek($this->fp,6,SEEK_CUR); // 6 bytes of 0's + $this->infoArray['channels'] = $this->_getInteger(2); + $this->infoArray['rows'] = $this->_getInteger(4); + $this->infoArray['columns'] = $this->_getInteger(4); + $this->infoArray['colorDepth'] = $this->_getInteger(2); + $this->infoArray['colorMode'] = $this->_getInteger(2); + + + /* COLOR MODE DATA SECTION */ //4bytes Length The length of the following color data. + $this->infoArray['colorModeDataSectionLength'] = $this->_getInteger(4); + fseek($this->fp,$this->infoArray['colorModeDataSectionLength'],SEEK_CUR); // ignore this snizzle + + /* IMAGE RESOURCES */ + $this->infoArray['imageResourcesSectionLength'] = $this->_getInteger(4); + fseek($this->fp,$this->infoArray['imageResourcesSectionLength'],SEEK_CUR); // ignore this snizzle + + /* LAYER AND MASK */ + $this->infoArray['layerMaskDataSectionLength'] = $this->_getInteger(4); + fseek($this->fp,$this->infoArray['layerMaskDataSectionLength'],SEEK_CUR); // ignore this snizzle + + + /* IMAGE DATA */ + $this->infoArray['compressionType'] = $this->_getInteger(2); + $this->infoArray['oneColorChannelPixelBytes'] = $this->infoArray['colorDepth']/8; + $this->colorBytesLength = $this->infoArray['rows']*$this->infoArray['columns']*$this->infoArray['oneColorChannelPixelBytes']; + + if ($this->infoArray['colorMode']==2) { + $this->infoArray['error'] = 'images with indexed colours are not supported yet'; + return false; + } + } else { + $this->infoArray['error'] = 'invalid or unsupported psd'; + return false; + } + } + + + function getImage() { + // decompress image data if required + switch($this->infoArray['compressionType']) { + // case 2:, case 3: zip not supported yet.. + case 1: + // packed bits + $this->infoArray['scanLinesByteCounts'] = array(); + for ($i=0; $i<($this->infoArray['rows']*$this->infoArray['channels']); $i++) $this->infoArray['scanLinesByteCounts'][] = $this->_getInteger(2); + $this->tempFileName = tempnam(realpath('/tmp'),'decompressedImageData'); + $tfp = fopen($this->tempFileName,'wb'); + foreach ($this->infoArray['scanLinesByteCounts'] as $scanLinesByteCount) { + fwrite($tfp,$this->_getPackedBitsDecoded(fread($this->fp,$scanLinesByteCount))); + } + fclose($tfp); + fclose($this->fp); + $this->fp = fopen($this->tempFileName,'r'); + default: + // continue with current file handle; + break; + } + + // let's write pixel by pixel.... + $image = imagecreatetruecolor($this->infoArray['columns'],$this->infoArray['rows']); + + for ($rowPointer = 0; ($rowPointer < $this->infoArray['rows']); $rowPointer++) { + for ($columnPointer = 0; ($columnPointer < $this->infoArray['columns']); $columnPointer++) { + /* The color mode of the file. Supported values are: Bitmap=0; + Grayscale=1; Indexed=2; RGB=3; CMYK=4; Multichannel=7; + Duotone=8; Lab=9. + */ + switch ($this->infoArray['colorMode']) { + case 2: // indexed... info should be able to extract from color mode data section. not implemented yet, so is grayscale + exit; + break; + case 0: + // bit by bit + if ($columnPointer == 0) $bitPointer = 0; + if ($bitPointer==0) $currentByteBits = str_pad(base_convert(bin2hex(fread($this->fp,1)), 16, 2),8,'0',STR_PAD_LEFT); + $r = $g = $b = (($currentByteBits[$bitPointer]=='1')?0:255); + $bitPointer++; + if ($bitPointer==8) $bitPointer = 0; + break; + + case 1: + case 8: // 8 is indexed with 1 color..., so grayscale + $r = $g = $b = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + break; + + case 4: // CMYK + $c = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + $currentPointerPos = ftell($this->fp); + fseek($this->fp,$this->colorBytesLength-1,SEEK_CUR); + $m = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + fseek($this->fp,$this->colorBytesLength-1,SEEK_CUR); + $y = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + fseek($this->fp,$this->colorBytesLength-1,SEEK_CUR); + $k = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + fseek($this->fp,$currentPointerPos); + $r = round(($c * $k) / (pow(2,$this->infoArray['colorDepth'])-1)); + $g = round(($m * $k) / (pow(2,$this->infoArray['colorDepth'])-1)); + $b = round(($y * $k) / (pow(2,$this->infoArray['colorDepth'])-1)); + + break; + + case 9: // hunter Lab + // i still need an understandable lab2rgb convert algorithm... if you have one, please let me know! + $l = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + $currentPointerPos = ftell($this->fp); + fseek($this->fp,$this->colorBytesLength-1,SEEK_CUR); + $a = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + fseek($this->fp,$this->colorBytesLength-1,SEEK_CUR); + $b = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + fseek($this->fp,$currentPointerPos); + + $r = $l; + $g = $a; + $b = $b; + + break; + default: + $r = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + $currentPointerPos = ftell($this->fp); + fseek($this->fp,$this->colorBytesLength-1,SEEK_CUR); + $g = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + fseek($this->fp,$this->colorBytesLength-1,SEEK_CUR); + $b = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']); + fseek($this->fp,$currentPointerPos); + break; + + } + + if (($this->infoArray['oneColorChannelPixelBytes']==2)) { + $r = $r >> 8; + $g = $g >> 8; + $b = $b >> 8; + } elseif (($this->infoArray['oneColorChannelPixelBytes']==4)) { + $r = $r >> 24; + $g = $g >> 24; + $b = $b >> 24; + } + + $pixelColor = imagecolorallocate($image,$r,$g,$b); + imagesetpixel($image,$columnPointer,$rowPointer,$pixelColor); + } + } + fclose($this->fp); + if (isset($this->tempFileName)) unlink($this->tempFileName); + return $image; + } + + /** + * + * PRIVATE FUNCTIONS + * + */ + + function _getPackedBitsDecoded($string) { + /* + The PackBits algorithm will precede a block of data with a one byte header n, where n is interpreted as follows: + n Meaning + 0 to 127 Copy the next n + 1 symbols verbatim + -127 to -1 Repeat the next symbol 1 - n times + -128 Do nothing + + Decoding: + Step 1. Read the block header (n). + Step 2. If the header is an EOF exit. + Step 3. If n is non-negative, copy the next n + 1 symbols to the output stream and go to step 1. + Step 4. If n is negative, write 1 - n copies of the next symbol to the output stream and go to step 1. + + */ + + $stringPointer = 0; + $returnString = ''; + + while (1) { + if (isset($string[$stringPointer])) $headerByteValue = $this->_unsignedToSigned(hexdec(bin2hex($string[$stringPointer])),1); + else return $returnString; + $stringPointer++; + + if ($headerByteValue >= 0) { + for ($i=0; $i <= $headerByteValue; $i++) { + $returnString .= $string[$stringPointer]; + $stringPointer++; + } + } else { + if ($headerByteValue != -128) { + $copyByte = $string[$stringPointer]; + $stringPointer++; + + for ($i=0; $i < (1-$headerByteValue); $i++) { + $returnString .= $copyByte; + } + } + } + } + } + + function _unsignedToSigned($int,$byteSize=1) { + switch($byteSize) { + case 1: + if ($int<128) return $int; + else return -256+$int; + break; + + case 2: + if ($int<32768) return $int; + else return -65536+$int; + + case 4: + if ($int<2147483648) return $int; + else return -4294967296+$int; + + default: + return $int; + } + } + + function _hexReverse($hex) { + $output = ''; + if (strlen($hex)%2) return false; + for ($pointer = strlen($hex);$pointer>=0;$pointer-=2) $output .= substr($hex,$pointer,2); + return $output; + } + + function _getInteger($byteCount=1) { + switch ($byteCount) { + case 4: + // for some strange reason this is still broken... + return @reset(unpack('N',fread($this->fp,4))); + break; + + case 2: + return @reset(unpack('n',fread($this->fp,2))); + break; + + default: + return hexdec($this->_hexReverse(bin2hex(fread($this->fp,$byteCount)))); + } + } +} + +/** + * Returns an image identifier representing the image obtained from the given filename, using only GD, returns an empty string on failure + * + * @param string $fileName + * @return image identifier + */ + +function imagecreatefrompsd($fileName) { + $psdReader = new PhpPsdReader($fileName); + if (isset($psdReader->infoArray['error'])) return ''; + else return $psdReader->getImage(); +} diff --git a/SharedCode/php_image_magician.php b/SharedCode/php_image_magician.php new file mode 100644 index 0000000..8b426c3 --- /dev/null +++ b/SharedCode/php_image_magician.php @@ -0,0 +1,3314 @@ + resizeImage(150, 100, 0); + # $magicianObj -> saveImage('images/car_small.jpg', 100); + # + # - See end of doc for more examples - + # + # Supported file types include: jpg, png, gif, bmp, psd (read) + # + # + # + # The following functions are taken from phpThumb() [available from + # http://phpthumb.sourceforge.net], and are used with written permission + # from James Heinrich. + # - GD2BMPstring + # - GetPixelColor + # - LittleEndian2String + # + # The following functions are from Marc Hibbins and are used with written + # permission (are also under the Attribution-ShareAlike + # [http://creativecommons.org/licenses/by-sa/3.0/] license. + # - + # + # PhpPsdReader is used with written permission from Tim de Koning. + # [http://www.kingsquare.nl/phppsdreader] + # + # + # + # Known issues & Limitations: + # ------------------------------- + # Not so much an issue, the image is destroyed on the deconstruct rather than + # when we have finished with it. The reason for this is that we don't know + # when we're finished with it as you can both save the image and display + # it directly to the screen (imagedestroy($this->imageResized)) + # + # Opening BMP files is slow. A test with 884 bmp files processed in a loop + # takes forever - over 5 min. This test inlcuded opening the file, then + # getting and displaying its width and height. + # + # $forceStretch: + # ------------------------------- + # On by default. + # $forceStretch can be disabled by calling method setForceStretch with false + # parameter. If disabled, if an images original size is smaller than the size + # specified by the user, the original size will be used. This is useful when + # dealing with small images. + # + # If enabled, images smaller than the size specified will be stretched to + # that size. + # + # Tips: + # ------------------------------- + # * If you're resizing a transparent png and saving it as a jpg, set + # $keepTransparency to false with: $magicianObj->setTransparency(false); + # + # FEATURES: + # * EASY TO USE + # * BMP SUPPORT (read & write) + # * PSD (photoshop) support (read) + # * RESIZE IMAGES + # - Preserve transparency (png, gif) + # - Apply sharpening (jpg) (requires PHP >= 5.1.0) + # - Set image quality (jpg, png) + # - Resize modes: + # - exact size + # - resize by width (auto height) + # - resize by height (auto width) + # - auto (automatically determine the best of the above modes to use) + # - crop - resize as best as it can then crop the rest + # - Force stretching of smaller images (upscale) + # * APPLY FILTERS + # - Convert to grey scale + # - Convert to black and white + # - Convert to sepia + # - Convert to negative + # * ROTATE IMAGES + # - Rotate using predefined "left", "right", or "180"; or any custom degree amount + # * EXTRACT EXIF DATA (requires exif module) + # - make + # - model + # - date + # - exposure + # - aperture + # - f-stop + # - iso + # - focal length + # - exposure program + # - metering mode + # - flash status + # - creator + # - copyright + # * ADD WATERMARK + # - Specify exact x, y placement + # - Or, specify using one of the 9 pre-defined placements such as "tl" + # (for top left), "m" (for middle), "br" (for bottom right) + # - also specify padding from edge amount (optional). + # - Set opacity of watermark (png). + # * ADD BORDER + # * USE HEX WHEN SPECIFYING COLORS (eg: #ffffff) + # * SAVE IMAGE OR OUTPUT TO SCREEN + # + # + # ========================================================================# + + +class imageLib +{ + + private $fileName; + private $image; + protected $imageResized; + private $widthOriginal; # Always be the original width + private $heightOriginal; + private $width; # Current width (width after resize) + private $height; + private $imageSize; + private $fileExtension; + + private $isImage = false; + + private $debug = true; + private $errorArray = array(); + + private $forceStretch = true; + private $aggresiveSharpening = false; + + private $transparentArray = array('.png', '.gif'); + private $keepTransparency = true; + private $fillColorArray = array('r'=>255, 'g'=>255, 'b'=>255); + + private $sharpenArray = array('jpg'); + + private $psdReaderPath; + private $filterOverlayPath; + + private $isInterlace; + + private $captionBoxPositionArray = array(); + + private $fontDir = 'fonts'; + + private $cropFromTopPercent = 10; + + +## -------------------------------------------------------- + + function __construct($fileName) + # Author: Jarrod Oberto + # Date: 27-02-08 + # Purpose: Constructor + # Param in: $fileName: File name and path. + # Param out: n/a + # Reference: + # Notes: + # + { + if (!$this->testGDInstalled()) { if ($this->debug) { die('The GD Library is not installed.'); }else{ die(); }}; + + $this->initialise(); + + // *** Save the image file name. Only store this incase you want to display it + $this->fileName = $fileName; + $this->fileExtension = strtolower(strrchr($fileName, '.')); + + // *** Open up the file + $this->image = $this->openImage($fileName); + + + // *** Assign here so we don't modify the original + $this->imageResized = $this->image; + + // *** If file is an image + $this->isImage = $this->testIsImage(); + + if ($this->isImage) + { + // *** Get width and height + $this->width = imagesx($this->image); + $this->widthOriginal = imagesx($this->image); + $this->height = imagesy($this->image); + $this->heightOriginal = imagesy($this->image); + + + /* Added 15-09-08 + * Get the filesize using this build in method. + * Stores an array of size + * + * $this->imageSize[1] = width + * $this->imageSize[2] = height + * $this->imageSize[3] = width x height + * + */ + $this->imageSize = getimagesize($this->fileName); + + } else { + $this->errorArray[] = 'File is not an image'; + } + } + +## -------------------------------------------------------- + + private function initialise () { + + $this->psdReaderPath = dirname(__FILE__) . '/classPhpPsdReader.php'; + $this->filterOverlayPath = dirname(__FILE__) . '/filters'; + + // *** Set if image should be interlaced or not. + $this->isInterlace = false; + } + + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Resize +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + + public function resizeImage($newWidth, $newHeight, $option = 0, $sharpen = false, $autoRotate = true) + # Author: Jarrod Oberto + # Date: 27-02-08 + # Purpose: Resizes the image + # Param in: $newWidth: + # $newHeight: + # $option: 0 / exact = defined size; + # 1 / portrait = keep aspect set height; + # 2 / landscape = keep aspect set width; + # 3 / auto = auto; + # 4 / crop= resize and crop; + # + # $option can also be an array containing options for + # cropping. E.G., array('crop', 'r') + # + # This array only applies to 'crop' and the 'r' refers to + # "crop right". Other value include; tl, t, tr, l, m (default), + # r, bl, b, br, or you can specify your own co-ords (which + # isn't recommended. + # + # $sharpen: true: sharpen (jpg only); + # false: don't sharpen + # Param out: n/a + # Reference: + # Notes: To clarify the $option input: + # 0 = The exact height and width dimensions you set. + # 1 = Whatever height is passed in will be the height that + # is set. The width will be calculated and set automatically + # to a the value that keeps the original aspect ratio. + # 2 = The same but based on the width. We try make the image the + # biggest size we can while stil fitting inside the box size + # 3 = Depending whether the image is landscape or portrait, this + # will automatically determine whether to resize via + # dimension 1,2 or 0 + # 4 = Will resize and then crop the image for best fit + # + # forceStretch can be applied to options 1,2,3 and 4 + # + { + + // *** We can pass in an array of options to change the crop position + $cropPos = 'm'; + if (is_array($option) && strtolower($option[0]) == 'crop') { + $cropPos = $option[1]; # get the crop option + } else if (strpos($option, '-') !== false) { + // *** Or pass in a hyphen seperated option + $optionPiecesArray = explode('-', $option); + $cropPos = end($optionPiecesArray); + } + + // *** Check the option is valid + $option = $this->prepOption($option); + + // *** Make sure the file passed in is valid + if (!$this->image) { if ($this->debug) { die('file ' . $this->getFileName() .' is missing or invalid'); }else{ die(); }}; + + // *** Get optimal width and height - based on $option + $dimensionsArray = $this->getDimensions($newWidth, $newHeight, $option); + + $optimalWidth = $dimensionsArray['optimalWidth']; + $optimalHeight = $dimensionsArray['optimalHeight']; + + // *** Resample - create image canvas of x, y size + $this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight); + $this->keepTransparancy($optimalWidth, $optimalHeight, $this->imageResized); + imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height); + + + // *** If '4', then crop too + if ($option == 4 || $option == 'crop') { + + if (($optimalWidth >= $newWidth && $optimalHeight >= $newHeight)) { + $this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos); + } + } + + // *** If Rotate. + if ($autoRotate) { + + $exifData = $this->getExif(false); + if (count($exifData) > 0) { + + switch($exifData['orientation']) { + case 8: + $this->imageResized = imagerotate($this->imageResized,90,0); + break; + case 3: + $this->imageResized = imagerotate($this->imageResized,180,0); + break; + case 6: + $this->imageResized = imagerotate($this->imageResized,-90,0); + break; + } + } + } + + // *** Sharpen image (if jpg and the user wishes to do so) + if ($sharpen && in_array($this->fileExtension, $this->sharpenArray)) { + + // *** Sharpen + $this->sharpen(); + } + } + +## -------------------------------------------------------- + + public function cropImage($newWidth, $newHeight, $cropPos = 'm') + # Author: Jarrod Oberto + # Date: 08-09-11 + # Purpose: Crops the image + # Param in: $newWidth: crop with + # $newHeight: crop height + # $cropPos: Can be any of the following: + # tl, t, tr, l, m, r, bl, b, br, auto + # Or: + # a custom position such as '30x50' + # Param out: n/a + # Reference: + # Notes: + # + { + + // *** Make sure the file passed in is valid + if (!$this->image) { if ($this->debug) { die('file ' . $this->getFileName() .' is missing or invalid'); }else{ die(); }}; + + $this->imageResized = $this->image; + $this->crop($this->width, $this->height, $newWidth, $newHeight, $cropPos); + + } + +## -------------------------------------------------------- + + private function keepTransparancy($width, $height, $im) + # Author: Jarrod Oberto + # Date: 08-04-11 + # Purpose: Keep transparency for png and gif image + # Param in: + # Param out: n/a + # Reference: + # Notes: + # + { + // *** If PNG, perform some transparency retention actions (gif untested) + if (in_array($this->fileExtension, $this->transparentArray) && $this->keepTransparency) { + imagealphablending($im, false); + imagesavealpha($im, true); + $transparent = imagecolorallocatealpha($im, 255, 255, 255, 127); + imagefilledrectangle($im, 0, 0, $width, $height, $transparent); + } else { + $color = imagecolorallocate($im, $this->fillColorArray['r'], $this->fillColorArray['g'], $this->fillColorArray['b']); + imagefilledrectangle($im, 0, 0, $width, $height, $color); + } + } + +## -------------------------------------------------------- + + private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos) + # Author: Jarrod Oberto + # Date: 15-09-08 + # Purpose: Crops the image + # Param in: $newWidth: + # $newHeight: + # Param out: n/a + # Reference: + # Notes: + # + { + + // *** Get cropping co-ordinates + $cropArray = $this->getCropPlacing($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos); + $cropStartX = $cropArray['x']; + $cropStartY = $cropArray['y']; + + // *** Crop this bad boy + $crop = imagecreatetruecolor($newWidth , $newHeight); + $this->keepTransparancy($optimalWidth, $optimalHeight, $crop); + imagecopyresampled($crop, $this->imageResized, 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight , $newWidth, $newHeight); + + $this->imageResized = $crop; + + // *** Set new width and height to our variables + $this->width = $newWidth; + $this->height = $newHeight; + } + +## -------------------------------------------------------- + + private function getCropPlacing($optimalWidth, $optimalHeight, $newWidth, $newHeight, $pos='m') + # + # Author: Jarrod Oberto + # Date: July 11 + # Purpose: Set the cropping area. + # Params in: + # Params out: (array) the crop x and y co-ordinates. + # Notes: When specifying the exact pixel crop position (eg 10x15), be + # very careful as it's easy to crop out of the image leaving + # black borders. + # + { + $pos = strtolower($pos); + + // *** If co-ords have been entered + if (strstr($pos, 'x')) { + $pos = str_replace(' ', '', $pos); + + $xyArray = explode('x', $pos); + list($cropStartX, $cropStartY) = $xyArray; + + } else { + + switch ($pos) { + case 'tl': + $cropStartX = 0; + $cropStartY = 0; + break; + + case 't': + $cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 ); + $cropStartY = 0; + break; + + case 'tr': + $cropStartX = $optimalWidth - $newWidth; + $cropStartY = 0; + break; + + case 'l': + $cropStartX = 0; + $cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 ); + break; + + case 'm': + $cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 ); + $cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 ); + break; + + case 'r': + $cropStartX = $optimalWidth - $newWidth; + $cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 ); + break; + + case 'bl': + $cropStartX = 0; + $cropStartY = $optimalHeight - $newHeight; + break; + + case 'b': + $cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 ); + $cropStartY = $optimalHeight - $newHeight; + break; + + case 'br': + $cropStartX = $optimalWidth - $newWidth; + $cropStartY = $optimalHeight - $newHeight; + break; + + case 'auto': + // *** If image is a portrait crop from top, not center. v1.5 + if ($optimalHeight > $optimalWidth) { + $cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 ); + $cropStartY = ($this->cropFromTopPercent /100) * $optimalHeight; + } else { + + // *** Else crop from the center + $cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 ); + $cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 ); + } + break; + + default: + // *** Default to center + $cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 ); + $cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 ); + break; + } + } + + return array('x' => $cropStartX, 'y' => $cropStartY); + } + +## -------------------------------------------------------- + + private function getDimensions($newWidth, $newHeight, $option) + # Author: Jarrod Oberto + # Date: 17-11-09 + # Purpose: Get new image dimensions based on user specificaions + # Param in: $newWidth: + # $newHeight: + # Param out: Array of new width and height values + # Reference: + # Notes: If $option = 3 then this function is call recursivly + # + # To clarify the $option input: + # 0 = The exact height and width dimensions you set. + # 1 = Whatever height is passed in will be the height that + # is set. The width will be calculated and set automatically + # to a the value that keeps the original aspect ratio. + # 2 = The same but based on the width. + # 3 = Depending whether the image is landscape or portrait, this + # will automatically determine whether to resize via + # dimension 1,2 or 0. + # 4 = Resize the image as much as possible, then crop the + # remainder. + { + + switch (strval($option)) + { + case '0': + case 'exact': + $optimalWidth = $newWidth; + $optimalHeight= $newHeight; + break; + case '1': + case 'portrait': + $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight); + $optimalWidth = $dimensionsArray['optimalWidth']; + $optimalHeight = $dimensionsArray['optimalHeight']; + break; + case '2': + case 'landscape': + $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight); + $optimalWidth = $dimensionsArray['optimalWidth']; + $optimalHeight = $dimensionsArray['optimalHeight']; + break; + case '3': + case 'auto': + $dimensionsArray = $this->getSizeByAuto($newWidth, $newHeight); + $optimalWidth = $dimensionsArray['optimalWidth']; + $optimalHeight = $dimensionsArray['optimalHeight']; + break; + case '4': + case 'crop': + $dimensionsArray = $this->getOptimalCrop($newWidth, $newHeight); + $optimalWidth = $dimensionsArray['optimalWidth']; + $optimalHeight = $dimensionsArray['optimalHeight']; + break; + } + + return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight); + } + +## -------------------------------------------------------- + + private function getSizeByFixedHeight($newWidth, $newHeight) + { + // *** If forcing is off... + if (!$this->forceStretch) { + + // *** ...check if actual height is less than target height + if ($this->height < $newHeight) { + return array('optimalWidth' => $this->width, 'optimalHeight' => $this->height); + } + } + + $ratio = $this->width / $this->height; + + $newWidth = $newHeight * $ratio; + + //return $newWidth; + return array('optimalWidth' => $newWidth, 'optimalHeight' => $newHeight); + } + +## -------------------------------------------------------- + + private function getSizeByFixedWidth($newWidth, $newHeight) + { + // *** If forcing is off... + if (!$this->forceStretch) { + + // *** ...check if actual width is less than target width + if ($this->width < $newWidth) { + return array('optimalWidth' => $this->width, 'optimalHeight' => $this->height); + } + } + + $ratio = $this->height / $this->width; + + $newHeight = $newWidth * $ratio; + + //return $newHeight; + return array('optimalWidth' => $newWidth, 'optimalHeight' => $newHeight); + } + +## -------------------------------------------------------- + + private function getSizeByAuto($newWidth, $newHeight) + # Author: Jarrod Oberto + # Date: 19-08-08 + # Purpose: Depending on the height, choose to resize by 0, 1, or 2 + # Param in: The new height and new width + # Notes: + # + { + // *** If forcing is off... + if (!$this->forceStretch) { + + // *** ...check if actual size is less than target size + if ($this->width < $newWidth && $this->height < $newHeight) { + return array('optimalWidth' => $this->width, 'optimalHeight' => $this->height); + } + } + + if ($this->height < $this->width) + // *** Image to be resized is wider (landscape) + { + //$optimalWidth = $newWidth; + //$optimalHeight= $this->getSizeByFixedWidth($newWidth); + + $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight); + $optimalWidth = $dimensionsArray['optimalWidth']; + $optimalHeight = $dimensionsArray['optimalHeight']; + } + elseif ($this->height > $this->width) + // *** Image to be resized is taller (portrait) + { + //$optimalWidth = $this->getSizeByFixedHeight($newHeight); + //$optimalHeight= $newHeight; + + $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight); + $optimalWidth = $dimensionsArray['optimalWidth']; + $optimalHeight = $dimensionsArray['optimalHeight']; + } + else + // *** Image to be resizerd is a square + { + + if ($newHeight < $newWidth) { + //$optimalWidth = $newWidth; + //$optimalHeight= $this->getSizeByFixedWidth($newWidth); + $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight); + $optimalWidth = $dimensionsArray['optimalWidth']; + $optimalHeight = $dimensionsArray['optimalHeight']; + } else if ($newHeight > $newWidth) { + //$optimalWidth = $this->getSizeByFixedHeight($newHeight); + //$optimalHeight= $newHeight; + $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight); + $optimalWidth = $dimensionsArray['optimalWidth']; + $optimalHeight = $dimensionsArray['optimalHeight']; + } else { + // *** Sqaure being resized to a square + $optimalWidth = $newWidth; + $optimalHeight= $newHeight; + } + } + + return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight); + } + +## -------------------------------------------------------- + + private function getOptimalCrop($newWidth, $newHeight) + # Author: Jarrod Oberto + # Date: 17-11-09 + # Purpose: Get optimal crop dimensions + # Param in: width and height as requested by user (fig 3) + # Param out: Array of optimal width and height (fig 2) + # Reference: + # Notes: The optimal width and height return are not the same as the + # same as the width and height passed in. For example: + # + # + # |-----------------| |------------| |-------| + # | | => |**| |**| => | | + # | | |**| |**| | | + # | | |------------| |-------| + # |-----------------| + # original optimal crop + # size size size + # Fig 1 2 3 + # + # 300 x 250 150 x 125 150 x 100 + # + # The optimal size is the smallest size (that is closest to the crop size) + # while retaining proportion/ratio. + # + # The crop size is the optimal size that has been cropped on one axis to + # make the image the exact size specified by the user. + # + # * represent cropped area + # + { + + // *** If forcing is off... + if (!$this->forceStretch) { + + // *** ...check if actual size is less than target size + if ($this->width < $newWidth && $this->height < $newHeight) { + return array('optimalWidth' => $this->width, 'optimalHeight' => $this->height); + } + } + + $heightRatio = $this->height / $newHeight; + $widthRatio = $this->width / $newWidth; + + if ($heightRatio < $widthRatio) { + $optimalRatio = $heightRatio; + } else { + $optimalRatio = $widthRatio; + } + + $optimalHeight = round( $this->height / $optimalRatio ); + $optimalWidth = round( $this->width / $optimalRatio ); + + return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight); + } + +## -------------------------------------------------------- + + private function sharpen() + # Author: Jarrod Oberto + # Date: 08 04 2011 + # Purpose: Sharpen image + # Param in: n/a + # Param out: n/a + # Reference: + # Notes: + # Credit: Incorporates Joe Lencioni (August 6, 2008) code + { + + if (version_compare(PHP_VERSION, '5.1.0') >= 0) { + + // *** + if ($this->aggresiveSharpening) { # A more aggressive sharpening solution + + $sharpenMatrix = array( + array( -1, -1, -1 ), + array( -1, 16, -1 ), + array( -1, -1, -1 ) + ); + $divisor = 8; + $offset = 0; + + imageconvolution($this->imageResized, $sharpenMatrix, $divisor, $offset); + } + else # More subtle and personally more desirable + { + $sharpness = $this->findSharp($this->widthOriginal, $this->width); + + $sharpenMatrix = array( + array(-1, -2, -1), + array(-2, $sharpness + 12, -2), //Lessen the effect of a filter by increasing the value in the center cell + array(-1, -2, -1) + ); + $divisor = $sharpness; // adjusts brightness + $offset = 0; + imageconvolution($this->imageResized, $sharpenMatrix, $divisor, $offset); + } + } + else + { + if ($this->debug) { die('Sharpening required PHP 5.1.0 or greater.'); } + } + } + + ## -------------------------------------------------------- + + private function sharpen2($level) + { + $sharpenMatrix = array( + array($level, $level, $level), + array($level, (8*$level)+1, $level), //Lessen the effect of a filter by increasing the value in the center cell + array($level, $level, $level) + ); + } + +## -------------------------------------------------------- + + private function findSharp($orig, $final) + # Author: Ryan Rud (http://adryrun.com) + # Purpose: Find optimal sharpness + # Param in: n/a + # Param out: n/a + # Reference: + # Notes: + # + { + $final = $final * (750.0 / $orig); + $a = 52; + $b = -0.27810650887573124; + $c = .00047337278106508946; + + $result = $a + $b * $final + $c * $final * $final; + + return max(round($result), 0); + } + +## -------------------------------------------------------- + + private function prepOption($option) + # Author: Jarrod Oberto + # Purpose: Prep option like change the passed in option to lowercase + # Param in: (str/int) $option: eg. 'exact', 'crop'. 0, 4 + # Param out: lowercase string + # Reference: + # Notes: + # + { + if (is_array($option)) { + if (strtolower($option[0]) == 'crop' && count($option) == 2) { + return 'crop'; + } else { + die('Crop resize option array is badly formatted.'); + } + } else if (strpos($option, 'crop') !== false) { + return 'crop'; + } + + if (is_string($option)) { + return strtolower($option); + } + + return $option; + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Presets +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + +# +# Preset are pre-defined templates you can apply to your image. +# +# These are inteded to be applied to thumbnail images. +# + + + public function borderPreset($preset) + { + switch ($preset) + { + + case 'simple': + $this->addBorder(7, '#fff'); + $this->addBorder(6, '#f2f1f0'); + $this->addBorder(2, '#fff'); + $this->addBorder(1, '#ccc'); + break; + default: + break; + } + + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Draw border +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function addBorder($thickness = 1, $rgbArray = array(255, 255, 255)) + # Author: Jarrod Oberto + # Date: 05-05-11 + # Purpose: Add a border to the image + # Param in: + # Param out: + # Reference: + # Notes: This border is added to the INSIDE of the image + # + { + if ($this->imageResized) { + + $rgbArray = $this->formatColor($rgbArray); + $r = $rgbArray['r']; + $g = $rgbArray['g']; + $b = $rgbArray['b']; + + + $x1 = 0; + $y1 = 0; + $x2 = ImageSX($this->imageResized) - 1; + $y2 = ImageSY($this->imageResized) - 1; + + $rgbArray = ImageColorAllocate($this->imageResized, $r, $g, $b); + + + for($i = 0; $i < $thickness; $i++) { + ImageRectangle($this->imageResized, $x1++, $y1++, $x2--, $y2--, $rgbArray); + } + } + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Gray Scale +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function greyScale() + # Author: Jarrod Oberto + # Date: 07-05-2011 + # Purpose: Make image greyscale + # Param in: n/a + # Param out: + # Reference: + # Notes: + # + { + if ($this->imageResized) { + imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE); + } + + } + + ## -------------------------------------------------------- + + public function greyScaleEnhanced() + # Author: Jarrod Oberto + # Date: 07-05-2011 + # Purpose: Make image greyscale + # Param in: n/a + # Param out: + # Reference: + # Notes: + # + { + if ($this->imageResized) { + imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE); + imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -15); + imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, 2); + $this->sharpen($this->width); + } + } + + ## -------------------------------------------------------- + + public function greyScaleDramatic() + # Alias of gd_filter_monopin + { + $this->gd_filter_monopin(); + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Black 'n White +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function blackAndWhite() + # Author: Jarrod Oberto + # Date: 07-05-2011 + # Purpose: Make image black and white + # Param in: n/a + # Param out: + # Reference: + # Notes: + # + { + if ($this->imageResized) { + + imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE); + imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -1000); + } + + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Negative +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function negative() + # Author: Jarrod Oberto + # Date: 07-05-2011 + # Purpose: Make image negative + # Param in: n/a + # Param out: + # Reference: + # Notes: + # + { + if ($this->imageResized) { + + imagefilter($this->imageResized, IMG_FILTER_NEGATE); + } + + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Sepia +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function sepia() + # Author: Jarrod Oberto + # Date: 07-05-2011 + # Purpose: Make image sepia + # Param in: n/a + # Param out: + # Reference: + # Notes: + # + { + if ($this->imageResized) { + imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE); + imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, -10); + imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -20); + imagefilter($this->imageResized, IMG_FILTER_COLORIZE, 60, 30, -15); + } + } + + ## -------------------------------------------------------- + + public function sepia2() + { + if ($this->imageResized) { + + $total = imagecolorstotal( $this->imageResized ); + for ( $i = 0; $i < $total; $i++ ) { + $index = imagecolorsforindex( $this->imageResized, $i ); + $red = ( $index["red"] * 0.393 + $index["green"] * 0.769 + $index["blue"] * 0.189 ) / 1.351; + $green = ( $index["red"] * 0.349 + $index["green"] * 0.686 + $index["blue"] * 0.168 ) / 1.203; + $blue = ( $index["red"] * 0.272 + $index["green"] * 0.534 + $index["blue"] * 0.131 ) / 2.140; + imagecolorset( $this->imageResized, $i, $red, $green, $blue ); + } + + + } + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Vintage +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function vintage() + # Alias of gd_filter_monopin + { + $this->gd_filter_vintage(); + } + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Presets By Marc Hibbins +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + + /** Apply 'Monopin' preset */ + public function gd_filter_monopin() + { + if ($this->imageResized) { + imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE); + imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, -15); + imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -15); + $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'vignette', 100); + } + } + + ## -------------------------------------------------------- + + public function gd_filter_vintage() + { + if ($this->imageResized) { + $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'vignette', 45); + imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, 20); + imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -35); + imagefilter($this->imageResized, IMG_FILTER_COLORIZE, 60, -10, 35); + imagefilter($this->imageResized, IMG_FILTER_SMOOTH, 7); + $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'scratch', 10); + } + } + + ## -------------------------------------------------------- + + /** Apply a PNG overlay */ + private function gd_apply_overlay($im, $type, $amount) + # + # Original Author: Marc Hibbins + # License: Attribution-ShareAlike 3.0 + # Purpose: + # Params in: + # Params out: + # Notes: + # + { + $width = imagesx($im); + $height = imagesy($im); + $filter = imagecreatetruecolor($width, $height); + + imagealphablending($filter, false); + imagesavealpha($filter, true); + + $transparent = imagecolorallocatealpha($filter, 255, 255, 255, 127); + imagefilledrectangle($filter, 0, 0, $width, $height, $transparent); + + // *** Resize overlay + $overlay = $this->filterOverlayPath . '/' . $type . '.png'; + $png = imagecreatefrompng($overlay); + imagecopyresampled($filter, $png, 0, 0, 0, 0, $width, $height, imagesx($png), imagesy($png)); + + $comp = imagecreatetruecolor($width, $height); + imagecopy($comp, $im, 0, 0, 0, 0, $width, $height); + imagecopy($comp, $filter, 0, 0, 0, 0, $width, $height); + imagecopymerge($im, $comp, 0, 0, 0, 0, $width, $height, $amount); + + imagedestroy($comp); + return $im; + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Colorise +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function image_colorize($rgb) + { + imageTrueColorToPalette($this->imageResized,true,256); + $numColors = imageColorsTotal($this->imageResized); + + for ($x = 0; $x < $numColors; $x++) { + list($r,$g,$b) = array_values(imageColorsForIndex($this->imageResized,$x)); + + // calculate grayscale in percent + $grayscale = ($r + $g + $b) / 3 / 0xff; + + imageColorSet($this->imageResized,$x, + $grayscale * $rgb[0], + $grayscale * $rgb[1], + $grayscale * $rgb[2] + ); + + } + + return true; + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Reflection +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function addReflection($reflectionHeight = 50, $startingTransparency = 30, $inside = false, $bgColor = '#fff', $stretch=false, $divider = 0) + { + + // *** Convert color + $rgbArray = $this->formatColor($bgColor); + $r = $rgbArray['r']; + $g = $rgbArray['g']; + $b = $rgbArray['b']; + + $im = $this->imageResized; + $li = imagecreatetruecolor($this->width, 1); + + $bgc = imagecolorallocate($li, $r, $g, $b); + imagefilledrectangle($li, 0, 0, $this->width, 1, $bgc); + + $bg = imagecreatetruecolor($this->width, $reflectionHeight); + $wh = imagecolorallocate($im, 255, 255, 255); + + $im = imagerotate($im, -180, $wh); + imagecopyresampled($bg, $im, 0, 0, 0, 0, $this->width, $this->height, $this->width, $this->height); + + $im = $bg; + + $bg = imagecreatetruecolor($this->width, $reflectionHeight); + + for ($x = 0; $x < $this->width; $x++) { + imagecopy($bg, $im, $x, 0, $this->width-$x -1, 0, 1, $reflectionHeight); + } + $im = $bg; + + $transaprencyAmount = $this->invertTransparency($startingTransparency, 100); + + + // *** Fade + if ($stretch) { + $step = 100/($reflectionHeight + $startingTransparency); + } else{ + $step = 100/$reflectionHeight; + } + for($i=0; $i<=$reflectionHeight; $i++){ + + if($startingTransparency>100) $startingTransparency = 100; + if($startingTransparency< 1) $startingTransparency = 1; + imagecopymerge($bg, $li, 0, $i, 0, 0, $this->width, 1, $startingTransparency); + $startingTransparency+=$step; + } + + // *** Apply fade + imagecopymerge($im, $li, 0, 0, 0, 0, $this->width, $divider, 100); // Divider + + + // *** width, height of reflection. + $x = imagesx($im); + $y = imagesy($im); + + + // *** Determines if the reflection should be displayed inside or outside the image + if ($inside) { + + // Create new blank image with sizes. + $final = imagecreatetruecolor($this->width, $this->height); + + imagecopymerge ($final, $this->imageResized, 0, 0, 0, $reflectionHeight, $this->width, $this->height - $reflectionHeight, 100); + imagecopymerge ($final, $im, 0, $this->height - $reflectionHeight, 0, 0, $x, $y, 100); + + } else { + + // Create new blank image with sizes. + $final = imagecreatetruecolor($this->width, $this->height + $y); + + imagecopymerge ($final, $this->imageResized, 0, 0, 0, 0, $this->width, $this->height, 100); + imagecopymerge ($final, $im, 0, $this->height, 0, 0, $x, $y, 100); + } + + $this->imageResized = $final; + + imagedestroy($li); + imagedestroy($im); + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Rotate +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function rotate($value = 90, $bgColor = 'transparent') + # Author: Jarrod Oberto + # Date: 07-05-2011 + # Purpose: Rotate image + # Param in: (mixed) $degrees: (int) number of degress to rotate image + # (str) param "left": rotate left + # (str) param "right": rotate right + # (str) param "upside": upside-down image + # Param out: + # Reference: + # Notes: The default direction of imageRotate() is counter clockwise. + # + { + if ($this->imageResized) { + + if (is_integer($value)) { + $degrees = $value; + } + + // *** Convert color + $rgbArray = $this->formatColor($bgColor); + $r = $rgbArray['r']; + $g = $rgbArray['g']; + $b = $rgbArray['b']; + if (isset($rgbArray['a'])) {$a = $rgbArray['a']; } + + if (is_string($value)) { + + $value = strtolower($value); + + switch ($value) { + case 'left': + $degrees = 90; + break; + case 'right': + $degrees = 270; + break; + case 'upside': + $degrees = 180; + break; + default: + break; + } + } + + // *** The default direction of imageRotate() is counter clockwise + // * This makes it clockwise + $degrees = 360 - $degrees; + + // *** Create background color + $bg = ImageColorAllocateAlpha($this->imageResized, $r, $g, $b, $a); + + // *** Fill with background + ImageFill($this->imageResized, 0, 0 , $bg); + + // *** Rotate + $this->imageResized = imagerotate($this->imageResized, $degrees, $bg); // Rotate 45 degrees and allocated the transparent colour as the one to make transparent (obviously) + + // Ensure alpha transparency + ImageSaveAlpha($this->imageResized,true); + + } + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Round corners +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function roundCorners($radius = 5, $bgColor = 'transparent') + # Author: Jarrod Oberto + # Date: 19-05-2011 + # Purpose: Create rounded corners on your image + # Param in: (int) radius = the amount of curvature + # (mixed) $bgColor = the corner background color + # Param out: n/a + # Reference: + # Notes: + # + { + + // *** Check if the user wants transparency + $isTransparent = false; + if (!is_array($bgColor)) { + if (strtolower($bgColor) == 'transparent') { + $isTransparent = true; + } + } + + + // *** If we use transparency, we need to color our curved mask with a unique color + if ($isTransparent) { + $bgColor = $this->findUnusedGreen(); + } + + // *** Convert color + $rgbArray = $this->formatColor($bgColor); + $r = $rgbArray['r']; + $g = $rgbArray['g']; + $b = $rgbArray['b']; + if (isset($rgbArray['a'])) {$a = $rgbArray['a']; } + + + + // *** Create top-left corner mask (square) + $cornerImg = imagecreatetruecolor($radius, $radius); + //$cornerImg = imagecreate($radius, $radius); + + //imagealphablending($cornerImg, true); + //imagesavealpha($cornerImg, true); + + //imagealphablending($this->imageResized, false); + //imagesavealpha($this->imageResized, true); + + // *** Give it a color + $maskColor = imagecolorallocate($cornerImg, 0, 0, 0); + + + // *** Replace the mask color (black) to transparent + imagecolortransparent($cornerImg, $maskColor); + + + // *** Create the image background color + $imagebgColor = imagecolorallocate($cornerImg, $r, $g, $b); + + + // *** Fill the corner area to the user defined color + imagefill($cornerImg, 0, 0, $imagebgColor); + + + imagefilledellipse($cornerImg, $radius, $radius, $radius * 2, $radius * 2, $maskColor ); + + + // *** Map to top left corner + imagecopymerge($this->imageResized, $cornerImg, 0, 0, 0, 0, $radius, $radius, 100); #tl + + // *** Map rounded corner to other corners by rotating and applying the mask + $cornerImg = imagerotate($cornerImg, 90, 0); + imagecopymerge($this->imageResized, $cornerImg, 0, $this->height - $radius, 0, 0, $radius, $radius, 100); #bl + + $cornerImg = imagerotate($cornerImg, 90, 0); + imagecopymerge($this->imageResized, $cornerImg, $this->width - $radius, $this->height - $radius, 0, 0, $radius, $radius, 100); #br + + $cornerImg = imagerotate($cornerImg, 90, 0); + imagecopymerge($this->imageResized, $cornerImg, $this->width - $radius, 0, 0, 0, $radius, $radius, 100); #tr + + + // *** If corners are to be transparent, we fill our chromakey color as transparent. + if ($isTransparent) { + //imagecolortransparent($this->imageResized, $imagebgColor); + $this->imageResized = $this->transparentImage($this->imageResized); + imagesavealpha($this->imageResized, true); + } + + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Shadow +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function addShadow($shadowAngle=45, $blur=15, $bgColor='transparent') + # + # Author: Jarrod Oberto (Adapted from Pascal Naidon) + # Ref: http://www.les-stooges.org/pascal/webdesign/vignettes/index.php?la=en + # Purpose: Add a drop shadow to your image + # Params in: (int) $angle: the angle of the shadow + # (int) $blur: the blur distance + # (mixed) $bgColor: the color of the background + # Params out: + # Notes: + # + { + // *** A higher number results in a smoother shadow + define('STEPS', $blur*2); + + // *** Set the shadow distance + $shadowDistance = $blur*0.25; + + // *** Set blur width and height + $blurWidth = $blurHeight = $blur; + + + if ($shadowAngle == 0) { + $distWidth = 0; + $distHeight = 0; + } else { + $distWidth = $shadowDistance * cos(deg2rad($shadowAngle)); + $distHeight = $shadowDistance * sin(deg2rad($shadowAngle)); + } + + + // *** Convert color + if (strtolower($bgColor) != 'transparent') { + $rgbArray = $this->formatColor($bgColor); + $r0 = $rgbArray['r']; + $g0 = $rgbArray['g']; + $b0 = $rgbArray['b']; + } + + + $image = $this->imageResized; + $width = $this->width; + $height = $this->height; + + + $newImage = imagecreatetruecolor($width, $height); + imagecopyresampled($newImage, $image, 0, 0, 0, 0, $width, $height, $width, $height); + + + // *** RGB + $rgb = imagecreatetruecolor($width+$blurWidth,$height+$blurHeight); + $colour = imagecolorallocate($rgb, 0, 0, 0); + imagefilledrectangle($rgb, 0, 0, $width+$blurWidth, $height+$blurHeight, $colour); + $colour = imagecolorallocate($rgb, 255, 255, 255); + //imagefilledrectangle($rgb, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, $width+$blurWidth*0.5-$distWidth, $height+$blurWidth*0.5-$distHeight, $colour); + imagefilledrectangle($rgb, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, $width+$blurWidth*0.5-$distWidth, $height+$blurWidth*0.5-$distHeight, $colour); + //imagecopymerge($rgb, $newImage, 1+$blurWidth*0.5-$distWidth, 1+$blurHeight*0.5-$distHeight, 0,0, $width, $height, 100); + imagecopymerge($rgb, $newImage, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, 0,0, $width+$blurWidth, $height+$blurHeight, 100); + + + // *** Shadow (alpha) + $shadow = imagecreatetruecolor($width+$blurWidth,$height+$blurHeight); + imagealphablending($shadow, false); + $colour = imagecolorallocate($shadow, 0, 0, 0); + imagefilledrectangle($shadow, 0, 0, $width+$blurWidth, $height+$blurHeight, $colour); + + + for ($i=0;$i<=STEPS;$i++) { + + $t = ((1.0*$i)/STEPS); + $intensity = 255*$t*$t; + + $colour = imagecolorallocate($shadow, $intensity, $intensity, $intensity); + $points = array( + $blurWidth*$t, $blurHeight, // Point 1 (x, y) + $blurWidth, $blurHeight*$t, // Point 2 (x, y) + $width, $blurHeight*$t, // Point 3 (x, y) + $width+$blurWidth*(1-$t), $blurHeight, // Point 4 (x, y) + $width+$blurWidth*(1-$t), $height, // Point 5 (x, y) + $width, $height+$blurHeight*(1-$t), // Point 6 (x, y) + $blurWidth, $height+$blurHeight*(1-$t), // Point 7 (x, y) + $blurWidth*$t, $height // Point 8 (x, y) + ); + imagepolygon($shadow, $points, 8, $colour); + } + + for($i=0;$i<=STEPS;$i++) { + + $t = ((1.0*$i)/STEPS); + $intensity = 255*$t*$t; + + $colour = imagecolorallocate($shadow, $intensity, $intensity, $intensity); + imagefilledarc($shadow, $blurWidth-1, $blurHeight-1, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 180, 268, $colour, IMG_ARC_PIE); + imagefilledarc($shadow, $width, $blurHeight-1, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 270, 358, $colour, IMG_ARC_PIE); + imagefilledarc($shadow, $width, $height, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 0, 90, $colour, IMG_ARC_PIE); + imagefilledarc($shadow, $blurWidth-1, $height, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 90, 180, $colour, IMG_ARC_PIE); + } + + + $colour = imagecolorallocate($shadow, 255, 255, 255); + imagefilledrectangle($shadow, $blurWidth, $blurHeight, $width, $height, $colour); + imagefilledrectangle($shadow, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, $width+$blurWidth*0.5-1-$distWidth, $height+$blurHeight*0.5-1-$distHeight, $colour); + + + // *** The magic + imagealphablending($rgb, false); + + for ($theX=0;$theX> 16) & 0xFF; + $g = ($colArray >> 8) & 0xFF; + $b = $colArray & 0xFF; + + // *** Get the alpha value for every pixel of the shadow image + $colArray = imagecolorat($shadow,$theX,$theY); + $a = $colArray & 0xFF; + $a = 127-floor($a/2); + $t = $a/128.0; + + // *** Create color + if(strtolower($bgColor) == 'transparent') { + $myColour = imagecolorallocatealpha($rgb,$r,$g,$b,$a); + } else { + $myColour = imagecolorallocate($rgb,$r*(1.0-$t)+$r0*$t,$g*(1.0-$t)+$g0*$t,$b*(1.0-$t)+$b0*$t); + } + + // *** Add color to new rgb image + imagesetpixel($rgb, $theX, $theY, $myColour); + } + } + + imagealphablending($rgb, true); + imagesavealpha($rgb, true); + + $this->imageResized = $rgb; + + imagedestroy($image); + imagedestroy($newImage); + imagedestroy($shadow); + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Add Caption Box +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function addCaptionBox($side='b', $thickness=50, $padding=0, $bgColor='#000', $transaprencyAmount=30) + # + # Author: Jarrod Oberto + # Date: 26 May 2011 + # Purpose: Add a caption box + # Params in: (str) $side: the side to add the caption box (t, r, b, or l). + # (int) $thickness: how thick you want the caption box to be. + # (mixed) $bgColor: The color of the caption box. + # (int) $transaprencyAmount: The amount of transparency to be + # applied. + # Params out: n/a + # Notes: + # + { + $side = strtolower($side); + + // *** Convert color + $rgbArray = $this->formatColor($bgColor); + $r = $rgbArray['r']; + $g = $rgbArray['g']; + $b = $rgbArray['b']; + + $positionArray = $this->calculateCaptionBoxPosition($side, $thickness, $padding); + + // *** Store incase we want to use method addTextToCaptionBox() + $this->captionBoxPositionArray = $positionArray; + + + $transaprencyAmount = $this->invertTransparency($transaprencyAmount, 127, false); + $transparent = imagecolorallocatealpha($this->imageResized, $r, $g, $b, $transaprencyAmount); + imagefilledrectangle($this->imageResized, $positionArray['x1'], $positionArray['y1'], $positionArray['x2'], $positionArray['y2'], $transparent); + } + + ## -------------------------------------------------------- + + public function addTextToCaptionBox($text, $fontColor='#fff', $fontSize = 12, $angle = 0, $font = null) + # + # Author: Jarrod Oberto + # Date: 03 Aug 11 + # Purpose: Simplify adding text to a caption box by automatically + # locating the center of the caption box + # Params in: The usually text paams (less a couple) + # Params out: n/a + # Notes: + # + { + + // *** Get the caption box measurements + if (count($this->captionBoxPositionArray) == 4) { + $x1 = $this->captionBoxPositionArray['x1']; + $x2 = $this->captionBoxPositionArray['x2']; + $y1 = $this->captionBoxPositionArray['y1']; + $y2 = $this->captionBoxPositionArray['y2']; + } else { + if ($this->debug) { die('No caption box found.'); }else{ return false; } + } + + + // *** Get text font + $font = $this->getTextFont($font); + + // *** Get text size + $textSizeArray = $this->getTextSize($fontSize, $angle, $font, $text); + $textWidth = $textSizeArray['width']; + $textHeight = $textSizeArray['height']; + + // *** Find the width/height middle points + $boxXMiddle = (($x2 - $x1) / 2); + $boxYMiddle = (($y2 - $y1) / 2); + + // *** Box middle - half the text width/height + $xPos = ($x1 + $boxXMiddle) - ($textWidth/2); + $yPos = ($y1 + $boxYMiddle) - ($textHeight/2); + + $pos = $xPos . 'x' . $yPos; + + $this->addText($text, $pos, $padding = 0, $fontColor, $fontSize, $angle, $font); + + } + + ## -------------------------------------------------------- + + private function calculateCaptionBoxPosition($side, $thickness, $padding) + { + $positionArray = array(); + + switch ($side) { + case 't': + $positionArray['x1'] = 0; + $positionArray['y1'] = $padding; + $positionArray['x2'] = $this->width; + $positionArray['y2'] = $thickness + $padding; + break; + case 'r': + $positionArray['x1'] = $this->width - $thickness - $padding; + $positionArray['y1'] = 0; + $positionArray['x2'] = $this->width - $padding; + $positionArray['y2'] = $this->height; + break; + case 'b': + $positionArray['x1'] = 0; + $positionArray['y1'] = $this->height - $thickness - $padding; + $positionArray['x2'] = $this->width; + $positionArray['y2'] = $this->height - $padding; + break; + case 'l': + $positionArray['x1'] = $padding; + $positionArray['y1'] = 0; + $positionArray['x2'] = $thickness + $padding; + $positionArray['y2'] = $this->height; + break; + + default: + break; + } + + return $positionArray; + + } + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Get EXIF Data +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function getExif($debug=true) + # Author: Jarrod Oberto + # Date: 07-05-2011 + # Purpose: Get image EXIF data + # Param in: n/a + # Param out: An associate array of EXIF data + # Reference: + # Notes: + # 23 May 13 : added orientation flag -jco + # + { + + if (!$this->debug || !$debug) { $debug = false; } + + // *** Check all is good - check the EXIF library exists and the file exists, too. + if (!$this->testEXIFInstalled()) { if ($debug) { die('The EXIF Library is not installed.'); }else{ return array(); }}; + if (!file_exists($this->fileName)) { if ($debug) { die('Image not found.'); }else{ return array(); }}; + if ($this->fileExtension != '.jpg') { if ($debug) { die('Metadata not supported for this image type.'); }else{ return array(); }}; + $exifData = exif_read_data($this->fileName, 'IFD0'); + + // *** Format the apperture value + $ev = isset($exifData['ApertureValue']) ? $exifData['ApertureValue'] : ''; + $apPeicesArray = explode('/', $ev); + if (count($apPeicesArray) == 2) { + $apertureValue = round($apPeicesArray[0] / $apPeicesArray[1], 2, PHP_ROUND_HALF_DOWN) . ' EV'; + } else { $apertureValue = '';} + + // *** Format the focal length + $focalLength = isset($exifData['FocalLength']) ? $exifData['FocalLength'] : ''; + $flPeicesArray = explode('/', $focalLength); + if (count($flPeicesArray) == 2) { + $focalLength = $flPeicesArray[0] / $flPeicesArray[1] . '.0 mm'; + } else { $focalLength = '';} + + // *** Format fNumber + $fNumber = isset($exifData['FNumber']) ? $exifData['FNumber'] : ''; + $fnPeicesArray = explode('/', $fNumber); + if (count($fnPeicesArray) == 2) { + $fNumber = $fnPeicesArray[0] / $fnPeicesArray[1]; + } else { $fNumber = '';} + + // *** Resolve ExposureProgram + if (isset($exifData['ExposureProgram'])) { $ep = $exifData['ExposureProgram']; } + if (isset($ep)) { $ep = $this->resolveExposureProgram($ep); } + + + // *** Resolve MeteringMode + $mm = isset($exifData['MeteringMode']) ? $exifData['MeteringMode'] : ''; + $mm = $this->resolveMeteringMode($mm); + + // *** Resolve Flash + $flash = isset($exifData['Flash']) ? $exifData['Flash'] : ''; + $flash = $this->resolveFlash($flash); + + + if (isset($exifData['Make'])) { + $exifDataArray['make'] = $exifData['Make']; + } else { $exifDataArray['make'] = ''; } + + if (isset($exifData['Model'])) { + $exifDataArray['model'] = $exifData['Model']; + } else { $exifDataArray['model'] = ''; } + + if (isset($exifData['DateTime'])) { + $exifDataArray['date'] = $exifData['DateTime']; + } else { $exifDataArray['date'] = ''; } + + if (isset($exifData['ExposureTime'])) { + $exifDataArray['exposure time'] = $exifData['ExposureTime'] . ' sec.'; + } else { $exifDataArray['exposure time'] = ''; } + + if ($apertureValue != '') { + $exifDataArray['aperture value'] = $apertureValue; + } else { $exifDataArray['aperture value'] = ''; } + + if (isset($exifData['COMPUTED']['ApertureFNumber'])) { + $exifDataArray['f-stop'] = $exifData['COMPUTED']['ApertureFNumber']; + } else { $exifDataArray['f-stop'] = ''; } + + if (isset($exifData['FNumber'])) { + $exifDataArray['fnumber'] = $exifData['FNumber']; + } else { $exifDataArray['fnumber'] = ''; } + + if ($fNumber != '') { + $exifDataArray['fnumber value'] = $fNumber; + } else { $exifDataArray['fnumber value'] = ''; } + + if (isset($exifData['ISOSpeedRatings'])) { + $exifDataArray['iso'] = $exifData['ISOSpeedRatings']; + } else { $exifDataArray['iso'] = ''; } + + if ($focalLength != '') { + $exifDataArray['focal length'] = $focalLength; + } else { $exifDataArray['focal length'] = ''; } + + if (isset($ep)) { + $exifDataArray['exposure program'] = $ep; + } else { $exifDataArray['exposure program'] = ''; } + + if ($mm != '') { + $exifDataArray['metering mode'] = $mm; + } else { $exifDataArray['metering mode'] = ''; } + + if ($flash != '') { + $exifDataArray['flash status'] = $flash; + } else { $exifDataArray['flash status'] = ''; } + + if (isset($exifData['Artist'])) { + $exifDataArray['creator'] = $exifData['Artist'] ; + } else { $exifDataArray['creator'] = ''; } + + if (isset($exifData['Copyright'])) { + $exifDataArray['copyright'] = $exifData['Copyright']; + } else { $exifDataArray['copyright'] = ''; } + + // *** Orientation + if (isset($exifData['Orientation'])) { + $exifDataArray['orientation'] = $exifData['Orientation']; + } else { $exifDataArray['orientation'] = ''; } + + return $exifDataArray; + } + + ## -------------------------------------------------------- + + private function resolveExposureProgram($ep) + { + switch ($ep) { + case 0: + $ep = ''; + break; + case 1: + $ep = 'manual'; + break; + case 2: + $ep = 'normal program'; + break; + case 3: + $ep = 'aperture priority'; + break; + case 4: + $ep = 'shutter priority'; + break; + case 5: + $ep = 'creative program'; + break; + case 6: + $ep = 'action program'; + break; + case 7: + $ep = 'portrait mode'; + break; + case 8: + $ep = 'landscape mode'; + break; + + default: + break; + } + + return $ep; + } + + ## -------------------------------------------------------- + + private function resolveMeteringMode($mm) + { + switch ($mm) { + case 0: + $mm = 'unknown'; + break; + case 1: + $mm = 'average'; + break; + case 2: + $mm = 'center weighted average'; + break; + case 3: + $mm = 'spot'; + break; + case 4: + $mm = 'multi spot'; + break; + case 5: + $mm = 'pattern'; + break; + case 6: + $mm = 'partial'; + break; + case 255: + $mm = 'other'; + break; + + default: + break; + } + + return $mm; + } + + ## -------------------------------------------------------- + + private function resolveFlash($flash) + { + switch ($flash) { + case 0: + $flash = 'flash did not fire'; + break; + case 1: + $flash = 'flash fired'; + break; + case 5: + $flash = 'strobe return light not detected'; + break; + case 7: + $flash = 'strobe return light detected'; + break; + case 9: + $flash = 'flash fired, compulsory flash mode'; + break; + case 13: + $flash = 'flash fired, compulsory flash mode, return light not detected'; + break; + case 15: + $flash = 'flash fired, compulsory flash mode, return light detected'; + break; + case 16: + $flash = 'flash did not fire, compulsory flash mode'; + break; + case 24: + $flash = 'flash did not fire, auto mode'; + break; + case 25: + $flash = 'flash fired, auto mode'; + break; + case 29: + $flash = 'flash fired, auto mode, return light not detected'; + break; + case 31: + $flash = 'flash fired, auto mode, return light detected'; + break; + case 32: + $flash = 'no flash function'; + break; + case 65: + $flash = 'flash fired, red-eye reduction mode'; + break; + case 69: + $flash = 'flash fired, red-eye reduction mode, return light not detected'; + break; + case 71: + $flash = 'flash fired, red-eye reduction mode, return light detected'; + break; + case 73: + $flash = 'flash fired, compulsory flash mode, red-eye reduction mode'; + break; + case 77: + $flash = 'flash fired, compulsory flash mode, red-eye reduction mode, return light not detected'; + break; + case 79: + $flash = 'flash fired, compulsory flash mode, red-eye reduction mode, return light detected'; + break; + case 89: + $flash = 'flash fired, auto mode, red-eye reduction mode'; + break; + case 93: + $flash = 'flash fired, auto mode, return light not detected, red-eye reduction mode'; + break; + case 95: + $flash = 'flash fired, auto mode, return light detected, red-eye reduction mode'; + break; + + default: + break; + } + + return $flash; + + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Get IPTC Data +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Write IPTC Data +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function writeIPTCcaption($value) + # Caption + { + $this->writeIPTC(120, $value); + } + + ## -------------------------------------------------------- + + public function writeIPTCwriter($value) + { + //$this->writeIPTC(65, $value); + } + + ## -------------------------------------------------------- + + private function writeIPTC($dat, $value) + { + + # LIMIT TO JPG + + $caption_block = $this->iptc_maketag(2, $dat, $value); + $image_string = iptcembed($caption_block, $this->fileName); + file_put_contents('iptc.jpg', $image_string); + } + +## -------------------------------------------------------- + + private function iptc_maketag($rec,$dat,$val) + # Author: Thies C. Arntzen + # Purpose: Function to format the new IPTC text + # Param in: $rec: Application record. (We’re working with #2) + # $dat: Index. (120 for caption, 118 for contact. See the IPTC IIM + # specification: + # http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf + # $val: Value/data/text. Make sure this is within the length + # constraints of the IPTC IIM specification + # Ref: http://blog.peterhaza.no/working-with-image-meta-data-in-exif-and-iptc-headers-from-php/ + # http://php.net/manual/en/function.iptcembed.php + # + { + $len = strlen($val); + if ($len < 0x8000) + return chr(0x1c).chr($rec).chr($dat). + chr($len >> 8). + chr($len & 0xff). + $val; + else + return chr(0x1c).chr($rec).chr($dat). + chr(0x80).chr(0x04). + chr(($len >> 24) & 0xff). + chr(($len >> 16) & 0xff). + chr(($len >> 8 ) & 0xff). + chr(($len ) & 0xff). + $val; + } + + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Write XMP Data +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + //http://xmpphptoolkit.sourceforge.net/ + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Add Text +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function addText($text, $pos = '20x20', $padding = 0, $fontColor='#fff', $fontSize = 12, $angle = 0, $font = null) + # Author: Jarrod Oberto + # Date: 18-11-09 + # Purpose: Add text to an image + # Param in: + # Param out: + # Reference: http://php.net/manual/en/function.imagettftext.php + # Notes: Make sure you supply the font. + # + { + + // *** Convert color + $rgbArray = $this->formatColor($fontColor); + $r = $rgbArray['r']; + $g = $rgbArray['g']; + $b = $rgbArray['b']; + + // *** Get text font + $font = $this->getTextFont($font); + + // *** Get text size + $textSizeArray = $this->getTextSize($fontSize, $angle, $font, $text); + $textWidth = $textSizeArray['width']; + $textHeight = $textSizeArray['height']; + + // *** Find co-ords to place text + $posArray = $this->calculatePosition($pos, $padding, $textWidth, $textHeight, false); + $x = $posArray['width']; + $y = $posArray['height']; + + $fontColor = imagecolorallocate($this->imageResized, $r, $g, $b); + + // *** Add text + imagettftext($this->imageResized, $fontSize, $angle, $x, $y, $fontColor, $font, $text); + } + + ## -------------------------------------------------------- + + private function getTextFont($font) + { + // *** Font path (shou + $fontPath = dirname(__FILE__) . '/' . $this->fontDir; + + + // *** The below is/may be needed depending on your version (see ref) + putenv('GDFONTPATH=' . realpath('.')); + + // *** Check if the passed in font exsits... + if ($font == null || !file_exists($font)) { + + // *** ...If not, default to this font. + $font = $fontPath . '/arimo.ttf'; + + // *** Check our default font exists... + if (!file_exists($font)) { + + // *** If not, return false + if ($this->debug) { die('Font not found'); }else{ return false; } + } + } + + return $font; + + } + + ## -------------------------------------------------------- + + private function getTextSize($fontSize, $angle, $font, $text) + { + + // *** Define box (so we can get the width) + $box = @imageTTFBbox($fontSize, $angle, $font, $text); + + // *** Get width of text from dimensions + $textWidth = abs($box[4] - $box[0]); + + // *** Get height of text from dimensions (should also be same as $fontSize) + $textHeight = abs($box[5] - $box[1]); + + return array('height' => $textHeight, 'width' => $textWidth); + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + Add Watermark +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + public function addWatermark($watermarkImage, $pos, $padding = 0, $opacity = 0) + # Author: Jarrod Oberto + # Date: 18-11-09 + # Purpose: Add watermark image + # Param in: (str) $watermark: The watermark image + # (str) $pos: Could be a pre-determined position such as: + # tl = top left, + # t = top (middle), + # tr = top right, + # l = left, + # m = middle, + # r = right, + # bl = bottom left, + # b = bottom (middle), + # br = bottom right + # Or, it could be a co-ordinate position such as: 50x100 + # + # (int) $padding: If using a pre-determined position you can + # adjust the padding from the edges by passing an amount + # in pixels. If using co-ordinates, this value is ignored. + # Param out: + # Reference: http://www.php.net/manual/en/image.examples-watermark.php + # Notes: Based on example in reference. + # + # + { + + // Load the stamp and the photo to apply the watermark to + $stamp = $this->openImage ($watermarkImage); # stamp + $im = $this->imageResized; # photo + + // *** Get stamps width and height + $sx = imagesx($stamp); + $sy = imagesy($stamp); + + // *** Find co-ords to place image + $posArray = $this->calculatePosition($pos, $padding, $sx, $sy); + $x = $posArray['width']; + $y = $posArray['height']; + + // *** Set watermark opacity + if (strtolower(strrchr($watermarkImage, '.')) == '.png') { + + $opacity = $this->invertTransparency($opacity, 100); + $this->filterOpacity($stamp, $opacity); + } + + // Copy the watermark image onto our photo + imagecopy($im, $stamp, $x, $y, 0, 0, imagesx($stamp), imagesy($stamp)); + + } + + ## -------------------------------------------------------- + + private function calculatePosition($pos, $padding, $assetWidth, $assetHeight, $upperLeft = true) + # + # Author: Jarrod Oberto + # Date: 08-05-11 + # Purpose: Calculate the x, y pixel cordinates of the asset to place + # Params in: (str) $pos: Either something like: "tl", "l", "br" or an + # exact position like: "100x50" + # (int) $padding: The amount of padding from the edge. Only + # used for the predefined $pos. + # (int) $assetWidth: The width of the asset to add to the image + # (int) $assetHeight: The height of the asset to add to the image + # (bol) $upperLeft: if true, the asset will be positioned based + # on the upper left x, y coords. If false, it means you're + # using the lower left as the basepoint and this will + # convert it to the upper left position + # Params out: + # NOTE: this is done from the UPPER left corner!! But will convert lower + # left basepoints to upper left if $upperleft is set to false + # + # + { + $pos = strtolower($pos); + + // *** If co-ords have been entered + if (strstr($pos, 'x')) { + $pos = str_replace(' ', '', $pos); + + $xyArray = explode('x', $pos); + list($width, $height) = $xyArray; + + } else { + + switch ($pos) { + case 'tl': + $width = 0 + $padding; + $height = 0 + $padding; + break; + + case 't': + $width = ($this->width / 2) - ($assetWidth / 2); + $height = 0 + $padding; + break; + + case 'tr': + $width = $this->width - $assetWidth - $padding; + $height = 0 + $padding;; + break; + + case 'l': + $width = 0 + $padding; + $height = ($this->height / 2) - ($assetHeight / 2); + break; + + case 'm': + $width = ($this->width / 2) - ($assetWidth / 2); + $height = ($this->height / 2) - ($assetHeight / 2); + break; + + case 'r': + $width = $this->width - $assetWidth - $padding; + $height = ($this->height / 2) - ($assetHeight / 2); + break; + + case 'bl': + $width = 0 + $padding; + $height = $this->height - $assetHeight - $padding; + break; + + case 'b': + $width = ($this->width / 2) - ($assetWidth / 2); + $height = $this->height - $assetHeight - $padding; + break; + + case 'br': + $width = $this->width - $assetWidth - $padding; + $height = $this->height - $assetHeight - $padding; + break; + + default: + $width = 0; + $height = 0; + break; + } + } + + if (!$upperLeft) { + $height = $height + $assetHeight; + } + + return array('width' => $width, 'height' => $height); + } + + + ## -------------------------------------------------------- + + private function filterOpacity(&$img, $opacity = 75) + # + # Author: aiden dot mail at freemail dot hu + # Author date: 29-03-08 08:16 + # Date added: 08-05-11 + # Purpose: Change opacity of image + # Params in: $img: Image resource id + # (int) $opacity: the opacity amount: 0-100, 100 being not opaque. + # Params out: (bool) true on success, else false + # Ref: http://www.php.net/manual/en/function.imagefilter.php#82162 + # Notes: png only + # + { + + if (!isset($opacity)) { + return false; + } + + if ($opacity == 100) { + return true; + } + + $opacity /= 100; + + //get image width and height + $w = imagesx($img); + $h = imagesy($img); + + //turn alpha blending off + imagealphablending($img, false); + + //find the most opaque pixel in the image (the one with the smallest alpha value) + $minalpha = 127; + for ($x = 0; $x < $w; $x++) + for ($y = 0; $y < $h; $y++) { + $alpha = ( imagecolorat($img, $x, $y) >> 24 ) & 0xFF; + if ($alpha < $minalpha) { + $minalpha = $alpha; + } + } + + //loop through image pixels and modify alpha for each + for ($x = 0; $x < $w; $x++) { + for ($y = 0; $y < $h; $y++) { + //get current alpha value (represents the TANSPARENCY!) + $colorxy = imagecolorat($img, $x, $y); + $alpha = ( $colorxy >> 24 ) & 0xFF; + //calculate new alpha + if ($minalpha !== 127) { + $alpha = 127 + 127 * $opacity * ( $alpha - 127 ) / ( 127 - $minalpha ); + } else { + $alpha += 127 * $opacity; + } + //get the color index with new alpha + $alphacolorxy = imagecolorallocatealpha($img, ( $colorxy >> 16 ) & 0xFF, ( $colorxy >> 8 ) & 0xFF, $colorxy & 0xFF, $alpha); + //set pixel with the new color + opacity + if (!imagesetpixel($img, $x, $y, $alphacolorxy)) { + + return false; + } + } + } + + return true; + } + +## -------------------------------------------------------- + + private function openImage($file) + # Author: Jarrod Oberto + # Date: 27-02-08 + # Purpose: + # Param in: + # Param out: n/a + # Reference: + # Notes: + # + { + + if (!file_exists($file) && !$this->checkStringStartsWith('http://', $file)) { if ($this->debug) { die('Image not found.'); }else{ die(); }}; + + // *** Get extension + $extension = strrchr($file, '.'); + $extension = strtolower($extension); + + switch($extension) + { + case '.jpg': + case '.jpeg': + $img = @imagecreatefromjpeg($file); + break; + case '.gif': + $img = @imagecreatefromgif($file); + break; + case '.png': + $img = @imagecreatefrompng($file); + break; + case '.bmp': + $img = @$this->imagecreatefrombmp($file); + break; + case '.psd': + $img = @$this->imagecreatefrompsd($file); + break; + + // ... etc + + default: + $img = false; + break; + } + + return $img; + } + +## -------------------------------------------------------- + + public function reset() + # + # Author: Jarrod Oberto + # Date: 30-08-11 + # Purpose: Reset the resource (allow further editing) + # Params in: + # Params out: + # Notes: + # + { + $this->__destruct(); + $this->image = null; + $this->imageResized = null; + gc_collect_cycles(); + $this->__construct($this->fileName); + } + +## -------------------------------------------------------- + + public function saveImage($savePath, $imageQuality="100") + # Author: Jarrod Oberto + # Date: 27-02-08 + # Purpose: Saves the image + # Param in: $savePath: Where to save the image including filename: + # $imageQuality: image quality you want the image saved at 0-100 + # Param out: n/a + # Reference: + # Notes: * gif doesn't have a quality parameter + # * jpg has a quality setting 0-100 (100 being the best) + # * png has a quality setting 0-9 (0 being the best) + # + # * bmp files have no native support for bmp files. We use a + # third party class to save as bmp. + { + + // *** Perform a check or two. + //if (!is_resource($this->imageResized)) { if ($this->debug) { die('saveImage: This is not a resource.'); }else{ die(); }} + $fileInfoArray = pathInfo($savePath); + clearstatcache(); + if (!is_writable($fileInfoArray['dirname'])) { if ($this->debug) { die('The path is not writable. Please check your permissions.'); }else{ die(); }} + + // *** Get extension + $extension = strrchr($savePath, '.'); + $extension = strtolower($extension); + + $error = ''; + + switch($extension) + { + case '.jpg': + case '.jpeg': + $this->checkInterlaceImage($this->isInterlace); + if (imagetypes() & IMG_JPG) { + imagejpeg($this->imageResized, $savePath, $imageQuality); + } else { + $error = 'jpg'; + } + break; + + case '.gif': + $this->checkInterlaceImage($this->isInterlace); + if (imagetypes() & IMG_GIF) { + imagegif($this->imageResized, $savePath); + } else { + $error = 'gif'; + } + break; + + case '.png': + // *** Scale quality from 0-100 to 0-9 + $scaleQuality = round(($imageQuality/100) * 9); + + // *** Invert qualit setting as 0 is best, not 9 + $invertScaleQuality = 9 - $scaleQuality; + + $this->checkInterlaceImage($this->isInterlace); + if (imagetypes() & IMG_PNG) { + imagepng($this->imageResized, $savePath, $invertScaleQuality); + } else { + $error = 'png'; + } + break; + + case '.bmp': + file_put_contents($savePath, $this->GD2BMPstring($this->imageResized)); + break; + + // ... etc + + default: + + // *** No extension - No save. + $this->errorArray[] = 'This file type (' . $extension . ') is not supported. File not saved.'; + break; + } + + //imagedestroy($this->imageResized); + + // *** Display error if a file type is not supported. + if ($error != '') { + $this->errorArray[] = $error . ' support is NOT enabled. File not saved.'; + } + } + +## -------------------------------------------------------- + + public function displayImage($fileType = 'jpg', $imageQuality="100") + # Author: Jarrod Oberto + # Date: 18-11-09 + # Purpose: Display images directly to the browser + # Param in: The image type you want to display + # Param out: + # Reference: + # Notes: + # + { + + //if (!is_resource($this->imageResized)) { if ($this->debug) { die('saveImage: This is not a resource.'); }else{ die(); }} + + switch($fileType) + { + case 'jpg': + case 'jpeg': + header('Content-type: image/jpeg'); + imagejpeg($this->imageResized, '', $imageQuality); + break; + case 'gif': + header('Content-type: image/gif'); + imagegif($this->imageResized); + break; + case 'png': + header('Content-type: image/png'); + + // *** Scale quality from 0-100 to 0-9 + $scaleQuality = round(($imageQuality/100) * 9); + + // *** Invert qualit setting as 0 is best, not 9 + $invertScaleQuality = 9 - $scaleQuality; + + imagepng($this->imageResized, '', $invertScaleQuality); + break; + case 'bmp': + echo 'bmp file format is not supported.'; + break; + + // ... etc + + default: + // *** No extension - No save. + break; + } + + //imagedestroy($this->imageResized); + } + +## -------------------------------------------------------- + + public function setTransparency($bool) + # Sep 2011 + { + $this->keepTransparency = $bool; + } + +## -------------------------------------------------------- + + public function setFillColor($value) + # Sep 2011 + # Param in: (mixed) $value: (array) Could be an array of RGB + # (str) Could be hex #ffffff or #fff, fff, ffffff + # + # If the keepTransparency is set to false, then no transparency is to be used. + # This is ideal when you want to save as jpg. + # + # this method allows you to set the background color to use instead of + # transparency. + # + { + $colorArray = $this->formatColor($value); + $this->fillColorArray = $colorArray; + } + +## -------------------------------------------------------- + + public function setCropFromTop($value) + # Sep 2011 + { + $this->cropFromTopPercent = $value; + } + +## -------------------------------------------------------- + + public function testGDInstalled() + # Author: Jarrod Oberto + # Date: 27-02-08 + # Purpose: Test to see if GD is installed + # Param in: n/a + # Param out: (bool) True is gd extension loaded otherwise false + # Reference: + # Notes: + # + { + if(extension_loaded('gd') && function_exists('gd_info')) + { + $gdInstalled = true; + } + else + { + $gdInstalled = false; + } + + return $gdInstalled; + } + +## -------------------------------------------------------- + + public function testEXIFInstalled() + # Author: Jarrod Oberto + # Date: 08-05-11 + # Purpose: Test to see if EXIF is installed + # Param in: n/a + # Param out: (bool) True is exif extension loaded otherwise false + # Reference: + # Notes: + # + { + if(extension_loaded('exif')) { + $exifInstalled = true; + } else { + $exifInstalled = false; + } + + return $exifInstalled; + } + +## -------------------------------------------------------- + + public function testIsImage() + # Author: Jarrod Oberto + # Date: 28 Nov 16 + # Purpose: Test if file is an image + # Param in: + # Param out: n/a + # Reference: + # Notes: A simpler, less restrictive method would be to just check for + # the 'image' part of 'image/gif', 'image/jpg', etc. + # + { + + $file = $this->fileName; + $isImage = false; + + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mimeType = finfo_file($finfo, $file); + finfo_close($finfo); + + switch($mimeType) { + case 'image/jpeg': + case 'image/gif': + case 'image/png': + case 'image/bmp': + case 'image/x-windows-bmp': + $isImage = true; + break; + default: + $isImage = false; + } + + return $isImage; + } + +## -------------------------------------------------------- + + public function getIsImage() + # Author: Jarrod Oberto + # Date: 28 Nov 16 + # Purpose: Get testIsImage result + # Param in: + # Param out: n/a + # Reference: + # Notes: + # + { + return $this->isImage; + } + +## -------------------------------------------------------- + + public function testFunct() + # Author: Jarrod Oberto + # Date: 27-02-08 + # Purpose: Test Function + # Param in: n/a + # Param out: n/a + # Reference: + # Notes: + # + { + echo $this->height; + } + +## -------------------------------------------------------- + + public function setForceStretch($value) + # Author: Jarrod Oberto + # Date: 23-12-10 + # Purpose: + # Param in: (bool) $value + # Param out: n/a + # Reference: + # Notes: + # + { + $this->forceStretch = $value; + } + +## -------------------------------------------------------- + + public function setFile($fileName) + # Author: Jarrod Oberto + # Date: 28-02-08 + # Purpose: + # Param in: n/a + # Param out: n/a + # Reference: + # Notes: + # + { + self::__construct($fileName); + } + +## -------------------------------------------------------- + + public function getFileName() + # Author: Jarrod Oberto + # Date: 10-09-08 + # Purpose: + # Param in: n/a + # Param out: n/a + # Reference: + # Notes: + # + { + return $this->fileName; + } + +## -------------------------------------------------------- + + public function getHeight() + { + return $this->height; + } + +## -------------------------------------------------------- + + public function getWidth() + { + return $this->width; + } + +## -------------------------------------------------------- + + public function getOriginalHeight() + { + return $this->heightOriginal; + } + +## -------------------------------------------------------- + + public function getOriginalWidth() + { + return $this->widthOriginal; + } + +## -------------------------------------------------------- + + public function getErrors() + # Author: Jarrod Oberto + # Date: 19-11-09 + # Purpose: Returns the error array + # Param in: n/a + # Param out: Array of errors + # Reference: + # Notes: + # + { + return $this->errorArray; + } + +## -------------------------------------------------------- + + private function checkInterlaceImage($isEnabled) + # jpg will use progressive (they don't use interace) + { + if ($isEnabled) { + imageinterlace($this->imageResized, $isEnabled); + } + } + +## -------------------------------------------------------- + + protected function formatColor($value) + # Author: Jarrod Oberto + # Date: 09-05-11 + # Purpose: Determine color method passed in and return color as RGB + # Param in: (mixed) $value: (array) Could be an array of RGB + # (str) Could be hex #ffffff or #fff, fff, ffffff + # Param out: + # Reference: + # Notes: + # + { + $rgbArray = array(); + + // *** If it's an array it should be R, G, B + if (is_array($value)) { + + if (key($value) == 0 && count($value) == 3) { + + $rgbArray['r'] = $value[0]; + $rgbArray['g'] = $value[1]; + $rgbArray['b'] = $value[2]; + + } else { + $rgbArray = $value; + } + } else if (strtolower($value) == 'transparent') { + + $rgbArray = array( + 'r' => 255, + 'g' => 255, + 'b' => 255, + 'a' => 127 + ); + + } else { + + // *** ...Else it should be hex. Let's make it RGB + $rgbArray = $this -> hex2dec($value); + } + + return $rgbArray; + } + +## -------------------------------------------------------- + + function hex2dec($hex) + # Purpose: Convert #hex color to RGB + { + $color = str_replace('#', '', $hex); + + if (strlen($color) == 3) { + $color = $color . $color; + } + + $rgb = array( + 'r' => hexdec(substr($color, 0, 2)), + 'g' => hexdec(substr($color, 2, 2)), + 'b' => hexdec(substr($color, 4, 2)), + 'a' => 0 + ); + return $rgb; + } + +## -------------------------------------------------------- + + private function createImageColor ($colorArray) + { + $r = $colorArray['r']; + $g = $colorArray['g']; + $b = $colorArray['b']; + + return imagecolorallocate($this->imageResized, $r, $g, $b); + } + +## -------------------------------------------------------- + + private function testColorExists($colorArray) + { + $r = $colorArray['r']; + $g = $colorArray['g']; + $b = $colorArray['b']; + + if (imagecolorexact($this->imageResized, $r, $g, $b) == -1) { + return false; + } else { + return true; + } + } + +## -------------------------------------------------------- + + private function findUnusedGreen() + # Purpose: We find a green color suitable to use like green-screen effect. + # Therefore, the color must not exist in the image. + { + $green = 255; + + do { + + $greenChroma = array(0, $green, 0); + $colorArray = $this->formatColor($greenChroma); + $match = $this->testColorExists($colorArray); + $green--; + + } while ($match == false && $green > 0); + + // *** If no match, just bite the bullet and use green value of 255 + if (!$match) { + $greenChroma = array(0, $green, 0); + } + + return $greenChroma; + } + +## -------------------------------------------------------- + + private function findUnusedBlue() + # Purpose: We find a green color suitable to use like green-screen effect. + # Therefore, the color must not exist in the image. + { + $blue = 255; + + do { + + $blueChroma = array(0, 0, $blue); + $colorArray = $this->formatColor($blueChroma); + $match = $this->testColorExists($colorArray); + $blue--; + + } while ($match == false && $blue > 0); + + // *** If no match, just bite the bullet and use blue value of 255 + if (!$match) { + $blueChroma = array(0, 0, $blue); + } + + return $blueChroma; + } + +## -------------------------------------------------------- + + private function invertTransparency($value, $originalMax, $invert=true) + # Purpose: This does two things: + # 1) Convert the range from 0-127 to 0-100 + # 2) Inverts value to 100 is not transparent while 0 is fully + # transparent (like Photoshop) + { + // *** Test max range + if ($value > $originalMax) { + $value = $originalMax; + } + + // *** Test min range + if ($value < 0) { + $value = 0; + } + + if ($invert) { + return $originalMax - (($value/100) * $originalMax); + } else { + return ($value/100) * $originalMax; + } + } + +## -------------------------------------------------------- + + private function transparentImage($src) + { + // *** making images with white bg transparent + $r1 = 0; + $g1 = 255; + $b1 = 0; + for ($x = 0; $x < imagesx($src); ++$x) { + for ($y = 0; $y < imagesy($src); ++$y) { + $color = imagecolorat($src, $x, $y); + $r = ($color >> 16) & 0xFF; + $g = ($color >> 8) & 0xFF; + $b = $color & 0xFF; + for ($i = 0; $i < 270; $i++) { + //if ($r . $g . $b == ($r1 + $i) . ($g1 + $i) . ($b1 + $i)) { + if ($r == 0 && $g == 255 && $b == 0) { + //if ($g == 255) { + $trans_colour = imagecolorallocatealpha($src, 0, 0, 0, 127); + imagefill($src, $x, $y, $trans_colour); + } + } + } + } + + return $src; + } + +## -------------------------------------------------------- + + function checkStringStartsWith($needle, $haystack) + # Check if a string starts with a specific pattern + { + return (substr($haystack, 0, strlen($needle))==$needle); + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + BMP SUPPORT (SAVING) - James Heinrich +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + private function GD2BMPstring(&$gd_image) + # Author: James Heinrich + # Purpose: Save file as type bmp + # Param in: The image canvas (passed as ref) + # Param out: + # Reference: + # Notes: This code was stripped out of two external files + # (phpthumb.bmp.php,phpthumb.functions.php) and added below to + # avoid dependancies. + # + { + $imageX = ImageSX($gd_image); + $imageY = ImageSY($gd_image); + + $BMP = ''; + for ($y = ($imageY - 1); $y >= 0; $y--) { + $thisline = ''; + for ($x = 0; $x < $imageX; $x++) { + $argb = $this->GetPixelColor($gd_image, $x, $y); + $thisline .= chr($argb['blue']).chr($argb['green']).chr($argb['red']); + } + while (strlen($thisline) % 4) { + $thisline .= "\x00"; + } + $BMP .= $thisline; + } + + $bmpSize = strlen($BMP) + 14 + 40; + // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp + $BITMAPFILEHEADER = 'BM'; // WORD bfType; + $BITMAPFILEHEADER .= $this->LittleEndian2String($bmpSize, 4); // DWORD bfSize; + $BITMAPFILEHEADER .= $this->LittleEndian2String( 0, 2); // WORD bfReserved1; + $BITMAPFILEHEADER .= $this->LittleEndian2String( 0, 2); // WORD bfReserved2; + $BITMAPFILEHEADER .= $this->LittleEndian2String( 54, 4); // DWORD bfOffBits; + + // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp + $BITMAPINFOHEADER = $this->LittleEndian2String( 40, 4); // DWORD biSize; + $BITMAPINFOHEADER .= $this->LittleEndian2String( $imageX, 4); // LONG biWidth; + $BITMAPINFOHEADER .= $this->LittleEndian2String( $imageY, 4); // LONG biHeight; + $BITMAPINFOHEADER .= $this->LittleEndian2String( 1, 2); // WORD biPlanes; + $BITMAPINFOHEADER .= $this->LittleEndian2String( 24, 2); // WORD biBitCount; + $BITMAPINFOHEADER .= $this->LittleEndian2String( 0, 4); // DWORD biCompression; + $BITMAPINFOHEADER .= $this->LittleEndian2String( 0, 4); // DWORD biSizeImage; + $BITMAPINFOHEADER .= $this->LittleEndian2String( 2835, 4); // LONG biXPelsPerMeter; + $BITMAPINFOHEADER .= $this->LittleEndian2String( 2835, 4); // LONG biYPelsPerMeter; + $BITMAPINFOHEADER .= $this->LittleEndian2String( 0, 4); // DWORD biClrUsed; + $BITMAPINFOHEADER .= $this->LittleEndian2String( 0, 4); // DWORD biClrImportant; + + return $BITMAPFILEHEADER.$BITMAPINFOHEADER.$BMP; + } + +## -------------------------------------------------------- + + private function GetPixelColor(&$img, $x, $y) + # Author: James Heinrich + # Purpose: + # Param in: + # Param out: + # Reference: + # Notes: + # + { + if (!is_resource($img)) { + return false; + } + return @ImageColorsForIndex($img, @ImageColorAt($img, $x, $y)); + } + +## -------------------------------------------------------- + + private function LittleEndian2String($number, $minbytes=1) + # Author: James Heinrich + # Purpose: BMP SUPPORT (SAVING) + # Param in: + # Param out: + # Reference: + # Notes: + # + { + $intstring = ''; + while ($number > 0) { + $intstring = $intstring.chr($number & 255); + $number >>= 8; + } + return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + BMP SUPPORT (READING) +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + private function ImageCreateFromBMP($filename) + # Author: DHKold + # Date: The 15th of June 2005 + # Version: 2.0B + # Purpose: To create an image from a BMP file. + # Param in: BMP file to open. + # Param out: Return a resource like the other ImageCreateFrom functions + # Reference: http://us3.php.net/manual/en/function.imagecreate.php#53879 + # Bug fix: Author: domelca at terra dot es + # Date: 06 March 2008 + # Fix: Correct 16bit BMP support + # Notes: + # + { + + //Ouverture du fichier en mode binaire + if (! $f1 = fopen($filename,"rb")) return FALSE; + + //1 : Chargement des ent�tes FICHIER + $FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1,14)); + if ($FILE['file_type'] != 19778) return FALSE; + + //2 : Chargement des ent�tes BMP + $BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'. + '/Vcompression/Vsize_bitmap/Vhoriz_resolution'. + '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1,40)); + $BMP['colors'] = pow(2,$BMP['bits_per_pixel']); + + if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset']; + + $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8; + $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']); + $BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4); + $BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4); + $BMP['decal'] = 4-(4*$BMP['decal']); + + if ($BMP['decal'] == 4) $BMP['decal'] = 0; + + //3 : Chargement des couleurs de la palette + $PALETTE = array(); + if ($BMP['colors'] < 16777216) + { + $PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4)); + } + + //4 : Cr�ation de l'image + $IMG = fread($f1,$BMP['size_bitmap']); + $VIDE = chr(0); + + $res = imagecreatetruecolor($BMP['width'],$BMP['height']); + $P = 0; + $Y = $BMP['height']-1; + while ($Y >= 0) + { + $X=0; + while ($X < $BMP['width']) + { + if ($BMP['bits_per_pixel'] == 24) + $COLOR = unpack("V",substr($IMG,$P,3).$VIDE); + elseif ($BMP['bits_per_pixel'] == 16) + { + + /* + * BMP 16bit fix + * ================= + * + * Ref: http://us3.php.net/manual/en/function.imagecreate.php#81604 + * + * Notes: + * "don't work with bmp 16 bits_per_pixel. change pixel + * generator for this." + * + */ + + // *** Original code (don't work) + //$COLOR = unpack("n",substr($IMG,$P,2)); + //$COLOR[1] = $PALETTE[$COLOR[1]+1]; + + $COLOR = unpack("v",substr($IMG,$P,2)); + $blue = ($COLOR[1] & 0x001f) << 3; + $green = ($COLOR[1] & 0x07e0) >> 3; + $red = ($COLOR[1] & 0xf800) >> 8; + $COLOR[1] = $red * 65536 + $green * 256 + $blue; + + } + elseif ($BMP['bits_per_pixel'] == 8) + { + $COLOR = unpack("n",$VIDE.substr($IMG,$P,1)); + $COLOR[1] = $PALETTE[$COLOR[1]+1]; + } + elseif ($BMP['bits_per_pixel'] == 4) + { + $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1)); + if (($P*2)%2 == 0) $COLOR[1] = ($COLOR[1] >> 4) ; else $COLOR[1] = ($COLOR[1] & 0x0F); + $COLOR[1] = $PALETTE[$COLOR[1]+1]; + } + elseif ($BMP['bits_per_pixel'] == 1) + { + $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1)); + if (($P*8)%8 == 0) $COLOR[1] = $COLOR[1] >>7; + elseif (($P*8)%8 == 1) $COLOR[1] = ($COLOR[1] & 0x40)>>6; + elseif (($P*8)%8 == 2) $COLOR[1] = ($COLOR[1] & 0x20)>>5; + elseif (($P*8)%8 == 3) $COLOR[1] = ($COLOR[1] & 0x10)>>4; + elseif (($P*8)%8 == 4) $COLOR[1] = ($COLOR[1] & 0x8)>>3; + elseif (($P*8)%8 == 5) $COLOR[1] = ($COLOR[1] & 0x4)>>2; + elseif (($P*8)%8 == 6) $COLOR[1] = ($COLOR[1] & 0x2)>>1; + elseif (($P*8)%8 == 7) $COLOR[1] = ($COLOR[1] & 0x1); + $COLOR[1] = $PALETTE[$COLOR[1]+1]; + } + else + return FALSE; + + imagesetpixel($res,$X,$Y,$COLOR[1]); + $X++; + $P += $BMP['bytes_per_pixel']; + } + + $Y--; + $P+=$BMP['decal']; + } + + //Fermeture du fichier + fclose($f1); + + return $res; + } + + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- + PSD SUPPORT (READING) +*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ + + private function imagecreatefrompsd($fileName) + # Author: Tim de Koning + # Version: 1.3 + # Purpose: To create an image from a PSD file. + # Param in: PSD file to open. + # Param out: Return a resource like the other ImageCreateFrom functions + # Reference: http://www.kingsquare.nl/phppsdreader + # Notes: + # + { + if (file_exists($this->psdReaderPath)) { + + + include_once($this->psdReaderPath); + + $psdReader = new PhpPsdReader($fileName); + + if (isset($psdReader->infoArray['error'])) return ''; + else return $psdReader->getImage(); + } else { + return false; + } + } + +## -------------------------------------------------------- + + public function __destruct() { + if (is_resource($this->imageResized)) { + imagedestroy($this->imageResized); + } + } + +## -------------------------------------------------------- + +} + + + + +/* + * Example with some API calls (outdated): + * + * + * =============================== + * Compulsary + * =============================== + * + * include("classes/resize_class.php"); + * + * // *** Initialise object + * $magicianObj = new resize('images/cars/large/a.jpg'); + * + * // *** Turn off stretching (optional) + * $magicianObj -> setForceStretch(false); + * + * // *** Resize object + * $magicianObj -> resizeImage(150, 100, 0); + * + * =============================== + * Image options - can run none, one, or all. + * =============================== + * + * // *** Add watermark + * $magicianObj -> addWatermark('stamp.png'); + * + * // *** Add text + * $magicianObj -> addText('testing...'); + * + * =============================== + * Output options - can run one, or the other, or both. + * =============================== + * + * // *** Save image to disk + * $magicianObj -> saveImage('images/cars/large/b.jpg', 100); + * + * // *** Or output to screen (params in can be jpg, gif, png) + * $magicianObj -> displayImage('png'); + * + * =============================== + * Return options - return errors. nice for debuggin. + * =============================== + * + * // *** Return error array + * $errorArray = $magicianObj -> getErrors(); + * + * + * =============================== + * Cleanup options - not really neccessary, but good practice + * =============================== + * + * // *** Free used memory + * $magicianObj -> __destruct(); + */ +?> diff --git a/api/.htaccess b/api/.htaccess new file mode 100644 index 0000000..99d5516 --- /dev/null +++ b/api/.htaccess @@ -0,0 +1,6 @@ +ErrorDocument 404 "Access Denied" +ErrorDocument 403 "Access Denied" +ErrorDocument 401 "Access Denied" + +RewriteEngine on +RewriteRule ^game/join/?$ /game/test.php [NC,L] \ No newline at end of file diff --git a/api/AbuseReport/InGameChat.old.php b/api/AbuseReport/InGameChat.old.php new file mode 100644 index 0000000..8b00c4f --- /dev/null +++ b/api/AbuseReport/InGameChat.old.php @@ -0,0 +1,110 @@ + + + + + Report Abuse + + +
+ An error has occurred.'; + echo 'You need to specifiy an username.
Go back'; + } + + if (strlen($username) > 20 and $error == false) { + $error = true; + echo '

An error has occurred.

'; + echo 'The username you specified is too long.
Go back'; + } + + if (strlen($reason) == 0 and $error == false) { + $error = true; + echo '

An error has occurred.

'; + echo 'You need to specifiy a reason.
Go back'; + } + + if (strlen($reason) > 256 and $error == false) { + $error = true; + echo '

An error has occurred.

'; + echo 'The reason you specified is too long.
Go back'; + } + if ($error == false) { + // Create database connection. + include_once $_SERVER['DOCUMENT_ROOT'].'/config.php'; + try{ + $dbcon = new PDO('mysql:host='.$db_host.';port='.$db_port.';dbname='.$db_name.'', $db_user, $db_passwd); + $dbcon->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + $stmt = $dbcon->prepare("SELECT * FROM users WHERE username=:uid;"); + $stmt->bindParam(':uid', $username, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $error = false; + + if ($stmt->rowCount() == 0) { + $error = true; + echo '

An error has occurred.

'; + echo 'The user you are reporting does not exist.
Go back'; + } + if ($error == false) { + if ($result['banned'] == 1) { + $error = true; + echo '

An error has occurred.

'; + echo 'The user you are reporting has been banned.
Go back'; + } + } + if ($error == false) { + // Check if the same user has reported already. + $stmt = $dbcon->prepare("SELECT * FROM reports WHERE reportIP=:ip AND target=:username;"); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->bindParam(':username', $username, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() > 0) { + echo '

An error has occurred.

'; + echo 'You have already reported this user.
Go back'; + }else{ + $query = "INSERT INTO reports (`target`, `reason`, `date`, `reportIP`) VALUES (:username, :reason, NOW(), :ip);"; + $stmt = $dbcon->prepare($query); + $stmt->bindParam(':username', $username, PDO::PARAM_STR); + $stmt->bindParam(':reason', $reason, PDO::PARAM_STR); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + + echo '

Thank you!

'; + echo 'Your report has been saved. Thanks again!'; + } + } + } + $dbcon = null; + exit; + } + ?> +

Report Abuse

+
+ Username of rule breaker + + Say what this user did wrong + + +
+
+ + \ No newline at end of file diff --git a/api/Analytics/Measurement.ashx b/api/Analytics/Measurement.ashx new file mode 100644 index 0000000..e69de29 diff --git a/api/Asset/GetScriptState.ashx b/api/Asset/GetScriptState.ashx new file mode 100644 index 0000000..a0aba93 --- /dev/null +++ b/api/Asset/GetScriptState.ashx @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/api/Asset/index.php b/api/Asset/index.php new file mode 100644 index 0000000..08551c0 --- /dev/null +++ b/api/Asset/index.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/api/Error/Dmp.ashx b/api/Error/Dmp.ashx new file mode 100644 index 0000000..e69de29 diff --git a/api/Game/Help.php b/api/Game/Help.php new file mode 100644 index 0000000..78db772 --- /dev/null +++ b/api/Game/Help.php @@ -0,0 +1,138 @@ + + + + + + + + +Help + +
+
+
+ +
+ + +
+

+ Besides using simple blocks, you can insert Things that other people have built into your Place. Use the Insert... menu in the game to browse.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Action + Primary + Alternate
+ Move Avatar + Arrow keys + ASDW keys
+ Move Avatar (no tool selected) + Click location with green disk +
+ Jump + Space Bar +
+ Look up/down/left/right + Right-click and drag mouse +
+ Look side to side + Move mouse to the far right or far left + +
+ Zoom in/out and up/down + Mouse wheel + I (in) and O (out) keys
+ Change Tool / Toggle tool on off + Number keys 1, 2, 3, ... + Click on the tool
+ Drop Tool + Backspace key +
+ Drop Hat + Equal (=) key +
+ Regenerate dead or stuck avatar + Avatar regenerates automatically + Exit and then return to the Place
+ First Person Mode + Zoom all the way in +
+ + +
+ + +
+
+ + diff --git a/api/Game/Join.ashx b/api/Game/Join.ashx new file mode 100644 index 0000000..2a3db85 --- /dev/null +++ b/api/Game/Join.ashx @@ -0,0 +1,96 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + $json = ["status"=>0, "authenticationUrl"=>null, "authenticationTicket"=>null, "joinScriptUrl"=>null]; + header("Content-Type: text/plain"); + if(!isset($_COOKIE["a_id"])){ + http_response_code(401); + exit; + } + if(!isset($_GET["gID"]) || (int)$_GET["gID"] != $_GET["gID"]){ + http_response_code(403); + exit; + } + + $stmtUid = $dbcon->prepare("SELECT userId FROM sessions WHERE sessionId=:id;"); + $stmtUid->bindParam(':id', $_COOKIE["a_id"], PDO::PARAM_STR); + $stmtUid->execute(); + $rUserId = $stmtUid->fetch(PDO::FETCH_ASSOC); + + // User row + $stmtU = $dbcon->prepare("SELECT * FROM users WHERE id=:id;"); + $stmtU->bindParam(':id', $rUserId["userId"], PDO::PARAM_INT); + $stmtU->execute(); + $rUser = $stmtU->fetch(PDO::FETCH_ASSOC); + + // Game row + $stmtG = $dbcon->prepare("SELECT * FROM games WHERE id=:id;"); + $stmtG->bindParam(':id', $_GET["gID"], PDO::PARAM_INT); + $stmtG->execute(); + $rGame = $stmtG->fetch(PDO::FETCH_ASSOC); + + if ($stmtU->rowCount() == 0 or $stmtG->rowCount() == 0){ + http_response_code(403); + exit; + } + + if ($rUser['publicBan'] == 1){ + http_response_code(403); + exit; + } + + if ($rGame['public'] == 0) { + if(!isset($_GET["key"])){ + http_response_code(403); + exit; + } + $gameKey = $rGame['key']; + $stmtU = $dbcon->prepare("SELECT * FROM gameKeys WHERE userid=:id AND `key` = :key;"); + $stmtU->bindParam(':id', $rUserId["userId"], PDO::PARAM_INT); + $stmtU->bindParam(':key', $_GET["key"], PDO::PARAM_STR); + $stmtU->execute(); + if ($stmtU->rowCount() == 0 and $rGame['creator_uid'] != $rUserId["userId"] and $rUser['rank'] == 0){ + http_response_code(403); + exit; + } + } + + $stmt = $dbcon->prepare("DELETE FROM gameJoins WHERE uid=:uid"); + $stmt->bindParam(':uid', $rUserId["userId"], PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $dbcon->prepare("INSERT INTO `gameJoins` (`uid`, `gameId`) VALUES (:uid, :gameId);"); + $stmt->bindParam(':uid', $rUserId["userId"], PDO::PARAM_INT); + $stmt->bindParam(':gameId', $_GET["gID"], PDO::PARAM_INT); + $stmt->execute(); + + // Badge awarding + $stmt = $dbcon->prepare("SELECT id FROM badges WHERE uid=:uid AND badgeId = 8;"); + $stmt->bindParam(':uid', $rUserId["userId"], PDO::PARAM_INT); + $stmt->execute(); + + if ($stmt->rowCount() == 0) { + $stmt = $dbcon->prepare("INSERT INTO `badges` (`uid`, `badgeId`) VALUES (:uid, 8);"); + $stmt->bindParam(':uid', $rUserId["userId"], PDO::PARAM_INT); + $stmt->execute(); + } + + // User row + $stmtC = $dbcon->prepare("SELECT * FROM users WHERE id=:id;"); + $stmtC->bindParam(':id', $rGame['creator_uid'], PDO::PARAM_INT); + $stmtC->execute(); + $creator = $stmtC->fetch(PDO::FETCH_ASSOC); + + $script = "\r\n" . json_encode(["BaseUrl"=>"http://www.graphictoria.cf/", "SeleniumTestMode"=>false, "PlaceId"=>$_GET["gID"], "MachineAddress"=>$rGame["ip"], "VendorId"=>0, "DataCenterId"=>0, "PingUrl"=>"https://graphictoria.cf/core/func/api/auth/ping.php", "PingInterval"=>60, "GenerateTeleportJoin"=>false, "GameId"=>"00000000-0000-0000-0000-000000000000", "UserId"=>$rUserId["userId"], "ClientTicket"=>$rUser["gameKey"], "IsRobloxPlace"=>($creator['rank']==1), "CreatorTypeEnum"=>"User", "CreatorId"=>$rGame['creator_uid'], "ChatStyle"=>"ClassicAndBubble", "SessionId"=>sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)), "ServerPort"=>$rGame['port'], "ClientPort"=>0, "SuperSafeChat"=>false, "IsUnknownOrUnder13"=>false, "MembershipType"=>($rUser["rank"]==1 ? "OutrageousBuildersClub" : "None"), "AccountAge"=>round((time()-strtotime($rUser["joinDate"])) / (60 * 60 * 24)), "UserName"=>$rUser["username"], "CharacterAppearance"=>"http://api.graphictoria.cf/user/getCharacter.php?uid=" . $rUserId["userId"] . "&key=D869593BF742A42F79915993EF1DB&tick=" . time(), "FollowUserId"=>0, "ScreenShotInfo"=>"", "VideoInfo"=>""], JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK); + $signature; + $key = file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/../key.pem"); + openssl_sign($script, $signature, $key, OPENSSL_ALGO_SHA1); + exit("--rbxsig%" . base64_encode($signature) . "%" . $script); +?> \ No newline at end of file diff --git a/api/Game/KeepAlivePinger.ashx b/api/Game/KeepAlivePinger.ashx new file mode 100644 index 0000000..e69de29 diff --git a/api/Game/MachineConfiguration.ashx b/api/Game/MachineConfiguration.ashx new file mode 100644 index 0000000..e69de29 diff --git a/api/Game/PlaceLauncher.ashx b/api/Game/PlaceLauncher.ashx new file mode 100644 index 0000000..c152b20 --- /dev/null +++ b/api/Game/PlaceLauncher.ashx @@ -0,0 +1,70 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + $json = ["status"=>0, "authenticationUrl"=>null, "authenticationTicket"=>null, "joinScriptUrl"=>null]; + header("Content-Type: text/plain"); + if(!isset($_COOKIE["a_id"])){ + http_response_code(401); + exit(json_encode($json)); + } + if(!isset($_GET["placeId"]) || (int)$_GET["placeId"] != $_GET["placeId"]){ + http_response_code(403); + exit(json_encode($json)); + } + + $stmtUid = $dbcon->prepare("SELECT userId FROM sessions WHERE sessionId=:id;"); + $stmtUid->bindParam(':id', $_COOKIE["a_id"], PDO::PARAM_STR); + $stmtUid->execute(); + $rUserId = $stmtUid->fetch(PDO::FETCH_ASSOC); + + // User row + $stmtU = $dbcon->prepare("SELECT * FROM users WHERE id=:id;"); + $stmtU->bindParam(':id', $rUserId["userId"], PDO::PARAM_INT); + $stmtU->execute(); + $rUser = $stmtU->fetch(PDO::FETCH_ASSOC); + + // Game row + $stmtG = $dbcon->prepare("SELECT * FROM games WHERE id=:id;"); + $stmtG->bindParam(':id', $_GET["placeId"], PDO::PARAM_INT); + $stmtG->execute(); + $rGame = $stmtG->fetch(PDO::FETCH_ASSOC); + + if ($stmtU->rowCount() == 0 or $stmtG->rowCount() == 0){ + http_response_code(403); + exit(json_encode($json)); + } + + if ($rUser['publicBan'] == 1){ + $json["status"]=6; + exit(json_encode($json)); + } + + if ($rGame['public'] == 0) { + if(!isset($_GET["key"])){ + $json["status"]=6; + exit(json_encode($json)); + } + $gameKey = $rGame['key']; + $stmtU = $dbcon->prepare("SELECT * FROM gameKeys WHERE userid=:id AND `key` = :key;"); + $stmtU->bindParam(':id', $rUserId["userId"], PDO::PARAM_INT); + $stmtU->bindParam(':key', $_GET["key"], PDO::PARAM_STR); + $stmtU->execute(); + if ($stmtU->rowCount() == 0 and $rGame['creator_uid'] != $rUserId["userId"] and $rUser['rank'] == 0){ + $json["status"]=6; + exit(json_encode($json)); + } + } + + $json["status"] = 2; + $json["authenticationUrl"] = "http://api.graphictoria.cf/Login/Negotiate.ashx"; + $json["authenticationTicket"] = $rUser["gameKey"]; + $json["joinScriptUrl"] = "http://api.graphictoria.cf/Game/Join.ashx?gID=" . $_GET["placeId"]; + exit(json_encode($json, JSON_UNESCAPED_SLASHES)); +?> \ No newline at end of file diff --git a/api/Game/Studio.ashx b/api/Game/Studio.ashx new file mode 100644 index 0000000..7e9c42a --- /dev/null +++ b/api/Game/Studio.ashx @@ -0,0 +1,4103 @@ +%CGacopXx4s4b9X1gsnJ0Ki7YqUd8AAHpI7r5RmfXXJwU+UOzJVzbMc4QyIXV5mhTlg30jihAL7vHp16ODKnHpCckg5Uuftvav9dYxQA+wSfRFFJ5pFrJL7LjOIqMHuhbp4YTnVHtO87MtXVhSwCoxXwKWuj19X4las22iucP4CY=%game:GetService("GuiService").ShowLegacyPlayerList = false +local SetGui = Instance.new("ScreenGui") +local SetMessageFrame = Instance.new("Frame") +local SetText = Instance.new("TextLabel") +SetGui.Name = "SetGui" +SetGui.Parent = game.CoreGui +SetMessageFrame.Name = "SetMessageFrame" +SetMessageFrame.Parent = SetGui +SetMessageFrame.BackgroundColor3 = Color3.new(0, 0, 0) +SetMessageFrame.BackgroundTransparency = 0.5 +SetMessageFrame.BorderSizePixel = 0 +SetMessageFrame.Position = UDim2.new(0.25, 0, .3850000026, 0) +SetMessageFrame.Size = UDim2.new(0.5, 0, 0.125, 0) +SetMessageFrame.SizeConstraint = Enum.SizeConstraint.RelativeXX + +SetText.Name = "SetText" +SetText.Parent = SetMessageFrame +SetText.BackgroundTransparency = 1 +SetText.Position = UDim2.new(0, 0, 0.425000012, 0) +SetText.Size = UDim2.new(1, 0, 0.150000006, 0) +SetText.Font = Enum.Font.ArialBold +SetText.FontSize = Enum.FontSize.Size24 +SetText.Text = "" +SetText.TextColor3 = Color3.new(1, 1, 1) +SetText.FontSize = "Size24" +SetText.TextWrap = true + +function setMessage(text) + if (text == "") then + SetMessageFrame.Visible = false + else + SetMessageFrame.Visible = true + SetText.Text = text + end +end +setMessage("") + +local RBXGui = game.CoreGui.RobloxGui +local Popup = Instance.new("Frame", RBXGui) +local Report = Instance.new("TextButton", Popup) +local Cancel = Instance.new("TextButton", Popup) +local Title = Instance.new("TextLabel", Popup) +local Info = Instance.new("TextLabel", Popup) +local DialogFrame = Instance.new("Frame", RBXGui) + +RBXGui.BottomLeftControl.Position = UDim2.new(0, 0, 1, -46) +RBXGui.BottomLeftControl.Size = UDim2.new(0, 130, 0, 46) +RBXGui.BottomLeftControl.Image = "" +RBXGui.BottomLeftControl.Exit.BackgroundTransparency = 1 +RBXGui.BottomLeftControl.Exit.Position = UDim2.new(0, 2, 0, -2) +RBXGui.BottomLeftControl.Exit.Size = UDim2.new(0, 56, 0, 41) +RBXGui.BottomLeftControl.Exit.Image = "rbxasset://textures/Exitnew.png" +RBXGui.BottomRightControl.BackgroundTransparency = 1 +RBXGui.BottomRightControl.Position = UDim2.new(1, -180, 1, -41) +RBXGui.BottomRightControl.Size = UDim2.new(0, 180, 0, 41) +RBXGui.BottomRightControl.Image = "" +RBXGui.BottomRightControl.CameraZoomIn.BackgroundTransparency = 1 +RBXGui.BottomRightControl.CameraZoomIn.Position = UDim2.new(1, -29, 1, -55) +RBXGui.BottomRightControl.CameraZoomIn.Size = UDim2.new(0, 25, 0, 25) +RBXGui.BottomRightControl.CameraZoomIn.Image = "rbxasset://textures/CameraZoomIn.png" +RBXGui.BottomRightControl.CameraZoomOut.BackgroundTransparency = 1 +RBXGui.BottomRightControl.CameraZoomOut.Position = UDim2.new(1, -29, 1, -29) +RBXGui.BottomRightControl.CameraZoomOut.Size = UDim2.new(0, 25, 0, 25) +RBXGui.BottomRightControl.CameraZoomOut.Image = "rbxasset://textures/CameraZoomOut.png" +RBXGui.BottomRightControl.CameraTiltUp.BackgroundTransparency = 1 +RBXGui.BottomRightControl.CameraTiltUp.Position = UDim2.new(1, -56, 1, -55) +RBXGui.BottomRightControl.CameraTiltUp.Size = UDim2.new(0, 25, 0, 25) +RBXGui.BottomRightControl.CameraTiltUp.Image = "rbxasset://textures/CameraTiltUp.png" +RBXGui.BottomRightControl.CameraTiltDown.BackgroundTransparency = 1 +RBXGui.BottomRightControl.CameraTiltDown.Position = UDim2.new(1, -56, 1, -29) +RBXGui.BottomRightControl.CameraTiltDown.Size = UDim2.new(0, 25, 0, 25) +RBXGui.BottomRightControl.CameraTiltDown.Image = "rbxasset://textures/CameraTiltDown.png" +RBXGui.BottomRightControl.Screenshot.BackgroundTransparency = 1 +RBXGui.BottomRightControl.Screenshot.Position = UDim2.new(1, -118, 1, -40) +RBXGui.BottomRightControl.Screenshot.Size = UDim2.new(0, 30, 0, 30) +RBXGui.BottomRightControl.Screenshot.Image = "rbxasset://textures/Screenshot.png" +RBXGui.BottomRightControl.RecordToggle.BackgroundTransparency = 1 +RBXGui.BottomRightControl.RecordToggle.Position = UDim2.new(1, -150, 1, -40) +RBXGui.BottomRightControl.RecordToggle.Size = UDim2.new(0, 30, 0, 30) +RBXGui.BottomRightControl.RecordToggle.Image = "rbxasset://textures/RecordToggle.png" +RBXGui.BottomRightControl.ToggleFullScreen.BackgroundTransparency = 1 +RBXGui.BottomRightControl.ToggleFullScreen.Position = UDim2.new(1, -85, 1, -48) +RBXGui.BottomRightControl.ToggleFullScreen.Size = UDim2.new(0, 24, 0, 41) +RBXGui.BottomRightControl.ToggleFullScreen.Image = "rbxasset://textures/ToggleFullScreennew.png" +RBXGui.BottomRightControl.ReportAbuse.Parent = nil +RBXGui.BottomLeftControl.SettingsButton.Parent = nil +RBXGui.BottomLeftControl.TogglePlayMode.Parent = nil +RBXGui.BottomLeftControl.ToolButton.Parent = nil +RBXGui.TopLeftControl.Parent = nil + +DialogFrame.Name = "DialogFrame" +DialogFrame.Size = UDim2.new(0.2, 0, 0.06, 0) +DialogFrame.Position = UDim2.new(0, 0, 0.94, 0) +DialogFrame.BackgroundTransparency = 1 + +Popup.Name = "Popup" +Popup.Position = UDim2.new(0.5, -165, 0.5, -125) +Popup.Size = UDim2.new(0, 330, 0, 250) +Popup.ZIndex = 4 +Popup.Style = Enum.FrameStyle.RobloxRound +Popup.Visible = false + +Report.Name = "Report" +Report.Position = UDim2.new(0, 20, 0, 170) +Report.Size = UDim2.new(0, 100, 0, 50) +Report.ZIndex = 5 +Report.Style = Enum.ButtonStyle.RobloxButton +Report.Font = Enum.Font.ArialBold +Report.FontSize = Enum.FontSize.Size24 +Report.Text = "Report" +Report.TextColor3 = Color3.new(0.972549, 0.972549, 0.972549) + +Cancel.Name = "Cancel" +Cancel.Position = UDim2.new(1, -120, 0, 170) +Cancel.Size = UDim2.new(0, 100, 0, 50) +Cancel.ZIndex = 5 +Cancel.Style = Enum.ButtonStyle.RobloxButton +Cancel.Font = Enum.Font.ArialBold +Cancel.FontSize = Enum.FontSize.Size24 +Cancel.Text = "Cancel" +Cancel.TextColor3 = Color3.new(0.972549, 0.972549, 0.972549) + +Title.Name = "Title" +Title.BackgroundTransparency = 1 +Title.Position = UDim2.new(0, 0, -0.0299999993, 0) +Title.Size = UDim2.new(1, 0, 0.5, 0) +Title.ZIndex = 5 +Title.Font = Enum.Font.ArialBold +Title.FontSize = Enum.FontSize.Size36 +Title.Text = "Report" +Title.TextColor3 = Color3.new(0.972549, 0.972549, 0.972549) +Title.TextWrap = true + +Info.Name = "Info" +Info.BackgroundTransparency = 1 +Info.Position = UDim2.new(0, 0, 0.219999999, 0) +Info.Size = UDim2.new(1, 0, 0.5, 0) +Info.ZIndex = 5 +Info.Font = Enum.Font.Arial +Info.FontSize = Enum.FontSize.Size18 +Info.Text = "Info" +Info.TextColor3 = Color3.new(0.972549, 0.972549, 0.972549) +Info.TextWrap = true + +Cancel.MouseButton1Click:connect(function() + Popup.Visible = not Popup.Visible +end) + +RBXGui.BottomRightControl.ToggleFullScreen.MouseLeave:connect(function() + RBXGui.BottomRightControl.ToggleFullScreen.Active = true + RBXGui.BottomRightControl.ToggleFullScreen.Image = "rbxasset://textures/ToggleFullScreennew.png" +end) + +RBXGui.BottomRightControl.ToggleFullScreen.MouseEnter:connect(function() + RBXGui.BottomRightControl.ToggleFullScreen.Image = "rbxasset://textures/ToggleFullScreen_dn.png" + RBXGui.BottomRightControl.ToggleFullScreen.Active = true +end) + +RBXGui.BottomLeftControl.Exit.MouseLeave:connect(function() + RBXGui.BottomLeftControl.Exit.Image = "rbxasset://textures/Exitnew.png" +end) + +RBXGui.BottomLeftControl.Exit.MouseEnter:connect(function() + RBXGui.BottomLeftControl.Exit.Image = "rbxasset://textures/Exit_dn.png" +end) + +game:GetService("GuiService").Changed:connect(function() + game:GetService("GuiService").ShowLegacyPlayerList = false + game.CoreGui.RobloxGui.PlayerListScript.Parent = nil + game.CoreGui.RobloxGui.PlayerListTopRightFrame.Parent = nil + game.CoreGui.RobloxGui.BigPlayerListWindowImposter.Parent = nil + game.CoreGui.RobloxGui.BigPlayerlist.Parent = nil +end) + +coroutine.resume(coroutine.create(function() + function waitForProperty(instance, name) + while not instance[name] do + instance.Changed:wait() + end + end + + function waitForChild(instance, name) + while not instance:FindFirstChild(name) do + instance.ChildAdded:wait() + end + end + + + local mainFrame + local choices = {} + local lastChoice + local choiceMap = {} + local currentConversationDialog + local currentConversationPartner + local currentAbortDialogScript + + local KillDialogs = {} + + local tooFarAwayMessage = "You are too far away to chat!" + local tooFarAwaySize = 300 + local characterWanderedOffMessage = "Chat ended because you walked away" + local characterWanderedOffSize = 350 + local conversationTimedOut = "Chat ended because you did not reply" + local conversationTimedOutSize = 350 + + local player + local screenGui + local chatNotificationGui + local messageDialog + local reenableDialogScript + local dialogMap = {} + local dialogConnections = {} + + local gui = game.CoreGui.RobloxGui + + + function currentTone() + if currentConversationDialog then + return currentConversationDialog.Tone + else + return Enum.DialogTone.Neutral + end + end + + + function createChatNotificationGui() + chatNotificationGui = Instance.new("BillboardGui") + chatNotificationGui.Name = "ChatNotificationGui" + chatNotificationGui.ExtentsOffset = Vector3.new(0,1,0) + chatNotificationGui.Size = UDim2.new(4, 0, 5.42857122, 0) + chatNotificationGui.SizeOffset = Vector2.new(0,0) + chatNotificationGui.StudsOffset = Vector3.new(0.4, 4.3, 0) + chatNotificationGui.Enabled = true + chatNotificationGui.Active = true + + local image = Instance.new("ImageLabel") + image.Name = "Image" + image.Active = false + image.BackgroundTransparency = 1 + image.Position = UDim2.new(0,0,0,0) + image.Size = UDim2.new(1.0,0,1.0,0) + image.Image = "" + image.Parent = chatNotificationGui + + + local button = Instance.new("ImageButton") + button.Name = "Button" + button.AutoButtonColor = false + button.Position = UDim2.new(0.0879999995, 0, 0.0529999994, 0) + button.Size = UDim2.new(0.829999983, 0, 0.460000008, 0) + button.Image = "" + button.BackgroundTransparency = 1 + button.Parent = image + end + + function getChatColor(tone) + if tone == Enum.DialogTone.Neutral then + return Enum.ChatColor.Blue + elseif tone == Enum.DialogTone.Friendly then + return Enum.ChatColor.Green + elseif tone == Enum.DialogTone.Enemy then + return Enum.ChatColor.Red + end + end + + function styleChoices(tone) + for i, obj in pairs(choices) do + resetColor(obj, tone) + end + resetColor(lastChoice, tone) + end + + function styleMainFrame(tone) + if tone == Enum.DialogTone.Neutral then + mainFrame.Style = Enum.FrameStyle.ChatBlue + mainFrame.Tail.Image = "rbxasset://textures/chatBubble_botBlue_tailRight.png" + elseif tone == Enum.DialogTone.Friendly then + mainFrame.Style = Enum.FrameStyle.ChatGreen + mainFrame.Tail.Image = "rbxasset://textures/chatBubble_botGreen_tailRight.png" + elseif tone == Enum.DialogTone.Enemy then + mainFrame.Style = Enum.FrameStyle.ChatRed + mainFrame.Tail.Image = "rbxasset://textures/chatBubble_botRed_tailRight.png" + end + + styleChoices(tone) + end + function setChatNotificationTone(gui, purpose, tone) + if tone == Enum.DialogTone.Neutral then + gui.Image.Image = "rbxasset://textures/chatBubble_botBlue_notify_bkg.png" + elseif tone == Enum.DialogTone.Friendly then + gui.Image.Image = "rbxasset://textures/chatBubble_botGreen_notify_bkg.png" + elseif tone == Enum.DialogTone.Enemy then + gui.Image.Image = "rbxasset://textures/chatBubble_botRed_notify_bkg.png" + end + if purpose == Enum.DialogPurpose.Quest then + gui.Image.Button.Image = "rbxasset://textures/chatBubble_bot_notify_bang.png" + elseif purpose == Enum.DialogPurpose.Help then + gui.Image.Button.Image = "rbxasset://textures/chatBubble_bot_notify_question.png" + elseif purpose == Enum.DialogPurpose.Shop then + gui.Image.Button.Image = "rbxasset://textures/chatBubble_bot_notify_money.png" + end + end + + function createMessageDialog() + messageDialog = Instance.new("Frame"); + messageDialog.Name = "DialogScriptMessage" + messageDialog.Style = Enum.FrameStyle.RobloxRound + messageDialog.Visible = false + + local text = Instance.new("TextLabel") + text.Name = "Text" + text.Position = UDim2.new(0,0,0,-1) + text.Size = UDim2.new(1,0,1,0) + text.FontSize = Enum.FontSize.Size14 + text.BackgroundTransparency = 1 + text.TextColor3 = Color3.new(1,1,1) + text.Parent = messageDialog + end + + function showMessage(msg, size) + messageDialog.Text.Text = msg + messageDialog.Size = UDim2.new(0,size,0,40) + messageDialog.Position = UDim2.new(0.5, -size/2, 0.5, -40) + messageDialog.Visible = true + wait(2) + messageDialog.Visible = false + end + + function variableDelay(str) + local length = math.min(string.len(str), 100) + wait(0.75 + ((length/75) * 1.5)) + end + + function resetColor(frame, tone) + if tone == Enum.DialogTone.Neutral then + frame.BackgroundColor3 = Color3.new(0/255, 0/255, 179/255) + frame.Number.TextColor3 = Color3.new(45/255, 142/255, 245/255) + elseif tone == Enum.DialogTone.Friendly then + frame.BackgroundColor3 = Color3.new(0/255, 77/255, 0/255) + frame.Number.TextColor3 = Color3.new(0/255, 190/255, 0/255) + elseif tone == Enum.DialogTone.Enemy then + frame.BackgroundColor3 = Color3.new(140/255, 0/255, 0/255) + frame.Number.TextColor3 = Color3.new(255/255,88/255, 79/255) + end + end + + function highlightColor(frame, tone) + if tone == Enum.DialogTone.Neutral then + frame.BackgroundColor3 = Color3.new(2/255, 108/255, 255/255) + frame.Number.TextColor3 = Color3.new(1, 1, 1) + elseif tone == Enum.DialogTone.Friendly then + frame.BackgroundColor3 = Color3.new(0/255, 128/255, 0/255) + frame.Number.TextColor3 = Color3.new(1, 1, 1) + elseif tone == Enum.DialogTone.Enemy then + frame.BackgroundColor3 = Color3.new(204/255, 0/255, 0/255) + frame.Number.TextColor3 = Color3.new(1, 1, 1) + end + end + + function wanderDialog() + mainFrame.Visible = false + endDialog() + showMessage(characterWanderedOffMessage, characterWanderedOffSize) + end + + function timeoutDialog() + mainFrame.Visible = false + endDialog() + showMessage(conversationTimedOut, conversationTimedOutSize) + end + function normalEndDialog() + endDialog() + end + + function endDialog() + if currentAbortDialogScript then + currentAbortDialogScript:Remove() + currentAbortDialogScript = nil + end + + local dialog = currentConversationDialog + currentConversationDialog = nil + if dialog and dialog.InUse then + + end + + for dialog, gui in pairs(dialogMap) do + if dialog and gui then + gui.Enabled = not dialog.InUse + end + end + + currentConversationPartner = nil + end + + function sanitizeMessage(msg) + if string.len(msg) == 0 then + return "..." + else + return msg + end + end + + function selectChoice(choice) + renewKillswitch(currentConversationDialog) + mainFrame.Visible = false + if choice == lastChoice then + game.Chat:Chat(game.Players.LocalPlayer.Character, "Goodbye!", getChatColor(currentTone())) + + normalEndDialog() + else + local dialogChoice = choiceMap[choice] + + game.Chat:Chat(game.Players.LocalPlayer.Character, sanitizeMessage(dialogChoice.UserDialog), getChatColor(currentTone())) + wait(1) + coroutine.resume(coroutine.create(function() + currentConversationDialog:SignalDialogChoiceSelected(player, dialogChoice) + end)) + game.Chat:Chat(currentConversationPartner, sanitizeMessage(dialogChoice.ResponseDialog), getChatColor(currentTone())) + + variableDelay(dialogChoice.ResponseDialog) + presentDialogChoices(currentConversationPartner, dialogChoice:GetChildren()) + end + end + + function newChoice(numberText) + local frame = Instance.new("TextButton") + frame.BackgroundColor3 = Color3.new(0/255, 0/255, 179/255) + frame.AutoButtonColor = false + frame.BorderSizePixel = 0 + frame.Text = "" + frame.MouseEnter:connect(function() highlightColor(frame, currentTone()) end) + frame.MouseLeave:connect(function() resetColor(frame, currentTone()) end) + frame.MouseButton1Click:connect(function() selectChoice(frame) end) + + local number = Instance.new("TextLabel") + number.Name = "Number" + number.TextColor3 = Color3.new(127/255, 212/255, 255/255) + number.Text = numberText + number.FontSize = Enum.FontSize.Size14 + number.BackgroundTransparency = 1 + number.Position = UDim2.new(0,4,0,2) + number.Size = UDim2.new(0,20,0,24) + number.TextXAlignment = Enum.TextXAlignment.Left + number.TextYAlignment = Enum.TextYAlignment.Top + number.Parent = frame + + local prompt = Instance.new("TextLabel") + prompt.Name = "UserPrompt" + prompt.BackgroundTransparency = 1 + prompt.TextColor3 = Color3.new(1,1,1) + prompt.FontSize = Enum.FontSize.Size14 + prompt.Position = UDim2.new(0,28, 0, 2) + prompt.Size = UDim2.new(1,-32, 1, -4) + prompt.TextXAlignment = Enum.TextXAlignment.Left + prompt.TextYAlignment = Enum.TextYAlignment.Top + prompt.TextWrap = true + prompt.Parent = frame + + return frame + end + function initialize(parent) + choices[1] = newChoice("1)") + choices[2] = newChoice("2)") + choices[3] = newChoice("3)") + choices[4] = newChoice("4)") + + lastChoice = newChoice("5)") + lastChoice.UserPrompt.Text = "Goodbye!" + lastChoice.Size = UDim2.new(1,0,0,28) + + mainFrame = Instance.new("Frame") + mainFrame.Name = "UserDialogArea" + mainFrame.Size = UDim2.new(0, 350, 0, 200) + mainFrame.Style = Enum.FrameStyle.ChatBlue + mainFrame.Visible = false + + imageLabel = Instance.new("ImageLabel") + imageLabel.Name = "Tail" + imageLabel.Size = UDim2.new(0,62,0,53) + imageLabel.Position = UDim2.new(1,8,0.25) + imageLabel.Image = "rbxasset://textures/chatBubble_botBlue_tailRight.png" + imageLabel.BackgroundTransparency = 1 + imageLabel.Parent = mainFrame + for n, obj in pairs(choices) do + obj.Parent = mainFrame + end + lastChoice.Parent = mainFrame + mainFrame.Parent = parent + end + + function presentDialogChoices(talkingPart, dialogChoices) + if not currentConversationDialog then + return + end + currentConversationPartner = talkingPart + local sortedDialogChoices = {} + for n, obj in pairs(dialogChoices) do + if obj:IsA("DialogChoice") then + table.insert(sortedDialogChoices, obj) + end + end + table.sort(sortedDialogChoices, function(a,b) return a.Name < b.Name end) + + if #sortedDialogChoices == 0 then + normalEndDialog() + return + end + local pos = 1 + local yPosition = 0 + choiceMap = {} + for n, obj in pairs(choices) do + obj.Visible = false + end + for n, obj in pairs(sortedDialogChoices) do + if pos <= #choices then + choices[pos].Size = UDim2.new(1, 0, 0, 24*3) + choices[pos].UserPrompt.Text = obj.UserDialog + local height = math.ceil(choices[pos].UserPrompt.TextBounds.Y/24)*24 + + choices[pos].Position = UDim2.new(0, 0, 0, yPosition) + choices[pos].Size = UDim2.new(1, 0, 0, height) + choices[pos].Visible = true + + choiceMap[choices[pos]] = obj + + yPosition = yPosition + height + pos = pos + 1 + end + end + lastChoice.Position = UDim2.new(0,0,0,yPosition) + lastChoice.Number.Text = pos .. ")" + mainFrame.Size = UDim2.new(0, 350, 0, yPosition+24+32) + mainFrame.Position = UDim2.new(0,20,0.0, -mainFrame.Size.Y.Offset-20) + styleMainFrame(currentTone()) + mainFrame.Visible = true + end + + function doDialog(dialog) + coroutine.resume(coroutine.create(function() + dialog.InUse = true + + + currentConversationDialog = dialog + game.Chat:Chat(dialog.Parent, dialog.InitialPrompt, getChatColor(dialog.Tone)) + variableDelay(dialog.InitialPrompt) + presentDialogChoices(dialog.Parent, dialog:GetChildren()) + end)) + end + + + function renewKillswitch(dialog) + if KillDialogs[dialog] == nil then + KillDialogs[dialog] = 0 + end + KillDialogs[dialog] = KillDialogs[dialog] + 1 + local CurrentDialogNumber = KillDialogs[dialog] + coroutine.resume(coroutine.create(function() + wait(15) + if KillDialogs[dialog] == CurrentDialogNumber then + if dialog:IsA("Dialog") then + dialog.InUse = false + end + end + end)) + end + + function checkForLeaveArea() + while currentConversationDialog do + if currentConversationDialog.Parent and (player:DistanceFromCharacter(currentConversationDialog.Parent.Position) >= currentConversationDialog.ConversationDistance) then + wanderDialog() + end + wait(1) + end + end + + function startDialog(dialog) + if dialog.Parent and dialog.Parent:IsA("BasePart") then + if player:DistanceFromCharacter(dialog.Parent.Position) >= dialog.ConversationDistance then + showMessage(tooFarAwayMessage, tooFarAwaySize) + return + end + for dialog, gui in pairs(dialogMap) do + if dialog and gui then + gui.Enabled = false + end + end + renewKillswitch(dialog) + delay(1, checkForLeaveArea) + doDialog(dialog) + end + end + + function removeDialog(dialog) + if dialogMap[dialog] then + dialogMap[dialog]:Remove() + dialogMap[dialog] = nil + end + if dialogConnections[dialog] then + dialogConnections[dialog]:disconnect() + dialogConnections[dialog] = nil + end + end + + function addDialog(dialog) + if dialog.Parent and dialog.Parent:IsA("BasePart") then + local chatGui = chatNotificationGui:clone() + chatGui.Enabled = not dialog.InUse + chatGui.Adornee = dialog.Parent + chatGui.Parent = game.CoreGui + chatGui.Image.Button.MouseButton1Click:connect(function() startDialog(dialog) end) + setChatNotificationTone(chatGui, dialog.Purpose, dialog.Tone) + dialogMap[dialog] = chatGui + dialogConnections[dialog] = dialog.Changed:connect(function(prop) + if prop == "Parent" and dialog.Parent then + removeDialog(dialog) + addDialog(dialog) + elseif prop == "InUse" then + chatGui.Enabled = not currentConversationDialog and not dialog.InUse + if dialog == currentConversationDialog then + timeoutDialog() + end + elseif prop == "Tone" or prop == "Purpose" then + setChatNotificationTone(chatGui, dialog.Purpose, dialog.Tone) + end + end) + end + end + + function fetchScripts() + + end + + function onLoad() + waitForProperty(game.Players, "LocalPlayer") + player = game.Players.LocalPlayer + waitForProperty(player, "Character") + fetchScripts() + createChatNotificationGui() + createMessageDialog() + messageDialog.Parent = gui + local frame = Instance.new("Frame") + frame.Name = "DialogFrame" + frame.Position = UDim2.new(0,0,0,0) + frame.Size = UDim2.new(0,0,0,0) + frame.BackgroundTransparency = 1 + frame.Parent = gui.DialogFrame + initialize(frame) + game.CollectionService.ItemAdded:connect(function(obj) if obj:IsA("Dialog") then addDialog(obj) end end) + game.CollectionService.ItemRemoved:connect(function(obj) if obj:IsA("Dialog") then removeDialog(obj) end end) + for i, obj in pairs(game.CollectionService:GetCollection("Dialog")) do + if obj:IsA("Dialog") then + addDialog(obj) + end + end + end + + onLoad() + end)) + + pcall(function() game:GetService("Players"):SetChatStyle(Enum.ChatStyle.ClassicAndBubble) end) + local tags = { + ["energycell"] = "http://xdiscuss.net/html/img/badges/2.png" + } + + coroutine.resume(coroutine.create(function() + game.CoreGui.PlayerListGUI.Parent = nil + end)) + + + coroutine.resume(coroutine.create(function() + for _,v in pairs(game:GetChildren()) do + if v.Name == "GuiRoot" then + coroutine.resume(coroutine.create(function() + v.ScoreHud.Parent = nil + end)) end end end)) + + + local PlayerlistScreenGui = Instance.new("ScreenGui",game.CoreGui) + PlayerlistScreenGui.Name = "PlayerListGUI" + + local t = {} + + local function ScopedConnect(parentInstance, instance, event, signalFunc, syncFunc, removeFunc) + local eventConnection = nil + local tryConnect = function() + if game:IsAncestorOf(parentInstance) then + if not eventConnection then + eventConnection = instance[event]:connect(signalFunc) + if syncFunc then syncFunc() end + end + else + if eventConnection then + eventConnection:disconnect() + if removeFunc then removeFunc() end + end + end + end + + local connection = parentInstance.AncestryChanged:connect(tryConnect) + + tryConnect() + + return connection + end + + local function CreateButtons(frame, buttons, yPos, ySize) + local buttonNum = 1 + local buttonObjs = {} + for i, obj in ipairs(buttons) do + local button = Instance.new("TextButton") + button.Name = "Button" .. buttonNum + button.Font = Enum.Font.Arial + button.FontSize = Enum.FontSize.Size18 + button.AutoButtonColor = true + button.Style = Enum.ButtonStyle.RobloxButtonDefault + button.Text = obj.Text + button.TextColor3 = Color3.new(1,1,1) + button.MouseButton1Click:connect(obj.Function) + button.Parent = frame + buttonObjs[buttonNum] = button + + buttonNum = buttonNum + 1 + end + local numButtons = buttonNum-1 + + if numButtons == 1 then + frame.Button1.Position = UDim2.new(0.35, 0, yPos.Scale, yPos.Offset) + frame.Button1.Size = UDim2.new(.4,0,ySize.Scale, ySize.Offset) + elseif numButtons == 2 then + frame.Button1.Position = UDim2.new(0.1, 0, yPos.Scale, yPos.Offset) + frame.Button1.Size = UDim2.new(.8/3,0, ySize.Scale, ySize.Offset) + + frame.Button2.Position = UDim2.new(0.55, 0, yPos.Scale, yPos.Offset) + frame.Button2.Size = UDim2.new(.35,0, ySize.Scale, ySize.Offset) + elseif numButtons >= 3 then + local spacing = .1 / numButtons + local buttonSize = .9 / numButtons + + buttonNum = 1 + while buttonNum <= numButtons do + buttonObjs[buttonNum].Position = UDim2.new(spacing*buttonNum + (buttonNum-1) * buttonSize, 0, yPos.Scale, yPos.Offset) + buttonObjs[buttonNum].Size = UDim2.new(buttonSize, 0, ySize.Scale, ySize.Offset) + buttonNum = buttonNum + 1 + end + end + end + + local function setSliderPos(newAbsPosX,slider,sliderPosition,bar,steps) + + local newStep = steps - 1 + + local relativePosX = math.min(1, math.max(0, (newAbsPosX - bar.AbsolutePosition.X) / bar.AbsoluteSize.X ) ) + local wholeNum, remainder = math.modf(relativePosX * newStep) + if remainder > 0.5 then + wholeNum = wholeNum + 1 + end + relativePosX = wholeNum/newStep + + local result = math.ceil(relativePosX * newStep) + if sliderPosition.Value ~= (result + 1) then + sliderPosition.Value = result + 1 + + if relativePosX == 1 then + slider.Position = UDim2.new(1,-slider.AbsoluteSize.X,slider.Position.Y.Scale,slider.Position.Y.Offset) + else + slider.Position = UDim2.new(relativePosX,0,slider.Position.Y.Scale,slider.Position.Y.Offset) + end + end + + end + + local function cancelSlide(areaSoak) + areaSoak.Visible = false + if areaSoakMouseMoveCon then areaSoakMouseMoveCon:disconnect() end + end + + t.CreateStyledMessageDialog = function(title, message, style, buttons) + local frame = Instance.new("Frame") + frame.Size = UDim2.new(0.5, 0, 0, 165) + frame.Position = UDim2.new(0.25, 0, 0.5, -72.5) + frame.Name = "MessageDialog" + frame.Active = true + frame.Style = Enum.FrameStyle.RobloxRound + + local styleImage = Instance.new("ImageLabel") + styleImage.Name = "StyleImage" + styleImage.BackgroundTransparency = 1 + styleImage.Position = UDim2.new(0,5,0,15) + if style == "error" or style == "Error" then + styleImage.Size = UDim2.new(0, 71, 0, 71) + styleImage.Image = "http://www.roblox.com/asset?id=42565285" + elseif style == "notify" or style == "Notify" then + styleImage.Size = UDim2.new(0, 71, 0, 71) + styleImage.Image = "http://www.roblox.com/asset?id=42604978" + elseif style == "confirm" or style == "Confirm" then + styleImage.Size = UDim2.new(0, 74, 0, 76) + styleImage.Image = "http://www.roblox.com/asset?id=42557901" + else + return t.CreateMessageDialog(title,message,buttons) + end + styleImage.Parent = frame + + local titleLabel = Instance.new("TextLabel") + titleLabel.Name = "Title" + titleLabel.Text = title + titleLabel.BackgroundTransparency = 1 + titleLabel.TextColor3 = Color3.new(221/255,221/255,221/255) + titleLabel.Position = UDim2.new(0, 80, 0, 0) + titleLabel.Size = UDim2.new(1, -80, 0, 40) + titleLabel.Font = Enum.Font.ArialBold + titleLabel.FontSize = Enum.FontSize.Size36 + titleLabel.TextXAlignment = Enum.TextXAlignment.Center + titleLabel.TextYAlignment = Enum.TextYAlignment.Center + titleLabel.Parent = frame + + local messageLabel = Instance.new("TextLabel") + messageLabel.Name = "Message" + messageLabel.Text = message + messageLabel.TextColor3 = Color3.new(221/255,221/255,221/255) + messageLabel.Position = UDim2.new(0.025, 80, 0, 45) + messageLabel.Size = UDim2.new(0.95, -80, 0, 55) + messageLabel.BackgroundTransparency = 1 + messageLabel.Font = Enum.Font.Arial + messageLabel.FontSize = Enum.FontSize.Size18 + messageLabel.TextWrap = true + messageLabel.TextXAlignment = Enum.TextXAlignment.Left + messageLabel.TextYAlignment = Enum.TextYAlignment.Top + messageLabel.Parent = frame + + CreateButtons(frame, buttons, UDim.new(0, 105), UDim.new(0, 40) ) + + return frame + end + + t.CreateMessageDialog = function(title, message, buttons) + local frame = Instance.new("Frame") + frame.Size = UDim2.new(0.5, 0, 0.5, 0) + frame.Position = UDim2.new(0.25, 0, 0.25, 0) + frame.Name = "MessageDialog" + frame.Active = true + frame.Style = Enum.FrameStyle.RobloxRound + + local titleLabel = Instance.new("TextLabel") + titleLabel.Name = "Title" + titleLabel.Text = title + titleLabel.BackgroundTransparency = 1 + titleLabel.TextColor3 = Color3.new(221/255,221/255,221/255) + titleLabel.Position = UDim2.new(0, 0, 0, 0) + titleLabel.Size = UDim2.new(1, 0, 0.15, 0) + titleLabel.Font = Enum.Font.ArialBold + titleLabel.FontSize = Enum.FontSize.Size36 + titleLabel.TextXAlignment = Enum.TextXAlignment.Center + titleLabel.TextYAlignment = Enum.TextYAlignment.Center + titleLabel.Parent = frame + + local messageLabel = Instance.new("TextLabel") + messageLabel.Name = "Message" + messageLabel.Text = message + messageLabel.TextColor3 = Color3.new(221/255,221/255,221/255) + messageLabel.Position = UDim2.new(0.025, 0, 0.175, 0) + messageLabel.Size = UDim2.new(0.95, 0, .55, 0) + messageLabel.BackgroundTransparency = 1 + messageLabel.Font = Enum.Font.Arial + messageLabel.FontSize = Enum.FontSize.Size18 + messageLabel.TextWrap = true + messageLabel.TextXAlignment = Enum.TextXAlignment.Left + messageLabel.TextYAlignment = Enum.TextYAlignment.Top + messageLabel.Parent = frame + + CreateButtons(frame, buttons, UDim.new(0.8,0), UDim.new(0.15, 0)) + + return frame + end + + t.CreateDropDownMenu = function(items, onSelect, forRoblox) + local width = UDim.new(0, 100) + local height = UDim.new(0, 32) + + local xPos = 0.055 + local frame = Instance.new("Frame") + frame.Name = "DropDownMenu" + frame.BackgroundTransparency = 1 + frame.Size = UDim2.new(width, height) + + local dropDownMenu = Instance.new("TextButton") + dropDownMenu.Name = "DropDownMenuButton" + dropDownMenu.TextWrap = true + dropDownMenu.TextColor3 = Color3.new(1,1,1) + dropDownMenu.Text = "Choose One" + dropDownMenu.Font = Enum.Font.ArialBold + dropDownMenu.FontSize = Enum.FontSize.Size18 + dropDownMenu.TextXAlignment = Enum.TextXAlignment.Left + dropDownMenu.TextYAlignment = Enum.TextYAlignment.Center + dropDownMenu.BackgroundTransparency = 1 + dropDownMenu.AutoButtonColor = true + dropDownMenu.Style = Enum.ButtonStyle.RobloxButton + dropDownMenu.Size = UDim2.new(1,0,1,0) + dropDownMenu.Parent = frame + dropDownMenu.ZIndex = 2 + + local dropDownIcon = Instance.new("ImageLabel") + dropDownIcon.Name = "Icon" + dropDownIcon.Active = false + dropDownIcon.Image = "http://www.roblox.com/asset/?id=45732894" + dropDownIcon.BackgroundTransparency = 1 + dropDownIcon.Size = UDim2.new(0,11,0,6) + dropDownIcon.Position = UDim2.new(1,-11,0.5, -2) + dropDownIcon.Parent = dropDownMenu + dropDownIcon.ZIndex = 2 + + local itemCount = #items + local dropDownItemCount = #items + local useScrollButtons = false + if dropDownItemCount > 6 then + useScrollButtons = true + dropDownItemCount = 6 + end + + local droppedDownMenu = Instance.new("TextButton") + droppedDownMenu.Name = "List" + droppedDownMenu.Text = "" + droppedDownMenu.BackgroundTransparency = 1 + droppedDownMenu.Style = Enum.ButtonStyle.RobloxButton + droppedDownMenu.Visible = false + droppedDownMenu.Active = true + droppedDownMenu.Position = UDim2.new(0,0,0,0) + droppedDownMenu.Size = UDim2.new(1,0, (1 + dropDownItemCount)*.8, 0) + droppedDownMenu.Parent = frame + droppedDownMenu.ZIndex = 2 + + local choiceButton = Instance.new("TextButton") + choiceButton.Name = "ChoiceButton" + choiceButton.BackgroundTransparency = 1 + choiceButton.BorderSizePixel = 0 + choiceButton.Text = "ReplaceMe" + choiceButton.TextColor3 = Color3.new(1,1,1) + choiceButton.TextXAlignment = Enum.TextXAlignment.Left + choiceButton.TextYAlignment = Enum.TextYAlignment.Center + choiceButton.BackgroundColor3 = Color3.new(1, 1, 1) + choiceButton.Font = Enum.Font.Arial + choiceButton.FontSize = Enum.FontSize.Size18 + if useScrollButtons then + choiceButton.Size = UDim2.new(1,-13, .8/((dropDownItemCount + 1)*.8),0) + else + choiceButton.Size = UDim2.new(1, 0, .8/((dropDownItemCount + 1)*.8),0) + end + choiceButton.TextWrap = true + choiceButton.ZIndex = 2 + + local dropDownSelected = false + + local scrollUpButton + local scrollDownButton + local scrollMouseCount = 0 + + local setZIndex = function(baseZIndex) + droppedDownMenu.ZIndex = baseZIndex +1 + if scrollUpButton then + scrollUpButton.ZIndex = baseZIndex + 3 + end + if scrollDownButton then + scrollDownButton.ZIndex = baseZIndex + 3 + end + + local children = droppedDownMenu:GetChildren() + if children then + for i, child in ipairs(children) do + if child.Name == "ChoiceButton" then + child.ZIndex = baseZIndex + 2 + elseif child.Name == "ClickCaptureButton" then + child.ZIndex = baseZIndex + end + end + end + end + + local scrollBarPosition = 1 + local updateScroll = function() + if scrollUpButton then + scrollUpButton.Active = scrollBarPosition > 1 + end + if scrollDownButton then + scrollDownButton.Active = scrollBarPosition + dropDownItemCount <= itemCount + end + + local children = droppedDownMenu:GetChildren() + if not children then return end + + local childNum = 1 + for i, obj in ipairs(children) do + if obj.Name == "ChoiceButton" then + if childNum < scrollBarPosition or childNum >= scrollBarPosition + dropDownItemCount then + obj.Visible = false + else + obj.Position = UDim2.new(0,0,((childNum-scrollBarPosition+1)*.8)/((dropDownItemCount+1)*.8),0) + obj.Visible = true + end + obj.TextColor3 = Color3.new(1,1,1) + obj.BackgroundTransparency = 1 + + childNum = childNum + 1 + end + end + end + local toggleVisibility = function() + dropDownSelected = not dropDownSelected + + dropDownMenu.Visible = not dropDownSelected + droppedDownMenu.Visible = dropDownSelected + if dropDownSelected then + setZIndex(4) + else + setZIndex(2) + end + if useScrollButtons then + updateScroll() + end + end + droppedDownMenu.MouseButton1Click:connect(toggleVisibility) + + local updateSelection = function(text) + local foundItem = false + local children = droppedDownMenu:GetChildren() + local childNum = 1 + if children then + for i, obj in ipairs(children) do + if obj.Name == "ChoiceButton" then + if obj.Text == text then + obj.Font = Enum.Font.ArialBold + foundItem = true + scrollBarPosition = childNum + else + obj.Font = Enum.Font.Arial + end + childNum = childNum + 1 + end + end + end + if not text then + dropDownMenu.Text = "Choose One" + scrollBarPosition = 1 + else + if not foundItem then + error("Invalid Selection Update -- " .. text) + end + + if scrollBarPosition + dropDownItemCount > itemCount + 1 then + scrollBarPosition = itemCount - dropDownItemCount + 1 + end + + dropDownMenu.Text = text + end + end + + local function scrollDown() + if scrollBarPosition + dropDownItemCount <= itemCount then + scrollBarPosition = scrollBarPosition + 1 + updateScroll() + return true + end + return false + end + local function scrollUp() + if scrollBarPosition > 1 then + scrollBarPosition = scrollBarPosition - 1 + updateScroll() + return true + end + return false + end + + if useScrollButtons then + scrollUpButton = Instance.new("ImageButton") + scrollUpButton.Name = "ScrollUpButton" + scrollUpButton.BackgroundTransparency = 1 + scrollUpButton.Image = "rbxasset://textures/ui/scrollbuttonUp.png" + scrollUpButton.Size = UDim2.new(0,17,0,17) + scrollUpButton.Position = UDim2.new(1,-11,(1*.8)/((dropDownItemCount+1)*.8),0) + scrollUpButton.MouseButton1Click:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + end) + scrollUpButton.MouseLeave:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + end) + scrollUpButton.MouseButton1Down:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + + scrollUp() + local val = scrollMouseCount + wait(0.5) + while val == scrollMouseCount do + if scrollUp() == false then + break + end + wait(0.1) + end + end) + + scrollUpButton.Parent = droppedDownMenu + + scrollDownButton = Instance.new("ImageButton") + scrollDownButton.Name = "ScrollDownButton" + scrollDownButton.BackgroundTransparency = 1 + scrollDownButton.Image = "rbxasset://textures/ui/scrollbuttonDown.png" + scrollDownButton.Size = UDim2.new(0,17,0,17) + scrollDownButton.Position = UDim2.new(1,-11,1,-11) + scrollDownButton.Parent = droppedDownMenu + scrollDownButton.MouseButton1Click:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + end) + scrollDownButton.MouseLeave:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + end) + scrollDownButton.MouseButton1Down:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + + scrollDown() + local val = scrollMouseCount + wait(0.5) + while val == scrollMouseCount do + if scrollDown() == false then + break + end + wait(0.1) + end + end) + + local scrollbar = Instance.new("ImageLabel") + scrollbar.Name = "ScrollBar" + scrollbar.Image = "rbxasset://textures/ui/scrollbar.png" + scrollbar.BackgroundTransparency = 1 + scrollbar.Size = UDim2.new(0, 18, (dropDownItemCount*.8)/((dropDownItemCount+1)*.8), -(17) - 11 - 4) + scrollbar.Position = UDim2.new(1,-11,(1*.8)/((dropDownItemCount+1)*.8),17+2) + scrollbar.Parent = droppedDownMenu + end + + for i,item in ipairs(items) do + local button = choiceButton:clone() + if forRoblox then + end + button.Text = item + button.Parent = droppedDownMenu + button.MouseButton1Click:connect(function() + button.TextColor3 = Color3.new(1,1,1) + button.BackgroundTransparency = 1 + + updateSelection(item) + onSelect(item) + + toggleVisibility() + end) + button.Visible = false + button.MouseEnter:connect(function() + button.TextColor3 = Color3.new(0,0,0) + button.BackgroundTransparency = 0 + button.Visible = false + end) + + button.MouseLeave:connect(function() + button.TextColor3 = Color3.new(1,1,1) + button.BackgroundTransparency = 1 + button.Visible = false + end) + end + updateScroll() + + local bigFakeButton = Instance.new("TextButton") + bigFakeButton.BackgroundTransparency = 1 + bigFakeButton.Name = "ClickCaptureButton" + bigFakeButton.Size = UDim2.new(0, 4000, 0, 3000) + bigFakeButton.Position = UDim2.new(0, -2000, 0, -1500) + bigFakeButton.ZIndex = 1 + bigFakeButton.Text = "" + bigFakeButton.Parent = droppedDownMenu + bigFakeButton.MouseButton1Click:connect(toggleVisibility) + + dropDownMenu.MouseButton1Click:connect(toggleVisibility) + return frame, updateSelection + end + + t.CreatePropertyDropDownMenu = function(instance, property, enum) + + local items = enum:GetEnumItems() + local names = {} + local nameToItem = {} + for i,obj in ipairs(items) do + names[i] = obj.Name + nameToItem[obj.Name] = obj + end + + local frame + local updateSelection + frame, updateSelection = t.CreateDropDownMenu(names, function(text) instance[property] = nameToItem[text] end) + + ScopedConnect(frame, instance, "Changed", + function(prop) + if prop == property then + updateSelection(instance[property].Name) + end + end, + function() + updateSelection(instance[property].Name) + end) + + return frame + end + + t.GetFontHeight = function(font, fontSize) + if font == nil or fontSize == nil then + error("Font and FontSize must be non-nil") + end + + if font == Enum.Font.Legacy then + if fontSize == Enum.FontSize.Size8 then + return 12 + elseif fontSize == Enum.FontSize.Size9 then + return 14 + elseif fontSize == Enum.FontSize.Size10 then + return 15 + elseif fontSize == Enum.FontSize.Size11 then + return 17 + elseif fontSize == Enum.FontSize.Size12 then + return 18 + elseif fontSize == Enum.FontSize.Size14 then + return 21 + elseif fontSize == Enum.FontSize.Size18 then + return 27 + elseif fontSize == Enum.FontSize.Size24 then + return 36 + elseif fontSize == Enum.FontSize.Size36 then + return 54 + elseif fontSize == Enum.FontSize.Size48 then + return 72 + else + error("Unknown FontSize") + end + elseif font == Enum.Font.Arial or font == Enum.Font.ArialBold then + if fontSize == Enum.FontSize.Size8 then + return 8 + elseif fontSize == Enum.FontSize.Size9 then + return 9 + elseif fontSize == Enum.FontSize.Size10 then + return 10 + elseif fontSize == Enum.FontSize.Size11 then + return 11 + elseif fontSize == Enum.FontSize.Size12 then + return 12 + elseif fontSize == Enum.FontSize.Size14 then + return 14 + elseif fontSize == Enum.FontSize.Size18 then + return 18 + elseif fontSize == Enum.FontSize.Size24 then + return 24 + elseif fontSize == Enum.FontSize.Size36 then + return 36 + elseif fontSize == Enum.FontSize.Size48 then + return 48 + else + error("Unknown FontSize") + end + else + error("Unknown Font " .. font) + end + end + + local function layoutGuiObjectsHelper(frame, guiObjects, settingsTable) + local totalPixels = frame.AbsoluteSize.Y + local pixelsRemaining = frame.AbsoluteSize.Y + for i, child in ipairs(guiObjects) do + if child:IsA("TextLabel") or child:IsA("TextButton") then + local isLabel = child:IsA("TextLabel") + if isLabel then + pixelsRemaining = pixelsRemaining - settingsTable["TextLabelPositionPadY"] + else + pixelsRemaining = pixelsRemaining - settingsTable["TextButtonPositionPadY"] + end + child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining) + child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, pixelsRemaining) + + if child.TextFits and child.TextBounds.Y < pixelsRemaining then + child.Visible = true + if isLabel then + child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.TextBounds.Y + settingsTable["TextLabelSizePadY"]) + else + child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.TextBounds.Y + settingsTable["TextButtonSizePadY"]) + end + + while not child.TextFits do + child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.AbsoluteSize.Y + 1) + end + pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y + + if isLabel then + pixelsRemaining = pixelsRemaining - settingsTable["TextLabelPositionPadY"] + else + pixelsRemaining = pixelsRemaining - settingsTable["TextButtonPositionPadY"] + end + else + child.Visible = false + pixelsRemaining = -1 + end + + else + child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining) + pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y + child.Visible = (pixelsRemaining >= 0) + end + end + end + + t.LayoutGuiObjects = function(frame, guiObjects, settingsTable) + if not frame:IsA("GuiObject") then + error("Frame must be a GuiObject") + end + for i, child in ipairs(guiObjects) do + if not child:IsA("GuiObject") then + error("All elements that are layed out must be of type GuiObject") + end + end + + if not settingsTable then + settingsTable = {} + end + + if not settingsTable["TextLabelSizePadY"] then + settingsTable["TextLabelSizePadY"] = 0 + end + if not settingsTable["TextLabelPositionPadY"] then + settingsTable["TextLabelPositionPadY"] = 0 + end + if not settingsTable["TextButtonSizePadY"] then + settingsTable["TextButtonSizePadY"] = 12 + end + if not settingsTable["TextButtonPositionPadY"] then + settingsTable["TextButtonPositionPadY"] = 2 + end + + local wrapperFrame = Instance.new("Frame") + wrapperFrame.Name = "WrapperFrame" + wrapperFrame.BackgroundTransparency = 1 + wrapperFrame.Size = UDim2.new(1,0,1,0) + wrapperFrame.Parent = frame + + for i, child in ipairs(guiObjects) do + child.Parent = wrapperFrame + end + + local recalculate = function() + wait() + layoutGuiObjectsHelper(wrapperFrame, guiObjects, settingsTable) + end + + frame.Changed:connect( + function(prop) + if prop == "AbsoluteSize" then + recalculate() + end + end) + frame.AncestryChanged:connect(recalculate) + + layoutGuiObjectsHelper(wrapperFrame, guiObjects, settingsTable) + end + + + t.CreateSlider = function(steps,width,position) + local sliderGui = Instance.new("Frame") + sliderGui.Size = UDim2.new(1,0,1,0) + sliderGui.BackgroundTransparency = 1 + sliderGui.Name = "SliderGui" + + local areaSoak = Instance.new("TextButton") + areaSoak.Name = "AreaSoak" + areaSoak.Text = "" + areaSoak.BackgroundTransparency = 1 + areaSoak.Active = false + areaSoak.Size = UDim2.new(1,0,1,0) + areaSoak.Visible = false + areaSoak.ZIndex = 4 + areaSoak.Parent = sliderGui + + local sliderPosition = Instance.new("IntValue") + sliderPosition.Name = "SliderPosition" + sliderPosition.Value = 0 + sliderPosition.Parent = sliderGui + + local id = math.random(1,100) + + local bar = Instance.new("Frame") + bar.Name = "Bar" + bar.BackgroundColor3 = Color3.new(0,0,0) + if type(width) == "number" then + bar.Size = UDim2.new(0,width,0,5) + else + bar.Size = UDim2.new(0,200,0,5) + end + bar.BorderColor3 = Color3.new(95/255,95/255,95/255) + bar.ZIndex = 2 + bar.Parent = sliderGui + + if position["X"] and position["X"]["Scale"] and position["X"]["Offset"] and position["Y"] and position["Y"]["Scale"] and position["Y"]["Offset"] then + bar.Position = position + end + + local slider = Instance.new("ImageButton") + slider.Name = "Slider" + slider.BackgroundTransparency = 1 + slider.Image = "rbxasset://textures/ui/Slider.png" + slider.Position = UDim2.new(0,0,0.5,-10) + slider.Size = UDim2.new(0,20,0,20) + slider.ZIndex = 3 + slider.Parent = bar + + local areaSoakMouseMoveCon = nil + + areaSoak.MouseLeave:connect(function() + if areaSoak.Visible then + cancelSlide(areaSoak) + end + end) + areaSoak.MouseButton1Up:connect(function() + if areaSoak.Visible then + cancelSlide(areaSoak) + end + end) + + slider.MouseButton1Down:connect(function() + areaSoak.Visible = true + if areaSoakMouseMoveCon then areaSoakMouseMoveCon:disconnect() end + areaSoakMouseMoveCon = areaSoak.MouseMoved:connect(function(x,y) + setSliderPos(x,slider,sliderPosition,bar,steps) + end) + end) + + slider.MouseButton1Up:connect(function() cancelSlide(areaSoak) end) + + sliderPosition.Changed:connect(function(prop) + local relativePosX = (sliderPosition.Value - 1) / steps + slider.Position = UDim2.new(relativePosX,0,slider.Position.Y.Scale,slider.Position.Y.Offset) + end) + + return sliderGui, sliderPosition + + end + + + t.CreateScrollingFrame = function(orderList,scrollStyle) + local frame = Instance.new("Frame") + frame.Name = "ScrollingFrame" + frame.BackgroundTransparency = 1 + frame.Size = UDim2.new(1,0,1,0) + + local scrollUpButton = Instance.new("ImageButton") + scrollUpButton.Name = "ScrollUpButton" + scrollUpButton.BackgroundTransparency = 1 + scrollUpButton.Image = "rbxasset://textures/ui/scrollbuttonUp.png" + scrollUpButton.Size = UDim2.new(0,17,0,17) + + + local scrollDownButton = Instance.new("ImageButton") + scrollDownButton.Name = "ScrollDownButton" + scrollDownButton.BackgroundTransparency = 1 + scrollDownButton.Image = "rbxasset://textures/ui/scrollbuttonDown.png" + scrollDownButton.Size = UDim2.new(0,17,0,17) + + local style = "simple" + if scrollStyle and tostring(scrollStyle) then + style = scrollStyle + end + + local scrollPosition = 1 + local rowSize = 1 + + local layoutGridScrollBar = function() + local guiObjects = {} + if orderList then + for i, child in ipairs(orderList) do + if child.Parent == frame then + table.insert(guiObjects, child) + end + end + else + local children = frame:GetChildren() + if children then + for i, child in ipairs(children) do + if child:IsA("GuiObject") then + table.insert(guiObjects, child) + end + end + end + end + if #guiObjects == 0 then + scrollUpButton.Active = false + scrollDownButton.Active = false + scrollPosition = 1 + return + end + + if scrollPosition > #guiObjects then + scrollPosition = #guiObjects + end + + local totalPixelsY = frame.AbsoluteSize.Y + local pixelsRemainingY = frame.AbsoluteSize.Y + + local totalPixelsX = frame.AbsoluteSize.X + + local xCounter = 0 + local rowSizeCounter = 0 + local setRowSize = true + + local pixelsBelowScrollbar = 0 + local pos = #guiObjects + while pixelsBelowScrollbar < totalPixelsY and pos >= 1 do + if pos >= scrollPosition then + pixelsBelowScrollbar = pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y + else + xCounter = xCounter + guiObjects[pos].AbsoluteSize.X + rowSizeCounter = rowSizeCounter + 1 + if xCounter >= totalPixelsX then + if setRowSize then + rowSize = rowSizeCounter - 1 + setRowSize = false + end + + rowSizeCounter = 0 + xCounter = 0 + if pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y <= totalPixelsY then + pixelsBelowScrollbar = pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y + if scrollPosition <= rowSize then + scrollPosition = rowSize + break + else + scrollPosition = scrollPosition - rowSize + end + else + break + end + end + end + pos = pos - 1 + end + + xCounter = 0 + pos = scrollPosition + rowSizeCounter = 0 + setRowSize = true + local lastChildSize = 0 + + local xOffset,yOffset = 0 + if guiObjects[1] then + yOffset = math.ceil(math.floor(math.fmod(totalPixelsY,guiObjects[1].AbsoluteSize.X))/2) + xOffset = math.ceil(math.floor(math.fmod(totalPixelsX,guiObjects[1].AbsoluteSize.Y))/2) + end + + for i, child in ipairs(guiObjects) do + if i < scrollPosition then + child.Visible = false + else + if pixelsRemainingY < 0 then + child.Visible = false + else + if setRowSize then rowSizeCounter = rowSizeCounter + 1 end + if xCounter + child.AbsoluteSize.X >= totalPixelsX then + if setRowSize then + rowSize = rowSizeCounter - 1 + setRowSize = false + end + xCounter = 0 + pixelsRemainingY = pixelsRemainingY - child.AbsoluteSize.Y + end + child.Position = UDim2.new(child.Position.X.Scale,xCounter + xOffset, 0, totalPixelsY - pixelsRemainingY + yOffset) + xCounter = xCounter + child.AbsoluteSize.X + child.Visible = ((pixelsRemainingY - child.AbsoluteSize.Y) >= 0) + lastChildSize = child.AbsoluteSize + end + end + end + + scrollUpButton.Active = (scrollPosition > 1) + if lastChildSize == 0 then + scrollDownButton.Active = false + else + scrollDownButton.Active = ((pixelsRemainingY - lastChildSize.Y) < 0) + end + end + + + local layoutSimpleScrollBar = function() + local guiObjects = {} + + if orderList then + for i, child in ipairs(orderList) do + if child.Parent == frame then + table.insert(guiObjects, child) + end + end + else + local children = frame:GetChildren() + if children then + for i, child in ipairs(children) do + if child:IsA("GuiObject") then + table.insert(guiObjects, child) + end + end + end + end + if #guiObjects == 0 then + scrollUpButton.Active = false + scrollDownButton.Active = false + scrollPosition = 1 + return + end + + if scrollPosition > #guiObjects then + scrollPosition = #guiObjects + end + + local totalPixels = frame.AbsoluteSize.Y + local pixelsRemaining = frame.AbsoluteSize.Y + + local pixelsBelowScrollbar = 0 + local pos = #guiObjects + while pixelsBelowScrollbar < totalPixels and pos >= 1 do + if pos >= scrollPosition then + pixelsBelowScrollbar = pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y + else + if pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y <= totalPixels then + pixelsBelowScrollbar = pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y + if scrollPosition <= 1 then + scrollPosition = 1 + break + else + scrollPosition = scrollPosition - 1 + end + else + break + end + end + pos = pos - 1 + end + pos = scrollPosition + for i, child in ipairs(guiObjects) do + if i < scrollPosition then + child.Visible = false + else + if pixelsRemaining < 0 then + child.Visible = false + else + child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining) + pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y + child.Visible = (pixelsRemaining >= 0) + end + end + end + scrollUpButton.Active = (scrollPosition > 1) + scrollDownButton.Active = (pixelsRemaining < 0) + end + + local reentrancyGuard = false + local recalculate = function() + if reentrancyGuard then + return + end + reentrancyGuard = true + wait() + local success, err = nil + if style == "grid" then + success, err = pcall(function() layoutGridScrollBar(frame) end) + elseif style == "simple" then + success, err = pcall(function() layoutSimpleScrollBar(frame) end) + end + if not success then print(err) end + + reentrancyGuard = false + end + + local scrollUp = function() + if scrollUpButton.Active then + scrollPosition = scrollPosition - rowSize + recalculate() + end + end + + local scrollDown = function() + if scrollDownButton.Active then + scrollPosition = scrollPosition + rowSize + recalculate() + end + end + + local scrollMouseCount = 0 + scrollUpButton.MouseButton1Click:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + end) + scrollUpButton.MouseLeave:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + end) + + scrollUpButton.MouseButton1Down:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + + scrollUp() + local val = scrollMouseCount + wait(0.5) + while val == scrollMouseCount do + if scrollUp() == false then + break + end + wait(0.1) + end + end) + + scrollDownButton.MouseButton1Click:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + end) + scrollDownButton.MouseLeave:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + end) + scrollDownButton.MouseButton1Down:connect( + function() + scrollMouseCount = scrollMouseCount + 1 + + scrollDown() + local val = scrollMouseCount + wait(0.5) + while val == scrollMouseCount do + if scrollDown() == false then + break + end + wait(0.1) + end + end) + + + frame.ChildAdded:connect(function() + recalculate() + end) + + frame.ChildRemoved:connect(function() + recalculate() + end) + + frame.Changed:connect( + function(prop) + if prop == "AbsoluteSize" then + recalculate() + end + end) + frame.AncestryChanged:connect(recalculate) + + return frame, scrollUpButton, scrollDownButton, recalculate + end + local function binaryGrow(min, max, fits) + if min > max then + return min + end + local biggestLegal = min + + while min <= max do + local mid = min + math.floor((max - min) / 2) + if fits(mid) and (biggestLegal == nil or biggestLegal < mid) then + biggestLegal = mid + min = mid + 1 + else + max = mid - 1 + end + end + return biggestLegal + end + + + local function binaryShrink(min, max, fits) + if min > max then + return min + end + local smallestLegal = max + + while min <= max do + local mid = min + math.floor((max - min) / 2) + if fits(mid) and (smallestLegal == nil or smallestLegal > mid) then + smallestLegal = mid + max = mid - 1 + else + min = mid + 1 + end + end + return smallestLegal + end + + + local function getGuiOwner(instance) + while instance ~= nil do + if instance:IsA("ScreenGui") or instance:IsA("BillboardGui") then + return instance + end + instance = instance.Parent + end + return nil + end + + t.AutoTruncateTextObject = function(textLabel) + + local text = textLabel.Text + textLabel.TextColor3 = Color3.new(1,1,1) + + local fullLabel = textLabel:Clone() + fullLabel.Name = "Full" .. textLabel.Name + fullLabel.BorderSizePixel = 0 + fullLabel.BackgroundTransparency = 0 + fullLabel.Text = text + fullLabel.TextXAlignment = Enum.TextXAlignment.Center + fullLabel.Position = UDim2.new(0,-3,0,0) + fullLabel.Size = UDim2.new(0,00,0,0) + fullLabel.Visible = false + fullLabel.Parent = textLabel + fullLabel.TextTransparency= 1 + local shortText = nil + local mouseEnterConnection = nil + local mouseLeaveConnection= nil + + local checkForResize = function() + if getGuiOwner(textLabel) == nil then + return + end + textLabel.Text = text + if textLabel.TextFits then + if mouseEnterConnection then + mouseEnterConnection:disconnect() + mouseEnterConnection = nil + end + if mouseLeaveConnection then + mouseLeaveConnection:disconnect() + mouseLeaveConnection = nil + end + else + local len = string.len(text) + textLabel.Text = text .. "~" + local textSize = binaryGrow(0, len, + function(pos) + if pos == 0 then + textLabel.Text = "~" + else + textLabel.Text = string.sub(text, 1, pos) .. "~" + end + return textLabel.TextFits + end) + shortText = string.sub(text, 1, textSize) .. "~" + textLabel.Text = shortText + if not fullLabel.TextFits then + fullLabel.Size = UDim2.new(0, 0, 0, 0) + end + local fullLabelSize = binaryShrink(textLabel.AbsoluteSize.X,fullLabel.AbsoluteSize.X, + function(size) + fullLabel.Size = UDim2.new(0, 0, 0, 0) + return fullLabel.TextFits + end) + fullLabel.Size = UDim2.new(0,0,0,0) + if mouseEnterConnection == nil then + mouseEnterConnection = textLabel.MouseEnter:connect( + function() + fullLabel.ZIndex = textLabel.ZIndex + 1 + fullLabel.Visible = false + end) + end + if mouseLeaveConnection == nil then + mouseLeaveConnection = textLabel.MouseLeave:connect( + function() + fullLabel.Visible = false + end) + end + end + end + textLabel.AncestryChanged:connect(checkForResize) + textLabel.Changed:connect( + function(prop) + if prop == "AbsoluteSize" then + checkForResize() + end + end) + + checkForResize() + + local function changeText(newText) + text = newText + fullLabel.Text = text + checkForResize() + end + + return textLabel, changeText + end + + local function TransitionTutorialPages(fromPage, toPage, transitionFrame, currentPageValue) + if fromPage then + fromPage.Visible = false + if transitionFrame.Visible == false then + transitionFrame.Size = fromPage.Size + transitionFrame.Position = fromPage.Position + end + else + if transitionFrame.Visible == false then + transitionFrame.Size = UDim2.new(0.0,50,0.0,50) + transitionFrame.Position = UDim2.new(0.5,-25,0.5,-25) + end + end + transitionFrame.Visible = true + currentPageValue.Value = nil + + local newsize, newPosition + if toPage then + toPage.Visible = true + + newSize = toPage.Size + newPosition = toPage.Position + + toPage.Visible = false + else + newSize = UDim2.new(0.0,50,0.0,50) + newPosition = UDim2.new(0.5,-25,0.5,-25) + end + transitionFrame:TweenSizeAndPosition(newSize, newPosition, Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.3, true, + function(state) + if state == Enum.TweenStatus.Completed then + transitionFrame.Visible = false + if toPage then + toPage.Visible = true + currentPageValue.Value = toPage + end + end + end) + end + + t.CreateTutorial = function(name, tutorialKey, createButtons) + local frame = Instance.new("Frame") + frame.Name = "Tutorial-" .. name + frame.BackgroundTransparency = 1 + frame.Size = UDim2.new(0.6, 0, 0.6, 0) + frame.Position = UDim2.new(0.2, 0, 0.2, 0) + + local transitionFrame = Instance.new("Frame") + transitionFrame.Name = "TransitionFrame" + transitionFrame.Style = Enum.FrameStyle.RobloxRound + transitionFrame.Size = UDim2.new(0.6, 0, 0.6, 0) + transitionFrame.Position = UDim2.new(0.2, 0, 0.2, 0) + transitionFrame.Visible = false + transitionFrame.Parent = frame + + local currentPageValue = Instance.new("ObjectValue") + currentPageValue.Name = "CurrentTutorialPage" + currentPageValue.Value = nil + currentPageValue.Parent = frame + + local boolValue = Instance.new("BoolValue") + boolValue.Name = "Buttons" + boolValue.Value = createButtons + boolValue.Parent = frame + + local pages = Instance.new("Frame") + pages.Name = "Pages" + pages.BackgroundTransparency = 1 + pages.Size = UDim2.new(1,0,1,0) + pages.Parent = frame + + local function getVisiblePageAndHideOthers() + local visiblePage = nil + local children = pages:GetChildren() + if children then + for i,child in ipairs(children) do + if child.Visible then + if visiblePage then + child.Visible = false + else + visiblePage = child + end + end + end + end + return visiblePage + end + + local showTutorial = function(alwaysShow) + if alwaysShow or UserSettings().GameSettings:GetTutorialState(tutorialKey) == false then + print("Showing tutorial-",tutorialKey) + local currentTutorialPage = getVisiblePageAndHideOthers() + + local firstPage = pages:FindFirstChild("TutorialPage1") + if firstPage then + TransitionTutorialPages(currentTutorialPage, firstPage, transitionFrame, currentPageValue) + else + error("Could not find TutorialPage1") + end + end + end + + local dismissTutorial = function() + local currentTutorialPage = getVisiblePageAndHideOthers() + + if currentTutorialPage then + TransitionTutorialPages(currentTutorialPage, nil, transitionFrame, currentPageValue) + end + + UserSettings().GameSettings:SetTutorialState(tutorialKey, true) + end + + local gotoPage = function(pageNum) + local page = pages:FindFirstChild("TutorialPage" .. pageNum) + local currentTutorialPage = getVisiblePageAndHideOthers() + TransitionTutorialPages(currentTutorialPage, page, transitionFrame, currentPageValue) + end + + return frame, showTutorial, dismissTutorial, gotoPage + end + + local function CreateBasicTutorialPage(name, handleResize, skipTutorial) + local frame = Instance.new("Frame") + frame.Name = "TutorialPage" + frame.Style = Enum.FrameStyle.RobloxRound + frame.Size = UDim2.new(0.6, 0, 0.6, 0) + frame.Position = UDim2.new(0.2, 0, 0.2, 0) + frame.Visible = false + + local frameHeader = Instance.new("TextLabel") + frameHeader.Name = "Header" + frameHeader.Text = name + frameHeader.BackgroundTransparency = 1 + frameHeader.FontSize = Enum.FontSize.Size24 + frameHeader.Font = Enum.Font.ArialBold + frameHeader.TextColor3 = Color3.new(1,1,1) + frameHeader.TextXAlignment = Enum.TextXAlignment.Center + frameHeader.TextWrap = true + frameHeader.Size = UDim2.new(1,-55, 0, 22) + frameHeader.Position = UDim2.new(0,0,0,0) + frameHeader.Parent = frame + + local skipButton = Instance.new("ImageButton") + skipButton.Name = "SkipButton" + skipButton.AutoButtonColor = false + skipButton.BackgroundTransparency = 1 + skipButton.Image = "http://www.roblox.com/asset/?id=37813546" + skipButton.MouseButton1Click:connect(function() + skipButton.Image = "http://www.roblox.com/asset/?id=37813546" + skipTutorial() + end) + skipButton.MouseEnter:connect(function() + skipButton.Image = "http://www.roblox.com/asset/?id=37813556" + end) + skipButton.MouseLeave:connect(function() + skipButton.Image = "http://www.roblox.com/asset/?id=37813546" + end) + skipButton.Size = UDim2.new(0, 55, 0, 22) + skipButton.Position = UDim2.new(1, -55, 0, 0) + skipButton.Parent = frame + + local innerFrame = Instance.new("Frame") + innerFrame.Name = "ContentFrame" + innerFrame.BackgroundTransparency = 1 + innerFrame.Position = UDim2.new(0,0,0,22) + innerFrame.Parent = frame + + local nextButton = Instance.new("TextButton") + nextButton.Name = "NextButton" + nextButton.Text = "Next" + nextButton.TextColor3 = Color3.new(1,1,1) + nextButton.Font = Enum.Font.Arial + nextButton.FontSize = Enum.FontSize.Size18 + nextButton.Style = Enum.ButtonStyle.RobloxButtonDefault + nextButton.Size = UDim2.new(0,80, 0, 32) + nextButton.Position = UDim2.new(0.5, 5, 1, -32) + nextButton.Active = false + nextButton.Visible = false + nextButton.Parent = frame + + local prevButton = Instance.new("TextButton") + prevButton.Name = "PrevButton" + prevButton.Text = "Previous" + prevButton.TextColor3 = Color3.new(1,1,1) + prevButton.Font = Enum.Font.Arial + prevButton.FontSize = Enum.FontSize.Size18 + prevButton.Style = Enum.ButtonStyle.RobloxButton + prevButton.Size = UDim2.new(0,80, 0, 32) + prevButton.Position = UDim2.new(0.5, -85, 1, -32) + prevButton.Active = false + prevButton.Visible = false + prevButton.Parent = frame + + innerFrame.Size = UDim2.new(1,0,1,-22-35) + + local parentConnection = nil + + local function basicHandleResize() + if frame.Visible and frame.Parent then + local maxSize = math.min(frame.Parent.AbsoluteSize.X, frame.Parent.AbsoluteSize.Y) + handleResize(200,maxSize) + end + end + + frame.Changed:connect( + function(prop) + if prop == "Parent" then + if parentConnection ~= nil then + parentConnection:disconnect() + parentConnection = nil + end + if frame.Parent and frame.Parent:IsA("GuiObject") then + parentConnection = frame.Parent.Changed:connect( + function(parentProp) + if parentProp == "AbsoluteSize" then + wait() + basicHandleResize() + end + end) + basicHandleResize() + end + end + + if prop == "Visible" then + basicHandleResize() + end + end) + + return frame, innerFrame + end + + t.CreateTextTutorialPage = function(name, text, skipTutorialFunc) + local frame = nil + local contentFrame = nil + + local textLabel = Instance.new("TextLabel") + textLabel.BackgroundTransparency = 1 + textLabel.TextColor3 = Color3.new(1,1,1) + textLabel.Text = text + textLabel.TextWrap = true + textLabel.TextXAlignment = Enum.TextXAlignment.Left + textLabel.TextYAlignment = Enum.TextYAlignment.Center + textLabel.Font = Enum.Font.Arial + textLabel.FontSize = Enum.FontSize.Size14 + textLabel.Size = UDim2.new(1,0,1,0) + + local function handleResize(minSize, maxSize) + size = binaryShrink(minSize, maxSize, + function(size) + frame.Size = UDim2.new(0, size, 0, size) + return textLabel.TextFits + end) + frame.Size = UDim2.new(0, size, 0, size) + frame.Position = UDim2.new(0.5, -size/2, 0.5, -size/2) + end + + frame, contentFrame = CreateBasicTutorialPage(name, handleResize, skipTutorialFunc) + textLabel.Parent = contentFrame + + return frame + end + + t.CreateImageTutorialPage = function(name, imageAsset, x, y, skipTutorialFunc) + local frame = nil + local contentFrame = nil + + local imageLabel = Instance.new("ImageLabel") + imageLabel.BackgroundTransparency = 1 + imageLabel.Image = imageAsset + imageLabel.Size = UDim2.new(0,x,0,y) + imageLabel.Position = UDim2.new(0.5,-x/2,0.5,-y/2) + + local function handleResize(minSize, maxSize) + size = binaryShrink(minSize, maxSize, + function(size) + return size >= x and size >= y + end) + if size >= x and size >= y then + imageLabel.Size = UDim2.new(0,x, 0,y) + imageLabel.Position = UDim2.new(0.5,-x/2, 0.5, -y/2) + else + if x > y then + imageLabel.Size = UDim2.new(1,0,y/x,0) + imageLabel.Position = UDim2.new(0,0, 0.5 - (y/x)/2, 0) + else + imageLabel.Size = UDim2.new(x/y,0,1, 0) + imageLabel.Position = UDim2.new(0.5-(x/y)/2, 0, 0, 0) + end + end + frame.Size = UDim2.new(0, size, 0, size) + frame.Position = UDim2.new(0.5, -size/2, 0.5, -size/2) + end + + frame, contentFrame = CreateBasicTutorialPage(name, handleResize, skipTutorialFunc) + imageLabel.Parent = contentFrame + + return frame + end + + t.AddTutorialPage = function(tutorial, tutorialPage) + local transitionFrame = tutorial.TransitionFrame + local currentPageValue = tutorial.CurrentTutorialPage + + if not tutorial.Buttons.Value then + tutorialPage.ContentFrame.Size = UDim2.new(1,0,1,-22) + tutorialPage.NextButton.Parent = nil + tutorialPage.PrevButton.Parent = nil + end + + local children = tutorial.Pages:GetChildren() + if children and #children > 0 then + tutorialPage.Name = "TutorialPage" .. (#children+1) + local previousPage = children[#children] + if not previousPage:IsA("GuiObject") then + error("All elements under Pages must be GuiObjects") + end + + if tutorial.Buttons.Value then + if previousPage.NextButton.Active then + error("NextButton already Active on previousPage, please only add pages with RbxGui.AddTutorialPage function") + end + previousPage.NextButton.MouseButton1Click:connect( + function() + TransitionTutorialPages(previousPage, tutorialPage, transitionFrame, currentPageValue) + end) + previousPage.NextButton.Active = true + previousPage.NextButton.Visible = true + + if tutorialPage.PrevButton.Active then + error("PrevButton already Active on tutorialPage, please only add pages with RbxGui.AddTutorialPage function") + end + tutorialPage.PrevButton.MouseButton1Click:connect( + function() + TransitionTutorialPages(tutorialPage, previousPage, transitionFrame, currentPageValue) + end) + tutorialPage.PrevButton.Active = true + tutorialPage.PrevButton.Visible = true + end + + tutorialPage.Parent = tutorial.Pages + else + tutorialPage.Name = "TutorialPage1" + tutorialPage.Parent = tutorial.Pages + end + end + + t.Help = + function(funcNameOrFunc) + if funcNameOrFunc == "CreatePropertyDropDownMenu" or funcNameOrFunc == t.CreatePropertyDropDownMenu then + return "Function CreatePropertyDropDownMenu. " .. + "Arguments: (instance, propertyName, enumType). " .. + "Side effect: returns a container with a drop-down-box that is linked to the property field of instance which is of type enumType" + end + if funcNameOrFunc == "CreateDropDownMenu" or funcNameOrFunc == t.CreateDropDownMenu then + return "Function CreateDropDownMenu. " .. + "Arguments: (items, onItemSelected). " .. + "Side effect: Returns 2 results, a container to the gui object and a updateSelection function for external updating. The container is a drop-down-box created around a list of items" + end + if funcNameOrFunc == "CreateMessageDialog" or funcNameOrFunc == t.CreateMessageDialog then + return "Function CreateMessageDialog. " .. + "Arguments: (title, message, buttons). " .. + "Side effect: Returns a gui object of a message box with title and message as passed in. buttons input is an array of Tables contains a Text and Function field for the text/callback of each button" + end + if funcNameOrFunc == "CreateStyledMessageDialog" or funcNameOrFunc == t.CreateStyledMessageDialog then + return "Function CreateStyledMessageDialog. " .. + "Arguments: (title, message, style, buttons). " .. + "Side effect: Returns a gui object of a message box with title and message as passed in. buttons input is an array of Tables contains a Text and Function field for the text/callback of each button, style is a string, either Error, Notify or Confirm" + end + if funcNameOrFunc == "GetFontHeight" or funcNameOrFunc == t.GetFontHeight then + return "Function GetFontHeight. " .. + "Arguments: (font, fontSize). " .. + "Side effect: returns the size in pixels of the given font + fontSize" + end + if funcNameOrFunc == "LayoutGuiObjects" or funcNameOrFunc == t.LayoutGuiObjects then + + end + if funcNameOrFunc == "CreateScrollingFrame" or funcNameOrFunc == t.CreateScrollingFrame then + return "Function CreateScrollingFrame. " .. + "Arguments: (orderList, style) " .. + "Side effect: returns 4 objects, (scrollFrame, scrollUpButton, scrollDownButton, recalculateFunction). scrollFrame can be filled with GuiObjects. It will lay them out and allow scrollUpButton/scrollDownButton to interact with them. Orderlist is optional (and specifies the order to layout the children. Without orderlist, it uses the children order. style is also optional, and allows for a grid styling if style is passed grid as a string. recalculateFunction can be called when a relayout is needed (when orderList changes)" + end + if funcNameOrFunc == "AutoTruncateTextObject" or funcNameOrFunc == t.AutoTruncateTextObject then + return "Function AutoTruncateTextObject. " .. + "Arguments: (textLabel) " .. + "Side effect: returns 2 objects, (textLabel, changeText). The textLabel input is modified to automatically truncate text (with ellipsis), if it gets too small to fit. changeText is a function that can be used to change the text, it takes 1 string as an argument" + end + if funcNameOrFunc == "CreateSlider" or funcNameOrFunc == t.CreateSlider then + return "Function CreateSlider. " .. + "Arguments: (steps, width, position) " .. + "Side effect: returns 2 objects, (sliderGui, sliderPosition). The steps argument specifies how many different positions the slider can hold along the bar. width specifies in pixels how wide the bar should be (modifiable afterwards if desired). position argument should be a UDim2 for slider positioning. sliderPosition is an IntValue whose current .Value specifies the specific step the slider is currently on." + end + end + + local RbxGui + local friendWord = "Friend" + local friendWordLowercase = "friend" + local bigEasingStyle = Enum.EasingStyle.Back + local smallEasingStyle = Enum.EasingStyle.Quart + local lightBackground = true + + local function waitForChild(instance, name) + coroutine.resume(coroutine.create(function() + + while not instance:FindFirstChild(name) do + instance.ChildAdded:wait() + end + end)) + end + + local function waitForProperty(instance, prop) + + end + + local function Color3I(r,g,b) + return Color3.new(r/255,g/255,b/255) + end + + function robloxLock(instance) + + end + + function ArrayRemove(t, obj) + for i, obj2 in ipairs(t) do + if obj == obj2 then + table.remove(t, i) + return true + end + end + return false + end + + local function getPlayers() + local result = {} + local players = game:GetService("Players"):GetChildren() + if players then + for i, player in ipairs(players) do + if player:IsA("Player") then + table.insert(result, player) + end + end + end + return result + end + + local brickColorTable = {} + for i = 0, 63 do + brickColorTable[BrickColor.palette(i).Name] = BrickColor.palette(i).Color + end + + local function remapColor(i, j) + brickColorTable[BrickColor.palette(i).Name] = BrickColor.palette(j).Color + end + + remapColor(13, 12) + remapColor(14, 12) + remapColor(15, 12) + remapColor(61, 29) + remapColor(63, 62) + remapColor(56, 50) + remapColor(45, 53) + remapColor(51, 20) + remapColor(4, 20) + remapColor(59, 35) + remapColor(60, 29) + + local function getColor(brickColor) + if brickColorTable[brickColor.Name] then + return brickColorTable[brickColor.Name] + else + return brickColor.Color; + end + end + + + + local function getTeams() + local result = {} + local teams = game:GetService("Teams"):GetChildren() + for i, team in ipairs(teams) do + if team:IsA("Team") then + table.insert(result, team) + end + end + return result + end + + local supportFriends = false + local currentBoardType = "PlayerList" + local currentStatCount = 0 + + local createBoardsFunction = nil + + + local playerTable = {} + local teamTable = {} + local teamColorTable = {} + + local removePlayerFunction = nil + local recreatePlayerFunction = nil + local addPlayerFunction = function(player) + if recreatePlayerFunction then + recreatePlayerFunction(player) + end + end + local sortPlayerListsFunction = nil + + local minimizedState = nil + local bigWindowImposter = nil + local smallWindowPosition = UDim2.new(0, -20, 0,5) + local smallWindowSize = UDim2.new(1,0,1,0) + local bigWindowSize = UDim2.new(0.6,0,0.6,0) + local bigWindowPosition = UDim2.new(.2, 0, .2,0) + + local debounceTeamsChanged = false + + local currentWindowState = "Small" + local previousWindowState = nil + local transitionWindowsFunction = nil + + local container = nil + local topRightTrayContainer = nil + + local playerContextMenu = nil + local contextMenuElements = {} + + local function addContextMenuLabel(getText1, getText2, isVisible) + local t = {} + t.Type = "Label" + t.GetText1 = getText1 + t.GetText2 = getText2 + t.IsVisible = isVisible + table.insert(contextMenuElements, t) + end + local function addContextMenuButton(text, isVisible, isActive, doIt) + local t = {} + t.Text = text + t.Type = "Button" + t.IsVisible = isVisible + t.IsActive = isActive + t.DoIt = doIt + table.insert(contextMenuElements, t) + end + + local function getFriendStatus(player) + if player == game.Players.LocalPlayer then + return Enum.FriendStatus.NotFriend + else + return Enum.FriendStatus.NotFriend + end + end + addContextMenuLabel( + function(player) + return "Loading..." + end, + nil, + function(player) + return getFriendStatus(player) == Enum.FriendStatus.Unknown + end) + addContextMenuButton("Send " .. friendWord .. " Request", + function(player) + return getFriendStatus(player) == Enum.FriendStatus.NotFriend + end, + function(player) + return true + end, + function(player) + return game.Players.LocalPlayer:RequestFriendship(player) + end + ) + addContextMenuButton("Un" .. friendWordLowercase, + function(player) + return getFriendStatus(player) == Enum.FriendStatus.Friend + end, + function(player) + return true + end, + function(player) + end + ) + addContextMenuButton("Accept " .. friendWord .. " Request", + function(player) + return getFriendStatus(player) == Enum.FriendStatus.FriendRequestReceived + end, + function(player) + return true + end, + function(player) + end + ) + + addContextMenuButton("Deny " .. friendWord .. " Request", + function(player) + return getFriendStatus(player) == Enum.FriendStatus.FriendRequestReceived + end, + function(player) + return true + end, + function(player) + return game.Players.LocalPlayer:RevokeFriendship(player) + end + ) + + addContextMenuButton("Cancel " .. friendWord .. " Request", + function(player) + return getFriendStatus(player) == Enum.FriendStatus.FriendRequestSent + end, + function(player) + return true + end, + function(player) + return game.Players.LocalPlayer:RevokeFriendship(player) + end + ) + + + local function getStatColumns(players) + for i, player in ipairs(players) do + local leaderstats = player:FindFirstChild("leaderstats") + if leaderstats then + local stats = {} + local children = leaderstats:GetChildren() + if children then + for i, stat in ipairs(children) do + if stat:IsA("IntValue") then + table.insert(stats, stat.Name) + else + table.insert(stats, stat.Name) + end + end + end + return stats + end + end + return nil + end + + local function determineBoardType() + local players = getPlayers() + + local foundLeaderstats = false + local numStats = 0 + local foundTeam = false + + local stats = getStatColumns(players) + if stats then + foundLeaderstats = true + numStats = #stats + end + + for i, player in ipairs(players) do + if not foundTeam then + if not player.Neutral then + foundTeam = true + break + end + end + end + + if foundLeaderstats and foundTeam then + return "TeamScore", numStats + elseif foundLeaderstats then + return "PlayerScore", numStats + elseif foundTeam then + return "TeamList", numStats + else + return "PlayerList", numStats + end + end + + local function toggleBigWindow() + if container == nil then + return + end + + if currentWindowState == "Big" then + if previousWindowState == nil or previousWindowState == "Big" or previousWindowState == "None" then + transitionWindowsFunction("None") + else + transitionWindowsFunction("Small") + end + else + previousWindowState = currentWindowState + transitionWindowsFunction("Big") + end + end + local previousBigPlayerList = nil + local function rebuildBoard(owner, boardType, numStats) + print("RebuildBoard") + if topRightTrayContainer == nil then + topRightTrayContainer = owner:FindFirstChild("PlayerListTopRightFrame") + if topRightTrayContainer == nil then + topRightTrayContainer = Instance.new("Frame") + topRightTrayContainer.Name = "PlayerListTopRightFrame" + topRightTrayContainer.BackgroundTransparency = 1 + topRightTrayContainer.Size = UDim2.new(0.2, 16, 0.42, 16) + topRightTrayContainer.Position = UDim2.new(0.8, 0, 0, 0) + topRightTrayContainer.Parent = container + end + end + if minimizedState == nil then + minimizedState = Instance.new("Frame") + minimizedState.Name = "MinimizedPlayerlist" + minimizedState.BackgroundTransparency = 1 + minimizedState.Position = UDim2.new(1, -166, 0,0) + minimizedState.Size = UDim2.new(0, 151, 0, 30) + playerListButton = Instance.new("ImageButton") + playerListButton.Name = "GoSmallButton" + playerListButton.Image = "rbxasset://textures/ui/playerlist_hidden_small.png" + playerListButton.BackgroundTransparency = 1 + playerListButton.Size = UDim2.new(0.0, 35, 0, 30) + playerListButton.Position = UDim2.new(1, -35, 0, 0) + playerListButton.MouseButton1Click:connect( + function() + transitionWindowsFunction("Small") + end) + playerListButton.Parent = minimizedState + + minimizedState.Visible = false + robloxLock(minimizedState) + minimizedState.Parent = topRightTrayContainer + end + if bigWindowImposter == nil then + bigWindowImposter = owner:FindFirstChild("BigPlayerListWindowImposter") + if bigWindowImposter == nil then + bigWindowImposter = Instance.new("Frame") + bigWindowImposter.Name = "BigPlayerListWindowImposter" + bigWindowImposter.Visible = false + bigWindowImposter.BackgroundColor3 = Color3.new(0,0,0) + bigWindowImposter.BackgroundTransparency = 0.7 + bigWindowImposter.BorderSizePixel = 0 + bigWindowImposter.Size = UDim2.new(0.4, 7, 0.4, 7) + bigWindowImposter.Position = UDim2.new(0.3, 0, 0.3, 0) + robloxLock(bigWindowImposter) + bigWindowImposter.Parent = container + end + end + if container == nil or container ~= owner then + container = owner + + topRightTrayContainer.Parent = container + bigWindowImposter.Parent = container + end + + local smallVisible = true + local bigVisible = false + if container then + if topRightTrayContainer then + if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then + smallVisible = topRightTrayContainer.SmallPlayerlist.Visible + topRightTrayContainer.SmallPlayerlist.Parent = nil + print("Removed small playerlist") + else + print("Did not remove small playerlist") + end + end + if container:FindFirstChild("BigPlayerlist") then + bigVisible = container.BigPlayerlist.Visible or (previousBigPlayerList ~= nil) + container.BigPlayerlist.Parent = nil + if previousBigPlayerList ~= nil then + pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end) + previousBigPlayerList = nil + end + end + end + + local smallBoard, bigBoard = createBoardsFunction(boardType, numStats) + if smallBoard then + smallBoard.Visible = smallVisible + smallBoard.Parent = topRightTrayContainer + end + if bigBoard then + if bigVisible then + previousBigPlayerList = bigBoard + local centerDialogSupported, msg = pcall(function() game.GuiService:AddCenterDialog(previousBigPlayerList, Enum.CenterDialogType.PlayerInitiatedDialog, + function() + previousBigPlayerList.Visible = true + end) + end) + bigBoard.Visible = bigVisible + else + bigBoard.Visible = false + end + bigBoard.Parent = container + end + return container + end + + local function showBigPlayerWindow() + if container:FindFirstChild("BigPlayerlist") then + if container.BigPlayerlist.Visible then + return + end + end + + bigWindowImposter.Visible = true + bigWindowImposter:TweenSizeAndPosition(bigWindowSize, bigWindowPosition, Enum.EasingDirection.Out, bigEasingStyle, 0.3, true, + function(state) + if state == Enum.TweenStatus.Completed then + bigWindowImposter.Visible = false + if container:FindFirstChild("BigPlayerlist") then + container.BigPlayerlist.Visible = true + end + end + end) + end + + local function hideBigPlayerWindow(completed) + if playerContextMenu then + playerContextMenu.Visible = false + end + + if container:FindFirstChild("BigPlayerlist") then + if container.BigPlayerlist.Visible == false and bigWindowImposter.Visible == false then + if completed then + completed() + end + return + end + container.BigPlayerlist.Visible = false + end + + local completedFunction = completed + bigWindowImposter.Visible = true + bigWindowImposter:TweenSizeAndPosition(UDim2.new(0.4, 0, 0.4, 0), UDim2.new(0.3, 0, 0.3, 0), Enum.EasingDirection.In, Enum.EasingStyle.Quart, 0.15, true, + function(state) + if state == Enum.TweenStatus.Completed then + bigWindowImposter.Visible = false + if completedFunction then + completedFunction() + end + end + end) + end + local function hideSmallPlayerWindow(completed) + if playerContextMenu then + playerContextMenu.Visible = false + end + if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then + local completedFunction = completed + if topRightTrayContainer.SmallPlayerlist.Visible then + topRightTrayContainer.SmallPlayerlist:TweenPosition(UDim2.new(1,0,smallWindowPosition.Y.Scale, smallWindowPosition.Y.Offset), Enum.EasingDirection.Out, smallEasingStyle, 0.3, true, + function(state) + if state == Enum.TweenStatus.Completed then + if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then + topRightTrayContainer.SmallPlayerlist.Visible = false + end + if completedFunction then + completedFunction() + end + end + end) + return + end + end + if completed then + completed() + end + end + + + transitionWindowsFunction = function(desiredState) + if desiredState == "Big" then + minimizedState.Visible = false + hideSmallPlayerWindow() + + if previousBigPlayerList ~= nil then + if previousBigPlayerList ~= container:FindFirstChild("BigPlayerlist") then + pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end) + previousBigPlayerList = nil + previousBigPlayerList = container:FindFirstChild("BigPlayerlist") + end + else + previousBigPlayerList = container:FindFirstChild("BigPlayerlist") + end + + if previousBigPlayerList then + local firstShow = false + local centerDialogSupported, msg = pcall(function() game.GuiService:AddCenterDialog(previousBigPlayerList, Enum.CenterDialogType.PlayerInitiatedDialog, + function() + if not firstShow then + showBigPlayerWindow() + firstShow = true + else + previousBigPlayerList.Visible = true + end + end) + end) + if centerDialogSupported == false then + print("Exception", msg) + showBigPlayerWindow() + end + else + showBigPlayerWindow() + end + currentWindowState = "Big" + elseif desiredState == "Small" then + minimizedState.Visible = false + if previousBigPlayerList ~= nil then + pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end) + previousBigPlayerList = nil + end + + hideBigPlayerWindow() + if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then + if not topRightTrayContainer.SmallPlayerlist.Visible or topRightTrayContainer.SmallPlayerlist.Position ~= smallWindowPosition then + topRightTrayContainer.SmallPlayerlist.Visible = true + topRightTrayContainer.SmallPlayerlist:TweenPosition(smallWindowPosition, Enum.EasingDirection.Out, smallEasingStyle, 0.3, true) + end + end + currentWindowState = "Small" + elseif desiredState == "None" then + if previousBigPlayerList ~= nil then + pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end) + previousBigPlayerList = nil + end + + local smallDone = false + local bigDone = false + hideSmallPlayerWindow( + function() + smallDone = true + if bigDone and smallDone then + minimizedState.Visible = true + end + end) + hideBigPlayerWindow( + function() + bigDone = true + if bigDone and smallDone then + minimizedState.Visible = true + end + end) + currentWindowState = "None" + end + end + + local function getStatValuesForPlayer(player) + local leaderstats = player:FindFirstChild("leaderstats") + if leaderstats then + local children = leaderstats:GetChildren() + if children then + local result = {} + for i, stat in ipairs(children) do + table.insert(result, stat) + end + return result, leaderstats + end + end + return nil + end + if UserSettings and LoadLibrary then + + RbxGui,msg = t + print("Libraries loaded") + + local function createTeamName(name, color) + local fontHeight = 20 + local frame = Instance.new("Frame") + frame.Name = "Team-" .. name + frame.BorderSizePixel = 0 + frame.BackgroundTransparency = 0.5 + frame.BackgroundColor3 = Color3.new(1,1,1) + frame.Size = UDim2.new(1, 0, 0, fontHeight) + frame.Position = UDim2.new(0,0,0,0) + + local label = Instance.new("TextLabel") + label.Name = "NameLabel" + label.Text = " " .. name + label.Font = Enum.Font.ArialBold + label.FontSize = Enum.FontSize.Size18 + label.Position = UDim2.new(0,0,0,0) + label.Size = UDim2.new(1,0,1,0) + label.TextColor3 = Color3.new(1,1,1) + label.BackgroundTransparency = 0.5 + label.BackgroundColor3 = getColor(color) + label.BorderSizePixel = 0 + label.TextXAlignment = Enum.TextXAlignment.Left + label = RbxGui.AutoTruncateTextObject(label) + label.Parent = frame + + return frame + end + + local function getFriendStatusIcon(friendStatus) + if friendStatus == Enum.FriendStatus.Unknown or friendStatus == Enum.FriendStatus.NotFriend then + return nil + elseif friendStatus == Enum.FriendStatus.Friend then + return "rbxasset://textures/ui/PlayerlistFriendIcon.png" + elseif friendStatus == Enum.FriendStatus.FriendRequestSent then + return "rbxasset://textures/ui/PlayerlistFriendRequestSentIcon.png" + elseif friendStatus == Enum.FriendStatus.FriendRequestReceived then + return "rbxasset://textures/ui/PlayerlistFriendRequestReceivedIcon.png" + else + error("Unknown FriendStatus: " .. friendStatus) + end + end + + local function getMembershipTypeIcon(membershipType) + if membershipType == Enum.MembershipType.None then + return "" + elseif membershipType == Enum.MembershipType.BuildersClub then + return "rbxasset://textures/ui/TinyBcIcon.png" + elseif membershipType == Enum.MembershipType.TurboBuildersClub then + return "rbxasset://textures/ui/TinyTbcIcon.png" + elseif membershipType == Enum.MembershipType.OutrageousBuildersClub then + return "rbxasset://textures/ui/TinyObcIcon.png" + else + error("Uknown membershipType" .. membershipType) + end + end + + + local function updatePlayerFriendStatus(nameObject, friendStatus) + local fontHeight = 20 + + local friendIconImage = getFriendStatusIcon(friendStatus) + nameObject.MembershipTypeLabel.FriendStatusLabel.Visible = (friendIconImage ~= nil) + + if friendIconImage ~= nil then + nameObject.MembershipTypeLabel.FriendStatusLabel.Image = friendIconImage + nameObject.NameLabel.Position =UDim2.new(0,2*fontHeight,0,1) + nameObject.NameLabel.Size = UDim2.new(1,-2*fontHeight,1,-2) + else + nameObject.NameLabel.Position = UDim2.new(0,fontHeight+1,0,1) + nameObject.NameLabel.Size = UDim2.new(1,-(fontHeight+1),1,-2) + end + end + local function updatePlayerName(nameObject, membershipStatus, teamColor) + local fontHeight = 20 + nameObject.Size = UDim2.new(1,0,0,fontHeight) + nameObject.MembershipTypeLabel.Image = tags[string.lower(nameObject.NameLabel.FullNameLabel.Text)] or "" + end + + + local function updatePlayerNameColor(player, teamColor) + local function updatePlayerNameColorHelper(nameObject) + if teamColor ~= nil then + nameObject.NameLabel.TextColor3 = getColor(teamColor) + nameObject.NameLabel.FullNameLabel.TextColor3 = getColor(teamColor) + else + nameObject.NameLabel.TextColor3 = Color3.new(1,1,1) + nameObject.NameLabel.FullNameLabel.TextColor3 = Color3.new(1,1,1) + end + end + + updatePlayerNameColorHelper(playerTable[player].NameObjectSmall) + updatePlayerNameColorHelper(playerTable[player].NameObjectBig) + end + + + local function createPlayerName(name, membershipStatus, teamColor, friendStatus) + local frame = Instance.new("Frame") + frame.Name = "Player_" .. name + if lightBackground then + frame.BackgroundColor3 = Color3.new(1,1,1) + else + frame.BackgroundColor3 = Color3.new(1,1,1) + end + frame.BackgroundTransparency = 0.5 + frame.BorderSizePixel = 0 + + local membershipStatusLabel = Instance.new("ImageLabel") + membershipStatusLabel.Name = "MembershipTypeLabel" + membershipStatusLabel.BackgroundTransparency = 1 + membershipStatusLabel.Size = UDim2.new(1,0,1,0) + membershipStatusLabel.Position = UDim2.new(0,0,0,0) + membershipStatusLabel.SizeConstraint = Enum.SizeConstraint.RelativeYY + membershipStatusLabel.Parent = frame + + local friendStatusLabel = Instance.new("ImageLabel") + friendStatusLabel.Name = "FriendStatusLabel" + friendStatusLabel.Visible = false + friendStatusLabel.BackgroundTransparency = 1 + friendStatusLabel.Size = UDim2.new(1,0,1,0) + friendStatusLabel.Position = UDim2.new(1,0,0,0) + friendStatusLabel.Parent = membershipStatusLabel + + local changeNameFunction + local nameLabel = Instance.new("TextLabel") + nameLabel.Name = "NameLabel" + nameLabel.Text = ""..name + nameLabel.Font = Enum.Font.ArialBold + nameLabel.FontSize = Enum.FontSize.Size14 + nameLabel.TextColor3 = Color3.new(1,1,1) + nameLabel.BackgroundTransparency = 1 + nameLabel.BackgroundColor3 = Color3.new(0,0,0) + nameLabel.TextXAlignment = Enum.TextXAlignment.Left + nameLabel, changeNameFunction = RbxGui.AutoTruncateTextObject(nameLabel) + nameLabel.Parent = frame + + updatePlayerName(frame, membershipStatus, teamColor) + if supportFriends then + updatePlayerFriendStatus(frame, friendStatus) + else + updatePlayerFriendStatus(frame, Enum.FriendStatus.NotFriend) + end + return frame, changeNameFunction + end + + local function createStatColumn(i, numColumns, isTeam, color3, isHeader) + local textLabel = Instance.new("TextLabel") + textLabel.Name = "Stat" .. i + textLabel.TextColor3 = Color3.new(1,1,1) + textLabel.TextXAlignment = Enum.TextXAlignment.Right + textLabel.TextYAlignment = Enum.TextYAlignment.Center + textLabel.FontSize = Enum.FontSize.Size14 + if isHeader then + textLabel.FontSize = Enum.FontSize.Size18 + else + textLabel.FontSize = Enum.FontSize.Size14 + end + if isHeader or isTeam then + textLabel.Font = Enum.Font.ArialBold + else + textLabel.Font = Enum.Font.Arial + end + + if isTeam then + textLabel.BackgroundColor3 = color3 + textLabel.Text = 0 + else + textLabel.BackgroundColor3 = Color3.new(0,0,0) + textLabel.Text = "" + end + textLabel.BackgroundTransparency = 1 + if i == numColumns then + textLabel.Size = UDim2.new(1/numColumns, -6, 1, 0) + else + textLabel.Size = UDim2.new(1/numColumns, -4, 1, 0) + end + + textLabel.Position = UDim2.new((i-1) * (1/numColumns), 0, 0, 0) + return RbxGui.AutoTruncateTextObject(textLabel) + end + + local function createStatHeaders(stats, numColumns, isBig) + local frame = Instance.new("Frame") + frame.Name = "Headers" + frame.BorderSizePixel = 0 + frame.BackgroundColor3 = Color3.new(0,0,0) + frame.BackgroundTransparency = 1 + + local nameSize + if isBig then + nameSize = 0.5 + elseif numColumns == 1 then + nameSize = 0.7 + elseif numColumns == 2 then + nameSize = 0.6 + else + nameSize = 0.45 + end + frame.Size = UDim2.new(1-nameSize, 0, 1,0) + if isBig then + frame.Position = UDim2.new(nameSize,-25, 0,0) + else + frame.Position = UDim2.new(nameSize,0, 0,0) + end + + local i = 1 + while i <= numColumns do + local headerColumn, changeText = createStatColumn(i, numColumns, false, nil, true) + changeText(stats[i]) + headerColumn.Parent = frame + i = i + 1 + end + return frame, textChangers + end + + local function createStatColumns(nameObject, numColumns, isTeam, isBig) + local frame = Instance.new("Frame") + frame.Name = nameObject.Name .. "_WithStats" + frame.BorderSizePixel = 0 + frame.BackgroundColor3 = nameObject.BackgroundColor3 + frame.BackgroundTransparency = nameObject.BackgroundTransparency + frame.Size = nameObject.Size + frame.Position = nameObject.Position + + nameObject.BackgroundTransparency = 1 + + if numColumns == 0 then + nameObject.Size = UDim2.new(1,0,1,0) + nameObject.Position = UDim2.new(0,0,0,0) + nameObject.Parent = frame + return frame + end + + local statFrame = Instance.new("Frame") + statFrame.Name = "Stats" + if isTeam then + statFrame.BorderSizePixel = 0 + statFrame.BackgroundColor3 = nameObject.NameLabel.BackgroundColor3 + statFrame.BackgroundTransparency = nameObject.NameLabel.BackgroundTransparency + else + statFrame.BackgroundTransparency = 1 + end + + local nameSize + if isBig then + nameSize = 0.5 + elseif numColumns == 1 then + nameSize = 0.7 + elseif numColumns == 2 then + nameSize = 0.6 + else + nameSize = 0.45 + end + nameObject.Size = UDim2.new(nameSize, 0, 1, 0) + nameObject.Position = UDim2.new(0, 0, 0, 0) + statFrame.Size = UDim2.new(1-nameSize,0, 1,0) + statFrame.Position = UDim2.new(nameSize,0, 0,0) + + nameObject.Parent = frame + statFrame.Parent = frame + + local textChangers = {} + local i = 1 + while i <= numColumns do + local statColumn, changeText = createStatColumn(i, numColumns, isTeam, statFrame.BackgroundColor3) + statColumn.Parent = statFrame + table.insert(textChangers, changeText) + + i = i + 1 + end + + return frame, statFrame, textChangers + end + + local function createAlternatingRows(objects) + for i, line in ipairs(objects) do + if i % 2 == 0 then + line.BackgroundTransparency = 1 + else + line.BackgroundTransparency = 0.95 + end + end + end + local removeFromTeam = nil + + local function clearTableEntry(obj, tableInfo) + if tableInfo.MainObjectSmall then + tableInfo.MainObjectSmall.Parent = nil + tableInfo.MainObjectSmall = nil + end + if tableInfo.MainObjectBig then + tableInfo.MainObjectBig.Parent = nil + tableInfo.MainObjectBig = nil + end + if tableInfo.Connections then + for i, connection in ipairs(tableInfo.Connections) do + connection:disconnect() + end + tableInfo.Connections = nil + end + if tableInfo.LeaderStatConnections then + for i, connection in ipairs(tableInfo.LeaderStatConnections) do + connection:disconnect() + end + tableInfo.LeaderStatConnections = nil + end + if tableInfo.CurrentTeam then + removeFromTeam(obj) + tableInfo.CurrentTeam = nil + end + if tableInfo.Players then + for i, player in ipairs(tableInfo.Players) do + playerTable[player].CurrentTeam = nil + end + tableInfo.Players = {} + end + if tableInfo.StatValues then + tableInfo.StatValues = nil + end + end + + local function resetPlayerTable() + for player, info in pairs(playerTable) do + clearTableEntry(player, info) + playerTable[player] = nil + end + playerTable = {} + end + + local function resetTeamTable() + for team, info in pairs(teamTable) do + clearTableEntry(team, info) + teamTable[team] = nil + end + teamTable = {} + teamColorTable = {} + end + + local function getBoardTypeInfo() + local isTeam = (currentBoardType == "TeamScore" or currentBoardType == "TeamList") + local isScore = (currentBoardType == "TeamScore" or currentBoardType == "PlayerScore") + return isTeam, isScore + end + + + local function recomputeTeamScore(team, column) + if not team or team == "Neutral" then + return + end + + local function recomputeScoreHelper(statChangers) + if statChangers and column <= #statChangers then + local sum = 0 + for i, p in ipairs(teamTable[team].Players) do + if playerTable[p].StatValues and column <= #playerTable[p].StatValues then + sum = sum + (playerTable[p].StatValues[column].Value or 0) + end + end + statChangers[column](sum) + end + end + + recomputeScoreHelper(teamTable[team].StatChangersSmall) + recomputeScoreHelper(teamTable[team].StatChangersBig) + end + local function recomputeCompleteTeamScore(team) + local col = 1 + while col <= currentStatCount do + recomputeTeamScore(team, col) + col = col + 1 + end + end + removeFromTeam = function(player) + if playerTable[player].CurrentTeam ~= nil then + ArrayRemove(teamTable[playerTable[player].CurrentTeam].Players, player) + recomputeCompleteTeamScore(playerTable[player].CurrentTeam) + playerTable[player].CurrentTeam = nil + end + end + + local function assignToTeam(player) + local isTeam, isScore = getBoardTypeInfo() + + if isTeam then + local newTeam = nil + + if player.Neutral or teamColorTable[player.TeamColor.Name] == nil then + newTeam = "Neutral" + else + newTeam = teamColorTable[player.TeamColor.Name] + end + + if playerTable[player].CurrentTeam == newTeam then + return + end + + removeFromTeam(player) + + playerTable[player].CurrentTeam = newTeam + table.insert(teamTable[newTeam].Players, player) + + if newTeam == "Neutral" then + updatePlayerNameColor(player, nil) + else + updatePlayerNameColor(player, player.TeamColor) + end + + recomputeCompleteTeamScore(newTeam) + if sortPlayerListsFunction then + sortPlayerListsFunction() + end + end + end + + local function buildTeamObject(team, numStatColumns, suffix) + local isTeam, isScore = getBoardTypeInfo() + local teamObject = createTeamName(team.Name, team.TeamColor) + if not teamTable[team] then + teamTable[team] = {} + end + teamTable[team]["NameObject" .. suffix] = teamObject + if isScore then + local statObject + local textChangers + teamObject, statObject, textChangers = createStatColumns(teamObject, numStatColumns, true, suffix == "Big") + teamTable[team]["StatObject" .. suffix] = statObject + teamTable[team]["StatChangers" .. suffix] = textChangers + end + teamTable[team]["MainObject" .. suffix] = teamObject + if not teamTable[team].Players then + teamTable[team].Players = {} + end + return teamObject + end + + local currentContextMenuPlayer = nil + local function updatePlayerContextMenu(player) + currentContextMenuPlayer = player + local elementHeight = 20 + local function highlight(button) + button.Visible = false + button.TextColor3 = Color3.new(0,0,0) + button.BackgroundColor3 = Color3.new(0.8,0.8,0.8) + end + local function clearHighlight(button) + button.Visible = false + button.TextColor3 = Color3.new(1,1,1) + button.BackgroundColor3 = Color3.new(0,0,0) + end + if playerContextMenu == nil then + playerContextMenu = Instance.new("Frame") + playerContextMenu.Name = "PlayerListContextMenu" + playerContextMenu.BackgroundTransparency = 1 + playerContextMenu.Visible = false + + local playerContextMenuButton = Instance.new("TextButton") + playerContextMenuButton.Name = "PlayerListContextMenuButton" + playerContextMenuButton.Text = "" + playerContextMenuButton.Style = Enum.ButtonStyle.RobloxButtonDefault + playerContextMenuButton.ZIndex = 4 + playerContextMenuButton.Size = UDim2.new(1, 0, 1, -20) + playerContextMenuButton.Visible = true + playerContextMenuButton.Parent = playerContextMenu + + for i, contextElement in ipairs(contextMenuElements) do + local element = contextElement + if element.Type == "Button" then + local button = Instance.new("TextButton") + button.Name = "ContextButton" .. i + button.BackgroundColor3 = Color3.new(0,0,0) + button.BorderSizePixel = 0 + button.TextXAlignment = Enum.TextXAlignment.Left + button.Text = " " .. contextElement.Text + button.Font = Enum.Font.Arial + button.FontSize = Enum.FontSize.Size14 + button.Size = UDim2.new(1, 8, 0, elementHeight) + button.TextColor3 = Color3.new(1,1,1) + button.ZIndex = 4 + button.Parent = playerContextMenuButton + button.MouseButton1Click:connect(function() + if button.Active then + local success, result = pcall(function() element.DoIt(currentContextMenuPlayer) end) + playerContextMenu.Visible = false + end + end) + + button.MouseEnter:connect(function() + if button.Active then + highlight(button) + end + end) + button.MouseLeave:connect(function() + if button.Active then + clearHighlight(button) + end + end) + + contextElement.Button = button + contextElement.Element = button + elseif element.Type == "Label" then + local frame = Instance.new("Frame") + frame.Name = "ContextLabel" .. i + frame.BackgroundTransparency = 1 + frame.Size = UDim2.new(1, 8, 0, elementHeight) + + local label = Instance.new("TextLabel") + label.Name = "Text1" + label.BackgroundTransparency = 1 + label.BackgroundColor3 = Color3.new(1,1,1) + label.BorderSizePixel = 0 + label.TextXAlignment = Enum.TextXAlignment.Left + label.Font = Enum.Font.ArialBold + label.FontSize = Enum.FontSize.Size14 + label.Position = UDim2.new(0.0, 0, 0, 0) + label.Size = UDim2.new(0.5, 0, 1, 0) + label.TextColor3 = Color3.new(1,1,1) + label.ZIndex = 4 + label.Parent = frame + element.Label1 = label + + if element.GetText2 then + label = Instance.new("TextLabel") + label.Name = "Text2" + label.BackgroundTransparency = 1 + label.BackgroundColor3 = Color3.new(1,1,1) + label.BorderSizePixel = 0 + label.TextXAlignment = Enum.TextXAlignment.Right + label.Font = Enum.Font.Arial + label.FontSize = Enum.FontSize.Size14 + label.Position = UDim2.new(0.5, 0, 0, 0) + label.Size = UDim2.new(0.5, 0, 1, 0) + label.TextColor3 = Color3.new(1,1,1) + label.ZIndex = 4 + label.Parent = frame + element.Label2 = label + end + frame.Parent = playerContextMenuButton + element.Label = frame + element.Element = frame + end + end + + playerContextMenu.ZIndex = 4 + playerContextMenu.MouseLeave:connect(function() playerContextMenu.Visible = false end) + robloxLock(playerContextMenu) + playerContextMenu.Parent = PlayerlistScreenGui + + end + + local elementPos = 0 + for i, contextElement in ipairs(contextMenuElements) do + local isVisible = false + + if contextElement.IsVisible then + local success, visible = pcall(function() return contextElement.IsVisible(currentContextMenuPlayer) end) + if success then + isVisible = visible + else + print("Error in IsVisible call: " .. visible) + end + end + + if contextElement.Type == "Button" then + contextElement.Button.Visible = isVisible + if contextElement.Button.Visible then + isVisible = true + clearHighlight(contextElement.Button) + if contextElement.IsActive then + local success, active = pcall(function() return contextElement.IsActive(currentContextMenuPlayer) end) + if success then + contextElement.Button.Active = active + else + print("Error in IsActive call: " .. active) + end + end + if contextElement.Button.Active then + contextElement.Button.TextColor3 = Color3.new(1,1,1) + else + contextElement.Button.TextColor3 = Color3.new(0.7,0.7,0.7) + end + end + elseif contextElement.Type == "Label" then + contextElement.Label.Visible = isVisible + if contextElement.Label.Visible then + local success, text = pcall(function() return contextElement.GetText1(currentContextMenuPlayer) end) + if success then + contextElement.Label1.Text = " " .. text + else + print("Error in GetText1 call: " .. text) + end + + if contextElement.GetText2 then + local success, text = pcall(function() return contextElement.GetText2(currentContextMenuPlayer) end) + if success then + contextElement.Label2.Text = " " .. text + else + print("Error in GetText2 call: " .. text) + end + end + end + end + if isVisible then + contextElement.Element.Position = UDim2.new(0,-4, 0, elementPos * elementHeight - 4) + elementPos = elementPos + 1 + end + end + playerContextMenu.Size = UDim2.new(0, 150, 0, elementPos*elementHeight + 13 + 20) + end + local function showPlayerMenu(player, x, y) + updatePlayerContextMenu(player) + x = x - (playerContextMenu.AbsoluteSize.X/2) + if x + playerContextMenu.AbsoluteSize.X >= PlayerlistScreenGui.AbsoluteSize.X then + x = PlayerlistScreenGui.AbsoluteSize.X - playerContextMenu.AbsoluteSize.X + end + playerContextMenu.Visible = true + playerContextMenu.Position = UDim2.new(0, x, 0, y-playerContextMenu.AbsoluteSize.Y) + end + + local function buildPlayerObject(player, numStatColumns, suffix) + local isTeam, isScore = getBoardTypeInfo() + + local playerObject = nil + local changePlayerNameFunction = nil + local currentColor = nil + if isTeam and not player.Neutral then + currentColor = player.TeamColor.Color + else + currentColor = Color3.new(1,1,1) + end + playerObject, changePlayerNameFunction = createPlayerName(player.Name, player.MembershipType, currentColor, getFriendStatus(player)) + + if not playerTable[player] then + playerTable[player] = {} + end + if not playerTable[player].Connections then + playerTable[player].Connections = {} + end + if not playerTable[player].CurrentTeam then + playerTable[player].CurrentTeam = nil + end + playerTable[player]["NameObject" .. suffix] = playerObject + playerTable[player]["ChangeName" .. suffix] = changePlayerNameFunction + + if isScore then + local statObject = nil + local textChangers = nil + playerObject, statObject, textChangers = createStatColumns(playerObject, numStatColumns, false, suffix == "Big") + playerTable[player]["StatObject" .. suffix]= statObject + playerTable[player]["StatChangers" .. suffix] = textChangers + + local statValues, leaderstats = getStatValuesForPlayer(player) + if not statValues or #statValues < numStatColumns then + if not playerTable[player].LeaderStatConnections then + playerTable[player].LeaderStatConnections = {} + end + if not leaderstats then + table.insert(playerTable[player].LeaderStatConnections, + player.ChildAdded:connect( + function(child) + if child.Name == "leaderstats" then + recreatePlayerFunction(player) + else + table.insert(playerTable[player].LeaderStatConnections, + child.Changed:connect( + function(prop) + if prop == "Name" and child.Name == "leaderstats" then + recreatePlayerFunction(player) + end + end)) + end + end)) + else + table.insert(playerTable[player].LeaderStatConnections, + leaderstats.ChildAdded:connect( + function(child) + recreatePlayerFunction(player) + end) + ) + table.insert(playerTable[player].LeaderStatConnections, + leaderstats.AncestryChanged:connect( + function(child) + recreatePlayerFunction(player) + end) + ) + end + end + if statValues then + + if not playerTable[player].StatValues then + playerTable[player].StatValues = {} + end + local pos = 1 + while pos <= numStatColumns and pos <= #statValues do + local currentColumn = pos + local statValue = statValues[pos] + print(playerTable[player].StatValues) + local statChanger = textChangers[pos] + + local updateStat = function(val) + statChanger(tonumber(val) or 0) + recomputeTeamScore(playerTable[player].CurrentTeam, currentColumn) + end + if pos > #playerTable[player].StatValues then + table.insert(playerTable[player].StatValues, statValue) + end + + table.insert(playerTable[player].Connections, + statValue.Changed:connect(updateStat) + ) + table.insert(playerTable[player].Connections, + statValue.AncestryChanged:connect( + function() + recreatePlayerFunction(player) + end) + ) + updateStat(statValue.Value) + pos = pos + 1 + end + + end + end + + if supportFriends and player ~= game.Players.LocalPlayer and player.userId > 0 and game.Players.LocalPlayer.userId > 0 then + local button = Instance.new("TextButton") + button.Name = playerObject.Name .. "Button" + button.Text = "" + button.Active = false + button.Size = playerObject.Size + button.Position = playerObject.Position + button.BackgroundColor3 = playerObject.BackgroundColor3 + + local secondButton = Instance.new("TextButton") + secondButton.Name = playerObject.Name .. "RealButton" + secondButton.Text = "" + secondButton.BackgroundTransparency = 1 + secondButton.BackgroundColor3 = playerObject.BackgroundColor3 + local theNameLabel = playerObject:findFirstChild("NameLabel",true) + if theNameLabel then + + theNameLabel.TextColor3 = Color3.new(1,1,1) + secondButton.Parent = theNameLabel.FullNameLabel + end + secondButton.Parent.BackgroundTransparency = 1 + secondButton.Parent.Visible = true + secondButton.ZIndex = 2 + secondButton.Size = UDim2.new(1,0,1,0) + + local previousTransparency = nil + table.insert(playerTable[player].Connections, + secondButton.MouseEnter:connect( + function() + if previousTransparency == nil then + previousTransparency = secondButton.BackgroundTransparency + end + + if lightBackground then + secondButton.Parent.BackgroundTransparency = 0 + else + secondButton.Parent.BackgroundTransparency = 1 + end + end)) + table.insert(playerTable[player].Connections, + secondButton.MouseLeave:connect( + function() + if previousTransparency ~= nil then + if lightBackground then + else + end + + previousTransparency = nil + end + secondButton.Parent.BackgroundTransparency = 1 + end)) + + local mouseDownX, mouseDownY + table.insert(playerTable[player].Connections, + secondButton.MouseButton1Down:connect(function(x,y) + mouseDownX = x + mouseDownY = y + end)) + table.insert(playerTable[player].Connections, + secondButton.MouseButton1Click:connect(function() + showPlayerMenu(player, mouseDownX, secondButton.AbsolutePosition.Y + secondButton.AbsoluteSize.Y ) + end)) + playerObject.BackgroundTransparency = 1 + playerObject.Size = UDim2.new(1,0,1,0) + playerObject.Position = UDim2.new(0,0,0,0) + playerObject.Parent = button + + playerTable[player]["MainObject" .. suffix] = button + + playerObject = button + else + playerTable[player]["MainObject" .. suffix] = playerObject + + end + table.insert(playerTable[player].Connections, + player.Changed:connect( + function(prop) + if prop == "MembershipType" then + updatePlayerName(playerTable[player]["NameObject" .. suffix], player.MembershipType, currentColor) + elseif prop == "Name" then + playerTable[player]["ChangeName" .. suffix](player.Name) + elseif prop == "Neutral" or prop == "TeamColor" then + assignToTeam(player) + end + end) + ) + return playerObject + end + + local function orderScrollList(scrollOrder, objectName, scrollFrame) + local pos = 0 + local order = {} + local isTeam, isScore = getBoardTypeInfo() + for i, obj in ipairs(scrollOrder) do + order[obj] = 0 + end + + if isTeam then + local teams = getTeams() + for i, team in ipairs(teams) do + order[teamTable[team][objectName]] = pos + pos = pos + 1 + for i, player in ipairs(teamTable[team].Players) do + if playerTable[player] then + order[playerTable[player][objectName]] = pos + pos = pos + 1 + end + end + end + + if #teamTable["Neutral"].Players > 0 then + teamTable["Neutral"][objectName].Parent = scrollFrame + order[teamTable["Neutral"][objectName]] = pos + pos = pos + 1 + for i, player in ipairs(teamTable["Neutral"].Players) do + order[playerTable[player][objectName]] = pos + pos = pos + 1 + end + else + teamTable["Neutral"][objectName].Parent = nil + end + else + local players = getPlayers() + for i, player in ipairs(players) do + order[playerTable[player][objectName]] = pos + pos = pos + 1 + end + end + + table.sort(scrollOrder, + function(a,b) + return order[a] < order[b] + end) + end + + + local function createPlayerListBasics(frame, isBig) + local headerFrame = Instance.new("Frame") + headerFrame.Name = "Header" + headerFrame.BackgroundTransparency = 1 + headerFrame.Size = UDim2.new(1,-13,0,26) + headerFrame.Position = UDim2.new(0,0,0,0) + headerFrame.Parent = frame + + local lowerPaneFrame = Instance.new("Frame") + lowerPaneFrame.Name = "ScrollingArea" + lowerPaneFrame.BackgroundTransparency = 1 + lowerPaneFrame.Size = UDim2.new(1,-3,1,-26) + lowerPaneFrame.Position = UDim2.new(0,0,0,26) + lowerPaneFrame.Parent = frame + + local scrollOrder = {} + local scrollFrame, scrollUp, scrollDown, recalculateScroll = RbxGui.CreateScrollingFrame(scrollOrder) + + + local scrollBar = Instance.new("Frame") + scrollBar.Name = "ScrollBar" + scrollBar.BackgroundTransparency = 0.9 + scrollBar.BackgroundColor3 = Color3.new(1,1,1) + scrollBar.BorderSizePixel = 0 + scrollBar.Size = UDim2.new(0, 17, 1, -36) + if isBig then scrollBar.Size = UDim2.new(0, 17, 1, -61) end + scrollBar.Parent = lowerPaneFrame + + scrollFrame.Parent = lowerPaneFrame + scrollUp.Parent = lowerPaneFrame + scrollDown.Parent = lowerPaneFrame + + if isBig then + scrollFrame.Position = UDim2.new(0,0,0,0) + scrollUp.Position = UDim2.new(1,-41,0,5) + scrollDown.Position = UDim2.new(1,-41,1,-35) + scrollBar.Position = UDim2.new(1, -41, 0, 24) + + scrollFrame.Size = UDim2.new(1,-48,1,0) + headerFrame.Size = UDim2.new(1,-20,0,32) + + else + scrollBar.Position = UDim2.new(1, -19, 0, 14) + scrollFrame.Position = UDim2.new(0,1,0,0) + scrollUp.Position = UDim2.new(1,-19,0,-5) + scrollDown.Position = UDim2.new(1,-19,1,-20) + + lowerPaneFrame.Position = UDim2.new(0,0,0,30) + + local toggleScrollBar = function(visible) + if visible then + scrollFrame.Size = UDim2.new(1,-16,1,0) + headerFrame.Size = UDim2.new(1,-16,0,32) + else + scrollFrame.Size = UDim2.new(1,0,1,0) + headerFrame.Size = UDim2.new(1,5,0,32) + end + scrollUp.Visible = visible + scrollDown.Visible = visible + scrollBar.Visible = visible + end + scrollUp.Changed:connect(function(prop) + if prop == "Active" then + toggleScrollBar(scrollUp.Active or scrollDown.Active) + end + end) + + scrollDown.Changed:connect(function(prop) + if prop == "Active" then + toggleScrollBar(scrollUp.Active or scrollDown.Active) + end + end) + + toggleScrollBar(scrollUp.Active or scrollDown.Active) + end + return headerFrame, scrollFrame, recalculateScroll, scrollOrder + end + + + + createBoardsFunction = function (boardType, numStatColumns) + print("Create Boards") + local smallFrame = Instance.new("Frame") + smallFrame.Name = "SmallPlayerlist" + smallFrame.Position = smallWindowPosition + smallFrame.Active = false + smallFrame.Size = smallWindowSize + smallFrame.BackgroundColor3 = Color3.new(0,0,0) + smallFrame.BackgroundTransparency = 0.7 + smallFrame.BorderSizePixel = 0 + + local bigFrame = Instance.new("Frame") + bigFrame.Name = "BigPlayerlist" + bigFrame.Size = bigWindowSize + bigFrame.Position = bigWindowPosition + bigFrame.BackgroundColor3 = Color3.new(0,0,0) + bigFrame.BackgroundTransparency = 0.7 + bigFrame.BorderSizePixel = 0 + bigFrame.Visible = false + + local bigFrameWrapper = Instance.new("Frame") + bigFrameWrapper.Name = "Expander" + bigFrameWrapper.Size = UDim2.new(1,21,1,16) + bigFrameWrapper.Position = UDim2.new(0, 0, 0,0) + bigFrameWrapper.BackgroundTransparency = 1 + bigFrameWrapper.Parent = bigFrame + + local smallHeaderFrame, scrollFrameSmall, recalculateScrollSmall, scrollOrderSmall = createPlayerListBasics(smallFrame, false) + local bigHeaderFrame, scrollFrameBig, recalculateScrollBig, scrollOrderBig = createPlayerListBasics(bigFrameWrapper, true) + + + local function fixPlayerlistSize(size) + + local newSize = size + if newSize >= 16 then + newSize = 16 + end + + smallFrame.Size = UDim2.new(1,1,0,31 + (newSize * 20)) + + end + scrollFrameSmall.ChildAdded:connect(function() + fixPlayerlistSize(#scrollFrameSmall:GetChildren()) + end) + scrollFrameSmall.ChildRemoved:connect(function() + fixPlayerlistSize(#scrollFrameSmall:GetChildren()) + end) + + local playerListButton = Instance.new("ImageButton") + playerListButton.Name = "GoBigButton" + playerListButton.BackgroundTransparency = 1 + playerListButton.Image = "rbxasset://textures/ui/playerlist_small_maximize.png" + playerListButton.Size = UDim2.new(0.0, 35, 0, 29) + playerListButton.Position = UDim2.new(0, 0, 0, 0) + playerListButton.MouseButton1Click:connect( + function() + toggleBigWindow() + end) + playerListButton.Parent = smallHeaderFrame + + playerListButton = Instance.new("ImageButton") + playerListButton.Name = "CloseButton" + playerListButton.BackgroundTransparency = 1 + playerListButton.Image = "rbxasset://textures/ui/playerlist_small_hide.png" + playerListButton.Size = UDim2.new(0.0, 38, 0, 29) + playerListButton.Position = UDim2.new(0, 35, 0, 0) + playerListButton.MouseButton1Click:connect( + function() + transitionWindowsFunction("None") + end) + playerListButton.Parent = smallHeaderFrame + + playerListButton = Instance.new("ImageButton") + playerListButton.Name = "CloseButton" + playerListButton.Image = "rbxasset://textures/ui/playerlist_big_hide.png" + playerListButton.BackgroundTransparency = 1 + playerListButton.Size = UDim2.new(0.0, 29, 0, 29) + playerListButton.Position = UDim2.new(1, -30, 0.5, -15) + playerListButton.MouseButton1Click:connect( + function() + toggleBigWindow() + end) + playerListButton.Parent = bigHeaderFrame + + local placeName = Instance.new("TextLabel") + placeName.Name = "PlaceName" + placeName.Text = " Player List" + placeName.FontSize = Enum.FontSize.Size24 + placeName.TextXAlignment = Enum.TextXAlignment.Left + placeName.Font = Enum.Font.ArialBold + placeName.BackgroundTransparency = 1 + placeName.TextColor3 = Color3.new(1,1,1) + placeName.Size = UDim2.new(0.5, 0, 1, 0) + placeName.Position = UDim2.new(0, 0, 0.0, 0) + placeName = RbxGui.AutoTruncateTextObject(placeName) + placeName.Parent = bigHeaderFrame + + + currentBoardType = boardType + currentStatCount = numStatColumns + local isTeam, isScore = getBoardTypeInfo() + local players = getPlayers() + + if isScore then + local statColumns = getStatColumns(players) + numStatColumns = #statColumns + if numStatColumns > 3 then + numStatColumns = 3 + end + createStatHeaders(statColumns, numStatColumns, false).Parent = smallHeaderFrame + createStatHeaders(statColumns, currentStatCount, true).Parent = bigHeaderFrame + end + resetPlayerTable() + + + + for i, player in ipairs(players) do + local playerObject = buildPlayerObject(player, numStatColumns, "Small") + table.insert(scrollOrderSmall, playerObject) + playerObject.Parent = scrollFrameSmall + + playerObject = buildPlayerObject(player, currentStatCount, "Big") + table.insert(scrollOrderBig, playerObject) + playerObject.Parent = scrollFrameBig + end + resetTeamTable() + + local teamStatObjects = {} + if isTeam then + local teams = getTeams() + local i = #teams + while i >= 1 do + local team = teams[i] + teamColorTable[team.TeamColor.Name] = team + i = i - 1 + end + for i, team in ipairs(teams) do + local teamObject = buildTeamObject(team, numStatColumns, "Small") + table.insert(scrollOrderSmall, teamObject) + teamObject.Parent = scrollFrameSmall + + + teamObject = buildTeamObject(team, currentStatCount, "Big") + table.insert(scrollOrderBig, teamObject) + teamObject.Parent = scrollFrameBig + end + + + + teamTable["Neutral"] = {} + teamTable["Neutral"].Players = {} + + local neutralTeamObject = createTeamName("Neutral", BrickColor.palette(8)) + teamTable["Neutral"].NameObjectSmall = neutralTeamObject + teamTable["Neutral"].StatObjectSmall = nil + teamTable["Neutral"].MainObjectSmall = neutralTeamObject + table.insert(scrollOrderSmall, neutralTeamObject) + + neutralTeamObject = createTeamName("Neutral", BrickColor.palette(8)) + teamTable["Neutral"].NameObjectBig = neutralTeamObject + teamTable["Neutral"].StatObjectBig = nil + teamTable["Neutral"].MainObjectBig = neutralTeamObject + table.insert(scrollOrderBig, neutralTeamObject) + + local neutralPlayers = {} + for i, player in ipairs(players) do + assignToTeam(player) + end + end + + + + removePlayerFunction = function(player) + if playerTable[player] then + ArrayRemove(scrollOrderSmall, playerTable[player].MainObjectSmall) + ArrayRemove(scrollOrderBig, playerTable[player].MainObjectBig) + + clearTableEntry(player, playerTable[player]) + + playerTable[player] = nil + end + end + recreatePlayerFunction = function(player) + removePlayerFunction(player) + + local playerObject = buildPlayerObject(player, numStatColumns, "Small") + table.insert(scrollOrderSmall, playerObject) + robloxLock(playerObject) + playerObject.Parent = scrollFrameSmall + + playerObject = buildPlayerObject(player, currentStatCount, "Big") + table.insert(scrollOrderBig, playerObject) + robloxLock(playerObject) + playerObject.Parent = scrollFrameBig + + local isTeam, isScore = getBoardTypeInfo() + if isTeam then + assignToTeam(player) + end + + sortPlayerListsFunction() + end + + sortPlayerListsFunction = function() + orderScrollList(scrollOrderSmall, "MainObjectSmall", scrollFrameSmall) + recalculateScrollSmall() + createAlternatingRows(scrollOrderSmall) + + orderScrollList(scrollOrderBig, "MainObjectBig", scrollFrameBig) + recalculateScrollBig() + createAlternatingRows(scrollOrderBig) + end + + sortPlayerListsFunction() + + robloxLock(smallFrame) + robloxLock(bigFrame) + return smallFrame, bigFrame + end + local function teamsChanged() + if debounceTeamsChanged then + return + end + + debounceTeamsChanged = true + wait() + rebuildBoard(PlayerlistScreenGui, determineBoardType()) + debounceTeamsChanged = false + end + + + local checkIfBoardChanged = function() + local newBoardType, numStats = determineBoardType() + if newBoardType ~= currentBoardType or numStats ~= currentStatCount then + rebuildBoard(PlayerlistScreenGui, newBoardType, numStats) + end + end + + local function buildPlayerList() + waitForChild(game, "Players") + waitForProperty(game.Players, "LocalPlayer") + + playerListEnabled = true + if not playerListEnabled then + return + end + + supportFriends = false + + local teams = game:GetService("Teams") + if teams then + local teamConnections = {} + + teams.ChildAdded:connect( + function(child) + if child:IsA("Team") then + teamsChanged() + teamConnections[child] = child.Changed:connect( + function(prop) + if prop == "TeamColor" or prop == "Name" then + teamsChanged() + end + end) + end + end) + teams.ChildRemoved:connect( + function(child) + if child:IsA("Team") then + if teamConnections[child] then + teamConnections[child]:disconnect() + teamConnections[child] = nil + end + teamsChanged() + end + end) + end + + game.Players.ChildAdded:connect( + function(player) + if player:IsA("Player") then + addPlayerFunction(player) + end + end) + + game.Players.ChildRemoved:connect( + function(player) + if player:IsA("Player") then + if removePlayerFunction then + removePlayerFunction(player) + end + end + end) + + rebuildBoard(PlayerlistScreenGui, determineBoardType()) + + + + delay(0, + function() + while true do + wait(5) + checkIfBoardChanged() + end + end) + end + buildPlayerList() + transitionWindowsFunction("Small") + end \ No newline at end of file diff --git a/api/Game/client.ashx b/api/Game/client.ashx new file mode 100644 index 0000000..e69de29 diff --git a/api/Game/test.php b/api/Game/test.php new file mode 100644 index 0000000..ba811cd --- /dev/null +++ b/api/Game/test.php @@ -0,0 +1,437 @@ + + +-- functions -------------------------- +function onPlayerAdded(player) + -- override +end + + + +-- MultiplayerSharedScript.lua inserted here ------ Prepended to Join.lua -- + +-- log app init time +pcall(function() + local t = ElapsedTime() + local platform = settings().Diagnostics.OsPlatform + game:HttpGet("http://www.gtoria.net/Game/JoinRate.ashx?st=0&i=0&p=-1&c=GameAppInit&r=Success&d=" .. (math.floor(t*1000)) .. "&ip=192.168.0.3&errorType=&platform=" .. platform, false) +end) + +pcall(function() game:SetPlaceID(1, false) end) + +local startTime = tick() +local connectResolved = false +local loadResolved = false +local joinResolved = false +local playResolved = true +local playStartTime = 0 + +local cdnSuccess = 0 +local cdnFailure = 0 + +-- if we are on a touch device, no blocking http calls allowed! This can cause a crash on iOS +-- In general we need a long term strategy to remove blocking http calls from all platforms +local isTouchDevice = Game:GetService("UserInputService").TouchEnabled + +settings()["Game Options"].CollisionSoundEnabled = true +pcall(function() settings().Rendering.EnableFRM = true end) +pcall(function() settings().Physics.Is30FpsThrottleEnabled = true end) +pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end) +pcall(function() settings().Physics.PhysicsEnvironmentalThrottle = Enum.EnviromentalPhysicsThrottle.DefaultAuto end) + +function reportContentProvider(time, queueLength, blocking) + pcall(function() + game:HttpGet("http://www.gtoria.net/Analytics/ContentProvider.ashx?t=" .. time .. "&ql=" .. queueLength, blocking and not isTouchDevice) + end) +end +function reportCdn(blocking) + pcall(function() + local newCdnSuccess = settings().Diagnostics.CdnSuccessCount + local newCdnFailure = settings().Diagnostics.CdnFailureCount + local successDelta = newCdnSuccess - cdnSuccess + local failureDelta = newCdnFailure - cdnFailure + cdnSuccess = newCdnSuccess + cdnFailure = newCdnFailure + if successDelta > 0 or failureDelta > 0 then + game:HttpGet("http://www.gtoria.net/Game/Cdn.ashx?source=client&success=" .. successDelta .. "&failure=" .. failureDelta, blocking and not isTouchDevice) + end + end) +end + +function reportDuration(category, result, duration, blocking,errorType) + if not errorType then + errorType = '' + end + local platform = settings().Diagnostics.OsPlatform + local bytesReceived = -1 + if stats().Network:getChildren()[2] ~= nil then + bytesReceived = stats().Network:getChildren()[2].Stats.totalBytesReceived:GetValue() + end + pcall(function() game:HttpGet("http://www.gtoria.net/Game/JoinRate.ashx?st=0&i=0&p=-1&c=" .. category .. "&r=" .. result .. "&d=" .. (math.floor(duration*1000)) .. "&b=" .. bytesReceived .. "&ip=192.168.0.3&errorType=" .. errorType .. "&platform=" .. platform, blocking and not isTouchDevice) end) +end +-- arguments --------------------------------------- +local threadSleepTime = ... + +if threadSleepTime==nil then + threadSleepTime = 15 +end + +local test = true + +print("! Joining game '' place -1 at 192.168.0.3") +local closeConnection = game.Close:connect(function() + if 0 then + reportCdn(true) + if not connectResolved then + local duration = tick() - startTime; + reportDuration("GameConnect", "Failure", duration, true) + elseif (not loadResolved) or (not joinResolved) then + local duration = tick() - startTime; + if not loadResolved then + loadResolved = true + reportDuration("GameLoad","Cancel", duration, true) + end + if not joinResolved then + joinResolved = true + reportDuration("GameJoin","Cancel", duration, true) + end + elseif not playResolved then + local duration = tick() - playStartTime; + playResolved = true + reportDuration("GameDuration","Success", duration, true) + end + if true then pcall(function() game:HttpPost("https://api.gtoria.net/auth/invalidate", "invalidate") end) end + end +end) + +game:GetService("ChangeHistoryService"):SetEnabled(false) +game:GetService("ContentProvider"):SetThreadPool(16) +game:GetService("InsertService"):SetBaseSetsUrl("http://www.gtoria.net/Game/Tools/InsertAsset.ashx?nsets=10&type=base") +game:GetService("InsertService"):SetUserSetsUrl("http://www.gtoria.net/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d") +game:GetService("InsertService"):SetCollectionUrl("http://www.gtoria.net/Game/Tools/InsertAsset.ashx?sid=%d") +game:GetService("InsertService"):SetAssetUrl("http://www.gtoria.net/Asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl("http://www.gtoria.net/Asset/?assetversionid=%d") + +pcall(function() game:GetService("SocialService"):SetFriendUrl("http://www.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=IsFriendsWith&playerid=%d&userid=%d") end) +pcall(function() game:GetService("SocialService"):SetBestFriendUrl("http://www.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=IsBestFriendsWith&playerid=%d&userid=%d") end) +pcall(function() game:GetService("SocialService"):SetGroupUrl("http://www.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=IsInGroup&playerid=%d&groupid=%d") end) +pcall(function() game:GetService("SocialService"):SetGroupRankUrl("http://www.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRank&playerid=%d&groupid=%d") end) +pcall(function() game:GetService("SocialService"):SetGroupRoleUrl("http://www.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRole&playerid=%d&groupid=%d") end) +pcall(function() game:GetService("GamePassService"):SetPlayerHasPassUrl("http://www.gtoria.net/Game/GamePass/GamePassHandler.ashx?Action=HasPass&UserID=%d&PassID=%d") end) +pcall(function() game:GetService("MarketplaceService"):SetProductInfoUrl("https://api.gtoria.net/marketplace/productinfo?assetId=%d") end) +pcall(function() game:GetService("MarketplaceService"):SetPlayerOwnsAssetUrl("https://api.gtoria.net/ownership/hasasset?userId=%d&assetId=%d") end) +pcall(function() game:SetCreatorID(0, Enum.CreatorType.User) end) + +-- Bubble chat. This is all-encapsulated to allow us to turn it off with a config setting +pcall(function() game:GetService("Players"):SetChatStyle(Enum.ChatStyle.Classic) end) + +local waitingForCharacter = false +local waitingForCharacterGuid = "86cadbcc-3b8b-493e-8d7b-4b103ae914e9"; +pcall( function() + if settings().Network.MtuOverride == 0 then + settings().Network.MtuOverride = 1400 + end +end) + + +-- globals ----------------------------------------- + +client = game:GetService("NetworkClient") +visit = game:GetService("Visit") + +-- functions --------------------------------------- +function ifSeleniumThenSetCookie(key, value) + if false then + game:GetService("CookiesService"):SetCookieValue(key, value) + end +end + +function setMessage(message) + -- todo: animated "..." + if not false then + game:SetMessage(message) + else + -- hack, good enought for now + game:SetMessage("Teleporting ...") + end +end + +function showErrorWindow(message, errorType, errorCategory) + if 0 then + if (not loadResolved) or (not joinResolved) then + local duration = tick() - startTime; + if not loadResolved then + loadResolved = true + reportDuration("GameLoad","Failure", duration, false,errorType) + end + if not joinResolved then + joinResolved = true + reportDuration("GameJoin",errorCategory, duration, false,errorType) + end + + pcall(function() game:HttpGet("?FilterName=Type&FilterValue=" .. errorType .. "&Type=JoinFailure", false) end) + elseif not playResolved then + local duration = tick() - playStartTime; + playResolved = true + reportDuration("GameDuration",errorCategory, duration, false,errorType) + + pcall(function() game:HttpGet("?FilterName=Type&FilterValue=" .. errorType .. "&Type=GameDisconnect", false) end) + end + end + + game:SetMessage(message) +end + +function registerPlay(key) + if true and game:GetService("CookiesService"):GetCookieValue(key) == "" then + game:GetService("CookiesService"):SetCookieValue(key, "{ \"userId\" : 0, \"placeId\" : -1, \"os\" : \"" .. settings().Diagnostics.OsPlatform .. "\" }") + end +end + +function analytics(name) + if not test and false then + pcall(function() game:HttpGet("?IPFilter=Primary&SecondaryFilterName=UserId&SecondaryFilterValue=0&Type=" .. name, false) end) + end +end + +function analyticsGuid(name, guid) + if not test and false then + pcall(function() game:HttpGet("?IPFilter=Primary&SecondaryFilterName=guid&SecondaryFilterValue=" .. guid .. "&Type=" .. name, false) end) + end +end + +function reportError(err, message) + print("***ERROR*** " .. err) + if not test then visit:SetUploadUrl("") end + client:Disconnect() + wait(4) + showErrorWindow("Error: " .. err, message, "Other") +end + +-- called when the client connection closes +function onDisconnection(peer, lostConnection) + if lostConnection then + if waitingForCharacter then analyticsGuid("Waiting for Character Lost Connection",waitingForCharacterGuid) end + showErrorWindow("You have lost the connection to the game", "LostConnection", "LostConnection") + else + if waitingForCharacter then analyticsGuid("Waiting for Character Game Shutdown",waitingForCharacterGuid) end + showErrorWindow("This game has shut down", "Kick", "Kick") + end + pcall(function() game:HttpGet("&disconnect=true", true) end) + if true then pcall(function() game:HttpPost("https://api.gtoria.net/auth/invalidate", "invalidate") end) end +end + +function requestCharacter(replicator) + + -- prepare code for when the Character appears + local connection + connection = player.Changed:connect(function (property) + if property=="Character" then + game:ClearMessage() + waitingForCharacter = false + analyticsGuid("Waiting for Character Success", waitingForCharacterGuid) + + connection:disconnect() + + if 0 then + if not joinResolved then + local duration = tick() - startTime; + joinResolved = true + reportDuration("GameJoin","Success", duration, false) + + playStartTime = tick() + playResolved = false + end + end + end + end) + + setMessage("Requesting character") + + if 0 and not loadResolved then + local duration = tick() - startTime; + loadResolved = true + reportDuration("GameLoad","Success", duration, false) + end + + local success, err = pcall(function() + replicator:RequestCharacter() + setMessage("Waiting for character") + waitingForCharacter = true + analyticsGuid("Waiting for Character Begin",waitingForCharacterGuid); + end) + if not success then + reportError(err,"W4C") + return + end +end + +-- called when the client connection is established +function onConnectionAccepted(url, replicator) + connectResolved = true + reportDuration("GameConnect", "Success", tick() - startTime, false) + + local waitingForMarker = true + + local success, err = pcall(function() + if not test then + visit:SetPing("", 300) + end + + if not false then + game:SetMessageBrickCount() + else + setMessage("Teleporting ...") + end + + replicator.Disconnection:connect(onDisconnection) + + -- Wait for a marker to return before creating the Player + local marker = replicator:SendMarker() + + marker.Received:connect(function() + waitingForMarker = false + requestCharacter(replicator) + end) + end) + + if not success then + reportError(err,"ConnectionAccepted") + return + end + + -- TODO: report marker progress + + while waitingForMarker do + workspace:ZoomToExtents() + wait(0.5) + end +end + +-- called when the client connection fails +function onConnectionFailed(_, error) + showErrorWindow("Failed to connect to the Game. (ID=" .. error .. ")", "ID" .. error, "Other") +end + +-- called when the client connection is rejected +function onConnectionRejected() + connectionFailed:disconnect() + showErrorWindow("This game is not available. Please try another", "WrongVersion", "WrongVersion") +end + +idled = false +function onPlayerIdled(time) + if time > 20*60 then + showErrorWindow(string.format("You were disconnected for being idle %d minutes", time/60), "Idle", "Idle") + client:Disconnect() + if not idled then + idled = true + end + end +end + + +-- main ------------------------------------------------------------ + +analytics("Start Join Script") + +ifSeleniumThenSetCookie("SeleniumTest1", "Started join script") + +pcall(function() settings().Diagnostics:LegacyScriptMode() end) +local success, err = pcall(function() + + game:SetRemoteBuildMode(true) + + setMessage("Connecting to Server") + client.ConnectionAccepted:connect(onConnectionAccepted) + client.ConnectionRejected:connect(onConnectionRejected) + connectionFailed = client.ConnectionFailed:connect(onConnectionFailed) + client.Ticket = "" + ifSeleniumThenSetCookie("SeleniumTest2", "Successfully connected to server") + + playerConnectSucces, player = pcall(function() return client:PlayerConnect(1, "192.168.0.3", 53640, 0, threadSleepTime) end) + if not playerConnectSucces then + --Old player connection scheme + player = game:GetService("Players"):CreateLocalPlayer(1) + analytics("Created Player") + client:Connect("192.168.0.3", 53640, 0, threadSleepTime) + else + analytics("Created Player") + end + + pcall(function() + registerPlay("rbx_evt_ftp") + delay(60*5, function() registerPlay("rbx_evt_fmp") end) + end) + + -- negotiate an auth token + if true then + pcall(function() game:HttpPost("https://api.gtoria.net/auth/negotiate?ticket=", "negotiate") end) + delay(300, function() + while true do + pcall(function() game:HttpPost("https://api.gtoria.net/auth/renew", "renew") end) + wait(300) + end + end) + end + + player:SetSuperSafeChat(true) + pcall(function() player:SetUnder13(true) end) + pcall(function() player:SetMembershipType(Enum.MembershipType.None) end) + pcall(function() player:SetAccountAge(0) end) + player.Idled:connect(onPlayerIdled) + + -- Overriden + onPlayerAdded(player) + + pcall(function() player.Name = [========[Player]========] end) + player.CharacterAppearance = "" + if not test then visit:SetUploadUrl("")end + + analytics("Connect Client") + +end) + +if not success then + reportError(err,"CreatePlayer") +end + +ifSeleniumThenSetCookie("SeleniumTest3", "Successfully created player") + +if not test then + -- TODO: Async get? + loadfile("")("", -1, 0) +end + +if 0 then + delay(60*5, function() + while true do + reportCdn(false) + wait(60*5) + end + end) + local cpTime = 30 + delay(cpTime, function() + while cpTime <= 480 do + reportContentProvider(cpTime, game:GetService("ContentProvider").RequestQueueSize, false) + wait(cpTime) + cpTime = cpTime * 2 + end + end) +end + +pcall(function() game:SetScreenshotInfo("") end) +pcall(function() game:SetVideoInfo('GamesROBLOX, video, free game, online virtual world') end) +-- use single quotes here because the video info string may have unescaped double quotes + +analytics("Join Finished") + +ifSeleniumThenSetCookie("SeleniumTest4", "Finished join") + \ No newline at end of file diff --git a/api/Game/visit.php b/api/Game/visit.php new file mode 100644 index 0000000..d33b0b4 --- /dev/null +++ b/api/Game/visit.php @@ -0,0 +1 @@ +%LruJmR3gQMo2oR+P7iDUdXBKjEVAKmCzZ8Hdk1ew8/fZGKyz9JMfk6DwuJgUdn10FJfpqXm3k0OEKv+ndf18QIVNZnR39guPB1B+2x0g0SR7sRJgZbtn4lDXJNk/rDDPgQmDZcLMpW24pJpsQIbDzmN63U6ihYlPWuDlU3r5Mzs=%game:GetService("RunService"):Run() local player = game.Players:CreateLocalPlayer(0) player:LoadCharacter() player.Character.Humanoid.Died:connect(function() wait(5) player:LoadCharacter() end) \ No newline at end of file diff --git a/api/Game/vsolo.php b/api/Game/vsolo.php new file mode 100644 index 0000000..a3174e3 --- /dev/null +++ b/api/Game/vsolo.php @@ -0,0 +1,11 @@ +game:GetService("Visit") +game:GetService("RunService"):Run() +local player = game.Players:CreateLocalPlayer(0) +player:LoadCharacter() +while (true) do + wait(0.1) + if (player.Character.Humanoid.Health == 0) then + wait(5) + player:LoadCharacter() + end +end \ No newline at end of file diff --git a/api/IDE/ClientToolbox.php b/api/IDE/ClientToolbox.php new file mode 100644 index 0000000..fcc39fe --- /dev/null +++ b/api/IDE/ClientToolbox.php @@ -0,0 +1 @@ +Coming soon. \ No newline at end of file diff --git a/api/IDE/Start.php b/api/IDE/Start.php new file mode 100644 index 0000000..b18ab3d --- /dev/null +++ b/api/IDE/Start.php @@ -0,0 +1,16 @@ + + + + Graphictoria + + + + + + + +

Welcome to Graphictoria!

+

If you want to build, create a new document and start building!
+ If you are here to host a server, create a new document and run the command you've been given.

+ + \ No newline at end of file diff --git a/api/Login/Negotiate.ashx b/api/Login/Negotiate.ashx new file mode 100644 index 0000000..9046da4 --- /dev/null +++ b/api/Login/Negotiate.ashx @@ -0,0 +1,24 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + $stmtU = $dbcon->prepare("SELECT id FROM users WHERE gameKey=:key;"); + $stmtU->bindParam(':key', $_GET["suggest"], PDO::PARAM_STR); + $stmtU->execute(); + $suggestion = $stmtU->fetch(PDO::FETCH_ASSOC); + if(isset($suggestion["id"])){ + $stmtS = $dbcon->prepare("SELECT id,sessionId FROM sessions WHERE userId=:id ORDER BY id DESC;"); + $stmtS->bindParam(':id', $suggestion["id"], PDO::PARAM_INT); + $stmtS->execute(); + $session = $stmtS->fetch(PDO::FETCH_ASSOC); + setcookie("a_id", $session["sessionId"], time() + (86400 * 30), "/", ".gtoria.net", false, true); + }else{ + http_response_code(403); + } +} \ No newline at end of file diff --git a/api/UploadMedia/Screenshot.php b/api/UploadMedia/Screenshot.php new file mode 100644 index 0000000..6effea5 --- /dev/null +++ b/api/UploadMedia/Screenshot.php @@ -0,0 +1,16 @@ + + + + + Graphictoria + + + +

Screenshot

+

You have taken a screenshot.

+ + \ No newline at end of file diff --git a/api/config.php b/api/config.php new file mode 100644 index 0000000..e571dd7 --- /dev/null +++ b/api/config.php @@ -0,0 +1,20 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } +?> diff --git a/api/downloads/.htaccess b/api/downloads/.htaccess new file mode 100644 index 0000000..523ed2f --- /dev/null +++ b/api/downloads/.htaccess @@ -0,0 +1,4 @@ +AuthType Basic +AuthName "Graphictoria Authentication Required" +AuthUserFile /var/www/api/downloads/.htpasswd +Require valid-user diff --git a/api/downloads/.htpasswd b/api/downloads/.htpasswd new file mode 100644 index 0000000..e537cb6 --- /dev/null +++ b/api/downloads/.htpasswd @@ -0,0 +1 @@ +gtdownloadhandler:$apr1$RlcMELmY$b4ES1BCGe1HZTj/7nEnUL0 diff --git a/api/hooks/getLatestClient.php b/api/hooks/getLatestClient.php new file mode 100644 index 0000000..2789eab --- /dev/null +++ b/api/hooks/getLatestClient.php @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/api/hooks/report.php b/api/hooks/report.php new file mode 100644 index 0000000..ed01d4f --- /dev/null +++ b/api/hooks/report.php @@ -0,0 +1,52 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + + $stmt = $dbcon->prepare("SELECT username FROM users WHERE `lastIP`=:ip ORDER BY id DESC LIMIT 1"); + $stmt->bindParam(':ip', $_SERVER['REMOTE_ADDR'], PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + $url = "https://canary.discordapp.com/api/webhooks/x/x"; + $dataArray = array('content' => "[Username (assumed): **".$result['username']."**] ".$message, + 'username' => "Exploiter Alert"); + + $httpOptions = array( + 'http' => array ( + 'header' => "Graphictoria-Server", + 'content-type' => 'multipart/form-data', + 'method' => "POST", + 'content' => http_build_query($dataArray) + ) + ); + + $context = stream_context_create($httpOptions); + $result = @file_get_contents($url, false, $context); + + echo 'success'; + $dbcon = null; +?> diff --git a/api/imageServer/faces/.gitkeep b/api/imageServer/faces/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/api/imageServer/gear/.gitkeep b/api/imageServer/gear/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/api/imageServer/hats/.gitkeep b/api/imageServer/hats/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/api/imageServer/heads/.gitkeep b/api/imageServer/heads/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/api/imageServer/pants/.gitkeep b/api/imageServer/pants/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/api/imageServer/server/.gitkeep b/api/imageServer/server/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/api/imageServer/shirts/.gitkeep b/api/imageServer/shirts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/api/imageServer/tshirts/.gitkeep b/api/imageServer/tshirts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/api/imageServer/user/.htaccess b/api/imageServer/user/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/api/imageServer/user/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/api/imageServer/user/headshot/.gitkeep b/api/imageServer/user/headshot/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/api/img/character.php b/api/img/character.php new file mode 100644 index 0000000..1434e47 --- /dev/null +++ b/api/img/character.php @@ -0,0 +1,70 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + $stmt = $dbcon->prepare("SELECT charap, id FROM users WHERE id=:id"); + $stmt->bindParam(':id', $userid, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $userid = $result['id']; + $dbcon = null; +?> +game.Lighting.TimeOfDay = "12" +local player = game.Players:CreateLocalPlayer(0) +player.CharacterAppearance = "http://api.xdiscuss.net/user/getCharacter.php?uid=&mode=ch&sid=1&key=D869593BF742A42F79915993EF1DB&tick=" .. tick() + +local loadCharacter = coroutine.create(function() + wait(0.5) + player:LoadCharacter() + wait(0.5) + player:LoadCharacter() + wait(0.25) + local count = 0 + local p = player.Backpack:GetChildren() + for i = 1, #p do + if p[i].className == "Tool" then + count = count + 1 + if (count == 1) then + game.Workspace.Player.Torso:findFirstChild("Right Shoulder").DesiredAngle = 1.58 + game.Workspace.Player.Torso:findFirstChild("Right Shoulder").CurrentAngle = 1.58 + game.Workspace.Player.Torso.Anchored = true + wait(0.25) + p[i].Parent = player.Character + end + end + end + +end) + +coroutine.resume(loadCharacter) \ No newline at end of file diff --git a/api/img/character2008.php b/api/img/character2008.php new file mode 100644 index 0000000..0f04814 --- /dev/null +++ b/api/img/character2008.php @@ -0,0 +1,78 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + $stmt = $dbcon->prepare("SELECT charap, id FROM users WHERE id=:id"); + $stmt->bindParam(':id', $userid, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $userid = $result['id']; +?> +game.Lighting.TimeOfDay = "12" +local player = game.Players:CreateLocalPlayer(0) +player.CharacterAppearance = "prepare("SELECT * FROM wearing WHERE uid=:uid;"); + $stmt->bindParam(':uid', $userid, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'http://api.xdiscuss.net/user/getcolors.php?uid='.$userid; + }else{ + echo 'http://api.xdiscuss.net/user/getcolors.php?uid='.$userid.';'; + } + $count = 0; + foreach($stmt as $result) { + if ($result['type'] == "gear") { + }else{ + if ($count !== $stmt->rowCount()) { + if (isset($_GET['mode'])) { + echo $result['aprString'].';'; + }else{ + echo $result['aprString'].'?tick='.time().';'; + } + }else{ + echo $result['aprString']; + if (isset($_GET['mode'])) { + echo $result['aprString']; + }else{ + echo $result['aprString'].'?tick='.time(); + } + } + } + $count++; + } + $dbcon = null; +?>" + +local loadCharacter = coroutine.create(function() + wait(0.5) + player:LoadCharacter() + wait(0.5) + player:LoadCharacter() + wait(0.25) + local count = 0 + local p = player.Backpack:GetChildren() + for i = 1, #p do + if p[i].className == "Tool" then + count = count + 1 + if (count == 1) then + game.Workspace.Player.Torso:findFirstChild("Right Shoulder").DesiredAngle = 1.58 + game.Workspace.Player.Torso:findFirstChild("Right Shoulder").CurrentAngle = 1.58 + game.Workspace.Player.Torso.Anchored = true + wait(0.25) + p[i].Parent = player.Character + end + end + end +end) + +coroutine.resume(loadCharacter) \ No newline at end of file diff --git a/api/img/doFinish.php b/api/img/doFinish.php new file mode 100644 index 0000000..a4d3fd6 --- /dev/null +++ b/api/img/doFinish.php @@ -0,0 +1,50 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + + if (isset($_GET['renderid']) and isset($_GET['type']) and isset($_GET['apiKey']) and isset($_GET['version'])) { + $renderId = $_GET['renderid']; + $type = $_GET['type']; + $apiKey = $_GET['apiKey']; + $version = $_GET['version']; + + if ($apiKey == "edrherhesrhserhserhrse") { + if ($type == "character") { + $time = time(); + if ($version == 1) { + $stmt = $dbcon->prepare("UPDATE users SET imgTime = :time WHERE id=:id"); + $stmt->bindParam(':id', $renderId, PDO::PARAM_INT); + $stmt->bindParam(':time', $time, PDO::PARAM_INT); + $stmt->execute(); + } + if ($version == 2) { + $stmt = $dbcon->prepare("UPDATE users SET imgTime2008 = :time WHERE id=:id"); + $stmt->bindParam(':id', $renderId, PDO::PARAM_INT); + $stmt->bindParam(':time', $time, PDO::PARAM_INT); + $stmt->execute(); + } + }elseif ($type == "hats" || $type == "heads" || $type == "gear") { + $stmt = $dbcon->prepare("UPDATE catalog SET imgTime = NOW() WHERE datafile=:id"); + $stmt->bindParam(':id', $renderId, PDO::PARAM_STR); + $stmt->execute(); + }elseif ($type == "server") { + $stmt = $dbcon->prepare("UPDATE games SET imgTime = NOW() WHERE id=:id"); + $stmt->bindParam(':id', $renderId, PDO::PARAM_STR); + $stmt->execute(); + } + $stmt = $dbcon->prepare('DELETE FROM renders WHERE render_id = :rid AND type = :type AND version = :version;'); + $stmt->bindParam(':rid', $renderId, PDO::PARAM_INT); + $stmt->bindParam(':version', $version, PDO::PARAM_INT); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->execute(); + } + } + + $dbcon = null; +?> \ No newline at end of file diff --git a/api/img/gear.php b/api/img/gear.php new file mode 100644 index 0000000..52864c5 --- /dev/null +++ b/api/img/gear.php @@ -0,0 +1,14 @@ + +game:Load("http://xdiscuss.net/data/assets/gear/models/"); +local p = game:GetChildren() +for i = 1, #p do +if p[i].className == "Tool" then +p[i].Parent = game.Workspace +end +end \ No newline at end of file diff --git a/api/img/getList.php b/api/img/getList.php new file mode 100644 index 0000000..083ef5c --- /dev/null +++ b/api/img/getList.php @@ -0,0 +1,20 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + + $stmt = $dbcon->prepare('SELECT render_id, type, version FROM renders ORDER BY id ASC LIMIT 1;'); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) { + echo 'no-render'; + }else{ + echo $result['render_id'].'-'.$result['type'].'-'.$result['version']; + } + $dbcon = null; +?> \ No newline at end of file diff --git a/api/img/hats.php b/api/img/hats.php new file mode 100644 index 0000000..3aa1568 --- /dev/null +++ b/api/img/hats.php @@ -0,0 +1,14 @@ + +game:Load("http://xdiscuss.net/data/assets/hats/models/"); +local p = game:GetChildren() +for i = 1, #p do +if p[i].className == "Hat" then +p[i].Parent = game.Workspace +end +end \ No newline at end of file diff --git a/api/img/heads.php b/api/img/heads.php new file mode 100644 index 0000000..133c7a2 --- /dev/null +++ b/api/img/heads.php @@ -0,0 +1,28 @@ + +local headPart = Instance.new("Part", game.Workspace) +headPart.Size = Vector3.new(2, 1, 1) +headPart.formFactor = "Symmetric" +headPart.BottomSurface = "Smooth" +headPart.TopSurface = "Smooth" +local faceDecal = Instance.new("Decal", headPart) +faceDecal.Texture = "rbxasset://textures//face.png" +faceDecal.Face = "Front" +game:Load("http://xdiscuss.net/data/assets/heads/models/") +local p = game:GetChildren() +for i = 1, #p do +if p[i].className == "BlockMesh" then +p[i].Parent = headPart +end +if p[i].className == "SpecialMesh" then +p[i].Parent = headPart +end +if p[i].className == "CylinderMesh" then +p[i].Parent = headPart +end +end \ No newline at end of file diff --git a/api/img/pants.php b/api/img/pants.php new file mode 100644 index 0000000..9606956 --- /dev/null +++ b/api/img/pants.php @@ -0,0 +1,28 @@ + +local player = game.Players:CreateLocalPlayer(0) +game.Lighting.TimeOfDay = "12" +player.CharacterAppearance = "http://api.xdiscuss.net/user/getCharacter.php?uid=1&mode=pants&key=D869593BF742A42F79915993EF1DB&sid=&tick=" .. tick() +local loadCharacter = coroutine.create(function() + wait(0.5) + player:LoadCharacter() + wait(0.5) + player:LoadCharacter() + + local p = player.Character:GetChildren() + for i = 1, #p do + if p[i].className == "Part" then + p[i].BrickColor = BrickColor.new("White") + end + end +end) + +coroutine.resume(loadCharacter) \ No newline at end of file diff --git a/api/img/server.php b/api/img/server.php new file mode 100644 index 0000000..b98d5b5 --- /dev/null +++ b/api/img/server.php @@ -0,0 +1,43 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + $stmt = $dbcon->prepare("SELECT placeURL, dedi FROM games WHERE id=:id"); + $stmt->bindParam(':id', $gameID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['dedi'] == 1) echo 'game:Load("'.htmlentities($result['placeURL'], ENT_QUOTES, "UTF-8").'");'; + $dbcon = null; +?> +local p = game.StarterPack:GetChildren() +for i = 1, #p do + p[i].Parent = nil +end +local p = game.Workspace:GetChildren() +for i = 1, #p do + if p[i].className == "Message" then + p[i].Parent = nil + end + if p[i].className == "Hint" then + p[i].Parent = nil + end +end + +local p = game.StarterGui:GetChildren() +for i = 1, #p do + p[i].Parent = nil +end \ No newline at end of file diff --git a/api/img/shirts.php b/api/img/shirts.php new file mode 100644 index 0000000..6b50115 --- /dev/null +++ b/api/img/shirts.php @@ -0,0 +1,28 @@ + +local player = game.Players:CreateLocalPlayer(0) +game.Lighting.TimeOfDay = "12" +player.CharacterAppearance = "http://api.xdiscuss.net/user/getCharacter.php?uid=1&key=D869593BF742A42F79915993EF1DB&mode=shirts&sid=&tick=" .. tick() +local loadCharacter = coroutine.create(function() + wait(0.5) + player:LoadCharacter() + wait(0.5) + player:LoadCharacter() + + local p = player.Character:GetChildren() + for i = 1, #p do + if p[i].className == "Part" then + p[i].BrickColor = BrickColor.new("White") + end + end +end) + +coroutine.resume(loadCharacter) \ No newline at end of file diff --git a/api/img/tshirts.php b/api/img/tshirts.php new file mode 100644 index 0000000..25833b6 --- /dev/null +++ b/api/img/tshirts.php @@ -0,0 +1,28 @@ + +local player = game.Players:CreateLocalPlayer(0) +game.Lighting.TimeOfDay = "12" +player.CharacterAppearance = "http://api.xdiscuss.net/user/getCharacter.php?uid=1&key=D869593BF742A42F79915993EF1DB&mode=ts&sid=&tick=" .. tick() +local loadCharacter = coroutine.create(function() + wait(0.5) + player:LoadCharacter() + wait(0.5) + player:LoadCharacter() + + local p = player.Character:GetChildren() + for i = 1, #p do + if p[i].className == "Part" then + p[i].BrickColor = BrickColor.new("White") + end + end +end) + +coroutine.resume(loadCharacter) \ No newline at end of file diff --git a/api/index.html b/api/index.html new file mode 100644 index 0000000..af95e30 --- /dev/null +++ b/api/index.html @@ -0,0 +1 @@ +API OK \ No newline at end of file diff --git a/api/launcher/GraphictoriaStudioLauncher.exe b/api/launcher/GraphictoriaStudioLauncher.exe new file mode 100644 index 0000000..9137f61 Binary files /dev/null and b/api/launcher/GraphictoriaStudioLauncher.exe differ diff --git a/api/launcher/message.txt b/api/launcher/message.txt new file mode 100644 index 0000000..7ef1075 --- /dev/null +++ b/api/launcher/message.txt @@ -0,0 +1 @@ +Version 4.4 has been released \ No newline at end of file diff --git a/api/launcher/security.php b/api/launcher/security.php new file mode 100644 index 0000000..a99db18 --- /dev/null +++ b/api/launcher/security.php @@ -0,0 +1,81 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + // Validate everything + $stmt = $dbcon->prepare("SELECT gameKey, rank FROM users WHERE id=:uid;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($result['gameKey'] != $key) echo 'no'; + if ($result['gameKey'] == $key && strlen($result['gameKey']) > 0) { + $stmt = $dbcon->prepare("SELECT * FROM games WHERE id=:id;"); + $stmt->bindParam(':id', $gameId, PDO::PARAM_INT); + $stmt->execute(); + $rGame = $stmt->fetch(PDO::FETCH_ASSOC); + + $gameKey = $rGame['key']; + if ($rGame['public'] == 0) { + $stmtU = $dbcon->prepare("SELECT * FROM gameKeys WHERE userid=:id AND `key` = :key;"); + $stmtU->bindParam(':id', $uid, PDO::PARAM_INT); + $stmtU->bindParam(':key', $gameKey, PDO::PARAM_STR); + $stmtU->execute(); + if ($stmtU->rowCount() == 0 and $rGame['creator_uid'] != $uid and $result['rank'] == 0 and $rGame['public'] == 0) { + echo 'no'; + $dbcon = null; + exit; + } + } + + $stmt = $dbcon->prepare("DELETE FROM gameJoins WHERE uid=:uid"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $dbcon->prepare("INSERT INTO `gameJoins` (`uid`, `gameId`) VALUES (:uid, :gameId);"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->bindParam(':gameId', $gameId, PDO::PARAM_INT); + $stmt->execute(); + + // Badge awarding + $stmt = $dbcon->prepare("SELECT id FROM badges WHERE uid=:uid AND badgeId = 8;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + + if ($stmt->rowCount() == 0) { + $stmt = $dbcon->prepare("INSERT INTO `badges` (`uid`, `badgeId`) VALUES (:uid, 8);"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + } + echo 'yes'; + } + + $dbcon = null; +?> \ No newline at end of file diff --git a/api/launcher/version.txt b/api/launcher/version.txt new file mode 100644 index 0000000..3208b09 --- /dev/null +++ b/api/launcher/version.txt @@ -0,0 +1 @@ +4.6.2 \ No newline at end of file diff --git a/api/launcher/version2008.txt b/api/launcher/version2008.txt new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/api/launcher/version2008.txt @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/api/launcher/version2011.txt b/api/launcher/version2011.txt new file mode 100644 index 0000000..c0943d3 --- /dev/null +++ b/api/launcher/version2011.txt @@ -0,0 +1 @@ +2.3 \ No newline at end of file diff --git a/api/places/baseplate.rbxl b/api/places/baseplate.rbxl new file mode 100644 index 0000000..b529d7d --- /dev/null +++ b/api/places/baseplate.rbxl @@ -0,0 +1,274 @@ + + null + nil + + + RBX1 + 0 + + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + + Workspace + null + true + + + + null + 0 + + 4.56909466 + 4.94566154 + 3.96710539 + 0.882947445 + -0.229501724 + 0.409552038 + -0 + 0.872367501 + 0.488850743 + -0.469471961 + -0.431629509 + 0.770254552 + + + -3.62194633 + -4.83135414 + -11.4379864 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + + Camera + true + + + + + true + -0.5 + 0.5 + 0 + 0 + -0.5 + 0.5 + 4 + 0 + 37 + + 0 + 0.600000024 + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + + true + false + 0.5 + 1 + 0.300000012 + -0.5 + 0.5 + 0 + 0 + -0.5 + 0.5 + 0 + 0 + true + 256 + Part + 0 + -0.5 + 0.5 + 0 + 0 + + 0 + 0 + 0 + + -0.5 + 0.5 + 3 + 0 + 0 + + 0 + 0 + 0 + + true + 1 + + 512 + 1.20000005 + 512 + + + + + + + Run Service + true + + + RBX4 + RBX5 + + + Instance + true + + + + + ContentFilter + true + + + + + Instance + true + + + + + 12 + Players + true + + + + + StarterPack + true + + + + + StarterGui + true + true + + + + + 0 + 10 + 1 + Soundscape + 1 + true + + RBX13 + RBX14 + RBX15 + RBX16 + RBX17 + RBX18 + RBX19 + RBX20 + RBX21 + RBX22 + RBX23 + RBX24 + RBX25 + RBX26 + + + + PhysicsService + true + + + + + BadgeService + true + + + + + Geometry + true + + + + + Instance + true + + + + + 300 + Debris + true + + + + + Instance + true + + + + + Instance + true + + + RBX34 + + + Selection + true + + + RBX36 + + + 4286743170 + 1 + 4278190080 + 4278190080 + 41.7332993 + Lighting + 4290098618 + 14:00:00 + true + + + + + ChangeHistoryService + true + + + \ No newline at end of file diff --git a/api/router/createServer.php b/api/router/createServer.php new file mode 100644 index 0000000..f50bba3 --- /dev/null +++ b/api/router/createServer.php @@ -0,0 +1,79 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + + // Define variables + $serverVersion = $_GET['version']; + $userID = $_GET['userID']; + $serverName = $_GET['serverName']; + $serverDescription = $_GET['serverDescription']; + $serverPrivacy = $_GET['serverPrivacy']; + $serverIP = $_GET['ip']; + $serverPort = $_GET['port']; + $placeURL = $_GET['placeURL']; + + + // Check the variables + if ($serverVersion != 3) exit; + if ($serverPrivacy != 0 && $serverPrivacy != 1) exit; + + if ($_GET['key'] != "894cfcdf-7714-4fea-a3f1-436d711a462b") exit; + if (strlen($userID) == 0 || strlen($serverName) == 0 || strlen($serverName) > 256 || strlen($serverDescription) > 256) exit; + + // Define and generate the server public and private key + $publicKey = md5(microtime().rand()); + $privateKey = md5(microtime().rand()); + + // Insert the game to the games table and it will appear online as soon the server pings. + $stmt = $dbcon->prepare("INSERT INTO games (`public`, `creator_uid`, `name`, `description`, `key`, `privatekey`, `version`, `ip`, `port`, `dedi`, `placeURL`) VALUES (:public, :user, :name, :description, :key, :serverkey, :version, :ip, :port, 1, :placeURL);"); + $stmt->bindParam(':public', $serverPrivacy, PDO::PARAM_INT); + $stmt->bindParam(':version', $serverVersion, PDO::PARAM_INT); + $stmt->bindParam(':serverkey', $privateKey, PDO::PARAM_STR); + $stmt->bindParam(':user', $userID, PDO::PARAM_INT); + $stmt->bindParam(':name', $serverName, PDO::PARAM_STR); + $stmt->bindParam(':description', $serverDescription, PDO::PARAM_STR); + $stmt->bindParam(':key', $publicKey, PDO::PARAM_STR); + $stmt->bindParam(':port', $serverPort, PDO::PARAM_INT); + $stmt->bindParam(':ip', $serverIP, PDO::PARAM_STR); + $stmt->bindParam(':placeURL', $placeURL, PDO::PARAM_STR); + $stmt->execute(); + + // Return the game ID and private key so we execute the proper Lua code on the router. + $stmt = $dbcon->prepare("SELECT * FROM games WHERE `creator_uid`=:uid ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':uid', $userID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + // Insert a request for the imageServer + if (file_exists("/var/www/api/imageServer/server/".$result['id'])) @unlink("/var/www/api/imageServer/server/".$result['id']); + if ($serverVersion == 1) { + $stmt = $dbcon->prepare("INSERT INTO renders (`render_id`, `type`, `version`) VALUES (:placeURL, 'server', 2);"); + $stmt->bindParam(':placeURL', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + }else{ + $stmt = $dbcon->prepare("INSERT INTO renders (`render_id`, `type`) VALUES (:placeURL, 'server');"); + $stmt->bindParam(':placeURL', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + // Return the ID and private key + echo $result['id'].'-'.$result['privatekey']; + $dbcon = null; +?> \ No newline at end of file diff --git a/api/router/deletePlace.php b/api/router/deletePlace.php new file mode 100644 index 0000000..c741dfb --- /dev/null +++ b/api/router/deletePlace.php @@ -0,0 +1,31 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + + $aa = '%'.$placeHash.'%'; + $stmt = $dbcon->prepare("SELECT * FROM games WHERE `placeURL` LIKE :aa ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':aa', $aa, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $id = $result['id']; + + $stmt = $dbcon->prepare("DELETE FROM renders WHERE render_id = :key AND type = 'server';"); + $stmt->bindParam(':key', $id, PDO::PARAM_STR); + $stmt->execute(); + $dbcon = null; + + echo 'success'; +?> \ No newline at end of file diff --git a/api/router/deleteServer.php b/api/router/deleteServer.php new file mode 100644 index 0000000..52054bd --- /dev/null +++ b/api/router/deleteServer.php @@ -0,0 +1,21 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + + $stmt = $dbcon->prepare('DELETE FROM games WHERE privatekey = :key;'); + $stmt->bindParam(':key', $serverKey, PDO::PARAM_STR); + $stmt->execute(); + echo 'success'; + $dbcon = null; +?> \ No newline at end of file diff --git a/api/router/getIP.php b/api/router/getIP.php new file mode 100644 index 0000000..324e0cc --- /dev/null +++ b/api/router/getIP.php @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/api/router/getPlayerCount.php b/api/router/getPlayerCount.php new file mode 100644 index 0000000..1c4e9b7 --- /dev/null +++ b/api/router/getPlayerCount.php @@ -0,0 +1,30 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + + $stmt = $dbcon->prepare('SELECT numPlayers, lastPing FROM games WHERE privatekey = :key ORDER BY id ASC LIMIT 1;'); + $stmt->bindParam(':key', $serverKey, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() > 0) { + $timeSince = round(abs(strtotime(date('Y-m-d H:i:s')) - strtotime($result['lastPing'])) / 60,2); + if ($timeSince > 5 || $result['lastPing'] == null) { + echo '0'; + }else{ + echo $result['numPlayers']; + } + }else{ + echo '0'; + } + + $dbcon = null; +?> \ No newline at end of file diff --git a/api/router/getRequests.php b/api/router/getRequests.php new file mode 100644 index 0000000..a1f8bde --- /dev/null +++ b/api/router/getRequests.php @@ -0,0 +1,29 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + + $stmt = $dbcon->prepare('SELECT * FROM serverRequests ORDER BY id ASC LIMIT 1;'); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $sID = $result['id']; + if ($stmt->rowCount() == 0) { + echo 'no-request'; + }else{ + echo $result['placeLocation'].'-'.$result['serverVersion'].'-'.$result['userID'].'-'.$result['serverName'].'-'.$result['serverDescription'].'-'.$result['serverPrivacy']; + } + + $stmt = $dbcon->prepare('DELETE FROM serverRequests WHERE id = :id'); + $stmt->bindParam(':id', $sID, PDO::PARAM_INT); + $stmt->execute(); + + $dbcon = null; +?> \ No newline at end of file diff --git a/api/server/checkPlayer.php b/api/server/checkPlayer.php new file mode 100644 index 0000000..3809a27 --- /dev/null +++ b/api/server/checkPlayer.php @@ -0,0 +1,77 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + $stmt = $dbcon->prepare("SELECT username FROM users WHERE id=:id;"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) { + echo 'no'; + }else{ + if ($result['username'] != $username) { + echo 'no'; + }else{ + $stmt = $dbcon->prepare("SELECT * FROM gameJoins WHERE uid=:id;"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) { + echo 'no'; + }else{ + $currentTime = date('Y-m-d H:i:s'); + $to_time = strtotime($currentTime); + $from_time = strtotime($result['time']); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 5) { + echo 'no'; + }else{ + if ($result['gameId'] != $gameId) { + echo 'no'; + }else{ + echo 'yes'; + } + } + } + } + } + $dbcon = null; +?> \ No newline at end of file diff --git a/api/server/ping.php b/api/server/ping.php new file mode 100644 index 0000000..0a51150 --- /dev/null +++ b/api/server/ping.php @@ -0,0 +1,54 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + $query = "SELECT lastPing FROM games WHERE `privatekey` = :key LIMIT 1;"; + $stmt = $dbcon->prepare($query); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + $currentTime = date('Y-m-d H:i:s'); + $from_time = strtotime($result['lastPing']); + $to_time = strtotime($currentTime); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 0.4) { + $stmt = $dbcon->prepare("UPDATE games SET lastPing=NOW() WHERE `privatekey`=:key;"); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->execute(); + } + + if (isset($_GET['players'])) { + $nPlayers = $_GET['players']; + $stmt = $dbcon->prepare("UPDATE games SET numPlayers=:np WHERE `privatekey`=:key;"); + $stmt->bindParam(':np', $nPlayers, PDO::PARAM_INT); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->execute(); + } + + $dbcon = null; +?> \ No newline at end of file diff --git a/api/server/pingPlayer.php b/api/server/pingPlayer.php new file mode 100644 index 0000000..6a11327 --- /dev/null +++ b/api/server/pingPlayer.php @@ -0,0 +1,75 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + $stmt = $dbcon->prepare("SELECT gameKey, banned FROM users WHERE id=:id;"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['gameKey'] == $key) { + $query = "SELECT lastSeen, inGame, banned FROM users WHERE id = :id LIMIT 1;"; + $stmt = $dbcon->prepare($query); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['inGame'] == 0) { + $stmt = $dbcon->prepare("UPDATE users SET inGame = 1 WHERE id=:id"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + } + + if ($result['inGame'] != $gameId) { + $stmt = $dbcon->prepare("UPDATE users SET inGameId = :gameId WHERE id=:id"); + $stmt->bindParam(':gameId', $gameId, PDO::PARAM_INT); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + } + + $currentTime = date('Y-m-d H:i:s'); + $from_time = strtotime($result['lastSeen']); + $to_time = strtotime($currentTime); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 0.4) { + $stmt = $dbcon->prepare("UPDATE users SET lastSeen = NOW() WHERE id=:id"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + } + } + + if ($result['banned'] > 0) { + echo 'banned'; + } + + $dbcon = null; +?> \ No newline at end of file diff --git a/api/serverscripts/server.php b/api/serverscripts/server.php new file mode 100644 index 0000000..be754d1 --- /dev/null +++ b/api/serverscripts/server.php @@ -0,0 +1,164 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + $stmtU = $dbcon->prepare("SELECT * FROM games WHERE `privatekey`=:key;"); + $stmtU->bindParam(':key', $key, PDO::PARAM_STR); + $stmtU->execute(); + $dbcon = null; + if ($stmtU->rowCount() == 0) { + exit; + } + $rGame = $stmtU->fetch(PDO::FETCH_ASSOC); + $serverID = $rGame['id']; + if (!isset($_GET['port'])) { + $serverPort = $rGame['port']; + }else{ + $serverPort = $_GET['port']; + } + $serverKey = $rGame['privatekey']; + $gameVersion = $rGame['version']; +?> + + + +game:GetService("NetworkServer"):Start(, 20) +game:GetService("RunService"):Run() +print("Graphictoria Server") + +coroutine.resume(coroutine.create(function() + while (true) do + + wait(60) + end +end)) + +function authenticate(newPlayer) + coroutine.resume(coroutine.create(function() + local verification = game:HttpGet("http://api.gtoria.net/server/checkPlayer.php?uname=" .. newPlayer.Name .. "&gameId=&uid=".. newPlayer.userId .."&tick=" .. tick(), true) + if (verification:sub(4) ~= "yes") then + local p = game:GetService("NetworkServer"):GetChildren() + for i = 1, #p do + if (p[i]:GetPlayer().Name == newPlayer.Name) then + print (newPlayer.Name .. " has failed to authenticate.") + wait() + p[i]:CloseConnection() + end + end + return false + else + return true + end + end)) + if (newPlayer.Name == "Player") then + coroutine.resume(coroutine.create(function() + local p = game:GetService("NetworkServer"):GetChildren() + for i = 1, #p do + if (p[i]:GetPlayer().Name == newPlayer.Name) then + print (newPlayer.Name .. " has connected with an invalid username.") + wait() + p[i]:CloseConnection() + end + end + end)) + return false + else + return true + end + local plrNum = 0 + local p = game.Players:GetChildren() + for i = 1, #p do + if (p[i].Name == newPlayer.Name) then + plrNum = plrNum + 1 + end + end + if (plrNum > 1) then + coroutine.resume(coroutine.create(function() + local p = game:GetService("NetworkServer"):GetChildren() + for i = 1, #p do + if (p[i]:GetPlayer().Name == newPlayer.Name) then + print (newPlayer.Name .. " has been kicked for duplication.") + wait() + p[i]:CloseConnection() + end + end + end)) + return false + else + return true + end +end + +function hex(str) + return (str:gsub('.', function (c) + return string.format('%02X', string.byte(c)) + end)) +end + +game:GetService("NetworkServer").ChildAdded:connect(function(newConnection) + newConnection.Name = "ClientConnection" + wait(1) + newConnection.Name = ("Connection " .. math.random(0, 99999999)) +end) + +game:GetService("Players").PlayerAdded:connect(function(newPlayer) + wait() + newPlayer.Chatted:connect(function(message) + local hexmsg = hex(message) + if (hexmsg:find'A0') then + newPlayer.Parent = nil + end + if (message == ";reset") then + newPlayer.Character.Humanoid.Health = 0 + end + end) + if (authenticate(newPlayer)) then + + print(newPlayer.Name .. " has joined this server") + --[[ + newPlayer.CharacterAdded:connect(function(newCharacter) + newCharacter.Humanoid.Died:connect(function() + + wait(5) + newPlayer:LoadCharacter() + end) + end) + ]] + newPlayer:LoadCharacter() + end +end) \ No newline at end of file diff --git a/api/user/enc.php b/api/user/enc.php new file mode 100644 index 0000000..9d5b5bb --- /dev/null +++ b/api/user/enc.php @@ -0,0 +1,52 @@ + ' ', 33 => '!', 34 => '"', 35 => '#', 36 => '$', + 37 => '%', 38 => '&', 39 => "'", 40 => '(', 41 => ')', + 42 => '*', 43 => '+', 44 => ',', 45 => '-', 46 => '.', + 47 => '/', 48 => '0', 49 => '1', 50 => '2', 51 => '3', + 52 => '4', 53 => '5', 54 => '6', 55 => '7', 56 => '8', + 57 => '9', 58 => ':', 59 => ';', 60 => '<', 61 => '=', + 62 => '>', 63 => '?', 64 => '@', 65 => 'A', 66 => 'B', + 67 => 'C', 68 => 'D', 69 => 'E', 70 => 'F', 71 => 'G', + 72 => 'H', 73 => 'I', 74 => 'J', 75 => 'K', 76 => 'L', + 77 => 'M', 78 => 'N', 79 => 'O', 80 => 'P', 81 => 'Q', + 82 => 'R', 83 => 'S', 84 => 'T', 85 => 'U', 86 => 'V', + 87 => 'W', 88 => 'X', 89 => 'Y', 90 => 'Z', 91 => '[', + 92 => '\\', 93 => ']', 94 => '^', 95 => '_', 96 => '`', + 97 => 'a', 98 => 'b', 99 => 'c', 100 => 'd', 101 => 'e', + 102 => 'f', 103 => 'g', 104 => 'h', 105 => 'i', 106 => 'j', + 107 => 'k', 108 => 'l', 109 => 'm', 110 => 'n', 111 => 'o', + 112 => 'p', 113 => 'q', 114 => 'r', 115 => 's', 116 => 't', + 117 => 'u', 118 => 'v', 119 => 'w', 120 => 'x', 121 => 'y', + 122 => 'z', 123 => '{', 124 => '|', 125 => '}' + ); + + public static function fromNumber($number) + { + $string = ''; + while($number) + { + $value = substr($number, 0, 2); + $number = substr($number, 2); + + if($value < 32) + { + $value .= substr($number, 0, 1); + $number = substr($number, 1); + } + + $string .= self::$charset[ (int) $value]; + } + return $string; + } + + public static function fromString($string) + { + $number = ''; + foreach(str_split($string) as $char) $number .= '\\'.ord($char); + return $number; + } +} +?> \ No newline at end of file diff --git a/api/user/game/getScript.php b/api/user/game/getScript.php new file mode 100644 index 0000000..bd66ee9 --- /dev/null +++ b/api/user/game/getScript.php @@ -0,0 +1,143 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + // User row + $stmtU = $dbcon->prepare("SELECT * FROM users WHERE id=:id;"); + $stmtU->bindParam(':id', $userID, PDO::PARAM_INT); + $stmtU->execute(); + $rUser = $stmtU->fetch(PDO::FETCH_ASSOC); + + // Game row + $stmtG = $dbcon->prepare("SELECT * FROM games WHERE id=:id;"); + $stmtG->bindParam(':id', $gameID, PDO::PARAM_INT); + $stmtG->execute(); + $rGame = $stmtG->fetch(PDO::FETCH_ASSOC); + + // Throw an error on the client if the game does not exist + if ($stmtU->rowCount() == 0 or $stmtG->rowCount() == 0) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") game:SetMessage("Server not found!");'); + + // Throw an error on the client if the user is not authorized to join the game + if ($rGame['public'] == 0) { + $gameKey = $rGame['key']; + $stmtU = $dbcon->prepare("SELECT * FROM gameKeys WHERE userid=:id AND `key` = :key;"); + $stmtU->bindParam(':id', $userID, PDO::PARAM_INT); + $stmtU->bindParam(':key', $gameKey, PDO::PARAM_STR); + $stmtU->execute(); + if ($stmtU->rowCount() == 0 and $rGame['creator_uid'] != $userID and $rUser['rank'] == 0) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") game:SetMessage("You are not authorized to join this server.");'); + } + + // If a special ban is enabled, it will "fail to connect". + if ($rUser['publicBan'] == 1) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") game:SetMessage("Unable to connect to Graphictoria");'); + + // Throw an error if the authentication keys do not match + if ($pkey != $rUser['gameKey']) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") game:SetMessage("Authentication has failed.");'); + + $dbcon = null; // We're done with the database. +?> +game:GetService("RunService"):Run() +local networkClient = game:GetService("NetworkClient") +local visit = game:GetService("Visit") +local player = game:GetService("Players"):CreateLocalPlayer() +player.Name = "" +game.Players:SetChatStyle(Enum.ChatStyle.ClassicAndBubble) +player.CharacterAppearance = "http://api.xdiscuss.net/user/getCharacter.php?uid=&key=D869593BF742A42F79915993EF1DB&tick=" +networkClient:SetOutgoingKBPSLimit(1) +networkClient:Connect("", , 0, 20) +visit:SetUploadUrl("") + +game:SetMessage("Connecting to Server...") + +networkClient.ConnectionAccepted:connect(function(p, replicator) + local isLoading = true + game:SetMessageBrickCount() + + replicator:SendMarker().Received:connect(function() + game:SetMessage("Waiting for Character...") + player.Changed:connect(function(change) + if (change == "Character") then + game:ClearMessage() + local screenGUI = Instance.new("ScreenGui", player.PlayerGui) + screenGUI.Name = "ResetGUI" + local resetBtn = Instance.new("TextButton", screenGUI) + resetBtn.Name = "ResetButton" + resetBtn.Text = "Reset" + resetBtn.Position = UDim2.new(0, 300, 0, -20) + resetBtn.Size = UDim2.new(0, 100, 0, 20) + resetBtn.TextColor3 = Color3.new(191, 191, 191) + resetBtn.BackgroundColor3 = Color3.new(105, 105, 105) + resetBtn.BackgroundTransparency = 0.4 + resetBtn.BorderSizePixel = 0 + + resetBtn.MouseButton1Click:connect(function() + player.Character:BreakJoints() + end) + isLoading = false + end + if (change == "Parent") then + game:SetMessage("This game has shut down.") + game:GetService("NetworkClient"):Disconnect() + end + end) + player.Chatted:connect(function(message) + game:SetMessage(message) + end) + end) + + replicator.Disconnection:connect(function() + game:SetMessage("You have lost the connection to the Server.") + end) + + while (isLoading) do + game.Workspace:ZoomToExtents() + wait(0.5) + end +end) + +networkClient.ConnectionRejected:connect(function() + game:SetMessage("Unable to connect because the client and server version do not match.") +end) + +networkClient.ConnectionFailed:connect(function(_, id) + game:SetMessage("Failed to connect to the Server. (ID=" .. id .. ")") +end) + +coroutine.resume(coroutine.create(function() + while (true) do + local ping = game:HttpGet("http://api.xdiscuss.net/server/pingPlayer.php?userID=&key=&gameID=&gameId=&tick=" .. tick(), true) + if (ping:sub(4) == "banned") then + game:SetMessage("Your account has been suspended from Graphictoria.") + game:GetService("NetworkClient"):Disconnect() + end + wait(30) + end +end)) \ No newline at end of file diff --git a/api/user/game/getScript2008.php b/api/user/game/getScript2008.php new file mode 100644 index 0000000..b3dfc53 --- /dev/null +++ b/api/user/game/getScript2008.php @@ -0,0 +1,155 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + // User row + $stmtU = $dbcon->prepare("SELECT * FROM users WHERE id=:id;"); + $stmtU->bindParam(':id', $userID, PDO::PARAM_INT); + $stmtU->execute(); + $rUser = $stmtU->fetch(PDO::FETCH_ASSOC); + + // Game row + $stmtG = $dbcon->prepare("SELECT * FROM games WHERE id=:id;"); + $stmtG->bindParam(':id', $gameID, PDO::PARAM_INT); + $stmtG->execute(); + $rGame = $stmtG->fetch(PDO::FETCH_ASSOC); + + // Throw an error on the client if the game does not exist + if ($stmtU->rowCount() == 0 or $stmtG->rowCount() == 0) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") game:SetMessage("Server not found!");'); + + // Throw an error on the client if the user is not authorized to join the game + if ($rGame['public'] == 0) { + $gameKey = $rGame['key']; + $stmtU = $dbcon->prepare("SELECT * FROM gameKeys WHERE userid=:id AND `key` = :key;"); + $stmtU->bindParam(':id', $userID, PDO::PARAM_INT); + $stmtU->bindParam(':key', $gameKey, PDO::PARAM_STR); + $stmtU->execute(); + if ($stmtU->rowCount() == 0 and $rGame['creator_uid'] != $userID and $rUser['rank'] == 0) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") game:SetMessage("You are not authorized to join this server.");'); + } + + // If a special ban is enabled, it will "fail to connect". + if ($rUser['publicBan'] == 1) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") game:SetMessage("Unable to connect to Graphictoria");'); + + // Throw an error if the authentication keys do not match + if ($pkey != $rUser['gameKey']) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") game:SetMessage("Authentication has failed.");'); +?> +game:GetService("RunService"):Run() +local networkClient = game:GetService("NetworkClient") +local visit = game:GetService("Visit") +local player = game:GetService("Players"):CreateLocalPlayer() +player.Name = "" +player.CharacterAppearance = "prepare("SELECT * FROM wearing WHERE uid=:uid;"); + $stmt->bindParam(':uid', $userID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'http://api.xdiscuss.net/user/getcolors.php?uid='.$userID; + }else{ + echo 'http://api.xdiscuss.net/user/getcolors.php?uid='.$userID.';'; + } + $count = 0; + foreach($stmt as $result) { + if ($result['type'] == "gear") { + }else{ + if ($count !== $stmt->rowCount()) { + if (isset($_GET['mode'])) { + echo $result['aprString'].';'; + }else{ + echo $result['aprString'].'?tick='.time().';'; + } + }else{ + echo $result['aprString']; + if (isset($_GET['mode'])) { + echo $result['aprString']; + }else{ + echo $result['aprString'].'?tick='.time(); + } + } + } + $count++; + } + $dbcon = null; +?>" +networkClient:Connect("", , 0, 20) +visit:SetUploadUrl("") + +game:SetMessage("Connecting to Server...") + +networkClient.ConnectionAccepted:connect(function(p, replicator) + local isLoading = true + game:SetMessageBrickCount() + + replicator:SendMarker().Received:connect(function() + game:SetMessage("Waiting for Character...") + player.Changed:connect(function(change) + if (change == "Character") then + game:ClearMessage() + isLoading = false + end + if (change == "Parent") then + game:SetMessage("This game has shut down.") + game:GetService("NetworkClient"):Disconnect() + end + end) + player.Chatted:connect(function(message) + game:SetMessage(message) + end) + end) + + replicator.Disconnection:connect(function() + game:SetMessage("You have lost the connection to the Server.") + end) + + while (isLoading) do + game.Workspace:ZoomToExtents() + wait(0.5) + end +end) + +networkClient.ConnectionRejected:connect(function() + game:SetMessage("Unable to connect because the client and server version do not match.") +end) + +networkClient.ConnectionFailed:connect(function(_, id) + game:SetMessage("Failed to connect to the Server. (ID=" .. id .. ")") +end) + +coroutine.resume(coroutine.create(function() + while (true) do + local ping = game:HttpGet("http://api.xdiscuss.net/server/pingPlayer.php?userID=&key=&gameID=&gameId=&tick=" .. tick(), false) + if (ping:sub(4) == "banned") then + game:SetMessage("Your account has been suspended from Graphictoria.") + game:GetService("NetworkClient"):Disconnect() + end + wait(30) + end +end)) \ No newline at end of file diff --git a/api/user/game/getScript2011.php b/api/user/game/getScript2011.php new file mode 100644 index 0000000..68f94ce --- /dev/null +++ b/api/user/game/getScript2011.php @@ -0,0 +1,108 @@ +loadfile("http://api.xdiscuss.net/Asset/?id=500&tick=" .. tick())() -- Main gui +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + // User row + $stmtU = $dbcon->prepare("SELECT * FROM users WHERE id=:id;"); + $stmtU->bindParam(':id', $userID, PDO::PARAM_INT); + $stmtU->execute(); + $rUser = $stmtU->fetch(PDO::FETCH_ASSOC); + + // Game row + $stmtG = $dbcon->prepare("SELECT * FROM games WHERE id=:id;"); + $stmtG->bindParam(':id', $gameID, PDO::PARAM_INT); + $stmtG->execute(); + $rGame = $stmtG->fetch(PDO::FETCH_ASSOC); + + // Throw an error on the client if the game does not exist + if ($stmtU->rowCount() == 0 or $stmtG->rowCount() == 0) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") setMessage("Server not found!");'); + + // Throw an error on the client if the user is not authorized to join the game + if ($rGame['public'] == 0) { + $gameKey = $rGame['key']; + $stmtU = $dbcon->prepare("SELECT * FROM gameKeys WHERE userid=:id AND `key` = :key;"); + $stmtU->bindParam(':id', $userID, PDO::PARAM_INT); + $stmtU->bindParam(':key', $gameKey, PDO::PARAM_STR); + $stmtU->execute(); + if ($stmtU->rowCount() == 0 and $rGame['creator_uid'] != $userID and $rUser['rank'] == 0) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") setMessage("You are not authorized to join this server.");'); + } + + // If a special ban is enabled, it will "fail to connect". + if ($rUser['publicBan'] == 1) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") setMessage("Unable to connect to Graphictoria");'); + + // Throw an error if the authentication keys do not match + if ($pkey != $rUser['gameKey']) die('game:GetService("NetworkClient") game:GetService("Visit"):SetUploadUrl("") setMessage("Authentication has failed.");'); + + $dbcon = null; // We're done with the database. +?> +game:GetService("RunService"):Run() +settings().Diagnostics:LegacyScriptMode() +game:SetRemoteBuildMode(true) + +setMessage("Connecting to the server") +local visit = game:GetService("Visit") +visit:SetUploadUrl("") +local networkClient = game:GetService("NetworkClient") +local player = networkClient:PlayerConnect(, "", , 20, 0) +player.Name = "" +player.CharacterAppearance = "http://api.xdiscuss.net/user/getCharacter.php?uid=&key=D869593BF742A42F79915993EF1DB&tick=" + +networkClient.ConnectionFailed:connect(function() + setMessage("Connection to the server has failed") +end) + +networkClient.ConnectionRejected:connect(function() + setMessage("Connection has been rejected") +end) + +networkClient.ConnectionAccepted:connect(function(peer, replicator) + local isLoading = true + player.Changed:connect(function(change) + if (change == "Character") then + isLoading = false + setMessage("") + end + end) + while (isLoading) do + wait(0.5) + game.Workspace:ZoomToExtents() + if (isLoading) then + setMessage("Loading game...") + end + end + replicator.Disconnection:connect(function() + setMessage("Connection to the server has been lost") + end) +end) + +loadfile("http://api.xdiscuss.net/Asset/?id=501&tick=" .. tick())() -- Player list \ No newline at end of file diff --git a/api/user/game/joinGame2008.php b/api/user/game/joinGame2008.php new file mode 100644 index 0000000..4d23ad3 --- /dev/null +++ b/api/user/game/joinGame2008.php @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/api/user/game/joinGame2011.php b/api/user/game/joinGame2011.php new file mode 100644 index 0000000..96ac0cf --- /dev/null +++ b/api/user/game/joinGame2011.php @@ -0,0 +1,43 @@ + \ No newline at end of file diff --git a/api/user/game/joinGameV2.php b/api/user/game/joinGameV2.php new file mode 100644 index 0000000..e9a9e51 --- /dev/null +++ b/api/user/game/joinGameV2.php @@ -0,0 +1,43 @@ + \ No newline at end of file diff --git a/api/user/game/joinGameV3.php b/api/user/game/joinGameV3.php new file mode 100644 index 0000000..34bd239 --- /dev/null +++ b/api/user/game/joinGameV3.php @@ -0,0 +1,43 @@ +&1"); + echo file_get_contents($linuxDirectory . ".luac"); + + @unlink($linuxDirectory . ".luac"); + @unlink($linuxDirectory . ".lua"); +?> \ No newline at end of file diff --git a/api/user/getCharacter.php b/api/user/getCharacter.php new file mode 100644 index 0000000..313e925 --- /dev/null +++ b/api/user/getCharacter.php @@ -0,0 +1,114 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + header('Content-Type: text/plain'); + header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); + header("Cache-Control: post-check=0, pre-check=0", false); + header("Pragma: no-cache"); + + if (isset($_GET['uid'])) { + $uid = $_GET['uid']; + if (strlen($uid) == 0) { + $dbcon = null; + exit; + } + if (!is_numeric($uid)) { + $dbcon = null; + exit; + } + }else{ + $dbcon = null; + exit; + } + + if (isset($_GET['mode'])) { + $mode = $_GET['mode']; + $assetId = $_GET['sid']; + if (!is_numeric($assetId)) { + $dbcon = null; + exit; + } + if ($mode != "ch") { + echo 'http://gtoria.net/asset/?id='.$assetId; + $dbcon = null; + exit; + } else { + echo ''; + } + } + + if (isset($_GET['dgear'])) { + $disableGear = true; + }else{ + $disableGear = false; + } + + $stmt = $dbcon->prepare("SELECT id FROM users WHERE id=:uid;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + $dbcon = null; + exit; + } + + $stmt = $dbcon->prepare("SELECT * FROM wearing WHERE uid=:uid;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'http://api.gtoria.net/user/getcolors.php?uid='.$uid.'&cachebuster='.time(); + }else{ + echo 'http://api.gtoria.net/user/getcolors.php?uid='.$uid.'&cachebuster='.time().';'; + } + + $equippedGearCatalogId = 0; + $count = 0; + foreach($stmt as $result) { + if ($disableGear == true and $result['type'] == "gear") { + }else{ + if($result['type'] == "gear") + { + $equippedGearCatalogId = $result['catalogId']; + } + if ($count !== $stmt->rowCount()-1) { + echo $result['aprString'].($result['type'] == "gear" ? '?equipped=1' : '').';'; + }else{ + echo $result['aprString'].($result['type'] == "gear" ? '?equipped=1' : ''); + } + } + $count++; + } + + if ($disableGear == false) { + $stmt = $dbcon->prepare('SELECT `datafile` FROM `catalog` cat JOIN `owneditems` own JOIN `wearing` wear WHERE own.`uid` = :pid AND own.`type` = "gear" AND wear.`uid` = :uid AND wear.`type` = "gear" AND NOT own.`catalogid` = wear.`catalogId` AND cat.`id` = own.`catalogid`;'); + $stmt->bindParam(':pid', $uid, PDO::PARAM_INT); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + + if($stmt->rowCount() > 0) + { + echo ';'; + } + + $count = 0; + foreach($stmt as $result) { + if ($count !== $stmt->rowCount()-1) { + echo 'http://gtoria.net/data/assets/gear/models/'.$result['datafile'].';'; + }else{ + echo 'http://gtoria.net/data/assets/gear/models/'.$result['datafile']; + } + $count++; + } + } + + $dbcon = null; +?> \ No newline at end of file diff --git a/api/user/getcolors.php b/api/user/getcolors.php new file mode 100644 index 0000000..d2001b9 --- /dev/null +++ b/api/user/getcolors.php @@ -0,0 +1,124 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + $stmt = $dbcon->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='head';"); + $stmt->bindParam(':id', $uid, PDO::PARAM_INT); + $stmt->execute(); + $head = $stmt->fetch(PDO::FETCH_ASSOC); + $headColor = true; + if ($stmt->rowCount() == 0) { + $headColor = false; + } + + $stmt = $dbcon->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='torso';"); + $stmt->bindParam(':id', $uid, PDO::PARAM_INT); + $stmt->execute(); + $torso = $stmt->fetch(PDO::FETCH_ASSOC); + $torsoColor = true; + if ($stmt->rowCount() == 0) { + $torsoColor = false; + } + + $stmt = $dbcon->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='rightarm';"); + $stmt->bindParam(':id', $uid, PDO::PARAM_INT); + $stmt->execute(); + $rightarm = $stmt->fetch(PDO::FETCH_ASSOC); + $rightarmColor = true; + if ($stmt->rowCount() == 0) { + $rightarmColor = false; + } + + $stmt = $dbcon->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='rightleg';"); + $stmt->bindParam(':id', $uid, PDO::PARAM_INT); + $stmt->execute(); + $rightleg = $stmt->fetch(PDO::FETCH_ASSOC); + $rightlegColor = true; + if ($stmt->rowCount() == 0) { + $rightlegColor = false; + } + + $stmt = $dbcon->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='leftarm';"); + $stmt->bindParam(':id', $uid, PDO::PARAM_INT); + $stmt->execute(); + $leftarm = $stmt->fetch(PDO::FETCH_ASSOC); + $leftarmColor = true; + if ($stmt->rowCount() == 0) { + $leftarmColor = false; + } + + $stmt = $dbcon->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='leftleg';"); + $stmt->bindParam(':id', $uid, PDO::PARAM_INT); + $stmt->execute(); + $leftleg = $stmt->fetch(PDO::FETCH_ASSOC); + $leftlegColor = true; + if ($stmt->rowCount() == 0) { + $leftlegColor = false; + } +?> + + + null + nil + + + '.$head['color'].''; + }else{ + echo '1'; + } + if ($leftarmColor == true) { + echo ''.$leftarm['color'].''; + }else{ + echo '1'; + } + if ($leftlegColor == true) { + echo ''.$leftleg['color'].''; + }else{ + echo '21'; + } + if ($rightarmColor == true) { + echo ''.$rightarm['color'].''; + }else{ + echo '1'; + } + if ($rightlegColor == true) { + echo ''.$rightleg['color'].''; + }else{ + echo '21'; + } + if ($torsoColor == true) { + echo ''.$torso['color'].''; + }else{ + echo '26'; + } + ?> + true + + + + \ No newline at end of file diff --git a/api/verify/checkUser.php b/api/verify/checkUser.php new file mode 100644 index 0000000..0848544 --- /dev/null +++ b/api/verify/checkUser.php @@ -0,0 +1,32 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + // Validate everything + $stmt = $dbcon->prepare("SELECT id, banned FROM users WHERE username=:uid;"); + $stmt->bindParam(':uid', $username, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($stmt->rowCount() == 0) echo 'not-found'; + if ($result['banned'] > 0) echo 'banned'; + + $dbcon = null; +?> \ No newline at end of file diff --git a/api/verify/findCode.php b/api/verify/findCode.php new file mode 100644 index 0000000..bbbf53c --- /dev/null +++ b/api/verify/findCode.php @@ -0,0 +1,41 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + // Validate everything + $stmt = $dbcon->prepare("SELECT id, about FROM users WHERE username=:uid;"); + $stmt->bindParam(':uid', $username, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + // Check if code is found + if (strpos($result['about'], $code) !== false) { + echo 'found'; + }else{ + echo 'not-found'; + } + + $dbcon = null; +?> \ No newline at end of file diff --git a/assetgame/.htaccess b/assetgame/.htaccess new file mode 100644 index 0000000..c507c1c --- /dev/null +++ b/assetgame/.htaccess @@ -0,0 +1,6 @@ +RewriteEngine on +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{REQUEST_FILENAME}.php -f +RewriteRule ^(.*?)/?$ $1.php [L] + +RewriteRule ^asset(.*)$ https://gtoria.net/asset/$1 [R=301,NC,L] \ No newline at end of file diff --git a/assetgame/game/edit.php b/assetgame/game/edit.php new file mode 100644 index 0000000..a3247a4 --- /dev/null +++ b/assetgame/game/edit.php @@ -0,0 +1,117 @@ + + +-- Prepended to Edit.lua and Visit.lua and Studio.lua-- + +function ifSeleniumThenSetCookie(key, value) + if false then + game:GetService("CookiesService"):SetCookieValue(key, value) + end +end + +ifSeleniumThenSetCookie("SeleniumTest1", "Inside the visit lua script") + +pcall(function() game:SetPlaceID() end) + +visit = game:GetService("Visit") + +local message = Instance.new("Message") +message.Parent = workspace +message.archivable = false + +game:GetService("ScriptInformationProvider"):SetAssetUrl("http://assetgame.gtoria.net/Asset/") +game:GetService("ContentProvider"):SetThreadPool(16) +pcall(function() game:GetService("InsertService"):SetFreeModelUrl("http://assetgame.gtoria.net/Game/Tools/InsertAsset.ashx?type=fm&q=%s&pg=%d&rs=%d") end) -- Used for free model search (insert tool) +pcall(function() game:GetService("InsertService"):SetFreeDecalUrl("http://assetgame.gtoria.net/Game/Tools/InsertAsset.ashx?type=fd&q=%s&pg=%d&rs=%d") end) -- Used for free decal search (insert tool) + +ifSeleniumThenSetCookie("SeleniumTest2", "Set URL service") + +settings().Diagnostics:LegacyScriptMode() + +game:GetService("InsertService"):SetBaseSetsUrl("http://assetgame.gtoria.net/Game/Tools/InsertAsset.ashx?nsets=10&type=base") +game:GetService("InsertService"):SetUserSetsUrl("http://assetgame.gtoria.net/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d") +game:GetService("InsertService"):SetCollectionUrl("http://assetgame.gtoria.net/Game/Tools/InsertAsset.ashx?sid=%d") +game:GetService("InsertService"):SetAssetUrl("http://assetgame.gtoria.net/Asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl("http://assetgame.gtoria.net/Asset/?assetversionid=%d") + +pcall(function() game:GetService("SocialService"):SetFriendUrl("http://assetgame.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=IsFriendsWith&playerid=%d&userid=%d") end) +pcall(function() game:GetService("SocialService"):SetBestFriendUrl("http://assetgame.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=IsBestFriendsWith&playerid=%d&userid=%d") end) +pcall(function() game:GetService("SocialService"):SetGroupUrl("http://assetgame.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=IsInGroup&playerid=%d&groupid=%d") end) +pcall(function() game:GetService("SocialService"):SetGroupRankUrl("http://assetgame.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRank&playerid=%d&groupid=%d") end) +pcall(function() game:GetService("SocialService"):SetGroupRoleUrl("http://assetgame.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRole&playerid=%d&groupid=%d") end) +pcall(function() game:GetService("GamePassService"):SetPlayerHasPassUrl("http://assetgame.gtoria.net/Game/GamePass/GamePassHandler.ashx?Action=HasPass&UserID=%d&PassID=%d") end) +pcall(function() game:GetService("MarketplaceService"):SetProductInfoUrl("https://api.gtoria.net/marketplace/productinfo?assetId=%d") end) +pcall(function() game:GetService("MarketplaceService"):SetDevProductInfoUrl("https://api.gtoria.net/marketplace/productDetails?productId=%d") end) +pcall(function() game:GetService("MarketplaceService"):SetPlayerOwnsAssetUrl("https://api.gtoria.net/ownership/hasasset?userId=%d&assetId=%d") end) +pcall(function() game:SetCreatorID(, Enum.CreatorType.User) end) + +ifSeleniumThenSetCookie("SeleniumTest3", "Set creator ID") + +pcall(function() game:SetScreenshotInfo("Graphictoria+Place") end) +pcall(function() game:SetVideoInfo("") end) + +function registerPlay(key) + if true and game:GetService("CookiesService"):GetCookieValue(key) == "" then + game:GetService("CookiesService"):SetCookieValue(key, "{ \"userId\" : , \"placeId\" : , \"os\" : \"" .. settings().Diagnostics.OsPlatform .. "\"}") + end +end + +pcall(function() + registerPlay("rbx_evt_ftp") + delay(60*5, function() registerPlay("rbx_evt_fmp") end) +end) + +ifSeleniumThenSetCookie("SeleniumTest4", "Exiting SingleplayerSharedScript")-- SingleplayerSharedScript.lua inserted here -- + +message.Text = "Loading Place. Please wait..." +coroutine.yield() +game:Load("") + +if #"" > 0 then + visit:SetUploadUrl("") +end + +message.Parent = nil + +game:GetService("ChangeHistoryService"):SetEnabled(true) + +visit:SetPing("http://assetgame.gtoria.net/Game/ClientPresence.ashx?version=old&PlaceID=&LocationType=Studio", 120) +game:HttpGet("http://assetgame.gtoria.net/Game/Statistics.ashx?UserID=&AssociatedCreatorID=&AssociatedCreatorType=User&AssociatedPlaceID=") + + \ No newline at end of file diff --git a/assetgame/game/gameserver.php b/assetgame/game/gameserver.php new file mode 100644 index 0000000..2b25e23 --- /dev/null +++ b/assetgame/game/gameserver.php @@ -0,0 +1,186 @@ + + +-- Start Game Script Arguments +local placeId, port, gameId, sleeptime, access, deprecated, timeout, machineAddress, gsmInterval, baseUrl, maxPlayers, maxGameInstances, injectScriptAssetID, apiKey, libraryRegistrationScriptAssetID, deprecated_pingTimesReportInterval, gameCode, universeId, preferredPlayerCapacity, matchmakingContextId, placeVisitAccessKey, assetGameSubdomain, protocol = ... + +-----------------------------------"CUSTOM" SHARED CODE---------------------------------- + +pcall(function() settings().Network.UseInstancePacketCache = true end) +pcall(function() settings().Network.UsePhysicsPacketCache = true end) +pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end) + + +settings().Network.PhysicsSend = Enum.PhysicsSendMethod.TopNErrors +settings().Network.ExperimentalPhysicsEnabled = true +settings().Network.WaitingForCharacterLogRate = 100 +pcall(function() settings().Diagnostics:LegacyScriptMode() end) + +-----------------------------------START GAME SHARED SCRIPT------------------------------ + +local assetId = placeId -- might be able to remove this now +local url = nil +local assetGameUrl = nil +if baseUrl~=nil and protocol ~= nil then + url = protocol .. "www." .. baseUrl --baseUrl is actually the domain, no leading . + assetGameUrl = protocol .. assetGameSubdomain .. "." .. baseUrl +end + +local scriptContext = game:GetService('ScriptContext') +pcall(function() scriptContext:AddStarterScript(libraryRegistrationScriptAssetID) end) +scriptContext.ScriptsDisabled = true + +game:SetPlaceID(assetId, false) +pcall(function () if universeId ~= nil then game:SetUniverseId(universeId) end end) +game:GetService("ChangeHistoryService"):SetEnabled(false) + +-- establish this peer as the Server +local ns = game:GetService("NetworkServer") +-- Detect cloud edit mode by checking for the dedicated cloud edit matchmaking context +local isCloudEdit = matchmakingContextId == 3 +if isCloudEdit then + print("Configuring as cloud edit server!") + game:SetServerSaveUrl(url .. "/ide/publish/UploadFromCloudEdit") + ns:ConfigureAsCloudEditServer() +end + +local badgeUrlFlagExists, badgeUrlFlagValue = pcall(function () return settings():GetFFlag("NewBadgeServiceUrlEnabled") end) +local newBadgeUrlEnabled = badgeUrlFlagExists and badgeUrlFlagValue +if url~=nil then + local apiProxyUrl = "https://api." .. baseUrl -- baseUrl is really the domain + + pcall(function() game:GetService("Players"):SetAbuseReportUrl(url .. "/AbuseReport/InGameChatHandler") end) + pcall(function() game:GetService("ScriptInformationProvider"):SetAssetUrl(assetGameUrl .. "/Asset/") end) + pcall(function() game:GetService("ContentProvider"):SetBaseUrl(url .. "/") end) + pcall(function() game:GetService("Players"):SetChatFilterUrl(assetGameUrl .. "/Game/ChatFilter") end) + + if gameCode then + game:SetVIPServerId(tostring(gameCode)) + end + + game:GetService("BadgeService"):SetPlaceId(placeId) + + if newBadgeUrlEnabled then + game:GetService("BadgeService"):SetAwardBadgeUrl(apiProxyUrl .. "/assets/award-badge?userId=%d&badgeId=%d&placeId=%d") + end + + if access ~= nil then + if not newBadgeUrlEnabled then + game:GetService("BadgeService"):SetAwardBadgeUrl(assetGameUrl .. "/Game/Badge/AwardBadge?UserID=%d&BadgeID=%d&PlaceID=%d") + end + + game:GetService("BadgeService"):SetHasBadgeUrl(assetGameUrl .. "/Game/Badge/HasBadge?UserID=%d&BadgeID=%d") + game:GetService("BadgeService"):SetIsBadgeDisabledUrl(assetGameUrl .. "/Game/Badge/IsBadgeDisabled?BadgeID=%d&PlaceID=%d") + + game:GetService("FriendService"):SetMakeFriendUrl(assetGameUrl .. "/Game/CreateFriend?firstUserId=%d&secondUserId=%d") + game:GetService("FriendService"):SetBreakFriendUrl(assetGameUrl .. "/Game/BreakFriend?firstUserId=%d&secondUserId=%d") + game:GetService("FriendService"):SetGetFriendsUrl(assetGameUrl .. "/Game/AreFriends?userId=%d") + end + game:GetService("BadgeService"):SetIsBadgeLegalUrl("") + game:GetService("InsertService"):SetBaseSetsUrl(assetGameUrl .. "/Game/Tools/InsertAsset?nsets=10&type=base") + game:GetService("InsertService"):SetUserSetsUrl(assetGameUrl .. "/Game/Tools/InsertAsset?nsets=20&type=user&userid=%d") + game:GetService("InsertService"):SetCollectionUrl(assetGameUrl .. "/Game/Tools/InsertAsset?sid=%d") + game:GetService("InsertService"):SetAssetUrl(assetGameUrl .. "/Asset/?id=%d") + game:GetService("InsertService"):SetAssetVersionUrl(assetGameUrl .. "/Asset/?assetversionid=%d") + + if gameCode then + pcall(function() loadfile(assetGameUrl .. "/Game/LoadPlaceInfo?PlaceId=" .. placeId .. "&gameCode=" .. tostring(gameCode))() end) + else + pcall(function() loadfile(assetGameUrl .. "/Game/LoadPlaceInfo?PlaceId=" .. placeId)() end) + end + + pcall(function() + if access then + loadfile(assetGameUrl .. "/Game/PlaceSpecificScript?PlaceId=" .. placeId)() + end + end) +end + +pcall(function() game:GetService("NetworkServer"):SetIsPlayerAuthenticationRequired(true) end) +settings().Diagnostics.LuaRamLimit = 0 + +game:GetService("Players").PlayerAdded:connect(function(player) + print("Player " .. player.userId .. " added") + + if assetGameUrl and access and placeId and player and player.userId then + local didTeleportIn = "False" + if player.TeleportedIn then didTeleportIn = "True" end + + game:HttpGet(assetGameUrl .. "/Game/ClientPresence?action=connect&PlaceID=" .. placeId .. "&UserID=" .. player.userId) + if not isCloudEdit then + game:HttpPost(assetGameUrl .. "/Game/PlaceVisit?UserID=" .. player.userId .. "&AssociatedPlaceID=" .. placeId .. "&placeVisitAccessKey=" .. placeVisitAccessKey .. "&IsTeleport=" .. didTeleportIn, "") + end + end +end) + +game:GetService("Players").PlayerRemoving:connect(function(player) + print("Player " .. player.userId .. " leaving") + + local isTeleportingOut = "False" + if player.Teleported then isTeleportingOut = "True" end + + if assetGameUrl and access and placeId and player and player.userId then + game:HttpGet(assetGameUrl .. "/Game/ClientPresence?action=disconnect&PlaceID=" .. placeId .. "&UserID=" .. player.userId .. "&IsTeleport=" .. isTeleportingOut) + end +end) + +local onlyCallGameLoadWhenInRccWithAccessKey = newBadgeUrlEnabled +if placeId ~= nil and assetGameUrl ~= nil and ((not onlyCallGameLoadWhenInRccWithAccessKey) or access ~= nil) then + -- yield so that file load happens in the heartbeat thread + wait() + + -- load the game + game:Load(assetGameUrl .. "/asset/?id=" .. placeId) +end + +-- Configure CloudEdit saving after place has been loaded +if isCloudEdit then + local doPeriodicSaves = true + local delayBetweenSavesSeconds = 5 * 60 -- 5 minutes + local function periodicSave() + if doPeriodicSaves then + game:ServerSave() + delay(delayBetweenSavesSeconds, periodicSave) + end + end + -- Spawn thread to save in the future + delay(delayBetweenSavesSeconds, periodicSave) + -- Hook into OnClose to save on shutdown + game.OnClose = function() + doPeriodicSaves = false + game:ServerSave() + end +end + +-- Now start the connection +ns:Start(port, sleeptime) + +if timeout then + scriptContext:SetTimeout(timeout) +end +scriptContext.ScriptsDisabled = false + + +-- StartGame -- +if not isCloudEdit then + if injectScriptAssetID and (injectScriptAssetID < 0) then + pcall(function() Game:LoadGame(injectScriptAssetID * -1) end) + else + pcall(function() Game:GetService("ScriptContext"):AddStarterScript(injectScriptAssetID) end) + end + + Game:GetService("RunService"):Run() +end + + \ No newline at end of file diff --git a/assetgame/game/getcurrentuser.php b/assetgame/game/getcurrentuser.php new file mode 100644 index 0000000..77116da --- /dev/null +++ b/assetgame/game/getcurrentuser.php @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/assetgame/game/join.php b/assetgame/game/join.php new file mode 100644 index 0000000..b9825c1 --- /dev/null +++ b/assetgame/game/join.php @@ -0,0 +1,84 @@ + 0, + "MachineAddress" => "localhost", + "ServerPort" => 53640, + "PingUrl" => "", + "PingInterval" => 120, + "UserName" => $username, + "SeleniumTestMode" => false, + "UserId" => $userId, + "SuperSafeChat" => $userId != $charappId, + "CharacterAppearance" => "http://api.gtoria.net/user/getCharacter.php?uid=" . $charappId . "&key=D869593BF742A42F79915993EF1DB", + /* Timestamp(n/j/Y h:i:s A);Signature(UserId\nJobId\nTimestamp) */ + "ClientTicket" => makeTicket($key, $userId, $jobId), + "GameId" => "00000000-0000-0000-0000-000000000000", + "PlaceId" => 0, + "MeasurementUrl" => "", + "WaitingForCharacterGuid" => makeGuid(), + "BaseUrl" => "http://www.gtoria.net/", + "ChatStyle" => "ClassicAndBubble", + "VendorId" => 0, + "ScreenShotInfo" => "", + "VideoInfo" => "GamesGraphictoria, video, free game, online virtual world", + "CreatorId" => 0, + "CreatorTypeEnum" => "User", + "MembershipType" => "None", + "AccountAge" => 0, + "CookieStoreFirstTimePlayKey" => "gt_evt_ftp", + "CookieStoreFiveMinutePlayKey" => "gt_evt_fmp", + "CookieStoreEnabled" => true, + "IsGraphictoriaPlace" => false, + "GenerateTeleportJoin" => false, + "IsUnknownOrUnder13" => $userId != $charappId, + /* SESSION_ID|GAME_ID|PLACE_ID|CLIENT_IP|PLATFORM_TYPE_ID|SESSION_STARTED|BROWSER_TRACKER_ID|PARTY_ID|AGE */ + "SessionId" => makeGuid() . '|00000000-0000-0000-0000-000000000000|0|' . auth::getIP() . '|5|' . gmdate('Y-m-d\TH:i:s\Z') . '|0|null|null', + "DataCenterId" => 0, + "UniverseId" => 0, + "BrowserTrackerId" => 0, + "UsePortraitMode" => false, + "FollowUserId" => 0, + "characterAppearanceId" => $charappId + ], + JSON_UNESCAPED_SLASHES + ); + $signature; + $data = "\r\n" . $data; + openssl_sign($data, $signature, $key, OPENSSL_ALGO_SHA1); + exit(sprintf("%s%%%s%%%s", "--rbxsig", base64_encode($signature), $data)); +?> \ No newline at end of file diff --git a/assetgame/game/visit.php b/assetgame/game/visit.php new file mode 100644 index 0000000..d1691c9 --- /dev/null +++ b/assetgame/game/visit.php @@ -0,0 +1,182 @@ + + +-- Prepended to Edit.lua and Visit.lua and Studio.lua and PlaySolo.lua-- + +function ifSeleniumThenSetCookie(key, value) + if false then + game:GetService("CookiesService"):SetCookieValue(key, value) + end +end + +ifSeleniumThenSetCookie("SeleniumTest1", "Inside the visit lua script") + +pcall(function() game:SetPlaceID(0) end) +pcall(function() game:SetUniverseId(0) end) + +visit = game:GetService("Visit") + +local message = Instance.new("Message") +message.Parent = workspace +message.archivable = false + +game:GetService("ScriptInformationProvider"):SetAssetUrl("http://assetgame.gtoria.net/Asset/") +game:GetService("ContentProvider"):SetThreadPool(16) +pcall(function() game:GetService("InsertService"):SetFreeModelUrl("http://assetgame.gtoria.net/Game/Tools/InsertAsset.ashx?type=fm&q=%s&pg=%d&rs=%d") end) -- Used for free model search (insert tool) +pcall(function() game:GetService("InsertService"):SetFreeDecalUrl("http://assetgame.gtoria.net/Game/Tools/InsertAsset.ashx?type=fd&q=%s&pg=%d&rs=%d") end) -- Used for free decal search (insert tool) + +ifSeleniumThenSetCookie("SeleniumTest2", "Set URL service") + +settings().Diagnostics:LegacyScriptMode() + +game:GetService("InsertService"):SetBaseSetsUrl("http://assetgame.gtoria.net/Game/Tools/InsertAsset.ashx?nsets=10&type=base") +game:GetService("InsertService"):SetUserSetsUrl("http://assetgame.gtoria.net/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d") +game:GetService("InsertService"):SetCollectionUrl("http://assetgame.gtoria.net/Game/Tools/InsertAsset.ashx?sid=%d") +game:GetService("InsertService"):SetAssetUrl("http://assetgame.gtoria.net/Asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl("http://assetgame.gtoria.net/Asset/?assetversionid=%d") + +pcall(function() game:GetService("SocialService"):SetFriendUrl("http://assetgame.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=IsFriendsWith&playerid=%d&userid=%d") end) +pcall(function() game:GetService("SocialService"):SetBestFriendUrl("http://assetgame.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=IsBestFriendsWith&playerid=%d&userid=%d") end) +pcall(function() game:GetService("SocialService"):SetGroupUrl("http://assetgame.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=IsInGroup&playerid=%d&groupid=%d") end) +pcall(function() game:GetService("SocialService"):SetGroupRankUrl("http://assetgame.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRank&playerid=%d&groupid=%d") end) +pcall(function() game:GetService("SocialService"):SetGroupRoleUrl("http://assetgame.gtoria.net/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRole&playerid=%d&groupid=%d") end) +pcall(function() game:GetService("GamePassService"):SetPlayerHasPassUrl("http://assetgame.gtoria.net/Game/GamePass/GamePassHandler.ashx?Action=HasPass&UserID=%d&PassID=%d") end) +pcall(function() game:GetService("MarketplaceService"):SetProductInfoUrl("https://api.gtoria.net/marketplace/productinfo?assetId=%d") end) +pcall(function() game:GetService("MarketplaceService"):SetDevProductInfoUrl("https://api.gtoria.net/marketplace/productDetails?productId=%d") end) +pcall(function() game:GetService("MarketplaceService"):SetPlayerOwnsAssetUrl("https://api.gtoria.net/ownership/hasasset?userId=%d&assetId=%d") end) +pcall(function() game:SetCreatorID(, Enum.CreatorType.User) end) + +ifSeleniumThenSetCookie("SeleniumTest3", "Set creator ID") + +pcall(function() game:SetScreenshotInfo("") end) +pcall(function() game:SetVideoInfo("") end) + +function registerPlay(key) + if true and game:GetService("CookiesService"):GetCookieValue(key) == "" then + game:GetService("CookiesService"):SetCookieValue(key, "{ \"userId\" : , \"placeId\" : 0, \"os\" : \"" .. settings().Diagnostics.OsPlatform .. "\"}") + end +end + +pcall(function() + registerPlay("rbx_evt_ftp") + delay(60*5, function() registerPlay("rbx_evt_fmp") end) +end) + +ifSeleniumThenSetCookie("SeleniumTest4", "Exiting SingleplayerSharedScript")-- SingleplayerSharedScript.lua inserted here -- + +pcall(function() settings().Rendering.EnableFRM = true end) +pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end) + +game:GetService("ChangeHistoryService"):SetEnabled(false) +pcall(function() game:GetService("Players"):SetBuildUserPermissionsUrl("http://assetgame.gtoria.net//Game/BuildActionPermissionCheck.ashx?assetId=0&userId=%d&isSolo=true") end) + +workspace:SetPhysicsThrottleEnabled(true) + +local addedBuildTools = false +local screenGui = game:GetService("CoreGui"):FindFirstChild("RobloxGui") + +local inStudio = false or false + +function doVisit() + message.Text = "Loading Game" + if false then + if false then + success, err = pcall(function() game:Load("") end) + if not success then + message.Text = "Could not teleport" + return + end + end + else + if false then + game:Load("") + pcall(function() visit:SetUploadUrl("") end) + else + pcall(function() visit:SetUploadUrl("") end) + end + end + + message.Text = "Running" + game:GetService("RunService"):Run() + + message.Text = "Creating Player" + player = game:GetService("Players"):CreateLocalPlayer() + --player.Name = [====[]====] + + player.CharacterAppearance = "http://api.gtoria.net/user/getCharacter.php?uid=&key=D869593BF742A42F79915993EF1DB" + local propExists, canAutoLoadChar = false + propExists = pcall(function() canAutoLoadChar = game.Players.CharacterAutoLoads end) + + if (propExists and canAutoLoadChar) or (not propExists) then + player:LoadCharacter() + end + + message.Text = "Setting GUI" + player:SetSuperSafeChat(false) + pcall(function() player:SetUnder13(False) end) + pcall(function() player:SetMembershipType(None) end) + pcall(function() player:SetAccountAge(0) end) + + if not inStudio and false then + message.Text = "Setting Ping" + visit:SetPing("http://assetgame.gtoria.net/Game/ClientPresence.ashx?version=old&PlaceID=0", 120) + + message.Text = "Sending Stats" + game:HttpGet("") + end + +end + +success, err = pcall(doVisit) + +if not inStudio and not addedBuildTools then + local playerName = Instance.new("StringValue") + playerName.Name = "PlayerName" + playerName.Value = player.Name + playerName.RobloxLocked = true + playerName.Parent = screenGui + + pcall(function() game:GetService("ScriptContext"):AddCoreScript(59431535,screenGui,"BuildToolsScript") end) + addedBuildTools = true +end + +if success then + message.Parent = nil +else + print(err) + if not inStudio then + if false then + pcall(function() visit:SetUploadUrl("") end) + end + end + wait(5) + message.Text = "Error on visit: " .. err + if not inStudio then + if false then + game:HttpPost("https://data.gtoria.net/Error/Lua.ashx", "Visit.lua: " .. err) + end + end +end + \ No newline at end of file diff --git a/assetgame/login/negotiate.php b/assetgame/login/negotiate.php new file mode 100644 index 0000000..4f42d42 --- /dev/null +++ b/assetgame/login/negotiate.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/clientsettings/.htaccess b/clientsettings/.htaccess new file mode 100644 index 0000000..f29e404 --- /dev/null +++ b/clientsettings/.htaccess @@ -0,0 +1,8 @@ +RewriteEngine on +RewriteRule ^Setting/QuietGet/Arbiter/?$ /arbiter.php [NC,L] +RewriteRule ^Setting/QuietGet/ClientAppSettings/?$ /cappsettings.php [NC,L] +RewriteRule ^Setting/QuietGet/ClientSharedSettings/?$ /cshared.php [NC,L] +RewriteRule ^Setting/QuietGet/RCCService6b87b04712da4fc95153b9b3ac0eaa40e98588f267955d71e183b26566ec8436218b2b1d458df64ef8c3573d167e200e1bcc52ced53485d832df9c21e6701900/?$ /pccservice.php [NC,L] +RewriteRule ^Setting/QuietGet/WindowsBootstrapperSettings/?$ /bootstrapper.php [NC,L] +RewriteRule ^Setting/QuietGet/WindowsStudioBootstrapperSettings/?$ /bootstrapper.php [NC,L] +ErrorDocument 404 /index.php \ No newline at end of file diff --git a/clientsettings/arbiter.php b/clientsettings/arbiter.php new file mode 100644 index 0000000..987c4e7 --- /dev/null +++ b/clientsettings/arbiter.php @@ -0,0 +1,8 @@ + +{ + "ThreadPoolConfig": "Threads16" +} \ No newline at end of file diff --git a/clientsettings/bootstrapper.php b/clientsettings/bootstrapper.php new file mode 100644 index 0000000..5de9d3a --- /dev/null +++ b/clientsettings/bootstrapper.php @@ -0,0 +1,35 @@ + +{ + "NeedInstallBgTask": "False", + "NeedRunBgTask": "False", + "IsPreDeployOn": "False", + "PreVersion": "", + "ForceSilentMode": "False", + "CountersLoad": "100", + "BetaPlayerLoad": "100", + "PerModeLoggingEnabled": "True", + "CountersFireImmediately": "True", + "ExeVersion": "0.1.0.0", + "GoogleAnalyticsAccountPropertyID": "disabled", + "ValidateInstalledExeVersion": "True", + "UseNewCdn": "True", + "UseNewVersionFetch": "False", + "CheckIsStudioOutofDate": "True", + "HttpBoostPostTimeout": "0", + "ShowInstallSuccessPrompt": "True", + "LogChromeProtocolFix": "False", + "InfluxUrl": "", + "InfluxDatabase": "", + "InfluxUser": "", + "InfluxPassword": "", + "InfluxHundredthsPercentage": "0", + "InfluxInstallHundredthsPercentage": "0", + "UseDataDomain": "True", + "CreateEdgeRegistry": "True", + "DeleteEdgeRegistry": "False", + "ReplaceCdnTxt": "True" +} \ No newline at end of file diff --git a/clientsettings/cappsettings.php b/clientsettings/cappsettings.php new file mode 100644 index 0000000..4ced22f --- /dev/null +++ b/clientsettings/cappsettings.php @@ -0,0 +1,1301 @@ +{ + "GoogleAnalyticsAccountPropertyID": "UA-197670272-3", + "GoogleAnalyticsAccountPropertyIDPlayer": "UA-197670272-2", + "AllowVideoPreRoll": "True", + "FLogAsserts": 0, + "FLogCloseDataModel": 3, + "CaptureQTStudioCountersEnabled": "True", + "CaptureMFCStudioCountersEnabled": "True", + "CaptureCountersIntervalInMinutes": 5, + "FLogServiceVectorResize": 4, + "FLogServiceCreation": 4, + "AxisAdornmentGrabSize": 12, + "FFlagProcessAllPacketsPerStep": "True", + "FFlagUS14116": "True", + "FFlagBlockBlockNarrowPhaseRefactor": "True", + "FFlagEnableRubberBandSelection": "True", + "FFlagQtStudioScreenshotEnabled": "True", + "FFlagFixNoPhysicsGlitchWithGyro": "True", + "FLogFullRenderObjects": 0, + "PublishedProjectsPageHeight": 535, + "PublishedProjectsPageUrl": "/ide/publish", + "StartPageUrl": "/ide/welcome", + "FFlagOpenNewWindowsInDefaultBrowser": "True", + "FFlagOnScreenProfiler": "True", + "FFlagInitializeNewPlace": "True", + "PrizeAssetIDs": "0=110718551=Congratulations! Your success at publishing a place has earned you the Eggcellent Builder hat. You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!;1=110719580=Congratulations! Your exploration of ROBLOX Studio's models has led you to the Eggstreme Builder hat. You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!;2=110720049=Congratulations! Your contribution to ROBLOX's content catalog has earned you the Eggsultant Contributor hat. You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!;3=110790044=Congratulations! Your large experience in scripting has earned you the rare Eggscruciatingly Deviled Scripter hat. You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!;4=110790072=Congratulations! Your flexibility at finding eggs has led you to the Yolk's on Us hat! You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!;5=110790103=Congratulations! You've found the Eggsplosion hat! You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!", + "PrizeAwarderURL": "/ostara/boon", + "MinNumberScriptExecutionsToGetPrize": 500, + "FFlagDebugCrashEnabled": "False", + "FLogHangDetection": 3, + "FFlagCharAnimationStats": "False", + "FFlagRenderOpenGLForcePOTTextures": "True", + "FFlagUseNewCameraZoomPath": "True", + "FFlagQTStudioPublishFailure": "True", + "ExcludeContactWithInteriorTerrainMinusYFace": "True", + "FFlagFixUphillClimb": "True", + "FFlagUseAveragedFloorHeight": "True", + "FFlagScaleExplosionLifetime": "True", + "PublishedProjectsPageWidth": 950, + "FFlagRenderFastClusterEverywhere": "True", + "FLogPlayerShutdownLuaTimeoutSeconds": 1, + "FFlagQtFixToolDragging": "True", + "FFlagSelectPartOnUndoRedo": "True", + "FFlagStatusBarProgress": "True", + "FFlagStudioCheckForUpgrade": "True", + "FFlagStudioInsertModelCounterEnabled": "True", + "FFlagStudioAuthenticationFailureCounterEnabled": "True", + "FFlagRenderCheckTextureContentProvider": "True", + "FFlagRenderLightGridEnabled": "True", + "FFlagStudioLightGridAPIVisible": "True", + "FFlagBetterSleepingJobErrorComputation": "True", + "FLogDXVideoMemory": 4, + "FFlagRenderNoLegacy": "True", + "FFlagStudioLightGridOnForNewPlaces": "True", + "FFlagPhysicsSkipRedundantJoinAll": "True", + "FFlagTerrainOptimizedLoad": "True", + "FFlagTerrainOptimizedStorage": "True", + "FFlagTerrainOptimizedCHS": "True", + "FFlagRenderGLES2": "True", + "FFlagStudioMacAddressValidationEnabled": "True", + "FFlagDoNotPassSunkEventsToPlayerMouse": "True", + "FFlagQtAutoSave": "True", + "FFlagRenderLoopExplicit": "True", + "FFlagStudioUseBinaryFormatForPlay": "True", + "FFlagPhysicsRemoveWorldAssemble_US16512": "True", + "FFlagNativeSafeChatRendering": "True", + "FFlagRenderNewMegaCluster": "True", + "FFlagAutoJumpForTouchDevices": "True", + "FLogOutlineBrightnessMin": 50, + "FLogOutlineBrightnessMax": 160, + "FLogOutlineThickness": 40, + "FFlagDE5511FixEnabled": "True", + "FFlagDE4423Fixed": "True", + "FFlagSymmetricContact": "True", + "FFlagLocalMD5": "True", + "FFlagStudioCookieParsingDisabled": "False", + "FFlagLastWakeTimeSleepingJobError": "True", + "FFlagPhysicsAllowAutoJointsWithSmallParts_DE6056": "True", + "FFlagPhysicsLockGroupDraggerHitPointOntoSurface_DE6174": "True", + "FFlagOutlineControlEnabled": "True", + "FFlagAllowCommentedScriptSigs": "True", + "FFlagDataModelUseBinaryFormatForSave": "True", + "FFlagStudioUseBinaryFormatForSave": "True", + "FFlagDebugAdornableCrash": "True", + "FFlagOverlayDataModelEnabled": "True", + "DFFlagFixInstanceParentDesyncBug": "True", + "FFlagPromoteAssemblyModifications": "False", + "FFlagFontSourceSans": "True", + "DFFlagCreateHumanoidRootNode": "True", + "FFlagRenderNewFonts": "True", + "FFlagStudioCookieDesegregation": "True", + "FFlagResponsiveJump": "True", + "FFlagGoogleAnalyticsTrackingEnabled": "True", + "FFlagNoCollideLadderFilter": "True", + "FFlagFlexibleTipping": "True", + "FFlagUseStrongerBalancer": "True", + "FFlagClampControllerVelocityMag": "True", + "DFFlagUseSaferChatMetadataLoading": "True", + "FFlagSinkActiveGuiObjectMouseEvents": "False", + "FLogLuaBridge": 2, + "DFFlagPromoteAssemblyModifications": "True", + "FFlagDeferredContacts": "True", + "FFlagFRMUse60FPSLockstepTable": "True", + "FFlagFRMAdjustForMultiCore": "True", + "FFlagPhysics60HZ": "True", + "FFlagQtRightClickContextMenu": "True", + "FFlagUseTopmostSettingToBringWindowToFront": "True", + "FFlagNewLightAPI": "True", + "FFlagRenderLightGridShadows": "True", + "FFlagRenderLightGridShadowsSmooth": "True", + "DFFlagSanitizeKeyframeUrl": "True", + "DFFlagDisableGetKeyframeSequence": "False", + "FFlagCreateServerScriptServiceInStudio": "True", + "FFlagCreateServerStorageInStudio": "True", + "FFlagCreateReplicatedStorageInStudio": "True", + "FFlagFilterEmoteChat": "True", + "DFFlagUseCharacterRootforCameraTarget": "True", + "FFlagImageRectEnabled": "True", + "FFlagNewWaterMaterialEnable": "True", + "DFFlagUserHttpAPIEnabled": "True", + "DFIntUserHttpAccessUserId0": 0, + "FFlagUserHttpAPIVisible": "True", + "FFlagCameraChangeHistory": "True", + "FFlagDE4640Fixed": "True", + "FFlagShowStreamingEnabledProp": "True", + "FFlagOptimizedDragger": "True", + "FFlagRenderNewMaterials": "True", + "FFlagRenderAnisotropy": "True", + "FFlagStudioInitializeViewOnPaint": "True", + "DFFlagPartsStreamingEnabled": "True", + "FFlagStudioLuaDebugger": "True", + "FFlagStudioLocalSpaceDragger": "True", + "FFlagGuiRotationEnabled": "True", + "FFlagDataStoreEnabled": "True", + "DFFlagDisableTeleportConfirmation": "True", + "DFFlagAllowTeleportFromServer": "True", + "DFFlagNonBlockingTeleport": "True", + "FFlagD3D9CrashOnError": "False", + "FFlagRibbonBarEnabled": "True", + "SFFlagInfiniteTerrain": "True", + "FFlagStudioScriptBlockAutocomplete": "True", + "FFlagRenderFixAnchoredLag": "True", + "DFFlagAllowAllUsersToUseHttpService": "True", + "GoogleAnalyticsAccountPropertyIDClient": "UA-197670272-2", + "FFlagSurfaceGuiVisible": "True", + "FFlagStudioIntellesenseEnabled": "True", + "FFlagAsyncPostMachineInfo": "True", + "FFlagModuleScriptsVisible": "True", + "FFlagModelPluginsEnabled": "True", + "FFlagGetUserIdFromPluginEnabled": "True", + "FFlagStudioPluginUIActionEnabled": "True", + "DFFlagRemoveAdornFromBucketInDtor": "True", + "FFlagRapidJSONEnabled": "True", + "DFFlagDE6959Fixed": "True", + "DFFlagScopedMutexOnJSONParser": "True", + "FFlagSupressNavOnTextBoxFocus": "True", + "DFFlagExplicitPostContentType": "True", + "DFFlagAddPlaceIdToAnimationRequests": "True", + "FFlagCreatePlaceEnabled": "True", + "DFFlagClientAdditionalPOSTHeaders": "True", + "FFlagEnableAnimationExport": "True", + "DFFlagAnimationAllowProdUrls": "False", + "FFlagGetUserIDFromPluginEnabled": "True", + "FFlagStudioContextualHelpEnabled": "True", + "FFlagLogServiceEnabled": "True", + "FFlagQtPlaySoloOptimization": "True", + "FFlagStudioBuildGui": "True", + "DFFlagListenForZVectorChanges": "True", + "DFFlagUserInputServiceProcessOnRender": "True", + "FFlagDE7421Fixed": "True", + "FFlagStudioExplorerActionsEnabledInScriptView": "True", + "FFlagHumanoidNetworkOptEnabled": "False", + "DFFlagEnableNPCServerAnimation": "True", + "DFFlagDataStoreUseUForGlobalDataStore": "True", + "DFFlagDataStoreAllowedForEveryone": "True", + "DFFlagBadTypeOnConnectErrorEnabled": "True", + "FFlagStudioRemoveUpdateUIThread": "True", + "FFlagPhysicsSkipUnnecessaryContactCreation": "False", + "FFlagUseNewHumanoidCache": "True", + "FFlagSecureReceiptsBackendEnabled": "True", + "FFlagOrderedDataStoreEnabled": "True", + "FFlagStudioLuaDebuggerGA": "True", + "FFlagNPSSetScriptDocsReadOnly": "True", + "FFlagRDBGHashStringComparison": "True", + "FFlagStudioDebuggerVisitDescendants": "True", + "FFlagDeprecateScriptInfoService": "True", + "FFlagIntellisenseScriptContextDatamodelSearchingEnabled": "True", + "FFlagSecureReceiptsFrontendEnabled": "True", + "DFFlagCreatePlaceEnabledForEveryone": "True", + "FFlagCreatePlaceInPlayerInventoryEnabled": "True", + "DFFlagAddRequestIdToDeveloperProductPurchases": "True", + "DFFlagUseYPCallInsteadOfPCallEnabled": "True", + "FFlagStudioMouseOffsetFixEnabled": "True", + "DFFlagPlaceValidation": "True", + "FFlagReconstructAssetUrl": "True", + "FFlagUseNewSoundEngine": "True", + "FIntMinMillisecondLengthForLongSoundChannel": 5000, + "FFlagStudioHideInsertedServices": "True", + "FFlagStudioAlwaysSetActionEnabledState": "True", + "FFlagRenderNew": "True", + "FIntRenderNewPercentWin": 100, + "FIntRenderNewPercentMac": 100, + "FLogGraphics": 6, + "FFlagStudioInSyncWebKitAuthentication": "False", + "DFFlagDisallowHopperServerScriptReplication": "True", + "FFlagInterpolationFix": "False", + "FFlagHeartbeatAt60Hz": "False", + "DFFlagFixProcessReceiptValueTypes": "True", + "DFFlagPhysicsSkipUnnecessaryContactCreation": "True", + "FFlagStudioLiveCoding": "True", + "FFlagPlayerHumanoidStep60Hz": "True", + "DFFlagCrispFilteringEnabled": "False", + "SFFlagProtocolSynchronization": "True", + "FFlagUserInputServicePipelineStudio": "True", + "FFlagUserInputServicePipelineWindowsClient": "True", + "FFlagUserInputServicePipelineMacClient": "True", + "FFlagStudioKeyboardMouseConfig": "True", + "DFFlagLogServiceEnabled": "True", + "DFFlagLoadAnimationsThroughInsertService": "True", + "FFlagFRMFogEnabled": "True", + "FLogBrowserActivity": 3, + "DFFlagPhysicsPacketAlwaysUseCurrentTime": "True", + "FFlagFixedStudioRotateTool": "True", + "FFlagRibbonBarEnabledGA": "True", + "FFlagRenderSafeChat": "False", + "DFFlagPhysicsAllowSimRadiusToDecreaseToOne": "True", + "DFFlagPhysicsAggressiveSimRadiusReduction": "True", + "DFFlagLuaYieldErrorNoResumeEnabled": "True", + "DFFlagEnableJointCache": "False", + "DFFlagOnCloseTimeoutEnabled": "True", + "FFlagStudioQuickInsertEnabled": "True", + "FFlagStudioPropertiesRespectCollisionToggle": "True", + "FFlagTweenServiceUsesRenderStep": "True", + "FFlagUseNewSoundEngine3dFix": "True", + "FFlagDebugUseDefaultGlobalSettings": "True", + "FFlagStudioMiddleMouseTrackCamera": "False", + "FFlagTurnOffiOSNativeControls": "True", + "DFFlagUseNewHumanoidHealthGui": "True", + "DFFlagLoggingConsoleEnabled": "True", + "DFFlagAllowModuleLoadingFromAssetId": "True", + "FFlagStudioZoomExtentsExplorerFixEnabled": "True", + "FFlagLuaDebuggerBreakOnError": "True", + "FFlagRetentionTrackingEnabled": "True", + "FFlagShowAlmostAllItemsInExplorer": "True", + "FFlagStudioFindInAllScriptsEnabled": "True", + "FFlagImprovedNameOcclusion": "True", + "FFlagHumanoidMoveToDefaultValueEnabled": "True", + "FFlagEnableDisplayDistances": "True", + "FFlagUseMinMaxZoomDistance": "True", + "SFFlagAllowPhysicsPacketCompression": "False", + "FFlagStudioOneClickColorPickerEnabled": "True", + "DFFlagHumanoidMoveToDefaultValueEnabled": "True", + "VideoPreRollWaitTimeSeconds": 45, + "FFlagBalancingRateLimit": "True", + "FFlagLadderCheckRate": "True", + "FFlagStateSpecificAutoJump": "True", + "SFFlagOneWaySimRadiusReplication": "True", + "DFFlagApiDictionaryCompression": "True", + "SFFlagPathBasedPartMovement": "True", + "FFlagEnsureInputIsCorrectState": "False", + "DFFlagLuaLoadStringStrictSecurity": "True", + "DFFlagCrossPacketCompression": "True", + "FFlagWorkspaceLoadStringEnabledHidden": "True", + "FFlagStudioPasteAsSiblingEnabled": "True", + "FFlagStudioDuplicateActionEnabled": "True", + "FFlagPreventInterpolationOnCFrameChange": "True", + "FLogNetworkPacketsReceive": 5, + "FFlagPlayPauseFix": "True", + "DFFlagCrashOnNetworkPacketError": "False", + "FFlagHumanoidStateInterfaces": "True", + "FFlagRenderDownloadAssets": "True", + "FFlagBreakOnErrorConfirmationDialog": "True", + "FFlagStudioAnalyticsEnabled": "True", + "FFlagAutoRotateFlag": "True", + "DFFlagUseImprovedLadderClimb": "True", + "FFlagUseCameraOffset": "True", + "FFlagRenderBlobShadows": "True", + "DFFlagWebParserDisableInstances": "False", + "FFlagStudioNewWiki": "True", + "DFFlagLogPacketErrorDetails": "False", + "FFlagLimitHorizontalDragForce": "True", + "FFlagEnableNonleathalExplosions": "True", + "DFFlagCreateSeatWeldOnServer": "True", + "FFlagGraphicsUseRetina": "True", + "FFlagDynamicEnvmapEnabled": "True", + "DFFlagDeferredTouchReplication": "True", + "DFFlagCreatePlayerGuiEarlier": "True", + "DFFlagProjectileOwnershipOptimization": "True", + "DFFlagLoadSourceForCoreScriptsBeforeInserting": "False", + "GoogleAnalyticsLoadStudio": 1, + "DFFlagTaskSchedulerFindJobOpt": "True", + "SFFlagPreventInterpolationOnCFrameChange": "True", + "DFIntNumPhysicsPacketsPerStep": 2, + "DFFlagDataStoreUrlEncodingEnabled": "True", + "FFlagShowWebPlaceNameOnTabWhenOpeningFromWeb": "True", + "DFFlagTrackTimesScriptLoadedFromLinkedSource": "True", + "FFlagToggleDevConsoleThroughChatCommandEnabled": "True", + "FFlagEnableFullMonitorsResolution": "True", + "DFFlagAlwaysUseHumanoidMass": "True", + "DFFlagUseStrongerGroundControl": "True", + "DFFlagCorrectlyReportSpeedOnRunStart": "True", + "FFlagLuaDebuggerImprovedToolTip": "True", + "FFlagLuaDebuggerPopulateFuncName": "True", + "FFlagLuaDebuggerNewCodeFlow": "True", + "DFFlagValidateCharacterAppearanceUrl": "False", + "FFlagStudioQuickAccessCustomization": "True", + "DFFlagTaskSchedulerUpdateJobPriorityOnWake": "True", + "DFFlagTaskSchedulerNotUpdateErrorOnPreStep": "True", + "FFlagWikiSelectionSearch": "True", + "DFIntTaskSchedularBatchErrorCalcFPS": 1200, + "FFlagSuppressNavOnTextBoxFocus": "False", + "FFlagEnabledMouseIconStack": "True", + "DFFlagFastClone": "True", + "DFFlagLuaNoTailCalls": "True", + "DFFlagFilterStreamingProps": "True", + "DFFlagNetworkOwnerOptEnabled": "True", + "DFFlagPathfindingEnabled": "True", + "FFlagEnableiOSSettingsLeave": "True", + "FFlagUseFollowCamera": "True", + "FFlagDefaultToFollowCameraOnTouch": "True", + "DFFlagAllowMoveToInMouseLookMove": "True", + "DFFlagAllowHumanoidDecalTransparency": "True", + "DFFlagSupportCsrfHeaders": "True", + "DFFlagConfigureInsertServiceFromSettings": "True", + "FFlagPathfindingClientComputeEnabled": "True", + "DFFlagLuaResumeSupportsCeeCalls": "True", + "DFFlagPhysicsSenderErrorCalcOpt": "True", + "DFFlagClearPlayerReceivingServerLogsOnLeave": "True", + "DFFlagConsoleCodeExecutionEnabled": "True", + "DFFlagCSGDictionaryReplication": "True", + "FFlagCSGToolsEnabled": "True", + "FFlagCSGDictionaryServiceEnabled": "True", + "FFlagCSGMeshRenderEnable": "True", + "FFlagCSGChangeHistory": "True", + "FFlagCSGMeshColorEnable": "True", + "FFlagCSGMeshColorToolsEnabled": "True", + "FFlagCSGScaleEnabled": "True", + "FFlagCSGPhysicsLevelOfDetailEnabled": "True", + "FFlagCylinderUsesConstantTessellation": "True", + "FFlagStudioDraggersScaleFixes": "True", + "FFlagCSGDecalsEnabled": "True", + "FFlagCSGMigrateChildData": "True", + "SFFlagBinaryStringReplicationFix": "True", + "FFlagHummanoidScaleEnable": "True", + "FFlagStudioDataModelIsStudioFix": "True", + "DFFlagWebParserEnforceASCIIEnabled": "True", + "DFFlagScriptDefaultSourceIsEmpty": "True", + "FFlagFixCaptureFocusInput": "True", + "FFlagFireUserInputServiceEventsAfterDMEvents": "True", + "FFlagVectorErrorOnNilArithmetic": "True", + "FFlagFontSmoothScalling": "True", + "DFFlagUseImageColor": "True", + "FFlagStopNoPhysicsStrafe": "True", + "DFFlagDebugLogNetworkErrorToDB": "False", + "FFlagLowQMaterialsEnable": "True", + "FFLagEnableFullMonitorsResolution": "True", + "FFlagStudioChildProcessCleanEnabled": "True", + "DFFlagAllowFullModelsWhenLoadingModules": "True", + "DFFlagRealWinInetHttpCacheBypassingEnabled": "True", + "FFlagNewUniverseInfoEndpointEnabled": "True", + "FFlagGameExplorerEnabled": "True", + "FFlagStudioUseBinaryFormatForModelPublish": "True", + "FFlagGraphicsFeatureLvlStatsEnable": "True", + "FFlagStudioEnableWebKitPlugins": "True", + "DFFlagSendHumanoidTouchedSignal": "True", + "DFFlagReduceHumanoidBounce": "True", + "DFFlagUseNewSounds": "True", + "FFlagFixHumanoidRootPartCollision": "True", + "FFlagEnableAndroidMenuLeave": "True", + "FFlagOnlyProcessGestureEventsWhenSunk": "True", + "FFlagAdServiceReportImpressions": "True", + "FFlagStudioUseExtendedHTTPTimeout": "True", + "FFlagStudioSeparateActionByActivationMethod": "False", + "DFFlagPhysicsSenderThrottleBasedOnBufferHealth": "True", + "DFFlagGetGroupInfoEnabled": "True", + "DFFlagGetGroupRelationsEnabled": "True", + "FStringPlaceFilter_StateBasedAnimationReplication": "True;175953385;174810327;181219650", + "SFFlagTopRepContSync": "True", + "FFlagStudioUseBinaryFormatForModelSave": "True", + "EnableFullMonitorsResolution": "True", + "DFFlagRenderSteppedServerExceptionEnabled": "True", + "FFlagUseWindowSizeFromGameSettings": "True", + "DFFlagCheckStudioApiAccess": "True", + "FFlagGameExplorerPublishEnabled": "True", + "DFFlagKeepXmlIdsBetweenLoads": "True", + "DFFlagReadXmlCDataEnabled": "True", + "FFlagStudioRemoveToolSounds": "True", + "FFlagStudioOneStudGridDefault": "True", + "FFlagStudioPartSymmetricByDefault": "True", + "FFlagStudioIncreasedBaseplateSize": "True", + "FFlagSkipSilentAudioOps": "True", + "SFFlagGuid64Bit": "False", + "FIntValidateLauncherPercent": 100, + "FFlagCSGDataLossFixEnabled": "True", + "DFStringRobloxAnalyticsURL": "http://ecsv2.gtoria.net/", + "DFFlagRobloxAnalyticsTrackingEnabled": "True", + "FFlagStudioOpenLastSaved": "False", + "FFlagStudioShowTutorialsByDefault": "True", + "FFlagStudioForceToolboxSize": "True", + "FFlagStudioExplorerDisabledByDefault": "True", + "FFlagStudioDefaultWidgetSizeChangesEnabled": "True", + "FFlagStudioUseScriptAnalyzer": "True", + "FFlagNoClimbPeople": "True", + "DFFlagAnimationFormatAssetId": "True", + "FFlagGetCorrectScreenResolutionFaster": "True", + "DFFlagFixTouchEndedReporting": "False", + "FFlagStudioTeleportPlaySolo": "True", + "FFlagCSGRemoveScriptScaleRestriction": "True", + "FFlagStudioDE7928FixEnabled": "True", + "FFlagDE8768FixEnabled": "True", + "FFlagStudioDE9108FixEnabled": "True", + "FFlagStudioPlaySoloMapDebuggerData": "True", + "FFlagLuaDebuggerCloneDebugger": "True", + "FFlagRenderLightgridInPerformEnable": "True", + "SFFlagStateBasedAnimationReplication": "True", + "FFlagVolumeControlInGameEnabled": "True", + "FFlagGameConfigurerUseStatsService": "True", + "FFlagStudioUseHttpAuthentication": "True", + "FFlagDetectTemplatesWhenSettingUpGameExplorerEnabled": "True", + "FFlagEntityNameEditingEnabled": "True", + "FFlagNewCreatePlaceFlowEnabled": "True", + "FFlagFakePlayableDevices": "False", + "FFlagMutePreRollSoundService": "True", + "DFFlagBodyMoverParentingFix": "True", + "DFFlagBroadcastServerOnAllInterfaces": "True", + "HttpUseCurlPercentageWinClient": 100, + "HttpUseCurlPercentageMacClient": 100, + "HttpUseCurlPercentageWinStudio": 100, + "HttpUseCurlPercentageMacStudio": 100, + "SFFlagReplicatedFirstEnabled": "True", + "DFFlagCSGShrinkForMargin": "True", + "FFlagPhysicsBulletConnectorPointRecalc": "True", + "DFIntBulletContactBreakThresholdPercent": 200, + "DFIntBulletContactBreakOrthogonalThresholdPercent": 200, + "FFlagPhysicsBulletConnectorMatching": "True", + "FFlagPhysicsBulletUseProximityMatching": "False", + "FFlagPhysicsCSGUsesBullet": "True", + "DFFlagCSGPhysicsDeserializeRefactor": "True", + "FFlagWedgeEnableDecalOnTop": "True", + "FFlagFrustumTestGUI": "True", + "FFlagFeatureLvlsDX11BeforeDeviceCreate": "True", + "FFlagStudioPasteSyncEnabled": "True", + "FFlagResetMouseCursorOnToolUnequip": "True", + "DFFlagUpdateCameraTarget": "True", + "DFFlagFixGhostClimb": "True", + "DFFlagUseStarterPlayer": "True", + "FFlagStudioFindCrashFixEnabled": "True", + "FFlagFixPartOffset": "True", + "DFFlagLuaCloseUpvalues": "True", + "FFlagRenderTextureCompositorUseBudgetForSize": "True", + "FFlagAllowOutOfBoxAssets": "False", + "FFlagRemoveTintingWhenActiveIsFalseOnImageButton": "True", + "FFlagStudioModuleScriptDefaultContents": "True", + "FFlagStudioHomeKeyChangeEnabled": "True", + "FFlagStudioOpenStartPageForLogin": "True", + "FFlagStudioNativeKeepSavedChanges": "True", + "FFlagSerializeCurrentlyOpenPlaceWhenPublishingGame": "True", + "FFlagGameNameLabelEnabled": "True", + "FFlagStudioValidateBootstrapper": "True", + "FFlagRakNetReadFast": "True", + "DFFlagPhysicsSenderSleepingUpdate": "True", + "FFlagUseShortShingles": "True", + "FFlagKKTChecks": "False", + "DFFlagUseApiProxyThrottling": "True", + "DFFlagValidateSetCharacter": "True", + "DFFlagUpdateHumanoidSimBodyInComputeForce": "True", + "DFFlagNetworkPendingItemsPreserveTimestamp": "True", + "FFlagStudioCSGRotationalFix": "True", + "FFlagNewLoadingScreen": "True", + "FFlagScrollingFrameOverridesButtonsOnTouch": "True", + "DFFlagStreamLargeAudioFiles": "True", + "DFFlagNewLuaChatScript": "True", + "DFFlagLoopedDefaultHumanoidAnimation": "True", + "FFlagSoundsRespectDelayedStop": "False", + "DFFlagCSGPhysicsErrorCatchingEnabled": "True", + "DFFlagFireStoppedAnimSignal": "True", + "FFlagStudioFixToolboxReload": "True", + "FFlagCSGDecalsV2": "True", + "FFlagLocalOptimizer": "True", + "DFFlagClearFailedUrlsWhenClearingCacheEnabled": "True", + "DFFlagSupportNamedAssetsShortcutUrl": "True", + "DFFlagUseW3CURIParser": "True", + "DFFlagContentProviderHttpCaching": "True", + "FFlagNoWallClimb": "False", + "FFlagSmoothMouseLock": "False", + "DFFlagCSGPhysicsNanPrevention": "True", + "FFlagStudioDE9818FixEnabled": "True", + "FFlagGameExplorerImagesEnabled": "True", + "FFlagStudioInsertOrientationFix": "True", + "FFlagStudioTabOrderingEnabled": "True", + "FFlagFramerateDeviationDroppedReport": "True", + "FFlagModuleScriptsPerVmEnabled": "False", + "FFlagGameExplorerImagesInsertEnabled": "True", + "FFlagTexturePropertyWidgetEnabled": "True", + "FFlagReloadAllImagesOnDataReload": "True", + "FFlagModuleScriptsPerVmEnabledFix2": "True", + "DFFlagFixBufferZoneContainsCheck": "False", + "FFlagStudioPlaceAssetFromToolbox": "True", + "FFlagChannelMasterMuting": "True", + "FFlagStudioUseDelayedSyntaxCheck": "True", + "FFlagStudioCommandLineSaveEditText": "True", + "FFlagStudioAddHelpInContextMenu": "True", + "DFIntHttpCacheCleanMinFilesRequired": 3000, + "DFIntHttpCacheCleanMaxFilesToKeep": 1500, + "FFlagCSGVoxelizer": "True", + "DFFlagCheckApiAccessInTransactionProcessing": "True", + "FFlagBindPurchaseValidateCallbackInMarketplaceService": "True", + "FFlagSetDataModelUniverseIdAfterPublishing": "True", + "FFlagOpenScriptWorksOnModulesEnabled": "True", + "FFlagStudioRibbonBarNewLayout": "True", + "FFlagStudioRibbonBarLayoutFixes": "True", + "FFlagStudioPlaceOnlineIndicator": "True", + "FFlagRenderWangTiles": "True", + "FFlagDisableBadUrl": "True", + "FFlagPrimalSolverLogBarrierIP": "True", + "FFlagDualSolverSimplex": "True", + "FFlagPrimalSolverSimplex": "True", + "FIntNumSmoothingPasses": 3, + "FFlagVerifyConnection": "True", + "FIntRegLambda": 1400, + "FFlagScriptAnalyzerPlaceholder": "True", + "FFlagStudioCSGAssets": "True", + "FFlagCSGStripPublishedData": "True", + "DFFlagRaycastReturnSurfaceNormal": "True", + "FFlagMoveGameExplorerActionsIntoContextMenu": "True", + "FFlagStudioAdvanceCookieExpirationBugFixEnabled": "True", + "FFlagNewBackpackScript": "True", + "FFlagNewPlayerListScript": "True", + "FFlagGameNameAtTopOfExplorer": "True", + "FFlagStudioActActionsAsTools": "True", + "FFlagStudioInsertAtMouseClick": "True", + "FFlagStopLoadingStockSounds": "True", + "DFFlagFixTimePositionReplication": "True", + "DFFlagHttpReportStatistics": "True", + "DFFlagEnableChatTestingInStudio": "True", + "DFIntHttpSendStatsEveryXSeconds": 300, + "FLogStepAnimatedJoints": 5, + "DFFlagLuaDisconnectFailingSlots": "False", + "DFFlagEnsureSoundPosIsUpdated": "True", + "DFFlagLoadStarterGearEarlier": "False", + "DFFlagBlockUsersInLuaChat": "True", + "FFlagRibbonPartInsertNotAllowedInModel": "True", + "DFFlagUsePlayerScripts": "True", + "DFFlagUserAccessUserSettings": "True", + "DFFlagUseLuaCameraAndControl": "True", + "DFFlagUseLuaPCInput": "True", + "DFFlagFixLuaMoveDirection": "True", + "DFFlagUseDecalLocalTransparencyModifier": "True", + "DFFlagUseFolder": "True", + "DFFlagUsePreferredSpawnInPlaySoloTeleport": "True", + "DFFlagFilterAddSelectionToSameDataModel": "False", + "FFlagGameExplorerAutofillImageNameFromFileName": "True", + "FFlagGameExplorerBulkImageUpload": "True", + "FFlagStudioAllowAudioSettings": "True", + "DFFlagUsePlayerInGroupLuaChat": "True", + "FFlagStudioDecalPasteFix": "True", + "FFlagStudioCtrlTabDocSwitchEnabled": "True", + "DFIntDraggerMaxMovePercent": 60, + "FFlagUseUniverseGetInfoCallToDetermineUniverseAccess": "True", + "FFlagMaxFriendsCount": "True", + "DFIntPercentApiRequestsRecordGoogleAnalytics": 0, + "FFlagSelectSpinlock": "True", + "FFlagFastZlibPath": "True", + "DFFlagWriteXmlCDataEnabled": "True", + "DFFlagUseSpawnPointOrientation": "True", + "DFFlagUsePlayerSpawnPoint": "True", + "DFFlagCSGPhysicsRecalculateBadContactsInConnectors": "True", + "FFlagStudioPartAlignmentChangeEnabled": "True", + "FFlagStudioToolBoxModelDragFix": "True", + "DFFlagOrder66": "False", + "FFlagCloudIconFixEnabled": "True", + "DFFlagFixHealthReplication": "True", + "DFFlagReplicateAnimationSpeed": "True", + "FFlagSurfaceLightEnabled": "True", + "FFlagLuaFollowers": "True", + "FFlagNewNotificationsScript": "True", + "FFlagStudioSendMouseIdleToPluginMouse": "True", + "DFFlagPhysicsOptimizeAssemblyHistory": "True", + "DFFlagPhysicsOptimizeBallBallContact": "True", + "DFFlagUseNewBubbleSkin": "True", + "DFFlagUse9FrameBackgroundTransparency": "True", + "DFFlagCheckForHeadHit": "False", + "DFFlagUseHttpsForAllCalls": "True", + "DFFlagLoadCoreModules": "True", + "FFlagStudioRecentSavesEnabled": "True", + "FFlagStudioToolBoxInsertUseRayTrace": "True", + "FFlagInterpolationUseWightedDelay": "True", + "FFlagUseInGameTopBar": "True", + "FFlagNewPurchaseScript": "True", + "FFlagStudioEnableGamepadSupport": "True", + "FFlagStudioRemoveDuplicateParts": "True", + "FFlagStudioLaunchDecalToolAfterDrag": "True", + "DFFlagHumanoidFloorPVUpdateSignal": "True", + "DFFlagHumanoidFloorDetectTeleport": "True", + "DFFlagHumanoidFloorForceBufferZone": "False", + "DFFlagHumanoidFloorManualDeltaUpdateManagment": "True", + "DFFlagHumanoidFloorManualFrictionLimitation": "True", + "FStringPlaceFilter_InterpolationTimingFix": "True;208770506", + "DFFlagUpdateHumanoidNameAndHealth": "True", + "DFFlagEnableHumanoidDisplayDistances": "True", + "FFlagFixTouchInputEventStates": "False", + "DFFlagInterpolationTimingFix": "True", + "FIntRenderGBufferMinQLvl": 15, + "FFlagResizeGuiOnStep": "True", + "FFlagDontFireFakeMouseEventsOnUIS": "True", + "FFlagCameraUseOwnViewport": "True", + "FFlagGameExplorerMoveImagesUnderAssetsGroup": "True", + "DFFlagNetworkFilterAllowToolWelds": "True", + "DFIntHttpInfluxHundredthsPercentage": 5, + "DFStringHttpInfluxURL": "http://ec2-54-84-170-153.compute-1.amazonaws.com:8086", + "DFStringHttpInfluxDatabase": "prod", + "DFStringHttpInfluxUser": "rob", + "DFStringHttpInfluxPassword": "faster1Play", + "FFlagStudioSpawnLocationsDefaultValues": "True", + "FFlagStudioDE11536FixEnabled": "True", + "FFlagStudioRibbonGroupResizeFixEnabled": "True", + "FFlagGradientStep": "True", + "FFlagUseNewContentProvider": "False", + "SFFlagEquipToolOnClient": "True", + "FFlagStartWindowMaximizedDefault": "True", + "FFlagUseNewKeyboardHandling": "True", + "FFlagCameraZoomNoModifier": "True", + "DFFlagRemoteValidateSubscribersError": "True", + "FFlagNewMenuSettingsScript": "True", + "DFFlagHttpCurlSanitizeUrl": "True", + "DFFlagRemoveDataModelDependenceInWaitForChild": "True", + "FFlagFilterAddSelectionToSameDataModel": "True", + "DFFlagUseCanManageApiToDetermineConsoleAccess": "True", + "DFIntMoveInGameChatToTopPlaceId": 1, + "FFlagStudioProgressIndicatorForInsertEnabled": "True", + "FFlagTerrainLazyGrid": "True", + "FFlagHintsRenderInUserGuiRect": "True", + "DFFlagCustomEmitterInstanceEnabled": "True", + "FFlagCustomEmitterRenderEnabled": "True", + "FFlagCustomEmitterLuaTypesEnabled": "True", + "FFlagCallSetFocusFromCorrectThread": "True", + "FFlagFastRevert": "True", + "FFlagSleepBeforeSpinlock": "True", + "FFlagSparseCheckFastFail": "True", + "FFlagStudioSmoothTerrainPlugin": "True", + "FFlagStudioLoadPluginsLate": "True", + "FFlagStudioInsertIntoStarterPack": "True", + "FFlagStudioIgnoreSSLErrors": "True", + "DFFlagFixJointReparentingDE11763": "True", + "DFFlagPhysicsInvalidateContactCache": "True", + "FFlagLuaMathNoise": "True", + "FFlagArcHandlesBidirectional": "True", + "FFlagChangeHistoryFixPendingChanges": "True", + "DFFlagWorkspaceSkipTerrainRaycastForSurfaceGui": "True", + "FFlagStudioBatchItemMapAddChild": "True", + "FFlagRenderCameraFocusFix": "True", + "DFFlagReplicatorWorkspaceProperties": "True", + "FFlagDirectX11Enable": "True", + "FFlagCheckDegenerateCases": "True", + "DFFlagUseServerCoreScripts": "True", + "DFFlagCorrectFloorNormal": "True", + "FFlagNewBadgeServiceUrlEnabled": "True", + "FFlagBubbleChatbarDocksAtTop": "True", + "FFlagSmoothTerrainClient": "True", + "FFlagLuaUseBuiltinEqForEnum": "True", + "FFlagPlaceLauncherThreadCheckDmClosed": "True", + "DFFlagAppendTrackerIdToTeleportUrl": "True", + "FFlagPlayerMouseRespectGuiOffset": "True", + "DFFlagReportElevatedPhysicsFPSToGA": "True", + "DFFlagPreventReturnOfElevatedPhysicsFPS": "True", + "FFlagStudioIgnoreMouseMoveOnIdle": "True", + "FFlagStudioDraggerFixes": "True", + "FLogUseLuaMemoryPool": 0, + "FFlagCSGNewTriangulate": "True", + "DFFlagLuaFixResumeWaiting": "True", + "FFlagFRMInStudio": "True", + "DFFlagFixLadderClimbSpeed": "True", + "DFFlagNoWalkAnimWeld": "False", + "DFFlagImprovedKick": "True", + "FFlagRenderFixLightGridDirty": "True", + "FFlagLoadLinkedScriptsOnDataModelLoad": "True", + "FFlagFixMeshOffset": "True", + "FIntLaunchInfluxHundredthsPercentage": 0, + "DFIntJoinInfluxHundredthsPercentage": 100, + "FFlagSmoothTerrain": "True", + "FFlagNewVehicleHud": "True", + "DFFlagHumanoidStandOnSeatDestroyed": "True", + "DFFlagGuiBase3dReplicateColor3WithBrickColor": "True", + "FFlagTaskSchedulerCyclicExecutiveStudio": "True", + "DFIntElevatedPhysicsFPSReportThresholdTenths": 585, + "DFIntExpireMarketPlaceServiceCacheSeconds": 60, + "DFFlagEnableMarketPlaceServiceCaching": "True", + "DFFlagUseNewAnalyticsApi": "True", + "DFFlagSmoothTerrainDebounceUpdates": "True", + "FFlagStudioAuthenticationCleanup": "True", + "FFlagRenderFixGBufferLOD": "True", + "FFlagStudioDraggerCrashFixEnabled": "True", + "FFlagDraggerCrashFixEnabled": "True", + "DFFlagEnableRapidJSONParser": "True", + "DFFlagPushLuaWorldRayOriginToNearClipPlane": "True", + "FFlagLoadTimeModificationTestFlag": "True", + "DFFlagPhysicsFastSmoothTerrainUpdate": "True", + "DFFlagSmoothTerrainPhysicsExpandPrimitiveOptimal": "True", + "DFFlagFixBytesOnJoinReporting": "True", + "FFlagRenderGBufferEverywhere": "False", + "DFFlagSmoothTerrainPhysicsRayAabbExact": "True", + "DFIntSmoothTerrainPhysicsRayAabbSlop": 1, + "DFIntMaxClusterKBPerSecond": 300, + "FLogLuaAssert": 0, + "FFlagSmoothTerrainCountCellVolume": "True", + "DFFlagSmoothTerrainWorldToCellUseDiagonals": "True", + "DFFlagFireSelectionChangeOncePerChange": "True", + "FIntLuaAssertCrash": 0, + "FFlagAlternateFontKerning": "True", + "FFlagRenderFixCameraFocus": "False", + "DFFlagCSGPhysicsSphereRotationIdentity": "True", + "DFFlagCSGPhysicsRefreshContactsManually": "True", + "FFlagStudioUndoEnabledForEdit": "True", + "DFIntLuaChatFloodCheckMessages": 7, + "DFIntLuaChatFloodCheckInterval": 15, + "FFlagLuaChatFiltering": "True", + "FFlagMobileToggleChatVisibleIcon": "True", + "FFlagGlowEnabled": "True", + "FFlagStudioDE9132FixEnabled": "True", + "DFFlagHttpCurlHandle301": "True", + "FFlagClientABTestingEnabled": "False", + "FFlagStudioSmoothTerrainForNewPlaces": "True", + "FFlagUsePGSSolver": "True", + "FFlagSimplifyKeyboardInputPath": "False", + "FFlagNewInGameDevConsole": "True", + "DFFlagUseNewFullscreenLogic": "True", + "DFFlagGetUserIdEnabled": "True", + "DFFlagGetUserNameEnabled": "True", + "DFFlagEnableAnimationInformationAccess": "True", + "DFFlagEnableAnimationTrackExtendedAPI": "True", + "FFlagRequestServerStatsV2Enabled": "True", + "FFlagPhysicsPreventGroupDraggerPlacementToMinus400_DE6267": "True", + "FFlagSpecificUserdataTypeErrors": "True", + "DFFlagScrollingFrameDraggingFix": "True", + "FFlagAutodetectCPU": "True", + "DFFlagSetRenderedFrameOnClumpChanged": "True", + "DFFlagDisableTimeoutDuringJoin": "True", + "DFFlagDesiredAltitudeDefaultInf": "True", + "DFFlagRCCDE13316CrashFix": "True", + "DFFlagUseStarterPlayerGA": "True", + "FFlagScrollingFrameMouseUpFix": "True", + "DFFlagDebrisServiceUseDestroy": "True", + "DFFlagAccessUserFeatureSetting": "True", + "DFFlagAllowBindActivate": "True", + "FFlagEnableControllerGuiSelection": "True", + "FFlagUseNewSoftwareMouseRender": "True", + "DFFlagDoNotHoldTagItemsForInitialData": "True", + "FFlagAltSpinlock": "True", + "FFlagSpinlock": "True", + "FFlagGraphicsGLReduceLatency": "True", + "DFFlagMovingHumananoidWakesFloor": "True", + "DFFlagSetNetworkOwnerAPIEnabled": "True", + "DFFlagSetNetworkOwnerAPIEnabledV2": "True", + "DFFlagGetFriendsEnabled": "True", + "DFFlagGetFriendsOnlineEnabled": "True", + "DFFlagUseNewTextBoxLogic": "True", + "FFlagOnScreenProfilerGPU": "True", + "FFlagConfigurableLineThickness": "True", + "DFFlagSpawnPointEnableProperty": "True", + "DFFlagConfigurableFallenPartDestroyHeight": "True", + "DFFlagMiddleMouseButtonEvent": "True", + "DFFlagEnablePreloadAsync": "True", + "DFFlagFoldersInGUIs": "True", + "DFFlagUserUseLuaVehicleController": "True", + "DFIntAndroidInfluxHundredthsPercentage": 0, + "FStringPlaceFilter_SetNetworkOwnerAPIEnabled": "True;13822889;257857728", + "FStringPlaceFilter_SetNetworkOwnerAPIEnabledV2": "True;13822889;257857728", + "DFFlagNoOwnershipLogicOnKernelJoint": "True", + "DFFlagEnableGetPlayingAnimationTracks": "True", + "FFlagCyclicExecutivePriorityJobs": "True", + "DFIntMaxMissedWorldStepsRemembered": 16, + "DFIntMacInfluxHundredthsPercentage": 0, + "DFIntiOSInfluxHundredthsPercentage": 100, + "FFlagStudioLockServiceParents": "True", + "DFFlagFirePlayerAddedAndPlayerRemovingOnClient": "True", + "DFFlagRecursiveWakeForBodyMovers": "True", + "DFFlagEnableHumanoidSetStateEnabled": "True", + "DFFlagSoundEndedEnabled": "True", + "DFFlagUseIntersectingOthersForSpawnEnabled": "True", + "DFFlagMoveToDontAlignToGrid": "True", + "DFFlagIsBestFriendsWithReturnFriendsWith": "True", + "DFFlagPlayerOwnsAssetFalseForInvalidUsers": "True", + "DFFlagIsUrlCheckAssetStringLength": "True", + "DFFlagEnableGoodbyeDialog": "True", + "DFFlagEnableReverseAnimations": "True", + "DFFlagEnableAnimationSetTimePosition": "True", + "DFFlagEnableMobileAutoJumpFlag": "True", + "DFFlagHttpDelaySendInfluxStats": "True", + "DFFlagDisableRequestMarker": "True", + "DFFlagDisableCharacterRequest": "True", + "FFlagStudioCollapsibleTutorials": "True", + "FStringStudioTutorialsUrl": "http://wiki.roblox.com/index.php?title=Studio_Tutorials_Test&studiomode=true", + "FStringStudioTutorialsTOCUrl": "http://wiki.roblox.com/index.php?title=Studio_Tutorials_Landing&studiomode=true", + "FFlagSandboxHash": "True", + "FFlagDE14316CrashFix": "True", + "FFlagDE14317CrashFix": "True", + "DFFlagSetNetworkOwnerCanSetCheck": "True", + "DFFlagLiveColorUpdatesCanceling": "True", + "DFFlagLuaGcPerVm": "True", + "FFlagGraphicsGLDisableDiscard": "True", + "FFlagStudioDeviceEmulationTouchInputFix": "True", + "FFlagTaskSchedulerCyclicExecutive": "True", + "DFFlagMakeWebPendingFriendRequests": "True", + "DFFlagGetLastestAssetVersionEnabled": "True", + "FFlagHideDeprecatedEnums": "True", + "FFlagSubmitEditedColor3WhenFocusLost": "True", + "DFFlagFixScriptableCameraRoll": "True", + "DFFlagSeparateBulletNarrowPhaseAndMidStepUpdates": "True", + "DFFlagUsePGSSolverSpringConstantScale": "True", + "DFFlagToolRequiresHandleProperty": "True", + "FFlagPlayerScriptsNotArchivable": "True", + "DFFlagClearAllChildrenUseDestroy": "True", + "DFFlagMaxPlayersEnabled": "True", + "DFFlagPreferredPlayersEnabled": "True", + "DFFlagLocalHumanoidSoundsEnabled": "True", + "DFFlagIncreaseSoundPositionClampLimit": "True", + "DFFlagNameOcculsionIgnoreTransparent": "True", + "DFFlagReconstructAssetUrlNew": "True", + "DFFlagAdjustFloorForce": "True", + "DFFlagFixAnimationPhaseInitialization": "True", + "FFlagLuaChatPhoneFontSize": "True", + "DFFlagUseAssetTypeHeader": "True", + "FStringPlaceFilter_ReservedServersEnabled": "True;107715348", + "FFlagCSGUnionCatchUnknownExceptions": "False", + "FIntGamePerfMonitorPercentage": 10, + "FFlagSoundTypeCheck": "True", + "DFFlagIncreaseScrollWheelMultiplyTime": "True", + "FFlagMacRemoveUserInputJob": "True", + "FFlagStudioNewFonts": "True", + "DFFlagApiCapitalizationChanges": "True", + "FFlagParticleCullFix": "True", + "DFFlagVideoCaptureTeleportFix": "False", + "DFFlagCoreGuiCustomizationApi": "True", + "DFFlagCustomTeleportLoadingScreen": "True", + "DFFlagCharacterAppearanceLoadedEnabled": "True", + "DFFlagVIPServerOwnerIdEnabled": "True", + "DFFlagEnableParticleVelocityInheritance": "True", + "DFFlagEnableParticleEmissionDirection": "True", + "DFFlagFixParticleDistribution": "True", + "DFFlagEnableParticleNewBoundingBox": "True", + "DFFlagEnableParticlePartLock": "True", + "DFFlagEnableParticleBurst": "True", + "DFFlagNoRunSteepSlope": "True", + "DFFlagHumanoidJumpPower": "True", + "FFlagControllerMenu": "True", + "FFlagFlyCamOnRenderStep": "True", + "DFFlagFullscreenEnabledWhileRecording": "True", + "DFFlagPreProcessTextBoxEvents": "True", + "DFFlagAllowHideHudShortcut": "False", + "DFFlagFixBallInsideBlockCollision": "True", + "FFlagPGSSolverBodyCacheLeakFix": "True", + "FFlagFixCrashAtShutdown": "True", + "DFFlagEquipClonedToolFix": "True", + "FFlagGamepadCursorChanges": "True", + "DFFlagCreatePlayerGuiLocal": "False", + "DFFlagDontUseInsertServiceOnAnimLoad": "True", + "DFFlagCyclicExecutiveFixNonCyclicJobRun": "True", + "DFFlagPhysicsFPSTimerFix": "True", + "FFlagCyclicExecutiveRenderJobRunsFirst": "True", + "FFlagPhysicsCylinders": "True", + "DFFlagPhysicsUseNewBulletContact": "True", + "FFlagReadCoordinateFrameFast": "False", + "DFFlagRayHitMaterial": "True", + "DFFlagPromptPurchaseOnGuestHandledInCoreScript": "True", + "DFFlagNonEmptyPcallError": "True", + "DFFlagDisplayTextBoxTextWhileTypingMobile": "False", + "DFFlagOverrideScollingDisabledWhenRecalulateNeeded": "True", + "DFFlagFixScrollingOffSurfaceGUIs": "True", + "DFFlagTextScaleDontWrapInWords": "True", + "DFFlagListenPositionEnabled": "True", + "DFFlagGetCharacterAppearanceEnabled": "True", + "DFFlagBackTabInputInStudio": "True", + "DFFlagTrackLastDownGUI": "True", + "DFFlagBulletFixCacheReuse": "True", + "DFFlagFastFilterHumanoidParts": "False", + "DFFlagProcessAcceleratorsBeforeGUINaviagtion": "True", + "DFFlagImageFailedToLoadContext": "True", + "DFFlagDontReorderScreenGuisWhenDescendantRemoving": "True", + "DFFlagSoundFailedToLoadContext": "True", + "DFFlagAnimationFailedToLoadContext": "True", + "DFFlagElasticEasingUseTwoPi": "True", + "SFFlagNetworkUseServerScope": "True", + "DFFlagHttpZeroLatencyCaching": "True", + "DFFlagPasteWithCapsLockOn": "True", + "DFFlagScriptExecutionContextApi": "True", + "DFFlagGetFocusedTextBoxEnabled": "True", + "DFFlagTextBoxIsFocusedEnabled": "True", + "DFFlagHttpCurlDomainTrimmingWithBaseURL": "False", + "DFFlagLoadFileUseRegularHttp": "True", + "DFFlagReplicatorCheckBadRebinding": "True", + "FFlagFastClusterThrottleUpdateWaiting": "True", + "DFFlagDeserializePacketThreadEnabled": "True", + "FFlagFontSizeUseLargest": "True", + "DFFlagRejectHashesInLinkedSource": "True", + "FFlagUpdatePrimitiveStateForceSleep": "True", + "FFlagPhysicsUseKDTreeForCSG": "True", + "DFFlagCSGLeftoverDataFix": "True", + "DFFlagGetGroupsAsyncEnabled": "True", + "FFlagStudioTutorialSeeAll": "True", + "DFFlagLimitScrollWheelMaxToHalfWindowSize": "True", + "FFlagGameExplorerCopyPath": "True", + "DFFlagFixRotatedHorizontalScrollBar": "True", + "DFFlagFixAnchoredSeatingPosition": "True", + "FFlagFixSlice9Scale": "True", + "DFFlagFullscreenRefocusingFix": "True", + "DFFlagEnableClimbingDirection": "True", + "FFlagPGSGlueJoint": "True", + "FFlagTweenCallbacksDuringRenderStep": "True", + "FFlagFRMFixCullFlicker": "True", + "DFFlagDisableProcessPacketsJobReschedule": "True", + "FFlagCSGVoxelizerPrecompute": "False", + "FFlagLazyRenderingCoordinateFrame": "True", + "FFlagPGSSteppingMotorFix": "True", + "FFlagGameExplorerCopyId": "True", + "DFFlagLockViolationScriptCrash": "False", + "DFFlagLockViolationInstanceCrash": "False", + "FFlagSpheresAllowedCustom": "True", + "DFFlagHumanoidCookieRecursive": "True", + "FFlagRwxFailReport": "True", + "FIntStudioInsertDeletionCheckTimeMS": 30000, + "DFFlagClampRocketThrustOnPGS": "True", + "DFFlagPGSWakePrimitivesWithBodyMoverPropertyChanges": "True", + "FFlagPGSUsesConstraintBasedBodyMovers": "True", + "FFlagUseNewSubdomainsInCoreScripts": "True", + "DFFlagEnableShowStatsLua": "True", + "FFlagSmoothTerrainPacked": "True", + "FFlagGraphicsGL3": "True", + "DFFlagUrlReconstructToAssetGame": "True", + "FFlagPGSApplyImpulsesAtMidpoints": "True", + "FFlagModifyDefaultMaterialProperties": "True", + "FIntPhysicalPropFriction_SMOOTH_PLASTIC_MATERIAL": 200, + "FIntPhysicalPropFriction_PLASTIC_MATERIAL": 300, + "FIntPhysicalPropFriction_NEON_MATERIAL": 300, + "FIntPhysicalPropFriction_SNOW_MATERIAL": 300, + "FIntPhysicalPropFriction_ALUMINUM_MATERIAL": 400, + "FIntPhysicalPropFriction_BRICK_MATERIAL": 800, + "FIntPhysicalPropFriction_CONCRETE_MATERIAL": 700, + "FIntPhysicalPropFriction_DIAMONDPLATE_MATERIAL": 350, + "FIntPhysicalPropFriction_SANDSTONE_MATERIAL": 500, + "FIntPhysicalPropFriction_SAND_MATERIAL": 500, + "FIntPhysicalPropFWeight_ICE_MATERIAL": 3000, + "FIntPhysicalPropFWeight_BRICK_MATERIAL": 300, + "FIntPhysicalPropFWeight_CONCRETE_MATERIAL": 300, + "FIntPhysicalPropFWeight_SANDSTONE_MATERIAL": 5000, + "FIntPhysicalPropFWeight_BASALT_MATERIAL": 300, + "FIntPhysicalPropFWeight_SAND_MATERIAL": 5000, + "FIntPhysicalPropElasticity_SAND_MATERIAL": 50, + "FIntPhysicalPropElasticity_SNOW_MATERIAL": 30, + "FIntPhysicalPropElasticity_MUD_MATERIAL": 70, + "FIntPhysicalPropElasticity_GROUND_MATERIAL": 100, + "FIntPhysicalPropElasticity_MARBLE_MATERIAL": 170, + "FIntPhysicalPropElasticity_BRICK_MATERIAL": 150, + "FIntPhysicalPropElasticity_PEBBLE_MATERIAL": 170, + "FIntPhysicalPropElasticity_COBBLESTONE_MATERIAL": 170, + "FIntPhysicalPropElasticity_ROCK_MATERIAL": 170, + "FIntPhysicalPropElasticity_SANDSTONE_MATERIAL": 150, + "FIntPhysicalPropElasticity_BASALT_MATERIAL": 150, + "FIntPhysicalPropElasticity_CRACKED_LAVA_MATERIAL": 150, + "FIntPhysicalPropElasticity_FABRIC_MATERIAL": 50, + "FIntPhysicalPropElasticity_WOOD_MATERIAL": 200, + "FIntPhysicalPropElasticity_WOODPLANKS_MATERIAL": 200, + "FIntPhysicalPropElasticity_ICE_MATERIAL": 150, + "FIntPhysicalPropElasticity_GLACIER_MATERIAL": 150, + "FIntPhysicalPropElasticity_RUST_MATERIAL": 200, + "FIntPhysicalPropElasticity_DIAMONDPLATE_MATERIAL": 250, + "FIntPhysicalPropElasticity_ALUMINUM_MATERIAL": 250, + "FIntPhysicalPropElasticity_METAL_MATERIAL": 250, + "FIntPhysicalPropElasticity_GRASS_MATERIAL": 100, + "DFFlagFixSeatingWhileSitting": "True", + "FFlagPGSSolverDefaultOnNewPlaces": "True", + "FFlagPGSVariablePenetrationMargin": "False", + "FFlagStudioEnableGameAnimationsTab": "True", + "FIntPGSPentrationMarginMax": 50000, + "FFlagStudioHideMouseCoursorOnCommand": "True", + "SFFlagNewPhysicalPropertiesForcedOnAll": "True", + "SFFlagMaterialPropertiesNewIsDefault": "True", + "DFFlagMaterialPropertiesEnabled": "True", + "FFlagWaterParams": "True", + "FFlagSpatialHashMoreRoots": "True", + "FFlagSkipAdornIfWorldIsNull": "True", + "DFStringWorkspaceMessageLink": "", + "DFStringWorkspaceMessageText": "", + "DFFlagWorkspaceNotificationMasterEnable": "False", + "DFIntStudioWorkspaceNotificationLevel": 0, + "FFlagLuaBasedBubbleChat": "True", + "DFFlagNetworkOwnershipRuleReplicates": "True", + "DFFlagSmoothTerrainWriteVoxelsOccupancyFix": "True", + "DFFlagCloudEditByPassCheckForServer": "True", + "DFFlagDraggerUsesNewPartOnDuplicate": "True", + "DFFlagRestoreTransparencyOnToolChange": "False", + "FFlagEnableLuaFollowers": "False", + "DFFlagUserServerFollowers": "True", + "FFlagNetworkReplicateTerrainProperties": "True", + "FFlagAllowInsertFreeModels": "False", + "FFlagInsertUnderFolder": "True", + "DFFlagPGSWakeOtherAssemblyForJoints": "True", + "FFlagStudioPropertySliderEnabled": "True", + "DFFlagSetNetworkOwnerFixAnchoring": "True", + "FFlagFixBulletGJKOptimization": "True", + "FFlagOSXUseSDL": "False", + "DFFlagPhysicalPropMassCalcOnJoin": "False", + "DFFlagBrickColorParseNonDeprecatedMatchEnabled": "True", + "FFlagWindowsUseSDL": "False", + "FFlagPhysicsOptimizeSendClumpChanged": "True", + "DFFlagHumanoidFeetIsPlastic": "True", + "DFFlagUseTerrainCustomPhysicalProperties": "True", + "DFFlagFormFactorDeprecated": "True", + "FFlagPGSVariablePenetrationMarginFix": "True", + "DFIntDataStoreMaxValueSize": 262144, + "DFFlagFilteringEnabledDialogFix": "True", + "DFFlagFixShapeChangeBug": "True", + "FFlagScriptAnalyzerFixLocalScope": "True", + "FFlagRenderVRBBGUI": "True", + "FFlagRenderVR": "True", + "DFFlagNetworkFixJoinDataItemOrder": "True", + "FFlagUseUserListMenu": "True", + "FFlagStudioImproveModelDragFidelity": "True", + "FFlagStudioOrthonormalizeSafeRotate": "True", + "FFlagMacInferredCrashReporting": "True", + "FFlagWindowsInferredCrashReporting": "True", + "FFlagCloudEditDoNotLoadCoreScripts": "True", + "FFlagStudioEmbeddedFindDialogEnabled": "False", + "FFlagUserAllCamerasInLua": "False", + "DFFlagMacInferredCrashReporting": "True", + "DFFlagWindowsInferredCrashReporting": "True", + "FFlagUseNewPromptEndHandling": "True", + "FFlagPhysPropConstructFromMaterial": "True", + "FFlagStudioToolboxModelDragToCastPoint": "True", + "FFlagStudioPushTreeWidgetUpdatesToMainThread": "True", + "FFlagStudioDE8774CrashFixEnabled": "True", + "DFFlagFixYieldThrottling": "True", + "FFlagCheckSleepOptimization": "True", + "DFFlagContactManagerOptimizedQueryExtents": "True", + "FFlagEnableSetCoreTopbarEnabled": "True", + "FFlagUseBuildGenericGameUrl": "True", + "DFFlagFixFallenPartsNotDeleted": "True", + "DFFlagTrackPhysicalPropertiesGA": "True", + "DFFlagSetNetworkOwnerFixAnchoring2": "True", + "FFlagUseSimpleRapidJson": "True", + "DFFlagTurnOffFakeEventsForCAS": "True", + "DFFlagTurnOffFakeEventsForInputEvents": "True", + "FFlagCancelPendingTextureLoads": "False", + "DFFlagUseR15Character": "True", + "DFFlagEnableHipHeight": "True", + "DFFlagCachedPoseInitialized": "False", + "DFFlagFixJumpGracePeriod": "True", + "FFlagFilterSinglePass": "True", + "DFFlagOrthonormalizeJointCoords": "True", + "DFFlagPhysicsSenderUseOwnerTimestamp": "False", + "DFFlagNamesOccludedAsDefault": "True", + "FFlagUserUseNewControlScript": "True", + "FFlagUseDynamicTypesetterUTF8": "True", + "FStringPlaceFilter_UseNewPersistenceSubdomain": "True;359173292", + "FStringPlaceFilter_UseNewDataStoreLogging": "True;359173292", + "FStringPlaceFilter_InsertServiceLoadModelErrorDoNotCreateEmpty": "True;359173292", + "FStringPlaceFilter_InsertServiceLoadModelErrorNoLuaExceptionReturnNull": "True;359173292", + "DFFlagUseNewPersistenceSubdomain": "True", + "DFFlagUseNewDataStoreLogging": "True", + "FFlagPlaceLauncherUsePOST": "False", + "FFlagStudioUpdateSAResultsInUIThread": "True", + "FFlagBillboardGuiVR": "True", + "FFlagHumanoidRenderBillboard": "True", + "FLogVR": 6, + "FFlagStudioRemoveDebuggerResumeLock": "True", + "FFlagPhysicsAnalyzerEnabled": "True", + "FFlagAnalyzerGroupUIEnabled": "True", + "DFFlagVariableHeartbeat": "True", + "DFFlagScreenShotDuplicationFix": "True", + "FFlagCSGDelayParentingOperationToEnd": "True", + "FFlagStudioTreeWidgetCheckDeletingFlagWhenDoingUpdates": "True", + "DFFlagUseComSiftUpdatedWebChatFilterParamsAndHeader": "False", + "DFFlagConstructModerationFilterTextParamsAndHeadersUseLegacyFilterParams": "False", + "FFlagMinMaxDistanceEnabled": "True", + "FFlagRollOffModeEnabled": "True", + "DFFlagGetLocalTeleportData": "True", + "FFlagUseNewXboxLoginFlow": "True", + "FStringPlaceFilter_MaterialPropertiesNewIsDefault": "False;276583453;29812337;264431831;189707;204990346;30869879;166456825;287335491;180714690;248409235;255280267;289685010;183384830;314906000;323111227", + "DFFlagFixSlowLadderClimb": "True", + "DFFlagHumanoidCheckForNegatives": "True", + "DFFlagFixMatrixToAxisAngle": "True", + "DFFlagMaskWeightCleared": "True", + "DFFlagUseStarterPlayerCharacterScripts": "True", + "DFFlagUseStarterPlayerHumanoid": "True", + "DFFlagUseStarterPlayerCharacter": "True", + "DFFlagAccessoriesAndAttachments": "True", + "FFlagTeamCreateOptimizeRemoteSelection": "True", + "FFlagReportInGameAssetSales": "True", + "FFlagFilterDoublePass": "False", + "DFFlagRaiseSendPriority": "False", + "FFlagStudioDelaySAResultsEnabled": "True", + "FFlagUsePreferredSoundDevice": "True", + "FFlagRenderLowLatencyLoop": "False", + "DFFlagLocalScriptSpawnPartAlwaysSetOwner": "True", + "DFFlagCloudEditSupportImmediatePublish": "True", + "FFlagFixSurfaceGuiGamepadNav": "True", + "DFFlagEnableAdColony": "False", + "FFlagEnableAdServiceVideoAds": "False", + "DFFlagInfluxDb09Enabled": "True", + "DFFlagTeleportSignalConnectOnServiceProvider": "True", + "DFFlagScriptContextGuardAgainstCStackOverflow": "True", + "FFlagFixPhysicalPropertiesComponentSet": "True", + "DFFlagMaterialPropertiesDivideByZeroWeights": "True", + "FFlagRemoveUnusedPhysicsSenders": "True", + "FFlagRemoveInterpolationReciever": "True", + "DFFlagActivatePGSMechanicalJoints": "True", + "FIntPhysicalPropDensity_ALUMINUM_MATERIAL": 2700, + "FFlagTreatCloudEditAsEditGameMode": "True", + "FFlagSendFilteredExceptionOnInferredStep": "True", + "DFFlagUrlReconstructToAssetGameSecure": "False", + "DFFlagUseModerationFilterTextV2": "True", + "FFlagGraphicsD3D10": "True", + "FFlagRenderFixFog": "True", + "FFlagUseNewAppBridgeWindows": "False", + "DFFlagNullCheckJointStepWithNullPrim": "True", + "FFlagJNIEnvScopeOptimization": "True", + "DFFlagSanitizeLoadingGUICorrected": "True", + "FFlagSendLegacyMachineConfigInfo": "False", + "FFlagUseNewBadgesCreatePage": "True", + "FFlagRetryWhenCloudEditEnabledEndpointFails": "True", + "DFFlagTeamCreateDoNotReplicateShowDevGuiProp": "True", + "FFlagStudioAddBackoffToNotificationsReconnects": "True", + "DFFlagInsertServiceForceLocalInTeamCreate": "True", + "FFlagGraphicsMacFix": "True", + "FFlagUseNewAppBridgeOSX": "True", + "FFlagNewColor3Functions": "True", + "DFFlagSmootherVehicleSeatControlSystem": "True", + "FFlagGameExplorerUseV2AliasEndpoint": "True", + "FFlagDisableAbortRender": "True", + "DFFlagInstancePredeleteNuke": "False", + "DFFlagSimpleHermiteSplineInterpolate": "False", + "DFFlagCleanUpInterpolationTimestamps": "True", + "SFFlagPhysicsPacketSendWorldStepTimestamp": "True", + "DFFlagUpdateTimeOnDelayedSamples": "False", + "DFFlagDisableMovementHistory": "True", + "DFFlagLookForDuplicateCoordinateFrameInBuffer": "True", + "DFFlagDoNotForwardClientTimestamp": "True", + "DFFlagZeroVelocityOnDelayedSamples": "True", + "DFFlagUpdateHermiteLastFrameWhenUpdatePrevFrame": "True", + "DFFlagCatchThrottledVelocityComponents": "True", + "DFIntThrottledVelocityThresholdTenths": 15, + "DFFlagShowFormFactorDeprecatedWarning": "False", + "FFlagStudioTeamCreateWebChatBackendEnabled": "True", + "DFFlagAnimationEasingStylesEnabled": "True", + "FFlagUseVRKeyboardInLua": "True", + "DFFlagCheckDataModelOnTeleportRetry": "True", + "FFlagTeamCreateEnableAutoReconnect": "True", + "FFlagTeamCreateUpdatedChatUIEnabled": "True", + "FFlagTeamCreateExpandableChatEditor": "True", + "DFStringHttpInfluxWriterPassword": "faster1Play", + "DFFlagOptimizeAnimator": "True", + "FFlagOptimizeAnimatorCalcJoints": "True", + "DFFlagStopUsingMaskWeight": "True", + "FFlagRenderNoDepthLast": "True", + "DFFlagFixTimeStampsForRunningNoThrottle": "True", + "DFIntInterpolationDelayFactorTenths": 10, + "DFFlagUseHermiteSplineInterpolation": "True", + "DFFlagChatServiceFilterStringForPlayerFromAndToStudioBypass": "True", + "FFlagCameraInterpolateMethodEnhancement": "False", + "DFFlagBlendPosesWithIsOver": "True", + "FStringPlaceFilter_InterpolationDelayFactorTenths": "10;189707", + "FStringPlaceFilter_CleanUpInterpolationTimestamps": "False;424096945", + "FStringPlaceFilter_DisableMovementHistory": "True;189707", + "FStringPlaceFilter_PhysicsPacketSendWorldStepTimestamp": "False;424096945", + "FStringPlaceFilter_UseHermiteSplineInterpolation": "False;424096945", + "FFlagRestrictSales": "True", + "DFFlagStacktraceOnKeysMustBeStrings": "False", + "FFlagBadTypeOnPcallEnabled": "True", + "FFlagFixMouseFireOnEmulatingTouch": "True", + "FFlagTeamCreateEnableOneToOneChat": "True", + "FFlagUseUpdatedSyntaxHighlighting": "True", + "FFlagFixStickyDragBelowOrigin": "True", + "FFlagFixBadMemoryOnStreamingGarbageCollection": "True", + "DFFlagFixAllCharacterStreaming": "True", + "FFlagDisableChangedServiceInTestMode": "True", + "FFlagAllowFullColorSequences": "True", + "FFlagStudioAllowFullColorSequences": "True", + "DFFlagDynamicGravity": "True", + "FFlagUseNewAppBridgeAndroid": "False", + "FFlagFixSurfaceGuiGazeSelect": "True", + "FFlagFixAlwaysOnTopSurfaceGuiInput": "True", + "FFlagStudioEnableAxWidget": "True", + "DFFlagFixDataStoreErrorMessage": "True", + "FFlagStudioAllowDataStoreUsageInCloudEdit": "True", + "FFlagSaveToTempFile": "True", + "DFFlagCSGPreventCrashesWhenPartOperationsNotInDataModel": "True", + "FStringPlaceFilter_NoAnimationTrackState": "True;177200271", + "DFFlagUsePointsNewBatchingImpl": "True", + "FFlagUseUpdatedKeyboardSettings": "False", + "DFFlagFixAnimationControllerAnimator": "True", + "DFFlagNoAnimationTrackState": "True", + "DFFlagFixNestedAnimators": "True", + "DFFlagWaitForToolHandleToEquip": "True", + "DFFlagUseNewFetchFriendsFunction": "True", + "FFlagStudioMeshIdMacCrashFixEnabled": "True", + "FFlagWindowsNoDmpRetry": "True", + "FFlagDeleteLogsOnMac": "True", + "FFlagDeleteLogsByDate": "True", + "FFlagTeamCreateSlowerFetchChatMessage": "True", + "FFlagTestMenuEnabledOnAllWindows": "True", + "FFlagCSGSetUsePartColorToTrueIfAllPartsSameColor": "True", + "FFlagSoundServiceGameConfigurerConfigureRunServiceRun": "True", + "DFFlagDoUpdateStepDetachedChannels": "True", + "DFFlagUseNewSetFMOD3D": "False", + "FFlagSoundChannelMaxDistanceStopFMODChannel": "True", + "FFlagRCCLoadFMOD": "True", + "FFlagSoundChannelUseV2Implementation": "False", + "FFlagRenderSoftParticles": "True", + "FFlagScriptContextSinglePendingThreadsQueue": "False", + "DFIntTeleportExceptionInfluxHundredthsPercentage": 10000, + "FIntStartupInfluxHundredthsPercentage": 100, + "FFlagCSGAllowUnorderedProperties": "False", + "DFFlagGamepadProcessMouseEvents": "False", + "DFFlagCrashTouchTransmitterIfRefDtor": "False", + "FFlagRenderUserGuiIn3DSpace": "True", + "FFlagScreenGuisClipDescendants": "False", + "FFlagUseNewNotificationPathLua": "True", + "FFlagStudioEditUDim2sWithoutBraces": "True", + "FFlagStudioConstructUDim2sWithUDims": "True", + "DFFlagGetPlayersByTeamEnabled": "True", + "DFFlagPlayerTeamPropertyEnabled": "True", + "FFlagStudioDefinePluginGlobalForLocalPlugins": "True", + "FFlagStudioEnableDockWidgetMaximize": "True", + "FFlagVideoDocumentationPluginEnabled": "True", + "FFlagStudioDisconnectPopuplaunchEditorSignals": "True", + "FFlagStudioPasteIntoMultipleObjects": "True", + "FFlagStudioBreakOnInfiniteLoops": "True", + "FFlagMessageOnLoadScriptValidationFail": "True", + "FFlagCSGTriangleCount": "True", + "FFlagStudioMockPurchasesEnabled": "True", + "FFlagStudioUseMarketplaceApiClient": "True", + "DFFlagUseGameAvatarTypeEnum": "False", + "FFlagStudioUsePlaySoloConfigurer": "True", + "DFFlagUseAvatarFetchAPI": "False", + "DFFlagSetHumanoidRegardlessOfNetworkOwnership": "True", + "FFlagFixStudioCursorJitter": "True", + "FFlagVoxelCompressedStorage": "True", + "FFlagSmoothTerrainLODEnabled": "True", + "FFlagBetterTabManagement": "True", + "DFFlagBlockCustomHttpHeaders": "False", + "FStringPlaceFilter_SoundChannelUseV2Implementation": "True;359173292;9528010;292439477;1818", + "FStringPlaceFilter_UseNewSetFMOD3D": "True;292439477;359173292;270607122;9528010;379011764;1818", + "FFlagStudioInsertAtTopCenterOfSelection": "True", + "FFlagGameExplorer9340FixEnabled": "True", + "DFFlagCloudEditRemoveEditorOnPlayerRemove": "True", + "FFlagWaitForChildTimeOut": "True", + "SFFlagTeamCreateReplicateCollisionFidelity": "True", + "FFlagStudioShowPropertiesByDefault": "True", + "FFlagStudioShowObjectExplorerByDefault": "True", + "FFlagStudioUpdateToolbarsOnFocus": "True", + "FFlagDeviceEmulationStatistics": "True", + "FFlagFixBoxSelectWithCtrl": "True", + "FFlagStudioTrimPropertyWhitespace": "True", + "FFlagStudioRenamePasteContextMenuOnMultipleSelection": "True", + "FFlagDebugCSGExportFailure": "False", + "FFlagFixCrashOnEmptyTextOnAutoComplete": "True", + "FFlagCancelInputOnGuiNavigation": "True", + "FFlagRemoveOldAnalyticsImplementation": "True", + "FFlagRemoveOldCountersImplementation": "True", + "FFlagUseNewAppBridgeStudio": "False", + "FFlagStudioAnalyticsRefactoring": "True", + "DFFlagRCCUseMarketplaceApiClient": "False", + "FFlagStudioIntellesenseOnAllMembersEnabled": "True", + "FFlagStudioRefreshSAResultsOnRemovingEnabled": "True", + "FFlagMeshPartUsageAnalytics": "True", + "FFlagTryAppendFileType": "True", + "FFlagStudioPreventTabStealFocus": "True", + "FFlagStudioNameMeshByFileName": "True", + "DFFlagDataStoreDisableReFetchingRecentKeys": "True", + "FFlagNewDefaultScriptSource": "True", + "FFlagStudioSanitizeFloatValues": "True", + "FFlagStudioEnableDebuggerPerfImprovements": "True", + "FFlagRecordForceStereo": "True", + "FFlagStudioVideoRecordFix": "True", + "FFlagStudioUseHttpsForUserInfo": "True", + "FFlagUseHttpsForGameserverAshx": "True", + "FFlagDisableScriptContextScriptsDisabled": "True", + "SFFlagTeamCreateReplicateTerrainRegion": "True", + "DFFlagDuplicateInstanceReferenceFix": "True", + "FFlagRakNetSupportIpV6": "False", + "FFlagUseToStringN": "False", + "FFlagUseFixedStepSlider": "True", + "FFlagStudioRenderRemotePlayerSelection": "True", + "FFlagStudioShiftRetainsResizeAspectRatio": "True", + "FFlagStudioCtrlPartResizeFromCenter": "True", + "FFlagStackTraceLinks": "True", + "FFlagStudioUpdateRestoreBehavior": "True", + "FFlagTouchTransmitterWeakPtr": "True", + "FFlagAdvancedRCCLoadFMODRetry": "True", + "FFlagAdvancedRCCLoadFMODReportDeviceInfo": "True", + "FFlagAdvancedRCCLoadFMODAttemptReportDeviceInfoOnFailure": "True", + "FFlagClientLoadFMODReportDeviceInfo": "True", + "DFIntReportDeviceInfoRate": 100, + "DFFlagSoundV2LogOnSetSoundId": "True", + "FFlagMouseUseUserInputServiceMouse": "False", + "SFFlagSoundChannelUseV2Implementation": "True", + "SFFlagUseNewSetFMOD3D": "True", + "FFlagCSGReportSuccessFailure": "True", + "FFlagLuaPreventCircularIncludes": "False", + "DFFlagPGSSolverUsesIslandizableCode": "True", + "DFFlagPGSSolverSimIslandsEnabled": "True" +} \ No newline at end of file diff --git a/clientsettings/cshared.php b/clientsettings/cshared.php new file mode 100644 index 0000000..fa7a3d1 --- /dev/null +++ b/clientsettings/cshared.php @@ -0,0 +1,9 @@ + +{ + "StatsGatheringScriptUrl": "", + "DFFlagLogAutoCamelCaseFixes": "True" +} \ No newline at end of file diff --git a/clientsettings/index.php b/clientsettings/index.php new file mode 100644 index 0000000..b0edcf7 --- /dev/null +++ b/clientsettings/index.php @@ -0,0 +1,5 @@ + +{ + "FFlagUS30484p1": "True", + "FIntUS30484p2": "10000", + "FFlagUS30484p3": "True", + "FLogRCCServiceInit": "0", + "FLogRCCServiceJobs": "0", + "FLogRCCDataModelInit": "0", + "DFIntTaskSchedulerThreadCountEnum": 16, + "DFFlagDebugCrashOnFailToLoadClientSettings": "False", + "DFFlagUseNewSecurityKeyApi": "True", + "DFFlagUseNewMemHashApi": "False", + "FIntRCCServiceThreadCount": 16, + "DFFlagUS30476": "True", + "FFlagUseDataDomain": "True", + "FFlagDep": "True", + "GoogleAnalyticsAccountPropertyID": "nah", + "GoogleAnalyticsAccountPropertyIDPlayer": "mm-nah", + "AllowVideoPreRoll": "True", + "FLogAsserts": "0", + "FLogCloseDataModel": "3", + "CaptureQTStudioCountersEnabled": "True", + "CaptureMFCStudioCountersEnabled": "True", + "CaptureCountersIntervalInMinutes": "5", + "FLogServiceVectorResize": "4", + "FLogServiceCreation": "4", + "AxisAdornmentGrabSize": "12", + "FFlagProcessAllPacketsPerStep": "True", + "FFlagUS14116": "True", + "FFlagBlockBlockNarrowPhaseRefactor": "True", + "FFlagEnableRubberBandSelection": "True", + "FFlagQtStudioScreenshotEnabled": "True", + "FFlagFixNoPhysicsGlitchWithGyro": "True", + "FLogFullRenderObjects": "0", + "PublishedProjectsPageHeight": "535", + "PublishedProjectsPageUrl": "/ide/publish", + "StartPageUrl": "/ide/welcome", + "FFlagOpenNewWindowsInDefaultBrowser": "True", + "FFlagOnScreenProfiler": "True", + "FFlagInitializeNewPlace": "True", + "PrizeAssetIDs": "0=110718551=Congratulations! Your success at publishing a place has earned you the Eggcellent Builder hat. You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!;1=110719580=Congratulations! Your exploration of ROBLOX Studio's models has led you to the Eggstreme Builder hat. You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!;2=110720049=Congratulations! Your contribution to ROBLOX's content catalog has earned you the Eggsultant Contributor hat. You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!;3=110790044=Congratulations! Your large experience in scripting has earned you the rare Eggscruciatingly Deviled Scripter hat. You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!;4=110790072=Congratulations! Your flexibility at finding eggs has led you to the Yolk's on Us hat! You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!;5=110790103=Congratulations! You've found the Eggsplosion hat! You can find this in your inventory on the website. Keep building to find more ROBLOX Studio Eggs!", + "PrizeAwarderURL": "/ostara/boon", + "MinNumberScriptExecutionsToGetPrize": "500", + "FFlagDebugCrashEnabled": "False", + "FLogHangDetection": "3", + "FFlagCharAnimationStats": "False", + "FFlagRenderOpenGLForcePOTTextures": "True", + "FFlagUseNewCameraZoomPath": "True", + "FFlagQTStudioPublishFailure": "True", + "ExcludeContactWithInteriorTerrainMinusYFace": "True", + "FFlagFixUphillClimb": "True", + "FFlagUseAveragedFloorHeight": "True", + "FFlagScaleExplosionLifetime": "True", + "PublishedProjectsPageWidth": "950", + "FFlagRenderFastClusterEverywhere": "True", + "FLogPlayerShutdownLuaTimeoutSeconds": "1", + "FFlagQtFixToolDragging": "True", + "FFlagSelectPartOnUndoRedo": "True", + "FFlagStatusBarProgress": "True", + "FFlagStudioCheckForUpgrade": "True", + "FFlagStudioInsertModelCounterEnabled": "True", + "FFlagStudioAuthenticationFailureCounterEnabled": "True", + "FFlagRenderCheckTextureContentProvider": "True", + "FFlagRenderLightGridEnabled": "True", + "FFlagStudioLightGridAPIVisible": "True", + "FFlagBetterSleepingJobErrorComputation": "True", + "FLogDXVideoMemory": "4", + "FFlagRenderNoLegacy": "True", + "FFlagStudioLightGridOnForNewPlaces": "True", + "FFlagPhysicsSkipRedundantJoinAll": "True", + "FFlagTerrainOptimizedLoad": "True", + "FFlagTerrainOptimizedStorage": "True", + "FFlagTerrainOptimizedCHS": "True", + "FFlagRenderGLES2": "True", + "FFlagStudioMacAddressValidationEnabled": "True", + "FFlagDoNotPassSunkEventsToPlayerMouse": "True", + "FFlagQtAutoSave": "True", + "FFlagRenderLoopExplicit": "True", + "FFlagStudioUseBinaryFormatForPlay": "True", + "FFlagPhysicsRemoveWorldAssemble_US16512": "True", + "FFlagNativeSafeChatRendering": "True", + "FFlagRenderNewMegaCluster": "True", + "FFlagAutoJumpForTouchDevices": "True", + "FLogOutlineBrightnessMin": "50", + "FLogOutlineBrightnessMax": "160", + "FLogOutlineThickness": "40", + "FFlagDE5511FixEnabled": "True", + "FFlagDE4423Fixed": "True", + "FFlagSymmetricContact": "True", + "FFlagLocalMD5": "True", + "FFlagStudioCookieParsingDisabled": "False", + "FFlagLastWakeTimeSleepingJobError": "True", + "FFlagPhysicsAllowAutoJointsWithSmallParts_DE6056": "True", + "FFlagPhysicsLockGroupDraggerHitPointOntoSurface_DE6174": "True", + "FFlagOutlineControlEnabled": "True", + "FFlagAllowCommentedScriptSigs": "True", + "FFlagDataModelUseBinaryFormatForSave": "True", + "FFlagStudioUseBinaryFormatForSave": "True", + "FFlagDebugAdornableCrash": "True", + "FFlagOverlayDataModelEnabled": "True", + "DFFlagFixInstanceParentDesyncBug": "True", + "FFlagPromoteAssemblyModifications": "False", + "FFlagFontSourceSans": "True", + "DFFlagCreateHumanoidRootNode": "True", + "FFlagRenderNewFonts": "True", + "FFlagStudioCookieDesegregation": "True", + "FFlagResponsiveJump": "True", + "FFlagGoogleAnalyticsTrackingEnabled": "True", + "FFlagNoCollideLadderFilter": "True", + "FFlagFlexibleTipping": "True", + "FFlagUseStrongerBalancer": "True", + "FFlagClampControllerVelocityMag": "True", + "DFFlagUseSaferChatMetadataLoading": "True", + "FFlagSinkActiveGuiObjectMouseEvents": "False", + "FLogLuaBridge": "2", + "DFFlagPromoteAssemblyModifications": "True", + "FFlagDeferredContacts": "True", + "FFlagFRMUse60FPSLockstepTable": "True", + "FFlagFRMAdjustForMultiCore": "True", + "FFlagPhysics60HZ": "True", + "FFlagQtRightClickContextMenu": "True", + "FFlagUseTopmostSettingToBringWindowToFront": "True", + "FFlagNewLightAPI": "True", + "FFlagRenderLightGridShadows": "True", + "FFlagRenderLightGridShadowsSmooth": "True", + "DFFlagSanitizeKeyframeUrl": "True", + "DFFlagDisableGetKeyframeSequence": "False", + "FFlagCreateServerScriptServiceInStudio": "True", + "FFlagCreateServerStorageInStudio": "True", + "FFlagCreateReplicatedStorageInStudio": "True", + "FFlagFilterEmoteChat": "True", + "DFFlagUseCharacterRootforCameraTarget": "True", + "FFlagImageRectEnabled": "True", + "FFlagNewWaterMaterialEnable": "True", + "DFFlagUserHttpAPIEnabled": "True", + "DFIntUserHttpAccessUserId0": "0", + "FFlagUserHttpAPIVisible": "True", + "FFlagCameraChangeHistory": "True", + "FFlagDE4640Fixed": "True", + "FFlagShowStreamingEnabledProp": "True", + "FFlagOptimizedDragger": "True", + "FFlagRenderNewMaterials": "True", + "FFlagRenderAnisotropy": "True", + "FFlagStudioInitializeViewOnPaint": "True", + "DFFlagPartsStreamingEnabled": "True", + "FFlagStudioLuaDebugger": "True", + "FFlagStudioLocalSpaceDragger": "True", + "FFlagGuiRotationEnabled": "True", + "FFlagDataStoreEnabled": "True", + "DFFlagDisableTeleportConfirmation": "True", + "DFFlagAllowTeleportFromServer": "True", + "DFFlagNonBlockingTeleport": "True", + "FFlagD3D9CrashOnError": "False", + "FFlagRibbonBarEnabled": "True", + "SFFlagInfiniteTerrain": "True", + "FFlagStudioScriptBlockAutocomplete": "True", + "FFlagRenderFixAnchoredLag": "True", + "DFFlagAllowAllUsersToUseHttpService": "True", + "GoogleAnalyticsAccountPropertyIDClient": "", + "FFlagSurfaceGuiVisible": "True", + "FFlagStudioIntellesenseEnabled": "True", + "FFlagAsyncPostMachineInfo": "True", + "FFlagModuleScriptsVisible": "True", + "FFlagModelPluginsEnabled": "True", + "FFlagGetUserIdFromPluginEnabled": "True", + "FFlagStudioPluginUIActionEnabled": "True", + "DFFlagRemoveAdornFromBucketInDtor": "True", + "FFlagRapidJSONEnabled": "True", + "DFFlagDE6959Fixed": "True", + "DFFlagScopedMutexOnJSONParser": "True", + "FFlagSupressNavOnTextBoxFocus": "True", + "DFFlagExplicitPostContentType": "True", + "DFFlagAddPlaceIdToAnimationRequests": "True", + "FFlagCreatePlaceEnabled": "True", + "DFFlagClientAdditionalPOSTHeaders": "True", + "FFlagEnableAnimationExport": "True", + "DFFlagAnimationAllowProdUrls": "True", + "FFlagGetUserIDFromPluginEnabled": "True", + "FFlagStudioContextualHelpEnabled": "True", + "FFlagLogServiceEnabled": "True", + "FFlagQtPlaySoloOptimization": "True", + "FFlagStudioBuildGui": "True", + "DFFlagListenForZVectorChanges": "True", + "DFFlagUserInputServiceProcessOnRender": "True", + "FFlagDE7421Fixed": "True", + "FFlagStudioExplorerActionsEnabledInScriptView": "True", + "FFlagHumanoidNetworkOptEnabled": "False", + "DFFlagEnableNPCServerAnimation": "True", + "DFFlagDataStoreUseUForGlobalDataStore": "True", + "DFFlagDataStoreAllowedForEveryone": "True", + "DFFlagBadTypeOnConnectErrorEnabled": "True", + "FFlagStudioRemoveUpdateUIThread": "True", + "FFlagPhysicsSkipUnnecessaryContactCreation": "False", + "FFlagUseNewHumanoidCache": "True", + "FFlagSecureReceiptsBackendEnabled": "True", + "FFlagOrderedDataStoreEnabled": "True", + "FFlagStudioLuaDebuggerGA": "True", + "FFlagNPSSetScriptDocsReadOnly": "True", + "FFlagRDBGHashStringComparison": "True", + "FFlagStudioDebuggerVisitDescendants": "True", + "FFlagDeprecateScriptInfoService": "True", + "FFlagIntellisenseScriptContextDatamodelSearchingEnabled": "True", + "FFlagSecureReceiptsFrontendEnabled": "True", + "DFFlagCreatePlaceEnabledForEveryone": "True", + "FFlagCreatePlaceInPlayerInventoryEnabled": "True", + "DFFlagAddRequestIdToDeveloperProductPurchases": "True", + "DFFlagUseYPCallInsteadOfPCallEnabled": "True", + "FFlagStudioMouseOffsetFixEnabled": "True", + "DFFlagPlaceValidation": "True", + "FFlagReconstructAssetUrl": "True", + "FFlagUseNewSoundEngine": "True", + "FIntMinMillisecondLengthForLongSoundChannel": "5000", + "FFlagStudioHideInsertedServices": "True", + "FFlagStudioAlwaysSetActionEnabledState": "True", + "FFlagRenderNew": "True", + "FIntRenderNewPercentWin": "100", + "FIntRenderNewPercentMac": "100", + "FLogGraphics": "6", + "FFlagStudioInSyncWebKitAuthentication": "False", + "DFFlagDisallowHopperServerScriptReplication": "True", + "FFlagInterpolationFix": "False", + "FFlagHeartbeatAt60Hz": "False", + "DFFlagFixProcessReceiptValueTypes": "True", + "DFFlagPhysicsSkipUnnecessaryContactCreation": "True", + "FFlagStudioLiveCoding": "True", + "FFlagPlayerHumanoidStep60Hz": "True", + "DFFlagCrispFilteringEnabled": "False", + "SFFlagProtocolSynchronization": "True", + "FFlagUserInputServicePipelineStudio": "True", + "FFlagUserInputServicePipelineWindowsClient": "True", + "FFlagUserInputServicePipelineMacClient": "True", + "FFlagStudioKeyboardMouseConfig": "True", + "DFFlagLogServiceEnabled": "True", + "DFFlagLoadAnimationsThroughInsertService": "True", + "FFlagFRMFogEnabled": "True", + "FLogBrowserActivity": "3", + "DFFlagPhysicsPacketAlwaysUseCurrentTime": "True", + "FFlagFixedStudioRotateTool": "True", + "FFlagRibbonBarEnabledGA": "True", + "FFlagRenderSafeChat": "False", + "DFFlagPhysicsAllowSimRadiusToDecreaseToOne": "True", + "DFFlagPhysicsAggressiveSimRadiusReduction": "True", + "DFFlagLuaYieldErrorNoResumeEnabled": "True", + "DFFlagEnableJointCache": "False", + "DFFlagOnCloseTimeoutEnabled": "True", + "FFlagStudioQuickInsertEnabled": "True", + "FFlagStudioPropertiesRespectCollisionToggle": "True", + "FFlagTweenServiceUsesRenderStep": "True", + "FFlagUseNewSoundEngine3dFix": "True", + "FFlagDebugUseDefaultGlobalSettings": "True", + "FFlagStudioMiddleMouseTrackCamera": "False", + "FFlagTurnOffiOSNativeControls": "True", + "DFFlagUseNewHumanoidHealthGui": "True", + "DFFlagLoggingConsoleEnabled": "True", + "DFFlagAllowModuleLoadingFromAssetId": "True", + "FFlagStudioZoomExtentsExplorerFixEnabled": "True", + "FFlagLuaDebuggerBreakOnError": "True", + "FFlagRetentionTrackingEnabled": "True", + "FFlagShowAlmostAllItemsInExplorer": "True", + "FFlagStudioFindInAllScriptsEnabled": "True", + "FFlagImprovedNameOcclusion": "True", + "FFlagHumanoidMoveToDefaultValueEnabled": "True", + "FFlagEnableDisplayDistances": "True", + "FFlagUseMinMaxZoomDistance": "True", + "SFFlagAllowPhysicsPacketCompression": "False", + "FFlagStudioOneClickColorPickerEnabled": "True", + "DFFlagHumanoidMoveToDefaultValueEnabled": "True", + "VideoPreRollWaitTimeSeconds": "45", + "FFlagBalancingRateLimit": "True", + "FFlagLadderCheckRate": "True", + "FFlagStateSpecificAutoJump": "True", + "SFFlagOneWaySimRadiusReplication": "True", + "DFFlagApiDictionaryCompression": "True", + "SFFlagPathBasedPartMovement": "True", + "FFlagEnsureInputIsCorrectState": "False", + "DFFlagLuaLoadStringStrictSecurity": "True", + "DFFlagCrossPacketCompression": "True", + "FFlagWorkspaceLoadStringEnabledHidden": "True", + "FFlagStudioPasteAsSiblingEnabled": "True", + "FFlagStudioDuplicateActionEnabled": "True", + "FFlagPreventInterpolationOnCFrameChange": "True", + "FLogNetworkPacketsReceive": "5", + "FFlagPlayPauseFix": "True", + "DFFlagCrashOnNetworkPacketError": "False", + "FFlagHumanoidStateInterfaces": "True", + "FFlagRenderDownloadAssets": "True", + "FFlagBreakOnErrorConfirmationDialog": "True", + "FFlagStudioAnalyticsEnabled": "True", + "FFlagAutoRotateFlag": "True", + "DFFlagUseImprovedLadderClimb": "True", + "FFlagUseCameraOffset": "True", + "FFlagRenderBlobShadows": "True", + "DFFlagWebParserDisableInstances": "False", + "FFlagStudioNewWiki": "True", + "DFFlagLogPacketErrorDetails": "False", + "FFlagLimitHorizontalDragForce": "True", + "FFlagEnableNonleathalExplosions": "True", + "DFFlagCreateSeatWeldOnServer": "True", + "FFlagGraphicsUseRetina": "True", + "FFlagDynamicEnvmapEnabled": "True", + "DFFlagDeferredTouchReplication": "True", + "DFFlagCreatePlayerGuiEarlier": "True", + "DFFlagProjectileOwnershipOptimization": "True", + "DFFlagLoadSourceForCoreScriptsBeforeInserting": "False", + "GoogleAnalyticsLoadStudio": "1", + "DFFlagTaskSchedulerFindJobOpt": "True", + "SFFlagPreventInterpolationOnCFrameChange": "True", + "DFIntNumPhysicsPacketsPerStep": "2", + "DFFlagDataStoreUrlEncodingEnabled": "True", + "FFlagShowWebPlaceNameOnTabWhenOpeningFromWeb": "True", + "DFFlagTrackTimesScriptLoadedFromLinkedSource": "True", + "FFlagToggleDevConsoleThroughChatCommandEnabled": "True", + "FFlagEnableFullMonitorsResolution": "True", + "DFFlagAlwaysUseHumanoidMass": "True", + "DFFlagUseStrongerGroundControl": "True", + "DFFlagCorrectlyReportSpeedOnRunStart": "True", + "FFlagLuaDebuggerImprovedToolTip": "True", + "FFlagLuaDebuggerPopulateFuncName": "True", + "FFlagLuaDebuggerNewCodeFlow": "True", + "DFFlagValidateCharacterAppearanceUrl": "false", + "FFlagStudioQuickAccessCustomization": "True", + "DFFlagTaskSchedulerUpdateJobPriorityOnWake": "True", + "DFFlagTaskSchedulerNotUpdateErrorOnPreStep": "True", + "FFlagWikiSelectionSearch": "True", + "DFIntTaskSchedularBatchErrorCalcFPS": "1200", + "FFlagSuppressNavOnTextBoxFocus": "False", + "FFlagEnabledMouseIconStack": "True", + "DFFlagFastClone": "True", + "DFFlagLuaNoTailCalls": "True", + "DFFlagFilterStreamingProps": "True", + "DFFlagNetworkOwnerOptEnabled": "True", + "DFFlagPathfindingEnabled": "True", + "FFlagEnableiOSSettingsLeave": "True", + "FFlagUseFollowCamera": "True", + "FFlagDefaultToFollowCameraOnTouch": "True", + "DFFlagAllowMoveToInMouseLookMove": "True", + "DFFlagAllowHumanoidDecalTransparency": "True", + "DFFlagSupportCsrfHeaders": "True", + "DFFlagConfigureInsertServiceFromSettings": "True", + "FFlagPathfindingClientComputeEnabled": "True", + "DFFlagLuaResumeSupportsCeeCalls": "True", + "DFFlagPhysicsSenderErrorCalcOpt": "True", + "DFFlagClearPlayerReceivingServerLogsOnLeave": "True", + "DFFlagConsoleCodeExecutionEnabled": "True", + "DFFlagCSGDictionaryReplication": "True", + "FFlagCSGToolsEnabled": "True", + "FFlagCSGDictionaryServiceEnabled": "True", + "FFlagCSGMeshRenderEnable": "True", + "FFlagCSGChangeHistory": "True", + "FFlagCSGMeshColorEnable": "True", + "FFlagCSGMeshColorToolsEnabled": "True", + "FFlagCSGScaleEnabled": "True", + "FFlagCylinderUsesConstantTessellation": "True", + "FFlagStudioDraggersScaleFixes": "True", + "FFlagCSGDecalsEnabled": "True", + "FFlagCSGMigrateChildData": "True", + "SFFlagBinaryStringReplicationFix": "True", + "FFlagHummanoidScaleEnable": "True", + "FFlagStudioDataModelIsStudioFix": "True", + "DFFlagWebParserEnforceASCIIEnabled": "True", + "DFFlagScriptDefaultSourceIsEmpty": "True", + "FFlagFixCaptureFocusInput": "True", + "FFlagFireUserInputServiceEventsAfterDMEvents": "True", + "FFlagVectorErrorOnNilArithmetic": "True", + "FFlagFontSmoothScalling": "True", + "DFFlagUseImageColor": "True", + "FFlagStopNoPhysicsStrafe": "True", + "DFFlagDebugLogNetworkErrorToDB": "False", + "FFlagLowQMaterialsEnable": "True", + "FFLagEnableFullMonitorsResolution": "True", + "FFlagStudioChildProcessCleanEnabled": "True", + "DFFlagAllowFullModelsWhenLoadingModules": "True", + "DFFlagRealWinInetHttpCacheBypassingEnabled": "True", + "FFlagNewUniverseInfoEndpointEnabled": "True", + "FFlagGameExplorerEnabled": "True", + "FFlagStudioUseBinaryFormatForModelPublish": "True", + "FFlagGraphicsFeatureLvlStatsEnable": "True", + "FFlagStudioEnableWebKitPlugins": "True", + "DFFlagSendHumanoidTouchedSignal": "True", + "DFFlagReduceHumanoidBounce": "True", + "DFFlagUseNewSounds": "True", + "FFlagFixHumanoidRootPartCollision": "True", + "FFlagEnableAndroidMenuLeave": "True", + "FFlagOnlyProcessGestureEventsWhenSunk": "True", + "FFlagAdServiceReportImpressions": "True", + "FFlagStudioUseExtendedHTTPTimeout": "True", + "FFlagStudioSeparateActionByActivationMethod": "False", + "DFFlagPhysicsSenderThrottleBasedOnBufferHealth": "True", + "DFFlagGetGroupInfoEnabled": "True", + "DFFlagGetGroupRelationsEnabled": "True", + "FStringPlaceFilter_StateBasedAnimationReplication": "True;175953385;174810327;181219650", + "SFFlagTopRepContSync": "True", + "FFlagStudioUseBinaryFormatForModelSave": "True", + "EnableFullMonitorsResolution": "True", + "DFFlagRenderSteppedServerExceptionEnabled": "True", + "FFlagUseWindowSizeFromGameSettings": "True", + "DFFlagCheckStudioApiAccess": "True", + "FFlagGameExplorerPublishEnabled": "True", + "DFFlagKeepXmlIdsBetweenLoads": "True", + "DFFlagReadXmlCDataEnabled": "True", + "FFlagStudioRemoveToolSounds": "True", + "FFlagStudioOneStudGridDefault": "True", + "FFlagStudioPartSymmetricByDefault": "True", + "FFlagStudioIncreasedBaseplateSize": "True", + "FFlagSkipSilentAudioOps": "True", + "SFFlagGuid64Bit": "False", + "FIntValidateLauncherPercent": "100", + "FFlagCSGDataLossFixEnabled": "True", + "DFStringRobloxAnalyticsURL": "http://ecsv2.gtoria.net/", + "DFFlagRobloxAnalyticsTrackingEnabled": "True", + "FFlagStudioOpenLastSaved": "False", + "FFlagStudioShowTutorialsByDefault": "True", + "FFlagStudioForceToolboxSize": "True", + "FFlagStudioExplorerDisabledByDefault": "True", + "FFlagStudioDefaultWidgetSizeChangesEnabled": "True", + "FFlagStudioUseScriptAnalyzer": "True", + "FFlagNoClimbPeople": "True", + "DFFlagAnimationFormatAssetId": "True", + "FFlagGetCorrectScreenResolutionFaster": "True", + "DFFlagFixTouchEndedReporting": "False", + "FFlagStudioTeleportPlaySolo": "True", + "FFlagCSGRemoveScriptScaleRestriction": "True", + "FFlagStudioDE7928FixEnabled": "True", + "FFlagDE8768FixEnabled": "True", + "FFlagStudioDE9108FixEnabled": "True", + "FFlagStudioPlaySoloMapDebuggerData": "True", + "FFlagLuaDebuggerCloneDebugger": "True", + "FFlagRenderLightgridInPerformEnable": "True", + "SFFlagStateBasedAnimationReplication": "True", + "FFlagVolumeControlInGameEnabled": "True", + "FFlagGameConfigurerUseStatsService": "True", + "FFlagStudioUseHttpAuthentication": "True", + "FFlagDetectTemplatesWhenSettingUpGameExplorerEnabled": "True", + "FFlagEntityNameEditingEnabled": "True", + "FFlagNewCreatePlaceFlowEnabled": "True", + "FFlagFakePlayableDevices": "False", + "FFlagMutePreRollSoundService": "True", + "DFFlagBodyMoverParentingFix": "True", + "DFFlagBroadcastServerOnAllInterfaces": "True", + "HttpUseCurlPercentageWinClient": "100", + "HttpUseCurlPercentageMacClient": "100", + "HttpUseCurlPercentageWinStudio": "100", + "HttpUseCurlPercentageMacStudio": "100", + "SFFlagReplicatedFirstEnabled": "True", + "DFFlagCSGShrinkForMargin": "True", + "FFlagPhysicsBulletConnectorPointRecalc": "True", + "DFIntBulletContactBreakThresholdPercent": "200", + "DFIntBulletContactBreakOrthogonalThresholdPercent": "200", + "FFlagPhysicsBulletConnectorMatching": "True", + "FFlagPhysicsBulletUseProximityMatching": "False", + "FFlagPhysicsCSGUsesBullet": "True", + "DFFlagCSGPhysicsDeserializeRefactor": "True", + "FFlagWedgeEnableDecalOnTop": "True", + "FFlagFrustumTestGUI": "True", + "FFlagFeatureLvlsDX11BeforeDeviceCreate": "True", + "FFlagStudioPasteSyncEnabled": "True", + "FFlagResetMouseCursorOnToolUnequip": "True", + "DFFlagUpdateCameraTarget": "True", + "DFFlagFixGhostClimb": "True", + "DFFlagUseStarterPlayer": "True", + "FFlagStudioFindCrashFixEnabled": "True", + "FFlagFixPartOffset": "True", + "DFFlagLuaCloseUpvalues": "True", + "FFlagRenderTextureCompositorUseBudgetForSize": "True", + "FFlagAllowOutOfBoxAssets": "False", + "FFlagRemoveTintingWhenActiveIsFalseOnImageButton": "True", + "FFlagStudioModuleScriptDefaultContents": "True", + "FFlagStudioHomeKeyChangeEnabled": "True", + "FFlagStudioOpenStartPageForLogin": "True", + "FFlagStudioNativeKeepSavedChanges": "True", + "FFlagSerializeCurrentlyOpenPlaceWhenPublishingGame": "True", + "FFlagGameNameLabelEnabled": "True", + "FFlagStudioValidateBootstrapper": "True", + "FFlagRakNetReadFast": "True", + "DFFlagPhysicsSenderSleepingUpdate": "True", + "FFlagUseShortShingles": "True", + "FFlagKKTChecks": "False", + "DFFlagUseApiProxyThrottling": "True", + "DFFlagValidateSetCharacter": "True", + "DFFlagUpdateHumanoidSimBodyInComputeForce": "True", + "DFFlagNetworkPendingItemsPreserveTimestamp": "True", + "FFlagStudioCSGRotationalFix": "True", + "FFlagNewLoadingScreen": "True", + "FFlagScrollingFrameOverridesButtonsOnTouch": "True", + "DFFlagStreamLargeAudioFiles": "True", + "DFFlagNewLuaChatScript": "True", + "DFFlagLoopedDefaultHumanoidAnimation": "True", + "FFlagSoundsRespectDelayedStop": "False", + "DFFlagCSGPhysicsErrorCatchingEnabled": "True", + "DFFlagFireStoppedAnimSignal": "True", + "FFlagStudioFixToolboxReload": "True", + "FFlagCSGDecalsV2": "True", + "FFlagLocalOptimizer": "True", + "DFFlagClearFailedUrlsWhenClearingCacheEnabled": "True", + "DFFlagSupportNamedAssetsShortcutUrl": "True", + "DFFlagUseW3CURIParser": "True", + "DFFlagContentProviderHttpCaching": "True", + "FFlagNoWallClimb": "False", + "FFlagSmoothMouseLock": "False", + "DFFlagCSGPhysicsNanPrevention": "True", + "FFlagStudioDE9818FixEnabled": "True", + "FFlagGameExplorerImagesEnabled": "True", + "FFlagStudioInsertOrientationFix": "True", + "FFlagStudioTabOrderingEnabled": "True", + "FFlagFramerateDeviationDroppedReport": "True", + "FFlagModuleScriptsPerVmEnabled": "False", + "FFlagGameExplorerImagesInsertEnabled": "True", + "FFlagTexturePropertyWidgetEnabled": "True", + "FFlagReloadAllImagesOnDataReload": "True", + "FFlagModuleScriptsPerVmEnabledFix2": "True", + "DFFlagFixBufferZoneContainsCheck": "False", + "FFlagStudioPlaceAssetFromToolbox": "True", + "FFlagChannelMasterMuting": "True", + "FFlagStudioUseDelayedSyntaxCheck": "True", + "FFlagStudioCommandLineSaveEditText": "True", + "FFlagStudioAddHelpInContextMenu": "True", + "DFIntHttpCacheCleanMinFilesRequired": "10000", + "DFIntHttpCacheCleanMaxFilesToKeep": "7500", + "FFlagCSGVoxelizer": "True", + "DFFlagCheckApiAccessInTransactionProcessing": "True", + "FFlagBindPurchaseValidateCallbackInMarketplaceService": "True", + "FFlagSetDataModelUniverseIdAfterPublishing": "True", + "FFlagOpenScriptWorksOnModulesEnabled": "True", + "FFlagStudioRibbonBarNewLayout": "True", + "FFlagStudioRibbonBarLayoutFixes": "True", + "FFlagStudioPlaceOnlineIndicator": "True", + "FFlagRenderWangTiles": "True", + "FFlagDisableBadUrl": "True", + "FFlagPrimalSolverLogBarrierIP": "True", + "FFlagDualSolverSimplex": "True", + "FFlagPrimalSolverSimplex": "True", + "FIntNumSmoothingPasses": "3", + "FFlagVerifyConnection": "True", + "FIntRegLambda": "1400", + "FFlagScriptAnalyzerPlaceholder": "True", + "FFlagStudioCSGAssets": "True", + "FFlagCSGStripPublishedData": "True", + "DFFlagRaycastReturnSurfaceNormal": "True", + "FFlagMoveGameExplorerActionsIntoContextMenu": "True", + "FFlagStudioAdvanceCookieExpirationBugFixEnabled": "True", + "FFlagNewBackpackScript": "True", + "FFlagNewPlayerListScript": "True", + "FFlagGameNameAtTopOfExplorer": "True", + "FFlagStudioActActionsAsTools": "True", + "FFlagStudioInsertAtMouseClick": "True", + "FFlagStopLoadingStockSounds": "True", + "DFFlagFixTimePositionReplication": "True", + "DFFlagHttpReportStatistics": "True", + "DFFlagEnableChatTestingInStudio": "True", + "DFIntHttpSendStatsEveryXSeconds": "300", + "FLogStepAnimatedJoints": "5", + "DFFlagLuaDisconnectFailingSlots": "False", + "DFFlagEnsureSoundPosIsUpdated": "True", + "DFFlagLoadStarterGearEarlier": "False", + "DFFlagBlockUsersInLuaChat": "True", + "FFlagRibbonPartInsertNotAllowedInModel": "True", + "DFFlagUsePlayerScripts": "True", + "DFFlagUserAccessUserSettings": "True", + "DFFlagUseLuaCameraAndControl": "True", + "DFFlagUseLuaPCInput": "True", + "DFFlagFixLuaMoveDirection": "True", + "DFFlagUseDecalLocalTransparencyModifier": "True", + "DFFlagUseFolder": "True", + "DFFlagUsePreferredSpawnInPlaySoloTeleport": "True", + "DFFlagFilterAddSelectionToSameDataModel": "False", + "FFlagGameExplorerAutofillImageNameFromFileName": "True", + "FFlagGameExplorerBulkImageUpload": "True", + "FFlagStudioAllowAudioSettings": "True", + "DFFlagUsePlayerInGroupLuaChat": "True", + "FFlagStudioDecalPasteFix": "True", + "FFlagStudioCtrlTabDocSwitchEnabled": "True", + "DFIntDraggerMaxMovePercent": "60", + "FFlagUseUniverseGetInfoCallToDetermineUniverseAccess": "True", + "FFlagMaxFriendsCount": "True", + "DFIntPercentApiRequestsRecordGoogleAnalytics": "0", + "FFlagSelectSpinlock": "True", + "FFlagFastZlibPath": "True", + "DFFlagWriteXmlCDataEnabled": "True", + "DFFlagUseSpawnPointOrientation": "True", + "DFFlagUsePlayerSpawnPoint": "True", + "DFFlagCSGPhysicsRecalculateBadContactsInConnectors": "True", + "FFlagStudioPartAlignmentChangeEnabled": "True", + "FFlagStudioToolBoxModelDragFix": "True", + "DFFlagOrder66": "False", + "FFlagCloudIconFixEnabled": "True", + "DFFlagFixHealthReplication": "True", + "DFFlagReplicateAnimationSpeed": "True", + "FFlagSurfaceLightEnabled": "True", + "FFlagLuaFollowers": "True", + "FFlagNewNotificationsScript": "True", + "FFlagStudioSendMouseIdleToPluginMouse": "True", + "DFFlagPhysicsOptimizeAssemblyHistory": "True", + "DFFlagPhysicsOptimizeBallBallContact": "True", + "DFFlagUseNewBubbleSkin": "True", + "DFFlagUse9FrameBackgroundTransparency": "True", + "DFFlagCheckForHeadHit": "False", + "DFFlagUseHttpsForAllCalls": "True", + "DFFlagLoadCoreModules": "True", + "FFlagStudioRecentSavesEnabled": "True", + "FFlagStudioToolBoxInsertUseRayTrace": "True", + "FFlagInterpolationUseWightedDelay": "True", + "FFlagUseInGameTopBar": "True", + "FFlagNewPurchaseScript": "True", + "FFlagStudioEnableGamepadSupport": "True", + "FFlagStudioRemoveDuplicateParts": "True", + "FFlagStudioLaunchDecalToolAfterDrag": "True", + "DFFlagHumanoidFloorPVUpdateSignal": "True", + "DFFlagHumanoidFloorDetectTeleport": "True", + "DFFlagHumanoidFloorForceBufferZone": "False", + "DFFlagHumanoidFloorManualDeltaUpdateManagment": "True", + "DFFlagHumanoidFloorManualFrictionLimitation": "True", + "FStringPlaceFilter_InterpolationTimingFix": "True;208770506", + "DFFlagUpdateHumanoidNameAndHealth": "True", + "DFFlagEnableHumanoidDisplayDistances": "True", + "FFlagFixTouchInputEventStates": "False", + "DFFlagInterpolationTimingFix": "True", + "FIntRenderGBufferMinQLvl": "16", + "FFlagResizeGuiOnStep": "True", + "FFlagDontFireFakeMouseEventsOnUIS": "True", + "FFlagCameraUseOwnViewport": "True", + "FFlagGameExplorerMoveImagesUnderAssetsGroup": "True", + "DFFlagNetworkFilterAllowToolWelds": "True", + "DFIntHttpInfluxHundredthsPercentage": "5", + "DFStringHttpInfluxURL": "http://ec2-54-84-170-153.compute-1.amazonaws.com:8086", + "DFStringHttpInfluxDatabase": "prod", + "DFStringHttpInfluxUser": "rob", + "DFStringHttpInfluxPassword": "faster1Play", + "FFlagStudioSpawnLocationsDefaultValues": "True", + "FFlagStudioDE11536FixEnabled": "True", + "FFlagStudioRibbonGroupResizeFixEnabled": "True", + "FFlagGradientStep": "True", + "FFlagUseNewContentProvider": "False", + "SFFlagEquipToolOnClient": "True", + "FFlagStartWindowMaximizedDefault": "True", + "FFlagUseNewKeyboardHandling": "True", + "FFlagCameraZoomNoModifier": "True", + "DFFlagRemoteValidateSubscribersError": "True", + "FFlagNewMenuSettingsScript": "True", + "DFFlagHttpCurlSanitizeUrl": "True", + "DFFlagRemoveDataModelDependenceInWaitForChild": "True", + "FFlagFilterAddSelectionToSameDataModel": "True", + "DFFlagUseCanManageApiToDetermineConsoleAccess": "True", + "DFIntMoveInGameChatToTopPlaceId": "1", + "FFlagStudioProgressIndicatorForInsertEnabled": "True", + "FFlagTerrainLazyGrid": "True", + "FFlagHintsRenderInUserGuiRect": "True", + "DFFlagCustomEmitterInstanceEnabled": "True", + "FFlagCustomEmitterRenderEnabled": "True", + "FFlagCustomEmitterLuaTypesEnabled": "True", + "FFlagCallSetFocusFromCorrectThread": "True", + "FFlagFastRevert": "True", + "FFlagSleepBeforeSpinlock": "True", + "FFlagSparseCheckFastFail": "True", + "FFlagStudioSmoothTerrainPlugin": "True", + "FFlagStudioLoadPluginsLate": "True", + "FFlagStudioInsertIntoStarterPack": "True", + "FFlagStudioIgnoreSSLErrors": "True", + "DFFlagFixJointReparentingDE11763": "True", + "DFFlagPhysicsInvalidateContactCache": "True", + "FFlagLuaMathNoise": "True", + "FFlagArcHandlesBidirectional": "True", + "FFlagChangeHistoryFixPendingChanges": "True", + "DFFlagWorkspaceSkipTerrainRaycastForSurfaceGui": "True", + "FFlagStudioBatchItemMapAddChild": "True", + "FFlagRenderCameraFocusFix": "True", + "DFFlagReplicatorWorkspaceProperties": "True", + "FFlagDirectX11Enable": "True", + "FFlagCheckDegenerateCases": "True", + "DFFlagUseServerCoreScripts": "True", + "DFFlagCorrectFloorNormal": "True", + "FFlagNewBadgeServiceUrlEnabled": "True", + "FFlagBubbleChatbarDocksAtTop": "True", + "FFlagSmoothTerrainClient": "True", + "FFlagLuaUseBuiltinEqForEnum": "True", + "FFlagPlaceLauncherThreadCheckDmClosed": "True", + "DFFlagAppendTrackerIdToTeleportUrl": "True", + "FFlagPlayerMouseRespectGuiOffset": "True", + "DFFlagReportElevatedPhysicsFPSToGA": "True", + "DFFlagPreventReturnOfElevatedPhysicsFPS": "True", + "FFlagStudioIgnoreMouseMoveOnIdle": "True", + "FFlagStudioDraggerFixes": "True", + "FLogUseLuaMemoryPool": "0", + "FFlagCSGNewTriangulate": "True", + "DFFlagLuaFixResumeWaiting": "True", + "FFlagFRMInStudio": "True", + "DFFlagFixLadderClimbSpeed": "True", + "DFFlagNoWalkAnimWeld": "False", + "DFFlagImprovedKick": "True", + "FFlagRenderFixLightGridDirty": "True", + "FFlagLoadLinkedScriptsOnDataModelLoad": "True", + "FFlagFixMeshOffset": "True", + "FIntLaunchInfluxHundredthsPercentage": "0", + "DFIntJoinInfluxHundredthsPercentage": "100", + "FFlagSmoothTerrain": "True", + "FFlagNewVehicleHud": "True", + "DFFlagHumanoidStandOnSeatDestroyed": "True", + "DFFlagGuiBase3dReplicateColor3WithBrickColor": "True", + "FFlagTaskSchedulerCyclicExecutiveStudio": "True", + "DFIntElevatedPhysicsFPSReportThresholdTenths": "585", + "DFIntExpireMarketPlaceServiceCacheSeconds": "60", + "DFFlagEnableMarketPlaceServiceCaching": "True", + "DFFlagUseNewAnalyticsApi": "True", + "DFFlagSmoothTerrainDebounceUpdates": "True", + "FFlagStudioAuthenticationCleanup": "True", + "FFlagRenderFixGBufferLOD": "True", + "FFlagStudioDraggerCrashFixEnabled": "True", + "FFlagDraggerCrashFixEnabled": "True", + "DFFlagEnableRapidJSONParser": "True", + "DFFlagPushLuaWorldRayOriginToNearClipPlane": "True", + "FFlagLoadTimeModificationTestFlag": "True", + "DFFlagPhysicsFastSmoothTerrainUpdate": "True", + "DFFlagSmoothTerrainPhysicsExpandPrimitiveOptimal": "True", + "DFFlagFixBytesOnJoinReporting": "True", + "FFlagRenderGBufferEverywhere": "False", + "DFFlagSmoothTerrainPhysicsRayAabbExact": "True", + "DFIntSmoothTerrainPhysicsRayAabbSlop": "1", + "DFIntMaxClusterKBPerSecond": "300", + "FLogLuaAssert": "0", + "FFlagSmoothTerrainCountCellVolume": "True", + "DFFlagSmoothTerrainWorldToCellUseDiagonals": "True", + "DFFlagFireSelectionChangeOncePerChange": "True", + "FIntLuaAssertCrash": "0", + "FFlagAlternateFontKerning": "True", + "FFlagRenderFixCameraFocus": "False", + "DFFlagCSGPhysicsSphereRotationIdentity": "True", + "DFFlagCSGPhysicsRefreshContactsManually": "True", + "FFlagStudioUndoEnabledForEdit": "True", + "DFIntLuaChatFloodCheckMessages": "7", + "DFIntLuaChatFloodCheckInterval": "15", + "FFlagLuaChatFiltering": "True", + "FFlagMobileToggleChatVisibleIcon": "True", + "FFlagGlowEnabled": "True", + "FFlagStudioDE9132FixEnabled": "True", + "DFFlagHttpCurlHandle301": "True", + "FFlagClientABTestingEnabled": "False", + "FFlagStudioSmoothTerrainForNewPlaces": "True", + "FFlagUsePGSSolver": "True", + "FFlagSimplifyKeyboardInputPath": "False", + "FFlagNewInGameDevConsole": "True", + "DFFlagUseNewFullscreenLogic": "True", + "DFFlagGetUserIdEnabled": "True", + "DFFlagGetUserNameEnabled": "True", + "DFFlagEnableAnimationInformationAccess": "True", + "DFFlagEnableAnimationTrackExtendedAPI": "True", + "FFlagRequestServerStatsV2Enabled": "True", + "FFlagPhysicsPreventGroupDraggerPlacementToMinus400_DE6267": "True", + "FFlagSpecificUserdataTypeErrors": "True", + "DFFlagScrollingFrameDraggingFix": "True", + "FFlagAutodetectCPU": "True", + "DFFlagSetRenderedFrameOnClumpChanged": "True", + "DFFlagDisableTimeoutDuringJoin": "True", + "DFFlagDesiredAltitudeDefaultInf": "True", + "DFFlagRCCDE13316CrashFix": "True", + "DFFlagUseStarterPlayerGA": "True", + "FFlagScrollingFrameMouseUpFix": "True", + "DFFlagDebrisServiceUseDestroy": "True", + "DFFlagAccessUserFeatureSetting": "True", + "DFFlagAllowBindActivate": "True", + "FFlagEnableControllerGuiSelection": "True", + "FFlagUseNewSoftwareMouseRender": "True", + "DFFlagDoNotHoldTagItemsForInitialData": "True", + "FFlagAltSpinlock": "True", + "FFlagSpinlock": "True", + "FFlagGraphicsGLReduceLatency": "True", + "DFFlagMovingHumananoidWakesFloor": "True", + "DFFlagSetNetworkOwnerAPIEnabled": "True", + "DFFlagSetNetworkOwnerAPIEnabledV2": "True", + "DFFlagGetFriendsEnabled": "True", + "DFFlagGetFriendsOnlineEnabled": "True", + "DFFlagUseNewTextBoxLogic": "True", + "FFlagOnScreenProfilerGPU": "True", + "FFlagConfigurableLineThickness": "True", + "DFFlagSpawnPointEnableProperty": "True", + "DFFlagConfigurableFallenPartDestroyHeight": "True", + "DFFlagMiddleMouseButtonEvent": "True", + "DFFlagEnablePreloadAsync": "True", + "DFFlagFoldersInGUIs": "True", + "DFFlagUserUseLuaVehicleController": "True", + "DFIntAndroidInfluxHundredthsPercentage": "0", + "FStringPlaceFilter_SetNetworkOwnerAPIEnabled": "True;13822889;257857728", + "FStringPlaceFilter_SetNetworkOwnerAPIEnabledV2": "True;13822889;257857728", + "DFFlagNoOwnershipLogicOnKernelJoint": "True", + "DFFlagEnableGetPlayingAnimationTracks": "True", + "FFlagCyclicExecutivePriorityJobs": "True", + "DFIntMaxMissedWorldStepsRemembered": "16", + "DFIntMacInfluxHundredthsPercentage": "0", + "DFIntiOSInfluxHundredthsPercentage": "100", + "FFlagStudioLockServiceParents": "True", + "DFFlagFirePlayerAddedAndPlayerRemovingOnClient": "True", + "DFFlagRecursiveWakeForBodyMovers": "True", + "DFFlagEnableHumanoidSetStateEnabled": "True", + "DFFlagSoundEndedEnabled": "True", + "DFFlagUseIntersectingOthersForSpawnEnabled": "True", + "DFFlagMoveToDontAlignToGrid": "True", + "DFFlagIsBestFriendsWithReturnFriendsWith": "True", + "DFFlagPlayerOwnsAssetFalseForInvalidUsers": "True", + "DFFlagIsUrlCheckAssetStringLength": "True", + "DFFlagEnableGoodbyeDialog": "True", + "DFFlagEnableReverseAnimations": "True", + "DFFlagEnableAnimationSetTimePosition": "True", + "DFFlagEnableMobileAutoJumpFlag": "True", + "DFFlagHttpDelaySendInfluxStats": "True", + "DFFlagDisableRequestMarker": "True", + "DFFlagDisableCharacterRequest": "True", + "FFlagStudioCollapsibleTutorials": "True", + "FStringStudioTutorialsUrl": "http://wiki.roblox.com/index.php?title=Studio_Tutorials_Test&studiomode=true", + "FStringStudioTutorialsTOCUrl": "http://wiki.roblox.com/index.php?title=Studio_Tutorials_Landing&studiomode=true", + "FFlagSandboxHash": "True", + "FFlagDE14316CrashFix": "True", + "FFlagDE14317CrashFix": "True", + "DFFlagSetNetworkOwnerCanSetCheck": "True", + "DFFlagLiveColorUpdatesCanceling": "True", + "DFFlagLuaGcPerVm": "True", + "FFlagGraphicsGLDisableDiscard": "True", + "FFlagStudioDeviceEmulationTouchInputFix": "True", + "FFlagTaskSchedulerCyclicExecutive": "True", + "DFFlagMakeWebPendingFriendRequests": "True", + "DFFlagGetLastestAssetVersionEnabled": "True", + "FFlagHideDeprecatedEnums": "True", + "FFlagSubmitEditedColor3WhenFocusLost": "True", + "DFFlagFixScriptableCameraRoll": "True", + "DFFlagSeparateBulletNarrowPhaseAndMidStepUpdates": "True", + "DFFlagUsePGSSolverSpringConstantScale": "True", + "DFFlagToolRequiresHandleProperty": "True", + "FFlagPlayerScriptsNotArchivable": "True", + "DFFlagClearAllChildrenUseDestroy": "True", + "DFFlagMaxPlayersEnabled": "True", + "DFFlagPreferredPlayersEnabled": "True", + "DFFlagLocalHumanoidSoundsEnabled": "True", + "DFFlagIncreaseSoundPositionClampLimit": "True", + "DFFlagNameOcculsionIgnoreTransparent": "True", + "DFFlagReconstructAssetUrlNew": "True", + "DFFlagAdjustFloorForce": "True", + "DFFlagFixAnimationPhaseInitialization": "True", + "FFlagLuaChatPhoneFontSize": "True", + "DFFlagUseAssetTypeHeader": "True", + "FStringPlaceFilter_ReservedServersEnabled": "True;107715348", + "FFlagCSGUnionCatchUnknownExceptions": "False", + "FIntGamePerfMonitorPercentage": "10", + "FFlagSoundTypeCheck": "True", + "DFFlagIncreaseScrollWheelMultiplyTime": "True", + "FFlagMacRemoveUserInputJob": "True", + "FFlagStudioNewFonts": "True", + "DFFlagApiCapitalizationChanges": "True", + "FFlagParticleCullFix": "True", + "DFFlagVideoCaptureTeleportFix": "False", + "DFFlagCoreGuiCustomizationApi": "True", + "DFFlagCustomTeleportLoadingScreen": "True", + "DFFlagCharacterAppearanceLoadedEnabled": "True", + "DFFlagVIPServerOwnerIdEnabled": "True", + "DFFlagEnableParticleVelocityInheritance": "True", + "DFFlagEnableParticleEmissionDirection": "True", + "DFFlagFixParticleDistribution": "True", + "DFFlagEnableParticleNewBoundingBox": "True", + "DFFlagEnableParticlePartLock": "True", + "DFFlagEnableParticleBurst": "True", + "DFFlagNoRunSteepSlope": "True", + "DFFlagHumanoidJumpPower": "True", + "FFlagControllerMenu": "True", + "FFlagFlyCamOnRenderStep": "True", + "DFFlagFullscreenEnabledWhileRecording": "True", + "DFFlagPreProcessTextBoxEvents": "True", + "DFFlagAllowHideHudShortcut": "False", + "DFFlagFixBallInsideBlockCollision": "True", + "FFlagPGSSolverBodyCacheLeakFix": "True", + "FFlagFixCrashAtShutdown": "True", + "DFFlagEquipClonedToolFix": "True", + "FFlagGamepadCursorChanges": "True", + "DFFlagCreatePlayerGuiLocal": "False", + "DFFlagDontUseInsertServiceOnAnimLoad": "True", + "DFFlagCyclicExecutiveFixNonCyclicJobRun": "True", + "DFFlagPhysicsFPSTimerFix": "True", + "FFlagCyclicExecutiveRenderJobRunsFirst": "True", + "FFlagPhysicsCylinders": "True", + "DFFlagPhysicsUseNewBulletContact": "True", + "FFlagReadCoordinateFrameFast": "False", + "DFFlagRayHitMaterial": "True", + "DFFlagPromptPurchaseOnGuestHandledInCoreScript": "True", + "DFFlagNonEmptyPcallError": "True", + "DFFlagDisplayTextBoxTextWhileTypingMobile": "False", + "DFFlagOverrideScollingDisabledWhenRecalulateNeeded": "True", + "DFFlagFixScrollingOffSurfaceGUIs": "True", + "DFFlagTextScaleDontWrapInWords": "True", + "DFFlagListenPositionEnabled": "True", + "DFFlagGetCharacterAppearanceEnabled": "True", + "DFFlagBackTabInputInStudio": "True", + "DFFlagTrackLastDownGUI": "True", + "DFFlagBulletFixCacheReuse": "True", + "DFFlagFastFilterHumanoidParts": "False", + "DFFlagProcessAcceleratorsBeforeGUINaviagtion": "True", + "DFFlagImageFailedToLoadContext": "True", + "DFFlagDontReorderScreenGuisWhenDescendantRemoving": "True", + "DFFlagSoundFailedToLoadContext": "True", + "DFFlagAnimationFailedToLoadContext": "True", + "DFFlagElasticEasingUseTwoPi": "True", + "SFFlagNetworkUseServerScope": "True", + "DFFlagHttpZeroLatencyCaching": "True", + "DFFlagPasteWithCapsLockOn": "True", + "DFFlagScriptExecutionContextApi": "True", + "DFFlagGetFocusedTextBoxEnabled": "True", + "DFFlagTextBoxIsFocusedEnabled": "True", + "DFFlagHttpCurlDomainTrimmingWithBaseURL": "False", + "DFFlagLoadFileUseRegularHttp": "True", + "DFFlagReplicatorCheckBadRebinding": "True", + "FFlagFastClusterThrottleUpdateWaiting": "True", + "DFFlagDeserializePacketThreadEnabled": "True", + "FFlagFontSizeUseLargest": "True", + "DFFlagRejectHashesInLinkedSource": "True", + "FFlagUpdatePrimitiveStateForceSleep": "True", + "FFlagPhysicsUseKDTreeForCSG": "True", + "DFFlagCSGLeftoverDataFix": "True", + "DFFlagGetGroupsAsyncEnabled": "True", + "FFlagStudioTutorialSeeAll": "True", + "DFFlagLimitScrollWheelMaxToHalfWindowSize": "True", + "FFlagGameExplorerCopyPath": "True", + "DFFlagFixRotatedHorizontalScrollBar": "True", + "DFFlagFixAnchoredSeatingPosition": "True", + "FFlagFixSlice9Scale": "True", + "DFFlagFullscreenRefocusingFix": "True", + "DFFlagEnableClimbingDirection": "True", + "FFlagPGSGlueJoint": "True", + "FFlagTweenCallbacksDuringRenderStep": "True", + "FFlagFRMFixCullFlicker": "True", + "DFFlagDisableProcessPacketsJobReschedule": "True", + "FFlagCSGVoxelizerPrecompute": "False", + "FFlagLazyRenderingCoordinateFrame": "True", + "FFlagPGSSteppingMotorFix": "True", + "FFlagGameExplorerCopyId": "True", + "DFFlagLockViolationScriptCrash": "False", + "DFFlagLockViolationInstanceCrash": "False", + "FFlagSpheresAllowedCustom": "True", + "DFFlagHumanoidCookieRecursive": "True", + "FFlagRwxFailReport": "True", + "FIntStudioInsertDeletionCheckTimeMS": "30000", + "DFFlagClampRocketThrustOnPGS": "True", + "DFFlagPGSWakePrimitivesWithBodyMoverPropertyChanges": "True", + "FFlagPGSUsesConstraintBasedBodyMovers": "True", + "FFlagUseNewSubdomainsInCoreScripts": "True", + "DFFlagEnableShowStatsLua": "True", + "FFlagSmoothTerrainPacked": "True", + "FFlagGraphicsGL3": "True", + "DFFlagUrlReconstructToAssetGame": "True", + "FFlagPGSApplyImpulsesAtMidpoints": "True", + "FFlagModifyDefaultMaterialProperties": "True", + "FIntPhysicalPropFriction_SMOOTH_PLASTIC_MATERIAL": "200", + "FIntPhysicalPropFriction_PLASTIC_MATERIAL": "300", + "FIntPhysicalPropFriction_NEON_MATERIAL": "300", + "FIntPhysicalPropFriction_SNOW_MATERIAL": "300", + "FIntPhysicalPropFriction_ALUMINUM_MATERIAL": "400", + "FIntPhysicalPropFriction_BRICK_MATERIAL": "800", + "FIntPhysicalPropFriction_CONCRETE_MATERIAL": "700", + "FIntPhysicalPropFriction_DIAMONDPLATE_MATERIAL": "350", + "FIntPhysicalPropFriction_SANDSTONE_MATERIAL": "500", + "FIntPhysicalPropFriction_SAND_MATERIAL": "500", + "FIntPhysicalPropFWeight_ICE_MATERIAL": "3000", + "FIntPhysicalPropFWeight_BRICK_MATERIAL": "300", + "FIntPhysicalPropFWeight_CONCRETE_MATERIAL": "300", + "FIntPhysicalPropFWeight_SANDSTONE_MATERIAL": "5000", + "FIntPhysicalPropFWeight_BASALT_MATERIAL": "300", + "FIntPhysicalPropFWeight_SAND_MATERIAL": "5000", + "FIntPhysicalPropElasticity_SAND_MATERIAL": "50", + "FIntPhysicalPropElasticity_SNOW_MATERIAL": "30", + "FIntPhysicalPropElasticity_MUD_MATERIAL": "70", + "FIntPhysicalPropElasticity_GROUND_MATERIAL": "100", + "FIntPhysicalPropElasticity_MARBLE_MATERIAL": "170", + "FIntPhysicalPropElasticity_BRICK_MATERIAL": "150", + "FIntPhysicalPropElasticity_PEBBLE_MATERIAL": "170", + "FIntPhysicalPropElasticity_COBBLESTONE_MATERIAL": "170", + "FIntPhysicalPropElasticity_ROCK_MATERIAL": "170", + "FIntPhysicalPropElasticity_SANDSTONE_MATERIAL": "150", + "FIntPhysicalPropElasticity_BASALT_MATERIAL": "150", + "FIntPhysicalPropElasticity_CRACKED_LAVA_MATERIAL": "150", + "FIntPhysicalPropElasticity_FABRIC_MATERIAL": "50", + "FIntPhysicalPropElasticity_WOOD_MATERIAL": "200", + "FIntPhysicalPropElasticity_WOODPLANKS_MATERIAL": "200", + "FIntPhysicalPropElasticity_ICE_MATERIAL": "150", + "FIntPhysicalPropElasticity_GLACIER_MATERIAL": "150", + "FIntPhysicalPropElasticity_RUST_MATERIAL": "200", + "FIntPhysicalPropElasticity_DIAMONDPLATE_MATERIAL": "250", + "FIntPhysicalPropElasticity_ALUMINUM_MATERIAL": "250", + "FIntPhysicalPropElasticity_METAL_MATERIAL": "250", + "FIntPhysicalPropElasticity_GRASS_MATERIAL": "100", + "DFFlagFixSeatingWhileSitting": "True", + "FFlagPGSSolverDefaultOnNewPlaces": "True", + "FFlagPGSVariablePenetrationMargin": "False", + "FFlagStudioEnableGameAnimationsTab": "True", + "FIntPGSPentrationMarginMax": "50000", + "FFlagStudioHideMouseCoursorOnCommand": "True", + "SFFlagNewPhysicalPropertiesForcedOnAll": "True", + "SFFlagMaterialPropertiesNewIsDefault": "True", + "DFFlagMaterialPropertiesEnabled": "True", + "FFlagWaterParams": "True", + "FFlagSpatialHashMoreRoots": "True", + "FFlagSkipAdornIfWorldIsNull": "True", + "DFStringWorkspaceMessageLink": "http://tinyurl.com/zv9tvjk", + "DFStringWorkspaceMessageText": "Starting May 23 you will no longer be able to use Legacy PhysicalPropertiesMode. Please click here to get more information about migrating your game.", + "DFIntStudioWorkspaceNotificationLevel": "0", + "FFlagLuaBasedBubbleChat": "True", + "DFFlagNetworkOwnershipRuleReplicates": "True", + "DFFlagSmoothTerrainWriteVoxelsOccupancyFix": "True", + "DFFlagCloudEditByPassCheckForServer": "True", + "DFFlagDraggerUsesNewPartOnDuplicate": "True", + "DFFlagRestoreTransparencyOnToolChange": "False", + "FFlagEnableLuaFollowers": "False", + "DFFlagUserServerFollowers": "True", + "FFlagNetworkReplicateTerrainProperties": "True", + "FFlagAllowInsertFreeModels": "False", + "FFlagInsertUnderFolder": "True", + "DFFlagPGSWakeOtherAssemblyForJoints": "True", + "FFlagStudioPropertySliderEnabled": "True", + "DFFlagSetNetworkOwnerFixAnchoring": "True", + "FFlagFixBulletGJKOptimization": "True", + "FFlagOSXUseSDL": "False", + "DFFlagPhysicalPropMassCalcOnJoin": "False", + "DFFlagBrickColorParseNonDeprecatedMatchEnabled": "True", + "FFlagWindowsUseSDL": "False", + "FFlagPhysicsOptimizeSendClumpChanged": "True", + "DFFlagHumanoidFeetIsPlastic": "True", + "DFFlagUseTerrainCustomPhysicalProperties": "True", + "DFFlagFormFactorDeprecated": "True", + "FFlagPGSVariablePenetrationMarginFix": "True", + "DFIntDataStoreMaxValueSize": "262144", + "DFFlagFilteringEnabledDialogFix": "True", + "DFFlagFixShapeChangeBug": "True", + "FFlagScriptAnalyzerFixLocalScope": "True", + "FFlagRenderVRBBGUI": "True", + "FFlagRenderVR": "True", + "DFFlagNetworkFixJoinDataItemOrder": "True", + "FFlagUseUserListMenu": "True", + "FFlagStudioImproveModelDragFidelity": "True", + "FFlagStudioOrthonormalizeSafeRotate": "True", + "FFlagMacInferredCrashReporting": "True", + "FFlagWindowsInferredCrashReporting": "True", + "FFlagCloudEditDoNotLoadCoreScripts": "True", + "FFlagStudioEmbeddedFindDialogEnabled": "False", + "FFlagUserAllCamerasInLua": "False", + "DFFlagMacInferredCrashReporting": "True", + "DFFlagWindowsInferredCrashReporting": "True", + "FFlagUseNewPromptEndHandling": "True", + "FFlagPhysPropConstructFromMaterial": "True", + "FFlagStudioToolboxModelDragToCastPoint": "True", + "FFlagStudioPushTreeWidgetUpdatesToMainThread": "True", + "DFFlagFixYieldThrottling": "True", + "FFlagCheckSleepOptimization": "True", + "DFFlagContactManagerOptimizedQueryExtents": "True", + "FFlagEnableSetCoreTopbarEnabled": "True", + "FFlagUseBuildGenericGameUrl": "True", + "DFFlagFixFallenPartsNotDeleted": "True", + "DFFlagTrackPhysicalPropertiesGA": "True", + "DFFlagSetNetworkOwnerFixAnchoring2": "True", + "FFlagUseSimpleRapidJson": "True", + "DFFlagTurnOffFakeEventsForCAS": "True", + "DFFlagTurnOffFakeEventsForInputEvents": "True", + "FFlagCancelPendingTextureLoads": "False", + "DFFlagUseR15Character": "True", + "DFFlagEnableHipHeight": "True", + "DFFlagCachedPoseInitialized": "True", + "DFFlagFixJumpGracePeriod": "True", + "FFlagFilterSinglePass": "True", + "DFFlagOrthonormalizeJointCoords": "True", + "DFFlagPhysicsSenderUseOwnerTimestamp": "False", + "DFFlagNamesOccludedAsDefault": "True", + "FFlagUserUseNewControlScript": "True", + "FFlagUseDynamicTypesetterUTF8": "True", + "FStringPlaceFilter_UseNewPersistenceSubdomain": "True;359173292", + "FStringPlaceFilter_UseNewDataStoreLogging": "True;359173292", + "FStringPlaceFilter_InsertServiceLoadModelErrorDoNotCreateEmpty": "True;359173292", + "FStringPlaceFilter_InsertServiceLoadModelErrorNoLuaExceptionReturnNull": "True;359173292", + "DFFlagUseNewPersistenceSubdomain": "True", + "DFFlagUseNewDataStoreLogging": "True", + "FFlagPlaceLauncherUsePOST": "False", + "FFlagStudioUpdateSAResultsInUIThread": "True", + "FFlagBillboardGuiVR": "True", + "FFlagHumanoidRenderBillboard": "True", + "FLogVR": "6", + "FFlagStudioRemoveDebuggerResumeLock": "True", + "FFlagPhysicsAnalyzerEnabled": "True", + "FFlagAnalyzerGroupUIEnabled": "True", + "DFFlagVariableHeartbeat": "True", + "DFFlagScreenShotDuplicationFix": "True", + "FFlagCSGDelayParentingOperationToEnd": "True", + "FFlagStudioTreeWidgetCheckDeletingFlagWhenDoingUpdates": "True", + "DFFlagUseComSiftUpdatedWebChatFilterParamsAndHeader": "False", + "DFFlagConstructModerationFilterTextParamsAndHeadersUseLegacyFilterParams": "False", + "FFlagMinMaxDistanceEnabled": "True", + "FFlagRollOffModeEnabled": "True", + "DFFlagGetLocalTeleportData": "True", + "FFlagUseNewXboxLoginFlow": "True", + "FStringPlaceFilter_MaterialPropertiesNewIsDefault": "False;276583453;29812337;264431831;189707;204990346;30869879;166456825;287335491;180714690;248409235;255280267;289685010;183384830;314906000;323111227", + "DFFlagFixSlowLadderClimb": "True", + "DFFlagHumanoidCheckForNegatives": "True", + "DFFlagFixMatrixToAxisAngle": "True", + "DFFlagMaskWeightCleared": "True", + "DFFlagUseStarterPlayerCharacterScripts": "True", + "DFFlagUseStarterPlayerHumanoid": "True", + "DFFlagUseStarterPlayerCharacter": "True", + "DFFlagAccessoriesAndAttachments": "True", + "FFlagTeamCreateOptimizeRemoteSelection": "True", + "FFlagReportInGameAssetSales": "True", + "FFlagFilterDoublePass": "False", + "DFFlagRaiseSendPriority": "False", + "FFlagStudioDelaySAResultsEnabled": "True", + "FFlagUsePreferredSoundDevice": "True", + "FFlagRenderLowLatencyLoop": "False", + "DFFlagLocalScriptSpawnPartAlwaysSetOwner": "True", + "DFFlagCloudEditSupportImmediatePublish": "True", + "FFlagFixSurfaceGuiGamepadNav": "True", + "DFFlagEnableAdColony": "False", + "FFlagEnableAdServiceVideoAds": "False", + "DFFlagInfluxDb09Enabled": "True", + "DFFlagTeleportSignalConnectOnServiceProvider": "True", + "DFFlagScriptContextGuardAgainstCStackOverflow": "True", + "FFlagFixPhysicalPropertiesComponentSet": "True", + "DFFlagMaterialPropertiesDivideByZeroWeights": "True", + "FFlagRemoveUnusedPhysicsSenders": "True", + "FFlagRemoveInterpolationReciever": "True", + "DFFlagActivatePGSMechanicalJoints": "True", + "FIntPhysicalPropDensity_ALUMINUM_MATERIAL": "2700", + "FFlagTreatCloudEditAsEditGameMode": "True", + "FFlagSendFilteredExceptionOnInferredStep": "True", + "DFFlagUrlReconstructToAssetGameSecure": "False", + "DFFlagUseModerationFilterTextV2": "True", + "FFlagGraphicsD3D10": "True", + "FFlagRenderFixFog": "True", + "FFlagUseNewAppBridgeWindows": "False", + "DFFlagNullCheckJointStepWithNullPrim": "True", + "FFlagJNIEnvScopeOptimization": "True", + "DFFlagSanitizeLoadingGUICorrected": "True", + "FFlagSendLegacyMachineConfigInfo": "False", + "FFlagUseNewBadgesCreatePage": "True", + "FFlagRetryWhenCloudEditEnabledEndpointFails": "True", + "DFFlagTeamCreateDoNotReplicateShowDevGuiProp": "True", + "FFlagStudioAddBackoffToNotificationsReconnects": "True", + "DFFlagInsertServiceForceLocalInTeamCreate": "True", + "FFlagGraphicsMacFix": "True", + "FFlagUseNewAppBridgeOSX": "True", + "FFlagNewColor3Functions": "True", + "DFFlagSmootherVehicleSeatControlSystem": "True", + "FFlagGameExplorerUseV2AliasEndpoint": "True", + "FFlagDisableAbortRender": "True", + "DFFlagInstancePredeleteNuke": "False", + "DFFlagSimpleHermiteSplineInterpolate": "False", + "DFFlagCleanUpInterpolationTimestamps": "True", + "SFFlagPhysicsPacketSendWorldStepTimestamp": "True", + "DFFlagUpdateTimeOnDelayedSamples": "False", + "DFFlagDisableMovementHistory": "True", + "DFFlagLookForDuplicateCoordinateFrameInBuffer": "True", + "DFFlagDoNotForwardClientTimestamp": "True", + "DFFlagZeroVelocityOnDelayedSamples": "True", + "DFFlagUpdateHermiteLastFrameWhenUpdatePrevFrame": "True", + "DFFlagCatchThrottledVelocityComponents": "True", + "DFIntThrottledVelocityThresholdTenths": "15", + "DFFlagShowFormFactorDeprecatedWarning": "False", + "FFlagStudioTeamCreateWebChatBackendEnabled": "True", + "DFFlagAnimationEasingStylesEnabled": "True", + "FFlagUseVRKeyboardInLua": "True", + "DFFlagCheckDataModelOnTeleportRetry": "True", + "FFlagTeamCreateEnableAutoReconnect": "True", + "FFlagTeamCreateUpdatedChatUIEnabled": "True", + "FFlagTeamCreateExpandableChatEditor": "True", + "DFStringHttpInfluxWriterPassword": "faster1Play", + "DFFlagOptimizeAnimator": "True", + "FFlagOptimizeAnimatorCalcJoints": "True", + "DFFlagStopUsingMaskWeight": "True", + "FFlagRenderNoDepthLast": "True", + "DFFlagFixTimeStampsForRunningNoThrottle": "True", + "DFIntInterpolationDelayFactorTenths": "10", + "DFFlagUseHermiteSplineInterpolation": "True", + "DFFlagChatServiceFilterStringForPlayerFromAndToStudioBypass": "True", + "FFlagCameraInterpolateMethodEnhancement": "False", + "DFFlagBlendPosesWithIsOver": "True", + "FStringPlaceFilter_InterpolationDelayFactorTenths": "10;189707", + "FStringPlaceFilter_CleanUpInterpolationTimestamps": "False;424096945", + "FStringPlaceFilter_DisableMovementHistory": "True;189707", + "FStringPlaceFilter_PhysicsPacketSendWorldStepTimestamp": "False;424096945", + "FStringPlaceFilter_UseHermiteSplineInterpolation": "False;424096945", + "FFlagRestrictSales": "True", + "DFFlagStacktraceOnKeysMustBeStrings": "False", + "FFlagBadTypeOnPcallEnabled": "True", + "FFlagFixMouseFireOnEmulatingTouch": "True", + "FFlagTeamCreateEnableOneToOneChat": "True", + "FFlagUseUpdatedSyntaxHighlighting": "True", + "FFlagFixStickyDragBelowOrigin": "True", + "FFlagFixBadMemoryOnStreamingGarbageCollection": "True", + "DFFlagFixAllCharacterStreaming": "True", + "FFlagDisableChangedServiceInTestMode": "True", + "FFlagAllowFullColorSequences": "True", + "FFlagStudioAllowFullColorSequences": "True", + "DFFlagDynamicGravity": "True", + "FFlagUseNewAppBridgeAndroid": "False", + "FFlagFixSurfaceGuiGazeSelect": "True", + "FFlagFixAlwaysOnTopSurfaceGuiInput": "True", + "FFlagStudioEnableAxWidget": "True", + "DFFlagFixDataStoreErrorMessage": "True", + "FFlagStudioAllowDataStoreUsageInCloudEdit": "True", + "FFlagSaveToTempFile": "True", + "DFFlagCSGPreventCrashesWhenPartOperationsNotInDataModel": "True", + "FStringPlaceFilter_NoAnimationTrackState": "True;177200271", + "DFFlagUsePointsNewBatchingImpl": "True", + "FFlagUseUpdatedKeyboardSettings": "False", + "DFFlagFixAnimationControllerAnimator": "True", + "DFFlagNoAnimationTrackState": "True", + "DFFlagFixNestedAnimators": "True", + "DFFlagWaitForToolHandleToEquip": "True", + "DFFlagUseNewFetchFriendsFunction": "True", + "FFlagStudioMeshIdMacCrashFixEnabled": "True", + "FFlagWindowsNoDmpRetry": "True", + "FFlagDeleteLogsOnMac": "True", + "FFlagDeleteLogsByDate": "True", + "FFlagTeamCreateSlowerFetchChatMessage": "True", + "FFlagTestMenuEnabledOnAllWindows": "True", + "FFlagCSGSetUsePartColorToTrueIfAllPartsSameColor": "True", + "FFlagSoundServiceGameConfigurerConfigureRunServiceRun": "True", + "DFFlagDoUpdateStepDetachedChannels": "True", + "DFFlagUseNewSetFMOD3D": "False", + "FFlagSoundChannelMaxDistanceStopFMODChannel": "True", + "FFlagRCCLoadFMOD": "True", + "FFlagSoundChannelUseV2Implementation": "False", + "FFlagRenderSoftParticles": "True", + "FFlagScriptContextSinglePendingThreadsQueue": "False", + "DFIntTeleportExceptionInfluxHundredthsPercentage": "10000", + "FIntStartupInfluxHundredthsPercentage": "100", + "FFlagCSGAllowUnorderedProperties": "False", + "DFFlagGamepadProcessMouseEvents": "False", + "DFFlagCrashTouchTransmitterIfRefDtor": "False", + "FFlagRenderUserGuiIn3DSpace": "True", + "FFlagScreenGuisClipDescendants": "False", + "FFlagUseNewNotificationPathLua": "True", + "FFlagStudioEditUDim2sWithoutBraces": "True", + "FFlagStudioConstructUDim2sWithUDims": "True", + "DFFlagGetPlayersByTeamEnabled": "True", + "DFFlagPlayerTeamPropertyEnabled": "True", + "FFlagStudioDefinePluginGlobalForLocalPlugins": "True", + "FFlagStudioEnableDockWidgetMaximize": "True", + "FFlagVideoDocumentationPluginEnabled": "True", + "FFlagStudioDisconnectPopuplaunchEditorSignals": "True", + "FFlagStudioPasteIntoMultipleObjects": "True", + "FFlagStudioBreakOnInfiniteLoops": "True", + "FFlagMessageOnLoadScriptValidationFail": "True", + "FFlagCSGTriangleCount": "True", + "FFlagStudioMockPurchasesEnabled": "True", + "FFlagStudioUseMarketplaceApiClient": "True", + "DFFlagUseGameAvatarTypeEnum": "False", + "FFlagStudioUsePlaySoloConfigurer": "True", + "DFFlagUseAvatarFetchAPI": "False", + "DFFlagSetHumanoidRegardlessOfNetworkOwnership": "True", + "FFlagFixStudioCursorJitter": "True", + "FFlagVoxelCompressedStorage": "True", + "FFlagSmoothTerrainLODEnabled": "True", + "FFlagBetterTabManagement": "True", + "DFFlagBlockCustomHttpHeaders": "False", + "FStringPlaceFilter_SoundChannelUseV2Implementation": "True;359173292;9528010;292439477;1818", + "FStringPlaceFilter_UseNewSetFMOD3D": "True;292439477;359173292;270607122;9528010;379011764;1818", + "FFlagStudioInsertAtTopCenterOfSelection": "True", + "FFlagGameExplorer9340FixEnabled": "True", + "DFFlagCloudEditRemoveEditorOnPlayerRemove": "True", + "FFlagWaitForChildTimeOut": "True", + "SFFlagTeamCreateReplicateCollisionFidelity": "True", + "FFlagStudioShowPropertiesByDefault": "True", + "FFlagStudioShowObjectExplorerByDefault": "True", + "FFlagStudioUpdateToolbarsOnFocus": "True", + "FFlagDeviceEmulationStatistics": "True", + "FFlagFixBoxSelectWithCtrl": "True", + "FFlagStudioTrimPropertyWhitespace": "True", + "FFlagStudioRenamePasteContextMenuOnMultipleSelection": "True", + "FFlagDebugCSGExportFailure": "False", + "FFlagFixCrashOnEmptyTextOnAutoComplete": "True", + "FFlagCancelInputOnGuiNavigation": "True", + "FFlagRemoveOldAnalyticsImplementation": "True", + "FFlagRemoveOldCountersImplementation": "True", + "FFlagUseNewAppBridgeStudio": "False", + "FFlagStudioAnalyticsRefactoring": "True", + "DFFlagRCCUseMarketplaceApiClient": "False", + "FFlagStudioIntellesenseOnAllMembersEnabled": "True", + "FFlagStudioRefreshSAResultsOnRemovingEnabled": "True", + "FFlagMeshPartUsageAnalytics": "True", + "FFlagTryAppendFileType": "True", + "FFlagStudioPreventTabStealFocus": "True", + "FFlagStudioNameMeshByFileName": "True", + "DFFlagDataStoreDisableReFetchingRecentKeys": "True", + "FFlagNewDefaultScriptSource": "True", + "FFlagStudioSanitizeFloatValues": "True", + "FFlagStudioEnableDebuggerPerfImprovements": "True", + "FFlagRecordForceStereo": "True", + "FFlagStudioVideoRecordFix": "True", + "FFlagStudioUseHttpsForUserInfo": "True", + "FFlagUseHttpsForGameserverAshx": "True", + "FFlagDisableScriptContextScriptsDisabled": "True", + "SFFlagTeamCreateReplicateTerrainRegion": "True", + "DFFlagDuplicateInstanceReferenceFix": "True", + "FFlagRakNetSupportIpV6": "False", + "FFlagUseToStringN": "False", + "FFlagUseFixedStepSlider": "True", + "FFlagStudioRenderRemotePlayerSelection": "True", + "FFlagStudioShiftRetainsResizeAspectRatio": "True", + "FFlagStudioCtrlPartResizeFromCenter": "True", + "FFlagStackTraceLinks": "True", + "FFlagStudioUpdateRestoreBehavior": "True", + "FFlagTouchTransmitterWeakPtr": "True", + "FFlagAdvancedRCCLoadFMODRetry": "True", + "FFlagAdvancedRCCLoadFMODReportDeviceInfo": "True", + "FFlagAdvancedRCCLoadFMODAttemptReportDeviceInfoOnFailure": "True", + "FFlagClientLoadFMODReportDeviceInfo": "True", + "DFIntReportDeviceInfoRate": "100", + "DFFlagSoundV2LogOnSetSoundId": "True", + "FFlagMouseUseUserInputServiceMouse": "False", + "SFFlagSoundChannelUseV2Implementation": "True", + "SFFlagUseNewSetFMOD3D": "True", + "FFlagCSGReportSuccessFailure": "True", + "FFlagLuaPreventCircularIncludes": "False" +} \ No newline at end of file diff --git a/ecsv2/studio/e.png b/ecsv2/studio/e.png new file mode 100644 index 0000000..fdf950d Binary files /dev/null and b/ecsv2/studio/e.png differ diff --git a/key.pem b/key.pem new file mode 100644 index 0000000..0ddeb0a --- /dev/null +++ b/key.pem @@ -0,0 +1,3 @@ +-----BEGIN RSA PRIVATE KEY----- + +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/renders/.htaccess b/renders/.htaccess new file mode 100644 index 0000000..efe017d --- /dev/null +++ b/renders/.htaccess @@ -0,0 +1,15 @@ +Options +FollowSymLinks -MultiViews +RewriteEngine On +RewriteBase / + +DirectorySlash Off + +ErrorDocument 404 /error.php +ErrorDocument 403 /error.php + +RewriteCond %{THE_REQUEST} \s/+(.+?)\.php[\s?] [NC] +RewriteRule .* error.php [L] + +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{DOCUMENT_ROOT}/$1.php -f +RewriteRule ^(.+?)/?$ $1.php [L] \ No newline at end of file diff --git a/renders/error.php b/renders/error.php new file mode 100644 index 0000000..e3a4dcc --- /dev/null +++ b/renders/error.php @@ -0,0 +1,23 @@ + 'InternalServerError', + '404' => 'NotFound', + '403' => 'AccessDenied', + '400' => 'BadRequest' +]; + +if(http_response_code() == 200) + http_response_code(404); + +header('Content-Type: application/json'); + +exit( + json_encode( + [ + 'Errors' => [ + $errors[http_response_code()] + ] + ] + ) +); \ No newline at end of file diff --git a/renders/v1/contact.php b/renders/v1/contact.php new file mode 100644 index 0000000..8f542f1 --- /dev/null +++ b/renders/v1/contact.php @@ -0,0 +1,104 @@ +prepare("SELECT `id` FROM `users` WHERE `username`=:name;"); + $stmt->bindParam(':name', $userName, PDO::PARAM_STR); + $stmt->execute(); + + if ($stmt->rowCount() == 0) { + ReturnError(400); + } + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $itemId = $result['id']; +} + +$scriptHeader = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/v1/infra/scripts/shared.txt'); +$scriptBody = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/v1/infra/scripts/' . strtolower($type) . '.txt'); + +$script = '-----------------------------------"CUSTOM" SHARED CODE----------------------------------'; +$script .= "\n\n"; +$script .= $scriptHeader; +$script .= "\n\n"; +$script .= '-----------------------------------START RENDER SCRIPT-----------------------------------'; +$script .= "\n\n"; +$script .= $scriptBody; + +$script = str_replace('%ITEMID%', '"' . strval($itemId) . '"', $script); +$script = str_replace('%POSE%', (isset($_GET['pose']) ? strval($_GET['pose']) : 0), $script); +$script = str_replace('%HASGEAR%', (isset($_GET['hasgear']) && $_GET['hasgear'] == 1 ? 'true' : 'false'), $script); + +$connection = new Roblox\Grid\Rcc\RCCServiceSoap('127.0.0.1', 64989); +$job = new Roblox\Grid\RCC\Job(sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535))); +$jobScript = new Roblox\Grid\RCC\ScriptExecution('render', $script); + +$startTime = microtime(true); + +$batch = $connection->BatchJob($job, $jobScript); + +$image = imagecreatefromstring(base64_decode($batch[0])); + +$newImage = imagecreatetruecolor(300, 300); +imagealphablending($newImage, false); +imagesavealpha($newImage, true); +$transparent = imagecolorallocatealpha($newImage, 255, 255, 255, 127); +imagefilledrectangle($newImage, 0, 0, 300, 300, $transparent); +imagecopyresampled($newImage, $image, 0, 0, 0, 0, 300, 300, 840, 840); + +ob_start(); + +imagepng($newImage, null, 9); + +$result = ob_get_contents(); +ob_end_clean(); + +$hash = md5($result . 'GT{04b8ba82-8e81-4428-b1ea-af1c76c9be1c}'); +$cdnResult = GetCdnUrl($hash); +$fileLocation = $cdnResult[0]; +$fileUrl = $cdnResult[1]; + +file_put_contents($fileLocation, $result); + +if(strtolower($type) == 'character') { + $stmt = $GLOBALS['dbcon']->prepare("UPDATE `users` SET `thumbnailHash`=:hash WHERE `id`=:id;"); + $stmt->bindParam(':hash', $hash, PDO::PARAM_STR); + $stmt->bindParam(':id', $itemId, PDO::PARAM_INT); + $stmt->execute(); +} elseif (strtolower($type) == 'headshot') { + $stmt = $GLOBALS['dbcon']->prepare("UPDATE `users` SET `headshotHash`=:hash WHERE `id`=:id;"); + $stmt->bindParam(':hash', $hash, PDO::PARAM_STR); + $stmt->bindParam(':id', $itemId, PDO::PARAM_INT); + $stmt->execute(); +} elseif (strtolower($type) == 'hats' || strtolower($type) == 'shirts' || strtolower($type) == 'tshirts' || strtolower($type) == 'pants' || strtolower($type) == 'heads' || strtolower($type) == 'gear') { + $stmt = $GLOBALS['dbcon']->prepare("UPDATE `catalog` SET `thumbnailHash`=:hash WHERE `id`=:id;"); + $stmt->bindParam(':hash', $hash, PDO::PARAM_STR); + $stmt->bindParam(':id', $itemId, PDO::PARAM_STR); + $stmt->execute(); +} + +header('Content-Type: application/json'); +exit(json_encode(['url' => $fileUrl, 'delta' => microtime(true) - $startTime, 'dependencies' => $batch[1]], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + +// EOF \ No newline at end of file diff --git a/renders/v1/full.php b/renders/v1/full.php new file mode 100644 index 0000000..49fbd85 --- /dev/null +++ b/renders/v1/full.php @@ -0,0 +1,64 @@ +prepare("SELECT `id` FROM `users` WHERE `username`=:name;"); + $stmt->bindParam(':name', $userName, PDO::PARAM_STR); + $stmt->execute(); + + if ($stmt->rowCount() == 0) { + ReturnError(400); + } + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $itemId = $result['id']; +} + +$scriptHeader = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/v1/infra/scripts/shared.txt'); +$scriptBody = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/v1/infra/scripts/' . strtolower($type) . '.txt'); + +$script = '-----------------------------------"CUSTOM" SHARED CODE----------------------------------'; +$script .= "\n\n"; +$script .= $scriptHeader; +$script .= "\n\n"; +$script .= '-----------------------------------START RENDER SCRIPT-----------------------------------'; +$script .= "\n\n"; +$script .= $scriptBody; + +$script = str_replace('%ITEMID%', '"' . strval($itemId) . '"', $script); +$script = str_replace('%POSE%', (isset($_GET['pose']) ? strval($_GET['pose']) : 0), $script); +$script = str_replace('%HASGEAR%', (isset($_GET['hasgear']) && $_GET['hasgear'] == 1 ? 'true' : 'false'), $script); + +$connection = new Roblox\Grid\Rcc\RCCServiceSoap('127.0.0.1', 64989); +$job = new Roblox\Grid\RCC\Job(sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535))); +$jobScript = new Roblox\Grid\RCC\ScriptExecution('render', $script); + +$startTime = microtime(true); + +$batch = $connection->BatchJob($job, $jobScript); + +header('Content-Type: image/png'); +exit(base64_decode($batch[0])); + +// EOF \ No newline at end of file diff --git a/renders/v1/infra/.htaccess b/renders/v1/infra/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/renders/v1/infra/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/renders/v1/infra/config.php b/renders/v1/infra/config.php new file mode 100644 index 0000000..9d1bb02 --- /dev/null +++ b/renders/v1/infra/config.php @@ -0,0 +1,3 @@ + maxDimension then + maxDimension = xy.magnitude + end + end + end + end +end + +-- Setup Camera +local maxHatOffset = 0.5 -- Maximum amount to move camera upward to accomodate large hats +maxDimension = math.min(1, maxDimension / 3) -- Confine maxdimension to specific bounds + +if quadratic then + maxDimension = maxDimension * maxDimension -- Zoom out on quadratic interpolation +end + +local viewOffset = Character.Head.CFrame * CFrame.new(cameraOffsetX, cameraOffsetY + maxHatOffset * maxDimension, 0.1) -- View vector offset from head +local positionOffset = Character.Head.CFrame + (CFrame.Angles(0, -math.pi / 16, 0).lookVector.unit * 3) -- Position vector offset from head + +local camera = Instance.new("Camera", Character) +camera.Name = "ThumbnailCamera" +camera.CameraType = Enum.CameraType.Scriptable +camera.CoordinateFrame = CFrame.new(positionOffset.p, viewOffset.p) +camera.FieldOfView = baseHatZoom + (maxHatZoom - baseHatZoom) * maxDimension + +-- format, width, height, sky, crop +return game:GetService("ThumbnailGenerator"):Click("PNG", 840, 840, true, false) \ No newline at end of file diff --git a/renders/v1/infra/scripts/pants.txt b/renders/v1/infra/scripts/pants.txt new file mode 100644 index 0000000..52c4abe --- /dev/null +++ b/renders/v1/infra/scripts/pants.txt @@ -0,0 +1,16 @@ +local ItemId = %ITEMID% + +local Player = game:GetService("Players"):CreateLocalPlayer(0) +Player.CharacterAppearance = "http://api.gtoria.net/user/getCharacter.php?uid=1&mode=pants&key=D869593BF742A42F79915993EF1DB&sid=" .. ItemId .. "&tick=" .. tick() +Player:LoadCharacter(false) + +local Character = Player.Character + +for _, Part in pairs(Character:GetDescendants())do + if Part:IsA("BasePart") then + Part.BrickColor = BrickColor.new("White") + end +end + +-- format, width, height, sky, crop +return game:GetService("ThumbnailGenerator"):Click("PNG", 840, 840, true, false) \ No newline at end of file diff --git a/renders/v1/infra/scripts/shared.txt b/renders/v1/infra/scripts/shared.txt new file mode 100644 index 0000000..81fe55a --- /dev/null +++ b/renders/v1/infra/scripts/shared.txt @@ -0,0 +1,7 @@ +settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4; +game:GetService("ContentProvider"):SetThreadPool(16) +game:GetService("Stats"):SetReportUrl("http://api.gtoria.net/reportstat?cock=1") + +local Lighting = game:GetService("Lighting") +Lighting.ClockTime = 13 +Lighting.GeographicLatitude = -5 \ No newline at end of file diff --git a/renders/v1/infra/scripts/shirts.txt b/renders/v1/infra/scripts/shirts.txt new file mode 100644 index 0000000..1eda14d --- /dev/null +++ b/renders/v1/infra/scripts/shirts.txt @@ -0,0 +1,16 @@ +local ItemId = %ITEMID% + +local Player = game:GetService("Players"):CreateLocalPlayer(0) +Player.CharacterAppearance = "http://api.gtoria.net/user/getCharacter.php?uid=1&mode=shirts&key=D869593BF742A42F79915993EF1DB&sid=" .. ItemId .. "&tick=" .. tick() +Player:LoadCharacter(false) + +local Character = Player.Character + +for _, Part in pairs(Character:GetDescendants())do + if Part:IsA("BasePart") then + Part.BrickColor = BrickColor.new("White") + end +end + +-- format, width, height, sky, crop +return game:GetService("ThumbnailGenerator"):Click("PNG", 840, 840, true, false) \ No newline at end of file diff --git a/renders/v1/infra/scripts/tshirts.txt b/renders/v1/infra/scripts/tshirts.txt new file mode 100644 index 0000000..9aadfaf --- /dev/null +++ b/renders/v1/infra/scripts/tshirts.txt @@ -0,0 +1,16 @@ +local ItemId = %ITEMID% + +local Player = game:GetService("Players"):CreateLocalPlayer(0) +Player.CharacterAppearance = "http://api.gtoria.net/user/getCharacter.php?uid=1&mode=ts&key=D869593BF742A42F79915993EF1DB&sid=" .. ItemId .. "&tick=" .. tick() +Player:LoadCharacter(false) + +local Character = Player.Character + +for _, Part in pairs(Character:GetDescendants())do + if Part:IsA("BasePart") then + Part.BrickColor = BrickColor.new("White") + end +end + +-- format, width, height, sky, crop +return game:GetService("ThumbnailGenerator"):Click("PNG", 840, 840, true, false) \ No newline at end of file diff --git a/setup/.gitkeep b/setup/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/thumbnailcdn/t0/.gitkeep b/thumbnailcdn/t0/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/thumbnailcdn/t1/.gitkeep b/thumbnailcdn/t1/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/thumbnailcdn/t2/.gitkeep b/thumbnailcdn/t2/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/thumbnailcdn/t3/.gitkeep b/thumbnailcdn/t3/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/thumbnailcdn/t4/.gitkeep b/thumbnailcdn/t4/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/thumbnailcdn/t5/.gitkeep b/thumbnailcdn/t5/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/thumbnailcdn/t6/.gitkeep b/thumbnailcdn/t6/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/thumbnailcdn/t7/.gitkeep b/thumbnailcdn/t7/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/versioncompatibility/.htaccess b/versioncompatibility/.htaccess new file mode 100644 index 0000000..ad37609 --- /dev/null +++ b/versioncompatibility/.htaccess @@ -0,0 +1,4 @@ +RewriteEngine on +RewriteRule ^GetAllowedMD5Hashes/?$ /allowedmd5hash.php [NC,L] +RewriteRule ^GetAllowedSecurityVersions/?$ /allowedsecver.php [NC,L] +ErrorDocument 404 /index.php \ No newline at end of file diff --git a/versioncompatibility/allowedmd5hash.php b/versioncompatibility/allowedmd5hash.php new file mode 100644 index 0000000..f6351df --- /dev/null +++ b/versioncompatibility/allowedmd5hash.php @@ -0,0 +1,3 @@ +["0bfc5dc8cf2e66d196040a6e1b0261ec"]])); \ No newline at end of file diff --git a/versioncompatibility/allowedsecver.php b/versioncompatibility/allowedsecver.php new file mode 100644 index 0000000..95c5311 --- /dev/null +++ b/versioncompatibility/allowedsecver.php @@ -0,0 +1,3 @@ +["0.1.0pcplayer"]])); \ No newline at end of file diff --git a/www/.htaccess b/www/.htaccess new file mode 100644 index 0000000..1d42b75 --- /dev/null +++ b/www/.htaccess @@ -0,0 +1,63 @@ +ErrorDocument 404 /core/views/error/notfound.php +ErrorDocument 403 /core/views/error/notfound.php + +RewriteEngine On + +RewriteRule ^user/dashboard/?$ /core/views/user/dashboard.php [NC,L] +RewriteRule ^user/settings/?$ /core/views/user/settings.php [NC,L] +RewriteRule ^user/messages/?$ /core/views/user/messages.php [NC,L] +RewriteRule ^user/messages/?-(.+)/?$ /core/views/user/messages.php [NC,L] +RewriteRule ^user/messages/?\+(.+)/?$ /core/views/user/messages.php [NC,L] +RewriteRule ^blog/?$ /core/views/blog/main.php [NC,L] +RewriteRule ^user/online/?$ /core/views/online.php [NC,L] +RewriteRule ^users/([^/]+)/?$ /core/views/users.php?term=$1 [NC,QSA,L] +RewriteRule ^users/?$ /core/views/users.php [NC,L] +RewriteRule ^admin/appeals/?$ /core/views/admin/appeals.php [NC,L] +RewriteRule ^admin/ban/?$ /core/views/admin/ban.php [NC,L] +RewriteRule ^admin/reports/?$ /core/views/admin/reports.php [NC,L] +RewriteRule ^admin/statistics/?$ /core/views/admin/statistics.php [NC,L] +RewriteRule ^admin/assets/?$ /core/views/admin/assets.php [NC,L] +RewriteRule ^admin/unban/?$ /core/views/admin/unban.php [NC,L] +RewriteRule ^admin/newhat/?$ /core/views/admin/newHat.php [NC,L] +RewriteRule ^admin/rewardPostie/?$ /core/views/admin/rewardPostie.php [NC,L] +RewriteRule ^admin/prune/?$ /core/views/admin/prune.php [NC,L] +RewriteRule ^admin/downloadasset/?$ /core/views/admin/assetDownloader.php [NC,L] +RewriteRule ^admin/render/?$ /core/views/admin/render.php [NC,L] +RewriteRule ^admin/?$ /core/views/admin/main.php [NC,L] +RewriteRule ^groups/search/([^/]+)/?$ /core/views/user/groups/search.php?term=$1 [NC,QSA,L] +RewriteRule ^groups/search/?$ /core/views/user/groups/search.php [NC,L] +RewriteRule ^groups/create/?$ /core/views/user/groups/create.php [NC,L] +RewriteRule ^groups/view/([^/]+)/?$ /core/views/user/groups/view.php?id=$1 [NC,QSA,L] +RewriteRule ^groups/admin/([^/]+)/?$ /core/views/user/groups/admin.php?id=$1 [NC,QSA,L] +RewriteRule ^groups/?$ /core/views/user/groups/main.php [NC,L] +RewriteRule ^user/character/?$ /core/views/user/character.php [NC,L] +RewriteRule ^user/logout/?$ /core/views/user/logout.php [NC,L] +RewriteRule ^forum/?$ /core/views/forum/index.php [NC,L] +RewriteRule ^forum-(.+)/?$ /core/views/forum/index.php [NC,L] +RewriteRule ^forum\+(.+)/?$ /core/views/forum/index.php [NC,L] +RewriteRule ^games/new/?$ /core/views/games/new.php [NC,L] +RewriteRule ^games/view/([^/]+)/?$ /core/views/games/view.php?id=$1 [NC,QSA,L] +RewriteRule ^games/?$ /core/views/games/main.php [NC,L] +RewriteRule ^account/suspended?$ /core/views/user/security/banned.php [NC,L] +RewriteRule ^account/appeal?$ /core/views/user/security/appeal.php [NC,L] +RewriteRule ^account/resetpassword/([^/]+)/([^/]+)/?$ /core/views/user/security/resetpassword.php?userid=$1&key=$2 [NC,QSA,L] +RewriteRule ^account/verification/email?$ /core/views/user/security/verifyEmail.php [NC,L] +RewriteRule ^account/verification/twostepauth?$ /core/views/user/security/twostepauth.php [NC,L] +RewriteRule ^catalog/?$ /core/views/catalog/main.php [NC,L] +RewriteRule ^catalog/upload/?$ /core/views/catalog/upload.php [NC,L] +RewriteRule ^catalog/item/([^/]+)/?$ /core/views/catalog/item.php?id=$1 [NC,QSA,L] +RewriteRule ^catalog/configure/([^/]+)/?$ /core/views/catalog/configureitem.php?id=$1 [NC,QSA,L] +RewriteRule ^user/profile/([^/]+)/?$ /core/views/user/profile.php?username=$1 [NC,QSA,L] +RewriteRule ^friends/?$ /core/views/friends/main.php [NC,L] +RewriteRule ^friends/requests/?$ /core/views/friends/requests.php [NC,L] +RewriteRule ^friends/show/([^/]+)/?$ /core/views/friends/show.php?id=$1 [NC,QSA,L] +RewriteRule ^ide/welcome/?$ /core/views/ide/welcome.php [NC,L] +RewriteRule ^ide/ClientToolbox/?$ /core/views/ide/toolbox.php [NC,L] +RewriteRule ^login/requestauth/?$ /core/views/ide/requestauth.php [NC,L] +RewriteRule ^avatar/([^/]+)/?$ /core/func/api/avatar/get.php?uname=$1 [NC,L] +RewriteRule ^avatar/?$ /core/func/api/avatar/get.php?uname=$1 [NC,L] +RewriteRule ^avatar-headshot/([^/]+)/?$ /core/func/api/avatar/get.php?uname=$1&headshot=1 [NC,L] +RewriteRule ^avatar-headshot/?$ /core/func/api/avatar/get.php?uname=$1&headshot=1 [NC,L] +RewriteRule ^asset/?$ /core/func/api/asset/get.php [NC,QSA,L] +RewriteRule ^game/join/?(.*)$ https://assetgame.gtoria.net/game/join/$1 [R=301,NC,L] +RewriteRule ^game/gameserver/?(.*)$ https://assetgame.gtoria.net/game/gameserver/$1 [R=301,NC,L] \ No newline at end of file diff --git a/www/core/func/api/account/getNotifications.php b/www/core/func/api/account/getNotifications.php new file mode 100644 index 0000000..577e96e --- /dev/null +++ b/www/core/func/api/account/getNotifications.php @@ -0,0 +1,10 @@ + diff --git a/www/core/func/api/account/liftBan.php b/www/core/func/api/account/liftBan.php new file mode 100644 index 0000000..a3a0293 --- /dev/null +++ b/www/core/func/api/account/liftBan.php @@ -0,0 +1,40 @@ + 1440) { + security::liftBan(); + }else{ + security::returnLiftError(); + } + } + if ($GLOBALS['userTable']['bantype'] == 3) { + if ($timeSince > 10080) { + security::liftBan(); + }else{ + security::returnLiftError(); + } + } + if ($GLOBALS['userTable']['bantype'] == 4) { + if ($timeSince > 43200) { + security::liftBan(); + }else{ + security::returnLiftError(); + } + } + }else{ + die("error"); + } + }else{ + die("error"); + } +?> \ No newline at end of file diff --git a/www/core/func/api/account/sendAppeal.php b/www/core/func/api/account/sendAppeal.php new file mode 100644 index 0000000..b321b61 --- /dev/null +++ b/www/core/func/api/account/sendAppeal.php @@ -0,0 +1,16 @@ + 0)) + { + exit('unfinished'); + } + + exit('appeal-error'); + }else{ + die("error"); + } +?> \ No newline at end of file diff --git a/www/core/func/api/admin/get/RBXAssetInformation.php b/www/core/func/api/admin/get/RBXAssetInformation.php new file mode 100644 index 0000000..8f2db34 --- /dev/null +++ b/www/core/func/api/admin/get/RBXAssetInformation.php @@ -0,0 +1,48 @@ += 41 && + $assetInformation['AssetTypeId'] <= 47 + ) || + $assetInformation['AssetTypeId'] == 19 + ) + ) + { + $thumb = json_decode(file_get_contents('https://thumbnails.roblox.com/v1/assets?assetIds=' . $assetId . '&size=250x250&format=Png&isCircular=false'), true)['data'][0]; + + exit( + json_encode( + [ + 'Name' => $assetInformation['Name'], + 'Image' => $thumb['imageUrl'], + 'Description' => $assetInformation['Description'], + 'Price' => $assetInformation['PriceInRobux'], + 'OnSale' => $assetInformation['IsForSale'] + ] + ) + ); + } + else + { + exit('invalid-type'); + } +?> \ No newline at end of file diff --git a/www/core/func/api/admin/get/getAssets.php b/www/core/func/api/admin/get/getAssets.php new file mode 100644 index 0000000..730a760 --- /dev/null +++ b/www/core/func/api/admin/get/getAssets.php @@ -0,0 +1,30 @@ +prepare("SELECT * FROM catalog WHERE approved = 0 AND declined = 0;"); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo '

There are no pending assets to approve at this moment.

'; + } + $count = 0; + foreach($stmt as $result) { + $itemName = htmlentities($result['name'], ENT_QUOTES, "UTF-8"); + if (strlen($itemName) > 16) { + $itemName = substr($itemName, 0, 7) . '...'; + } + echo '
'.$itemName.'
'; + $creator = $result['creator_uid']; + $stmt = $dbcon->prepare("SELECT username FROM users WHERE id=:id;"); + $stmt->bindParam(':id', $creator, PDO::PARAM_INT); + $stmt->execute(); + $result2 = $stmt->fetch(PDO::FETCH_ASSOC); + $username = $result2['username']; + echo '

Type : '.$result['type'].'
Uploaded by '.$username.'
'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/admin/post/UploadRBXAsset.php b/www/core/func/api/admin/post/UploadRBXAsset.php new file mode 100644 index 0000000..28ad339 --- /dev/null +++ b/www/core/func/api/admin/post/UploadRBXAsset.php @@ -0,0 +1,154 @@ + 'mesh', + 'SoundId' => 'sound', + 'TextureId' => 'texture' + ]; + + $assets = []; + + $assetInformation = json_decode(file_get_contents('https://api.roblox.com/Marketplace/ProductInfo?assetId=' . intval($assetId)), true); + if(isset($assetInformation['AssetTypeId'])) + { + if ($assetInformation['AssetTypeId'] == 8 || ($assetInformation['AssetTypeId'] >= 41 && $assetInformation['AssetTypeId'] <= 47)) { + $assetType = 'hats'; + + $assetPaths['MeshId'] = 'mesh'; + $assetPaths['SoundId'] = 'sound'; + $assetPaths['TextureId'] = 'texture'; + + } elseif ($assetInformation['AssetTypeId'] == 19) { + $assetType = 'gear'; + + $assetPaths['MeshId'] = 'meshes'; + $assetPaths['SoundId'] = 'sounds'; + $assetPaths['TextureId'] = 'textures'; + + } + + foreach($assetPaths as $key => $folder) { + $assets[$folder] = []; + } + } + + if(!$assetType) { + exit('error'); + } + + $isOnSale = ($_POST['isOnSale'] == 'true'); + + if( + preg_match('/^[a-zA-Z0-9]+$/', $dataFile) != 1 || + preg_match('/[\r\n]+/', $dataFile) > 0 + ) exit('invalid-datafile'); + + if(file_exists($_SERVER['DOCUMENT_ROOT'] . '/data/assets/' . $assetType . '/models/' . $dataFile)) exit('already-exists'); + + if(strlen($name) < 3) exit('invalid-length'); + + if(intval($price) != $price || intval($price) < 0) exit('invalid-price'); + + if(intval($currencyType) != 0 && intval($currencyType) != 1) exit('invalid-price'); + + $data = file_get_contents('https://assetdelivery.roblox.com/v1/asset/?id=' . intval($assetId)); + + $decoded = @gzdecode($data); + if($decoded) $data = $decoded; + + $assetContent = simplexml_load_string($data, null, LIBXML_NOXMLDECL); + if($assetContent == false) exit('no-xml'); + + $items = $assetContent->xpath('//Item'); + foreach($items as $item) + { + if(isset($item['class']) && (string) $item['class'] == 'SpecialMesh' || (string) $item['class'] == 'Sound') + { + $contentProperties = $item[0]->Properties->Content; + foreach($contentProperties as $content) + { + $type = $assetPaths[(string)$content['name']]; + + $ogUrl = (string)$content->url; + + $url = str_replace('rbxassetid://', 'https://www.roblox.com/asset/?id=', $ogUrl); + $url = str_replace('http:', 'https:', $url); + $url = str_replace('www.', '', $url); + $url = str_replace('roblox.com/asset', 'roblox.com/v1/asset', $url); + $url = str_replace('://', '://assetdelivery.', $url); + + $content->url = $url; + + array_push($assets[$type], $content); + } + } + } + + $opts = [ + 'http' => [ + 'method' => 'GET', + 'header' => 'User-Agent: Roblox/Graphictoria\r\n' + ] + ]; + + $context = stream_context_create($opts); + + foreach($assets as $type => $value) + { + if(count($value) != 0) + { + $doIncrement = (count($value) > 1); + foreach($value as $index => $asset) + { + $download = file_get_contents((string)$asset->url, false, $context); + + if(@gzdecode($download)) $download=gzdecode($download); + + $fileName = $dataFile . ($doIncrement ? '_' . ($index+1) : ''); + $asset->url = 'http://gtoria.net/data/assets/' . $assetType . '/' . $type . '/' . $fileName; + + file_put_contents($_SERVER['DOCUMENT_ROOT'] . '/data/assets/' . $assetType . '/' . $type . '/' . $fileName, $download); + } + } + } + + file_put_contents($_SERVER['DOCUMENT_ROOT'] . '/data/assets/' . $assetType . '/models/' . $dataFile, dom_import_simplexml($assetContent)->ownerDocument->saveXML(null, LIBXML_NOEMPTYTAG)); + + $stmt = $dbcon->prepare("INSERT INTO `catalog` (`price`, `currencyType`, `creator_uid`, `name`, `description`, `type`, `approved`, `datafile`, `buyable`, `rbxasset`) VALUES (:price, :ctype, 15, :name, :description, '" . $assetType . "', 1, :datafile, :buyable, 1);"); + $stmt->bindParam(':price', $price, PDO::PARAM_INT); + $stmt->bindParam(':ctype', $currencyType, PDO::PARAM_INT); + $stmt->bindParam(':name', $name, PDO::PARAM_STR); + $stmt->bindParam(':description', $description, PDO::PARAM_STR); + $stmt->bindParam(':datafile', $dataFile, PDO::PARAM_STR); + $stmt->bindParam(':buyable', $isOnSale, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $dbcon->prepare('INSERT INTO `owneditems`(`uid`, `catalogid`, `type`, `rbxasset`) SELECT :user, `id`, "' . $assetType . '", 1 FROM `catalog` WHERE `catalog`.`datafile` = :datafile;'); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':datafile', $dataFile, PDO::PARAM_STR); + $stmt->execute(); + + context::requestImage($dataFile, $assetType); + + exit('success'); + }else{ + exit('error'); + } +?> \ No newline at end of file diff --git a/www/core/func/api/admin/post/approveAsset.php b/www/core/func/api/admin/post/approveAsset.php new file mode 100644 index 0000000..a759d98 --- /dev/null +++ b/www/core/func/api/admin/post/approveAsset.php @@ -0,0 +1,66 @@ +prepare("SELECT * FROM catalog WHERE id=:id;"); + $stmt->bindParam(':id', $itemID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'error'; + exit; + } + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $assetID = $result['assetid']; + $type = $result['type']; + $creatorID = $result['creator_uid']; + $assetName = $result['name']; + + if ($result['approved'] == 0 and $result['declined'] == 0) { + if ($type == "shirts" or $type == "pants" or $type == "tshirts") { + context::requestImage($assetID, $type); + } + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE catalog SET approved = 1 WHERE id=:id"); + $stmt->bindParam(':id', $itemID, PDO::PARAM_INT); + $stmt->execute(); + + if ($type != "decals") { + $query = "INSERT INTO ownedItems (`uid`, `catalogid`, `type`) VALUES (:uid, :catid, :type);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $creatorID, PDO::PARAM_INT); + $stmt->bindParam(':catid', $itemID, PDO::PARAM_INT); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->execute(); + } + + if ($type != "decals") { + $message = 'Your asset named '.$assetName.' has been approved and can be seen in the catalog. You also have received the item in your inventory. Your item can be found at https://gtoria.net/catalog/item/'.$itemID; + }else{ + $message = 'Your asset named '.$assetName.' has been approved and can be seen in the catalog. Your item can be found at https://gtoria.net/catalog/item/'.$itemID; + } + + $title = 'Asset Approval result for '.$assetName; + $query = "INSERT INTO messages (`recv_uid`, `sender_uid`, `title`, `content`) VALUES (:userId2, 15, :title, :msg);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':userId2', $creatorID, PDO::PARAM_INT); + $stmt->bindParam(':msg', $message, PDO::PARAM_STR); + $stmt->bindParam(':title', $title, PDO::PARAM_STR); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/admin/post/banUser.php b/www/core/func/api/admin/post/banUser.php new file mode 100644 index 0000000..b7ff944 --- /dev/null +++ b/www/core/func/api/admin/post/banUser.php @@ -0,0 +1,104 @@ + 5) die("invalid-duration"); + + if (strtolower($username) == strtolower($GLOBALS['userTable']['username'])) { + echo 'can-not-ban-yourself'; + exit; + } + + if (strlen($banReason) > 512) { + echo 'reason-too-long'; + exit; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT id, rank, banned, email, username FROM users WHERE username=:uname;"); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'no-user'; + exit; + } + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $email = $result['email']; + $udb = $result['username']; + if ($GLOBALS['userTable']['rank'] == 1) { + if ($result['rank'] == 1) { + echo 'can-not-ban-user'; + exit; + } + }else{ + if ($result['rank'] > 0) { + echo 'can-not-ban-user'; + exit; + } + } + + if ($result['banned'] == 1) { + echo 'user-already-banned'; + exit; + } + + $query = "UPDATE `users` SET `banned`=1 WHERE `username`=:uname;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + + $query = "UPDATE `users` SET `bantype`=:type WHERE `username`=:uname;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->bindParam(':type', $duration, PDO::PARAM_STR); + $stmt->execute(); + + $query = "UPDATE `users` SET `banreason`=:reason WHERE `username`=:uname;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->bindParam(':reason', $banReason, PDO::PARAM_STR); + $stmt->execute(); + + $query = "UPDATE `users` SET `bantime`=NOW() WHERE `username`=:uname;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + + // Get userID + $query = "SELECT id FROM users WHERE username = :uname"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $userID = $result['id']; + + $query = "INSERT INTO `banlogs` (`banned_by_uid`, `banned_by_uname`, `banned_uid`, `banned_uname`, `reason`, `bantype`) VALUES (:bannedbyuid, :bannedbyuname, :banneduid, :banneduname, :reason, :bantype);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':bannedbyuid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':bannedbyuname', $GLOBALS['userTable']['username'], PDO::PARAM_STR); + $stmt->bindParam(':banneduid', $userID, PDO::PARAM_INT); + $stmt->bindParam(':banneduname', $username, PDO::PARAM_STR); + $stmt->bindParam(':reason', $banReason, PDO::PARAM_STR); + $stmt->bindParam(':bantype', $duration, PDO::PARAM_INT); + $stmt->execute(); + + context::sendDiscordMessage(":first_place: ".$GLOBALS['userTable']['username']." has banned **".$username."** for reason **".$banReason."** (banType=".$duration.")"); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/admin/post/denyAsset.php b/www/core/func/api/admin/post/denyAsset.php new file mode 100644 index 0000000..3d3c291 --- /dev/null +++ b/www/core/func/api/admin/post/denyAsset.php @@ -0,0 +1,52 @@ +prepare("SELECT * FROM catalog WHERE id=:id;"); + $stmt->bindParam(':id', $itemID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $assetID = $result['assetid']; + $type = $result['type']; + $creatorID = $result['creator_uid']; + $assetName = $result['name']; + $fileHash = $result['fileHash']; + + if ($result['approved'] == 0 and $result['declined'] == 0) { + $stmt = $GLOBALS['dbcon']->prepare("UPDATE catalog SET declined = 1 WHERE id=:id"); + $stmt->bindParam(':id', $itemID, PDO::PARAM_INT); + $stmt->execute(); + + if ($result['type'] == "tshirts" || $result['type'] == "shirts" || $result['type'] == "pants" || $result['type'] == "decals") { + @unlink($_SERVER['DOCUMENT_ROOT'].'/data/assets/uploads/'.$result['fileHash']); + } + + $query = "INSERT INTO badHashes (`hash`) VALUES (:hash);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':hash', $fileHash, PDO::PARAM_STR); + $stmt->execute(); + + $message = 'Your asset named '.$assetName.' has been denied because it violated our rules. You have not been refunded.'; + $title = 'Asset Approval result for '.$assetName; + $query = "INSERT INTO messages (`recv_uid`, `sender_uid`, `title`, `content`) VALUES (:userId2, 15, :title, :msg);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':userId2', $creatorID, PDO::PARAM_INT); + $stmt->bindParam(':msg', $message, PDO::PARAM_STR); + $stmt->bindParam(':title', $title, PDO::PARAM_STR); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/admin/post/newHat.php b/www/core/func/api/admin/post/newHat.php new file mode 100644 index 0000000..34b7cd2 --- /dev/null +++ b/www/core/func/api/admin/post/newHat.php @@ -0,0 +1,151 @@ + 50) { + echo 'name-too-long'; + exit; + } + + if (strlen($datafileName) > 50) { + echo 'datafilename-too-long'; + exit; + } + + if (strlen($hatDescription) > 128) { + echo 'description-too-long'; + exit; + } + // Validate some things like, force price to be an integer and such + if ($isBuyable != "true" && $isBuyable != "false") { + echo 'illegal-buyable'; + exit; + } + + if ($isBuyable != "false") { + $buyable = 1; + }else{ + $buyable = 0; + } + + if ($rbxasset != "false") { + $rbxassetv = 1; + }else{ + $rbxassetv = 0; + } + + if (is_numeric($hatPrice) == false && $hatPrice != 0 || $hatPrice < 1) die("price-too-low"); + + // Check if the datafile name is in use (if this fails, there are several other checks in place) + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM catalog WHERE datafile=:dfile"); + $stmt->bindParam(':dfile', $datafileName, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + echo 'datafile-mesh-already-exists'; + exit; + } + + // Now move on to the files part, where the upload happens + // Check if the meshfile is a text file as meshes are pretty much text files + /*$finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime = finfo_file($finfo, $_FILES['meshFile']['tmp_name']); + if ($mime != "text/plain") { + echo 'meshfile-illegalFileType'; + exit; + }*/ + + // Move the file if it is a text/mesh file. + $uploadDirectory_meshFile = $_SERVER['DOCUMENT_ROOT'].'/data/assets/hats/mesh/'; + $uploadDirectory_modelFile = $_SERVER['DOCUMENT_ROOT'].'/data/assets/hats/models/'; + $uploadDirectory_textureFile = $_SERVER['DOCUMENT_ROOT'].'/data/assets/hats/texture/'; + if (!file_exists($uploadDirectory_meshFile.$datafileName)) { + if (!move_uploaded_file($_FILES["meshFile"]["tmp_name"], $uploadDirectory_meshFile.$datafileName)) { + echo 'file-upload-error'; + exit; + } + }else{ + echo 'datafile-mesh-already-exists'; + exit; + } + + // Parse the XML file (or model file) + if (!file_exists($uploadDirectory_modelFile.$datafileName)) { + $XMLFile = fopen($uploadDirectory_modelFile.$datafileName, "w") or die("file-upload-error"); + fwrite($XMLFile, $xmlContent); + fclose($XMLFile); + }else{ + echo 'datafile-mesh-already-exists'; + exit; + } + + // Now parse the texture file + $check = @getimagesize($_FILES["textureFile"]["tmp_name"]); + if (!$check) { + echo 'texture-illegalFileType'; + exit; + } + + $imageFileType = pathinfo($_FILES['textureFile']["name"], PATHINFO_EXTENSION); + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime = finfo_file($finfo, $_FILES['textureFile']['tmp_name']); + if ($imageFileType != "jpg" && $imageFileType != "JPG" && $imageFileType != "png" && $imageFileType != "PNG" && $imageFileType != "jpeg" && $imageFileType != "JPEG" && $mime != "image/png" && $mime != "image/jpeg") { + echo 'texture-illegalFileType'; + exit; + } + + if (exif_imagetype($_FILES['textureFile']['tmp_name']) != IMAGETYPE_PNG && exif_imagetype($_FILES['textureFile']['tmp_name']) != IMAGETYPE_JPEG) { + echo 'texture-illegalFileType'; + exit; + } + + if (!file_exists($uploadDirectory_textureFile .$datafileName)) { + if (!move_uploaded_file($_FILES["textureFile"]["tmp_name"], $uploadDirectory_textureFile.$datafileName)) { + echo 'file-upload-error'; + exit; + } + }else{ + echo 'datafile-mesh-already-exists'; + exit; + } + + // It looks like we've come this far, that we can finally add the hat to the catalog and render its image. + $stmt = $dbcon->prepare("INSERT INTO catalog (`price`, `creator_uid`, `name`, `description`, `type`, `approved`, `datafile`, `buyable`, `rbxasset`) VALUES (:price, :user, :name, :description, 'hats', 1, :datafile, :buyable, :rbxasset);"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':price', $hatPrice, PDO::PARAM_INT); + $stmt->bindParam(':name', $hatName, PDO::PARAM_STR); + $stmt->bindParam(':description', $hatDescription, PDO::PARAM_STR); + $stmt->bindParam(':datafile', $datafileName, PDO::PARAM_STR); + $stmt->bindParam(':buyable', $buyable, PDO::PARAM_INT); + $stmt->bindParam(':rbxasset', $rbxassetv, PDO::PARAM_INT); + $stmt->execute(); + + context::requestImage($datafileName, "hats"); + + echo 'success'; + }else{ + echo 'no-file'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/admin/post/prunePosts.php b/www/core/func/api/admin/post/prunePosts.php new file mode 100644 index 0000000..f84ede1 --- /dev/null +++ b/www/core/func/api/admin/post/prunePosts.php @@ -0,0 +1,154 @@ +prepare("SELECT id, rank, banned FROM users WHERE username=:uname;"); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'no-user'; + exit; + } + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($GLOBALS['userTable']['rank'] == 1) { + if ($result['rank'] == 1) { + echo 'can-not-prune-user'; + exit; + } + }else{ + if ($result['rank'] > 0) { + echo 'can-not-prune-user'; + exit; + } + } + + $userID = $result['id']; + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET posts = 0 WHERE id=:id;"); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM topics WHERE author_uid = :uid"); + $stmt->bindParam(':uid', $userID, PDO::PARAM_STR); + $stmt->execute(); + foreach($stmt as $result) { + $postID = $result['id']; + $forumId = $result['forumId']; + $stmt = $GLOBALS['dbcon']->prepare("UPDATE `topics` SET `deleted` = 1 WHERE`id` = :id"); + $stmt->bindParam(':id', $postID, PDO::PARAM_STR); + $stmt->execute(); + + $query = "SELECT `id` FROM `topics` WHERE `forumId` = :id AND `deleted` = 0;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->execute(); + $total = $stmt->rowCount(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE forums SET posts = :posts WHERE id=:id;"); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $total, PDO::PARAM_INT); + $stmt->execute(); + + $query = "SELECT id FROM replies WHERE forumId=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->execute(); + $total = $stmt->rowCount(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE forums SET replies = :posts WHERE id=:id;"); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $total, PDO::PARAM_INT); + $stmt->execute(); + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT id, postId, forumId FROM replies WHERE author_uid = :uid"); + $stmt->bindParam(':uid', $userID, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + $replyID = $result['id']; + $postID = $result['postId']; + $forumId = $result['forumId']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT post_time FROM replies WHERE postId = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $postID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $postTime = $result['post_time']; + + if ($stmt->rowCount() > 0) { + $query = "UPDATE `topics` SET `lastActivity`=:date WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postID, PDO::PARAM_INT); + $stmt->bindParam(':date', $postTime, PDO::PARAM_STR); + $stmt->execute(); + }else{ + $stmt = $GLOBALS['dbcon']->prepare("SELECT postTime FROM topics WHERE id = :id;"); + $stmt->bindParam(':id', $postID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $OPPostTime = $result['postTime']; + + $query = "UPDATE `topics` SET `lastActivity`=:date WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postID, PDO::PARAM_INT); + $stmt->bindParam(':date', $OPPostTime , PDO::PARAM_STR); + $stmt->execute(); + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM replies WHERE postId = :id;"); + $stmt->bindParam(':id', $postID, PDO::PARAM_INT); + $stmt->execute(); + $replyCount = $stmt->rowCount(); + + $query = "UPDATE `topics` SET `replies`=:rCount WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postID, PDO::PARAM_INT); + $stmt->bindParam(':rCount', $replyCount , PDO::PARAM_STR); + $stmt->execute(); + + $query = "SELECT * FROM topics WHERE forumId=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->execute(); + $total = $stmt->rowCount(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE forums SET posts = :posts WHERE id=:id;"); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $total, PDO::PARAM_INT); + $stmt->execute(); + + $query = "SELECT * FROM replies WHERE forumId=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->execute(); + $total = $stmt->rowCount(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE forums SET replies = :posts WHERE id=:id;"); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $total, PDO::PARAM_INT); + $stmt->execute(); + } + + context::sendDiscordMessage($GLOBALS['userTable']['username'].' has pruned the posts of user **'.$username.'**'); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/admin/post/render.php b/www/core/func/api/admin/post/render.php new file mode 100644 index 0000000..faa5337 --- /dev/null +++ b/www/core/func/api/admin/post/render.php @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/www/core/func/api/admin/post/rewardPostie.php b/www/core/func/api/admin/post/rewardPostie.php new file mode 100644 index 0000000..1ff13f9 --- /dev/null +++ b/www/core/func/api/admin/post/rewardPostie.php @@ -0,0 +1,61 @@ +prepare("SELECT id, rank, banned, lastAward2, posties FROM users WHERE username=:uname;"); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'no-user'; + exit; + } + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + // Rate limiting + $timeSince = round(abs(strtotime(date('Y-m-d H:i:s')) - strtotime($result['lastAward2'])) / 60,2); + if ($timeSince < 5) { + echo 'can-not-reward-user'; + exit; + } + + $newPosties = $result['posties']+10; + + $query = "UPDATE `users` SET `posties`=:newPosties WHERE `username`=:uname;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':newPosties', $newPosties, PDO::PARAM_STR); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + + $query = "UPDATE `users` SET `lastAward2`=NOW() WHERE `username`=:uname;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + + context::sendDiscordMessage($GLOBALS['userTable']['username'].' has awarded 10 posties to user **'.$username.'**'); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/admin/post/unbanUser.php b/www/core/func/api/admin/post/unbanUser.php new file mode 100644 index 0000000..c958bb0 --- /dev/null +++ b/www/core/func/api/admin/post/unbanUser.php @@ -0,0 +1,66 @@ +prepare("SELECT id, rank, banned FROM users WHERE username=:uname;"); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'no-user'; + exit; + } + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($GLOBALS['userTable']['rank'] == 1) { + if ($result['rank'] == 1) { + echo 'can-not-unban-user'; + exit; + } + }else{ + if ($result['rank'] > 0) { + echo 'can-not-unban-user'; + exit; + } + } + + if ($result['banned'] == 0) { + echo 'user-not-banned'; + exit; + } + + $query = "UPDATE `users` SET `banned`=0 WHERE `username`=:uname;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + + $query = "UPDATE `users` SET `bantype`=0 WHERE `username`=:uname;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + + $query = "UPDATE `users` SET `banreason`=NULL WHERE `username`=:uname;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uname', $username, PDO::PARAM_STR); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/asset/get.php b/www/core/func/api/asset/get.php new file mode 100644 index 0000000..7a6aa74 --- /dev/null +++ b/www/core/func/api/asset/get.php @@ -0,0 +1,188 @@ +prepare('SELECT `creator_uid`, `buyable`, `deleted`, `approved`, `type`, `fileHash`, `assetid`, `datafile` FROM `catalog` WHERE `id`=:id LIMIT 1;'); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + + if($stmt->rowCount() === 0) + { + if(isset($_SERVER['HTTP_USER_AGENT']) && str_starts_with(strtolower($_SERVER['HTTP_USER_AGENT']), 'roblox/')) + { + $stmt = $GLOBALS['dbcon']->prepare('SELECT `id`, `filehash` FROM `rbxassets` WHERE `rbxassetid`=:id AND `version`=:version LIMIT 1;'); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->bindParam(':version', $version, PDO::PARAM_INT); + $stmt->execute(); + + $rows = $stmt->rowCount(); + + if($rows == 0 || $version == 0) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_USERAGENT, 'Roblox/Graphictoria'); + curl_setopt($ch, CURLOPT_URL, 'https://platinum.roblox.com/v1/asset/?id=' . $id . '&version=' . $version); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('host: assetdelivery.roblox.com')); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_NOBODY, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET'); + $result = curl_exec($ch); + curl_close($ch); + + $responseCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); + + switch($responseCode) + { + case 403: + case 404: + if($stmt->rowCount() != 0) + { + $asset = $stmt->fetch(PDO::FETCH_ASSOC); + + http_response_code(301); + header('location: ' . getCdnUrl($asset['filehash'])); + exit; + } + break; + default: + break; + } + + switch($responseCode) + { + case 403: + http_response_code(403); + exit; + break; + case 404: + http_response_code(404); + exit; + break; + default: + break; + } + + $headers = []; + + foreach(explode("\r\n", $result) as $header) + { + $parsed = explode(': ', $header); + + if(isset($parsed[1])) + $headers[$parsed[0]] = $parsed[1]; + } + + if(isset($headers['location'])) + { + $hash = substr($headers['location'], strrpos($headers['location'], '/') + 1); + + if($stmt->rowCount() == 0) + { + $stmt = $GLOBALS['dbcon']->prepare('INSERT INTO `rbxassets` (`rbxassetid`, `version`, `filehash`) VALUES(:id, :version, :hash);'); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->bindParam(':version', $version, PDO::PARAM_INT); + $stmt->bindParam(':hash', $hash, PDO::PARAM_STR); + $stmt->execute(); + } + else + { + $tableid = $stmt->fetch(PDO::FETCH_ASSOC)['id']; + + $stmt = $GLOBALS['dbcon']->prepare('UPDATE `rbxassets` SET `rbxassetid`=:id, `version`=:version, `filehash`=:hash WHERE `id`=:tbid;'); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->bindParam(':tbid', $id, PDO::PARAM_INT); + $stmt->bindParam(':version', $version, PDO::PARAM_INT); + $stmt->bindParam(':hash', $hash, PDO::PARAM_STR); + $stmt->execute(); + } + + http_response_code(301); + header('location: ' . getCdnUrl($hash)); + exit; + } + elseif($rows != 0) + { + $asset = $stmt->fetch(PDO::FETCH_ASSOC); + + http_response_code(301); + header('location: ' . getCdnUrl($asset['filehash'])); + exit; + } + + http_response_code(404); + exit; + } + else + { + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + http_response_code(301); + header('location: ' . getCdnUrl($result['filehash'])); + exit; + } + } + + http_response_code(404); + exit; + } + + $asset = $stmt->fetch(PDO::FETCH_ASSOC); + + if($asset['deleted'] == 1 || $asset['approved'] == 0) + { + http_response_code(403); + exit; + } + + http_response_code(301); + switch($asset['type']) + { + case 'heads': + case 'faces': + case 'gear': + case 'hats': + header('location: https://www.gtoria.net/data/assets/' . $asset['type'] . '/models/' . $asset['datafile']); + break; + case 'tshirts': + case 'pants': + case 'shirts': + header('location: https://www.gtoria.net/data/assets/' . $asset['type'] . '/models/get.php?id=' . $asset['assetid']); + break; + case 'decals': + header('location: https://www.gtoria.net/data/assets/uploads/' . $asset['fileHash']); + break; + default: + http_response_code(400); + break; + } \ No newline at end of file diff --git a/www/core/func/api/auth/changeEmailVerify.php b/www/core/func/api/auth/changeEmailVerify.php new file mode 100644 index 0000000..b46e5e5 --- /dev/null +++ b/www/core/func/api/auth/changeEmailVerify.php @@ -0,0 +1,68 @@ + 128) die("inc-email"); + if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) die("inc-email"); + $domain = substr($newEmail, strpos($newEmail, '@') + 1); + if (checkdnsrr($domain) == false) die("inc-email"); + $from_time = strtotime($GLOBALS['userTable']['lastUpload']); + $to_time = strtotime(context::getCurrentTime()); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince < 60) die("rate-limit"); + + // Email domain whitelist, to stop disposable and fake emails. + $good_emails = array('@outlook', '@protonmail.com', '@gtoria.net', '@roblox.com', '@icloud.com', '@protonmail.ch', '@google.com', + "@yahoo.com.br", "@hotmail.com.br", "@outlook.com.br", "@uol.com.br", "@bol.com.br", "@terra.com.br", "@ig.com.br", "@itelefonica.com.br", "@r7.com", "@zipmail.com.br", "@globo.com", "@globomail.com", "@oi.com.br", + "@yahoo.com.mx", "@live.com.mx", "@hotmail.es", "@hotmail.com.mx", "@prodigy.net.mx", + "@hotmail.com.ar", "@live.com.ar", "@yahoo.com.ar", "@fibertel.com.ar", "@speedy.com.ar", "@arnet.com.ar", + "@hotmail.be", "@live.be", "@skynet.be", "@voo.be", "@tvcablenet.be", "@telenet.be", + "@mail.ru", "@rambler.ru", "@yandex.ru", "@ya.ru", "@list.ru", + "@gmx.de", "@hotmail.de", "@live.de", "@online.de", "@t-online.de", "@web.de", "@yahoo.de", + "@hotmail.fr", "@live.fr", "@laposte.net", "@yahoo.fr", "@wanadoo.fr", "@orange.fr", "@gmx.fr", "@sfr.fr", "@neuf.fr", "@free.fr", + "@sina.com", "@qq.com", "@naver.com", "@hanmail.net", "@daum.net", "@nate.com", "@yahoo.co.jp", "@yahoo.co.kr", "@yahoo.co.id", "@yahoo.co.in", "@yahoo.com.sg", "@yahoo.com.ph", + "@btinternet.com", "@virginmedia.com", "@blueyonder.co.uk", "@freeserve.co.uk", "@live.co.uk", + "@ntlworld.com", "@o2.co.uk", "@orange.net", "@sky.com", "@talktalk.co.uk", "@tiscali.co.uk", + "@virgin.net", "@wanadoo.co.uk", "@bt.com", "@bellsouth.net", "@charter.net", "@cox.net", "@earthlink.net", "@juno.com", + "@email.com", "@games.com", "@gmx.net", "@hush.com", "@hushmail.com", "@icloud.com", "@inbox.com", + "@lavabit.com", "@love.com", "@outlook.com", "@pobox.com", "@rocketmail.com", + "@safe-mail.net", "@wow.com", "@ygm.com", "@ymail.com", "@zoho.com", "@fastmail.fm", + "@yandex.com","@iname.com", "@aol.com", "@att.net", "@comcast.net", "@facebook.com", "@gmail.com", "@gmx.com", "@googlemail.com", + "@google.com", "@hotmail.com", "@hotmail.co.uk", "@mac.com", "@me.com", "@mail.com", "@msn.com", + "@live.com", "@sbcglobal.net", "@verizon.net", "@yahoo.com", "@yahoo.co.uk" + ); + + if (!context::contains(strtolower($newEmail), $good_emails)) die("unknown-email"); + $auth_hash = crypt($currentPassword, $GLOBALS['userTable']['password_salt']); + if ($GLOBALS['userTable']['password'] != md5($currentPassword) and $auth_hash != $GLOBALS['userTable']['password_hash']) die("inc-password"); + + $stmt = $dbcon->prepare("SELECT email FROM users WHERE email = :email;"); + $stmt->bindParam(':email', $newEmail, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() > 0) die("email-in-use"); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET email = :email WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':email', $newEmail, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET lastUpload = NOW() WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET emailcodeTime = NULL WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/auth/forgot.php b/www/core/func/api/auth/forgot.php new file mode 100644 index 0000000..b48b5b3 --- /dev/null +++ b/www/core/func/api/auth/forgot.php @@ -0,0 +1,85 @@ +prepare("SELECT id, username, email FROM users WHERE username = :username OR email = :email;"); + $stmt->bindParam(':username', $username, PDO::PARAM_STR); + $stmt->bindParam(':email', $username, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'no-user'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $userID = $result['id']; + $username = $result['username']; + $email = $result['email']; + + $query = "SELECT * FROM pwdreset WHERE ip = :ip LIMIT 1;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() > 0) { + $currentTime = date('Y-m-d H:i:s'); + $to_time = strtotime($currentTime); + $from_time = strtotime($result['date']); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince < 5) { + echo 'rate-limit'; + exit; + } + } + + // If IP is changed. + $query = "SELECT * FROM passwordresets WHERE userId = :uid LIMIT 1;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $userID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() > 0) { + $currentTime = date('Y-m-d H:i:s'); + $to_time = strtotime($currentTime); + $from_time = strtotime($result['date']); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince < 5) { + echo 'rate-limit'; + exit; + }else{ + // Delete every other request + $stmt = $dbcon->prepare("DELETE FROM passwordresets WHERE userId = :uid"); + $stmt->bindParam(':uid', $userID, PDO::PARAM_INT); + $stmt->execute(); + } + } + + $stmt = $dbcon->prepare("DELETE FROM pwdreset WHERE ip = :ip"); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $dbcon->prepare("INSERT INTO `pwdreset` (`ip`) VALUES (:ip);"); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + + $key = context::random_str(256); + $stmt = $dbcon->prepare("INSERT INTO `passwordresets` (`userId`, `key`) VALUES (:uid, :key);"); + $stmt->bindParam(':uid', $userID, PDO::PARAM_INT); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->execute(); + + mailHandler::sendMail('Hello '.$username.'! You can reset your password at https://gtoria.net/account/resetpassword/'.$userID.'/'.$key.' if you did not request this, you can ignore this.

Graphictoria
Please know that this message was generated automatically, do not reply to this. If you need help, send a message to support@gtoria.net.', "Hello ".$username."! You can reset your password at https://gtoria.net/account/resetpassword/".$userID."/".$key." if you did not request this, you can ignore this.", $email, "Graphictoria Password Reset", $username); + + echo 'success'; + }else{ + echo 'error'; + exit; + } +?> \ No newline at end of file diff --git a/www/core/func/api/auth/login.php b/www/core/func/api/auth/login.php new file mode 100644 index 0000000..77e8cec --- /dev/null +++ b/www/core/func/api/auth/login.php @@ -0,0 +1,238 @@ +prepare($query); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 1) { + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['count'] == 3) { + $from_time = strtotime($result['time']); + $to_time = strtotime(context::getCurrentTime()); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince < 2) { + echo 'rate-limit'; + exit; + } + } + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT id, password_salt, password, password_hash, registerIP, passwordVersion, rank, posts, username FROM users WHERE username = :username OR email = :email;"); + $stmt->bindParam(':username', $username, PDO::PARAM_STR); + $stmt->bindParam(':email', $username, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) { + echo 'no-user'; + exit; + } + + $auth_hash = crypt($password, $result['password_salt']); + if ($auth_hash == $result['password_hash']) { + if ($result['registerIP'] == NULL) { + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET registerIP = :ip WHERE id = :id;"); + $stmt->bindParam(':id', $result['id'], PDO::PARAM_INT); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + } + + /*$stmt = $GLOBALS['dbcon']->prepare('DELETE FROM `sessions` WHERE `userId`=:userId;'); + $stmt->bindParam(':userId', $result['id'], PDO::PARAM_INT); + $stmt->execute();*/ + + $form_code = md5(uniqid()); + $aid = context::random_str(128); + $location = $_SERVER["HTTP_CF_IPCOUNTRY"]; + $stmt = $GLOBALS['dbcon']->prepare('INSERT INTO `sessions` (`userId`, `sessionId`, `csrfToken`, `useragent`, `location`) VALUES (:userId, :sid, :csrf, :useragent, :location);'); + $stmt->bindParam(':userId', $result['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $aid, PDO::PARAM_STR); + $stmt->bindParam(':csrf', $form_code, PDO::PARAM_STR); + $stmt->bindParam(':useragent', $_SERVER['HTTP_USER_AGENT'], PDO::PARAM_STR); + $stmt->bindParam(':location', $location, PDO::PARAM_STR); + $stmt->execute(); + + if (isset($_COOKIE['auth_uid']) || isset($_COOKIE['a_id'])) { + setcookie('auth_uid', "", time() - 3600); + setcookie('a_id', "", time() - 3600); + } + + setcookie("auth_uid", $result['id'], time() + (86400 * 30), "/", ".gtoria.net", false, true); + setcookie("a_id", $aid, time() + (86400 * 30), "/", ".gtoria.net", false, true); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET lastIP = :ip WHERE id = :id;"); + $stmt->bindParam(':id', $result['id'], PDO::PARAM_INT); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + + $key = sha1($form_code); + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET gameKey = :key WHERE id = :id;"); + $stmt->bindParam(':id', $result['id'], PDO::PARAM_INT); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->execute(); + + // Award badges + if ($result['rank'] == 1) { + // Check if the admin badge is owned + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM badges WHERE uid = :uid AND badgeId = 2"); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + // Award badge + $query = "INSERT INTO badges (`uid`, `badgeId`) VALUES (:uid, 2);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + // Check if the moderator badge is owned + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM badges WHERE uid = :uid AND badgeId = 3"); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + // Award badge + $query = "INSERT INTO badges (`uid`, `badgeId`) VALUES (:uid, 3);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + } + } + + if ($result['rank'] == 2) { + // Check if the moderator badge is owned + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM badges WHERE uid = :uid AND badgeId = 3"); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + // Award badge + $query = "INSERT INTO badges (`uid`, `badgeId`) VALUES (:uid, 3);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + // Remove admin badge if any + $query = "DELETE FROM badges WHERE badgeId = 2 AND uid = :uid"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + if ($result['rank'] == 0) { + // Remove staff badges if any + $query = "DELETE FROM badges WHERE badgeId = 2 AND uid = :uid"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + + $query = "DELETE FROM badges WHERE badgeId = 3 AND uid = :uid"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + // Check if the member badge is owned + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM badges WHERE uid = :uid AND badgeId = 5"); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + // Award badge + $query = "INSERT INTO badges (`uid`, `badgeId`) VALUES (:uid, 5);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + // Get forum post count + $postCount = $result['posts']; + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM badges WHERE uid = :uid AND badgeId = 4"); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0 and $postCount > 999) { + // Award badge + $query = "INSERT INTO badges (`uid`, `badgeId`) VALUES (:uid, 4);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + }else{ + if ($postCount < 1000) { + $query = "DELETE FROM badges WHERE badgeId = 4 AND uid = :uid"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + } + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM badges WHERE uid = :uid AND badgeId = 7"); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0 and $result['id'] < 101) { + // Award badge + $query = "INSERT INTO badges (`uid`, `badgeId`) VALUES (:uid, 7);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + }else{ + if ($result['id'] > 100) { + $query = "DELETE FROM badges WHERE badgeId = 7 AND uid = :uid"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + } + } + + context::sendDiscordMessage("**User logged in** | ".$result['username']); + + echo 'success'; + exit; + }else{ + $query = "SELECT * FROM loginAttempts WHERE ip = :ip"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + + $delete = false; + if ($stmt->rowCount() == 3) { + $query = "DELETE FROM loginAttempts WHERE ip = :ip;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + $delete = true; + } + + if ($delete == true) { + $count = 1; + }else{ + $count = $stmt->rowCount()+1; + } + + $query = "INSERT INTO loginAttempts (`ip`, `uid`, `count`) VALUES (:ip, :uid, :count);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->bindParam(':uid', $result['id'], PDO::PARAM_INT); + $stmt->bindParam(':count', $count, PDO::PARAM_INT); + $stmt->execute(); + echo 'incorrect-password'; + exit; + } + + }else{ + echo 'error'; + exit; + } +?> diff --git a/www/core/func/api/auth/ping.php b/www/core/func/api/auth/ping.php new file mode 100644 index 0000000..e69de29 diff --git a/www/core/func/api/auth/register.php b/www/core/func/api/auth/register.php new file mode 100644 index 0000000..331e5f3 --- /dev/null +++ b/www/core/func/api/auth/register.php @@ -0,0 +1,171 @@ + 20) { + echo 'username-too-long'; + exit; + } + + if(!preg_match("/^[a-zA-Z0-9]+[\w\.]?[a-zA-Z0-9]+$/", $username) == 1 || str_contains($username, "\n") || str_contains($username, "\r") || str_contains($username, "\t")) { + echo 'illegal-username'; + exit; + } + + if (strlen($email) == 0) { + echo 'no-email'; + exit; + } + + if (strlen($email) > 128) { + echo 'email-too-long'; + exit; + } + + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + echo 'illegal-email'; + exit; + } + + $domain = substr($email, strpos($email, '@') + 1); + if (checkdnsrr($domain) == false) die("illegal-email"); + + // Email domain whitelist, to stop disposable and fake emails. Will only be checked at initial register. + $good_emails = array('@outlook', '@protonmail.com', '@gtoria.net', '@roblox.com', '@icloud.com', '@protonmail.ch', '@google.com', + "@yahoo.com.br", "@hotmail.com.br", "@outlook.com.br", "@uol.com.br", "@bol.com.br", "@terra.com.br", "@ig.com.br", "@itelefonica.com.br", "@r7.com", "@zipmail.com.br", "@globo.com", "@globomail.com", "@oi.com.br", + "@yahoo.com.mx", "@live.com.mx", "@hotmail.es", "@hotmail.com.mx", "@prodigy.net.mx", + "@hotmail.com.ar", "@live.com.ar", "@yahoo.com.ar", "@fibertel.com.ar", "@speedy.com.ar", "@arnet.com.ar", + "@hotmail.be", "@live.be", "@skynet.be", "@voo.be", "@tvcablenet.be", "@telenet.be", + "@mail.ru", "@rambler.ru", "@yandex.ru", "@ya.ru", "@list.ru", + "@gmx.de", "@hotmail.de", "@live.de", "@online.de", "@t-online.de", "@web.de", "@yahoo.de", + "@hotmail.fr", "@live.fr", "@laposte.net", "@yahoo.fr", "@wanadoo.fr", "@orange.fr", "@gmx.fr", "@sfr.fr", "@neuf.fr", "@free.fr", + "@sina.com", "@qq.com", "@naver.com", "@hanmail.net", "@daum.net", "@nate.com", "@yahoo.co.jp", "@yahoo.co.kr", "@yahoo.co.id", "@yahoo.co.in", "@yahoo.com.sg", "@yahoo.com.ph", + "@btinternet.com", "@virginmedia.com", "@blueyonder.co.uk", "@freeserve.co.uk", "@live.co.uk", + "@ntlworld.com", "@o2.co.uk", "@orange.net", "@sky.com", "@talktalk.co.uk", "@tiscali.co.uk", + "@virgin.net", "@wanadoo.co.uk", "@bt.com", "@bellsouth.net", "@charter.net", "@cox.net", "@earthlink.net", "@juno.com", + "@email.com", "@games.com", "@gmx.net", "@hush.com", "@hushmail.com", "@icloud.com", "@inbox.com", + "@lavabit.com", "@love.com", "@outlook.com", "@pobox.com", "@rocketmail.com", + "@safe-mail.net", "@wow.com", "@ygm.com", "@ymail.com", "@zoho.com", "@fastmail.fm", + "@yandex.com","@iname.com", "@aol.com", "@att.net", "@comcast.net", "@facebook.com", "@gmail.com", "@gmx.com", "@googlemail.com", + "@google.com", "@hotmail.com", "@hotmail.co.uk", "@mac.com", "@me.com", "@mail.com", "@msn.com", + "@live.com", "@sbcglobal.net", "@verizon.net", "@yahoo.com", "@yahoo.co.uk" + ); + + if(context::contains($email, ["@outlook.com", "@outlook.com.br", "@outlook", "@aol.com", "@yahoo."])) die('no-outlook'); + + if (!context::contains(strtolower($email), $good_emails)) die("unknown-email"); + + if (strlen($password1) == 0) { + echo 'no-password'; + exit; + } + + if (strlen($password2) == 0) { + echo 'no-password'; + exit; + } + + if ($password1 != $password2) { + echo 'passwords-mismatch'; + exit; + } + + if (strlen($password1) < 6) { + echo 'password-too-short'; + exit; + } + + if (strlen($password1) > 40) { + echo 'password-too-long'; + exit; + } + + $stmt = $dbcon->prepare("SELECT * FROM users WHERE email = :email;"); + $stmt->bindParam(':email', $email, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + echo 'email-already-used'; + exit; + } + + $stmt = $dbcon->prepare("SELECT * FROM users WHERE username = :username;"); + $stmt->bindParam(':username', $username, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + echo 'username-already-used'; + exit; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT joinDate FROM users WHERE registerIP = :ip ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $currentTime = context::getCurrentTime(); + $to_time = strtotime($currentTime); + $from_time = strtotime($result['joinDate']); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince < 1440) { + echo 'rate-limit'; + exit; + } + } + + // Still here? Continue. Please use password_hash... + $salt = '$2a$07$'.uniqid(mt_rand(), true).'$'; + $hash = crypt($password1, $salt); + $stmt = $dbcon->prepare("INSERT INTO users (`username`, `password_hash`, `password_salt`, `email`, `registerIP`, `passwordVersion`) VALUES (:user, :hash, :salt, :email, :ip, 2);"); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->bindParam(':user', $username, PDO::PARAM_STR); + $stmt->bindParam(':hash', $hash, PDO::PARAM_STR); + $stmt->bindParam(':salt', $salt, PDO::PARAM_STR); + $stmt->bindParam(':email', $email, PDO::PARAM_STR); + $stmt->execute(); + + context::sendDiscordMessage("**New user registered!** | ".$username); + + echo 'success'; + }else{ + echo 'error'; + exit; + } +?> diff --git a/www/core/func/api/auth/resetPassword.php b/www/core/func/api/auth/resetPassword.php new file mode 100644 index 0000000..8ca76ef --- /dev/null +++ b/www/core/func/api/auth/resetPassword.php @@ -0,0 +1,75 @@ +prepare($query); + $stmt->bindParam(':uid', $userID, PDO::PARAM_INT); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0 or $result['used'] == 1) { + echo 'invalid-key'; + exit; + } + + $currentTime = date('Y-m-d H:i:s'); + $to_time = strtotime($currentTime); + $from_time = strtotime($result['date']); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 5) { + echo 'key-expired'; + exit; + } + + if ($password1 != $password2) { + echo 'password-mismatch'; + exit; + } + + if (strlen($password1) > 42) { + echo 'password-too-long'; + exit; + } + + if (strlen($password1) < 6) { + echo 'password-too-short'; + exit; + } + + $salt = '$2a$07$'.uniqid(mt_rand(), true).'$'; + $hash = crypt($password1, $salt); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET password_salt = :salt WHERE id = :user;"); + $stmt->bindParam(':user', $userID, PDO::PARAM_INT); + $stmt->bindParam(':salt', $salt, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET password_hash = :hash WHERE id = :user;"); + $stmt->bindParam(':user', $userID, PDO::PARAM_INT); + $stmt->bindParam(':hash', $hash, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE passwordresets SET used = 1 WHERE `key` = :key AND userid = :uid;"); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->bindParam(':uid', $userID, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("DELETE FROM sessions WHERE userId = :uid;"); + $stmt->bindParam(':uid', $userID, PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/auth/twostep.php b/www/core/func/api/auth/twostep.php new file mode 100644 index 0000000..eea9bfa --- /dev/null +++ b/www/core/func/api/auth/twostep.php @@ -0,0 +1,37 @@ +checkCode($GLOBALS['userTable']['authKey'], $factorCode)) { + echo 'wrong-code'; + exit; + }else{ + $stmt = $GLOBALS['dbcon']->prepare("UPDATE sessions SET factorFinish = 1 WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['sessionTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + echo 'success'; + } + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/auth/verifyEmail.php b/www/core/func/api/auth/verifyEmail.php new file mode 100644 index 0000000..2aa396d --- /dev/null +++ b/www/core/func/api/auth/verifyEmail.php @@ -0,0 +1,29 @@ + \ No newline at end of file diff --git a/www/core/func/api/avatar/get.php b/www/core/func/api/avatar/get.php new file mode 100644 index 0000000..e026a25 --- /dev/null +++ b/www/core/func/api/avatar/get.php @@ -0,0 +1,64 @@ +prepare('SELECT `id`, `thumbnailHash`, `headshotHash` FROM `users` WHERE `username` = :uname;'); + $UserQuery->bindParam(':uname', $_GET['uname'], PDO::PARAM_STR); + $UserQuery->execute(); + + if($UserQuery->rowCount() == 0) + { + header('Content-Type: application/json'); + http_response_code(400); + exit( + json_encode( + [ + 'errors' => [ + 'Unknown user.' + ] + ], + JSON_UNESCAPED_SLASHES + ) + ); + } + + $Result = $UserQuery->fetch(PDO::FETCH_ASSOC); + + if(isset($_GET['headshot'])) { + header('location: ' . context::getUserHeadshotImage($Result)); + exit; + } else { + header('location: ' . context::getUserImage($Result)); + exit; + } +} +else +{ + header('Content-Type: application/json'); + http_response_code(400); + exit( + json_encode( + [ + 'errors' => [ + 'Request is malformed. Check your query and try again.' + ] + ], + JSON_UNESCAPED_SLASHES + ) + ); +} \ No newline at end of file diff --git a/www/core/func/api/blog/addPost.php b/www/core/func/api/blog/addPost.php new file mode 100644 index 0000000..e37b78f --- /dev/null +++ b/www/core/func/api/blog/addPost.php @@ -0,0 +1,19 @@ + + + +
+

+ + \ No newline at end of file diff --git a/www/core/func/api/blog/getPosts.php b/www/core/func/api/blog/getPosts.php new file mode 100644 index 0000000..4b316e5 --- /dev/null +++ b/www/core/func/api/blog/getPosts.php @@ -0,0 +1,17 @@ +prepare("SELECT * FROM blogposts ORDER BY id DESC"); + $stmt->execute(); + foreach($stmt as $result) { + $userSheet = context::getUserSheetByID($result['poster_uid']); + echo '
+
+
+
+

'.context::secureString($result['title']).'

+

Posted by '.$userSheet['username'].', '.context::humanTimingSince(strtotime($result['date'])).' ago

+
+
+
'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/blog/loadPost.php b/www/core/func/api/blog/loadPost.php new file mode 100644 index 0000000..f8d46f5 --- /dev/null +++ b/www/core/func/api/blog/loadPost.php @@ -0,0 +1,25 @@ +prepare("SELECT * FROM blogposts WHERE id = :id"); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) die("Post not found"); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $userSheet = context::getUserSheetByID($result['poster_uid']); + $content = context::secureString($result['content']); + $content = preg_replace("/\s*[a-zA-Z\/\/:\.]*youtube.com\/watch\?v=([a-zA-Z0-9\-_]+)([a-zA-Z0-9\/\*\-\_\?\&\;\%\=\.]*)/i","

", $content); + $content = preg_replace("/https?:\/\/[^ ]+?(?:\.jpg|\.jpeg|\.png|\.gif)/",'

', $content); + $content = context::showBBcodes($content); + echo ' +
+
+
'.context::getOnline($userSheet).''.$userSheet['username'].'
+
+

'.nl2br($content).'

+
+
+
'; +?> \ No newline at end of file diff --git a/www/core/func/api/catalog/getItems.php b/www/core/func/api/catalog/getItems.php new file mode 100644 index 0000000..debdc40 --- /dev/null +++ b/www/core/func/api/catalog/getItems.php @@ -0,0 +1,73 @@ +type = "'.$type.'";'; + + if (isset($term) and strlen($term) > 0) { + $searchTermSQL = '%'.$term.'%'; + if ($GLOBALS['loggedIn']) $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM catalog WHERE name LIKE :term AND approved = 1 AND type = :type ORDER BY id DESC LIMIT 16 OFFSET :offset"); + if (!$GLOBALS['loggedIn']) $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM catalog WHERE name LIKE :term AND approved = 1 AND type = :type AND rbxasset = 0 ORDER BY id DESC LIMIT 16 OFFSET :offset"); + $stmt->bindParam(':term', $searchTermSQL, PDO::PARAM_STR); + }else{ + if ($GLOBALS['loggedIn']) $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM catalog WHERE type = :type AND buyable = 1 AND approved = 1 ORDER BY id DESC LIMIT 16 OFFSET :offset"); + if (!$GLOBALS['loggedIn']) $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM catalog WHERE type = :type AND buyable = 1 AND approved = 1 AND rbxasset = 0 ORDER BY id DESC LIMIT 16 OFFSET :offset"); + } + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->execute(); + $count = 0; + echo '
'; + foreach($stmt as $result) { + $count++; + if ($count < 16) { + echo '
'; + echo ''; + echo '
'; + $itemName = context::secureString($result['name']); + if (strlen($itemName) >= 40) { + $itemName = substr($itemName, 0, 37). " ... "; + } + echo '
'.$itemName.'
'; + if ($result['type'] != "decals") { + if ($result['currencyType'] == 0) { + echo '
'.$result['price'].'

'; + }else{ + echo '
'.$result['price'].'

'; + } + } + echo 'Details'; + echo '
'; + } + } + if ($count == 0) { + echo '

Nothing found

'; + } + if ($count > 15) { + echo ''; + } + echo '
'; + }else{ + echo 'An error occurred'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/catalog/post/buyItem.php b/www/core/func/api/catalog/post/buyItem.php new file mode 100644 index 0000000..668edd1 --- /dev/null +++ b/www/core/func/api/catalog/post/buyItem.php @@ -0,0 +1,99 @@ +prepare("SELECT * FROM ownedItems WHERE uid=:id AND catalogid = :catid"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':catid', $itemId, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + $owned = true; + }else{ + $owned = false; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM catalog WHERE id=:id"); + $stmt->bindParam(':id', $itemId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $owneruserID = $result['creator_uid']; + + if ($owned == false and $result['buyable'] == 1 and $result['type'] !== "decals" and $result['approved'] == 1) { + if ($result['id'] != $itemId) { + echo 'error'; + exit; + } + $canBuy = false; + if ($result['currencyType'] == 0) { + if ($result['price'] < $GLOBALS['userTable']['coins'] or $result['price'] == $GLOBALS['userTable']['coins']) { + $canBuy = true; + } + } + + if ($result['currencyType'] == 1) { + if ($result['price'] < $GLOBALS['userTable']['posties'] or $result['price'] == $GLOBALS['userTable']['posties']) { + $canBuy = true; + } + } + + if ($canBuy == true) { + if ($result['currencyType'] == 0) { + $newBalance = $GLOBALS['userTable']['coins']-$result['price']; + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET coins = :coins WHERE id = :user;"); + $stmt->bindParam(':coins', $newBalance, PDO::PARAM_INT); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + // If the buyer's account is over 1 week old, award the seller + $timeSince = round(abs(strtotime(context::getCurrentTime()) - strtotime($GLOBALS['userTable']['joinDate'])) / 60,2); + if ($timeSince > 10080) { + $awardPrice = round($result['price']/2); + + // Get seller's current coins + $stmt = $GLOBALS['dbcon']->prepare("SELECT coins FROM users WHERE id=:id"); + $stmt->bindParam(':id', $owneruserID, PDO::PARAM_INT); + $stmt->execute(); + $resultSeller = $stmt->fetch(PDO::FETCH_ASSOC); + + $currentSCoins = $resultSeller['coins']; + $newSCoins = $resultSeller['coins']+$awardPrice; + + // Award the seller right here + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET coins = :coins WHERE id = :user;"); + $stmt->bindParam(':coins', $newSCoins, PDO::PARAM_INT); + $stmt->bindParam(':user', $owneruserID, PDO::PARAM_INT); + $stmt->execute(); + } + }else{ + $newBalance = $GLOBALS['userTable']['posties']-$result['price']; + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET posties = :coins WHERE id = :user;"); + $stmt->bindParam(':coins', $newBalance, PDO::PARAM_INT); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + $stmt = $GLOBALS['dbcon']->prepare("INSERT INTO ownedItems (`uid`, `catalogid`, `type`, `rbxasset`) VALUES (:user, :itemid, :type, :rbxasset);"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':itemid', $result['id'], PDO::PARAM_INT); + $stmt->bindParam(':rbxasset', $result['rbxasset'], PDO::PARAM_INT); + $stmt->bindParam(':type', $result['type'], PDO::PARAM_STR); + $stmt->execute(); + + echo $newBalance; + }else{ + echo 'error'; + } + }else{ + echo 'error'; + } + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/catalog/post/deleteItem.php b/www/core/func/api/catalog/post/deleteItem.php new file mode 100644 index 0000000..b3cd66e --- /dev/null +++ b/www/core/func/api/catalog/post/deleteItem.php @@ -0,0 +1,90 @@ +prepare("SELECT * FROM catalog WHERE id=:id"); + $stmt->bindParam(':id', $itemId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $fileHash = $result['fileHash']; + if ($result['type'] != "tshirts" and $result['type'] != "shirts" and $result['type'] != "pants" and $result['type'] != "decals") { + echo 'error'; + exit; + } + + // Make deleted true + $stmt = $GLOBALS['dbcon']->prepare("UPDATE catalog SET deleted = 1 WHERE fileHash = :id;"); + $stmt->bindParam(':id', $fileHash, PDO::PARAM_STR); + $stmt->execute(); + + // Make item unbuyable + $stmt = $GLOBALS['dbcon']->prepare("UPDATE catalog SET buyable = 0 WHERE fileHash = :id;"); + $stmt->bindParam(':id', $fileHash, PDO::PARAM_STR); + $stmt->execute(); + + // Set deleted true in owned items with the same file hash + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM catalog WHERE fileHash = :id"); + $stmt->bindParam(':id', $fileHash, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + $iID = $result['id']; + $stmt = $GLOBALS['dbcon']->prepare("UPDATE ownedItems SET deleted = 1 WHERE catalogid = :id;"); + $stmt->bindParam(':id', $iID, PDO::PARAM_INT); + $stmt->execute(); + } + + // Delete the actual file + if ($result['type'] == "tshirts" || $result['type'] == "shirts" || $result['type'] == "pants" || $result['type'] == "decals") { + @unlink($_SERVER['DOCUMENT_ROOT'].'/data/assets/uploads/'.$result['fileHash']); + } + + if ($result['type'] != "decals") { + // Remove from wearing + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM catalog WHERE fileHash = :id"); + $stmt->bindParam(':id', $fileHash, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + $iID2 = $result['id']; + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM wearing WHERE catalogId = :id"); + $stmt->bindParam(':id', $iID2, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + // Delete and put a request up in the imageServer + $query = "DELETE FROM `wearing` WHERE `id`=:id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $id = $result['id']; + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + + $uid = $result['uid']; + // Add request to imageServer + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id`, `charap` AS `pose` FROM `users` WHERE `id`=:uid;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $pose = $result['pose']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id` FROM `wearing` WHERE `uid`=:uid AND `type`=\"gear\";"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $hasGear = (($stmt->rowCount() > 0) ? 1 : 0); + + context::requestImage($uid, "headshot", $pose, $hasGear); + context::requestImage($uid, "character", $pose, $hasGear); + } + } + } + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/catalog/post/uploadItem.php b/www/core/func/api/catalog/post/uploadItem.php new file mode 100644 index 0000000..8504b87 --- /dev/null +++ b/www/core/func/api/catalog/post/uploadItem.php @@ -0,0 +1,219 @@ + 32) { + echo 'name-too-long'; + exit; + } + + if (!preg_match("/^[\w*?!\/@#$%\^&*\(\) -]+$/", $itemName) == 1) { + die("name-too-short"); + } + + if (strlen($itemName) < 5) { + echo 'name-too-short'; + exit; + } + + if (strlen($description) > 128) { + echo 'description-too-long'; + exit; + } + + if (is_numeric($itemPrice) == false && $typeString != "decals") die("price-too-low"); + + if ($itemPrice < 1 && $typeString != "decals") { + echo 'price-too-low'; + exit; + } + + if ($typeString == "decals") { + $itemPrice = 0; + } + + // Check last upload date, if less than a minute return 'rate-limit' + $timeSince = round(abs(strtotime(context::getCurrentTime()) - strtotime($GLOBALS['userTable']['lastUpload'])) / 60,2); + if ($timeSince < 1) { + echo 'rate-limit'; + exit; + } + + // Get the latest assetID, if nothing, default to 1 + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM catalog WHERE type=:dbtype ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':dbtype', $typeString, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) { + $assetId = 1; + }else{ + $assetId = $result['assetid']+1; + } + + $uploadDirectory = $_SERVER['DOCUMENT_ROOT'].'/data/assets/uploads/'; + + // Check the file + $check = @getimagesize($_FILES["file"]["tmp_name"]); + list($width, $height) = @getimagesize($_FILES["file"]["tmp_name"]); + if ($width != 585 && $height != 559) { + if ($typeString == "shirts" or $typeString == "pants") { + echo 'incorrect-size'; + exit; + } + } + + if (!$check) { + echo 'no-image'; + exit; + } + + if ($_FILES["file"]["size"] > 10000000) { + echo 'file-too-large'; + exit; + } + + $imageFileType = pathinfo($_FILES['file']["name"], PATHINFO_EXTENSION); + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime = finfo_file($finfo, $_FILES['file']['tmp_name']); + if ($imageFileType != "jpg" && $imageFileType != "JPG" && $imageFileType != "png" && $imageFileType != "PNG" && $imageFileType != "jpeg" && $imageFileType != "JPEG" && $mime != "image/png" && $mime != "image/jpeg") { + echo 'incorrect-extension'; + exit; + } + + if (exif_imagetype($_FILES['file']['tmp_name']) != IMAGETYPE_PNG && exif_imagetype($_FILES['file']['tmp_name']) != IMAGETYPE_JPEG) { + echo 'incorrect-extension'; + exit; + } + + + // Check user balance, should be easy + if ($GLOBALS['userTable']['coins'] < 5) { + echo 'not-enough-coins'; + exit; + } + + // Check if the file hash is not in badHashes + $fileHash = hash_file('sha512', $_FILES["file"]["tmp_name"]); + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM badHashes WHERE hash=:fileHash"); + $stmt->bindParam(':fileHash', $fileHash, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + echo 'bad-hash'; + exit; + } + + // Move the file to the right directory. Upload complete! + // Only move if it doesn't exist already. + if (!file_exists($uploadDirectory.$fileHash)) { + if (!move_uploaded_file($_FILES["file"]["tmp_name"], $uploadDirectory.$fileHash)) { + echo 'file-upload-error'; + exit; + } + } + + $newCoins = $GLOBALS['userTable']['coins']-5; + $stmt = $dbcon->prepare("UPDATE users SET coins = :coins WHERE id = :user;"); + $stmt->bindParam(':coins', $newCoins, PDO::PARAM_INT); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + // Set last upload to now, for security purposes. + $stmt = $dbcon->prepare("UPDATE users SET lastUpload = NOW() WHERE id = :user;"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + // Add to catalog. But keep un-approved until approved, of course, unless the hash is already approved. + // Check if any asset with the same hash is approved or not. Why approve the same file again? + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM catalog WHERE fileHash=:fileHash ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':fileHash', $fileHash, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $approved = 0; + if ($result['approved'] == 1 && $result['fileHash'] == $fileHash && $result['deleted'] == 0) { + $approved = 1; + } + + $stmt = $dbcon->prepare("INSERT INTO catalog (`price`, `creator_uid`, `assetid`, `name`, `description`, `type`, `approved`, `fileHash`) VALUES (:price, :user, :assetid, :name, :description, :type, :approved, :fileHash);"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':price', $itemPrice, PDO::PARAM_INT); + $stmt->bindParam(':type', $typeString, PDO::PARAM_STR); + $stmt->bindParam(':assetid', $assetId, PDO::PARAM_INT); + $stmt->bindParam(':name', $itemName, PDO::PARAM_STR); + $stmt->bindParam(':description', $description, PDO::PARAM_STR); + $stmt->bindParam(':approved', $approved, PDO::PARAM_INT); + $stmt->bindParam(':fileHash', $fileHash, PDO::PARAM_STR); + $stmt->execute(); + + if ($approved == 0) { + // Send the uploader a message so they can keep track of the progress of approval. + if ($typeString != "decals") { + $message = 'Your asset named '.$itemName.' is pending approval. You will receive another message after approval. Once approved, you will receive the item.'; + }else{ + $message = 'Your asset named '.$itemName.' is pending approval. You will receive another message after approval. Once approved, the decal will be visible in the catalog.'; + } + }else{ + $message = 'Your asset named '.$itemName.' has already been approved in the past. So, you can already make use of it and it is visible on the catalog.'; + } + $title = 'Asset Approval for '.$itemName; + $query = "INSERT INTO messages (`recv_uid`, `sender_uid`, `title`, `content`) VALUES (:userId2, 15, :title, :msg);"; + $stmt = $dbcon->prepare($query); + $stmt->bindParam(':userId2', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':msg', $message, PDO::PARAM_STR); + $stmt->bindParam(':title', $title, PDO::PARAM_STR); + $stmt->execute(); + + if ($approved == 1) { + if ($typeString == "shirts" or $typeString == "pants" or $typeString == "tshirts") { + context::requestImage($assetId, $typeString); + } + + // Get latest asset by this user + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM catalog WHERE creator_uid = :uid ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $catId = $result['id']; + + if ($typeString != "decals") { + $query = "INSERT INTO ownedItems (`uid`, `catalogid`, `type`) VALUES (:uid, :catid, :type);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':catid', $catId, PDO::PARAM_INT); + $stmt->bindParam(':type', $typeString, PDO::PARAM_STR); + $stmt->execute(); + } + } + + // We're done! Yahoo! + echo $newCoins; + }else{ + echo 'no-file'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/character/getInventory.php b/www/core/func/api/character/getInventory.php new file mode 100644 index 0000000..84199c5 --- /dev/null +++ b/www/core/func/api/character/getInventory.php @@ -0,0 +1,134 @@ +prepare("SELECT catalogid FROM ownedItems WHERE type = :type AND uid = :uid AND deleted=0 ORDER BY id DESC LIMIT 7 OFFSET :offset;"); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'No items found.'; + } + $count = 0; + foreach($stmt as $resultOwned) { + $count++; + if ($count < 7) { + $wearing = false; + $disableWear = false; + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM wearing WHERE uid = :uid AND catalogid = :id"); + $stmt->bindParam(':id', $resultOwned['catalogid'], PDO::PARAM_INT); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + $wearing = true; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM wearing WHERE uid = :uid AND type = :type"); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 4 and $type == "hats") { + $disableWear = true; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT deleted, name, type, datafile, assetid, id, fileHash, imgTime FROM catalog WHERE id = :id"); + $stmt->bindParam(':id', $resultOwned['catalogid'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['deleted'] == 0) { + $itemName = $result['name']; + if (strlen($itemName) > 16) { + $itemName = substr($itemName, 0, 13) . '...'; + } + + echo '
'.htmlentities($itemName, ENT_QUOTES, "UTF-8").'
'; + echo ''; + if ($wearing == true) { + echo '
'; + }else{ + if ($disableWear == false) { + echo '
'; + }else{ + echo '
Wear'; + } + } + echo '
'; + } + } + } + echo '
'; + if ($page > 0) { + echo '« Previous'; + } + if ($count > 6) { + echo 'Next »'; + } + if ($count == 0 and $page > 0) { + exit; + } + echo '
'; +?> \ No newline at end of file diff --git a/www/core/func/api/character/getWearing.php b/www/core/func/api/character/getWearing.php new file mode 100644 index 0000000..d3d6e74 --- /dev/null +++ b/www/core/func/api/character/getWearing.php @@ -0,0 +1,93 @@ +prepare("SELECT catalogId FROM wearing WHERE uid = :uid AND type = :type"); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'You are not wearing any item.'; + } + foreach($stmt as $resultWearing) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT id, deleted, datafile, type, assetid, name, fileHash, imgTime FROM catalog WHERE id = :id;"); + $stmt->bindParam(':id', $resultWearing['catalogId'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['deleted'] == 0) { + $itemName = $result['name']; + if (strlen($itemName) > 16) { + $itemName = substr($itemName, 0, 13) . '...'; + } + echo '
'.htmlentities($itemName, ENT_QUOTES, "UTF-8").'
'; + echo ''; + echo '
'; + echo '
'; + } + } +?> \ No newline at end of file diff --git a/www/core/func/api/character/post/changePose.php b/www/core/func/api/character/post/changePose.php new file mode 100644 index 0000000..e31b24b --- /dev/null +++ b/www/core/func/api/character/post/changePose.php @@ -0,0 +1,32 @@ +prepare($query); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->bindParam(':pose', $poseID, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id` FROM `wearing` WHERE `uid`=:uid AND `type`=\"gear\";"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $hasGear = (($stmt->rowCount() > 0) ? 1 : 0); + + context::requestImage($uid, "headshot", $poseID, $hasGear); + context::requestImage($uid, "character", $poseID, $hasGear); + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/character/post/regenCharacter.php b/www/core/func/api/character/post/regenCharacter.php new file mode 100644 index 0000000..c2a9716 --- /dev/null +++ b/www/core/func/api/character/post/regenCharacter.php @@ -0,0 +1,25 @@ +prepare("SELECT `id`, `charap` AS `pose` FROM `users` WHERE `id`=:uid;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $pose = $result['pose']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id` FROM `wearing` WHERE `uid`=:uid AND `type`=\"gear\";"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $hasGear = (($stmt->rowCount() > 0) ? 1 : 0); + + context::requestImage($uid, "headshot", $pose, $hasGear); + context::requestImage($uid, "character", $pose, $hasGear); + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/character/post/removeItem.php b/www/core/func/api/character/post/removeItem.php new file mode 100644 index 0000000..c555e19 --- /dev/null +++ b/www/core/func/api/character/post/removeItem.php @@ -0,0 +1,32 @@ +prepare("DELETE FROM wearing WHERE catalogId=:id AND uid=:user"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':id', $catalogId, PDO::PARAM_INT); + $stmt->execute(); + + $uid = $GLOBALS['userTable']['id']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id`, `charap` AS `pose` FROM `users` WHERE `id`=:uid;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $pose = $result['pose']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id` FROM `wearing` WHERE `uid`=:uid AND `type`=\"gear\";"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $hasGear = (($stmt->rowCount() > 0) ? 1 : 0); + + context::requestImage($uid, "headshot", $pose, $hasGear); + context::requestImage($uid, "character", $pose, $hasGear); + } +?> \ No newline at end of file diff --git a/www/core/func/api/character/post/wearItem.php b/www/core/func/api/character/post/wearItem.php new file mode 100644 index 0000000..404f028 --- /dev/null +++ b/www/core/func/api/character/post/wearItem.php @@ -0,0 +1,92 @@ +prepare("SELECT id FROM wearing WHERE uid = :uid AND catalogid = :id"); + $stmt->bindParam(':id', $catalogId, PDO::PARAM_INT); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + exit; + } + + + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM ownedItems WHERE catalogId = :id AND uid = :uid"); + $stmt->bindParam(':id', $catalogId, PDO::PARAM_INT); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + exit; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT id, deleted, assetid, datafile FROM catalog WHERE id = :id"); + $stmt->bindParam(':id', $catalogId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['deleted'] == 1) { + exit; + } + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM wearing WHERE uid = :uid AND type = :type"); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $resultcheck = $stmt->fetch(PDO::FETCH_ASSOC); + if ($type == "hats") { + if ($stmt->rowCount() == 5) { + exit; + } + }else{ + if ($stmt->rowCount() > 0) { + $stmt = $GLOBALS['dbcon']->prepare("DELETE FROM wearing WHERE catalogId=:id AND uid=:user"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':id', $resultcheck['catalogId'], PDO::PARAM_INT); + $stmt->execute(); + } + } + if ($type == "hats" or $type == "gear" or $type == "faces" or $type == "heads") { + $aprString = "http://gtoria.net/data/assets/".$type."/models/".$result['datafile']; + } + if ($type == "shirts" or $type == "pants" or $type == "tshirts") { + $aprString = "http://gtoria.net/data/assets/".$type."/models/get.php?id=".$result['assetid']; + } + if ($type == "torso" or $type == "leftarm" or $type == "leftleg" or $type == "rightarm" or $type == "rightleg") { + $aprString = "http://gtoria.net/data/assets/package/models/".$result['datafile']; + } + $stmt = $GLOBALS['dbcon']->prepare("INSERT INTO wearing (`uid`, `catalogid`, `type`, `aprString`) VALUES (:user, :itemid, :type, :aprString);"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':itemid', $catalogId, PDO::PARAM_INT); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->bindParam(':aprString', $aprString, PDO::PARAM_STR); + $stmt->execute(); + + $uid = $GLOBALS['userTable']['id']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id`, `charap` AS `pose` FROM `users` WHERE `id`=:uid;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $pose = $result['pose']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id` FROM `wearing` WHERE `uid`=:uid AND `type`=\"gear\";"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $hasGear = (($stmt->rowCount() > 0) ? 1 : 0); + + context::requestImage($uid, "headshot", $pose, $hasGear); + context::requestImage($uid, "character", $pose, $hasGear); + + exit; + } +?> \ No newline at end of file diff --git a/www/core/func/api/chat/createChat.php b/www/core/func/api/chat/createChat.php new file mode 100644 index 0000000..b97a2a8 --- /dev/null +++ b/www/core/func/api/chat/createChat.php @@ -0,0 +1,49 @@ + 64) die("chat-name-too-long"); + + if (context::getTimeSince($GLOBALS['userTable']['lastUpload']) < 5) { + die("rate-limit"); + } + + $stmt = $GLOBALS['dbcon']->prepare('UPDATE users SET lastUpload = NOW() WHERE id = :uid;'); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $chatKey = context::random_str(32); + $chatJoinKey = context::random_str(8); + $stmt = $GLOBALS['dbcon']->prepare("INSERT INTO chat_sessions (`chatName`, `chatKey`, `chatJoinKey`) VALUES (:chatName, :chatKey, :chatJoinKey);"); + $stmt->bindParam(':chatName', $chatName, PDO::PARAM_STR); + $stmt->bindParam(':chatKey', $chatKey, PDO::PARAM_STR); + $stmt->bindParam(':chatJoinKey', $chatJoinKey, PDO::PARAM_STR); + $stmt->execute(); + + $query = "SELECT id, chatName, chatKey FROM chat_sessions WHERE chatKey = :chatKey"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':chatKey', $chatKey, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $chatID = $result['id']; + + $stmt = $GLOBALS['dbcon']->prepare("INSERT INTO chat_members (`chat_id`, `userId`, `rank`) VALUES (:chatId, :userId, 1);"); + $stmt->bindParam(':chatId', $chatID, PDO::PARAM_STR); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $rows[] = array('chat_id' => $result['id'], + 'chatName' => context::secureString($result['chatName']), + 'chatKey' => context::secureString($result['chatKey'])); + + die(json_encode($rows)); + }else{ + die("error"); + } +?> \ No newline at end of file diff --git a/www/core/func/api/chat/getChatInfo.php b/www/core/func/api/chat/getChatInfo.php new file mode 100644 index 0000000..1f42815 --- /dev/null +++ b/www/core/func/api/chat/getChatInfo.php @@ -0,0 +1,27 @@ +prepare('SELECT id FROM chat_members WHERE userId = :userId AND chat_id = :id'); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) die("error"); + + $stmt = $GLOBALS['dbcon']->prepare('SELECT chatName, chatJoinKey FROM chat_sessions WHERE id = :chatID'); + $stmt->bindParam(':chatID', $id, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + $stmt = $GLOBALS['dbcon']->prepare('SELECT id FROM chat_members WHERE chat_id = :chatID'); + $stmt->bindParam(':chatID', $id, PDO::PARAM_INT); + $stmt->execute(); + $chatMemberCount = $stmt->rowCount(); + + $rows[] = array('chatMembers' => $chatMemberCount, 'chatName' => context::secureString($result['chatName']), 'joinKey' => context::secureString($result['chatJoinKey'])); + die(json_encode($rows)); + }else{ + die("error"); + } +?> \ No newline at end of file diff --git a/www/core/func/api/chat/getList.php b/www/core/func/api/chat/getList.php new file mode 100644 index 0000000..c435351 --- /dev/null +++ b/www/core/func/api/chat/getList.php @@ -0,0 +1,23 @@ +prepare('SELECT chat_id FROM chat_members WHERE userId = :userId'); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $rows = array(); + foreach($stmt as $result) { + $chatID = $result['chat_id']; + $stmtChat = $GLOBALS['dbcon']->prepare('SELECT id, chatName, chatKey FROM chat_sessions WHERE id = :chatID'); + $stmtChat->bindParam(':chatID', $chatID, PDO::PARAM_INT); + $stmtChat->execute(); + foreach($stmtChat as $resultChat) { + $rows[] = array('chat_id' => $resultChat['id'], + 'chatName' => context::secureString($resultChat['chatName']), + 'chatKey' => context::secureString($resultChat['chatKey'])); + } + } + die(json_encode($rows)); + }else{ + die("error"); + } +?> \ No newline at end of file diff --git a/www/core/func/api/chat/getMessages.php b/www/core/func/api/chat/getMessages.php new file mode 100644 index 0000000..8801058 --- /dev/null +++ b/www/core/func/api/chat/getMessages.php @@ -0,0 +1,54 @@ +prepare('SELECT id FROM chat_members WHERE userId = :userId AND chat_id = :id'); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) api::returnString("error"); + + $stmtChat = $GLOBALS['dbcon']->prepare('SELECT * FROM chat_messages WHERE chat_id = :chatID AND date > :timestamp'); + $stmtChat->bindParam(':chatID', $id, PDO::PARAM_INT); + $stmtChat->bindParam(':timestamp', $timestamp, PDO::PARAM_INT); + $stmtChat->execute(); + $rows = array(); + foreach($stmtChat as $resultChat) { + $stmt = $GLOBALS['dbcon']->prepare('SELECT id, username, rank FROM users WHERE id = :userId'); + $stmt->bindParam(':userId', $resultChat['userId'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if($result) + { + $setRight = false; + if ($result['username'] == $GLOBALS['userTable']['username']) $setRight = true; + if ($result['rank'] > 0) { + $color = "red"; + $rank = 1; + }else{ + $color = "black"; + $rank = 0; + } + + $message = context::secureString($resultChat['message']); + $message = context::parseEmoticon($message); + + $rows[] = array('messageId' => $resultChat['id'], + 'userId' => $resultChat['userId'], + 'username' => $result['username'], + 'staff' => $rank, + 'setRight' => $setRight, + 'userColor' => $color, + 'date' => $resultChat['date'], + 'userID' => $result['id'], + 'message' => $message); + } + } + // Get all chat messages + die(json_encode($rows)); + }else{ + die("error"); + } +?> \ No newline at end of file diff --git a/www/core/func/api/chat/getTyping.php b/www/core/func/api/chat/getTyping.php new file mode 100644 index 0000000..8684810 --- /dev/null +++ b/www/core/func/api/chat/getTyping.php @@ -0,0 +1,34 @@ +prepare('SELECT lastType, userId FROM chat_members WHERE chat_id = :chatID'); + $stmt->bindParam(':chatID', $chatId, PDO::PARAM_INT); + $stmt->execute(); + $rows_typing = array(); + $usernames = array(); + $count = 0; + foreach($stmt as $result) { + if (context::getTimeSince($result['lastType']) < 0.06 && $result['userId'] != $GLOBALS['userTable']['id']) { + $count++; + $username = context::IDToUsername($result['userId']); + $usernames[] = $username; + } + } + + if ($count == 0) { + $mode = "none"; + }else if ($count < 3) { + $mode = "showTyping"; + }else { + $mode = "severalTyping"; + } + $rows_typing[] = array('usernames' => $usernames, 'mode' => $mode); + $json = context::jsonToSingle(json_encode($rows_typing)); + die($json); + }else{ + die("error"); + } +?> \ No newline at end of file diff --git a/www/core/func/api/chat/joinChat.php b/www/core/func/api/chat/joinChat.php new file mode 100644 index 0000000..3da1625 --- /dev/null +++ b/www/core/func/api/chat/joinChat.php @@ -0,0 +1,49 @@ + 64) die("chat-code-too-long"); + + $query = "SELECT id, chatJoinKey, chatName, chatKey FROM chat_sessions WHERE chatJoinKey = :chatKey"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':chatKey', $chatCode, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 0) die("invalid-code"); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $chatID = $result['id']; + $chatKey = $result['chatKey']; + + $query = "SELECT id FROM chat_members WHERE chat_id = :chatId AND userId = :userId"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':chatId', $chatID, PDO::PARAM_INT); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) die("already-in"); + + $stmt = $GLOBALS['dbcon']->prepare("INSERT INTO chat_members (`chat_id`, `userId`, `rank`) VALUES (:chatId, :userId, 0);"); + $stmt->bindParam(':chatId', $chatID, PDO::PARAM_INT); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $time = time(); + $message = $GLOBALS['userTable']['username'].' has joined'; + $stmt = $GLOBALS['dbcon']->prepare("INSERT INTO chat_messages (`chat_id`, `userId`, `message`, `bot`, `date`) VALUES (:chatId, 0, :message, 1, :time);"); + $stmt->bindParam(':chatId', $chatID, PDO::PARAM_INT); + $stmt->bindParam(':message', $message, PDO::PARAM_STR); + $stmt->bindParam(':time', $time, PDO::PARAM_INT); + $stmt->execute(); + + $rows[] = array('chat_id' => $result['id'], + 'chatName' => context::secureString($result['chatName']), + 'chatKey' => context::secureString($result['chatKey'])); + + die(json_encode($rows)); + }else{ + die("error"); + } +?> \ No newline at end of file diff --git a/www/core/func/api/chat/sendMessage.php b/www/core/func/api/chat/sendMessage.php new file mode 100644 index 0000000..cfb7e59 --- /dev/null +++ b/www/core/func/api/chat/sendMessage.php @@ -0,0 +1,41 @@ +prepare('SELECT id, chatKey FROM chat_sessions WHERE id = :chatId'); + $stmt->bindParam(':chatId', $_POST['chatId'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) die("error"); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + // Check if everything matches up + if (strlen($message) > 128) die("message-too-long"); + + if (strlen($message) < 1) die("message-too-short"); + + // Also check if the current user is indeed in the chat. + $stmt = $GLOBALS['dbcon']->prepare('SELECT id FROM chat_members WHERE userId = :userId AND chat_id = :id'); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':id', $chatId, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) die("error"); + + // If we're still here, we can go and add the message, encrypt message again + $time = time(); + $stmt = $GLOBALS['dbcon']->prepare("INSERT INTO chat_messages (`chat_id`, `userId`, `message`, `date`) VALUES (:chatId, :userId, :message, :timestamp);"); + $stmt->bindParam(':chatId', $chatId, PDO::PARAM_INT); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':message', $message, PDO::PARAM_STR); + $stmt->bindParam(':timestamp', $time, PDO::PARAM_INT); + $stmt->execute(); + + die("success"); + }else{ + die("error"); + } +?> \ No newline at end of file diff --git a/www/core/func/api/chat/sendTyping.php b/www/core/func/api/chat/sendTyping.php new file mode 100644 index 0000000..b2b4e80 --- /dev/null +++ b/www/core/func/api/chat/sendTyping.php @@ -0,0 +1,17 @@ +prepare($query); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':chatId', $chatId, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 0) die("error"); + die("success"); + }else{ + die("error"); + } +?> \ No newline at end of file diff --git a/www/core/func/api/chat/sounds/old.mp3 b/www/core/func/api/chat/sounds/old.mp3 new file mode 100644 index 0000000..22718c3 Binary files /dev/null and b/www/core/func/api/chat/sounds/old.mp3 differ diff --git a/www/core/func/api/chat/sounds/send.mp3 b/www/core/func/api/chat/sounds/send.mp3 new file mode 100644 index 0000000..5cc39f3 Binary files /dev/null and b/www/core/func/api/chat/sounds/send.mp3 differ diff --git a/www/core/func/api/forum/doSearch.php b/www/core/func/api/forum/doSearch.php new file mode 100644 index 0000000..e272d34 --- /dev/null +++ b/www/core/func/api/forum/doSearch.php @@ -0,0 +1,27 @@ +prepare('SELECT name, id FROM forums WHERE id = :id;'); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) exit; + } + echo ''; + echo '
'; + echo '
'; + echo '

Using this utility, you can search for posts. Just enter something and our system will search for you

'; + if($GLOBALS['loggedIn'] && $GLOBALS['userTable']['rank'] > 0) + { + echo '-ADMIN ONLY-(doesnt work yet)

Search by Username

'; + echo '
'; + echo ''; + } + }else{ + echo 'An error occurred'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/editPost.php b/www/core/func/api/forum/editPost.php new file mode 100644 index 0000000..af14a5c --- /dev/null +++ b/www/core/func/api/forum/editPost.php @@ -0,0 +1,34 @@ +prepare("SELECT * FROM topics WHERE id = :fId AND developer = 0"); + $stmt->bindParam(':fId', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) die("Forum not found"); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if($result['deleted'] === 1) + { + exit('This post has been moderated.'); + } + else if($result['author_uid'] == $GLOBALS['userTable']['id'] || $GLOBALS['userTable']['rank'] > 0) + { + echo '

Editing post '.context::secureString($result['title']).'

'; + include_once $_SERVER['DOCUMENT_ROOT'].'/core/func/api/forum/views/editPost.php'; + } + else + { + echo 'You can\'t edit this post'; + } + }else{ + echo 'An error occurred'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/getCatagories.php b/www/core/func/api/forum/getCatagories.php new file mode 100644 index 0000000..3d8908c --- /dev/null +++ b/www/core/func/api/forum/getCatagories.php @@ -0,0 +1,19 @@ +prepare("SELECT name, id FROM catagories WHERE developer = 0"); + $stmt->execute(); + foreach($stmt as $result) { + echo '

'.context::secureString($result['name']).'

    '; + $stmt = $dbcon->prepare("SELECT id, name FROM forums WHERE catid = :id"); + $stmt->bindParam(':id', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + echo '
  • '.context::secureString($result['name']).'

  • '; + } + echo '
'; + } + if($GLOBALS['loggedIn']) + { + echo '

Manage

'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/getMiniProfile.php b/www/core/func/api/forum/getMiniProfile.php new file mode 100644 index 0000000..fdb6bf0 --- /dev/null +++ b/www/core/func/api/forum/getMiniProfile.php @@ -0,0 +1,34 @@ +prepare('SELECT id, thumbnailHash, headshotHash, username, banned, lastSeen, rank, posts FROM users WHERE username = :username;'); + $stmt->bindParam(':username', $username, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0 or $result['banned'] == 1) { + echo 'User not found or suspended'; + echo ''; + exit; + } + echo ''; + echo ''; + if ($result['rank'] == 1) { + echo '

Administrator

'; + } + if ($result['rank'] == 2) { + echo '

Moderator

'; + } + echo '

Posts: '.$result['posts'].'

'; + echo '
Full Profile'; + if ($GLOBALS['loggedIn'] == true && $GLOBALS['userTable']['username'] != $result['username']) { + echo 'Send Message'; + } + echo '
'; + }else{ + echo 'An error occurred'; + } +?> diff --git a/www/core/func/api/forum/getPosts.php b/www/core/func/api/forum/getPosts.php new file mode 100644 index 0000000..934acf2 --- /dev/null +++ b/www/core/func/api/forum/getPosts.php @@ -0,0 +1,279 @@ + 0) { + $keyword = $_GET['keyword']; + if (is_array($keyword)) exit; + $searchTermSQL = '%'.$keyword.'%'; + } + + $offset = $page*25; + //include_once $_SERVER['DOCUMENT_ROOT'].'/core/func/includes.php'; + if ($page == 0) { + $query = 'SELECT `id`, `posts`, `replies`, `name`, `description`, `locked` FROM `forums` WHERE `id` = :fId AND `developer` = 0;'; + if($id==='my-posts') + $query = 'SELECT `posts` FROM `users` WHERE `id` = :uid LIMIT 1;'; + $stmt = $GLOBALS['dbcon']->prepare($query); + if($id==='my-posts') + { + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + } + else + { + $stmt->bindParam(':fId', $id, PDO::PARAM_INT); + } + $stmt->execute(); + if($id==='my-posts') + { + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if($result['posts'] === 0) + { + exit('You haven\'t posted on the forums yet. When you make a post, it\'ll appear here!'); + } + echo ''; + echo '

Your Topics

'; + echo ''; + if (!isset($keyword)) echo '

View all of your Graphictoria posts.

'; + if (isset($keyword)) echo '

Searching by name: '.context::secureString($_GET['keyword']).'

'; + } + else + { + if ($stmt->rowCount() == 0) { + echo 'Forum not found'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $id = $result['id']; + echo ''; + echo '

'.context::secureString($result['name']).'

'; + echo ''; + if (!isset($keyword)) echo '

'.context::secureString($result['description']).'

'; + if (isset($keyword)) echo '

Searching by name: '.context::secureString($_GET['keyword']).'

'; + } + }else{ + if($id !== 'my-posts') + { + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM forums WHERE id = :fId"); + $stmt->bindParam(':fId', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'Forum not found'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $id = $result['id']; + } + } + + function showLockedStatus($locked) { + if ($locked == 1) { + return ''; + } + } + + function showPinStatus() { + return ''; + } + + + // Pinned posts + if ($page == 0 && !isset($keyword)) { + $query = "SELECT id, locked, deleted, author_uid, postTime, lastActivity, views, replies, title FROM topics WHERE forumId = :fId AND pinned = 1".(!$GLOBALS['loggedIn'] || $GLOBALS['userTable']['rank'] === 0 ? ' AND deleted = 0' : '')." ORDER BY lastActivity ASC;"; + if($id==='my-posts') + $query = 'SELECT `id`, `locked`, `deleted`, `author_uid`, `postTime`, `lastActivity`, `views`, `replies`, `title`, `forumId` FROM `topics` WHERE `author_uid` = :uid AND `pinned` = 1'.(!$GLOBALS['loggedIn'] || $GLOBALS['userTable']['rank'] === 0 ? ' AND `deleted` = 0' : '').' ORDER BY `lastActivity` ASC;'; + $stmt = $GLOBALS['dbcon']->prepare($query); + if($id==='my-posts') + { + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + } + else + { + $stmt->bindParam(':fId', $id, PDO::PARAM_INT); + } + $stmt->execute(); + echo '
'; + $count = 0; + foreach($stmt as $result) { + $count++; + if ($count < 25) { + $userSheet = context::getUserSheetByIDForum($result['author_uid']); + if ($userSheet['rank'] == 0) { + $usern = $userSheet['username']; + }elseif ($userSheet['rank'] == 1) { + $usern = ' '.$userSheet['username'].''; + }elseif ($userSheet['rank'] == 2) { + $usern = ' '.$userSheet['username'].''; + } + echo '
'; + echo '
'; + echo '

'.showPinStatus().($result['locked'] ? ' '.showLockedStatus(1).' ': ' ').($result['deleted']===1 ? ' ' : '').context::secureString($result['title']).'

'; + //echo '

'.showPinStatus().' '.context::secureString($result['title']).'

'; + echo ''; + echo '

Posted by '.$usern.'

'; + if($id==='my-posts') + echo '
'; + } + } + } + + if (!isset($keyword)) + { + $query = "SELECT id, deleted, author_uid, locked, postTime, lastActivity, views, replies, title FROM topics WHERE forumId = :fId AND pinned = 0".(!$GLOBALS['loggedIn'] || $GLOBALS['userTable']['rank'] === 0 ? ' AND `deleted` = 0' : '')." ORDER BY lastActivity DESC LIMIT 26 OFFSET :offset"; + if($id === 'my-posts') + $query = 'SELECT `id`, `deleted`, `forumId`, `author_uid`, `locked`, `postTime`, `lastActivity`, `views`, `replies`, `title` FROM `topics` WHERE `author_uid` = :uid AND `pinned` = 0'.(!$GLOBALS['loggedIn'] || $GLOBALS['userTable']['rank'] === 0 ? ' AND `deleted` = 0' : '').' ORDER BY `lastActivity` DESC LIMIT 26 OFFSET :offset'; + $stmt = $GLOBALS['dbcon']->prepare($query); + } + if (isset($keyword)) + { + $query = "SELECT id, deleted, author_uid, locked, postTime, lastActivity, views, replies, title FROM topics WHERE forumId = :fId".(!$GLOBALS['loggedIn'] || $GLOBALS['userTable']['rank'] === 0 ? ' AND `deleted` = 0' : '')." AND title LIKE :term ORDER BY lastActivity DESC LIMIT 26 OFFSET :offset"; + if($id === 'my-posts') + $query = 'SELECT `id`, `deleted`, `forumId`, `author_uid`, `locked`, `postTime`, `lastActivity`, `views`, `replies`, `title` FROM `topics` WHERE `author_uid` = :uid'.(!$GLOBALS['loggedIn'] || $GLOBALS['userTable']['rank'] === 0 ? ' AND `deleted` = 0' : '').' AND `title` LIKE :term ORDER BY `lastActivity` DESC LIMIT 26 OFFSET :offset'; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':term', $searchTermSQL, PDO::PARAM_STR); + } + if($id === 'my-posts') + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + else + $stmt->bindParam(':fId', $id, PDO::PARAM_INT); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->execute(); + echo '
'; + $count = 0; + foreach($stmt as $result) { + $count++; + if ($count < 25) { + $userSheet = context::getUserSheetByIDForum($result['author_uid']); + if ($userSheet['rank'] == 0) { + $usern = $userSheet['username']; + }elseif ($userSheet['rank'] == 1) { + $usern = ' '.$userSheet['username'].''; + }elseif ($userSheet['rank'] == 2) { + $usern = ' '.$userSheet['username'].''; + } + echo '
'; + echo '
'; + echo '

'.showLockedStatus($result['locked']).' '.($result['deleted']===1 ? ' ' : '').context::secureString($result['title']).'

'; + echo ''; + echo '

Posted by '.$usern.'

'; + if($id==='my-posts') + echo '
'; + } + } + if ($stmt->rowCount() == 0) { + echo 'There seems to be no posts in this subforum. You could start the first one!'; + } + if ($count > 25) { + if (!isset($keyword)) echo ''; + if (isset($keyword)) echo ''; + } + echo '
'; + }else{ + echo 'An error occurred'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/newPost.php b/www/core/func/api/forum/newPost.php new file mode 100644 index 0000000..15c4db2 --- /dev/null +++ b/www/core/func/api/forum/newPost.php @@ -0,0 +1,23 @@ +prepare("SELECT * FROM forums WHERE id = :fId AND developer = 0"); + $stmt->bindParam(':fId', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) die("Forum not found"); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + echo '

Posting in '.context::secureString($result['name']).'

'; + include_once $_SERVER['DOCUMENT_ROOT'].'/core/func/api/forum/views/newPost.php'; + }else{ + echo 'An error occurred'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/newReply.php b/www/core/func/api/forum/newReply.php new file mode 100644 index 0000000..42c4395 --- /dev/null +++ b/www/core/func/api/forum/newReply.php @@ -0,0 +1,37 @@ +prepare("SELECT * FROM topics WHERE id = :fId AND developer = 0"); + $stmt->bindParam(':fId', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'Post could not be found'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if($result['deleted'] === 1 && $GLOBALS['userTable']['rank'] == 0) + { + exit('This post has been moderated.'); + } + else if($result['locked'] == 0 || $GLOBALS['userTable']['rank'] > 0) + { + echo '

Replying to '.context::secureString($result['title']).'

'; + include_once $_SERVER['DOCUMENT_ROOT'].'/core/func/api/forum/views/newReply.php'; + } + else + { + echo 'This post is locked'; + exit; + } + }else{ + echo 'An error occurred'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/post/deletePost.php b/www/core/func/api/forum/post/deletePost.php new file mode 100644 index 0000000..07ac16f --- /dev/null +++ b/www/core/func/api/forum/post/deletePost.php @@ -0,0 +1,87 @@ +prepare("SELECT deleted, author_uid, forumId FROM topics WHERE id = :id AND developer = 0 ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0 || $result['deleted'] === 1) { + echo 'error'; + exit; + } + + $timeSince = round(abs(strtotime(context::getCurrentTime()) - strtotime($GLOBALS['userTable']['lastUpload'])) / 60,2); + if ($timeSince < 1 && $GLOBALS['userTable']['rank'] != 1) { + echo 'rate-limit'; + exit; + } + + $forumId = $result['forumId']; + $userId = $result['author_uid']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT posts FROM users WHERE id = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + $posts = $result['posts']-1; + + $query = "UPDATE `users` SET `posts`=:posts WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $posts, PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `topics` SET `deleted`=1 WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + /*$query = "DELETE FROM `replies` WHERE `postId`=:id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + $query = "DELETE FROM `read` WHERE `postId`=:id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute();*/ + + $query = "SELECT * FROM topics WHERE forumId=:id AND `deleted`=0;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->execute(); + $total = $stmt->rowCount(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE forums SET posts = :posts WHERE id=:id;"); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $total, PDO::PARAM_INT); + $stmt->execute(); + + $query = "SELECT * FROM replies WHERE forumId=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->execute(); + $total = $stmt->rowCount(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE forums SET replies = :posts WHERE id=:id;"); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $total, PDO::PARAM_INT); + $stmt->execute(); + + if ($GLOBALS['userTable']['rank'] != 1) { + $stmt = $dbcon->prepare("UPDATE users SET lastUpload = NOW() WHERE id = :user;"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/post/editPost.php b/www/core/func/api/forum/post/editPost.php new file mode 100644 index 0000000..038fde7 --- /dev/null +++ b/www/core/func/api/forum/post/editPost.php @@ -0,0 +1,104 @@ +You have already posted this'); + + $badwords = array("fucking", "gay", "rape", "incest", "beastiality", "cum", "maggot", "bullshit", "fuck", "penis", + "dick", "vagina", "vag", "faggot", "fag", "nigger", "asshole", "shit", "bitch", "anal", "stfu", + "cunt", "pussy", "hump", "meatspin", "redtube", "porn", "kys", "xvideos", "hentai", "gangbang", "milf", + "n*", "nobelium", "whore", "wtf", "horny", "raping", "s3x", "boob", "nigga", "nlgga", "gt2008", + "cock", "dicc", "idiot", "nibba", "nibber", "nude", "kesner", "brickopolis", "nobe", "diemauer", "nuts", + "rhodum", "otorium", ".ga", ".cf", ".gg", ".ml", "brickopolis", "mercury", "polygon", "pizzaboxer", + "calvy", "tadah", "alphaland", "finalb"); + + $badwords2 = array("sex", "porn"); + if (context::contains($postContent, $badwords2)) { + echo 'This edit contains filtered words.'; + exit; + } + + + // Check without special characters removed + if (context::contains($contentCheck, $badwords)) { + echo 'This edit contains filtered words.'; + exit; + } + + // Check with special characters removed, except *. + $contentCheck = preg_replace("/[^A-Za-z0-9*]/", '', $contentCheck); + if (context::contains($contentCheck, $badwords)) { + echo 'This edit contains filtered words.'; + exit; + } + + if (strlen($postContent) < 5 or strlen($contentCheck) < 5) { + echo 'content-too-short'; + exit; + } + + if (strlen($postContent) > 30000) { + echo 'content-too-long'; + exit; + } + + if($shortenedContent != $postContent) + { + exit('no-newline-spam'); + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM topics WHERE id = :id AND developer = 0"); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if(($result['author_uid'] === $GLOBALS['userTable']['id'] || $GLOBALS['userTable']['rank'] > 0) && $result['deleted'] === 0) + { + $timeSince = round(abs(strtotime(date('Y-m-d H:i:s')) - strtotime($result['updatedOn'])) / 60,2); + if ($timeSince < 1 and $GLOBALS['userTable']['rank'] == 0) { + echo 'rate-limit'; + exit; + } + + if ($stmt->rowCount() == 0) { + echo 'no-post'; + exit; + } + + $query = "UPDATE `topics` SET `updatedOn`=NOW(), `updatedBy`=:uid, `content`=:content WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':content', $postContent, PDO::PARAM_STR); + $stmt->execute(); + + $query = "DELETE FROM `read` WHERE `postId`=:id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + echo ''; + } + else + { + echo 'access-denied'; + } + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/post/lockPost.php b/www/core/func/api/forum/post/lockPost.php new file mode 100644 index 0000000..beaae23 --- /dev/null +++ b/www/core/func/api/forum/post/lockPost.php @@ -0,0 +1,32 @@ +prepare('SELECT `deleted` FROM `topics` WHERE `id`=:id'); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if($result['deleted'] === 1) + { + exit('error'); + } + + $query = "UPDATE `topics` SET `locked`=1 WHERE `id`=:id AND `developer` = 0;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `topics` SET `lockedByStaff`=1 WHERE `id`=:id AND `developer` = 0;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/post/newPost.php b/www/core/func/api/forum/post/newPost.php new file mode 100644 index 0000000..f6d3aa2 --- /dev/null +++ b/www/core/func/api/forum/post/newPost.php @@ -0,0 +1,168 @@ +You have already posted this'); + + // Fixes things like "this i>s ex<" + $badwords2 = array("sex", "porn"); + if (context::contains($postContent, $badwords2)) { + echo 'This post contains filtered words.'; + exit; + } + + // Check without special characters removed, will catch stuff like N* + if (context::contains($contentCheck, $badwords) or context::contains($titleCheck, $badwords)) { + echo 'This post or post title contains filtered words.'; + exit; + } + + // Check again but with special characters removed, except * + $titleCheck = preg_replace("/[^A-Za-z0-9*]/", '', $titleCheck); + $contentCheck = preg_replace("/[^A-Za-z0-9*]/", '', $contentCheck); + if (context::contains($contentCheck, $badwords) or context::contains($titleCheck, $badwords)) { + echo 'This post or post title contains filtered words.'; + exit; + } + + if (!preg_match("/^[\w*?!\/@',:#$%\^&*\(\) -]+$/", $postTitle) == 1) { + die('Invalid characters in title.'); + } + + if (strlen($postTitle) < 5 or strlen($titleCheck) < 5) { + echo 'title-too-short'; + exit; + } + + if (strlen($postTitle) > 128) { + echo 'title-too-long'; + exit; + } + + if (strlen($postContent) < 5 or strlen($contentCheck) < 5) { + echo 'content-too-short'; + exit; + } + + if (strlen($postContent) > 30000) { + echo 'content-too-long'; + exit; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT lastPost, joinDate, rank FROM users WHERE id = :id"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $timeSince = round(abs(strtotime(date('Y-m-d H:i:s')) - strtotime($result['lastPost'])) / 60,2); + if ($timeSince < 1 and $result['rank'] == 0) { + echo 'rate-limit'; + exit; + } + + $timeSince = round(abs(strtotime(date('Y-m-d H:i:s')) - strtotime($result['joinDate'])) / 60,2); + if ($timeSince < 1440 and $result['rank'] == 0) { + echo 'account-age'; + exit; + } + + if($shortenedContent != $postContent) + { + exit('no-newline-spam'); + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM forums WHERE id = :id AND developer = 0"); + $stmt->bindParam(':id', $forum, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'no-forum'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['locked'] == 1 and $GLOBALS['userTable']['rank'] != 1) { + echo 'access-denied'; + exit; + } + + $query = "INSERT INTO topics (`forumId`, `title`, `author_uid`, `content`, `lastActivity`) VALUES (:forumid, :topicname, :poster, :content, NOW());"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':forumid', $forum, PDO::PARAM_INT); + $stmt->bindParam(':topicname', $postTitle, PDO::PARAM_STR); + $stmt->bindParam(':poster', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':content', $postContent, PDO::PARAM_STR); + $stmt->execute(); + + $query = "UPDATE `users` SET `lastPost`=NOW() WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `users` SET `lastForumContent`=:content WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':content', $postContent, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM forums WHERE id = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $forum, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $posts = $result['posts']+1; + $query = "UPDATE `forums` SET `posts`=:posts WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $forum, PDO::PARAM_INT); + $stmt->bindParam(':posts', $posts, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT posts FROM users WHERE id = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $posts = $result['posts']+1; + $query = "UPDATE `users` SET `posts`=:posts WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':posts', $posts, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $dbcon->prepare("SELECT id FROM topics WHERE author_uid = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + echo ''; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/post/newReply.php b/www/core/func/api/forum/post/newReply.php new file mode 100644 index 0000000..27d03e7 --- /dev/null +++ b/www/core/func/api/forum/post/newReply.php @@ -0,0 +1,168 @@ +You have already posted this'); + + $badwords = array("fucking", "gay", "rape", "incest", "beastiality", "cum", "maggot", "bullshit", "fuck", "penis", + "dick", "vagina", "vag", "faggot", "fag", "nigger", "asshole", "shit", "bitch", "anal", "stfu", + "cunt", "pussy", "hump", "meatspin", "redtube", "porn", "kys", "xvideos", "hentai", "gangbang", "milf", + "n*", "nobelium", "whore", "wtf", "horny", "raping", "s3x", "boob", "nigga", "nlgga", "gt2008", + "cock", "dicc", "idiot", "nibba", "nibber", "nude", "kesner", "brickopolis", "nobe", "diemauer", "nuts", + "rhodum", "otorium", ".ga", ".cf", ".gg", ".ml", "brickopolis", "mercury", "polygon", "pizzaboxer", + "calvy", "tadah", "alphaland", "finalb"); + + $badwords2 = array("sex", "porn"); + if (context::contains($replyContent, $badwords2)) { + echo 'This reply contains filtered words.'; + exit; + } + + + // Check without special characters removed + if (context::contains($contentCheck, $badwords)) { + echo 'This reply contains filtered words.'; + exit; + } + + // Check with special characters removed, except *. + $contentCheck = preg_replace("/[^A-Za-z0-9*]/", '', $contentCheck); + if (context::contains($contentCheck, $badwords)) { + echo 'This reply contains filtered words.'; + exit; + } + + if (strlen($replyContent) < 1 or strlen($contentCheck) < 1) { + echo 'content-too-short'; + exit; + } + + if (strlen($replyContent) > 30000) { + echo 'content-too-long'; + exit; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT lastPost, joinDate, rank FROM users WHERE id = :id"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $timeSince = round(abs(strtotime(date('Y-m-d H:i:s')) - strtotime($result['lastPost'])) / 60,2); + if ($timeSince < 1 and $result['rank'] == 0) { + echo 'rate-limit'; + exit; + } + + $timeSince = round(abs(strtotime(date('Y-m-d H:i:s')) - strtotime($result['joinDate'])) / 60,2); + if ($timeSince < 1440 and $result['rank'] == 0) { + echo 'account-age'; + exit; + } + + if($shortenedContent != $replyContent) + { + exit('no-newline-spam'); + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM topics WHERE id = :id AND developer = 0"); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'no-post'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $postId = $result['id']; + $forumId = $result['forumId']; + if (($result['locked'] == 1 || $result['deleted'] === 1) and $GLOBALS['userTable']['rank'] == 0) { + echo 'access-denied'; + exit; + } + + $query = "INSERT INTO replies (`postId`, `content`, `author_uid`, `forumId`) VALUES (:id, :content, :poster, :forumId);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->bindParam(':poster', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':content', $replyContent, PDO::PARAM_STR); + $stmt->bindParam(':forumId', $forumId, PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `topics` SET `lastActivity`=NOW() WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `users` SET `lastPost`=NOW() WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `users` SET `lastForumContent`=:content WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':content', $replyContent, PDO::PARAM_STR); + $stmt->execute(); + + $query = "DELETE FROM `read` WHERE `postId`=:id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT replies FROM forums WHERE id = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + $posts = $result['replies']+1; + + $query = "UPDATE `forums` SET `replies`=:posts WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $posts, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT replies FROM topics WHERE id = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + $posts = $result['replies']+1; + + $query = "UPDATE `topics` SET `replies`=:posts WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $posts, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT posts FROM users WHERE id = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + $posts = $result['posts']+1; + $query = "UPDATE `users` SET `posts`=:posts WHERE `id`=:id;"; + $stmt = $dbcon->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':posts', $posts, PDO::PARAM_INT); + $stmt->execute(); + + echo ''; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/post/pinPost.php b/www/core/func/api/forum/post/pinPost.php new file mode 100644 index 0000000..3e8bbc7 --- /dev/null +++ b/www/core/func/api/forum/post/pinPost.php @@ -0,0 +1,29 @@ +prepare('SELECT `deleted` FROM `topics` WHERE `id`=:id'); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if($result['deleted'] === 1) + { + exit('error'); + } + + $query = "UPDATE `topics` SET `pinned`=1 WHERE `id`=:id AND `developer` = 0;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/post/reinstatePost.php b/www/core/func/api/forum/post/reinstatePost.php new file mode 100644 index 0000000..99443b9 --- /dev/null +++ b/www/core/func/api/forum/post/reinstatePost.php @@ -0,0 +1,87 @@ +prepare("SELECT deleted, author_uid, forumId FROM topics WHERE id = :id AND developer = 0 ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0 || $result['deleted'] === 0) { + echo 'error'; + exit; + } + + $timeSince = round(abs(strtotime(context::getCurrentTime()) - strtotime($GLOBALS['userTable']['lastUpload'])) / 60,2); + if ($timeSince < 1 && $GLOBALS['userTable']['rank'] != 1) { + echo 'rate-limit'; + exit; + } + + $forumId = $result['forumId']; + $userId = $result['author_uid']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT posts FROM users WHERE id = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + $posts = $result['posts']+1; + + $query = "UPDATE `users` SET `posts`=:posts WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $posts, PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `topics` SET `deleted`=0 WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + /*$query = "DELETE FROM `replies` WHERE `postId`=:id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + $query = "DELETE FROM `read` WHERE `postId`=:id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute();*/ + + $query = "SELECT * FROM topics WHERE forumId=:id AND `deleted`=0;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->execute(); + $total = $stmt->rowCount(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE forums SET posts = :posts WHERE id=:id;"); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $total, PDO::PARAM_INT); + $stmt->execute(); + + $query = "SELECT * FROM replies WHERE forumId=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->execute(); + $total = $stmt->rowCount(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE forums SET replies = :posts WHERE id=:id;"); + $stmt->bindParam(':id', $forumId, PDO::PARAM_INT); + $stmt->bindParam(':posts', $total, PDO::PARAM_INT); + $stmt->execute(); + + if ($GLOBALS['userTable']['rank'] != 1) { + $stmt = $dbcon->prepare("UPDATE users SET lastUpload = NOW() WHERE id = :user;"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/post/unlockPost.php b/www/core/func/api/forum/post/unlockPost.php new file mode 100644 index 0000000..a3ce991 --- /dev/null +++ b/www/core/func/api/forum/post/unlockPost.php @@ -0,0 +1,34 @@ +prepare('SELECT `deleted` FROM `topics` WHERE `id`=:id'); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if($result['deleted'] === 1) + { + exit('error'); + } + + $query = "UPDATE `topics` SET `locked`=0 WHERE `id`=:id AND `developer`=0;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `topics` SET `lockedByStaff`=0 WHERE `id`=:id AND `developer`=0;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/post/unpinPost.php b/www/core/func/api/forum/post/unpinPost.php new file mode 100644 index 0000000..03e4a22 --- /dev/null +++ b/www/core/func/api/forum/post/unpinPost.php @@ -0,0 +1,29 @@ +prepare('SELECT `deleted` FROM `topics` WHERE `id`=:id'); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if($result['deleted'] === 1) + { + exit('error'); + } + + $query = "UPDATE `topics` SET `pinned`=0 WHERE `id`=:id AND `developer` = 0;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $postId, PDO::PARAM_INT); + $stmt->execute(); + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/showPost.php b/www/core/func/api/forum/showPost.php new file mode 100644 index 0000000..570ca00 --- /dev/null +++ b/www/core/func/api/forum/showPost.php @@ -0,0 +1,267 @@ + +prepare("SELECT id, author_uid, postTime, lastActivity, views, replies, title, content, forumId, locked, pinned, updatedOn, updatedBy, deleted FROM topics WHERE id = :id AND developer = 0"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'Topic not found'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $id = $result['id']; + $isAuthor = ((($GLOBALS['loggedIn'] && $result['author_uid'] === $GLOBALS['userTable']['id']) || ($GLOBALS['loggedIn'] && $GLOBALS['userTable']['rank'] > 0)) && $result['deleted'] === 0); + $stmtr = $GLOBALS['dbcon']->prepare("SELECT id FROM `read` WHERE `userId` = :id AND `postId` = :pid;"); + $stmtr->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmtr->bindParam(':pid', $id, PDO::PARAM_INT); + $stmtr->execute(); + $resultread = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmtr->rowCount() == 0) { + $read = false; + }else{ + $read = true; + } + + if ($read == false and $loggedIn == true and $result['deleted'] == 0) { + $query = "INSERT INTO `read` (`userId`, `postId`) VALUES (:userId, :postId);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':postId', $id, PDO::PARAM_INT); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE topics SET views = views + 1 WHERE id = :id"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + } + + if($result['deleted'] === 0 || ($GLOBALS['loggedIn'] && $GLOBALS['userTable']['rank'] > 0)) + { + echo ''; + } + echo '

'.($result['deleted'] === 0 || ($GLOBALS['loggedIn'] && $GLOBALS['userTable']['rank'] > 0) ? context::secureString($result['title']) : '[ Content Deleted ]').'

'; + echo ''; + $userSheet = context::getUserSheetByID($result['author_uid']); + if ($userSheet['rank'] == 0) { + $usern = $userSheet['username']; + }elseif ($userSheet['rank'] == 1) { + $usern = ''.$userSheet['username'].''; + }elseif ($userSheet['rank'] == 2) { + $usern = ''.$userSheet['username'].''; + } + echo '

Started by '.$usern.'

'; + if($result['deleted'] === 1) + echo '
This post has been deleted for violating our Terms of Service.
'; + echo '
+
'.context::getOnline($userSheet).''.$usern.'
+
'; + if ($userSheet['rank'] == 1) { + echo '

Administrator

'; + } + if ($userSheet['rank'] == 2) { + echo '

Moderator

'; + } + context::checkTopPoster($userSheet['id']); + echo 'Joined: '.date('M j Y', strtotime($userSheet['joinDate'])).'
+ Posts: '.$userSheet['posts'].' +
'; + if($result['deleted'] === 0 || ($GLOBALS['loggedIn'] && $GLOBALS['userTable']['rank'] > 0)) + { + $content = strip_tags($result['content']); + $content = context::secureString($content); + $content = preg_replace("/\s*[a-zA-Z\/\/:\.]*youtube.com\/watch\?v=([a-zA-Z0-9\-_]+)([a-zA-Z0-9\/\*\-\_\?\&\;\%\=\.]*)/i","

", $content); + if ($userSheet['rank'] > 0) { + $content = preg_replace("/https?:\/\/[^ ]+?(?:\.jpg|\.jpeg|\.png|\.gif)/",'', $content); + } + $content = context::showBBcodes($content); + $content = context::parseEmoticon($content); + } + else + { + $content = '[ Content Deleted ]'; + } + + $updater; + $updaterColor; + if(isset($result['updatedOn'])) + { + $updater = context::getUserSheetByID($result['updatedBy']); + switch($userSheet['rank']) + { + case 1: + { + $updaterColor = ''; + break; + } + case 2: + { + $updaterColor = ''; + break; + } + default: + { + $updaterColor = ''; + break; + } + } + } + + echo '
+

Posted on: '.date('M j Y g:i A', strtotime($result['postTime'])) . (isset($result['updatedOn']) ? '

Edited on: '.date('M j Y g:i A', strtotime($result['updatedOn'])).' by '.$updaterColor.$updater['username'].'

' : '') .'
+ '.nl2br($content).' +
'; + }else{ + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM topics WHERE id = :id"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'Topic not found!'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $id = $result['id']; + } + + // Indev feature, never finished + /*if($id == 4118) + { + echo '
+ +
'; + }*/ + + if($result['deleted'] === 0 || ($GLOBALS['loggedIn'] && $GLOBALS['userTable']['rank'] > 0)) + { + $stmt = $dbcon->prepare("SELECT author_uid, content, post_time FROM replies WHERE postId = :id ORDER BY id DESC LIMIT 16 OFFSET :offset;"); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + $count = 0; + foreach($stmt as $result) { + $count++; + if ($count < 16) { + $userSheet = context::getUserSheetByID($result['author_uid']); + if ($userSheet['rank'] == 0) { + $usern = $userSheet['username']; + }elseif ($userSheet['rank'] == 1) { + $usern = ''.$userSheet['username'].''; + }elseif ($userSheet['rank'] == 2) { + $usern = ''.$userSheet['username'].''; + } + echo '
+
'.context::getOnline($userSheet).''.$usern.'
+
'; + if ($userSheet['rank'] == 1) { + echo '

Administrator

'; + } + if ($userSheet['rank'] == 2) { + echo '

Moderator

'; + } + context::checkTopPoster($userSheet['id']); + echo 'Joined: '.date('M j Y', strtotime($userSheet['joinDate'])).'
+ Posts: '.$userSheet['posts'].' +
'; + $content = strip_tags($result['content']); + $content = context::secureString($content); + $content = preg_replace("/\s*[a-zA-Z\/\/:\.]*youtube.com\/watch\?v=([a-zA-Z0-9\-_]+)([a-zA-Z0-9\/\*\-\_\?\&\;\%\=\.]*)/i","", $content); + if ($userSheet['rank'] > 0) { + $content = preg_replace("/https?:\/\/[^ ]+?(?:\.jpg|\.jpeg|\.png|\.gif)/",'', $content); + } + $content = context::showBBcodes($content); + $content = context::parseEmoticon($content); + echo '
+

Posted on: '.date('M j Y g:i A', strtotime($result['post_time'])).'

+ '.nl2br($content).' +
'; + } + } + if ($count > 15) { + echo ''; + } + } + else + { + echo '

(Replies to this thread have been hidden as the initial thread has been deleted. This thread had '.$result['replies'].' '.($result['replies']===1 ? 'reply' : 'replies').'.)

'; + } + }else{ + echo 'An error occurred'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/forum/views/editPost.php b/www/core/func/api/forum/views/editPost.php new file mode 100644 index 0000000..082d4e7 --- /dev/null +++ b/www/core/func/api/forum/views/editPost.php @@ -0,0 +1,22 @@ + + +
+

+ + \ No newline at end of file diff --git a/www/core/func/api/forum/views/newPost.php b/www/core/func/api/forum/views/newPost.php new file mode 100644 index 0000000..0701fea --- /dev/null +++ b/www/core/func/api/forum/views/newPost.php @@ -0,0 +1,24 @@ + + +
+
+

+ +
+
+

Rules of Posting

+

Read the Terms of Service before posting on the Forums.

+

Posting low quality content/post farming will result in your post being deleted and a warning being added to your account.

\ No newline at end of file diff --git a/www/core/func/api/forum/views/newReply.php b/www/core/func/api/forum/views/newReply.php new file mode 100644 index 0000000..60dfce3 --- /dev/null +++ b/www/core/func/api/forum/views/newReply.php @@ -0,0 +1,19 @@ + + +
+

+ + \ No newline at end of file diff --git a/www/core/func/api/friends/get/getFriends.php b/www/core/func/api/friends/get/getFriends.php new file mode 100644 index 0000000..fdbc7f8 --- /dev/null +++ b/www/core/func/api/friends/get/getFriends.php @@ -0,0 +1,65 @@ +prepare("SELECT * FROM `friends` WHERE `userId1` = :id ORDER BY id DESC LIMIT 10 OFFSET :offset;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0 && $page == 0) { + echo 'You have no Graphictoria friends. Why not make some?'; + } + + echo '
'; + $count = 0; + foreach($stmt as $result) { + $count++; + if ($count < 10) { + $userId = $result['userId2']; + $stmt = $dbcon->prepare("SELECT username, thumbnailHash, headshotHash, id, lastSeen FROM users WHERE id = :id;"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $resultuser = $stmt->fetch(PDO::FETCH_ASSOC); + $username = $resultuser['username']; + if (strlen($username) > 10) { + $username = substr($username, 0, 7) . '...'; + } + echo ''; + } + } + echo '
'; + echo '
'; + if ($page > 0) { + echo '« Previous'; + } + if ($count > 9) { + echo 'Next »'; + } + if ($count == 0 and $page > 0) { + exit; + } + echo '
'; +?> \ No newline at end of file diff --git a/www/core/func/api/friends/get/getRequests.php b/www/core/func/api/friends/get/getRequests.php new file mode 100644 index 0000000..786c22f --- /dev/null +++ b/www/core/func/api/friends/get/getRequests.php @@ -0,0 +1,65 @@ +prepare("SELECT * FROM `friendRequests` WHERE `recvuid` = :id ORDER BY id DESC LIMIT 10 OFFSET :offset;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0 && $page == 0) { + echo 'You do not have any friend request inbound.'; + } + + echo '
'; + $count = 0; + foreach($stmt as $result) { + $count++; + if ($count < 10) { + $userId = $result['senduid']; + $stmt = $dbcon->prepare("SELECT username, thumbnailHash, headshotHash, id, lastSeen FROM users WHERE id = :id;"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $resultuser = $stmt->fetch(PDO::FETCH_ASSOC); + $username = $resultuser['username']; + if (strlen($username) > 10) { + $username = substr($username, 0, 7) . '...'; + } + echo ''; + } + } + echo '
'; + echo '
'; + if ($page > 0) { + echo '« Previous'; + } + if ($count > 9) { + echo 'Next »'; + } + if ($count == 0 and $page > 0) { + exit; + } + echo '
'; +?> \ No newline at end of file diff --git a/www/core/func/api/friends/get/showFriends.php b/www/core/func/api/friends/get/showFriends.php new file mode 100644 index 0000000..a0b0d33 --- /dev/null +++ b/www/core/func/api/friends/get/showFriends.php @@ -0,0 +1,80 @@ +prepare("SELECT id FROM users WHERE id = :id;"); + $stmt->bindParam(':id', $userid, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $userid = $result['id']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM `friends` WHERE `userId1` = :id ORDER BY id DESC LIMIT 10 OFFSET :offset;"); + $stmt->bindParam(':id', $userid, PDO::PARAM_INT); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0 && $page == 0) { + echo 'This user has no friends.'; + } + + echo '
'; + $count = 0; + foreach($stmt as $result) { + $count++; + if ($count < 10) { + $userId = $result['userId2']; + $stmt = $dbcon->prepare("SELECT username, thumbnailHash, headshotHash, id, lastSeen FROM users WHERE id = :id;"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $resultuser = $stmt->fetch(PDO::FETCH_ASSOC); + $username = $resultuser['username']; + if (strlen($username) > 10) { + $username = substr($username, 0, 7) . '...'; + } + echo ''; + } + } + echo '
'; + echo '
'; + if ($page > 0) { + echo '« Previous'; + } + if ($count > 9) { + echo 'Next »'; + } + if ($count == 0 and $page > 0) { + exit; + } + echo '
'; +?> \ No newline at end of file diff --git a/www/core/func/api/friends/post/acceptRequest.php b/www/core/func/api/friends/post/acceptRequest.php new file mode 100644 index 0000000..6cb54c5 --- /dev/null +++ b/www/core/func/api/friends/post/acceptRequest.php @@ -0,0 +1,100 @@ +prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) { + echo 'error'; + exit; + } + + if ($result['senduid'] == $GLOBALS['userTable']['id'] and $stmt->rowCount() > 0) { + $query = "DELETE FROM `friendRequests` WHERE `senduid` = :sid AND `recvuid` = :id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + echo 'error'; + exit; + } + + $query = "SELECT * FROM `friends` WHERE `userId1` = :id AND `userId2` = :sid"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + $query = "DELETE FROM `friendRequests` WHERE `senduid` = :sid AND `recvuid` = :id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + echo 'error'; + exit; + } + + $query = "SELECT * FROM `friends` WHERE `userId1` = :sid AND `userId2` = :id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + $query = "DELETE FROM `friendRequests` WHERE `senduid` = :id AND `recvuid` = :sid;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + echo 'error'; + exit; + } + + $query = "INSERT INTO friends (`userId1`, `userId2`) VALUES (:userId1, :userId2);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':userId1', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':userId2', $userID, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':userId1', $userID, PDO::PARAM_INT); + $stmt->bindParam(':userId2', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $query = "DELETE FROM `friendRequests` WHERE `senduid` = :sid AND `recvuid` = :id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM users WHERE id = :id"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $resultuinfo = $stmt->fetch(PDO::FETCH_ASSOC); + $message = ''.htmlentities($resultuinfo['username'], ENT_QUOTES, "UTF-8").' has accepted your friend request. Start a conversation by replying!'; + $query = "INSERT INTO messages (`recv_uid`, `sender_uid`, `title`, `content`) VALUES (:userId1, :userId2, 'Friend Request Accepted', :msg);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':userId1', $userID, PDO::PARAM_INT); + $stmt->bindParam(':userId2', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':msg', $message, PDO::PARAM_STR); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/friends/post/ignoreRequest.php b/www/core/func/api/friends/post/ignoreRequest.php new file mode 100644 index 0000000..badb90e --- /dev/null +++ b/www/core/func/api/friends/post/ignoreRequest.php @@ -0,0 +1,21 @@ +prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/friends/post/removeFriend.php b/www/core/func/api/friends/post/removeFriend.php new file mode 100644 index 0000000..d901bee --- /dev/null +++ b/www/core/func/api/friends/post/removeFriend.php @@ -0,0 +1,26 @@ +prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $dbcon->prepare($query); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->bindParam(':sid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/friends/post/sendRequest.php b/www/core/func/api/friends/post/sendRequest.php new file mode 100644 index 0000000..7a36818 --- /dev/null +++ b/www/core/func/api/friends/post/sendRequest.php @@ -0,0 +1,60 @@ +prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + echo 'error'; + exit; + } + + $query = "SELECT * FROM `friendRequests` WHERE `senduid` = :id AND `recvuid` = :sid"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + echo 'error'; + exit; + } + + $currentTime = context::getCurrentTime(); + $from_time = strtotime($GLOBALS['userTable']['lastFR']); + $to_time = strtotime($currentTime); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince < 1) { + echo 'rate-limit'; + exit; + }else{ + $query = "UPDATE users SET lastFR = NOW() WHERE id=:id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + $query = "INSERT INTO friendRequests (`senduid`, `recvuid`) VALUES (:userId1, :userId2);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':userId1', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':userId2', $userID, PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/games/addKey.php b/www/core/func/api/games/addKey.php new file mode 100644 index 0000000..11301a5 --- /dev/null +++ b/www/core/func/api/games/addKey.php @@ -0,0 +1,44 @@ +Please enter a key.
'; + $err = true; + } + + if ($err == false) { + $stmt = $dbcon->prepare("SELECT * FROM games WHERE `key` = :key"); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->execute(); + + if ($stmt->rowCount() == 0) { + echo '
Invalid key.
'; + }else{ + // Check if already submitted. + $stmt = $dbcon->prepare("SELECT * FROM gameKeys WHERE `key` = :key AND userid = :uid"); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + if ($stmt->rowCount() == 0) { + $stmt = $dbcon->prepare("INSERT INTO `gameKeys` (`userid`, `key`) VALUES (:uid, :key);"); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + echo '
Key added!
'; + }else{ + echo '
You have already submitted this key.
'; + } + } + } + }else{ + echo '
Something happened.
'; + } + }else{ + echo '
You need to be signed in to add a server to your list.
'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/games/deleteServer.php b/www/core/func/api/games/deleteServer.php new file mode 100644 index 0000000..0fdcd9a --- /dev/null +++ b/www/core/func/api/games/deleteServer.php @@ -0,0 +1,30 @@ +prepare("SELECT * FROM games WHERE id = :id;"); + $stmt->bindParam(':id', $serverID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($result['creator_uid'] != $GLOBALS['userTable']['id'] && $GLOBALS['userTable']['rank'] == 0) { + echo 'error'; + } + + if ($result['dedi'] == 1 && $GLOBALS['userTable']['rank'] != 1) die("error"); + + $stmt = $dbcon->prepare("DELETE FROM games WHERE id = :id;"); + $stmt->bindParam(':id', $serverID, PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/games/getLatestServer.php b/www/core/func/api/games/getLatestServer.php new file mode 100644 index 0000000..daf7235 --- /dev/null +++ b/www/core/func/api/games/getLatestServer.php @@ -0,0 +1,69 @@ + 5){ + return 'Offline'; + }else{ + return 'Online'; + } + } + + function getOnline2($ping) { + $currentTime = date('Y-m-d H:i:s'); + $from_time = strtotime($ping); + $to_time = strtotime($currentTime); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 2) { + return false; + }else{ + return true; + } + } + + + function getPlayerCount($serverID, $dbcon) { + $count = 0; + $stmt = $GLOBALS['dbcon']->prepare("SELECT lastSeen, inGame FROM users WHERE inGameId = :id"); + $stmt->bindParam(':id', $serverID, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + if (getOnline2($result['lastSeen']) == true and $result['inGame'] == 1) { + $count++; + } + } + return $count; + } + + function getDescription($description) { + if (strlen($description) > 0) { + return htmlentities($description, ENT_QUOTES, "UTF-8"); + }else{ + return 'No description.'; + } + } + + if ($GLOBALS['loggedIn']) { + $stmt = $dbcon->prepare("SELECT * FROM games WHERE `creator_uid` = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo '

Nothing found

Looks like there is nothing here

'; + } + foreach($stmt as $result) { + $creator = $result['creator_uid']; + $stmt = $dbcon->prepare("SELECT * FROM users WHERE id = :id"); + $stmt->bindParam(':id', $creator, PDO::PARAM_INT); + $stmt->execute(); + $result2 = $stmt->fetch(PDO::FETCH_ASSOC); + echo '
'; + echo '

'.htmlentities(user::filter($result['name']), ENT_QUOTES, "UTF-8").'

Creator : '.$result2['username'].'

Status : '.getOnline($result['lastPing']).'
Online Players : '.getPlayerCount($result['id'], $dbcon).'
View
'; + echo '
'; + } + }else{ + echo '

You need to be logged in

Please login and try again.

'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/games/getMy.php b/www/core/func/api/games/getMy.php new file mode 100644 index 0000000..029870a --- /dev/null +++ b/www/core/func/api/games/getMy.php @@ -0,0 +1,88 @@ + 5){ + return 'Offline'; + }else{ + return 'Online'; + } + } + + function getDescription($description) { + if (strlen($description) > 0) { + return htmlentities($description, ENT_QUOTES, "UTF-8"); + }else{ + return 'No description.'; + } + } + + function getOnline2($ping) { + $currentTime = date('Y-m-d H:i:s'); + $from_time = strtotime($ping); + $to_time = strtotime($currentTime); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 2) { + return false; + }else{ + return true; + } + } + + function getPlayerCount($serverID, $dbcon) { + $count = 0; + $stmt = $GLOBALS['dbcon']->prepare("SELECT lastSeen, inGame FROM users WHERE inGameId = :id"); + $stmt->bindParam(':id', $serverID, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + if (getOnline2($result['lastSeen']) == true and $result['inGame'] == 1) { + $count++; + } + } + return $count; + } + + if ($GLOBALS['loggedIn']) { + $stmt = $dbcon->prepare("SELECT * FROM gameKeys WHERE userid = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $count = 0; + foreach($stmt as $result) { + if (isset($_GET['version'])) { + $version = $_GET['version']; + if (is_array($version) == true) exit; + if ($version != 3) exit; + $gameId = $result['key']; + $stmt = $dbcon->prepare("SELECT * FROM games WHERE `key` = :key AND `version` = :version;"); + $stmt->bindParam(':version', $version, PDO::PARAM_INT); + $stmt->bindParam(':key', $gameId, PDO::PARAM_STR); + }else{ + $stmt = $dbcon->prepare("SELECT * FROM games WHERE `key` = :key;"); + $stmt->bindParam(':key', $gameId, PDO::PARAM_STR); + } + $stmt->execute(); + if ($stmt->rowCount() > 0) { + $count++; + } + + foreach($stmt as $result) { + $creator = $result['creator_uid']; + $stmt = $dbcon->prepare("SELECT * FROM users WHERE id = :id"); + $stmt->bindParam(':id', $creator, PDO::PARAM_INT); + $stmt->execute(); + $result2 = $stmt->fetch(PDO::FETCH_ASSOC); + echo '
'; + echo '

'.htmlentities(user::filter($result['name']), ENT_QUOTES, "UTF-8").'

Creator : '.$result2['username'].'

Status : '.getOnline($result['lastPing']).'
Online Players : '.getPlayerCount($result['id'], $dbcon).'
View
'; + echo '
'; + } + } + if ($count == 0) { + echo '

Nothing found

Looks like there is nothing here

'; + } + }else{ + echo '

You need to be logged in

Please login and try again.

'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/games/getMyS.php b/www/core/func/api/games/getMyS.php new file mode 100644 index 0000000..6cc4f0a --- /dev/null +++ b/www/core/func/api/games/getMyS.php @@ -0,0 +1,78 @@ + 5){ + return 'Offline'; + }else{ + return 'Online'; + } + } + + function getOnline2($ping) { + $currentTime = date('Y-m-d H:i:s'); + $from_time = strtotime($ping); + $to_time = strtotime($currentTime); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 2) { + return false; + }else{ + return true; + } + } + + + function getPlayerCount($serverID, $dbcon) { + $count = 0; + $stmt = $GLOBALS['dbcon']->prepare("SELECT lastSeen, inGame FROM users WHERE inGameId = :id"); + $stmt->bindParam(':id', $serverID, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + if (getOnline2($result['lastSeen']) == true and $result['inGame'] == 1) { + $count++; + } + } + return $count; + } + + function getDescription($description) { + if (strlen($description) > 0) { + return htmlentities($description, ENT_QUOTES, "UTF-8"); + }else{ + return 'No description.'; + } + } + + if ($GLOBALS['loggedIn']) { + if (isset($_GET['version'])) { + $version = $_GET['version']; + if (is_array($version) == true) exit; + if ($version != 3) exit; + $stmt = $dbcon->prepare("SELECT * FROM games WHERE `creator_uid` = :id AND `version` = :version;"); + $stmt->bindParam(':version', $version, PDO::PARAM_INT); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + }else{ + $stmt = $dbcon->prepare("SELECT * FROM games WHERE `creator_uid` = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + } + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo '

Nothing found

Looks like there is nothing here

'; + } + foreach($stmt as $result) { + $creator = $result['creator_uid']; + $stmt = $dbcon->prepare("SELECT * FROM users WHERE id = :id"); + $stmt->bindParam(':id', $creator, PDO::PARAM_INT); + $stmt->execute(); + $result2 = $stmt->fetch(PDO::FETCH_ASSOC); + echo '
'; + echo '

'.htmlentities(user::filter($result['name']), ENT_QUOTES, "UTF-8").'

Creator : '.$result2['username'].'

Status : '.getOnline($result['lastPing']).'
Online Players : '.getPlayerCount($result['id'], $dbcon).'
View
'; + echo '
'; + } + }else{ + echo '

You need to be logged in

Please login and try again.

'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/games/getPublic.php b/www/core/func/api/games/getPublic.php new file mode 100644 index 0000000..6106b7b --- /dev/null +++ b/www/core/func/api/games/getPublic.php @@ -0,0 +1,137 @@ +prepare("SELECT * FROM games WHERE public = 1 AND version = 0 ORDER BY id DESC"); + }elseif($version == 1) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM games WHERE public = 1 AND version = 1 ORDER BY id DESC"); + }elseif ($version == 2) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM games WHERE public = 1 AND version = 2 ORDER BY id DESC"); + }elseif ($version == 3) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM games WHERE public = 1 AND version = 3 ORDER BY id DESC"); + }else{ + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM games WHERE public = 1 ORDER BY id DESC"); + } + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo '

Looks like there are no public games for this version!

You could try adding your own server and setting it to public.

'; + exit; + } + + function getOnline($ping) { + $currentTime = date('Y-m-d H:i:s'); + $from_time = strtotime($ping); + $to_time = strtotime($currentTime); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 2){ + return 'Offline'; + }else{ + return 'Online'; + } + } + + function getOnline2($ping) { + $currentTime = date('Y-m-d H:i:s'); + $from_time = strtotime($ping); + $to_time = strtotime($currentTime); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 2) { + return false; + }else{ + return true; + } + } + + function getDedicated($dedi) { + if ($dedi == 1) return ' '; + return ''; + } + + function getPlayerCount($serverID, $dbcon, $dedicated, $pCount) { + if ($dedicated == 0) { + $count = 0; + $stmt = $GLOBALS['dbcon']->prepare("SELECT lastSeen, inGame FROM users WHERE inGameId = :id"); + $stmt->bindParam(':id', $serverID, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + if (getOnline2($result['lastSeen']) == true and $result['inGame'] == 1) { + $count++; + } + } + return $count; + }else{ + return $pCount; + } + } + + function getDescription($description) { + if (strlen($description) > 0) { + return htmlentities($description, ENT_QUOTES, "UTF-8"); + }else{ + return 'No description.'; + } + } + + function getImage($result2, $serverID, $imgTime) { + if (file_exists("/var/www/api/imageServer/server/".$serverID.".png") && $GLOBALS['loggedIn']) { + return "http://api.gtoria.net/imageServer/server/".$serverID.".png?v=".strtotime($imgTime); + }else{ + return context::getUserImage($result2); + } + } + + function getVersion($gVersion) { + if ($GLOBALS['gameVersion'] == 4) { + if ($gVersion == 3) $versionString = "2016"; + return 'Version : '.$versionString.'
'; + } + } + + $count = 0; + if ($stmt->rowCount() > 0) { + echo '
'; + } + foreach($stmt as $result) { + if (getOnline2($result['lastPing']) == true) { + $count++; + $creator = $result['creator_uid']; + $stmt = $GLOBALS['dbcon']->prepare("SELECT username, id, imgTime FROM users WHERE id = :id"); + $stmt->bindParam(':id', $creator, PDO::PARAM_INT); + $stmt->execute(); + $result2 = $stmt->fetch(PDO::FETCH_ASSOC); + $gameName = context::secureString($result['name']); + if (strlen($gameName) >= 20) { + $gameName = substr($gameName, 0, 17). " ... "; + } + echo '
'; + echo '

'.getDedicated($result['dedi']).$gameName.'

Creator : '.$result2['username'].'

Status : '.getOnline($result['lastPing']).'
Online Players : '.getPlayerCount($result['id'], $dbcon, $result['dedi'], $result['numPlayers']).'
'.getVersion($result['version']).'View
'; + echo '
'; + } + } + if ($stmt->rowCount() > 0) { + echo '
'; + } + + if ($count == 0) { + echo '

Looks like there are no online games for this version!

You could try adding your own server and setting it to public.

'; + } + + echo ''; +?> \ No newline at end of file diff --git a/www/core/func/api/games/post/addServer.php b/www/core/func/api/games/post/addServer.php new file mode 100644 index 0000000..4358c08 --- /dev/null +++ b/www/core/func/api/games/post/addServer.php @@ -0,0 +1,101 @@ + 32) { + echo 'server-name-too-long'; + exit; + } + + if (strlen($serverName) < 4) { + echo 'server-name-too-short'; + exit; + } + + if (!preg_match("/^[\w*?!\/@',:#$%\^&*\(\) -]+$/", $serverName) == 1) { + die("server-name-too-short"); + } + + if (strlen($serverName) > 128) { + echo 'server-description-too-long'; + exit; + } + + if (strlen($serverIP) == 0) { + echo 'server-ip-too-short'; + exit; + } + + if (strlen($serverIP) > 64) { + echo 'server-ip-too-long'; + exit; + } + + if (strlen($serverPort) == 0) { + echo 'server-port-too-short'; + exit; + } + + if (strlen($serverPort) > 5) { + echo 'server-port-too-long'; + exit; + } + + if (is_numeric($serverPort == false) || $serverPort > 64000) die("invalid-port"); + + if (filter_var($serverIP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) == false) { + echo 'invalid-ip'; + exit; + } + + if ($privacyType != 0 && $privacyType != 1) { + echo 'invalid-privacy'; + exit; + } + + if ($gameVersion != 3) { + echo 'invalid-version'; + exit; + } + + $key = md5(microtime().rand()); + $serverkey = md5(microtime().rand()); + $stmt = $dbcon->prepare("INSERT INTO games (`public`, `creator_uid`, `name`, `description`, `ip`, `port`, `key`, `privatekey`, `version`) VALUES (:public, :user, :name, :description, :ip, :port, :key, :serverkey, :version);"); + $stmt->bindParam(':public', $privacyType, PDO::PARAM_INT); + $stmt->bindParam(':version', $gameVersion, PDO::PARAM_INT); + $stmt->bindParam(':serverkey', $serverkey, PDO::PARAM_STR); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':name', $serverName, PDO::PARAM_STR); + $stmt->bindParam(':description', $serverDescription, PDO::PARAM_STR); + $stmt->bindParam(':ip', $serverIP, PDO::PARAM_STR); + $stmt->bindParam(':port', $serverPort, PDO::PARAM_INT); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $dbcon->prepare("SELECT * FROM games WHERE `creator_uid`=:uid ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $id = $result['id']; + echo $id; + + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/games/post/addServerDedicated.php b/www/core/func/api/games/post/addServerDedicated.php new file mode 100644 index 0000000..f1a1db5 --- /dev/null +++ b/www/core/func/api/games/post/addServerDedicated.php @@ -0,0 +1,114 @@ + 32) { + echo 'server-name-too-long'; + exit; + } + + if (!preg_match("/^[\w*?!\/@',:#$%\^&*\(\) -]+$/", $serverName) == 1) { + die("server-name-too-short"); + } + + if (strlen($serverName) < 4) { + echo 'server-name-too-short'; + exit; + } + + if ($privacyType != 0 && $privacyType != 1) { + echo 'invalid-privacy'; + exit; + } + + if (strlen($serverDescription) > 128) { + echo 'server-description-too-long'; + exit; + } + + if (isset($_FILES['placeFile'])) { + // Upload the place file properly. + $fileContent = @file_get_contents($_FILES['placeFile']['tmp_name']); + if (strpos($fileContent, 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"') == false) { + die("invalid-placefile"); + } + + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime = finfo_file($finfo, $_FILES['placeFile']['tmp_name']); + if ($mime != "text/plain") { + die("invalid-placefile"); + } + + $imageFileType = pathinfo($_FILES['placeFile']["name"], PATHINFO_EXTENSION); + if ($imageFileType != "rbxl" && $imageFileType != "RBXL") die("invalid-placefile"); + + // Still alive? Proceed to upload the place file. + $uploadDirectory = $_SERVER['DOCUMENT_ROOT'].'/data/assets/uploads/'; + $fileHash = hash_file('sha512', $_FILES["placeFile"]["tmp_name"]); + if (!file_exists($uploadDirectory.$fileHash)) { + if (!move_uploaded_file($_FILES["placeFile"]["tmp_name"], $uploadDirectory.$fileHash)) { + die("file-move-error"); + } + } + + $webDirectory = "http://gtoria.net/data/assets/uploads/".$fileHash; + }else{ + if ($genPlace == 0) die("error"); + if ($genPlace == 1) $webDirectory = "http://api.gtoria.net/places/baseplate.rbxl"; + } + + // If we're still here, we can continue to request the server. + $stmt = $dbcon->prepare("INSERT INTO serverRequests (`placeLocation`, `serverName`, `serverDescription`, `serverVersion`, `userID`, `serverPrivacy`) VALUES (:placeLocation, :serverName, :serverDescription, :version, :userID, :privacy);"); + $stmt->bindParam(':placeLocation', $webDirectory, PDO::PARAM_STR); + $stmt->bindParam(':serverName', $serverName, PDO::PARAM_STR); + $stmt->bindParam(':userID', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':version', $version, PDO::PARAM_INT); + $stmt->bindParam(':privacy', $privacyType, PDO::PARAM_INT); + $stmt->bindParam(':serverDescription', $serverDescription, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $dbcon->prepare("UPDATE users SET lastUpload = NOW() WHERE id = :user;"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/games/post/deleteServer.php b/www/core/func/api/games/post/deleteServer.php new file mode 100644 index 0000000..5fff240 --- /dev/null +++ b/www/core/func/api/games/post/deleteServer.php @@ -0,0 +1,29 @@ +prepare("SELECT * FROM games WHERE id = :id;"); + $stmt->bindParam(':id', $serverID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($result['creator_uid'] != $GLOBALS['userTable']['id'] && $GLOBALS['userTable']['rank'] == 0) { + echo 'error'; + exit; + } + + $stmt = $dbcon->prepare("DELETE FROM games WHERE id = :id;"); + $stmt->bindParam(':id', $serverID, PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/games/viewGame.php b/www/core/func/api/games/viewGame.php new file mode 100644 index 0000000..228a390 --- /dev/null +++ b/www/core/func/api/games/viewGame.php @@ -0,0 +1,77 @@ + 2) { + return false; + }else{ + return true; + } + } + + function getPlayerCount($serverID, $dbcon) { + $count = 0; + $stmt = $GLOBALS['dbcon']->prepare("SELECT lastSeen, inGame FROM users WHERE inGameId = :id"); + $stmt->bindParam(':id', $serverID, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + if (getOnline($result['lastSeen']) == true and $result['inGame'] == 1) { + $count++; + } + } + return $count; + } + + if (isset($_GET['id'])) { + $gameID = $_GET['id']; + if (is_array($gameID)) { + exit; + } + include_once $_SERVER['DOCUMENT_ROOT'].'/core/func/includes.php'; + $stmt = $GLOBALS['dbcon']->prepare('SELECT * FROM games WHERE id= :id'); + $stmt->bindParam(':id', $gameID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) { + echo 'Game not found!'; + echo ''; + exit; + } + echo ''; + $stmt = $dbcon->prepare("SELECT username, id, thumbnailHash, headshotHash FROM users WHERE id = :id"); + $stmt->bindParam(':id', $result['creator_uid'], PDO::PARAM_INT); + $stmt->execute(); + $resultuser = $stmt->fetch(PDO::FETCH_ASSOC); + echo '
'; + if (file_exists($_SERVER["DOCUMENT_ROOT"] . "/../api/imageServer/server/".$result['id'].".png") && $GLOBALS['loggedIn']) { + echo ""; + } + echo '
'; + echo '
'; + echo '
'; + echo 'Creator : '.$resultuser['username'].'
'; + echo 'Created : '.date('M j Y g:i A', strtotime($result['date'])).'
'; + if (getOnline($result['lastPing'])) { + echo 'Status : Online
'; + }else{ + echo 'Status : Offline
'; + } + if ($result['dedi'] == 0) + echo 'Online Players : '.getPlayerCount($result['id'], $GLOBALS['dbcon']).'
'; + if ($result['dedi'] == 1) { + echo 'Online Players : '.$result['numPlayers'].'
'; + } + if ($GLOBALS['loggedIn']) { + //if ($result['version'] == 3) echo 'Play'; + if ($result['version'] == 3) echo 'Play'; + }else{ + echo 'Play'; + } + echo 'Full Page'; + echo '
'; + }else{ + echo 'An error occurred'; + } +?> diff --git a/www/core/func/api/groups/get/getMembers.php b/www/core/func/api/groups/get/getMembers.php new file mode 100644 index 0000000..63222f0 --- /dev/null +++ b/www/core/func/api/groups/get/getMembers.php @@ -0,0 +1,69 @@ +prepare("SELECT * FROM group_members WHERE gid = :id ORDER BY id DESC LIMIT 9 OFFSET :offset;"); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->bindParam(':id', $groupId, PDO::PARAM_INT); + $stmt->execute(); + + $stmtc = $GLOBALS['dbcon']->prepare("SELECT id FROM group_members WHERE gid = :id"); + $stmtc->bindParam(':id', $groupId, PDO::PARAM_INT); + $stmtc->execute(); + echo ''; + + $count = 0; + if ($stmt->rowCount() == 0) { + echo 'No members found'; + } + echo '
'; + foreach($stmt as $result) { + $count++; + if ($count < 9) { + $userId = $result['uid']; + $stmt = $GLOBALS['dbcon']->prepare("SELECT username, thumbnailHash, headshotHash, lastSeen, id FROM users WHERE id = :id"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $resultuser = $stmt->fetch(PDO::FETCH_ASSOC); + $username = $resultuser['username']; + if (strlen($username) > 10) { + $username = substr($username, 0, 7) . '...'; + } + echo '

'; + echo '
'; + echo context::getOnline($resultuser); + echo ''.htmlentities($username, ENT_QUOTES, "UTF-8").'

'; + } + } + echo '
'; + if ($page > 0) { + echo '« Previous'; + } + if ($count > 6) { + echo 'Next »'; + } + if ($count == 0 and $page > 0) { + exit; + } + echo '
'; +?> \ No newline at end of file diff --git a/www/core/func/api/groups/post/changeDescription.php b/www/core/func/api/groups/post/changeDescription.php new file mode 100644 index 0000000..f2edaee --- /dev/null +++ b/www/core/func/api/groups/post/changeDescription.php @@ -0,0 +1,36 @@ + 256 or strlen($descriptionValue) > 256) { + echo 'description-too-long'; + exit; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM groups WHERE id = :id"); + $stmt->bindParam(':id', $groupId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if (($result['closed'] == 1 || $result['cuid'] != $GLOBALS['userTable']['id']) and $GLOBALS['userTable']['rank'] == 0) { + echo 'error'; + exit; + } + + $query = "UPDATE `groups` SET `description`=:description WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $groupId, PDO::PARAM_INT); + $stmt->bindParam(':description', $descriptionValue, PDO::PARAM_STR); + $stmt->execute(); + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/groups/post/createGroup.php b/www/core/func/api/groups/post/createGroup.php new file mode 100644 index 0000000..5e9284c --- /dev/null +++ b/www/core/func/api/groups/post/createGroup.php @@ -0,0 +1,82 @@ + 32 or strlen($groupName) > 32) { + echo 'group-name-too-long'; + exit; + } + + $descriptionCheck = preg_replace("/[^ \w]+/", "", $groupDescription); + $descriptionCheck = preg_replace('/\s+/', '', $descriptionCheck); + if (strlen($descriptionCheck) > 256 or strlen($groupDescription) > 256) { + echo 'description-too-long'; + exit; + } + + if ($GLOBALS['userTable']['coins'] < 50) { + echo 'no-coins'; + exit; + } + + $count = 0; + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM group_members WHERE uid = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $count = $count + $stmt->rowCount(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM groups WHERE cuid = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $count = $count + $stmt->rowCount(); + + if ($count > 9) { + echo 'in-too-many-groups'; + exit; + } + + $newCoins = $GLOBALS['userTable']['coins']-50; + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET coins = :coins WHERE id = :user;"); + $stmt->bindParam(':coins', $newCoins, PDO::PARAM_INT); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $query = "INSERT INTO groups (`cuid`, `name`, `description`) VALUES (:cuid, :name, :description);"; + $stmt = $dbcon->prepare($query); + $stmt->bindParam(':cuid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':name', $groupName, PDO::PARAM_STR); + $stmt->bindParam(':description', $groupDescription, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $dbcon->prepare("SELECT * FROM groups WHERE cuid = :id ORDER BY id DESC LIMIT 1;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + echo $result['id']; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/groups/post/joinGroup.php b/www/core/func/api/groups/post/joinGroup.php new file mode 100644 index 0000000..86428e4 --- /dev/null +++ b/www/core/func/api/groups/post/joinGroup.php @@ -0,0 +1,62 @@ +prepare("SELECT * FROM group_members WHERE uid = :uid AND gid = :id"); + $stmt->bindParam(':id', $groupId, PDO::PARAM_INT); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + echo 'error'; + exit; + } + + // Get all group information. + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM groups WHERE id = :id"); + $stmt->bindParam(':id', $groupId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if($result['closed'] == 1) { + exit('error'); + } + + // Check if owned by this user. + if ($GLOBALS['userTable']['id'] == $result['cuid']) { + echo 'error'; + exit; + } + + $count = 0; + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM group_members WHERE uid = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $count = $count + $stmt->rowCount(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT id FROM groups WHERE cuid = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $count = $count + $stmt->rowCount(); + + if ($count > 9) { + echo 'in-too-many-groups'; + exit; + } + + // Join group + $query = "INSERT INTO group_members (`uid`, `gid`) VALUES (:uid, :gid);"; + $stmt = $dbcon->prepare($query); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':gid', $groupId, PDO::PARAM_STR); + $stmt->execute(); + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/groups/post/leaveDelete.php b/www/core/func/api/groups/post/leaveDelete.php new file mode 100644 index 0000000..46b57b8 --- /dev/null +++ b/www/core/func/api/groups/post/leaveDelete.php @@ -0,0 +1,52 @@ +prepare("SELECT * FROM groups WHERE id = :id"); + $stmt->bindParam(':id', $groupId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($result['closed'] == 1) { + exit('error'); + } + + // Check if not owned by this user. + if ($GLOBALS['userTable']['id'] != $result['cuid']) { + echo 'error'; + exit; + } + + // Check if not a member. + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM group_members WHERE uid = :uid AND gid = :id"); + $stmt->bindParam(':id', $groupId, PDO::PARAM_INT); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + echo 'error'; + exit; + } + + // Delete group and all its members. + $query = "DELETE FROM `group_members` WHERE `gid`=:groupId;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':groupId', $groupId, PDO::PARAM_INT); + $stmt->execute(); + + $query = "DELETE FROM `groups` WHERE `id`=:groupId;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':groupId', $groupId, PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/groups/post/leaveGroup.php b/www/core/func/api/groups/post/leaveGroup.php new file mode 100644 index 0000000..9b67dfe --- /dev/null +++ b/www/core/func/api/groups/post/leaveGroup.php @@ -0,0 +1,43 @@ +prepare("SELECT * FROM groups WHERE id = :id"); + $stmt->bindParam(':id', $groupId, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + // Check if owned by this user. + if ($GLOBALS['userTable']['id'] == $result['cuid']) { + echo 'error'; + exit; + } + + // Check if member. + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM group_members WHERE uid = :uid AND gid = :id"); + $stmt->bindParam(':id', $groupId, PDO::PARAM_INT); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'error'; + exit; + } + + // Leave group + $query = "DELETE FROM `group_members` WHERE `gid`=:groupId AND `uid`=:userId;"; + $stmt = $dbcon->prepare($query); + $stmt->bindParam(':groupId', $groupId, PDO::PARAM_INT); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/groups/searchGroup.php b/www/core/func/api/groups/searchGroup.php new file mode 100644 index 0000000..11fa50b --- /dev/null +++ b/www/core/func/api/groups/searchGroup.php @@ -0,0 +1,54 @@ +prepare("SELECT * FROM groups WHERE name LIKE :term AND `closed` = 0 ORDER BY id DESC LIMIT 11 OFFSET :offset;"); + }else{ + $stmt = $dbcon->prepare("SELECT * FROM groups WHERE name LIKE :term AND `closed` = 0 ORDER BY name ASC LIMIT 11 OFFSET :offset;"); + } + $stmt->bindParam(':term', $searchTermSQL, PDO::PARAM_STR); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo '
Nothing found.
'; + } + $count = 0; + foreach($stmt as $result) { + $count++; + if ($count < 11) { + echo '
'; + echo '
'; + echo '
'; + echo ''; + echo '
'; + echo '
'; + if ($result['description'] == NULL) { + $description = "This user has not configured anything to display here"; + }else{ + $description = ''.context::secureString($result['description']).''; + } + + echo '

'.context::secureString($result['name']).'

Description:

'.$description.'

'; + echo '
'; + } + } + if ($count > 10) { + echo ''; + } +?> \ No newline at end of file diff --git a/www/core/func/api/messages/getMessages.php b/www/core/func/api/messages/getMessages.php new file mode 100644 index 0000000..594bfaf --- /dev/null +++ b/www/core/func/api/messages/getMessages.php @@ -0,0 +1,91 @@ +Messages'; + } + + function showReadStatus($read) { + if ($read == 0) { + return ''; + } + } + + $offset = $page*25; + include_once $_SERVER['DOCUMENT_ROOT'].'/core/func/includes.php'; + if ($GLOBALS['loggedIn'] == false) { + exit; + } + if ($filter == 0) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM messages WHERE recv_uid = :rId ORDER BY id DESC LIMIT 26 OFFSET :offset"); + }elseif ($filter == 1) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM messages WHERE recv_uid = :rId AND `read` = 0 ORDER BY id DESC LIMIT 26 OFFSET :offset"); + }elseif ($filter == 2) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM messages WHERE recv_uid = :rId AND `read` = 1 ORDER BY id DESC LIMIT 26 OFFSET :offset"); + }elseif ($filter == 3) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM messages WHERE sender_uid = :rId ORDER BY id DESC LIMIT 26 OFFSET :offset"); + }else{ + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM messages WHERE recv_uid = :rId ORDER BY id DESC LIMIT 26 OFFSET :offset"); + } + + $stmt->bindParam(':rId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->execute(); + echo '
'; + $count = 0; + foreach($stmt as $result) { + $count++; + if ($count < 25) { + if ($filter == 3) { + $userSheet = context::getUserSheetByID($result['recv_uid']); + }else{ + $userSheet = context::getUserSheetByID($result['sender_uid']); + } + if ($userSheet['rank'] == 0) { + $usern = $userSheet['username']; + }elseif ($userSheet['rank'] == 1) { + $usern = ''.$userSheet['username'].''; + }elseif ($userSheet['rank'] == 2) { + $usern = ''.$userSheet['username'].''; + } + echo '
'; + echo '

'.showReadStatus($result['read']).' '.context::secureString($result['title']).'

'; + echo ''; + if ($filter == 3) { + echo '

Sent to '.$usern.'

'; + }else{ + echo '

Sent by '.$usern.'

'; + } + echo '
'; + } + } + if ($stmt->rowCount() == 0) { + echo 'You do not have any message'; + } + if ($count > 25) { + echo ''; + } + echo '
'; + }else{ + echo 'An error occurred'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/messages/newMessage.php b/www/core/func/api/messages/newMessage.php new file mode 100644 index 0000000..6890590 --- /dev/null +++ b/www/core/func/api/messages/newMessage.php @@ -0,0 +1,33 @@ +prepare("SELECT * FROM users WHERE username = :fId"); + $stmt->bindParam(':fId', $username, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'User not found'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['id'] == $GLOBALS['userTable']['id']) { + echo 'You can not send messages to yourself'; + exit; + } + if ($result['banned'] == 1) { + echo 'You can not send messages to a banned user'; + exit; + } + echo '

Sending a new message to '.context::secureString($result['username']).'

'; + include_once $_SERVER['DOCUMENT_ROOT'].'/core/func/api/messages/views/newMessage.php'; + }else{ + echo 'An error occurred'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/messages/post/newMessage.php b/www/core/func/api/messages/post/newMessage.php new file mode 100644 index 0000000..3fc287a --- /dev/null +++ b/www/core/func/api/messages/post/newMessage.php @@ -0,0 +1,73 @@ + 128) { + echo 'title-too-long'; + exit; + } + + if (strlen($messageContent) < 5) { + echo 'content-too-short'; + exit; + } + + if (strlen($messageContent) > 30000) { + echo 'content-too-long'; + exit; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT lastPost, joinDate, rank FROM users WHERE id = :id"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $timeSince = round(abs(strtotime(date('Y-m-d H:i:s')) - strtotime($result['lastPost'])) / 60,2); + if ($timeSince < 0.4 and $result['rank'] == 0) { + echo 'rate-limit'; + exit; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT id, banned FROM users WHERE id = :id"); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'no-user'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['banned'] == 1) { + echo 'user-banned'; + exit; + } + + $query = "INSERT INTO messages (`recv_uid`, `sender_uid`, `title`, `content`) VALUES (:recv_uid, :sender_uid, :title, :content);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':sender_uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':recv_uid', $userID, PDO::PARAM_INT); + $stmt->bindParam(':title', $messageTitle, PDO::PARAM_STR); + $stmt->bindParam(':content', $messageContent, PDO::PARAM_STR); + $stmt->execute(); + + $query = "UPDATE `users` SET `lastPost`=NOW() WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/messages/showMessage.php b/www/core/func/api/messages/showMessage.php new file mode 100644 index 0000000..29b2803 --- /dev/null +++ b/www/core/func/api/messages/showMessage.php @@ -0,0 +1,84 @@ +prepare("SELECT * FROM messages WHERE id = :id"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'Message not found'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['recv_uid'] != $GLOBALS['userTable']['id'] && $result['sender_uid'] != $GLOBALS['userTable']['id']) { + echo 'Message not found!'; + exit; + } + if ($result['read'] == 0) { + $read = false; + }else{ + $read = true; + } + if ($read == false and $loggedIn == true) { + if ($result['recv_uid'] == $GLOBALS['userTable']['id']) { + $stmt = $GLOBALS['dbcon']->prepare("UPDATE messages SET `read` = 1 WHERE id = :id"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + } + } + + $id = $result['id']; + if ($result['recv_uid'] == $GLOBALS['userTable']['id']) { + $userSheet = context::getUserSheetByID($result['sender_uid']); + }else{ + $userSheet = context::getUserSheetByID($result['recv_uid']); + } + echo ''; + echo '

'.context::secureString($result['title']).'

'; + echo ''; + if ($userSheet['rank'] == 0) { + $usern = $userSheet['username']; + }elseif ($userSheet['rank'] == 1) { + $usern = ''.$userSheet['username'].''; + }elseif ($userSheet['rank'] == 2) { + $usern = ''.$userSheet['username'].''; + } + echo '
+
'.context::getOnline($userSheet).''.$usern.'
+
'; + if ($userSheet['rank'] == 1) { + echo '

Administrator

'; + } + if ($userSheet['rank'] == 2) { + echo '

Moderator

'; + } + echo 'Posts: '.$userSheet['posts'].'
+ Joined: '.date('M j Y', strtotime($userSheet['joinDate'])).' +
'; + $content = strip_tags($result['content']); + $content = context::secureString($content); + if ($userSheet['rank'] > 0) { + $content = preg_replace("/\s*[a-zA-Z\/\/:\.]*youtube.com\/watch\?v=([a-zA-Z0-9\-_]+)([a-zA-Z0-9\/\*\-\_\?\&\;\%\=\.]*)/i","

", $content); + $content = preg_replace("/https?:\/\/[^ ]+?(?:\.jpg|\.jpeg|\.png|\.gif)/",'

', $content); + } + echo '
+ Sent on: '.date('M j Y g:i A', strtotime($result['date'])).'
+ '.nl2br($content).' +
'; + }else{ + echo 'An error occurred'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/messages/views/newMessage.php b/www/core/func/api/messages/views/newMessage.php new file mode 100644 index 0000000..99f2397 --- /dev/null +++ b/www/core/func/api/messages/views/newMessage.php @@ -0,0 +1,9 @@ + +
+ + + \ No newline at end of file diff --git a/www/core/func/api/profile/getInventory.php b/www/core/func/api/profile/getInventory.php new file mode 100644 index 0000000..ed1c209 --- /dev/null +++ b/www/core/func/api/profile/getInventory.php @@ -0,0 +1,114 @@ +prepare("SELECT catalogid FROM ownedItems WHERE type = :type AND uid = :uid AND deleted=0 ORDER BY id DESC LIMIT 7 OFFSET :offset;"); + if (!$GLOBALS['loggedIn']) + $stmt = $GLOBALS['dbcon']->prepare("SELECT catalogid FROM ownedItems WHERE type = :type AND uid = :uid AND deleted=0 AND rbxasset=0 ORDER BY id DESC LIMIT 7 OFFSET :offset;"); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->bindParam(':uid', $userId, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'No items found.'; + } + $count = 0; + foreach($stmt as $resultOwned) { + $count++; + if ($count < 7) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT deleted, name, type, datafile, assetid, id, fileHash, imgTime, rbxasset FROM catalog WHERE id = :id AND type = :type"); + $stmt->bindParam(':id', $resultOwned['catalogid'], PDO::PARAM_INT); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result['deleted'] == 0 && $result['type'] == $type) { + $itemName = $result['name']; + if (strlen($itemName) > 16) { + $itemName = substr($itemName, 0, 13) . '...'; + } + + echo '
'.htmlentities($itemName, ENT_QUOTES, "UTF-8").'
'; + echo ''; + echo '
Details'; + echo '
'; + } + } + } + echo '
'; + if ($page > 0) { + echo '« Previous'; + } + if ($count > 6) { + echo 'Next »'; + } + if ($count == 0 and $page > 0) { + exit; + } + echo '
'; +?> \ No newline at end of file diff --git a/www/core/func/api/settings/get/twoStep.php b/www/core/func/api/settings/get/twoStep.php new file mode 100644 index 0000000..d17c17a --- /dev/null +++ b/www/core/func/api/settings/get/twoStep.php @@ -0,0 +1,27 @@ +Click the button below to activate two step authentication. You will be asked to test your key before it will be fully enabled.

+ '; + }else{ + $gAuth = new GoogleAuthenticator(); + if ($GLOBALS['userTable']['2faEnabled'] == 0 and $GLOBALS['userTable']['2faInit'] == 1) { + echo '

Your secret key is '.$GLOBALS['userTable']['authKey'].'

'; + echo '

You can also use the QR code to add your secret key automatically.

'; + echo '

'; + echo '

Because you have not yet verified if this works, you will not be asked for a code the next time you login. Please finish the setup.

'; + echo ''; + echo ''; + }else{ + echo '

Your secret key is '.$GLOBALS['userTable']['authKey'].'

'; + echo '

You can also use the QR code to add your secret key automatically.

'; + echo '
'; + echo ''; + } + } +?> \ No newline at end of file diff --git a/www/core/func/api/settings/post/changeEmail.php b/www/core/func/api/settings/post/changeEmail.php new file mode 100644 index 0000000..d19b727 --- /dev/null +++ b/www/core/func/api/settings/post/changeEmail.php @@ -0,0 +1,77 @@ +prepare("SELECT email FROM users WHERE email = :email;"); + $stmt->bindParam(':email', $newEmail, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() > 0) die("email-in-use"); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET email = :email WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':email', $newEmail, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET lastUpload = NOW() WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET emailverified = 0 WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET emailcodeTime = NULL WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> diff --git a/www/core/func/api/settings/post/changePassword.php b/www/core/func/api/settings/post/changePassword.php new file mode 100644 index 0000000..e83628f --- /dev/null +++ b/www/core/func/api/settings/post/changePassword.php @@ -0,0 +1,76 @@ + 40) { + echo 'password-too-long'; + exit; + } + + $auth_hash = crypt($currentPassword, $GLOBALS['userTable']['password_salt']); + if ($auth_hash != $GLOBALS['userTable']['password_hash']) { + echo 'wrong-password'; + exit; + } + + $salt = '$2a$07$'.uniqid(mt_rand(), true).'$'; + $hash = crypt($newPassword1, $salt); + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET password_salt = :salt WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':salt', $salt, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET password_hash = :hash WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':hash', $hash, PDO::PARAM_STR); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET passwordVersion = 2 WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET password = NULL WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET passwordChangeIP = :ip WHERE id = :id;"); + $IP = auth::getIP(); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET passwordChangeDate = NOW() WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("DELETE FROM sessions WHERE userId = :userId"); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + echo 'success'; + }else{ + echo 'error'; + } +?> diff --git a/www/core/func/api/settings/post/disableTwo.php b/www/core/func/api/settings/post/disableTwo.php new file mode 100644 index 0000000..f022fe2 --- /dev/null +++ b/www/core/func/api/settings/post/disableTwo.php @@ -0,0 +1,34 @@ +prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `users` SET `2faInit`=0 WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'staff-block'; + exit; + } + } + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/settings/post/enableTwo.php b/www/core/func/api/settings/post/enableTwo.php new file mode 100644 index 0000000..a02f464 --- /dev/null +++ b/www/core/func/api/settings/post/enableTwo.php @@ -0,0 +1,33 @@ +prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + include_once $_SERVER['DOCUMENT_ROOT'].'/core/func/libs/google/GoogleAuthenticator.php'; + $gAuth = new GoogleAuthenticator(); + $code = $gAuth->generateSecret(); + + $query = "UPDATE users SET `authKey`=:code WHERE `id`=:uid;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':code', $code, PDO::PARAM_STR); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + } + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/settings/post/enableTwoFinal.php b/www/core/func/api/settings/post/enableTwoFinal.php new file mode 100644 index 0000000..0616ff3 --- /dev/null +++ b/www/core/func/api/settings/post/enableTwoFinal.php @@ -0,0 +1,47 @@ +checkCode($GLOBALS['userTable']['authKey'], $finalCode)) { + echo 'wrong-code'; + exit; + } + + $query = "UPDATE `users` SET `2faInit`=1 WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `users` SET `2faEnabled`=1 WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $query = "UPDATE `sessions` SET `factorFinish`=1 WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['sessionTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + echo 'success'; + } + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/settings/post/updateAbout.php b/www/core/func/api/settings/post/updateAbout.php new file mode 100644 index 0000000..53896ed --- /dev/null +++ b/www/core/func/api/settings/post/updateAbout.php @@ -0,0 +1,42 @@ + 256) die("error"); + + // Apparently, we'll need filters here too since users can't just shut their mouths. + $badwords = array("fucking", "gay", "rape", "incest", "beastiality", "cum", "maggot", "bullshit", "fuck", "penis", + "dick", "vagina", "vag", "faggot", "fag", "nigger", "asshole", "shit", "bitch", "anal", "stfu", + "cunt", "pussy", "hump", "meatspin", "redtube", "porn", "kys", "xvideos", "hentai", "gangbang", "milf", + "n*", "nobelium", "whore", "wtf", "horny", "raping", "s3x", "boob", "nigga", "nlgga", "gt2008", + "cock", "dicc", "idiot", "nibba", "nibber", "nude", "kesner", "brickopolis", "nobe", "diemauer", "nuts", + "rhodum", "otorium", ".ga", ".cf", ".gg", ".ml", "brickopolis", "mercury", "polygon", "pizzaboxer", + "calvy", "tadah", "alphaland", "finalb"); + + $badwords2 = array("sex", "porn"); + $contentCheck = preg_replace('!\s+!', ' ', $aboutContent); + $contentCheck = strip_tags($contentCheck); + $contentCheck = preg_replace("/&#?[a-z0-9]+;/i","", $contentCheck); + $contentCheck = preg_replace('!\s+!', ' ', $contentCheck); + $contentCheck = strtolower(preg_replace('|[[\/\!]*?[^\[\]]*?]|si', '', $contentCheck)); + $contentCheck = preg_replace('/\s+/', '', $contentCheck); + + if (context::contains($contentCheck, $badwords2)) die("filtered"); + + // Check without special characters removed + if (context::contains($contentCheck, $badwords)) die("filtered"); + + if(!preg_match("/^.+$/i", $aboutContent) == 1 && strlen($aboutContent) != 0) die("filtered"); + + $query = "UPDATE `users` SET `about`=:about WHERE `id`=:id;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':about', $aboutContent, PDO::PARAM_STR); + $stmt->execute(); + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/settings/post/updateTheme.php b/www/core/func/api/settings/post/updateTheme.php new file mode 100644 index 0000000..555a302 --- /dev/null +++ b/www/core/func/api/settings/post/updateTheme.php @@ -0,0 +1,19 @@ +prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':theme', $theme, PDO::PARAM_INT); + $stmt->execute(); + + echo 'success'; + }else{ + echo 'error'; + } +?> \ No newline at end of file diff --git a/www/core/func/api/users/getOnline.php b/www/core/func/api/users/getOnline.php new file mode 100644 index 0000000..ff2a9b8 --- /dev/null +++ b/www/core/func/api/users/getOnline.php @@ -0,0 +1,36 @@ +
+
Users currently online
+
+prepare("SELECT lastSeen, id, username, inGame, rank FROM users WHERE banned = 0 AND hideStatus = 0 ORDER BY id ASC;"); + $stmt->execute(); + $count = 0; + foreach($stmt as $result) { + $from_time = strtotime($result['lastSeen']); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince < 5){ + $count++; + if ($result['inGame'] == 1) { + if ($result['rank'] == 0) { + echo ''.$result['username'].' '; + }else{ + echo ''.$result['username'].' '; + } + }elseif ($result['rank'] > 0) { + echo ''.$result['username'].' '; + }else{ + echo ''.$result['username'].' '; + } + } + } + if ($count == 0) { + echo 'There are no users online at this moment.'; + } + echo ''; +?> +
+
\ No newline at end of file diff --git a/www/core/func/api/users/searchUser.php b/www/core/func/api/users/searchUser.php new file mode 100644 index 0000000..8393e0e --- /dev/null +++ b/www/core/func/api/users/searchUser.php @@ -0,0 +1,71 @@ +prepare("SELECT * FROM users WHERE username LIKE :term AND banned = 0 ORDER BY lastSeen DESC LIMIT 11 OFFSET :offset;"); + }else{ + $stmt = $dbcon->prepare("SELECT * FROM users WHERE username LIKE :term AND banned = 0 ORDER BY username ASC LIMIT 11 OFFSET :offset;"); + } + $stmt->bindParam(':term', $searchTermSQL, PDO::PARAM_STR); + $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo '
Nothing found.
'; + } + $count = 0; + foreach($stmt as $result) { + $count++; + if ($count < 11) { + echo '
'; + echo '
'; + echo '
'; + echo ''; + echo '
'; + echo '
'; + if ($result['lastSeen'] == NULL) { + $lastSeen = "Never"; + }else{ + $lastSeen = date('M j Y g:i A', strtotime($result['lastSeen'])); + } + + if ($result['about'] == NULL) { + $about = "This user has not configured anything to display here"; + }else{ + $about = ''.context::secureString($result['about']).''; + } + + $usern = ''; + if ($result['banned'] == 1) { + $usern = ''.$result['username'].''; + }elseif ($result['rank'] == 0) { + $usern = $result['username']; + }elseif ($result['rank'] == 1) { + $usern = ''.$result['username'].''; + }elseif ($result['rank'] == 2) { + $usern = ''.$result['username'].''; + } + + echo '

'.context::getOnline($result).' '.$usern.'

Last seen: '.$lastSeen.'
About:

'.$about.'

'; + echo '
'; + } + } + if ($count > 10) { + echo ''; + } +?> \ No newline at end of file diff --git a/www/core/func/auth/main.php b/www/core/func/auth/main.php new file mode 100644 index 0000000..0617a45 --- /dev/null +++ b/www/core/func/auth/main.php @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/www/core/func/auth/sessionHandler.php b/www/core/func/auth/sessionHandler.php new file mode 100644 index 0000000..4bae064 --- /dev/null +++ b/www/core/func/auth/sessionHandler.php @@ -0,0 +1,123 @@ +prepare('SELECT lastUsed, id, csrfToken, factorFinish, location, userId, useragent FROM sessions WHERE userId = :userId AND sessionId = :sessionId LIMIT 1;'); + $stmt->bindParam(':userId', $_COOKIE['auth_uid'], PDO::PARAM_INT); + $stmt->bindParam(':sessionId', $_COOKIE['a_id'], PDO::PARAM_STR); + $stmt->execute(); + $resultSession = $stmt->fetch(PDO::FETCH_ASSOC); + $removeSession = false; + $sesexpired = false; + if ($stmt->rowCount() > 0) { + $from_time = strtotime($resultSession['lastUsed']); + $sessionId = $resultSession['id']; + $to_time = strtotime(context::getCurrentTime()); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 1440 || $removeSession == true) { + $sesexpired = true; + $stmt = $GLOBALS['dbcon']->prepare('DELETE FROM sessions WHERE id=:id;'); + $stmt->bindParam(':id', $sessionId, PDO::PARAM_INT); + $stmt->execute(); + } + } + + + if ($stmt->rowCount() > 0 && $sesexpired == false) { + $GLOBALS['loggedIn'] = true; + $query = "SELECT * FROM users WHERE id = :id LIMIT 1;"; + $stmt = $dbcon->prepare($query); + $stmt->bindParam(':id', $_COOKIE['auth_uid'], PDO::PARAM_STR); + $stmt->execute(); + $GLOBALS['userTable'] = $stmt->fetch(PDO::FETCH_ASSOC); + $GLOBALS['sessionTable'] = $resultSession; + $GLOBALS['csrf_token'] = $resultSession['csrfToken']; + + $IP = auth::getIP(); + if ($GLOBALS['userTable']['lastIP'] != $IP) { + $stmt = $dbcon->prepare("UPDATE users SET lastIP = :ip WHERE username = :user;"); + $stmt->bindParam(':user', $GLOBALS['userTable']['username'], PDO::PARAM_STR); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + } + + if ($GLOBALS['userTable']['banned'] == 1 && strpos($_SERVER['SCRIPT_NAME'], "banned.php") == false && strpos($_SERVER['SCRIPT_NAME'], "appeal.php") == false) { + if (!isset($GLOBALS['bypassRedirect'])) { + header("Location: /account/suspended"); + exit; + } + } + + if (security::getUserEmailVerified() == false && $GLOBALS['userTable']['banned'] == 0) { + $timeSince = round(abs(strtotime(context::getCurrentTime()) - strtotime($GLOBALS['userTable']['emailcodeTime'])) / 60,2); + if ($timeSince > 15) { + security::sendEmailVerificationMessage(); + } + if (strpos($_SERVER['SCRIPT_NAME'], "verifyEmail.php") == false) { + if (!isset($GLOBALS['bypassRedirect'])) { + header("Location: /account/verification/email"); + exit; + } + } + } + + if ($GLOBALS['sessionTable']['factorFinish'] == 0 && $GLOBALS['userTable']['banned'] == 0 && $GLOBALS['userTable']['2faEnabled'] == 1 && security::getUserEmailVerified() == true && strpos($_SERVER['SCRIPT_NAME'], "twostepauth.php") == false) { + if (!isset($GLOBALS['bypassRedirect'])) { + header("Location: /account/verification/twostepauth"); + exit; + } + } + + $from_time = strtotime($GLOBALS['userTable']['lastAward']); + $to_time = strtotime(context::getCurrentTime()); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 1440) { + $newCoins = $GLOBALS['userTable']['coins']+15; + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET coins = :coins WHERE id = :user;"); + $stmt->bindParam(':coins', $newCoins, PDO::PARAM_INT); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET lastAward = NOW() WHERE id = :user;"); + $stmt->bindParam(':user', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + $from_time = strtotime($GLOBALS['sessionTable']['lastUsed']); + $to_time = strtotime(context::getCurrentTime()); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 3) { + $stmt = $GLOBALS['dbcon']->prepare("UPDATE sessions SET lastUsed = NOW() WHERE id = :sid;"); + $stmt->bindParam(':sid', $GLOBALS['sessionTable']['id'], PDO::PARAM_STR); + $stmt->execute(); + } + + $from_time = strtotime($GLOBALS['userTable']['lastSeen']); + $to_time = strtotime(context::getCurrentTime()); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 3) { + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET lastSeen = NOW() WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + if ($GLOBALS['userTable']['inGame'] == 1 and !isset($GLOBALS['ignoreGame'])) { + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET inGame = 0 WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + } + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET lastIP = :ip WHERE id = :id;"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':ip', $IP, PDO::PARAM_STR); + $stmt->execute(); + } + + if ($GLOBALS['loggedIn'] == false) { + $GLOBALS['csrf_token'] = sha1(auth::getIP()); + if (isset($_COOKIE['auth_uid']) || isset($_COOKIE['a_id'])) { + setcookie('auth_uid', "", time() - 3600); + setcookie('a_id', "", time() - 3600); + } + } + } +?> diff --git a/www/core/func/character/colors.php b/www/core/func/character/colors.php new file mode 100644 index 0000000..553d485 --- /dev/null +++ b/www/core/func/character/colors.php @@ -0,0 +1,556 @@ + + + +prepare("SELECT * FROM characterColors WHERE uid=:id AND type='head';"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $head = $stmt->fetch(PDO::FETCH_ASSOC); + $headColor = true; + if ($stmt->rowCount() == 0) { + $headColor = false; + } + if ($headColor == false) { + $head_hex = "#F2F3F3"; + }else{ + $h_color_int = $head['color']; + if ($h_color_int == 1) { + $head_hex = "#F2F3F3"; + }elseif ($h_color_int == 208) { + $head_hex = "#E5E4DF"; + }elseif ($h_color_int == 194) { + $head_hex = "#A3A2A5"; + }elseif ($h_color_int == 199) { + $head_hex = "#635F62"; + }elseif ($h_color_int == 26) { + $head_hex = "#1B2A35"; + }elseif ($h_color_int == 21) { + $head_hex = "#C4281C"; + }elseif ($h_color_int == 24) { + $head_hex = "#F5CD30"; + }elseif ($h_color_int == 226) { + $head_hex = "#FDEA8D"; + }elseif ($h_color_int == 23) { + $head_hex = "#0D69AC"; + }elseif ($h_color_int == 107) { + $head_hex = "#008F9C"; + }elseif ($h_color_int == 102) { + $head_hex = "#6E99CA"; + }elseif ($h_color_int == 11) { + $head_hex = "#80BBDB"; + }elseif ($h_color_int == 45) { + $head_hex = "#B4D2E4"; + }elseif ($h_color_int == 135) { + $head_hex = "#74869D"; + }elseif ($h_color_int == 105) { + $head_hex = "#E29B40"; + }elseif ($h_color_int == 141) { + $head_hex = "#27462D"; + }elseif ($h_color_int == 37) { + $head_hex = "#4B974B"; + }elseif ($h_color_int == 119) { + $head_hex = "#A4BD47"; + }elseif ($h_color_int == 29) { + $head_hex = "#A1C48C"; + }elseif ($h_color_int == 151) { + $head_hex = "#789082"; + }elseif ($h_color_int == 38) { + $head_hex = "#A05F35"; + }elseif ($h_color_int == 192) { + $head_hex = "#694028"; + }elseif ($h_color_int == 104) { + $head_hex = "#6B327C"; + }elseif ($h_color_int == 9) { + $head_hex = "#E8BAC8"; + }elseif ($h_color_int == 101) { + $head_hex = "#DA867A"; + }elseif ($h_color_int == 5) { + $head_hex = "#D7C59A"; + }elseif ($h_color_int == 153) { + $head_hex = "#957977"; + }elseif ($h_color_int == 217) { + $head_hex = "#7C5C46"; + }elseif ($h_color_int == 18) { + $head_hex = "#CC8E69"; + }elseif ($h_color_int == 125) { + $head_hex = "#EAB892"; + }else{ + $head_hex = "#F2F3F3"; + } + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='torso';"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $torso = $stmt->fetch(PDO::FETCH_ASSOC); + $torsoColor = true; + if ($stmt->rowCount() == 0) { + $torsoColor = false; + } + if ($torsoColor == false) { + $torso_hex = "#1B2A35"; + }else{ + $t_color_int = $torso['color']; + if ($t_color_int == 1) { + $torso_hex = "#F2F3F3"; + }elseif ($t_color_int == 208) { + $torso_hex = "#E5E4DF"; + }elseif ($t_color_int == 194) { + $torso_hex = "#A3A2A5"; + }elseif ($t_color_int == 199) { + $torso_hex = "#635F62"; + }elseif ($t_color_int == 26) { + $torso_hex = "#1B2A35"; + }elseif ($t_color_int == 21) { + $torso_hex = "#C4281C"; + }elseif ($t_color_int == 24) { + $torso_hex = "#F5CD30"; + }elseif ($t_color_int == 226) { + $torso_hex = "#FDEA8D"; + }elseif ($t_color_int == 23) { + $torso_hex = "#0D69AC"; + }elseif ($t_color_int == 107) { + $torso_hex = "#008F9C"; + }elseif ($t_color_int == 102) { + $torso_hex = "#6E99CA"; + }elseif ($t_color_int == 11) { + $torso_hex = "#80BBDB"; + }elseif ($t_color_int == 45) { + $torso_hex = "#B4D2E4"; + }elseif ($t_color_int == 135) { + $torso_hex = "#74869D"; + }elseif ($t_color_int == 105) { + $torso_hex = "#E29B40"; + }elseif ($t_color_int == 141) { + $torso_hex = "#27462D"; + }elseif ($t_color_int == 37) { + $torso_hex = "#4B974B"; + }elseif ($t_color_int == 119) { + $torso_hex = "#A4BD47"; + }elseif ($t_color_int == 29) { + $torso_hex = "#A1C48C"; + }elseif ($t_color_int == 151) { + $torso_hex = "#789082"; + }elseif ($t_color_int == 38) { + $torso_hex = "#A05F35"; + }elseif ($t_color_int == 192) { + $torso_hex = "#694028"; + }elseif ($t_color_int == 104) { + $torso_hex = "#6B327C"; + }elseif ($t_color_int == 9) { + $torso_hex = "#E8BAC8"; + }elseif ($t_color_int == 101) { + $torso_hex = "#DA867A"; + }elseif ($t_color_int == 5) { + $torso_hex = "#D7C59A"; + }elseif ($t_color_int == 153) { + $torso_hex = "#957977"; + }elseif ($t_color_int == 217) { + $torso_hex = "#7C5C46"; + }elseif ($t_color_int == 18) { + $torso_hex = "#CC8E69"; + }elseif ($t_color_int == 125) { + $torso_hex = "#EAB892"; + }else{ + $torso_hex = "#1B2A35"; + } + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='rightarm';"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $rightarm = $stmt->fetch(PDO::FETCH_ASSOC); + $rightarmColor = true; + if ($stmt->rowCount() == 0) { + $rightarmColor = false; + } + if ($rightarmColor == false) { + $rarm_hex = "#F2F3F3"; + }else{ + $rarm_color_int = $rightarm['color']; + if ($rarm_color_int == 1) { + $rarm_hex = "#F2F3F3"; + }elseif ($rarm_color_int == 208) { + $rarm_hex = "#E5E4DF"; + }elseif ($rarm_color_int == 194) { + $rarm_hex = "#A3A2A5"; + }elseif ($rarm_color_int == 199) { + $rarm_hex = "#635F62"; + }elseif ($rarm_color_int == 26) { + $rarm_hex = "#1B2A35"; + }elseif ($rarm_color_int == 21) { + $rarm_hex = "#C4281C"; + }elseif ($rarm_color_int == 24) { + $rarm_hex = "#F5CD30"; + }elseif ($rarm_color_int == 226) { + $rarm_hex = "#FDEA8D"; + }elseif ($rarm_color_int == 23) { + $rarm_hex = "#0D69AC"; + }elseif ($rarm_color_int == 107) { + $rarm_hex = "#008F9C"; + }elseif ($rarm_color_int == 102) { + $rarm_hex = "#6E99CA"; + }elseif ($rarm_color_int == 11) { + $rarm_hex = "#80BBDB"; + }elseif ($rarm_color_int == 45) { + $rarm_hex = "#B4D2E4"; + }elseif ($rarm_color_int == 135) { + $rarm_hex = "#74869D"; + }elseif ($rarm_color_int == 105) { + $rarm_hex = "#E29B40"; + }elseif ($rarm_color_int == 141) { + $rarm_hex = "#27462D"; + }elseif ($rarm_color_int == 37) { + $rarm_hex = "#4B974B"; + }elseif ($rarm_color_int == 119) { + $rarm_hex = "#A4BD47"; + }elseif ($rarm_color_int == 29) { + $rarm_hex = "#A1C48C"; + }elseif ($rarm_color_int == 151) { + $rarm_hex = "#789082"; + }elseif ($rarm_color_int == 38) { + $rarm_hex = "#A05F35"; + }elseif ($rarm_color_int == 192) { + $rarm_hex = "#694028"; + }elseif ($rarm_color_int == 104) { + $rarm_hex = "#6B327C"; + }elseif ($rarm_color_int == 9) { + $rarm_hex = "#E8BAC8"; + }elseif ($rarm_color_int == 101) { + $rarm_hex = "#DA867A"; + }elseif ($rarm_color_int == 5) { + $rarm_hex = "#D7C59A"; + }elseif ($rarm_color_int == 153) { + $rarm_hex = "#957977"; + }elseif ($rarm_color_int == 217) { + $rarm_hex = "#7C5C46"; + }elseif ($rarm_color_int == 18) { + $rarm_hex = "#CC8E69"; + }elseif ($rarm_color_int == 125) { + $rarm_hex = "#EAB892"; + }else{ + $rarm_hex = "#F2F3F3"; + } + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='rightleg';"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $rightleg = $stmt->fetch(PDO::FETCH_ASSOC); + $rightlegColor = true; + if ($stmt->rowCount() == 0) { + $rightlegColor = false; + } + if ($rightlegColor == false) { + $rl_hex = "#C4281C"; + }else{ + $rl_color_int = $rightleg['color']; + if ($rl_color_int == 1) { + $rl_hex = "#F2F3F3"; + }elseif ($rl_color_int == 208) { + $rl_hex = "#E5E4DF"; + }elseif ($rl_color_int == 194) { + $rl_hex = "#A3A2A5"; + }elseif ($rl_color_int == 199) { + $rl_hex = "#635F62"; + }elseif ($rl_color_int == 26) { + $rl_hex = "#1B2A35"; + }elseif ($rl_color_int == 21) { + $rl_hex = "#C4281C"; + }elseif ($rl_color_int == 24) { + $rl_hex = "#F5CD30"; + }elseif ($rl_color_int == 226) { + $rl_hex = "#FDEA8D"; + }elseif ($rl_color_int == 23) { + $rl_hex = "#0D69AC"; + }elseif ($rl_color_int == 107) { + $rl_hex = "#008F9C"; + }elseif ($rl_color_int == 102) { + $rl_hex = "#6E99CA"; + }elseif ($rl_color_int == 11) { + $rl_hex = "#80BBDB"; + }elseif ($rl_color_int == 45) { + $rl_hex = "#B4D2E4"; + }elseif ($rl_color_int == 135) { + $rl_hex = "#74869D"; + }elseif ($rl_color_int == 105) { + $rl_hex = "#E29B40"; + }elseif ($rl_color_int == 141) { + $rl_hex = "#27462D"; + }elseif ($rl_color_int == 37) { + $rl_hex = "#4B974B"; + }elseif ($rl_color_int == 119) { + $rl_hex = "#A4BD47"; + }elseif ($rl_color_int == 29) { + $rl_hex = "#A1C48C"; + }elseif ($rl_color_int == 151) { + $rl_hex = "#789082"; + }elseif ($rl_color_int == 38) { + $rl_hex = "#A05F35"; + }elseif ($rl_color_int == 192) { + $rl_hex = "#694028"; + }elseif ($rl_color_int == 104) { + $rl_hex = "#6B327C"; + }elseif ($rl_color_int == 9) { + $rl_hex = "#E8BAC8"; + }elseif ($rl_color_int == 101) { + $rl_hex = "#DA867A"; + }elseif ($rl_color_int == 5) { + $rl_hex = "#D7C59A"; + }elseif ($rl_color_int == 153) { + $rl_hex = "#957977"; + }elseif ($rl_color_int == 217) { + $rl_hex = "#7C5C46"; + }elseif ($rl_color_int == 18) { + $rl_hex = "#CC8E69"; + }elseif ($rl_color_int == 125) { + $rl_hex = "#EAB892"; + }else{ + $rl_hex = "#C4281C"; + } + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='leftarm';"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $leftarm = $stmt->fetch(PDO::FETCH_ASSOC); + $leftarmColor = true; + if ($stmt->rowCount() == 0) { + $leftarmColor = false; + } + if ($leftarmColor == false) { + $la_hex = "#F2F3F3"; + }else{ + $la_color_int = $leftarm['color']; + if ($la_color_int == 1) { + $la_hex = "#F2F3F3"; + }elseif ($la_color_int == 208) { + $la_hex = "#E5E4DF"; + }elseif ($la_color_int == 194) { + $la_hex = "#A3A2A5"; + }elseif ($la_color_int == 199) { + $la_hex = "#635F62"; + }elseif ($la_color_int == 26) { + $la_hex = "#1B2A35"; + }elseif ($la_color_int == 21) { + $la_hex = "#C4281C"; + }elseif ($la_color_int == 24) { + $la_hex = "#F5CD30"; + }elseif ($la_color_int == 226) { + $la_hex = "#FDEA8D"; + }elseif ($la_color_int == 23) { + $la_hex = "#0D69AC"; + }elseif ($la_color_int == 107) { + $la_hex = "#008F9C"; + }elseif ($la_color_int == 102) { + $la_hex = "#6E99CA"; + }elseif ($la_color_int == 11) { + $la_hex = "#80BBDB"; + }elseif ($la_color_int == 45) { + $la_hex = "#B4D2E4"; + }elseif ($la_color_int == 135) { + $la_hex = "#74869D"; + }elseif ($la_color_int == 105) { + $la_hex = "#E29B40"; + }elseif ($la_color_int == 141) { + $la_hex = "#27462D"; + }elseif ($la_color_int == 37) { + $la_hex = "#4B974B"; + }elseif ($la_color_int == 119) { + $la_hex = "#A4BD47"; + }elseif ($la_color_int == 29) { + $la_hex = "#A1C48C"; + }elseif ($la_color_int == 151) { + $la_hex = "#789082"; + }elseif ($la_color_int == 38) { + $la_hex = "#A05F35"; + }elseif ($la_color_int == 192) { + $la_hex = "#694028"; + }elseif ($la_color_int == 104) { + $la_hex = "#6B327C"; + }elseif ($la_color_int == 9) { + $la_hex = "#E8BAC8"; + }elseif ($la_color_int == 101) { + $la_hex = "#DA867A"; + }elseif ($la_color_int == 5) { + $la_hex = "#D7C59A"; + }elseif ($la_color_int == 153) { + $la_hex = "#957977"; + }elseif ($la_color_int == 217) { + $la_hex = "#7C5C46"; + }elseif ($la_color_int == 18) { + $la_hex = "#CC8E69"; + }elseif ($la_color_int == 125) { + $la_hex = "#EAB892"; + }else{ + $la_hex = "#F2F3F3"; + } + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM characterColors WHERE uid=:id AND type='leftleg';"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $leftleg = $stmt->fetch(PDO::FETCH_ASSOC); + $leftlegColor = true; + if ($stmt->rowCount() == 0) { + $leftlegColor = false; + } + if ($leftlegColor == false) { + $ll_hex = "#C4281C"; + }else{ + $ll_color_int = $leftleg['color']; + if ($ll_color_int == 1) { + $ll_hex = "#F2F3F3"; + }elseif ($ll_color_int == 208) { + $ll_hex = "#E5E4DF"; + }elseif ($ll_color_int == 194) { + $ll_hex = "#A3A2A5"; + }elseif ($ll_color_int == 199) { + $ll_hex = "#635F62"; + }elseif ($ll_color_int == 26) { + $ll_hex = "#1B2A35"; + }elseif ($ll_color_int == 21) { + $ll_hex = "#C4281C"; + }elseif ($ll_color_int == 24) { + $ll_hex = "#F5CD30"; + }elseif ($ll_color_int == 226) { + $ll_hex = "#FDEA8D"; + }elseif ($ll_color_int == 23) { + $ll_hex = "#0D69AC"; + }elseif ($ll_color_int == 107) { + $ll_hex = "#008F9C"; + }elseif ($ll_color_int == 102) { + $ll_hex = "#6E99CA"; + }elseif ($ll_color_int == 11) { + $ll_hex = "#80BBDB"; + }elseif ($ll_color_int == 45) { + $ll_hex = "#B4D2E4"; + }elseif ($ll_color_int == 135) { + $ll_hex = "#74869D"; + }elseif ($ll_color_int == 105) { + $ll_hex = "#E29B40"; + }elseif ($ll_color_int == 141) { + $ll_hex = "#27462D"; + }elseif ($ll_color_int == 37) { + $ll_hex = "#4B974B"; + }elseif ($ll_color_int == 119) { + $ll_hex = "#A4BD47"; + }elseif ($ll_color_int == 29) { + $ll_hex = "#A1C48C"; + }elseif ($ll_color_int == 151) { + $ll_hex = "#789082"; + }elseif ($ll_color_int == 38) { + $ll_hex = "#A05F35"; + }elseif ($ll_color_int == 192) { + $ll_hex = "#694028"; + }elseif ($ll_color_int == 104) { + $ll_hex = "#6B327C"; + }elseif ($ll_color_int == 9) { + $ll_hex = "#E8BAC8"; + }elseif ($ll_color_int == 101) { + $ll_hex = "#DA867A"; + }elseif ($ll_color_int == 5) { + $ll_hex = "#D7C59A"; + }elseif ($ll_color_int == 153) { + $ll_hex = "#957977"; + }elseif ($ll_color_int == 217) { + $ll_hex = "#7C5C46"; + }elseif ($ll_color_int == 18) { + $ll_hex = "#CC8E69"; + }elseif ($ll_color_int == 125) { + $ll_hex = "#EAB892"; + }else{ + $ll_hex = "#C4281C"; + } + } +?> +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/www/core/func/character/js/colorJs.js b/www/core/func/character/js/colorJs.js new file mode 100644 index 0000000..50c44e2 --- /dev/null +++ b/www/core/func/character/js/colorJs.js @@ -0,0 +1,584 @@ +function updateColor(code) { + $('#colordialog').modal('toggle'); + if (bodyPart == "head") { + $.get("/core/func/character/postsColor.php?updateHead=" + code, function(data){ + if (code == 1) { + $("#head").css('background', "#F2F3F3"); + } + if (code == 208) { + $("#head").css('background', "#E5E4DF"); + } + if (code == 194) { + $("#head").css('background', "#A3A2A5"); + } + if (code == 199) { + $("#head").css('background', "#635F62"); + } + if (code == 26) { + $("#head").css('background', "#1B2A35"); + } + if (code == 21) { + $("#head").css('background', "#C4281C"); + } + if (code == 24) { + $("#head").css('background', "#F5CD30"); + } + if (code == 226) { + $("#head").css('background', "#FDEA8D"); + } + if (code == 23) { + $("#head").css('background', "#0D69AC"); + } + if (code == 107) { + $("#head").css('background', "#008F9C"); + } + if (code == 102) { + $("#head").css('background', "#6E99CA"); + } + if (code == 11) { + $("#head").css('background', "#80BBDB"); + } + if (code == 45) { + $("#head").css('background', "#B4D2E4"); + } + if (code == 135) { + $("#head").css('background', "#74869D"); + } + if (code == 105) { + $("#head").css('background', "#E29B40"); + } + if (code == 141) { + $("#head").css('background', "#27462D"); + } + if (code == 37) { + $("#head").css('background', "#4B974B"); + } + if (code == 119) { + $("#head").css('background', "#A4BD47"); + } + if (code == 29) { + $("#head").css('background', "#A1C48C"); + } + if (code == 151) { + $("#head").css('background', "#789082"); + } + if (code == 38) { + $("#head").css('background', "#A05F35"); + } + if (code == 192) { + $("#head").css('background', "#694028"); + } + if (code == 104) { + $("#head").css('background', "#6B327C"); + } + if (code == 9) { + $("#head").css('background', "#E8BAC8"); + } + if (code == 101) { + $("#head").css('background', "#DA867A"); + } + if (code == 5) { + $("#head").css('background', "#D7C59A"); + } + if (code == 153) { + $("#head").css('background', "#957977"); + } + if (code == 217) { + $("#head").css('background', "#7C5C46"); + } + if (code == 18) { + $("#head").css('background', "#CC8E69"); + } + if (code == 125) { + $("#head").css('background', "#EAB892"); + } + }).done(function(){ + doGenIfName(); + });; + } + + if (bodyPart == "leftarm") { + $.get("/core/func/character/postsColor.php?updateLeftArm=" + code, function(data){ + if (code == 1) { + $("#leftarm").css('background', "#F2F3F3"); + } + if (code == 208) { + $("#leftarm").css('background', "#E5E4DF"); + } + if (code == 194) { + $("#leftarm").css('background', "#A3A2A5"); + } + if (code == 199) { + $("#leftarm").css('background', "#A3A2A5"); + } + if (code == 26) { + $("#leftarm").css('background', "#1B2A35"); + } + if (code == 21) { + $("#leftarm").css('background', "#C4281C"); + } + if (code == 24) { + $("#leftarm").css('background', "#F5CD30"); + } + if (code == 226) { + $("#leftarm").css('background', "#FDEA8D"); + } + if (code == 23) { + $("#leftarm").css('background', "#0D69AC"); + } + if (code == 107) { + $("#leftarm").css('background', "#008F9C"); + } + if (code == 102) { + $("#leftarm").css('background', "#6E99CA"); + } + if (code == 11) { + $("#leftarm").css('background', "#80BBDB"); + } + if (code == 45) { + $("#leftarm").css('background', "#B4D2E4"); + } + if (code == 135) { + $("#leftarm").css('background', "#74869D"); + } + if (code == 105) { + $("#leftarm").css('background', "#E29B40"); + } + if (code == 141) { + $("#leftarm").css('background', "#27462D"); + } + if (code == 37) { + $("#leftarm").css('background', "#4B974B"); + } + if (code == 119) { + $("#leftarm").css('background', "#A4BD47"); + } + if (code == 29) { + $("#leftarm").css('background', "#A1C48C"); + } + if (code == 151) { + $("#leftarm").css('background', "#789082"); + } + if (code == 38) { + $("#leftarm").css('background', "#A05F35"); + } + if (code == 192) { + $("#leftarm").css('background', "#694028"); + } + if (code == 104) { + $("#leftarm").css('background', "#6B327C"); + } + if (code == 9) { + $("#leftarm").css('background', "#E8BAC8"); + } + if (code == 101) { + $("#leftarm").css('background', "#DA867A"); + } + if (code == 5) { + $("#leftarm").css('background', "#D7C59A"); + } + if (code == 153) { + $("#leftarm").css('background', "#957977"); + } + if (code == 217) { + $("#leftarm").css('background', "#7C5C46"); + } + if (code == 18) { + $("#leftarm").css('background', "#CC8E69"); + } + if (code == 125) { + $("#leftarm").css('background', "#EAB892"); + } + }).done(function(){ + doGenIfName(); + });; + } + + if (bodyPart == "torso") { + $.get("/core/func/character/postsColor.php?updateTorso=" + code, function(data){ + if (code == 1) { + $("#torso").css('background', "#F2F3F3"); + } + if (code == 208) { + $("#torso").css('background', "#E5E4DF"); + } + if (code == 194) { + $("#torso").css('background', "#A3A2A5"); + } + if (code == 199) { + $("#torso").css('background', "#A3A2A5"); + } + if (code == 26) { + $("#torso").css('background', "#1B2A35"); + } + if (code == 21) { + $("#torso").css('background', "#C4281C"); + } + if (code == 24) { + $("#torso").css('background', "#F5CD30"); + } + if (code == 226) { + $("#torso").css('background', "#FDEA8D"); + } + if (code == 23) { + $("#torso").css('background', "#0D69AC"); + } + if (code == 107) { + $("#torso").css('background', "#008F9C"); + } + if (code == 102) { + $("#torso").css('background', "#6E99CA"); + } + if (code == 11) { + $("#torso").css('background', "#80BBDB"); + } + if (code == 45) { + $("#torso").css('background', "#B4D2E4"); + } + if (code == 135) { + $("#torso").css('background', "#74869D"); + } + if (code == 105) { + $("#torso").css('background', "#E29B40"); + } + if (code == 141) { + $("#torso").css('background', "#27462D"); + } + if (code == 37) { + $("#torso").css('background', "#4B974B"); + } + if (code == 119) { + $("#torso").css('background', "#A4BD47"); + } + if (code == 29) { + $("#torso").css('background', "#A1C48C"); + } + if (code == 151) { + $("#torso").css('background', "#789082"); + } + if (code == 38) { + $("#torso").css('background', "#A05F35"); + } + if (code == 192) { + $("#torso").css('background', "#694028"); + } + if (code == 104) { + $("#torso").css('background', "#6B327C"); + } + if (code == 9) { + $("#torso").css('background', "#E8BAC8"); + } + if (code == 101) { + $("#torso").css('background', "#DA867A"); + } + if (code == 5) { + $("#torso").css('background', "#D7C59A"); + } + if (code == 153) { + $("#torso").css('background', "#957977"); + } + if (code == 217) { + $("#torso").css('background', "#7C5C46"); + } + if (code == 18) { + $("#torso").css('background', "#CC8E69"); + } + if (code == 125) { + $("#torso").css('background', "#EAB892"); + } + }).done(function(){ + doGenIfName(); + });; + } + + if (bodyPart == "rightarm") { + $.get("/core/func/character/postsColor.php?updateRightArm=" + code, function(data){ + if (code == 1) { + $("#rightarm").css('background', "#F2F3F3"); + } + if (code == 208) { + $("#rightarm").css('background', "#E5E4DF"); + } + if (code == 194) { + $("#rightarm").css('background', "#A3A2A5"); + } + if (code == 199) { + $("#rightarm").css('background', "#A3A2A5"); + } + if (code == 26) { + $("#rightarm").css('background', "#1B2A35"); + } + if (code == 21) { + $("#rightarm").css('background', "#C4281C"); + } + if (code == 24) { + $("#rightarm").css('background', "#F5CD30"); + } + if (code == 226) { + $("#rightarm").css('background', "#FDEA8D"); + } + if (code == 23) { + $("#rightarm").css('background', "#0D69AC"); + } + if (code == 107) { + $("#rightarm").css('background', "#008F9C"); + } + if (code == 102) { + $("#rightarm").css('background', "#6E99CA"); + } + if (code == 11) { + $("#rightarm").css('background', "#80BBDB"); + } + if (code == 45) { + $("#rightarm").css('background', "#B4D2E4"); + } + if (code == 135) { + $("#rightarm").css('background', "#74869D"); + } + if (code == 105) { + $("#rightarm").css('background', "#E29B40"); + } + if (code == 141) { + $("#rightarm").css('background', "#27462D"); + } + if (code == 37) { + $("#rightarm").css('background', "#4B974B"); + } + if (code == 119) { + $("#rightarm").css('background', "#A4BD47"); + } + if (code == 29) { + $("#rightarm").css('background', "#A1C48C"); + } + if (code == 151) { + $("#rightarm").css('background', "#789082"); + } + if (code == 38) { + $("#rightarm").css('background', "#A05F35"); + } + if (code == 192) { + $("#rightarm").css('background', "#694028"); + } + if (code == 104) { + $("#rightarm").css('background', "#6B327C"); + } + if (code == 9) { + $("#rightarm").css('background', "#E8BAC8"); + } + if (code == 101) { + $("#rightarm").css('background', "#DA867A"); + } + if (code == 5) { + $("#rightarm").css('background', "#D7C59A"); + } + if (code == 153) { + $("#rightarm").css('background', "#957977"); + } + if (code == 217) { + $("#rightarm").css('background', "#7C5C46"); + } + if (code == 18) { + $("#rightarm").css('background', "#CC8E69"); + } + if (code == 125) { + $("#rightarm").css('background', "#EAB892"); + } + }).done(function(){ + doGenIfName(); + });; + } + + if (bodyPart == "leftleg") { + $.get("/core/func/character/postsColor.php?updateLeftLeg=" + code, function(data){ + if (code == 1) { + $("#leftleg").css('background', "#F2F3F3"); + } + if (code == 208) { + $("#leftleg").css('background', "#E5E4DF"); + } + if (code == 194) { + $("#leftleg").css('background', "#A3A2A5"); + } + if (code == 199) { + $("#leftleg").css('background', "#A3A2A5"); + } + if (code == 26) { + $("#leftleg").css('background', "#1B2A35"); + } + if (code == 21) { + $("#leftleg").css('background', "#C4281C"); + } + if (code == 24) { + $("#leftleg").css('background', "#F5CD30"); + } + if (code == 226) { + $("#leftleg").css('background', "#FDEA8D"); + } + if (code == 23) { + $("#leftleg").css('background', "#0D69AC"); + } + if (code == 107) { + $("#leftleg").css('background', "#008F9C"); + } + if (code == 102) { + $("#leftleg").css('background', "#6E99CA"); + } + if (code == 11) { + $("#leftleg").css('background', "#80BBDB"); + } + if (code == 45) { + $("#leftleg").css('background', "#B4D2E4"); + } + if (code == 135) { + $("#leftleg").css('background', "#74869D"); + } + if (code == 105) { + $("#leftleg").css('background', "#E29B40"); + } + if (code == 141) { + $("#leftleg").css('background', "#27462D"); + } + if (code == 37) { + $("#leftleg").css('background', "#4B974B"); + } + if (code == 119) { + $("#leftleg").css('background', "#A4BD47"); + } + if (code == 29) { + $("#leftleg").css('background', "#A1C48C"); + } + if (code == 151) { + $("#leftleg").css('background', "#789082"); + } + if (code == 38) { + $("#leftleg").css('background', "#A05F35"); + } + if (code == 192) { + $("#leftleg").css('background', "#694028"); + } + if (code == 104) { + $("#leftleg").css('background', "#6B327C"); + } + if (code == 9) { + $("#leftleg").css('background', "#E8BAC8"); + } + if (code == 101) { + $("#leftleg").css('background', "#DA867A"); + } + if (code == 5) { + $("#leftleg").css('background', "#D7C59A"); + } + if (code == 153) { + $("#leftleg").css('background', "#957977"); + } + if (code == 217) { + $("#leftleg").css('background', "#7C5C46"); + } + if (code == 18) { + $("#leftleg").css('background', "#CC8E69"); + } + if (code == 125) { + $("#leftleg").css('background', "#EAB892"); + } + }).done(function(){ + doGenIfName(); + });; + } + + if (bodyPart == "rightleg") { + $.get("/core/func/character/postsColor.php?updateRightLeg=" + code, function(data){ + if (code == 1) { + $("#rightleg").css('background', "#F2F3F3"); + } + if (code == 208) { + $("#rightleg").css('background', "#E5E4DF"); + } + if (code == 194) { + $("#rightleg").css('background', "#A3A2A5"); + } + if (code == 199) { + $("#rightleg").css('background', "#A3A2A5"); + } + if (code == 26) { + $("#rightleg").css('background', "#1B2A35"); + } + if (code == 21) { + $("#rightleg").css('background', "#C4281C"); + } + if (code == 24) { + $("#rightleg").css('background', "#F5CD30"); + } + if (code == 226) { + $("#rightleg").css('background', "#FDEA8D"); + } + if (code == 23) { + $("#rightleg").css('background', "#0D69AC"); + } + if (code == 107) { + $("#rightleg").css('background', "#008F9C"); + } + if (code == 102) { + $("#rightleg").css('background', "#6E99CA"); + } + if (code == 11) { + $("#rightleg").css('background', "#80BBDB"); + } + if (code == 45) { + $("#rightleg").css('background', "#B4D2E4"); + } + if (code == 135) { + $("#rightleg").css('background', "#74869D"); + } + if (code == 105) { + $("#rightleg").css('background', "#E29B40"); + } + if (code == 141) { + $("#rightleg").css('background', "#27462D"); + } + if (code == 37) { + $("#rightleg").css('background', "#4B974B"); + } + if (code == 119) { + $("#rightleg").css('background', "#A4BD47"); + } + if (code == 29) { + $("#rightleg").css('background', "#A1C48C"); + } + if (code == 151) { + $("#rightleg").css('background', "#789082"); + } + if (code == 38) { + $("#rightleg").css('background', "#A05F35"); + } + if (code == 192) { + $("#rightleg").css('background', "#694028"); + } + if (code == 104) { + $("#rightleg").css('background', "#6B327C"); + } + if (code == 9) { + $("#rightleg").css('background', "#E8BAC8"); + } + if (code == 101) { + $("#rightleg").css('background', "#DA867A"); + } + if (code == 5) { + $("#rightleg").css('background', "#D7C59A"); + } + if (code == 153) { + $("#rightleg").css('background', "#957977"); + } + if (code == 217) { + $("#rightleg").css('background', "#7C5C46"); + } + if (code == 18) { + $("#rightleg").css('background', "#CC8E69"); + } + if (code == 125) { + $("#rightleg").css('background', "#EAB892"); + } + }).done(function(){ + doGenIfName(); + }); + } +} \ No newline at end of file diff --git a/www/core/func/character/postsColor.php b/www/core/func/character/postsColor.php new file mode 100644 index 0000000..0260be7 --- /dev/null +++ b/www/core/func/character/postsColor.php @@ -0,0 +1,112 @@ +prepare("SELECT * FROM characterColors WHERE uid = :uid AND type = :type"); + $stmt->bindParam(':uid', $_COOKIE['auth_uid'], PDO::PARAM_INT); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + $query = "INSERT INTO characterColors (`uid`, `color`, `type`) VALUES (:uid, :color, :type);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $_COOKIE['auth_uid'], PDO::PARAM_INT); + $stmt->bindParam(':color', $color, PDO::PARAM_INT); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->execute(); + }else{ + $query = "UPDATE `characterColors` SET `color`=:color WHERE `uid`=:uid AND `type`=:type "; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':uid', $_COOKIE['auth_uid'], PDO::PARAM_INT); + $stmt->bindParam(':color', $color, PDO::PARAM_INT); + $stmt->bindParam(':type', $type, PDO::PARAM_STR); + $stmt->execute(); + } + + $uid = $GLOBALS['userTable']['id']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id`, `charap` AS `pose` FROM `users` WHERE `id`=:uid;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $pose = $result['pose']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id` FROM `wearing` WHERE `uid`=:uid AND `type`=\"gear\";"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $hasGear = (($stmt->rowCount() > 0) ? 1 : 0); + + context::requestImage($uid, "headshot", $pose, $hasGear); + context::requestImage($uid, "character", $pose, $hasGear); + } + + if (isset($_GET['updateHead'])) { + $color = $_GET['updateHead']; + if (checkColor($color) == true) { + setColor($color, "head", $dbcon); + echo '
Updated Head Color!
'; + }else{ + echo '
This color can not be applied.
'; + } + } + + if (isset($_GET['updateTorso'])) { + $color = $_GET['updateTorso']; + if (checkColor($color) == true) { + setColor($color, "torso", $dbcon); + echo '
Updated Torso Color!
'; + }else{ + echo '
This color can not be applied.
'; + } + } + + if (isset($_GET['updateRightArm'])) { + $color = $_GET['updateRightArm']; + if (checkColor($color) == true) { + setColor($color, "rightarm", $dbcon); + echo '
Updated Right Arm Color!
'; + }else{ + echo '
This color can not be applied.
'; + } + } + + if (isset($_GET['updateRightLeg'])) { + $color = $_GET['updateRightLeg']; + if (checkColor($color) == true) { + setColor($color, "rightleg", $dbcon); + echo '
Updated Right Leg Color!
'; + }else{ + echo '
This color can not be applied.
'; + } + } + + if (isset($_GET['updateLeftArm'])) { + $color = $_GET['updateLeftArm']; + if (checkColor($color) == true) { + setColor($color, "leftarm", $dbcon); + echo '
Updated Left Arm Color!
'; + }else{ + echo '
This color can not be applied.
'; + } + } + + if (isset($_GET['updateLeftLeg'])) { + $color = $_GET['updateLeftLeg']; + if (checkColor($color) == true) { + echo '
Updated Left Leg Color!
'; + setColor($color, "leftleg", $dbcon); + }else{ + echo '
This color can not be applied.
'; + } + } +?> \ No newline at end of file diff --git a/www/core/func/config/config.php b/www/core/func/config/config.php new file mode 100644 index 0000000..8f716d1 --- /dev/null +++ b/www/core/func/config/config.php @@ -0,0 +1,12 @@ + diff --git a/www/core/func/config/main.php b/www/core/func/config/main.php new file mode 100644 index 0000000..09a7bb1 --- /dev/null +++ b/www/core/func/config/main.php @@ -0,0 +1,8 @@ + diff --git a/www/core/func/connectivity/main.php b/www/core/func/connectivity/main.php new file mode 100644 index 0000000..c0fd343 --- /dev/null +++ b/www/core/func/connectivity/main.php @@ -0,0 +1,26 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $GLOBALS['dbcon']->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + $GLOBALS['dbcon']->setAttribute(PDO::ATTR_PERSISTENT , true); + }catch (exception $e) { + echo 'We could not connect to the database. Our potatoes are working on it.'; + exit; + } + } + + public static function closeDatabaseConnection() { + if (isset($GLOBALS['dbcon'])) { + $GLOBALS['dbcon'] = null; + } + } + } +?> diff --git a/www/core/func/context.php b/www/core/func/context.php new file mode 100644 index 0000000..ee867a0 --- /dev/null +++ b/www/core/func/context.php @@ -0,0 +1,377 @@ +prepare($query); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + public static function getUserSheetByIDForum($userID) { + $query = "SELECT `id`, `username`, `rank`, `thumbnailHash`, `headshotHash` FROM `users` WHERE `id` = :id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + public static function jsonToSingle($json) { + $jsonNew = substr($json, 1); + $jsonNew = substr_replace($jsonNew, "", -1); + return $jsonNew; + } + + public static function getTimeSince($timeFrom) { + $to_time = strtotime(context::getCurrentTime()); + $from_time = strtotime($timeFrom); + return round(abs($to_time - $from_time) / 60,2); + } + + public static function IDToUsername($userID) { + $stmt = $GLOBALS['dbcon']->prepare('SELECT username FROM users WHERE id = :userId'); + $stmt->bindParam(':userId', $userID, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + return $result['username']; + } + + public static function getCDNUrl($hash) { + $st = 31; + for($i = 0; $i < 32; $i++) + { + $st ^= ord($hash[$i]); + } + + return 'https://t' . strval($st % 8) . '.gtoria.net/' . $hash; + } + + public static function getUserImage($userSheet) { + if(!isset($userSheet['thumbnailHash'])) + { + $uid = $userSheet['id']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id`, `charap` AS `pose` FROM `users` WHERE `id`=:uid;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $pose = $result['pose']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id` FROM `wearing` WHERE `uid`=:uid AND `type`=\"gear\";"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $hasGear = (($stmt->rowCount() > 0) ? 1 : 0); + + context::requestImage($uid, "character", $pose, $hasGear); + + return 'https://gtoria.net/html/img/characters/busy.png'; + } + + return context::getCDNUrl($userSheet['thumbnailHash']); + } + + public static function getUserHeadshotImage($userSheet) { + if(!isset($userSheet['headshotHash'])) + { + $uid = $userSheet['id']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id`, `charap` AS `pose` FROM `users` WHERE `id`=:uid;"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $pose = $result['pose']; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `id` FROM `wearing` WHERE `uid`=:uid AND `type`=\"gear\";"); + $stmt->bindParam(':uid', $uid, PDO::PARAM_INT); + $stmt->execute(); + $hasGear = (($stmt->rowCount() > 0) ? 1 : 0); + + context::requestImage($uid, "headshot", $pose, $hasGear); + + return 'https://gtoria.net/html/img/characters/busy.png'; + } + + return context::getCDNUrl($userSheet['headshotHash']); + } + + public static function getGroupImage($userID) { + $query = "SELECT thumbnailHash, username FROM users WHERE id = :id"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + $userSheet = $stmt->fetch(PDO::FETCH_ASSOC); + return context::getUserImage($userSheet); + } + + public static function getOnline($userSheet) { + $from_time = strtotime($userSheet['lastSeen']); + $to_time = strtotime(context::getCurrentTime()); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 5) { + return ''; + }else{ + return ''; + } + } + + public static function humanTiming ($time) { + $time = time()-$time; + $time = 86400-$time; + $time = ($time<1)? 1 : $time; + $tokens = array ( + 31536000 => 'year', + 2592000 => 'month', + 604800 => 'week', + 86400 => 'day', + 3600 => 'hour', + 60 => 'minute', + 1 => 'second' + ); + foreach ($tokens as $unit => $text) { + if ($time < $unit) continue; + $numberOfUnits = floor($time / $unit); + return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':''); + } + } + + public static function humanTimingSince($time) + { + + $time = time() - $time; // to get the time since that moment + $time = ($time<1)? 1 : $time; + $tokens = array ( + 31536000 => 'year', + 2592000 => 'month', + 604800 => 'week', + 86400 => 'day', + 3600 => 'hour', + 60 => 'minute', + 1 => 'second' + ); + + foreach ($tokens as $unit => $text) { + if ($time < $unit) continue; + $numberOfUnits = floor($time / $unit); + return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':''); + } + + } + + public static function getItemThumbnail($type, $id, $datafile, $fileHash) { + if ($type == "faces") return '/data/assets/faces/thumbnail/'.$datafile.'.png'; + if ($type == "decals") return "/data/assets/uploads/".$fileHash; + + $stmt = $GLOBALS['dbcon']->prepare('SELECT `id`, `thumbnailHash` FROM `catalog` WHERE `id`=:id'); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if(!isset($result['thumbnailHash'])) { + context::requestImage($id, $type); + + return 'https://gtoria.net/html/img/hats/busy.png'; + } + + return context::getCDNUrl($result['thumbnailHash']); + } + + public static function getItemThumbnailC($type, $id, $datafile, $fileHash, $time) { + if ($type == "faces") return '/data/assets/faces/thumbnail/'.$datafile.'.png?tick='.strtotime($time); + if ($type == "decals") return "/data/assets/uploads/".$fileHash; + + $stmt = $GLOBALS['dbcon']->prepare('SELECT `id`, `thumbnailHash` FROM `catalog` WHERE `id`=:id'); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if(!isset($result['thumbnailHash'])) { + context::requestImage($id, $type); + + return 'https://gtoria.net/html/img/hats/busy.png'; + } + + return context::getCDNUrl($result['thumbnailHash']); + } + + public static function requestImage($ID, $type, $pose = null, $hasgear = null) { + return render::render($ID, $type, $pose, $hasgear); + } + + public static function checkTopPoster($userID) { + $query = "SELECT id, posts FROM users ORDER BY posts DESC LIMIT 15"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->execute(); + foreach ($stmt as $result) { + if ($userID == $result['id']) { + echo '

Top 15 Poster

'; + } + } + } + + public static function buildFriendButton($userID) { + if ($GLOBALS['loggedIn'] == true) { + echo ''; + $query = "SELECT * FROM `friends` WHERE `userId1` = :id AND `userId2` = :sid"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + $friend = true; + }else{ + $friend = false; + } + + $query = "SELECT * FROM `friendRequests` WHERE `senduid` = :id AND `recvuid` = :sid"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':sid', $userID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + $requestSent = true; + }else{ + $requestSent = false; + } + + $query = "SELECT * FROM `friendRequests` WHERE `senduid` = :id AND `recvuid` = :sid"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->bindParam(':sid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + $requestSenta = true; + }else{ + $requestSenta = false; + } + + if ($friend == false) { + if ($requestSent == true or $requestSenta == true) { + echo ' Pending'; + }else{ + echo ' Add'; + } + }else{ + echo ' Remove'; + } + } + } + + public static function showBBcodes($text) { + // BBcode array + $find = array( + '~\[b\](.*?)\[/b\]~s', + '~\[i\](.*?)\[/i\]~s', + '~\[u\](.*?)\[/u\]~s', + '~\[quote\](.*?)\[/quote\]~s', + '~\[red\](.*?)\[/red\]~s', + '~\[blue\](.*?)\[/blue\]~s', + '~\[s\](.*?)\[/s\]~s', + '~\[code\](.*?)\[/code\]~s' + ); + // HTML tags to replace BBcode + $replace = array( + '$1', + '$1', + '$1', + '
$1',
+				'$1',
+				'$1',
+				'$1',
+				'$1'
+			);
+			// Replacing the BBcodes with corresponding HTML tags
+			return preg_replace($find,$replace,$text);
+		}
+		
+		public static function getImageRequestCount() {
+			$query = "SELECT id FROM `renders`;";
+			$stmt = $GLOBALS['dbcon']->prepare($query);
+			$stmt->execute();
+			return $stmt->rowCount();
+		}
+		
+		public static function getUserCount() {
+			$query = "SELECT COUNT(*) FROM users;";
+			$stmt = $GLOBALS['dbcon']->prepare($query);
+			$stmt->execute();
+			$result = $stmt->fetchColumn(0);
+			return $result;
+		}
+		
+		public static function getRouterCount() {
+			$query = "SELECT id FROM `serverRequests`;";
+			$stmt = $GLOBALS['dbcon']->prepare($query);
+			$stmt->execute();
+			return $stmt->rowCount();
+		}
+		
+		public static function sendDiscordMessage($message) {
+			$url = "https://canary.discord.com/api/webhooks/826812837041143859/SpKZ-evqHCKrpyELzPAiGONbxiHcJVY0kxC7v0k4yL6NheV3SAld5NX89KUJPI1g0dGH";
+			$dataArray = array('content' => $message, 
+			'username' => "Graphictoria");
+			
+			$httpOptions = array(
+				'http' => array (
+					'header' => "Graphictoria-Server",
+					'content-type' => 'multipart/form-data',
+					'method' => "POST",
+					'content' => http_build_query($dataArray)
+				)
+			);
+			
+			$context = stream_context_create($httpOptions);
+			$result = @file_get_contents($url, false, $context);
+		}
+		
+		public static function parseEmoticon($content) {
+			$content = str_replace(":afro:",'', $content);
+			$content = str_replace(":afro-1:",'', $content);
+			$content = str_replace(":agent:",'', $content);
+			$content = str_replace(":alien:",'', $content);
+			$content = str_replace(":alien-1:",'', $content);
+			$content = str_replace(":angel:",'', $content);
+			$content = str_replace(":angry:",'', $content);
+			$content = str_replace(":angry-1:",'', $content);
+			$content = str_replace(":angry-2:",'', $content);
+			$content = str_replace(":angry-3:",'', $content);
+			$content = str_replace(":angry-4:",'', $content);
+			$content = str_replace(":angry-5:",'', $content);
+			$content = str_replace(":arguing:",'', $content);
+			$content = str_replace(":arrogant:",'', $content);
+			$content = str_replace(":asian:",'', $content);
+			$content = str_replace(":asian-1:",'', $content);
+			$content = str_replace(":avatar:",'', $content);
+			$content = str_replace(":skeleton:",'', $content);
+			$content = str_replace(":superhero:",'', $content);
+			$content = str_replace(":vampire:",'', $content);
+			$content = str_replace(":zombie:",'', $content);
+			
+			return $content;
+		}
+	}
+?>
diff --git a/www/core/func/html/main.php b/www/core/func/html/main.php
new file mode 100644
index 0000000..63a8788
--- /dev/null
+++ b/www/core/func/html/main.php
@@ -0,0 +1,57 @@
+
+				
+				
+					
+				
'; + }elseif ($rand == 1) { + echo '
+ + + +
'; + } + } + + public static function buildMatched() { + echo '
+ +
'; + } + } +?> diff --git a/www/core/func/html/views/footer.php b/www/core/func/html/views/footer.php new file mode 100644 index 0000000..adb06b1 --- /dev/null +++ b/www/core/func/html/views/footer.php @@ -0,0 +1,24 @@ + +
+'; + }else{ + echo '
'; + } + + $query = "SELECT COUNT(*) FROM `users` WHERE `banned`=0;"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->execute(); + $renders = $stmt->fetchColumn(0); +?> +
Graphictoria
+

All rights belong to their respective owners

+ Privacy | Terms | DMCA | Users: +
\ No newline at end of file diff --git a/www/core/func/html/views/head.php b/www/core/func/html/views/head.php new file mode 100644 index 0000000..845af83 --- /dev/null +++ b/www/core/func/html/views/head.php @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + +'; + if ($GLOBALS['loggedIn'] && $GLOBALS['userTable']['themeChoice'] == 0) echo ''; + + if (!$GLOBALS['loggedIn']) echo ''; +?> \ No newline at end of file diff --git a/www/core/func/html/views/navigation.php b/www/core/func/html/views/navigation.php new file mode 100644 index 0000000..8be37e7 --- /dev/null +++ b/www/core/func/html/views/navigation.php @@ -0,0 +1,219 @@ + + + +prepare($query); + $stmt->execute(); + $renders = $stmt->fetchColumn(0); + + $news_on = true; + + if($renders >= 30) + { + echo '
'; + echo '
Warning - The render queue is currently '.($renders < 200 ? 'high' : 'very high').'. It may take a while for your avatar to change. ('.$renders.' renders queued)
'; + } + + if ($news_on) { + if($renders < 30) + { + echo '
'; + } + echo '
Outgoing mail is temporarily disabled.
'; + //echo '
The thumbnail system has been redone completely. If you see a blank thumbnail, it may still be rendering. If the issue persists, please send a message in the #bugs section of the Discord server.
'; + //echo '
Graphictoria is under maintenance
'; + //echo '
Note - The client is not released yet. (Join our Discord!)
'; + //echo '
Graphictoria Studio has been released early!   Download it here!
'; + //echo '
Maintenance is currently being performed on the database. The site may experience some unexpected downtime.
'; + //echo '
Heads up! More hats have been uploaded to the catalog. (client still isnt released yet)
'; + //echo '
We\'re currently experiencing some techinical difficulties. Avatar generation may be slow.
'; + //echo '
We\'re currently experiencing some techinical difficulties. The site may go down periodically.
'; + //echo '
Avatars are currently being re-rendered. It may be a while before you see your avatar changes to take effect.
'; + //echo ''; + }else{ + echo '
'; + } +?> + + + + + + + +
+
Chat
+
+
+ +
+
+
+ \ No newline at end of file diff --git a/www/core/func/html/views/navigation/loginPanel.php b/www/core/func/html/views/navigation/loginPanel.php new file mode 100644 index 0000000..3fcae72 --- /dev/null +++ b/www/core/func/html/views/navigation/loginPanel.php @@ -0,0 +1,10 @@ + +
  • + + +
  • +
  • + + +

    Fill in the fields to login

    +
  • \ No newline at end of file diff --git a/www/core/func/html/views/navigation/rightNavigation.php b/www/core/func/html/views/navigation/rightNavigation.php new file mode 100644 index 0000000..64b59cd --- /dev/null +++ b/www/core/func/html/views/navigation/rightNavigation.php @@ -0,0 +1,49 @@ + + +
  • +
  • +prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $numRequests = $stmt->rowCount(); + + if ($numRequests == 0) { + echo '
  • '; + }else{ + echo '
  • '.$numRequests.'
  • '; + } + + $query = "SELECT id FROM `messages` WHERE `recv_uid` = :id AND `read` = 0"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + $numMessages = $stmt->rowCount(); + + if ($numMessages == 0) { + echo '
  • '; + }else{ + echo '
  • '.$numMessages.'
  • '; + } +?> + +
  • style="position: relative;border:solid 1px #e7e7e7;height:50px;width:50px;height:50px;border-radius:50%;overflow: hidden;margin-top:3px;left:10px" class="img-circle">>
  • + \ No newline at end of file diff --git a/www/core/func/includes.php b/www/core/func/includes.php new file mode 100644 index 0000000..592b309 --- /dev/null +++ b/www/core/func/includes.php @@ -0,0 +1,51 @@ + \ No newline at end of file diff --git a/www/core/func/js/account/appeal.js b/www/core/func/js/account/appeal.js new file mode 100644 index 0000000..6cd15aa --- /dev/null +++ b/www/core/func/js/account/appeal.js @@ -0,0 +1,83 @@ +// xlxi wuz here +// gt's codebase is trash, so it takes a while to add features like this + +$(document).ready( + function() + { + $('#appealExplanation').on( + 'input propertychange paste', + function() + { + let textValue = $('#appealExplanation').val() + if(textValue.length <= 50) + { + if ($('#sendAppeal').is(':disabled') == false) + { + $('#sendAppeal').prop('disabled', true); + $('#explanationError').html('Your explanation must be over 50 characters. Please note that spamming characters will result in your appeal being denied.'); + } + } + else + { + if ($('#sendAppeal').is(':disabled') == true) + { + $('#sendAppeal').prop('disabled', false); + $('#explanationError').html(''); + } + } + } + ); + + $('#sendAppeal').click( + function() + { + if ($('#sendAppeal').is(':disabled') == false) + { + $('#sendAppeal').prop('disabled', true); + $('#appealExplanation').prop('disabled', true); + let csrf = $('meta[name="csrf-token"]').attr('content'); + $.post( + '/core/func/api/account/sendAppeal.php', + { + csrf: csrf, + explanation: $('#appealExplanation').val() + } + ) + .done( + function(response) + { + $('#sendAppeal').prop('disabled', false); + $('#appealExplanation').prop('disabled', false); + if (response == 'error') + { + $('#appealStatus').html('
    Network error. Try again later.
    '); + } + else if (response == 'bad-length') + { + $('#appealStatus').html('
    Please make sure your explanation is over 50 characters.
    '); + } + else if (response == 'appeal-error') + { + $('#appealStatus').html('
    Something went wrong while sending your appeal, please try again. If this error persists, contact support.
    '); + } + else if (response == 'unfinished') + { + $('#appealStatus').html('
    The appeal system isn\'t finished yet! Your appeal has not been sent.
    '); + } + else + { + window.location.reload(); + } + } + ) + .fail( + function() + { + $('#appealStatus').html('
    Network error. Try again later.
    '); + } + ); + } + } + ); + } +); \ No newline at end of file diff --git a/www/core/func/js/account/suspended.js b/www/core/func/js/account/suspended.js new file mode 100644 index 0000000..8f33ddf --- /dev/null +++ b/www/core/func/js/account/suspended.js @@ -0,0 +1,24 @@ +$(document).ready(function() { + $("#liftBan").click(function() { + if ($("#liftBan").is(":disabled") == false) { + $("#liftBan").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/account/liftBan.php', { + csrf: csrf_token + }) + .done(function(response) { + $("#liftBan").prop("disabled", false); + if (response == "error") { + $("#sStatus").html("
    Network error. Try again later.
    "); + }else if (response == "ban-lift-error") { + $("#sStatus").html("
    Your ban has not been expired yet.
    "); + }else{ + window.location = "/"; + } + }) + .fail(function() { + $("#sStatus").html("
    Network error. Try again later.
    "); + }); + } + }) +}); \ No newline at end of file diff --git a/www/core/func/js/addServer.js b/www/core/func/js/addServer.js new file mode 100644 index 0000000..8ef83d0 --- /dev/null +++ b/www/core/func/js/addServer.js @@ -0,0 +1,167 @@ +$(document).ready(function() { + $(document).on('change', '#serverType', function() { + $("#addServer02").prop("disabled", false); + $("#serverName").prop("disabled", false); + $("#serverDescription").prop("disabled", false); + $("#placeFile").prop("disabled", false); + $("#addServer01").prop("disabled", false); + $("#serverName").prop("disabled", false); + $("#serverDescription").prop("disabled", false); + $("#serverIP").prop("disabled", false); + $("#serverPort").prop("disabled", false); + $("#privacyType").prop("disabled", false); + + if ($(this).find("option:selected").attr('value') == 0) { + $("#selfHostOptions").css("display", "block"); + $("#dedicatedOptions").css("display", "none"); + }else{ + $("#selfHostOptions").css("display", "none"); + $("#dedicatedOptions").css("display", "block"); + } + }) + $("#addServer01").click(function() { + if ($("#addServer01").is(":disabled") == false) { + $("#addServer01").prop("disabled", true); + $("#serverName").prop("disabled", true); + $("#serverDescription").prop("disabled", true); + $("#serverIP").prop("disabled", true); + $("#serverPort").prop("disabled", true); + $("#privacyType").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + var serverName = $("#serverName").val(); + var serverDescription = $("#serverDescription").val(); + var serverIP = $("#serverIP").val(); + var serverPort = $("#serverPort").val(); + var privacyType = $("#privacyType").val(); + var gameVersion = $("#versionType").val(); + $.post('/core/func/api/games/post/addServer.php', { + csrf: csrf_token, + serverName: serverName, + serverDescription: serverDescription, + serverIP: serverIP, + serverPort: serverPort, + privacyType: privacyType, + gameVersion: gameVersion + }) + .done(function(response) { + $("#addServer01").prop("disabled", false); + $("#serverName").prop("disabled", false); + $("#serverDescription").prop("disabled", false); + $("#serverIP").prop("disabled", false); + $("#serverPort").prop("disabled", false); + $("#privacyType").prop("disabled", false); + + if (response == "error") { + $("#aStatus").html("
    Could not add the server because a network error has occurred.
    "); + }else if (response == "server-name-too-long") { + $("#aStatus").html("
    Your server name is too long.
    "); + }else if (response == "server-name-too-short") { + $("#aStatus").html("
    Your server name is too short.
    "); + }else if (response == "server-description-too-long") { + $("#aStatus").html("
    Your server description is too long.
    "); + }else if (response == "server-ip-too-short") { + $("#aStatus").html("
    Your server IP is too short.
    "); + }else if (response == "server-ip-too-long") { + $("#aStatus").html("
    Your server IP is too long.
    "); + }else if (response == "server-port-too-short") { + $("#aStatus").html("
    Your server port is too short.
    "); + }else if (response == "server-port-too-long") { + $("#aStatus").html("
    Your server port is too long.
    "); + }else if (response == "invalid-port") { + $("#aStatus").html("
    Your server port is invalid.
    "); + }else if (response == "invalid-ip") { + $("#aStatus").html("
    Your server IP is invalid.
    "); + }else if (response == "invalid-privacy") { + $("#aStatus").html("
    An error has occurred.
    "); + }else if (response == "invalid-version") { + $("#aStatus").html("
    Invalid server version received.
    "); + }else{ + window.location = "/games/view/"+response; + } + }) + .fail(function() { + $("#aStatus").html("
    Could not add the server because a network error has occurred.
    "); + }); + } + }) + + $(document).on('change', '#placeFile', function() { + $(".place").css("filter", "grayscale(100%)"); + }); + + $("#placeFile").click(function() { + $(".place").css("box-shadow", "none"); + }); + + $(".place").click(function() { + if ($('#placeFile')[0].files[0] == undefined) + $(this).css("box-shadow", "0 0 0 1px #75caeb"); + }); + + $("#addServer02").click(function() { + if ($("#addServer02").is(":disabled") == false) { + $("#addServer02").prop("disabled", true); + $("#serverName").prop("disabled", true); + $("#serverDescription").prop("disabled", true); + $("#placeFile").prop("disabled", true); + $("#versionTypeDedi").prop("disabled", true); + $("#privacyTypeDedi").prop("disabled", true); + + var genPlace = 0; + if ($("#place0").css("box-shadow") != "none") genPlace = 1; + + var formData = new FormData(); + formData.append('placeFile', $('#placeFile')[0].files[0]); + formData.append('serverName', $("#serverName").val()); + formData.append('serverDescription', $("#serverDescription").val()); + formData.append('versionType', $("#versionTypeDedi").val()); + formData.append('privacyType', $("#privacyTypeDedi").val()); + formData.append('csrf_token', $('meta[name="csrf-token"]').attr('content')); + formData.append('genPlace', genPlace); + $.ajax({ + type: "POST", + url : "/core/func/api/games/post/addServerDedicated.php", + data : formData, + cache: false, + contentType: false, + processData: false, + success: function(data) { + $("#addServer02").prop("disabled", false); + $("#serverName").prop("disabled", false); + $("#serverDescription").prop("disabled", false); + $("#placeFile").prop("disabled", false); + $("#versionTypeDedi").prop("disabled", false); + $("#privacyTypeDedi").prop("disabled", false); + if (data == "server-name-too-long") { + $("#aStatus").html("
    Server name is too long
    "); + }else if (data == "server-name-too-short") { + $("#aStatus").html("
    Server name is too short
    "); + }else if (data == "server-description-too-long") { + $("#aStatus").html("
    Server description is too long
    "); + }else if (data == "invalid-placefile") { + $("#aStatus").html("
    Invalid place file.
    "); + }else if (data == "rate-limit") { + $("#aStatus").html("
    You can only add a server each 5 minutes.
    "); + }else if (data == "invalid-privacy") { + $("#aStatus").html("
    Invalid privacy type received
    "); + }else if (data == "success") { + $("#aStatus").html("
    Your dedicated server will be up and running shortly.
    "); + $(".profileCard").html("

    Loading

    Please wait as we load your server

    "); + setTimeout(function(){ + $(".profileCardContainer").load("/core/func/api/games/getLatestServer.php"); + $(".profileCardContainerHead").append("

    Your server has been created

    "); + $(".profileCard").remove(); + $(".alert").remove(); + }, 10000); + }else if (data == "error") { + $("#aStatus").html("
    Please select a place
    "); + }else{ + $("#aStatus").html("
    Could not add the server because a network error has occurred.
    "); + } + },error: function() { + $("#aStatus").html("
    Could not add the server because a network error has occurred.
    "); + } + }); + } + }) +}) \ No newline at end of file diff --git a/www/core/func/js/admin/assetApproval.js b/www/core/func/js/admin/assetApproval.js new file mode 100644 index 0000000..b65373b --- /dev/null +++ b/www/core/func/js/admin/assetApproval.js @@ -0,0 +1,46 @@ +$(document).ready(function() { + console.log("Asset approval ready!"); + $("#assetContainer").load("/core/func/api/admin/get/getAssets.php"); +}); + +function approveAsset(itemID) { + $(".btn-success").prop("disabled", true); + $(".btn-danger").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/admin/post/approveAsset.php', { + csrf: csrf_token, + itemID: itemID, + }) + .done(function(response) { + console.log(response); + if (response == "success") { + $("#assetContainer").load("/core/func/api/admin/get/getAssets.php"); + }else{ + alert("Could not approve this asset because an error occurred." + response); + } + }) + .fail(function() { + alert("An error occurred while reaching the Graphictoria servers. Please contact a developer."); + }); +} + +function denyAsset(itemID) { + $(".btn-success").prop("disabled", true); + $(".btn-danger").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/admin/post/denyAsset.php', { + csrf: csrf_token, + itemID: itemID, + }) + .done(function(response) { + console.log(response); + if (response == "success") { + $("#assetContainer").load("/core/func/api/admin/get/getAssets.php"); + }else{ + alert("Could not deny this asset because an error occurred." + response); + } + }) + .fail(function() { + alert("An error occurred while reaching the Graphictoria servers. Please contact a developer."); + }); +} \ No newline at end of file diff --git a/www/core/func/js/admin/banUser.js b/www/core/func/js/admin/banUser.js new file mode 100644 index 0000000..69f3e4e --- /dev/null +++ b/www/core/func/js/admin/banUser.js @@ -0,0 +1,48 @@ +$(document).ready(function() { + $("#banUser").click(function() { + if ($("#banUser").is(":disabled") == false) { + $("#banUser").prop("disabled", true); + $("#username").prop("disabled", true); + $("#banReason").prop("disabled", true); + $("#duration").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + var username = $("#username").val(); + var banReason = $("#banReason").val(); + var duration = $("#duration").val(); + $.post('/core/func/api/admin/post/banUser.php', { + csrf: csrf_token, + username: username, + banReason: banReason, + duration: duration + }) + .done(function(response) { + $("#banUser").prop("disabled", false); + $("#username").prop("disabled", false); + $("#banReason").prop("disabled", false); + $("#duration").prop("disabled", false); + if (response == "error") { + $("#bStatus").html("
    Could not ban this user because a network error occurred.
    "); + }else if (response == "missing-info") { + $("#bStatus").html("
    You must fill in all fields to ban this user.
    "); + }else if (response == "invalid-duration") { + $("#bStatus").html("
    Invalid duration.
    "); + }else if (response == "can-not-ban-yourself") { + $("#bStatus").html("
    You can not ban yourself.
    "); + }else if (response == "reason-too-long") { + $("#bStatus").html("
    The ban reason is too long.
    "); + }else if (response == "no-user") { + $("#bStatus").html("
    The user has not been found.
    "); + }else if (response == "can-not-ban-user") { + $("#bStatus").html("
    We could not ban this user.
    "); + }else if (response == "user-already-banned") { + $("#bStatus").html("
    This user has already been banned. If you wish to unban, use the unban utility.
    "); + }else if (response == "success") { + $("#bStatus").html("
    This user has been banned.
    "); + } + }) + .fail(function() { + $("#bStatus").html("
    Could not ban this user because a network error occurred.
    "); + }); + } + }) +}) \ No newline at end of file diff --git a/www/core/func/js/admin/prunePosts.js b/www/core/func/js/admin/prunePosts.js new file mode 100644 index 0000000..6eaea54 --- /dev/null +++ b/www/core/func/js/admin/prunePosts.js @@ -0,0 +1,34 @@ +$(document).ready(function() { + $("#prunePosts").click(function() { + if ($("#prunePosts").is(":disabled") == false) { + $("#prunePosts").prop("disabled", true); + $("#username").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + var username = $("#username").val(); + $.post('/core/func/api/admin/post/prunePosts.php', { + csrf: csrf_token, + username: username + }) + .done(function(response) { + $("#prunePosts").prop("disabled", false); + $("#username").prop("disabled", false); + if (response == "missing-info") { + $("#bStatus").html("
    We are missing information from you. Please check again.
    "); + }else if (response == "can-not-prune-yourself") { + $("#bStatus").html("
    You can not prune your own posts.
    "); + }else if (response == "no-user") { + $("#bStatus").html("
    The user you are trying to prune does not exist.
    "); + }else if (response == "can-not-prune-user") { + $("#bStatus").html("
    You can not prune this user.
    "); + }else if (response == "error") { + $("#bStatus").html("
    Could not prune posts from this user because a network error occurred.
    "); + }else if (response == "success") { + $("#bStatus").html("
    Posts pruned.
    "); + } + }) + .fail(function() { + $("#bStatus").html("
    Could not prune posts from this user because a network error occurred.
    "); + }); + } + }) +}) \ No newline at end of file diff --git a/www/core/func/js/admin/rPostie.js b/www/core/func/js/admin/rPostie.js new file mode 100644 index 0000000..162060e --- /dev/null +++ b/www/core/func/js/admin/rPostie.js @@ -0,0 +1,35 @@ +$(document).ready(function() { + $("#rewardPostie").click(function() { + if ($("#rewardPostie").is(":disabled") == false) { + $("#rewardPostie").prop("disabled", true); + $("#username").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + var username = $("#username").val(); + $.post('/core/func/api/admin/post/rewardPostie.php', { + csrf: csrf_token, + username: username + }) + .done(function(response) { + console.log(response) + $("#rewardPostie").prop("disabled", false); + $("#username").prop("disabled", false); + if (response == "error") { + $("#bStatus").html("
    Could reward this user because a network error occurred.
    "); + }else if (response == "missing-info") { + $("#bStatus").html("
    We are missing information from you. Please check and try again.
    "); + }else if (response == "can-not-reward-yourself") { + $("#bStatus").html("
    You can not reward yourself!
    "); + }else if (response == "no-user") { + $("#bStatus").html("
    This user has not been found.
    "); + }else if (response == "can-not-reward-user") { + $("#bStatus").html("
    This user has already been rewarded recently. Try again later
    "); + }else if (response == "success") { + $("#bStatus").html("
    This user has recieved 10 posties!
    "); + } + }) + .fail(function() { + $("#bStatus").html("
    Could reward this user because a network error occurred.
    "); + }); + } + }) +}) \ No newline at end of file diff --git a/www/core/func/js/admin/rbxAssetUpload.js b/www/core/func/js/admin/rbxAssetUpload.js new file mode 100644 index 0000000..c0675c7 --- /dev/null +++ b/www/core/func/js/admin/rbxAssetUpload.js @@ -0,0 +1,149 @@ +// XlXi wuz here + +const downloadBase = '

    After downloading an asset, you will be prompted with controls to change the name, price, etc... This may not work with newer assets.

    \n\n'; + +$(document).ready( + function() + { + $('#downloaderPanel').html(downloadBase); + + $('#downloadButton').click( + function() + { + if ($('#downloadButton').is(':disabled') == false) + { + $('#downloadButton').prop('disabled', true); + $('#assetId').prop('disabled', true); + $('#Status').html('
    Attempting download, please wait...
    '); + + var assetId = $('#assetId').val(); + $.get('/core/func/api/admin/get/RBXAssetInformation.php', { + assetId: assetId + }) + .done(function(response) { + if(response != 'success') + { + $('#downloadButton').prop('disabled', false); + $('#assetId').prop('disabled', false); + } + + if (response == 'error') { + $('#aStatus').html('
    It\'s a number, how could you possibly mess that up?!
    '); + }else if (response == 'missing-info') { + $('#aStatus').html('
    Please enter an asset ID to use this tool.
    '); + }else if (response == 'invalid-type') { + $('#aStatus').html('
    Please enter a valid asset ID. The asset must be of type 8, 41, 42, 43, 44, 45, 46, or 47.
    '); + }else{ + $('#aStatus').html(''); + var responseJSON = JSON.parse(response); + + $('#downloaderPanel').html('

    Note : Abuse of this system WILL result in a revoke of all rights, and a demotion if you are staff

    This is the asset you\'re uploading

    Please configure the asset below this card.

    Wrong asset?

    ' + responseJSON.Name + '

    R$ ' + (responseJSON.Price && responseJSON.Price || 0) + '

    ' + responseJSON.Description + '

    Configure Asset



    '); + + $('#uploadAssetButton').click( + function() + { + if($('#uploadAssetButton').is(':disabled') == false) + { + $('#assetName').prop('disabled', true); + $('#assetDescription').prop('disabled', true); + $('#assetDataFile').prop('disabled', true); + $('#assetCurrencyType').prop('disabled', true); + $('#assetPrice').prop('disabled', true); + $('#assetOnSale').prop('disabled', true); + $('#uploadAssetButton').prop('disabled', true); + + var Name = $('#assetName').val(); + var Description = $('#assetDescription').val(); + var DataFile = $('#assetDataFile').val(); + var CurrencyType = $('#assetCurrencyType').val(); + var Price = $('#assetPrice').val(); + var IsOnSale = $('#assetOnSale').prop('checked'); + + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + + $.post('/core/func/api/admin/post/UploadRBXAsset.php', { + csrf: csrf_token, + assetId: assetId, + name: Name, + description: Description, + dataFile: DataFile, + currencyType: CurrencyType, + price: Price, + isOnSale: IsOnSale + }).done(function(response) { + + if (response == 'success') { + $('#downloaderPanel').html(''); + + var countTime = 400; + + var timer = setInterval( + function() + { + if(countTime <= 0) + { + clearInterval(timer); + $('#aStatus').html('
    Redirecting...
    '); + window.location = '/admin'; + + return; + } + + $('#aStatus').html('
    Successfully uploaded the asset. Redirecting in ' + Math.floor(countTime/100) + ' seconds...
    '); + countTime -= 10; + }, + 100 + ); + + return; + } + + $('#assetName').prop('disabled', false); + $('#assetDescription').prop('disabled', false); + $('#assetDataFile').prop('disabled', false); + $('#assetCurrencyType').prop('disabled', false); + $('#assetPrice').prop('disabled', false); + $('#assetOnSale').prop('disabled', false); + $('#uploadAssetButton').prop('disabled', false); + + if (response == 'error') { + $('#aStatus').html('
    An error occurred while attempting to upload the asset. Please try again.
    '); + }else if (response == 'missing-info') { + $('#aStatus').html('
    Make sure you fill in all of the fields.
    '); + }else if (response == 'invalid-datafile') { + $('#aStatus').html('
    Please ensure that your datafile is alphanumeric.
    '); + }else if (response == 'no-xml') { + $('#aStatus').html('
    Unable to load asset XML.
    '); + }else if (response == 'invalid-length') { + $('#aStatus').html('
    Asset names must be over 3 characters.
    '); + }else if (response == 'invalid-price') { + $('#aStatus').html('
    Please enter a valid price.
    '); + }else if (response == 'already-exists') { + $('#aStatus').html('
    This data file already exists. Try again with a different data file.
    '); + } + }).fail(function() { + $('#aStatus').html('
    A network error occurred while attempting to upload the asset.
    '); + + $('#assetName').prop('disabled', false); + $('#assetDescription').prop('disabled', false); + $('#assetDataFile').prop('disabled', false); + $('#assetCurrencyType').prop('disabled', false); + $('#assetPrice').prop('disabled', false); + $('#assetOnSale').prop('disabled', false); + $('#uploadAssetButton').prop('disabled', false); + }); + } + } + ); + } + }) + .fail(function() { + $('#aStatus').html('
    Could not download this asset because a network error occurred.
    '); + $('#downloadButton').prop('disabled', false); + $('#assetId').prop('disabled', false); + }); + } + } + ); + } +); \ No newline at end of file diff --git a/www/core/func/js/admin/uhat.js b/www/core/func/js/admin/uhat.js new file mode 100644 index 0000000..9aa0556 --- /dev/null +++ b/www/core/func/js/admin/uhat.js @@ -0,0 +1,78 @@ +$(document).ready(function() { + $("#doUpload").click(function() { + if ($("#doUpload").is(":disabled") == false) { + $("#doUpload").prop("disabled", true); + $("#meshFile").prop("disabled", true); + $("#textureFile").prop("disabled", true); + $("#xmlContent").prop("disabled", true); + $("#isBuyable").prop("disabled", true); + $("#hatPrice").prop("disabled", true); + $("#isRBX").prop("disabled", true); + $("#hatName").prop("disabled", true); + $("#hatDescription").prop("disabled", true); + $("#datafileName").prop("disabled", true); + var formData = new FormData(); + formData.append('meshFile', $('#meshFile')[0].files[0]); + formData.append('textureFile', $('#textureFile')[0].files[0]); + formData.append('hatName', $("#hatName").val()); + formData.append('hatDescription', $("#hatDescription").val()); + formData.append('hatPrice', $("#hatPrice").val()); + formData.append('datafileName', $("#datafileName").val()); + formData.append('isBuyable', $('#isBuyable').prop('checked')); + formData.append('RBXAsset', $('#isRBX').prop('checked')); + formData.append('xmlContent', $("#xmlContent").val()); + formData.append('csrf_token', $('meta[name="csrf-token"]').attr('content')); + $.ajax({ + type: "POST", + url : "/core/func/api/admin/post/newHat.php", + data : formData, + cache: false, + contentType: false, + processData: false, + success: function(response) { + console.log(response); + $("#doUpload").prop("disabled", false); + $("#meshFile").prop("disabled", false); + $("#textureFile").prop("disabled", false); + $("#xmlContent").prop("disabled", false); + $("#isBuyable").prop("disabled", false); + $("#isRBX").prop("disabled", false); + $("#hatPrice").prop("disabled", false); + $("#hatName").prop("disabled", false); + $("#hatDescription").prop("disabled", false); + $("#datafileName").prop("disabled", false); + if (response == "success") { + $("#cStatus").html("
    Hat uploaded!
    "); + }else if (response == "error") { + $("#cStatus").html("
    Could not upload this hat because a network error occurred. Attempt again later.
    "); + }else if (response == "missing-info" || response == "no-file") { + $("#cStatus").html("
    We are missing information from you. Please check again.
    "); + }else if (response == "name-too-long") { + $("#cStatus").html("
    Your hat name is too long.
    "); + }else if (response == "datafilename-too-long") { + $("#cStatus").html("
    Your datafile name is too long.
    "); + }else if (response == "description-too-long") { + $("#cStatus").html("
    Your description is too long.
    "); + }else if (response == "illegal-buyable") { + $("#cStatus").html("
    Illegal alter detected.
    "); + }else if (response == "price-too-low") { + $("#cStatus").html("
    Price is too low. Min: 1 coin
    "); + }else if (response == "datafile-mesh-already-exists") { + $("#cStatus").html("
    This datafile name already exists. Please try another one.
    "); + }else if (response == "meshfile-illegalFileType") { + $("#cStatus").html("
    Your mesh file type is incorrect.
    "); + }else if (response == "file-upload-error") { + $("#cStatus").html("
    File upload failed. Please notify an administrator.
    "); + }else if (response == "texture-illegalFileType") { + $("#cStatus").html("
    Your texture file type is incorrect.
    "); + }else{ + $("#cStatus").html("
    Could not upload this hat because a network error occurred. Attempt again later.
    "); + } + }, + error: function() { + $("#cStatus").html("
    Could not upload this hat because a network error occurred. Attempt again later.
    "); + } + }); + } + }) +}); \ No newline at end of file diff --git a/www/core/func/js/admin/unbanUser.js b/www/core/func/js/admin/unbanUser.js new file mode 100644 index 0000000..fff356c --- /dev/null +++ b/www/core/func/js/admin/unbanUser.js @@ -0,0 +1,36 @@ +$(document).ready(function() { + $("#unbanUser").click(function() { + if ($("#unbanUser").is(":disabled") == false) { + $("#unbanUser").prop("disabled", true); + $("#username").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + var username = $("#username").val(); + $.post('/core/func/api/admin/post/unbanUser.php', { + csrf: csrf_token, + username: username + }) + .done(function(response) { + $("#unbanUser").prop("disabled", false); + $("#username").prop("disabled", false); + if (response == "error") { + $("#bStatus").html("
    Could not unban this user because a network error occurred.
    "); + }else if (response == "missing-info") { + $("#bStatus").html("
    We are missing information from you. Please check and try again.
    "); + }else if (response == "can-not-unban-yourself") { + $("#bStatus").html("
    You can not unban yourself!
    "); + }else if (response == "no-user") { + $("#bStatus").html("
    This user has not been found.
    "); + }else if (response == "can-not-unban-user") { + $("#bStatus").html("
    You can not unban this user.
    "); + }else if (response == "user-not-banned") { + $("#bStatus").html("
    You can not unban this user because this user is not banned.
    "); + }else if (response == "success") { + $("#bStatus").html("
    This user has been unbanned!
    "); + } + }) + .fail(function() { + $("#bStatus").html("
    Could not unban this user because a network error occurred.
    "); + }); + } + }) +}) \ No newline at end of file diff --git a/www/core/func/js/auth/login.js b/www/core/func/js/auth/login.js new file mode 100644 index 0000000..d4e3338 --- /dev/null +++ b/www/core/func/js/auth/login.js @@ -0,0 +1,86 @@ +$(document).ready(function() { + $("#lSubmit").click(function() { + if ($("#lSubmit").is(":disabled") == false) { + $("#lSubmit").prop("disabled", true); + $("#lForgot").prop("disabled", true); + $("#lUsername").prop("disabled", true); + $("#lPassword").prop("disabled", true); + + var user = $("#lUsername").val(); + var passwd = $("#lPassword").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/auth/login.php', { + username: user, + passwd: passwd, + csrf: csrf_token + }) + .done(function(response) { + $("#lSubmit").prop("disabled", false); + $("#lForgot").prop("disabled", false); + $("#lUsername").prop("disabled", false); + $("#lPassword").prop("disabled", false); + if (response == "error") { + $("#lStatus").css("color", "red").html("Network error. Try again later."); + }else if (response == "missing-info") { + $("#lStatus").css("color", "red").html("Please fill in all fields."); + }else if (response == "no-user") { + $("#lStatus").css("color", "red").html("No user found with this name."); + }else if (response == "success") { + window.location.reload(); + }else if (response == "incorrect-password") { + $("#lStatus").css("color", "red").html("Incorrect password specified."); + }else if (response == "rate-limit") { + $("#lStatus").css("color", "red").html("Please wait a bit..."); + } + }) + .fail(function() { + $("#lStatus").css("color", "red").html("Network error. Try again later."); + }); + } + }); + + // Toggle on enter + $("#lUsername").keyup(function(event) { + if(event.keyCode == 13) { + $("#lSubmit").click(); + } + }) + + $("#lPassword").keyup(function(event) { + if(event.keyCode == 13) { + $("#lSubmit").click(); + } + }) + + $("#lForgot").click(function() { + if ($("#lForgot").is(":disabled") == false) { + $("#lSubmit").prop("disabled", true); + $("#lForgot").prop("disabled", true); + $("#lUsername").prop("disabled", true); + $("#lPassword").prop("disabled", true); + var user = $("#lUsername").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/auth/forgot.php', { + username: user, + csrf: csrf_token + }) + .done(function(response) { + $("#lSubmit").prop("disabled", false); + $("#lForgot").prop("disabled", false); + $("#lUsername").prop("disabled", false); + $("#lPassword").prop("disabled", false); + console.log(response); + if (response == "no-user") { + $("#lStatus").css("color", "red").html("User not found"); + }else if (response == "rate-limit") { + $("#lStatus").css("color", "red").html("Please wait"); + }else if (response == "success") { + $("#lStatus").css("color", "green").html("E-Mail sent!"); + } + }) + .fail(function() { + $("#lStatus").css("color", "red").html("Reset password request failed."); + }); + } + }) +}); \ No newline at end of file diff --git a/www/core/func/js/auth/register.js b/www/core/func/js/auth/register.js new file mode 100644 index 0000000..fe62da8 --- /dev/null +++ b/www/core/func/js/auth/register.js @@ -0,0 +1,86 @@ +$(document).ready(function() { + $("#rSubmit").click(function() { + if ($("#rSubmit").is(":disabled") == false) { + $("#rUsername").prop("disabled", true); + $("#rEmail").prop("disabled", true); + $("#rPassword1").prop("disabled", true); + $("#rPassword2").prop("disabled", true); + $("#rSubmit").prop("disabled", true); + + var formData = new FormData(); + formData.append('username', $("#rUsername").val()); + formData.append('email', $("#rEmail").val()); + formData.append('passwd1', $("#rPassword1").val()); + formData.append('passwd2', $("#rPassword2").val()); + formData.append('captcha', $("#g-recaptcha-response").val()); + formData.append('csrf', $('meta[name="csrf-token"]').attr('content')); + + $.ajax({ + type: "POST", + url : "/core/func/api/auth/register.php", + data : formData, + cache: false, + contentType: false, + processData: false, + success: function(data) { + $("#rUsername").prop("disabled", false); + $("#rEmail").prop("disabled", false); + $("#rPassword1").prop("disabled", false); + $("#rPassword2").prop("disabled", false); + $("#rSubmit").prop("disabled", false); + if (data != "success") { + grecaptcha.reset(); + } + if (data == "error") { + $("#rStatus").html("
    Unexpected network error. Try again later.
    "); + }else if (data == "no-outlook") { + $("#rStatus").html("
    All outgoing traffic to outlook, yahoo, and aol are currently disabled due to their use of Spamhaus.
    "); + }else if (data == "invalid-username") { + $("#rStatus").html("
    Your username is invalid. Please try something else.
    "); + }else if (data == "illegal-username") { + $("#rStatus").html("
    Your username contains illegal characters.
    "); + }else if (data == "username-too-long") { + $("#rStatus").html("
    Your username is too long.
    "); + }else if (data == "username-too-short") { + $("#rStatus").html("
    Your username is too short.
    "); + }else if (data == "no-username") { + $("#rStatus").html("
    Please enter a username.
    "); + }else if (data == "no-email") { + $("#rStatus").html("
    Please enter an E-Mail.
    "); + }else if (data == "no-email") { + $("#rStatus").html("
    Please enter an E-Mail.
    "); + }else if (data == "email-too-long") { + $("#rStatus").html("
    Your E-Mail is too long.
    "); + }else if (data == "illegal-email") { + $("#rStatus").html("
    Invalid E-Mail has been specified.
    "); + }else if (data == "no-password") { + $("#rStatus").html("
    Please fill in both password fields.
    "); + }else if (data == "passwords-mismatch") { + $("#rStatus").html("
    Your password and password confirmation do not match.
    "); + }else if (data == "password-too-short") { + $("#rStatus").html("
    Your password is too short.
    "); + }else if (data == "password-too-long") { + $("#rStatus").html("
    Your password is too long.
    "); + }else if (data == "email-already-used") { + $("#rStatus").html("
    The E-Mail you have entered is already being used.
    "); + }else if (data == "username-already-used") { + $("#rStatus").html("
    The username you have entered is already being used.
    "); + }else if (data == "success") { + $("#rStatus").html("
    Your account has been created!
    "); + }else if (data == "incorrect-captcha") { + $("#rStatus").html("
    Either you did not do the captcha or the captcha is incorrect.
    "); + }else if (data == "missing-info") { + $("#rStatus").html("
    Please finish the registration form.
    "); + }else if (data == "rate-limit") { + $("#rStatus").html("
    You have recently made an account. Please wait before making a new one.
    "); + }else if (data == "unknown-email") { + $("#rStatus").html("
    The email you have entered is unknown. Please use known email providers such as gmail or outlook
    "); + } + }, + error: function() { + $("#rStatus").html("
    Unexpected network error. Try again later.
    "); + } + }); + } + }) +}); \ No newline at end of file diff --git a/www/core/func/js/auth/resetPassword.js b/www/core/func/js/auth/resetPassword.js new file mode 100644 index 0000000..41fb01c --- /dev/null +++ b/www/core/func/js/auth/resetPassword.js @@ -0,0 +1,40 @@ +function resetPassword(key, userID) { + if ($("#changePassword").is(":disabled") == false) { + $("#changePassword").prop("changePassword", true); + $("#password1").prop("disabled", true); + $("#password2").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + var password1 = $("#password1").val(); + var password2 = $("#password2").val(); + $.post('/core/func/api/auth/resetPassword.php', { + password1: password1, + password2: password2, + key: key, + userID, userID, + csrf: csrf_token + }) + .done(function(response) { + $("#changePassword").prop("changePassword", false); + $("#password1").prop("disabled", false); + $("#password2").prop("disabled", false); + if (response == "error") { + $("#rStatus").html("
    Network error. Try again later.
    "); + }else if (response == "invalid-key") { + $("#rStatus").html("
    The key specified is invalid.
    "); + }else if (response == "key-expired") { + $("#rStatus").html("
    Your key has expired. Please request a new password reset.
    "); + }else if (response == "password-mismatch") { + $("#rStatus").html("
    The passwords you have specified do not match.
    "); + }else if (response == "password-too-long") { + $("#rStatus").html("
    Your new password is too long.
    "); + }else if (response == "password-too-short") { + $("#rStatus").html("
    Your new password is too short.
    "); + }else if (response == "success") { + window.location = "/"; + } + }) + .fail(function() { + $("#rStatus").html("
    Network error. Try again later.
    "); + }); + } +} \ No newline at end of file diff --git a/www/core/func/js/auth/twostep.js b/www/core/func/js/auth/twostep.js new file mode 100644 index 0000000..30f8b9e --- /dev/null +++ b/www/core/func/js/auth/twostep.js @@ -0,0 +1,37 @@ +$(document).ready(function() { + $("#finishAuth").click(function() { + if ($("#finishAuth").is(":disabled") == false) { + $("#finishAuth").prop("disabled", true); + $("#factorCode").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + var twoStepCode = $("#factorCode").val(); + $.post('/core/func/api/auth/twostep.php', { + factorCode: twoStepCode, + csrf: csrf_token + }) + .done(function(response) { + $("#finishAuth").prop("disabled", false); + $("#factorCode").prop("disabled", false); + if (response == "success") { + window.location = "/"; + }else if (response == "wrong-code") { + $("#tStatus").html("
    You have entered an incorrect code.
    "); + }else if (response == "missing-info") { + $("#tStatus").html("
    Please enter a code.
    "); + }else{ + $("#tStatus").html("
    Network error. Try again later.
    "); + } + }) + .fail(function() { + $("#tStatus").html("
    Network error. Try again later.
    "); + }); + } + }) + + // Toggle on enter + $("#factorCode").keyup(function(event) { + if(event.keyCode == 13) { + $("#finishAuth").click(); + } + }) +}); \ No newline at end of file diff --git a/www/core/func/js/auth/verifyEmail.js b/www/core/func/js/auth/verifyEmail.js new file mode 100644 index 0000000..befd0d9 --- /dev/null +++ b/www/core/func/js/auth/verifyEmail.js @@ -0,0 +1,70 @@ +$(document).ready(function() { + $("#verifyEmailCode").click(function() { + if ($("#verifyEmailCode").is(":disabled") == false) { + $("#verifyEmailCode").prop("disabled", true); + $("#emailCode").prop("disabled", true); + + var emailCode = $("#emailCode").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/auth/verifyEmail.php', { + emailCode: emailCode, + csrf: csrf_token + }) + .done(function(response) { + $("#verifyEmailCode").prop("disabled", false); + $("#emailCode").prop("disabled", false); + if (response == "error") { + $("#vStatus").html("
    Network error. Try again later.
    "); + }else if (response == "incorrect-code") { + $("#vStatus").html("
    You have entered an incorrect verification code.
    "); + }else if (response == "success") { + window.location = "/"; + } + }) + .fail(function() { + $("#vStatus").html("
    Network error. Try again later.
    "); + }); + } + }) + + $("#confirmChange").click(function() { + if ($("#confirmChange").is(":disabled") == false) { + $("#confirmChange").prop("disabled", true); + $("#currentPassword").prop("disabled", true); + $("#newEmail").prop("disabled", true); + + var newEmail = $("#newEmail").val(); + var currentPassword = $("#currentPassword").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/auth/changeEmailVerify.php', { + currentPassword: currentPassword, + newEmail: newEmail, + csrf: csrf_token + }) + .done(function(response) { + $("#confirmChange").prop("disabled", false); + $("#currentPassword").prop("disabled", false); + $("#newEmail").prop("disabled", false); + console.log(response); + if (response == "error") $(".gModalErrorContainer").html('
    Could not change your email because a network error occurred.
    '); + if (response == "missing-info") $(".gModalErrorContainer").html('
    You are missing information. Please check and try again.
    '); + if (response == "inc-password") $(".gModalErrorContainer").html('
    Your current password is incorrect.
    '); + if (response == "inc-email") $(".gModalErrorContainer").html('
    You have entered an invalid or illegal email address.
    '); + if (response == "email-in-use") $(".gModalErrorContainer").html('
    That email address is already in use by another user. Use another one.
    '); + if (response == "unknown-email") $(".gModalErrorContainer").html('
    You are using an unknown email service. Please use a more known one such as hotmail or gmail.
    '); + if (response == "rate-limit") $(".gModalErrorContainer").html('
    You have recently changed your email address. Please wait at least 60 minutes before trying again.
    '); + if (response == "success") window.location.reload(); + }) + .fail(function() { + $(".gModalErrorContainer").html('
    Could not change your email because a network error occurred.
    '); + }); + } + }) + + // Toggle on enter + $("#emailCode").keyup(function(event) { + if(event.keyCode == 13) { + $("#verifyEmailCode").click(); + } + }) +}); \ No newline at end of file diff --git a/www/core/func/js/blog.js b/www/core/func/js/blog.js new file mode 100644 index 0000000..43d2435 --- /dev/null +++ b/www/core/func/js/blog.js @@ -0,0 +1,28 @@ +$(document).ready(function() { + loadMain(); +}); + +function loadPost(postID) { + if (postID != undefined) { + $("#newPostBtn").css("display", "none"); + $.get("/core/func/api/blog/loadPost.php?id=" + postID, function(data) { + $("#postContainer").html(data); + }) + } +} + +function loadMain() { + $.get("/core/func/api/blog/getPosts.php", function(data) { + $("#newPostBtn").css("display", "block"); + $("#title").html("Graphictoria Blog"); + $("#postContainer").html(data); + }) +} + +function loadNew() { + $("#newPostBtn").css("display", "none"); + $.get("/core/func/api/blog/addPost.php", function(data) { + $("#title").html("New Post"); + $("#postContainer").html(data); + }) +} \ No newline at end of file diff --git a/www/core/func/js/catalog.js b/www/core/func/js/catalog.js new file mode 100644 index 0000000..6e9acdd --- /dev/null +++ b/www/core/func/js/catalog.js @@ -0,0 +1,52 @@ +$(document).ready(function() { + var type = "hats"; + var page = 0; + $("#itemsContainer").load("/core/func/api/catalog/getItems.php?type=hats&page=0"); + + $("#typeHats").click(function() { + switchType("hats"); + }); + + $("#typeHeads").click(function() { + switchType("heads"); + }); + + $("#typeFaces").click(function() { + switchType("faces"); + }); + + $("#typetshirts").click(function() { + switchType("tshirts"); + }); + + $("#typeshirts").click(function() { + switchType("shirts"); + }); + + $("#typepants").click(function() { + switchType("pants"); + }); + + $("#typeGear").click(function() { + switchType("gear"); + }); + + $("#typeDecals").click(function() { + switchType("decals"); + }); +}); + +function switchType(type) { + page = 0; + type = type; + $("#itemsContainer").load("/core/func/api/catalog/getItems.php?type=" + type + "&page=0"); +} + +function searchItem() { + var itemName = $("#searchCValue").val(); + if (itemName.length == 0) { + $('#errorModal').modal({ show: true}); + }else{ + $("#itemsContainer").load("/core/func/api/catalog/getItems.php?type=" + type + "&page=" +page+"&term=" + encodeURIComponent(itemName)); + } +} \ No newline at end of file diff --git a/www/core/func/js/character.js b/www/core/func/js/character.js new file mode 100644 index 0000000..890844c --- /dev/null +++ b/www/core/func/js/character.js @@ -0,0 +1,161 @@ +let userName; + +function postPose(pose) { + $("#normal").addClass('disabled'); // yet another hack with these buttons since anchor tags can't be disabled + $("#walking").addClass('disabled'); + $("#sitting").addClass('disabled'); + $("#overlord").addClass('disabled'); + $("#poseStatus").html("
    Changing pose...
    "); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + var pose = pose; + $.post('/core/func/api/character/post/changePose.php', { + csrf: csrf_token, + pose: pose + }).done(function(){ + doGenIfName(); + $("#normal").removeClass('disabled'); + $("#walking").removeClass('disabled'); + $("#sitting").removeClass('disabled'); + $("#overlord").removeClass('disabled'); + $("#poseStatus").html("
    Changed pose to " + pose + "
    "); + }); +} + +$(document).ready(function() { + $("#normal").click(function() { + if ($("#normal").hasClass('disabled') == false) { + postPose("normal"); + } + }) + + $("#walking").click(function() { + if ($("#walking").hasClass('disabled') == false) { + postPose("walking"); + } + }) + + $("#sitting").click(function() { + if ($("#sitting").hasClass('disabled') == false) { + postPose("sitting"); + } + }) + + $("#overlord").click(function() { + if ($("#overlord").hasClass('disabled') == false) { + postPose("overlord"); + } + }) + + $("#regen").click(function() { + if ($("#regen").is(":disabled") == false) { + $("#regen").remove(); + $("#poseStatus").html("
    Regenerating character...
    "); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/character/post/regenCharacter.php', { + csrf: csrf_token + }).done(function() { + $("#poseStatus").html("
    Regenerated character
    "); + }); + } + }) + + + switchTo("hats"); + + $("#showHats").click(function() { + switchTo("hats"); + }) + + $("#showHeads").click(function() { + switchTo("heads"); + }) + + $("#showFaces").click(function() { + switchTo("faces"); + }) + + $("#showTshirts").click(function() { + switchTo("tshirts"); + }) + + $("#showShirts").click(function() { + switchTo("shirts"); + }) + + $("#showPants").click(function() { + switchTo("pants"); + }) + + $("#showGear").click(function() { + switchTo("gear"); + }) +}); + +function doGenIfName() +{ + if(userName == undefined) + { + return; + } + startGeneration(userName); +} + +function startGeneration(uName) { + //now with a new HACK YUCK YUCKY EWWWWWWW - xlxi + userName = uName; + + var characterElement = document.getElementById('character'); + characterElement.src = 'https://www.gtoria.net/avatar/' + uName + '?time=' + Math.random(); +} + +function startSpinners() +{ + $('#inventoryItems').html('
    '); + $('#wearing').html('
    '); +} + +function switchTo(type) { + type = type; + startSpinners(); + $("#inventoryItems").load("/core/func/api/character/getInventory.php?type=" + type); + $("#wearing").load("/core/func/api/character/getWearing.php?type=" + type); +} + +function loadPage(type, page) { + $("#inventoryItems").load("/core/func/api/character/getInventory.php?type=" + type + "&page=" + page); +} + +function wearItem(itemId, type, page) { + if ($(".wearItem").is(":disabled") == false) { + $(".wearItem").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + startSpinners(); + $.post('/core/func/api/character/post/wearItem.php', { + csrf: csrf_token, + itemId: itemId, + type: type + }) + .done(function() { + doGenIfName(); + $("#inventoryItems").load("/core/func/api/character/getInventory.php?type=" + type + "&page=" + page); + $("#wearing").load("/core/func/api/character/getWearing.php?type=" + type); + }); + } +} + +function removeItem(itemId, type, page) { + if ($(".removeItem").is(":disabled") == false) { + $(".removeItem").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + startSpinners(); + $.post('/core/func/api/character/post/removeItem.php', { + csrf: csrf_token, + itemId: itemId + }) + .done(function() { + doGenIfName(); + $("#inventoryItems").load("/core/func/api/character/getInventory.php?type=" + type + "&page=" + page); + $("#wearing").load("/core/func/api/character/getWearing.php?type=" + type); + }); + } +} diff --git a/www/core/func/js/chat.js b/www/core/func/js/chat.js new file mode 100644 index 0000000..d741606 --- /dev/null +++ b/www/core/func/js/chat.js @@ -0,0 +1,473 @@ +var lastMessageResponse; +var getMessages = null; +var getTypingThread = null; +var dChatOpen = false; +var lastTimestamp = 0; +var isTyping = false; +var infoOpen = false; +var chatMemberCount; +var lastMessageID = 0; +var ackIds = []; + +$(function() { + $(".loadChatList").click(function() { + loadChatList(); + removeChatInfoBar(); + }) + $(".chatOpener").click(function() { + $(".chatContainer").empty(); + $(".chatContainer").html("Loading..."); + if (dChatOpen == false) { + dChatOpen = true; + $(this).css("top", "calc(100% - 371px)"); + $(".chatOpenbackground").css("display", "block"); + $(".chatOptions").css("display", "block"); + loadChatList(); + }else{ + dChatOpen = false; + $(this).css("top", "calc(100% - 24px)"); + $(".chatOpenbackground").css("display", "none"); + $(".chatOptions").css("display", "none"); + $(".chatOpener").html("Chat"); + stopIntervals(); + } + }) + + if ($(".mobileView").css("display") == "block") { + $(".backandtext").css("top", $(".navbar").css("height")) + } +}); + +function stopIntervals() { + for (var i = 1; i < 9999; i++) { + window.clearInterval(i); + getMessages = undefined; + } +} + +function loadChatList() { + clean(); + if ($(".mobileView").css("display") == "block") { + window.history.pushState("", "", "/chat"); + var appContainer = $("#appContainer"); + appContainer.load("/core/func/views/app/user/chatlist.php", function() { + $.get("/core/func/api/chat/getList.php", function(data) { + var json = JSON.parse(data); + $(".chatContainer").empty(); + for (var i = 0; i < json.length; i++) { + var fHTML = json[i].chat_id; + $(".chatContainer").append('
  • ' + json[i].chatName + '
  • '); + } + if (data == "[]") { + $(".chatContainer").append("
    You are in no chats at the moment
    "); + } + }); + }); + }else{ + $(".chatOpener").html("Chat"); + $(".addChatBtn").css("display", "block"); + $(".chatOptions").html(""); + $.get("/core/func/api/chat/getList.php", function(data) { + var json = JSON.parse(data); + $(".chatContainer").empty(); + for (var i = 0; i < json.length; i++) { + var fHTML = json[i].chat_id; + $(".chatContainer").append('
  • ' + json[i].chatName + '
  • '); + } + if (data == "[]") { + $(".chatContainer").append("
    You are in no chats at the moment
    "); + } + }); + } +} + +function renderChatChoices() { + $("#gModalTitle").html("Add Chat"); + $("#gModalBody").html("

    Do you want to create a new chat or join one?

    "); + $(".gModalErrorContainer").html(""); + $("#gModalBody").append(""); + $("#gModalBody").append(""); + $("#globalModal2").modal('show'); +} + +function renderCreateChat() { + $("#gModalTitle").html("Create Chat"); + $("#gModalBody").html("

    Give your chat a name and press create!

    "); + $(".gModalErrorContainer").html(""); + $("#gModalBody").append(""); + $("#gModalBody").append(""); +} + +function renderJoinChat() { + $("#gModalTitle").html("Join Chat"); + $("#gModalBody").html("

    Enter a Chat Code and join a chat

    "); + $(".gModalErrorContainer").html(""); + $("#gModalBody").append(""); + $("#gModalBody").append(""); +} + +function createChat() { + $(".create_chat_name").prop("disabled", true); + $(".create_chat").prop("disabled", true); + var chatName = $(".create_chat_name").val(); + $.post('/core/func/api/chat/createChat.php', { + chatName: chatName, + csrfToken: $('meta[name="csrf-token"]').attr('content') + }) + .done(function(response) { + $(".create_chat_name").prop("disabled", false); + $(".create_chat").prop("disabled", false); + if (response == "no-name") { + $(".gModalErrorContainer").html('
    You must have a chat name
    '); + }else if (response == "chat-name-too-long") { + $(".gModalErrorContainer").html('
    Your chat name is too long
    '); + }else if (response == "rate-limit") { + $(".gModalErrorContainer").html('
    Please wait before making another chat
    '); + }else{ + if ($(".mobileView").css("display") == "none") { + loadChatList(); + } + var json = JSON.parse(response); + for (var i = 0; i < json.length; i++) { + loadChat(json[i].chatKey, json[i].chatName, json[i].chat_id); + } + $("#globalModal2").modal('hide'); + } + }) + .fail(function() { + $(".gModalErrorContainer").html('
    Could not create a chat because a network error occurred
    '); + }); +} + +function joinChat() { + $(".join_code").prop("disabled", true); + $(".join_chat").prop("disabled", true); + var chatCode = $(".join_code").val(); + $.post('/core/func/api/chat/joinChat.php', { + chatCode: chatCode, + csrfToken: $('meta[name="csrf-token"]').attr('content') + }) + .done(function(response) { + $(".join_code").prop("disabled", false); + $(".join_chat").prop("disabled", false); + if (response == "no-code") { + $(".gModalErrorContainer").html('
    Please enter a chat code
    '); + }else if (response == "chat-code-too-long") { + $(".gModalErrorContainer").html('
    The chat code you entered is too long
    '); + }else if (response == "invalid-code") { + $(".gModalErrorContainer").html('
    You have entered an invalid chat code
    '); + }else if (response == "already-in") { + $(".gModalErrorContainer").html('
    You are already in this chat
    '); + }else{ + if ($(".mobileView").css("display") == "none") { + loadChatList(); + } + var json = JSON.parse(response); + for (var i = 0; i < json.length; i++) { + loadChat(json[i].chatKey, json[i].chatName, json[i].chat_id); + } + $("#globalModal2").modal('hide'); + } + }) + .fail(function() { + $(".gModalErrorContainer").html('
    Could not join the chat because a network error occurred
    '); + }); +} + +function clean() { + infoOpen = false; + stopIntervals(); + lastMessageResponse = undefined; + lastTimestamp = 0; +} + +function sendTyping(chatId) { + if (!isTyping) { + isTyping = true; + $.post('/core/func/api/chat/sendTyping.php', { + chatId: chatId, + csrfToken: $('meta[name="csrf-token"]').attr('content') + }).fail(function() { + console.log("Error sending typing status"); + }); + setTimeout(function() { + isTyping = false; + }, 3000); + } +} + + +function executeAsync(func) { + setTimeout(func, 0); +} + +function chatInfo(chatKey, chatName, chatId) { + if (infoOpen) { + infoOpen = false; + loadChat(chatKey, chatName, chatId); + }else{ + clean(); + infoOpen = true; + console.log("Got chat info"); + $(".backandtext").html("Chat information"); + $(".chatContainer").empty(); + } +} + +function loadChat(chatKey, chatName, chatId) { + ackIds = []; + if ($(".mobileView").css("display") == "none") { + $(".list-group-item").css("background-color", "#fff"); + $("."+chatId).css("background-color", "#ddd"); + $(".chatOptions").html(''); + } + clean(); + $(".chatContainer").html('
    '); + + $("#chatMessageBox").css("width", "215px"); + $("#sendChatButton").css("margin-left", "215px").css("width", "41px") + $(".addChatBtn").css("display", "none"); + + $('#chatMessageBox').keypress(function (e) { + var key = e.which; + if(key == 13) { + $('#sendChatButton').click(); + } + }); + + $(window).resize(function() { + if ($(".mobileView").css("display") == "block") { + $(window).scrollTop($(document).height()); + } + }); + + $.get("/core/func/api/chat/getChatInfo.php?id=" + chatId, function(data) { + if (data == "error" || data == "[]") { + loadChatList(); + throw new Error("An error occurred"); + } + var json = JSON.parse(data); + for (var i = 0; i < json.length; i++) { + chatMemberCount = json[i].chatMembers; + if (chatMemberCount == 1) { + var bText = "member"; + }else{ + var bText = "members"; + } + chatJoinKey = json[i].joinKey; + $(".chatOpener").html(json[i].chatName); + } + + if ($(".mobileView").css("display") == "block") { + $(".chatBreaker").empty(); + times = 0; + while(times < $(".backandtext").height()/24) { + $(".chatBreaker").append("
    "); + times++; + } + } + + executeAsync(loadMessagesInChat(chatKey, chatId)); + executeAsync(getTyping(chatId)); + getMessages = setInterval(function() { + loadMessagesInChat(chatKey, chatId); + }, 1000); + + getTypingThread = setInterval(function() { + getTyping(chatId); + }, 3000); + $(".messageContainer").css("opacity", 0); + $(".loadContainer").append('
    ').hide(); + }).fail(function() { + loadChat(chatKey, chatName, chatId); + }); +} + +function getTyping(chatId) { + $.get("/core/func/api/chat/getTyping.php?chatId=" + chatId, function(data) { + var json = JSON.parse(data); + if (chatMemberCount == 1) { + var bText = "member"; + }else{ + var bText = "members"; + } + //if ($(".mobileView").css("display") == "block") { + if (json.mode == "none") { + $(".chatOptions").html(chatMemberCount + " " + bText + " [Invite code: " + chatJoinKey + "]"); + } + if (json.mode == "showTyping") { + var username1 = json.usernames[0]; + var username2 = json.usernames[1]; + var arrayLength = json.usernames.length; + if (arrayLength == 1) { + $(".chatOptions").html(username1 + " is typing...").css("position", "fixed"); + }else{ + $(".chatOptions").html(username1 + " and " + username2 + " are typing..."); + } + } + if (json.mode == "several") { + $(".chatOptions").html("Several people are typing"); + } + //} + }); +} + +function parseDate(date) { + var dateMessage = new Date(date*1000); + var dateCurrent = new Date(); + var dayMessageNum = dateMessage.getDate(); + var yearMessage = dateMessage.getFullYear(); + var monthMessage = dateMessage.getMonth(); + + var dateCurrentNum = dateCurrent.getDate(); + var currentYear = dateCurrent.getFullYear(); + var currentMonth = dateCurrent.getMonth(); + var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + var dayOfWeekMessage = days[dateMessage.getDay()] + var dayOfWeekCurrent = days[dateCurrent.getDay()] + dateCurrent.setDate(dateCurrent.getDate() - 1); + var dayYesterday = days[dateCurrent.getDay()]; + if (dayOfWeekCurrent == dayOfWeekMessage && dayMessageNum == dateCurrentNum && yearMessage == currentYear && monthMessage == currentMonth) { + date = intlDate2.format(new Date(1000*date)); + }else if (dayOfWeekMessage == dayYesterday && dayMessageNum == dateCurrentNum-1 && yearMessage == currentYear && monthMessage == currentMonth) { + date = "yesterday at " + intlDate2.format(new Date(1000*date)); + }else{ + date = intlDate.format(new Date(1000*date)); + date = date.replace("M01", "Jan"); + date = date.replace("M02", "Feb"); + date = date.replace("M03", "Mar"); + date = date.replace("M04", "Apr"); + date = date.replace("M05", "May"); + date = date.replace("M06", "Jun"); + date = date.replace("M07", "Jul"); + date = date.replace("M08", "Aug"); + date = date.replace("M09", "Sep"); + date = date.replace("M10", "Oct"); + date = date.replace("M11", "Nov"); + date = date.replace("M12", "Dec"); + } + return date; +} + +function loadMessagesInChat(chatKey, chatId) { + $.get("/core/func/api/chat/getMessages.php?id=" + chatId + "×tamp=" + lastTimestamp, function(data) { + if (data == "error") { + loadChatList(); + throw new Error("An error occurred"); + } + if (lastMessageResponse != data) { + if ($(".messageContainer").css("opacity") == 0) { + $("#loadContainer").empty(); + $(".messageContainer").animate({opacity: 1}); + } + var json = JSON.parse(data); + if (data == "[]" && lastTimestamp == 0) { + createMessage("Welcome to the beginning of this chat.", "GraphictoriaBot", "red", 2, undefined, true, false); + } + for (var i = 0; i < json.length; i++) { + if (lastMessageID != json[i].messageId && ackIds.includes(json[i].messageId) == false) { + lastMessageID = json[i].messageId; + ackIds.push(json[i].messageId); + lastTimestamp = Math.round((new Date()).getTime() / 1000); + if (json[i].userId == 0) { + createMessage(json[i].message, "Graphictoria", "red", 2, json[i].date, true, false, 0); + }else{ + createMessage(json[i].message, json[i].username, json[i].userColor, json[i].staff, json[i].date, false, json[i].setRight, json[i].userID); + } + if (lastMessageResponse != undefined && data != "[]" && json[i].setRight == false) { + console.log("New message"); + var receiveSound = new Audio('/core/func/api/chat/sounds/send.mp3') + receiveSound.play(); + } + }else{ + console.log("Caught duplicate message"); + } + } + if (data != "[]") { + if ($(".mobileView").css("display") == "block") { + $(window).scrollTop($(document).height()); + }else{ + $(".chatOpenbackground").scrollTop($(".chatOpenbackground")[0].scrollHeight); + } + } + } + lastMessageResponse = data; + }); +} + +var options = { + weekday: 'long', + month: 'short', + year: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric' +},intlDate = new Intl.DateTimeFormat(undefined, options); + +var options2 = { + hour: 'numeric', + minute: 'numeric' +},intlDate2 = new Intl.DateTimeFormat(undefined, options2); + +function createMessage(message, sender, cColor, tagId, date, doCenter, setRight, userID) { + var color = cColor; + var cHTML = ""; + if (doCenter == true) { + cHTML = "center"; + } + + var sHTML = '' + sender + ''; + + if (setRight == false) { + if (tagId == 1) { + var uHTML = ' ' + sHTML + ':'; + }else if (tagId == 2) { + var uHTML = 'BOT ' + sender + ':'; + }else{ + var uHTML = sHTML + ':'; + } + }else{ + var uHTML = ""; + } + + if (setRight == true) { + var style = "style=\"float:right;max-width:100%\""; + }else{ + var style = "style=\"width:100%\""; + } + + if (date == undefined) { + date = ""; + }else{ + date = parseDate(date); + } + $(".messageContainer").append('
    ' + uHTML + ' ' + message + ' ' + date + '
    '); +} + +function sendMessageInChat(chatId, chatKey) { + $("#sendChatButton").prop("disabled", true); + $("#chatMessageBox").prop("disabled", true); + var chatMessage = $("#chatMessageBox").val(); + $("#chatMessageBox").val(""); + $.post('/core/func/api/chat/sendMessage.php', { + message: chatMessage, + chatId: chatId, + csrfToken: $('meta[name="csrf-token"]').attr('content') + }) + .done(function(response) { + $("#sendChatButton").prop("disabled", false); + $("#chatMessageBox").prop("disabled", false); + $("#chatMessageBox").focus(); + if (response == "message-too-short") { + $("#chatMessageBox").css("border", "1px solid #d9534f"); + }else if (response == "message-too-long") { + $("#chatMessageBox").css("border", "1px solid #d9534f"); + }else{ + $("#chatMessageBox").css("border", "0px solid #F44336"); + } + }) + .fail(function() { + console.log("Error while sending message"); + $("#chatMessageBox").css("border", "0px solid #F44336"); + }); +} \ No newline at end of file diff --git a/www/core/func/js/createGroup.js b/www/core/func/js/createGroup.js new file mode 100644 index 0000000..afa4be8 --- /dev/null +++ b/www/core/func/js/createGroup.js @@ -0,0 +1,42 @@ +$(document).ready(function() { + $("#createGroup").click(function() { + if ($("#createGroup").is(":disabled") == false) { + $("#createGroup").prop("disabled", true); + $("#groupName").prop("disabled", true); + $("#groupDescription").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + var groupName = $("#groupName").val(); + var groupDescription = $("#groupDescription").val(); + $.post('/core/func/api/groups/post/createGroup.php', { + csrf: csrf_token, + groupName: groupName, + groupDescription: groupDescription + }) + .done(function(response) { + $("#createGroup").prop("disabled", false); + $("#groupName").prop("disabled", false); + $("#groupDescription").prop("disabled", false); + if (response == "error") { + $("#gStatus").html("
    Could not create a group because a network error has occurred.
    "); + }else if (response == "no-name") { + $("#gStatus").html("
    Please specify a group name
    "); + }else if (response == "group-name-too-short") { + $("#gStatus").html("
    Your group name is too short
    "); + }else if (response == "group-name-too-long") { + $("#gStatus").html("
    Your group name is too long
    "); + }else if (response == "description-too-long") { + $("#gStatus").html("
    Your group description is too long
    "); + }else if (response == "no-coins") { + $("#gStatus").html("
    You do not have enough coins to create a group
    "); + }else if (response == "in-too-many-groups") { + $("#gStatus").html("
    You are in too many groups
    "); + }else{ + window.location = "/groups/view/"+response; + } + }) + .fail(function() { + $("#gStatus").html("
    Could not create a group because a network error has occurred.
    "); + }); + } + }) +}) \ No newline at end of file diff --git a/www/core/func/js/forum.js b/www/core/func/js/forum.js new file mode 100644 index 0000000..823de56 --- /dev/null +++ b/www/core/func/js/forum.js @@ -0,0 +1,371 @@ +// EnergyCell +var page = 0; +function loadForum(id) { + if ($(window).width() > 767) { + $(".adContainer").css("display", "block"); + } + $("#posts").html('


    '); + window.history.pushState("", "", '/forum-'+id); + $("html, body").animate({ scrollTop: 0 }, "fast"); + $("#posts").load("/core/func/api/forum/getPosts.php?id=" + id, function() { + if ($("#posts").height() < 1395) { + $(".adContainer").css("display", "none"); + }else{ + $(".adContainer").css("display", "block"); + } + $("#posts").hide().fadeIn('fast'); + }) + page = 0; +} + +function loadMiniProfile(userID) { + $(".modalUsername").html(""); + $(".miniProfileContent").html('
    '); + $(".miniProfileContent").load("/core/func/api/forum/getMiniProfile.php?id=" + userID); + $('.miniProfile').modal('show').hide().fadeIn('fast'); +} + +function search(forumID) { + $(".modalUsername").html(""); + $(".miniProfileContent").html(''); + $(".miniProfileContent").load("/core/func/api/forum/doSearch.php?id=" + forumID); + $('.miniProfile').modal('show').hide().fadeIn('fast'); +} + +function doSearch(forumID) { + var keyword = $("#searchboxValue").val(); + if (keyword.length == 0) { + $("#searchError").html('Please enter something'); + }else{ + $('.miniProfile').modal('hide'); + $("#posts").html('


    '); + $("html, body").animate({ scrollTop: 0 }, "fast"); + $("#posts").load("/core/func/api/forum/getPosts.php?id=" + forumID + "&keyword=" + encodeURIComponent(keyword), function() { + if ($("#posts").height() < 1395) { + $(".adContainer").css("display", "none"); + }else{ + $(".adContainer").css("display", "block"); + } + $("#posts").hide().fadeIn('fast'); + }) + page = 0; + } +} + +function loadPost(id) { + $(".adContainer").css("display", "none"); + $("#posts").html('


    '); + window.history.pushState("", "", '/forum+'+id); + $("html, body").animate({ scrollTop: 0 }, "fast"); + $("#posts").load("/core/func/api/forum/showPost.php?id=" + id, function() { + $("#posts").hide().fadeIn('fast'); + if ($("#posts").height() < 1395) { + $(".adContainer").css("display", "none"); + }else{ + $(".adContainer").css("display", "block"); + } + }) + page = 0; +} +function loadMore(page, postId) { + $(".loadMore").prop("disabled", true); + $.get("/core/func/api/forum/showPost.php?id=" + postId + "&page=" + page, function(data) { + $(".loadMore").remove(); + $("#posts").append(data); + }); +} + +function loadMoreForum(page, forumId) { + $(".loadMore").prop("disabled", true); + $.get("/core/func/api/forum/getPosts.php?id=" + forumId + "&page=" + page, function(data) { + $(".loadMore").remove(); + $("#posts").append(data); + }); +} + +function loadMoreForumSearch(page, forumId, keyword) { + $(".loadMore").prop("disabled", true); + $.get("/core/func/api/forum/getPosts.php?id=" + forumId + "&page=" + page + "&keyword=" + keyword, function(data) { + $(".loadMore").remove(); + $("#posts").append(data); + }); +} + +function newPost(forumId) { + $(".adContainer").css("display", "none"); + $("#posts").html('


    '); + $("#posts").load("/core/func/api/forum/newPost.php?id=" + forumId); + $("html, body").animate({ scrollTop: 0 }, "fast"); +} + +function newReply(postId) { + $(".adContainer").css("display", "none"); + $("#posts").html('


    '); + $("#posts").load("/core/func/api/forum/newReply.php?id=" + postId); + $("html, body").animate({ scrollTop: 0 }, "fast"); +} + +function editPost(postId) +{ + // xlxi on 7/2/2021 do not steal plz :D + $(".adContainer").css("display", "none"); + $("#posts").html('


    '); + $("#posts").load("/core/func/api/forum/editPost.php?id=" + postId); + $("html, body").animate({ scrollTop: 0 }, "fast"); +} + +function postMessage(forumId) { + if ($("#postMessage").is(":disabled") == false) { + $("#postMessage").prop("disabled", true); + $("#postContent").prop("disabled", true); + $("#postTitle").prop("disabled", true); + + var postTitle = $("#postTitle").val(); + var postContent = $("#postContent").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/forum/post/newPost.php', { + postTitle: postTitle, + postContent: postContent, + csrf: csrf_token, + forum: forumId + }) + .done(function(response) { + $("#postMessage").prop("disabled", false); + $("#postContent").prop("disabled", false); + $("#postTitle").prop("disabled", false); + + if (response == "error") { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }else if (response == "title-too-short") { + $("#pStatus").css("color", "red").html("Your title is too short. Try something else."); + }else if (response == "title-too-long") { + $("#pStatus").css("color", "red").html("Your title is too long. Try something else."); + }else if (response == "content-too-short") { + $("#pStatus").css("color", "red").html("Your content is too short."); + }else if (response == "content-too-long") { + $("#pStatus").css("color", "red").html("Your content is too long."); + }else if (response == "no-newline-spam") { + $("#pStatus").css("color", "red").html("Hey there! Your post is a little too spammy, calm down a little with the new lines!"); + }else if (response == "rate-limit") { + $("#pStatus").css("color", "red").html("You are posting too fast. Please wait."); + }else if (response == "account-age") { + $("#pStatus").css("color", "red").html("Your account needs to be a day old to post."); + }else if (response == "no-forum") { + $("#pStatus").css("color", "red").html("The forum you are trying to post in does not exist."); + }else if (response == "access-denied") { + $("#pStatus").css("color", "red").html("You have no permission to post in this forum."); + }else{ + $("#pStatus").html(response); + } + }) + .fail(function() { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }); + } +} + +function postReply(postId) { + if ($("#postReply").is(":disabled") == false) { + $("#postReply").prop("disabled", true); + $("#replyContent").prop("disabled", true); + + var replyContent = $("#replyContent").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/forum/post/newReply.php', { + replyContent: replyContent, + csrf: csrf_token, + postId: postId + }) + .done(function(response) { + $("#postReply").prop("disabled", false); + $("#replyContent").prop("disabled", false); + + if (response == "error") { + $("#rStatus").css("color", "red").html("Network error. Try again later."); + }else if (response == "content-too-short") { + $("#rStatus").css("color", "red").html("Your reply is too short."); + }else if (response == "content-too-long") { + $("#rStatus").css("color", "red").html("Your reply is too long."); + }else if (response == "no-newline-spam") { + $("#rStatus").css("color", "red").html("Hey there! Your post is a little too spammy, calm down a little with the new lines!"); + }else if (response == "rate-limit") { + $("#rStatus").css("color", "red").html("You are posting too fast. Please wait."); + }else if (response == "account-age") { + $("#rStatus").css("color", "red").html("Your account must be at least a day old to use the forums."); + }else if (response == "no-post") { + $("#rStatus").css("color", "red").html("The post you are trying to reply to does not exist."); + }else if (response == "access-denied") { + $("#rStatus").css("color", "red").html("Access Denied."); + }else{ + $("#rStatus").html(response); + } + }) + .fail(function() { + $("#rStatus").css("color", "red").html("Network error. Try again later."); + }); + } +} + +function doEditPost(postId) +{ + if ($("#doEditPost").is(":disabled") == false) { + $("#doEditPost").prop("disabled", true); + $("#postContent").prop("disabled", true); + + var postContent = $("#postContent").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/forum/post/editPost.php', { + postContent: postContent, + csrf: csrf_token, + postId: postId + }) + .done(function(response) { + $("#doEditPost").prop("disabled", false); + $("#postContent").prop("disabled", false); + + if (response == "error") { + $("#rStatus").css("color", "red").html("Network error. Try again later."); + }else if (response == "content-too-short") { + $("#rStatus").css("color", "red").html("Your post is too short."); + }else if (response == "content-too-long") { + $("#rStatus").css("color", "red").html("Your post is too long."); + }else if (response == "no-newline-spam") { + $("#rStatus").css("color", "red").html("Hey there! Your post is a little too spammy, calm down a little with the new lines!"); + }else if (response == "rate-limit") { + $("#rStatus").css("color", "red").html("You are editing too fast. Please wait."); + }else if (response == "no-post") { + $("#rStatus").css("color", "red").html("The post you are trying to edit to does not exist."); + }else if (response == "access-denied") { + $("#rStatus").css("color", "red").html("Access Denied."); + }else{ + $("#rStatus").html(response); + } + }) + .fail(function() { + $("#rStatus").css("color", "red").html("Network error. Try again later."); + }); + } +} + +function lockPost(postId) { + if ($("#lockPost").is(":disabled") == false) { + $("#lockPost").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/forum/post/lockPost.php', { + csrf: csrf_token, + postId: postId + }) + .done(function(response) { + if (response == "error") { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }else{ + loadPost(postId); + } + }) + .fail(function() { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }); + } +} + +function unlockPost(postId) { + if ($("#unlockPost").is(":disabled") == false) { + $("#unlockPost").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/forum/post/unlockPost.php', { + csrf: csrf_token, + postId: postId + }) + .done(function(response) { + if (response == "error") { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }else{ + loadPost(postId); + } + }) + .fail(function() { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }); + } +} + +function pinPost(postId) { + if ($("#pinPost").is(":disabled") == false) { + $("#pinPost").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/forum/post/pinPost.php', { + csrf: csrf_token, + postId: postId + }) + .done(function(response) { + if (response == "error") { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }else{ + loadPost(postId); + } + }) + .fail(function() { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }); + } +} + +function unpinPost(postId) { + if ($("#unpinPost").is(":disabled") == false) { + $("#unpinPost").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/forum/post/unpinPost.php', { + csrf: csrf_token, + postId: postId + }) + .done(function(response) { + if (response == "error") { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }else{ + loadPost(postId); + } + }) + .fail(function() { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }); + } +} + +$(document).ready(function() { + $("#catagories").load("/core/func/api/forum/getCatagories.php"); + var load = false; + var url = window.location.href; + var n = url.indexOf("+"); + if (n > 0) {; + loadPost(url.substring(n+1)); + load = true; + } + var n = url.indexOf("-"); + if (n > 0) {; + loadForum(url.substring(n+1)); + load = true; + } + + if (load == false) { + loadForum(2); + } + + $(window).on('popstate', function() { + var load = false; + var url = window.location.href; + var n = url.indexOf("+"); + if (n > 0) {; + loadPost(url.substring(n+1)); + load = true; + } + var n = url.indexOf("-"); + if (n > 0) {; + loadForum(url.substring(n+1)); + load = true; + } + + if (load == false) { + loadForum(2); + } + }) +}); \ No newline at end of file diff --git a/www/core/func/js/forumModeration.js b/www/core/func/js/forumModeration.js new file mode 100644 index 0000000..d0c6ddb --- /dev/null +++ b/www/core/func/js/forumModeration.js @@ -0,0 +1,54 @@ +//XlXi +//Forum moderation functions + +function deletePost(postId, forumId) { + if ($("#deletePost").is(":disabled") == false) { + if ($("#deletePost").text() != "Are you sure?") { + $("#deletePost").text("Are you sure?"); + }else{ + $("#deletePost").prop("disabled", true); + $("#deletePost").text("Deleting Post..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/forum/post/deletePost.php', { + csrf: csrf_token, + postId: postId + }) + .done(function(response) { + if (response == "error") { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }else{ + loadForum(forumId); + } + }) + .fail(function() { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }); + } + } +} + +function reinstatePost(postId, forumId) { + if ($("#reinstatePost").is(":disabled") == false) { + if ($("#reinstatePost").text() != "Are you sure?") { + $("#reinstatePost").text("Are you sure?"); + }else{ + $("#reinstatePost").prop("disabled", true); + $("#reinstatePost").text("Reinstating Post..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/forum/post/reinstatePost.php', { + csrf: csrf_token, + postId: postId + }) + .done(function(response) { + if (response == "error") { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }else{ + loadPost(postId); + } + }) + .fail(function() { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }); + } + } +} \ No newline at end of file diff --git a/www/core/func/js/friends/main.js b/www/core/func/js/friends/main.js new file mode 100644 index 0000000..fc6fcf0 --- /dev/null +++ b/www/core/func/js/friends/main.js @@ -0,0 +1,33 @@ +function loadFriends(page) { + $("#friendsContainer").load("/core/func/api/friends/get/getFriends.php?page=" + page); +} + +$(document).ready(function() { + loadFriends(0); +}); + +function removeFriend(userID, currentPage) { + if ($(".rmFr").is(":disabled") == false) { + if ($("[value="+userID+"]").text() != "Are you sure?") { + $("[value="+userID+"]").text("Are you sure?"); + }else{ + $(".rmFr").prop("disabled", true); + $("[value="+userID+"]").text("Deleting Friend..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/friends/post/removeFriend.php', { + csrf: csrf_token, + userID: userID + }) + .done(function(response) { + if (response == "error") { + $("#fStatus").html("
    Network error. Try again later.
    "); + }else{ + loadFriends(currentPage); + } + }) + .fail(function() { + $("#fStatus").html("
    Network error. Try again later.
    "); + }); + } + } +} \ No newline at end of file diff --git a/www/core/func/js/friends/profile.js b/www/core/func/js/friends/profile.js new file mode 100644 index 0000000..5a988e3 --- /dev/null +++ b/www/core/func/js/friends/profile.js @@ -0,0 +1,48 @@ +function deleteFriendProfile(userID) { + if ($(".friendBtn").is(":disabled") == false) { + if ($(".friendBtn").text() != "Are you sure?") { + $(".friendBtn").text("Are you sure?"); + }else{ + $(".friendBtn").prop("disabled", true); + $(".friendBtn").text("Deleting Friend..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/friends/post/removeFriend.php', { + csrf: csrf_token, + userID: userID + }) + .done(function(response) { + if (response == "error") { + $("#pStatus").html("
    Network error. Try again later.
    "); + }else{ + $(".friendBtn").remove(); + } + }) + .fail(function() { + $("#pStatus").html("
    Network error. Try again later.
    "); + }); + } + } +} + +function sendRequest(userID) { + $(".friendBtn").prop("disabled", true); + $(".friendBtn").text("Sending..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/friends/post/sendRequest.php', { + csrf: csrf_token, + userID: userID + }) + .done(function(response) { + console.log(response); + if (response == "error") { + $("#pStatus").html("
    Network error. Try again later.
    "); + }else if (response == "rate-limit") { + $("#pStatus").html("
    Please wait before sending another friend request.
    "); + }else{ + $(".friendBtn").remove(); + } + }) + .fail(function() { + $("#pStatus").html("
    Network error. Try again later.
    "); + }); +} \ No newline at end of file diff --git a/www/core/func/js/friends/requests.js b/www/core/func/js/friends/requests.js new file mode 100644 index 0000000..3838a4c --- /dev/null +++ b/www/core/func/js/friends/requests.js @@ -0,0 +1,55 @@ +function loadRequests(page) { + $("#friendsContainer").load("/core/func/api/friends/get/getRequests.php?page=" + page); +} + +$(document).ready(function() { + loadRequests(0); +}); + +function ignoreRequest(userID, currentPage) { + if ($(".btn-xs").is(":disabled") == false) { + if ($("[value="+userID+"ignore]").text() != "Are you sure?") { + $("[value="+userID+"ignore]").text("Are you sure?"); + }else{ + $(".btn-xs").prop("disabled", true); + $("[value="+userID+"ignore]").text("Deleting Friend Request..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/friends/post/ignoreRequest.php', { + csrf: csrf_token, + userID: userID + }) + .done(function(response) { + if (response == "error") { + $("#fStatus").html("
    Network error. Try again later.
    "); + }else{ + loadRequests(currentPage); + } + }) + .fail(function() { + $("#fStatus").html("
    Network error. Try again later.
    "); + }); + } + } +} + +function acceptRequest(userID, currentPage) { + if ($(".btn-xs").is(":disabled") == false) { + $(".btn-xs").prop("disabled", true); + $("[value="+userID+"]").text("Accepting friend request..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/friends/post/acceptRequest.php', { + csrf: csrf_token, + userID: userID + }) + .done(function(response) { + if (response == "error") { + $("#fStatus").html("
    Could not add friend.
    "); + }else{ + loadRequests(currentPage); + } + }) + .fail(function() { + $("#fStatus").html("
    Network error. Try again later.
    "); + }); + } +} \ No newline at end of file diff --git a/www/core/func/js/friends/show.js b/www/core/func/js/friends/show.js new file mode 100644 index 0000000..bf6d319 --- /dev/null +++ b/www/core/func/js/friends/show.js @@ -0,0 +1,3 @@ +function loadFriends(userID, page) { + $("#friendsContainer").load("/core/func/api/friends/get/showFriends.php?userid=" + userID + "&page=" + page); +} \ No newline at end of file diff --git a/www/core/func/js/gamesPage.js b/www/core/func/js/gamesPage.js new file mode 100644 index 0000000..424ce24 --- /dev/null +++ b/www/core/func/js/gamesPage.js @@ -0,0 +1,103 @@ +var refreshId1; +var refreshId2; +var refreshId3; +var version = 0; + +function stopInterval(variable) { + clearInterval(variable); +} + +function doCSS(id) { + if (refreshId1) { + stopInterval(refreshId1); + } + + if (refreshId2) { + stopInterval(refreshId2); + } + + if (refreshId3) { + stopInterval(refreshId3); + } + if (id == 1) { + $("#showPublic").css("background-color", "#ddd"); + $("#showMy").css("background-color", "#f0f0f0"); + $("#showMyS").css("background-color", "#f0f0f0"); + $("#result").load("/core/func/api/games/getPublic.php?version=" + version); + refreshId1 = setInterval(function() { + $("#result").load("/core/func/api/games/getPublic.php?version=" + version); + }, 15000); + }else if (id == 2) { + $("#showPublic").css("background-color", "#f0f0f0"); + $("#ShowMyS").css("background-color", "#f0f0f0"); + $("#showMy").css("background-color", "#ddd"); + $("#result").load("/core/func/api/games/getMy.php?version=" + version); + refreshId2 = setInterval(function() { + $("#result").load("/core/func/api/games/getMy.php?version=" + version); + }, 15000); + }else if (id == 3) { + $("#showPublic").css("background-color", "#f0f0f0"); + $("#showMy").css("background-color", "#f0f0f0"); + $("#showMyS").css("background-color", "#ddd"); + $("#result").load("/core/func/api/games/getMyS.php?version=" + version); + refreshId3 = setInterval(function() { + $("#result").load("/core/func/api/games/getMyS.php?version=" + version); + }, 15000); + } +} + +$(document).ready(function() { + $("#v3").click(function() { + $("#showPublic").css("display", "table-cell"); + $("#showMy").css("display", "table-cell"); + $("#showMyS").css("display", "table-cell"); + $("#v0").addClass("active"); + $(".d16").css("display", "table-cell"); + version = 3; + doCSS(1); + $("#showPublic").click(); + }); + $("#addServer").click(function() { + $.post("/core/func/api/games/addKey.php",{key: $("#serverKey").val()}).done(function(data) { + $("#addKeyResult").html(data); + }).fail(function() { + $("#addKeyResult").css("color", "white").html("
    Could not add a server to your list because an error occurred.
    "); + }); + }); + $("#showPublic").click(function() { + $('#showPublic').attr("disabled", true); + $('#showMy').attr("disabled", false); + $('#showMyS').attr("disabled", false); + doCSS(1); + }); + $("#showMy").click(function() { + $('#showPublic').attr("disabled", false); + $('#showMy').attr("disabled", true); + $('#showMyS').attr("disabled", false); + doCSS(2); + }); + $("#showMyS").click(function() { + $('#showPublic').attr("disabled", false); + $('#showMy').attr("disabled", false); + $('#showMyS').attr("disabled", true); + doCSS(3); + }); + + // Toggle on enter + $("#serverKey").keyup(function(event) { + if(event.keyCode == 13) { + $("#addServer").click(); + } + }) + + $("#v3").click(); +}) + +function viewGame(gameID) { + if (gameID != undefined) { + $(".gameTitle").html("Loading..."); + $(".gameContent").html(''); + $(".gameContent").load("/core/func/api/games/viewGame.php?id=" + gameID); + $('.viewGame').modal('show').hide().fadeIn('fast'); + } +} \ No newline at end of file diff --git a/www/core/func/js/groupAdmin.js b/www/core/func/js/groupAdmin.js new file mode 100644 index 0000000..9a8c858 --- /dev/null +++ b/www/core/func/js/groupAdmin.js @@ -0,0 +1,28 @@ +function changeGroupDescription(groupId) { + if ($("#changeDescription").is(":disabled") == false) { + $("#changeDescription").prop("disabled", true); + $("#descriptionValue").prop("disabled", true); + + var descriptionValue = $("#descriptionValue").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/groups/post/changeDescription.php', { + descriptionValue: descriptionValue, + csrf: csrf_token, + groupId: groupId + }) + .done(function(response) { + $("#changeDescription").prop("disabled", false); + $("#descriptionValue").prop("disabled", false); + if (response == "error") { + $("#aStatus").html("
    Network error. Try again later.
    "); + }else if (response == "description-too-long") { + $("#aStatus").html("
    Your description is too long.
    "); + }else if (response == "success") { + $("#aStatus").html("
    Changed Group Description!
    "); + } + }) + .fail(function() { + $("#aStatus").html("
    Network error. Try again later.
    "); + }); + } +} \ No newline at end of file diff --git a/www/core/func/js/html/navigation.js b/www/core/func/js/html/navigation.js new file mode 100644 index 0000000..08ff2e2 --- /dev/null +++ b/www/core/func/js/html/navigation.js @@ -0,0 +1,110 @@ +function doPing() { + $.get("/core/func/api/auth/ping.php", function(response) { + console.log("Ping complete"); + }) +} + +$(document).ready(function() { + $(function () { + $("[data-toggle='tooltip']").tooltip(); + }); + $('.dropdown').on('show.bs.dropdown', function() { + $(this).find('.dropdown-menu').first().stop(true, true).slideDown(); + }); + + // Add slideUp animation to Bootstrap dropdown when collapsing. + $('.dropdown').on('hide.bs.dropdown', function() { + $(this).find('.dropdown-menu').first().stop(true, true).slideUp(); + }); + + $('#navbarSideButton').click(function() { + $(".navbar-side").show(); + //$(".navbottomMargin").css("margin-bottom", "0px"); + //$("body").css("position", "relative").css("top", "0px"); + $('.navbarSide').addClass('reveal'); + $(".overlay").fadeIn(500); + $('html, body').css({ + overflow: 'hidden', + height: '100%' + }); + }); + + $(".navbottomMargin-m").css("margin-bottom", $(".navbar").css("height")); + + $('.overlay').on('click', function() { + //$(".navbottomMargin").css("margin-bottom", "53px"); + //$("#appContainer").css("position", "initial").css("top", "0px"); + $('.navbarSide').removeClass('reveal'); + $('.overlay').fadeOut(500, function () { + $(".navbar-side").hide(); + }) + $('html, body').css({ + overflow: 'auto', + height: 'auto' + }); + }); + + $(".side-link").on('click', function() { + $(".overlay").click(); + }); + + $("#searchUser").click(function() { + var searchValue = $("#searchValue").val(); + if (searchValue.length != 0) { + if ($("#searchValue").attr("placeholder") == "Username") { + window.location = "/users/" + searchValue; + }else if ($("#searchValue").attr("placeholder") == "Group name") { + window.location = "/groups/search/" + searchValue; + } + }else{ + $("#navSearch").addClass("has-error"); + } + }) + + if ($(window).width() < 1200) { + $("#searchUser").hide(); + $("#searchValue").hide(); + $("#switchSearch").hide(); + }else{ + $("#searchUser").show(); + $("#searchValue").show(); + $("#switchSearch").show(); + } + + $(window).on('resize', function() { + if ($(window).width() < 1200) { + $("#searchUser").hide(); + $("#searchValue").hide(); + $("#switchSearch").hide(); + }else{ + $("#searchUser").show(); + $("#searchValue").show(); + $("#switchSearch").show(); + } + }); + + // Toggle on enter + $("#searchValue").keyup(function(event) { + if(event.keyCode == 13) { + $("#searchUser").click(); + } + }) + + $("#switchSearch").click(function() { + if ($("#searchValue").attr("placeholder") == "Username") { + $("#searchValue").attr("placeholder", "Group name") + }else if ($("#searchValue").attr("placeholder") == "Group name") { + $("#searchValue").attr("placeholder", "Username") + } + }) + + doPing(); + setInterval(function(){ + doPing(); + }, 30000); +}); + +function showDMCA() { + $(".gModalContent").html('

    DMCA

    If you find anything that violates, please send an email. Please remember that Graphictoria is non-profit and also hosted in a country where a DMCA can be ignored.

    Note: If we notice that your message is not coming from the actual sender, we will ignore your email. Do not waste your time sending fake emails because we will find that out. If you are serious about sending legal inqueries, do it from an email we can verify is real.

    We have received a bunch of fake DMCAs up to the point we can never be sure if it is real or fake. If you are sending a real email and are actually the owner of a copyright, use your legal email and use the actual email server so we can verify headers.

    Graphictoria is not willfully wanting to destroy anyone\'s brand or property, we exist because we want to revive older versions of an amazing game and do things not possible on the real platform. We do not sell any memberships, and we will never do so. We give full rights to their respective owners.

    Because we are unable to see who sends an email through CloudFlare abuse (I could impersonate a big company and get away with it, and so can everyone else), we will be ignoring all messages sent through that.

    '); + $('.globalModal').modal({ show: true}); +} \ No newline at end of file diff --git a/www/core/func/js/ide/welcome.js b/www/core/func/js/ide/welcome.js new file mode 100644 index 0000000..b922505 --- /dev/null +++ b/www/core/func/js/ide/welcome.js @@ -0,0 +1,30 @@ +const templates = document.getElementsByClassName('gameCard'); + +function loadFile(file) +{ + window.external.OpenRecentFile(decodeURIComponent(file)); +} + +function loadTemplate(name) +{ + window.external.StartGame('','','https://assetgame.gtoria.net/game/edit?TemplateName=' + name); +} + +function preloadImage(url, templateCard) +{ + var preloadImage = new Image(); + preloadImage.onload = function() { + templateCard.src = url; + }; + preloadImage.src = url; +} + +for(var i = 0; i < templates.length; i++) +{ + var templateCard = templates[i].children.thumbnail; + var templateImage = templateCard.dataset.src; + + templateCard.removeAttribute('data-src'); + + preloadImage(templateImage, templateCard); +} \ No newline at end of file diff --git a/www/core/func/js/item.js b/www/core/func/js/item.js new file mode 100644 index 0000000..11959b1 --- /dev/null +++ b/www/core/func/js/item.js @@ -0,0 +1,50 @@ +function buyItem(itemId) { + if ($("#buyItem").is(":disabled") == false) { + $("#buyItem").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/catalog/post/buyItem.php', { + csrf: csrf_token, + itemId: itemId + }) + .done(function(response) { + console.log(response); + if (response == "error") { + $("#iStatus").html("
    Could not buy this item.
    "); + }else{ + $("#iStatus").html("
    You have bought this item!
    "); + $("#buyItem").text("Already owned"); + if (cType == "coins") + $("#userCoins").html(response); + if (cType == "posties") + $("#userPosties").html(response); + } + }) + .fail(function() { + $("#iStatus").html("
    Could not buy this item because a network error occurred. Try again later.
    "); + }); + } +} + +function deleteItem(itemId) { + if ($("#deleteItem").is(":disabled") == false) { + $("#deleteItem").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/catalog/post/deleteItem.php', { + csrf: csrf_token, + itemId: itemId + }) + .done(function(response) { + console.log(response); + if (response == "error") { + $("#iStatus").html("
    Could not delete this item.
    "); + }else if (response == "success") { + window.location = "/catalog/"; + }else{ + $("#iStatus").html("
    Could not delete this item because a network error occurred. Try again later.
    "); + } + }) + .fail(function() { + $("#iStatus").html("
    Could not delete this item because a network error occurred. Try again later.
    "); + }); + } +} \ No newline at end of file diff --git a/www/core/func/js/messages.js b/www/core/func/js/messages.js new file mode 100644 index 0000000..694fa2e --- /dev/null +++ b/www/core/func/js/messages.js @@ -0,0 +1,106 @@ +var page = 0; +function loadMessages(filterId) { + $("#messages").html('


    '); + $("#messages").load("/core/func/api/messages/getMessages.php?filter=" + filterId); + $("html, body").animate({ scrollTop: 0 }, "slow"); + page = 0; +} + +function loadMore(page, filterId) { + $(".loadMore").remove(); + $.get("/core/func/api/messages/getMessages.php?filter=" + filterId + "&page=" + page, function(data) { + $("#messages").append(data); + }); +} + +function loadMessage(id) { + $("#messages").html('


    '); + $("#messages").load("/core/func/api/messages/showMessage.php?id=" + id); + $("html, body").animate({ scrollTop: 0 }, "slow"); + page = 0; +} + +function sendMessage(username) { + $("#messages").html('


    '); + $("#messages").load("/core/func/api/messages/newMessage.php?username=" + username); +} + +function sendMessagePost(userID) { + if ($("#sendMessage").is(":disabled") == false) { + $("#sendMessage").prop("disabled", true); + $("#messageContent").prop("disabled", true); + $("#messageTitle").prop("disabled", true); + + var messageTitle = $("#messageTitle").val(); + var messageContent = $("#messageContent").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/messages/post/newMessage.php', { + messageTitle: messageTitle, + messageContent: messageContent, + csrf: csrf_token, + userID: userID + }) + .done(function(response) { + $("#sendMessage").prop("disabled", false); + $("#messageContent").prop("disabled", false); + $("#messageTitle").prop("disabled", false); + if (response == "error") { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }else if (response == "title-too-short") { + $("#pStatus").css("color", "red").html("Your title is too short."); + }else if (response == "title-too-long") { + $("#pStatus").css("color", "red").html("Your title is too long."); + }else if (response == "content-too-short") { + $("#pStatus").css("color", "red").html("Your message is too short."); + }else if (response == "content-too-long") { + $("#pStatus").css("color", "red").html("Your message is too long."); + }else if (response == "no-user") { + $("#pStatus").css("color", "red").html("The user you are sending a message to does not exist."); + }else if (response == "no-banned") { + $("#pStatus").css("color", "red").html("The user you are sending a message to has been banned."); + }else if (response == "no-banned") { + $("#pStatus").css("color", "red").html("The user you are sending a message to has been banned."); + }else if (response == "success") { + loadMessages(0); + window.history.pushState("", "", '/user/messages'); + $("#mStatus").html("
    Your message has been sent!
    "); + } + }) + .fail(function() { + $("#pStatus").css("color", "red").html("Network error. Try again later."); + }); + } +} + +$(document).ready(function() { + var load = false; + var url = window.location.href; + var n = url.indexOf("+"); + if (n > 0) {; + sendMessage(url.substring(n+1)); + load = true; + } + + if (load == false) { + loadMessages(0); + } + $("#allMessages").click(function() { + console.log("All Messages"); + loadMessages(0); + }) + + $("#unreadMessages").click(function() { + console.log("Unread Messages"); + loadMessages(1); + }) + + $("#readMessages").click(function() { + console.log("Read Messages"); + loadMessages(2); + }) + + $("#sentOnly").click(function() { + console.log("Sent Only"); + loadMessages(3); + }) +}); \ No newline at end of file diff --git a/www/core/func/js/profile.js b/www/core/func/js/profile.js new file mode 100644 index 0000000..830061a --- /dev/null +++ b/www/core/func/js/profile.js @@ -0,0 +1,40 @@ +$(document).ready(function() { + switchTo("hats"); + + $("#showHats").click(function() { + switchTo("hats"); + }) + + $("#showHeads").click(function() { + switchTo("heads"); + }) + + $("#showFaces").click(function() { + switchTo("faces"); + }) + + $("#showTshirts").click(function() { + switchTo("tshirts"); + }) + + $("#showShirts").click(function() { + switchTo("shirts"); + }) + + $("#showPants").click(function() { + switchTo("pants"); + }) + + $("#showGear").click(function() { + switchTo("gear"); + }) +}); + +function switchTo(type) { + type = type; + $("#inventoryItems").load("/core/func/api/profile/getInventory.php?type=" + type + "&userId=" + userId); +} + +function loadPage(type, page) { + $("#inventoryItems").load("/core/func/api/profile/getInventory.php?type=" + type + "&userId=" + userId + "&page=" + page); +} diff --git a/www/core/func/js/searchGroup.js b/www/core/func/js/searchGroup.js new file mode 100644 index 0000000..a7b5ef0 --- /dev/null +++ b/www/core/func/js/searchGroup.js @@ -0,0 +1,17 @@ +$(document).ready(function() { + $("#doSearch_2").click(function() { + var searchValue = $("#searchValue_2").val() + $.get("/core/func/api/groups/searchGroup.php?term=" + searchValue + "&page=0", function(data) { + $("#searchResults").html(data); + }); + }) + + $("#doSearch_2").click(); +}); + +function loadMore(page, term) { + $(".searchGroup").remove(); + $.get("/core/func/api/groups/searchGroup.php?term=" + term + "&page=" + page, function(data) { + $("#searchResults").append(data); + }); +} \ No newline at end of file diff --git a/www/core/func/js/settings.js b/www/core/func/js/settings.js new file mode 100644 index 0000000..b08c67c --- /dev/null +++ b/www/core/func/js/settings.js @@ -0,0 +1,235 @@ +$(document).ready(function() { + $("#updateAbout").click(function() { + if ($("#updateAbout").is(":disabled") == false) { + $("#updateAbout").prop("disabled", true); + $("#aboutValue").prop("disabled", true); + + var aboutContent = $("#aboutValue").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/settings/post/updateAbout.php', { + aboutContent: aboutContent, + csrf: csrf_token + }) + .done(function(response) { + $("#updateAbout").prop("disabled", false); + $("#aboutValue").prop("disabled", false); + if (response == "filtered") { + $("#sStatus").html("
    Your about text contains bad words! Please remove them
    "); + $("#aboutValue").val(""); + }else{ + $("#sStatus").html("
    About text changed!
    "); + } + }) + .fail(function() { + $("#sStatus").html("
    Network error. Try again later.
    "); + }); + } + }) + + $("#enableRegular").click(function() { + if ($("#enableRegular").is(":disabled") == false) { + $("#enableRegular").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/settings/post/updateTheme.php', { + theme: 0, + csrf: csrf_token + }) + .done(function(response) { + $("#enableRegular").prop("disabled", false); + $("#aboutValue").prop("disabled", false); + if (response == "success") { + window.location.reload(); + }else{ + $("#sStatus").html("
    Network error. Try again later.
    "); + } + }) + .fail(function() { + $("#sStatus").html("
    Network error. Try again later.
    "); + }); + } + }) + + $("#enableDark").click(function() { + if ($("#enableRegular").is(":disabled") == false) { + $("#enableDark").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/settings/post/updateTheme.php', { + theme: 1, + csrf: csrf_token + }) + .done(function(response) { + $("#enableDark").prop("disabled", false); + $("#aboutValue").prop("disabled", false); + if (response == "success") { + window.location.reload(); + }else{ + $("#sStatus").html("
    Network error. Try again later.
    "); + } + }) + .fail(function() { + $("#sStatus").html("
    Network error. Try again later.
    "); + }); + } + }) + + $("#changePassword").click(function() { + if ($("#changePassword").is(":disabled") == false) { + $("#changePassword").prop("disabled", true); + $("#nPassword1").prop("disabled", true); + $("#nPassword2").prop("disabled", true); + $("#curPassword").prop("disabled", true); + + var npass1 = $("#nPassword1").val(); + var npass2 = $("#nPassword2").val(); + var currentPassword = $("#curPassword").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/settings/post/changePassword.php', { + newPassword1: npass1, + newPassword2: npass2, + currentPassword: currentPassword, + csrf: csrf_token + }) + .done(function(response) { + $("#changePassword").prop("disabled", false); + $("#nPassword1").prop("disabled", false); + $("#nPassword2").prop("disabled", false); + $("#curPassword").prop("disabled", false); + + if (response == "error") { + $("#cPassStatus").html("
    Network error. Try again later.
    "); + }else if (response == "missing-info") { + $("#cPassStatus").html("
    Please fill in everything
    "); + }else if (response == "confirm-failed") { + $("#cPassStatus").html("
    Password confirmation has failed
    "); + }else if (response == "password-too-short") { + $("#cPassStatus").html("
    Your new password is too short
    "); + }else if (response == "password-too-long") { + $("#cPassStatus").html("
    Your new password is too long
    "); + }else if (response == "wrong-password") { + $("#cPassStatus").html("
    Your current password is incorrect
    "); + }else if (response == "success") { + window.location = "/"; + } + }) + .fail(function() { + $("#cPassStatus").html("
    Network error. Try again later.
    "); + }); + } + }) + + $("#changeEmail").click(function() { + if ($("#changeEmail").is(":disabled") == false) { + $("#changeEmail").prop("disabled", true); + $("#email").prop("disabled", true); + $("#password").prop("disabled", true); + + var newEmail = $("#email").val(); + var currentPassword = $("#password").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/settings/post/changeEmail.php', { + newEmail: newEmail, + currentPassword: currentPassword, + csrf: csrf_token + }) + .done(function(response) { + $("#changeEmail").prop("disabled", false); + $("#email").prop("disabled", false); + $("#password").prop("disabled", false); + if (response == "error") { + $("#cEmailStatus").html("
    Network error. Try again later.
    "); + }else if (response == "missing-info") { + $("#cEmailStatus").html("
    Please fill in everything
    "); + }else if (response == "success") { + window.location = "/"; + }else if (response == "wrong-password") { + $("#cEmailStatus").html("
    Your current password is incorrect
    "); + }else if (response == "rate-limit") { + $("#cEmailStatus").html("
    You have recently verified your current email, please wait at least 5 minutes before doing this
    "); + }else if (response == "unknown-email") { + $("#cEmailStatus").html("
    You are using an unknown email provider. Use a more known one such as hotmail or gmail
    "); + }else if (response == "email-in-use") { + $("#cEmailStatus").html("
    That email is already in use by another user.
    "); + } + }) + .fail(function() { + $("#cEmailStatus").html("
    Network error. Try again later.
    "); + }); + } + }) + + $("#enableTwo").click(function() { + if ($("#enableTwo").is(":disabled") == false) { + $("#enableTwo").prop("disabled", true); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/settings/post/enableTwo.php', { + csrf: csrf_token + }) + .done(function(response) { + $("#updateAbout").prop("disabled", false); + $("#aboutValue").prop("disabled", false); + if (response == "success") { + $("#twocontainer").load("/core/func/api/settings/get/twoStep.php"); + }else{ + $("#sStatus").html("
    Network error. Try again later.
    "); + } + }) + .fail(function() { + $("#sStatus").html("
    Network error. Try again later.
    "); + }); + } + }) +}); + +function enableTwoFinal() { + if ($("#enableTwoFinal").is(":disabled") == false) { + $("#enableTwoFinal").prop("disabled", true); + $("#finalCode").prop("disabled", true); + var finalCode = $("#finalCode").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/settings/post/enableTwoFinal.php', { + finalCode: finalCode, + csrf: csrf_token + }) + .done(function(response) { + $("#enableTwoFinal").prop("disabled", false); + $("#finalCode").prop("disabled", false); + if (response == "success") { + $("#sStatus").html("
    Two Step Authentication has been enabled! You will be asked to use it the next time you login.
    "); + $("#twocontainer").load("/core/func/api/settings/get/twoStep.php"); + }else if(response == "missing-info") { + $("#sStatus").html("
    Please enter your authentication code.
    "); + }else if(response == "wrong-code") { + $("#sStatus").html("
    Your authentication code is incorrect. Did you use the Authy or Google authentication mobile app?
    "); + }else{ + $("#sStatus").html("
    Network error. Try again later.
    "); + } + }) + .fail(function() { + $("#sStatus").html("
    Network error. Try again later.
    "); + }); + } +} + +function disableFactor() { + if ($("#disableTwo").is(":disabled") == false) { + $("#disableTwo").prop("disabled", true); + var finalCode = $("#finalCode").val(); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/settings/post/disableTwo.php', { + csrf: csrf_token + }) + .done(function(response) { + $("#disableTwo").prop("disabled", false); + if (response == "success") { + $("#twocontainer").load("/core/func/api/settings/get/twoStep.php"); + }else if (response == "staff-block") { + $("#sStatus").html("
    Staff members may not disable this feature.
    "); + }else{ + $("#sStatus").html("
    Network error. Try again later.
    ") + } + }) + .fail(function() { + $("#sStatus").html("
    Network error. Try again later.
    "); + }); + } +} \ No newline at end of file diff --git a/www/core/func/js/uploadItem.js b/www/core/func/js/uploadItem.js new file mode 100644 index 0000000..994ec1e --- /dev/null +++ b/www/core/func/js/uploadItem.js @@ -0,0 +1,83 @@ +$(document).ready(function() { + $(document).on('change', '#itemTypeValue', function() { + if ($(this).find("option:selected").attr('value') == 3) { + $("#itempriceContainer").css("display", "none"); + }else{ + $("#itempriceContainer").css("display", "block"); + } + }) + + $("#uploadItem").click(function() { + if ($("#uploadItem").is(":disabled") == false) { + $("#uploadItem").prop("disabled", true); + $("#itemNameValue").prop("disabled", true); + $("#itemDescriptionValue").prop("disabled", true); + $("#itemTypeValue").prop("disabled", true); + $("#itemPriceValue").prop("disabled", true); + $("#fileValue").prop("disabled", true); + + var formData = new FormData(); + formData.append('file', $('#fileValue')[0].files[0]); + formData.append('itemName', $("#itemNameValue").val()); + formData.append('itemDescription', $("#itemDescriptionValue").val()); + formData.append('itemType', $("#itemTypeValue").val()); + formData.append('itemPrice', $("#itemPriceValue").val()); + formData.append('csrf_token', $('meta[name="csrf-token"]').attr('content')); + $.ajax({ + type: "POST", + url : "/core/func/api/catalog/post/uploadItem.php", + data : formData, + cache: false, + contentType: false, + processData: false, + success: function(data) { + console.log(data); + $("#uploadItem").prop("disabled", false); + $("#itemNameValue").prop("disabled", false); + $("#itemDescriptionValue").prop("disabled", false); + $("#itemTypeValue").prop("disabled", false); + $("#itemPriceValue").prop("disabled", false); + $("#fileValue").prop("disabled", false); + + if (data == "error") { + $("#uploadStatus").html("
    Could not upload item because a network error occurred.
    "); + }else if (data == "name-too-short") { + $("#uploadStatus").html("
    Your item name is too short. Try something else.
    "); + }else if (data == "name-too-long") { + $("#uploadStatus").html("
    Your item name is too long. Try something else.
    "); + }else if (data == "description-too-long") { + $("#uploadStatus").html("
    Your description is too long. Try something else.
    "); + }else if (data == "price-too-low") { + $("#uploadStatus").html("
    Your item price is too low.
    "); + }else if (data == "rate-limit") { + $("#uploadStatus").html("
    Please wait a bit before uploading again.
    "); + }else if (data == "rate-limit") { + $("#uploadStatus").html("
    Please wait a bit before uploading again.
    "); + }else if (data == "incorrect-size") { + $("#uploadStatus").html("
    Your image has an incorrect size. Did you use the template?
    "); + }else if (data == "no-image") { + $("#uploadStatus").html("
    The file you have tried to upload is not an image.
    "); + }else if (data == "file-too-large") { + $("#uploadStatus").html("
    The file you have tried to upload is too large.
    "); + }else if (data == "incorrect-extension") { + $("#uploadStatus").html("
    The file you have tried to upload is not a valid image.
    "); + }else if (data == "not-enough-coins") { + $("#uploadStatus").html("
    You do not have enough coins to upload an item.
    "); + }else if (data == "file-upload-error") { + $("#uploadStatus").html("
    An error occured while uploading your item. Please contact an administrator.
    "); + }else if (data == "no-file") { + $("#uploadStatus").html("
    Please select a file to upload.
    "); + }else if (data == "bad-hash") { + $("#uploadStatus").html("
    This item can not be uploaded to Graphictoria.
    "); + }else{ + $("#uploadStatus").html("
    Your file has been uploaded. It will appear in the catalog when it has been approved.
    "); + $("#userCoins").html(data); + } + }, + error: function() { + $("#uploadStatus").html("
    Could not upload item because a network error occurred.
    "); + } + }); + } + }) +}); \ No newline at end of file diff --git a/www/core/func/js/users.js b/www/core/func/js/users.js new file mode 100644 index 0000000..bc4e570 --- /dev/null +++ b/www/core/func/js/users.js @@ -0,0 +1,17 @@ +$(document).ready(function() { + $("#doSearch_2").click(function() { + var searchValue = $("#searchValue_2").val() + $.get("/core/func/api/users/searchUser.php?term=" + searchValue + "&page=0", function(data) { + $("#searchResults").html(data); + }); + }) + + $("#doSearch_2").click(); +}); + +function loadMore(page, term) { + $(".searchUser").remove(); + $.get("/core/func/api/users/searchUser.php?term=" + term + "&page=" + page, function(data) { + $("#searchResults").append(data); + }); +} \ No newline at end of file diff --git a/www/core/func/js/viewGroup.js b/www/core/func/js/viewGroup.js new file mode 100644 index 0000000..ec88943 --- /dev/null +++ b/www/core/func/js/viewGroup.js @@ -0,0 +1,81 @@ +function getMembers(groupId, page) { + $("#memberField").load("/core/func/api/groups/get/getMembers.php?gid=" + groupId + "&page=" + page); +} + +function leaveDelete(groupId) { + if ($("#leaveDelete").is(":disabled") == false) { + if ($("#leaveDelete").text() != "Are you sure?") { + $("#leaveDelete").text("Are you sure?"); + }else{ + $("#leaveDelete").prop("disabled", true); + $("#leaveDelete").text("Leaving and deleting your group..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/groups/post/leaveDelete.php', { + csrf: csrf_token, + groupId: groupId + }) + .done(function(response) { + if (response == "error") { + $("#gStatus").html("
    Could not leave your group because a network error occurred.
    "); + }else{ + window.location = "/groups"; + } + }) + .fail(function() { + $("#gStatus").html("
    Could not leave your group because a network error occurred.
    "); + }); + } + } +} + +function leaveGroup(groupId) { + if ($("#leaveGroup").is(":disabled") == false) { + if ($("#leaveGroup").text() != "Are you sure?") { + $("#leaveGroup").text("Are you sure?"); + }else{ + $("#leaveGroup").prop("disabled", true); + $("#leaveGroup").text("Leaving group..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/groups/post/leaveGroup.php', { + csrf: csrf_token, + groupId: groupId + }) + .done(function(response) { + if (response == "error") { + $("#gStatus").html("
    Could not leave your group because a network error occurred.
    "); + }else{ + $("#leaveGroup").remove(); + getMembers(groupId, 0); + } + }) + .fail(function() { + $("#gStatus").html("
    Could not leave your group because a network error occurred.
    "); + }); + } + } +} + +function joinGroup(groupId) { + if ($("#joinGroup").is(":disabled") == false) { + $("#joinGroup").prop("disabled", true); + $("#joinGroup").text("Joining group..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/groups/post/joinGroup.php', { + csrf: csrf_token, + groupId: groupId + }) + .done(function(response) { + if (response == "error") { + $("#gStatus").html("
    Could not join this group because a network error occurred.
    "); + }else if (response == "in-too-many-groups") { + $("#gStatus").html("
    You are in too many groups.
    "); + }else{ + $("#joinGroup").remove(); + getMembers(groupId, 0); + } + }) + .fail(function() { + $("#gStatus").html("
    Could not join this group because a network error occurred.
    "); + }); + } +} \ No newline at end of file diff --git a/www/core/func/js/viewServer.js b/www/core/func/js/viewServer.js new file mode 100644 index 0000000..7d41be8 --- /dev/null +++ b/www/core/func/js/viewServer.js @@ -0,0 +1,25 @@ +function deleteServer(serverID) { + if ($("#deletePost").is(":disabled") == false) { + if ($("#deleteServer").text() != "Are you sure?") { + $("#deleteServer").text("Are you sure?"); + }else{ + $("#deleteServer").prop("disabled", true); + $("#deleteServer").text("Deleting Server..."); + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + $.post('/core/func/api/games/post/deleteServer.php', { + csrf: csrf_token, + serverID: serverID + }) + .done(function(response) { + if (response == "error") { + $("#vStatus").html("
    Could not delete this server because a network error occurred
    ") + }else if (response == "success") { + window.location = "/games"; + } + }) + .fail(function() { + $("#vStatus").html("
    Could not delete this server because a network error occurred
    "); + }); + } + } +} \ No newline at end of file diff --git a/www/core/func/libs/aes/GibberishAES.php b/www/core/func/libs/aes/GibberishAES.php new file mode 100644 index 0000000..4e8df5c --- /dev/null +++ b/www/core/func/libs/aes/GibberishAES.php @@ -0,0 +1,540 @@ += 5.3.3 + * or + * Mcrypt functions installed and PHP version < 7.1.0-alpha + * + * For PHP under version 7 it is recommendable you to install within your project + * "PHP 5.x support for random_bytes() and random_int()", + * @link https://github.com/paragonie/random_compat + * + * Usage: + * + * // This is a secret pass-phrase, keep it in a safe place and don't loose it. + * $pass = 'my secret pass-phrase, it should be long'; + * + * // The string to be encrypted. + * $string = 'my secret message'; + * + * // This is the result after encryption of the given string. + * $encrypted_string = GibberishAES::enc($string, $pass); + * + * // This is the result after decryption of the previously encrypted string. + * // $decrypted_string == $string (should be). + * $decrypted_string = GibberishAES::dec($encrypted_string, $pass); + * echo $decrypted_string; + * + * // The default key-size is 256 bits. 128 and 192 bits are also allowed. + * // Example: + * $old_key_size = GibberishAES::size(); + * GibberishAES::size(192); + * // The short way: $old_key_size = GibberishAES::size(192); + * $encrypted_string = GibberishAES::enc($string, $pass); + * $decrypted_string = GibberishAES::dec($encrypted_string, $pass); + * GibberishAES::size($old_key_size); + * echo $decrypted_string; + * + * @author Ivan Tcholakov , 2012-2016. + * Code repository: @link https://github.com/ivantcholakov/gibberish-aes-php + * + * @version 1.3.1 + * + * @license The MIT License (MIT) + * @link http://opensource.org/licenses/MIT + */ + +class GibberishAES { + + // The default key size in bits. + protected static $key_size = 256; + + // The allowed key sizes in bits. + protected static $valid_key_sizes = array(128, 192, 256); + + protected static $random_bytes_exists = null; + protected static $openssl_encrypt_exists = null; + protected static $openssl_decrypt_exists = null; + protected static $mcrypt_exists = null; + protected static $mbstring_func_overload = null; + + // This is a static class, instances are disabled. + final private function __construct() {} + final private function __clone() {} + + /** + * Crypt AES (256, 192, 128) + * + * @param string $string The input message to be encrypted. + * @param string $pass The secret pass-phrase, choose a long string + * (64 characters for example) for keeping high entropy. + * The pass-phrase is converted internaly into + * a binary key that is to be used for encryption. + * @return mixed base64 encrypted string, FALSE on failure. + */ + public static function enc($string, $pass) { + + $key_size = self::$key_size; + + // Set a random salt. + $salt = self::random_bytes(8); + + $salted = ''; + $dx = ''; + + // Lengths in bytes: + $key_length = (int) ($key_size / 8); + $block_length = 16; // 128 bits, iv has the same length. + // $salted_length = $key_length (32, 24, 16) + $block_length (16) = (48, 40, 32) + $salted_length = $key_length + $block_length; + + while (self::strlen($salted) < $salted_length) { + + $dx = md5($dx.$pass.$salt, true); + $salted .= $dx; + } + + $key = self::substr($salted, 0, $key_length); + $iv = self::substr($salted, $key_length, $block_length); + + $encrypted = self::aes_cbc_encrypt($string, $key, $iv); + + return $encrypted !== false ? base64_encode('Salted__'.$salt.$encrypted) : false; + } + + /** + * Decrypt AES (256, 192, 128) + * + * @param string $string The input message to be decrypted. + * @param string $pass The secret pass-phrase that has been used for encryption. + * @return mixed base64 decrypted string, FALSE on failure. + */ + public static function dec($string, $pass) { + + $key_size = self::$key_size; + + // Lengths in bytes: + $key_length = (int) ($key_size / 8); + $block_length = 16; + + $data = base64_decode($string); + $salt = self::substr($data, 8, 8); + $encrypted = self::substr($data, 16); + + /** + * From https://github.com/mdp/gibberish-aes + * + * Number of rounds depends on the size of the AES in use + * 3 rounds for 256 + * 2 rounds for the key, 1 for the IV + * 2 rounds for 128 + * 1 round for the key, 1 round for the IV + * 3 rounds for 192 since it's not evenly divided by 128 bits + */ + $rounds = 3; + if ($key_size == 128) { + $rounds = 2; + } + + $data00 = $pass.$salt; + $md5_hash = array(); + $md5_hash[0] = md5($data00, true); + $result = $md5_hash[0]; + + for ($i = 1; $i < $rounds; $i++) { + + $md5_hash[$i] = md5($md5_hash[$i - 1].$data00, true); + $result .= $md5_hash[$i]; + } + + $key = self::substr($result, 0, $key_length); + $iv = self::substr($result, $key_length, $block_length); + + return self::aes_cbc_decrypt($encrypted, $key, $iv); + } + + /** + * Sets the key-size for encryption/decryption in number of bits + * @param mixed $newsize The new key size. The valid integer values are: 128, 192, 256 (default) + * $newsize may be NULL or may be omited - in this case + * this method is just a getter of the current key size value. + * @return integer Returns the old key size value. + */ + public static function size($newsize = null) { + + $result = self::$key_size; + + if (is_null($newsize)) { + return $result; + } + + $newsize = (string) $newsize; + + if ($newsize == '') { + return $result; + } + + $valid_integer = ctype_digit($newsize); + + $newsize = (int) $newsize; + + if (!$valid_integer || !in_array($newsize, self::$valid_key_sizes)) { + trigger_error('GibberishAES: Invalid key size value was to be set. It should be an integer value (number of bits) amongst: '.implode(', ', self::$valid_key_sizes).'.', E_USER_WARNING); + } else { + self::$key_size = $newsize; + } + + return $result; + } + + // Non-public methods ------------------------------------------------------ + + protected static function random_bytes_exists() { + + if (!isset(self::$random_bytes_exists)) { + + self::$random_bytes_exists = false; + + if (function_exists('random_bytes')) { + + try + { + $test = random_bytes(1); + self::$random_bytes_exists = true; + } + catch (Exception $e) { + // Do nothing. + } + } + } + + return self::$random_bytes_exists; + } + + protected static function openssl_encrypt_exists() { + + if (!isset(self::$openssl_encrypt_exists)) { + self::$openssl_encrypt_exists = function_exists('openssl_encrypt') + // We need the $iv parameter. + && version_compare(PHP_VERSION, '5.3.3', '>='); + } + + return self::$openssl_encrypt_exists; + } + + protected static function openssl_decrypt_exists() { + + if (!isset(self::$openssl_decrypt_exists)) { + self::$openssl_decrypt_exists = function_exists('openssl_decrypt') + // We need the $iv parameter. + && version_compare(PHP_VERSION, '5.3.3', '>='); + } + + return self::$openssl_decrypt_exists; + } + + protected static function mcrypt_exists() { + + if (!isset(self::$mcrypt_exists)) { + + if (version_compare(PHP_VERSION, '7.1.0-alpha', '>=')) { + // Avoid using mcrypt on PHP 7.1.x since deprecation notices are thrown. + self::$mcrypt_exists = false; + } else { + self::$mcrypt_exists = function_exists('mcrypt_encrypt'); + } + } + + return self::$mcrypt_exists; + } + + protected static function is_windows() { + + // Beware about 'Darwin'. + return 0 === stripos(PHP_OS, 'win'); + } + + protected static function mbstring_func_overload() { + + if (!isset(self::$mbstring_func_overload)) { + self::$mbstring_func_overload = extension_loaded('mbstring') && ini_get('mbstring.func_overload'); + } + + return self::$mbstring_func_overload; + } + + protected static function strlen($str) { + + return self::mbstring_func_overload() ? mb_strlen($str, '8bit') : strlen($str); + } + + protected static function substr($str, $start, $length = null) { + + if (self::mbstring_func_overload()) { + + // mb_substr($str, $start, null, '8bit') returns an empty string on PHP 5.3 + isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start); + + return mb_substr($str, $start, $length, '8bit'); + } + + return isset($length) ? substr($str, $start, $length) : substr($str, $start); + } + + protected static function random_bytes($length) { + + $length = (int) $length; + + if (self::random_bytes_exists()) { + + try + { + return random_bytes($length); + } + catch (Exception $e) { + // Do nothing, continue. + } + } + + // Rename the parameter on order it to fit with the code below. + $len = $length; + + /* + * The following code fragment has been taken from Secure-random-bytes-in-PHP + * project, released under the New BSD License. + * @see https://github.com/GeorgeArgyros/Secure-random-bytes-in-PHP + * + * + * + * Author: + * George Argyros + * + * Copyright (c) 2012, George Argyros + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the 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 GEORGE ARGYROS 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. + * + * + * + * The function is providing, at least at the systems tested :), + * $len bytes of entropy under any PHP installation or operating system. + * The execution time should be at most 10-20 ms in any system. + */ + + $SSLstr = '4'; // http://xkcd.com/221/ + + /* + * No build-in crypto randomness function found. We collect any entropy + * available in the PHP core PRNGs along with some filesystem info and memory + * stats. To make this data cryptographically strong we add data either from + * /dev/urandom or if its unavailable, we gather entropy by measuring the + * time needed to compute a number of SHA-1 hashes. + */ + $str = ''; + $bits_per_round = 2; // bits of entropy collected in each clock drift round + $msec_per_round = 400; // expected running time of each round in microseconds + $hash_len = 20; // SHA-1 Hash length + $total = $len; // total bytes of entropy to collect + $handle = @fopen('/dev/urandom', 'rb'); + if ($handle && function_exists('stream_set_read_buffer')) { + @stream_set_read_buffer($handle, 0); + } + + do { + $bytes = ($total > $hash_len) ? $hash_len : $total; + $total -= $bytes; + //collect any entropy available from the PHP system and filesystem + $entropy = rand() . uniqid(mt_rand(), true) . $SSLstr; + $entropy .= implode('', @fstat(@fopen(__FILE__, 'r'))); + $entropy .= memory_get_usage() . getmypid(); + $entropy .= serialize($_ENV) . serialize($_SERVER); + if (function_exists('posix_times')) { + $entropy .= serialize(posix_times()); + } + if (function_exists('zend_thread_id')) { + $entropy .= zend_thread_id(); + } + if ($handle) { + $entropy .= @fread($handle, $bytes); + } else { + // Measure the time that the operations will take on average + for ($i = 0; $i < 3; $i++) { + $c1 = microtime(true); + $var = sha1(mt_rand()); + for ($j = 0; $j < 50; $j++) { + $var = sha1($var); + } + $c2 = microtime(true); + $entropy .= $c1 . $c2; + } + // Based on the above measurement determine the total rounds + // in order to bound the total running time. + $rounds = (int) ($msec_per_round * 50 / (int) (($c2 - $c1) * 1000000)); + // Take the additional measurements. On average we can expect + // at least $bits_per_round bits of entropy from each measurement. + $iter = $bytes * (int) (ceil(8 / $bits_per_round)); + for ($i = 0; $i < $iter; $i++) { + $c1 = microtime(); + $var = sha1(mt_rand()); + for ($j = 0; $j < $rounds; $j++) { + $var = sha1($var); + } + $c2 = microtime(); + $entropy .= $c1 . $c2; + } + } + // We assume sha1 is a deterministic extractor for the $entropy variable. + $str .= sha1($entropy, true); + + // Modified by Ivan Tcholakov, 16-MAR-2015. + //} while ($len > strlen($str)); + } while ($len > self::strlen($str)); + // + + if ($handle) { + @fclose($handle); + } + + // Modified by Ivan Tcholakov, 16-MAR-2015. + //return substr($str, 0, $len); + return self::substr($str, 0, $len); + // + + /* + * End of code fragment from Secure-random-bytes-in-PHP project. + */ + } + + protected static function aes_cbc_encrypt($string, $key, $iv) { + + $key_size = self::$key_size; + + if (self::openssl_encrypt_exists()) { + return openssl_encrypt($string, "aes-$key_size-cbc", $key, true, $iv); + } + + if (self::mcrypt_exists()) { + + // Info: http://www.chilkatsoft.com/p/php_aes.asp + // http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation + + $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); + + if (mcrypt_generic_init($cipher, $key, $iv) != -1) { + + $encrypted = mcrypt_generic($cipher, self::pkcs7_pad($string)); + mcrypt_generic_deinit($cipher); + mcrypt_module_close($cipher); + + return $encrypted; + } + + return false; + } + + trigger_error('GibberishAES: System requirements failure, please, check them.', E_USER_WARNING); + + return false; + } + + protected static function aes_cbc_decrypt($crypted, $key, $iv) { + + $key_size = self::$key_size; + + if (self::openssl_decrypt_exists()) { + return openssl_decrypt($crypted, "aes-$key_size-cbc", $key, true, $iv); + } + + if (self::mcrypt_exists()) { + + $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); + + if (mcrypt_generic_init($cipher, $key, $iv) != -1) { + + $decrypted = mdecrypt_generic($cipher, $crypted); + mcrypt_generic_deinit($cipher); + mcrypt_module_close($cipher); + + return self::remove_pkcs7_pad($decrypted); + } + + return false; + } + + trigger_error('GibberishAES: System requirements failure, please, check them.', E_USER_WARNING); + + return false; + } + + // See http://www.php.net/manual/en/function.mcrypt-decrypt.php#105985 + + protected static function pkcs7_pad($string) { + + // 128 bits: $block_length = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); + $block_length = 16; + $pad = $block_length - (self::strlen($string) % $block_length); + + return $string.str_repeat(chr($pad), $pad); + } + + protected static function remove_pkcs7_pad($string) { + + // 128 bits: $block_length = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); + $block_length = 16; + $len = self::strlen($string); + $pad = ord($string[$len - 1]); + + if ($pad > 0 && $pad <= $block_length) { + + $valid_pad = true; + + for ($i = 1; $i <= $pad; $i++) { + + if (ord($string[$len - $i]) != $pad) { + $valid_pad = false; + break; + } + } + + if ($valid_pad) { + $string = self::substr($string, 0, $len - $pad); + } + } + + return $string; + } + +} diff --git a/www/core/func/libs/google/FixedByteNotation.php b/www/core/func/libs/google/FixedByteNotation.php new file mode 100644 index 0000000..97d22f7 --- /dev/null +++ b/www/core/func/libs/google/FixedByteNotation.php @@ -0,0 +1,276 @@ += ($radix <<= 1) && $bitsPerCharacter < 8) { + $bitsPerCharacter++; + } + + $radix >>= 1; + + } elseif ($bitsPerCharacter > 8) { + // $bitsPerCharacter must not be greater than 8 + $bitsPerCharacter = 8; + $radix = 256; + + } else { + $radix = 1 << $bitsPerCharacter; + } + + $this->_chars = $chars; + $this->_bitsPerCharacter = $bitsPerCharacter; + $this->_radix = $radix; + $this->_rightPadFinalBits = $rightPadFinalBits; + $this->_padFinalGroup = $padFinalGroup; + $this->_padCharacter = $padCharacter[0]; + } + + /** + * Encode a string + * + * @param string $rawString Binary data to encode + * @return string + */ + public function encode($rawString) + { + // Unpack string into an array of bytes + $bytes = unpack('C*', $rawString); + $byteCount = count($bytes); + + $encodedString = ''; + $byte = array_shift($bytes); + $bitsRead = 0; + + $chars = $this->_chars; + $bitsPerCharacter = $this->_bitsPerCharacter; + $rightPadFinalBits = $this->_rightPadFinalBits; + $padFinalGroup = $this->_padFinalGroup; + $padCharacter = $this->_padCharacter; + + // Generate encoded output; + // each loop produces one encoded character + for ($c = 0; $c < $byteCount * 8 / $bitsPerCharacter; $c++) { + + // Get the bits needed for this encoded character + if ($bitsRead + $bitsPerCharacter > 8) { + // Not enough bits remain in this byte for the current + // character + // Save the remaining bits before getting the next byte + $oldBitCount = 8 - $bitsRead; + $oldBits = $byte ^ ($byte >> $oldBitCount << $oldBitCount); + $newBitCount = $bitsPerCharacter - $oldBitCount; + + if (!$bytes) { + // Last bits; match final character and exit loop + if ($rightPadFinalBits) $oldBits <<= $newBitCount; + $encodedString .= $chars[$oldBits]; + + if ($padFinalGroup) { + // Array of the lowest common multiples of + // $bitsPerCharacter and 8, divided by 8 + $lcmMap = array(1 => 1, 2 => 1, 3 => 3, 4 => 1, + 5 => 5, 6 => 3, 7 => 7, 8 => 1); + $bytesPerGroup = $lcmMap[$bitsPerCharacter]; + $pads = $bytesPerGroup * 8 / $bitsPerCharacter + - ceil((strlen($rawString) % $bytesPerGroup) + * 8 / $bitsPerCharacter); + $encodedString .= str_repeat($padCharacter[0], $pads); + } + + break; + } + + // Get next byte + $byte = array_shift($bytes); + $bitsRead = 0; + + } else { + $oldBitCount = 0; + $newBitCount = $bitsPerCharacter; + } + + // Read only the needed bits from this byte + $bits = $byte >> 8 - ($bitsRead + ($newBitCount)); + $bits ^= $bits >> $newBitCount << $newBitCount; + $bitsRead += $newBitCount; + + if ($oldBitCount) { + // Bits come from seperate bytes, add $oldBits to $bits + $bits = ($oldBits << $newBitCount) | $bits; + } + + $encodedString .= $chars[$bits]; + } + + return $encodedString; + } + + /** + * Decode a string + * + * @param string $encodedString Data to decode + * @param boolean $caseSensitive + * @param boolean $strict Returns NULL if $encodedString contains + * an undecodable character + * @return string|NULL + */ + public function decode($encodedString, $caseSensitive = TRUE, + $strict = FALSE) + { + if (!$encodedString || !is_string($encodedString)) { + // Empty string, nothing to decode + return ''; + } + + $chars = $this->_chars; + $bitsPerCharacter = $this->_bitsPerCharacter; + $radix = $this->_radix; + $rightPadFinalBits = $this->_rightPadFinalBits; + $padFinalGroup = $this->_padFinalGroup; + $padCharacter = $this->_padCharacter; + + // Get index of encoded characters + if ($this->_charmap) { + $charmap = $this->_charmap; + + } else { + $charmap = array(); + + for ($i = 0; $i < $radix; $i++) { + $charmap[$chars[$i]] = $i; + } + + $this->_charmap = $charmap; + } + + // The last encoded character is $encodedString[$lastNotatedIndex] + $lastNotatedIndex = strlen($encodedString) - 1; + + // Remove trailing padding characters + while ($encodedString[$lastNotatedIndex] == $padCharacter[0]) { + $encodedString = substr($encodedString, 0, $lastNotatedIndex); + $lastNotatedIndex--; + } + + $rawString = ''; + $byte = 0; + $bitsWritten = 0; + + // Convert each encoded character to a series of unencoded bits + for ($c = 0; $c <= $lastNotatedIndex; $c++) { + + if (!isset($charmap[$encodedString[$c]]) && !$caseSensitive) { + // Encoded character was not found; try other case + if (isset($charmap[$cUpper + = strtoupper($encodedString[$c])])) { + $charmap[$encodedString[$c]] = $charmap[$cUpper]; + + } elseif (isset($charmap[$cLower + = strtolower($encodedString[$c])])) { + $charmap[$encodedString[$c]] = $charmap[$cLower]; + } + } + + if (isset($charmap[$encodedString[$c]])) { + $bitsNeeded = 8 - $bitsWritten; + $unusedBitCount = $bitsPerCharacter - $bitsNeeded; + + // Get the new bits ready + if ($bitsNeeded > $bitsPerCharacter) { + // New bits aren't enough to complete a byte; shift them + // left into position + $newBits = $charmap[$encodedString[$c]] << $bitsNeeded + - $bitsPerCharacter; + $bitsWritten += $bitsPerCharacter; + + } elseif ($c != $lastNotatedIndex || $rightPadFinalBits) { + // Zero or more too many bits to complete a byte; + // shift right + $newBits = $charmap[$encodedString[$c]] >> $unusedBitCount; + $bitsWritten = 8; //$bitsWritten += $bitsNeeded; + + } else { + // Final bits don't need to be shifted + $newBits = $charmap[$encodedString[$c]]; + $bitsWritten = 8; + } + + $byte |= $newBits; + + if ($bitsWritten == 8 || $c == $lastNotatedIndex) { + // Byte is ready to be written + $rawString .= pack('C', $byte); + + if ($c != $lastNotatedIndex) { + // Start the next byte + $bitsWritten = $unusedBitCount; + $byte = ($charmap[$encodedString[$c]] + ^ ($newBits << $unusedBitCount)) << 8 - $bitsWritten; + } + } + + } elseif ($strict) { + // Unable to decode character; abort + return NULL; + } + } + + return $rawString; + } +} diff --git a/www/core/func/libs/google/GoogleAuthenticator.php b/www/core/func/libs/google/GoogleAuthenticator.php new file mode 100644 index 0000000..c986f38 --- /dev/null +++ b/www/core/func/libs/google/GoogleAuthenticator.php @@ -0,0 +1,90 @@ +getCode($secret,$time + $i) == $code) { + return true; + } + } + + return false; + + } + + public function getCode($secret,$time = null) { + + if (!$time) { + $time = floor(time() / 30); + } + $base32 = new FixedBitNotation(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', TRUE, TRUE); + $secret = $base32->decode($secret); + + $time = pack("N", $time); + $time = str_pad($time,8, chr(0), STR_PAD_LEFT); + + $hash = hash_hmac('sha1',$time,$secret,true); + $offset = ord(substr($hash,-1)); + $offset = $offset & 0xF; + + $truncatedHash = self::hashToInt($hash, $offset) & 0x7FFFFFFF; + $pinValue = str_pad($truncatedHash % self::$PIN_MODULO,6,"0",STR_PAD_LEFT);; + return $pinValue; + } + + protected function hashToInt($bytes, $start) { + $input = substr($bytes, $start, strlen($bytes) - $start); + $val2 = unpack("N",substr($input,0,4)); + return $val2[1]; + } + + public function getUrl($user, $hostname, $secret) { + $url = sprintf("otpauth://totp/%s@%s&secret=%s", $user, $hostname, $secret); + $encoder = "https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl="; + $encoderURL = sprintf( "%sotpauth://totp/%s@%s&secret=%s",$encoder, $user, $hostname, $secret); + + $encoderURL = str_replace("&secret", "?secret", $encoderURL); + + return $encoderURL; + + } + + public function generateSecret() { + $secret = ""; + for($i = 1; $i<= self::$SECRET_LENGTH;$i++) { + $c = rand(0,255); + $secret .= pack("c",$c); + } + $base32 = new FixedBitNotation(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', TRUE, TRUE); + return $base32->encode($secret); + + + } + +} + diff --git a/www/core/func/libs/mail/PHPMailerAutoload.php b/www/core/func/libs/mail/PHPMailerAutoload.php new file mode 100644 index 0000000..44ffcf6 --- /dev/null +++ b/www/core/func/libs/mail/PHPMailerAutoload.php @@ -0,0 +1,49 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2014 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +/** + * PHPMailer SPL autoloader. + * @param string $classname The name of the class to load + */ +function PHPMailerAutoload($classname) +{ + //Can't use __DIR__ as it's only in PHP 5.3+ + $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'class.'.strtolower($classname).'.php'; + if (is_readable($filename)) { + require $filename; + } +} + +if (version_compare(PHP_VERSION, '5.1.2', '>=')) { + //SPL autoloading was introduced in PHP 5.1.2 + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + spl_autoload_register('PHPMailerAutoload', true, true); + } else { + spl_autoload_register('PHPMailerAutoload'); + } +} else { + /** + * Fall back to traditional autoload for old PHP versions + * @param string $classname The name of the class to load + */ + function spl_autoload_register($classname) + { + PHPMailerAutoload($classname); + } +} diff --git a/www/core/func/libs/mail/class.phpmailer.php b/www/core/func/libs/mail/class.phpmailer.php new file mode 100644 index 0000000..b7d7f7a --- /dev/null +++ b/www/core/func/libs/mail/class.phpmailer.php @@ -0,0 +1,3966 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2014 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +/** + * PHPMailer - PHP email creation and transport class. + * @package PHPMailer + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + */ +class PHPMailer +{ + /** + * The PHPMailer Version number. + * @var string + */ + public $Version = '5.2.16'; + + /** + * Email priority. + * Options: null (default), 1 = High, 3 = Normal, 5 = low. + * When null, the header is not set at all. + * @var integer + */ + public $Priority = null; + + /** + * The character set of the message. + * @var string + */ + public $CharSet = 'iso-8859-1'; + + /** + * The MIME Content-type of the message. + * @var string + */ + public $ContentType = 'text/plain'; + + /** + * The message encoding. + * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". + * @var string + */ + public $Encoding = '8bit'; + + /** + * Holds the most recent mailer error message. + * @var string + */ + public $ErrorInfo = ''; + + /** + * The From email address for the message. + * @var string + */ + public $From = 'root@localhost'; + + /** + * The From name of the message. + * @var string + */ + public $FromName = 'Root User'; + + /** + * The Sender email (Return-Path) of the message. + * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. + * @var string + */ + public $Sender = ''; + + /** + * The Return-Path of the message. + * If empty, it will be set to either From or Sender. + * @var string + * @deprecated Email senders should never set a return-path header; + * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything. + * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference + */ + public $ReturnPath = ''; + + /** + * The Subject of the message. + * @var string + */ + public $Subject = ''; + + /** + * An HTML or plain text message body. + * If HTML then call isHTML(true). + * @var string + */ + public $Body = ''; + + /** + * The plain-text message body. + * This body can be read by mail clients that do not have HTML email + * capability such as mutt & Eudora. + * Clients that can read HTML will view the normal Body. + * @var string + */ + public $AltBody = ''; + + /** + * An iCal message part body. + * Only supported in simple alt or alt_inline message types + * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator + * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ + * @link http://kigkonsult.se/iCalcreator/ + * @var string + */ + public $Ical = ''; + + /** + * The complete compiled MIME message body. + * @access protected + * @var string + */ + protected $MIMEBody = ''; + + /** + * The complete compiled MIME message headers. + * @var string + * @access protected + */ + protected $MIMEHeader = ''; + + /** + * Extra headers that createHeader() doesn't fold in. + * @var string + * @access protected + */ + protected $mailHeader = ''; + + /** + * Word-wrap the message body to this number of chars. + * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. + * @var integer + */ + public $WordWrap = 0; + + /** + * Which method to use to send mail. + * Options: "mail", "sendmail", or "smtp". + * @var string + */ + public $Mailer = 'mail'; + + /** + * The path to the sendmail program. + * @var string + */ + public $Sendmail = '/usr/sbin/sendmail'; + + /** + * Whether mail() uses a fully sendmail-compatible MTA. + * One which supports sendmail's "-oi -f" options. + * @var boolean + */ + public $UseSendmailOptions = true; + + /** + * Path to PHPMailer plugins. + * Useful if the SMTP class is not in the PHP include path. + * @var string + * @deprecated Should not be needed now there is an autoloader. + */ + public $PluginDir = ''; + + /** + * The email address that a reading confirmation should be sent to, also known as read receipt. + * @var string + */ + public $ConfirmReadingTo = ''; + + /** + * The hostname to use in the Message-ID header and as default HELO string. + * If empty, PHPMailer attempts to find one with, in order, + * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value + * 'localhost.localdomain'. + * @var string + */ + public $Hostname = ''; + + /** + * An ID to be used in the Message-ID header. + * If empty, a unique id will be generated. + * You can set your own, but it must be in the format "", + * as defined in RFC5322 section 3.6.4 or it will be ignored. + * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 + * @var string + */ + public $MessageID = ''; + + /** + * The message Date to be used in the Date header. + * If empty, the current date will be added. + * @var string + */ + public $MessageDate = ''; + + /** + * SMTP hosts. + * Either a single hostname or multiple semicolon-delimited hostnames. + * You can also specify a different port + * for each host by using this format: [hostname:port] + * (e.g. "smtp1.example.com:25;smtp2.example.com"). + * You can also specify encryption type, for example: + * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). + * Hosts will be tried in order. + * @var string + */ + public $Host = 'localhost'; + + /** + * The default SMTP server port. + * @var integer + * @TODO Why is this needed when the SMTP class takes care of it? + */ + public $Port = 25; + + /** + * The SMTP HELO of the message. + * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find + * one with the same method described above for $Hostname. + * @var string + * @see PHPMailer::$Hostname + */ + public $Helo = ''; + + /** + * What kind of encryption to use on the SMTP connection. + * Options: '', 'ssl' or 'tls' + * @var string + */ + public $SMTPSecure = ''; + + /** + * Whether to enable TLS encryption automatically if a server supports it, + * even if `SMTPSecure` is not set to 'tls'. + * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. + * @var boolean + */ + public $SMTPAutoTLS = true; + + /** + * Whether to use SMTP authentication. + * Uses the Username and Password properties. + * @var boolean + * @see PHPMailer::$Username + * @see PHPMailer::$Password + */ + public $SMTPAuth = false; + + /** + * Options array passed to stream_context_create when connecting via SMTP. + * @var array + */ + public $SMTPOptions = array(); + + /** + * SMTP username. + * @var string + */ + public $Username = ''; + + /** + * SMTP password. + * @var string + */ + public $Password = ''; + + /** + * SMTP auth type. + * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified + * @var string + */ + public $AuthType = ''; + + /** + * SMTP realm. + * Used for NTLM auth + * @var string + */ + public $Realm = ''; + + /** + * SMTP workstation. + * Used for NTLM auth + * @var string + */ + public $Workstation = ''; + + /** + * The SMTP server timeout in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 + * @var integer + */ + public $Timeout = 300; + + /** + * SMTP class debug output mode. + * Debug output level. + * Options: + * * `0` No output + * * `1` Commands + * * `2` Data and commands + * * `3` As 2 plus connection status + * * `4` Low-level data output + * @var integer + * @see SMTP::$do_debug + */ + public $SMTPDebug = 0; + + /** + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
    `, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini + * + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * + * @var string|callable + * @see SMTP::$Debugoutput + */ + public $Debugoutput = 'echo'; + + /** + * Whether to keep SMTP connection open after each message. + * If this is set to true then to close the connection + * requires an explicit call to smtpClose(). + * @var boolean + */ + public $SMTPKeepAlive = false; + + /** + * Whether to split multiple to addresses into multiple messages + * or send them all in one message. + * Only supported in `mail` and `sendmail` transports, not in SMTP. + * @var boolean + */ + public $SingleTo = false; + + /** + * Storage for addresses when SingleTo is enabled. + * @var array + * @TODO This should really not be public + */ + public $SingleToArray = array(); + + /** + * Whether to generate VERP addresses on send. + * Only applicable when sending via SMTP. + * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path + * @link http://www.postfix.org/VERP_README.html Postfix VERP info + * @var boolean + */ + public $do_verp = false; + + /** + * Whether to allow sending messages with an empty body. + * @var boolean + */ + public $AllowEmpty = false; + + /** + * The default line ending. + * @note The default remains "\n". We force CRLF where we know + * it must be used via self::CRLF. + * @var string + */ + public $LE = "\n"; + + /** + * DKIM selector. + * @var string + */ + public $DKIM_selector = ''; + + /** + * DKIM Identity. + * Usually the email address used as the source of the email. + * @var string + */ + public $DKIM_identity = ''; + + /** + * DKIM passphrase. + * Used if your key is encrypted. + * @var string + */ + public $DKIM_passphrase = ''; + + /** + * DKIM signing domain name. + * @example 'example.com' + * @var string + */ + public $DKIM_domain = ''; + + /** + * DKIM private key file path. + * @var string + */ + public $DKIM_private = ''; + + /** + * Callback Action function name. + * + * The function that handles the result of the send email action. + * It is called out by send() for each email sent. + * + * Value can be any php callable: http://www.php.net/is_callable + * + * Parameters: + * boolean $result result of the send action + * string $to email address of the recipient + * string $cc cc email addresses + * string $bcc bcc email addresses + * string $subject the subject + * string $body the email body + * string $from email address of sender + * @var string + */ + public $action_function = ''; + + /** + * What to put in the X-Mailer header. + * Options: An empty string for PHPMailer default, whitespace for none, or a string to use + * @var string + */ + public $XMailer = ''; + + /** + * Which validator to use by default when validating email addresses. + * May be a callable to inject your own validator, but there are several built-in validators. + * @see PHPMailer::validateAddress() + * @var string|callable + * @static + */ + public static $validator = 'auto'; + + /** + * An instance of the SMTP sender class. + * @var SMTP + * @access protected + */ + protected $smtp = null; + + /** + * The array of 'to' names and addresses. + * @var array + * @access protected + */ + protected $to = array(); + + /** + * The array of 'cc' names and addresses. + * @var array + * @access protected + */ + protected $cc = array(); + + /** + * The array of 'bcc' names and addresses. + * @var array + * @access protected + */ + protected $bcc = array(); + + /** + * The array of reply-to names and addresses. + * @var array + * @access protected + */ + protected $ReplyTo = array(); + + /** + * An array of all kinds of addresses. + * Includes all of $to, $cc, $bcc + * @var array + * @access protected + * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc + */ + protected $all_recipients = array(); + + /** + * An array of names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $all_recipients + * and one of $to, $cc, or $bcc. + * This array is used only for addresses with IDN. + * @var array + * @access protected + * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc + * @see PHPMailer::$all_recipients + */ + protected $RecipientsQueue = array(); + + /** + * An array of reply-to names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $ReplyTo. + * This array is used only for addresses with IDN. + * @var array + * @access protected + * @see PHPMailer::$ReplyTo + */ + protected $ReplyToQueue = array(); + + /** + * The array of attachments. + * @var array + * @access protected + */ + protected $attachment = array(); + + /** + * The array of custom headers. + * @var array + * @access protected + */ + protected $CustomHeader = array(); + + /** + * The most recent Message-ID (including angular brackets). + * @var string + * @access protected + */ + protected $lastMessageID = ''; + + /** + * The message's MIME type. + * @var string + * @access protected + */ + protected $message_type = ''; + + /** + * The array of MIME boundary strings. + * @var array + * @access protected + */ + protected $boundary = array(); + + /** + * The array of available languages. + * @var array + * @access protected + */ + protected $language = array(); + + /** + * The number of errors encountered. + * @var integer + * @access protected + */ + protected $error_count = 0; + + /** + * The S/MIME certificate file path. + * @var string + * @access protected + */ + protected $sign_cert_file = ''; + + /** + * The S/MIME key file path. + * @var string + * @access protected + */ + protected $sign_key_file = ''; + + /** + * The optional S/MIME extra certificates ("CA Chain") file path. + * @var string + * @access protected + */ + protected $sign_extracerts_file = ''; + + /** + * The S/MIME password for the key. + * Used only if the key is encrypted. + * @var string + * @access protected + */ + protected $sign_key_pass = ''; + + /** + * Whether to throw exceptions for errors. + * @var boolean + * @access protected + */ + protected $exceptions = false; + + /** + * Unique ID used for message ID and boundaries. + * @var string + * @access protected + */ + protected $uniqueid = ''; + + /** + * Error severity: message only, continue processing. + */ + const STOP_MESSAGE = 0; + + /** + * Error severity: message, likely ok to continue processing. + */ + const STOP_CONTINUE = 1; + + /** + * Error severity: message, plus full stop, critical error reached. + */ + const STOP_CRITICAL = 2; + + /** + * SMTP RFC standard line ending. + */ + const CRLF = "\r\n"; + + /** + * The maximum line length allowed by RFC 2822 section 2.1.1 + * @var integer + */ + const MAX_LINE_LENGTH = 998; + + /** + * Constructor. + * @param boolean $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = null) + { + if ($exceptions !== null) { + $this->exceptions = (boolean)$exceptions; + } + } + + /** + * Destructor. + */ + public function __destruct() + { + //Close any open SMTP connection nicely + $this->smtpClose(); + } + + /** + * Call mail() in a safe_mode-aware fashion. + * Also, unless sendmail_path points to sendmail (or something that + * claims to be sendmail), don't pass params (not a perfect fix, + * but it will do) + * @param string $to To + * @param string $subject Subject + * @param string $body Message Body + * @param string $header Additional Header(s) + * @param string $params Params + * @access private + * @return boolean + */ + private function mailPassthru($to, $subject, $body, $header, $params) + { + //Check overloading of mail function to avoid double-encoding + if (ini_get('mbstring.func_overload') & 1) { + $subject = $this->secureHeader($subject); + } else { + $subject = $this->encodeHeader($this->secureHeader($subject)); + } + + //Can't use additional_parameters in safe_mode + //@link http://php.net/manual/en/function.mail.php + if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) { + $result = @mail($to, $subject, $body, $header); + } else { + $result = @mail($to, $subject, $body, $header, $params); + } + return $result; + } + /** + * Output debugging info via user-defined method. + * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). + * @see PHPMailer::$Debugoutput + * @see PHPMailer::$SMTPDebug + * @param string $str + */ + protected function edebug($str) + { + if ($this->SMTPDebug <= 0) { + return; + } + //Avoid clash with built-in function names + if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { + call_user_func($this->Debugoutput, $str, $this->SMTPDebug); + return; + } + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ) + . "
    \n"; + break; + case 'echo': + default: + //Normalize line breaks + $str = preg_replace('/\r\n?/ms', "\n", $str); + echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( + "\n", + "\n \t ", + trim($str) + ) . "\n"; + } + } + + /** + * Sets message type to HTML or plain. + * @param boolean $isHtml True for HTML mode. + * @return void + */ + public function isHTML($isHtml = true) + { + if ($isHtml) { + $this->ContentType = 'text/html'; + } else { + $this->ContentType = 'text/plain'; + } + } + + /** + * Send messages using SMTP. + * @return void + */ + public function isSMTP() + { + $this->Mailer = 'smtp'; + } + + /** + * Send messages using PHP's mail() function. + * @return void + */ + public function isMail() + { + $this->Mailer = 'mail'; + } + + /** + * Send messages using $Sendmail. + * @return void + */ + public function isSendmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (!stristr($ini_sendmail_path, 'sendmail')) { + $this->Sendmail = '/usr/sbin/sendmail'; + } else { + $this->Sendmail = $ini_sendmail_path; + } + $this->Mailer = 'sendmail'; + } + + /** + * Send messages using qmail. + * @return void + */ + public function isQmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (!stristr($ini_sendmail_path, 'qmail')) { + $this->Sendmail = '/var/qmail/bin/qmail-inject'; + } else { + $this->Sendmail = $ini_sendmail_path; + } + $this->Mailer = 'qmail'; + } + + /** + * Add a "To" address. + * @param string $address The email address to send to + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + */ + public function addAddress($address, $name = '') + { + return $this->addOrEnqueueAnAddress('to', $address, $name); + } + + /** + * Add a "CC" address. + * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. + * @param string $address The email address to send to + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + */ + public function addCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('cc', $address, $name); + } + + /** + * Add a "BCC" address. + * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. + * @param string $address The email address to send to + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + */ + public function addBCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('bcc', $address, $name); + } + + /** + * Add a "Reply-To" address. + * @param string $address The email address to reply to + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + */ + public function addReplyTo($address, $name = '') + { + return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer + * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still + * be modified after calling this function), addition of such addresses is delayed until send(). + * Addresses that have been added already return false, but do not throw exceptions. + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to + * @param string $name + * @throws phpmailerException + * @return boolean true on success, false if address already used or invalid in some way + * @access protected + */ + protected function addOrEnqueueAnAddress($kind, $address, $name) + { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + if (($pos = strrpos($address, '@')) === false) { + // At-sign is misssing. + $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + $params = array($kind, $address, $name); + // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. + if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) { + if ($kind != 'Reply-To') { + if (!array_key_exists($address, $this->RecipientsQueue)) { + $this->RecipientsQueue[$address] = $params; + return true; + } + } else { + if (!array_key_exists($address, $this->ReplyToQueue)) { + $this->ReplyToQueue[$address] = $params; + return true; + } + } + return false; + } + // Immediately add standard addresses without IDN. + return call_user_func_array(array($this, 'addAnAddress'), $params); + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. + * Addresses that have been added already return false, but do not throw exceptions. + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to + * @param string $name + * @throws phpmailerException + * @return boolean true on success, false if address already used or invalid in some way + * @access protected + */ + protected function addAnAddress($kind, $address, $name = '') + { + if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) { + $error_message = $this->lang('Invalid recipient kind: ') . $kind; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + if (!$this->validateAddress($address)) { + $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + if ($kind != 'Reply-To') { + if (!array_key_exists(strtolower($address), $this->all_recipients)) { + array_push($this->$kind, array($address, $name)); + $this->all_recipients[strtolower($address)] = true; + return true; + } + } else { + if (!array_key_exists(strtolower($address), $this->ReplyTo)) { + $this->ReplyTo[strtolower($address)] = array($address, $name); + return true; + } + } + return false; + } + + /** + * Parse and validate a string containing one or more RFC822-style comma-separated email addresses + * of the form "display name
    " into an array of name/address pairs. + * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. + * Note that quotes in the name part are removed. + * @param string $addrstr The address list string + * @param bool $useimap Whether to use the IMAP extension to parse the list + * @return array + * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation + */ + public function parseAddresses($addrstr, $useimap = true) + { + $addresses = array(); + if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { + //Use this built-in parser if it's available + $list = imap_rfc822_parse_adrlist($addrstr, ''); + foreach ($list as $address) { + if ($address->host != '.SYNTAX-ERROR.') { + if ($this->validateAddress($address->mailbox . '@' . $address->host)) { + $addresses[] = array( + 'name' => (property_exists($address, 'personal') ? $address->personal : ''), + 'address' => $address->mailbox . '@' . $address->host + ); + } + } + } + } else { + //Use this simpler parser + $list = explode(',', $addrstr); + foreach ($list as $address) { + $address = trim($address); + //Is there a separate name part? + if (strpos($address, '<') === false) { + //No separate name, just use the whole thing + if ($this->validateAddress($address)) { + $addresses[] = array( + 'name' => '', + 'address' => $address + ); + } + } else { + list($name, $email) = explode('<', $address); + $email = trim(str_replace('>', '', $email)); + if ($this->validateAddress($email)) { + $addresses[] = array( + 'name' => trim(str_replace(array('"', "'"), '', $name)), + 'address' => $email + ); + } + } + } + } + return $addresses; + } + + /** + * Set the From and FromName properties. + * @param string $address + * @param string $name + * @param boolean $auto Whether to also set the Sender address, defaults to true + * @throws phpmailerException + * @return boolean + */ + public function setFrom($address, $name = '', $auto = true) + { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + // Don't validate now addresses with IDN. Will be done in send(). + if (($pos = strrpos($address, '@')) === false or + (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and + !$this->validateAddress($address)) { + $error_message = $this->lang('invalid_address') . " (setFrom) $address"; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + $this->From = $address; + $this->FromName = $name; + if ($auto) { + if (empty($this->Sender)) { + $this->Sender = $address; + } + } + return true; + } + + /** + * Return the Message-ID header of the last email. + * Technically this is the value from the last time the headers were created, + * but it's also the message ID of the last sent message except in + * pathological cases. + * @return string + */ + public function getLastMessageID() + { + return $this->lastMessageID; + } + + /** + * Check that a string looks like an email address. + * @param string $address The email address to check + * @param string|callable $patternselect A selector for the validation pattern to use : + * * `auto` Pick best pattern automatically; + * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; + * * `pcre` Use old PCRE implementation; + * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; + * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. + * * `noregex` Don't use a regex: super fast, really dumb. + * Alternatively you may pass in a callable to inject your own validator, for example: + * PHPMailer::validateAddress('user@example.com', function($address) { + * return (strpos($address, '@') !== false); + * }); + * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. + * @return boolean + * @static + * @access public + */ + public static function validateAddress($address, $patternselect = null) + { + if (is_null($patternselect)) { + $patternselect = self::$validator; + } + if (is_callable($patternselect)) { + return call_user_func($patternselect, $address); + } + //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 + if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { + return false; + } + if (!$patternselect or $patternselect == 'auto') { + //Check this constant first so it works when extension_loaded() is disabled by safe mode + //Constant was added in PHP 5.2.4 + if (defined('PCRE_VERSION')) { + //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2 + if (version_compare(PCRE_VERSION, '8.0.3') >= 0) { + $patternselect = 'pcre8'; + } else { + $patternselect = 'pcre'; + } + } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) { + //Fall back to older PCRE + $patternselect = 'pcre'; + } else { + //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension + if (version_compare(PHP_VERSION, '5.2.0') >= 0) { + $patternselect = 'php'; + } else { + $patternselect = 'noregex'; + } + } + } + switch ($patternselect) { + case 'pcre8': + /** + * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains. + * @link http://squiloople.com/2009/12/20/email-address-validation/ + * @copyright 2009-2010 Michael Rushton + * Feel free to use and redistribute this code. But please keep this copyright notice. + */ + return (boolean)preg_match( + '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . + '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . + '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . + '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . + '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . + '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . + '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . + '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . + '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', + $address + ); + case 'pcre': + //An older regex that doesn't need a recent PCRE + return (boolean)preg_match( + '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . + '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . + '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . + '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' . + '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' . + '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' . + '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' . + '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' . + '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' . + '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', + $address + ); + case 'html5': + /** + * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. + * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) + */ + return (boolean)preg_match( + '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . + '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', + $address + ); + case 'noregex': + //No PCRE! Do something _very_ approximate! + //Check the address is 3 chars or longer and contains an @ that's not the first or last char + return (strlen($address) >= 3 + and strpos($address, '@') >= 1 + and strpos($address, '@') != strlen($address) - 1); + case 'php': + default: + return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL); + } + } + + /** + * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the + * "intl" and "mbstring" PHP extensions. + * @return bool "true" if required functions for IDN support are present + */ + public function idnSupported() + { + // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2. + return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding'); + } + + /** + * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. + * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. + * This function silently returns unmodified address if: + * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) + * - Conversion to punycode is impossible (e.g. required PHP functions are not available) + * or fails for any reason (e.g. domain has characters not allowed in an IDN) + * @see PHPMailer::$CharSet + * @param string $address The email address to convert + * @return string The encoded address in ASCII form + */ + public function punyencodeAddress($address) + { + // Verify we have required functions, CharSet, and at-sign. + if ($this->idnSupported() and + !empty($this->CharSet) and + ($pos = strrpos($address, '@')) !== false) { + $domain = substr($address, ++$pos); + // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. + if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { + $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); + if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? + idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : + idn_to_ascii($domain)) !== false) { + return substr($address, 0, $pos) . $punycode; + } + } + } + return $address; + } + + /** + * Create a message and send it. + * Uses the sending method specified by $Mailer. + * @throws phpmailerException + * @return boolean false on error - See the ErrorInfo property for details of the error. + */ + public function send() + { + try { + if (!$this->preSend()) { + return false; + } + return $this->postSend(); + } catch (phpmailerException $exc) { + $this->mailHeader = ''; + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + return false; + } + } + + /** + * Prepare a message for sending. + * @throws phpmailerException + * @return boolean + */ + public function preSend() + { + try { + $this->error_count = 0; // Reset errors + $this->mailHeader = ''; + + // Dequeue recipient and Reply-To addresses with IDN + foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { + $params[1] = $this->punyencodeAddress($params[1]); + call_user_func_array(array($this, 'addAnAddress'), $params); + } + if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { + throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL); + } + + // Validate From, Sender, and ConfirmReadingTo addresses + foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) { + $this->$address_kind = trim($this->$address_kind); + if (empty($this->$address_kind)) { + continue; + } + $this->$address_kind = $this->punyencodeAddress($this->$address_kind); + if (!$this->validateAddress($this->$address_kind)) { + $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + } + + // Set whether the message is multipart/alternative + if ($this->alternativeExists()) { + $this->ContentType = 'multipart/alternative'; + } + + $this->setMessageType(); + // Refuse to send an empty message unless we are specifically allowing it + if (!$this->AllowEmpty and empty($this->Body)) { + throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL); + } + + // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) + $this->MIMEHeader = ''; + $this->MIMEBody = $this->createBody(); + // createBody may have added some headers, so retain them + $tempheaders = $this->MIMEHeader; + $this->MIMEHeader = $this->createHeader(); + $this->MIMEHeader .= $tempheaders; + + // To capture the complete message when using mail(), create + // an extra header list which createHeader() doesn't fold in + if ($this->Mailer == 'mail') { + if (count($this->to) > 0) { + $this->mailHeader .= $this->addrAppend('To', $this->to); + } else { + $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + $this->mailHeader .= $this->headerLine( + 'Subject', + $this->encodeHeader($this->secureHeader(trim($this->Subject))) + ); + } + + // Sign with DKIM if enabled + if (!empty($this->DKIM_domain) + && !empty($this->DKIM_private) + && !empty($this->DKIM_selector) + && file_exists($this->DKIM_private)) { + $header_dkim = $this->DKIM_Add( + $this->MIMEHeader . $this->mailHeader, + $this->encodeHeader($this->secureHeader($this->Subject)), + $this->MIMEBody + ); + $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF . + str_replace("\r\n", "\n", $header_dkim) . self::CRLF; + } + return true; + } catch (phpmailerException $exc) { + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + return false; + } + } + + /** + * Actually send a message. + * Send the email via the selected mechanism + * @throws phpmailerException + * @return boolean + */ + public function postSend() + { + try { + // Choose the mailer and send through it + switch ($this->Mailer) { + case 'sendmail': + case 'qmail': + return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); + case 'smtp': + return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); + case 'mail': + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + default: + $sendMethod = $this->Mailer.'Send'; + if (method_exists($this, $sendMethod)) { + return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); + } + + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + } + } catch (phpmailerException $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + } + return false; + } + + /** + * Send mail using the $Sendmail program. + * @param string $header The message headers + * @param string $body The message body + * @see PHPMailer::$Sendmail + * @throws phpmailerException + * @access protected + * @return boolean + */ + protected function sendmailSend($header, $body) + { + if ($this->Sender != '') { + if ($this->Mailer == 'qmail') { + $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + } else { + $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + } + } else { + if ($this->Mailer == 'qmail') { + $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail)); + } else { + $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail)); + } + } + if ($this->SingleTo) { + foreach ($this->SingleToArray as $toAddr) { + if (!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, 'To: ' . $toAddr . "\n"); + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + $this->doCallback( + ($result == 0), + array($toAddr), + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From + ); + if ($result != 0) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + } else { + if (!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + $this->doCallback( + ($result == 0), + $this->to, + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From + ); + if ($result != 0) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + return true; + } + + /** + * Send mail using the PHP mail() function. + * @param string $header The message headers + * @param string $body The message body + * @link http://www.php.net/manual/en/book.mail.php + * @throws phpmailerException + * @access protected + * @return boolean + */ + protected function mailSend($header, $body) + { + $toArr = array(); + foreach ($this->to as $toaddr) { + $toArr[] = $this->addrFormat($toaddr); + } + $to = implode(', ', $toArr); + + $params = null; + //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver + if (!empty($this->Sender)) { + $params = sprintf('-f%s', $this->Sender); + } + if ($this->Sender != '' and !ini_get('safe_mode')) { + $old_from = ini_get('sendmail_from'); + ini_set('sendmail_from', $this->Sender); + } + $result = false; + if ($this->SingleTo and count($toArr) > 1) { + foreach ($toArr as $toAddr) { + $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); + $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); + } + } else { + $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); + $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); + } + if (isset($old_from)) { + ini_set('sendmail_from', $old_from); + } + if (!$result) { + throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); + } + return true; + } + + /** + * Get an instance to use for SMTP operations. + * Override this function to load your own SMTP implementation + * @return SMTP + */ + public function getSMTPInstance() + { + if (!is_object($this->smtp)) { + $this->smtp = new SMTP; + } + return $this->smtp; + } + + /** + * Send mail via SMTP. + * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. + * Uses the PHPMailerSMTP class by default. + * @see PHPMailer::getSMTPInstance() to use a different class. + * @param string $header The message headers + * @param string $body The message body + * @throws phpmailerException + * @uses SMTP + * @access protected + * @return boolean + */ + protected function smtpSend($header, $body) + { + $bad_rcpt = array(); + if (!$this->smtpConnect($this->SMTPOptions)) { + throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); + } + if ('' == $this->Sender) { + $smtp_from = $this->From; + } else { + $smtp_from = $this->Sender; + } + if (!$this->smtp->mail($smtp_from)) { + $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); + throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); + } + + // Attempt to send to all recipients + foreach (array($this->to, $this->cc, $this->bcc) as $togroup) { + foreach ($togroup as $to) { + if (!$this->smtp->recipient($to[0])) { + $error = $this->smtp->getError(); + $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']); + $isSent = false; + } else { + $isSent = true; + } + $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From); + } + } + + // Only send the DATA command if we have viable recipients + if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { + throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); + } + if ($this->SMTPKeepAlive) { + $this->smtp->reset(); + } else { + $this->smtp->quit(); + $this->smtp->close(); + } + //Create error message for any bad addresses + if (count($bad_rcpt) > 0) { + $errstr = ''; + foreach ($bad_rcpt as $bad) { + $errstr .= $bad['to'] . ': ' . $bad['error']; + } + throw new phpmailerException( + $this->lang('recipients_failed') . $errstr, + self::STOP_CONTINUE + ); + } + return true; + } + + /** + * Initiate a connection to an SMTP server. + * Returns false if the operation failed. + * @param array $options An array of options compatible with stream_context_create() + * @uses SMTP + * @access public + * @throws phpmailerException + * @return boolean + */ + public function smtpConnect($options = null) + { + if (is_null($this->smtp)) { + $this->smtp = $this->getSMTPInstance(); + } + + //If no options are provided, use whatever is set in the instance + if (is_null($options)) { + $options = $this->SMTPOptions; + } + + // Already connected? + if ($this->smtp->connected()) { + return true; + } + + $this->smtp->setTimeout($this->Timeout); + $this->smtp->setDebugLevel($this->SMTPDebug); + $this->smtp->setDebugOutput($this->Debugoutput); + $this->smtp->setVerp($this->do_verp); + $hosts = explode(';', $this->Host); + $lastexception = null; + + foreach ($hosts as $hostentry) { + $hostinfo = array(); + if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { + // Not a valid host entry + continue; + } + // $hostinfo[2]: optional ssl or tls prefix + // $hostinfo[3]: the hostname + // $hostinfo[4]: optional port number + // The host string prefix can temporarily override the current setting for SMTPSecure + // If it's not specified, the default value is used + $prefix = ''; + $secure = $this->SMTPSecure; + $tls = ($this->SMTPSecure == 'tls'); + if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { + $prefix = 'ssl://'; + $tls = false; // Can't have SSL and TLS at the same time + $secure = 'ssl'; + } elseif ($hostinfo[2] == 'tls') { + $tls = true; + // tls doesn't use a prefix + $secure = 'tls'; + } + //Do we need the OpenSSL extension? + $sslext = defined('OPENSSL_ALGO_SHA1'); + if ('tls' === $secure or 'ssl' === $secure) { + //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled + if (!$sslext) { + throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); + } + } + $host = $hostinfo[3]; + $port = $this->Port; + $tport = (integer)$hostinfo[4]; + if ($tport > 0 and $tport < 65536) { + $port = $tport; + } + if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->serverHostname(); + } + $this->smtp->hello($hello); + //Automatically enable TLS encryption if: + // * it's not disabled + // * we have openssl extension + // * we are not already using SSL + // * the server offers STARTTLS + if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { + $tls = true; + } + if ($tls) { + if (!$this->smtp->startTLS()) { + throw new phpmailerException($this->lang('connect_host')); + } + // We must resend EHLO after TLS negotiation + $this->smtp->hello($hello); + } + if ($this->SMTPAuth) { + if (!$this->smtp->authenticate( + $this->Username, + $this->Password, + $this->AuthType, + $this->Realm, + $this->Workstation + ) + ) { + throw new phpmailerException($this->lang('authenticate')); + } + } + return true; + } catch (phpmailerException $exc) { + $lastexception = $exc; + $this->edebug($exc->getMessage()); + // We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->quit(); + } + } + } + // If we get here, all connection attempts have failed, so close connection hard + $this->smtp->close(); + // As we've caught all exceptions, just report whatever the last one was + if ($this->exceptions and !is_null($lastexception)) { + throw $lastexception; + } + return false; + } + + /** + * Close the active SMTP session if one exists. + * @return void + */ + public function smtpClose() + { + if (is_a($this->smtp, 'SMTP')) { + if ($this->smtp->connected()) { + $this->smtp->quit(); + $this->smtp->close(); + } + } + } + + /** + * Set the language for error messages. + * Returns false if it cannot load the language file. + * The default language is English. + * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") + * @param string $lang_path Path to the language file directory, with trailing separator (slash) + * @return boolean + * @access public + */ + public function setLanguage($langcode = 'en', $lang_path = '') + { + // Backwards compatibility for renamed language codes + $renamed_langcodes = array( + 'br' => 'pt_br', + 'cz' => 'cs', + 'dk' => 'da', + 'no' => 'nb', + 'se' => 'sv', + ); + + if (isset($renamed_langcodes[$langcode])) { + $langcode = $renamed_langcodes[$langcode]; + } + + // Define full set of translatable strings in English + $PHPMAILER_LANG = array( + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'data_not_accepted' => 'SMTP Error: data not accepted.', + 'empty_message' => 'Message body empty', + 'encoding' => 'Unknown encoding: ', + 'execute' => 'Could not execute: ', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'from_failed' => 'The following From address failed: ', + 'instantiate' => 'Could not instantiate mail function.', + 'invalid_address' => 'Invalid address: ', + 'mailer_not_supported' => ' mailer is not supported.', + 'provide_address' => 'You must provide at least one recipient email address.', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'signing' => 'Signing Error: ', + 'smtp_connect_failed' => 'SMTP connect() failed.', + 'smtp_error' => 'SMTP server error: ', + 'variable_set' => 'Cannot set or reset variable: ', + 'extension_missing' => 'Extension missing: ' + ); + if (empty($lang_path)) { + // Calculate an absolute path so it can work if CWD is not here + $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; + } + //Validate $langcode + if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { + $langcode = 'en'; + } + $foundlang = true; + $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; + // There is no English translation file + if ($langcode != 'en') { + // Make sure language file path is readable + if (!is_readable($lang_file)) { + $foundlang = false; + } else { + // Overwrite language-specific strings. + // This way we'll never have missing translation keys. + $foundlang = include $lang_file; + } + } + $this->language = $PHPMAILER_LANG; + return (boolean)$foundlang; // Returns false if language not found + } + + /** + * Get the array of strings for the current language. + * @return array + */ + public function getTranslations() + { + return $this->language; + } + + /** + * Create recipient headers. + * @access public + * @param string $type + * @param array $addr An array of recipient, + * where each recipient is a 2-element indexed array with element 0 containing an address + * and element 1 containing a name, like: + * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User')) + * @return string + */ + public function addrAppend($type, $addr) + { + $addresses = array(); + foreach ($addr as $address) { + $addresses[] = $this->addrFormat($address); + } + return $type . ': ' . implode(', ', $addresses) . $this->LE; + } + + /** + * Format an address for use in a message header. + * @access public + * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name + * like array('joe@example.com', 'Joe User') + * @return string + */ + public function addrFormat($addr) + { + if (empty($addr[1])) { // No name provided + return $this->secureHeader($addr[0]); + } else { + return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( + $addr[0] + ) . '>'; + } + } + + /** + * Word-wrap message. + * For use with mailers that do not automatically perform wrapping + * and for quoted-printable encoded messages. + * Original written by philippe. + * @param string $message The message to wrap + * @param integer $length The line length to wrap to + * @param boolean $qp_mode Whether to run in Quoted-Printable mode + * @access public + * @return string + */ + public function wrapText($message, $length, $qp_mode = false) + { + if ($qp_mode) { + $soft_break = sprintf(' =%s', $this->LE); + } else { + $soft_break = $this->LE; + } + // If utf-8 encoding is used, we will need to make sure we don't + // split multibyte characters when we wrap + $is_utf8 = (strtolower($this->CharSet) == 'utf-8'); + $lelen = strlen($this->LE); + $crlflen = strlen(self::CRLF); + + $message = $this->fixEOL($message); + //Remove a trailing line break + if (substr($message, -$lelen) == $this->LE) { + $message = substr($message, 0, -$lelen); + } + + //Split message into lines + $lines = explode($this->LE, $message); + //Message will be rebuilt in here + $message = ''; + foreach ($lines as $line) { + $words = explode(' ', $line); + $buf = ''; + $firstword = true; + foreach ($words as $word) { + if ($qp_mode and (strlen($word) > $length)) { + $space_left = $length - strlen($buf) - $crlflen; + if (!$firstword) { + if ($space_left > 20) { + $len = $space_left; + if ($is_utf8) { + $len = $this->utf8CharBoundary($word, $len); + } elseif (substr($word, $len - 1, 1) == '=') { + $len--; + } elseif (substr($word, $len - 2, 1) == '=') { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= ' ' . $part; + $message .= $buf . sprintf('=%s', self::CRLF); + } else { + $message .= $buf . $soft_break; + } + $buf = ''; + } + while (strlen($word) > 0) { + if ($length <= 0) { + break; + } + $len = $length; + if ($is_utf8) { + $len = $this->utf8CharBoundary($word, $len); + } elseif (substr($word, $len - 1, 1) == '=') { + $len--; + } elseif (substr($word, $len - 2, 1) == '=') { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) { + $message .= $part . sprintf('=%s', self::CRLF); + } else { + $buf = $part; + } + } + } else { + $buf_o = $buf; + if (!$firstword) { + $buf .= ' '; + } + $buf .= $word; + + if (strlen($buf) > $length and $buf_o != '') { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + $firstword = false; + } + $message .= $buf . self::CRLF; + } + + return $message; + } + + /** + * Find the last character boundary prior to $maxLength in a utf-8 + * quoted-printable encoded string. + * Original written by Colin Brown. + * @access public + * @param string $encodedText utf-8 QP text + * @param integer $maxLength Find the last character boundary prior to this length + * @return integer + */ + public function utf8CharBoundary($encodedText, $maxLength) + { + $foundSplitPos = false; + $lookBack = 3; + while (!$foundSplitPos) { + $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); + $encodedCharPos = strpos($lastChunk, '='); + if (false !== $encodedCharPos) { + // Found start of encoded character byte within $lookBack block. + // Check the encoded byte value (the 2 chars after the '=') + $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); + $dec = hexdec($hex); + if ($dec < 128) { + // Single byte character. + // If the encoded char was found at pos 0, it will fit + // otherwise reduce maxLength to start of the encoded char + if ($encodedCharPos > 0) { + $maxLength = $maxLength - ($lookBack - $encodedCharPos); + } + $foundSplitPos = true; + } elseif ($dec >= 192) { + // First byte of a multi byte character + // Reduce maxLength to split at start of character + $maxLength = $maxLength - ($lookBack - $encodedCharPos); + $foundSplitPos = true; + } elseif ($dec < 192) { + // Middle byte of a multi byte character, look further back + $lookBack += 3; + } + } else { + // No encoded character found + $foundSplitPos = true; + } + } + return $maxLength; + } + + /** + * Apply word wrapping to the message body. + * Wraps the message body to the number of chars set in the WordWrap property. + * You should only do this to plain-text bodies as wrapping HTML tags may break them. + * This is called automatically by createBody(), so you don't need to call it yourself. + * @access public + * @return void + */ + public function setWordWrap() + { + if ($this->WordWrap < 1) { + return; + } + + switch ($this->message_type) { + case 'alt': + case 'alt_inline': + case 'alt_attach': + case 'alt_inline_attach': + $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap); + break; + default: + $this->Body = $this->wrapText($this->Body, $this->WordWrap); + break; + } + } + + /** + * Assemble message headers. + * @access public + * @return string The assembled headers + */ + public function createHeader() + { + $result = ''; + + if ($this->MessageDate == '') { + $this->MessageDate = self::rfcDate(); + } + $result .= $this->headerLine('Date', $this->MessageDate); + + // To be created automatically by mail() + if ($this->SingleTo) { + if ($this->Mailer != 'mail') { + foreach ($this->to as $toaddr) { + $this->SingleToArray[] = $this->addrFormat($toaddr); + } + } + } else { + if (count($this->to) > 0) { + if ($this->Mailer != 'mail') { + $result .= $this->addrAppend('To', $this->to); + } + } elseif (count($this->cc) == 0) { + $result .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + } + + $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName))); + + // sendmail and mail() extract Cc from the header before sending + if (count($this->cc) > 0) { + $result .= $this->addrAppend('Cc', $this->cc); + } + + // sendmail and mail() extract Bcc from the header before sending + if (( + $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail' + ) + and count($this->bcc) > 0 + ) { + $result .= $this->addrAppend('Bcc', $this->bcc); + } + + if (count($this->ReplyTo) > 0) { + $result .= $this->addrAppend('Reply-To', $this->ReplyTo); + } + + // mail() sets the subject itself + if ($this->Mailer != 'mail') { + $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); + } + + // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4 + // https://tools.ietf.org/html/rfc5322#section-3.6.4 + if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { + $this->lastMessageID = $this->MessageID; + } else { + $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); + } + $result .= $this->headerLine('Message-ID', $this->lastMessageID); + if (!is_null($this->Priority)) { + $result .= $this->headerLine('X-Priority', $this->Priority); + } + if ($this->XMailer == '') { + $result .= $this->headerLine( + 'X-Mailer', + 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)' + ); + } else { + $myXmailer = trim($this->XMailer); + if ($myXmailer) { + $result .= $this->headerLine('X-Mailer', $myXmailer); + } + } + + if ($this->ConfirmReadingTo != '') { + $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); + } + + // Add custom headers + foreach ($this->CustomHeader as $header) { + $result .= $this->headerLine( + trim($header[0]), + $this->encodeHeader(trim($header[1])) + ); + } + if (!$this->sign_key_file) { + $result .= $this->headerLine('MIME-Version', '1.0'); + $result .= $this->getMailMIME(); + } + + return $result; + } + + /** + * Get the message MIME type headers. + * @access public + * @return string + */ + public function getMailMIME() + { + $result = ''; + $ismultipart = true; + switch ($this->message_type) { + case 'inline': + $result .= $this->headerLine('Content-Type', 'multipart/related;'); + $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + case 'attach': + case 'inline_attach': + case 'alt_attach': + case 'alt_inline_attach': + $result .= $this->headerLine('Content-Type', 'multipart/mixed;'); + $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + case 'alt': + case 'alt_inline': + $result .= $this->headerLine('Content-Type', 'multipart/alternative;'); + $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + default: + // Catches case 'plain': and case '': + $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); + $ismultipart = false; + break; + } + // RFC1341 part 5 says 7bit is assumed if not specified + if ($this->Encoding != '7bit') { + // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE + if ($ismultipart) { + if ($this->Encoding == '8bit') { + $result .= $this->headerLine('Content-Transfer-Encoding', '8bit'); + } + // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible + } else { + $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); + } + } + + if ($this->Mailer != 'mail') { + $result .= $this->LE; + } + + return $result; + } + + /** + * Returns the whole MIME message. + * Includes complete headers and body. + * Only valid post preSend(). + * @see PHPMailer::preSend() + * @access public + * @return string + */ + public function getSentMIMEMessage() + { + return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; + } + + /** + * Assemble the message body. + * Returns an empty string on failure. + * @access public + * @throws phpmailerException + * @return string The assembled message body + */ + public function createBody() + { + $body = ''; + //Create unique IDs and preset boundaries + $this->uniqueid = md5(uniqid(time())); + $this->boundary[1] = 'b1_' . $this->uniqueid; + $this->boundary[2] = 'b2_' . $this->uniqueid; + $this->boundary[3] = 'b3_' . $this->uniqueid; + + if ($this->sign_key_file) { + $body .= $this->getMailMIME() . $this->LE; + } + + $this->setWordWrap(); + + $bodyEncoding = $this->Encoding; + $bodyCharSet = $this->CharSet; + //Can we do a 7-bit downgrade? + if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { + $bodyEncoding = '7bit'; + //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit + $bodyCharSet = 'us-ascii'; + } + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding for the body part only + if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { + $bodyEncoding = 'quoted-printable'; + } + + $altBodyEncoding = $this->Encoding; + $altBodyCharSet = $this->CharSet; + //Can we do a 7-bit downgrade? + if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { + $altBodyEncoding = '7bit'; + //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit + $altBodyCharSet = 'us-ascii'; + } + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding for the alt body part only + if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { + $altBodyEncoding = 'quoted-printable'; + } + //Use this as a preamble in all multipart message types + $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE; + switch ($this->message_type) { + case 'inline': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[1]); + break; + case 'attach': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'inline_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/related;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[2]); + $body .= $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'alt': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + if (!empty($this->Ical)) { + $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); + $body .= $this->encodeString($this->Ical, $this->Encoding); + $body .= $this->LE . $this->LE; + } + $body .= $this->endBoundary($this->boundary[1]); + break; + case 'alt_inline': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/related;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[2]); + $body .= $this->LE; + $body .= $this->endBoundary($this->boundary[1]); + break; + case 'alt_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->endBoundary($this->boundary[2]); + $body .= $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'alt_inline_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->textLine('--' . $this->boundary[2]); + $body .= $this->headerLine('Content-Type', 'multipart/related;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[3]); + $body .= $this->LE; + $body .= $this->endBoundary($this->boundary[2]); + $body .= $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + default: + // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types + //Reset the `Encoding` property in case we changed it for line length reasons + $this->Encoding = $bodyEncoding; + $body .= $this->encodeString($this->Body, $this->Encoding); + break; + } + + if ($this->isError()) { + $body = ''; + } elseif ($this->sign_key_file) { + try { + if (!defined('PKCS7_TEXT')) { + throw new phpmailerException($this->lang('extension_missing') . 'openssl'); + } + // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 + $file = tempnam(sys_get_temp_dir(), 'mail'); + if (false === file_put_contents($file, $body)) { + throw new phpmailerException($this->lang('signing') . ' Could not write temp file'); + } + $signed = tempnam(sys_get_temp_dir(), 'signed'); + //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 + if (empty($this->sign_extracerts_file)) { + $sign = @openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), + null + ); + } else { + $sign = @openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), + null, + PKCS7_DETACHED, + $this->sign_extracerts_file + ); + } + if ($sign) { + @unlink($file); + $body = file_get_contents($signed); + @unlink($signed); + //The message returned by openssl contains both headers and body, so need to split them up + $parts = explode("\n\n", $body, 2); + $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE; + $body = $parts[1]; + } else { + @unlink($file); + @unlink($signed); + throw new phpmailerException($this->lang('signing') . openssl_error_string()); + } + } catch (phpmailerException $exc) { + $body = ''; + if ($this->exceptions) { + throw $exc; + } + } + } + return $body; + } + + /** + * Return the start of a message boundary. + * @access protected + * @param string $boundary + * @param string $charSet + * @param string $contentType + * @param string $encoding + * @return string + */ + protected function getBoundary($boundary, $charSet, $contentType, $encoding) + { + $result = ''; + if ($charSet == '') { + $charSet = $this->CharSet; + } + if ($contentType == '') { + $contentType = $this->ContentType; + } + if ($encoding == '') { + $encoding = $this->Encoding; + } + $result .= $this->textLine('--' . $boundary); + $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); + $result .= $this->LE; + // RFC1341 part 5 says 7bit is assumed if not specified + if ($encoding != '7bit') { + $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); + } + $result .= $this->LE; + + return $result; + } + + /** + * Return the end of a message boundary. + * @access protected + * @param string $boundary + * @return string + */ + protected function endBoundary($boundary) + { + return $this->LE . '--' . $boundary . '--' . $this->LE; + } + + /** + * Set the message type. + * PHPMailer only supports some preset message types, not arbitrary MIME structures. + * @access protected + * @return void + */ + protected function setMessageType() + { + $type = array(); + if ($this->alternativeExists()) { + $type[] = 'alt'; + } + if ($this->inlineImageExists()) { + $type[] = 'inline'; + } + if ($this->attachmentExists()) { + $type[] = 'attach'; + } + $this->message_type = implode('_', $type); + if ($this->message_type == '') { + //The 'plain' message_type refers to the message having a single body element, not that it is plain-text + $this->message_type = 'plain'; + } + } + + /** + * Format a header line. + * @access public + * @param string $name + * @param string $value + * @return string + */ + public function headerLine($name, $value) + { + return $name . ': ' . $value . $this->LE; + } + + /** + * Return a formatted mail line. + * @access public + * @param string $value + * @return string + */ + public function textLine($value) + { + return $value . $this->LE; + } + + /** + * Add an attachment from a path on the filesystem. + * Returns false if the file could not be found or read. + * @param string $path Path to the attachment. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @param string $disposition Disposition to use + * @throws phpmailerException + * @return boolean + */ + public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') + { + try { + if (!@is_file($path)) { + throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE); + } + + // If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($path); + } + + $filename = basename($path); + if ($name == '') { + $name = $filename; + } + + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => $disposition, + 7 => 0 + ); + + } catch (phpmailerException $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + return false; + } + return true; + } + + /** + * Return the array of attachments. + * @return array + */ + public function getAttachments() + { + return $this->attachment; + } + + /** + * Attach all file, string, and binary attachments to the message. + * Returns an empty string on failure. + * @access protected + * @param string $disposition_type + * @param string $boundary + * @return string + */ + protected function attachAll($disposition_type, $boundary) + { + // Return text of body + $mime = array(); + $cidUniq = array(); + $incl = array(); + + // Add all attachments + foreach ($this->attachment as $attachment) { + // Check if it is a valid disposition_filter + if ($attachment[6] == $disposition_type) { + // Check for string attachment + $string = ''; + $path = ''; + $bString = $attachment[5]; + if ($bString) { + $string = $attachment[0]; + } else { + $path = $attachment[0]; + } + + $inclhash = md5(serialize($attachment)); + if (in_array($inclhash, $incl)) { + continue; + } + $incl[] = $inclhash; + $name = $attachment[2]; + $encoding = $attachment[3]; + $type = $attachment[4]; + $disposition = $attachment[6]; + $cid = $attachment[7]; + if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) { + continue; + } + $cidUniq[$cid] = true; + + $mime[] = sprintf('--%s%s', $boundary, $this->LE); + //Only include a filename property if we have one + if (!empty($name)) { + $mime[] = sprintf( + 'Content-Type: %s; name="%s"%s', + $type, + $this->encodeHeader($this->secureHeader($name)), + $this->LE + ); + } else { + $mime[] = sprintf( + 'Content-Type: %s%s', + $type, + $this->LE + ); + } + // RFC1341 part 5 says 7bit is assumed if not specified + if ($encoding != '7bit') { + $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE); + } + + if ($disposition == 'inline') { + $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE); + } + + // If a filename contains any of these chars, it should be quoted, + // but not otherwise: RFC2183 & RFC2045 5.1 + // Fixes a warning in IETF's msglint MIME checker + // Allow for bypassing the Content-Disposition header totally + if (!(empty($disposition))) { + $encoded_name = $this->encodeHeader($this->secureHeader($name)); + if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) { + $mime[] = sprintf( + 'Content-Disposition: %s; filename="%s"%s', + $disposition, + $encoded_name, + $this->LE . $this->LE + ); + } else { + if (!empty($encoded_name)) { + $mime[] = sprintf( + 'Content-Disposition: %s; filename=%s%s', + $disposition, + $encoded_name, + $this->LE . $this->LE + ); + } else { + $mime[] = sprintf( + 'Content-Disposition: %s%s', + $disposition, + $this->LE . $this->LE + ); + } + } + } else { + $mime[] = $this->LE; + } + + // Encode as string attachment + if ($bString) { + $mime[] = $this->encodeString($string, $encoding); + if ($this->isError()) { + return ''; + } + $mime[] = $this->LE . $this->LE; + } else { + $mime[] = $this->encodeFile($path, $encoding); + if ($this->isError()) { + return ''; + } + $mime[] = $this->LE . $this->LE; + } + } + } + + $mime[] = sprintf('--%s--%s', $boundary, $this->LE); + + return implode('', $mime); + } + + /** + * Encode a file attachment in requested format. + * Returns an empty string on failure. + * @param string $path The full path to the file + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @throws phpmailerException + * @access protected + * @return string + */ + protected function encodeFile($path, $encoding = 'base64') + { + try { + if (!is_readable($path)) { + throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE); + } + $magic_quotes = get_magic_quotes_runtime(); + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime(false); + } else { + //Doesn't exist in PHP 5.4, but we don't need to check because + //get_magic_quotes_runtime always returns false in 5.4+ + //so it will never get here + ini_set('magic_quotes_runtime', false); + } + } + $file_buffer = file_get_contents($path); + $file_buffer = $this->encodeString($file_buffer, $encoding); + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime($magic_quotes); + } else { + ini_set('magic_quotes_runtime', $magic_quotes); + } + } + return $file_buffer; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + return ''; + } + } + + /** + * Encode a string in requested format. + * Returns an empty string on failure. + * @param string $str The text to encode + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @access public + * @return string + */ + public function encodeString($str, $encoding = 'base64') + { + $encoded = ''; + switch (strtolower($encoding)) { + case 'base64': + $encoded = chunk_split(base64_encode($str), 76, $this->LE); + break; + case '7bit': + case '8bit': + $encoded = $this->fixEOL($str); + // Make sure it ends with a line break + if (substr($encoded, -(strlen($this->LE))) != $this->LE) { + $encoded .= $this->LE; + } + break; + case 'binary': + $encoded = $str; + break; + case 'quoted-printable': + $encoded = $this->encodeQP($str); + break; + default: + $this->setError($this->lang('encoding') . $encoding); + break; + } + return $encoded; + } + + /** + * Encode a header string optimally. + * Picks shortest of Q, B, quoted-printable or none. + * @access public + * @param string $str + * @param string $position + * @return string + */ + public function encodeHeader($str, $position = 'text') + { + $matchcount = 0; + switch (strtolower($position)) { + case 'phrase': + if (!preg_match('/[\200-\377]/', $str)) { + // Can't use addslashes as we don't know the value of magic_quotes_sybase + $encoded = addcslashes($str, "\0..\37\177\\\""); + if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { + return ($encoded); + } else { + return ("\"$encoded\""); + } + } + $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); + break; + /** @noinspection PhpMissingBreakStatementInspection */ + case 'comment': + $matchcount = preg_match_all('/[()"]/', $str, $matches); + // Intentional fall-through + case 'text': + default: + $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); + break; + } + + //There are no chars that need encoding + if ($matchcount == 0) { + return ($str); + } + + $maxlen = 75 - 7 - strlen($this->CharSet); + // Try to select the encoding which should produce the shortest output + if ($matchcount > strlen($str) / 3) { + // More than a third of the content will need encoding, so B encoding will be most efficient + $encoding = 'B'; + if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { + // Use a custom function which correctly encodes and wraps long + // multibyte strings without breaking lines within a character + $encoded = $this->base64EncodeWrapMB($str, "\n"); + } else { + $encoded = base64_encode($str); + $maxlen -= $maxlen % 4; + $encoded = trim(chunk_split($encoded, $maxlen, "\n")); + } + } else { + $encoding = 'Q'; + $encoded = $this->encodeQ($str, $position); + $encoded = $this->wrapText($encoded, $maxlen, true); + $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); + } + + $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); + $encoded = trim(str_replace("\n", $this->LE, $encoded)); + + return $encoded; + } + + /** + * Check if a string contains multi-byte characters. + * @access public + * @param string $str multi-byte text to wrap encode + * @return boolean + */ + public function hasMultiBytes($str) + { + if (function_exists('mb_strlen')) { + return (strlen($str) > mb_strlen($str, $this->CharSet)); + } else { // Assume no multibytes (we can't handle without mbstring functions anyway) + return false; + } + } + + /** + * Does a string contain any 8-bit chars (in any charset)? + * @param string $text + * @return boolean + */ + public function has8bitChars($text) + { + return (boolean)preg_match('/[\x80-\xFF]/', $text); + } + + /** + * Encode and wrap long multibyte strings for mail headers + * without breaking lines within a character. + * Adapted from a function by paravoid + * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 + * @access public + * @param string $str multi-byte text to wrap encode + * @param string $linebreak string to use as linefeed/end-of-line + * @return string + */ + public function base64EncodeWrapMB($str, $linebreak = null) + { + $start = '=?' . $this->CharSet . '?B?'; + $end = '?='; + $encoded = ''; + if ($linebreak === null) { + $linebreak = $this->LE; + } + + $mb_length = mb_strlen($str, $this->CharSet); + // Each line must have length <= 75, including $start and $end + $length = 75 - strlen($start) - strlen($end); + // Average multi-byte ratio + $ratio = $mb_length / strlen($str); + // Base64 has a 4:3 ratio + $avgLength = floor($length * $ratio * .75); + + for ($i = 0; $i < $mb_length; $i += $offset) { + $lookBack = 0; + do { + $offset = $avgLength - $lookBack; + $chunk = mb_substr($str, $i, $offset, $this->CharSet); + $chunk = base64_encode($chunk); + $lookBack++; + } while (strlen($chunk) > $length); + $encoded .= $chunk . $linebreak; + } + + // Chomp the last linefeed + $encoded = substr($encoded, 0, -strlen($linebreak)); + return $encoded; + } + + /** + * Encode a string in quoted-printable format. + * According to RFC2045 section 6.7. + * @access public + * @param string $string The text to encode + * @param integer $line_max Number of chars allowed on a line before wrapping + * @return string + * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment + */ + public function encodeQP($string, $line_max = 76) + { + // Use native function if it's available (>= PHP5.3) + if (function_exists('quoted_printable_encode')) { + return quoted_printable_encode($string); + } + // Fall back to a pure PHP implementation + $string = str_replace( + array('%20', '%0D%0A.', '%0D%0A', '%'), + array(' ', "\r\n=2E", "\r\n", '='), + rawurlencode($string) + ); + return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); + } + + /** + * Backward compatibility wrapper for an old QP encoding function that was removed. + * @see PHPMailer::encodeQP() + * @access public + * @param string $string + * @param integer $line_max + * @param boolean $space_conv + * @return string + * @deprecated Use encodeQP instead. + */ + public function encodeQPphp( + $string, + $line_max = 76, + /** @noinspection PhpUnusedParameterInspection */ $space_conv = false + ) { + return $this->encodeQP($string, $line_max); + } + + /** + * Encode a string using Q encoding. + * @link http://tools.ietf.org/html/rfc2047 + * @param string $str the text to encode + * @param string $position Where the text is going to be used, see the RFC for what that means + * @access public + * @return string + */ + public function encodeQ($str, $position = 'text') + { + // There should not be any EOL in the string + $pattern = ''; + $encoded = str_replace(array("\r", "\n"), '', $str); + switch (strtolower($position)) { + case 'phrase': + // RFC 2047 section 5.3 + $pattern = '^A-Za-z0-9!*+\/ -'; + break; + /** @noinspection PhpMissingBreakStatementInspection */ + case 'comment': + // RFC 2047 section 5.2 + $pattern = '\(\)"'; + // intentional fall-through + // for this reason we build the $pattern without including delimiters and [] + case 'text': + default: + // RFC 2047 section 5.1 + // Replace every high ascii, control, =, ? and _ characters + $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; + break; + } + $matches = array(); + if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { + // If the string contains an '=', make sure it's the first thing we replace + // so as to avoid double-encoding + $eqkey = array_search('=', $matches[0]); + if (false !== $eqkey) { + unset($matches[0][$eqkey]); + array_unshift($matches[0], '='); + } + foreach (array_unique($matches[0]) as $char) { + $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); + } + } + // Replace every spaces to _ (more readable than =20) + return str_replace(' ', '_', $encoded); + } + + /** + * Add a string or binary attachment (non-filesystem). + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * @param string $string String attachment data. + * @param string $filename Name of the attachment. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @param string $disposition Disposition to use + * @return void + */ + public function addStringAttachment( + $string, + $filename, + $encoding = 'base64', + $type = '', + $disposition = 'attachment' + ) { + // If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($filename); + } + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $filename, + 2 => basename($filename), + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => $disposition, + 7 => 0 + ); + } + + /** + * Add an embedded (inline) attachment from a file. + * This can include images, sounds, and just about any other document type. + * These differ from 'regular' attachments in that they are intended to be + * displayed inline with the message, not just attached for download. + * This is used in HTML messages that embed the images + * the HTML refers to using the $cid value. + * @param string $path Path to the attachment. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File MIME type. + * @param string $disposition Disposition to use + * @return boolean True on successfully adding an attachment + */ + public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') + { + if (!@is_file($path)) { + $this->setError($this->lang('file_access') . $path); + return false; + } + + // If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($path); + } + + $filename = basename($path); + if ($name == '') { + $name = $filename; + } + + // Append to $attachment array + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => $disposition, + 7 => $cid + ); + return true; + } + + /** + * Add an embedded stringified attachment. + * This can include images, sounds, and just about any other document type. + * Be sure to set the $type to an image type for images: + * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. + * @param string $string The attachment binary data. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. + * @param string $name + * @param string $encoding File encoding (see $Encoding). + * @param string $type MIME type. + * @param string $disposition Disposition to use + * @return boolean True on successfully adding an attachment + */ + public function addStringEmbeddedImage( + $string, + $cid, + $name = '', + $encoding = 'base64', + $type = '', + $disposition = 'inline' + ) { + // If a MIME type is not specified, try to work it out from the name + if ($type == '' and !empty($name)) { + $type = self::filenameToType($name); + } + + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $name, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => $disposition, + 7 => $cid + ); + return true; + } + + /** + * Check if an inline attachment is present. + * @access public + * @return boolean + */ + public function inlineImageExists() + { + foreach ($this->attachment as $attachment) { + if ($attachment[6] == 'inline') { + return true; + } + } + return false; + } + + /** + * Check if an attachment (non-inline) is present. + * @return boolean + */ + public function attachmentExists() + { + foreach ($this->attachment as $attachment) { + if ($attachment[6] == 'attachment') { + return true; + } + } + return false; + } + + /** + * Check if this message has an alternative body set. + * @return boolean + */ + public function alternativeExists() + { + return !empty($this->AltBody); + } + + /** + * Clear queued addresses of given kind. + * @access protected + * @param string $kind 'to', 'cc', or 'bcc' + * @return void + */ + public function clearQueuedAddresses($kind) + { + $RecipientsQueue = $this->RecipientsQueue; + foreach ($RecipientsQueue as $address => $params) { + if ($params[0] == $kind) { + unset($this->RecipientsQueue[$address]); + } + } + } + + /** + * Clear all To recipients. + * @return void + */ + public function clearAddresses() + { + foreach ($this->to as $to) { + unset($this->all_recipients[strtolower($to[0])]); + } + $this->to = array(); + $this->clearQueuedAddresses('to'); + } + + /** + * Clear all CC recipients. + * @return void + */ + public function clearCCs() + { + foreach ($this->cc as $cc) { + unset($this->all_recipients[strtolower($cc[0])]); + } + $this->cc = array(); + $this->clearQueuedAddresses('cc'); + } + + /** + * Clear all BCC recipients. + * @return void + */ + public function clearBCCs() + { + foreach ($this->bcc as $bcc) { + unset($this->all_recipients[strtolower($bcc[0])]); + } + $this->bcc = array(); + $this->clearQueuedAddresses('bcc'); + } + + /** + * Clear all ReplyTo recipients. + * @return void + */ + public function clearReplyTos() + { + $this->ReplyTo = array(); + $this->ReplyToQueue = array(); + } + + /** + * Clear all recipient types. + * @return void + */ + public function clearAllRecipients() + { + $this->to = array(); + $this->cc = array(); + $this->bcc = array(); + $this->all_recipients = array(); + $this->RecipientsQueue = array(); + } + + /** + * Clear all filesystem, string, and binary attachments. + * @return void + */ + public function clearAttachments() + { + $this->attachment = array(); + } + + /** + * Clear all custom headers. + * @return void + */ + public function clearCustomHeaders() + { + $this->CustomHeader = array(); + } + + /** + * Add an error message to the error container. + * @access protected + * @param string $msg + * @return void + */ + protected function setError($msg) + { + $this->error_count++; + if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { + $lasterror = $this->smtp->getError(); + if (!empty($lasterror['error'])) { + $msg .= $this->lang('smtp_error') . $lasterror['error']; + if (!empty($lasterror['detail'])) { + $msg .= ' Detail: '. $lasterror['detail']; + } + if (!empty($lasterror['smtp_code'])) { + $msg .= ' SMTP code: ' . $lasterror['smtp_code']; + } + if (!empty($lasterror['smtp_code_ex'])) { + $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; + } + } + } + $this->ErrorInfo = $msg; + } + + /** + * Return an RFC 822 formatted date. + * @access public + * @return string + * @static + */ + public static function rfcDate() + { + // Set the time zone to whatever the default is to avoid 500 errors + // Will default to UTC if it's not set properly in php.ini + date_default_timezone_set(@date_default_timezone_get()); + return date('D, j M Y H:i:s O'); + } + + /** + * Get the server hostname. + * Returns 'localhost.localdomain' if unknown. + * @access protected + * @return string + */ + protected function serverHostname() + { + $result = 'localhost.localdomain'; + if (!empty($this->Hostname)) { + $result = $this->Hostname; + } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) { + $result = $_SERVER['SERVER_NAME']; + } elseif (function_exists('gethostname') && gethostname() !== false) { + $result = gethostname(); + } elseif (php_uname('n') !== false) { + $result = php_uname('n'); + } + return $result; + } + + /** + * Get an error message in the current language. + * @access protected + * @param string $key + * @return string + */ + protected function lang($key) + { + if (count($this->language) < 1) { + $this->setLanguage('en'); // set the default language + } + + if (array_key_exists($key, $this->language)) { + if ($key == 'smtp_connect_failed') { + //Include a link to troubleshooting docs on SMTP connection failure + //this is by far the biggest cause of support questions + //but it's usually not PHPMailer's fault. + return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; + } + return $this->language[$key]; + } else { + //Return the key as a fallback + return $key; + } + } + + /** + * Check if an error occurred. + * @access public + * @return boolean True if an error did occur. + */ + public function isError() + { + return ($this->error_count > 0); + } + + /** + * Ensure consistent line endings in a string. + * Changes every end of line from CRLF, CR or LF to $this->LE. + * @access public + * @param string $str String to fixEOL + * @return string + */ + public function fixEOL($str) + { + // Normalise to \n + $nstr = str_replace(array("\r\n", "\r"), "\n", $str); + // Now convert LE as needed + if ($this->LE !== "\n") { + $nstr = str_replace("\n", $this->LE, $nstr); + } + return $nstr; + } + + /** + * Add a custom header. + * $name value can be overloaded to contain + * both header name and value (name:value) + * @access public + * @param string $name Custom header name + * @param string $value Header value + * @return void + */ + public function addCustomHeader($name, $value = null) + { + if ($value === null) { + // Value passed in as name:value + $this->CustomHeader[] = explode(':', $name, 2); + } else { + $this->CustomHeader[] = array($name, $value); + } + } + + /** + * Returns all custom headers. + * @return array + */ + public function getCustomHeaders() + { + return $this->CustomHeader; + } + + /** + * Create a message body from an HTML string. + * Automatically inlines images and creates a plain-text version by converting the HTML, + * overwriting any existing values in Body and AltBody. + * $basedir is used when handling relative image paths, e.g. + * will look for an image file in $basedir/images/a.png and convert it to inline. + * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself. + * @access public + * @param string $message HTML message string + * @param string $basedir base directory for relative paths to images + * @param boolean|callable $advanced Whether to use the internal HTML to text converter + * or your own custom converter @see PHPMailer::html2text() + * @return string $message The transformed message Body + */ + public function msgHTML($message, $basedir = '', $advanced = false) + { + preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); + if (array_key_exists(2, $images)) { + foreach ($images[2] as $imgindex => $url) { + // Convert data URIs into embedded images + if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { + $data = substr($url, strpos($url, ',')); + if ($match[2]) { + $data = base64_decode($data); + } else { + $data = rawurldecode($data); + } + $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 + if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) { + $message = str_replace( + $images[0][$imgindex], + $images[1][$imgindex] . '="cid:' . $cid . '"', + $message + ); + } + } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) { + // Do not change urls for absolute images (thanks to corvuscorax) + // Do not change urls that are already inline images + $filename = basename($url); + $directory = dirname($url); + if ($directory == '.') { + $directory = ''; + } + $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 + if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { + $basedir .= '/'; + } + if (strlen($directory) > 1 && substr($directory, -1) != '/') { + $directory .= '/'; + } + if ($this->addEmbeddedImage( + $basedir . $directory . $filename, + $cid, + $filename, + 'base64', + self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) + ) + ) { + $message = preg_replace( + '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', + $images[1][$imgindex] . '="cid:' . $cid . '"', + $message + ); + } + } + } + } + $this->isHTML(true); + // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better + $this->Body = $this->normalizeBreaks($message); + $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); + if (!$this->alternativeExists()) { + $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . + self::CRLF . self::CRLF; + } + return $this->Body; + } + + /** + * Convert an HTML string into plain text. + * This is used by msgHTML(). + * Note - older versions of this function used a bundled advanced converter + * which was been removed for license reasons in #232. + * Example usage: + * + * // Use default conversion + * $plain = $mail->html2text($html); + * // Use your own custom converter + * $plain = $mail->html2text($html, function($html) { + * $converter = new MyHtml2text($html); + * return $converter->get_text(); + * }); + * + * @param string $html The HTML text to convert + * @param boolean|callable $advanced Any boolean value to use the internal converter, + * or provide your own callable for custom conversion. + * @return string + */ + public function html2text($html, $advanced = false) + { + if (is_callable($advanced)) { + return call_user_func($advanced, $html); + } + return html_entity_decode( + trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), + ENT_QUOTES, + $this->CharSet + ); + } + + /** + * Get the MIME type for a file extension. + * @param string $ext File extension + * @access public + * @return string MIME type of file. + * @static + */ + public static function _mime_types($ext = '') + { + $mimes = array( + 'xl' => 'application/excel', + 'js' => 'application/javascript', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'bin' => 'application/macbinary', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'class' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'psd' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'php3' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mpga' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'eml' => 'message/rfc822', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'log' => 'text/plain', + 'text' => 'text/plain', + 'txt' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'vcf' => 'text/vcard', + 'vcard' => 'text/vcard', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mov' => 'video/quicktime', + 'qt' => 'video/quicktime', + 'rv' => 'video/vnd.rn-realvideo', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie' + ); + if (array_key_exists(strtolower($ext), $mimes)) { + return $mimes[strtolower($ext)]; + } + return 'application/octet-stream'; + } + + /** + * Map a file name to a MIME type. + * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. + * @param string $filename A file name or full path, does not need to exist as a file + * @return string + * @static + */ + public static function filenameToType($filename) + { + // In case the path is a URL, strip any query string before getting extension + $qpos = strpos($filename, '?'); + if (false !== $qpos) { + $filename = substr($filename, 0, $qpos); + } + $pathinfo = self::mb_pathinfo($filename); + return self::_mime_types($pathinfo['extension']); + } + + /** + * Multi-byte-safe pathinfo replacement. + * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. + * Works similarly to the one in PHP >= 5.2.0 + * @link http://www.php.net/manual/en/function.pathinfo.php#107461 + * @param string $path A filename or path, does not need to exist as a file + * @param integer|string $options Either a PATHINFO_* constant, + * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 + * @return string|array + * @static + */ + public static function mb_pathinfo($path, $options = null) + { + $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); + $pathinfo = array(); + if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) { + if (array_key_exists(1, $pathinfo)) { + $ret['dirname'] = $pathinfo[1]; + } + if (array_key_exists(2, $pathinfo)) { + $ret['basename'] = $pathinfo[2]; + } + if (array_key_exists(5, $pathinfo)) { + $ret['extension'] = $pathinfo[5]; + } + if (array_key_exists(3, $pathinfo)) { + $ret['filename'] = $pathinfo[3]; + } + } + switch ($options) { + case PATHINFO_DIRNAME: + case 'dirname': + return $ret['dirname']; + case PATHINFO_BASENAME: + case 'basename': + return $ret['basename']; + case PATHINFO_EXTENSION: + case 'extension': + return $ret['extension']; + case PATHINFO_FILENAME: + case 'filename': + return $ret['filename']; + default: + return $ret; + } + } + + /** + * Set or reset instance properties. + * You should avoid this function - it's more verbose, less efficient, more error-prone and + * harder to debug than setting properties directly. + * Usage Example: + * `$mail->set('SMTPSecure', 'tls');` + * is the same as: + * `$mail->SMTPSecure = 'tls';` + * @access public + * @param string $name The property name to set + * @param mixed $value The value to set the property to + * @return boolean + * @TODO Should this not be using the __set() magic function? + */ + public function set($name, $value = '') + { + if (property_exists($this, $name)) { + $this->$name = $value; + return true; + } else { + $this->setError($this->lang('variable_set') . $name); + return false; + } + } + + /** + * Strip newlines to prevent header injection. + * @access public + * @param string $str + * @return string + */ + public function secureHeader($str) + { + return trim(str_replace(array("\r", "\n"), '', $str)); + } + + /** + * Normalize line breaks in a string. + * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. + * Defaults to CRLF (for message bodies) and preserves consecutive breaks. + * @param string $text + * @param string $breaktype What kind of line break to use, defaults to CRLF + * @return string + * @access public + * @static + */ + public static function normalizeBreaks($text, $breaktype = "\r\n") + { + return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); + } + + /** + * Set the public and private key files and password for S/MIME signing. + * @access public + * @param string $cert_filename + * @param string $key_filename + * @param string $key_pass Password for private key + * @param string $extracerts_filename Optional path to chain certificate + */ + public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') + { + $this->sign_cert_file = $cert_filename; + $this->sign_key_file = $key_filename; + $this->sign_key_pass = $key_pass; + $this->sign_extracerts_file = $extracerts_filename; + } + + /** + * Quoted-Printable-encode a DKIM header. + * @access public + * @param string $txt + * @return string + */ + public function DKIM_QP($txt) + { + $line = ''; + for ($i = 0; $i < strlen($txt); $i++) { + $ord = ord($txt[$i]); + if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { + $line .= $txt[$i]; + } else { + $line .= '=' . sprintf('%02X', $ord); + } + } + return $line; + } + + /** + * Generate a DKIM signature. + * @access public + * @param string $signHeader + * @throws phpmailerException + * @return string The DKIM signature value + */ + public function DKIM_Sign($signHeader) + { + if (!defined('PKCS7_TEXT')) { + if ($this->exceptions) { + throw new phpmailerException($this->lang('extension_missing') . 'openssl'); + } + return ''; + } + $privKeyStr = file_get_contents($this->DKIM_private); + if ('' != $this->DKIM_passphrase) { + $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); + } else { + $privKey = openssl_pkey_get_private($privKeyStr); + } + //Workaround for missing digest algorithms in old PHP & OpenSSL versions + //@link http://stackoverflow.com/a/11117338/333340 + if (version_compare(PHP_VERSION, '5.3.0') >= 0 and + in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { + if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { + openssl_pkey_free($privKey); + return base64_encode($signature); + } + } else { + $pinfo = openssl_pkey_get_details($privKey); + $hash = hash('sha256', $signHeader); + //'Magic' constant for SHA256 from RFC3447 + //@link https://tools.ietf.org/html/rfc3447#page-43 + $t = '3031300d060960864801650304020105000420' . $hash; + $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3); + $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t); + + if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) { + openssl_pkey_free($privKey); + return base64_encode($signature); + } + } + openssl_pkey_free($privKey); + return ''; + } + + /** + * Generate a DKIM canonicalization header. + * @access public + * @param string $signHeader Header + * @return string + */ + public function DKIM_HeaderC($signHeader) + { + $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader); + $lines = explode("\r\n", $signHeader); + foreach ($lines as $key => $line) { + list($heading, $value) = explode(':', $line, 2); + $heading = strtolower($heading); + $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces + $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value + } + $signHeader = implode("\r\n", $lines); + return $signHeader; + } + + /** + * Generate a DKIM canonicalization body. + * @access public + * @param string $body Message Body + * @return string + */ + public function DKIM_BodyC($body) + { + if ($body == '') { + return "\r\n"; + } + // stabilize line endings + $body = str_replace("\r\n", "\n", $body); + $body = str_replace("\n", "\r\n", $body); + // END stabilize line endings + while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { + $body = substr($body, 0, strlen($body) - 2); + } + return $body; + } + + /** + * Create the DKIM header and body in a new message header. + * @access public + * @param string $headers_line Header lines + * @param string $subject Subject + * @param string $body Body + * @return string + */ + public function DKIM_Add($headers_line, $subject, $body) + { + $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms + $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body + $DKIMquery = 'dns/txt'; // Query method + $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) + $subject_header = "Subject: $subject"; + $headers = explode($this->LE, $headers_line); + $from_header = ''; + $to_header = ''; + $date_header = ''; + $current = ''; + foreach ($headers as $header) { + if (strpos($header, 'From:') === 0) { + $from_header = $header; + $current = 'from_header'; + } elseif (strpos($header, 'To:') === 0) { + $to_header = $header; + $current = 'to_header'; + } elseif (strpos($header, 'Date:') === 0) { + $date_header = $header; + $current = 'date_header'; + } else { + if (!empty($$current) && strpos($header, ' =?') === 0) { + $$current .= $header; + } else { + $current = ''; + } + } + } + $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); + $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); + $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); + $subject = str_replace( + '|', + '=7C', + $this->DKIM_QP($subject_header) + ); // Copied header fields (dkim-quoted-printable) + $body = $this->DKIM_BodyC($body); + $DKIMlen = strlen($body); // Length of body + $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body + if ('' == $this->DKIM_identity) { + $ident = ''; + } else { + $ident = ' i=' . $this->DKIM_identity . ';'; + } + $dkimhdrs = 'DKIM-Signature: v=1; a=' . + $DKIMsignatureType . '; q=' . + $DKIMquery . '; l=' . + $DKIMlen . '; s=' . + $this->DKIM_selector . + ";\r\n" . + "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . + "\th=From:To:Date:Subject;\r\n" . + "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . + "\tz=$from\r\n" . + "\t|$to\r\n" . + "\t|$date\r\n" . + "\t|$subject;\r\n" . + "\tbh=" . $DKIMb64 . ";\r\n" . + "\tb="; + $toSign = $this->DKIM_HeaderC( + $from_header . "\r\n" . + $to_header . "\r\n" . + $date_header . "\r\n" . + $subject_header . "\r\n" . + $dkimhdrs + ); + $signed = $this->DKIM_Sign($toSign); + return $dkimhdrs . $signed . "\r\n"; + } + + /** + * Detect if a string contains a line longer than the maximum line length allowed. + * @param string $str + * @return boolean + * @static + */ + public static function hasLineLongerThanMax($str) + { + //+2 to include CRLF line break for a 1000 total + return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str); + } + + /** + * Allows for public read access to 'to' property. + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * @access public + * @return array + */ + public function getToAddresses() + { + return $this->to; + } + + /** + * Allows for public read access to 'cc' property. + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * @access public + * @return array + */ + public function getCcAddresses() + { + return $this->cc; + } + + /** + * Allows for public read access to 'bcc' property. + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * @access public + * @return array + */ + public function getBccAddresses() + { + return $this->bcc; + } + + /** + * Allows for public read access to 'ReplyTo' property. + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * @access public + * @return array + */ + public function getReplyToAddresses() + { + return $this->ReplyTo; + } + + /** + * Allows for public read access to 'all_recipients' property. + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * @access public + * @return array + */ + public function getAllRecipientAddresses() + { + return $this->all_recipients; + } + + /** + * Perform a callback. + * @param boolean $isSent + * @param array $to + * @param array $cc + * @param array $bcc + * @param string $subject + * @param string $body + * @param string $from + */ + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from) + { + if (!empty($this->action_function) && is_callable($this->action_function)) { + $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); + call_user_func_array($this->action_function, $params); + } + } +} + +/** + * PHPMailer exception handler + * @package PHPMailer + */ +class phpmailerException extends Exception +{ + /** + * Prettify error message output + * @return string + */ + public function errorMessage() + { + $errorMsg = '' . $this->getMessage() . "
    \n"; + return $errorMsg; + } +} diff --git a/www/core/func/libs/mail/class.phpmaileroauth.php b/www/core/func/libs/mail/class.phpmaileroauth.php new file mode 100644 index 0000000..b1bb09f --- /dev/null +++ b/www/core/func/libs/mail/class.phpmaileroauth.php @@ -0,0 +1,197 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2014 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +/** + * PHPMailerOAuth - PHPMailer subclass adding OAuth support. + * @package PHPMailer + * @author @sherryl4george + * @author Marcus Bointon (@Synchro) + */ +class PHPMailerOAuth extends PHPMailer +{ + /** + * The OAuth user's email address + * @var string + */ + public $oauthUserEmail = ''; + + /** + * The OAuth refresh token + * @var string + */ + public $oauthRefreshToken = ''; + + /** + * The OAuth client ID + * @var string + */ + public $oauthClientId = ''; + + /** + * The OAuth client secret + * @var string + */ + public $oauthClientSecret = ''; + + /** + * An instance of the PHPMailerOAuthGoogle class. + * @var PHPMailerOAuthGoogle + * @access protected + */ + protected $oauth = null; + + /** + * Get a PHPMailerOAuthGoogle instance to use. + * @return PHPMailerOAuthGoogle + */ + public function getOAUTHInstance() + { + if (!is_object($this->oauth)) { + $this->oauth = new PHPMailerOAuthGoogle( + $this->oauthUserEmail, + $this->oauthClientSecret, + $this->oauthClientId, + $this->oauthRefreshToken + ); + } + return $this->oauth; + } + + /** + * Initiate a connection to an SMTP server. + * Overrides the original smtpConnect method to add support for OAuth. + * @param array $options An array of options compatible with stream_context_create() + * @uses SMTP + * @access public + * @return bool + * @throws phpmailerException + */ + public function smtpConnect($options = array()) + { + if (is_null($this->smtp)) { + $this->smtp = $this->getSMTPInstance(); + } + + if (is_null($this->oauth)) { + $this->oauth = $this->getOAUTHInstance(); + } + + // Already connected? + if ($this->smtp->connected()) { + return true; + } + + $this->smtp->setTimeout($this->Timeout); + $this->smtp->setDebugLevel($this->SMTPDebug); + $this->smtp->setDebugOutput($this->Debugoutput); + $this->smtp->setVerp($this->do_verp); + $hosts = explode(';', $this->Host); + $lastexception = null; + + foreach ($hosts as $hostentry) { + $hostinfo = array(); + if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { + // Not a valid host entry + continue; + } + // $hostinfo[2]: optional ssl or tls prefix + // $hostinfo[3]: the hostname + // $hostinfo[4]: optional port number + // The host string prefix can temporarily override the current setting for SMTPSecure + // If it's not specified, the default value is used + $prefix = ''; + $secure = $this->SMTPSecure; + $tls = ($this->SMTPSecure == 'tls'); + if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { + $prefix = 'ssl://'; + $tls = false; // Can't have SSL and TLS at the same time + $secure = 'ssl'; + } elseif ($hostinfo[2] == 'tls') { + $tls = true; + // tls doesn't use a prefix + $secure = 'tls'; + } + //Do we need the OpenSSL extension? + $sslext = defined('OPENSSL_ALGO_SHA1'); + if ('tls' === $secure or 'ssl' === $secure) { + //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled + if (!$sslext) { + throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); + } + } + $host = $hostinfo[3]; + $port = $this->Port; + $tport = (integer)$hostinfo[4]; + if ($tport > 0 and $tport < 65536) { + $port = $tport; + } + if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->serverHostname(); + } + $this->smtp->hello($hello); + //Automatically enable TLS encryption if: + // * it's not disabled + // * we have openssl extension + // * we are not already using SSL + // * the server offers STARTTLS + if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { + $tls = true; + } + if ($tls) { + if (!$this->smtp->startTLS()) { + throw new phpmailerException($this->lang('connect_host')); + } + // We must resend HELO after tls negotiation + $this->smtp->hello($hello); + } + if ($this->SMTPAuth) { + if (!$this->smtp->authenticate( + $this->Username, + $this->Password, + $this->AuthType, + $this->Realm, + $this->Workstation, + $this->oauth + ) + ) { + throw new phpmailerException($this->lang('authenticate')); + } + } + return true; + } catch (phpmailerException $exc) { + $lastexception = $exc; + $this->edebug($exc->getMessage()); + // We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->quit(); + } + } + } + // If we get here, all connection attempts have failed, so close connection hard + $this->smtp->close(); + // As we've caught all exceptions, just report whatever the last one was + if ($this->exceptions and !is_null($lastexception)) { + throw $lastexception; + } + return false; + } +} diff --git a/www/core/func/libs/mail/class.phpmaileroauthgoogle.php b/www/core/func/libs/mail/class.phpmaileroauthgoogle.php new file mode 100644 index 0000000..71c9bd3 --- /dev/null +++ b/www/core/func/libs/mail/class.phpmaileroauthgoogle.php @@ -0,0 +1,77 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2014 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +/** + * PHPMailerOAuthGoogle - Wrapper for League OAuth2 Google provider. + * @package PHPMailer + * @author @sherryl4george + * @author Marcus Bointon (@Synchro) + * @link https://github.com/thephpleague/oauth2-client + */ +class PHPMailerOAuthGoogle +{ + private $oauthUserEmail = ''; + private $oauthRefreshToken = ''; + private $oauthClientId = ''; + private $oauthClientSecret = ''; + + /** + * @param string $UserEmail + * @param string $ClientSecret + * @param string $ClientId + * @param string $RefreshToken + */ + public function __construct( + $UserEmail, + $ClientSecret, + $ClientId, + $RefreshToken + ) { + $this->oauthClientId = $ClientId; + $this->oauthClientSecret = $ClientSecret; + $this->oauthRefreshToken = $RefreshToken; + $this->oauthUserEmail = $UserEmail; + } + + private function getProvider() + { + return new League\OAuth2\Client\Provider\Google([ + 'clientId' => $this->oauthClientId, + 'clientSecret' => $this->oauthClientSecret + ]); + } + + private function getGrant() + { + return new \League\OAuth2\Client\Grant\RefreshToken(); + } + + private function getToken() + { + $provider = $this->getProvider(); + $grant = $this->getGrant(); + return $provider->getAccessToken($grant, ['refresh_token' => $this->oauthRefreshToken]); + } + + public function getOauth64() + { + $token = $this->getToken(); + return base64_encode("user=" . $this->oauthUserEmail . "\001auth=Bearer " . $token . "\001\001"); + } +} diff --git a/www/core/func/libs/mail/class.pop3.php b/www/core/func/libs/mail/class.pop3.php new file mode 100644 index 0000000..56ad1bf --- /dev/null +++ b/www/core/func/libs/mail/class.pop3.php @@ -0,0 +1,407 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2014 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +/** + * PHPMailer POP-Before-SMTP Authentication Class. + * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication. + * Does not support APOP. + * @package PHPMailer + * @author Richard Davey (original author) + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + */ +class POP3 +{ + /** + * The POP3 PHPMailer Version number. + * @var string + * @access public + */ + public $Version = '5.2.16'; + + /** + * Default POP3 port number. + * @var integer + * @access public + */ + public $POP3_PORT = 110; + + /** + * Default timeout in seconds. + * @var integer + * @access public + */ + public $POP3_TIMEOUT = 30; + + /** + * POP3 Carriage Return + Line Feed. + * @var string + * @access public + * @deprecated Use the constant instead + */ + public $CRLF = "\r\n"; + + /** + * Debug display level. + * Options: 0 = no, 1+ = yes + * @var integer + * @access public + */ + public $do_debug = 0; + + /** + * POP3 mail server hostname. + * @var string + * @access public + */ + public $host; + + /** + * POP3 port number. + * @var integer + * @access public + */ + public $port; + + /** + * POP3 Timeout Value in seconds. + * @var integer + * @access public + */ + public $tval; + + /** + * POP3 username + * @var string + * @access public + */ + public $username; + + /** + * POP3 password. + * @var string + * @access public + */ + public $password; + + /** + * Resource handle for the POP3 connection socket. + * @var resource + * @access protected + */ + protected $pop_conn; + + /** + * Are we connected? + * @var boolean + * @access protected + */ + protected $connected = false; + + /** + * Error container. + * @var array + * @access protected + */ + protected $errors = array(); + + /** + * Line break constant + */ + const CRLF = "\r\n"; + + /** + * Simple static wrapper for all-in-one POP before SMTP + * @param $host + * @param integer|boolean $port The port number to connect to + * @param integer|boolean $timeout The timeout value + * @param string $username + * @param string $password + * @param integer $debug_level + * @return boolean + */ + public static function popBeforeSmtp( + $host, + $port = false, + $timeout = false, + $username = '', + $password = '', + $debug_level = 0 + ) { + $pop = new POP3; + return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level); + } + + /** + * Authenticate with a POP3 server. + * A connect, login, disconnect sequence + * appropriate for POP-before SMTP authorisation. + * @access public + * @param string $host The hostname to connect to + * @param integer|boolean $port The port number to connect to + * @param integer|boolean $timeout The timeout value + * @param string $username + * @param string $password + * @param integer $debug_level + * @return boolean + */ + public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0) + { + $this->host = $host; + // If no port value provided, use default + if (false === $port) { + $this->port = $this->POP3_PORT; + } else { + $this->port = (integer)$port; + } + // If no timeout value provided, use default + if (false === $timeout) { + $this->tval = $this->POP3_TIMEOUT; + } else { + $this->tval = (integer)$timeout; + } + $this->do_debug = $debug_level; + $this->username = $username; + $this->password = $password; + // Reset the error log + $this->errors = array(); + // connect + $result = $this->connect($this->host, $this->port, $this->tval); + if ($result) { + $login_result = $this->login($this->username, $this->password); + if ($login_result) { + $this->disconnect(); + return true; + } + } + // We need to disconnect regardless of whether the login succeeded + $this->disconnect(); + return false; + } + + /** + * Connect to a POP3 server. + * @access public + * @param string $host + * @param integer|boolean $port + * @param integer $tval + * @return boolean + */ + public function connect($host, $port = false, $tval = 30) + { + // Are we already connected? + if ($this->connected) { + return true; + } + + //On Windows this will raise a PHP Warning error if the hostname doesn't exist. + //Rather than suppress it with @fsockopen, capture it cleanly instead + set_error_handler(array($this, 'catchWarning')); + + if (false === $port) { + $port = $this->POP3_PORT; + } + + // connect to the POP3 server + $this->pop_conn = fsockopen( + $host, // POP3 Host + $port, // Port # + $errno, // Error Number + $errstr, // Error Message + $tval + ); // Timeout (seconds) + // Restore the error handler + restore_error_handler(); + + // Did we connect? + if (false === $this->pop_conn) { + // It would appear not... + $this->setError(array( + 'error' => "Failed to connect to server $host on port $port", + 'errno' => $errno, + 'errstr' => $errstr + )); + return false; + } + + // Increase the stream time-out + stream_set_timeout($this->pop_conn, $tval, 0); + + // Get the POP3 server response + $pop3_response = $this->getResponse(); + // Check for the +OK + if ($this->checkResponse($pop3_response)) { + // The connection is established and the POP3 server is talking + $this->connected = true; + return true; + } + return false; + } + + /** + * Log in to the POP3 server. + * Does not support APOP (RFC 2828, 4949). + * @access public + * @param string $username + * @param string $password + * @return boolean + */ + public function login($username = '', $password = '') + { + if (!$this->connected) { + $this->setError('Not connected to POP3 server'); + } + if (empty($username)) { + $username = $this->username; + } + if (empty($password)) { + $password = $this->password; + } + + // Send the Username + $this->sendString("USER $username" . self::CRLF); + $pop3_response = $this->getResponse(); + if ($this->checkResponse($pop3_response)) { + // Send the Password + $this->sendString("PASS $password" . self::CRLF); + $pop3_response = $this->getResponse(); + if ($this->checkResponse($pop3_response)) { + return true; + } + } + return false; + } + + /** + * Disconnect from the POP3 server. + * @access public + */ + public function disconnect() + { + $this->sendString('QUIT'); + //The QUIT command may cause the daemon to exit, which will kill our connection + //So ignore errors here + try { + @fclose($this->pop_conn); + } catch (Exception $e) { + //Do nothing + }; + } + + /** + * Get a response from the POP3 server. + * $size is the maximum number of bytes to retrieve + * @param integer $size + * @return string + * @access protected + */ + protected function getResponse($size = 128) + { + $response = fgets($this->pop_conn, $size); + if ($this->do_debug >= 1) { + echo "Server -> Client: $response"; + } + return $response; + } + + /** + * Send raw data to the POP3 server. + * @param string $string + * @return integer + * @access protected + */ + protected function sendString($string) + { + if ($this->pop_conn) { + if ($this->do_debug >= 2) { //Show client messages when debug >= 2 + echo "Client -> Server: $string"; + } + return fwrite($this->pop_conn, $string, strlen($string)); + } + return 0; + } + + /** + * Checks the POP3 server response. + * Looks for for +OK or -ERR. + * @param string $string + * @return boolean + * @access protected + */ + protected function checkResponse($string) + { + if (substr($string, 0, 3) !== '+OK') { + $this->setError(array( + 'error' => "Server reported an error: $string", + 'errno' => 0, + 'errstr' => '' + )); + return false; + } else { + return true; + } + } + + /** + * Add an error to the internal error store. + * Also display debug output if it's enabled. + * @param $error + * @access protected + */ + protected function setError($error) + { + $this->errors[] = $error; + if ($this->do_debug >= 1) { + echo '
    ';
    +            foreach ($this->errors as $error) {
    +                print_r($error);
    +            }
    +            echo '
    '; + } + } + + /** + * Get an array of error messages, if any. + * @return array + */ + public function getErrors() + { + return $this->errors; + } + + /** + * POP3 connection error handler. + * @param integer $errno + * @param string $errstr + * @param string $errfile + * @param integer $errline + * @access protected + */ + protected function catchWarning($errno, $errstr, $errfile, $errline) + { + $this->setError(array( + 'error' => "Connecting to the POP3 server raised a PHP warning: ", + 'errno' => $errno, + 'errstr' => $errstr, + 'errfile' => $errfile, + 'errline' => $errline + )); + } +} diff --git a/www/core/func/libs/mail/class.smtp.php b/www/core/func/libs/mail/class.smtp.php new file mode 100644 index 0000000..b5a7ccc --- /dev/null +++ b/www/core/func/libs/mail/class.smtp.php @@ -0,0 +1,1214 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2014 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +/** + * PHPMailer RFC821 SMTP email transport class. + * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. + * @package PHPMailer + * @author Chris Ryan + * @author Marcus Bointon + */ +class SMTP +{ + /** + * The PHPMailer SMTP version number. + * @var string + */ + const VERSION = '5.2.16'; + + /** + * SMTP line break constant. + * @var string + */ + const CRLF = "\r\n"; + + /** + * The SMTP port to use if one is not specified. + * @var integer + */ + const DEFAULT_SMTP_PORT = 25; + + /** + * The maximum line length allowed by RFC 2822 section 2.1.1 + * @var integer + */ + const MAX_LINE_LENGTH = 998; + + /** + * Debug level for no output + */ + const DEBUG_OFF = 0; + + /** + * Debug level to show client -> server messages + */ + const DEBUG_CLIENT = 1; + + /** + * Debug level to show client -> server and server -> client messages + */ + const DEBUG_SERVER = 2; + + /** + * Debug level to show connection status, client -> server and server -> client messages + */ + const DEBUG_CONNECTION = 3; + + /** + * Debug level to show all messages + */ + const DEBUG_LOWLEVEL = 4; + + /** + * The PHPMailer SMTP Version number. + * @var string + * @deprecated Use the `VERSION` constant instead + * @see SMTP::VERSION + */ + public $Version = '5.2.16'; + + /** + * SMTP server port number. + * @var integer + * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead + * @see SMTP::DEFAULT_SMTP_PORT + */ + public $SMTP_PORT = 25; + + /** + * SMTP reply line ending. + * @var string + * @deprecated Use the `CRLF` constant instead + * @see SMTP::CRLF + */ + public $CRLF = "\r\n"; + + /** + * Debug output level. + * Options: + * * self::DEBUG_OFF (`0`) No debug output, default + * * self::DEBUG_CLIENT (`1`) Client commands + * * self::DEBUG_SERVER (`2`) Client commands and server responses + * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status + * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages + * @var integer + */ + public $do_debug = self::DEBUG_OFF; + + /** + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
    `, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini + * + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * + * @var string|callable + */ + public $Debugoutput = 'echo'; + + /** + * Whether to use VERP. + * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path + * @link http://www.postfix.org/VERP_README.html Info on VERP + * @var boolean + */ + public $do_verp = false; + + /** + * The timeout value for connection, in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 + * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. + * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 + * @var integer + */ + public $Timeout = 300; + + /** + * How long to wait for commands to complete, in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 + * @var integer + */ + public $Timelimit = 300; + + /** + * The socket for the server connection. + * @var resource + */ + protected $smtp_conn; + + /** + * Error information, if any, for the last SMTP command. + * @var array + */ + protected $error = array( + 'error' => '', + 'detail' => '', + 'smtp_code' => '', + 'smtp_code_ex' => '' + ); + + /** + * The reply the server sent to us for HELO. + * If null, no HELO string has yet been received. + * @var string|null + */ + protected $helo_rply = null; + + /** + * The set of SMTP extensions sent in reply to EHLO command. + * Indexes of the array are extension names. + * Value at index 'HELO' or 'EHLO' (according to command that was sent) + * represents the server name. In case of HELO it is the only element of the array. + * Other values can be boolean TRUE or an array containing extension options. + * If null, no HELO/EHLO string has yet been received. + * @var array|null + */ + protected $server_caps = null; + + /** + * The most recent reply received from the server. + * @var string + */ + protected $last_reply = ''; + + /** + * Output debugging info via a user-selected method. + * @see SMTP::$Debugoutput + * @see SMTP::$do_debug + * @param string $str Debug string to output + * @param integer $level The debug level of this message; see DEBUG_* constants + * @return void + */ + protected function edebug($str, $level = 0) + { + if ($level > $this->do_debug) { + return; + } + //Avoid clash with built-in function names + if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { + call_user_func($this->Debugoutput, $str, $level); + return; + } + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ) + . "
    \n"; + break; + case 'echo': + default: + //Normalize line breaks + $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); + echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( + "\n", + "\n \t ", + trim($str) + )."\n"; + } + } + + /** + * Connect to an SMTP server. + * @param string $host SMTP server IP or host name + * @param integer $port The port number to connect to + * @param integer $timeout How long to wait for the connection to open + * @param array $options An array of options for stream_context_create() + * @access public + * @return boolean + */ + public function connect($host, $port = null, $timeout = 30, $options = array()) + { + static $streamok; + //This is enabled by default since 5.0.0 but some providers disable it + //Check this once and cache the result + if (is_null($streamok)) { + $streamok = function_exists('stream_socket_client'); + } + // Clear errors to avoid confusion + $this->setError(''); + // Make sure we are __not__ connected + if ($this->connected()) { + // Already connected, generate error + $this->setError('Already connected to a server'); + return false; + } + if (empty($port)) { + $port = self::DEFAULT_SMTP_PORT; + } + // Connect to the SMTP server + $this->edebug( + "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true), + self::DEBUG_CONNECTION + ); + $errno = 0; + $errstr = ''; + if ($streamok) { + $socket_context = stream_context_create($options); + set_error_handler(array($this, 'errorHandler')); + $this->smtp_conn = stream_socket_client( + $host . ":" . $port, + $errno, + $errstr, + $timeout, + STREAM_CLIENT_CONNECT, + $socket_context + ); + restore_error_handler(); + } else { + //Fall back to fsockopen which should work in more places, but is missing some features + $this->edebug( + "Connection: stream_socket_client not available, falling back to fsockopen", + self::DEBUG_CONNECTION + ); + set_error_handler(array($this, 'errorHandler')); + $this->smtp_conn = fsockopen( + $host, + $port, + $errno, + $errstr, + $timeout + ); + restore_error_handler(); + } + // Verify we connected properly + if (!is_resource($this->smtp_conn)) { + $this->setError( + 'Failed to connect to server', + $errno, + $errstr + ); + $this->edebug( + 'SMTP ERROR: ' . $this->error['error'] + . ": $errstr ($errno)", + self::DEBUG_CLIENT + ); + return false; + } + $this->edebug('Connection: opened', self::DEBUG_CONNECTION); + // SMTP server can take longer to respond, give longer timeout for first read + // Windows does not have support for this timeout function + if (substr(PHP_OS, 0, 3) != 'WIN') { + $max = ini_get('max_execution_time'); + // Don't bother if unlimited + if ($max != 0 && $timeout > $max) { + @set_time_limit($timeout); + } + stream_set_timeout($this->smtp_conn, $timeout, 0); + } + // Get any announcement + $announce = $this->get_lines(); + $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); + return true; + } + + /** + * Initiate a TLS (encrypted) session. + * @access public + * @return boolean + */ + public function startTLS() + { + if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { + return false; + } + + //Allow the best TLS version(s) we can + $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; + + //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT + //so add them back in manually if we can + if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; + } + + // Begin encrypted connection + if (!stream_socket_enable_crypto( + $this->smtp_conn, + true, + $crypto_method + )) { + return false; + } + return true; + } + + /** + * Perform SMTP authentication. + * Must be run after hello(). + * @see hello() + * @param string $username The user name + * @param string $password The password + * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2) + * @param string $realm The auth realm for NTLM + * @param string $workstation The auth workstation for NTLM + * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) + * @return bool True if successfully authenticated.* @access public + */ + public function authenticate( + $username, + $password, + $authtype = null, + $realm = '', + $workstation = '', + $OAuth = null + ) { + if (!$this->server_caps) { + $this->setError('Authentication is not allowed before HELO/EHLO'); + return false; + } + + if (array_key_exists('EHLO', $this->server_caps)) { + // SMTP extensions are available. Let's try to find a proper authentication method + + if (!array_key_exists('AUTH', $this->server_caps)) { + $this->setError('Authentication is not allowed at this stage'); + // 'at this stage' means that auth may be allowed after the stage changes + // e.g. after STARTTLS + return false; + } + + self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); + self::edebug( + 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), + self::DEBUG_LOWLEVEL + ); + + if (empty($authtype)) { + foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN', 'NTLM', 'XOAUTH2') as $method) { + if (in_array($method, $this->server_caps['AUTH'])) { + $authtype = $method; + break; + } + } + if (empty($authtype)) { + $this->setError('No supported authentication methods found'); + return false; + } + self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); + } + + if (!in_array($authtype, $this->server_caps['AUTH'])) { + $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); + return false; + } + } elseif (empty($authtype)) { + $authtype = 'LOGIN'; + } + switch ($authtype) { + case 'PLAIN': + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { + return false; + } + // Send encoded username and password + if (!$this->sendCommand( + 'User & Password', + base64_encode("\0" . $username . "\0" . $password), + 235 + ) + ) { + return false; + } + break; + case 'LOGIN': + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { + return false; + } + if (!$this->sendCommand("Username", base64_encode($username), 334)) { + return false; + } + if (!$this->sendCommand("Password", base64_encode($password), 235)) { + return false; + } + break; + case 'XOAUTH2': + //If the OAuth Instance is not set. Can be a case when PHPMailer is used + //instead of PHPMailerOAuth + if (is_null($OAuth)) { + return false; + } + $oauth = $OAuth->getOauth64(); + + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { + return false; + } + break; + case 'NTLM': + /* + * ntlm_sasl_client.php + * Bundled with Permission + * + * How to telnet in windows: + * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx + * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication + */ + require_once 'extras/ntlm_sasl_client.php'; + $temp = new stdClass; + $ntlm_client = new ntlm_sasl_client_class; + //Check that functions are available + if (!$ntlm_client->initialize($temp)) { + $this->setError($temp->error); + $this->edebug( + 'You need to enable some modules in your php.ini file: ' + . $this->error['error'], + self::DEBUG_CLIENT + ); + return false; + } + //msg1 + $msg1 = $ntlm_client->typeMsg1($realm, $workstation); //msg1 + + if (!$this->sendCommand( + 'AUTH NTLM', + 'AUTH NTLM ' . base64_encode($msg1), + 334 + ) + ) { + return false; + } + //Though 0 based, there is a white space after the 3 digit number + //msg2 + $challenge = substr($this->last_reply, 3); + $challenge = base64_decode($challenge); + $ntlm_res = $ntlm_client->NTLMResponse( + substr($challenge, 24, 8), + $password + ); + //msg3 + $msg3 = $ntlm_client->typeMsg3( + $ntlm_res, + $username, + $realm, + $workstation + ); + // send encoded username + return $this->sendCommand('Username', base64_encode($msg3), 235); + case 'CRAM-MD5': + // Start authentication + if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { + return false; + } + // Get the challenge + $challenge = base64_decode(substr($this->last_reply, 4)); + + // Build the response + $response = $username . ' ' . $this->hmac($challenge, $password); + + // send encoded credentials + return $this->sendCommand('Username', base64_encode($response), 235); + default: + $this->setError("Authentication method \"$authtype\" is not supported"); + return false; + } + return true; + } + + /** + * Calculate an MD5 HMAC hash. + * Works like hash_hmac('md5', $data, $key) + * in case that function is not available + * @param string $data The data to hash + * @param string $key The key to hash with + * @access protected + * @return string + */ + protected function hmac($data, $key) + { + if (function_exists('hash_hmac')) { + return hash_hmac('md5', $data, $key); + } + + // The following borrowed from + // http://php.net/manual/en/function.mhash.php#27225 + + // RFC 2104 HMAC implementation for php. + // Creates an md5 HMAC. + // Eliminates the need to install mhash to compute a HMAC + // by Lance Rushing + + $bytelen = 64; // byte length for md5 + if (strlen($key) > $bytelen) { + $key = pack('H*', md5($key)); + } + $key = str_pad($key, $bytelen, chr(0x00)); + $ipad = str_pad('', $bytelen, chr(0x36)); + $opad = str_pad('', $bytelen, chr(0x5c)); + $k_ipad = $key ^ $ipad; + $k_opad = $key ^ $opad; + + return md5($k_opad . pack('H*', md5($k_ipad . $data))); + } + + /** + * Check connection state. + * @access public + * @return boolean True if connected. + */ + public function connected() + { + if (is_resource($this->smtp_conn)) { + $sock_status = stream_get_meta_data($this->smtp_conn); + if ($sock_status['eof']) { + // The socket is valid but we are not connected + $this->edebug( + 'SMTP NOTICE: EOF caught while checking if connected', + self::DEBUG_CLIENT + ); + $this->close(); + return false; + } + return true; // everything looks good + } + return false; + } + + /** + * Close the socket and clean up the state of the class. + * Don't use this function without first trying to use QUIT. + * @see quit() + * @access public + * @return void + */ + public function close() + { + $this->setError(''); + $this->server_caps = null; + $this->helo_rply = null; + if (is_resource($this->smtp_conn)) { + // close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = null; //Makes for cleaner serialization + $this->edebug('Connection: closed', self::DEBUG_CONNECTION); + } + } + + /** + * Send an SMTP DATA command. + * Issues a data command and sends the msg_data to the server, + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being separated by and additional . + * Implements rfc 821: DATA + * @param string $msg_data Message data to send + * @access public + * @return boolean + */ + public function data($msg_data) + { + //This will use the standard timelimit + if (!$this->sendCommand('DATA', 'DATA', 354)) { + return false; + } + + /* The server is ready to accept data! + * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) + * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into + * smaller lines to fit within the limit. + * We will also look for lines that start with a '.' and prepend an additional '.'. + * NOTE: this does not count towards line-length limit. + */ + + // Normalize line breaks before exploding + $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); + + /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field + * of the first line (':' separated) does not contain a space then it _should_ be a header and we will + * process all lines before a blank line as headers. + */ + + $field = substr($lines[0], 0, strpos($lines[0], ':')); + $in_headers = false; + if (!empty($field) && strpos($field, ' ') === false) { + $in_headers = true; + } + + foreach ($lines as $line) { + $lines_out = array(); + if ($in_headers and $line == '') { + $in_headers = false; + } + //Break this line up into several smaller lines if it's too long + //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), + while (isset($line[self::MAX_LINE_LENGTH])) { + //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on + //so as to avoid breaking in the middle of a word + $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); + //Deliberately matches both false and 0 + if (!$pos) { + //No nice break found, add a hard break + $pos = self::MAX_LINE_LENGTH - 1; + $lines_out[] = substr($line, 0, $pos); + $line = substr($line, $pos); + } else { + //Break at the found point + $lines_out[] = substr($line, 0, $pos); + //Move along by the amount we dealt with + $line = substr($line, $pos + 1); + } + //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 + if ($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + //Send the lines to the server + foreach ($lines_out as $line_out) { + //RFC2821 section 4.5.2 + if (!empty($line_out) and $line_out[0] == '.') { + $line_out = '.' . $line_out; + } + $this->client_send($line_out . self::CRLF); + } + } + + //Message data has been sent, complete the command + //Increase timelimit for end of DATA command + $savetimelimit = $this->Timelimit; + $this->Timelimit = $this->Timelimit * 2; + $result = $this->sendCommand('DATA END', '.', 250); + //Restore timelimit + $this->Timelimit = $savetimelimit; + return $result; + } + + /** + * Send an SMTP HELO or EHLO command. + * Used to identify the sending server to the receiving server. + * This makes sure that client and server are in a known state. + * Implements RFC 821: HELO + * and RFC 2821 EHLO. + * @param string $host The host name or IP to connect to + * @access public + * @return boolean + */ + public function hello($host = '') + { + //Try extended hello first (RFC 2821) + return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); + } + + /** + * Send an SMTP HELO or EHLO command. + * Low-level implementation used by hello() + * @see hello() + * @param string $hello The HELO string + * @param string $host The hostname to say we are + * @access protected + * @return boolean + */ + protected function sendHello($hello, $host) + { + $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); + $this->helo_rply = $this->last_reply; + if ($noerror) { + $this->parseHelloFields($hello); + } else { + $this->server_caps = null; + } + return $noerror; + } + + /** + * Parse a reply to HELO/EHLO command to discover server extensions. + * In case of HELO, the only parameter that can be discovered is a server name. + * @access protected + * @param string $type - 'HELO' or 'EHLO' + */ + protected function parseHelloFields($type) + { + $this->server_caps = array(); + $lines = explode("\n", $this->helo_rply); + + foreach ($lines as $n => $s) { + //First 4 chars contain response code followed by - or space + $s = trim(substr($s, 4)); + if (empty($s)) { + continue; + } + $fields = explode(' ', $s); + if (!empty($fields)) { + if (!$n) { + $name = $type; + $fields = $fields[0]; + } else { + $name = array_shift($fields); + switch ($name) { + case 'SIZE': + $fields = ($fields ? $fields[0] : 0); + break; + case 'AUTH': + if (!is_array($fields)) { + $fields = array(); + } + break; + default: + $fields = true; + } + } + $this->server_caps[$name] = $fields; + } + } + } + + /** + * Send an SMTP MAIL command. + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more recipient + * commands may be called followed by a data command. + * Implements rfc 821: MAIL FROM: + * @param string $from Source address of this message + * @access public + * @return boolean + */ + public function mail($from) + { + $useVerp = ($this->do_verp ? ' XVERP' : ''); + return $this->sendCommand( + 'MAIL FROM', + 'MAIL FROM:<' . $from . '>' . $useVerp, + 250 + ); + } + + /** + * Send an SMTP QUIT command. + * Closes the socket if there is no error or the $close_on_error argument is true. + * Implements from rfc 821: QUIT + * @param boolean $close_on_error Should the connection close if an error occurs? + * @access public + * @return boolean + */ + public function quit($close_on_error = true) + { + $noerror = $this->sendCommand('QUIT', 'QUIT', 221); + $err = $this->error; //Save any error + if ($noerror or $close_on_error) { + $this->close(); + $this->error = $err; //Restore any error from the quit command + } + return $noerror; + } + + /** + * Send an SMTP RCPT command. + * Sets the TO argument to $toaddr. + * Returns true if the recipient was accepted false if it was rejected. + * Implements from rfc 821: RCPT TO: + * @param string $address The address the message is being sent to + * @access public + * @return boolean + */ + public function recipient($address) + { + return $this->sendCommand( + 'RCPT TO', + 'RCPT TO:<' . $address . '>', + array(250, 251) + ); + } + + /** + * Send an SMTP RSET command. + * Abort any transaction that is currently in progress. + * Implements rfc 821: RSET + * @access public + * @return boolean True on success. + */ + public function reset() + { + return $this->sendCommand('RSET', 'RSET', 250); + } + + /** + * Send a command to an SMTP server and check its return code. + * @param string $command The command name - not sent to the server + * @param string $commandstring The actual command to send + * @param integer|array $expect One or more expected integer success codes + * @access protected + * @return boolean True on success. + */ + protected function sendCommand($command, $commandstring, $expect) + { + if (!$this->connected()) { + $this->setError("Called $command without being connected"); + return false; + } + //Reject line breaks in all commands + if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { + $this->setError("Command '$command' contained line breaks"); + return false; + } + $this->client_send($commandstring . self::CRLF); + + $this->last_reply = $this->get_lines(); + // Fetch SMTP code and possible error code explanation + $matches = array(); + if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) { + $code = $matches[1]; + $code_ex = (count($matches) > 2 ? $matches[2] : null); + // Cut off error code from each response line + $detail = preg_replace( + "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m", + '', + $this->last_reply + ); + } else { + // Fall back to simple parsing if regex fails + $code = substr($this->last_reply, 0, 3); + $code_ex = null; + $detail = substr($this->last_reply, 4); + } + + $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); + + if (!in_array($code, (array)$expect)) { + $this->setError( + "$command command failed", + $detail, + $code, + $code_ex + ); + $this->edebug( + 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, + self::DEBUG_CLIENT + ); + return false; + } + + $this->setError(''); + return true; + } + + /** + * Send an SMTP SAML command. + * Starts a mail transaction from the email address specified in $from. + * Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more recipient + * commands may be called followed by a data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * Implements rfc 821: SAML FROM: + * @param string $from The address the message is from + * @access public + * @return boolean + */ + public function sendAndMail($from) + { + return $this->sendCommand('SAML', "SAML FROM:$from", 250); + } + + /** + * Send an SMTP VRFY command. + * @param string $name The name to verify + * @access public + * @return boolean + */ + public function verify($name) + { + return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); + } + + /** + * Send an SMTP NOOP command. + * Used to keep keep-alives alive, doesn't actually do anything + * @access public + * @return boolean + */ + public function noop() + { + return $this->sendCommand('NOOP', 'NOOP', 250); + } + + /** + * Send an SMTP TURN command. + * This is an optional command for SMTP that this class does not support. + * This method is here to make the RFC821 Definition complete for this class + * and _may_ be implemented in future + * Implements from rfc 821: TURN + * @access public + * @return boolean + */ + public function turn() + { + $this->setError('The SMTP TURN command is not implemented'); + $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); + return false; + } + + /** + * Send raw data to the server. + * @param string $data The data to send + * @access public + * @return integer|boolean The number of bytes sent to the server or false on error + */ + public function client_send($data) + { + $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); + return fwrite($this->smtp_conn, $data); + } + + /** + * Get the latest error. + * @access public + * @return array + */ + public function getError() + { + return $this->error; + } + + /** + * Get SMTP extensions available on the server + * @access public + * @return array|null + */ + public function getServerExtList() + { + return $this->server_caps; + } + + /** + * A multipurpose method + * The method works in three ways, dependent on argument value and current state + * 1. HELO/EHLO was not sent - returns null and set up $this->error + * 2. HELO was sent + * $name = 'HELO': returns server name + * $name = 'EHLO': returns boolean false + * $name = any string: returns null and set up $this->error + * 3. EHLO was sent + * $name = 'HELO'|'EHLO': returns server name + * $name = any string: if extension $name exists, returns boolean True + * or its options. Otherwise returns boolean False + * In other words, one can use this method to detect 3 conditions: + * - null returned: handshake was not or we don't know about ext (refer to $this->error) + * - false returned: the requested feature exactly not exists + * - positive value returned: the requested feature exists + * @param string $name Name of SMTP extension or 'HELO'|'EHLO' + * @return mixed + */ + public function getServerExt($name) + { + if (!$this->server_caps) { + $this->setError('No HELO/EHLO was sent'); + return null; + } + + // the tight logic knot ;) + if (!array_key_exists($name, $this->server_caps)) { + if ($name == 'HELO') { + return $this->server_caps['EHLO']; + } + if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) { + return false; + } + $this->setError('HELO handshake was used. Client knows nothing about server extensions'); + return null; + } + + return $this->server_caps[$name]; + } + + /** + * Get the last reply from the server. + * @access public + * @return string + */ + public function getLastReply() + { + return $this->last_reply; + } + + /** + * Read the SMTP server's response. + * Either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + * @access protected + * @return string + */ + protected function get_lines() + { + // If the connection is bad, give up straight away + if (!is_resource($this->smtp_conn)) { + return ''; + } + $data = ''; + $endtime = 0; + stream_set_timeout($this->smtp_conn, $this->Timeout); + if ($this->Timelimit > 0) { + $endtime = time() + $this->Timelimit; + } + while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { + $str = @fgets($this->smtp_conn, 515); + $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); + $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); + $data .= $str; + // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen + if ((isset($str[3]) and $str[3] == ' ')) { + break; + } + // Timed-out? Log and break + $info = stream_get_meta_data($this->smtp_conn); + if ($info['timed_out']) { + $this->edebug( + 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', + self::DEBUG_LOWLEVEL + ); + break; + } + // Now check if reads took too long + if ($endtime and time() > $endtime) { + $this->edebug( + 'SMTP -> get_lines(): timelimit reached ('. + $this->Timelimit . ' sec)', + self::DEBUG_LOWLEVEL + ); + break; + } + } + return $data; + } + + /** + * Enable or disable VERP address generation. + * @param boolean $enabled + */ + public function setVerp($enabled = false) + { + $this->do_verp = $enabled; + } + + /** + * Get VERP address generation mode. + * @return boolean + */ + public function getVerp() + { + return $this->do_verp; + } + + /** + * Set error messages and codes. + * @param string $message The error message + * @param string $detail Further detail on the error + * @param string $smtp_code An associated SMTP error code + * @param string $smtp_code_ex Extended SMTP code + */ + protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') + { + $this->error = array( + 'error' => $message, + 'detail' => $detail, + 'smtp_code' => $smtp_code, + 'smtp_code_ex' => $smtp_code_ex + ); + } + + /** + * Set debug output method. + * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. + */ + public function setDebugOutput($method = 'echo') + { + $this->Debugoutput = $method; + } + + /** + * Get debug output method. + * @return string + */ + public function getDebugOutput() + { + return $this->Debugoutput; + } + + /** + * Set debug output level. + * @param integer $level + */ + public function setDebugLevel($level = 0) + { + $this->do_debug = $level; + } + + /** + * Get debug output level. + * @return integer + */ + public function getDebugLevel() + { + return $this->do_debug; + } + + /** + * Set SMTP timeout. + * @param integer $timeout + */ + public function setTimeout($timeout = 0) + { + $this->Timeout = $timeout; + } + + /** + * Get SMTP timeout. + * @return integer + */ + public function getTimeout() + { + return $this->Timeout; + } + + /** + * Reports an error number and string. + * @param integer $errno The error number returned by PHP. + * @param string $errmsg The error message returned by PHP. + */ + protected function errorHandler($errno, $errmsg) + { + $notice = 'Connection: Failed to connect to server.'; + $this->setError( + $notice, + $errno, + $errmsg + ); + $this->edebug( + $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, + self::DEBUG_CONNECTION + ); + } +} diff --git a/www/core/func/libs/mail/composer.json b/www/core/func/libs/mail/composer.json new file mode 100644 index 0000000..1112fb9 --- /dev/null +++ b/www/core/func/libs/mail/composer.json @@ -0,0 +1,44 @@ +{ + "name": "phpmailer/phpmailer", + "type": "library", + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "require": { + "php": ">=5.0.0" + }, + "require-dev": { + "phpdocumentor/phpdocumentor": "*", + "phpunit/phpunit": "4.7.*" + }, + "suggest": { + "league/oauth2-google": "Needed for Google XOAUTH2 authentication" + }, + "autoload": { + "classmap": [ + "class.phpmailer.php", + "class.phpmaileroauth.php", + "class.phpmaileroauthgoogle.php", + "class.smtp.php", + "class.pop3.php", + "extras/EasyPeasyICS.php", + "extras/ntlm_sasl_client.php" + ] + }, + "license": "LGPL-2.1" +} diff --git a/www/core/func/libs/mail/composer.lock b/www/core/func/libs/mail/composer.lock new file mode 100644 index 0000000..9edbf55 --- /dev/null +++ b/www/core/func/libs/mail/composer.lock @@ -0,0 +1,3576 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "ca5abc72444d9608a35c39f9064c139b", + "content-hash": "8b66ed71ae9ca8cd0258c814615d624f", + "packages": [], + "packages-dev": [ + { + "name": "cilex/cilex", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Cilex/Cilex.git", + "reference": "7acd965a609a56d0345e8b6071c261fbdb926cb5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Cilex/Cilex/zipball/7acd965a609a56d0345e8b6071c261fbdb926cb5", + "reference": "7acd965a609a56d0345e8b6071c261fbdb926cb5", + "shasum": "" + }, + "require": { + "cilex/console-service-provider": "1.*", + "php": ">=5.3.3", + "pimple/pimple": "~1.0", + "symfony/finder": "~2.1", + "symfony/process": "~2.1" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*", + "symfony/validator": "~2.1" + }, + "suggest": { + "monolog/monolog": ">=1.0.0", + "symfony/validator": ">=1.0.0", + "symfony/yaml": ">=1.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Cilex": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "description": "The PHP micro-framework for Command line tools based on the Symfony2 Components", + "homepage": "http://cilex.github.com", + "keywords": [ + "cli", + "microframework" + ], + "time": "2014-03-29 14:03:13" + }, + { + "name": "cilex/console-service-provider", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/Cilex/console-service-provider.git", + "reference": "25ee3d1875243d38e1a3448ff94bdf944f70d24e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Cilex/console-service-provider/zipball/25ee3d1875243d38e1a3448ff94bdf944f70d24e", + "reference": "25ee3d1875243d38e1a3448ff94bdf944f70d24e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "pimple/pimple": "1.*@dev", + "symfony/console": "~2.1" + }, + "require-dev": { + "cilex/cilex": "1.*@dev", + "silex/silex": "1.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Cilex\\Provider\\Console": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "description": "Console Service Provider", + "keywords": [ + "cilex", + "console", + "pimple", + "service-provider", + "silex" + ], + "time": "2012-12-19 10:50:58" + }, + { + "name": "container-interop/container-interop", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/container-interop/container-interop.git", + "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", + "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Interop\\Container\\": "src/Interop/Container/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", + "time": "2014-12-30 15:22:37" + }, + { + "name": "doctrine/annotations", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Annotations\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2015-08-31 12:32:49" + }, + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09 13:34:57" + }, + { + "name": "erusev/parsedown", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/erusev/parsedown.git", + "reference": "3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7", + "reference": "3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Parsedown": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", + "keywords": [ + "markdown", + "parser" + ], + "time": "2015-10-04 16:44:32" + }, + { + "name": "herrera-io/json", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/kherge-abandoned/php-json.git", + "reference": "60c696c9370a1e5136816ca557c17f82a6fa83f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kherge-abandoned/php-json/zipball/60c696c9370a1e5136816ca557c17f82a6fa83f1", + "reference": "60c696c9370a1e5136816ca557c17f82a6fa83f1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "justinrainbow/json-schema": ">=1.0,<2.0-dev", + "php": ">=5.3.3", + "seld/jsonlint": ">=1.0,<2.0-dev" + }, + "require-dev": { + "herrera-io/phpunit-test-case": "1.*", + "mikey179/vfsstream": "1.1.0", + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "files": [ + "src/lib/json_version.php" + ], + "psr-0": { + "Herrera\\Json": "src/lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kevin Herrera", + "email": "kevin@herrera.io", + "homepage": "http://kevin.herrera.io/", + "role": "Developer" + } + ], + "description": "A library for simplifying JSON linting and validation.", + "homepage": "http://herrera-io.github.com/php-json", + "keywords": [ + "json", + "lint", + "schema", + "validate" + ], + "time": "2013-10-30 16:51:34" + }, + { + "name": "herrera-io/phar-update", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/kherge-abandoned/php-phar-update.git", + "reference": "00a79e1d5b8cf3c080a2e3becf1ddf7a7fea025b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kherge-abandoned/php-phar-update/zipball/00a79e1d5b8cf3c080a2e3becf1ddf7a7fea025b", + "reference": "00a79e1d5b8cf3c080a2e3becf1ddf7a7fea025b", + "shasum": "" + }, + "require": { + "herrera-io/json": "1.*", + "kherge/version": "1.*", + "php": ">=5.3.3" + }, + "require-dev": { + "herrera-io/phpunit-test-case": "1.*", + "mikey179/vfsstream": "1.1.0", + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "files": [ + "src/lib/constants.php" + ], + "psr-0": { + "Herrera\\Phar\\Update": "src/lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kevin Herrera", + "email": "kevin@herrera.io", + "homepage": "http://kevin.herrera.io/", + "role": "Developer" + } + ], + "description": "A library for self-updating Phars.", + "homepage": "http://herrera-io.github.com/php-phar-update", + "keywords": [ + "phar", + "update" + ], + "time": "2013-10-30 17:23:01" + }, + { + "name": "jms/metadata", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/metadata.git", + "reference": "22b72455559a25777cfd28c4ffda81ff7639f353" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/22b72455559a25777cfd28c4ffda81ff7639f353", + "reference": "22b72455559a25777cfd28c4ffda81ff7639f353", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "doctrine/cache": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Metadata\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache" + ], + "authors": [ + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Class/method/property metadata management in PHP", + "keywords": [ + "annotations", + "metadata", + "xml", + "yaml" + ], + "time": "2014-07-12 07:13:19" + }, + { + "name": "jms/parser-lib", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/parser-lib.git", + "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/c509473bc1b4866415627af0e1c6cc8ac97fa51d", + "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d", + "shasum": "" + }, + "require": { + "phpoption/phpoption": ">=0.9,<2.0-dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "JMS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "description": "A library for easily creating recursive-descent parsers.", + "time": "2012-11-18 18:08:43" + }, + { + "name": "jms/serializer", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/serializer.git", + "reference": "fe13a1f993ea3456e195b7820692f2eb2b6bbb48" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/fe13a1f993ea3456e195b7820692f2eb2b6bbb48", + "reference": "fe13a1f993ea3456e195b7820692f2eb2b6bbb48", + "shasum": "" + }, + "require": { + "doctrine/annotations": "1.*", + "doctrine/instantiator": "~1.0.3", + "jms/metadata": "~1.1", + "jms/parser-lib": "1.*", + "php": ">=5.4.0", + "phpcollection/phpcollection": "~0.1" + }, + "conflict": { + "twig/twig": "<1.12" + }, + "require-dev": { + "doctrine/orm": "~2.1", + "doctrine/phpcr-odm": "~1.0.1", + "jackalope/jackalope-doctrine-dbal": "1.0.*", + "phpunit/phpunit": "~4.0", + "propel/propel1": "~1.7", + "symfony/filesystem": "2.*", + "symfony/form": "~2.1", + "symfony/translation": "~2.0", + "symfony/validator": "~2.0", + "symfony/yaml": "2.*", + "twig/twig": "~1.12|~2.0" + }, + "suggest": { + "symfony/yaml": "Required if you'd like to serialize data to YAML format." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-0": { + "JMS\\Serializer": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", + "homepage": "http://jmsyst.com/libs/serializer", + "keywords": [ + "deserialization", + "jaxb", + "json", + "serialization", + "xml" + ], + "time": "2015-10-27 09:24:41" + }, + { + "name": "justinrainbow/json-schema", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/cc84765fb7317f6b07bd8ac78364747f95b86341", + "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341", + "shasum": "" + }, + "require": { + "php": ">=5.3.29" + }, + "require-dev": { + "json-schema/json-schema-test-suite": "1.1.0", + "phpdocumentor/phpdocumentor": "~2", + "phpunit/phpunit": "~3.7" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "time": "2016-01-25 15:43:01" + }, + { + "name": "kherge/version", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/kherge-abandoned/Version.git", + "reference": "f07cf83f8ce533be8f93d2893d96d674bbeb7e30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kherge-abandoned/Version/zipball/f07cf83f8ce533be8f93d2893d96d674bbeb7e30", + "reference": "f07cf83f8ce533be8f93d2893d96d674bbeb7e30", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "KevinGH\\Version": "src/lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kevin Herrera", + "email": "me@kevingh.com", + "homepage": "http://www.kevingh.com/" + } + ], + "description": "A parsing and comparison library for semantic versioning.", + "homepage": "http://github.com/kherge/Version", + "time": "2012-08-16 17:13:03" + }, + { + "name": "monolog/monolog", + "version": "1.19.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5f56ed5212dc509c8dc8caeba2715732abb32dbf", + "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "raven/raven": "^0.13", + "ruflin/elastica": ">=0.90 <3.0", + "swiftmailer/swiftmailer": "~5.3" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "raven/raven": "Allow sending log messages to a Sentry server", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2016-04-12 18:29:35" + }, + { + "name": "nikic/php-parser", + "version": "v1.4.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", + "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "files": [ + "lib/bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2015-09-19 14:15:08" + }, + { + "name": "phpcollection/phpcollection", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-collection.git", + "reference": "b8bf55a0a929ca43b01232b36719f176f86c7e83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/b8bf55a0a929ca43b01232b36719f176f86c7e83", + "reference": "b8bf55a0a929ca43b01232b36719f176f86c7e83", + "shasum": "" + }, + "require": { + "phpoption/phpoption": "1.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.3-dev" + } + }, + "autoload": { + "psr-0": { + "PhpCollection": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "General-Purpose Collection Library for PHP", + "keywords": [ + "collection", + "list", + "map", + "sequence", + "set" + ], + "time": "2014-03-11 13:46:42" + }, + { + "name": "phpdocumentor/fileset", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/Fileset.git", + "reference": "bfa78d8fa9763dfce6d0e5d3730c1d8ab25d34b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/Fileset/zipball/bfa78d8fa9763dfce6d0e5d3730c1d8ab25d34b0", + "reference": "bfa78d8fa9763dfce6d0e5d3730c1d8ab25d34b0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/finder": "~2.1" + }, + "require-dev": { + "phpunit/phpunit": "~3.7" + }, + "type": "library", + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/", + "tests/unit/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Fileset component for collecting a set of files given directories and file paths", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "files", + "fileset", + "phpdoc" + ], + "time": "2013-08-06 21:07:42" + }, + { + "name": "phpdocumentor/graphviz", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/GraphViz.git", + "reference": "a906a90a9f230535f25ea31caf81b2323956283f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/GraphViz/zipball/a906a90a9f230535f25ea31caf81b2323956283f", + "reference": "a906a90a9f230535f25ea31caf81b2323956283f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/", + "tests/unit" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2016-02-02 13:00:08" + }, + { + "name": "phpdocumentor/phpdocumentor", + "version": "v2.9.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/phpDocumentor2.git", + "reference": "be607da0eef9b9249c43c5b4820d25d631c73667" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/phpDocumentor2/zipball/be607da0eef9b9249c43c5b4820d25d631c73667", + "reference": "be607da0eef9b9249c43c5b4820d25d631c73667", + "shasum": "" + }, + "require": { + "cilex/cilex": "~1.0", + "erusev/parsedown": "~1.0", + "herrera-io/phar-update": "1.0.3", + "jms/serializer": ">=0.12", + "monolog/monolog": "~1.6", + "php": ">=5.3.3", + "phpdocumentor/fileset": "~1.0", + "phpdocumentor/graphviz": "~1.0", + "phpdocumentor/reflection": "^3.0", + "phpdocumentor/reflection-docblock": "~2.0", + "symfony/config": "~2.3", + "symfony/console": "~2.3", + "symfony/event-dispatcher": "~2.1", + "symfony/process": "~2.0", + "symfony/stopwatch": "~2.3", + "symfony/validator": "~2.2", + "twig/twig": "~1.3", + "zendframework/zend-cache": "~2.1", + "zendframework/zend-config": "~2.1", + "zendframework/zend-filter": "~2.1", + "zendframework/zend-i18n": "~2.1", + "zendframework/zend-serializer": "~2.1", + "zendframework/zend-servicemanager": "~2.1", + "zendframework/zend-stdlib": "~2.1", + "zetacomponents/document": ">=1.3.1" + }, + "require-dev": { + "behat/behat": "~3.0", + "mikey179/vfsstream": "~1.2", + "mockery/mockery": "~0.9@dev", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.4", + "symfony/expression-language": "~2.4" + }, + "suggest": { + "ext-twig": "Enabling the twig extension improves the generation of twig based templates.", + "ext-xslcache": "Enabling the XSLCache extension improves the generation of xml based templates." + }, + "bin": [ + "bin/phpdoc.php", + "bin/phpdoc" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "2.9-dev" + } + }, + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/", + "tests/unit/" + ], + "Cilex\\Provider": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Documentation Generator for PHP", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "api", + "application", + "dga", + "documentation", + "phpdoc" + ], + "time": "2016-05-22 09:50:56" + }, + { + "name": "phpdocumentor/reflection", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/Reflection.git", + "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", + "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^1.0", + "php": ">=5.3.3", + "phpdocumentor/reflection-docblock": "~2.0", + "psr/log": "~1.0" + }, + "require-dev": { + "behat/behat": "~2.4", + "mockery/mockery": "~0.8", + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/", + "tests/unit/", + "tests/mocks/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Reflection library to do Static Analysis for PHP Projects", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2016-05-21 08:42:32" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2015-02-03 12:10:50" + }, + { + "name": "phpoption/phpoption", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", + "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-0": { + "PhpOption\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "time": "2015-07-25 16:39:46" + }, + { + "name": "phpspec/prophecy", + "version": "v1.6.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "58a8137754bc24b25740d4281399a4a3596058e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", + "reference": "58a8137754bc24b25740d4281399a4a3596058e0", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1", + "sebastian/recursion-context": "^1.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2016-06-07 08:13:47" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "^1.3.2", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2015-10-06 15:47:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2015-06-21 13:08:43" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21 13:50:34" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4|~5" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2016-05-12 18:03:57" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2015-09-15 10:49:45" + }, + { + "name": "phpunit/phpunit", + "version": "4.7.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "9b97f9d807b862c2de2a36e86690000801c85724" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9b97f9d807b862c2de2a36e86690000801c85724", + "reference": "9b97f9d807b862c2de2a36e86690000801c85724", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpspec/prophecy": "~1.3,>=1.3.1", + "phpunit/php-code-coverage": "~2.1", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": ">=1.0.6", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.1", + "sebastian/diff": "~1.2", + "sebastian/environment": "~1.2", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.1|~3.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.7.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2015-07-13 11:28:34" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.3.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2015-10-02 06:51:40" + }, + { + "name": "pimple/pimple", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", + "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ], + "time": "2013-11-22 08:30:29" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" + }, + { + "name": "sebastian/comparator", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2015-07-26 15:48:44" + }, + { + "name": "sebastian/diff", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2015-12-08 07:14:41" + }, + { + "name": "sebastian/environment", + "version": "1.3.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716", + "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2016-05-17 03:18:57" + }, + { + "name": "sebastian/exporter", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2016-06-17 09:04:28" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12 03:26:01" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2015-11-11 19:50:13" + }, + { + "name": "sebastian/version", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2015-06-21 13:59:46" + }, + { + "name": "seld/jsonlint", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "66834d3e3566bb5798db7294619388786ae99394" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/66834d3e3566bb5798db7294619388786ae99394", + "reference": "66834d3e3566bb5798db7294619388786ae99394", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "time": "2015-11-21 02:21:41" + }, + { + "name": "symfony/config", + "version": "v2.8.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "a2edd59c2163c65747fc3f35d132b5a39266bd05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/a2edd59c2163c65747fc3f35d132b5a39266bd05", + "reference": "a2edd59c2163c65747fc3f35d132b5a39266bd05", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/filesystem": "~2.3|~3.0.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "https://symfony.com", + "time": "2016-06-06 11:11:27" + }, + { + "name": "symfony/console", + "version": "v2.8.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", + "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/process": "~2.1|~3.0.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2016-06-06 15:06:25" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.8.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "2a6b8713f8bdb582058cfda463527f195b066110" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2a6b8713f8bdb582058cfda463527f195b066110", + "reference": "2a6b8713f8bdb582058cfda463527f195b066110", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.0,>=2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.6|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2016-06-06 11:11:27" + }, + { + "name": "symfony/filesystem", + "version": "v3.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "74fec3511b62cb934b64bce1d96f06fffa4beafd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/74fec3511b62cb934b64bce1d96f06fffa4beafd", + "reference": "74fec3511b62cb934b64bce1d96f06fffa4beafd", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2016-04-12 18:09:53" + }, + { + "name": "symfony/finder", + "version": "v2.8.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "3ec095fab1800222732ca522a95dce8fa124007b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/3ec095fab1800222732ca522a95dce8fa124007b", + "reference": "3ec095fab1800222732ca522a95dce8fa124007b", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2016-06-06 11:11:27" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "dff51f72b0706335131b00a7f49606168c582594" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", + "reference": "dff51f72b0706335131b00a7f49606168c582594", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/process", + "version": "v2.8.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "115347d00c342198cdc52a7bd8bc15b5ab43500c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/115347d00c342198cdc52a7bd8bc15b5ab43500c", + "reference": "115347d00c342198cdc52a7bd8bc15b5ab43500c", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2016-06-06 11:11:27" + }, + { + "name": "symfony/stopwatch", + "version": "v2.8.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "5e628055488bcc42dbace3af65be435d094e37e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5e628055488bcc42dbace3af65be435d094e37e4", + "reference": "5e628055488bcc42dbace3af65be435d094e37e4", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "https://symfony.com", + "time": "2016-06-06 11:11:27" + }, + { + "name": "symfony/translation", + "version": "v3.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "2b0aacaa613c0ec1ad8046f972d8abdcb19c1db7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/2b0aacaa613c0ec1ad8046f972d8abdcb19c1db7", + "reference": "2b0aacaa613c0ec1ad8046f972d8abdcb19c1db7", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/config": "<2.8" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0", + "symfony/intl": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0" + }, + "suggest": { + "psr/log": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Translation Component", + "homepage": "https://symfony.com", + "time": "2016-06-06 11:33:26" + }, + { + "name": "symfony/validator", + "version": "v2.8.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/validator.git", + "reference": "4c8f9fd8e2150dbc4745ef13378e690588365df0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/validator/zipball/4c8f9fd8e2150dbc4745ef13378e690588365df0", + "reference": "4c8f9fd8e2150dbc4745ef13378e690588365df0", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation": "~2.4|~3.0.0" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "egulias/email-validator": "~1.2,>=1.2.1", + "symfony/config": "~2.2|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0", + "symfony/http-foundation": "~2.1|~3.0.0", + "symfony/intl": "~2.7.4|~2.8|~3.0.0", + "symfony/property-access": "~2.3|~3.0.0", + "symfony/yaml": "~2.0,>=2.0.5|~3.0.0" + }, + "suggest": { + "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", + "doctrine/cache": "For using the default cached annotation reader and metadata cache.", + "egulias/email-validator": "Strict (RFC compliant) email validation", + "symfony/config": "", + "symfony/expression-language": "For using the 2.4 Expression validator", + "symfony/http-foundation": "", + "symfony/intl": "", + "symfony/property-access": "For using the 2.4 Validator API", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Validator\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Validator Component", + "homepage": "https://symfony.com", + "time": "2016-04-14 08:48:44" + }, + { + "name": "symfony/yaml", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "c5a7e7fc273c758b92b85dcb9c46149ccda89623" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c5a7e7fc273c758b92b85dcb9c46149ccda89623", + "reference": "c5a7e7fc273c758b92b85dcb9c46149ccda89623", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2016-06-14 11:18:07" + }, + { + "name": "twig/twig", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "3566d311a92aae4deec6e48682dc5a4528c4a512" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/3566d311a92aae4deec6e48682dc5a4528c4a512", + "reference": "3566d311a92aae4deec6e48682dc5a4528c4a512", + "shasum": "" + }, + "require": { + "php": ">=5.2.7" + }, + "require-dev": { + "symfony/debug": "~2.7", + "symfony/phpunit-bridge": "~2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.24-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + }, + { + "name": "Twig Team", + "homepage": "http://twig.sensiolabs.org/contributors", + "role": "Contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ], + "time": "2016-05-30 09:11:59" + }, + { + "name": "zendframework/zend-cache", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-cache.git", + "reference": "2c68def8f96ce842d2f2a9a69e2f3508c2f5312d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/2c68def8f96ce842d2f2a9a69e2f3508c2f5312d", + "reference": "2c68def8f96ce842d2f2a9a69e2f3508c2f5312d", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpbench/phpbench": "^0.10.0", + "phpunit/phpunit": "^4.5", + "zendframework/zend-serializer": "^2.6", + "zendframework/zend-session": "^2.6.2" + }, + "suggest": { + "ext-apc": "APC or compatible extension, to use the APC storage adapter", + "ext-apcu": "APCU >= 5.1.0, to use the APCu storage adapter", + "ext-dba": "DBA, to use the DBA storage adapter", + "ext-memcache": "Memcache >= 2.0.0 to use the Memcache storage adapter", + "ext-memcached": "Memcached >= 1.0.0 to use the Memcached storage adapter", + "ext-mongo": "Mongo, to use MongoDb storage adapter", + "ext-redis": "Redis, to use Redis storage adapter", + "ext-wincache": "WinCache, to use the WinCache storage adapter", + "ext-xcache": "XCache, to use the XCache storage adapter", + "mongofill/mongofill": "Alternative to ext-mongo - a pure PHP implementation designed as a drop in replacement", + "zendframework/zend-serializer": "Zend\\Serializer component", + "zendframework/zend-session": "Zend\\Session component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" + }, + "zf": { + "component": "Zend\\Cache", + "config-provider": "Zend\\Cache\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Zend\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a generic way to cache any data", + "homepage": "https://github.com/zendframework/zend-cache", + "keywords": [ + "cache", + "zf2" + ], + "time": "2016-05-12 21:47:55" + }, + { + "name": "zendframework/zend-config", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-config.git", + "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "zendframework/zend-filter": "^2.6", + "zendframework/zend-i18n": "^2.5", + "zendframework/zend-json": "^2.6.1", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + }, + "suggest": { + "zendframework/zend-filter": "Zend\\Filter component", + "zendframework/zend-i18n": "Zend\\I18n component", + "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", + "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Config\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a nested object property based user interface for accessing this configuration data within application code", + "homepage": "https://github.com/zendframework/zend-config", + "keywords": [ + "config", + "zf2" + ], + "time": "2016-02-04 23:01:10" + }, + { + "name": "zendframework/zend-eventmanager", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-eventmanager.git", + "reference": "5c80bdee0e952be112dcec0968bad770082c3a6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/5c80bdee0e952be112dcec0968bad770082c3a6e", + "reference": "5c80bdee0e952be112dcec0968bad770082c3a6e", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0" + }, + "require-dev": { + "athletic/athletic": "^0.1", + "container-interop/container-interop": "^1.1.0", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "^2.0", + "zendframework/zend-stdlib": "^2.7.3 || ^3.0" + }, + "suggest": { + "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", + "zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev", + "dev-develop": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\EventManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Trigger and listen to events within a PHP application", + "homepage": "https://github.com/zendframework/zend-eventmanager", + "keywords": [ + "event", + "eventmanager", + "events", + "zf2" + ], + "time": "2016-02-18 20:53:00" + }, + { + "name": "zendframework/zend-filter", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-filter.git", + "reference": "84c50246428efb0a1e52868e162dab3e149d5b80" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/84c50246428efb0a1e52868e162dab3e149d5b80", + "reference": "84c50246428efb0a1e52868e162dab3e149d5b80", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "pear/archive_tar": "^1.4", + "phpunit/phpunit": "~4.0", + "zendframework/zend-crypt": "^2.6", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", + "zendframework/zend-uri": "^2.5" + }, + "suggest": { + "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", + "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", + "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" + }, + "zf": { + "component": "Zend\\Filter", + "config-provider": "Zend\\Filter\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Zend\\Filter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a set of commonly needed data filters", + "homepage": "https://github.com/zendframework/zend-filter", + "keywords": [ + "filter", + "zf2" + ], + "time": "2016-04-18 18:32:43" + }, + { + "name": "zendframework/zend-hydrator", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-hydrator.git", + "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", + "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "^2.0@dev", + "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", + "zendframework/zend-filter": "^2.6", + "zendframework/zend-inputfilter": "^2.6", + "zendframework/zend-serializer": "^2.6.1", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + }, + "suggest": { + "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", + "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", + "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-release-1.0": "1.0-dev", + "dev-release-1.1": "1.1-dev", + "dev-master": "2.0-dev", + "dev-develop": "2.1-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Hydrator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-hydrator", + "keywords": [ + "hydrator", + "zf2" + ], + "time": "2016-02-18 22:38:26" + }, + { + "name": "zendframework/zend-i18n", + "version": "2.7.3", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-i18n.git", + "reference": "b2db0d8246a865c659f93199f90f5fc2cd2f3cd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/b2db0d8246a865c659f93199f90f5fc2cd2f3cd8", + "reference": "b2db0d8246a865c659f93199f90f5fc2cd2f3cd8", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "zendframework/zend-cache": "^2.6.1", + "zendframework/zend-config": "^2.6", + "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", + "zendframework/zend-filter": "^2.6.1", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", + "zendframework/zend-validator": "^2.6", + "zendframework/zend-view": "^2.6.3" + }, + "suggest": { + "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", + "zendframework/zend-cache": "Zend\\Cache component", + "zendframework/zend-config": "Zend\\Config component", + "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", + "zendframework/zend-filter": "You should install this package to use the provided filters", + "zendframework/zend-i18n-resources": "Translation resources", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component", + "zendframework/zend-validator": "You should install this package to use the provided validators", + "zendframework/zend-view": "You should install this package to use the provided view helpers" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" + }, + "zf": { + "component": "Zend\\I18n", + "config-provider": "Zend\\I18n\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Zend\\I18n\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-i18n", + "keywords": [ + "i18n", + "zf2" + ], + "time": "2016-06-07 21:08:30" + }, + { + "name": "zendframework/zend-json", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-json.git", + "reference": "f42a1588e75c2a3e338cd94c37906231e616daab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-json/zipball/f42a1588e75c2a3e338cd94c37906231e616daab", + "reference": "f42a1588e75c2a3e338cd94c37906231e616daab", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "^2.3", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "suggest": { + "zendframework/zend-json-server": "For implementing JSON-RPC servers", + "zendframework/zend-xml2json": "For converting XML documents to JSON" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev", + "dev-develop": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Json\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", + "homepage": "https://github.com/zendframework/zend-json", + "keywords": [ + "json", + "zf2" + ], + "time": "2016-04-01 02:34:00" + }, + { + "name": "zendframework/zend-serializer", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-serializer.git", + "reference": "ff74ea020f5f90866eb28365327e9bc765a61a6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/ff74ea020f5f90866eb28365327e9bc765a61a6e", + "reference": "ff74ea020f5f90866eb28365327e9bc765a61a6e", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0", + "zendframework/zend-json": "^2.5 || ^3.0", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5", + "squizlabs/php_codesniffer": "^2.3.1", + "zendframework/zend-math": "^2.6", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + }, + "suggest": { + "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev", + "dev-develop": "2.9-dev" + }, + "zf": { + "component": "Zend\\Serializer", + "config-provider": "Zend\\Serializer\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Zend\\Serializer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides an adapter based interface to simply generate storable representation of PHP types by different facilities, and recover", + "homepage": "https://github.com/zendframework/zend-serializer", + "keywords": [ + "serializer", + "zf2" + ], + "time": "2016-06-21 17:01:55" + }, + { + "name": "zendframework/zend-servicemanager", + "version": "2.7.6", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-servicemanager.git", + "reference": "a6db4d13b9141fccce5dcb553df0295d6ad7d477" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/a6db4d13b9141fccce5dcb553df0295d6ad7d477", + "reference": "a6db4d13b9141fccce5dcb553df0295d6ad7d477", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "~1.0", + "php": "^5.5 || ^7.0" + }, + "require-dev": { + "athletic/athletic": "dev-master", + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "zendframework/zend-di": "~2.5", + "zendframework/zend-mvc": "~2.5" + }, + "suggest": { + "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", + "zendframework/zend-di": "Zend\\Di component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\ServiceManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-servicemanager", + "keywords": [ + "servicemanager", + "zf2" + ], + "time": "2016-04-27 19:07:40" + }, + { + "name": "zendframework/zend-stdlib", + "version": "2.7.7", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-stdlib.git", + "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", + "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "zendframework/zend-hydrator": "~1.1" + }, + "require-dev": { + "athletic/athletic": "~0.1", + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "zendframework/zend-config": "~2.5", + "zendframework/zend-eventmanager": "~2.5", + "zendframework/zend-filter": "~2.5", + "zendframework/zend-inputfilter": "~2.5", + "zendframework/zend-serializer": "~2.5", + "zendframework/zend-servicemanager": "~2.5" + }, + "suggest": { + "zendframework/zend-eventmanager": "To support aggregate hydrator usage", + "zendframework/zend-filter": "To support naming strategy hydrator usage", + "zendframework/zend-serializer": "Zend\\Serializer component", + "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-release-2.7": "2.7-dev", + "dev-master": "3.0-dev", + "dev-develop": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-stdlib", + "keywords": [ + "stdlib", + "zf2" + ], + "time": "2016-04-12 21:17:31" + }, + { + "name": "zetacomponents/base", + "version": "1.9", + "source": { + "type": "git", + "url": "https://github.com/zetacomponents/Base.git", + "reference": "f20df24e8de3e48b6b69b2503f917e457281e687" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zetacomponents/Base/zipball/f20df24e8de3e48b6b69b2503f917e457281e687", + "reference": "f20df24e8de3e48b6b69b2503f917e457281e687", + "shasum": "" + }, + "require-dev": { + "zetacomponents/unit-test": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "src" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Sergey Alexeev" + }, + { + "name": "Sebastian Bergmann" + }, + { + "name": "Jan Borsodi" + }, + { + "name": "Raymond Bosman" + }, + { + "name": "Frederik Holljen" + }, + { + "name": "Kore Nordmann" + }, + { + "name": "Derick Rethans" + }, + { + "name": "Vadym Savchuk" + }, + { + "name": "Tobias Schlitt" + }, + { + "name": "Alexandru Stanoi" + } + ], + "description": "The Base package provides the basic infrastructure that all packages rely on. Therefore every component relies on this package.", + "homepage": "https://github.com/zetacomponents", + "time": "2014-09-19 03:28:34" + }, + { + "name": "zetacomponents/document", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/zetacomponents/Document.git", + "reference": "688abfde573cf3fe0730f82538fbd7aa9fc95bc8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zetacomponents/Document/zipball/688abfde573cf3fe0730f82538fbd7aa9fc95bc8", + "reference": "688abfde573cf3fe0730f82538fbd7aa9fc95bc8", + "shasum": "" + }, + "require": { + "zetacomponents/base": "*" + }, + "require-dev": { + "zetacomponents/unit-test": "dev-master" + }, + "type": "library", + "autoload": { + "classmap": [ + "src" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Sebastian Bergmann" + }, + { + "name": "Kore Nordmann" + }, + { + "name": "Derick Rethans" + }, + { + "name": "Tobias Schlitt" + }, + { + "name": "Alexandru Stanoi" + } + ], + "description": "The Document components provides a general conversion framework for different semantic document markup languages like XHTML, Docbook, RST and similar.", + "homepage": "https://github.com/zetacomponents", + "time": "2013-12-19 11:40:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.0.0" + }, + "platform-dev": [] +} diff --git a/www/core/func/libs/mail/extras/EasyPeasyICS.php b/www/core/func/libs/mail/extras/EasyPeasyICS.php new file mode 100644 index 0000000..d8bfcfa --- /dev/null +++ b/www/core/func/libs/mail/extras/EasyPeasyICS.php @@ -0,0 +1,148 @@ + + * @author Manuel Reinhard + * + * Built with inspiration from + * http://stackoverflow.com/questions/1463480/how-can-i-use-php-to-dynamically-publish-an-ical-file-to-be-read-by-google-calend/1464355#1464355 + * History: + * 2010/12/17 - Manuel Reinhard - when it all started + * 2014 PHPMailer project becomes maintainer + */ + +/** + * Class EasyPeasyICS. + * Simple ICS data generator + * @package phpmailer + * @subpackage easypeasyics + */ +class EasyPeasyICS +{ + /** + * The name of the calendar + * @var string + */ + protected $calendarName; + /** + * The array of events to add to this calendar + * @var array + */ + protected $events = array(); + + /** + * Constructor + * @param string $calendarName + */ + public function __construct($calendarName = "") + { + $this->calendarName = $calendarName; + } + + /** + * Add an event to this calendar. + * @param string $start The start date and time as a unix timestamp + * @param string $end The end date and time as a unix timestamp + * @param string $summary A summary or title for the event + * @param string $description A description of the event + * @param string $url A URL for the event + * @param string $uid A unique identifier for the event - generated automatically if not provided + * @return array An array of event details, including any generated UID + */ + public function addEvent($start, $end, $summary = '', $description = '', $url = '', $uid = '') + { + if (empty($uid)) { + $uid = md5(uniqid(mt_rand(), true)) . '@EasyPeasyICS'; + } + $event = array( + 'start' => gmdate('Ymd', $start) . 'T' . gmdate('His', $start) . 'Z', + 'end' => gmdate('Ymd', $end) . 'T' . gmdate('His', $end) . 'Z', + 'summary' => $summary, + 'description' => $description, + 'url' => $url, + 'uid' => $uid + ); + $this->events[] = $event; + return $event; + } + + /** + * @return array Get the array of events. + */ + public function getEvents() + { + return $this->events; + } + + /** + * Clear all events. + */ + public function clearEvents() + { + $this->events = array(); + } + + /** + * Get the name of the calendar. + * @return string + */ + public function getName() + { + return $this->calendarName; + } + + /** + * Set the name of the calendar. + * @param $name + */ + public function setName($name) + { + $this->calendarName = $name; + } + + /** + * Render and optionally output a vcal string. + * @param bool $output Whether to output the calendar data directly (the default). + * @return string The complete rendered vlal + */ + public function render($output = true) + { + //Add header + $ics = 'BEGIN:VCALENDAR +METHOD:PUBLISH +VERSION:2.0 +X-WR-CALNAME:' . $this->calendarName . ' +PRODID:-//hacksw/handcal//NONSGML v1.0//EN'; + + //Add events + foreach ($this->events as $event) { + $ics .= ' +BEGIN:VEVENT +UID:' . $event['uid'] . ' +DTSTAMP:' . gmdate('Ymd') . 'T' . gmdate('His') . 'Z +DTSTART:' . $event['start'] . ' +DTEND:' . $event['end'] . ' +SUMMARY:' . str_replace("\n", "\\n", $event['summary']) . ' +DESCRIPTION:' . str_replace("\n", "\\n", $event['description']) . ' +URL;VALUE=URI:' . $event['url'] . ' +END:VEVENT'; + } + + //Add footer + $ics .= ' +END:VCALENDAR'; + + if ($output) { + //Output + $filename = $this->calendarName; + //Filename needs quoting if it contains spaces + if (strpos($filename, ' ') !== false) { + $filename = '"'.$filename.'"'; + } + header('Content-type: text/calendar; charset=utf-8'); + header('Content-Disposition: inline; filename=' . $filename . '.ics'); + echo $ics; + } + return $ics; + } +} diff --git a/www/core/func/libs/mail/extras/README.md b/www/core/func/libs/mail/extras/README.md new file mode 100644 index 0000000..dac79e0 --- /dev/null +++ b/www/core/func/libs/mail/extras/README.md @@ -0,0 +1,17 @@ +#PHPMailer Extras + +These classes provide optional additional functions to PHPMailer. + +These are not loaded by the PHPMailer autoloader, so in some cases you may need to `require` them yourself before using them. + +##EasyPeasyICS + +This class was originally written by Manuel Reinhard and provides a simple means of generating ICS/vCal files that are used in sending calendar events. PHPMailer does not use it directly, but you can use it to generate content appropriate for placing in the `Ical` property of PHPMailer. The PHPMailer project is now its official home as Manuel has given permission for that and is no longer maintaining it himself. + +##htmlfilter + +This class by Konstantin Riabitsev and Jim Jagielski implements HTML filtering to remove potentially malicious tags, such as ` + +

    *Tumbleweed floats by*
    There's nothing to see here.

    + + + + + \ No newline at end of file diff --git a/www/core/views/admin/assetDownloader.php b/www/core/views/admin/assetDownloader.php new file mode 100644 index 0000000..6101504 --- /dev/null +++ b/www/core/views/admin/assetDownloader.php @@ -0,0 +1,32 @@ + + + + + <?php echo config::getName();?> | Roblox Asset Uploader + + + + +
    +
    + +

    Roblox Asset Uploader

    +
    +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/admin/assets.php b/www/core/views/admin/assets.php new file mode 100644 index 0000000..cd0cfe7 --- /dev/null +++ b/www/core/views/admin/assets.php @@ -0,0 +1,38 @@ + + + + + <?php echo config::getName();?> | Asset Approval + + + + + +
    +

    Asset Approval

    + Things to not approve +
      +
    • Pornographic content such as naked human beings.
    • +
    • Photos of staff members or members. Only allow known people such as Bill Gates.
    • +
    • Deny all copyrighted content, such as logos, etc.
    • +
    • Punish uploaders if they break obvious rules.
    • +
    +
    +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/admin/ban.php b/www/core/views/admin/ban.php new file mode 100644 index 0000000..fbbf3e3 --- /dev/null +++ b/www/core/views/admin/ban.php @@ -0,0 +1,40 @@ + + + + + <?php echo config::getName();?> | Ban User + + + + +
    +
    + +
    +

    Ban User

    + + + + + +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/admin/main.php b/www/core/views/admin/main.php new file mode 100644 index 0000000..a382533 --- /dev/null +++ b/www/core/views/admin/main.php @@ -0,0 +1,102 @@ +prepare("SELECT COUNT(*) FROM `catalog` WHERE `approved` = 0 AND `declined` = 0;"); + $stmt->execute(); + + $assetsQueued = $stmt->fetchColumn(0); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT COUNT(*) FROM `appeals` WHERE `accepted` = 0 AND `denied` = 0;"); + $stmt->execute(); + $pendingAppeals = $stmt->fetchColumn(0); +?> + + + + <?php echo config::getName();?> | Admin Panel + + + + + +
    +

    Admin Panel

    +
    + +
    +
    Ban User
    +
    + + +
    + + +
    + + + +
    + +
    + + + \ No newline at end of file diff --git a/www/core/views/admin/newHat.php b/www/core/views/admin/newHat.php new file mode 100644 index 0000000..7f2e888 --- /dev/null +++ b/www/core/views/admin/newHat.php @@ -0,0 +1,72 @@ + + + + + <?php echo config::getName();?> | New Hat + + + + + +
    +
    +
    +

    Before you upload

    +

    Make sure that the mesh type is correctly converted

    +

    Assure that the heights of the "Handle" part is correct

    +

    Make sure that the XML file is correctly formatted

    +

    If something goes wrong, please contact an administrator to solve this

    +
    +
    +
    +

    New Hat

    +

    This tool allows you to upload a new hat to the website. Hats are structured with a texture, mesh file and a model file (which is the XML file)

    +

    Note : The XML file must be tested before upload, you can not change the XML file after upload. However, this may change in the future.

    +

    Note : Do NOT upload R15 or future XML files, they will NOT work!

    +

    Note : Abuse of this system WILL result in a revoke of all rights, and a demotion if you are staff

    + + + + + + Buyable + Roblox Asset (ALWAYS CHECK IF NOT CUSTOM MADE!!!) +
    +
    +
    +

    Mesh File

    +

    The mesh file should be a correct extension

    + +
    +
    +

    Texture File

    +

    Should be PNG, but JPG is also supported

    + +
    +
    +
    +

    If you are ready to upload, press the button below

    + +
    +
    +

    Notes

    +

    Hats require three file types, a mesh file (its extension must be converted first), a texture file (simply an image file) and a datafile (basically XML)

    +

    Once you upload the hat, it will appear in your inventory and you will get it for free

    +

    The price must be at least 1 coin, otherwise an error will be returned

    +

    The ImageServer will only correctly render your hat if the height and widths are correct, otherwise it'll appear cut in the image

    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/admin/prune.php b/www/core/views/admin/prune.php new file mode 100644 index 0000000..624557e --- /dev/null +++ b/www/core/views/admin/prune.php @@ -0,0 +1,33 @@ + + + + + <?php echo config::getName();?> | Prune Posts + + + + +
    +
    + +
    +

    Prune Posts

    +

    This utility will remove all posts and replies of a certain user. Quite useful for if the user is a spammer and is not stopping.

    +

    Please know that this can not be undone and that this will only have to be done if no other option is possible.

    + + +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/admin/render.php b/www/core/views/admin/render.php new file mode 100644 index 0000000..bc3fa33 --- /dev/null +++ b/www/core/views/admin/render.php @@ -0,0 +1,31 @@ + + + + + <?php echo config::getName();?> | Rerender Asset + + + + +
    +
    +

    Rerender Asset

    +
    +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/admin/reports.php b/www/core/views/admin/reports.php new file mode 100644 index 0000000..13d851d --- /dev/null +++ b/www/core/views/admin/reports.php @@ -0,0 +1,47 @@ + + + + + <?php echo config::getName();?> | Reports + + + + +
    +
    +

    Reports

    +
    + + + + + + + + prepare("SELECT * FROM reports ORDER BY id DESC;"); + $stmt->execute(); + $count = 0; + foreach($stmt as $result) { + $count++; + echo ''; + } + ?> + +
    Reported UserReasonDate Reported
    '.$result['target'].''.htmlentities($result['reason'], ENT_QUOTES, "UTF-8").''.date('M j Y g:i A', strtotime($result['date'])).'
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/admin/rewardPostie.php b/www/core/views/admin/rewardPostie.php new file mode 100644 index 0000000..47dca05 --- /dev/null +++ b/www/core/views/admin/rewardPostie.php @@ -0,0 +1,32 @@ + + + + + <?php echo config::getName();?> | Reward postie + + + + +
    +
    + +
    +

    Reward Postie

    +

    Have you found great content? You can now reward users with posties! With posties, you can buy exclusive items on Graphictoria

    + + +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/admin/statistics.php b/www/core/views/admin/statistics.php new file mode 100644 index 0000000..ed9b671 --- /dev/null +++ b/www/core/views/admin/statistics.php @@ -0,0 +1,83 @@ + + + + + <?php echo config::getName();?> | Statistics + + + + +
    + prepare("SELECT id FROM users;"); + $stmt->execute(); + $amusers = $stmt->rowCount(); + + $stmt = $dbcon->prepare("SELECT id FROM users WHERE banned = 1;"); + $stmt->execute(); + $busers = $stmt->rowCount(); + + $stmt = $dbcon->prepare("SELECT id FROM users WHERE banned = 0;"); + $stmt->execute(); + $ubusers = $stmt->rowCount(); + + $stmt = $dbcon->prepare("SELECT id FROM topics"); + $stmt->execute(); + $topcount = $stmt->rowCount(); + + $stmt = $dbcon->prepare("SELECT id FROM replies"); + $stmt->execute(); + $repcount = $stmt->rowCount(); + + $totalp = ($topcount + $repcount); + + $stmt = $dbcon->prepare("SELECT id FROM users WHERE rank > 0;"); + $stmt->execute(); + $staffc = $stmt->rowCount(); + + $stmt = $dbcon->prepare("SELECT id FROM catalog"); + $stmt->execute(); + $catcount = $stmt->rowCount(); + + $stmt = $dbcon->prepare("SELECT id FROM `read`"); + $stmt->execute(); + $readc = $stmt->rowCount(); + + $stmt = $dbcon->prepare("SELECT id FROM `wearing`"); + $stmt->execute(); + $wearc = $stmt->rowCount(); + + $stmt = $dbcon->prepare("SELECT id FROM `messages`"); + $stmt->execute(); + $messagecount = $stmt->rowCount(); + + $total = ($amusers + $busers + $ubusers + $topcount + $repcount + $totalp + $staffc + $catcount + $readc + $wearc + $messagecount); + ?> +

    Statistics

    + There are currently registered accounts.
    + There are forum posts.
    + There are read forum topics.
    + There are sent private messages.
    + There are items being worn.
    + There are forum topics.
    + There are forum replies.
    + There are banned users.
    + There are unbanned users.
    + There are catalog items.
    + There are staff members.
    + + If we count this all up, we get the number . +
    + + + \ No newline at end of file diff --git a/www/core/views/admin/unban.php b/www/core/views/admin/unban.php new file mode 100644 index 0000000..5097eaf --- /dev/null +++ b/www/core/views/admin/unban.php @@ -0,0 +1,31 @@ + + + + + <?php echo config::getName();?> | Unban User + + + + +
    +
    + +
    +

    Unban User

    + + +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/blog/main.php b/www/core/views/blog/main.php new file mode 100644 index 0000000..1f1efc8 --- /dev/null +++ b/www/core/views/blog/main.php @@ -0,0 +1,39 @@ + + + + + <?php echo config::getName();?> | Blog + + + + + +
    +
    + + //
    ?> +
    +

    Graphictoria Blog

    + New Post'; + ?> +
    +
    +
    +
    + +
    +
    +
    +
    + + //
    ?> + + + + + \ No newline at end of file diff --git a/www/core/views/catalog/configureitem.php b/www/core/views/catalog/configureitem.php new file mode 100644 index 0000000..cb21465 --- /dev/null +++ b/www/core/views/catalog/configureitem.php @@ -0,0 +1,115 @@ + + + + + <?php echo config::getName();?> | Configure Item + + + + '; + echo 'Incorrect itemId'; + html::buildFooter(); + exit; + } + }else{ + html::getNavigation(); + echo '
    '; + echo 'No ID specified
    '; + html::buildFooter(); + exit; + } + $stmt = $dbcon->prepare("SELECT * FROM catalog WHERE id=:id"); + $stmt->bindParam(':id', $itemId, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + html::getNavigation(); + echo '
    '; + echo 'Item not found
    '; + html::buildFooter(); + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $itemId = $result['id']; + if ($result['deleted'] == 1 or $result['declined'] == 1 or $result['approved'] == 0) { + html::getNavigation(); + echo '
    '; + echo 'Item not found
    '; + html::buildFooter(); + exit; + } + + if ($GLOBALS['loggedIn'] == false) { + header("Location: /catalog"); + exit; + } + + if($result['creator_uid'] != $GLOBALS['userTable']['id'] && $GLOBALS['userTable']['rank'] != 1) + { + /*html::getNavigation(); + echo '
    '; + echo 'You can\'t modify this item.
    '; + html::buildFooter();*/ + header("Location: /catalog"); + exit; + } + + html::getNavigation(); + ?> +
    + + + +
    +
    +

    | Configure Item

    +

    - not finished -

    +

    - not finished -

    +

    - not finished -

    +

    - not finished -

    + + + + + + +
    + + 0) ? + '' + : + null + ?> +
    + 0) ? + ' +
    ' + : + null + ?> +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/catalog/item.php b/www/core/views/catalog/item.php new file mode 100644 index 0000000..0327a17 --- /dev/null +++ b/www/core/views/catalog/item.php @@ -0,0 +1,167 @@ + + + + + <?php echo config::getName();?> | Item + + + + + '; + echo 'Incorrect itemId'; + html::buildFooter(); + exit; + } + }else{ + html::getNavigation(); + echo '
    '; + echo 'No ID specified
    '; + html::buildFooter(); + exit; + } + $stmt = $dbcon->prepare("SELECT * FROM catalog WHERE id=:id"); + $stmt->bindParam(':id', $itemId, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + html::getNavigation(); + echo '
    '; + echo 'Item not found
    '; + html::buildFooter(); + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $itemId = $result['id']; + if ($result['deleted'] == 1 or $result['declined'] == 1 or $result['approved'] == 0) { + html::getNavigation(); + echo '
    '; + echo 'Item not found
    '; + html::buildFooter(); + exit; + } + + if ($GLOBALS['loggedIn'] == false && $result['rbxasset'] == 1) { + header("Location: /catalog"); + exit; + } + + $stmt = $dbcon->prepare("SELECT * FROM users WHERE id=:id"); + $stmt->bindParam(':id', $result['creator_uid'], PDO::PARAM_INT); + $stmt->execute(); + $result_user = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($GLOBALS['loggedIn'] == true) { + $stmt = $dbcon->prepare("SELECT * FROM ownedItems WHERE uid=:id AND catalogid = :catid"); + $stmt->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->bindParam(':catid', $itemId, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + $owned = true; + }else{ + $owned = false; + } + } + + $stmt = $dbcon->prepare("SELECT * FROM ownedItems WHERE catalogid = :catid"); + $stmt->bindParam(':catid', $itemId, PDO::PARAM_INT); + $stmt->execute(); + $boughtTimes = $stmt->rowCount(); + html::getNavigation(); + ?> +
    +
    + var cType = "coins";'; + if ($result['currencyType'] == 1) echo ''; + ?> + + + +
    +
    +
    +
    +

    + +
    +
    +

    Details

    + + + + + Creator: '.$result_user['username'].'

    '; + if ($result['type'] != "decals") { + if ($result['currencyType'] == 0) echo '

    Price: '.$result['price'].' coins

    Bought: '.$boughtTimes.' times

    '; + if ($result['currencyType'] == 1) echo '

    Price: '.$result['price'].' posties

    Bought: '.$boughtTimes.' times

    '; + } + ?> +

    Date Created:

    + Already owned'; + }else{ + if ($result['buyable'] == 1) { + if ($result['currencyType'] == 0) { + if ($GLOBALS['userTable']['coins'] > $result['price'] or $GLOBALS['userTable']['coins'] == $result['price']) { + echo ''; + }else{ + echo ''; + } + }else{ + if ($GLOBALS['userTable']['posties'] > $result['price'] or $GLOBALS['userTable']['posties'] == $result['price']) { + echo ''; + }else{ + echo ''; + } + } + }else{ + echo ''; + } + } + } + if ($loggedIn) { + if ($GLOBALS['userTable']['rank'] > 0) { + if ($result['type'] == "shirts" or $result['type'] == "tshirts" or $result['type'] == "pants" or $result['type'] == "decals") { + echo ''; + } + } + } + ?> +
    +
    + Use this link to use this decal in-game
    '; + echo 'http://gtoria.net/data/assets/uploads/'.$result['fileHash'].'
    '; + } + ?> + Description
    + 0) { + echo context::secureString($result['description']); + }else{ + echo '

    No Description

    '; + } + ?> +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/catalog/main.php b/www/core/views/catalog/main.php new file mode 100644 index 0000000..018f16b --- /dev/null +++ b/www/core/views/catalog/main.php @@ -0,0 +1,73 @@ + + + + + <?php echo config::getName();?> | Catalog + + + + + + + +
    +
    +
    + + +
    +
    +
    +

    Catalog

    + + +
    +
    + + + + +
    +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/catalog/upload.php b/www/core/views/catalog/upload.php new file mode 100644 index 0000000..4914c9f --- /dev/null +++ b/www/core/views/catalog/upload.php @@ -0,0 +1,70 @@ + + + + + <?php echo config::getName();?> | Upload Item + + + + + + + +
    +

    Upload Item

    +

    In here you can upload an asset. This will cost you 5 coins. If your item gets deleted, you will not be granted a refund. In addition, you also need to wait for your asset to be approved.

    +
    +
    +
    +
    + + + + + + +
    + + +
    + + +
    + +
    +
    +
    + Information about assets +
      +
    • Uploading assets will cost you 5 coins. You can only upload 1 item each minute to prevent spamming.
    • +
    • Please make sure you are not breaking the terms of service. If you upload the wrong content, your account may get a punishment.
    • +
    • Shirts and Pants should have the resolution of 585x559, otherwise it will not upload.
    • +
    • Accepted formats are PNG and JPG.
    • +
    • Your item will go through approval. You will receive your item after approval, otherwise it'll be deleted.

    • + + +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/error/notfound.php b/www/core/views/error/notfound.php new file mode 100644 index 0000000..7596c85 --- /dev/null +++ b/www/core/views/error/notfound.php @@ -0,0 +1,20 @@ + + + + + <?php echo config::getName();?> | Page not found + + + + +
    +

    Page not found

    +

    The page you were looking for was not found on our servers.
    If you believe this is on error, please contact us at support@gtoria.net describing your problem

    +
    + + + \ No newline at end of file diff --git a/www/core/views/forum/index.php b/www/core/views/forum/index.php new file mode 100644 index 0000000..3dc1a75 --- /dev/null +++ b/www/core/views/forum/index.php @@ -0,0 +1,61 @@ + + + + + <?php echo config::getName();?> | Forum + + + + + + + 0) + { + echo ''; + } + ?> + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/friends/main.php b/www/core/views/friends/main.php new file mode 100644 index 0000000..785514f --- /dev/null +++ b/www/core/views/friends/main.php @@ -0,0 +1,41 @@ + + + + + <?php echo config::getName();?> | Friends + + + + + + +
    +
    + +

    My Friends

    + +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/friends/requests.php b/www/core/views/friends/requests.php new file mode 100644 index 0000000..856368e --- /dev/null +++ b/www/core/views/friends/requests.php @@ -0,0 +1,41 @@ + + + + + <?php echo config::getName();?> | Friend Requests + + + + + + +
    +
    + +

    My Friend Requests

    + +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/friends/show.php b/www/core/views/friends/show.php new file mode 100644 index 0000000..55e2201 --- /dev/null +++ b/www/core/views/friends/show.php @@ -0,0 +1,65 @@ + + + + + <?php echo config::getName();?> | Friends + + + + +
    + '; + html::buildFooter(); + exit; + } + }else{ + echo 'No ID specified.
    '; + html::buildFooter(); + exit; + } + $stmt = $GLOBALS['dbcon']->prepare("SELECT username, id FROM users WHERE username = :username"); + $stmt->bindParam(':username', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'User not found'; + html::buildFooter(); + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $id = $result['id']; + ?> + + +

    's friends

    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + + + + \ No newline at end of file diff --git a/www/core/views/games/download.php b/www/core/views/games/download.php new file mode 100644 index 0000000..f7f9484 --- /dev/null +++ b/www/core/views/games/download.php @@ -0,0 +1,33 @@ + + + + + <?php echo config::getName();?> | Download + + + + +
    +

    Browser is not supported

    +

    Use another browser such as Chrome to download. This is because this browser does not support the way of downloading.

    +
    + + + \ No newline at end of file diff --git a/www/core/views/games/main.php b/www/core/views/games/main.php new file mode 100644 index 0000000..31bb929 --- /dev/null +++ b/www/core/views/games/main.php @@ -0,0 +1,73 @@ + + + + + <?php echo config::getName();?> | Games + + + + + +
    +
    + + +
    +
    + +
    Looking to host a server? You can add a server here!
    +
    +
    + Enter key directly : + +
    +
    + 2016 +
    +
    + View Public Servers + Private Servers + My Servers + Download 2016'; + } + ?> +
    +
    +
    +
    +
    +
    + + +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/games/new.php b/www/core/views/games/new.php new file mode 100644 index 0000000..9aed19d --- /dev/null +++ b/www/core/views/games/new.php @@ -0,0 +1,86 @@ + + + + + <?php echo config::getName();?> | New Server + + + + + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +

    New Server

    + + + + + + +
    + + + + + + + + + +
    + +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/games/view.php b/www/core/views/games/view.php new file mode 100644 index 0000000..81fe6f0 --- /dev/null +++ b/www/core/views/games/view.php @@ -0,0 +1,147 @@ + + + + + <?php echo config::getName();?> | View Server + + + + +
    + prepare("SELECT * FROM games WHERE id = :id;"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'Server not found'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $id = $result['id']; + if ($result['public'] == 0) { + $gameKey = $result['key']; + $stmtU = $dbcon->prepare("SELECT * FROM gameKeys WHERE userid=:id AND `key` = :key;"); + $stmtU->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmtU->bindParam(':key', $gameKey, PDO::PARAM_STR); + $stmtU->execute(); + if ($GLOBALS['loggedIn']) { + if ($stmtU->rowCount() == 0 and $result['creator_uid'] != $GLOBALS['userTable']['id'] and $GLOBALS['userTable']['rank'] == 0) { + echo 'Server not found'; + exit; + } + }else{ + echo 'Server not found'; + exit; + } + } + $stmt = $dbcon->prepare("SELECT username, id, thumbnailHash, headshotHash FROM users WHERE id = :id"); + $stmt->bindParam(':id', $result['creator_uid'], PDO::PARAM_INT); + $stmt->execute(); + $resultuser = $stmt->fetch(PDO::FETCH_ASSOC); + ?> + + + +
    +
    +
    +
    +
    + +
    +
    + +

    +

    Creator:

    +

    Date Created:

    + Description: None

    '; + }else{ + echo '

    Description: '.user::filter(context::secureString($result['description'])).'

    '; + } + + if ($GLOBALS['loggedIn']) { + if ($result['version'] == 3) echo 'Play'; + }else{ + echo 'Play'; + } + + if ($GLOBALS['loggedIn']) { + if ($GLOBALS['userTable']['rank'] > 0 or $GLOBALS['userTable']['id'] == $result['creator_uid']) { + if ($result['dedi'] == 0) echo ''; + } + if ($result['dedi'] == 1) { + if ($GLOBALS['userTable']['rank'] == 1 || $GLOBALS['userTable']['id'] == $result['creator_uid']) echo ''; + } + } + ?> +
    +
    +
    +
    +

    Online Players

    +
    + prepare("SELECT lastSeen, inGame, username, thumbnailHash, headshotHash, id FROM users WHERE inGameId = :id ORDER BY id"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + $count = 0; + foreach($stmt as $resultk) { + $from_time = strtotime($resultk['lastSeen']); + $to_time = strtotime(context::getCurrentTime()); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince < 2 and $resultk['inGame'] == 1) { + $count++; + $username = $resultk['username']; + if (strlen($username) > 10) { + $username = substr($username, 0, 7) . '...'; + } + echo ''; + } + } + if ($count == 0) { + echo 'There is nobody online.'; + } + echo ''; + ?> +
    +
    +

    Command

    Use this command to start your server

    dofile("http://api.gtoria.net/serverscripts/server.php?key='.$result['privatekey'].'")
    '; + } + if ($result['public'] == 0) { + echo '

    Invites

    Use this key to invite people to your server

    '.$result['key'].'
    '; + } + } + ?> +
    + + + + \ No newline at end of file diff --git a/www/core/views/ide/requestauth.php b/www/core/views/ide/requestauth.php new file mode 100644 index 0000000..25aebe2 --- /dev/null +++ b/www/core/views/ide/requestauth.php @@ -0,0 +1,8 @@ + + + + + <?php echo config::getName();?> | Toolbox + + + +

    toolbox is in progress maybe :I

    + test model + + \ No newline at end of file diff --git a/www/core/views/ide/welcome.php b/www/core/views/ide/welcome.php new file mode 100644 index 0000000..c186f60 --- /dev/null +++ b/www/core/views/ide/welcome.php @@ -0,0 +1,67 @@ + + + + + <?php echo config::getName();?> | Welcome + + + + + +
    +

    Welcome to Graphictoria Studio

    +
    +
    +

    Recently Opened Files

    + ' . htmlspecialchars(urldecode(urldecode($value))) . ''; + } + } + } + + if($places == 0) + { + echo '

    You haven\'t opened any places yet!

    '; + } + ?> +
    +
    +
    +

    Place Templates

    +
    +
    + baseplate +

    Baseplate

    +
    +
    + terrain +

    Flat Terrain

    +
    +
    + ffa +

    Free for All

    +
    +
    + tdm +

    Team Death Match

    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/www/core/views/online.php b/www/core/views/online.php new file mode 100644 index 0000000..5b0098b --- /dev/null +++ b/www/core/views/online.php @@ -0,0 +1,64 @@ + + + + + <?php echo config::getName();?> | Online Users + + + + + + prepare("SELECT `lastSeen` FROM `users` WHERE `banned` = 0 AND `hideStatus` = 0 ORDER BY `lastSeen` DESC;"); + $stmt->execute(); + $hcount = 0; + + foreach($stmt as $result) { + $from_time = strtotime($result['lastSeen']); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince < 1440){ + $hcount++; + } + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT COUNT(*) FROM `users`;"); + $stmt->execute(); + $users = $stmt->fetchColumn(0); + ?> +
    +
    +
    +
    +
    Users currently online
    +
    +
    +
    +
    +
    +
    +
    Statistics
    +
    + We currently have registered users.
    + In the past 24 hours, there have been users online. +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/character.php b/www/core/views/user/character.php new file mode 100644 index 0000000..739aa00 --- /dev/null +++ b/www/core/views/user/character.php @@ -0,0 +1,86 @@ + + + + + <?php echo config::getName();?> | Character + + + + +
    + + +
    +
    +
    +
    +
    +

    Character

    +
    + + + Regenerate + +
    +
    +
    +
    +

    Colors

    + +
    +
    +
    + +
    +

    Inventory

    + +
    +
    +
    +
    +

    Wearing

    +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/dashboard.php b/www/core/views/user/dashboard.php new file mode 100644 index 0000000..ec50570 --- /dev/null +++ b/www/core/views/user/dashboard.php @@ -0,0 +1,30 @@ + + + + + <?php echo config::getName();?> | Dashboard + + + + +
    +
    +
    +

    Hello,

    + +
    +
    +
    +

    Wall posts will appear here

    +

    We will work on this sooner or later

    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/groups/admin.php b/www/core/views/user/groups/admin.php new file mode 100644 index 0000000..99c9369 --- /dev/null +++ b/www/core/views/user/groups/admin.php @@ -0,0 +1,67 @@ + + + + + <?php echo config::getName();?> | Group Admin + + + + + + +
    + prepare("SELECT * FROM groups WHERE id = :id"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'Group has not been found'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $creator_uid = $result['cuid']; + $id = $result['id']; + if (($result['closed'] == 1 || $creator_uid != $GLOBALS['userTable']['id']) and $GLOBALS['userTable']['rank'] == 0) { + echo 'Access Denied'; + exit; + } + ?> + +
    +
    +
    +

    Group Admin

    + + + + Return to Group +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/groups/create.php b/www/core/views/user/groups/create.php new file mode 100644 index 0000000..1dd6c5f --- /dev/null +++ b/www/core/views/user/groups/create.php @@ -0,0 +1,56 @@ + + + + + <?php echo config::getName();?> | Create Group + + + + + + + +
    +
    +
    +
    +

    Create a Group

    +

    In here you can create a group that anyone can join and be a part of.

    +
    + + + + +
    + +
    +
    +
    +

    Things to remember

    +
      +
    • Group Names can not be longer than 32 characters
    • +
    • Group Names must be at least 5 characters
    • +
    • Descriptions can not be longer than 256 characters
    • +
    • Descriptions are optional
    • +
    • Creating a Group will cost you 50 coins
    • +
    • You can only be in 10 groups at a time
    • +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/groups/main.php b/www/core/views/user/groups/main.php new file mode 100644 index 0000000..ba4e5d6 --- /dev/null +++ b/www/core/views/user/groups/main.php @@ -0,0 +1,37 @@ + + + + + <?php echo config::getName();?> | Groups + + + + + + +
    +
    +
    +

    Groups

    +

    Groups will make team-work a lot easier, interact with friends and make new ones!

    + Create new Group + Search Group +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/groups/search.php b/www/core/views/user/groups/search.php new file mode 100644 index 0000000..93f3e83 --- /dev/null +++ b/www/core/views/user/groups/search.php @@ -0,0 +1,47 @@ + + + + + <?php echo config::getName();?> | Groups + + + + + + + +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/groups/view.php b/www/core/views/user/groups/view.php new file mode 100644 index 0000000..2755e9e --- /dev/null +++ b/www/core/views/user/groups/view.php @@ -0,0 +1,120 @@ + + + + + <?php echo config::getName();?> | Group + + + + +
    + prepare("SELECT * FROM groups WHERE id = :id"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'Group has not been found'; + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if($result['closed'] == 1) { + echo '
    This group has been locked.

    '; + if ($GLOBALS['loggedIn'] == true) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM group_members WHERE uid = :uid AND gid = :id"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() > 0) { + echo ''; + } + } + exit('
    '); + } + $creator_uid = $result['cuid']; + $id = $result['id']; + ?> + + + + +
    +
    +
    +
    +

    + prepare("SELECT thumbnailHash, headshotHash, id, username FROM users WHERE id = :id"); + $stmt->bindParam(':id', $creator_uid, PDO::PARAM_INT); + $stmt->execute(); + $resultuser = $stmt->fetch(PDO::FETCH_ASSOC); + ?> + +

    Creator:

    +

    Date Created:

    + Leave and delete Group'; + }else{ + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM group_members WHERE uid = :uid AND gid = :id"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->bindParam(':uid', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo ''; + }else{ + echo ''; + } + } + if ($GLOBALS['userTable']['id'] == $result['cuid'] or $GLOBALS['userTable']['rank'] > 0) { + echo 'Group Admin'; + } + }else{ + echo ''; + } + ?> +
    +
    +

    Description

    + No Description
    '; + }else{ + echo '

    '.nl2br(context::secureString($result['description'])).'

    '; + } + ?> +
    +
    +
    +
    +

    Members

    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/logout.php b/www/core/views/user/logout.php new file mode 100644 index 0000000..c3cf532 --- /dev/null +++ b/www/core/views/user/logout.php @@ -0,0 +1,32 @@ +prepare('SELECT * FROM sessions WHERE userId = :userId AND sessionId = :sessionId;'); + $stmt->bindParam(':userId', $_COOKIE['auth_uid'], PDO::PARAM_INT); + $stmt->bindParam(':sessionId', $_COOKIE['a_id'], PDO::PARAM_STR); + $stmt->execute(); + $resultSession = $stmt->fetch(PDO::FETCH_ASSOC); + $sessionId = $resultSession['id']; + + $stmt = $dbcon->prepare('DELETE FROM sessions WHERE id=:id;'); + $stmt->bindParam(':id', $sessionId, PDO::PARAM_INT); + $stmt->execute(); + + unset($_COOKIE['auth_uid']); + unset($_COOKIE['a_id']); + setcookie('auth_uid', "", time() - 3600); + setcookie('a_id', "", time() - 3600); + + if (isset($_SERVER['HTTP_REFERER'])) { + header("Location: ".$_SERVER['HTTP_REFERER']); + exit; + }else{ + header("Location: /"); + exit; + } +?> \ No newline at end of file diff --git a/www/core/views/user/messages.php b/www/core/views/user/messages.php new file mode 100644 index 0000000..e66e8a2 --- /dev/null +++ b/www/core/views/user/messages.php @@ -0,0 +1,46 @@ + + + + + <?php echo config::getName();?> | Messages + + + + + + + +
    + +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/profile.php b/www/core/views/user/profile.php new file mode 100644 index 0000000..f0f7b28 --- /dev/null +++ b/www/core/views/user/profile.php @@ -0,0 +1,351 @@ + + + + + <?php echo config::getName();?> | Profile + + + + +
    + '; + html::buildFooter(); + exit; + } + }else{ + echo 'No username specified
    '; + html::buildFooter(); + exit; + } + + $stmt = $GLOBALS['dbcon']->prepare('SELECT * FROM users WHERE username = :username;'); + $stmt->bindParam(':username', $username, PDO::PARAM_STR); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'User not found'; + html::buildFooter(); + exit; + } + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $userID = $result['id']; + $p_username = $result['username']; + if ($result['banned'] == 1 && $result['bantype']==5) { + echo 'This user has been suspended'; + html::buildFooter(); + exit; + } + echo ''; + + $stmtr = $GLOBALS['dbcon']->prepare("SELECT id FROM `profile_views` WHERE `viewer` = :id AND `profile` = :pid;"); + $stmtr->bindParam(':id', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmtr->bindParam(':pid', $userID, PDO::PARAM_INT); + $stmtr->execute(); + if ($stmtr->rowCount() == 0) { + $found = false; + }else{ + $found = true; + } + if ($found == false and $loggedIn == true) { + $result['profileviews'] = $result['profileviews']+1; + $query = "INSERT INTO `profile_views` (`viewer`, `profile`) VALUES (:userId, :profile);"; + $stmt = $GLOBALS['dbcon']->prepare($query); + $stmt->bindParam(':profile', $userID, PDO::PARAM_INT); + $stmt->bindParam(':userId', $GLOBALS['userTable']['id'], PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("UPDATE users SET profileviews = profileviews + 1 WHERE id = :id"); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + } + ?> +
    + +
    +
    +

    + ● ') { + if ($GLOBALS['loggedIn']) { + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM gameJoins WHERE uid = :id"); + $stmt->bindParam(':id', $result['id'], PDO::PARAM_INT); + $stmt->execute(); + $resultGame = $stmt->fetch(PDO::FETCH_ASSOC); + echo ' In Game || Follow
    '; + }else{ + echo ' In Game
    '; + } + } + ?> + + + Send Message'; + context::buildFriendButton($result['id']); + echo '
    '; + } + } + ?> +

    + '.context::secureString($result['about']).''; + } + ?> +

    +
    +
    +

    Statistics

    +

    Join Date:

    +

    Last Seen:

    + +

    +

    Forum Posts:

    +

    Profile views:

    +
    +
    +

    Groups

    + prepare("SELECT * FROM groups WHERE cuid = :id;"); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + $count++; + $name = ''; + if($result['closed'] == 1) { + $name = '[ Content Deleted ' . $result['id'] . ' ]'; + } else { + $name = context::secureString($result['name']); + } + $gstr .= ''.$name.', '; + } + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM group_members WHERE uid = :id;"); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + foreach($stmt as $result) { + $count++; + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM groups WHERE id = :id"); + $gId = $result['gid']; + $stmt->bindParam(':id', $gId, PDO::PARAM_INT); + $stmt->execute(); + $resultGroupM = $stmt->fetch(PDO::FETCH_ASSOC); + $name = ''; + if($resultGroupM['closed'] == 1) { + $name = '[ Content Deleted ' . $resultGroupM['id'] . ' ]'; + } else { + $name = context::secureString($resultGroupM['name']); + } + $gstr .= ''.$name.', '; + } + if ($count == 0) { + echo '
    This user is not in any group.
    '; + } else { + echo substr($gstr, 0, -2); + } + ?> +
    + 0) + { + $dontShow = false; + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `lastIP`, `registerIP` FROM `users` WHERE `id` = :id LIMIT 1;"); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT `username`, `banned`, `rank` FROM `users` WHERE `id` != :id AND (`lastIP` = :ip1 OR `registerIP` = :ip2) ORDER BY `id`;"); + $stmt->bindParam(':ip1', $result['lastIP'], PDO::PARAM_INT); + $stmt->bindParam(':ip2', $result['registerIP'], PDO::PARAM_INT); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach($result as $user) + { + if($user['rank'] > 0) + $dontShow = true; + } + + if(!$dontShow && $stmt->rowCount() > 0) + { + echo '

    Possible alt accounts

    '; + foreach($result as $user) + { + $usern = ''; + if ($user['banned'] == 1) { + $usern = ''.$user['username'].''; + }elseif ($user['rank'] == 0) { + $usern = $user['username']; + }elseif ($user['rank'] == 1) { + $usern = ''.$user['username'].''; + }elseif ($user['rank'] == 2) { + $usern = ''.$user['username'].''; + } + echo ''.$usern.' '; + } + echo '
    '; + } + } + ?> + +
    + prepare("SELECT `banned` FROM `users` WHERE `id` = :id LIMIT 1;"); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if($result['banned']==1) + { + echo '
    This user has been temporarily suspended.
    '; + } + ?> +
    +

    Friends

    + prepare("SELECT * FROM friends WHERE userId1 = :id;"); + $stmtc->bindParam(':id', $userID, PDO::PARAM_INT); + $stmtc->execute(); + + $stmt = $GLOBALS['dbcon']->prepare("SELECT * FROM friends WHERE userId1 = :id ORDER BY id DESC LIMIT 8;"); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'This user has no friends.'; + }else{ + echo '
    '; + } + echo ''; + foreach($stmt as $result) { + $userId = $result['userId2']; + $stmt = $GLOBALS['dbcon']->prepare("SELECT username, thumbnailHash, headshotHash, lastSeen, id FROM users WHERE id = :id"); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); + $stmt->execute(); + $resultuser = $stmt->fetch(PDO::FETCH_ASSOC); + $username = $resultuser['username']; + if (strlen($username) > 10) { + $username = substr($username, 0, 7) . '...'; + } + echo ''; + } + if ($stmt->rowCount() > 0) { + echo '
    '; + } + if ($stmtc->rowCount() > 8) { + echo 'Show all friends'; + } + ?> +
    +
    +

    Graphictoria Badges

    + prepare("SELECT `badgeId` FROM `badges` WHERE `uid` = :id ORDER BY FIELD(`badgeId`, 5, 7, 6, 4, 8, 3, 2, 1) ASC;"); + $stmt->bindParam(':id', $userID, PDO::PARAM_INT); + $stmt->execute(); + if ($stmt->rowCount() == 0) { + echo 'This user has no badges.'; + } + else{ + echo '
    '; + } + $name = ""; + $description = ""; + foreach($stmt as $resultBadge) { + if ($resultBadge['badgeId'] == 1) { + $name = "Administrator"; + $description = "Owned by the administrators of Graphictoria. Owners of this badge control the whole entire website and can change everything. Users who do not have this badge are not administrators."; + } + if ($resultBadge['badgeId'] == 2) { + $name = "Administrator"; + $description = "Owned by the administrators of Graphictoria. Owners of this badge control the whole entire website and can change everything. Users who do not have this badge are not administrators."; + } + if ($resultBadge['badgeId'] == 3) { + $name = "Moderator"; + $description = "Owned by the moderators of Graphictoria. Owners of this badge moderate the website. Users who do not have this badge are not moderators."; + } + if ($resultBadge['badgeId'] == 4) { + $name = "Forumer"; + $description = "Owned by the active forumers who managed to get 1000 posts!"; + } + if ($resultBadge['badgeId'] == 5) { + $name = "Member"; + $description = "This badge is owned by every single user of Graphictoria and proves that you have played Graphictoria."; + } + if ($resultBadge['badgeId'] == 6) { + $name = "Roblox Staff"; + $description = "This badge is only awarded to verified, legitimate Roblox staff members."; + } + if ($resultBadge['badgeId'] == 7) { + $name = "Before 100"; + $description = "Owned by the very first 100 users of Graphictoria. Users with this badge know the story of how we came to be."; + } + if ($resultBadge['badgeId'] == 8) { + $name = "Gamer"; + $description = "This user has successfully joined a game at least once"; + } + echo '

    '.$name.'

    '; + } + if ($stmt->rowCount() > 0) { + echo '
    '; + } + ?> +
    + +
    +

    Inventory

    + +
    +
    +
    + +
    +
    + + +
    + + + + \ No newline at end of file diff --git a/www/core/views/user/security/appeal.php b/www/core/views/user/security/appeal.php new file mode 100644 index 0000000..595be3b --- /dev/null +++ b/www/core/views/user/security/appeal.php @@ -0,0 +1,66 @@ + + + + + <?php echo config::getName();?> | Appeal + + + + '; + } + ?> +
    + + + +
    +
    + +
    this isnt done yet, it does nothing
    +
    +

    Ban Appeal

    +

    Feel like you've been incorrectly punished? Fill out the form below explaining why you've been banned incorrectly and a moderator will review it. It may take a few days before any action is taken.

    + +

    Your explanation must be over 50 characters. Please note that spamming characters will result in your appeal being denied.

    +
    + +

    * Abusing the appeal system will result in a permanent ban, along with the removal of your ability to appeal.

    +
    + +
    Your appeal has been sent and will be reviewed shortly! It may take a few days for your appeal to be read.
    + Back + 1): + ?> +

    You aren't eligible to appeal.

    + Back + +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/security/banned.php b/www/core/views/user/security/banned.php new file mode 100644 index 0000000..b75c95b --- /dev/null +++ b/www/core/views/user/security/banned.php @@ -0,0 +1,73 @@ + + + + + <?php echo config::getName();?> | <?=($GLOBALS['userTable']['bantype'] == 5 or $GLOBALS['userTable']['bantype'] == 0) ? 'Account Deleted' : 'Suspended'?> + + + + + +
    + + + +
    +
    +
    +
    + '.$type.''; + if ($type == "Account Deleted") { + $message = "You will not be able to re-activate your account."; + }elseif ($type == "Warning") { + $message = "You can re-activate your account now."; + }else{ + $message = "You will be able to re-activate your account once the suspension has been expired."; + } + echo '

    Reviewed: '.date('M j Y g:i A', strtotime($GLOBALS['userTable']['bantime'])).'

    '; + echo '

    Moderator Note: '.context::secureString($GLOBALS['userTable']['banreason']).'

    '; + echo '

    '.$message.'

    '; + + if ($GLOBALS['userTable']['bantype'] != 5 && $GLOBALS['userTable']['bantype'] != 0) { + echo ''; + } + ?> +
    + +

    Believe you've been unfairly banned? Appeal here.

    + +

    You have an appeal pending.

    + +

    Your appeal has been denied. You cannot re-appeal for this ban.

    + +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/security/resetpassword.php b/www/core/views/user/security/resetpassword.php new file mode 100644 index 0000000..d54bfac --- /dev/null +++ b/www/core/views/user/security/resetpassword.php @@ -0,0 +1,62 @@ +prepare($query); + $stmt->bindParam(':uid', $userID, PDO::PARAM_INT); + $stmt->bindParam(':key', $key, PDO::PARAM_STR); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0 or $result['used'] == 1) { + header("Location: /"); + exit; + } + + $currentTime = date('Y-m-d H:i:s'); + $to_time = strtotime($currentTime); + $from_time = strtotime($result['date']); + $timeSince = round(abs($to_time - $from_time) / 60,2); + if ($timeSince > 5) { + header("Location: /"); + exit; + } +?> + + + + <?php echo config::getName();?> | Reset Password + + + + +
    + +
    +
    +
    +

    Reset Password

    +

    If you have lost your password, you can reset it here

    + + + +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/security/twostepauth.php b/www/core/views/user/security/twostepauth.php new file mode 100644 index 0000000..f7af091 --- /dev/null +++ b/www/core/views/user/security/twostepauth.php @@ -0,0 +1,31 @@ + + + + + <?php echo config::getName();?> | Two Step Authentication + + + + + +
    +
    +
    +
    +

    Two Step Authentication

    +

    Sign in with the authentication code generated by your mobile app

    + + +
    +

    If you need help, please contact support@gtoria.net and we will gladly help you

    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/user/security/verifyEmail.php b/www/core/views/user/security/verifyEmail.php new file mode 100644 index 0000000..316075f --- /dev/null +++ b/www/core/views/user/security/verifyEmail.php @@ -0,0 +1,50 @@ + + + + + <?php echo config::getName();?> | Verify Email + + + + + +
    +
    +
    Outgoing emails to hotmail and outlook are currently paused. You may have to use gmail to receive the verification code.
    +
    +
    +

    Verify your email

    +

    An email has been sent to which contains your verification code. Enter that code below.

    +

    All emails will be automatically resent after 15 minutes in case you did not receive it.

    +

    If you are unable to find your email, check your spam or junk folder.

    +

    If you made a mistake entering your email address, click here

    + + +
    +

    If you need help, please contact support@gtoria.net and we will gladly help you

    +
    +
    + + + + \ No newline at end of file diff --git a/www/core/views/user/settings.php b/www/core/views/user/settings.php new file mode 100644 index 0000000..b22841e --- /dev/null +++ b/www/core/views/user/settings.php @@ -0,0 +1,91 @@ + + + + + <?php echo config::getName();?> | Settings + + + + + + + +
    +

    Settings

    +
    + +
    +
    +
    +

    About Me

    You can set your text here which will appear on your profile.

    + + +
    +
    +
    +
    +
    +

    Change Password

    You can change your password here

    +
    +
    + +
    +
    + +
    +
    + + +
    +
    +
    +
    +
    +

    Change E-Mail

    You can change your e-mail here

    + + + +
    +
    +
    +
    +
    +
    +

    Two Step Authentication

    This feature requires you to enter a secondary code in order to login.

    +
    + +
    +
    +
    +
    +
    +

    Appearance

    How the website looks

    +
    + + +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/core/views/users.php b/www/core/views/users.php new file mode 100644 index 0000000..d9026ad --- /dev/null +++ b/www/core/views/users.php @@ -0,0 +1,50 @@ + + + + + <?php echo config::getName();?> | Users + + + + + +
    +
    + + +
    +
    +
    +
    +
    + + + + +
    +
    + Online users +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/www/data/.htaccess b/www/data/.htaccess new file mode 100644 index 0000000..e1858cb --- /dev/null +++ b/www/data/.htaccess @@ -0,0 +1,4 @@ +Satisfy Any +Allow from all + +RewriteEngine on \ No newline at end of file diff --git a/www/data/assets/config.php b/www/data/assets/config.php new file mode 100644 index 0000000..6a0aac9 --- /dev/null +++ b/www/data/assets/config.php @@ -0,0 +1,12 @@ + diff --git a/www/data/assets/decals/.gitkeep b/www/data/assets/decals/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/faces/models/.gitkeep b/www/data/assets/faces/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/faces/texture/.gitkeep b/www/data/assets/faces/texture/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/faces/thumbnail/.gitkeep b/www/data/assets/faces/thumbnail/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/gear/animations/.gitkeep b/www/data/assets/gear/animations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/gear/icons/.gitkeep b/www/data/assets/gear/icons/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/gear/meshes/.gitkeep b/www/data/assets/gear/meshes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/gear/models/.gitkeep b/www/data/assets/gear/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/gear/sounds/.gitkeep b/www/data/assets/gear/sounds/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/gear/textures/.gitkeep b/www/data/assets/gear/textures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/gear/thumbnail/.gitkeep b/www/data/assets/gear/thumbnail/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/hats/mesh/.gitkeep b/www/data/assets/hats/mesh/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/hats/models/.gitkeep b/www/data/assets/hats/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/hats/sound/.gitkeep b/www/data/assets/hats/sound/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/hats/texture/.gitkeep b/www/data/assets/hats/texture/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/hats/thumbnail/.gitkeep b/www/data/assets/hats/thumbnail/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/heads/mesh/.gitkeep b/www/data/assets/heads/mesh/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/heads/models/.gitkeep b/www/data/assets/heads/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/heads/texture/.gitkeep b/www/data/assets/heads/texture/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/heads/thumbnail/.gitkeep b/www/data/assets/heads/thumbnail/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/pants/.gitkeep b/www/data/assets/pants/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/pants/models/get.php b/www/data/assets/pants/models/get.php new file mode 100644 index 0000000..dd23199 --- /dev/null +++ b/www/data/assets/pants/models/get.php @@ -0,0 +1,45 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + + $stmt = $dbcon->prepare("SELECT * FROM `catalog` WHERE `assetid`=:id AND `type`='pants';"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) { + $dbcon = null; + exit; + } + + header('Content-type: text/xml'); + $dbcon = null; +?> + + + + null + nil + + + + http://gtoria.net/data/assets/uploads/ + + Pants + true + + + \ No newline at end of file diff --git a/www/data/assets/pants/textures/.gitkeep b/www/data/assets/pants/textures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/shirts/.gitkeep b/www/data/assets/shirts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/shirts/models/get.php b/www/data/assets/shirts/models/get.php new file mode 100644 index 0000000..f13eebb --- /dev/null +++ b/www/data/assets/shirts/models/get.php @@ -0,0 +1,45 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + echo $e->getMessage(); + } + + $stmt = $dbcon->prepare("SELECT * FROM `catalog` WHERE `assetid`=:id AND `type`='shirts';"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) { + $dbcon = null; + exit; + } + + header('Content-type: text/xml'); + $dbcon = null; +?> + + + + null + nil + + + + http://gtoria.net/data/assets/uploads/ + + Shirt + true + + + \ No newline at end of file diff --git a/www/data/assets/shirts/textures/.gitkeep b/www/data/assets/shirts/textures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/tshirts/.gitkeep b/www/data/assets/tshirts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/assets/tshirts/models/get.php b/www/data/assets/tshirts/models/get.php new file mode 100644 index 0000000..d4611be --- /dev/null +++ b/www/data/assets/tshirts/models/get.php @@ -0,0 +1,45 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbcon->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + }catch (PDOExpection $e){ + exit; + } + + $stmt = $dbcon->prepare("SELECT * FROM `catalog` WHERE `assetid`=:id AND `type`='tshirts';"); + $stmt->bindParam(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($stmt->rowCount() == 0) { + $dbcon = null; + exit; + } + + header('Content-type: text/xml'); + $dbcon = null; +?> + + + + null + nil + + + + http://gtoria.net/data/assets/uploads/ + + Shirt Graphic + true + + + \ No newline at end of file diff --git a/www/data/assets/uploads/.gitkeep b/www/data/assets/uploads/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/finobe/.gitkeep b/www/data/finobe/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/data/socialcreditsounds/die1.mp3 b/www/data/socialcreditsounds/die1.mp3 new file mode 100644 index 0000000..b1adada Binary files /dev/null and b/www/data/socialcreditsounds/die1.mp3 differ diff --git a/www/data/socialcreditsounds/die2.mp3 b/www/data/socialcreditsounds/die2.mp3 new file mode 100644 index 0000000..1119a1e Binary files /dev/null and b/www/data/socialcreditsounds/die2.mp3 differ diff --git a/www/data/socialcreditsounds/die3.mp3 b/www/data/socialcreditsounds/die3.mp3 new file mode 100644 index 0000000..01e1d82 Binary files /dev/null and b/www/data/socialcreditsounds/die3.mp3 differ diff --git a/www/data/templates/baseplate.rbxl b/www/data/templates/baseplate.rbxl new file mode 100644 index 0000000..95e6f95 Binary files /dev/null and b/www/data/templates/baseplate.rbxl differ diff --git a/www/data/templates/clothing4.png b/www/data/templates/clothing4.png new file mode 100644 index 0000000..be72b4b Binary files /dev/null and b/www/data/templates/clothing4.png differ diff --git a/www/data/templates/ffa.rbxl b/www/data/templates/ffa.rbxl new file mode 100644 index 0000000..f1c6bf7 Binary files /dev/null and b/www/data/templates/ffa.rbxl differ diff --git a/www/data/templates/tdm.rbxl b/www/data/templates/tdm.rbxl new file mode 100644 index 0000000..ce827fa Binary files /dev/null and b/www/data/templates/tdm.rbxl differ diff --git a/www/data/templates/terrain.rbxl b/www/data/templates/terrain.rbxl new file mode 100644 index 0000000..89fabbc Binary files /dev/null and b/www/data/templates/terrain.rbxl differ diff --git a/www/favicon.ico b/www/favicon.ico new file mode 100644 index 0000000..4c7f546 Binary files /dev/null and b/www/favicon.ico differ diff --git a/www/html/img/background/background-main.png b/www/html/img/background/background-main.png new file mode 100644 index 0000000..2b28f76 Binary files /dev/null and b/www/html/img/background/background-main.png differ diff --git a/www/html/img/badges/2.png b/www/html/img/badges/2.png new file mode 100644 index 0000000..9ae8c28 Binary files /dev/null and b/www/html/img/badges/2.png differ diff --git a/www/html/img/badges/3.png b/www/html/img/badges/3.png new file mode 100644 index 0000000..c5b44b5 Binary files /dev/null and b/www/html/img/badges/3.png differ diff --git a/www/html/img/badges/4.png b/www/html/img/badges/4.png new file mode 100644 index 0000000..c2b1b3d Binary files /dev/null and b/www/html/img/badges/4.png differ diff --git a/www/html/img/badges/5.png b/www/html/img/badges/5.png new file mode 100644 index 0000000..2c8ea04 Binary files /dev/null and b/www/html/img/badges/5.png differ diff --git a/www/html/img/badges/6.png b/www/html/img/badges/6.png new file mode 100644 index 0000000..6dd7206 Binary files /dev/null and b/www/html/img/badges/6.png differ diff --git a/www/html/img/badges/7.png b/www/html/img/badges/7.png new file mode 100644 index 0000000..dca5fce Binary files /dev/null and b/www/html/img/badges/7.png differ diff --git a/www/html/img/badges/8.png b/www/html/img/badges/8.png new file mode 100644 index 0000000..ab826ad Binary files /dev/null and b/www/html/img/badges/8.png differ diff --git a/www/html/img/characters/busy.png b/www/html/img/characters/busy.png new file mode 100644 index 0000000..5cd671f Binary files /dev/null and b/www/html/img/characters/busy.png differ diff --git a/www/html/img/emoticons/afro-1.png b/www/html/img/emoticons/afro-1.png new file mode 100644 index 0000000..0e41358 Binary files /dev/null and b/www/html/img/emoticons/afro-1.png differ diff --git a/www/html/img/emoticons/afro.png b/www/html/img/emoticons/afro.png new file mode 100644 index 0000000..ab281cb Binary files /dev/null and b/www/html/img/emoticons/afro.png differ diff --git a/www/html/img/emoticons/agent.png b/www/html/img/emoticons/agent.png new file mode 100644 index 0000000..be4e56c Binary files /dev/null and b/www/html/img/emoticons/agent.png differ diff --git a/www/html/img/emoticons/alien-1.png b/www/html/img/emoticons/alien-1.png new file mode 100644 index 0000000..e1b808b Binary files /dev/null and b/www/html/img/emoticons/alien-1.png differ diff --git a/www/html/img/emoticons/alien.png b/www/html/img/emoticons/alien.png new file mode 100644 index 0000000..13df463 Binary files /dev/null and b/www/html/img/emoticons/alien.png differ diff --git a/www/html/img/emoticons/angel.png b/www/html/img/emoticons/angel.png new file mode 100644 index 0000000..6ddd62f Binary files /dev/null and b/www/html/img/emoticons/angel.png differ diff --git a/www/html/img/emoticons/angry-1.png b/www/html/img/emoticons/angry-1.png new file mode 100644 index 0000000..1cfefee Binary files /dev/null and b/www/html/img/emoticons/angry-1.png differ diff --git a/www/html/img/emoticons/angry-2.png b/www/html/img/emoticons/angry-2.png new file mode 100644 index 0000000..e1b032d Binary files /dev/null and b/www/html/img/emoticons/angry-2.png differ diff --git a/www/html/img/emoticons/angry-3.png b/www/html/img/emoticons/angry-3.png new file mode 100644 index 0000000..1b332b2 Binary files /dev/null and b/www/html/img/emoticons/angry-3.png differ diff --git a/www/html/img/emoticons/angry-4.png b/www/html/img/emoticons/angry-4.png new file mode 100644 index 0000000..b2aed05 Binary files /dev/null and b/www/html/img/emoticons/angry-4.png differ diff --git a/www/html/img/emoticons/angry-5.png b/www/html/img/emoticons/angry-5.png new file mode 100644 index 0000000..fc5915b Binary files /dev/null and b/www/html/img/emoticons/angry-5.png differ diff --git a/www/html/img/emoticons/angry.png b/www/html/img/emoticons/angry.png new file mode 100644 index 0000000..9fd63f0 Binary files /dev/null and b/www/html/img/emoticons/angry.png differ diff --git a/www/html/img/emoticons/arguing.png b/www/html/img/emoticons/arguing.png new file mode 100644 index 0000000..b706679 Binary files /dev/null and b/www/html/img/emoticons/arguing.png differ diff --git a/www/html/img/emoticons/arrogant.png b/www/html/img/emoticons/arrogant.png new file mode 100644 index 0000000..3e8b303 Binary files /dev/null and b/www/html/img/emoticons/arrogant.png differ diff --git a/www/html/img/emoticons/asian-1.png b/www/html/img/emoticons/asian-1.png new file mode 100644 index 0000000..f278d79 Binary files /dev/null and b/www/html/img/emoticons/asian-1.png differ diff --git a/www/html/img/emoticons/asian.png b/www/html/img/emoticons/asian.png new file mode 100644 index 0000000..cb95d29 Binary files /dev/null and b/www/html/img/emoticons/asian.png differ diff --git a/www/html/img/emoticons/avatar.png b/www/html/img/emoticons/avatar.png new file mode 100644 index 0000000..8d70d8f Binary files /dev/null and b/www/html/img/emoticons/avatar.png differ diff --git a/www/html/img/emoticons/baby-1.png b/www/html/img/emoticons/baby-1.png new file mode 100644 index 0000000..cc3777d Binary files /dev/null and b/www/html/img/emoticons/baby-1.png differ diff --git a/www/html/img/emoticons/baby-2.png b/www/html/img/emoticons/baby-2.png new file mode 100644 index 0000000..fb3a021 Binary files /dev/null and b/www/html/img/emoticons/baby-2.png differ diff --git a/www/html/img/emoticons/baby.png b/www/html/img/emoticons/baby.png new file mode 100644 index 0000000..cbdf5e7 Binary files /dev/null and b/www/html/img/emoticons/baby.png differ diff --git a/www/html/img/emoticons/bully.png b/www/html/img/emoticons/bully.png new file mode 100644 index 0000000..1dadf78 Binary files /dev/null and b/www/html/img/emoticons/bully.png differ diff --git a/www/html/img/emoticons/burglar.png b/www/html/img/emoticons/burglar.png new file mode 100644 index 0000000..21dcbdb Binary files /dev/null and b/www/html/img/emoticons/burglar.png differ diff --git a/www/html/img/emoticons/businessman.png b/www/html/img/emoticons/businessman.png new file mode 100644 index 0000000..f8fa850 Binary files /dev/null and b/www/html/img/emoticons/businessman.png differ diff --git a/www/html/img/emoticons/cheeky-1.png b/www/html/img/emoticons/cheeky-1.png new file mode 100644 index 0000000..5d3e73b Binary files /dev/null and b/www/html/img/emoticons/cheeky-1.png differ diff --git a/www/html/img/emoticons/cheeky.png b/www/html/img/emoticons/cheeky.png new file mode 100644 index 0000000..b0f17ba Binary files /dev/null and b/www/html/img/emoticons/cheeky.png differ diff --git a/www/html/img/emoticons/clown.png b/www/html/img/emoticons/clown.png new file mode 100644 index 0000000..4a93ce1 Binary files /dev/null and b/www/html/img/emoticons/clown.png differ diff --git a/www/html/img/emoticons/confused-1.png b/www/html/img/emoticons/confused-1.png new file mode 100644 index 0000000..1d95c78 Binary files /dev/null and b/www/html/img/emoticons/confused-1.png differ diff --git a/www/html/img/emoticons/confused-2.png b/www/html/img/emoticons/confused-2.png new file mode 100644 index 0000000..7c4ae3a Binary files /dev/null and b/www/html/img/emoticons/confused-2.png differ diff --git a/www/html/img/emoticons/confused-3.png b/www/html/img/emoticons/confused-3.png new file mode 100644 index 0000000..d823de2 Binary files /dev/null and b/www/html/img/emoticons/confused-3.png differ diff --git a/www/html/img/emoticons/confused.png b/www/html/img/emoticons/confused.png new file mode 100644 index 0000000..01e3572 Binary files /dev/null and b/www/html/img/emoticons/confused.png differ diff --git a/www/html/img/emoticons/creepy.png b/www/html/img/emoticons/creepy.png new file mode 100644 index 0000000..477a8b5 Binary files /dev/null and b/www/html/img/emoticons/creepy.png differ diff --git a/www/html/img/emoticons/crying-1.png b/www/html/img/emoticons/crying-1.png new file mode 100644 index 0000000..47b63e5 Binary files /dev/null and b/www/html/img/emoticons/crying-1.png differ diff --git a/www/html/img/emoticons/crying-2.png b/www/html/img/emoticons/crying-2.png new file mode 100644 index 0000000..45175f0 Binary files /dev/null and b/www/html/img/emoticons/crying-2.png differ diff --git a/www/html/img/emoticons/crying-3.png b/www/html/img/emoticons/crying-3.png new file mode 100644 index 0000000..15cbcfc Binary files /dev/null and b/www/html/img/emoticons/crying-3.png differ diff --git a/www/html/img/emoticons/crying.png b/www/html/img/emoticons/crying.png new file mode 100644 index 0000000..f57e711 Binary files /dev/null and b/www/html/img/emoticons/crying.png differ diff --git a/www/html/img/emoticons/dazed-1.png b/www/html/img/emoticons/dazed-1.png new file mode 100644 index 0000000..4c9bf83 Binary files /dev/null and b/www/html/img/emoticons/dazed-1.png differ diff --git a/www/html/img/emoticons/dazed-2.png b/www/html/img/emoticons/dazed-2.png new file mode 100644 index 0000000..e6d2b92 Binary files /dev/null and b/www/html/img/emoticons/dazed-2.png differ diff --git a/www/html/img/emoticons/dazed-3.png b/www/html/img/emoticons/dazed-3.png new file mode 100644 index 0000000..f589595 Binary files /dev/null and b/www/html/img/emoticons/dazed-3.png differ diff --git a/www/html/img/emoticons/dazed.png b/www/html/img/emoticons/dazed.png new file mode 100644 index 0000000..1ac9e9c Binary files /dev/null and b/www/html/img/emoticons/dazed.png differ diff --git a/www/html/img/emoticons/dead-1.png b/www/html/img/emoticons/dead-1.png new file mode 100644 index 0000000..96ac819 Binary files /dev/null and b/www/html/img/emoticons/dead-1.png differ diff --git a/www/html/img/emoticons/dead-2.png b/www/html/img/emoticons/dead-2.png new file mode 100644 index 0000000..f1315d3 Binary files /dev/null and b/www/html/img/emoticons/dead-2.png differ diff --git a/www/html/img/emoticons/dead-3.png b/www/html/img/emoticons/dead-3.png new file mode 100644 index 0000000..b1c3e79 Binary files /dev/null and b/www/html/img/emoticons/dead-3.png differ diff --git a/www/html/img/emoticons/dead-4.png b/www/html/img/emoticons/dead-4.png new file mode 100644 index 0000000..51cdb97 Binary files /dev/null and b/www/html/img/emoticons/dead-4.png differ diff --git a/www/html/img/emoticons/dead-5.png b/www/html/img/emoticons/dead-5.png new file mode 100644 index 0000000..148cbe1 Binary files /dev/null and b/www/html/img/emoticons/dead-5.png differ diff --git a/www/html/img/emoticons/dead-6.png b/www/html/img/emoticons/dead-6.png new file mode 100644 index 0000000..187868f Binary files /dev/null and b/www/html/img/emoticons/dead-6.png differ diff --git a/www/html/img/emoticons/dead.png b/www/html/img/emoticons/dead.png new file mode 100644 index 0000000..550561e Binary files /dev/null and b/www/html/img/emoticons/dead.png differ diff --git a/www/html/img/emoticons/desperate-1.png b/www/html/img/emoticons/desperate-1.png new file mode 100644 index 0000000..5d01937 Binary files /dev/null and b/www/html/img/emoticons/desperate-1.png differ diff --git a/www/html/img/emoticons/desperate.png b/www/html/img/emoticons/desperate.png new file mode 100644 index 0000000..5511010 Binary files /dev/null and b/www/html/img/emoticons/desperate.png differ diff --git a/www/html/img/emoticons/detective.png b/www/html/img/emoticons/detective.png new file mode 100644 index 0000000..f14d543 Binary files /dev/null and b/www/html/img/emoticons/detective.png differ diff --git a/www/html/img/emoticons/dissapointment.png b/www/html/img/emoticons/dissapointment.png new file mode 100644 index 0000000..d76d9a3 Binary files /dev/null and b/www/html/img/emoticons/dissapointment.png differ diff --git a/www/html/img/emoticons/doctor.png b/www/html/img/emoticons/doctor.png new file mode 100644 index 0000000..451f0b1 Binary files /dev/null and b/www/html/img/emoticons/doctor.png differ diff --git a/www/html/img/emoticons/drunk.png b/www/html/img/emoticons/drunk.png new file mode 100644 index 0000000..d1a33de Binary files /dev/null and b/www/html/img/emoticons/drunk.png differ diff --git a/www/html/img/emoticons/dumb.png b/www/html/img/emoticons/dumb.png new file mode 100644 index 0000000..1e371bb Binary files /dev/null and b/www/html/img/emoticons/dumb.png differ diff --git a/www/html/img/emoticons/emo-1.png b/www/html/img/emoticons/emo-1.png new file mode 100644 index 0000000..d9ded01 Binary files /dev/null and b/www/html/img/emoticons/emo-1.png differ diff --git a/www/html/img/emoticons/emo-2.png b/www/html/img/emoticons/emo-2.png new file mode 100644 index 0000000..cd945fc Binary files /dev/null and b/www/html/img/emoticons/emo-2.png differ diff --git a/www/html/img/emoticons/emo.png b/www/html/img/emoticons/emo.png new file mode 100644 index 0000000..db19f71 Binary files /dev/null and b/www/html/img/emoticons/emo.png differ diff --git a/www/html/img/emoticons/emoticon.png b/www/html/img/emoticons/emoticon.png new file mode 100644 index 0000000..503a0a2 Binary files /dev/null and b/www/html/img/emoticons/emoticon.png differ diff --git a/www/html/img/emoticons/evil.png b/www/html/img/emoticons/evil.png new file mode 100644 index 0000000..cce71d8 Binary files /dev/null and b/www/html/img/emoticons/evil.png differ diff --git a/www/html/img/emoticons/faint-1.png b/www/html/img/emoticons/faint-1.png new file mode 100644 index 0000000..31bafc3 Binary files /dev/null and b/www/html/img/emoticons/faint-1.png differ diff --git a/www/html/img/emoticons/faint.png b/www/html/img/emoticons/faint.png new file mode 100644 index 0000000..66d34c6 Binary files /dev/null and b/www/html/img/emoticons/faint.png differ diff --git a/www/html/img/emoticons/flirt-1.png b/www/html/img/emoticons/flirt-1.png new file mode 100644 index 0000000..24f00db Binary files /dev/null and b/www/html/img/emoticons/flirt-1.png differ diff --git a/www/html/img/emoticons/flirt-2.png b/www/html/img/emoticons/flirt-2.png new file mode 100644 index 0000000..df863b1 Binary files /dev/null and b/www/html/img/emoticons/flirt-2.png differ diff --git a/www/html/img/emoticons/flirt.png b/www/html/img/emoticons/flirt.png new file mode 100644 index 0000000..42cdce1 Binary files /dev/null and b/www/html/img/emoticons/flirt.png differ diff --git a/www/html/img/emoticons/flirty.png b/www/html/img/emoticons/flirty.png new file mode 100644 index 0000000..30a796c Binary files /dev/null and b/www/html/img/emoticons/flirty.png differ diff --git a/www/html/img/emoticons/gangster.png b/www/html/img/emoticons/gangster.png new file mode 100644 index 0000000..953353a Binary files /dev/null and b/www/html/img/emoticons/gangster.png differ diff --git a/www/html/img/emoticons/geek-1.png b/www/html/img/emoticons/geek-1.png new file mode 100644 index 0000000..52bf1d3 Binary files /dev/null and b/www/html/img/emoticons/geek-1.png differ diff --git a/www/html/img/emoticons/geek.png b/www/html/img/emoticons/geek.png new file mode 100644 index 0000000..7124045 Binary files /dev/null and b/www/html/img/emoticons/geek.png differ diff --git a/www/html/img/emoticons/gentleman-1.png b/www/html/img/emoticons/gentleman-1.png new file mode 100644 index 0000000..43a068b Binary files /dev/null and b/www/html/img/emoticons/gentleman-1.png differ diff --git a/www/html/img/emoticons/gentleman-2.png b/www/html/img/emoticons/gentleman-2.png new file mode 100644 index 0000000..d385848 Binary files /dev/null and b/www/html/img/emoticons/gentleman-2.png differ diff --git a/www/html/img/emoticons/gentleman-3.png b/www/html/img/emoticons/gentleman-3.png new file mode 100644 index 0000000..da84b05 Binary files /dev/null and b/www/html/img/emoticons/gentleman-3.png differ diff --git a/www/html/img/emoticons/gentleman-4.png b/www/html/img/emoticons/gentleman-4.png new file mode 100644 index 0000000..4fa0ae8 Binary files /dev/null and b/www/html/img/emoticons/gentleman-4.png differ diff --git a/www/html/img/emoticons/gentleman.png b/www/html/img/emoticons/gentleman.png new file mode 100644 index 0000000..5e1302e Binary files /dev/null and b/www/html/img/emoticons/gentleman.png differ diff --git a/www/html/img/emoticons/ginger.png b/www/html/img/emoticons/ginger.png new file mode 100644 index 0000000..ce12831 Binary files /dev/null and b/www/html/img/emoticons/ginger.png differ diff --git a/www/html/img/emoticons/girl-1.png b/www/html/img/emoticons/girl-1.png new file mode 100644 index 0000000..eaaee43 Binary files /dev/null and b/www/html/img/emoticons/girl-1.png differ diff --git a/www/html/img/emoticons/girl.png b/www/html/img/emoticons/girl.png new file mode 100644 index 0000000..277c31a Binary files /dev/null and b/www/html/img/emoticons/girl.png differ diff --git a/www/html/img/emoticons/goofy-1.png b/www/html/img/emoticons/goofy-1.png new file mode 100644 index 0000000..a680b1a Binary files /dev/null and b/www/html/img/emoticons/goofy-1.png differ diff --git a/www/html/img/emoticons/goofy-2.png b/www/html/img/emoticons/goofy-2.png new file mode 100644 index 0000000..8bf730a Binary files /dev/null and b/www/html/img/emoticons/goofy-2.png differ diff --git a/www/html/img/emoticons/goofy-3.png b/www/html/img/emoticons/goofy-3.png new file mode 100644 index 0000000..594e59d Binary files /dev/null and b/www/html/img/emoticons/goofy-3.png differ diff --git a/www/html/img/emoticons/goofy-4.png b/www/html/img/emoticons/goofy-4.png new file mode 100644 index 0000000..4cccb95 Binary files /dev/null and b/www/html/img/emoticons/goofy-4.png differ diff --git a/www/html/img/emoticons/goofy.png b/www/html/img/emoticons/goofy.png new file mode 100644 index 0000000..4d2a639 Binary files /dev/null and b/www/html/img/emoticons/goofy.png differ diff --git a/www/html/img/emoticons/grubby-1.png b/www/html/img/emoticons/grubby-1.png new file mode 100644 index 0000000..7e4d568 Binary files /dev/null and b/www/html/img/emoticons/grubby-1.png differ diff --git a/www/html/img/emoticons/grubby.png b/www/html/img/emoticons/grubby.png new file mode 100644 index 0000000..1780dd2 Binary files /dev/null and b/www/html/img/emoticons/grubby.png differ diff --git a/www/html/img/emoticons/happy-1.png b/www/html/img/emoticons/happy-1.png new file mode 100644 index 0000000..aaff83c Binary files /dev/null and b/www/html/img/emoticons/happy-1.png differ diff --git a/www/html/img/emoticons/happy-10.png b/www/html/img/emoticons/happy-10.png new file mode 100644 index 0000000..c096885 Binary files /dev/null and b/www/html/img/emoticons/happy-10.png differ diff --git a/www/html/img/emoticons/happy-11.png b/www/html/img/emoticons/happy-11.png new file mode 100644 index 0000000..add80d0 Binary files /dev/null and b/www/html/img/emoticons/happy-11.png differ diff --git a/www/html/img/emoticons/happy-12.png b/www/html/img/emoticons/happy-12.png new file mode 100644 index 0000000..b2c386f Binary files /dev/null and b/www/html/img/emoticons/happy-12.png differ diff --git a/www/html/img/emoticons/happy-13.png b/www/html/img/emoticons/happy-13.png new file mode 100644 index 0000000..3f6699c Binary files /dev/null and b/www/html/img/emoticons/happy-13.png differ diff --git a/www/html/img/emoticons/happy-14.png b/www/html/img/emoticons/happy-14.png new file mode 100644 index 0000000..33dd951 Binary files /dev/null and b/www/html/img/emoticons/happy-14.png differ diff --git a/www/html/img/emoticons/happy-15.png b/www/html/img/emoticons/happy-15.png new file mode 100644 index 0000000..2798e4a Binary files /dev/null and b/www/html/img/emoticons/happy-15.png differ diff --git a/www/html/img/emoticons/happy-16.png b/www/html/img/emoticons/happy-16.png new file mode 100644 index 0000000..54f651e Binary files /dev/null and b/www/html/img/emoticons/happy-16.png differ diff --git a/www/html/img/emoticons/happy-2.png b/www/html/img/emoticons/happy-2.png new file mode 100644 index 0000000..f5ca485 Binary files /dev/null and b/www/html/img/emoticons/happy-2.png differ diff --git a/www/html/img/emoticons/happy-3.png b/www/html/img/emoticons/happy-3.png new file mode 100644 index 0000000..70d1b1e Binary files /dev/null and b/www/html/img/emoticons/happy-3.png differ diff --git a/www/html/img/emoticons/happy-4.png b/www/html/img/emoticons/happy-4.png new file mode 100644 index 0000000..c17b45a Binary files /dev/null and b/www/html/img/emoticons/happy-4.png differ diff --git a/www/html/img/emoticons/happy-5.png b/www/html/img/emoticons/happy-5.png new file mode 100644 index 0000000..2925d57 Binary files /dev/null and b/www/html/img/emoticons/happy-5.png differ diff --git a/www/html/img/emoticons/happy-6.png b/www/html/img/emoticons/happy-6.png new file mode 100644 index 0000000..0ccb86d Binary files /dev/null and b/www/html/img/emoticons/happy-6.png differ diff --git a/www/html/img/emoticons/happy-7.png b/www/html/img/emoticons/happy-7.png new file mode 100644 index 0000000..390106b Binary files /dev/null and b/www/html/img/emoticons/happy-7.png differ diff --git a/www/html/img/emoticons/happy-8.png b/www/html/img/emoticons/happy-8.png new file mode 100644 index 0000000..33ebd27 Binary files /dev/null and b/www/html/img/emoticons/happy-8.png differ diff --git a/www/html/img/emoticons/happy-9.png b/www/html/img/emoticons/happy-9.png new file mode 100644 index 0000000..8158ec5 Binary files /dev/null and b/www/html/img/emoticons/happy-9.png differ diff --git a/www/html/img/emoticons/happy.png b/www/html/img/emoticons/happy.png new file mode 100644 index 0000000..4d93947 Binary files /dev/null and b/www/html/img/emoticons/happy.png differ diff --git a/www/html/img/emoticons/harry-potter.png b/www/html/img/emoticons/harry-potter.png new file mode 100644 index 0000000..6e1eb8d Binary files /dev/null and b/www/html/img/emoticons/harry-potter.png differ diff --git a/www/html/img/emoticons/heisenberg.png b/www/html/img/emoticons/heisenberg.png new file mode 100644 index 0000000..d7fc8a4 Binary files /dev/null and b/www/html/img/emoticons/heisenberg.png differ diff --git a/www/html/img/emoticons/hipster-1.png b/www/html/img/emoticons/hipster-1.png new file mode 100644 index 0000000..5e18b02 Binary files /dev/null and b/www/html/img/emoticons/hipster-1.png differ diff --git a/www/html/img/emoticons/hipster-2.png b/www/html/img/emoticons/hipster-2.png new file mode 100644 index 0000000..1fd4ebe Binary files /dev/null and b/www/html/img/emoticons/hipster-2.png differ diff --git a/www/html/img/emoticons/hipster.png b/www/html/img/emoticons/hipster.png new file mode 100644 index 0000000..8478a92 Binary files /dev/null and b/www/html/img/emoticons/hipster.png differ diff --git a/www/html/img/emoticons/in-love-1.png b/www/html/img/emoticons/in-love-1.png new file mode 100644 index 0000000..5874a15 Binary files /dev/null and b/www/html/img/emoticons/in-love-1.png differ diff --git a/www/html/img/emoticons/in-love-2.png b/www/html/img/emoticons/in-love-2.png new file mode 100644 index 0000000..0982215 Binary files /dev/null and b/www/html/img/emoticons/in-love-2.png differ diff --git a/www/html/img/emoticons/in-love-3.png b/www/html/img/emoticons/in-love-3.png new file mode 100644 index 0000000..cdec924 Binary files /dev/null and b/www/html/img/emoticons/in-love-3.png differ diff --git a/www/html/img/emoticons/in-love-4.png b/www/html/img/emoticons/in-love-4.png new file mode 100644 index 0000000..2fec6af Binary files /dev/null and b/www/html/img/emoticons/in-love-4.png differ diff --git a/www/html/img/emoticons/in-love-5.png b/www/html/img/emoticons/in-love-5.png new file mode 100644 index 0000000..19a53e7 Binary files /dev/null and b/www/html/img/emoticons/in-love-5.png differ diff --git a/www/html/img/emoticons/in-love-6.png b/www/html/img/emoticons/in-love-6.png new file mode 100644 index 0000000..771e1a1 Binary files /dev/null and b/www/html/img/emoticons/in-love-6.png differ diff --git a/www/html/img/emoticons/in-love.png b/www/html/img/emoticons/in-love.png new file mode 100644 index 0000000..7b7b0b3 Binary files /dev/null and b/www/html/img/emoticons/in-love.png differ diff --git a/www/html/img/emoticons/japan.png b/www/html/img/emoticons/japan.png new file mode 100644 index 0000000..f8c8b30 Binary files /dev/null and b/www/html/img/emoticons/japan.png differ diff --git a/www/html/img/emoticons/jew.png b/www/html/img/emoticons/jew.png new file mode 100644 index 0000000..79963a3 Binary files /dev/null and b/www/html/img/emoticons/jew.png differ diff --git a/www/html/img/emoticons/joyful-1.png b/www/html/img/emoticons/joyful-1.png new file mode 100644 index 0000000..c86ff3b Binary files /dev/null and b/www/html/img/emoticons/joyful-1.png differ diff --git a/www/html/img/emoticons/joyful-2.png b/www/html/img/emoticons/joyful-2.png new file mode 100644 index 0000000..ae72f02 Binary files /dev/null and b/www/html/img/emoticons/joyful-2.png differ diff --git a/www/html/img/emoticons/joyful.png b/www/html/img/emoticons/joyful.png new file mode 100644 index 0000000..e543251 Binary files /dev/null and b/www/html/img/emoticons/joyful.png differ diff --git a/www/html/img/emoticons/kiss-1.png b/www/html/img/emoticons/kiss-1.png new file mode 100644 index 0000000..5007b92 Binary files /dev/null and b/www/html/img/emoticons/kiss-1.png differ diff --git a/www/html/img/emoticons/kiss-2.png b/www/html/img/emoticons/kiss-2.png new file mode 100644 index 0000000..d024bd1 Binary files /dev/null and b/www/html/img/emoticons/kiss-2.png differ diff --git a/www/html/img/emoticons/kiss-3.png b/www/html/img/emoticons/kiss-3.png new file mode 100644 index 0000000..95ef8c1 Binary files /dev/null and b/www/html/img/emoticons/kiss-3.png differ diff --git a/www/html/img/emoticons/kiss-4.png b/www/html/img/emoticons/kiss-4.png new file mode 100644 index 0000000..82e65c9 Binary files /dev/null and b/www/html/img/emoticons/kiss-4.png differ diff --git a/www/html/img/emoticons/kiss.png b/www/html/img/emoticons/kiss.png new file mode 100644 index 0000000..02b7455 Binary files /dev/null and b/www/html/img/emoticons/kiss.png differ diff --git a/www/html/img/emoticons/laughing-1.png b/www/html/img/emoticons/laughing-1.png new file mode 100644 index 0000000..65fb9ac Binary files /dev/null and b/www/html/img/emoticons/laughing-1.png differ diff --git a/www/html/img/emoticons/laughing-2.png b/www/html/img/emoticons/laughing-2.png new file mode 100644 index 0000000..d9babcf Binary files /dev/null and b/www/html/img/emoticons/laughing-2.png differ diff --git a/www/html/img/emoticons/laughing-3.png b/www/html/img/emoticons/laughing-3.png new file mode 100644 index 0000000..441c478 Binary files /dev/null and b/www/html/img/emoticons/laughing-3.png differ diff --git a/www/html/img/emoticons/laughing.png b/www/html/img/emoticons/laughing.png new file mode 100644 index 0000000..5dec7c1 Binary files /dev/null and b/www/html/img/emoticons/laughing.png differ diff --git a/www/html/img/emoticons/listening.png b/www/html/img/emoticons/listening.png new file mode 100644 index 0000000..12b8b48 Binary files /dev/null and b/www/html/img/emoticons/listening.png differ diff --git a/www/html/img/emoticons/love.png b/www/html/img/emoticons/love.png new file mode 100644 index 0000000..e2078eb Binary files /dev/null and b/www/html/img/emoticons/love.png differ diff --git a/www/html/img/emoticons/manly.png b/www/html/img/emoticons/manly.png new file mode 100644 index 0000000..15102dc Binary files /dev/null and b/www/html/img/emoticons/manly.png differ diff --git a/www/html/img/emoticons/miserly-1.png b/www/html/img/emoticons/miserly-1.png new file mode 100644 index 0000000..78783cb Binary files /dev/null and b/www/html/img/emoticons/miserly-1.png differ diff --git a/www/html/img/emoticons/miserly.png b/www/html/img/emoticons/miserly.png new file mode 100644 index 0000000..41c6871 Binary files /dev/null and b/www/html/img/emoticons/miserly.png differ diff --git a/www/html/img/emoticons/nerd-1.png b/www/html/img/emoticons/nerd-1.png new file mode 100644 index 0000000..eb850eb Binary files /dev/null and b/www/html/img/emoticons/nerd-1.png differ diff --git a/www/html/img/emoticons/nerd-2.png b/www/html/img/emoticons/nerd-2.png new file mode 100644 index 0000000..1ed02aa Binary files /dev/null and b/www/html/img/emoticons/nerd-2.png differ diff --git a/www/html/img/emoticons/nerd-3.png b/www/html/img/emoticons/nerd-3.png new file mode 100644 index 0000000..ebf9f12 Binary files /dev/null and b/www/html/img/emoticons/nerd-3.png differ diff --git a/www/html/img/emoticons/nerd-4.png b/www/html/img/emoticons/nerd-4.png new file mode 100644 index 0000000..fc18093 Binary files /dev/null and b/www/html/img/emoticons/nerd-4.png differ diff --git a/www/html/img/emoticons/nerd.png b/www/html/img/emoticons/nerd.png new file mode 100644 index 0000000..bee5b1f Binary files /dev/null and b/www/html/img/emoticons/nerd.png differ diff --git a/www/html/img/emoticons/ninja.png b/www/html/img/emoticons/ninja.png new file mode 100644 index 0000000..1380f53 Binary files /dev/null and b/www/html/img/emoticons/ninja.png differ diff --git a/www/html/img/emoticons/pirate-1.png b/www/html/img/emoticons/pirate-1.png new file mode 100644 index 0000000..db0f469 Binary files /dev/null and b/www/html/img/emoticons/pirate-1.png differ diff --git a/www/html/img/emoticons/pirate-2.png b/www/html/img/emoticons/pirate-2.png new file mode 100644 index 0000000..2f5d5cf Binary files /dev/null and b/www/html/img/emoticons/pirate-2.png differ diff --git a/www/html/img/emoticons/pirate.png b/www/html/img/emoticons/pirate.png new file mode 100644 index 0000000..9b5cc2c Binary files /dev/null and b/www/html/img/emoticons/pirate.png differ diff --git a/www/html/img/emoticons/punk-1.png b/www/html/img/emoticons/punk-1.png new file mode 100644 index 0000000..5e590db Binary files /dev/null and b/www/html/img/emoticons/punk-1.png differ diff --git a/www/html/img/emoticons/punk-2.png b/www/html/img/emoticons/punk-2.png new file mode 100644 index 0000000..81dcbd9 Binary files /dev/null and b/www/html/img/emoticons/punk-2.png differ diff --git a/www/html/img/emoticons/punk.png b/www/html/img/emoticons/punk.png new file mode 100644 index 0000000..0df310b Binary files /dev/null and b/www/html/img/emoticons/punk.png differ diff --git a/www/html/img/emoticons/rapper.png b/www/html/img/emoticons/rapper.png new file mode 100644 index 0000000..5f3af8b Binary files /dev/null and b/www/html/img/emoticons/rapper.png differ diff --git a/www/html/img/emoticons/relieved.png b/www/html/img/emoticons/relieved.png new file mode 100644 index 0000000..ff9359d Binary files /dev/null and b/www/html/img/emoticons/relieved.png differ diff --git a/www/html/img/emoticons/rich-1.png b/www/html/img/emoticons/rich-1.png new file mode 100644 index 0000000..1aa872c Binary files /dev/null and b/www/html/img/emoticons/rich-1.png differ diff --git a/www/html/img/emoticons/rich-2.png b/www/html/img/emoticons/rich-2.png new file mode 100644 index 0000000..fad3462 Binary files /dev/null and b/www/html/img/emoticons/rich-2.png differ diff --git a/www/html/img/emoticons/rich.png b/www/html/img/emoticons/rich.png new file mode 100644 index 0000000..48e78df Binary files /dev/null and b/www/html/img/emoticons/rich.png differ diff --git a/www/html/img/emoticons/rockstar.png b/www/html/img/emoticons/rockstar.png new file mode 100644 index 0000000..1976989 Binary files /dev/null and b/www/html/img/emoticons/rockstar.png differ diff --git a/www/html/img/emoticons/sad-1.png b/www/html/img/emoticons/sad-1.png new file mode 100644 index 0000000..4c45781 Binary files /dev/null and b/www/html/img/emoticons/sad-1.png differ diff --git a/www/html/img/emoticons/sad-2.png b/www/html/img/emoticons/sad-2.png new file mode 100644 index 0000000..997c19b Binary files /dev/null and b/www/html/img/emoticons/sad-2.png differ diff --git a/www/html/img/emoticons/sad-3.png b/www/html/img/emoticons/sad-3.png new file mode 100644 index 0000000..55bb651 Binary files /dev/null and b/www/html/img/emoticons/sad-3.png differ diff --git a/www/html/img/emoticons/sad-4.png b/www/html/img/emoticons/sad-4.png new file mode 100644 index 0000000..5e455a4 Binary files /dev/null and b/www/html/img/emoticons/sad-4.png differ diff --git a/www/html/img/emoticons/sad-5.png b/www/html/img/emoticons/sad-5.png new file mode 100644 index 0000000..7fcfd6e Binary files /dev/null and b/www/html/img/emoticons/sad-5.png differ diff --git a/www/html/img/emoticons/sad-6.png b/www/html/img/emoticons/sad-6.png new file mode 100644 index 0000000..bc4a357 Binary files /dev/null and b/www/html/img/emoticons/sad-6.png differ diff --git a/www/html/img/emoticons/sad.png b/www/html/img/emoticons/sad.png new file mode 100644 index 0000000..07450ea Binary files /dev/null and b/www/html/img/emoticons/sad.png differ diff --git a/www/html/img/emoticons/scared-1.png b/www/html/img/emoticons/scared-1.png new file mode 100644 index 0000000..b763075 Binary files /dev/null and b/www/html/img/emoticons/scared-1.png differ diff --git a/www/html/img/emoticons/scared-2.png b/www/html/img/emoticons/scared-2.png new file mode 100644 index 0000000..a02c0c1 Binary files /dev/null and b/www/html/img/emoticons/scared-2.png differ diff --git a/www/html/img/emoticons/scared-3.png b/www/html/img/emoticons/scared-3.png new file mode 100644 index 0000000..e0794b7 Binary files /dev/null and b/www/html/img/emoticons/scared-3.png differ diff --git a/www/html/img/emoticons/scared.png b/www/html/img/emoticons/scared.png new file mode 100644 index 0000000..07f9abb Binary files /dev/null and b/www/html/img/emoticons/scared.png differ diff --git a/www/html/img/emoticons/sceptic-1.png b/www/html/img/emoticons/sceptic-1.png new file mode 100644 index 0000000..2c1712b Binary files /dev/null and b/www/html/img/emoticons/sceptic-1.png differ diff --git a/www/html/img/emoticons/sceptic-2.png b/www/html/img/emoticons/sceptic-2.png new file mode 100644 index 0000000..900af62 Binary files /dev/null and b/www/html/img/emoticons/sceptic-2.png differ diff --git a/www/html/img/emoticons/sceptic-3.png b/www/html/img/emoticons/sceptic-3.png new file mode 100644 index 0000000..4c63234 Binary files /dev/null and b/www/html/img/emoticons/sceptic-3.png differ diff --git a/www/html/img/emoticons/sceptic-4.png b/www/html/img/emoticons/sceptic-4.png new file mode 100644 index 0000000..c2b0290 Binary files /dev/null and b/www/html/img/emoticons/sceptic-4.png differ diff --git a/www/html/img/emoticons/sceptic-5.png b/www/html/img/emoticons/sceptic-5.png new file mode 100644 index 0000000..a834932 Binary files /dev/null and b/www/html/img/emoticons/sceptic-5.png differ diff --git a/www/html/img/emoticons/sceptic-6.png b/www/html/img/emoticons/sceptic-6.png new file mode 100644 index 0000000..f721502 Binary files /dev/null and b/www/html/img/emoticons/sceptic-6.png differ diff --git a/www/html/img/emoticons/sceptic-7.png b/www/html/img/emoticons/sceptic-7.png new file mode 100644 index 0000000..eb16766 Binary files /dev/null and b/www/html/img/emoticons/sceptic-7.png differ diff --git a/www/html/img/emoticons/sceptic.png b/www/html/img/emoticons/sceptic.png new file mode 100644 index 0000000..29dbf39 Binary files /dev/null and b/www/html/img/emoticons/sceptic.png differ diff --git a/www/html/img/emoticons/secret.png b/www/html/img/emoticons/secret.png new file mode 100644 index 0000000..4d8af27 Binary files /dev/null and b/www/html/img/emoticons/secret.png differ diff --git a/www/html/img/emoticons/shocked-1.png b/www/html/img/emoticons/shocked-1.png new file mode 100644 index 0000000..bcef6b3 Binary files /dev/null and b/www/html/img/emoticons/shocked-1.png differ diff --git a/www/html/img/emoticons/shocked-2.png b/www/html/img/emoticons/shocked-2.png new file mode 100644 index 0000000..92da83f Binary files /dev/null and b/www/html/img/emoticons/shocked-2.png differ diff --git a/www/html/img/emoticons/shocked-3.png b/www/html/img/emoticons/shocked-3.png new file mode 100644 index 0000000..76d157e Binary files /dev/null and b/www/html/img/emoticons/shocked-3.png differ diff --git a/www/html/img/emoticons/shocked.png b/www/html/img/emoticons/shocked.png new file mode 100644 index 0000000..3a5b38a Binary files /dev/null and b/www/html/img/emoticons/shocked.png differ diff --git a/www/html/img/emoticons/sick-1.png b/www/html/img/emoticons/sick-1.png new file mode 100644 index 0000000..0546c96 Binary files /dev/null and b/www/html/img/emoticons/sick-1.png differ diff --git a/www/html/img/emoticons/sick-2.png b/www/html/img/emoticons/sick-2.png new file mode 100644 index 0000000..e7cbfeb Binary files /dev/null and b/www/html/img/emoticons/sick-2.png differ diff --git a/www/html/img/emoticons/sick-3.png b/www/html/img/emoticons/sick-3.png new file mode 100644 index 0000000..888f0aa Binary files /dev/null and b/www/html/img/emoticons/sick-3.png differ diff --git a/www/html/img/emoticons/sick-4.png b/www/html/img/emoticons/sick-4.png new file mode 100644 index 0000000..bf11b6c Binary files /dev/null and b/www/html/img/emoticons/sick-4.png differ diff --git a/www/html/img/emoticons/sick.png b/www/html/img/emoticons/sick.png new file mode 100644 index 0000000..77c9b7f Binary files /dev/null and b/www/html/img/emoticons/sick.png differ diff --git a/www/html/img/emoticons/silent.png b/www/html/img/emoticons/silent.png new file mode 100644 index 0000000..0355f2f Binary files /dev/null and b/www/html/img/emoticons/silent.png differ diff --git a/www/html/img/emoticons/skeleton.png b/www/html/img/emoticons/skeleton.png new file mode 100644 index 0000000..3b69b24 Binary files /dev/null and b/www/html/img/emoticons/skeleton.png differ diff --git a/www/html/img/emoticons/smile.png b/www/html/img/emoticons/smile.png new file mode 100644 index 0000000..9316c35 Binary files /dev/null and b/www/html/img/emoticons/smile.png differ diff --git a/www/html/img/emoticons/smiling-1.png b/www/html/img/emoticons/smiling-1.png new file mode 100644 index 0000000..ee20b43 Binary files /dev/null and b/www/html/img/emoticons/smiling-1.png differ diff --git a/www/html/img/emoticons/smiling.png b/www/html/img/emoticons/smiling.png new file mode 100644 index 0000000..3e4b545 Binary files /dev/null and b/www/html/img/emoticons/smiling.png differ diff --git a/www/html/img/emoticons/smoked.png b/www/html/img/emoticons/smoked.png new file mode 100644 index 0000000..ffc2b7c Binary files /dev/null and b/www/html/img/emoticons/smoked.png differ diff --git a/www/html/img/emoticons/smug-1.png b/www/html/img/emoticons/smug-1.png new file mode 100644 index 0000000..06b2726 Binary files /dev/null and b/www/html/img/emoticons/smug-1.png differ diff --git a/www/html/img/emoticons/smug-2.png b/www/html/img/emoticons/smug-2.png new file mode 100644 index 0000000..f31cd92 Binary files /dev/null and b/www/html/img/emoticons/smug-2.png differ diff --git a/www/html/img/emoticons/smug-3.png b/www/html/img/emoticons/smug-3.png new file mode 100644 index 0000000..8029499 Binary files /dev/null and b/www/html/img/emoticons/smug-3.png differ diff --git a/www/html/img/emoticons/smug-4.png b/www/html/img/emoticons/smug-4.png new file mode 100644 index 0000000..12230da Binary files /dev/null and b/www/html/img/emoticons/smug-4.png differ diff --git a/www/html/img/emoticons/smug-5.png b/www/html/img/emoticons/smug-5.png new file mode 100644 index 0000000..ac9451b Binary files /dev/null and b/www/html/img/emoticons/smug-5.png differ diff --git a/www/html/img/emoticons/smug-6.png b/www/html/img/emoticons/smug-6.png new file mode 100644 index 0000000..0622196 Binary files /dev/null and b/www/html/img/emoticons/smug-6.png differ diff --git a/www/html/img/emoticons/smug.png b/www/html/img/emoticons/smug.png new file mode 100644 index 0000000..9781ba4 Binary files /dev/null and b/www/html/img/emoticons/smug.png differ diff --git a/www/html/img/emoticons/sporty.png b/www/html/img/emoticons/sporty.png new file mode 100644 index 0000000..8d8eff5 Binary files /dev/null and b/www/html/img/emoticons/sporty.png differ diff --git a/www/html/img/emoticons/stunned.png b/www/html/img/emoticons/stunned.png new file mode 100644 index 0000000..d8c17dc Binary files /dev/null and b/www/html/img/emoticons/stunned.png differ diff --git a/www/html/img/emoticons/superhero-1.png b/www/html/img/emoticons/superhero-1.png new file mode 100644 index 0000000..63d0250 Binary files /dev/null and b/www/html/img/emoticons/superhero-1.png differ diff --git a/www/html/img/emoticons/superhero-2.png b/www/html/img/emoticons/superhero-2.png new file mode 100644 index 0000000..756e7dc Binary files /dev/null and b/www/html/img/emoticons/superhero-2.png differ diff --git a/www/html/img/emoticons/superhero-3.png b/www/html/img/emoticons/superhero-3.png new file mode 100644 index 0000000..1efcf64 Binary files /dev/null and b/www/html/img/emoticons/superhero-3.png differ diff --git a/www/html/img/emoticons/superhero-4.png b/www/html/img/emoticons/superhero-4.png new file mode 100644 index 0000000..4f8975b Binary files /dev/null and b/www/html/img/emoticons/superhero-4.png differ diff --git a/www/html/img/emoticons/superhero.png b/www/html/img/emoticons/superhero.png new file mode 100644 index 0000000..0fc1bc2 Binary files /dev/null and b/www/html/img/emoticons/superhero.png differ diff --git a/www/html/img/emoticons/surprised-1.png b/www/html/img/emoticons/surprised-1.png new file mode 100644 index 0000000..5b7cf8f Binary files /dev/null and b/www/html/img/emoticons/surprised-1.png differ diff --git a/www/html/img/emoticons/surprised.png b/www/html/img/emoticons/surprised.png new file mode 100644 index 0000000..e55a30f Binary files /dev/null and b/www/html/img/emoticons/surprised.png differ diff --git a/www/html/img/emoticons/thinking.png b/www/html/img/emoticons/thinking.png new file mode 100644 index 0000000..8dbedc1 Binary files /dev/null and b/www/html/img/emoticons/thinking.png differ diff --git a/www/html/img/emoticons/tired-1.png b/www/html/img/emoticons/tired-1.png new file mode 100644 index 0000000..cd74791 Binary files /dev/null and b/www/html/img/emoticons/tired-1.png differ diff --git a/www/html/img/emoticons/tired-2.png b/www/html/img/emoticons/tired-2.png new file mode 100644 index 0000000..cf10865 Binary files /dev/null and b/www/html/img/emoticons/tired-2.png differ diff --git a/www/html/img/emoticons/tired-3.png b/www/html/img/emoticons/tired-3.png new file mode 100644 index 0000000..975e802 Binary files /dev/null and b/www/html/img/emoticons/tired-3.png differ diff --git a/www/html/img/emoticons/tired.png b/www/html/img/emoticons/tired.png new file mode 100644 index 0000000..6073a88 Binary files /dev/null and b/www/html/img/emoticons/tired.png differ diff --git a/www/html/img/emoticons/tough-1.png b/www/html/img/emoticons/tough-1.png new file mode 100644 index 0000000..c049b5e Binary files /dev/null and b/www/html/img/emoticons/tough-1.png differ diff --git a/www/html/img/emoticons/tough.png b/www/html/img/emoticons/tough.png new file mode 100644 index 0000000..0a2a574 Binary files /dev/null and b/www/html/img/emoticons/tough.png differ diff --git a/www/html/img/emoticons/trendy.png b/www/html/img/emoticons/trendy.png new file mode 100644 index 0000000..93b1173 Binary files /dev/null and b/www/html/img/emoticons/trendy.png differ diff --git a/www/html/img/emoticons/vampire-1.png b/www/html/img/emoticons/vampire-1.png new file mode 100644 index 0000000..279b12b Binary files /dev/null and b/www/html/img/emoticons/vampire-1.png differ diff --git a/www/html/img/emoticons/vampire.png b/www/html/img/emoticons/vampire.png new file mode 100644 index 0000000..c3c8b8a Binary files /dev/null and b/www/html/img/emoticons/vampire.png differ diff --git a/www/html/img/emoticons/wink-1.png b/www/html/img/emoticons/wink-1.png new file mode 100644 index 0000000..a8932c8 Binary files /dev/null and b/www/html/img/emoticons/wink-1.png differ diff --git a/www/html/img/emoticons/wink-2.png b/www/html/img/emoticons/wink-2.png new file mode 100644 index 0000000..5009f83 Binary files /dev/null and b/www/html/img/emoticons/wink-2.png differ diff --git a/www/html/img/emoticons/wink.png b/www/html/img/emoticons/wink.png new file mode 100644 index 0000000..538b75d Binary files /dev/null and b/www/html/img/emoticons/wink.png differ diff --git a/www/html/img/emoticons/winking-1.png b/www/html/img/emoticons/winking-1.png new file mode 100644 index 0000000..dabdaa0 Binary files /dev/null and b/www/html/img/emoticons/winking-1.png differ diff --git a/www/html/img/emoticons/winking.png b/www/html/img/emoticons/winking.png new file mode 100644 index 0000000..d1c0f6d Binary files /dev/null and b/www/html/img/emoticons/winking.png differ diff --git a/www/html/img/emoticons/yawning-1.png b/www/html/img/emoticons/yawning-1.png new file mode 100644 index 0000000..a3e729f Binary files /dev/null and b/www/html/img/emoticons/yawning-1.png differ diff --git a/www/html/img/emoticons/yawning-2.png b/www/html/img/emoticons/yawning-2.png new file mode 100644 index 0000000..acb73ea Binary files /dev/null and b/www/html/img/emoticons/yawning-2.png differ diff --git a/www/html/img/emoticons/yawning-3.png b/www/html/img/emoticons/yawning-3.png new file mode 100644 index 0000000..2d391d8 Binary files /dev/null and b/www/html/img/emoticons/yawning-3.png differ diff --git a/www/html/img/emoticons/yawning.png b/www/html/img/emoticons/yawning.png new file mode 100644 index 0000000..bbd7e71 Binary files /dev/null and b/www/html/img/emoticons/yawning.png differ diff --git a/www/html/img/emoticons/yelling.png b/www/html/img/emoticons/yelling.png new file mode 100644 index 0000000..25096f7 Binary files /dev/null and b/www/html/img/emoticons/yelling.png differ diff --git a/www/html/img/emoticons/zombie.png b/www/html/img/emoticons/zombie.png new file mode 100644 index 0000000..262d336 Binary files /dev/null and b/www/html/img/emoticons/zombie.png differ diff --git a/www/html/img/hats/busy.png b/www/html/img/hats/busy.png new file mode 100644 index 0000000..fc2d4bb Binary files /dev/null and b/www/html/img/hats/busy.png differ diff --git a/www/html/img/logos/Roblox.png b/www/html/img/logos/Roblox.png new file mode 100644 index 0000000..2b2ff32 Binary files /dev/null and b/www/html/img/logos/Roblox.png differ diff --git a/www/html/img/places/baseplate.png b/www/html/img/places/baseplate.png new file mode 100644 index 0000000..1f6c7b9 Binary files /dev/null and b/www/html/img/places/baseplate.png differ diff --git a/www/html/img/templates/baseplate.png b/www/html/img/templates/baseplate.png new file mode 100644 index 0000000..7b60940 Binary files /dev/null and b/www/html/img/templates/baseplate.png differ diff --git a/www/html/img/templates/ffa.png b/www/html/img/templates/ffa.png new file mode 100644 index 0000000..13a7db2 Binary files /dev/null and b/www/html/img/templates/ffa.png differ diff --git a/www/html/img/templates/placeholder.png b/www/html/img/templates/placeholder.png new file mode 100644 index 0000000..d791814 Binary files /dev/null and b/www/html/img/templates/placeholder.png differ diff --git a/www/html/img/templates/tdm.png b/www/html/img/templates/tdm.png new file mode 100644 index 0000000..2f9b5f3 Binary files /dev/null and b/www/html/img/templates/tdm.png differ diff --git a/www/html/img/templates/terrain.png b/www/html/img/templates/terrain.png new file mode 100644 index 0000000..9d6d45a Binary files /dev/null and b/www/html/img/templates/terrain.png differ diff --git a/www/index.php b/www/index.php new file mode 100644 index 0000000..2df88b4 --- /dev/null +++ b/www/index.php @@ -0,0 +1,62 @@ + + + + + <?php echo config::getName();?> + + + + + + + + +
    +
    +
    +

    Welcome to !

    +

    Forum, chat and play games with other players and make friends

    +

    Customize your character with items found in our catalog

    +
    +
    + +

    Create an account

    +

    If you are new to , you can sign up by filling in the form below

    +
    + + + + +
    + +
    +
    +
    + + +