/******************************************************************************* * Copyright (c) 2015 Zend Technologies and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Zend Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.php.internal.debug.core.zend.debugger.handlers; import java.io.*; import java.net.URI; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.internal.resources.WorkspaceRoot; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.debug.core.IBreakpointManager; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.php.debug.core.debugger.messages.IDebugMessage; import org.eclipse.php.debug.core.debugger.messages.IDebugResponseMessage; import org.eclipse.php.debug.core.debugger.parameters.IDebugParametersKeys; import org.eclipse.php.internal.debug.core.IPHPDebugConstants; import org.eclipse.php.internal.debug.core.Logger; import org.eclipse.php.internal.debug.core.PHPDebugPlugin; import org.eclipse.php.internal.debug.core.model.IPHPExceptionBreakpoint; import org.eclipse.php.internal.debug.core.model.PHPConditionalBreakpoint; import org.eclipse.php.internal.debug.core.pathmapper.PathEntry; import org.eclipse.php.internal.debug.core.pathmapper.PathEntry.Type; import org.eclipse.php.internal.debug.core.pathmapper.PathMapper; import org.eclipse.php.internal.debug.core.pathmapper.PathMapper.Mapping.MappingSource; import org.eclipse.php.internal.debug.core.pathmapper.PathMapperRegistry; import org.eclipse.php.internal.debug.core.pathmapper.VirtualPath; import org.eclipse.php.internal.debug.core.preferences.PHPDebugCorePreferenceNames; import org.eclipse.php.internal.debug.core.zend.debugger.Breakpoint; import org.eclipse.php.internal.debug.core.zend.debugger.RemoteDebugger; import org.eclipse.php.internal.debug.core.zend.debugger.messages.FileContentRequest; import org.eclipse.php.internal.debug.core.zend.debugger.messages.FileContentResponse; import org.eclipse.php.internal.debug.core.zend.model.PHPDebugTarget; import org.eclipse.wst.sse.ui.internal.StructuredResourceMarkerAnnotationModel; /** * Handle a server request for the delivery of the file content. This class is * used if debugger protocol is < 2006040902 */ @SuppressWarnings("restriction") public class FileContentRequestStaleHandler extends AbstractFileContentRequestHandler { private int reqID; private String lastFileName; private String encoding; private PHPDebugTarget debugTarget; private boolean isFirstFileToDebug; private FileContentRequest contentRequest; public FileContentRequestStaleHandler() { isFirstFileToDebug = true; } /* * (non-Javadoc) * * @see * org.eclipse.php.debug.core.debugger.handlers.IDebugMessageHandler#handle( * org.eclipse.php.debug.core.debugger.messages.IDebugMessage, * org.eclipse.php.internal.debug.core.zend.model.PHPDebugTarget) */ public void handle(IDebugMessage request, PHPDebugTarget debugTarget) { this.debugTarget = debugTarget; RemoteDebugger remoteDebugger = (RemoteDebugger) debugTarget.getRemoteDebugger(); boolean isWebServerDebugger = Boolean.toString(true) .equals(debugTarget.getLaunch().getAttribute(IDebugParametersKeys.WEB_SERVER_DEBUGGER)); contentRequest = (FileContentRequest) request; reqID = contentRequest.getID(); String currentFileName = contentRequest.getFileName(); debugTarget.setLastFileName(currentFileName); String debugType = getDebugType(); lastFileName = currentFileName; encoding = contentRequest.getTransferEncoding(); // If is full Path if (VirtualPath.isAbsolute(lastFileName)) { Path testWSPath = new Path(lastFileName); IFile testWSFile = null; if (testWSPath.segmentCount() > 1) { testWSFile = ResourcesPlugin.getWorkspace().getRoot().getFile(testWSPath); } if (isFirstFileToDebug && testWSFile != null && testWSFile.exists()) { // this is an RSE file, do nothing } // not a Dummy file request else if (isDummyFile()) { return; } // Exe script else if (debugType.equals(IDebugParametersKeys.PHP_EXE_SCRIPT_DEBUG)) { lastFileName = null;// this will inform the debugger to use its // own copy when getResponseMessage() is // called isFirstFileToDebug = false; return; } // web script OR web page OR toolbar debug else if (isWebServerDebugger) { if (isFirstFileToDebug) {// we already checked this it not the // Dummy file mapFirstFile(currentFileName); if (debugType.equals(IDebugParametersKeys.PHP_WEB_SCRIPT_DEBUG)) { VirtualPath remotePath = new VirtualPath(currentFileName); remotePath.removeLastSegment(); remoteDebugger.setCurrentWorkingDirectory(remotePath.toString()); } } lastFileName = remoteDebugger.convertToLocalFilename(currentFileName); } } // Other - Relative,RSE else { lastFileName = remoteDebugger.convertToLocalFilename(currentFileName); } // For each file request in Debug mode, send breakpoint request to the // debugger if (lastFileName != null && debugTarget.getLaunch().getLaunchMode().equals(ILaunchManager.DEBUG_MODE)) { addBreakPoints(debugTarget, currentFileName); } isFirstFileToDebug = false; } private void addBreakPoints(PHPDebugTarget debugTarget, String currentFileName) { // send synchronized Breakpoint request IBreakpointManager breakpointManager = debugTarget.getBreakpointManager(); if (!breakpointManager.isEnabled()) { return; } IBreakpoint[] breakpoints = breakpointManager.getBreakpoints(IPHPDebugConstants.ID_PHP_DEBUG_CORE); for (IBreakpoint element : breakpoints) { if (element instanceof IPHPExceptionBreakpoint) { // Not supported continue; } IResource resourceWithBreakPoint = element.getMarker().getResource(); String resourcePathName = ""; //$NON-NLS-1$ // handle a breakpoint on external file if (resourceWithBreakPoint instanceof WorkspaceRoot) {// external try { resourcePathName = element.getMarker() .getAttribute(StructuredResourceMarkerAnnotationModel.SECONDARY_ID_KEY).toString(); } catch (CoreException ce) { PHPDebugPlugin.log(ce); return; } } else {// workspace IPath resourceLocation = resourceWithBreakPoint.getLocation(); if (resourceLocation == null) { resourcePathName = resourceWithBreakPoint.getLocationURI().toString(); } else { resourcePathName = resourceLocation.toOSString(); } } String comparablePathName = ""; //$NON-NLS-1$ if (new File(lastFileName).exists()) { comparablePathName = lastFileName; } else { IFile tmpIFile = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(lastFileName)); IPath tmpLocation = tmpIFile.getLocation(); if (tmpIFile.exists()) { if (tmpLocation == null) { comparablePathName = tmpIFile.getLocationURI().toString(); } else { comparablePathName = tmpLocation.toOSString(); } } } if (new VirtualPath(resourcePathName).equals(new VirtualPath(comparablePathName))) { // send break point try { PHPConditionalBreakpoint phpBP = (PHPConditionalBreakpoint) element; Breakpoint runtimeBreakpoint = phpBP.getRuntimeBreakpoint(); int lineNumber = (Integer) element.getMarker().getAttribute(IMarker.LINE_NUMBER); Breakpoint tmpBreakpoint = new Breakpoint(currentFileName, lineNumber); if (tmpBreakpoint.isEnable()) { debugTarget.getRemoteDebugger().addBreakpoint(tmpBreakpoint); } runtimeBreakpoint.setID(tmpBreakpoint.getID()); } catch (Exception e) { Logger.logException(e); } } } } private void mapFirstFile(String currentFileName) { PathEntry pathEntry = null; String debugFileName = null; ILaunchConfiguration launchConfiguration = debugTarget.getLaunch().getLaunchConfiguration(); PathMapper pathMapper = PathMapperRegistry.getByLaunchConfiguration(launchConfiguration); if (pathMapper != null) { try { debugFileName = launchConfiguration.getAttribute(IPHPDebugConstants.ATTR_FILE, (String) null); } catch (CoreException e) { Logger.logException(e); return; } if (debugFileName != null) { IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(debugFileName); if (resource instanceof IFile) { pathEntry = new PathEntry(debugFileName, Type.WORKSPACE, resource.getParent()); } else if (new File(debugFileName).exists()) { pathEntry = new PathEntry(debugFileName, Type.EXTERNAL, new File(debugFileName).getParentFile()); } } if (pathEntry != null) { // Map remote file to the map point: pathMapper.addEntry(currentFileName, pathEntry, MappingSource.ENVIRONMENT); PathMapperRegistry.storeToPreferences(); } } } public IDebugResponseMessage getResponseMessage() { FileContentResponse response = new FileContentResponse(); response.setID(reqID); try { byte[] content = null; if (isDummyFile()) { content = getDummyContent(); } else { IResource member = null; if (lastFileName != null) { member = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(lastFileName)); } if (member != null) { IPath location = member.getLocation(); if (location != null) { File file = location.toFile(); content = getBytesFromFile(file); } else if (member.exists()) { // probably RSE URI uri = member.getLocationURI(); content = getBytesFromURI(uri); } } else if (lastFileName != null) { // try to get the file content directly from the file system File file = new File(lastFileName); if (file.exists()) { content = getBytesFromFile(file); } } } if (content == null) { content = new byte[0]; } setResponseContent(response, contentRequest, content); } catch (FileNotFoundException e) { // No need to log it. The server will throw an error notification to // the console indicating that the file was not found. } catch (NullPointerException npe) { // No need to log it. The server will throw an error notification to // the console indicating that the file was not found. } catch (Throwable t) { Logger.logException("Fail to send the file content to the server", //$NON-NLS-1$ t); } return response; } /* * Returns if the current file name is actually a dummy file. */ private boolean isDummyFile() { return lastFileName != null && lastFileName.endsWith(getDummyFileName()); } /* * Returns the dummy file name that was set in the preferences. Cache the * name through the lifecycle of this instance. */ private String getDummyFileName() { return Platform.getPreferencesService().getString(PHPDebugPlugin.ID, PHPDebugCorePreferenceNames.ZEND_DEBUG_DUMMY_FILE, "", //$NON-NLS-1$ null); } private String getDebugType() { String debugType = ""; //$NON-NLS-1$ try { debugType = debugTarget.getLaunch().getLaunchConfiguration() .getAttribute(IDebugParametersKeys.PHP_DEBUG_TYPE, ""); //$NON-NLS-1$ } catch (CoreException ce) { PHPDebugPlugin.log(ce); } return debugType; } /* * Returns the dummy content for the current file name. */ private byte[] getDummyContent() { String originalFileName = ""; //$NON-NLS-1$ try { ILaunchConfiguration launchConfiguration = debugTarget.getLaunch().getLaunchConfiguration(); // The dummy request should be made on the full path of the debugged // file. originalFileName = launchConfiguration.getAttribute(IPHPDebugConstants.ATTR_FILE_FULL_PATH, ""); //$NON-NLS-1$ } catch (CoreException e) { } StringBuilder contentBuf = new StringBuilder("<?php "); //$NON-NLS-1$ File originalFile = new File(originalFileName); if (!originalFileName.startsWith("\\\\") && originalFile.exists() //$NON-NLS-1$ && getDebugType().equals(IDebugParametersKeys.PHP_EXE_SCRIPT_DEBUG)) { String parentDirectory = originalFile.getParentFile().getAbsolutePath(); parentDirectory = parentDirectory.replaceAll("\\\\", "\\\\\\\\"); //$NON-NLS-1$ //$NON-NLS-2$ contentBuf.append("chdir('").append(parentDirectory).append("'); "); //$NON-NLS-1$ //$NON-NLS-2$ } originalFileName = originalFileName.replaceAll("\\\\", "\\\\\\\\"); //$NON-NLS-1$ //$NON-NLS-2$ contentBuf.append("include('").append(originalFileName) //$NON-NLS-1$ .append("'); ?>"); //$NON-NLS-1$ String content = contentBuf.toString(); if (encoding != null) { try { return content.getBytes(encoding); } catch (UnsupportedEncodingException e) { Logger.logException( "Failed to create dummy content in the '" //$NON-NLS-1$ + encoding + "' encoding. \nCreating with the default encoding.", //$NON-NLS-1$ e); } } return content.getBytes(); } // Read and returns all the bytes from the given file. private byte[] getBytesFromFile(File file) throws Exception { long length = file.length(); if (length > Integer.MAX_VALUE) { // the file is too big throw new Exception("The requested file '" + lastFileName + "' is too big"); //$NON-NLS-1$ //$NON-NLS-2$ } // TODO - There is no handle of file encoding! byte[] bytes = new byte[(int) length]; DataInputStream in = new DataInputStream(new FileInputStream(file)); in.readFully(bytes); in.close(); return bytes; } // Read and returns all the bytes from the given file URI. private byte[] getBytesFromURI(URI uri) throws Exception { IFileStore fileStore = EFS.getStore(uri); IFileInfo fileInfo = fileStore.fetchInfo(); long length = fileInfo.getLength(); if (length > Integer.MAX_VALUE) { // the file is too big throw new Exception("The requested file '" + lastFileName + "' is too big"); //$NON-NLS-1$ //$NON-NLS-2$ } // TODO - There is no handle of file encoding! byte[] bytes = new byte[(int) length]; InputStream openInputStream = fileStore.openInputStream(EFS.NONE, null); DataInputStream in = new DataInputStream(openInputStream); in.readFully(bytes); in.close(); return bytes; } }