API
4 days ago
Access
1 month ago
Application
1 month ago
Archive
1 month ago
ArchiveProcessor
4 days ago
Archiver
2 years ago
AssetManager
1 month ago
Auth
1 month ago
Category
1 month ago
Changes
3 months ago
CliMulti
1 month ago
Columns
4 days ago
Concurrency
1 month ago
Config
1 month ago
Container
3 months ago
CronArchive
1 month ago
DataAccess
4 days ago
DataFiles
2 years ago
DataTable
1 month ago
Db
4 days ago
DeviceDetector
1 year ago
Email
2 years ago
Exception
6 months ago
Http
4 days ago
Intl
5 months ago
Log
2 years ago
Mail
1 year ago
Measurable
8 months ago
Menu
1 month ago
Metrics
1 month ago
Notification
8 months ago
Period
1 month ago
Plugin
4 days ago
Policy
3 months ago
ProfessionalServices
1 year ago
Report
1 year ago
ReportRenderer
4 days ago
Request
1 month ago
Scheduler
1 month ago
Segment
1 month ago
Session
4 days ago
Settings
4 days ago
Tracker
4 days ago
Translation
3 months ago
Twig
1 year ago
UpdateCheck
5 months ago
Updater
1 month ago
Updates
2 months ago
Validators
1 year ago
View
3 months ago
ViewDataTable
4 days ago
Visualization
4 days ago
Widget
1 month ago
.htaccess
2 years ago
Access.php
1 month ago
Archive.php
3 months ago
ArchiveProcessor.php
4 days ago
AssetManager.php
1 month ago
Auth.php
8 months ago
AuthResult.php
8 months ago
BaseFactory.php
2 years ago
Cache.php
1 month ago
CacheId.php
6 months ago
CliMulti.php
1 month ago
Common.php
1 month ago
Config.php
1 month ago
Console.php
5 months ago
Context.php
2 years ago
Cookie.php
1 month ago
CronArchive.php
1 month ago
DI.php
5 months ago
DataArray.php
1 month ago
DataTable.php
4 days ago
Date.php
3 months ago
Db.php
3 months ago
DbHelper.php
1 month ago
Development.php
1 month ago
ErrorHandler.php
8 months ago
EventDispatcher.php
3 months ago
ExceptionHandler.php
6 months ago
FileIntegrity.php
1 month ago
Filechecks.php
1 year ago
Filesystem.php
4 days ago
FrontController.php
1 month ago
Http.php
4 days ago
IP.php
1 year ago
Log.php
5 months ago
LogDeleter.php
1 month ago
Mail.php
1 year ago
Metrics.php
3 months ago
NoAccessException.php
2 years ago
Nonce.php
8 months ago
Notification.php
3 months ago
NumberFormatter.php
1 month ago
Option.php
1 month ago
Period.php
1 month ago
Piwik.php
1 month ago
Plugin.php
3 months ago
Process.php
3 months ago
Profiler.php
1 month ago
ProxyHeaders.php
6 months ago
ProxyHttp.php
1 month ago
QuickForm2.php
5 months ago
RankingQuery.php
3 months ago
ReportRenderer.php
4 days ago
Request.php
3 months ago
Segment.php
4 days ago
Sequence.php
1 month ago
Session.php
2 months ago
SettingsPiwik.php
1 month ago
SettingsServer.php
1 month ago
Singleton.php
2 years ago
Site.php
3 months ago
SiteContentDetector.php
4 days ago
SupportedBrowser.php
2 years ago
TCPDF.php
1 year ago
Theme.php
1 month ago
Timer.php
1 month ago
Tracker.php
1 month ago
Twig.php
4 days ago
Unzip.php
1 year ago
UpdateCheck.php
3 months ago
Updater.php
1 month ago
UpdaterErrorException.php
2 years ago
Updates.php
1 month ago
Url.php
1 month ago
UrlHelper.php
3 months ago
Version.php
4 days ago
View.php
1 month ago
bootstrap.php
1 year ago
dispatch.php
2 years ago
testMinimumPhpVersion.php
8 months ago
Filechecks.php
211 lines
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * Matomo - free/libre analytics platform |
| 5 | * |
| 6 | * @link https://matomo.org |
| 7 | * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later |
| 8 | */ |
| 9 | namespace Piwik; |
| 10 | |
| 11 | use Piwik\Exception\MissingFilePermissionException; |
| 12 | class Filechecks |
| 13 | { |
| 14 | /** |
| 15 | * Check if this installation can be auto-updated. |
| 16 | * For performance, we look for clues rather than an exhaustive test. |
| 17 | * |
| 18 | * @return bool |
| 19 | */ |
| 20 | public static function canAutoUpdate() |
| 21 | { |
| 22 | if (!is_writable(PIWIK_INCLUDE_PATH . '/') || !is_writable(PIWIK_DOCUMENT_ROOT . '/index.php') || !is_writable(PIWIK_INCLUDE_PATH . '/core') || !is_writable(PIWIK_DOCUMENT_ROOT . '/config/global.ini.php')) { |
| 23 | return \false; |
| 24 | } |
| 25 | return \true; |
| 26 | } |
| 27 | /** |
| 28 | * Checks if directories are writable and create them if they do not exist. |
| 29 | * |
| 30 | * @param array $directoriesToCheck array of directories to check - if not given default Piwik directories that needs write permission are checked |
| 31 | * @return array directory name => true|false (is writable) |
| 32 | */ |
| 33 | public static function checkDirectoriesWritable($directoriesToCheck) |
| 34 | { |
| 35 | $resultCheck = array(); |
| 36 | foreach ($directoriesToCheck as $directoryToCheck) { |
| 37 | \Piwik\Filesystem::mkdir($directoryToCheck); |
| 38 | $directory = \Piwik\Filesystem::realpath($directoryToCheck); |
| 39 | if ($directory !== \false) { |
| 40 | $resultCheck[$directory] = is_writable($directoryToCheck); |
| 41 | } |
| 42 | } |
| 43 | return $resultCheck; |
| 44 | } |
| 45 | /** |
| 46 | * Checks that the directories Piwik needs write access are actually writable |
| 47 | * Displays a nice error page if permissions are missing on some directories |
| 48 | * |
| 49 | * @param array $directoriesToCheck Array of directory names to check |
| 50 | */ |
| 51 | public static function dieIfDirectoriesNotWritable($directoriesToCheck = null) |
| 52 | { |
| 53 | $resultCheck = self::checkDirectoriesWritable($directoriesToCheck); |
| 54 | if (array_search(\false, $resultCheck) === \false) { |
| 55 | return; |
| 56 | } |
| 57 | $directoryList = ''; |
| 58 | foreach ($resultCheck as $dir => $bool) { |
| 59 | $realpath = \Piwik\Filesystem::realpath($dir); |
| 60 | if (!empty($realpath) && $bool === \false) { |
| 61 | $directoryList .= self::getMakeWritableCommand($realpath); |
| 62 | } |
| 63 | } |
| 64 | // Also give the chown since the chmod is only 755 |
| 65 | if (!\Piwik\SettingsServer::isWindows()) { |
| 66 | $realpath = \Piwik\Filesystem::realpath(PIWIK_INCLUDE_PATH . '/'); |
| 67 | $directoryList = "<code>chown -R " . self::getUserAndGroup() . " " . $realpath . "</code><br />" . $directoryList; |
| 68 | } |
| 69 | $optionalUserInfo = ''; |
| 70 | if (function_exists('shell_exec')) { |
| 71 | $currentUser = self::getUser(); |
| 72 | if (!empty($currentUser)) { |
| 73 | $optionalUserInfo = " (running as user '" . $currentUser . "')"; |
| 74 | } |
| 75 | } |
| 76 | $directoryMessage = "<p><b>Matomo couldn't write to some directories {$optionalUserInfo}</b>.</p>"; |
| 77 | $directoryMessage .= "<p>Try to Execute the following commands on your server, to allow Write access on these directories" . ":</p>" . "<blockquote>{$directoryList}</blockquote>" . "<p>If this doesn't work, you can try to create the directories with your FTP software, and set the CHMOD to 0755 (or 0777 if 0755 is not enough). To do so with your FTP software, right click on the directories then click permissions.</p>" . "<p>After applying the modifications, you can <a href='index.php'>refresh the page</a>.</p>" . "<p>If you need more help, try <a target='_blank' rel='noreferrer noopener' href='https://matomo.org'>Matomo.org</a>.</p>"; |
| 78 | $ex = new MissingFilePermissionException($directoryMessage); |
| 79 | $ex->setIsHtmlMessage(); |
| 80 | throw $ex; |
| 81 | } |
| 82 | /** |
| 83 | * Returns the help message when the auto update can't run because of missing permissions |
| 84 | * |
| 85 | * @return string |
| 86 | */ |
| 87 | public static function getAutoUpdateMakeWritableMessage() |
| 88 | { |
| 89 | $realpath = \Piwik\Filesystem::realpath(PIWIK_INCLUDE_PATH . '/'); |
| 90 | $message = ''; |
| 91 | if (!\Piwik\SettingsServer::isWindows()) { |
| 92 | $message .= "<br /><code>" . self::getCommandToChangeOwnerOfPiwikFiles() . "</code><br />"; |
| 93 | } |
| 94 | $message .= self::getMakeWritableCommand($realpath); |
| 95 | if (!\Piwik\SettingsServer::isWindows()) { |
| 96 | $message .= '<code>chmod 755 ' . $realpath . '/console</code><br />'; |
| 97 | } |
| 98 | $message .= 'After you execute these commands (or change permissions via your FTP software), refresh the page and you should be able to use the "Automatic Update" feature.'; |
| 99 | return $message; |
| 100 | } |
| 101 | /** |
| 102 | * Returns friendly error message explaining how to fix permissions |
| 103 | * |
| 104 | * @param string $path to the directory missing permissions |
| 105 | * @return string Error message |
| 106 | */ |
| 107 | public static function getErrorMessageMissingPermissions($path) |
| 108 | { |
| 109 | $message = "Please check that the web server has enough permission to write to these files/directories:<br />"; |
| 110 | if (\Piwik\SettingsServer::isWindows()) { |
| 111 | $message .= "On Windows, check that the folder is not read only and is writable.\n\n\t\t\t\t\t\tYou can try to execute:<br />"; |
| 112 | } else { |
| 113 | $message .= "For example, on a GNU/Linux server if your Apache httpd user is " . \Piwik\Common::sanitizeInputValue(self::getUser()) . ", you can try to execute:<br />\n" . "<code>chown -R " . \Piwik\Common::sanitizeInputValue(self::getUserAndGroup()) . " " . \Piwik\Common::sanitizeInputValue($path) . "</code><br />"; |
| 114 | } |
| 115 | $message .= self::getMakeWritableCommand($path); |
| 116 | return $message; |
| 117 | } |
| 118 | public static function getUserAndGroup() |
| 119 | { |
| 120 | $user = self::getUser(); |
| 121 | if (!function_exists('shell_exec')) { |
| 122 | return $user . ':' . $user; |
| 123 | } |
| 124 | $group = trim(shell_exec('groups ' . $user . ' | cut -f3 -d" "') ?? ''); |
| 125 | if (empty($group) && function_exists('posix_getegid') && function_exists('posix_getgrgid')) { |
| 126 | $currentGroupId = posix_getegid(); |
| 127 | $group = posix_getpwuid($currentGroupId); |
| 128 | if (!empty($group['name'])) { |
| 129 | $group = $group['name']; |
| 130 | } else { |
| 131 | $group = $currentGroupId; |
| 132 | } |
| 133 | } |
| 134 | if (empty($group)) { |
| 135 | $group = 'www-data'; |
| 136 | } |
| 137 | return $user . ':' . $group; |
| 138 | } |
| 139 | public static function getUser() |
| 140 | { |
| 141 | if (function_exists('shell_exec')) { |
| 142 | return trim(shell_exec('whoami') ?? ''); |
| 143 | } |
| 144 | $currentUser = get_current_user(); |
| 145 | if (empty($currentUser) && function_exists('posix_geteuid') && function_exists('posix_getpwuid')) { |
| 146 | $currentUserId = posix_geteuid(); |
| 147 | $user = posix_getpwuid($currentUserId); |
| 148 | if (!empty($user['name'])) { |
| 149 | $currentUser = $user['name']; |
| 150 | } else { |
| 151 | $currentUser = $currentUserId; |
| 152 | } |
| 153 | } |
| 154 | if (empty($currentUser)) { |
| 155 | $currentUser = 'www-data'; |
| 156 | } |
| 157 | return $currentUser; |
| 158 | } |
| 159 | /** |
| 160 | * Returns the help text displayed to suggest which command to run to give writable access to a file or directory |
| 161 | * |
| 162 | * @param string $realpath |
| 163 | * @return string |
| 164 | */ |
| 165 | private static function getMakeWritableCommand($realpath) |
| 166 | { |
| 167 | $realpath = \Piwik\Common::sanitizeInputValue($realpath); |
| 168 | if (\Piwik\SettingsServer::isWindows()) { |
| 169 | return "<code>cacls {$realpath} /t /g " . \Piwik\Common::sanitizeInputValue(self::getUser()) . ":f</code><br />\n"; |
| 170 | } |
| 171 | return "<code>find {$realpath} -type f -exec chmod 644 {} \\;</code><br /><code>find {$realpath} -type d -exec chmod 755 {} \\;</code><br />"; |
| 172 | } |
| 173 | /** |
| 174 | * @return string |
| 175 | */ |
| 176 | public static function getCommandToChangeOwnerOfPiwikFiles() |
| 177 | { |
| 178 | $realpath = \Piwik\Filesystem::realpath(PIWIK_INCLUDE_PATH . '/'); |
| 179 | return "chown -R " . self::getUserAndGroup() . " " . $realpath; |
| 180 | } |
| 181 | public static function getOwnerOfPiwikFiles() |
| 182 | { |
| 183 | $index = \Piwik\Filesystem::realpath(PIWIK_INCLUDE_PATH . '/index.php'); |
| 184 | $stat = stat($index); |
| 185 | if (!$stat) { |
| 186 | return ''; |
| 187 | } |
| 188 | if (function_exists('posix_getgrgid')) { |
| 189 | $group = posix_getgrgid($stat[5]); |
| 190 | if (!empty($group['name'])) { |
| 191 | $group = $group['name']; |
| 192 | } else { |
| 193 | $group = $stat[5]; |
| 194 | } |
| 195 | } else { |
| 196 | return ''; |
| 197 | } |
| 198 | if (function_exists('posix_getpwuid')) { |
| 199 | $user = posix_getpwuid($stat[4]); |
| 200 | if (!empty($user['name'])) { |
| 201 | $user = $user['name']; |
| 202 | } else { |
| 203 | $user = $stat[4]; |
| 204 | } |
| 205 | } else { |
| 206 | return ''; |
| 207 | } |
| 208 | return "{$user}:{$group}"; |
| 209 | } |
| 210 | } |
| 211 |