writeConfigFile( [ 'debug' => [ 'enable' => $toggle, 'enable_log' => $toggle, 'verbosity' => 10 ] ] ); return true; } /** * @param $c_obj * @param $cli_mode * @return bool */ function uploadSystemDiagnostic( $c_obj, $cli_mode ) { $install_obj = new Install(); if ( $install_obj->checkDiskSpace() !== 0 ) { Debug::Text( 'ERROR: Not enough disk space.', __FILE__, __LINE__, __METHOD__, 10 ); return false; } $this->cli_mode = $cli_mode; //Set script execution time to 24 hours. Users may have slow upload speed and large log files. set_time_limit( 86400 ); global $config_vars; if ( !isset( $config_vars['cache']['dir'] ) ) { //Just in case the cache directory is not set. $config_vars['cache']['dir'] = Environment::getBasePath(); } $temp_dir = $config_vars['cache']['dir'] . DIRECTORY_SEPARATOR . 'system_diagnostics' . DIRECTORY_SEPARATOR; //Check if tempdir is writable. if ( $install_obj->checkWritableCacheDirectory() !== 0 ) { Debug::Text( 'ERROR: Cache directory is not writable.', __FILE__, __LINE__, __METHOD__, 10 ); return false; } //Create lock file so that this function cannot be ran more than once simultaneously. $lock_file = new LockFile( $config_vars['cache']['dir'] . DIRECTORY_SEPARATOR . 'system_diagnostic.lock' ); if ( $lock_file->exists() == false ) { if ( $lock_file->create() == true ) { Debug::text( 'System diagnostic lock file created. Starting process of gathering system logs.', __FILE__, __LINE__, __METHOD__, 10 ); $this->getProgressBarObject()->start( $this->getAPIMessageID(), 104, null, TTi18n::getText( 'Starting Upload Process...' ) ); //104 = 4 steps plus 100 for upload progress. $this->cleanUpTempDir( $temp_dir, false ); $registration_key = SystemSettingFactory::getSystemSettingValueByKey( 'registration_key' ); $zip_name = Misc::sanitizeFileName( $c_obj->getName() . '-' . date( 'Ymd-Hmi' ) .'-'. ( ( $registration_key != '' ) ? $registration_key : '0000000000000000000000000000000000000000' ) ); $zip_file = $temp_dir . $zip_name . '.zip'; if ( OPERATING_SYSTEM === 'WIN' ) { $apache_log_name = Environment::getBasePath() . '..' . DIRECTORY_SEPARATOR . 'apache2'. DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR .'error.log'; } else { $apache_log_name = '/var/log/apache2/error.log'; } if ( OPERATING_SYSTEM === 'WIN' ) { $installer_log_name = Environment::getBasePath() . '..' . DIRECTORY_SEPARATOR .'install.log'; } else { $installer_log_name = null; } if ( OPERATING_SYSTEM === 'WIN' ) { $sql_log_name = Environment::getBasePath() . '..' . DIRECTORY_SEPARATOR .'upgrade_sql_error_timetrex.log'; } else { $sql_log_name = null; } $timetrex_log_name = $config_vars['path']['log'] . DIRECTORY_SEPARATOR . 'timetrex.log'; //Using file_exists instead of is_dir as file_exists checks for both files and directories incase of name collision. if ( !file_exists( $temp_dir ) ) { mkdir( $temp_dir ); } if ( $this->cli_mode === true ) { echo "Collecting Log Files...\n"; } else { $this->getProgressBarObject()->set( $this->getAPIMessageID(), 1, TTi18n::getText( 'Collecting Log Files...' ) ); } $zip = new ZipArchive; if ( $zip->open( $zip_file, ZipArchive::CREATE ) === true ) { //Create phpInfo() log. ob_start(); phpinfo(); $php_info = ob_get_contents(); ob_end_clean(); //Remove HTML formatting. $php_info = strip_tags( $php_info ); //Remove CSS styling from top of output. $php_info = strstr( $php_info, 'PHP Version' ); $zip->addFromString( 'php_info.log', $php_info ); global $db; //Create system_info.log $system_info = 'Operating System: '. php_uname() . PHP_EOL; $system_info .= 'PostgreSQL: ' . Debug::varDump( $db->ServerInfo( true ) ) . PHP_EOL; $system_info .= 'Total Disk Space: ' . round( disk_total_space( dirname( __FILE__ ) ) / 1024 / 1024 / 1024 ) . 'gb Free Space: ' . round( disk_free_space( dirname( __FILE__ ) ) / 1024 / 1024 / 1024 ) . 'gb' . PHP_EOL; $zip->addFromString( 'system_info.log', $system_info ); //Zip apache log files. if ( file_exists( $apache_log_name ) ) { $zip->addFile( $apache_log_name, basename( $apache_log_name ) ); } else { Debug::Text( 'Unable to locate Apache log files. May be caused by permission issues, or it doesn\'t exist: ' . $apache_log_name, __FILE__, __LINE__, __METHOD__, 10 ); } if ( file_exists( $installer_log_name ) ) { $zip->addFile( $installer_log_name, basename( $installer_log_name ) ); } else { Debug::Text( 'Unable to locate Windows Installer log files. May be caused by permission issues, or it doesn\'t exist: ' . $installer_log_name, __FILE__, __LINE__, __METHOD__, 10 ); } if ( file_exists( $sql_log_name ) ) { $zip->addFile( $sql_log_name, basename( $sql_log_name ) ); } else { Debug::Text( 'Unable to locate SQL error log files. May be caused by permission issues, or it doesn\'t exist: ' . $sql_log_name, __FILE__, __LINE__, __METHOD__, 10 ); } //Include all TimeTrex files. $root_path = realpath( Environment::getBasePath() ); $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $root_path ), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ( $files as $name => $file ) { // Skip directories (they would be added automatically) if ( !$file->isDir() ) { // Add current file to archive under the 'timetrex' directory. $zip->addFile( $file->getRealPath(), str_replace( '\\', '/', 'timetrex'. DIRECTORY_SEPARATOR . substr( $file->getRealPath(), strlen( $root_path ) + 1 ) ) ); //Replace all backslashes from Windows with forward slashes as recommended in: https://www.php.net/manual/en/ziparchive.addfile.php } } unset( $files, $file_path, $relative_path, $root_path ); //Zip TimeTrex log files -- Do this last so we can get as many log lines into it as possible. if ( file_exists( $timetrex_log_name ) ) { Debug::Text( ' Writing debug buffer to log prior to uploading...', __FILE__, __LINE__, __METHOD__, 10 ); Debug::writeToLog(); //Write all previous debug lines to log before uploading it. **Keep this here, even though its in TTShutdown() as well** $zip->addFile( $timetrex_log_name, basename( $timetrex_log_name ) ); } else { Debug::Text( 'Unable to locate TimeTrex log files. May be caused by permission issues.' . $timetrex_log_name, __FILE__, __LINE__, __METHOD__, 10 ); } if ( $this->cli_mode === true ) { echo "Compressing Logs...\n"; } else { $this->getProgressBarObject()->set( $this->getAPIMessageID(), 2, TTi18n::getText( 'Compressing Logs...' ) ); } $retval = $zip->close(); if ( $retval === true && file_exists( $zip_file ) ) { if ( $this->cli_mode === true ) { echo "Securely Uploading Logs...\n"; } else { $this->getProgressBarObject()->set( $this->getAPIMessageID(), 3, TTi18n::getText( 'Securely Uploading Logs...' ) ); } $fp = fopen( $zip_file, 'r' ); $curl_handler = curl_init(); curl_setopt( $curl_handler, CURLOPT_URL, 'https://nextcloud.timetrex.com/s/6NePJBkdqGeLaDM' ); curl_setopt( $curl_handler, CURLOPT_SSL_VERIFYPEER, false ); //False to avoid curl error - "unable to get local issuer certificate." curl_setopt( $curl_handler, CURLOPT_RETURNTRANSFER, true ); $curl_retval = curl_exec( $curl_handler ); $doc = new DOMDocument(); //LIBXML_NOERROR to ignore HTML errors on the page we are loading. $doc->loadHTML( $curl_retval, LIBXML_NOERROR ); $head = $doc->getElementsByTagName( 'head' ); if ( isset( $head[0] ) ) { $request_token = $head[0]->getAttribute( 'data-requesttoken' ); $basic_authorization_token = base64_encode( $doc->getElementById( 'sharingToken' )->getAttribute( 'value' ) . ':' ); } curl_setopt( $curl_handler, CURLOPT_URL, 'https://nextcloud.timetrex.com/public.php/webdav/' . $zip_name . '.zip' ); curl_setopt( $curl_handler, CURLOPT_HTTPHEADER, [ 'requesttoken: ' . $request_token, 'authorization: Basic ' . $basic_authorization_token, ] ); curl_setopt( $curl_handler, CURLOPT_PUT, true ); curl_setopt( $curl_handler, CURLOPT_INFILESIZE, filesize( $zip_file ) ); curl_setopt( $curl_handler, CURLOPT_INFILE, $fp ); curl_setopt( $curl_handler, CURLOPT_NOPROGRESS, false ); curl_setopt( $curl_handler, CURLOPT_PROGRESSFUNCTION, [ $this, 'updateUploadProgress' ] ); curl_setopt( $curl_handler, CURLOPT_TIMEOUT, 0 ); curl_setopt( $curl_handler, CURLINFO_HEADER_OUT, true ); $this->curl_progress_start = microtime( true ); $curl_retval = curl_exec( $curl_handler ); Debug::Text( 'CURL Return Response: ' . Debug::varDump( $curl_retval ), __FILE__, __LINE__, __METHOD__, 10 ); if ( $this->cli_mode === true ) { echo 'Finished Uploading Logs...'."\n"; } else { $this->getProgressBarObject()->set( $this->getAPIMessageID(), 104, TTi18n::getText( 'Finished Uploading Logs...' ) ); } if ( curl_errno( $curl_handler ) ) { $error_msg = curl_error( $curl_handler ); Debug::Text( 'CURL ERROR: ' . $error_msg, __FILE__, __LINE__, __METHOD__, 10 ); } else { Debug::Text( 'CURL Upload success.', __FILE__, __LINE__, __METHOD__, 10 ); } curl_close( $curl_handler ); $this->cleanUpTempDir( $temp_dir, true ); } } else { return false; } } $lock_file->delete(); } else { Debug::text( 'Skipping... uploading system diagnostics. Lock file exists...', __FILE__, __LINE__, __METHOD__, 10 ); if ( $this->cli_mode === true ) { echo "NOTICE: Lock file exists, diagnostics already being collected elsewhere, skipping...\n"; } return false; } return true; } /** * @param $temp_dir * @param $remove_temp_dir * @return bool */ function cleanUpTempDir( $temp_dir, $remove_temp_dir ) { Debug::Text( 'Cleaning up Temp Dir: ' . $temp_dir, __FILE__, __LINE__, __METHOD__, 10 ); if ( is_dir( $temp_dir ) ) { if ( $this->cli_mode === true ) { echo "Cleaning Up...\n"; } else { $this->getProgressBarObject()->set( $this->getAPIMessageID(), 3, TTi18n::getText( 'Cleaning Up' ) ); } $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $temp_dir, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ); foreach ( $files as $fileinfo ) { if ( $fileinfo->isDir() === true ) { @rmdir( $fileinfo->getRealPath() ); } else { Misc::unlink( $fileinfo->getRealPath() ); } } if ( $remove_temp_dir === true ) { //Since windows deleting files is async, its likely rare that the directory can be deleted, even after a sleeping for 10 seconds. @rmdir( $temp_dir ); } clearstatcache(); //Clear any stat cache when done. Debug::Text( 'Done cleaning up Temp Dir: ' . $temp_dir, __FILE__, __LINE__, __METHOD__, 10 ); } return true; } /** * Updates API or CLI with upload progress as a percent. */ function updateUploadProgress( $curl_handler, $download_file_size, $downloaded, $upload_file_size, $uploaded ) { //Debug::text( ' Download File Size: '. $download_file_size .' Downloaded: '. $downloaded .' Upload File Size: '. $upload_file_size .' Uploaded: '. $uploaded, __FILE__, __LINE__, __METHOD__, 10 ); //echo ' Download File Size: '. $download_file_size .' Downloaded: '. $downloaded .' Upload File Size: '. $upload_file_size .' Uploaded: '. $uploaded ."\n"; if ( $upload_file_size > 0 ) { //Prevent division by 0. $elapsed_upload_time = ( microtime( true ) - $this->curl_progress_start ); if ( $elapsed_upload_time == 0 ) { $bytes_per_second = $uploaded; } else { $bytes_per_second = ( $uploaded / $elapsed_upload_time ); } $kilobytes_per_second = round( $bytes_per_second / 1024, 2 ); $progress = round( ( $uploaded / $upload_file_size ) * 100 ); if ( $this->curl_last_progress === null || $progress !== $this->curl_last_progress ) { //Only update progress when it changes. if ( $this->cli_mode === true ) { if ( $progress % 10 == 0 ) { echo 'Securely Uploading Logs... ' . $progress . '% - ' . $kilobytes_per_second . "KB/s\n"; } } else { //Add 3 to the progress because there are 3 steps before it. $this->getProgressBarObject()->set( $this->getAPIMessageID(), ( floor( $progress ) + 3 ), TTi18n::getText( 'Securely Uploading Logs...' ) . ' ' . $progress . '% - '. $kilobytes_per_second .'KB/s' ); } } $this->curl_last_progress = $progress; } } /** * @param object $obj * @return bool */ function setProgressBarObject( $obj ) { if ( is_object( $obj ) ) { $this->progress_bar_obj = $obj; return true; } return false; } /** * @return null|ProgressBar */ function getProgressBarObject() { if ( !is_object( $this->progress_bar_obj ) ) { $this->progress_bar_obj = new ProgressBar(); } return $this->progress_bar_obj; } /** * Returns the API messageID for each individual call. * @return bool|null */ function getAPIMessageID() { if ( $this->api_message_id != null ) { return $this->api_message_id; } return false; } /** * @param string $id UUID * @return bool */ function setAPIMessageID( $id ) { Debug::Text( 'API Message ID: ' . $id, __FILE__, __LINE__, __METHOD__, 10 ); if ( $id != '' ) { $this->api_message_id = $id; return true; } return false; } } ?>