/** */ function wfGetType($filename) { global $wgTrivialMimeDetection; # trivial detection by file extension, # used for thumbnails (thumb.php) if ($wgTrivialMimeDetection) { $ext = strtolower(strrchr($filename, '.')); switch ($ext) { case '.gif': return 'image/gif'; case '.png': return 'image/png'; case '.jpg': return 'image/jpeg'; case '.jpeg': return 'image/jpeg'; } return 'unknown/unknown'; } else { $magic =& wfGetMimeMagic(); return $magic->guessMimeType($filename); //full fancy mime detection } }
/** * Checks if the mime type of the uploaded file matches the file extension. * * @param string $mime the mime type of the uploaded file * @param string $extension The filename extension that the file is to be served with * @return bool */ function verifyExtension($mime, $extension) { $fname = 'SpecialUpload::verifyExtension'; $magic =& wfGetMimeMagic(); if (!$mime || $mime == 'unknown' || $mime == 'unknown/unknown') { if (!$magic->isRecognizableExtension($extension)) { wfDebug("{$fname}: passing file with unknown detected mime type; unrecognized extension '{$extension}', can't verify\n"); return true; } else { wfDebug("{$fname}: rejecting file with unknown detected mime type; recognized extension '{$extension}', so probably invalid file\n"); return false; } } $match = $magic->isMatchingExtension($extension, $mime); if ($match === NULL) { wfDebug("{$fname}: no file extension known for mime type {$mime}, passing file\n"); return true; } elseif ($match === true) { wfDebug("{$fname}: mime type {$mime} matches extension {$extension}, passing file\n"); #TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it! return true; } else { wfDebug("{$fname}: mime type {$mime} mismatches file extension {$extension}, rejecting file\n"); return false; } }
/** * Checks if the mime type of the uploaded file matches the file extension. * * @param string $mime the mime type of the uploaded file * @param string $extension The filename extension that the file is to be served with * @return bool */ function verifyExtension($mime, $extension) { $fname = 'SpecialUpload::verifyExtension'; if (!$mime || $mime == "unknown" || $mime == "unknown/unknown") { wfDebug("{$fname}: passing file with unknown mime type\n"); return true; } $magic =& wfGetMimeMagic(); $match = $magic->isMatchingExtension($extension, $mime); if ($match === NULL) { wfDebug("{$fname}: no file extension known for mime type {$mime}, passing file\n"); return true; } elseif ($match === true) { wfDebug("{$fname}: mime type {$mime} matches extension {$extension}, passing file\n"); #TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it! return true; } else { wfDebug("{$fname}: mime type {$mime} mismatches file extension {$extension}, rejecting file\n"); return false; } }
/** * Verifies that it's ok to include the uploaded file * * @param string $tmpfile the full path of the temporary file to verify * @param string $extension The filename extension that the file is to be served with * @return mixed true of the file is verified, a WikiError object otherwise. */ function verify($tmpfile, $extension) { $fname = "AnyWikiDraw_body::verify"; #magically determine mime type // BEGIN PATCH MediaWiki 1.7.1 //$magic=& MimeMagic::singleton(); //$mime= $magic->guessMimeType($tmpfile,false); $magic =& wfGetMimeMagic(); $mime = $magic->guessMimeType($tmpfile, false); // END PATCH MediaWiki 1.7.1 #check mime type, if desired global $wgVerifyMimeType; if ($wgVerifyMimeType) { #check mime type against file extension if (!$this->verifyExtension($mime, $extension)) { //return new WikiErrorMsg( 'uploadcorrupt' ); return false; } /* #check mime type blacklist global $wgMimeTypeBlacklist; if( isset($wgMimeTypeBlacklist) && !is_null($wgMimeTypeBlacklist) && $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) { //return new WikiErrorMsg( 'badfiletype', htmlspecialchars( $mime ) ); wfDebug($fname.' badfiletype'); return false; }*/ } #check for htmlish code and javascript if ($this->detectScript($tmpfile, $mime, $extension)) { //return new WikiErrorMsg( 'uploadscripted' ); wfDebug($fname . ' uploadscripted'); return false; } /** * Scan the uploaded file for viruses */ $virus = $this->detectVirus($tmpfile); if ($virus) { //return new WikiErrorMsg( 'uploadvirus', htmlspecialchars($virus) ); wfDebug($fname . ' uploadvirus'); return false; } //wfDebug( "$fname: all clear; passing.\n" ); return true; }
/** * Load metadata from the file itself */ function loadFromFile() { global $wgUseSharedUploads, $wgSharedUploadDirectory, $wgLang, $wgShowEXIF; $fname = 'Image::loadFromFile'; wfProfileIn($fname); $this->imagePath = $this->getFullPath(); $this->fileExists = file_exists($this->imagePath); $this->fromSharedDirectory = false; $gis = array(); if (!$this->fileExists) { wfDebug("{$fname}: " . $this->imagePath . " not found locally!\n"); } # If the file is not found, and a shared upload directory is used, look for it there. if (!$this->fileExists && $wgUseSharedUploads && $wgSharedUploadDirectory) { # In case we're on a wgCapitalLinks=false wiki, we # capitalize the first letter of the filename before # looking it up in the shared repository. $sharedImage = Image::newFromName($wgLang->ucfirst($this->name)); $this->fileExists = file_exists($sharedImage->getFullPath(true)); if ($this->fileExists) { $this->name = $sharedImage->name; $this->imagePath = $this->getFullPath(true); $this->fromSharedDirectory = true; } } if ($this->fileExists) { $magic =& wfGetMimeMagic(); $this->mime = $magic->guessMimeType($this->imagePath, true); $this->type = $magic->getMediaType($this->imagePath, $this->mime); # Get size in bytes $this->size = filesize($this->imagePath); $magic =& wfGetMimeMagic(); # Height and width if ($this->mime == 'image/svg') { wfSuppressWarnings(); $gis = wfGetSVGsize($this->imagePath); wfRestoreWarnings(); } elseif (!$magic->isPHPImageType($this->mime)) { # Don't try to get the width and height of sound and video files, that's bad for performance $gis[0] = 0; //width $gis[1] = 0; //height $gis[2] = 0; //unknown $gis[3] = ""; //width height string } else { wfSuppressWarnings(); $gis = getimagesize($this->imagePath); wfRestoreWarnings(); } wfDebug("{$fname}: " . $this->imagePath . " loaded, " . $this->size . " bytes, " . $this->mime . ".\n"); } else { $gis[0] = 0; //width $gis[1] = 0; //height $gis[2] = 0; //unknown $gis[3] = ""; //width height string $this->mime = NULL; $this->type = MEDIATYPE_UNKNOWN; wfDebug("{$fname}: " . $this->imagePath . " NOT FOUND!\n"); } $this->width = $gis[0]; $this->height = $gis[1]; #NOTE: $gis[2] contains a code for the image type. This is no longer used. #NOTE: we have to set this flag early to avoid load() to be called # be some of the functions below. This may lead to recursion or other bad things! # as ther's only one thread of execution, this should be safe anyway. $this->dataLoaded = true; if ($this->fileExists && $wgShowEXIF) { $this->metadata = serialize($this->retrieveExifData()); } else { $this->metadata = serialize(array()); } if (isset($gis['bits'])) { $this->bits = $gis['bits']; } else { $this->bits = 0; } wfProfileOut($fname); }
/** * This function registers all of the MW hook functions necessary for the * map extension to function. These hooks are added using the array_unshift * function in order to get the hook added first. This is because if another * extension returns false from its hook function, it will short-circuit * any hooks that come later in the list. **/ function wfGoogleMaps_Install() { global $wgGoogleMapsKey, $wgGoogleMapsKeys, $wgGoogleMapsDisableEditorsMap, $wgGoogleMapsEnablePaths, $wgGoogleMapsDefaults, $wgGoogleMapsMessages, $wgGoogleMapsCustomMessages, $wgGoogleMapsUrlPath, $wgXhtmlNamespaces, $wgGoogleMapsTemplateVariables, $wgJsMimeType, $wgLanguageCode, $wgLang, $wgParser, $wgProxyKey, $wgVersion, $wgGoogleMaps, $wgHooks, $wgScriptPath, $wgSpecialPages, $wgTitle; // set up some default values for the various extension configuration parameters // to keep from getting PHP notices if running in strict mode if (!isset($wgGoogleMapsKey)) { $wgGoogleMapsKey = null; } if (!isset($wgGoogleMapsKeys)) { $wgGoogleMapsKeys = array(); } if (!isset($wgGoogleMapsDisableEditorsMap)) { $wgGoogleMapsDisableEditorsMap = false; } if (!isset($wgGoogleMapsEnablePaths)) { $wgGoogleMapsEnablePaths = false; } if (!isset($wgGoogleMapsDefaults)) { $wgGoogleMapsDefaults = null; } if (!isset($wgGoogleMapsCustomMessages)) { $wgGoogleMapsCustomMessages = null; } if (!isset($wgGoogleMapsUrlPath)) { $wgGoogleMapsUrlPath = "{$wgScriptPath}/extensions/GoogleMaps"; } if (!isset($wgXhtmlNamespaces)) { $wgXhtmlNamespaces = null; } if (!isset($wgGoogleMapsTemplateVariables)) { $wgGoogleMapsTemplateVariables = false; } // make poly-lines work with IE in MW 1.9+. See MW bug #7667 if (is_array($wgXhtmlNamespaces)) { $wgXhtmlNamespaces['v'] = 'urn:schemas-microsoft-com:vml'; $wgGoogleMapsEnablePaths = true; } // determine the proper API key. if any of the array keys in the mApiKeys // array matches the current page URL, that key is used. otherwise, the // default in mApiKey is used. $longest = 0; if (is_array($wgGoogleMapsKeys)) { foreach (array_keys($wgGoogleMapsKeys) as $key) { $path = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; if (strlen($key) > $longest && substr($path, 0, strlen($key)) == $key) { $longest = strlen($key); $wgGoogleMapsKey = $wgGoogleMapsKeys[$key]; } } } // add the google mime types $magic = null; if (version_compare($wgVersion, "1.8") >= 0) { $magic = MimeMagic::singleton(); } else { $magic = wfGetMimeMagic(); } $magic->mExtToMime['kml'] = 'application/vnd.google-earth.kml+xml'; $magic->mExtToMime['kmz'] = 'application/vnd.google-earth.kmz'; // instantiate the extension $wgGoogleMaps = new GoogleMaps($wgGoogleMapsKey, $wgGoogleMapsUrlPath, $wgGoogleMapsEnablePaths, $wgGoogleMapsDefaults, $wgGoogleMapsMessages, $wgGoogleMapsCustomMessages, $wgGoogleMapsTemplateVariables, $wgJsMimeType, $wgLang, $wgProxyKey, $wgTitle); // This hook will add the interactive editing map to the article edit page. // This hook was introduced in MW 1.6 $editHook = array($wgGoogleMaps, 'editForm'); if (version_compare($wgVersion, "1.6") >= 0) { if (!$wgGoogleMapsDisableEditorsMap) { $wgHooks['EditPageBeforeEditToolbar'][] = $editHook; } } // This hook will do some post-processing on the javascript that has been added // to an article. $hook = 'wfGoogleMaps_CommentJS'; if (isset($wgHooks['ParserAfterTidy']) && is_array($wgHooks['ParserAfterTidy'])) { array_unshift($wgHooks['ParserAfterTidy'], $hook); } else { $wgHooks['ParserAfterTidy'] = array($hook); } // This hook will be called any time the parser encounters a <googlemap> // tag in an article. This will render the actual Google map code in // the article. /* // This is no longer needed due to wfGoogleMaps_InstallParser if( version_compare( $wgVersion, "1.6" ) >= 0 ) { $wgParser->setHook( 'googlemap', array( $wgGoogleMaps, 'render16' ) ); $wgParser->setHook( 'googlemapkml', array( $wgGoogleMaps, 'renderKmlLink' ) ); } else { $wgParser->setHook( 'googlemap', 'wfGoogleMaps_Render15' ); $wgParser->setHook( 'googlemapkml', 'wfGoogleMaps_RenderKmlLink15' ); } */ // Set up the special page $wgSpecialPages['GoogleMapsKML'] = array('GoogleMapsKML', 'GoogleMapsKML'); }
/** * Restore all or specified deleted revisions to the given file. * Permissions and logging are left to the caller. * * May throw database exceptions on error. * * @param $versions set of record ids of deleted items to restore, * or empty to restore all revisions. * @return the number of file revisions restored if successful, * or false on failure */ function restore($versions = array()) { if (!FileStore::lock()) { wfDebug(__METHOD__ . " could not acquire filestore lock\n"); return false; } $transaction = new FSTransaction(); try { $dbw = wfGetDB(DB_MASTER); $dbw->begin(); // Re-confirm whether this image presently exists; // if no we'll need to create an image record for the // first item we restore. $exists = $dbw->selectField('image', '1', array('img_name' => $this->name), __METHOD__); // Fetch all or selected archived revisions for the file, // sorted from the most recent to the oldest. $conditions = array('fa_name' => $this->name); if ($versions) { $conditions['fa_id'] = $versions; } $result = $dbw->select('filearchive', '*', $conditions, __METHOD__, array('ORDER BY' => 'fa_timestamp DESC')); if ($dbw->numRows($result) < count($versions)) { // There's some kind of conflict or confusion; // we can't restore everything we were asked to. wfDebug(__METHOD__ . ": couldn't find requested items\n"); $dbw->rollback(); FileStore::unlock(); return false; } if ($dbw->numRows($result) == 0) { // Nothing to do. wfDebug(__METHOD__ . ": nothing to do\n"); $dbw->rollback(); FileStore::unlock(); return true; } $revisions = 0; while ($row = $dbw->fetchObject($result)) { $revisions++; $store = FileStore::get($row->fa_storage_group); if (!$store) { wfDebug(__METHOD__ . ": skipping row with no file.\n"); continue; } if ($revisions == 1 && !$exists) { $destDir = wfImageDir($row->fa_name); if (!is_dir($destDir)) { wfMkdirParents($destDir); } $destPath = $destDir . DIRECTORY_SEPARATOR . $row->fa_name; // We may have to fill in data if this was originally // an archived file revision. if (is_null($row->fa_metadata)) { $tempFile = $store->filePath($row->fa_storage_key); $metadata = serialize($this->retrieveExifData($tempFile)); $magic = wfGetMimeMagic(); $mime = $magic->guessMimeType($tempFile, true); $media_type = $magic->getMediaType($tempFile, $mime); list($major_mime, $minor_mime) = self::splitMime($mime); } else { $metadata = $row->fa_metadata; $major_mime = $row->fa_major_mime; $minor_mime = $row->fa_minor_mime; $media_type = $row->fa_media_type; } $table = 'image'; $fields = array('img_name' => $row->fa_name, 'img_size' => $row->fa_size, 'img_width' => $row->fa_width, 'img_height' => $row->fa_height, 'img_metadata' => $metadata, 'img_bits' => $row->fa_bits, 'img_media_type' => $media_type, 'img_major_mime' => $major_mime, 'img_minor_mime' => $minor_mime, 'img_description' => $row->fa_description, 'img_user' => $row->fa_user, 'img_user_text' => $row->fa_user_text, 'img_timestamp' => $row->fa_timestamp); } else { $archiveName = $row->fa_archive_name; if ($archiveName == '') { // This was originally a current version; we // have to devise a new archive name for it. // Format is <timestamp of archiving>!<name> $archiveName = wfTimestamp(TS_MW, $row->fa_deleted_timestamp) . '!' . $row->fa_name; } $destDir = wfImageArchiveDir($row->fa_name); if (!is_dir($destDir)) { wfMkdirParents($destDir); } $destPath = $destDir . DIRECTORY_SEPARATOR . $archiveName; $table = 'oldimage'; $fields = array('oi_name' => $row->fa_name, 'oi_archive_name' => $archiveName, 'oi_size' => $row->fa_size, 'oi_width' => $row->fa_width, 'oi_height' => $row->fa_height, 'oi_bits' => $row->fa_bits, 'oi_description' => $row->fa_description, 'oi_user' => $row->fa_user, 'oi_user_text' => $row->fa_user_text, 'oi_timestamp' => $row->fa_timestamp); } $dbw->insert($table, $fields, __METHOD__); /// @fixme this delete is not totally safe, potentially $dbw->delete('filearchive', array('fa_id' => $row->fa_id), __METHOD__); // Check if any other stored revisions use this file; // if so, we shouldn't remove the file from the deletion // archives so they will still work. $useCount = $dbw->selectField('filearchive', 'COUNT(*)', array('fa_storage_group' => $row->fa_storage_group, 'fa_storage_key' => $row->fa_storage_key), __METHOD__); if ($useCount == 0) { wfDebug(__METHOD__ . ": nothing else using {$row->fa_storage_key}, will deleting after\n"); $flags = FileStore::DELETE_ORIGINAL; } else { $flags = 0; } $transaction->add($store->export($row->fa_storage_key, $destPath, $flags)); } $dbw->immediateCommit(); } catch (MWException $e) { wfDebug(__METHOD__ . " caught error, aborting\n"); $transaction->rollback(); throw $e; } $transaction->commit(); FileStore::unlock(); if ($revisions > 0) { if (!$exists) { wfDebug(__METHOD__ . " restored {$revisions} items, creating a new current\n"); // Update site_stats $site_stats = $dbw->tableName('site_stats'); $dbw->query("UPDATE {$site_stats} SET ss_images=ss_images+1", __METHOD__); $this->purgeEverything(); } else { wfDebug(__METHOD__ . " restored {$revisions} as archived versions\n"); $this->purgeDescription(); } } return $revisions; }