} else { $commentText = file_get_contents($f); if (!$commentText) { echo " Failed to load comment file {$f}, using default comment. "; } } } if (!$commentText) { $commentText = $comment; } } # Import the file if (isset($options['dry'])) { echo " publishing {$file} by '" . $wgUser->getName() . "', comment '{$commentText}'... "; } else { $mwProps = new MWFileProps(MimeMagic::singleton()); $props = $mwProps->getPropsFromPath($file, true); $flags = 0; $publishOptions = []; $handler = MediaHandler::getHandler($props['mime']); if ($handler) { $publishOptions['headers'] = $handler->getStreamHeaders($props['metadata']); } else { $publishOptions['headers'] = []; } $archive = $image->publish($file, $flags, $publishOptions); if (!$archive->isGood()) { echo "failed. (" . $archive->getWikiText(false, false, 'en') . ")\n"; $failed++; continue; }
/** * A verification routine suitable for partial files * * Runs the blacklist checks, but not any checks that may * assume the entire file is present. * * @return mixed True for valid or array with error message key. */ protected function verifyPartialFile() { global $wgAllowJavaUploads, $wgDisableUploadScriptChecks; # getTitle() sets some internal parameters like $this->mFinalExtension $this->getTitle(); $mwProps = new MWFileProps(MimeMagic::singleton()); $this->mFileProps = $mwProps->getPropsFromPath($this->mTempPath, $this->mFinalExtension); # check MIME type, if desired $mime = $this->mFileProps['file-mime']; $status = $this->verifyMimeType($mime); if ($status !== true) { return $status; } # check for htmlish code and javascript if (!$wgDisableUploadScriptChecks) { if (self::detectScript($this->mTempPath, $mime, $this->mFinalExtension)) { return ['uploadscripted']; } if ($this->mFinalExtension == 'svg' || $mime == 'image/svg+xml') { $svgStatus = $this->detectScriptInSvg($this->mTempPath, true); if ($svgStatus !== false) { return $svgStatus; } } } # Check for Java applets, which if uploaded can bypass cross-site # restrictions. if (!$wgAllowJavaUploads) { $this->mJavaDetected = false; $zipStatus = ZipDirectoryReader::read($this->mTempPath, [$this, 'zipEntryCallback']); if (!$zipStatus->isOK()) { $errors = $zipStatus->getErrorsArray(); $error = reset($errors); if ($error[0] !== 'zip-wrong-format') { return $error; } } if ($this->mJavaDetected) { return ['uploadjava']; } } # Scan the uploaded file for viruses $virus = $this->detectVirus($this->mTempPath); if ($virus) { return ['uploadvirus', $virus]; } return true; }
/** * Get properties of a file with a given virtual URL/storage path. * Properties should ultimately be obtained via FSFile::getProps(). * * @param string $virtualUrl * @return array */ public function getFileProps($virtualUrl) { $fsFile = $this->getLocalReference($virtualUrl); $mwProps = new MWFileProps(MimeMagic::singleton()); if ($fsFile) { $props = $mwProps->getPropsFromPath($fsFile->getPath(), true); } else { $props = $mwProps->newPlaceholderProps(); } return $props; }
/** * Stash a file in a temp directory and record that we did this in the * database, along with other metadata. * * @param string $path Path to file you want stashed * @param string $sourceType The type of upload that generated this file * (currently, I believe, 'file' or null) * @throws UploadStashBadPathException * @throws UploadStashFileException * @throws UploadStashNotLoggedInException * @return UploadStashFile|null File, or null on failure */ public function stashFile($path, $sourceType = null) { if (!is_file($path)) { wfDebug(__METHOD__ . " tried to stash file at '{$path}', but it doesn't exist\n"); throw new UploadStashBadPathException("path doesn't exist"); } $mwProps = new MWFileProps(MimeMagic::singleton()); $fileProps = $mwProps->getPropsFromPath($path, true); wfDebug(__METHOD__ . " stashing file at '{$path}'\n"); // we will be initializing from some tmpnam files that don't have extensions. // most of MediaWiki assumes all uploaded files have good extensions. So, we fix this. $extension = self::getExtensionForPath($path); if (!preg_match("/\\.\\Q{$extension}\\E\$/", $path)) { $pathWithGoodExtension = "{$path}.{$extension}"; } else { $pathWithGoodExtension = $path; } // If no key was supplied, make one. a mysql insertid would be totally // reasonable here, except that for historical reasons, the key is this // random thing instead. At least it's not guessable. // Some things that when combined will make a suitably unique key. // see: http://www.jwz.org/doc/mid.html list($usec, $sec) = explode(' ', microtime()); $usec = substr($usec, 2); $key = Wikimedia\base_convert($sec . $usec, 10, 36) . '.' . Wikimedia\base_convert(mt_rand(), 10, 36) . '.' . $this->userId . '.' . $extension; $this->fileProps[$key] = $fileProps; if (!preg_match(self::KEY_FORMAT_REGEX, $key)) { throw new UploadStashBadPathException("key '{$key}' is not in a proper format"); } wfDebug(__METHOD__ . " key for '{$path}': {$key}\n"); // if not already in a temporary area, put it there $storeStatus = $this->repo->storeTemp(basename($pathWithGoodExtension), $path); if (!$storeStatus->isOK()) { // It is a convention in MediaWiki to only return one error per API // exception, even if multiple errors are available. We use reset() // to pick the "first" thing that was wrong, preferring errors to // warnings. This is a bit lame, as we may have more info in the // $storeStatus and we're throwing it away, but to fix it means // redesigning API errors significantly. // $storeStatus->value just contains the virtual URL (if anything) // which is probably useless to the caller. $error = $storeStatus->getErrorsArray(); $error = reset($error); if (!count($error)) { $error = $storeStatus->getWarningsArray(); $error = reset($error); if (!count($error)) { $error = ['unknown', 'no error recorded']; } } // At this point, $error should contain the single "most important" // error, plus any parameters. $errorMsg = array_shift($error); throw new UploadStashFileException("Error storing file in '{$path}': " . wfMessage($errorMsg, $error)->text()); } $stashPath = $storeStatus->value; // fetch the current user ID if (!$this->isLoggedIn) { throw new UploadStashNotLoggedInException(__METHOD__ . ' No user is logged in, files must belong to users'); } // insert the file metadata into the db. wfDebug(__METHOD__ . " inserting {$stashPath} under {$key}\n"); $dbw = $this->repo->getMasterDB(); $serializedFileProps = serialize($fileProps); if (strlen($serializedFileProps) > self::MAX_US_PROPS_SIZE) { // Database is going to truncate this and make the field invalid. // Prioritize important metadata over file handler metadata. // File handler should be prepared to regenerate invalid metadata if needed. $fileProps['metadata'] = false; $serializedFileProps = serialize($fileProps); } $this->fileMetadata[$key] = ['us_id' => $dbw->nextSequenceValue('uploadstash_us_id_seq'), 'us_user' => $this->userId, 'us_key' => $key, 'us_orig_path' => $path, 'us_path' => $stashPath, 'us_props' => $dbw->encodeBlob($serializedFileProps), 'us_size' => $fileProps['size'], 'us_sha1' => $fileProps['sha1'], 'us_mime' => $fileProps['mime'], 'us_media_type' => $fileProps['media_type'], 'us_image_width' => $fileProps['width'], 'us_image_height' => $fileProps['height'], 'us_image_bits' => $fileProps['bits'], 'us_source_type' => $sourceType, 'us_timestamp' => $dbw->timestamp(), 'us_status' => 'finished']; $dbw->insert('uploadstash', $this->fileMetadata[$key], __METHOD__); // store the insertid in the class variable so immediate retrieval // (possibly laggy) isn't necesary. $this->fileMetadata[$key]['us_id'] = $dbw->insertId(); # create the UploadStashFile object for this file. $this->initFile($key); return $this->getFile($key); }
/** * Upload a file and record it in the DB * @param string|FSFile $src Source storage path, virtual URL, or filesystem path * @param string $comment Upload description * @param string $pageText Text to use for the new description page, * if a new description page is created * @param int|bool $flags Flags for publish() * @param array|bool $props File properties, if known. This can be used to * reduce the upload time when uploading virtual URLs for which the file * info is already known * @param string|bool $timestamp Timestamp for img_timestamp, or false to use the * current time * @param User|null $user User object or null to use $wgUser * @param string[] $tags Change tags to add to the log entry and page revision. * (This doesn't check $user's permissions.) * @return Status On success, the value member contains the * archive name, or an empty string if it was a new file. */ function upload($src, $comment, $pageText, $flags = 0, $props = false, $timestamp = false, $user = null, $tags = []) { global $wgContLang; if ($this->getRepo()->getReadOnlyReason() !== false) { return $this->readOnlyFatalStatus(); } $srcPath = $src instanceof FSFile ? $src->getPath() : $src; if (!$props) { if ($this->repo->isVirtualUrl($srcPath) || FileBackend::isStoragePath($srcPath)) { $props = $this->repo->getFileProps($srcPath); } else { $mwProps = new MWFileProps(MimeMagic::singleton()); $props = $mwProps->getPropsFromPath($srcPath, true); } } $options = []; $handler = MediaHandler::getHandler($props['mime']); if ($handler) { $options['headers'] = $handler->getStreamHeaders($props['metadata']); } else { $options['headers'] = []; } // Trim spaces on user supplied text $comment = trim($comment); // Truncate nicely or the DB will do it for us // non-nicely (dangling multi-byte chars, non-truncated version in cache). $comment = $wgContLang->truncate($comment, 255); $this->lock(); // begin $status = $this->publish($src, $flags, $options); if ($status->successCount >= 2) { // There will be a copy+(one of move,copy,store). // The first succeeding does not commit us to updating the DB // since it simply copied the current version to a timestamped file name. // It is only *preferable* to avoid leaving such files orphaned. // Once the second operation goes through, then the current version was // updated and we must therefore update the DB too. $oldver = $status->value; if (!$this->recordUpload2($oldver, $comment, $pageText, $props, $timestamp, $user, $tags)) { $status->fatal('filenotfound', $srcPath); } } $this->unlock(); // done return $status; }
/** * @param string $fileName * @return array */ function getFileProps($fileName) { if (FileRepo::isVirtualUrl($fileName)) { list($repoName, , ) = $this->splitVirtualUrl($fileName); if ($repoName === '') { $repoName = 'local'; } $repo = $this->getRepo($repoName); return $repo->getFileProps($fileName); } else { $mwProps = new MWFileProps(MimeMagic::singleton()); return $mwProps->getPropsFromPath($fileName, true); } }