get_info( $id, $message ); if ( $info ){ return $info; } } return FALSE; } function ptcp_get_pubkey( $id, $message = NULL, $useKeySent = FALSE ){ if ( $useKeySent && $message && ( $key = $message->get('Public-Key') ) ){ return $key; } $info = ptcp_get_id_info( $id, $message ); return isset( $info['public_key'] ) ? $info['public_key'] : FALSE; } function ptcp_is_in_list( $ptcpId, $ptcpList ){ global $ServerData; if ( ! strpos( $ptcpId, '@' ) ){ $ptcpId .= '@' . $ServerData['name']; } foreach ( $ptcpList as $key => $id ){ if ( ! strpos( $id, '@' ) ){ $ptcpList[ $key ] .= '@' . $ServerData['name']; } } return in_array( $ptcpId, $ptcpList ); } function ptcp_is_authorized( $ptcpId, $messageType, $object = NULL ){ global $AuthCheckers; foreach ( $AuthCheckers as $classOrObject ){ $result = call_user_func( [ $classOrObject, 'is_authorized' ], $ptcpId, $messageType, $object ); if ( $result !== NULL ){ return $result; } } return ptcp_get_id_info( $ptcpId ) != FALSE; } function ptcp_handle_message( $message, $receipt ){ global $MessageHandlers; foreach ( $MessageHandlers as $classOrObject ){ $methodName = 'handle_' . str_replace( '-', '_', $message->get('Message-Type') ); if ( ! method_exists( $classOrObject, $methodName ) ){ continue; } $result = call_user_func( [ $classOrObject, $methodName ], $message, $receipt ); if ( $result !== FALSE ){ return $result; } } foreach ( $MessageHandlers as $classOrObject ){ if ( ! method_exists( $classOrObject, 'router' ) ){ continue; } $methodName = call_user_func( [ $classOrObject, 'router' ], $message->get('Message-Type') ); if ( $methodName !== FALSE && method_exists( $classOrObject, $methodName ) ){ $result = call_user_func( [ $classOrObject, $methodName ], $message, $receipt ); if ( $result !== FALSE ){ return $result; } } } return FALSE; } function ptcp_subdir_rules( $estimatedTotal ){ global $Config; $itemsPerDirectory = $Config['Max-Items-Per-Directory'] ?? 9999; $directoriesNeeded = ceil( $estimatedTotal / $itemsPerDirectory ); $totalPathLength = ceil( log( $directoriesNeeded, 16 ) ); $maxDirNameLength = floor( log( $itemsPerDirectory, 16 ) ); $subdirDepth = ceil( $totalPathLength / $maxDirNameLength ); $subdirNameLength = $subdirDepth ? ceil( $totalPathLength / $subdirDepth ) : 0; return [ (int) $subdirDepth, (int) $subdirNameLength ]; } function pxtc_update_object( &$object, $newData ){ foreach ( $newData as $key => $value ){ if ( is_null( $value ) ){ unset( $object[ $key ] ); } elseif ( is_array( $value ) && isset( $object[ $key ] ) && is_array( $object[ $key ] ) && count( array_filter( array_keys( $value ), 'is_string' ) ) > 0 ){ pxtc_update_object( $object[ $key ], $value ); } else { $object[ $key ] = $value; } } } // Main script error_reporting( E_ALL ); ini_set( 'display_errors', 1 ); $Timestamp = time(); $startTime = microtime( TRUE ); $BaseDir = __DIR__; $RequestHeaders = apache_request_headers(); $PartcpVersion = $RequestHeaders['X-Partcp-Version'] ?? '0.8'; $MessageHandlers = []; $AuthCheckers = []; $IdProviders = []; $Counter = NULL; $Crypto = NULL; $FsWriter = NULL; header('Content-Type: text/plain'); require_once 'lib/file_system.class.php'; require_once 'lib/incoming_message.class.php'; require_once 'lib/outgoing_message.class.php'; require_once 'config.php'; $DataDir = $Config['Data-Directory'] ?? "{$BaseDir}/data"; $TempDir = $Config['Temp-Directory'] ?? "{$BaseDir}/tmp"; $rootDirs[] = $DataDir; if ( file_exists( "{$DataDir}/_continuations" ) ){ $listing = glob( "{$DataDir}/_continuations/[0-9]*" ); foreach ( $listing as $file ){ $dir = file_get_contents( $file ); if ( $dir && file_exists( $dir ) ){ array_unshift( $rootDirs, $point ); } } } $FileSystem = new ParTCP_File_System( $rootDirs ); $ServerData = $Config['Server-Data']; if ( empty( $ServerData['name'] ) ){ $ServerData['name'] = $_SERVER['SERVER_NAME']; } // If there is nothing to process, show contents of this script file if ( empty( $_POST['message'] ) ){ $_POST['message'] = file_get_contents('php://input'); if ( empty( $_POST['message'] ) ){ readfile( __FILE__ ); exit; } } // Load modules if ( ! empty( $ServerData['modules'] ) ){ foreach ( $ServerData['modules'] as $module ){ $path = "{$BaseDir}/modules/{$module}/init.php"; if ( file_exists( $path ) ){ include $path; } else { ptcp_error( "Module {$module} could not be initialized" ); } } } // Prepare processing $Message = new ParTCP_Incoming_Message( $_POST['message'], $Crypto, 'ptcp_get_pubkey' ); $Receipt = new ParTCP_Outgoing_Message( $Crypto, 'ptcp_get_pubkey' ); $Receipt->set( 'From', $ServerData['name'] ); if ( $id = $Message->get('From') ){ $pubKey = ptcp_get_pubkey( $id, $Message, TRUE ); $Receipt->set_recipient( $Message->get('From'), $pubKey ); } else { $Receipt->set_recipient(''); } if ( empty( $pubKey ) && ( $pubKey = $Message->get('Public-Key') ) ){ $Receipt->recipientPubKey = $pubKey; } $Receipt->set_date(); $Receipt->set( 'Message-Type', 'receipt' ); $Receipt->set( 'Original-Message', $_POST['message'] ); // Process message and deliver receipt if ( ! $Message->get('Message-Type') ){ $Receipt->set( 'Message-Type', 'rejection-notice' ); $Receipt->set( 'Rejection-Reason', 'Message-Type header is missing' ); die( $Receipt->dump_signed() ); } $result = ptcp_handle_message( $Message, $Receipt ); if ( $result === FALSE ){ $Receipt->set( 'Message-Type', 'rejection-notice' ); $Receipt->set( 'Rejection-Reason', "Unknown message type '{$Message->get('Message-Type')}'" ); die( $Receipt->dump_signed() ); } if ( version_compare( $PartcpVersion, '1.0', '<' ) ){ $tmpMsg = new ParTCP_Incoming_Message( $result, $Crypto ); $type = $tmpMsg->get('Message-Type'); if ( $type == 'rejection-notice' ){ header( $_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request' ); } elseif ( $type == 'failure-notice' ){ header( $_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error' ); } die( $result ); } header( 'X-Elapsed-Time: ' . sprintf( '%.3f ms', 1000 * ( microtime( TRUE ) - $startTime ) ) ); echo $result; // end of file process.php