Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
| 1 | <?php |
||
| 33 | class CKFinder_Connector_CommandHandler_CopyFiles extends CKFinder_Connector_CommandHandler_XmlCommandHandlerBase |
||
| 34 | { |
||
| 35 | /** |
||
| 36 | * Command name |
||
| 37 | * |
||
| 38 | * @access private |
||
| 39 | * @var string |
||
| 40 | */ |
||
| 41 | var $command = "CopyFiles"; |
||
| 42 | |||
| 43 | |||
| 44 | /** |
||
| 45 | * handle request and build XML |
||
| 46 | * @access protected |
||
| 47 | * |
||
| 48 | */ |
||
| 49 | function buildXml() |
||
| 50 | { |
||
| 51 | if (empty($_POST['CKFinderCommand']) || $_POST['CKFinderCommand'] != 'true') { |
||
| 52 | $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST); |
||
| 53 | } |
||
| 54 | |||
| 55 | $clientPath = $this->_currentFolder->getClientPath(); |
||
| 56 | $sServerDir = $this->_currentFolder->getServerPath(); |
||
| 57 | $currentResourceTypeConfig = $this->_currentFolder->getResourceTypeConfig(); |
||
| 58 | $_config =& CKFinder_Connector_Core_Factory::getInstance("Core_Config"); |
||
| 59 | $_aclConfig = $_config->getAccessControlConfig(); |
||
| 60 | $aclMasks = array(); |
||
| 61 | $_resourceTypeConfig = array(); |
||
| 62 | |||
| 63 | if (!$this->_currentFolder->checkAcl(CKFINDER_CONNECTOR_ACL_FILE_RENAME | CKFINDER_CONNECTOR_ACL_FILE_UPLOAD | CKFINDER_CONNECTOR_ACL_FILE_DELETE)) { |
||
| 64 | $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_UNAUTHORIZED); |
||
| 65 | } |
||
| 66 | |||
| 67 | // Create the "Errors" node. |
||
| 68 | $oErrorsNode = new CKFinder_Connector_Utils_XmlNode("Errors"); |
||
| 69 | $errorCode = CKFINDER_CONNECTOR_ERROR_NONE; |
||
| 70 | $copied = 0; |
||
| 71 | $copiedAll = 0; |
||
| 72 | if (!empty($_POST['copied'])) { |
||
| 73 | $copiedAll = intval($_POST['copied']); |
||
| 74 | } |
||
| 75 | $checkedPaths = array(); |
||
| 76 | |||
| 77 | $oCopyFilesNode = new Ckfinder_Connector_Utils_XmlNode("CopyFiles"); |
||
| 78 | |||
| 79 | if (!empty($_POST['files']) && is_array($_POST['files'])) { |
||
| 80 | foreach ($_POST['files'] as $index => $arr) { |
||
| 81 | if (empty($arr['name'])) { |
||
| 82 | continue; |
||
| 83 | } |
||
| 84 | if (!isset($arr['name'], $arr['type'], $arr['folder'])) { |
||
| 85 | $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST); |
||
| 86 | } |
||
| 87 | |||
| 88 | // file name |
||
| 89 | $name = CKFinder_Connector_Utils_FileSystem::convertToFilesystemEncoding($arr['name']); |
||
| 90 | // resource type |
||
| 91 | $type = $arr['type']; |
||
| 92 | // client path |
||
| 93 | $path = CKFinder_Connector_Utils_FileSystem::convertToFilesystemEncoding($arr['folder']); |
||
| 94 | // options |
||
| 95 | $options = (!empty($arr['options'])) ? $arr['options'] : ''; |
||
| 96 | |||
| 97 | $destinationFilePath = $sServerDir.$name; |
||
| 98 | |||
| 99 | // check #1 (path) |
||
| 100 | if (!CKFinder_Connector_Utils_FileSystem::checkFileName($name) || preg_match(CKFINDER_REGEX_INVALID_PATH, $path)) { |
||
| 101 | $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST); |
||
| 102 | } |
||
| 103 | |||
| 104 | // get resource type config for current file |
||
| 105 | if (!isset($_resourceTypeConfig[$type])) { |
||
| 106 | $_resourceTypeConfig[$type] = $_config->getResourceTypeConfig($type); |
||
| 107 | } |
||
| 108 | |||
| 109 | // check #2 (resource type) |
||
| 110 | if (is_null($_resourceTypeConfig[$type])) { |
||
| 111 | $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST); |
||
| 112 | } |
||
| 113 | |||
| 114 | // check #3 (extension) |
||
| 115 | if (!$_resourceTypeConfig[$type]->checkExtension($name, false)) { |
||
| 116 | $errorCode = CKFINDER_CONNECTOR_ERROR_INVALID_EXTENSION; |
||
| 117 | $this->appendErrorNode($oErrorsNode, $errorCode, $name, $type, $path); |
||
| 118 | continue; |
||
| 119 | } |
||
| 120 | |||
| 121 | // check #4 (extension) - when moving to another resource type, double check extension |
||
| 122 | if ($currentResourceTypeConfig->getName() != $type) { |
||
| 123 | if (!$currentResourceTypeConfig->checkExtension($name, false)) { |
||
| 124 | $errorCode = CKFINDER_CONNECTOR_ERROR_INVALID_EXTENSION; |
||
| 125 | $this->appendErrorNode($oErrorsNode, $errorCode, $name, $type, $path); |
||
| 126 | continue; |
||
| 127 | } |
||
| 128 | } |
||
| 129 | |||
| 130 | // check #5 (hidden folders) |
||
| 131 | // cache results |
||
| 132 | if (empty($checkedPaths[$path])) { |
||
| 133 | $checkedPaths[$path] = true; |
||
| 134 | |||
| 135 | if ($_resourceTypeConfig[$type]->checkIsHiddenPath($path)) { |
||
| 136 | $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST); |
||
| 137 | } |
||
| 138 | } |
||
| 139 | |||
| 140 | $sourceFilePath = $_resourceTypeConfig[$type]->getDirectory().$path.$name; |
||
| 141 | |||
| 142 | // check #6 (hidden file name) |
||
| 143 | if ($currentResourceTypeConfig->checkIsHiddenFile($name)) { |
||
| 144 | $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST); |
||
| 145 | } |
||
| 146 | |||
| 147 | // check #7 (Access Control, need file view permission to source files) |
||
| 148 | if (!isset($aclMasks[$type."@".$path])) { |
||
| 149 | $aclMasks[$type."@".$path] = $_aclConfig->getComputedMask($type, $path); |
||
| 150 | } |
||
| 151 | |||
| 152 | $isAuthorized = (($aclMasks[$type."@".$path] & CKFINDER_CONNECTOR_ACL_FILE_VIEW) == CKFINDER_CONNECTOR_ACL_FILE_VIEW); |
||
| 153 | if (!$isAuthorized) { |
||
| 154 | $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_UNAUTHORIZED); |
||
| 155 | } |
||
| 156 | |||
| 157 | // check #8 (invalid file name) |
||
| 158 | if (!file_exists($sourceFilePath) || !is_file($sourceFilePath)) { |
||
| 159 | $errorCode = CKFINDER_CONNECTOR_ERROR_FILE_NOT_FOUND; |
||
| 160 | $this->appendErrorNode($oErrorsNode, $errorCode, $name, $type, $path); |
||
| 161 | continue; |
||
| 162 | } |
||
| 163 | |||
| 164 | // check #9 (max size) |
||
| 165 | if ($currentResourceTypeConfig->getName() != $type) { |
||
| 166 | $maxSize = $currentResourceTypeConfig->getMaxSize(); |
||
| 167 | $fileSize = filesize($sourceFilePath); |
||
| 168 | if ($maxSize && $fileSize>$maxSize) { |
||
| 169 | $errorCode = CKFINDER_CONNECTOR_ERROR_UPLOADED_TOO_BIG; |
||
| 170 | $this->appendErrorNode($oErrorsNode, $errorCode, $name, $type, $path); |
||
| 171 | continue; |
||
| 172 | } |
||
| 173 | } |
||
| 174 | |||
| 175 | //$overwrite |
||
| 176 | // finally, no errors so far, we may attempt to copy a file |
||
| 177 | // protection against copying files to itself |
||
| 178 | if ($sourceFilePath == $destinationFilePath) { |
||
| 179 | $errorCode = CKFINDER_CONNECTOR_ERROR_SOURCE_AND_TARGET_PATH_EQUAL; |
||
| 180 | $this->appendErrorNode($oErrorsNode, $errorCode, $name, $type, $path); |
||
| 181 | continue; |
||
| 182 | } |
||
| 183 | // check if file exists if we don't force overwriting |
||
| 184 | else if (file_exists($destinationFilePath) && strpos($options, "overwrite") === false) { |
||
| 185 | if (strpos($options, "autorename") !== false) { |
||
| 186 | $iCounter = 1; |
||
| 187 | while (true) |
||
| 188 | { |
||
| 189 | $fileName = CKFinder_Connector_Utils_FileSystem::getFileNameWithoutExtension($name) . |
||
| 190 | "(" . $iCounter . ")" . "." . |
||
| 191 | CKFinder_Connector_Utils_FileSystem::getExtension($name); |
||
| 192 | |||
| 193 | $destinationFilePath = $sServerDir.$fileName; |
||
| 194 | if (!file_exists($destinationFilePath)) { |
||
| 195 | break; |
||
| 196 | } |
||
| 197 | else { |
||
| 198 | $iCounter++; |
||
| 199 | } |
||
| 200 | } |
||
| 201 | if (!@copy($sourceFilePath, $destinationFilePath)) { |
||
| 202 | $errorCode = CKFINDER_CONNECTOR_ERROR_ACCESS_DENIED; |
||
| 203 | $this->appendErrorNode($oErrorsNode, $errorCode, $name, $type, $path); |
||
| 204 | continue; |
||
| 205 | } |
||
| 206 | else { |
||
| 207 | $copied++; |
||
| 208 | } |
||
| 209 | } |
||
| 210 | else { |
||
| 211 | $errorCode = CKFINDER_CONNECTOR_ERROR_ALREADY_EXIST; |
||
| 212 | $this->appendErrorNode($oErrorsNode, $errorCode, $name, $type, $path); |
||
| 213 | continue; |
||
| 214 | } |
||
| 215 | } |
||
| 216 | // copy() overwrites without warning |
||
| 217 | else { |
||
| 218 | if (!@copy($sourceFilePath, $destinationFilePath)) { |
||
| 219 | $errorCode = CKFINDER_CONNECTOR_ERROR_ACCESS_DENIED; |
||
| 220 | $this->appendErrorNode($oErrorsNode, $errorCode, $name, $type, $path); |
||
| 221 | continue; |
||
| 222 | } |
||
| 223 | else { |
||
| 224 | $copied++; |
||
| 225 | } |
||
| 226 | } |
||
| 227 | } |
||
| 228 | } |
||
| 229 | |||
| 230 | $this->_connectorNode->addChild($oCopyFilesNode); |
||
| 231 | if ($errorCode != CKFINDER_CONNECTOR_ERROR_NONE) { |
||
| 232 | $this->_connectorNode->addChild($oErrorsNode); |
||
| 233 | } |
||
| 234 | $oCopyFilesNode->addAttribute("copied", $copied); |
||
| 235 | $oCopyFilesNode->addAttribute("copiedTotal", $copiedAll + $copied); |
||
| 236 | |||
| 237 | /** |
||
| 238 | * Note: actually we could have more than one error. |
||
| 239 | * This is just a flag for CKFinder interface telling it to check all errors. |
||
| 240 | */ |
||
| 241 | if ($errorCode != CKFINDER_CONNECTOR_ERROR_NONE) { |
||
| 242 | $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_COPY_FAILED); |
||
| 243 | } |
||
| 244 | } |
||
| 245 | |||
| 246 | function appendErrorNode(&$oErrorsNode, $errorCode, $name, $type, $path) |
||
| 247 | { |
||
| 248 | $oErrorNode = new CKFinder_Connector_Utils_XmlNode("Error"); |
||
| 249 | $oErrorNode->addAttribute("code", $errorCode); |
||
| 250 | $oErrorNode->addAttribute("name", CKFinder_Connector_Utils_FileSystem::convertToConnectorEncoding($name)); |
||
| 251 | $oErrorNode->addAttribute("type", $type); |
||
| 252 | $oErrorNode->addAttribute("folder", $path); |
||
| 253 | $oErrorsNode->addChild($oErrorNode); |
||
| 254 | } |
||
| 255 | } |
||
| 256 |