Invision Power Services, Inc. * @copyright (c) Invision Power Services, Inc. * @license https://www.invisioncommunity.com/legal/standards/ * @package Invision Community * @subpackage Nexus * @since 01 Apr 2015 */ use IPS\Dispatcher\External; use IPS\nexus\Customer; use IPS\nexus\Purchase\LicenseKey; use IPS\Output; use IPS\Request; use const IPS\NEXUS_LKEY_API_ALLOW_IP_OVERRIDE; use const IPS\NEXUS_LKEY_API_CHECK_IP; use const IPS\NEXUS_LKEY_API_DISABLE; define('REPORT_EXCEPTIONS', TRUE); require_once '../../../../init.php'; External::i(); if ( NEXUS_LKEY_API_ALLOW_IP_OVERRIDE ) { $_SERVER['REMOTE_ADDR'] = isset( Request::i()->ip ) ? Request::i()->ip : $_SERVER['REMOTE_ADDR']; } /** * API Exception */ class ApiException extends Exception { } /** * API class to verify license keys */ class Api { /** * Output Error * * @param int $code Status code * @param string $message Status message * @return void */ public function error( int $code, string $message ) : void { Output::setCacheTime( false ); Output::i()->sendOutput( json_encode( array( 'errorCode' => $code, 'errorMessage' => $message ) ), 400, 'application/json' ); } /** * Get key * * @param bool $validateIdentifier Should the identifier be validated? * @return LicenseKey * @throws ApiException */ protected function getKey( bool $validateIdentifier=TRUE ) : LicenseKey { try { $key = LicenseKey::load( isset( Request::i()->key ) ? Request::i()->key : NULL ); if ( $key->key !== Request::i()->key ) { throw new ApiException( 'BAD_KEY_OR_ID', 105 ); } if ( $validateIdentifier and $identifier = $this->getIdentifier( $key ) and ( !isset( Request::i()->identifier ) or $identifier != Request::i()->identifier ) ) { throw new ApiException( 'BAD_KEY_OR_ID', 101 ); } if ( !$key->active ) { throw new ApiException( 'INACTIVE', 102 ); } if ( $key->purchase->cancelled ) { throw new ApiException( 'INACTIVE', 103 ); } if ( !$key->purchase->active ) { throw new ApiException( 'INACTIVE', 104 ); } return $key; } catch (OutOfRangeException ) { throw new ApiException( 'BAD_KEY_OR_ID', 101 ); } } /** * Get identifier * * @param LicenseKey $key The license key * @return string|NULL */ protected function getIdentifier( LicenseKey $key ) : string|null { $identifier = NULL; switch ( $key->identifier ) { case 'name': $identifier = Customer::load( $key->member )->cm_name; break; case 'email': $identifier = Customer::load( $key->member )->email; break; case 'username': $identifier = Customer::load( $key->member )->name; break; default: $cfields = $key->purchase->custom_fields; if ( isset( $cfields[ $key->identifier ] ) ) { $identifier = $cfields[ $key->identifier ]; } break; } return $identifier; } /** * Activate * * @return void * @throws ApiException */ public function activate() : void { $key = $this->getKey( FALSE ); if ( $key->max_uses != -1 and $key->uses >= $key->max_uses ) { throw new ApiException( 'MAX_USES', 201 ); } $identifier = $this->getIdentifier( $key ); $providedIdentifier = isset( Request::i()->identifier ) ? Request::i()->identifier : NULL; if ( isset( Request::i()->setIdentifier ) and Request::i()->setIdentifier ) { if ( $identifier != $providedIdentifier ) { if ( $identifier or in_array( $key->identifier, array( 'name', 'email', 'username' ) ) ) { throw new ApiException( 'BAD_KEY_OR_ID', 101 ); } else { $cfields = $key->purchase->custom_fields; $cfields[ $key->identifier ] = $providedIdentifier; $key->purchase->custom_fields = $cfields; $key->purchase->save(); } } } elseif( $identifier != $providedIdentifier ) { throw new ApiException( 'BAD_KEY_OR_ID', 101 ); } if ( defined( 'NEXUS_LKEY_API_ALLOW_IP_OVERRIDE' ) ) { $ip = $this->params['ip'] ?: $_SERVER['REMOTE_ADDR']; } else { $ip = $_SERVER['REMOTE_ADDR']; } $activateData = $key->activate_data; $k = empty( $activateData ) ? 0 : max( array_keys( $activateData ) ); $k++; $activateData[ $k ] = array( 'activated' => time(), 'ip' => $ip, 'last_checked' => 0, 'extra' => isset( Request::i()->extra ) ? json_decode( Request::i()->extra ) : NULL, ); $key->activate_data = $activateData; $key->uses++; $key->save(); Customer::load( $key->member )->log( 'lkey', array( 'type' => 'activated', 'key' => $key->key, 'ps_id' => $key->purchase->id ), FALSE ); Output::setCacheTime( false ); Output::i()->sendOutput( json_encode( array( 'response' => 'OKAY', 'usage_id' => $k ) ), 200, 'application/json' ); } /** * Check * * @return void * @throws ApiException */ public function check() : void { try { $key = $this->getKey(); } catch ( ApiException $e ) { switch ( $e->getCode() ) { case 102: case 103: Output::i()->sendOutput( json_encode( array( 'status' => 'INACTIVE' ) ), 200, 'application/json', Output::getCacheHeaders( time(), 360 ) ); case 104: Output::i()->sendOutput( json_encode( array( 'status' => 'EXPIRED' ) ), 200, 'application/json', Output::getCacheHeaders( time(), 360 ) ); default: throw $e; } } $activateData = $key->activate_data; if ( !isset( Request::i()->usage_id ) or !isset( $activateData[ Request::i()->usage_id ] ) ) { throw new ApiException( 'BAD_USAGE_ID', 303 ); } if ( NEXUS_LKEY_API_CHECK_IP and $activateData[ Request::i()->usage_id ]['ip'] != $_SERVER['REMOTE_ADDR'] ) { throw new ApiException( 'BAD_IP', 304 ); } $activateData[ Request::i()->usage_id ]['last_checked'] = time(); $key->activate_data = $activateData; $key->save(); Output::i()->sendOutput( json_encode( array( 'status' => 'ACTIVE', 'uses' => $key->uses, 'max_uses' => $key->max_uses ) ), 200, 'application/json', Output::getCacheHeaders( time(), 360 ) ); } /** * Get Information * * @return void * @throws ApiException */ public function info() : void { $key = $this->getKey(); $children = array(); foreach ( $key->purchase->children( NULL ) as $child ) { $children[ $child->id ] = array( 'id' => $child->id, 'name' => $child->name, 'app' => $child->app, 'type' => $child->type, 'item_id' => $child->item_id, 'active' => $child->cancelled ? 0 : $child->active, 'start' => $child->start, 'expire' => $child->expire, 'lkey' => $child->licenseKey() ? $child->licenseKey()->key : NULL, ); } Output::i()->sendOutput( json_encode( array( 'key' => $key->key, 'identifier' => $this->getIdentifier( $key ), 'generated' => $key->generated->getTimestamp(), 'expires' => $key->purchase->expire ? $key->purchase->expire->getTimestamp() : NULL, 'usage_data' => $key->activate_data, 'purchase_id' => $key->purchase->id, 'purchase_name' => $key->purchase->name, 'purchase_pkg' => $key->purchase->item_id, 'purchase_active' => $key->purchase->cancelled ? FALSE : $key->purchase->active, 'purchase_start' => $key->purchase->start->getTimestamp(), 'purchase_expire' => $key->purchase->expire ? $key->purchase->expire->getTimestamp() : NULL, 'purchase_children' => $children, 'customer_name' => Customer::load( $key->member )->cm_name, 'customer_email' => Customer::load( $key->member )->email, 'uses' => $key->uses, 'max_uses' => $key->max_uses ) ), 200, 'application/json', Output::getCacheHeaders( time(), 360 ) ); } /** * Update extra information * * @return void * @throws ApiException */ public function updateExtra() : void { $key = $this->getKey(); $activateData = $key->activate_data; if ( !isset( Request::i()->usage_id ) or !isset( $activateData[ Request::i()->usage_id ] ) ) { throw new ApiException( 'BAD_USAGE_ID', 303 ); } if ( NEXUS_LKEY_API_CHECK_IP and $activateData[ Request::i()->usage_id ]['ip'] != $_SERVER['REMOTE_ADDR'] ) { throw new ApiException( 'BAD_IP', 304 ); } $activateData[ Request::i()->usage_id ]['extra'] = isset( Request::i()->extra ) ? json_decode( Request::i()->extra ) : NULL; $key->activate_data = $activateData; $key->save(); Output::setCacheTime( false ); Output::i()->sendOutput( json_encode( array( 'status' => 'OKAY' ) ), 200, 'application/json' ); } } $api = new Api; if ( !NEXUS_LKEY_API_DISABLE ) { foreach ( array( 'activate', 'check', 'info', 'updateExtra' ) as $k ) { if ( isset( Request::i()->$k ) ) { try { $api->$k(); } catch ( ApiException $e ) { $api->error( $e->getCode(), $e->getMessage() ); } catch ( Exception ) { $api->error( 0, 'INTERNAL_ERROR' ); } } } $api->error( 0, 'BAD_METHOD' ); } else { $api->error( 0, 'API_DISABLED' ); }