init web ems all

This commit is contained in:
agtuser
2024-09-27 17:13:36 +08:00
parent 81c97acbe9
commit 5cc56f8078
4263 changed files with 798779 additions and 0 deletions

View File

@@ -0,0 +1,402 @@
# phpMyAdmin Advisory rules file
#
# Use only UNIX style newlines
#
# This file is being parsed by Advisor.php, which should handle syntax
# errors correctly. However, PHP Warnings and the like are being consumed by
# the phpMyAdmin error handler, so those won't show up E.g.: Justification line
# is empty because you used an unescape percent sign, sprintf() returns an
# empty string and no warning/error is shown
#
# Rule Syntax:
# 'rule' identifier[the name of the rule] eexpr [an optional precondition]
# expr [variable or value calculation used for the test]
# expr [test, if evaluted to 'true' it fires the rule. Use 'value' to insert the calculated value (without quotes)]
# string [the issue (what is the problem?)]
# string [the recommendation (how do i fix it?)]
# formatted-string '|' comma-seperated-expr [the justification (result of the calculated value / why did this rule fire?)]
# comma-seperated-expr: expr(,expr)*
# eexpr: [expr] - expr enclosed in []
# expr: a php code literal with extras:
# - variable names are replaced with their respective values
# - fired('name of rule') is replaced with true/false when given rule has
# been fired. Note however that this is a very simple rules engine.
# Rules are only checked in sequential order as they are written down
# here. If given rule has not been checked yet, fired() will always
# evaluate to false
# - 'value' is replaced with the calculated value. If it is a string, it
# will be put within single quotes
# - other than that you may use any php function, initialized variable or
# constant
#
# identifier: A string enclosed in single quotes
# string: A quoteless string, may contain HTML. Variable names enclosed in
# curly braces are replaced with links to directly edit this variable.
# e.g. {tmp_table_size}
# formatted-string: You may use classic php sprintf() string formatting here,
# the arguments must be appended after a trailing pipe (|) as
# mentioned in above syntax percent signs (%) are
# automatically escaped (%%) in the following cases: When
# followed by a space, dot or comma and at the end of the
# line)
#
# Comments start with #
#
# Queries
rule 'Uptime below one day'
Uptime
value < 86400
Uptime is less than 1 day, performance tuning may not be accurate.
To have more accurate averages it is recommended to let the server run for longer than a day before running this analyzer
The uptime is only %s | ADVISOR_timespanFormat(Uptime)
rule 'Questions below 1,000'
Questions
value < 1000
Fewer than 1,000 questions have been run against this server. The recommendations may not be accurate.
Let the server run for a longer time until it has executed a greater amount of queries.
Current amount of Questions: %s | Questions
rule 'Percentage of slow queries' [Questions > 0]
Slow_queries / Questions * 100
value >= 5
There is a lot of slow queries compared to the overall amount of Queries.
You might want to increase {long_query_time} or optimize the queries listed in the slow query log
The slow query rate should be below 5%, your value is %s%. | round(value,2)
rule 'Slow query rate' [Questions > 0]
(Slow_queries / Questions * 100) / Uptime
value * 60 * 60 > 1
There is a high percentage of slow queries compared to the server uptime.
You might want to increase {long_query_time} or optimize the queries listed in the slow query log
You have a slow query rate of %s per hour, you should have less than 1% per hour. | ADVISOR_bytime(value,2)
rule 'Long query time'
long_query_time
value >= 10
{long_query_time} is set to 10 seconds or more, thus only slow queries that take above 10 seconds are logged.
It is suggested to set {long_query_time} to a lower value, depending on your environment. Usually a value of 1-5 seconds is suggested.
long_query_time is currently set to %ds. | value
rule 'Slow query logging' [PMA_MYSQL_INT_VERSION < 50600]
log_slow_queries
value == 'OFF'
The slow query log is disabled.
Enable slow query logging by setting {log_slow_queries} to 'ON'. This will help troubleshooting badly performing queries.
log_slow_queries is set to 'OFF'
rule 'Slow query logging' [PMA_MYSQL_INT_VERSION >= 50600]
slow_query_log
value == 'OFF'
The slow query log is disabled.
Enable slow query logging by setting {slow_query_log} to 'ON'. This will help troubleshooting badly performing queries.
slow_query_log is set to 'OFF'
#
# versions
rule 'Release Series'
version
substr(value,0,2) <= '5.' && substr(value,2,1) < 1
The MySQL server version less than 5.1.
You should upgrade, as MySQL 5.1 has improved performance, and MySQL 5.5 even more so.
Current version: %s | value
rule 'Minor Version' [! fired('Release Series')]
version
substr(value,0,2) <= '5.' && substr(value,2,1) <= 1 && substr(value,4,2) < 30
Version less than 5.1.30 (the first GA release of 5.1).
You should upgrade, as recent versions of MySQL 5.1 have improved performance and MySQL 5.5 even more so.
Current version: %s | value
rule 'Minor Version' [! fired('Release Series')]
version
substr(value,0,1) == 5 && substr(value,2,1) == 5 && substr(value,4,2) < 8
Version less than 5.5.8 (the first GA release of 5.5).
You should upgrade, to a stable version of MySQL 5.5.
Current version: %s | value
rule 'Distribution'
version_comment
preg_match('/source/i',value)
Version is compiled from source, not a MySQL official binary.
If you did not compile from source, you may be using a package modified by a distribution. The MySQL manual only is accurate for official MySQL binaries, not any package distributions (such as RedHat, Debian/Ubuntu etc).
'source' found in version_comment
rule 'Distribution'
version_comment
preg_match('/percona/i',value)
The MySQL manual only is accurate for official MySQL binaries.
Percona documentation is at <a href="https://www.percona.com/software/documentation/">https://www.percona.com/software/documentation/</a>
'percona' found in version_comment
rule 'MySQL Architecture'
system_memory
value > 3072*1024 && !preg_match('/64/',version_compile_machine) && !preg_match('/64/',version_compile_os)
MySQL is not compiled as a 64-bit package.
Your memory capacity is above 3 GiB (assuming the Server is on localhost), so MySQL might not be able to access all of your memory. You might want to consider installing the 64-bit version of MySQL.
Available memory on this host: %s | ADVISOR_formatByteDown(value*1024, 2, 2)
#
# Query cache
rule 'Query caching method' [!fired('Query cache disabled')]
Questions / Uptime
value > 100
Suboptimal caching method.
You are using the MySQL Query cache with a fairly high traffic database. It might be worth considering to use <a href="https://dev.mysql.com/doc/refman/5.5/en/ha-memcached.html">memcached</a> instead of the MySQL Query cache, especially if you have multiple slaves.
The query cache is enabled and the server receives %d queries per second. This rule fires if there is more than 100 queries per second. | round(value,1)
#
# Sorts
rule 'Percentage of sorts that cause temporary tables' [Sort_scan + Sort_range > 0]
Sort_merge_passes / (Sort_scan + Sort_range) * 100
value > 10
Too many sorts are causing temporary tables.
Consider increasing {sort_buffer_size} and/or {read_rnd_buffer_size}, depending on your system memory limits.
%s% of all sorts cause temporary tables, this value should be lower than 10%. | round(value,1)
rule 'Rate of sorts that cause temporary tables'
Sort_merge_passes / Uptime
value * 60 * 60 > 1
Too many sorts are causing temporary tables.
Consider increasing {sort_buffer_size} and/or {read_rnd_buffer_size}, depending on your system memory limits.
Temporary tables average: %s, this value should be less than 1 per hour. | ADVISOR_bytime(value,2)
rule 'Sort rows'
Sort_rows / Uptime
value * 60 >= 1
There are lots of rows being sorted.
While there is nothing wrong with a high amount of row sorting, you might want to make sure that the queries which require a lot of sorting use indexed columns in the ORDER BY clause, as this will result in much faster sorting.
Sorted rows average: %s | ADVISOR_bytime(value,2)
# Joins, scans
rule 'Rate of joins without indexes'
(Select_range_check + Select_scan + Select_full_join) / Uptime
value * 60 * 60 > 1
There are too many joins without indexes.
This means that joins are doing full table scans. Adding indexes for the columns being used in the join conditions will greatly speed up table joins.
Table joins average: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
rule 'Rate of reading first index entry'
Handler_read_first / Uptime
value * 60 * 60 > 1
The rate of reading the first index entry is high.
This usually indicates frequent full index scans. Full index scans are faster than table scans but require lots of CPU cycles in big tables, if those tables that have or had high volumes of UPDATEs and DELETEs, running 'OPTIMIZE TABLE' might reduce the amount of and/or speed up full index scans. Other than that full index scans can only be reduced by rewriting queries.
Index scans average: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
rule 'Rate of reading fixed position'
Handler_read_rnd / Uptime
value * 60 * 60 > 1
The rate of reading data from a fixed position is high.
This indicates that many queries need to sort results and/or do a full table scan, including join queries that do not use indexes. Add indexes where applicable.
Rate of reading fixed position average: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
rule 'Rate of reading next table row'
Handler_read_rnd_next / Uptime
value * 60 * 60 > 1
The rate of reading the next table row is high.
This indicates that many queries are doing full table scans. Add indexes where applicable.
Rate of reading next table row: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
# temp tables
rule 'Different tmp_table_size and max_heap_table_size'
tmp_table_size - max_heap_table_size
value !=0
{tmp_table_size} and {max_heap_table_size} are not the same.
If you have deliberately changed one of either: The server uses the lower value of either to determine the maximum size of in-memory tables. So if you wish to increase the in-memory table limit you will have to increase the other value as well.
Current values are tmp_table_size: %s, max_heap_table_size: %s | ADVISOR_formatByteDown(tmp_table_size, 2, 2), ADVISOR_formatByteDown(max_heap_table_size, 2, 2)
rule 'Percentage of temp tables on disk' [Created_tmp_tables + Created_tmp_disk_tables > 0]
Created_tmp_disk_tables / (Created_tmp_tables + Created_tmp_disk_tables) * 100
value > 25
Many temporary tables are being written to disk instead of being kept in memory.
Increasing {max_heap_table_size} and {tmp_table_size} might help. However some temporary tables are always being written to disk, independent of the value of these variables. To eliminate these you will have to rewrite your queries to avoid those conditions (Within a temporary table: Presence of a BLOB or TEXT column or presence of a column bigger than 512 bytes) as mentioned in the beginning of an <a href="https://www.facebook.com/note.php?note_id=10150111255065841&comments">Article by the Pythian Group</a>
%s% of all temporary tables are being written to disk, this value should be below 25% | round(value,1)
rule 'Temp disk rate' [!fired('Percentage of temp tables on disk')]
Created_tmp_disk_tables / Uptime
value * 60 * 60 > 1
Many temporary tables are being written to disk instead of being kept in memory.
Increasing {max_heap_table_size} and {tmp_table_size} might help. However some temporary tables are always being written to disk, independent of the value of these variables. To eliminate these you will have to rewrite your queries to avoid those conditions (Within a temporary table: Presence of a BLOB or TEXT column or presence of a column bigger than 512 bytes) as mentioned in the <a href="https://dev.mysql.com/doc/refman/5.5/en/internal-temporary-tables.html">MySQL Documentation</a>
Rate of temporary tables being written to disk: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
#
# MyISAM index cache
rule 'MyISAM key buffer size'
key_buffer_size
value == 0
Key buffer is not initialized. No MyISAM indexes will be cached.
Set {key_buffer_size} depending on the size of your MyISAM indexes. 64M is a good start.
key_buffer_size is 0
rule 'Max % MyISAM key buffer ever used' [key_buffer_size > 0]
Key_blocks_used * key_cache_block_size / key_buffer_size * 100
value < 95
MyISAM key buffer (index cache) % used is low.
You may need to decrease the size of {key_buffer_size}, re-examine your tables to see if indexes have been removed, or examine queries and expectations about what indexes are being used.
max % MyISAM key buffer ever used: %s%, this value should be above 95% | round(value,1)
# Don't fire if above rule fired - we don't need the same advice twice
rule 'Percentage of MyISAM key buffer used' [key_buffer_size > 0 && !fired('Max % MyISAM key buffer ever used')]
( 1 - Key_blocks_unused * key_cache_block_size / key_buffer_size) * 100
value < 95
MyISAM key buffer (index cache) % used is low.
You may need to decrease the size of {key_buffer_size}, re-examine your tables to see if indexes have been removed, or examine queries and expectations about what indexes are being used.
% MyISAM key buffer used: %s%, this value should be above 95% | round(value,1)
rule 'Percentage of index reads from memory' [Key_read_requests > 0]
100 - (Key_reads / Key_read_requests * 100)
value < 95
The % of indexes that use the MyISAM key buffer is low.
You may need to increase {key_buffer_size}.
Index reads from memory: %s%, this value should be above 95% | round(value,1)
#
# other caches
rule 'Rate of table open'
Opened_tables / Uptime
value*60*60 > 10
The rate of opening tables is high.
Opening tables requires disk I/O which is costly. Increasing {table_open_cache} might avoid this.
Opened table rate: %s, this value should be less than 10 per hour | ADVISOR_bytime(value,2)
rule 'Percentage of used open files limit'
Open_files / open_files_limit * 100
value > 85
The number of open files is approaching the max number of open files. You may get a "Too many open files" error.
Consider increasing {open_files_limit}, and check the error log when restarting after changing {open_files_limit}.
The number of opened files is at %s% of the limit. It should be below 85% | round(value,1)
rule 'Rate of open files'
Open_files / Uptime
value * 60 * 60 > 5
The rate of opening files is high.
Consider increasing {open_files_limit}, and check the error log when restarting after changing {open_files_limit}.
Opened files rate: %s, this value should be less than 5 per hour | ADVISOR_bytime(value,2)
rule 'Immediate table locks %' [Table_locks_waited + Table_locks_immediate > 0]
Table_locks_immediate / (Table_locks_waited + Table_locks_immediate) * 100
value < 95
Too many table locks were not granted immediately.
Optimize queries and/or use InnoDB to reduce lock wait.
Immediate table locks: %s%, this value should be above 95% | round(value,1)
rule 'Table lock wait rate'
Table_locks_waited / Uptime
value * 60 * 60 > 1
Too many table locks were not granted immediately.
Optimize queries and/or use InnoDB to reduce lock wait.
Table lock wait rate: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
rule 'Thread cache'
thread_cache_size
value < 1
Thread cache is disabled, resulting in more overhead from new connections to MySQL.
Enable the thread cache by setting {thread_cache_size} > 0.
The thread cache is set to 0
rule 'Thread cache hit rate %' [thread_cache_size > 0]
100 - Threads_created / Connections
value < 80
Thread cache is not efficient.
Increase {thread_cache_size}.
Thread cache hitrate: %s%, this value should be above 80% | round(value,1)
rule 'Threads that are slow to launch' [slow_launch_time > 0]
Slow_launch_threads
value > 0
There are too many threads that are slow to launch.
This generally happens in case of general system overload as it is pretty simple operations. You might want to monitor your system load carefully.
%s thread(s) took longer than %s seconds to start, it should be 0 | value, slow_launch_time
rule 'Slow launch time'
slow_launch_time
value > 2
Slow_launch_time is above 2s.
Set {slow_launch_time} to 1s or 2s to correctly count threads that are slow to launch.
slow_launch_time is set to %s | value
#
#Connections
rule 'Percentage of used connections'
Max_used_connections / max_connections * 100
value > 80
The maximum amount of used connections is getting close to the value of {max_connections}.
Increase {max_connections}, or decrease {wait_timeout} so that connections that do not close database handlers properly get killed sooner. Make sure the code closes database handlers properly.
Max_used_connections is at %s% of max_connections, it should be below 80% | round(value,1)
rule 'Percentage of aborted connections'
Aborted_connects / Connections * 100
value > 1
Too many connections are aborted.
Connections are usually aborted when they cannot be authorized. <a href="https://www.percona.com/blog/2008/08/23/how-to-track-down-the-source-of-aborted_connects/">This article</a> might help you track down the source.
%s% of all connections are aborted. This value should be below 1% | round(value,1)
rule 'Rate of aborted connections'
Aborted_connects / Uptime
value * 60 * 60 > 1
Too many connections are aborted.
Connections are usually aborted when they cannot be authorized. <a href="https://www.percona.com/blog/2008/08/23/how-to-track-down-the-source-of-aborted_connects/">This article</a> might help you track down the source.
Aborted connections rate is at %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
rule 'Percentage of aborted clients'
Aborted_clients / Connections * 100
value > 2
Too many clients are aborted.
Clients are usually aborted when they did not close their connection to MySQL properly. This can be due to network issues or code not closing a database handler properly. Check your network and code.
%s% of all clients are aborted. This value should be below 2% | round(value,1)
rule 'Rate of aborted clients'
Aborted_clients / Uptime
value * 60 * 60 > 1
Too many clients are aborted.
Clients are usually aborted when they did not close their connection to MySQL properly. This can be due to network issues or code not closing a database handler properly. Check your network and code.
Aborted client rate is at %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
#
# InnoDB
rule 'Is InnoDB disabled?' [PMA_MYSQL_INT_VERSION < 50600]
have_innodb
value != "YES"
You do not have InnoDB enabled.
InnoDB is usually the better choice for table engines.
have_innodb is set to 'value'
rule 'InnoDB log size' [innodb_buffer_pool_size > 0]
innodb_log_file_size / innodb_buffer_pool_size * 100
value < 20 && innodb_log_file_size / (1024 * 1024) < 256
The InnoDB log file size is not an appropriate size, in relation to the InnoDB buffer pool.
Especially on a system with a lot of writes to InnoDB tables you should set {innodb_log_file_size} to 25% of {innodb_buffer_pool_size}. However the bigger this value, the longer the recovery time will be when database crashes, so this value should not be set much higher than 256 MiB. Please note however that you cannot simply change the value of this variable. You need to shutdown the server, remove the InnoDB log files, set the new value in my.cnf, start the server, then check the error logs if everything went fine. See also <a href="https://mysqldatabaseadministration.blogspot.com/2007/01/increase-innodblogfilesize-proper-way.html">this blog entry</a>
Your InnoDB log size is at %s% in relation to the InnoDB buffer pool size, it should not be below 20% | round(value,1)
rule 'Max InnoDB log size' [innodb_buffer_pool_size > 0 && innodb_log_file_size / innodb_buffer_pool_size * 100 < 30]
innodb_log_file_size / (1024 * 1024)
value > 256
The InnoDB log file size is inadequately large.
It is usually sufficient to set {innodb_log_file_size} to 25% of the size of {innodb_buffer_pool_size}. A very big {innodb_log_file_size} slows down the recovery time after a database crash considerably. See also <a href="https://www.percona.com/blog/2006/07/03/choosing-proper-innodb_log_file_size/">this Article</a>. You need to shutdown the server, remove the InnoDB log files, set the new value in my.cnf, start the server, then check the error logs if everything went fine. See also <a href="https://mysqldatabaseadministration.blogspot.com/2007/01/increase-innodblogfilesize-proper-way.html">this blog entry</a>
Your absolute InnoDB log size is %s MiB | round(value,1)
rule 'InnoDB buffer pool size' [system_memory > 0]
innodb_buffer_pool_size / system_memory * 100
value < 60
Your InnoDB buffer pool is fairly small.
The InnoDB buffer pool has a profound impact on performance for InnoDB tables. Assign all your remaining memory to this buffer. For database servers that use solely InnoDB as storage engine and have no other services (e.g. a web server) running, you may set this as high as 80% of your available memory. If that is not the case, you need to carefully assess the memory consumption of your other services and non-InnoDB-Tables and set this variable accordingly. If it is set too high, your system will start swapping, which decreases performance significantly. See also <a href="https://www.percona.com/blog/2007/11/03/choosing-innodb_buffer_pool_size/">this article</a>
You are currently using %s% of your memory for the InnoDB buffer pool. This rule fires if you are assigning less than 60%, however this might be perfectly adequate for your system if you don't have much InnoDB tables or other services running on the same machine. | value
#
# other
rule 'MyISAM concurrent inserts'
concurrent_insert
value === 0 || value === 'NEVER'
Enable {concurrent_insert} by setting it to 1
Setting {concurrent_insert} to 1 reduces contention between readers and writers for a given table. See also <a href="https://dev.mysql.com/doc/refman/5.5/en/concurrent-inserts.html">MySQL Documentation</a>
concurrent_insert is set to 0
# INSERT DELAYED USAGE
#Delayed_errors 0
#Delayed_insert_threads 0
#Delayed_writes 0
#Not_flushed_delayed_rows

View File

@@ -0,0 +1,57 @@
# phpMyAdmin Advisory rules file
#
# See doc in advisory_rules_generic.txt
#
#
# Query cache
# Lame: 'ON' == 0 is true, so you need to compare 'ON' == '0'
rule 'Query cache disabled'
query_cache_size
value == 0 || query_cache_type == 'OFF' || query_cache_type == '0'
The query cache is not enabled.
The query cache is known to greatly improve performance if configured correctly. Enable it by setting {query_cache_size} to a 2 digit MiB value and setting {query_cache_type} to 'ON'. <b>Note:</b> If you are using memcached, ignore this recommendation.
query_cache_size is set to 0 or query_cache_type is set to 'OFF'
rule 'Query cache efficiency (%)' [Com_select + Qcache_hits > 0 && !fired('Query cache disabled')]
Qcache_hits / (Com_select + Qcache_hits) * 100
value < 20
Query cache not running efficiently, it has a low hit rate.
Consider increasing {query_cache_limit}.
The current query cache hit rate of %s% is below 20% | round(value,1)
rule 'Query Cache usage' [!fired('Query cache disabled')]
100 - Qcache_free_memory / query_cache_size * 100
value < 80
Less than 80% of the query cache is being utilized.
This might be caused by {query_cache_limit} being too low. Flushing the query cache might help as well.
The current ratio of free query cache memory to total query cache size is %s%. It should be above 80% | round(value,1)
rule 'Query cache fragmentation' [!fired('Query cache disabled')]
Qcache_free_blocks / (Qcache_total_blocks / 2) * 100
value > 20
The query cache is considerably fragmented.
Severe fragmentation is likely to (further) increase Qcache_lowmem_prunes. This might be caused by many Query cache low memory prunes due to {query_cache_size} being too small. For a immediate but short lived fix you can flush the query cache (might lock the query cache for a long time). Carefully adjusting {query_cache_min_res_unit} to a lower value might help too, e.g. you can set it to the average size of your queries in the cache using this formula: (query_cache_size - qcache_free_memory) / qcache_queries_in_cache
The cache is currently fragmented by %s% , with 100% fragmentation meaning that the query cache is an alternating pattern of free and used blocks. This value should be below 20%. | round(value,1)
rule 'Query cache low memory prunes' [Qcache_inserts > 0 && !fired('Query cache disabled')]
Qcache_lowmem_prunes / Qcache_inserts * 100
value > 0.1
Cached queries are removed due to low query cache memory from the query cache.
You might want to increase {query_cache_size}, however keep in mind that the overhead of maintaining the cache is likely to increase with its size, so do this in small increments and monitor the results.
The ratio of removed queries to inserted queries is %s%. The lower this value is, the better (This rules firing limit: 0.1%) | round(value,1)
rule 'Query cache max size' [!fired('Query cache disabled')]
query_cache_size
value > 1024 * 1024 * 128
The query cache size is above 128 MiB. Big query caches may cause significant overhead that is required to maintain the cache.
Depending on your environment, it might be performance increasing to reduce this value.
Current query cache size: %s | ADVISOR_formatByteDown(value, 2, 2)
rule 'Query cache min result size' [!fired('Query cache disabled')]
query_cache_limit
value == 1024*1024
The max size of the result set in the query cache is the default of 1 MiB.
Changing {query_cache_limit} (usually by increasing) may increase efficiency. This variable determines the maximum size a query result may have to be inserted into the query cache. If there are many query results above 1 MiB that are well cacheable (many reads, little writes) then increasing {query_cache_limit} will increase efficiency. Whereas in the case of many query results being above 1 MiB that are not very well cacheable (often invalidated due to table updates) increasing {query_cache_limit} might reduce efficiency.
query_cache_limit is set to 1 MiB

View File

@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----

View File

@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----

View File

@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,16 @@
phpMyAdmin SSL certificates
===========================
This directory contains copy of root certificates used to sign phpmyadmin.net
and reports.phpmyadmin.net websites. It is used to allow operation on systems
where the certificates are missing or wrongly configured (happens on Windows
with wrongly compiled CURL).
Currently included SSL certificates:
* ISRG Root X1
* DST Root CA X3
See https://letsencrypt.org/certificates/ for more info on them.
In case of update, the filenames can be generated using c_rehash tool.

View File

@@ -0,0 +1,51 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----

View File

@@ -0,0 +1,29 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Get user's global privileges and some db-specific privileges
*
* @package PhpMyAdmin
*/
if (! defined('PHPMYADMIN')) {
exit;
}
use PhpMyAdmin\CheckUserPrivileges;
$checkUserPrivileges = new CheckUserPrivileges($GLOBALS['dbi']);
list($username, $hostname) = $GLOBALS['dbi']->getCurrentUserAndHost();
if ($username === '') { // MySQL is started with --skip-grant-tables
$GLOBALS['is_create_db_priv'] = true;
$GLOBALS['is_reload_priv'] = true;
$GLOBALS['db_to_create'] = '';
$GLOBALS['dbs_where_create_table_allowed'] = array('*');
$GLOBALS['dbs_to_test'] = false;
$GLOBALS['db_priv'] = true;
$GLOBALS['col_priv'] = true;
$GLOBALS['table_priv'] = true;
$GLOBALS['proc_priv'] = true;
} else {
$checkUserPrivileges->analyseShowGrant();
}

View File

@@ -0,0 +1,675 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* A simple rules engine, that parses and executes the rules in advisory_rules.txt.
* Adjusted to phpMyAdmin.
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin;
use Exception;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\SysInfo;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
/**
* Advisor class
*
* @package PhpMyAdmin
*/
class Advisor
{
const GENERIC_RULES_FILE = 'libraries/advisory_rules_generic.txt';
const BEFORE_MYSQL80003_RULES_FILE = 'libraries/advisory_rules_mysql_before80003.txt';
protected $dbi;
protected $variables;
protected $globals;
protected $parseResult;
protected $runResult;
protected $expression;
/**
* Constructor
*
* @param DatabaseInterface $dbi DatabaseInterface object
* @param ExpressionLanguage $expression ExpressionLanguage object
*/
public function __construct(DatabaseInterface $dbi, ExpressionLanguage $expression)
{
$this->dbi = $dbi;
$this->expression = $expression;
/*
* Register functions for ExpressionLanguage, we intentionally
* do not implement support for compile as we do not use it.
*/
$this->expression->register(
'round',
function (){},
function ($arguments, $num) {
return round($num);
}
);
$this->expression->register(
'substr',
function (){},
function ($arguments, $string, $start, $length) {
return substr($string, $start, $length);
}
);
$this->expression->register(
'preg_match',
function (){},
function ($arguments, $pattern , $subject) {
return preg_match($pattern, $subject);
}
);
$this->expression->register(
'ADVISOR_bytime',
function (){},
function ($arguments, $num, $precision) {
return self::byTime($num, $precision);
}
);
$this->expression->register(
'ADVISOR_timespanFormat',
function (){},
function ($arguments, $seconds) {
return self::timespanFormat($seconds);
}
);
$this->expression->register(
'ADVISOR_formatByteDown',
function (){},
function ($arguments, $value, $limes = 6, $comma = 0) {
return self::formatByteDown($value, $limes, $comma);
}
);
$this->expression->register(
'fired',
function (){},
function ($arguments, $value) {
if (!isset($this->runResult['fired'])) {
return 0;
}
// Did matching rule fire?
foreach ($this->runResult['fired'] as $rule) {
if ($rule['id'] == $value) {
return '1';
}
}
return '0';
}
);
/* Some global variables for advisor */
$this->globals = array(
'PMA_MYSQL_INT_VERSION' => $this->dbi->getVersion(),
);
}
/**
* Get variables
*
* @return mixed
*/
public function getVariables()
{
return $this->variables;
}
/**
* Set variables
*
* @param array $variables Variables
*
* @return Advisor
*/
public function setVariables(array $variables)
{
$this->variables = $variables;
return $this;
}
/**
* Set a variable and its value
*
* @param string|int $variable Variable to set
* @param mixed $value Value to set
*
* @return $this
*/
public function setVariable($variable, $value)
{
$this->variables[$variable] = $value;
return $this;
}
/**
* Get parseResult
*
* @return mixed
*/
public function getParseResult()
{
return $this->parseResult;
}
/**
* Set parseResult
*
* @param array $parseResult Parse result
*
* @return Advisor
*/
public function setParseResult(array $parseResult)
{
$this->parseResult = $parseResult;
return $this;
}
/**
* Get runResult
*
* @return mixed
*/
public function getRunResult()
{
return $this->runResult;
}
/**
* Set runResult
*
* @param array $runResult Run result
*
* @return Advisor
*/
public function setRunResult(array $runResult)
{
$this->runResult = $runResult;
return $this;
}
/**
* Parses and executes advisor rules
*
* @return array with run and parse results
*/
public function run()
{
// HowTo: A simple Advisory system in 3 easy steps.
// Step 1: Get some variables to evaluate on
$this->setVariables(
array_merge(
$this->dbi->fetchResult('SHOW GLOBAL STATUS', 0, 1),
$this->dbi->fetchResult('SHOW GLOBAL VARIABLES', 0, 1)
)
);
// Add total memory to variables as well
$sysinfo = SysInfo::get();
$memory = $sysinfo->memory();
$this->variables['system_memory']
= isset($memory['MemTotal']) ? $memory['MemTotal'] : 0;
$ruleFiles = $this->defineRulesFiles();
// Step 2: Read and parse the list of rules
$parsedResults = [];
foreach ($ruleFiles as $ruleFile) {
$parsedResults[] = $this->parseRulesFile($ruleFile);
}
$this->setParseResult(call_user_func_array('array_merge_recursive', $parsedResults));
// Step 3: Feed the variables to the rules and let them fire. Sets
// $runResult
$this->runRules();
return array(
'parse' => array('errors' => $this->parseResult['errors']),
'run' => $this->runResult
);
}
/**
* Stores current error in run results.
*
* @param string $description description of an error.
* @param Exception $exception exception raised
*
* @return void
*/
public function storeError($description, $exception)
{
$this->runResult['errors'][] = $description
. ' '
. sprintf(
__('Error when evaluating: %s'),
$exception->getMessage()
);
}
/**
* Executes advisor rules
*
* @return boolean
*/
public function runRules()
{
$this->setRunResult(
array(
'fired' => array(),
'notfired' => array(),
'unchecked' => array(),
'errors' => array(),
)
);
foreach ($this->parseResult['rules'] as $rule) {
$this->variables['value'] = 0;
$precond = true;
if (isset($rule['precondition'])) {
try {
$precond = $this->ruleExprEvaluate($rule['precondition']);
} catch (Exception $e) {
$this->storeError(
sprintf(
__('Failed evaluating precondition for rule \'%s\'.'),
$rule['name']
),
$e
);
continue;
}
}
if (! $precond) {
$this->addRule('unchecked', $rule);
} else {
try {
$value = $this->ruleExprEvaluate($rule['formula']);
} catch (Exception $e) {
$this->storeError(
sprintf(
__('Failed calculating value for rule \'%s\'.'),
$rule['name']
),
$e
);
continue;
}
$this->variables['value'] = $value;
try {
if ($this->ruleExprEvaluate($rule['test'])) {
$this->addRule('fired', $rule);
} else {
$this->addRule('notfired', $rule);
}
} catch (Exception $e) {
$this->storeError(
sprintf(
__('Failed running test for rule \'%s\'.'),
$rule['name']
),
$e
);
}
}
}
return true;
}
/**
* Escapes percent string to be used in format string.
*
* @param string $str string to escape
*
* @return string
*/
public static function escapePercent($str)
{
return preg_replace('/%( |,|\.|$|\(|\)|<|>)/', '%%\1', $str);
}
/**
* Wrapper function for translating.
*
* @param string $str the string
* @param string $param the parameters
*
* @return string
*/
public function translate($str, $param = null)
{
$string = _gettext(self::escapePercent($str));
if (! is_null($param)) {
$params = $this->ruleExprEvaluate('[' . $param . ']');
} else {
$params = array();
}
return vsprintf($string, $params);
}
/**
* Splits justification to text and formula.
*
* @param array $rule the rule
*
* @return string[]
*/
public static function splitJustification(array $rule)
{
$jst = preg_split('/\s*\|\s*/', $rule['justification'], 2);
if (count($jst) > 1) {
return array($jst[0], $jst[1]);
}
return array($rule['justification']);
}
/**
* Adds a rule to the result list
*
* @param string $type type of rule
* @param array $rule rule itself
*
* @return void
*/
public function addRule($type, array $rule)
{
switch ($type) {
case 'notfired':
case 'fired':
$jst = self::splitJustification($rule);
if (count($jst) > 1) {
try {
/* Translate */
$str = $this->translate($jst[0], $jst[1]);
} catch (Exception $e) {
$this->storeError(
sprintf(
__('Failed formatting string for rule \'%s\'.'),
$rule['name']
),
$e
);
return;
}
$rule['justification'] = $str;
} else {
$rule['justification'] = $this->translate($rule['justification']);
}
$rule['id'] = $rule['name'];
$rule['name'] = $this->translate($rule['name']);
$rule['issue'] = $this->translate($rule['issue']);
// Replaces {server_variable} with 'server_variable'
// linking to server_variables.php
$rule['recommendation'] = preg_replace_callback(
'/\{([a-z_0-9]+)\}/Ui',
array($this, 'replaceVariable'),
$this->translate($rule['recommendation'])
);
// Replaces external Links with Core::linkURL() generated links
$rule['recommendation'] = preg_replace_callback(
'#href=("|\')(https?://[^\1]+)\1#i',
array($this, 'replaceLinkURL'),
$rule['recommendation']
);
break;
}
$this->runResult[$type][] = $rule;
}
/**
* Defines the rules files to use
*
* @return array
*/
protected function defineRulesFiles()
{
$isMariaDB = false !== strpos($this->getVariables()['version'], 'MariaDB');
$ruleFiles = [self::GENERIC_RULES_FILE];
// If MariaDB (= not MySQL) OR MYSQL < 8.0.3, add another rules file.
if ($isMariaDB || $this->globals['PMA_MYSQL_INT_VERSION'] < 80003) {
$ruleFiles[] = self::BEFORE_MYSQL80003_RULES_FILE;
}
return $ruleFiles;
}
/**
* Callback for wrapping links with Core::linkURL
*
* @param array $matches List of matched elements form preg_replace_callback
*
* @return string Replacement value
*/
private function replaceLinkURL(array $matches)
{
return 'href="' . Core::linkURL($matches[2]) . '" target="_blank" rel="noopener noreferrer"';
}
/**
* Callback for wrapping variable edit links
*
* @param array $matches List of matched elements form preg_replace_callback
*
* @return string Replacement value
*/
private function replaceVariable(array $matches)
{
return '<a href="server_variables.php' . Url::getCommon(array('filter' => $matches[1]))
. '">' . htmlspecialchars($matches[1]) . '</a>';
}
/**
* Runs a code expression, replacing variable names with their respective
* values
*
* @param string $expr expression to evaluate
*
* @return integer result of evaluated expression
*
* @throws Exception
*/
public function ruleExprEvaluate($expr)
{
// Actually evaluate the code
// This can throw exception
$value = $this->expression->evaluate(
$expr,
array_merge($this->variables, $this->globals)
);
return $value;
}
/**
* Reads the rule file into an array, throwing errors messages on syntax
* errors.
*
* @param string $filename Name of file to parse
*
* @return array with parsed data
*/
public static function parseRulesFile($filename)
{
$file = file($filename, FILE_IGNORE_NEW_LINES);
$errors = array();
$rules = array();
$lines = array();
if ($file === false) {
$errors[] = sprintf(
__('Error in reading file: The file \'%s\' does not exist or is not readable!'),
$filename
);
return array('rules' => $rules, 'lines' => $lines, 'errors' => $errors);
}
$ruleSyntax = array(
'name', 'formula', 'test', 'issue', 'recommendation', 'justification'
);
$numRules = count($ruleSyntax);
$numLines = count($file);
$ruleNo = -1;
$ruleLine = -1;
for ($i = 0; $i < $numLines; $i++) {
$line = $file[$i];
if ($line == "" || $line[0] == '#') {
continue;
}
// Reading new rule
if (substr($line, 0, 4) == 'rule') {
if ($ruleLine > 0) {
$errors[] = sprintf(
__(
'Invalid rule declaration on line %1$s, expected line '
. '%2$s of previous rule.'
),
$i + 1,
$ruleSyntax[$ruleLine++]
);
continue;
}
if (preg_match("/rule\s'(.*)'( \[(.*)\])?$/", $line, $match)) {
$ruleLine = 1;
$ruleNo++;
$rules[$ruleNo] = array('name' => $match[1]);
$lines[$ruleNo] = array('name' => $i + 1);
if (isset($match[3])) {
$rules[$ruleNo]['precondition'] = $match[3];
$lines[$ruleNo]['precondition'] = $i + 1;
}
} else {
$errors[] = sprintf(
__('Invalid rule declaration on line %s.'),
$i + 1
);
}
continue;
} else {
if ($ruleLine == -1) {
$errors[] = sprintf(
__('Unexpected characters on line %s.'),
$i + 1
);
}
}
// Reading rule lines
if ($ruleLine > 0) {
if (!isset($line[0])) {
continue; // Empty lines are ok
}
// Non tabbed lines are not
if ($line[0] != "\t") {
$errors[] = sprintf(
__(
'Unexpected character on line %1$s. Expected tab, but '
. 'found "%2$s".'
),
$i + 1,
$line[0]
);
continue;
}
$rules[$ruleNo][$ruleSyntax[$ruleLine]] = chop(
mb_substr($line, 1)
);
$lines[$ruleNo][$ruleSyntax[$ruleLine]] = $i + 1;
++$ruleLine;
}
// Rule complete
if ($ruleLine == $numRules) {
$ruleLine = -1;
}
}
return array('rules' => $rules, 'lines' => $lines, 'errors' => $errors);
}
/**
* Formats interval like 10 per hour
*
* @param integer $num number to format
* @param integer $precision required precision
*
* @return string formatted string
*/
public static function byTime($num, $precision)
{
if ($num >= 1) { // per second
$per = __('per second');
} elseif ($num * 60 >= 1) { // per minute
$num = $num * 60;
$per = __('per minute');
} elseif ($num * 60 * 60 >= 1 ) { // per hour
$num = $num * 60 * 60;
$per = __('per hour');
} else {
$num = $num * 60 * 60 * 24;
$per = __('per day');
}
$num = round($num, $precision);
if ($num == 0) {
$num = '<' . pow(10, -$precision);
}
return "$num $per";
}
/**
* Wrapper for PhpMyAdmin\Util::timespanFormat
*
* This function is used when evaluating advisory_rules.txt
*
* @param int $seconds the timespan
*
* @return string the formatted value
*/
public static function timespanFormat($seconds)
{
return Util::timespanFormat($seconds);
}
/**
* Wrapper around PhpMyAdmin\Util::formatByteDown
*
* This function is used when evaluating advisory_rules.txt
*
* @param double $value the value to format
* @param int $limes the sensitiveness
* @param int $comma the number of decimals to retain
*
* @return string the formatted value with unit
*/
public static function formatByteDown($value, $limes = 6, $comma = 0)
{
return implode(' ', Util::formatByteDown($value, $limes, $comma));
}
}

View File

@@ -0,0 +1,383 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Handles bookmarking SQL queries
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Relation;
use PhpMyAdmin\Util;
/**
* Handles bookmarking SQL queries
*
* @package PhpMyAdmin
*/
class Bookmark
{
/**
* ID of the bookmark
*
* @var int
*/
private $_id;
/**
* Database the bookmark belongs to
*
* @var string
*/
private $_database;
/**
* The user to whom the bookmark belongs, empty for public bookmarks
*
* @var string
*/
private $_user;
/**
* Label of the bookmark
*
* @var string
*/
private $_label;
/**
* SQL query that is bookmarked
*
* @var string
*/
private $_query;
/**
* @var DatabaseInterface
*/
private $dbi;
/**
* Current user
*
* @var string
*/
private $user;
public function __construct(DatabaseInterface $dbi, $user)
{
$this->dbi = $dbi;
$this->user = $user;
}
/**
* Returns the ID of the bookmark
*
* @return int
*/
public function getId()
{
return $this->_id;
}
/**
* Returns the database of the bookmark
*
* @return string
*/
public function getDatabase()
{
return $this->_database;
}
/**
* Returns the user whom the bookmark belongs to
*
* @return string
*/
public function getUser()
{
return $this->_user;
}
/**
* Returns the label of the bookmark
*
* @return string
*/
public function getLabel()
{
return $this->_label;
}
/**
* Returns the query
*
* @return string
*/
public function getQuery()
{
return $this->_query;
}
/**
* Adds a bookmark
*
* @return boolean whether the INSERT succeeds or not
*
* @access public
*/
public function save()
{
$cfgBookmark = self::getParams($this->user);
if (empty($cfgBookmark)) {
return false;
}
$query = "INSERT INTO " . Util::backquote($cfgBookmark['db'])
. "." . Util::backquote($cfgBookmark['table'])
. " (id, dbase, user, query, label) VALUES (NULL, "
. "'" . $this->dbi->escapeString($this->_database) . "', "
. "'" . $this->dbi->escapeString($this->_user) . "', "
. "'" . $this->dbi->escapeString($this->_query) . "', "
. "'" . $this->dbi->escapeString($this->_label) . "')";
return $this->dbi->query($query, DatabaseInterface::CONNECT_CONTROL);
}
/**
* Deletes a bookmark
*
* @return bool true if successful
*
* @access public
*/
public function delete()
{
$cfgBookmark = self::getParams($this->user);
if (empty($cfgBookmark)) {
return false;
}
$query = "DELETE FROM " . Util::backquote($cfgBookmark['db'])
. "." . Util::backquote($cfgBookmark['table'])
. " WHERE id = " . $this->_id;
return $this->dbi->tryQuery($query, DatabaseInterface::CONNECT_CONTROL);
}
/**
* Returns the number of variables in a bookmark
*
* @return number number of variables
*/
public function getVariableCount()
{
$matches = array();
preg_match_all("/\[VARIABLE[0-9]*\]/", $this->_query, $matches, PREG_SET_ORDER);
return count($matches);
}
/**
* Replace the placeholders in the bookmark query with variables
*
* @param array $variables array of variables
*
* @return string query with variables applied
*/
public function applyVariables(array $variables)
{
// remove comments that encloses a variable placeholder
$query = preg_replace(
'|/\*(.*\[VARIABLE[0-9]*\].*)\*/|imsU',
'${1}',
$this->_query
);
// replace variable placeholders with values
$number_of_variables = $this->getVariableCount();
for ($i = 1; $i <= $number_of_variables; $i++) {
$var = '';
if (! empty($variables[$i])) {
$var = $this->dbi->escapeString($variables[$i]);
}
$query = str_replace('[VARIABLE' . $i . ']', $var, $query);
// backward compatibility
if ($i == 1) {
$query = str_replace('[VARIABLE]', $var, $query);
}
}
return $query;
}
/**
* Defines the bookmark parameters for the current user
*
* @param string $user Current user
* @return array the bookmark parameters for the current user
* @access public
*/
public static function getParams($user)
{
static $cfgBookmark = null;
if (null !== $cfgBookmark) {
return $cfgBookmark;
}
$relation = new Relation();
$cfgRelation = $relation->getRelationsParam();
if ($cfgRelation['bookmarkwork']) {
$cfgBookmark = array(
'user' => $user,
'db' => $cfgRelation['db'],
'table' => $cfgRelation['bookmark'],
);
} else {
$cfgBookmark = false;
}
return $cfgBookmark;
}
/**
* Creates a Bookmark object from the parameters
*
* @param DatabaseInterface $dbi DatabaseInterface object
* @param string $user Current user
* @param array $bkm_fields the properties of the bookmark to add; here,
* $bkm_fields['bkm_sql_query'] is urlencoded
* @param boolean $all_users whether to make the bookmark
* available for all users
*
* @return Bookmark|false
*/
public static function createBookmark(
DatabaseInterface $dbi,
$user,
array $bkm_fields,
$all_users = false
) {
if (!(isset($bkm_fields['bkm_sql_query'])
&& strlen($bkm_fields['bkm_sql_query']) > 0
&& isset($bkm_fields['bkm_label'])
&& strlen($bkm_fields['bkm_label']) > 0)
) {
return false;
}
$bookmark = new Bookmark($dbi, $user);
$bookmark->_database = $bkm_fields['bkm_database'];
$bookmark->_label = $bkm_fields['bkm_label'];
$bookmark->_query = $bkm_fields['bkm_sql_query'];
$bookmark->_user = $all_users ? '' : $bkm_fields['bkm_user'];
return $bookmark;
}
/**
* Gets the list of bookmarks defined for the current database
*
* @param DatabaseInterface $dbi DatabaseInterface object
* @param string $user Current user
* @param string|bool $db the current database name or false
*
* @return Bookmark[] the bookmarks list
*
* @access public
*/
public static function getList(DatabaseInterface $dbi, $user, $db = false)
{
$cfgBookmark = self::getParams($user);
if (empty($cfgBookmark)) {
return array();
}
$query = "SELECT * FROM " . Util::backquote($cfgBookmark['db'])
. "." . Util::backquote($cfgBookmark['table'])
. " WHERE ( `user` = ''"
. " OR `user` = '" . $dbi->escapeString($cfgBookmark['user']) . "' )";
if ($db !== false) {
$query .= " AND dbase = '" . $dbi->escapeString($db) . "'";
}
$query .= " ORDER BY label ASC";
$result = $dbi->fetchResult(
$query,
null,
null,
DatabaseInterface::CONNECT_CONTROL,
DatabaseInterface::QUERY_STORE
);
if (! empty($result)) {
$bookmarks = array();
foreach ($result as $row) {
$bookmark = new Bookmark($dbi, $user);
$bookmark->_id = $row['id'];
$bookmark->_database = $row['dbase'];
$bookmark->_user = $row['user'];
$bookmark->_label = $row['label'];
$bookmark->_query = $row['query'];
$bookmarks[] = $bookmark;
}
return $bookmarks;
}
return array();
}
/**
* Retrieve a specific bookmark
*
* @param DatabaseInterface $dbi DatabaseInterface object
* @param string $user Current user
* @param string $db the current database name
* @param mixed $id an identifier of the bookmark to get
* @param string $id_field which field to look up the identifier
* @param boolean $action_bookmark_all true: get all bookmarks regardless
* of the owning user
* @param boolean $exact_user_match whether to ignore bookmarks with no user
*
* @return Bookmark the bookmark
*
* @access public
*
*/
public static function get(
DatabaseInterface $dbi,
$user,
$db,
$id,
$id_field = 'id',
$action_bookmark_all = false,
$exact_user_match = false
) {
$cfgBookmark = self::getParams($user);
if (empty($cfgBookmark)) {
return null;
}
$query = "SELECT * FROM " . Util::backquote($cfgBookmark['db'])
. "." . Util::backquote($cfgBookmark['table'])
. " WHERE dbase = '" . $dbi->escapeString($db) . "'";
if (! $action_bookmark_all) {
$query .= " AND (user = '"
. $dbi->escapeString($cfgBookmark['user']) . "'";
if (! $exact_user_match) {
$query .= " OR user = ''";
}
$query .= ")";
}
$query .= " AND " . Util::backquote($id_field)
. " = '" . $dbi->escapeString($id) . "' LIMIT 1";
$result = $dbi->fetchSingleRow($query, 'ASSOC', DatabaseInterface::CONNECT_CONTROL);
if (! empty($result)) {
$bookmark = new Bookmark($dbi, $user);
$bookmark->_id = $result['id'];
$bookmark->_database = $result['dbase'];
$bookmark->_user = $result['user'];
$bookmark->_label = $result['label'];
$bookmark->_query = $result['query'];
return $bookmark;
}
return null;
}
}

View File

@@ -0,0 +1,346 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Contains functions used by browse_foreigners.php
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
/**
* PhpMyAdmin\BrowseForeigners class
*
* @package PhpMyAdmin
*/
class BrowseForeigners
{
private $limitChars;
private $maxRows;
private $repeatCells;
private $showAll;
private $themeImage;
/**
* Constructor
*
* @param int $limitChars Maximum number of characters to show
* @param int $maxRows Number of rows to display
* @param int $repeatCells Repeat the headers every X cells, or 0 to deactivate
* @param boolean $showAll Shows the 'Show all' button or not
* @param string $themeImage Theme image path
*/
public function __construct(
$limitChars,
$maxRows,
$repeatCells,
$showAll,
$themeImage
) {
$this->limitChars = (int) $limitChars;
$this->maxRows = (int) $maxRows;
$this->repeatCells = (int) $repeatCells;
$this->showAll = (bool) $showAll;
$this->themeImage = $themeImage;
}
/**
* Function to get html for one relational key
*
* @param integer $horizontal_count the current horizontal count
* @param string $header table header
* @param array $keys all the keys
* @param integer $indexByKeyname index by keyname
* @param array $descriptions descriptions
* @param integer $indexByDescription index by description
* @param string $current_value current value on the edit form
*
* @return string $html the generated html
*/
private function getHtmlForOneKey(
$horizontal_count,
$header,
array $keys,
$indexByKeyname,
array $descriptions,
$indexByDescription,
$current_value
) {
$horizontal_count++;
$output = '';
// whether the key name corresponds to the selected value in the form
$rightKeynameIsSelected = false;
$leftKeynameIsSelected = false;
if ($this->repeatCells > 0 && $horizontal_count > $this->repeatCells) {
$output .= $header;
$horizontal_count = 0;
}
// key names and descriptions for the left section,
// sorted by key names
$leftKeyname = $keys[$indexByKeyname];
list(
$leftDescription,
$leftDescriptionTitle
) = $this->getDescriptionAndTitle($descriptions[$indexByKeyname]);
// key names and descriptions for the right section,
// sorted by descriptions
$rightKeyname = $keys[$indexByDescription];
list(
$rightDescription,
$rightDescriptionTitle
) = $this->getDescriptionAndTitle($descriptions[$indexByDescription]);
$indexByDescription++;
if (! empty($current_value)) {
$rightKeynameIsSelected = $rightKeyname == $current_value;
$leftKeynameIsSelected = $leftKeyname == $current_value;
}
$output .= '<tr class="noclick">';
$output .= Template::get('table/browse_foreigners/column_element')->render([
'keyname' => $leftKeyname,
'description' => $leftDescription,
'title' => $leftDescriptionTitle,
'is_selected' => $leftKeynameIsSelected,
'nowrap' => true,
]);
$output .= Template::get('table/browse_foreigners/column_element')->render([
'keyname' => $leftKeyname,
'description' => $leftDescription,
'title' => $leftDescriptionTitle,
'is_selected' => $leftKeynameIsSelected,
'nowrap' => false,
]);
$output .= '<td width="20%">'
. '<img src="' . $this->themeImage . 'spacer.png" alt=""'
. ' width="1" height="1" /></td>';
$output .= Template::get('table/browse_foreigners/column_element')->render([
'keyname' => $rightKeyname,
'description' => $rightDescription,
'title' => $rightDescriptionTitle,
'is_selected' => $rightKeynameIsSelected,
'nowrap' => false,
]);
$output .= Template::get('table/browse_foreigners/column_element')->render([
'keyname' => $rightKeyname,
'description' => $rightDescription,
'title' => $rightDescriptionTitle,
'is_selected' => $rightKeynameIsSelected,
'nowrap' => true,
]);
$output .= '</tr>';
return array($output, $horizontal_count, $indexByDescription);
}
/**
* Function to get html for relational field selection
*
* @param string $db current database
* @param string $table current table
* @param string $field field
* @param array $foreignData foreign column data
* @param string $fieldkey field key
* @param string $current_value current columns's value
*
* @return string
*/
public function getHtmlForRelationalFieldSelection(
$db,
$table,
$field,
array $foreignData,
$fieldkey,
$current_value
) {
$gotopage = $this->getHtmlForGotoPage($foreignData);
$foreignShowAll = Template::get('table/browse_foreigners/show_all')->render([
'foreign_data' => $foreignData,
'show_all' => $this->showAll,
'max_rows' => $this->maxRows,
]);
$output = '<form class="ajax" '
. 'id="browse_foreign_form" name="browse_foreign_from" '
. 'action="browse_foreigners.php" method="post">'
. '<fieldset>'
. Url::getHiddenInputs($db, $table)
. '<input type="hidden" name="field" value="' . htmlspecialchars($field)
. '" />'
. '<input type="hidden" name="fieldkey" value="'
. (isset($fieldkey) ? htmlspecialchars($fieldkey) : '') . '" />';
if (isset($_POST['rownumber'])) {
$output .= '<input type="hidden" name="rownumber" value="'
. htmlspecialchars($_POST['rownumber']) . '" />';
}
$filter_value = (isset($_POST['foreign_filter'])
? htmlspecialchars($_POST['foreign_filter'])
: '');
$output .= '<span class="formelement">'
. '<label for="input_foreign_filter">' . __('Search:') . '</label>'
. '<input type="text" name="foreign_filter" '
. 'id="input_foreign_filter" '
. 'value="' . $filter_value . '" data-old="' . $filter_value . '" '
. '/>'
. '<input type="submit" name="submit_foreign_filter" value="'
. __('Go') . '" />'
. '</span>'
. '<span class="formelement">' . $gotopage . '</span>'
. '<span class="formelement">' . $foreignShowAll . '</span>'
. '</fieldset>'
. '</form>';
$output .= '<table width="100%" id="browse_foreign_table">';
if (!is_array($foreignData['disp_row'])) {
$output .= '</tbody>'
. '</table>';
return $output;
}
$header = '<tr>
<th>' . __('Keyname') . '</th>
<th>' . __('Description') . '</th>
<td width="20%"></td>
<th>' . __('Description') . '</th>
<th>' . __('Keyname') . '</th>
</tr>';
$output .= '<thead>' . $header . '</thead>' . "\n"
. '<tfoot>' . $header . '</tfoot>' . "\n"
. '<tbody>' . "\n";
$descriptions = array();
$keys = array();
foreach ($foreignData['disp_row'] as $relrow) {
if ($foreignData['foreign_display'] != false) {
$descriptions[] = $relrow[$foreignData['foreign_display']];
} else {
$descriptions[] = '';
}
$keys[] = $relrow[$foreignData['foreign_field']];
}
asort($keys);
$horizontal_count = 0;
$indexByDescription = 0;
foreach ($keys as $indexByKeyname => $value) {
list(
$html,
$horizontal_count,
$indexByDescription
) = $this->getHtmlForOneKey(
$horizontal_count,
$header,
$keys,
$indexByKeyname,
$descriptions,
$indexByDescription,
$current_value
);
$output .= $html;
}
$output .= '</tbody>'
. '</table>';
return $output;
}
/**
* Get the description (possibly truncated) and the title
*
* @param string $description the key name's description
*
* @return array the new description and title
*/
private function getDescriptionAndTitle($description)
{
if (mb_strlen($description) <= $this->limitChars) {
$description = htmlspecialchars(
$description
);
$descriptionTitle = '';
} else {
$descriptionTitle = htmlspecialchars(
$description
);
$description = htmlspecialchars(
mb_substr(
$description, 0, $this->limitChars
)
. '...'
);
}
return array($description, $descriptionTitle);
}
/**
* Function to get html for the goto page option
*
* @param array|null $foreignData foreign data
*
* @return string
*/
private function getHtmlForGotoPage($foreignData)
{
$gotopage = '';
isset($_POST['pos']) ? $pos = $_POST['pos'] : $pos = 0;
if ($foreignData === null || ! is_array($foreignData['disp_row'])) {
return $gotopage;
}
$pageNow = @floor($pos / $this->maxRows) + 1;
$nbTotalPage = @ceil($foreignData['the_total'] / $this->maxRows);
if ($foreignData['the_total'] > $this->maxRows) {
$gotopage = Util::pageselector(
'pos',
$this->maxRows,
$pageNow,
$nbTotalPage,
200,
5,
5,
20,
10,
__('Page number:')
);
}
return $gotopage;
}
/**
* Function to get foreign limit
*
* @param string $foreignShowAll foreign navigation
*
* @return string
*/
public function getForeignLimit($foreignShowAll)
{
if (isset($foreignShowAll) && $foreignShowAll == __('Show all')) {
return null;
}
isset($_POST['pos']) ? $pos = $_POST['pos'] : $pos = 0;
return 'LIMIT ' . $pos . ', ' . $this->maxRows . ' ';
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,704 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* MySQL charset metadata and manipulations
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Util;
/**
* Class used to manage MySQL charsets
*
* @package PhpMyAdmin
*/
class Charsets
{
/**
* MySQL charsets map
*
* @var array
*/
public static $mysql_charset_map = array(
'big5' => 'big5',
'cp-866' => 'cp866',
'euc-jp' => 'ujis',
'euc-kr' => 'euckr',
'gb2312' => 'gb2312',
'gbk' => 'gbk',
'iso-8859-1' => 'latin1',
'iso-8859-2' => 'latin2',
'iso-8859-7' => 'greek',
'iso-8859-8' => 'hebrew',
'iso-8859-8-i' => 'hebrew',
'iso-8859-9' => 'latin5',
'iso-8859-13' => 'latin7',
'iso-8859-15' => 'latin1',
'koi8-r' => 'koi8r',
'shift_jis' => 'sjis',
'tis-620' => 'tis620',
'utf-8' => 'utf8',
'windows-1250' => 'cp1250',
'windows-1251' => 'cp1251',
'windows-1252' => 'latin1',
'windows-1256' => 'cp1256',
'windows-1257' => 'cp1257',
);
private static $_charsets = array();
/**
* The charset for the server
*
* @var string
*/
private static $_charset_server;
private static $_charsets_descriptions = array();
private static $_collations = array();
private static $_default_collations = array();
/**
* Loads charset data from the MySQL server.
*
* @param DatabaseInterface $dbi DatabaseInterface instance
* @param boolean $disableIs Disable use of INFORMATION_SCHEMA
*
* @return void
*/
private static function loadCharsets(DatabaseInterface $dbi, $disableIs)
{
/* Data already loaded */
if (count(self::$_charsets) > 0) {
return;
}
if ($disableIs) {
$sql = 'SHOW CHARACTER SET';
} else {
$sql = 'SELECT `CHARACTER_SET_NAME` AS `Charset`,'
. ' `DESCRIPTION` AS `Description`'
. ' FROM `information_schema`.`CHARACTER_SETS`';
}
$res = $dbi->query($sql);
self::$_charsets = array();
while ($row = $dbi->fetchAssoc($res)) {
$name = $row['Charset'];
self::$_charsets[] = $name;
self::$_charsets_descriptions[$name] = $row['Description'];
}
$dbi->freeResult($res);
sort(self::$_charsets, SORT_STRING);
}
/**
* Loads collation data from the MySQL server.
*
* @param DatabaseInterface $dbi DatabaseInterface instance
* @param boolean $disableIs Disable use of INFORMATION_SCHEMA
*
* @return void
*/
private static function loadCollations(DatabaseInterface $dbi, $disableIs)
{
/* Data already loaded */
if (count(self::$_collations) > 0) {
return;
}
if ($disableIs) {
$sql = 'SHOW COLLATION';
} else {
$sql = 'SELECT `CHARACTER_SET_NAME` AS `Charset`,'
. ' `COLLATION_NAME` AS `Collation`, `IS_DEFAULT` AS `Default`'
. ' FROM `information_schema`.`COLLATIONS`';
}
$res = $dbi->query($sql);
while ($row = $dbi->fetchAssoc($res)) {
$char_set_name = $row['Charset'];
$name = $row['Collation'];
self::$_collations[$char_set_name][] = $name;
if ($row['Default'] == 'Yes' || $row['Default'] == '1') {
self::$_default_collations[$char_set_name] = $name;
}
}
$dbi->freeResult($res);
foreach (self::$_collations as $key => $value) {
sort(self::$_collations[$key], SORT_STRING);
}
}
/**
* Get current MySQL server charset.
*
* @param DatabaseInterface $dbi DatabaseInterface instance
*
* @return string
*/
public static function getServerCharset(DatabaseInterface $dbi)
{
if (self::$_charset_server) {
return self::$_charset_server;
} else {
$charsetServer = $dbi->getVariable('character_set_server');
if (! is_string($charsetServer)) {// MySQL 5.7.8 fallback, issue #15614
$charsetServer = $dbi->fetchValue("SELECT @@character_set_server;");
}
self::$_charset_server = $charsetServer;
return self::$_charset_server;
}
}
/**
* Get MySQL charsets
*
* @param DatabaseInterface $dbi DatabaseInterface instance
* @param boolean $disableIs Disable use of INFORMATION_SCHEMA
*
* @return array
*/
public static function getMySQLCharsets(DatabaseInterface $dbi, $disableIs)
{
self::loadCharsets($dbi, $disableIs);
return self::$_charsets;
}
/**
* Get MySQL charsets descriptions
*
* @param DatabaseInterface $dbi DatabaseInterface instance
* @param boolean $disableIs Disable use of INFORMATION_SCHEMA
*
* @return array
*/
public static function getMySQLCharsetsDescriptions(DatabaseInterface $dbi, $disableIs)
{
self::loadCharsets($dbi, $disableIs);
return self::$_charsets_descriptions;
}
/**
* Get MySQL collations
*
* @param DatabaseInterface $dbi DatabaseInterface instance
* @param boolean $disableIs Disable use of INFORMATION_SCHEMA
*
* @return array
*/
public static function getMySQLCollations(DatabaseInterface $dbi, $disableIs)
{
self::loadCollations($dbi, $disableIs);
return self::$_collations;
}
/**
* Get MySQL default collations
*
* @param DatabaseInterface $dbi DatabaseInterface instance
* @param boolean $disableIs Disable use of INFORMATION_SCHEMA
*
* @return array
*/
public static function getMySQLCollationsDefault(DatabaseInterface $dbi, $disableIs)
{
self::loadCollations($dbi, $disableIs);
return self::$_default_collations;
}
/**
* Generate charset dropdown box
*
* @param DatabaseInterface $dbi DatabaseInterface instance
* @param boolean $disableIs Disable use of INFORMATION_SCHEMA
* @param string $name Element name
* @param string $id Element id
* @param null|string $default Default value
* @param bool $label Label
* @param bool $submitOnChange Submit on change
*
* @return string
*/
public static function getCharsetDropdownBox(
DatabaseInterface $dbi,
$disableIs,
$name = null,
$id = null,
$default = null,
$label = true,
$submitOnChange = false
) {
self::loadCharsets($dbi, $disableIs);
if (empty($name)) {
$name = 'character_set';
}
$return_str = '<select lang="en" dir="ltr" name="'
. htmlspecialchars($name) . '"'
. (empty($id) ? '' : ' id="' . htmlspecialchars($id) . '"')
. ($submitOnChange ? ' class="autosubmit"' : '') . '>' . "\n";
if ($label) {
$return_str .= '<option value="">'
. __('Charset')
. '</option>' . "\n";
}
$return_str .= '<option value=""></option>' . "\n";
foreach (self::$_charsets as $current_charset) {
$current_cs_descr
= empty(self::$_charsets_descriptions[$current_charset])
? $current_charset
: self::$_charsets_descriptions[$current_charset];
$return_str .= '<option value="' . $current_charset
. '" title="' . $current_cs_descr . '"'
. ($default == $current_charset ? ' selected="selected"' : '') . '>'
. $current_charset . '</option>' . "\n";
}
$return_str .= '</select>' . "\n";
return $return_str;
}
/**
* Generate collation dropdown box
*
* @param DatabaseInterface $dbi DatabaseInterface instance
* @param boolean $disableIs Disable use of INFORMATION_SCHEMA
* @param string $name Element name
* @param string $id Element id
* @param null|string $default Default value
* @param bool $label Label
* @param bool $submitOnChange Submit on change
*
* @return string
*/
public static function getCollationDropdownBox(
DatabaseInterface $dbi,
$disableIs,
$name = null,
$id = null,
$default = null,
$label = true,
$submitOnChange = false
) {
self::loadCharsets($dbi, $disableIs);
self::loadCollations($dbi, $disableIs);
if (empty($name)) {
$name = 'collation';
}
$return_str = '<select lang="en" dir="ltr" name="'
. htmlspecialchars($name) . '"'
. (empty($id) ? '' : ' id="' . htmlspecialchars($id) . '"')
. ($submitOnChange ? ' class="autosubmit"' : '') . '>' . "\n";
if ($label) {
$return_str .= '<option value="">'
. __('Collation')
. '</option>' . "\n";
}
$return_str .= '<option value=""></option>' . "\n";
foreach (self::$_charsets as $current_charset) {
$current_cs_descr
= empty(self::$_charsets_descriptions[$current_charset])
? $current_charset
: self::$_charsets_descriptions[$current_charset];
$return_str .= '<optgroup label="' . $current_charset
. '" title="' . $current_cs_descr . '">' . "\n";
foreach (self::$_collations[$current_charset] as $current_collation) {
$return_str .= '<option value="' . $current_collation
. '" title="' . self::getCollationDescr($current_collation) . '"'
. ($default == $current_collation ? ' selected="selected"' : '')
. '>'
. $current_collation . '</option>' . "\n";
}
$return_str .= '</optgroup>' . "\n";
}
$return_str .= '</select>' . "\n";
return $return_str;
}
/**
* Returns description for given collation
*
* @param string $collation MySQL collation string
*
* @return string collation description
*/
public static function getCollationDescr($collation)
{
$parts = explode('_', $collation);
$name = __('Unknown');
$variant = null;
$suffixes = array();
$unicode = false;
$unknown = false;
$level = 0;
foreach ($parts as $part) {
if ($level == 0) {
/* Next will be language */
$level = 1;
/* First should be charset */
switch ($part) {
case 'binary':
$name = _pgettext('Collation', 'Binary');
break;
// Unicode charsets
case 'utf8mb4':
$variant = 'UCA 4.0.0';
// Fall through to other unicode
case 'ucs2':
case 'utf8':
case 'utf16':
case 'utf16le':
case 'utf16be':
case 'utf32':
$name = _pgettext('Collation', 'Unicode');
$unicode = true;
break;
// West European charsets
case 'ascii':
case 'cp850':
case 'dec8':
case 'hp8':
case 'latin1':
case 'macroman':
$name = _pgettext('Collation', 'West European');
break;
// Central European charsets
case 'cp1250':
case 'cp852':
case 'latin2':
case 'macce':
$name = _pgettext('Collation', 'Central European');
break;
// Russian charsets
case 'cp866':
case 'koi8r':
$name = _pgettext('Collation', 'Russian');
break;
// Chinese charsets
case 'gb2312':
case 'gbk':
$name = _pgettext('Collation', 'Simplified Chinese');
break;
case 'big5':
$name = _pgettext('Collation', 'Traditional Chinese');
break;
case 'gb18030':
$name = _pgettext('Collation', 'Chinese');
$unicode = true;
break;
// Japanese charsets
case 'sjis':
case 'ujis':
case 'cp932':
case 'eucjpms':
$name = _pgettext('Collation', 'Japanese');
break;
// Baltic charsets
case 'cp1257':
case 'latin7':
$name = _pgettext('Collation', 'Baltic');
break;
// Other
case 'armscii8':
case 'armscii':
$name = _pgettext('Collation', 'Armenian');
break;
case 'cp1251':
$name = _pgettext('Collation', 'Cyrillic');
break;
case 'cp1256':
$name = _pgettext('Collation', 'Arabic');
break;
case 'euckr':
$name = _pgettext('Collation', 'Korean');
break;
case 'hebrew':
$name = _pgettext('Collation', 'Hebrew');
break;
case 'geostd8':
$name = _pgettext('Collation', 'Georgian');
break;
case 'greek':
$name = _pgettext('Collation', 'Greek');
break;
case 'keybcs2':
$name = _pgettext('Collation', 'Czech-Slovak');
break;
case 'koi8u':
$name = _pgettext('Collation', 'Ukrainian');
break;
case 'latin5':
$name = _pgettext('Collation', 'Turkish');
break;
case 'swe7':
$name = _pgettext('Collation', 'Swedish');
break;
case 'tis620':
$name = _pgettext('Collation', 'Thai');
break;
default:
$name = _pgettext('Collation', 'Unknown');
$unknown = true;
break;
}
continue;
}
if ($level == 1) {
/* Next will be variant unless changed later */
$level = 4;
/* Locale name or code */
$found = true;
switch ($part) {
case 'general':
break;
case 'bulgarian':
case 'bg':
$name = _pgettext('Collation', 'Bulgarian');
break;
case 'chinese':
case 'cn':
case 'zh':
if ($unicode) {
$name = _pgettext('Collation', 'Chinese');
}
break;
case 'croatian':
case 'hr':
$name = _pgettext('Collation', 'Croatian');
break;
case 'czech':
case 'cs':
$name = _pgettext('Collation', 'Czech');
break;
case 'danish':
case 'da':
$name = _pgettext('Collation', 'Danish');
break;
case 'english':
case 'en':
$name = _pgettext('Collation', 'English');
break;
case 'esperanto':
case 'eo':
$name = _pgettext('Collation', 'Esperanto');
break;
case 'estonian':
case 'et':
$name = _pgettext('Collation', 'Estonian');
break;
case 'german1':
$name = _pgettext('Collation', 'German (dictionary order)');
break;
case 'german2':
$name = _pgettext('Collation', 'German (phone book order)');
break;
case 'german':
case 'de':
/* Name is set later */
$level = 2;
break;
case 'hungarian':
case 'hu':
$name = _pgettext('Collation', 'Hungarian');
break;
case 'icelandic':
case 'is':
$name = _pgettext('Collation', 'Icelandic');
break;
case 'japanese':
case 'ja':
$name = _pgettext('Collation', 'Japanese');
break;
case 'la':
$name = _pgettext('Collation', 'Classical Latin');
break;
case 'latvian':
case 'lv':
$name = _pgettext('Collation', 'Latvian');
break;
case 'lithuanian':
case 'lt':
$name = _pgettext('Collation', 'Lithuanian');
break;
case 'korean':
case 'ko':
$name = _pgettext('Collation', 'Korean');
break;
case 'myanmar':
case 'my':
$name = _pgettext('Collation', 'Burmese');
break;
case 'persian':
$name = _pgettext('Collation', 'Persian');
break;
case 'polish':
case 'pl':
$name = _pgettext('Collation', 'Polish');
break;
case 'roman':
$name = _pgettext('Collation', 'West European');
break;
case 'romanian':
case 'ro':
$name = _pgettext('Collation', 'Romanian');
break;
case 'ru':
$name = _pgettext('Collation', 'Russian');
break;
case 'si':
case 'sinhala':
$name = _pgettext('Collation', 'Sinhalese');
break;
case 'slovak':
case 'sk':
$name = _pgettext('Collation', 'Slovak');
break;
case 'slovenian':
case 'sl':
$name = _pgettext('Collation', 'Slovenian');
break;
case 'spanish':
$name = _pgettext('Collation', 'Spanish (modern)');
break;
case 'es':
/* Name is set later */
$level = 3;
break;
case 'spanish2':
$name = _pgettext('Collation', 'Spanish (traditional)');
break;
case 'swedish':
case 'sv':
$name = _pgettext('Collation', 'Swedish');
break;
case 'thai':
case 'th':
$name = _pgettext('Collation', 'Thai');
break;
case 'turkish':
case 'tr':
$name = _pgettext('Collation', 'Turkish');
break;
case 'ukrainian':
case 'uk':
$name = _pgettext('Collation', 'Ukrainian');
break;
case 'vietnamese':
case 'vi':
$name = _pgettext('Collation', 'Vietnamese');
break;
case 'unicode':
if ($unknown) {
$name = _pgettext('Collation', 'Unicode');
}
break;
default:
$found = false;
}
if ($found) {
continue;
}
// Not parsed token, fall to next level
}
if ($level == 2) {
/* Next will be variant */
$level = 4;
/* Germal variant */
if ($part == 'pb') {
$name = _pgettext('Collation', 'German (phone book order)');
continue;
}
$name = _pgettext('Collation', 'German (dictionary order)');
// Not parsed token, fall to next level
}
if ($level == 3) {
/* Next will be variant */
$level = 4;
/* Spanish variant */
if ($part == 'trad') {
$name = _pgettext('Collation', 'Spanish (traditional)');
continue;
}
$name = _pgettext('Collation', 'Spanish (modern)');
// Not parsed token, fall to next level
}
if ($level == 4) {
/* Next will be suffix */
$level = 5;
/* Variant */
$found = true;
switch ($part) {
case '0900':
$variant = 'UCA 9.0.0';
break;
case '520':
$variant = 'UCA 5.2.0';
break;
case 'mysql561':
$variant = 'MySQL 5.6.1';
break;
case 'mysql500':
$variant = 'MySQL 5.0.0';
break;
default:
$found = false;
}
if ($found) {
continue;
}
// Not parsed token, fall to next level
}
if ($level == 5) {
/* Suffixes */
switch ($part) {
case 'ci':
$suffixes[] = _pgettext('Collation variant', 'case-insensitive');
break;
case 'cs':
$suffixes[] = _pgettext('Collation variant', 'case-sensitive');
break;
case 'ai':
$suffixes[] = _pgettext('Collation variant', 'accent-insensitive');
break;
case 'as':
$suffixes[] = _pgettext('Collation variant', 'accent-sensitive');
break;
case 'ks':
$suffixes[] = _pgettext('Collation variant', 'kana-sensitive');
break;
case 'w2':
case 'l2':
$suffixes[] = _pgettext('Collation variant', 'multi-level');
break;
case 'bin':
$suffixes[] = _pgettext('Collation variant', 'binary');
break;
case 'nopad':
$suffixes[] = _pgettext('Collation variant', 'no-pad');
break;
}
}
}
$result = $name;
if (! is_null($variant)) {
$result .= ' (' . $variant . ')';
}
if (count($suffixes) > 0) {
$result .= ', ' . implode(', ', $suffixes);
}
return $result;
}
}

View File

@@ -0,0 +1,338 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Get user's global privileges and some db-specific privileges
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Util;
/**
* PhpMyAdmin\CheckUserPrivileges class
*
* @package PhpMyAdmin
*/
class CheckUserPrivileges
{
/**
* @var DatabaseInterface
*/
private $dbi;
/**
* Constructor
*
* @param DatabaseInterface $dbi DatabaseInterface object
*/
public function __construct(DatabaseInterface $dbi)
{
$this->dbi = $dbi;
}
/**
* Extracts details from a result row of a SHOW GRANT query
*
* @param string $row grant row
*
* @return array
*/
public function getItemsFromShowGrantsRow($row)
{
$db_name_offset = mb_strpos($row, ' ON ') + 4;
$tblname_end_offset = mb_strpos($row, ' TO ');
$tblname_start_offset = false;
if ($__tblname_start_offset = mb_strpos($row, '`.', $db_name_offset)) {
if ($__tblname_start_offset < $tblname_end_offset) {
$tblname_start_offset = $__tblname_start_offset + 1;
}
}
if (!$tblname_start_offset) {
$tblname_start_offset = mb_strpos($row, '.', $db_name_offset);
}
$show_grants_dbname = mb_substr(
$row,
$db_name_offset,
$tblname_start_offset - $db_name_offset
);
$show_grants_dbname = Util::unQuote($show_grants_dbname, '`');
$show_grants_str = mb_substr(
$row,
6,
(mb_strpos($row, ' ON ') - 6)
);
$show_grants_tblname = mb_substr(
$row,
$tblname_start_offset + 1,
$tblname_end_offset - $tblname_start_offset - 1
);
$show_grants_tblname = Util::unQuote($show_grants_tblname, '`');
return array(
$show_grants_str,
$show_grants_dbname,
$show_grants_tblname
);
}
/**
* Check if user has required privileges for
* performing 'Adjust privileges' operations
*
* @param string $show_grants_str string containing grants for user
* @param string $show_grants_dbname name of db extracted from grant string
* @param string $show_grants_tblname name of table extracted from grant string
*
* @return void
*/
public function checkRequiredPrivilegesForAdjust(
$show_grants_str,
$show_grants_dbname,
$show_grants_tblname
) {
// '... ALL PRIVILEGES ON *.* ...' OR '... ALL PRIVILEGES ON `mysql`.* ..'
// OR
// SELECT, INSERT, UPDATE, DELETE .... ON *.* OR `mysql`.*
if ($show_grants_str == 'ALL'
|| $show_grants_str == 'ALL PRIVILEGES'
|| (mb_strpos(
$show_grants_str, 'SELECT, INSERT, UPDATE, DELETE'
) !== false)
) {
if ($show_grants_dbname == '*'
&& $show_grants_tblname == '*'
) {
$GLOBALS['col_priv'] = true;
$GLOBALS['db_priv'] = true;
$GLOBALS['proc_priv'] = true;
$GLOBALS['table_priv'] = true;
if ($show_grants_str == 'ALL PRIVILEGES'
|| $show_grants_str == 'ALL'
) {
$GLOBALS['is_reload_priv'] = true;
}
}
// check for specific tables in `mysql` db
// Ex. '... ALL PRIVILEGES on `mysql`.`columns_priv` .. '
if ($show_grants_dbname == 'mysql') {
switch ($show_grants_tblname) {
case "columns_priv":
$GLOBALS['col_priv'] = true;
break;
case "db":
$GLOBALS['db_priv'] = true;
break;
case "procs_priv":
$GLOBALS['proc_priv'] = true;
break;
case "tables_priv":
$GLOBALS['table_priv'] = true;
break;
case "*":
$GLOBALS['col_priv'] = true;
$GLOBALS['db_priv'] = true;
$GLOBALS['proc_priv'] = true;
$GLOBALS['table_priv'] = true;
break;
default:
}
}
}
}
/**
* sets privilege information extracted from SHOW GRANTS result
*
* Detection for some CREATE privilege.
*
* Since MySQL 4.1.2, we can easily detect current user's grants using $userlink
* (no control user needed) and we don't have to try any other method for
* detection
*
* @todo fix to get really all privileges, not only explicitly defined for this user
* from MySQL manual: (https://dev.mysql.com/doc/refman/5.0/en/show-grants.html)
* SHOW GRANTS displays only the privileges granted explicitly to the named
* account. Other privileges might be available to the account, but they are not
* displayed. For example, if an anonymous account exists, the named account
* might be able to use its privileges, but SHOW GRANTS will not display them.
*
* @return void
*/
public function analyseShowGrant()
{
if (Util::cacheExists('is_create_db_priv')) {
$GLOBALS['is_create_db_priv'] = Util::cacheGet(
'is_create_db_priv'
);
$GLOBALS['is_reload_priv'] = Util::cacheGet(
'is_reload_priv'
);
$GLOBALS['db_to_create'] = Util::cacheGet(
'db_to_create'
);
$GLOBALS['dbs_where_create_table_allowed'] = Util::cacheGet(
'dbs_where_create_table_allowed'
);
$GLOBALS['dbs_to_test'] = Util::cacheGet(
'dbs_to_test'
);
$GLOBALS['db_priv'] = Util::cacheGet(
'db_priv'
);
$GLOBALS['col_priv'] = Util::cacheGet(
'col_priv'
);
$GLOBALS['table_priv'] = Util::cacheGet(
'table_priv'
);
$GLOBALS['proc_priv'] = Util::cacheGet(
'proc_priv'
);
return;
}
// defaults
$GLOBALS['is_create_db_priv'] = false;
$GLOBALS['is_reload_priv'] = false;
$GLOBALS['db_to_create'] = '';
$GLOBALS['dbs_where_create_table_allowed'] = array();
$GLOBALS['dbs_to_test'] = $this->dbi->getSystemSchemas();
$GLOBALS['proc_priv'] = false;
$GLOBALS['db_priv'] = false;
$GLOBALS['col_priv'] = false;
$GLOBALS['table_priv'] = false;
$rs_usr = $this->dbi->tryQuery('SHOW GRANTS');
if (! $rs_usr) {
return;
}
$re0 = '(^|(\\\\\\\\)+|[^\\\\])'; // non-escaped wildcards
$re1 = '(^|[^\\\\])(\\\)+'; // escaped wildcards
while ($row = $this->dbi->fetchRow($rs_usr)) {
list(
$show_grants_str,
$show_grants_dbname,
$show_grants_tblname
) = $this->getItemsFromShowGrantsRow($row[0]);
if ($show_grants_dbname == '*') {
if ($show_grants_str != 'USAGE') {
$GLOBALS['dbs_to_test'] = false;
}
} elseif ($GLOBALS['dbs_to_test'] !== false) {
$GLOBALS['dbs_to_test'][] = $show_grants_dbname;
}
if (
mb_strpos($show_grants_str,'RELOAD') !== false
) {
$GLOBALS['is_reload_priv'] = true;
}
// check for the required privileges for adjust
$this->checkRequiredPrivilegesForAdjust(
$show_grants_str,
$show_grants_dbname,
$show_grants_tblname
);
/**
* @todo if we find CREATE VIEW but not CREATE, do not offer
* the create database dialog box
*/
if ($show_grants_str == 'ALL'
|| $show_grants_str == 'ALL PRIVILEGES'
|| $show_grants_str == 'CREATE'
|| strpos($show_grants_str, 'CREATE,') !== false
) {
if ($show_grants_dbname == '*') {
// a global CREATE privilege
$GLOBALS['is_create_db_priv'] = true;
$GLOBALS['is_reload_priv'] = true;
$GLOBALS['db_to_create'] = '';
$GLOBALS['dbs_where_create_table_allowed'][] = '*';
// @todo we should not break here, cause GRANT ALL *.*
// could be revoked by a later rule like GRANT SELECT ON db.*
break;
} else {
// this array may contain wildcards
$GLOBALS['dbs_where_create_table_allowed'][] = $show_grants_dbname;
$dbname_to_test = Util::backquote($show_grants_dbname);
if ($GLOBALS['is_create_db_priv']) {
// no need for any more tests if we already know this
continue;
}
// does this db exist?
if ((preg_match('/' . $re0 . '%|_/', $show_grants_dbname)
&& ! preg_match('/\\\\%|\\\\_/', $show_grants_dbname))
|| (! $this->dbi->tryQuery(
'USE ' . preg_replace(
'/' . $re1 . '(%|_)/', '\\1\\3', $dbname_to_test
)
)
&& mb_substr($this->dbi->getError(), 1, 4) != 1044)
) {
/**
* Do not handle the underscore wildcard
* (this case must be rare anyway)
*/
$GLOBALS['db_to_create'] = preg_replace(
'/' . $re0 . '%/', '\\1',
$show_grants_dbname
);
$GLOBALS['db_to_create'] = preg_replace(
'/' . $re1 . '(%|_)/', '\\1\\3',
$GLOBALS['db_to_create']
);
$GLOBALS['is_create_db_priv'] = true;
/**
* @todo collect $GLOBALS['db_to_create'] into an array,
* to display a drop-down in the "Create database" dialog
*/
// we don't break, we want all possible databases
//break;
} // end if
} // end elseif
} // end if
} // end while
$this->dbi->freeResult($rs_usr);
// must also cacheUnset() them in
// PhpMyAdmin\Plugins\Auth\AuthenticationCookie
Util::cacheSet('is_create_db_priv', $GLOBALS['is_create_db_priv']);
Util::cacheSet('is_reload_priv', $GLOBALS['is_reload_priv']);
Util::cacheSet('db_to_create', $GLOBALS['db_to_create']);
Util::cacheSet(
'dbs_where_create_table_allowed',
$GLOBALS['dbs_where_create_table_allowed']
);
Util::cacheSet('dbs_to_test', $GLOBALS['dbs_to_test']);
Util::cacheSet('proc_priv', $GLOBALS['proc_priv']);
Util::cacheSet('table_priv', $GLOBALS['table_priv']);
Util::cacheSet('col_priv', $GLOBALS['col_priv']);
Util::cacheSet('db_priv', $GLOBALS['db_priv']);
} // end function
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,531 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Config file management
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Config;
use PhpMyAdmin\Core;
/**
* Config file management class.
* Stores its data in $_SESSION
*
* @package PhpMyAdmin
*/
class ConfigFile
{
/**
* Stores default PMA config from config.default.php
* @var array
*/
private $_defaultCfg;
/**
* Stores allowed values for non-standard fields
* @var array
*/
private $_cfgDb;
/**
* Stores original PMA config, not modified by user preferences
* @var Config
*/
private $_baseCfg;
/**
* Whether we are currently working in PMA Setup context
* @var bool
*/
private $_isInSetup;
/**
* Keys which will be always written to config file
* @var array
*/
private $_persistKeys = array();
/**
* Changes keys while updating config in {@link updateWithGlobalConfig()}
* or reading by {@link getConfig()} or {@link getConfigArray()}
* @var array
*/
private $_cfgUpdateReadMapping = array();
/**
* Key filter for {@link set()}
* @var array|null
*/
private $_setFilter;
/**
* Instance id (key in $_SESSION array, separate for each server -
* ConfigFile{server id})
* @var string
*/
private $_id;
/**
* Result for {@link _flattenArray()}
* @var array|null
*/
private $_flattenArrayResult;
/**
* Constructor
*
* @param array|null $base_config base configuration read from
* {@link PhpMyAdmin\Config::$base_config},
* use only when not in PMA Setup
*/
public function __construct($base_config = null)
{
// load default config values
$cfg = &$this->_defaultCfg;
include './libraries/config.default.php';
$cfg['fontsize'] = '82%';
// load additional config information
$cfg_db = &$this->_cfgDb;
include './libraries/config.values.php';
// apply default values overrides
if (count($cfg_db['_overrides'])) {
foreach ($cfg_db['_overrides'] as $path => $value) {
Core::arrayWrite($path, $cfg, $value);
}
}
$this->_baseCfg = $base_config;
$this->_isInSetup = is_null($base_config);
$this->_id = 'ConfigFile' . $GLOBALS['server'];
if (!isset($_SESSION[$this->_id])) {
$_SESSION[$this->_id] = array();
}
}
/**
* Sets names of config options which will be placed in config file even if
* they are set to their default values (use only full paths)
*
* @param array $keys the names of the config options
*
* @return void
*/
public function setPersistKeys(array $keys)
{
// checking key presence is much faster than searching so move values
// to keys
$this->_persistKeys = array_flip($keys);
}
/**
* Returns flipped array set by {@link setPersistKeys()}
*
* @return array
*/
public function getPersistKeysMap()
{
return $this->_persistKeys;
}
/**
* By default ConfigFile allows setting of all configuration keys, use
* this method to set up a filter on {@link set()} method
*
* @param array|null $keys array of allowed keys or null to remove filter
*
* @return void
*/
public function setAllowedKeys($keys)
{
if ($keys === null) {
$this->_setFilter = null;
return;
}
// checking key presence is much faster than searching so move values
// to keys
$this->_setFilter = array_flip($keys);
}
/**
* Sets path mapping for updating config in
* {@link updateWithGlobalConfig()} or reading
* by {@link getConfig()} or {@link getConfigArray()}
*
* @param array $mapping Contains the mapping of "Server/config options"
* to "Server/1/config options"
*
* @return void
*/
public function setCfgUpdateReadMapping(array $mapping)
{
$this->_cfgUpdateReadMapping = $mapping;
}
/**
* Resets configuration data
*
* @return void
*/
public function resetConfigData()
{
$_SESSION[$this->_id] = array();
}
/**
* Sets configuration data (overrides old data)
*
* @param array $cfg Configuration options
*
* @return void
*/
public function setConfigData(array $cfg)
{
$_SESSION[$this->_id] = $cfg;
}
/**
* Sets config value
*
* @param string $path Path
* @param mixed $value Value
* @param string $canonical_path Canonical path
*
* @return void
*/
public function set($path, $value, $canonical_path = null)
{
if ($canonical_path === null) {
$canonical_path = $this->getCanonicalPath($path);
}
// apply key whitelist
if ($this->_setFilter !== null
&& ! isset($this->_setFilter[$canonical_path])
) {
return;
}
// if the path isn't protected it may be removed
if (isset($this->_persistKeys[$canonical_path])) {
Core::arrayWrite($path, $_SESSION[$this->_id], $value);
return;
}
$default_value = $this->getDefault($canonical_path);
$remove_path = $value === $default_value;
if ($this->_isInSetup) {
// remove if it has a default value or is empty
$remove_path = $remove_path
|| (empty($value) && empty($default_value));
} else {
// get original config values not overwritten by user
// preferences to allow for overwriting options set in
// config.inc.php with default values
$instance_default_value = Core::arrayRead(
$canonical_path,
$this->_baseCfg
);
// remove if it has a default value and base config (config.inc.php)
// uses default value
$remove_path = $remove_path
&& ($instance_default_value === $default_value);
}
if ($remove_path) {
Core::arrayRemove($path, $_SESSION[$this->_id]);
return;
}
Core::arrayWrite($path, $_SESSION[$this->_id], $value);
}
/**
* Flattens multidimensional array, changes indices to paths
* (eg. 'key/subkey').
* Used as array_walk() callback.
*
* @param mixed $value Value
* @param mixed $key Key
* @param mixed $prefix Prefix
*
* @return void
*/
private function _flattenArray($value, $key, $prefix)
{
// no recursion for numeric arrays
if (is_array($value) && !isset($value[0])) {
$prefix .= $key . '/';
array_walk($value, array($this, '_flattenArray'), $prefix);
} else {
$this->_flattenArrayResult[$prefix . $key] = $value;
}
}
/**
* Returns default config in a flattened array
*
* @return array
*/
public function getFlatDefaultConfig()
{
$this->_flattenArrayResult = array();
array_walk($this->_defaultCfg, array($this, '_flattenArray'), '');
$flat_cfg = $this->_flattenArrayResult;
$this->_flattenArrayResult = null;
return $flat_cfg;
}
/**
* Updates config with values read from given array
* (config will contain differences to defaults from config.defaults.php).
*
* @param array $cfg Configuration
*
* @return void
*/
public function updateWithGlobalConfig(array $cfg)
{
// load config array and flatten it
$this->_flattenArrayResult = array();
array_walk($cfg, array($this, '_flattenArray'), '');
$flat_cfg = $this->_flattenArrayResult;
$this->_flattenArrayResult = null;
// save values map for translating a few user preferences paths,
// should be complemented by code reading from generated config
// to perform inverse mapping
foreach ($flat_cfg as $path => $value) {
if (isset($this->_cfgUpdateReadMapping[$path])) {
$path = $this->_cfgUpdateReadMapping[$path];
}
$this->set($path, $value, $path);
}
}
/**
* Returns config value or $default if it's not set
*
* @param string $path Path of config file
* @param mixed $default Default values
*
* @return mixed
*/
public function get($path, $default = null)
{
return Core::arrayRead($path, $_SESSION[$this->_id], $default);
}
/**
* Returns default config value or $default it it's not set ie. it doesn't
* exist in config.default.php ($cfg) and config.values.php
* ($_cfg_db['_overrides'])
*
* @param string $canonical_path Canonical path
* @param mixed $default Default value
*
* @return mixed
*/
public function getDefault($canonical_path, $default = null)
{
return Core::arrayRead($canonical_path, $this->_defaultCfg, $default);
}
/**
* Returns config value, if it's not set uses the default one; returns
* $default if the path isn't set and doesn't contain a default value
*
* @param string $path Path
* @param mixed $default Default value
*
* @return mixed
*/
public function getValue($path, $default = null)
{
$v = Core::arrayRead($path, $_SESSION[$this->_id], null);
if ($v !== null) {
return $v;
}
$path = $this->getCanonicalPath($path);
return $this->getDefault($path, $default);
}
/**
* Returns canonical path
*
* @param string $path Path
*
* @return string
*/
public function getCanonicalPath($path)
{
return preg_replace('#^Servers/([\d]+)/#', 'Servers/1/', $path);
}
/**
* Returns config database entry for $path ($cfg_db in config_info.php)
*
* @param string $path path of the variable in config db
* @param mixed $default default value
*
* @return mixed
*/
public function getDbEntry($path, $default = null)
{
return Core::arrayRead($path, $this->_cfgDb, $default);
}
/**
* Returns server count
*
* @return int
*/
public function getServerCount()
{
return isset($_SESSION[$this->_id]['Servers'])
? count($_SESSION[$this->_id]['Servers'])
: 0;
}
/**
* Returns server list
*
* @return array|null
*/
public function getServers()
{
return isset($_SESSION[$this->_id]['Servers'])
? $_SESSION[$this->_id]['Servers']
: null;
}
/**
* Returns DSN of given server
*
* @param integer $server server index
*
* @return string
*/
public function getServerDSN($server)
{
if (!isset($_SESSION[$this->_id]['Servers'][$server])) {
return '';
}
$path = 'Servers/' . $server;
$dsn = 'mysqli://';
if ($this->getValue("$path/auth_type") == 'config') {
$dsn .= $this->getValue("$path/user");
if (! empty($this->getValue("$path/password"))) {
$dsn .= ':***';
}
$dsn .= '@';
}
if ($this->getValue("$path/host") != 'localhost') {
$dsn .= $this->getValue("$path/host");
$port = $this->getValue("$path/port");
if ($port) {
$dsn .= ':' . $port;
}
} else {
$dsn .= $this->getValue("$path/socket");
}
return $dsn;
}
/**
* Returns server name
*
* @param int $id server index
*
* @return string
*/
public function getServerName($id)
{
if (!isset($_SESSION[$this->_id]['Servers'][$id])) {
return '';
}
$verbose = $this->get("Servers/$id/verbose");
if (!empty($verbose)) {
return $verbose;
}
$host = $this->get("Servers/$id/host");
return empty($host) ? 'localhost' : $host;
}
/**
* Removes server
*
* @param int $server server index
*
* @return void
*/
public function removeServer($server)
{
if (!isset($_SESSION[$this->_id]['Servers'][$server])) {
return;
}
$last_server = $this->getServerCount();
for ($i = $server; $i < $last_server; $i++) {
$_SESSION[$this->_id]['Servers'][$i]
= $_SESSION[$this->_id]['Servers'][$i + 1];
}
unset($_SESSION[$this->_id]['Servers'][$last_server]);
if (isset($_SESSION[$this->_id]['ServerDefault'])
&& $_SESSION[$this->_id]['ServerDefault'] == $last_server
) {
unset($_SESSION[$this->_id]['ServerDefault']);
}
}
/**
* Returns configuration array (full, multidimensional format)
*
* @return array
*/
public function getConfig()
{
$c = $_SESSION[$this->_id];
foreach ($this->_cfgUpdateReadMapping as $map_to => $map_from) {
// if the key $c exists in $map_to
if (Core::arrayRead($map_to, $c) !== null) {
Core::arrayWrite($map_to, $c, Core::arrayRead($map_from, $c));
Core::arrayRemove($map_from, $c);
}
}
return $c;
}
/**
* Returns configuration array (flat format)
*
* @return array
*/
public function getConfigArray()
{
$this->_flattenArrayResult = array();
array_walk($_SESSION[$this->_id], array($this, '_flattenArray'), '');
$c = $this->_flattenArrayResult;
$this->_flattenArrayResult = null;
$persistKeys = array_diff(
array_keys($this->_persistKeys),
array_keys($c)
);
foreach ($persistKeys as $k) {
$c[$k] = $this->getDefault($this->getCanonicalPath($k));
}
foreach ($this->_cfgUpdateReadMapping as $map_to => $map_from) {
if (!isset($c[$map_from])) {
continue;
}
$c[$map_to] = $c[$map_from];
unset($c[$map_from]);
}
return $c;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,233 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Form handling code.
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
/**
* Base class for forms, loads default configuration options, checks allowed
* values etc.
*
* @package PhpMyAdmin
*/
class Form
{
/**
* Form name
* @var string
*/
public $name;
/**
* Arbitrary index, doesn't affect class' behavior
* @var int
*/
public $index;
/**
* Form fields (paths), filled by {@link readFormPaths()}, indexed by field name
* @var array
*/
public $fields;
/**
* Stores default values for some fields (eg. pmadb tables)
* @var array
*/
public $default;
/**
* Caches field types, indexed by field names
* @var array
*/
private $_fieldsTypes;
/**
* ConfigFile instance
* @var ConfigFile
*/
private $_configFile;
/**
* Constructor, reads default config values
*
* @param string $form_name Form name
* @param array $form Form data
* @param ConfigFile $cf Config file instance
* @param int $index arbitrary index, stored in Form::$index
*/
public function __construct(
$form_name, array $form, ConfigFile $cf, $index = null
) {
$this->index = $index;
$this->_configFile = $cf;
$this->loadForm($form_name, $form);
}
/**
* Returns type of given option
*
* @param string $option_name path or field name
*
* @return string|null one of: boolean, integer, double, string, select, array
*/
public function getOptionType($option_name)
{
$key = ltrim(
mb_substr(
$option_name,
mb_strrpos($option_name, '/')
),
'/'
);
return isset($this->_fieldsTypes[$key])
? $this->_fieldsTypes[$key]
: null;
}
/**
* Returns allowed values for select fields
*
* @param string $option_path Option path
*
* @return array
*/
public function getOptionValueList($option_path)
{
$value = $this->_configFile->getDbEntry($option_path);
if ($value === null) {
trigger_error("$option_path - select options not defined", E_USER_ERROR);
return array();
}
if (!is_array($value)) {
trigger_error("$option_path - not a static value list", E_USER_ERROR);
return array();
}
// convert array('#', 'a', 'b') to array('a', 'b')
if (isset($value[0]) && $value[0] === '#') {
// remove first element ('#')
array_shift($value);
// $value has keys and value names, return it
return $value;
}
// convert value list array('a', 'b') to array('a' => 'a', 'b' => 'b')
$has_string_keys = false;
$keys = array();
for ($i = 0, $nb = count($value); $i < $nb; $i++) {
if (!isset($value[$i])) {
$has_string_keys = true;
break;
}
$keys[] = is_bool($value[$i]) ? (int)$value[$i] : $value[$i];
}
if (! $has_string_keys) {
$value = array_combine($keys, $value);
}
// $value has keys and value names, return it
return $value;
}
/**
* array_walk callback function, reads path of form fields from
* array (see docs for \PhpMyAdmin\Config\Forms\BaseForm::getForms)
*
* @param mixed $value Value
* @param mixed $key Key
* @param mixed $prefix Prefix
*
* @return void
*/
private function _readFormPathsCallback($value, $key, $prefix)
{
static $group_counter = 0;
if (is_array($value)) {
$prefix .= $key . '/';
array_walk($value, array($this, '_readFormPathsCallback'), $prefix);
return;
}
if (!is_int($key)) {
$this->default[$prefix . $key] = $value;
$value = $key;
}
// add unique id to group ends
if ($value == ':group:end') {
$value .= ':' . $group_counter++;
}
$this->fields[] = $prefix . $value;
}
/**
* Reads form paths to {@link $fields}
*
* @param array $form Form
*
* @return void
*/
protected function readFormPaths(array $form)
{
// flatten form fields' paths and save them to $fields
$this->fields = array();
array_walk($form, array($this, '_readFormPathsCallback'), '');
// $this->fields is an array of the form: [0..n] => 'field path'
// change numeric indexes to contain field names (last part of the path)
$paths = $this->fields;
$this->fields = array();
foreach ($paths as $path) {
$key = ltrim(
mb_substr($path, mb_strrpos($path, '/')),
'/'
);
$this->fields[$key] = $path;
}
// now $this->fields is an array of the form: 'field name' => 'field path'
}
/**
* Reads fields' types to $this->_fieldsTypes
*
* @return void
*/
protected function readTypes()
{
$cf = $this->_configFile;
foreach ($this->fields as $name => $path) {
if (mb_strpos($name, ':group:') === 0) {
$this->_fieldsTypes[$name] = 'group';
continue;
}
$v = $cf->getDbEntry($path);
if ($v !== null) {
$type = is_array($v) ? 'select' : $v;
} else {
$type = gettype($cf->getDefault($path));
}
$this->_fieldsTypes[$name] = $type;
}
}
/**
* Reads form settings and prepares class to work with given subset of
* config file
*
* @param string $form_name Form name
* @param array $form Form
*
* @return void
*/
public function loadForm($form_name, array $form)
{
$this->name = $form_name;
$this->readFormPaths($form);
$this->readTypes();
}
}

View File

@@ -0,0 +1,880 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Form management class, displays and processes forms
*
* Explanation of used terms:
* o work_path - original field path, eg. Servers/4/verbose
* o system_path - work_path modified so that it points to the first server,
* eg. Servers/1/verbose
* o translated_path - work_path modified for HTML field name, a path with
* slashes changed to hyphens, eg. Servers-4-verbose
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\Descriptions;
use PhpMyAdmin\Config\Form;
use PhpMyAdmin\Config\FormDisplayTemplate;
use PhpMyAdmin\Config\Forms\User\UserFormList;
use PhpMyAdmin\Config\Validator;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\Util;
/**
* Form management class, displays and processes forms
*
* @package PhpMyAdmin
*/
class FormDisplay
{
/**
* ConfigFile instance
* @var ConfigFile
*/
private $_configFile;
/**
* Form list
* @var Form[]
*/
private $_forms = array();
/**
* Stores validation errors, indexed by paths
* [ Form_name ] is an array of form errors
* [path] is a string storing error associated with single field
* @var array
*/
private $_errors = array();
/**
* Paths changed so that they can be used as HTML ids, indexed by paths
* @var array
*/
private $_translatedPaths = array();
/**
* Server paths change indexes so we define maps from current server
* path to the first one, indexed by work path
* @var array
*/
private $_systemPaths = array();
/**
* Language strings which will be sent to PMA_messages JS variable
* Will be looked up in $GLOBALS: str{value} or strSetup{value}
* @var array
*/
private $_jsLangStrings = array();
/**
* Tells whether forms have been validated
* @var bool
*/
private $_isValidated = true;
/**
* Dictionary with user preferences keys
* @var array|null
*/
private $_userprefsKeys;
/**
* Dictionary with disallowed user preferences keys
* @var array
*/
private $_userprefsDisallow;
/**
* Constructor
*
* @param ConfigFile $cf Config file instance
*/
public function __construct(ConfigFile $cf)
{
$this->_jsLangStrings = array(
'error_nan_p' => __('Not a positive number!'),
'error_nan_nneg' => __('Not a non-negative number!'),
'error_incorrect_port' => __('Not a valid port number!'),
'error_invalid_value' => __('Incorrect value!'),
'error_value_lte' => __('Value must be less than or equal to %s!'));
$this->_configFile = $cf;
// initialize validators
Validator::getValidators($this->_configFile);
}
/**
* Returns {@link ConfigFile} associated with this instance
*
* @return ConfigFile
*/
public function getConfigFile()
{
return $this->_configFile;
}
/**
* Registers form in form manager
*
* @param string $form_name Form name
* @param array $form Form data
* @param int $server_id 0 if new server, validation; >= 1 if editing a server
*
* @return void
*/
public function registerForm($form_name, array $form, $server_id = null)
{
$this->_forms[$form_name] = new Form(
$form_name, $form, $this->_configFile, $server_id
);
$this->_isValidated = false;
foreach ($this->_forms[$form_name]->fields as $path) {
$work_path = $server_id === null
? $path
: str_replace('Servers/1/', "Servers/$server_id/", $path);
$this->_systemPaths[$work_path] = $path;
$this->_translatedPaths[$work_path] = str_replace('/', '-', $work_path);
}
}
/**
* Processes forms, returns true on successful save
*
* @param bool $allow_partial_save allows for partial form saving
* on failed validation
* @param bool $check_form_submit whether check for $_POST['submit_save']
*
* @return boolean whether processing was successful
*/
public function process($allow_partial_save = true, $check_form_submit = true)
{
if ($check_form_submit && !isset($_POST['submit_save'])) {
return false;
}
// save forms
if (count($this->_forms) > 0) {
return $this->save(array_keys($this->_forms), $allow_partial_save);
}
return false;
}
/**
* Runs validation for all registered forms
*
* @return void
*/
private function _validate()
{
if ($this->_isValidated) {
return;
}
$paths = array();
$values = array();
foreach ($this->_forms as $form) {
/* @var $form Form */
$paths[] = $form->name;
// collect values and paths
foreach ($form->fields as $path) {
$work_path = array_search($path, $this->_systemPaths);
$values[$path] = $this->_configFile->getValue($work_path);
$paths[] = $path;
}
}
// run validation
$errors = Validator::validate(
$this->_configFile, $paths, $values, false
);
// change error keys from canonical paths to work paths
if (is_array($errors) && count($errors) > 0) {
$this->_errors = array();
foreach ($errors as $path => $error_list) {
$work_path = array_search($path, $this->_systemPaths);
// field error
if (! $work_path) {
// form error, fix path
$work_path = $path;
}
$this->_errors[$work_path] = $error_list;
}
}
$this->_isValidated = true;
}
/**
* Outputs HTML for the forms under the menu tab
*
* @param bool $show_restore_default whether to show "restore default"
* button besides the input field
* @param array &$js_default stores JavaScript code
* to be displayed
* @param array &$js will be updated with javascript code
* @param bool $show_buttons whether show submit and reset button
*
* @return string $htmlOutput
*/
private function _displayForms(
$show_restore_default, array &$js_default, array &$js, $show_buttons
) {
$htmlOutput = '';
$validators = Validator::getValidators($this->_configFile);
foreach ($this->_forms as $form) {
/* @var $form Form */
$form_errors = isset($this->_errors[$form->name])
? $this->_errors[$form->name] : null;
$htmlOutput .= FormDisplayTemplate::displayFieldsetTop(
Descriptions::get("Form_{$form->name}"),
Descriptions::get("Form_{$form->name}", 'desc'),
$form_errors,
array('id' => $form->name)
);
foreach ($form->fields as $field => $path) {
$work_path = array_search($path, $this->_systemPaths);
$translated_path = $this->_translatedPaths[$work_path];
// always true/false for user preferences display
// otherwise null
$userprefs_allow = isset($this->_userprefsKeys[$path])
? !isset($this->_userprefsDisallow[$path])
: null;
// display input
$htmlOutput .= $this->_displayFieldInput(
$form,
$field,
$path,
$work_path,
$translated_path,
$show_restore_default,
$userprefs_allow,
$js_default
);
// register JS validators for this field
if (isset($validators[$path])) {
FormDisplayTemplate::addJsValidate($translated_path, $validators[$path], $js);
}
}
$htmlOutput .= FormDisplayTemplate::displayFieldsetBottom($show_buttons);
}
return $htmlOutput;
}
/**
* Outputs HTML for forms
*
* @param bool $tabbed_form if true, use a form with tabs
* @param bool $show_restore_default whether show "restore default" button
* besides the input field
* @param bool $show_buttons whether show submit and reset button
* @param string $form_action action attribute for the form
* @param array|null $hidden_fields array of form hidden fields (key: field
* name)
*
* @return string HTML for forms
*/
public function getDisplay(
$tabbed_form = false,
$show_restore_default = false,
$show_buttons = true,
$form_action = null,
$hidden_fields = null
) {
static $js_lang_sent = false;
$htmlOutput = '';
$js = array();
$js_default = array();
$htmlOutput .= FormDisplayTemplate::displayFormTop($form_action, 'post', $hidden_fields);
if ($tabbed_form) {
$tabs = array();
foreach ($this->_forms as $form) {
$tabs[$form->name] = Descriptions::get("Form_$form->name");
}
$htmlOutput .= FormDisplayTemplate::displayTabsTop($tabs);
}
// validate only when we aren't displaying a "new server" form
$is_new_server = false;
foreach ($this->_forms as $form) {
/* @var $form Form */
if ($form->index === 0) {
$is_new_server = true;
break;
}
}
if (! $is_new_server) {
$this->_validate();
}
// user preferences
$this->_loadUserprefsInfo();
// display forms
$htmlOutput .= $this->_displayForms(
$show_restore_default, $js_default, $js, $show_buttons
);
if ($tabbed_form) {
$htmlOutput .= FormDisplayTemplate::displayTabsBottom();
}
$htmlOutput .= FormDisplayTemplate::displayFormBottom();
// if not already done, send strings used for validation to JavaScript
if (! $js_lang_sent) {
$js_lang_sent = true;
$js_lang = array();
foreach ($this->_jsLangStrings as $strName => $strValue) {
$js_lang[] = "'$strName': '" . Sanitize::jsFormat($strValue, false) . '\'';
}
$js[] = "$.extend(PMA_messages, {\n\t"
. implode(",\n\t", $js_lang) . '})';
}
$js[] = "$.extend(defaultValues, {\n\t"
. implode(",\n\t", $js_default) . '})';
$htmlOutput .= FormDisplayTemplate::displayJavascript($js);
return $htmlOutput;
}
/**
* Prepares data for input field display and outputs HTML code
*
* @param Form $form Form object
* @param string $field field name as it appears in $form
* @param string $system_path field path, eg. Servers/1/verbose
* @param string $work_path work path, eg. Servers/4/verbose
* @param string $translated_path work path changed so that it can be
* used as XHTML id
* @param bool $show_restore_default whether show "restore default" button
* besides the input field
* @param bool|null $userprefs_allow whether user preferences are enabled
* for this field (null - no support,
* true/false - enabled/disabled)
* @param array &$js_default array which stores JavaScript code
* to be displayed
*
* @return string HTML for input field
*/
private function _displayFieldInput(
Form $form, $field, $system_path, $work_path,
$translated_path, $show_restore_default, $userprefs_allow, array &$js_default
) {
$name = Descriptions::get($system_path);
$description = Descriptions::get($system_path, 'desc');
$value = $this->_configFile->get($work_path);
$value_default = $this->_configFile->getDefault($system_path);
$value_is_default = false;
if ($value === null || $value === $value_default) {
$value = $value_default;
$value_is_default = true;
}
$opts = array(
'doc' => $this->getDocLink($system_path),
'show_restore_default' => $show_restore_default,
'userprefs_allow' => $userprefs_allow,
'userprefs_comment' => Descriptions::get($system_path, 'cmt')
);
if (isset($form->default[$system_path])) {
$opts['setvalue'] = $form->default[$system_path];
}
if (isset($this->_errors[$work_path])) {
$opts['errors'] = $this->_errors[$work_path];
}
$type = '';
switch ($form->getOptionType($field)) {
case 'string':
$type = 'text';
break;
case 'short_string':
$type = 'short_text';
break;
case 'double':
case 'integer':
$type = 'number_text';
break;
case 'boolean':
$type = 'checkbox';
break;
case 'select':
$type = 'select';
$opts['values'] = $form->getOptionValueList($form->fields[$field]);
break;
case 'array':
$type = 'list';
$value = (array) $value;
$value_default = (array) $value_default;
break;
case 'group':
// :group:end is changed to :group:end:{unique id} in Form class
$htmlOutput = '';
if (mb_substr($field, 7, 4) != 'end:') {
$htmlOutput .= FormDisplayTemplate::displayGroupHeader(
mb_substr($field, 7)
);
} else {
FormDisplayTemplate::displayGroupFooter();
}
return $htmlOutput;
case 'NULL':
trigger_error("Field $system_path has no type", E_USER_WARNING);
return null;
}
// detect password fields
if ($type === 'text'
&& (mb_substr($translated_path, -9) === '-password'
|| mb_substr($translated_path, -4) === 'pass'
|| mb_substr($translated_path, -4) === 'Pass')
) {
$type = 'password';
}
// TrustedProxies requires changes before displaying
if ($system_path == 'TrustedProxies') {
foreach ($value as $ip => &$v) {
if (!preg_match('/^-\d+$/', $ip)) {
$v = $ip . ': ' . $v;
}
}
}
$this->_setComments($system_path, $opts);
// send default value to form's JS
$js_line = '\'' . $translated_path . '\': ';
switch ($type) {
case 'text':
case 'short_text':
case 'number_text':
case 'password':
$js_line .= '\'' . Sanitize::escapeJsString($value_default) . '\'';
break;
case 'checkbox':
$js_line .= $value_default ? 'true' : 'false';
break;
case 'select':
$value_default_js = is_bool($value_default)
? (int) $value_default
: $value_default;
$js_line .= '[\'' . Sanitize::escapeJsString($value_default_js) . '\']';
break;
case 'list':
$js_line .= '\'' . Sanitize::escapeJsString(implode("\n", $value_default))
. '\'';
break;
}
$js_default[] = $js_line;
return FormDisplayTemplate::displayInput(
$translated_path, $name, $type, $value,
$description, $value_is_default, $opts
);
}
/**
* Displays errors
*
* @return string HTML for errors
*/
public function displayErrors()
{
$this->_validate();
if (count($this->_errors) == 0) {
return null;
}
$htmlOutput = '';
foreach ($this->_errors as $system_path => $error_list) {
if (isset($this->_systemPaths[$system_path])) {
$name = Descriptions::get($this->_systemPaths[$system_path]);
} else {
$name = Descriptions::get('Form_' . $system_path);
}
$htmlOutput .= FormDisplayTemplate::displayErrors($name, $error_list);
}
return $htmlOutput;
}
/**
* Reverts erroneous fields to their default values
*
* @return void
*/
public function fixErrors()
{
$this->_validate();
if (count($this->_errors) == 0) {
return;
}
$cf = $this->_configFile;
foreach (array_keys($this->_errors) as $work_path) {
if (!isset($this->_systemPaths[$work_path])) {
continue;
}
$canonical_path = $this->_systemPaths[$work_path];
$cf->set($work_path, $cf->getDefault($canonical_path));
}
}
/**
* Validates select field and casts $value to correct type
*
* @param string &$value Current value
* @param array $allowed List of allowed values
*
* @return bool
*/
private function _validateSelect(&$value, array $allowed)
{
$value_cmp = is_bool($value)
? (int) $value
: $value;
foreach ($allowed as $vk => $v) {
// equality comparison only if both values are numeric or not numeric
// (allows to skip 0 == 'string' equalling to true)
// or identity (for string-string)
if (($vk == $value && !(is_numeric($value_cmp) xor is_numeric($vk)))
|| $vk === $value
) {
// keep boolean value as boolean
if (!is_bool($value)) {
settype($value, gettype($vk));
}
return true;
}
}
return false;
}
/**
* Validates and saves form data to session
*
* @param array|string $forms array of form names
* @param bool $allow_partial_save allows for partial form saving on
* failed validation
*
* @return boolean true on success (no errors and all saved)
*/
public function save($forms, $allow_partial_save = true)
{
$result = true;
$forms = (array) $forms;
$values = array();
$to_save = array();
$is_setup_script = $GLOBALS['PMA_Config']->get('is_setup');
if ($is_setup_script) {
$this->_loadUserprefsInfo();
}
$this->_errors = array();
foreach ($forms as $form_name) {
/* @var $form Form */
if (isset($this->_forms[$form_name])) {
$form = $this->_forms[$form_name];
} else {
continue;
}
// get current server id
$change_index = $form->index === 0
? $this->_configFile->getServerCount() + 1
: false;
// grab POST values
foreach ($form->fields as $field => $system_path) {
$work_path = array_search($system_path, $this->_systemPaths);
$key = $this->_translatedPaths[$work_path];
$type = $form->getOptionType($field);
// skip groups
if ($type == 'group') {
continue;
}
// ensure the value is set
if (!isset($_POST[$key])) {
// checkboxes aren't set by browsers if they're off
if ($type == 'boolean') {
$_POST[$key] = false;
} else {
$this->_errors[$form->name][] = sprintf(
__('Missing data for %s'),
'<i>' . Descriptions::get($system_path) . '</i>'
);
$result = false;
continue;
}
}
// user preferences allow/disallow
if ($is_setup_script
&& isset($this->_userprefsKeys[$system_path])
) {
if (isset($this->_userprefsDisallow[$system_path])
&& isset($_POST[$key . '-userprefs-allow'])
) {
unset($this->_userprefsDisallow[$system_path]);
} elseif (!isset($_POST[$key . '-userprefs-allow'])) {
$this->_userprefsDisallow[$system_path] = true;
}
}
// cast variables to correct type
switch ($type) {
case 'double':
$_POST[$key] = Util::requestString($_POST[$key]);
settype($_POST[$key], 'float');
break;
case 'boolean':
case 'integer':
if ($_POST[$key] !== '') {
$_POST[$key] = Util::requestString($_POST[$key]);
settype($_POST[$key], $type);
}
break;
case 'select':
$successfully_validated = $this->_validateSelect(
$_POST[$key],
$form->getOptionValueList($system_path)
);
if (! $successfully_validated) {
$this->_errors[$work_path][] = __('Incorrect value!');
$result = false;
// "continue" for the $form->fields foreach-loop
continue 2;
}
break;
case 'string':
case 'short_string':
$_POST[$key] = Util::requestString($_POST[$key]);
break;
case 'array':
// eliminate empty values and ensure we have an array
$post_values = is_array($_POST[$key])
? $_POST[$key]
: explode("\n", $_POST[$key]);
$_POST[$key] = array();
$this->_fillPostArrayParameters($post_values, $key);
break;
}
// now we have value with proper type
$values[$system_path] = $_POST[$key];
if ($change_index !== false) {
$work_path = str_replace(
"Servers/$form->index/",
"Servers/$change_index/", $work_path
);
}
$to_save[$work_path] = $system_path;
}
}
// save forms
if (!$allow_partial_save && !empty($this->_errors)) {
// don't look for non-critical errors
$this->_validate();
return $result;
}
foreach ($to_save as $work_path => $path) {
// TrustedProxies requires changes before saving
if ($path == 'TrustedProxies') {
$proxies = array();
$i = 0;
foreach ($values[$path] as $value) {
$matches = array();
$match = preg_match(
"/^(.+):(?:[ ]?)(\\w+)$/", $value, $matches
);
if ($match) {
// correct 'IP: HTTP header' pair
$ip = trim($matches[1]);
$proxies[$ip] = trim($matches[2]);
} else {
// save also incorrect values
$proxies["-$i"] = $value;
$i++;
}
}
$values[$path] = $proxies;
}
$this->_configFile->set($work_path, $values[$path], $path);
}
if ($is_setup_script) {
$this->_configFile->set(
'UserprefsDisallow',
array_keys($this->_userprefsDisallow)
);
}
// don't look for non-critical errors
$this->_validate();
return $result;
}
/**
* Tells whether form validation failed
*
* @return boolean
*/
public function hasErrors()
{
return count($this->_errors) > 0;
}
/**
* Returns link to documentation
*
* @param string $path Path to documentation
*
* @return string
*/
public function getDocLink($path)
{
$test = mb_substr($path, 0, 6);
if ($test == 'Import' || $test == 'Export') {
return '';
}
return Util::getDocuLink(
'config',
'cfg_' . $this->_getOptName($path)
);
}
/**
* Changes path so it can be used in URLs
*
* @param string $path Path
*
* @return string
*/
private function _getOptName($path)
{
return str_replace(array('Servers/1/', '/'), array('Servers/', '_'), $path);
}
/**
* Fills out {@link userprefs_keys} and {@link userprefs_disallow}
*
* @return void
*/
private function _loadUserprefsInfo()
{
if ($this->_userprefsKeys !== null) {
return;
}
$this->_userprefsKeys = array_flip(UserFormList::getFields());
// read real config for user preferences display
$userprefs_disallow = $GLOBALS['PMA_Config']->get('is_setup')
? $this->_configFile->get('UserprefsDisallow', array())
: $GLOBALS['cfg']['UserprefsDisallow'];
$this->_userprefsDisallow = array_flip($userprefs_disallow);
}
/**
* Sets field comments and warnings based on current environment
*
* @param string $system_path Path to settings
* @param array &$opts Chosen options
*
* @return void
*/
private function _setComments($system_path, array &$opts)
{
// RecodingEngine - mark unavailable types
if ($system_path == 'RecodingEngine') {
$comment = '';
if (!function_exists('iconv')) {
$opts['values']['iconv'] .= ' (' . __('unavailable') . ')';
$comment = sprintf(
__('"%s" requires %s extension'), 'iconv', 'iconv'
);
}
if (!function_exists('recode_string')) {
$opts['values']['recode'] .= ' (' . __('unavailable') . ')';
$comment .= ($comment ? ", " : '') . sprintf(
__('"%s" requires %s extension'),
'recode', 'recode'
);
}
/* mbstring is always there thanks to polyfill */
$opts['comment'] = $comment;
$opts['comment_warning'] = true;
}
// ZipDump, GZipDump, BZipDump - check function availability
if ($system_path == 'ZipDump'
|| $system_path == 'GZipDump'
|| $system_path == 'BZipDump'
) {
$comment = '';
$funcs = array(
'ZipDump' => array('zip_open', 'gzcompress'),
'GZipDump' => array('gzopen', 'gzencode'),
'BZipDump' => array('bzopen', 'bzcompress'));
if (!function_exists($funcs[$system_path][0])) {
$comment = sprintf(
__(
'Compressed import will not work due to missing function %s.'
),
$funcs[$system_path][0]
);
}
if (!function_exists($funcs[$system_path][1])) {
$comment .= ($comment ? '; ' : '') . sprintf(
__(
'Compressed export will not work due to missing function %s.'
),
$funcs[$system_path][1]
);
}
$opts['comment'] = $comment;
$opts['comment_warning'] = true;
}
if (! $GLOBALS['PMA_Config']->get('is_setup')) {
if (($system_path == 'MaxDbList' || $system_path == 'MaxTableList'
|| $system_path == 'QueryHistoryMax')
) {
$opts['comment'] = sprintf(
__('maximum %s'), $GLOBALS['cfg'][$system_path]
);
}
}
}
/**
* Copy items of an array to $_POST variable
*
* @param array $post_values List of parameters
* @param string $key Array key
*
* @return void
*/
private function _fillPostArrayParameters(array $post_values, $key)
{
foreach ($post_values as $v) {
$v = Util::requestString($v);
if ($v !== '') {
$_POST[$key][] = $v;
}
}
}
}

View File

@@ -0,0 +1,493 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Form templates
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
/**
* PhpMyAdmin\Config\FormDisplayTemplate class
*
* @package PhpMyAdmin
*/
class FormDisplayTemplate
{
/**
* Displays top part of the form
*
* @param string $action default: $_SERVER['REQUEST_URI']
* @param string $method 'post' or 'get'
* @param array|null $hidden_fields array of form hidden fields (key: field name)
*
* @return string
*/
public static function displayFormTop($action = null, $method = 'post', $hidden_fields = null)
{
static $has_check_page_refresh = false;
if ($action === null) {
$action = $_SERVER['REQUEST_URI'];
}
if ($method != 'post') {
$method = 'get';
}
$htmlOutput = '<form method="' . $method . '" action="'
. htmlspecialchars($action) . '" class="config-form disableAjax">';
$htmlOutput .= '<input type="hidden" name="tab_hash" value="" />';
// we do validation on page refresh when browser remembers field values,
// add a field with known value which will be used for checks
if (! $has_check_page_refresh) {
$has_check_page_refresh = true;
$htmlOutput .= '<input type="hidden" name="check_page_refresh" '
. ' id="check_page_refresh" value="" />' . "\n";
}
$htmlOutput .= Url::getHiddenInputs('', '', 0, 'server') . "\n";
$htmlOutput .= Url::getHiddenFields((array)$hidden_fields);
return $htmlOutput;
}
/**
* Displays form tabs which are given by an array indexed by fieldset id
* ({@link self::displayFieldsetTop}), with values being tab titles.
*
* @param array $tabs tab names
*
* @return string
*/
public static function displayTabsTop(array $tabs)
{
$items = array();
foreach ($tabs as $tab_id => $tab_name) {
$items[] = array(
'content' => htmlspecialchars($tab_name),
'url' => array(
'href' => '#' . $tab_id,
),
);
}
$htmlOutput = Template::get('list/unordered')->render(
array(
'class' => 'tabs responsivetable',
'items' => $items,
)
);
$htmlOutput .= '<br />';
$htmlOutput .= '<div class="tabs_contents">';
return $htmlOutput;
}
/**
* Displays top part of a fieldset
*
* @param string $title title of fieldset
* @param string $description description shown on top of fieldset
* @param array|null $errors error messages to display
* @param array $attributes optional extra attributes of fieldset
*
* @return string
*/
public static function displayFieldsetTop(
$title = '',
$description = '',
$errors = null,
array $attributes = array()
) {
global $_FormDisplayGroup;
$_FormDisplayGroup = 0;
$attributes = array_merge(array('class' => 'optbox'), $attributes);
return Template::get('config/form_display/fieldset_top')->render([
'attributes' => $attributes,
'title' => $title,
'description' => $description,
'errors' => $errors,
]);
}
/**
* Displays input field
*
* $opts keys:
* o doc - (string) documentation link
* o errors - error array
* o setvalue - (string) shows button allowing to set predefined value
* o show_restore_default - (boolean) whether show "restore default" button
* o userprefs_allow - whether user preferences are enabled for this field
* (null - no support, true/false - enabled/disabled)
* o userprefs_comment - (string) field comment
* o values - key - value pairs for <select> fields
* o values_escaped - (boolean) tells whether values array is already escaped
* (defaults to false)
* o values_disabled - (array)list of disabled values (keys from values)
* o comment - (string) tooltip comment
* o comment_warning - (bool) whether this comments warns about something
*
* @param string $path config option path
* @param string $name config option name
* @param string $type type of config option
* @param mixed $value current value
* @param string $description verbose description
* @param bool $value_is_default whether value is default
* @param array|null $opts see above description
*
* @return string
*/
public static function displayInput($path, $name, $type, $value, $description = '',
$value_is_default = true, $opts = null
) {
global $_FormDisplayGroup;
static $icons; // An array of IMG tags used further below in the function
if (defined('TESTSUITE')) {
$icons = null;
}
$is_setup_script = $GLOBALS['PMA_Config']->get('is_setup');
if ($icons === null) { // if the static variables have not been initialised
$icons = array();
// Icon definitions:
// The same indexes will be used in the $icons array.
// The first element contains the filename and the second
// element is used for the "alt" and "title" attributes.
$icon_init = array(
'edit' => array('b_edit', ''),
'help' => array('b_help', __('Documentation')),
'reload' => array('s_reload', ''),
'tblops' => array('b_tblops', '')
);
if ($is_setup_script) {
// When called from the setup script, we don't have access to the
// sprite-aware getImage() function because the PMA_theme class
// has not been loaded, so we generate the img tags manually.
foreach ($icon_init as $k => $v) {
$title = '';
if (! empty($v[1])) {
$title = ' title="' . $v[1] . '"';
}
$icons[$k] = sprintf(
'<img alt="%s" src="%s"%s />',
$v[1],
"../themes/pmahomme/img/{$v[0]}.png",
$title
);
}
} else {
// In this case we just use getImage() because it's available
foreach ($icon_init as $k => $v) {
$icons[$k] = Util::getImage(
$v[0], $v[1]
);
}
}
}
$has_errors = isset($opts['errors']) && !empty($opts['errors']);
$option_is_disabled = ! $is_setup_script && isset($opts['userprefs_allow'])
&& ! $opts['userprefs_allow'];
$name_id = 'name="' . htmlspecialchars($path) . '" id="'
. htmlspecialchars($path) . '"';
$field_class = $type == 'checkbox' ? 'checkbox' : '';
if (! $value_is_default) {
$field_class .= ($field_class == '' ? '' : ' ')
. ($has_errors ? 'custom field-error' : 'custom');
}
$field_class = $field_class ? ' class="' . $field_class . '"' : '';
$tr_class = $_FormDisplayGroup > 0
? 'group-field group-field-' . $_FormDisplayGroup
: '';
if (isset($opts['setvalue']) && $opts['setvalue'] == ':group') {
unset($opts['setvalue']);
$_FormDisplayGroup++;
$tr_class = 'group-header-field group-header-' . $_FormDisplayGroup;
}
if ($option_is_disabled) {
$tr_class .= ($tr_class ? ' ' : '') . 'disabled-field';
}
$tr_class = $tr_class ? ' class="' . $tr_class . '"' : '';
$htmlOutput = '<tr' . $tr_class . '>';
$htmlOutput .= '<th>';
$htmlOutput .= '<label for="' . htmlspecialchars($path) . '">' . htmlspecialchars_decode($name)
. '</label>';
if (! empty($opts['doc'])) {
$htmlOutput .= '<span class="doc">';
$htmlOutput .= '<a href="' . $opts['doc']
. '" target="documentation">' . $icons['help'] . '</a>';
$htmlOutput .= "\n";
$htmlOutput .= '</span>';
}
if ($option_is_disabled) {
$htmlOutput .= '<span class="disabled-notice" title="';
$htmlOutput .= __(
'This setting is disabled, it will not be applied to your configuration.'
);
$htmlOutput .= '">' . __('Disabled') . "</span>";
}
if (!empty($description)) {
$htmlOutput .= '<small>' . $description . '</small>';
}
$htmlOutput .= '</th>';
$htmlOutput .= '<td>';
switch ($type) {
case 'text':
$htmlOutput .= '<input type="text" class="all85" ' . $name_id . $field_class
. ' value="' . htmlspecialchars($value) . '" />';
break;
case 'password':
$htmlOutput .= '<input type="password" class="all85" ' . $name_id . $field_class
. ' value="' . htmlspecialchars($value) . '" />';
break;
case 'short_text':
// As seen in the reporting server (#15042) we sometimes receive
// an array here. No clue about its origin nor content, so let's avoid
// a notice on htmlspecialchars().
if (! is_array($value)) {
$htmlOutput .= '<input type="text" size="25" ' . $name_id
. $field_class . ' value="' . htmlspecialchars($value)
. '" />';
}
break;
case 'number_text':
$htmlOutput .= '<input type="number" ' . $name_id . $field_class
. ' value="' . htmlspecialchars($value) . '" />';
break;
case 'checkbox':
$htmlOutput .= '<span' . $field_class . '><input type="checkbox" ' . $name_id
. ($value ? ' checked="checked"' : '') . ' /></span>';
break;
case 'select':
$htmlOutput .= '<select class="all85" ' . $name_id . $field_class . '>';
$escape = !(isset($opts['values_escaped']) && $opts['values_escaped']);
$values_disabled = isset($opts['values_disabled'])
? array_flip($opts['values_disabled']) : array();
foreach ($opts['values'] as $opt_value_key => $opt_value) {
// set names for boolean values
if (is_bool($opt_value)) {
$opt_value = mb_strtolower(
$opt_value ? __('Yes') : __('No')
);
}
// escape if necessary
if ($escape) {
$display = htmlspecialchars($opt_value);
$display_value = htmlspecialchars($opt_value_key);
} else {
$display = $opt_value;
$display_value = $opt_value_key;
}
// compare with selected value
// boolean values are cast to integers when used as array keys
$selected = is_bool($value)
? (int) $value === $opt_value_key
: $opt_value_key === $value;
$htmlOutput .= '<option value="' . $display_value . '"';
if ($selected) {
$htmlOutput .= ' selected="selected"';
}
if (isset($values_disabled[$opt_value_key])) {
$htmlOutput .= ' disabled="disabled"';
}
$htmlOutput .= '>' . $display . '</option>';
}
$htmlOutput .= '</select>';
break;
case 'list':
$htmlOutput .= '<textarea cols="35" rows="5" ' . $name_id . $field_class
. '>' . htmlspecialchars(implode("\n", $value)) . '</textarea>';
break;
}
if (isset($opts['comment']) && $opts['comment']) {
$class = 'field-comment-mark';
if (isset($opts['comment_warning']) && $opts['comment_warning']) {
$class .= ' field-comment-warning';
}
$htmlOutput .= '<span class="' . $class . '" title="'
. htmlspecialchars($opts['comment']) . '">i</span>';
}
if ($is_setup_script
&& isset($opts['userprefs_comment'])
&& $opts['userprefs_comment']
) {
$htmlOutput .= '<a class="userprefs-comment" title="'
. htmlspecialchars($opts['userprefs_comment']) . '">'
. $icons['tblops'] . '</a>';
}
if (isset($opts['setvalue']) && $opts['setvalue']) {
$htmlOutput .= '<a class="set-value hide" href="#'
. htmlspecialchars("$path={$opts['setvalue']}") . '" title="'
. sprintf(__('Set value: %s'), htmlspecialchars($opts['setvalue']))
. '">' . $icons['edit'] . '</a>';
}
if (isset($opts['show_restore_default']) && $opts['show_restore_default']) {
$htmlOutput .= '<a class="restore-default hide" href="#' . $path . '" title="'
. __('Restore default value') . '">' . $icons['reload'] . '</a>';
}
// this must match with displayErrors() in scripts/config.js
if ($has_errors) {
$htmlOutput .= "\n <dl class=\"inline_errors\">";
foreach ($opts['errors'] as $error) {
$htmlOutput .= '<dd>' . htmlspecialchars($error) . '</dd>';
}
$htmlOutput .= '</dl>';
}
$htmlOutput .= '</td>';
if ($is_setup_script && isset($opts['userprefs_allow'])) {
$htmlOutput .= '<td class="userprefs-allow" title="' .
__('Allow users to customize this value') . '">';
$htmlOutput .= '<input type="checkbox" name="' . $path
. '-userprefs-allow" ';
if ($opts['userprefs_allow']) {
$htmlOutput .= 'checked="checked"';
};
$htmlOutput .= '/>';
$htmlOutput .= '</td>';
} elseif ($is_setup_script) {
$htmlOutput .= '<td>&nbsp;</td>';
}
$htmlOutput .= '</tr>';
return $htmlOutput;
}
/**
* Display group header
*
* @param string $headerText Text of header
*
* @return string|void
*/
public static function displayGroupHeader($headerText)
{
global $_FormDisplayGroup;
$_FormDisplayGroup++;
if (! $headerText) {
return null;
}
$colspan = $GLOBALS['PMA_Config']->get('is_setup') ? 3 : 2;
return Template::get('config/form_display/group_header')->render([
'group' => $_FormDisplayGroup,
'colspan' => $colspan,
'header_text' => $headerText,
]);
}
/**
* Display group footer
*
* @return void
*/
public static function displayGroupFooter()
{
global $_FormDisplayGroup;
$_FormDisplayGroup--;
}
/**
* Displays bottom part of a fieldset
*
* @param bool $showButtons Whether show submit and reset button
*
* @return string
*/
public static function displayFieldsetBottom($showButtons = true)
{
return Template::get('config/form_display/fieldset_bottom')->render([
'show_buttons' => $showButtons,
'is_setup' => $GLOBALS['PMA_Config']->get('is_setup'),
]);
}
/**
* Closes form tabs
*
* @return string
*/
public static function displayTabsBottom()
{
return Template::get('config/form_display/tabs_bottom')->render();
}
/**
* Displays bottom part of the form
*
* @return string
*/
public static function displayFormBottom()
{
return Template::get('config/form_display/form_bottom')->render();
}
/**
* Appends JS validation code to $js_array
*
* @param string $field_id ID of field to validate
* @param string|array $validators validators callback
* @param array &$js_array will be updated with javascript code
*
* @return void
*/
public static function addJsValidate($field_id, $validators, array &$js_array)
{
foreach ((array)$validators as $validator) {
$validator = (array)$validator;
$v_name = array_shift($validator);
$v_name = "PMA_" . $v_name;
$v_args = array();
foreach ($validator as $arg) {
$v_args[] = Sanitize::escapeJsString($arg);
}
$v_args = $v_args ? ", ['" . implode("', '", $v_args) . "']" : '';
$js_array[] = "validateField('$field_id', '$v_name', true$v_args)";
}
}
/**
* Displays JavaScript code
*
* @param array $js_array lines of javascript code
*
* @return string
*/
public static function displayJavascript(array $js_array)
{
if (empty($js_array)) {
return null;
}
return Template::get('javascript/display')->render(
array('js_array' => $js_array,)
);
}
/**
* Displays error list
*
* @param string $name Name of item with errors
* @param array $errorList List of errors to show
*
* @return string HTML for errors
*/
public static function displayErrors($name, array $errorList)
{
return Template::get('config/form_display/errors')->render([
'name' => $name,
'error_list' => $errorList,
]);
}
}

View File

@@ -0,0 +1,85 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Base class for preferences.
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\FormDisplay;
/**
* Base form for user preferences
*/
abstract class BaseForm extends FormDisplay
{
/**
* Constructor
*
* @param ConfigFile $cf Config file instance
* @param int|null $server_id 0 if new server, validation; >= 1 if editing a server
*/
public function __construct(ConfigFile $cf, $server_id = null)
{
parent::__construct($cf);
foreach (static::getForms() as $form_name => $form) {
$this->registerForm($form_name, $form, $server_id);
}
}
/**
* List of available forms, each form is described as an array of fields to display.
* Fields MUST have their counterparts in the $cfg array.
*
* To define form field, use the notation below:
* $forms['Form group']['Form name'] = array('Option/path');
*
* You can assign default values set by special button ("set value: ..."), eg.:
* 'Servers/1/pmadb' => 'phpmyadmin'
*
* To group options, use:
* ':group:' . __('group name') // just define a group
* or
* 'option' => ':group' // group starting from this option
* End group blocks with:
* ':group:end'
*
* @todo This should be abstract, but that does not work in PHP 5
*
* @return array
*/
public static function getForms()
{
return array();
}
/**
* Returns list of fields used in the form.
*
* @return string[]
*/
public static function getFields()
{
$names = [];
foreach (static::getForms() as $form) {
foreach ($form as $k => $v) {
$names[] = is_int($k) ? $v : $k;
}
}
return $names;
}
/**
* Returns name of the form
*
* @todo This should be abstract, but that does not work in PHP 5
*
* @return string
*/
public static function getName()
{
return '';
}
}

View File

@@ -0,0 +1,127 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms;
use PhpMyAdmin\Config\ConfigFile;
class BaseFormList
{
/**
* List of all forms
*/
protected static $all = array();
protected static $ns = 'PhpMyAdmin\\Config\\Forms\\';
private $_forms;
public static function getAll()
{
return static::$all;
}
public static function isValid($name)
{
return in_array($name, static::$all);
}
public static function get($name)
{
if (static::isValid($name)) {
return static::$ns . $name . 'Form';
}
return null;
}
/**
* Constructor
*
* @param ConfigFile $cf Config file instance
*/
public function __construct(ConfigFile $cf)
{
$this->_forms = array();
foreach (static::$all as $form) {
$class = static::get($form);
$this->_forms[] = new $class($cf);
}
}
/**
* Processes forms, returns true on successful save
*
* @param bool $allow_partial_save allows for partial form saving
* on failed validation
* @param bool $check_form_submit whether check for $_POST['submit_save']
*
* @return boolean whether processing was successful
*/
public function process($allow_partial_save = true, $check_form_submit = true)
{
$ret = true;
foreach ($this->_forms as $form) {
$ret = $ret && $form->process($allow_partial_save, $check_form_submit);
}
return $ret;
}
/**
* Displays errors
*
* @return string HTML for errors
*/
public function displayErrors()
{
$ret = '';
foreach ($this->_forms as $form) {
$ret .= $form->displayErrors();
}
return $ret;
}
/**
* Reverts erroneous fields to their default values
*
* @return void
*/
public function fixErrors()
{
foreach ($this->_forms as $form) {
$form->fixErrors();
}
}
/**
* Tells whether form validation failed
*
* @return boolean
*/
public function hasErrors()
{
$ret = false;
foreach ($this->_forms as $form) {
$ret = $ret || $form->hasErrors();
}
return $ret;
}
/**
* Returns list of fields used in the form.
*
* @return string[]
*/
public static function getFields()
{
$names = [];
foreach (static::$all as $form) {
$class = static::get($form);
$names = array_merge($names, $class::getFields());
}
return $names;
}
}

View File

@@ -0,0 +1,21 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Page;
use PhpMyAdmin\Config\Forms\BaseForm;
use PhpMyAdmin\Config\Forms\User\MainForm;
class BrowseForm extends BaseForm
{
public static function getForms()
{
return [
'Browse' => MainForm::getForms()['Browse']
];
}
}

View File

@@ -0,0 +1,22 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Page;
use PhpMyAdmin\Config\Forms\BaseForm;
use PhpMyAdmin\Config\Forms\User\MainForm;
class DbStructureForm extends BaseForm
{
public static function getForms()
{
return [
'DbStructure' => MainForm::getForms()['DbStructure']
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Page;
use PhpMyAdmin\Config\Forms\BaseForm;
use PhpMyAdmin\Config\Forms\User\MainForm;
use PhpMyAdmin\Config\Forms\User\FeaturesForm;
class EditForm extends BaseForm
{
public static function getForms()
{
return [
'Edit' => MainForm::getForms()['Edit'],
'Text_fields' => FeaturesForm::getForms()['Text_fields'],
];
}
}

View File

@@ -0,0 +1,12 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Page;
class ExportForm extends \PhpMyAdmin\Config\Forms\User\ExportForm
{
}

View File

@@ -0,0 +1,12 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Page;
class ImportForm extends \PhpMyAdmin\Config\Forms\User\ImportForm
{
}

View File

@@ -0,0 +1,12 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Page;
class NaviForm extends \PhpMyAdmin\Config\Forms\User\NaviForm
{
}

View File

@@ -0,0 +1,25 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Page preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Page;
use PhpMyAdmin\Config\Forms\BaseFormList;
class PageFormList extends BaseFormList
{
protected static $all = array(
'Browse',
'DbStructure',
'Edit',
'Export',
'Import',
'Navi',
'Sql',
'TableStructure',
);
protected static $ns = '\\PhpMyAdmin\\Config\\Forms\\Page\\';
}

View File

@@ -0,0 +1,12 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Page;
class SqlForm extends \PhpMyAdmin\Config\Forms\User\SqlForm
{
}

View File

@@ -0,0 +1,22 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Page;
use PhpMyAdmin\Config\Forms\BaseForm;
use PhpMyAdmin\Config\Forms\User\MainForm;
class TableStructureForm extends BaseForm
{
public static function getForms()
{
return [
'TableStructure' => MainForm::getForms()['TableStructure']
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Setup;
use PhpMyAdmin\Config\Forms\BaseForm;
class ConfigForm extends BaseForm
{
public static function getForms()
{
return array(
'Config' => array(
'DefaultLang',
'ServerDefault'
),
);
}
}

View File

@@ -0,0 +1,12 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Setup;
class ExportForm extends \PhpMyAdmin\Config\Forms\User\ExportForm
{
}

View File

@@ -0,0 +1,63 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Setup;
class FeaturesForm extends \PhpMyAdmin\Config\Forms\User\FeaturesForm
{
public static function getForms()
{
$result = parent::getForms();
/* Remove only_db/hide_db, we have proper Server form in setup */
$result['Databases'] = array_diff(
$result['Databases'],
['Servers/1/only_db', 'Servers/1/hide_db']
);
/* Following are not available to user */
$result['Import_export'] = array(
'UploadDir',
'SaveDir',
'RecodingEngine' => ':group',
'IconvExtraParams',
':group:end',
'ZipDump',
'GZipDump',
'BZipDump',
'CompressOnFly'
);
$result['Security'] = array(
'blowfish_secret',
'CheckConfigurationPermissions',
'TrustedProxies',
'AllowUserDropDatabase',
'AllowArbitraryServer',
'ArbitraryServerRegexp',
'LoginCookieRecall',
'LoginCookieStore',
'LoginCookieDeleteAll',
'CaptchaLoginPublicKey',
'CaptchaLoginPrivateKey'
);
$result['Developer'] = array(
'UserprefsDeveloperTab',
'DBG/sql',
);
$result['Other_core_settings'] = array(
'OBGzip',
'PersistentConnections',
'ExecTimeLimit',
'MemoryLimit',
'UseDbSearch',
'ProxyUrl',
'ProxyUser',
'ProxyPass',
'AllowThirdPartyFraming',
'ZeroConf',
);
return $result;
}
}

View File

@@ -0,0 +1,12 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Setup;
class ImportForm extends \PhpMyAdmin\Config\Forms\User\ImportForm
{
}

View File

@@ -0,0 +1,20 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Setup;
class MainForm extends \PhpMyAdmin\Config\Forms\User\MainForm
{
public static function getForms()
{
$result = parent::getForms();
/* Following are not available to user */
$result['Startup'][] = 'ShowPhpInfo';
$result['Startup'][] = 'ShowChgPassword';
return $result;
}
}

View File

@@ -0,0 +1,12 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Setup;
class NaviForm extends \PhpMyAdmin\Config\Forms\User\NaviForm
{
}

View File

@@ -0,0 +1,81 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Setup;
use PhpMyAdmin\Config\Forms\BaseForm;
class ServersForm extends BaseForm
{
public static function getForms()
{
return array(
'Server' => array('Servers' => array(1 => array(
'verbose',
'host',
'port',
'socket',
'ssl',
'compress'))),
'Server_auth' => array('Servers' => array(1 => array(
'auth_type',
':group:' . __('Config authentication'),
'user',
'password',
':group:end',
':group:' . __('HTTP authentication'),
'auth_http_realm',
':group:end',
':group:' . __('Signon authentication'),
'SignonSession',
'SignonURL',
'LogoutURL'))),
'Server_config' => array('Servers' => array(1 => array(
'only_db',
'hide_db',
'AllowRoot',
'AllowNoPassword',
'DisableIS',
'AllowDeny/order',
'AllowDeny/rules',
'SessionTimeZone'))),
'Server_pmadb' => array('Servers' => array(1 => array(
'pmadb' => 'phpmyadmin',
'controlhost',
'controlport',
'controluser',
'controlpass',
'bookmarktable' => 'pma__bookmark',
'relation' => 'pma__relation',
'userconfig' => 'pma__userconfig',
'users' => 'pma__users',
'usergroups' => 'pma__usergroups',
'navigationhiding' => 'pma__navigationhiding',
'table_info' => 'pma__table_info',
'column_info' => 'pma__column_info',
'history' => 'pma__history',
'recent' => 'pma__recent',
'favorite' => 'pma__favorite',
'table_uiprefs' => 'pma__table_uiprefs',
'tracking' => 'pma__tracking',
'table_coords' => 'pma__table_coords',
'pdf_pages' => 'pma__pdf_pages',
'savedsearches' => 'pma__savedsearches',
'central_columns' => 'pma__central_columns',
'designer_settings' => 'pma__designer_settings',
'export_templates' => 'pma__export_templates',
'MaxTableUiprefs' => 100))),
'Server_tracking' => array('Servers' => array(1 => array(
'tracking_version_auto_create',
'tracking_default_statements',
'tracking_add_drop_view',
'tracking_add_drop_table',
'tracking_add_drop_database',
))),
);
}
}

View File

@@ -0,0 +1,25 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Setup preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Setup;
use PhpMyAdmin\Config\Forms\BaseFormList;
class SetupFormList extends BaseFormList
{
protected static $all = array(
'Config',
'Export',
'Features',
'Import',
'Main',
'Navi',
'Servers',
'Sql',
);
protected static $ns = '\\PhpMyAdmin\\Config\\Forms\\Setup\\';
}

View File

@@ -0,0 +1,19 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\Setup;
class SqlForm extends \PhpMyAdmin\Config\Forms\User\SqlForm
{
public static function getForms()
{
$result = parent::getForms();
/* Following are not available to user */
$result['Sql_queries'][] = 'QueryHistoryDB';
return $result;
}
}

View File

@@ -0,0 +1,142 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\User;
use PhpMyAdmin\Config\Forms\BaseForm;
class ExportForm extends BaseForm
{
public static function getForms()
{
return array(
'Export_defaults' => array(
'Export/method',
':group:' . __('Quick'),
'Export/quick_export_onserver',
'Export/quick_export_onserver_overwrite',
':group:end',
':group:' . __('Custom'),
'Export/format',
'Export/compression',
'Export/charset',
'Export/lock_tables',
'Export/as_separate_files',
'Export/asfile' => ':group',
'Export/onserver',
'Export/onserver_overwrite',
':group:end',
'Export/file_template_table',
'Export/file_template_database',
'Export/file_template_server'
),
'Sql' => array(
'Export/sql_include_comments' => ':group',
'Export/sql_dates',
'Export/sql_relation',
'Export/sql_mime',
':group:end',
'Export/sql_use_transaction',
'Export/sql_disable_fk',
'Export/sql_views_as_tables',
'Export/sql_metadata',
'Export/sql_compatibility',
'Export/sql_structure_or_data',
':group:' . __('Structure'),
'Export/sql_drop_database',
'Export/sql_create_database',
'Export/sql_drop_table',
'Export/sql_create_table' => ':group',
'Export/sql_if_not_exists',
'Export/sql_auto_increment',
':group:end',
'Export/sql_create_view',
'Export/sql_procedure_function',
'Export/sql_create_trigger',
'Export/sql_backquotes',
':group:end',
':group:' . __('Data'),
'Export/sql_delayed',
'Export/sql_ignore',
'Export/sql_type',
'Export/sql_insert_syntax',
'Export/sql_max_query_size',
'Export/sql_hex_for_binary',
'Export/sql_utc_time'
),
'CodeGen' => array(
'Export/codegen_format'
),
'Csv' => array(
':group:' . __('CSV'),
'Export/csv_separator',
'Export/csv_enclosed',
'Export/csv_escaped',
'Export/csv_terminated',
'Export/csv_null',
'Export/csv_removeCRLF',
'Export/csv_columns',
':group:end',
':group:' . __('CSV for MS Excel'),
'Export/excel_null',
'Export/excel_removeCRLF',
'Export/excel_columns',
'Export/excel_edition'
),
'Latex' => array(
'Export/latex_caption',
'Export/latex_structure_or_data',
':group:' . __('Structure'),
'Export/latex_structure_caption',
'Export/latex_structure_continued_caption',
'Export/latex_structure_label',
'Export/latex_relation',
'Export/latex_comments',
'Export/latex_mime',
':group:end',
':group:' . __('Data'),
'Export/latex_columns',
'Export/latex_data_caption',
'Export/latex_data_continued_caption',
'Export/latex_data_label',
'Export/latex_null'
),
'Microsoft_Office' => array(
':group:' . __('Microsoft Word 2000'),
'Export/htmlword_structure_or_data',
'Export/htmlword_null',
'Export/htmlword_columns'),
'Open_Document' => array(
':group:' . __('OpenDocument Spreadsheet'),
'Export/ods_columns',
'Export/ods_null',
':group:end',
':group:' . __('OpenDocument Text'),
'Export/odt_structure_or_data',
':group:' . __('Structure'),
'Export/odt_relation',
'Export/odt_comments',
'Export/odt_mime',
':group:end',
':group:' . __('Data'),
'Export/odt_columns',
'Export/odt_null'
),
'Texy' => array(
'Export/texytext_structure_or_data',
':group:' . __('Data'),
'Export/texytext_null',
'Export/texytext_columns'
),
);
}
public static function getName()
{
return __('Export');
}
}

View File

@@ -0,0 +1,84 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\User;
use PhpMyAdmin\Config\Forms\BaseForm;
class FeaturesForm extends BaseForm
{
public static function getForms()
{
$result = array(
'General' => array(
'VersionCheck',
'NaturalOrder',
'InitialSlidersState',
'SkipLockedTables',
'DisableMultiTableMaintenance',
'ShowHint',
'SendErrorReports',
'ConsoleEnterExecutes',
'DisableShortcutKeys',
'FontSize',
),
'Databases' => array(
'Servers/1/only_db', // saves to Server/only_db
'Servers/1/hide_db', // saves to Server/hide_db
'MaxDbList',
'MaxTableList',
'DefaultConnectionCollation',
),
'Text_fields' => array(
'CharEditing',
'MinSizeForInputField',
'MaxSizeForInputField',
'CharTextareaCols',
'CharTextareaRows',
'TextareaCols',
'TextareaRows',
'LongtextDoubleTextarea'
),
'Page_titles' => array(
'TitleDefault',
'TitleTable',
'TitleDatabase',
'TitleServer'
),
'Warnings' => array(
'PmaNoRelation_DisableWarning',
'SuhosinDisableWarning',
'LoginCookieValidityDisableWarning',
'ReservedWordDisableWarning'
),
'Console' => array(
'Console/Mode',
'Console/StartHistory',
'Console/AlwaysExpand',
'Console/CurrentQuery',
'Console/EnterExecutes',
'Console/DarkTheme',
'Console/Height',
'Console/GroupQueries',
'Console/OrderBy',
'Console/Order',
),
);
// skip Developer form if no setting is available
if ($GLOBALS['cfg']['UserprefsDeveloperTab']) {
$result['Developer'] = array(
'DBG/sql'
);
}
return $result;
}
public static function getName()
{
return __('Features');
}
}

View File

@@ -0,0 +1,60 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\User;
use PhpMyAdmin\Config\Forms\BaseForm;
class ImportForm extends BaseForm
{
public static function getForms()
{
return array(
'Import_defaults' => array(
'Import/format',
'Import/charset',
'Import/allow_interrupt',
'Import/skip_queries'
),
'Sql' => array(
'Import/sql_compatibility',
'Import/sql_no_auto_value_on_zero',
'Import/sql_read_as_multibytes'
),
'Csv' => array(
':group:' . __('CSV'),
'Import/csv_replace',
'Import/csv_ignore',
'Import/csv_terminated',
'Import/csv_enclosed',
'Import/csv_escaped',
'Import/csv_col_names',
':group:end',
':group:' . __('CSV using LOAD DATA'),
'Import/ldi_replace',
'Import/ldi_ignore',
'Import/ldi_terminated',
'Import/ldi_enclosed',
'Import/ldi_escaped',
'Import/ldi_local_option'
),
'Open_Document' => array(
':group:' . __('OpenDocument Spreadsheet'),
'Import/ods_col_names',
'Import/ods_empty_rows',
'Import/ods_recognize_percentages',
'Import/ods_recognize_currency'
),
);
}
public static function getName()
{
return __('Import');
}
}

View File

@@ -0,0 +1,86 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\User;
use PhpMyAdmin\Config\Forms\BaseForm;
class MainForm extends BaseForm
{
public static function getForms()
{
return array(
'Startup' => array(
'ShowCreateDb',
'ShowStats',
'ShowServerInfo'
),
'DbStructure' => array(
'ShowDbStructureCharset',
'ShowDbStructureComment',
'ShowDbStructureCreation',
'ShowDbStructureLastUpdate',
'ShowDbStructureLastCheck'
),
'TableStructure' => array(
'HideStructureActions',
'ShowColumnComments',
':group:' . __('Default transformations'),
'DefaultTransformations/Hex',
'DefaultTransformations/Substring',
'DefaultTransformations/Bool2Text',
'DefaultTransformations/External',
'DefaultTransformations/PreApPend',
'DefaultTransformations/DateFormat',
'DefaultTransformations/Inline',
'DefaultTransformations/TextImageLink',
'DefaultTransformations/TextLink',
':group:end'
),
'Browse' => array(
'TableNavigationLinksMode',
'ActionLinksMode',
'ShowAll',
'MaxRows',
'Order',
'BrowsePointerEnable',
'BrowseMarkerEnable',
'GridEditing',
'SaveCellsAtOnce',
'RepeatCells',
'LimitChars',
'RowActionLinks',
'RowActionLinksWithoutUnique',
'TablePrimaryKeyOrder',
'RememberSorting',
'RelationalDisplay'
),
'Edit' => array(
'ProtectBinary',
'ShowFunctionFields',
'ShowFieldTypesInDataEditView',
'InsertRows',
'ForeignKeyDropdownOrder',
'ForeignKeyMaxLimit'
),
'Tabs' => array(
'TabsMode',
'DefaultTabServer',
'DefaultTabDatabase',
'DefaultTabTable'
),
'DisplayRelationalSchema' => array(
'PDFDefaultPageSize'
),
);
}
public static function getName()
{
return __('Main panel');
}
}

View File

@@ -0,0 +1,61 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\User;
use PhpMyAdmin\Config\Forms\BaseForm;
class NaviForm extends BaseForm
{
public static function getForms()
{
return array(
'Navi_panel' => array(
'ShowDatabasesNavigationAsTree',
'NavigationLinkWithMainPanel',
'NavigationDisplayLogo',
'NavigationLogoLink',
'NavigationLogoLinkWindow',
'NavigationTreePointerEnable',
'FirstLevelNavigationItems',
'NavigationTreeDisplayItemFilterMinimum',
'NumRecentTables',
'NumFavoriteTables',
'NavigationWidth',
),
'Navi_tree' => array(
'MaxNavigationItems',
'NavigationTreeEnableGrouping',
'NavigationTreeEnableExpansion',
'NavigationTreeShowTables',
'NavigationTreeShowViews',
'NavigationTreeShowFunctions',
'NavigationTreeShowProcedures',
'NavigationTreeShowEvents'
),
'Navi_servers' => array(
'NavigationDisplayServers',
'DisplayServersList',
),
'Navi_databases' => array(
'NavigationTreeDisplayDbFilterMinimum',
'NavigationTreeDbSeparator'
),
'Navi_tables' => array(
'NavigationTreeDefaultTabTable',
'NavigationTreeDefaultTabTable2',
'NavigationTreeTableSeparator',
'NavigationTreeTableLevel',
),
);
}
public static function getName()
{
return __('Navigation panel');
}
}

View File

@@ -0,0 +1,42 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\User;
use PhpMyAdmin\Config\Forms\BaseForm;
class SqlForm extends BaseForm
{
public static function getForms()
{
return array(
'Sql_queries' => array(
'ShowSQL',
'Confirm',
'QueryHistoryMax',
'IgnoreMultiSubmitErrors',
'MaxCharactersInDisplayedSQL',
'RetainQueryBox',
'CodemirrorEnable',
'LintEnable',
'EnableAutocompleteForTablesAndColumns',
'DefaultForeignKeyChecks',
),
'Sql_box' => array(
'SQLQuery/Edit',
'SQLQuery/Explain',
'SQLQuery/ShowAsPHP',
'SQLQuery/Refresh',
),
);
}
public static function getName()
{
return __('SQL queries');
}
}

View File

@@ -0,0 +1,23 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* User preferences form
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config\Forms\User;
use PhpMyAdmin\Config\Forms\BaseFormList;
class UserFormList extends BaseFormList
{
protected static $all = array(
'Features',
'Sql',
'Navi',
'Main',
'Import',
'Export',
);
protected static $ns = '\\PhpMyAdmin\\Config\\Forms\\User\\';
}

View File

@@ -0,0 +1,231 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Page-related settings
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\FormDisplay;
use PhpMyAdmin\Config\Forms\Page\PageFormList;
use PhpMyAdmin\Core;
use PhpMyAdmin\Message;
use PhpMyAdmin\Response;
use PhpMyAdmin\UserPreferences;
/**
* Page-related settings
*
* @package PhpMyAdmin
*/
class PageSettings
{
/**
* Contains id of the form element
* @var string
*/
private $_elemId = 'page_settings_modal';
/**
* Name of the group to show
* @var string
*/
private $_groupName = '';
/**
* Contains HTML of errors
* @var string
*/
private $_errorHTML = '';
/**
* Contains HTML of settings
* @var string
*/
private $_HTML = '';
/**
* @var UserPreferences
*/
private $userPreferences;
/**
* Constructor
*
* @param string $formGroupName The name of config form group to display
* @param string $elemId Id of the div containing settings
*/
public function __construct($formGroupName, $elemId = null)
{
$this->userPreferences = new UserPreferences();
$form_class = PageFormList::get($formGroupName);
if (is_null($form_class)) {
return;
}
if (isset($_REQUEST['printview']) && $_REQUEST['printview'] == '1') {
return;
}
if (!empty($elemId)) {
$this->_elemId = $elemId;
}
$this->_groupName = $formGroupName;
$cf = new ConfigFile($GLOBALS['PMA_Config']->base_settings);
$this->userPreferences->pageInit($cf);
$form_display = new $form_class($cf);
// Process form
$error = null;
if (isset($_POST['submit_save'])
&& $_POST['submit_save'] == $formGroupName
) {
$this->_processPageSettings($form_display, $cf, $error);
}
// Display forms
$this->_HTML = $this->_getPageSettingsDisplay($form_display, $error);
}
/**
* Process response to form
*
* @param FormDisplay &$form_display Form
* @param ConfigFile &$cf Configuration file
* @param Message|null &$error Error message
*
* @return void
*/
private function _processPageSettings(&$form_display, &$cf, &$error)
{
if ($form_display->process(false) && !$form_display->hasErrors()) {
// save settings
$result = $this->userPreferences->save($cf->getConfigArray());
if ($result === true) {
// reload page
$response = Response::getInstance();
Core::sendHeaderLocation(
$response->getFooter()->getSelfUrl('unencoded')
);
exit();
} else {
$error = $result;
}
}
}
/**
* Store errors in _errorHTML
*
* @param FormDisplay &$form_display Form
* @param Message|null &$error Error message
*
* @return void
*/
private function _storeError(&$form_display, &$error)
{
$retval = '';
if ($error) {
$retval .= $error->getDisplay();
}
if ($form_display->hasErrors()) {
// form has errors
$retval .= '<div class="error config-form">'
. '<b>' . __(
'Cannot save settings, submitted configuration form contains '
. 'errors!'
) . '</b>'
. $form_display->displayErrors()
. '</div>';
}
$this->_errorHTML = $retval;
}
/**
* Display page-related settings
*
* @param FormDisplay &$form_display Form
* @param Message &$error Error message
*
* @return string
*/
private function _getPageSettingsDisplay(&$form_display, &$error)
{
$response = Response::getInstance();
$retval = '';
$this->_storeError($form_display, $error);
$retval .= '<div id="' . $this->_elemId . '">';
$retval .= '<div class="page_settings">';
$retval .= $form_display->getDisplay(
true,
true,
false,
$response->getFooter()->getSelfUrl(),
array(
'submit_save' => $this->_groupName
)
);
$retval .= '</div>';
$retval .= '</div>';
return $retval;
}
/**
* Get HTML output
*
* @return string
*/
public function getHTML()
{
return $this->_HTML;
}
/**
* Get error HTML output
*
* @return string
*/
public function getErrorHTML()
{
return $this->_errorHTML;
}
/**
* Group to show for Page-related settings
* @param string $formGroupName The name of config form group to display
* @return PageSettings
*/
public static function showGroup($formGroupName)
{
$object = new PageSettings($formGroupName);
$response = Response::getInstance();
$response->addHTML($object->getErrorHTML());
$response->addHTML($object->getHTML());
return $object;
}
/**
* Get HTML for navigation settings
* @return string
*/
public static function getNaviSettings()
{
$object = new PageSettings('Navi', 'pma_navigation_settings');
$response = Response::getInstance();
$response->addHTML($object->getErrorHTML());
return $object->getHTML();
}
}

View File

@@ -0,0 +1,560 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Server config checks management
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\Descriptions;
use PhpMyAdmin\Core;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\Setup\Index as SetupIndex;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
/**
* Performs various compatibility, security and consistency checks on current config
*
* Outputs results to message list, must be called between SetupIndex::messagesBegin()
* and SetupIndex::messagesEnd()
*
* @package PhpMyAdmin
*/
class ServerConfigChecks
{
/**
* @var ConfigFile configurations being checked
*/
protected $cfg;
/**
* Constructor.
*
* @param ConfigFile $cfg Configuration
*/
public function __construct(ConfigFile $cfg)
{
$this->cfg = $cfg;
}
/**
* Perform config checks
*
* @return void
*/
public function performConfigChecks()
{
$blowfishSecret = $this->cfg->get('blowfish_secret');
$blowfishSecretSet = false;
$cookieAuthUsed = false;
list($cookieAuthUsed, $blowfishSecret, $blowfishSecretSet)
= $this->performConfigChecksServers(
$cookieAuthUsed, $blowfishSecret, $blowfishSecretSet
);
$this->performConfigChecksCookieAuthUsed(
$cookieAuthUsed, $blowfishSecretSet,
$blowfishSecret
);
//
// $cfg['AllowArbitraryServer']
// should be disabled
//
if ($this->cfg->getValue('AllowArbitraryServer')) {
$sAllowArbitraryServerWarn = sprintf(
__(
'This %soption%s should be disabled as it allows attackers to '
. 'bruteforce login to any MySQL server. If you feel this is necessary, '
. 'use %srestrict login to MySQL server%s or %strusted proxies list%s. '
. 'However, IP-based protection with trusted proxies list may not be '
. 'reliable if your IP belongs to an ISP where thousands of users, '
. 'including you, are connected to.'
),
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Security]',
'[/a]',
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Security]',
'[/a]',
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Security]',
'[/a]'
);
SetupIndex::messagesSet(
'notice',
'AllowArbitraryServer',
Descriptions::get('AllowArbitraryServer'),
Sanitize::sanitize($sAllowArbitraryServerWarn)
);
}
$this->performConfigChecksLoginCookie();
$sDirectoryNotice = __(
'This value should be double checked to ensure that this directory is '
. 'neither world accessible nor readable or writable by other users on '
. 'your server.'
);
//
// $cfg['SaveDir']
// should not be world-accessible
//
if ($this->cfg->getValue('SaveDir') != '') {
SetupIndex::messagesSet(
'notice',
'SaveDir',
Descriptions::get('SaveDir'),
Sanitize::sanitize($sDirectoryNotice)
);
}
//
// $cfg['TempDir']
// should not be world-accessible
//
if ($this->cfg->getValue('TempDir') != '') {
SetupIndex::messagesSet(
'notice',
'TempDir',
Descriptions::get('TempDir'),
Sanitize::sanitize($sDirectoryNotice)
);
}
$this->performConfigChecksZips();
}
/**
* Check config of servers
*
* @param boolean $cookieAuthUsed Cookie auth is used
* @param string $blowfishSecret Blowfish secret
* @param boolean $blowfishSecretSet Blowfish secret set
*
* @return array
*/
protected function performConfigChecksServers(
$cookieAuthUsed, $blowfishSecret,
$blowfishSecretSet
) {
$serverCnt = $this->cfg->getServerCount();
for ($i = 1; $i <= $serverCnt; $i++) {
$cookieAuthServer
= ($this->cfg->getValue("Servers/$i/auth_type") == 'cookie');
$cookieAuthUsed |= $cookieAuthServer;
$serverName = $this->performConfigChecksServersGetServerName(
$this->cfg->getServerName($i), $i
);
$serverName = htmlspecialchars($serverName);
list($blowfishSecret, $blowfishSecretSet)
= $this->performConfigChecksServersSetBlowfishSecret(
$blowfishSecret, $cookieAuthServer, $blowfishSecretSet
);
//
// $cfg['Servers'][$i]['ssl']
// should be enabled if possible
//
if (!$this->cfg->getValue("Servers/$i/ssl")) {
$title = Descriptions::get('Servers/1/ssl') . " ($serverName)";
SetupIndex::messagesSet(
'notice',
"Servers/$i/ssl",
$title,
__(
'You should use SSL connections if your database server '
. 'supports it.'
)
);
}
$sSecurityInfoMsg = Sanitize::sanitize(sprintf(
__(
'If you feel this is necessary, use additional protection settings - '
. '%1$shost authentication%2$s settings and %3$strusted proxies list%4%s. '
. 'However, IP-based protection may not be reliable if your IP belongs '
. 'to an ISP where thousands of users, including you, are connected to.'
),
'[a@' . Url::getCommon(array('page' => 'servers', 'mode' => 'edit', 'id' => $i)) . '#tab_Server_config]',
'[/a]',
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Security]',
'[/a]'
));
//
// $cfg['Servers'][$i]['auth_type']
// warn about full user credentials if 'auth_type' is 'config'
//
if ($this->cfg->getValue("Servers/$i/auth_type") == 'config'
&& $this->cfg->getValue("Servers/$i/user") != ''
&& $this->cfg->getValue("Servers/$i/password") != ''
) {
$title = Descriptions::get('Servers/1/auth_type')
. " ($serverName)";
SetupIndex::messagesSet(
'notice',
"Servers/$i/auth_type",
$title,
Sanitize::sanitize(sprintf(
__(
'You set the [kbd]config[/kbd] authentication type and included '
. 'username and password for auto-login, which is not a desirable '
. 'option for live hosts. Anyone who knows or guesses your phpMyAdmin '
. 'URL can directly access your phpMyAdmin panel. Set %1$sauthentication '
. 'type%2$s to [kbd]cookie[/kbd] or [kbd]http[/kbd].'
),
'[a@' . Url::getCommon(array('page' => 'servers', 'mode' => 'edit', 'id' => $i)) . '#tab_Server]',
'[/a]'
))
. ' ' . $sSecurityInfoMsg
);
}
//
// $cfg['Servers'][$i]['AllowRoot']
// $cfg['Servers'][$i]['AllowNoPassword']
// serious security flaw
//
if ($this->cfg->getValue("Servers/$i/AllowRoot")
&& $this->cfg->getValue("Servers/$i/AllowNoPassword")
) {
$title = Descriptions::get('Servers/1/AllowNoPassword')
. " ($serverName)";
SetupIndex::messagesSet(
'notice',
"Servers/$i/AllowNoPassword",
$title,
__('You allow for connecting to the server without a password.')
. ' ' . $sSecurityInfoMsg
);
}
}
return array($cookieAuthUsed, $blowfishSecret, $blowfishSecretSet);
}
/**
* Set blowfish secret
*
* @param string $blowfishSecret Blowfish secret
* @param boolean $cookieAuthServer Cookie auth is used
* @param boolean $blowfishSecretSet Blowfish secret set
*
* @return array
*/
protected function performConfigChecksServersSetBlowfishSecret(
$blowfishSecret, $cookieAuthServer, $blowfishSecretSet
) {
if ($cookieAuthServer && $blowfishSecret === null) {
$blowfishSecretSet = true;
$this->cfg->set('blowfish_secret', Util::generateRandom(32));
}
return array($blowfishSecret, $blowfishSecretSet);
}
/**
* Define server name
*
* @param string $serverName Server name
* @param int $serverId Server id
*
* @return string Server name
*/
protected function performConfigChecksServersGetServerName(
$serverName, $serverId
) {
if ($serverName == 'localhost') {
$serverName .= " [$serverId]";
return $serverName;
}
return $serverName;
}
/**
* Perform config checks for zip part.
*
* @return void
*/
protected function performConfigChecksZips() {
$this->performConfigChecksServerGZipdump();
$this->performConfigChecksServerBZipdump();
$this->performConfigChecksServersZipdump();
}
/**
* Perform config checks for zip part.
*
* @return void
*/
protected function performConfigChecksServersZipdump() {
//
// $cfg['ZipDump']
// requires zip_open in import
//
if ($this->cfg->getValue('ZipDump') && !$this->functionExists('zip_open')) {
SetupIndex::messagesSet(
'error',
'ZipDump_import',
Descriptions::get('ZipDump'),
Sanitize::sanitize(sprintf(
__(
'%sZip decompression%s requires functions (%s) which are unavailable '
. 'on this system.'
),
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Import_export]',
'[/a]',
'zip_open'
))
);
}
//
// $cfg['ZipDump']
// requires gzcompress in export
//
if ($this->cfg->getValue('ZipDump') && !$this->functionExists('gzcompress')) {
SetupIndex::messagesSet(
'error',
'ZipDump_export',
Descriptions::get('ZipDump'),
Sanitize::sanitize(sprintf(
__(
'%sZip compression%s requires functions (%s) which are unavailable on '
. 'this system.'
),
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Import_export]',
'[/a]',
'gzcompress'
))
);
}
}
/**
* Check config of servers
*
* @param boolean $cookieAuthUsed Cookie auth is used
* @param boolean $blowfishSecretSet Blowfish secret set
* @param string $blowfishSecret Blowfish secret
*
* @return array
*/
protected function performConfigChecksCookieAuthUsed(
$cookieAuthUsed, $blowfishSecretSet,
$blowfishSecret
) {
//
// $cfg['blowfish_secret']
// it's required for 'cookie' authentication
//
if ($cookieAuthUsed) {
if ($blowfishSecretSet) {
// 'cookie' auth used, blowfish_secret was generated
SetupIndex::messagesSet(
'notice',
'blowfish_secret_created',
Descriptions::get('blowfish_secret'),
Sanitize::sanitize(__(
'You didn\'t have blowfish secret set and have enabled '
. '[kbd]cookie[/kbd] authentication, so a key was automatically '
. 'generated for you. It is used to encrypt cookies; you don\'t need to '
. 'remember it.'
))
);
} else {
$blowfishWarnings = array();
// check length
if (strlen($blowfishSecret) < 32) {
// too short key
$blowfishWarnings[] = __(
'Key is too short, it should have at least 32 characters.'
);
}
// check used characters
$hasDigits = (bool)preg_match('/\d/', $blowfishSecret);
$hasChars = (bool)preg_match('/\S/', $blowfishSecret);
$hasNonword = (bool)preg_match('/\W/', $blowfishSecret);
if (!$hasDigits || !$hasChars || !$hasNonword) {
$blowfishWarnings[] = Sanitize::sanitize(
__(
'Key should contain letters, numbers [em]and[/em] '
. 'special characters.'
)
);
}
if (!empty($blowfishWarnings)) {
SetupIndex::messagesSet(
'error',
'blowfish_warnings' . count($blowfishWarnings),
Descriptions::get('blowfish_secret'),
implode('<br />', $blowfishWarnings)
);
}
}
}
}
/**
* Check configuration for login cookie
*
* @return void
*/
protected function performConfigChecksLoginCookie() {
//
// $cfg['LoginCookieValidity']
// value greater than session.gc_maxlifetime will cause
// random session invalidation after that time
$loginCookieValidity = $this->cfg->getValue('LoginCookieValidity');
if ($loginCookieValidity > ini_get('session.gc_maxlifetime')
) {
SetupIndex::messagesSet(
'error',
'LoginCookieValidity',
Descriptions::get('LoginCookieValidity'),
Sanitize::sanitize(sprintf(
__(
'%1$sLogin cookie validity%2$s greater than %3$ssession.gc_maxlifetime%4$s may '
. 'cause random session invalidation (currently session.gc_maxlifetime '
. 'is %5$d).'
),
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Security]',
'[/a]',
'[a@' . Core::getPHPDocLink('session.configuration.php#ini.session.gc-maxlifetime') . ']',
'[/a]',
ini_get('session.gc_maxlifetime')
))
);
}
//
// $cfg['LoginCookieValidity']
// should be at most 1800 (30 min)
//
if ($loginCookieValidity > 1800) {
SetupIndex::messagesSet(
'notice',
'LoginCookieValidity',
Descriptions::get('LoginCookieValidity'),
Sanitize::sanitize(sprintf(
__(
'%sLogin cookie validity%s should be set to 1800 seconds (30 minutes) '
. 'at most. Values larger than 1800 may pose a security risk such as '
. 'impersonation.'
),
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Security]',
'[/a]'
))
);
}
//
// $cfg['LoginCookieValidity']
// $cfg['LoginCookieStore']
// LoginCookieValidity must be less or equal to LoginCookieStore
//
if (($this->cfg->getValue('LoginCookieStore') != 0)
&& ($loginCookieValidity > $this->cfg->getValue('LoginCookieStore'))
) {
SetupIndex::messagesSet(
'error',
'LoginCookieValidity',
Descriptions::get('LoginCookieValidity'),
Sanitize::sanitize(sprintf(
__(
'If using [kbd]cookie[/kbd] authentication and %sLogin cookie store%s '
. 'is not 0, %sLogin cookie validity%s must be set to a value less or '
. 'equal to it.'
),
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Security]',
'[/a]',
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Security]',
'[/a]'
))
);
}
}
/**
* Check GZipDump configuration
*
* @return void
*/
protected function performConfigChecksServerBZipdump()
{
//
// $cfg['BZipDump']
// requires bzip2 functions
//
if ($this->cfg->getValue('BZipDump')
&& (!$this->functionExists('bzopen') || !$this->functionExists('bzcompress'))
) {
$functions = $this->functionExists('bzopen')
? '' :
'bzopen';
$functions .= $this->functionExists('bzcompress')
? ''
: ($functions ? ', ' : '') . 'bzcompress';
SetupIndex::messagesSet(
'error',
'BZipDump',
Descriptions::get('BZipDump'),
Sanitize::sanitize(
sprintf(
__(
'%1$sBzip2 compression and decompression%2$s requires functions (%3$s) which '
. 'are unavailable on this system.'
),
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Import_export]',
'[/a]',
$functions
)
)
);
}
}
/**
* Check GZipDump configuration
*
* @return void
*/
protected function performConfigChecksServerGZipdump()
{
//
// $cfg['GZipDump']
// requires zlib functions
//
if ($this->cfg->getValue('GZipDump')
&& (!$this->functionExists('gzopen') || !$this->functionExists('gzencode'))
) {
SetupIndex::messagesSet(
'error',
'GZipDump',
Descriptions::get('GZipDump'),
Sanitize::sanitize(sprintf(
__(
'%1$sGZip compression and decompression%2$s requires functions (%3$s) which '
. 'are unavailable on this system.'
),
'[a@' . Url::getCommon(array('page' => 'form', 'formset' => 'Features')) . '#tab_Import_export]',
'[/a]',
'gzencode'
))
);
}
}
/**
* Wrapper around function_exists to allow mock in test
*
* @param string $name Function name
*
* @return boolean
*/
protected function functionExists($name)
{
return function_exists($name);
}
}

View File

@@ -0,0 +1,589 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Form validation for configuration editor
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Util;
/**
* Validation class for various validation functions
*
* Validation function takes two argument: id for which it is called
* and array of fields' values (usually values for entire formset, as defined
* in forms.inc.php).
* The function must always return an array with an error (or error array)
* assigned to a form element (formset name or field path). Even if there are
* no errors, key must be set with an empty value.
*
* Validation functions are assigned in $cfg_db['_validators'] (config.values.php).
*
* @package PhpMyAdmin
*/
class Validator
{
/**
* Returns validator list
*
* @param ConfigFile $cf Config file instance
*
* @return array
*/
public static function getValidators(ConfigFile $cf)
{
static $validators = null;
if ($validators !== null) {
return $validators;
}
$validators = $cf->getDbEntry('_validators', array());
if ($GLOBALS['PMA_Config']->get('is_setup')) {
return $validators;
}
// not in setup script: load additional validators for user
// preferences we need original config values not overwritten
// by user preferences, creating a new PhpMyAdmin\Config instance is a
// better idea than hacking into its code
$uvs = $cf->getDbEntry('_userValidators', array());
foreach ($uvs as $field => $uv_list) {
$uv_list = (array)$uv_list;
foreach ($uv_list as &$uv) {
if (!is_array($uv)) {
continue;
}
for ($i = 1, $nb = count($uv); $i < $nb; $i++) {
if (mb_substr($uv[$i], 0, 6) == 'value:') {
$uv[$i] = Core::arrayRead(
mb_substr($uv[$i], 6),
$GLOBALS['PMA_Config']->base_settings
);
}
}
}
$validators[$field] = isset($validators[$field])
? array_merge((array)$validators[$field], $uv_list)
: $uv_list;
}
return $validators;
}
/**
* Runs validation $validator_id on values $values and returns error list.
*
* Return values:
* o array, keys - field path or formset id, values - array of errors
* when $isPostSource is true values is an empty array to allow for error list
* cleanup in HTML document
* o false - when no validators match name(s) given by $validator_id
*
* @param ConfigFile $cf Config file instance
* @param string|array $validator_id ID of validator(s) to run
* @param array &$values Values to validate
* @param bool $isPostSource tells whether $values are directly from
* POST request
*
* @return bool|array
*/
public static function validate(
ConfigFile $cf, $validator_id, array &$values, $isPostSource
) {
// find validators
$validator_id = (array) $validator_id;
$validators = static::getValidators($cf);
$vids = array();
foreach ($validator_id as &$vid) {
$vid = $cf->getCanonicalPath($vid);
if (isset($validators[$vid])) {
$vids[] = $vid;
}
}
if (empty($vids)) {
return false;
}
// create argument list with canonical paths and remember path mapping
$arguments = array();
$key_map = array();
foreach ($values as $k => $v) {
$k2 = $isPostSource ? str_replace('-', '/', $k) : $k;
$k2 = mb_strpos($k2, '/')
? $cf->getCanonicalPath($k2)
: $k2;
$key_map[$k2] = $k;
$arguments[$k2] = $v;
}
// validate
$result = array();
foreach ($vids as $vid) {
// call appropriate validation functions
foreach ((array)$validators[$vid] as $validator) {
$vdef = (array) $validator;
$vname = array_shift($vdef);
$vname = 'PhpMyAdmin\Config\Validator::' . $vname;
$args = array_merge(array($vid, &$arguments), $vdef);
$r = call_user_func_array($vname, $args);
// merge results
if (!is_array($r)) {
continue;
}
foreach ($r as $key => $error_list) {
// skip empty values if $isPostSource is false
if (! $isPostSource && empty($error_list)) {
continue;
}
if (! isset($result[$key])) {
$result[$key] = array();
}
$result[$key] = array_merge(
$result[$key], (array)$error_list
);
}
}
}
// restore original paths
$new_result = array();
foreach ($result as $k => $v) {
$k2 = isset($key_map[$k]) ? $key_map[$k] : $k;
if (is_array($v)) {
$new_result[$k2] = array_map('htmlspecialchars', $v);
} else {
$new_result[$k2] = htmlspecialchars($v);
}
}
return empty($new_result) ? true : $new_result;
}
/**
* Test database connection
*
* @param string $host host name
* @param string $port tcp port to use
* @param string $socket socket to use
* @param string $user username to use
* @param string $pass password to use
* @param string $error_key key to use in return array
*
* @return bool|array
*/
public static function testDBConnection(
$host,
$port,
$socket,
$user,
$pass = null,
$error_key = 'Server'
) {
if ($GLOBALS['cfg']['DBG']['demo']) {
// Connection test disabled on the demo server!
return true;
}
$error = null;
$host = Core::sanitizeMySQLHost($host);
if (function_exists('error_clear_last')) {
/* PHP 7 only code */
error_clear_last();
}
if (DatabaseInterface::checkDbExtension('mysqli')) {
$socket = empty($socket) ? null : $socket;
$port = empty($port) ? null : $port;
$extension = 'mysqli';
} else {
$socket = empty($socket) ? null : ':' . ($socket[0] == '/' ? '' : '/') . $socket;
$port = empty($port) ? null : ':' . $port;
$extension = 'mysql';
}
if ($extension == 'mysql') {
$conn = @mysql_connect($host . $port . $socket, $user, $pass);
if (! $conn) {
$error = __('Could not connect to the database server!');
} else {
mysql_close($conn);
}
} else {
$conn = @mysqli_connect($host, $user, $pass, null, $port, $socket);
if (! $conn) {
$error = __('Could not connect to the database server!');
} else {
mysqli_close($conn);
}
}
if (! is_null($error)) {
$error .= ' - ' . error_get_last();
}
return is_null($error) ? true : array($error_key => $error);
}
/**
* Validate server config
*
* @param string $path path to config, not used
* keep this parameter since the method is invoked using
* reflection along with other similar methods
* @param array $values config values
*
* @return array
*/
public static function validateServer($path, array $values)
{
$result = array(
'Server' => '',
'Servers/1/user' => '',
'Servers/1/SignonSession' => '',
'Servers/1/SignonURL' => ''
);
$error = false;
if (empty($values['Servers/1/auth_type'])) {
$values['Servers/1/auth_type'] = '';
$result['Servers/1/auth_type'] = __('Invalid authentication type!');
$error = true;
}
if ($values['Servers/1/auth_type'] == 'config'
&& empty($values['Servers/1/user'])
) {
$result['Servers/1/user'] = __(
'Empty username while using [kbd]config[/kbd] authentication method!'
);
$error = true;
}
if ($values['Servers/1/auth_type'] == 'signon'
&& empty($values['Servers/1/SignonSession'])
) {
$result['Servers/1/SignonSession'] = __(
'Empty signon session name '
. 'while using [kbd]signon[/kbd] authentication method!'
);
$error = true;
}
if ($values['Servers/1/auth_type'] == 'signon'
&& empty($values['Servers/1/SignonURL'])
) {
$result['Servers/1/SignonURL'] = __(
'Empty signon URL while using [kbd]signon[/kbd] authentication '
. 'method!'
);
$error = true;
}
if (! $error && $values['Servers/1/auth_type'] == 'config') {
$password = '';
if (! empty($values['Servers/1/password'])) {
$password = $values['Servers/1/password'];
}
$test = static::testDBConnection(
empty($values['Servers/1/host']) ? '' : $values['Servers/1/host'],
empty($values['Servers/1/port']) ? '' : $values['Servers/1/port'],
empty($values['Servers/1/socket']) ? '' : $values['Servers/1/socket'],
empty($values['Servers/1/user']) ? '' : $values['Servers/1/user'],
$password,
'Server'
);
if ($test !== true) {
$result = array_merge($result, $test);
}
}
return $result;
}
/**
* Validate pmadb config
*
* @param string $path path to config, not used
* keep this parameter since the method is invoked using
* reflection along with other similar methods
* @param array $values config values
*
* @return array
*/
public static function validatePMAStorage($path, array $values)
{
$result = array(
'Server_pmadb' => '',
'Servers/1/controluser' => '',
'Servers/1/controlpass' => ''
);
$error = false;
if (empty($values['Servers/1/pmadb'])) {
return $result;
}
$result = array();
if (empty($values['Servers/1/controluser'])) {
$result['Servers/1/controluser'] = __(
'Empty phpMyAdmin control user while using phpMyAdmin configuration '
. 'storage!'
);
$error = true;
}
if (empty($values['Servers/1/controlpass'])) {
$result['Servers/1/controlpass'] = __(
'Empty phpMyAdmin control user password while using phpMyAdmin '
. 'configuration storage!'
);
$error = true;
}
if (! $error) {
$test = static::testDBConnection(
empty($values['Servers/1/host']) ? '' : $values['Servers/1/host'],
empty($values['Servers/1/port']) ? '' : $values['Servers/1/port'],
empty($values['Servers/1/socket']) ? '' : $values['Servers/1/socket'],
empty($values['Servers/1/controluser']) ? '' : $values['Servers/1/controluser'],
empty($values['Servers/1/controlpass']) ? '' : $values['Servers/1/controlpass'],
'Server_pmadb'
);
if ($test !== true) {
$result = array_merge($result, $test);
}
}
return $result;
}
/**
* Validates regular expression
*
* @param string $path path to config
* @param array $values config values
*
* @return array
*/
public static function validateRegex($path, array $values)
{
$result = array($path => '');
if (empty($values[$path])) {
return $result;
}
if (function_exists('error_clear_last')) {
/* PHP 7 only code */
error_clear_last();
$last_error = null;
} else {
// As fallback we trigger another error to ensure
// that preg error will be different
@strpos();
$last_error = error_get_last();
}
$matches = array();
// in libraries/ListDatabase.php _checkHideDatabase(),
// a '/' is used as the delimiter for hide_db
@preg_match('/' . Util::requestString($values[$path]) . '/', '', $matches);
$current_error = error_get_last();
if ($current_error !== $last_error) {
$error = preg_replace('/^preg_match\(\): /', '', $current_error['message']);
return array($path => $error);
}
return $result;
}
/**
* Validates TrustedProxies field
*
* @param string $path path to config
* @param array $values config values
*
* @return array
*/
public static function validateTrustedProxies($path, array $values)
{
$result = array($path => array());
if (empty($values[$path])) {
return $result;
}
if (is_array($values[$path]) || is_object($values[$path])) {
// value already processed by FormDisplay::save
$lines = array();
foreach ($values[$path] as $ip => $v) {
$v = Util::requestString($v);
$lines[] = preg_match('/^-\d+$/', $ip)
? $v
: $ip . ': ' . $v;
}
} else {
// AJAX validation
$lines = explode("\n", $values[$path]);
}
foreach ($lines as $line) {
$line = trim($line);
$matches = array();
// we catch anything that may (or may not) be an IP
if (!preg_match("/^(.+):(?:[ ]?)\\w+$/", $line, $matches)) {
$result[$path][] = __('Incorrect value:') . ' '
. htmlspecialchars($line);
continue;
}
// now let's check whether we really have an IP address
if (filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false
&& filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false
) {
$ip = htmlspecialchars(trim($matches[1]));
$result[$path][] = sprintf(__('Incorrect IP address: %s'), $ip);
continue;
}
}
return $result;
}
/**
* Tests integer value
*
* @param string $path path to config
* @param array $values config values
* @param bool $allow_neg allow negative values
* @param bool $allow_zero allow zero
* @param int $max_value max allowed value
* @param string $error_string error message string
*
* @return string empty string if test is successful
*/
public static function validateNumber(
$path,
array $values,
$allow_neg,
$allow_zero,
$max_value,
$error_string
) {
if (empty($values[$path])) {
return '';
}
$value = Util::requestString($values[$path]);
if (intval($value) != $value
|| (! $allow_neg && $value < 0)
|| (! $allow_zero && $value == 0)
|| $value > $max_value
) {
return $error_string;
}
return '';
}
/**
* Validates port number
*
* @param string $path path to config
* @param array $values config values
*
* @return array
*/
public static function validatePortNumber($path, array $values)
{
return array(
$path => static::validateNumber(
$path,
$values,
false,
false,
65535,
__('Not a valid port number!')
)
);
}
/**
* Validates positive number
*
* @param string $path path to config
* @param array $values config values
*
* @return array
*/
public static function validatePositiveNumber($path, array $values)
{
return array(
$path => static::validateNumber(
$path,
$values,
false,
false,
PHP_INT_MAX,
__('Not a positive number!')
)
);
}
/**
* Validates non-negative number
*
* @param string $path path to config
* @param array $values config values
*
* @return array
*/
public static function validateNonNegativeNumber($path, array $values)
{
return array(
$path => static::validateNumber(
$path,
$values,
false,
true,
PHP_INT_MAX,
__('Not a non-negative number!')
)
);
}
/**
* Validates value according to given regular expression
* Pattern and modifiers must be a valid for PCRE <b>and</b> JavaScript RegExp
*
* @param string $path path to config
* @param array $values config values
* @param string $regex regular expression to match
*
* @return array
*/
public static function validateByRegex($path, array $values, $regex)
{
if (!isset($values[$path])) {
return '';
}
$result = preg_match($regex, Util::requestString($values[$path]));
return array($path => ($result ? '' : __('Incorrect value!')));
}
/**
* Validates upper bound for numeric inputs
*
* @param string $path path to config
* @param array $values config values
* @param int $max_value maximal allowed value
*
* @return array
*/
public static function validateUpperBound($path, array $values, $max_value)
{
$result = $values[$path] <= $max_value;
return array($path => ($result ? ''
: sprintf(__('Value must be less than or equal to %s!'), $max_value)));
}
}

View File

@@ -0,0 +1,152 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Used to render the console of PMA's pages
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin;
use PhpMyAdmin\Bookmark;
use PhpMyAdmin\Relation;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
/**
* Class used to output the console
*
* @package PhpMyAdmin
*/
class Console
{
/**
* Whether to display anything
*
* @access private
* @var bool
*/
private $_isEnabled;
/**
* Whether we are servicing an ajax request.
*
* @access private
* @var bool
*/
private $_isAjax;
/**
* @var Relation
*/
private $relation;
/**
* Creates a new class instance
*/
public function __construct()
{
$this->_isEnabled = true;
$this->relation = new Relation();
}
/**
* Set the ajax flag to indicate whether
* we are servicing an ajax request
*
* @param bool $isAjax Whether we are servicing an ajax request
*
* @return void
*/
public function setAjax($isAjax)
{
$this->_isAjax = (boolean) $isAjax;
}
/**
* Disables the rendering of the footer
*
* @return void
*/
public function disable()
{
$this->_isEnabled = false;
}
/**
* Renders the bookmark content
*
* @access public
* @return string
*/
public static function getBookmarkContent()
{
$cfgBookmark = Bookmark::getParams($GLOBALS['cfg']['Server']['user']);
if ($cfgBookmark) {
$bookmarks = Bookmark::getList(
$GLOBALS['dbi'],
$GLOBALS['cfg']['Server']['user']
);
$count_bookmarks = count($bookmarks);
if ($count_bookmarks > 0) {
$welcomeMessage = sprintf(
_ngettext(
'Showing %1$d bookmark (both private and shared)',
'Showing %1$d bookmarks (both private and shared)',
$count_bookmarks
),
$count_bookmarks
);
} else {
$welcomeMessage = __('No bookmarks');
}
unset($count_bookmarks, $private_message, $shared_message);
return Template::get('console/bookmark_content')
->render(
array(
'welcome_message' => $welcomeMessage,
'bookmarks' => $bookmarks,
)
);
}
return '';
}
/**
* Returns the list of JS scripts required by console
*
* @return array list of scripts
*/
public function getScripts()
{
return array('console.js');
}
/**
* Renders the console
*
* @access public
* @return string
*/
public function getDisplay()
{
if ((! $this->_isAjax) && $this->_isEnabled) {
$cfgBookmark = Bookmark::getParams(
$GLOBALS['cfg']['Server']['user']
);
$image = Util::getImage('console', __('SQL Query Console'));
$_sql_history = $this->relation->getHistory(
$GLOBALS['cfg']['Server']['user']
);
$bookmarkContent = static::getBookmarkContent();
return Template::get('console/display')->render([
'cfg_bookmark' => $cfgBookmark,
'image' => $image,
'sql_history' => $_sql_history,
'bookmark_content' => $bookmarkContent,
]);
}
return '';
}
}

View File

@@ -0,0 +1,39 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\Controller
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Response;
/**
* Base class for all of controller
*
* @package PhpMyAdmin\Controllers
*/
abstract class Controller
{
/**
* @var Response
*/
protected $response;
/**
* @var DatabaseInterface
*/
protected $dbi;
/**
* Constructor
*/
public function __construct($response, $dbi)
{
$this->response = $response;
$this->dbi = $dbi;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\DatabaseController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers;
/**
* Handles database related logic
*
* @package PhpMyAdmin\Controllers
*/
abstract class DatabaseController extends Controller
{
/**
* @var string $db
*/
protected $db;
/**
* Constructor
*/
public function __construct($response, $dbi, $db)
{
parent::__construct($response, $dbi);
$this->db = $db;
}
}

View File

@@ -0,0 +1,263 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\Server\ServerBinlogController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\Controller;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\Server\Common;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
/**
* Handles viewing binary logs
*
* @package PhpMyAdmin\Controllers
*/
class ServerBinlogController extends Controller
{
/**
* array binary log files
*/
protected $binary_logs;
/**
* Constructs ServerBinlogController
*/
public function __construct($response, $dbi)
{
parent::__construct($response, $dbi);
$this->binary_logs = $this->dbi->fetchResult(
'SHOW MASTER LOGS',
'Log_name',
null,
DatabaseInterface::CONNECT_USER,
DatabaseInterface::QUERY_STORE
);
}
/**
* Index action
*
* @return void
*/
public function indexAction()
{
/**
* Does the common work
*/
include_once 'libraries/server_common.inc.php';
$url_params = array();
if (! isset($_POST['log'])
|| ! array_key_exists($_POST['log'], $this->binary_logs)
) {
$_POST['log'] = '';
} else {
$url_params['log'] = $_POST['log'];
}
if (!empty($_POST['dontlimitchars'])) {
$url_params['dontlimitchars'] = 1;
}
$this->response->addHTML(
Template::get('server/sub_page_header')->render([
'type' => 'binlog',
])
);
$this->response->addHTML($this->_getLogSelector($url_params));
$this->response->addHTML($this->_getLogInfo($url_params));
}
/**
* Returns the html for log selector.
*
* @param array $url_params links parameters
*
* @return string
*/
private function _getLogSelector(array $url_params)
{
return Template::get('server/binlog/log_selector')->render(
array(
'url_params' => $url_params,
'binary_logs' => $this->binary_logs,
'log' => $_POST['log'],
)
);
}
/**
* Returns the html for binary log information.
*
* @param array $url_params links parameters
*
* @return string
*/
private function _getLogInfo(array $url_params)
{
/**
* Need to find the real end of rows?
*/
if (! isset($_POST['pos'])) {
$pos = 0;
} else {
/* We need this to be a integer */
$pos = (int) $_POST['pos'];
}
$sql_query = 'SHOW BINLOG EVENTS';
if (! empty($_POST['log'])) {
$sql_query .= ' IN \'' . $_POST['log'] . '\'';
}
$sql_query .= ' LIMIT ' . $pos . ', ' . intval($GLOBALS['cfg']['MaxRows']);
/**
* Sends the query
*/
$result = $this->dbi->query($sql_query);
/**
* prepare some vars for displaying the result table
*/
// Gets the list of fields properties
if (isset($result) && $result) {
$num_rows = $this->dbi->numRows($result);
} else {
$num_rows = 0;
}
if (empty($_POST['dontlimitchars'])) {
$dontlimitchars = false;
} else {
$dontlimitchars = true;
$url_params['dontlimitchars'] = 1;
}
//html output
$html = Util::getMessage(Message::success(), $sql_query);
$html .= '<table id="binlogTable">'
. '<thead>'
. '<tr>'
. '<td colspan="6" class="center">';
$html .= $this->_getNavigationRow($url_params, $pos, $num_rows, $dontlimitchars);
$html .= '</td>'
. '</tr>'
. '<tr>'
. '<th>' . __('Log name') . '</th>'
. '<th>' . __('Position') . '</th>'
. '<th>' . __('Event type') . '</th>'
. '<th>' . __('Server ID') . '</th>'
. '<th>' . __('Original position') . '</th>'
. '<th>' . __('Information') . '</th>'
. '</tr>'
. '</thead>'
. '<tbody>';
$html .= $this->_getAllLogItemInfo($result, $dontlimitchars);
$html .= '</tbody>'
. '</table>';
return $html;
}
/**
* Returns the html for Navigation Row.
*
* @param array $url_params Links parameters
* @param int $pos Position to display
* @param int $num_rows Number of results row
* @param bool $dontlimitchars Whether limit chars
*
* @return string
*/
private function _getNavigationRow(array $url_params, $pos, $num_rows, $dontlimitchars)
{
$html = "";
// we do not know how much rows are in the binlog
// so we can just force 'NEXT' button
if ($pos > 0) {
$this_url_params = $url_params;
if ($pos > $GLOBALS['cfg']['MaxRows']) {
$this_url_params['pos'] = $pos - $GLOBALS['cfg']['MaxRows'];
}
$html .= '<a href="server_binlog.php" data-post="'
. Url::getCommon($this_url_params, '', false) . '"';
if (Util::showIcons('TableNavigationLinksMode')) {
$html .= ' title="' . _pgettext('Previous page', 'Previous') . '">';
} else {
$html .= '>' . _pgettext('Previous page', 'Previous');
} // end if... else...
$html .= ' &lt; </a> - ';
}
$this_url_params = $url_params;
if ($pos > 0) {
$this_url_params['pos'] = $pos;
}
if ($dontlimitchars) {
unset($this_url_params['dontlimitchars']);
$tempTitle = __('Truncate Shown Queries');
$tempImgMode = 'partial';
} else {
$this_url_params['dontlimitchars'] = 1;
$tempTitle = __('Show Full Queries');
$tempImgMode = 'full';
}
$html .= '<a href="server_binlog.php" data-post="' . Url::getCommon($this_url_params, '', false)
. '" title="' . $tempTitle . '">'
. '<img src="' . $GLOBALS['pmaThemeImage'] . 's_' . $tempImgMode
. 'text.png" alt="' . $tempTitle . '" /></a>';
// we do not now how much rows are in the binlog
// so we can just force 'NEXT' button
if ($num_rows >= $GLOBALS['cfg']['MaxRows']) {
$this_url_params = $url_params;
$this_url_params['pos'] = $pos + $GLOBALS['cfg']['MaxRows'];
$html .= ' - <a href="server_binlog.php" data-post="'
. Url::getCommon($this_url_params, '', false)
. '"';
if (Util::showIcons('TableNavigationLinksMode')) {
$html .= ' title="' . _pgettext('Next page', 'Next') . '">';
} else {
$html .= '>' . _pgettext('Next page', 'Next');
} // end if... else...
$html .= ' &gt; </a>';
}
return $html;
}
/**
* Returns the html for all binary log items.
*
* @param resource $result MySQL Query result
* @param bool $dontlimitchars Whether limit chars
*
* @return string
*/
private function _getAllLogItemInfo($result, $dontlimitchars)
{
$html = "";
while ($value = $this->dbi->fetchAssoc($result)) {
$html .= Template::get('server/binlog/log_row')->render(
array(
'value' => $value,
'dontlimitchars' => $dontlimitchars,
)
);
}
return $html;
}
}

View File

@@ -0,0 +1,76 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\Server\ServerCollationsController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\Controller;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\Server\Common;
use PhpMyAdmin\Template;
/**
* Handles viewing character sets and collations
*
* @package PhpMyAdmin\Controllers
*/
class ServerCollationsController extends Controller
{
/**
* Index action
*
* @return void
*/
public function indexAction()
{
$dbi = $GLOBALS['dbi'];
$disableIs = $GLOBALS['cfg']['Server']['DisableIS'];
/**
* Does the common work
*/
include_once 'libraries/server_common.inc.php';
$this->response->addHTML(
Template::get('server/sub_page_header')->render([
'type' => 'collations',
])
);
$this->response->addHTML(
$this->_getHtmlForCharsets(
Charsets::getMySQLCharsets($dbi, $disableIs),
Charsets::getMySQLCollations($dbi, $disableIs),
Charsets::getMySQLCharsetsDescriptions($dbi, $disableIs),
Charsets::getMySQLCollationsDefault($dbi, $disableIs)
)
);
}
/**
* Returns the html for server Character Sets and Collations.
*
* @param array $mysqlCharsets Mysql Charsets list
* @param array $mysqlCollations Mysql Collations list
* @param array $mysqlCharsetsDesc Charsets descriptions
* @param array $mysqlDftCollations Default Collations list
*
* @return string
*/
function _getHtmlForCharsets(array $mysqlCharsets, array $mysqlCollations,
array $mysqlCharsetsDesc, array $mysqlDftCollations
) {
return Template::get('server/collations/charsets')->render(
array(
'mysql_charsets' => $mysqlCharsets,
'mysql_collations' => $mysqlCollations,
'mysql_charsets_desc' => $mysqlCharsetsDesc,
'mysql_dft_collations' => $mysqlDftCollations,
)
);
}
}

View File

@@ -0,0 +1,470 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\Server\ServerDatabasesController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\Controller;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\Response;
use PhpMyAdmin\Server\Common;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
/**
* Handles viewing and creating and deleting databases
*
* @package PhpMyAdmin\Controllers
*/
class ServerDatabasesController extends Controller
{
/**
* @var array array of database details
*/
private $_databases;
/**
* @var int number of databases
*/
private $_database_count;
/**
* @var string sort by column
*/
private $_sort_by;
/**
* @var string sort order of databases
*/
private $_sort_order;
/**
* @var boolean whether to show database statistics
*/
private $_dbstats;
/**
* @var int position in list navigation
*/
private $_pos;
/**
* Index action
*
* @return void
*/
public function indexAction()
{
include_once 'libraries/check_user_privileges.inc.php';
$response = Response::getInstance();
if (isset($_POST['drop_selected_dbs'])
&& $response->isAjax()
&& ($GLOBALS['dbi']->isSuperuser() || $GLOBALS['cfg']['AllowUserDropDatabase'])
) {
$this->dropDatabasesAction();
return;
}
include_once 'libraries/replication.inc.php';
if (isset($_POST['new_db'])
&& $response->isAjax()
) {
$this->createDatabaseAction();
return;
}
include_once 'libraries/server_common.inc.php';
$header = $this->response->getHeader();
$scripts = $header->getScripts();
$scripts->addFile('server_databases.js');
$this->_setSortDetails();
$this->_dbstats = ! empty($_POST['dbstats']);
$this->_pos = empty($_REQUEST['pos']) ? 0 : (int) $_REQUEST['pos'];
/**
* Gets the databases list
*/
if ($GLOBALS['server'] > 0) {
$this->_databases = $this->dbi->getDatabasesFull(
null, $this->_dbstats, DatabaseInterface::CONNECT_USER, $this->_sort_by,
$this->_sort_order, $this->_pos, true
);
$this->_database_count = count($GLOBALS['dblist']->databases);
} else {
$this->_database_count = 0;
}
if ($this->_database_count > 0 && ! empty($this->_databases)) {
$databases = $this->_getHtmlForDatabases($replication_types);
}
$this->response->addHTML(Template::get('server/databases/index')->render([
'show_create_db' => $GLOBALS['cfg']['ShowCreateDb'],
'is_create_db_priv' => $GLOBALS['is_create_db_priv'],
'dbstats' => $this->_dbstats,
'db_to_create' => $GLOBALS['db_to_create'],
'server_collation' => $GLOBALS['dbi']->getServerCollation(),
'databases' => isset($databases) ? $databases : null,
'dbi' => $GLOBALS['dbi'],
'disable_is' => $GLOBALS['cfg']['Server']['DisableIS'],
]));
}
/**
* Handles creating a new database
*
* @return void
*/
public function createDatabaseAction()
{
// lower_case_table_names=1 `DB` becomes `db`
if ($GLOBALS['dbi']->getLowerCaseNames() === '1') {
$_POST['new_db'] = mb_strtolower(
$_POST['new_db']
);
}
/**
* Builds and executes the db creation sql query
*/
$sql_query = 'CREATE DATABASE ' . Util::backquote($_POST['new_db']);
if (! empty($_POST['db_collation'])) {
list($db_charset) = explode('_', $_POST['db_collation']);
$charsets = Charsets::getMySQLCharsets(
$GLOBALS['dbi'],
$GLOBALS['cfg']['Server']['DisableIS']
);
$collations = Charsets::getMySQLCollations(
$GLOBALS['dbi'],
$GLOBALS['cfg']['Server']['DisableIS']
);
if (in_array($db_charset, $charsets)
&& in_array($_POST['db_collation'], $collations[$db_charset])
) {
$sql_query .= ' DEFAULT'
. Util::getCharsetQueryPart($_POST['db_collation']);
}
}
$sql_query .= ';';
$result = $GLOBALS['dbi']->tryQuery($sql_query);
if (! $result) {
// avoid displaying the not-created db name in header or navi panel
$GLOBALS['db'] = '';
$message = Message::rawError($GLOBALS['dbi']->getError());
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $message);
} else {
$GLOBALS['db'] = $_POST['new_db'];
$message = Message::success(__('Database %1$s has been created.'));
$message->addParam($_POST['new_db']);
$this->response->addJSON('message', $message);
$this->response->addJSON(
'sql_query', Util::getMessage(null, $sql_query, 'success')
);
$this->response->addJSON(
'url_query',
Util::getScriptNameForOption(
$GLOBALS['cfg']['DefaultTabDatabase'], 'database'
)
. Url::getCommon(array('db' => $_POST['new_db']))
);
}
}
/**
* Handles dropping multiple databases
*
* @return void
*/
public function dropDatabasesAction()
{
if (! isset($_POST['selected_dbs'])) {
$message = Message::error(__('No databases selected.'));
} else {
$action = 'server_databases.php';
$err_url = $action . Url::getCommon();
$GLOBALS['submit_mult'] = 'drop_db';
$GLOBALS['mult_btn'] = __('Yes');
include 'libraries/mult_submits.inc.php';
if (empty($message)) { // no error message
$number_of_databases = count($selected);
$message = Message::success(
_ngettext(
'%1$d database has been dropped successfully.',
'%1$d databases have been dropped successfully.',
$number_of_databases
)
);
$message->addParam($number_of_databases);
}
}
if ($message instanceof Message) {
$this->response->setRequestStatus($message->isSuccess());
$this->response->addJSON('message', $message);
}
}
/**
* Extracts parameters $sort_order and $sort_by
*
* @return void
*/
private function _setSortDetails()
{
if (empty($_REQUEST['sort_by'])) {
$this->_sort_by = 'SCHEMA_NAME';
} else {
$sort_by_whitelist = array(
'SCHEMA_NAME',
'DEFAULT_COLLATION_NAME',
'SCHEMA_TABLES',
'SCHEMA_TABLE_ROWS',
'SCHEMA_DATA_LENGTH',
'SCHEMA_INDEX_LENGTH',
'SCHEMA_LENGTH',
'SCHEMA_DATA_FREE'
);
if (in_array($_REQUEST['sort_by'], $sort_by_whitelist)) {
$this->_sort_by = $_REQUEST['sort_by'];
} else {
$this->_sort_by = 'SCHEMA_NAME';
}
}
if (isset($_REQUEST['sort_order'])
&& mb_strtolower($_REQUEST['sort_order']) == 'desc'
) {
$this->_sort_order = 'desc';
} else {
$this->_sort_order = 'asc';
}
}
/**
* Returns the html for Database List
*
* @param array $replication_types replication types
*
* @return string
*/
private function _getHtmlForDatabases(array $replication_types)
{
$first_database = reset($this->_databases);
// table col order
$column_order = $this->_getColumnOrder();
// calculate aggregate stats to display in footer
foreach ($this->_databases as $current) {
foreach ($column_order as $stat_name => $stat) {
if (array_key_exists($stat_name, $current)
&& is_numeric($stat['footer'])
) {
$column_order[$stat_name]['footer'] += $current[$stat_name];
}
}
}
$_url_params = array(
'pos' => $this->_pos,
'dbstats' => $this->_dbstats,
'sort_by' => $this->_sort_by,
'sort_order' => $this->_sort_order,
);
$html = Template::get('server/databases/databases_header')->render([
'database_count' => $this->_database_count,
'pos' => $this->_pos,
'url_params' => $_url_params,
'max_db_list' => $GLOBALS['cfg']['MaxDbList'],
'sort_by' => $this->_sort_by,
'sort_order' => $this->_sort_order,
'column_order' => $column_order,
'first_database' => $first_database,
'master_replication' => $GLOBALS['replication_info']['master']['status'],
'slave_replication' => $GLOBALS['replication_info']['slave']['status'],
'is_superuser' => $GLOBALS['dbi']->isSuperuser(),
'allow_user_drop_database' => $GLOBALS['cfg']['AllowUserDropDatabase'],
]);
$html .= $this->_getHtmlForTableBody($column_order, $replication_types);
$html .= Template::get('server/databases/databases_footer')->render([
'column_order' => $column_order,
'first_database' => $first_database,
'master_replication' => $GLOBALS['replication_info']['master']['status'],
'slave_replication' => $GLOBALS['replication_info']['slave']['status'],
'database_count' => $this->_database_count,
'is_superuser' => $GLOBALS['dbi']->isSuperuser(),
'allow_user_drop_database' => $GLOBALS['cfg']['AllowUserDropDatabase'],
'pma_theme_image' => $GLOBALS['pmaThemeImage'],
'text_dir' => $GLOBALS['text_dir'],
'dbstats' => $this->_dbstats,
]);
return $html;
}
/**
* Prepares the $column_order array
*
* @return array
*/
private function _getColumnOrder()
{
$column_order = array();
$column_order['DEFAULT_COLLATION_NAME'] = array(
'disp_name' => __('Collation'),
'description_function' => array(Charsets::class, 'getCollationDescr'),
'format' => 'string',
'footer' => $this->dbi->getServerCollation(),
);
$column_order['SCHEMA_TABLES'] = array(
'disp_name' => __('Tables'),
'format' => 'number',
'footer' => 0,
);
$column_order['SCHEMA_TABLE_ROWS'] = array(
'disp_name' => __('Rows'),
'format' => 'number',
'footer' => 0,
);
$column_order['SCHEMA_DATA_LENGTH'] = array(
'disp_name' => __('Data'),
'format' => 'byte',
'footer' => 0,
);
$column_order['SCHEMA_INDEX_LENGTH'] = array(
'disp_name' => __('Indexes'),
'format' => 'byte',
'footer' => 0,
);
$column_order['SCHEMA_LENGTH'] = array(
'disp_name' => __('Total'),
'format' => 'byte',
'footer' => 0,
);
$column_order['SCHEMA_DATA_FREE'] = array(
'disp_name' => __('Overhead'),
'format' => 'byte',
'footer' => 0,
);
return $column_order;
}
/**
* Returns the html for Database List
*
* @param array $column_order column order
* @param array $replication_types replication types
*
* @return string
*/
private function _getHtmlForTableBody(array $column_order, array $replication_types)
{
$html = '<tbody>' . "\n";
foreach ($this->_databases as $current) {
$tr_class = ' db-row';
if ($this->dbi->isSystemSchema($current['SCHEMA_NAME'], true)) {
$tr_class .= ' noclick';
}
$generated_html = $this->_buildHtmlForDb(
$current,
$column_order,
$replication_types,
$GLOBALS['replication_info'],
$tr_class
);
$html .= $generated_html;
} // end foreach ($this->_databases as $key => $current)
$html .= '</tbody>';
return $html;
}
/**
* Builds the HTML for one database to display in the list
* of databases from server_databases.php
*
* @param array $current current database
* @param array $column_order column order
* @param array $replication_types replication types
* @param array $replication_info replication info
* @param string $tr_class HTMl class for the row
*
* @return array $column_order, $out
*/
function _buildHtmlForDb(
array $current, array $column_order,
array $replication_types, array $replication_info, $tr_class = ''
) {
$master_replication = $slave_replication = '';
foreach ($replication_types as $type) {
if ($replication_info[$type]['status']) {
$out = '';
$key = array_search(
$current["SCHEMA_NAME"],
$replication_info[$type]['Ignore_DB']
);
if (strlen($key) > 0) {
$out = Util::getIcon(
's_cancel',
__('Not replicated')
);
} else {
$key = array_search(
$current["SCHEMA_NAME"], $replication_info[$type]['Do_DB']
);
if (strlen($key) > 0
|| count($replication_info[$type]['Do_DB']) == 0
) {
// if ($key != null) did not work for index "0"
$out = Util::getIcon(
's_success',
__('Replicated')
);
}
}
if ($type == 'master') {
$master_replication = $out;
} elseif ($type == 'slave') {
$slave_replication = $out;
}
}
}
return Template::get('server/databases/table_row')->render([
'current' => $current,
'tr_class' => $tr_class,
'column_order' => $column_order,
'master_replication_status' => $GLOBALS['replication_info']['master']['status'],
'master_replication' => $master_replication,
'slave_replication_status' => $GLOBALS['replication_info']['slave']['status'],
'slave_replication' => $slave_replication,
'is_superuser' => $GLOBALS['dbi']->isSuperuser(),
'allow_user_drop_database' => $GLOBALS['cfg']['AllowUserDropDatabase'],
'is_system_schema' => $GLOBALS['dbi']->isSystemSchema($current['SCHEMA_NAME'], true),
'default_tab_database' => $GLOBALS['cfg']['DefaultTabDatabase'],
]);
}
}

View File

@@ -0,0 +1,100 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\Server\ServerEnginesController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\Controller;
use PhpMyAdmin\Server\Common;
use PhpMyAdmin\StorageEngine;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
/**
* Handles viewing storage engine details
*
* @package PhpMyAdmin\Controllers
*/
class ServerEnginesController extends Controller
{
/**
* Index action
*
* @return void
*/
public function indexAction()
{
/**
* Does the common work
*/
require 'libraries/server_common.inc.php';
/**
* Displays the sub-page heading
*/
$this->response->addHTML(
Template::get('server/sub_page_header')->render([
'type' => 'engines',
])
);
/**
* Did the user request information about a certain storage engine?
*/
if (empty($_REQUEST['engine'])
|| ! StorageEngine::isValid($_REQUEST['engine'])
) {
$this->response->addHTML($this->_getHtmlForAllServerEngines());
} else {
$engine = StorageEngine::getEngine($_REQUEST['engine']);
$this->response->addHTML($this->_getHtmlForServerEngine($engine));
}
}
/**
* Return HTML with all Storage Engine information
*
* @return string
*/
private function _getHtmlForAllServerEngines()
{
return Template::get('server/engines/engines')->render(
array('engines' => StorageEngine::getStorageEngines())
);
}
/**
* Return HTML for a given Storage Engine
*
* @param StorageEngine $engine storage engine
*
* @return string
*/
private function _getHtmlForServerEngine(StorageEngine $engine)
{
$page = isset($_REQUEST['page']) ? $_REQUEST['page'] : '';
$pageOutput = ! empty($page) ? $engine->getPage($page) : '';
/**
* Displays details about a given Storage Engine
*/
return Template::get('server/engines/engine')->render(
array(
'title' => $engine->getTitle(),
'help_page' => $engine->getMysqlHelpPage(),
'comment' => $engine->getComment(),
'info_pages' => $engine->getInfoPages(),
'support' => $engine->getSupportInformationMessage(),
'variables' => $engine->getHtmlVariables(),
'page_output' => $pageOutput,
'page' => $page,
'engine' => $_REQUEST['engine'],
)
);
}
}

View File

@@ -0,0 +1,111 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\Server\ServerPluginsController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\Controller;
use PhpMyAdmin\Server\Common;
use PhpMyAdmin\Template;
/**
* Handles viewing server plugin details
*
* @package PhpMyAdmin\Controllers
*/
class ServerPluginsController extends Controller
{
/**
* @var array plugin details
*/
protected $plugins;
/**
* Constructs ServerPluginsController
*/
public function __construct($response, $dbi)
{
parent::__construct($response, $dbi);
$this->_setServerPlugins();
}
/**
* Index action
*
* @return void
*/
public function indexAction()
{
include 'libraries/server_common.inc.php';
$header = $this->response->getHeader();
$scripts = $header->getScripts();
$scripts->addFile('vendor/jquery/jquery.tablesorter.js');
$scripts->addFile('server_plugins.js');
/**
* Displays the page
*/
$this->response->addHTML(
Template::get('server/sub_page_header')->render([
'type' => 'plugins',
])
);
$this->response->addHTML($this->_getPluginsHtml());
}
/**
* Sets details about server plugins
*
* @return void
*/
private function _setServerPlugins()
{
$sql = "SELECT plugin_name,
plugin_type,
(plugin_status = 'ACTIVE') AS is_active,
plugin_type_version,
plugin_author,
plugin_description,
plugin_license
FROM information_schema.plugins
ORDER BY plugin_type, plugin_name";
$res = $this->dbi->query($sql);
$this->plugins = array();
while ($row = $this->dbi->fetchAssoc($res)) {
$this->plugins[$row['plugin_type']][] = $row;
}
$this->dbi->freeResult($res);
ksort($this->plugins);
}
/**
* Returns the html for plugin Tab.
*
* @return string
*/
private function _getPluginsHtml()
{
$html = '<div id="plugins_plugins">';
$html .= Template::get('server/plugins/section_links')
->render(array('plugins' => $this->plugins));
foreach ($this->plugins as $plugin_type => $plugin_list) {
$html .= Template::get('server/plugins/section')
->render(
array(
'plugin_type' => $plugin_type,
'plugin_list' => $plugin_list,
)
);
}
$html .= '</div>';
return $html;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,246 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\Table\TableChartController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers\Table;
use PhpMyAdmin\Controllers\TableController;
use PhpMyAdmin\Message;
use PhpMyAdmin\Response;
use PhpMyAdmin\SqlParser\Components\Limit;
use PhpMyAdmin\SqlParser\Parser;
use PhpMyAdmin\Table;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
/**
* Handles table related logic
*
* @package PhpMyAdmin\Controllers
*/
class TableChartController extends TableController
{
/**
* @var string $sql_query
*/
protected $sql_query;
/**
* @var string $url_query
*/
protected $url_query;
/**
* @var array $cfg
*/
protected $cfg;
/**
* Constructor
*
* @param string $sql_query Query
* @param string $url_query Query URL
* @param array $cfg Configuration
*/
public function __construct(
$response,
$dbi,
$db,
$table,
$sql_query,
$url_query,
array $cfg
) {
parent::__construct($response, $dbi, $db, $table);
$this->sql_query = $sql_query;
$this->url_query = $url_query;
$this->cfg = $cfg;
}
/**
* Execute the query and return the result
*
* @return void
*/
public function indexAction()
{
$response = Response::getInstance();
if ($response->isAjax()
&& isset($_REQUEST['pos'])
&& isset($_REQUEST['session_max_rows'])
) {
$this->ajaxAction();
return;
}
// Throw error if no sql query is set
if (!isset($this->sql_query) || $this->sql_query == '') {
$this->response->setRequestStatus(false);
$this->response->addHTML(
Message::error(__('No SQL query was set to fetch data.'))
);
return;
}
$this->response->getHeader()->getScripts()->addFiles(
array(
'chart.js',
'tbl_chart.js',
'vendor/jqplot/jquery.jqplot.js',
'vendor/jqplot/plugins/jqplot.barRenderer.js',
'vendor/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js',
'vendor/jqplot/plugins/jqplot.canvasTextRenderer.js',
'vendor/jqplot/plugins/jqplot.categoryAxisRenderer.js',
'vendor/jqplot/plugins/jqplot.dateAxisRenderer.js',
'vendor/jqplot/plugins/jqplot.pointLabels.js',
'vendor/jqplot/plugins/jqplot.pieRenderer.js',
'vendor/jqplot/plugins/jqplot.enhancedPieLegendRenderer.js',
'vendor/jqplot/plugins/jqplot.highlighter.js'
)
);
/**
* Extract values for common work
* @todo Extract common files
*/
$db = &$this->db;
$table = &$this->table;
$url_params = array();
/**
* Runs common work
*/
if (strlen($this->table) > 0) {
$url_params['goto'] = Util::getScriptNameForOption(
$this->cfg['DefaultTabTable'], 'table'
);
$url_params['back'] = 'tbl_sql.php';
include 'libraries/tbl_common.inc.php';
$GLOBALS['dbi']->selectDb($GLOBALS['db']);
} elseif (strlen($this->db) > 0) {
$url_params['goto'] = Util::getScriptNameForOption(
$this->cfg['DefaultTabDatabase'], 'database'
);
$url_params['back'] = 'sql.php';
include 'libraries/db_common.inc.php';
} else {
$url_params['goto'] = Util::getScriptNameForOption(
$this->cfg['DefaultTabServer'], 'server'
);
$url_params['back'] = 'sql.php';
include 'libraries/server_common.inc.php';
}
$data = array();
$result = $this->dbi->tryQuery($this->sql_query);
$fields_meta = $this->dbi->getFieldsMeta($result);
while ($row = $this->dbi->fetchAssoc($result)) {
$data[] = $row;
}
$keys = array_keys($data[0]);
$numeric_types = array('int', 'real');
$numeric_column_count = 0;
foreach ($keys as $idx => $key) {
if (in_array($fields_meta[$idx]->type, $numeric_types)) {
$numeric_column_count++;
}
}
if ($numeric_column_count == 0) {
$this->response->setRequestStatus(false);
$this->response->addJSON(
'message',
__('No numeric columns present in the table to plot.')
);
return;
}
$url_params['db'] = $this->db;
$url_params['reload'] = 1;
/**
* Displays the page
*/
$this->response->addHTML(
Template::get('table/chart/tbl_chart')->render(
array(
'url_query' => $this->url_query,
'url_params' => $url_params,
'keys' => $keys,
'fields_meta' => $fields_meta,
'numeric_types' => $numeric_types,
'numeric_column_count' => $numeric_column_count,
'sql_query' => $this->sql_query
)
)
);
}
/**
* Handle ajax request
*
* @return void
*/
public function ajaxAction()
{
/**
* Extract values for common work
* @todo Extract common files
*/
$db = &$this->db;
$table = &$this->table;
if (strlen($this->table) > 0 && strlen($this->db) > 0) {
include './libraries/tbl_common.inc.php';
}
$parser = new Parser($this->sql_query);
$statement = $parser->statements[0];
if (empty($statement->limit)) {
$statement->limit = new Limit(
$_REQUEST['session_max_rows'], $_REQUEST['pos']
);
} else {
$start = $statement->limit->offset + $_REQUEST['pos'];
$rows = min(
$_REQUEST['session_max_rows'],
$statement->limit->rowCount - $_REQUEST['pos']
);
$statement->limit = new Limit($rows, $start);
}
$sql_with_limit = $statement->build();
$data = array();
$result = $this->dbi->tryQuery($sql_with_limit);
while ($row = $this->dbi->fetchAssoc($result)) {
$data[] = $row;
}
if (empty($data)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No data to display'));
return;
}
$sanitized_data = array();
foreach ($data as $data_row_number => $data_row) {
$tmp_row = array();
foreach ($data_row as $data_column => $data_value) {
$tmp_row[htmlspecialchars($data_column)] = htmlspecialchars(
$data_value
);
}
$sanitized_data[] = $tmp_row;
}
$this->response->setRequestStatus(true);
$this->response->addJSON('message', null);
$this->response->addJSON('chartData', json_encode($sanitized_data));
}
}

View File

@@ -0,0 +1,222 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\Table\TableIndexesController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers\Table;
use PhpMyAdmin\Controllers\TableController;
use PhpMyAdmin\Core;
use PhpMyAdmin\Gis\GisVisualization;
use PhpMyAdmin\Message;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
require_once 'libraries/common.inc.php';
require_once 'libraries/db_common.inc.php';
/**
* Class TableGisVisualizationController
*
* @package PhpMyAdmin\Controllers
*/
class TableGisVisualizationController extends TableController
{
/**
* @var array $url_params
*/
protected $url_params;
/**
* @var string $sql_query
*/
protected $sql_query;
/**
* @var array $visualizationSettings
*/
protected $visualizationSettings;
/**
* @var \PhpMyAdmin\Gis\GisVisualization $visualization
*/
protected $visualization;
/**
* Constructor
*
* @param string $sql_query SQL query for retrieving GIS data
* @param array $url_params array of URL parameters
* @param string $goto goto script
* @param string $back back script
* @param array $visualizationSettings visualization settings
*/
public function __construct(
$response,
$dbi,
$db,
$table,
$sql_query,
array $url_params,
$goto,
$back,
array $visualizationSettings
) {
parent::__construct($response, $dbi, $db, $table);
$this->sql_query = $sql_query;
$this->url_params = $url_params;
$this->url_params['goto'] = $goto;
$this->url_params['back'] = $back;
$this->visualizationSettings = $visualizationSettings;
}
/**
* Save to file
*
* @return void
*/
public function saveToFileAction()
{
$this->response->disable();
$file_name = $this->visualizationSettings['spatialColumn'];
$save_format = $_GET['fileFormat'];
$this->visualization->toFile($file_name, $save_format);
}
/**
* Index
*
* @return void
*/
public function indexAction()
{
// Throw error if no sql query is set
if (! isset($this->sql_query) || $this->sql_query == '') {
$this->response->setRequestStatus(false);
$this->response->addHTML(
Message::error(__('No SQL query was set to fetch data.'))
);
return;
}
// Execute the query and return the result
$result = $this->dbi->tryQuery($this->sql_query);
// Get the meta data of results
$meta = $this->dbi->getFieldsMeta($result);
// Find the candidate fields for label column and spatial column
$labelCandidates = array();
$spatialCandidates = array();
foreach ($meta as $column_meta) {
if ($column_meta->type == 'geometry') {
$spatialCandidates[] = $column_meta->name;
} else {
$labelCandidates[] = $column_meta->name;
}
}
// Get settings if any posted
if (Core::isValid($_POST['visualizationSettings'], 'array')) {
$this->visualizationSettings = $_POST['visualizationSettings'];
}
// Check mysql version
$this->visualizationSettings['mysqlVersion'] = $this->dbi->getVersion();
if (!isset($this->visualizationSettings['labelColumn'])
&& isset($labelCandidates[0])
) {
$this->visualizationSettings['labelColumn'] = '';
}
// If spatial column is not set, use first geometric column as spatial column
if (! isset($this->visualizationSettings['spatialColumn'])) {
$this->visualizationSettings['spatialColumn'] = $spatialCandidates[0];
}
// Convert geometric columns from bytes to text.
$pos = isset($_GET['pos']) ? $_GET['pos']
: $_SESSION['tmpval']['pos'];
if (isset($_GET['session_max_rows'])) {
$rows = $_GET['session_max_rows'];
} else {
if ($_SESSION['tmpval']['max_rows'] != 'all') {
$rows = $_SESSION['tmpval']['max_rows'];
} else {
$rows = $GLOBALS['cfg']['MaxRows'];
}
}
$this->visualization = GisVisualization::get(
$this->sql_query,
$this->visualizationSettings,
$rows,
$pos
);
if (isset($_GET['saveToFile'])) {
$this->saveToFileAction();
return;
}
$this->response->getHeader()->getScripts()->addFiles(
array(
'vendor/openlayers/OpenLayers.js',
'vendor/jquery/jquery.svg.js',
'tbl_gis_visualization.js',
)
);
// If all the rows contain SRID, use OpenStreetMaps on the initial loading.
if (! isset($_POST['displayVisualization'])) {
if ($this->visualization->hasSrid()) {
$this->visualizationSettings['choice'] = 'useBaseLayer';
} else {
unset($this->visualizationSettings['choice']);
}
}
$this->visualization->setUserSpecifiedSettings($this->visualizationSettings);
if ($this->visualizationSettings != null) {
foreach ($this->visualization->getSettings() as $setting => $val) {
if (! isset($this->visualizationSettings[$setting])) {
$this->visualizationSettings[$setting] = $val;
}
}
}
/**
* Displays the page
*/
$this->url_params['sql_query'] = $this->sql_query;
$downloadUrl = 'tbl_gis_visualization.php' . Url::getCommon(
array_merge(
$this->url_params,
array(
'sql_signature' => Core::signSqlQuery($this->sql_query),
'saveToFile' => true,
'session_max_rows' => $rows,
'pos' => $pos
)
)
);
$html = Template::get('table/gis_visualization/gis_visualization')->render(
array(
'url_params' => $this->url_params,
'download_url' => $downloadUrl,
'label_candidates' => $labelCandidates,
'spatial_candidates' => $spatialCandidates,
'visualization_settings' => $this->visualizationSettings,
'sql_query' => $this->sql_query,
'visualization' => $this->visualization->toImage('svg'),
'draw_ol' => $this->visualization->asOl(),
'pma_theme_image' => $GLOBALS['pmaThemeImage']
)
);
$this->response->addHTML($html);
}
}

View File

@@ -0,0 +1,177 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\Table\TableIndexesController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers\Table;
use PhpMyAdmin\Controllers\TableController;
use PhpMyAdmin\Index;
use PhpMyAdmin\Message;
use PhpMyAdmin\Response;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
/**
* Class TableIndexesController
*
* @package PhpMyAdmin\Controllers
*/
class TableIndexesController extends TableController
{
/**
* @var Index $index
*/
protected $index;
/**
* Constructor
*
* @param Index $index Index
*/
public function __construct(
$response,
$dbi,
$db,
$table,
$index
) {
parent::__construct($response, $dbi, $db, $table);
$this->index = $index;
}
/**
* Index
*
* @return void
*/
public function indexAction()
{
if (isset($_POST['do_save_data'])) {
$this->doSaveDataAction();
return;
} // end builds the new index
$this->displayFormAction();
}
/**
* Display the form to edit/create an index
*
* @return void
*/
public function displayFormAction()
{
$GLOBALS['dbi']->selectDb($GLOBALS['db']);
$add_fields = 0;
if (isset($_POST['index']) && is_array($_POST['index'])) {
// coming already from form
if (isset($_POST['index']['columns']['names'])) {
$add_fields = count($_POST['index']['columns']['names'])
- $this->index->getColumnCount();
}
if (isset($_POST['add_fields'])) {
$add_fields += $_POST['added_fields'];
}
} elseif (isset($_POST['create_index'])) {
$add_fields = $_POST['added_fields'];
} // end preparing form values
// Get fields and stores their name/type
if (isset($_POST['create_edit_table'])) {
$fields = json_decode($_POST['columns'], true);
$index_params = array(
'Non_unique' => ($_POST['index']['Index_choice'] == 'UNIQUE')
? '0' : '1',
);
$this->index->set($index_params);
$add_fields = count($fields);
} else {
$fields = $this->dbi->getTable($this->db, $this->table)
->getNameAndTypeOfTheColumns();
}
$form_params = array(
'db' => $this->db,
'table' => $this->table,
);
if (isset($_POST['create_index'])) {
$form_params['create_index'] = 1;
} elseif (isset($_POST['old_index'])) {
$form_params['old_index'] = $_POST['old_index'];
} elseif (isset($_POST['index'])) {
$form_params['old_index'] = $_POST['index'];
}
$this->response->getHeader()->getScripts()->addFile('indexes.js');
$this->response->addHTML(
Template::get('table/index_form')->render(
array(
'fields' => $fields,
'index' => $this->index,
'form_params' => $form_params,
'add_fields' => $add_fields,
'create_edit_table' => isset($_POST['create_edit_table'])
)
)
);
}
/**
* Process the data from the edit/create index form,
* run the query to build the new index
* and moves back to "tbl_sql.php"
*
* @return void
*/
public function doSaveDataAction()
{
$error = false;
$sql_query = $this->dbi->getTable($this->db, $this->table)
->getSqlQueryForIndexCreateOrEdit($this->index, $error);
// If there is a request for SQL previewing.
if (isset($_POST['preview_sql'])) {
$this->response->addJSON(
'sql_data',
Template::get('preview_sql')
->render(
array(
'query_data' => $sql_query
)
)
);
} elseif (!$error) {
$this->dbi->query($sql_query);
$response = Response::getInstance();
if ($response->isAjax()) {
$message = Message::success(
__('Table %1$s has been altered successfully.')
);
$message->addParam($this->table);
$this->response->addJSON(
'message', Util::getMessage($message, $sql_query, 'success')
);
$this->response->addJSON(
'index_table',
Index::getHtmlForIndexes(
$this->table, $this->db
)
);
} else {
include 'tbl_structure.php';
}
} else {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $error);
}
}
}

View File

@@ -0,0 +1,390 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\Table\TableRelationController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers\Table;
use PhpMyAdmin\Controllers\TableController;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Index;
use PhpMyAdmin\Relation;
use PhpMyAdmin\Table;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
/**
* Handles table relation logic
*
* @package PhpMyAdmin\Controllers
*/
class TableRelationController extends TableController
{
/**
* @var array $options_array
*/
protected $options_array;
/**
* @var array $cfgRelation
*/
protected $cfgRelation;
/**
* @var array $existrel
*/
protected $existrel;
/**
* @var string $tbl_storage_engine
*/
protected $tbl_storage_engine;
/**
* @var array $existrel_foreign
*/
protected $existrel_foreign;
/**
* @var Table $udp_query
*/
protected $upd_query;
/**
* @var Relation $relation
*/
private $relation;
/**
* Constructor
*
* @param array|null $options_array Options
* @param array|null $cfgRelation Config relation
* @param string $tbl_storage_engine Table storage engine
* @param array|null $existrel Relations
* @param array|null $existrel_foreign External relations
* @param string $upd_query Update query
*/
public function __construct(
$response,
$dbi,
$db,
$table,
$options_array,
$cfgRelation,
$tbl_storage_engine,
$existrel,
$existrel_foreign,
$upd_query
) {
parent::__construct($response, $dbi, $db, $table);
$this->options_array = $options_array;
$this->cfgRelation = $cfgRelation;
$this->tbl_storage_engine = $tbl_storage_engine;
$this->existrel = $existrel;
$this->existrel_foreign = $existrel_foreign;
$this->upd_query = $upd_query;
$this->relation = new Relation();
}
/**
* Index
*
* @return void
*/
public function indexAction()
{
// Send table of column names to populate corresponding dropdowns depending
// on the current selection
if (isset($_POST['getDropdownValues'])
&& $_POST['getDropdownValues'] === 'true'
) {
// if both db and table are selected
if (isset($_POST['foreignTable'])) {
$this->getDropdownValueForTableAction();
} else { // if only the db is selected
$this->getDropdownValueForDbAction();
}
return;
}
$this->response->getHeader()->getScripts()->addFiles(
array(
'tbl_relation.js',
'indexes.js'
)
);
// Set the database
$this->dbi->selectDb($this->db);
// updates for Internal relations
if (isset($_POST['destination_db']) && $this->cfgRelation['relwork']) {
$this->updateForInternalRelationAction();
}
// updates for foreign keys
$this->updateForForeignKeysAction();
// Updates for display field
if ($this->cfgRelation['displaywork'] && isset($_POST['display_field'])) {
$this->updateForDisplayField();
}
// If we did an update, refresh our data
if (isset($_POST['destination_db']) && $this->cfgRelation['relwork']) {
$this->existrel = $this->relation->getForeigners(
$this->db, $this->table, '', 'internal'
);
}
if (isset($_POST['destination_foreign_db'])
&& Util::isForeignKeySupported($this->tbl_storage_engine)
) {
$this->existrel_foreign = $this->relation->getForeigners(
$this->db, $this->table, '', 'foreign'
);
}
// display secondary level tabs if necessary
$engine = $this->dbi->getTable($this->db, $this->table)->getStorageEngine();
$this->response->addHTML(
Template::get('table/secondary_tabs')->render(
array(
'url_params' => array(
'db' => $GLOBALS['db'],
'table' => $GLOBALS['table']
),
'is_foreign_key_supported' => Util::isForeignKeySupported($engine),
'cfg_relation' => $this->relation->getRelationsParam(),
)
)
);
$this->response->addHTML('<div id="structure_content">');
/**
* Dialog
*/
// Now find out the columns of our $table
// need to use DatabaseInterface::QUERY_STORE with $this->dbi->numRows()
// in mysqli
$columns = $this->dbi->getColumns($this->db, $this->table);
$column_array = array();
$column_array[''] = '';
foreach ($columns as $column) {
if (strtoupper($this->tbl_storage_engine) == 'INNODB'
|| ! empty($column['Key'])
) {
$column_array[$column['Field']] = $column['Field'];
}
}
if ($GLOBALS['cfg']['NaturalOrder']) {
uksort($column_array, 'strnatcasecmp');
}
// common form
$this->response->addHTML(
Template::get('table/relation/common_form')->render([
'db' => $this->db,
'table' => $this->table,
'cfg_relation' => $this->cfgRelation,
'tbl_storage_engine' => $this->tbl_storage_engine,
'existrel' => isset($this->existrel) ? $this->existrel : array(),
'existrel_foreign' => is_array($this->existrel_foreign) && array_key_exists('foreign_keys_data', $this->existrel_foreign)
? $this->existrel_foreign['foreign_keys_data'] : array(),
'options_array' => $this->options_array,
'column_array' => $column_array,
'save_row' => array_values($columns),
'url_params' => $GLOBALS['url_params'],
'databases' => $GLOBALS['dblist']->databases,
'dbi' => $GLOBALS['dbi'],
])
);
if (Util::isForeignKeySupported($this->tbl_storage_engine)) {
$this->response->addHTML(Index::getHtmlForDisplayIndexes());
}
$this->response->addHTML('</div>');
}
/**
* Update for display field
*
* @return void
*/
public function updateForDisplayField()
{
if ($this->upd_query->updateDisplayField(
$_POST['display_field'], $this->cfgRelation
)
) {
$this->response->addHTML(
Util::getMessage(
__('Display column was successfully updated.'),
'', 'success'
)
);
}
}
/**
* Update for FK
*
* @return void
*/
public function updateForForeignKeysAction()
{
$multi_edit_columns_name = isset($_POST['foreign_key_fields_name'])
? $_POST['foreign_key_fields_name']
: null;
$preview_sql_data = '';
$seen_error = false;
// (for now, one index name only; we keep the definitions if the
// foreign db is not the same)
if (isset($_POST['destination_foreign_db'])
&& isset($_POST['destination_foreign_table'])
&& isset($_POST['destination_foreign_column'])) {
list($html, $preview_sql_data, $display_query, $seen_error)
= $this->upd_query->updateForeignKeys(
$_POST['destination_foreign_db'],
$multi_edit_columns_name, $_POST['destination_foreign_table'],
$_POST['destination_foreign_column'], $this->options_array,
$this->table,
is_array($this->existrel_foreign) && array_key_exists('foreign_keys_data', $this->existrel_foreign)
? $this->existrel_foreign['foreign_keys_data']
: []
);
$this->response->addHTML($html);
}
// If there is a request for SQL previewing.
if (isset($_POST['preview_sql'])) {
Core::previewSQL($preview_sql_data);
}
if (!empty($display_query) && !$seen_error) {
$GLOBALS['display_query'] = $display_query;
$this->response->addHTML(
Util::getMessage(
__('Your SQL query has been executed successfully.'),
null, 'success'
)
);
}
}
/**
* Update for internal relation
*
* @return void
*/
public function updateForInternalRelationAction()
{
$multi_edit_columns_name = isset($_POST['fields_name'])
? $_POST['fields_name']
: null;
if ($this->upd_query->updateInternalRelations(
$multi_edit_columns_name,
$_POST['destination_db'],
$_POST['destination_table'],
$_POST['destination_column'],
$this->cfgRelation,
isset($this->existrel) ? $this->existrel : null
)
) {
$this->response->addHTML(
Util::getMessage(
__('Internal relationships were successfully updated.'),
'', 'success'
)
);
}
}
/**
* Send table columns for foreign table dropdown
*
* @return void
*
*/
public function getDropdownValueForTableAction()
{
$foreignTable = $_POST['foreignTable'];
$table_obj = $this->dbi->getTable($_POST['foreignDb'], $foreignTable);
// Since views do not have keys defined on them provide the full list of
// columns
if ($table_obj->isView()) {
$columnList = $table_obj->getColumns(false, false);
} else {
$columnList = $table_obj->getIndexedColumns(false, false);
}
$columns = array();
foreach ($columnList as $column) {
$columns[] = htmlspecialchars($column);
}
if ($GLOBALS['cfg']['NaturalOrder']) {
usort($columns, 'strnatcasecmp');
}
$this->response->addJSON('columns', $columns);
// @todo should be: $server->db($db)->table($table)->primary()
$primary = Index::getPrimary($foreignTable, $_POST['foreignDb']);
if (false === $primary) {
return;
}
$this->response->addJSON('primary', array_keys($primary->getColumns()));
}
/**
* Send database selection values for dropdown
*
* @return void
*
*/
public function getDropdownValueForDbAction()
{
$tables = array();
$foreign = isset($_POST['foreign']) && $_POST['foreign'] === 'true';
if ($foreign) {
$query = 'SHOW TABLE STATUS FROM '
. Util::backquote($_POST['foreignDb']);
$tables_rs = $this->dbi->query(
$query,
DatabaseInterface::CONNECT_USER,
DatabaseInterface::QUERY_STORE
);
while ($row = $this->dbi->fetchArray($tables_rs)) {
if (isset($row['Engine'])
&& mb_strtoupper($row['Engine']) == $this->tbl_storage_engine
) {
$tables[] = htmlspecialchars($row['Name']);
}
}
} else {
$query = 'SHOW TABLES FROM '
. Util::backquote($_POST['foreignDb']);
$tables_rs = $this->dbi->query(
$query,
DatabaseInterface::CONNECT_USER,
DatabaseInterface::QUERY_STORE
);
while ($row = $this->dbi->fetchArray($tables_rs)) {
$tables[] = htmlspecialchars($row[0]);
}
}
if ($GLOBALS['cfg']['NaturalOrder']) {
usort($tables, 'strnatcasecmp');
}
$this->response->addJSON('tables', $tables);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Controllers\TableController
*
* @package PhpMyAdmin\Controllers
*/
namespace PhpMyAdmin\Controllers;
/**
* Handles table related logic
*
* @package PhpMyAdmin\Controllers
*/
abstract class TableController extends Controller
{
/**
* @var string $db
*/
protected $db;
/**
* @var string $table
*/
protected $table;
/**
* Constructor
*/
public function __construct(
$response,
$dbi,
$db,
$table
) {
parent::__construct($response, $dbi);
$this->db = $db;
$this->table = $table;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,556 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\CreateAddField class
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Index;
use PhpMyAdmin\Table;
use PhpMyAdmin\Util;
/**
* Set of functions for tbl_create.php and tbl_addfield.php
*
* @package PhpMyAdmin
*/
class CreateAddField
{
/**
* @var DatabaseInterface
*/
private $dbi;
/**
* Constructor
*
* @param DatabaseInterface $dbi DatabaseInterface interface
*/
public function __construct(DatabaseInterface $dbi)
{
$this->dbi = $dbi;
}
/**
* Transforms the radio button field_key into 4 arrays
*
* @return array An array of arrays which represents column keys for each index type
*/
private function getIndexedColumns()
{
$fieldCount = count($_POST['field_name']);
$fieldPrimary = json_decode($_POST['primary_indexes'], true);
$fieldIndex = json_decode($_POST['indexes'], true);
$fieldUnique = json_decode($_POST['unique_indexes'], true);
$fieldFullText = json_decode($_POST['fulltext_indexes'], true);
$fieldSpatial = json_decode($_POST['spatial_indexes'], true);
return [
$fieldCount,
$fieldPrimary,
$fieldIndex,
$fieldUnique,
$fieldFullText,
$fieldSpatial,
];
}
/**
* Initiate the column creation statement according to the table creation or
* add columns to a existing table
*
* @param int $fieldCount number of columns
* @param boolean $isCreateTable true if requirement is to get the statement
* for table creation
*
* @return array $definitions An array of initial sql statements
* according to the request
*/
private function buildColumnCreationStatement(
$fieldCount,
$isCreateTable = true
) {
$definitions = [];
$previousField = -1;
for ($i = 0; $i < $fieldCount; ++$i) {
// '0' is also empty for php :-(
if (strlen($_POST['field_name'][$i]) === 0) {
continue;
}
$definition = $this->getStatementPrefix($isCreateTable) .
Table::generateFieldSpec(
trim($_POST['field_name'][$i]),
$_POST['field_type'][$i],
$_POST['field_length'][$i],
$_POST['field_attribute'][$i],
isset($_POST['field_collation'][$i])
? $_POST['field_collation'][$i]
: '',
isset($_POST['field_null'][$i])
? $_POST['field_null'][$i]
: 'NO',
$_POST['field_default_type'][$i],
$_POST['field_default_value'][$i],
isset($_POST['field_extra'][$i])
? $_POST['field_extra'][$i]
: false,
isset($_POST['field_comments'][$i])
? $_POST['field_comments'][$i]
: '',
isset($_POST['field_virtuality'][$i])
? $_POST['field_virtuality'][$i]
: '',
isset($_POST['field_expression'][$i])
? $_POST['field_expression'][$i]
: ''
);
$definition .= $this->setColumnCreationStatementSuffix($i, $previousField, $isCreateTable);
$previousField = $i;
$definitions[] = $definition;
} // end for
return $definitions;
}
/**
* Set column creation suffix according to requested position of the new column
*
* @param int $currentFieldNumber current column number
* @param int $previousField previous field for ALTER statement
* @param boolean $isCreateTable true if requirement is to get the statement
* for table creation
*
* @return string $sqlSuffix suffix
*/
private function setColumnCreationStatementSuffix(
$currentFieldNumber,
$previousField,
$isCreateTable = true
) {
// no suffix is needed if request is a table creation
$sqlSuffix = ' ';
if ($isCreateTable) {
return $sqlSuffix;
}
if ((string) $_POST['field_where'] === 'last') {
return $sqlSuffix;
}
// Only the first field can be added somewhere other than at the end
if ($previousField == -1) {
if ((string) $_POST['field_where'] === 'first') {
$sqlSuffix .= ' FIRST';
} else if (! empty($_POST['after_field'])) {
$sqlSuffix .= ' AFTER '
. Util::backquote($_POST['after_field']);
}
} else {
$sqlSuffix .= ' AFTER '
. Util::backquote(
$_POST['field_name'][$previousField]
);
}
return $sqlSuffix;
}
/**
* Create relevant index statements
*
* @param array $index an array of index columns
* @param string $indexChoice index choice that which represents
* the index type of $indexed_fields
* @param boolean $isCreateTable true if requirement is to get the statement
* for table creation
*
* @return array an array of sql statements for indexes
*/
private function buildIndexStatements(
array $index,
$indexChoice,
$isCreateTable = true
) {
$statement = [];
if (!count($index)) {
return $statement;
}
$sqlQuery = $this->getStatementPrefix($isCreateTable)
. ' ' . $indexChoice;
if (! empty($index['Key_name']) && $index['Key_name'] != 'PRIMARY') {
$sqlQuery .= ' ' . Util::backquote($index['Key_name']);
}
$indexFields = [];
foreach ($index['columns'] as $key => $column) {
$indexFields[$key] = Util::backquote(
$_POST['field_name'][$column['col_index']]
);
if ($column['size']) {
$indexFields[$key] .= '(' . $column['size'] . ')';
}
}
$sqlQuery .= ' (' . implode(', ', $indexFields) . ')';
$keyBlockSizes = $index['Key_block_size'];
if (! empty($keyBlockSizes)) {
$sqlQuery .= " KEY_BLOCK_SIZE = "
. $this->dbi->escapeString($keyBlockSizes);
}
// specifying index type is allowed only for primary, unique and index only
$type = $index['Index_type'];
if ($index['Index_choice'] != 'SPATIAL'
&& $index['Index_choice'] != 'FULLTEXT'
&& in_array($type, Index::getIndexTypes())
) {
$sqlQuery .= ' USING ' . $type;
}
$parser = $index['Parser'];
if ($index['Index_choice'] == 'FULLTEXT' && ! empty($parser)) {
$sqlQuery .= " WITH PARSER " . $this->dbi->escapeString($parser);
}
$comment = $index['Index_comment'];
if (! empty($comment)) {
$sqlQuery .= " COMMENT '" . $this->dbi->escapeString($comment)
. "'";
}
$statement[] = $sqlQuery;
return $statement;
}
/**
* Statement prefix for the buildColumnCreationStatement()
*
* @param boolean $isCreateTable true if requirement is to get the statement
* for table creation
*
* @return string $sqlPrefix prefix
*/
private function getStatementPrefix($isCreateTable = true)
{
$sqlPrefix = " ";
if (! $isCreateTable) {
$sqlPrefix = ' ADD ';
}
return $sqlPrefix;
}
/**
* Merge index definitions for one type of index
*
* @param array $definitions the index definitions to merge to
* @param boolean $isCreateTable true if requirement is to get the statement
* for table creation
* @param array $indexedColumns the columns for one type of index
* @param string $indexKeyword the index keyword to use in the definition
*
* @return array $index_definitions
*/
private function mergeIndexStatements(
array $definitions,
$isCreateTable,
array $indexedColumns,
$indexKeyword
) {
foreach ($indexedColumns as $index) {
$statements = $this->buildIndexStatements(
$index,
" " . $indexKeyword . " ",
$isCreateTable
);
$definitions = array_merge($definitions, $statements);
}
return $definitions;
}
/**
* Returns sql statement according to the column and index specifications as
* requested
*
* @param boolean $isCreateTable true if requirement is to get the statement
* for table creation
*
* @return string sql statement
*/
private function getColumnCreationStatements($isCreateTable = true)
{
$sqlStatement = "";
list(
$fieldCount,
$fieldPrimary,
$fieldIndex,
$fieldUnique,
$fieldFullText,
$fieldSpatial
) = $this->getIndexedColumns();
$definitions = $this->buildColumnCreationStatement(
$fieldCount,
$isCreateTable
);
// Builds the PRIMARY KEY statements
$primaryKeyStatements = $this->buildIndexStatements(
isset($fieldPrimary[0]) ? $fieldPrimary[0] : [],
" PRIMARY KEY ",
$isCreateTable
);
$definitions = array_merge($definitions, $primaryKeyStatements);
// Builds the INDEX statements
$definitions = $this->mergeIndexStatements(
$definitions,
$isCreateTable,
$fieldIndex,
"INDEX"
);
// Builds the UNIQUE statements
$definitions = $this->mergeIndexStatements(
$definitions,
$isCreateTable,
$fieldUnique,
"UNIQUE"
);
// Builds the FULLTEXT statements
$definitions = $this->mergeIndexStatements(
$definitions,
$isCreateTable,
$fieldFullText,
"FULLTEXT"
);
// Builds the SPATIAL statements
$definitions = $this->mergeIndexStatements(
$definitions,
$isCreateTable,
$fieldSpatial,
"SPATIAL"
);
if (count($definitions)) {
$sqlStatement = implode(', ', $definitions);
}
$sqlStatement = preg_replace('@, $@', '', $sqlStatement);
return $sqlStatement;
}
/**
* Returns the partitioning clause
*
* @return string partitioning clause
*/
public function getPartitionsDefinition()
{
$sqlQuery = "";
if (! empty($_POST['partition_by'])
&& ! empty($_POST['partition_expr'])
&& ! empty($_POST['partition_count'])
&& $_POST['partition_count'] > 1
) {
$sqlQuery .= " PARTITION BY " . $_POST['partition_by']
. " (" . $_POST['partition_expr'] . ")"
. " PARTITIONS " . $_POST['partition_count'];
}
if (! empty($_POST['subpartition_by'])
&& ! empty($_POST['subpartition_expr'])
&& ! empty($_POST['subpartition_count'])
&& $_POST['subpartition_count'] > 1
) {
$sqlQuery .= " SUBPARTITION BY " . $_POST['subpartition_by']
. " (" . $_POST['subpartition_expr'] . ")"
. " SUBPARTITIONS " . $_POST['subpartition_count'];
}
if (! empty($_POST['partitions'])) {
$i = 0;
$partitions = [];
foreach ($_POST['partitions'] as $partition) {
$partitions[] = $this->getPartitionDefinition($partition);
$i++;
}
$sqlQuery .= " (" . implode(", ", $partitions) . ")";
}
return $sqlQuery;
}
/**
* Returns the definition of a partition/subpartition
*
* @param array $partition array of partition/subpartition detiails
* @param boolean $isSubPartition whether a subpartition
*
* @return string partition/subpartition definition
*/
private function getPartitionDefinition(array $partition, $isSubPartition = false)
{
$sqlQuery = " " . ($isSubPartition ? "SUB" : "") . "PARTITION ";
$sqlQuery .= $partition['name'];
if (! empty($partition['value_type'])) {
$sqlQuery .= " VALUES " . $partition['value_type'];
if ($partition['value_type'] != 'LESS THAN MAXVALUE') {
$sqlQuery .= " (" . $partition['value'] . ")";
}
}
if (! empty($partition['engine'])) {
$sqlQuery .= " ENGINE = " . $partition['engine'];
}
if (! empty($partition['comment'])) {
$sqlQuery .= " COMMENT = '" . $partition['comment'] . "'";
}
if (! empty($partition['data_directory'])) {
$sqlQuery .= " DATA DIRECTORY = '" . $partition['data_directory'] . "'";
}
if (! empty($partition['index_directory'])) {
$sqlQuery .= " INDEX_DIRECTORY = '" . $partition['index_directory'] . "'";
}
if (! empty($partition['max_rows'])) {
$sqlQuery .= " MAX_ROWS = " . $partition['max_rows'];
}
if (! empty($partition['min_rows'])) {
$sqlQuery .= " MIN_ROWS = " . $partition['min_rows'];
}
if (! empty($partition['tablespace'])) {
$sqlQuery .= " TABLESPACE = " . $partition['tablespace'];
}
if (! empty($partition['node_group'])) {
$sqlQuery .= " NODEGROUP = " . $partition['node_group'];
}
if (! empty($partition['subpartitions'])) {
$j = 0;
$subpartitions = [];
foreach ($partition['subpartitions'] as $subpartition) {
$subpartitions[] = $this->getPartitionDefinition(
$subpartition,
true
);
$j++;
}
$sqlQuery .= " (" . implode(", ", $subpartitions) . ")";
}
return $sqlQuery;
}
/**
* Function to get table creation sql query
*
* @param string $db database name
* @param string $table table name
*
* @return string
*/
public function getTableCreationQuery($db, $table)
{
// get column addition statements
$sqlStatement = $this->getColumnCreationStatements(true);
// Builds the 'create table' statement
$sqlQuery = 'CREATE TABLE ' . Util::backquote($db) . '.'
. Util::backquote(trim($table)) . ' (' . $sqlStatement . ')';
// Adds table type, character set, comments and partition definition
if (!empty($_POST['tbl_storage_engine'])
&& ($_POST['tbl_storage_engine'] != 'Default')
) {
$sqlQuery .= ' ENGINE = ' . $_POST['tbl_storage_engine'];
}
if (!empty($_POST['tbl_collation'])) {
$sqlQuery .= Util::getCharsetQueryPart($_POST['tbl_collation']);
}
if (! empty($_POST['connection'])
&& ! empty($_POST['tbl_storage_engine'])
&& $_POST['tbl_storage_engine'] == 'FEDERATED'
) {
$sqlQuery .= " CONNECTION = '"
. $this->dbi->escapeString($_POST['connection']) . "'";
}
if (!empty($_POST['comment'])) {
$sqlQuery .= ' COMMENT = \''
. $this->dbi->escapeString($_POST['comment']) . '\'';
}
$sqlQuery .= $this->getPartitionsDefinition();
$sqlQuery .= ';';
return $sqlQuery;
}
/**
* Function to get the number of fields for the table creation form
*
* @return int
*/
public function getNumberOfFieldsFromRequest()
{
// Limit to 4096 fields (MySQL maximal value)
$mysqlLimit = 4096;
if (isset($_POST['submit_num_fields'])) { // adding new fields
$numberOfFields = intval($_POST['orig_num_fields']) + intval($_POST['added_fields']);
} elseif (isset($_POST['orig_num_fields'])) { // retaining existing fields
$numberOfFields = intval($_POST['orig_num_fields']);
} elseif (isset($_POST['num_fields'])
&& intval($_POST['num_fields']) > 0
) { // new table with specified number of fields
$numberOfFields = intval($_POST['num_fields']);
} else { // new table with unspecified number of fields
$numberOfFields = 4;
}
return min($numberOfFields, $mysqlLimit);
}
/**
* Function to execute the column creation statement
*
* @param string $db current database
* @param string $table current table
* @param string $errorUrl error page url
*
* @return array
*/
public function tryColumnCreationQuery($db, $table, $errorUrl)
{
// get column addition statements
$sqlStatement = $this->getColumnCreationStatements(false);
// To allow replication, we first select the db to use and then run queries
// on this db.
if (!($this->dbi->selectDb($db))) {
Util::mysqlDie(
$this->dbi->getError(),
'USE ' . Util::backquote($db),
false,
$errorUrl
);
}
$sqlQuery = 'ALTER TABLE ' .
Util::backquote($table) . ' ' . $sqlStatement . ';';
// If there is a request for SQL previewing.
if (isset($_POST['preview_sql'])) {
Core::previewSQL($sqlQuery);
}
return [$this->dbi->tryQuery($sqlQuery), $sqlQuery];
}
}

View File

@@ -0,0 +1,161 @@
<?php
namespace PhpMyAdmin\Crypto;
use Exception;
use phpseclib\Crypt\AES;
use phpseclib\Crypt\Random;
final class Crypto
{
/** @var bool */
private $hasRandomBytesSupport;
/** @var bool */
private $hasSodiumSupport;
/**
* @param bool $forceFallback Force the usage of the fallback functions.
*/
public function __construct($forceFallback = false)
{
$this->hasRandomBytesSupport = ! $forceFallback && is_callable('random_bytes');
$this->hasSodiumSupport = ! $forceFallback
&& $this->hasRandomBytesSupport
&& is_callable('sodium_crypto_secretbox')
&& is_callable('sodium_crypto_secretbox_open')
&& defined('SODIUM_CRYPTO_SECRETBOX_NONCEBYTES')
&& defined('SODIUM_CRYPTO_SECRETBOX_KEYBYTES');
}
/**
* @param string $plaintext
*
* @return string
*/
public function encrypt($plaintext)
{
if ($this->hasSodiumSupport) {
return $this->encryptWithSodium($plaintext);
}
return $this->encryptWithPhpseclib($plaintext);
}
/**
* @param string $ciphertext
*
* @return string
*/
public function decrypt($ciphertext)
{
if ($this->hasSodiumSupport) {
return $this->decryptWithSodium($ciphertext);
}
return $this->decryptWithPhpseclib($ciphertext);
}
/**
* @return string
*/
private function getEncryptionKey()
{
global $PMA_Config;
$keyLength = $this->hasSodiumSupport ? SODIUM_CRYPTO_SECRETBOX_KEYBYTES : 32;
$key = $PMA_Config->get('URLQueryEncryptionSecretKey');
if (is_string($key) && mb_strlen($key, '8bit') === $keyLength) {
return $key;
}
$key = isset($_SESSION['URLQueryEncryptionSecretKey']) ? $_SESSION['URLQueryEncryptionSecretKey'] : null;
if (is_string($key) && mb_strlen($key, '8bit') === $keyLength) {
return $key;
}
$key = $this->hasRandomBytesSupport ? random_bytes($keyLength) : Random::string($keyLength);
$_SESSION['URLQueryEncryptionSecretKey'] = $key;
return $key;
}
/**
* @param string $plaintext
*
* @return string
*/
private function encryptWithPhpseclib($plaintext)
{
$key = $this->getEncryptionKey();
$cipher = new AES(AES::MODE_CBC);
$iv = $this->hasRandomBytesSupport ? random_bytes(16) : Random::string(16);
$cipher->setIV($iv);
$cipher->setKey($key);
$ciphertext = $cipher->encrypt($plaintext);
$hmac = hash_hmac('sha256', $iv . $ciphertext, $key, true);
return $hmac . $iv . $ciphertext;
}
/**
* @param string $encrypted
*
* @return string|null
*/
private function decryptWithPhpseclib($encrypted)
{
$key = $this->getEncryptionKey();
$hmac = mb_substr($encrypted, 0, 32, '8bit');
$iv = mb_substr($encrypted, 32, 16, '8bit');
$ciphertext = mb_substr($encrypted, 48, null, '8bit');
$calculatedHmac = hash_hmac('sha256', $iv . $ciphertext, $key, true);
if (! hash_equals($hmac, $calculatedHmac)) {
return null;
}
$cipher = new AES(AES::MODE_CBC);
$cipher->setIV($iv);
$cipher->setKey($key);
return $cipher->decrypt($ciphertext);
}
/**
* @param string $plaintext
*
* @return string
*/
private function encryptWithSodium($plaintext)
{
$key = $this->getEncryptionKey();
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $key);
return $nonce . $ciphertext;
}
/**
* @param string $encrypted
*
* @return string|null
*/
private function decryptWithSodium($encrypted)
{
$key = $this->getEncryptionKey();
$nonce = mb_substr($encrypted, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($encrypted, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
try {
$decrypted = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
} catch (Exception $e) {
return null;
}
if ($decrypted === false) {
return null;
}
return $decrypted;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* holds the PhpMyAdmin\Database\DatabaseList class
*
* @package PhpMyAdmin
*
*/
namespace PhpMyAdmin\Database;
use PhpMyAdmin\ListDatabase;
/**
* holds the DatabaseList class
*
* @package PhpMyAdmin
*/
class DatabaseList
{
/**
* Holds database list
*
* @var ListDatabase
*/
protected $databases = null;
/**
* magic access to protected/inaccessible members/properties
*
* @param string $param parameter name
*
* @return mixed
* @see https://www.php.net/language.oop5.overloading
*/
public function __get($param)
{
switch ($param) {
case 'databases' :
return $this->getDatabaseList();
}
return null;
}
/**
* Accessor to PMA::$databases
*
* @return ListDatabase
*/
public function getDatabaseList()
{
if (null === $this->databases) {
$this->databases = new ListDatabase();
}
return $this->databases;
}
}

View File

@@ -0,0 +1,432 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Database\Designer class
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Database;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\Plugins;
use PhpMyAdmin\Plugins\SchemaPlugin;
use PhpMyAdmin\Relation;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
/**
* Set of functions related to database designer
*
* @package PhpMyAdmin
*/
class Designer
{
/**
* @var Relation $relation
*/
private $relation;
/**
* Constructor
*/
public function __construct()
{
$this->relation = new Relation();
}
/**
* Function to get html for displaying the page edit/delete form
*
* @param string $db database name
* @param string $operation 'edit' or 'delete' depending on the operation
*
* @return string html content
*/
public function getHtmlForEditOrDeletePages($db, $operation)
{
$cfgRelation = $this->relation->getRelationsParam();
return Template::get('database/designer/edit_delete_pages')->render([
'db' => $db,
'operation' => $operation,
'pdfwork' => $cfgRelation['pdfwork'],
'pages' => $this->getPageIdsAndNames($db),
]);
}
/**
* Function to get html for displaying the page save as form
*
* @param string $db database name
*
* @return string html content
*/
public function getHtmlForPageSaveAs($db)
{
$cfgRelation = $this->relation->getRelationsParam();
return Template::get('database/designer/page_save_as')->render([
'db' => $db,
'pdfwork' => $cfgRelation['pdfwork'],
'pages' => $this->getPageIdsAndNames($db),
]);
}
/**
* Retrieve IDs and names of schema pages
*
* @param string $db database name
*
* @return array array of schema page id and names
*/
private function getPageIdsAndNames($db)
{
$result = [];
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return $result;
}
$page_query = "SELECT `page_nr`, `page_descr` FROM "
. Util::backquote($cfgRelation['db']) . "."
. Util::backquote($cfgRelation['pdf_pages'])
. " WHERE db_name = '" . $GLOBALS['dbi']->escapeString($db) . "'"
. " ORDER BY `page_descr`";
$page_rs = $this->relation->queryAsControlUser(
$page_query,
false,
DatabaseInterface::QUERY_STORE
);
while ($curr_page = $GLOBALS['dbi']->fetchAssoc($page_rs)) {
$result[intval($curr_page['page_nr'])] = $curr_page['page_descr'];
}
return $result;
}
/**
* Function to get html for displaying the schema export
*
* @param string $db database name
* @param int $page the page to be exported
*
* @return string
*/
public function getHtmlForSchemaExport($db, $page)
{
/* Scan for schema plugins */
/* @var $export_list SchemaPlugin[] */
$export_list = Plugins::getPlugins(
"schema",
'libraries/classes/Plugins/Schema/',
null
);
/* Fail if we didn't find any schema plugin */
if (empty($export_list)) {
return Message::error(
__('Could not load schema plugins, please check your installation!')
)->getDisplay();
}
return Template::get('database/designer/schema_export')
->render(
[
'db' => $db,
'page' => $page,
'export_list' => $export_list
]
);
}
/**
* Returns HTML for including some variable to be accessed by JavaScript
*
* @param array $script_tables array on foreign key support for each table
* @param array $script_contr initialization data array
* @param Designer\DesignerTable[] $script_display_field displayed tables in designer with their display fields
* @param int $display_page page number of the selected page
*
* @return string html
*/
public function getHtmlForJsFields(
array $script_tables,
array $script_contr,
array $script_display_field,
$display_page
) {
$displayedFields = [];
foreach ($script_display_field as $designerTable) {
if ($designerTable->getDisplayField() !== null) {
$displayedFields[$designerTable->getTableName()] = $designerTable->getDisplayField();
}
}
$cfgRelation = $this->relation->getRelationsParam();
$designerConfig = new \stdClass();
$designerConfig->db = $_GET['db'];
$designerConfig->scriptTables = $script_tables;
$designerConfig->scriptContr = $script_contr;
$designerConfig->server = $GLOBALS['server'];
$designerConfig->scriptDisplayField = $displayedFields;
$designerConfig->displayPage = (int) $display_page;
$designerConfig->tablesEnabled = $cfgRelation['pdfwork'];
return Template::get('database/designer/js_fields')->render([
'designer_config' => json_encode($designerConfig)
]);
}
/**
* Returns HTML for the menu bar of the designer page
*
* @param boolean $visualBuilder whether this is visual query builder
* @param string $selectedPage name of the selected page
* @param array $paramsArray array with class name for various buttons
* on side menu
*
* @return string html
*/
public function getPageMenu($visualBuilder, $selectedPage, array $paramsArray)
{
return Template::get('database/designer/side_menu')->render([
'visual_builder' => $visualBuilder,
'selected_page' => $selectedPage,
'params_array' => $paramsArray,
'theme' => $GLOBALS['PMA_Theme'],
]);
}
/**
* Returns array of stored values of Designer Settings
*
* @return array stored values
*/
private function getSideMenuParamsArray()
{
$params = [];
$cfgRelation = $this->relation->getRelationsParam();
if ($cfgRelation['designersettingswork']) {
$query = 'SELECT `settings_data` FROM '
. Util::backquote($cfgRelation['db']) . '.'
. Util::backquote($cfgRelation['designer_settings'])
. ' WHERE ' . Util::backquote('username') . ' = "'
. $GLOBALS['dbi']->escapeString($GLOBALS['cfg']['Server']['user'])
. '";';
$result = $GLOBALS['dbi']->fetchSingleRow($query);
$params = json_decode($result['settings_data'], true);
}
return $params;
}
/**
* Returns class names for various buttons on Designer Side Menu
*
* @return array class names of various buttons
*/
public function returnClassNamesFromMenuButtons()
{
$classes_array = [];
$params_array = $this->getSideMenuParamsArray();
if (isset($params_array['angular_direct'])
&& $params_array['angular_direct'] == 'angular'
) {
$classes_array['angular_direct'] = 'M_butt_Selected_down';
} else {
$classes_array['angular_direct'] = 'M_butt';
}
if (isset($params_array['snap_to_grid'])
&& $params_array['snap_to_grid'] == 'on'
) {
$classes_array['snap_to_grid'] = 'M_butt_Selected_down';
} else {
$classes_array['snap_to_grid'] = 'M_butt';
}
if (isset($params_array['pin_text'])
&& $params_array['pin_text'] == 'true'
) {
$classes_array['pin_text'] = 'M_butt_Selected_down';
} else {
$classes_array['pin_text'] = 'M_butt';
}
if (isset($params_array['relation_lines'])
&& $params_array['relation_lines'] == 'false'
) {
$classes_array['relation_lines'] = 'M_butt_Selected_down';
} else {
$classes_array['relation_lines'] = 'M_butt';
}
if (isset($params_array['small_big_all'])
&& $params_array['small_big_all'] == 'v'
) {
$classes_array['small_big_all'] = 'M_butt_Selected_down';
} else {
$classes_array['small_big_all'] = 'M_butt';
}
if (isset($params_array['side_menu'])
&& $params_array['side_menu'] == 'true'
) {
$classes_array['side_menu'] = 'M_butt_Selected_down';
} else {
$classes_array['side_menu'] = 'M_butt';
}
return $classes_array;
}
/**
* Returns HTML for the canvas element
*
* @return string html
*/
public function getHtmlCanvas()
{
return Template::get('database/designer/canvas')->render();
}
/**
* Return HTML for the table list
*
* @return string html
*/
public function getHtmlTableList()
{
return Template::get('database/designer/table_list')->render([
'theme' => $GLOBALS['PMA_Theme'],
]);
}
/**
* Get HTML to display tables on designer page
*
* @param string $db The database name from the request
* @param array $designerTables The designer tables
* @param array $tab_pos tables positions
* @param int $display_page page number of the selected page
* @param array $tab_column table column info
* @param array $tables_all_keys all indices
* @param array $tables_pk_or_unique_keys unique or primary indices
*
* @return string html
*/
public function getDatabaseTables(
$db,
array $designerTables,
array $tab_pos,
$display_page,
array $tab_column,
array $tables_all_keys,
array $tables_pk_or_unique_keys
) {
return Template::get('database/designer/database_tables')->render([
'db' => $GLOBALS['db'],
'get_db' => $db,
'has_query' => isset($_REQUEST['query']),
'tab_pos' => $tab_pos,
'display_page' => $display_page,
'tab_column' => $tab_column,
'tables_all_keys' => $tables_all_keys,
'tables_pk_or_unique_keys' => $tables_pk_or_unique_keys,
'tables' => $designerTables,
'theme' => $GLOBALS['PMA_Theme'],
]);
}
/**
* Returns HTML for the new relations panel.
*
* @return string html
*/
public function getNewRelationPanel()
{
return Template::get('database/designer/new_relation_panel')
->render();
}
/**
* Returns HTML for the relations delete panel
*
* @return string html
*/
public function getDeleteRelationPanel()
{
return Template::get('database/designer/delete_relation_panel')
->render();
}
/**
* Returns HTML for the options panel
*
* @return string html
*/
public function getOptionsPanel()
{
return Template::get('database/designer/options_panel')->render();
}
/**
* Get HTML for the 'rename to' panel
*
* @return string html
*/
public function getRenameToPanel()
{
return Template::get('database/designer/rename_to_panel')
->render();
}
/**
* Returns HTML for the 'having' panel
*
* @return string html
*/
public function getHavingQueryPanel()
{
return Template::get('database/designer/having_query_panel')
->render();
}
/**
* Returns HTML for the 'aggregate' panel
*
* @return string html
*/
public function getAggregateQueryPanel()
{
return Template::get('database/designer/aggregate_query_panel')
->render();
}
/**
* Returns HTML for the 'where' panel
*
* @return string html
*/
public function getWhereQueryPanel()
{
return Template::get('database/designer/where_query_panel')
->render();
}
/**
* Returns HTML for the query details panel
*
* @param string $db Database name
*
* @return string html
*/
public function getQueryDetails($db)
{
return Template::get('database/designer/query_details')->render([
'db' => $db,
]);
}
}

View File

@@ -0,0 +1,792 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Database\Designer\Common class
*
* @package PhpMyAdmin-Designer
*/
namespace PhpMyAdmin\Database\Designer;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Index;
use PhpMyAdmin\Relation;
use PhpMyAdmin\Table;
use PhpMyAdmin\Util;
use PhpMyAdmin\Database\Designer\DesignerTable;
/**
* Common functions for Designer
*
* @package PhpMyAdmin-Designer
*/
class Common
{
/**
* @var Relation $relation
*/
private $relation;
/**
* Constructor
*/
public function __construct()
{
$this->relation = new Relation();
}
/**
* Retrieves table info and returns it
*
* @param string $db (optional) Filter only a DB ($table is required if you use $db)
* @param string $table (optional) Filter only a table ($db is now required)
* @return DesignerTable[] with table info
*/
public function getTablesInfo($db = null, $table = null)
{
$designerTables = array();
$db = ($db === null) ? $GLOBALS['db'] : $db;
// seems to be needed later
$GLOBALS['dbi']->selectDb($db);
if ($db === null && $table === null) {
$tables = $GLOBALS['dbi']->getTablesFull($db);
} else {
$tables = $GLOBALS['dbi']->getTablesFull($db, $table);
}
foreach ($tables as $one_table) {
$DF = $this->relation->getDisplayField($db, $one_table['TABLE_NAME']);
$DF = is_string($DF) ? $DF : '';
$DF = ($DF !== '') ? $DF : null;
$designerTables[] = new DesignerTable(
$db,
$one_table['TABLE_NAME'],
is_string($one_table['ENGINE']) ? $one_table['ENGINE'] : '',
$DF
);
}
return $designerTables;
}
/**
* Retrieves table column info
*
* @param DesignerTable[] $designerTables The designer tables
* @return array table column nfo
*/
public function getColumnsInfo($designerTables)
{
//$GLOBALS['dbi']->selectDb($GLOBALS['db']);
$tabColumn = array();
foreach($designerTables as $designerTable) {
$fieldsRs = $GLOBALS['dbi']->query(
$GLOBALS['dbi']->getColumnsSql(
$designerTable->getDatabaseName(),
$designerTable->getTableName(),
null,
true
),
DatabaseInterface::CONNECT_USER,
DatabaseInterface::QUERY_STORE
);
$j = 0;
while ($row = $GLOBALS['dbi']->fetchAssoc($fieldsRs)) {
if (! isset($tabColumn[$designerTable->getDbTableString()])) {
$tabColumn[$designerTable->getDbTableString()] = [];
}
$tabColumn[$designerTable->getDbTableString()]['COLUMN_ID'][$j] = $j;
$tabColumn[$designerTable->getDbTableString()]['COLUMN_NAME'][$j] = $row['Field'];
$tabColumn[$designerTable->getDbTableString()]['TYPE'][$j] = $row['Type'];
$tabColumn[$designerTable->getDbTableString()]['NULLABLE'][$j] = $row['Null'];
$j++;
}
}
return $tabColumn;
}
/**
* Returns JavaScript code for initializing vars
*
* @param DesignerTable[] $designerTables The designer tables
* @return string JavaScript code
*/
public function getScriptContr($designerTables)
{
$GLOBALS['dbi']->selectDb($GLOBALS['db']);
$con = array();
$con["C_NAME"] = array();
$i = 0;
$alltab_rs = $GLOBALS['dbi']->query(
'SHOW TABLES FROM ' . Util::backquote($GLOBALS['db']),
DatabaseInterface::CONNECT_USER,
DatabaseInterface::QUERY_STORE
);
while ($val = @$GLOBALS['dbi']->fetchRow($alltab_rs)) {
$row = $this->relation->getForeigners($GLOBALS['db'], $val[0], '', 'internal');
if ($row !== false) {
foreach ($row as $field => $value) {
$con['C_NAME'][$i] = '';
$con['DTN'][$i] = rawurlencode($GLOBALS['db'] . "." . $val[0]);
$con['DCN'][$i] = rawurlencode($field);
$con['STN'][$i] = rawurlencode(
$value['foreign_db'] . "." . $value['foreign_table']
);
$con['SCN'][$i] = rawurlencode($value['foreign_field']);
$i++;
}
}
$row = $this->relation->getForeigners($GLOBALS['db'], $val[0], '', 'foreign');
// We do not have access to the foreign keys if he user has partial access to the columns
if ($row !== false && isset($row['foreign_keys_data'])) {
foreach ($row['foreign_keys_data'] as $one_key) {
foreach ($one_key['index_list'] as $index => $one_field) {
$con['C_NAME'][$i] = rawurlencode($one_key['constraint']);
$con['DTN'][$i] = rawurlencode($GLOBALS['db'] . "." . $val[0]);
$con['DCN'][$i] = rawurlencode($one_field);
$con['STN'][$i] = rawurlencode(
(isset($one_key['ref_db_name']) ?
$one_key['ref_db_name'] : $GLOBALS['db'])
. "." . $one_key['ref_table_name']
);
$con['SCN'][$i] = rawurlencode($one_key['ref_index_list'][$index]);
$i++;
}
}
}
}
$tableDbNames = [];
foreach($designerTables as $designerTable) {
$tableDbNames[] = $designerTable->getDbTableString();
}
$ti = 0;
$retval = array();
for ($i = 0, $cnt = count($con["C_NAME"]); $i < $cnt; $i++) {
$c_name_i = $con['C_NAME'][$i];
$dtn_i = $con['DTN'][$i];
$retval[$ti] = array();
$retval[$ti][$c_name_i] = array();
if (in_array($dtn_i, $tableDbNames) && in_array($con['STN'][$i], $tableDbNames)) {
$retval[$ti][$c_name_i][$dtn_i] = array();
$retval[$ti][$c_name_i][$dtn_i][$con['DCN'][$i]] = array(
0 => $con['STN'][$i],
1 => $con['SCN'][$i]
);
}
$ti++;
}
return $retval;
}
/**
* Returns UNIQUE and PRIMARY indices
*
* @param DesignerTable[] $designerTables The designer tables
* @return array unique or primary indices
*/
public function getPkOrUniqueKeys($designerTables)
{
return $this->getAllKeys($designerTables, true);
}
/**
* Returns all indices
*
* @param DesignerTable[] $designerTables The designer tables
* @param bool $unique_only whether to include only unique ones
*
* @return array indices
*/
public function getAllKeys($designerTables, $unique_only = false)
{
$keys = array();
foreach ($designerTables as $designerTable) {
$schema = $designerTable->getDatabaseName();
// for now, take into account only the first index segment
foreach (Index::getFromTable($designerTable->getTableName(), $schema) as $index) {
if ($unique_only && ! $index->isUnique()) {
continue;
}
$columns = $index->getColumns();
foreach ($columns as $column_name => $dummy) {
$keys[$schema . '.' . $designerTable->getTableName() . '.' . $column_name] = 1;
}
}
}
return $keys;
}
/**
* Return j_tab and h_tab arrays
*
* @param DesignerTable[] $designerTables The designer tables
* @return array
*/
public function getScriptTabs($designerTables)
{
$retval = array(
'j_tabs' => array(),
'h_tabs' => array()
);
foreach($designerTables as $designerTable) {
$key = rawurlencode($designerTable->getDbTableString());
$retval['j_tabs'][$key] = $designerTable->supportsForeignkeys() ? 1 : 0;
$retval['h_tabs'][$key] = 1;
}
return $retval;
}
/**
* Returns table positions of a given pdf page
*
* @param int $pg pdf page id
*
* @return array of table positions
*/
public function getTablePositions($pg)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return array();
}
$query = "
SELECT CONCAT_WS('.', `db_name`, `table_name`) AS `name`,
`db_name` as `dbName`, `table_name` as `tableName`,
`x` AS `X`,
`y` AS `Y`,
1 AS `V`,
1 AS `H`
FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['table_coords']) . "
WHERE pdf_page_number = " . intval($pg);
$tab_pos = $GLOBALS['dbi']->fetchResult(
$query,
'name',
null,
DatabaseInterface::CONNECT_CONTROL,
DatabaseInterface::QUERY_STORE
);
return $tab_pos;
}
/**
* Returns page name of a given pdf page
*
* @param int $pg pdf page id
*
* @return string|null table name
*/
public function getPageName($pg)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return null;
}
$query = "SELECT `page_descr`"
. " FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['pdf_pages'])
. " WHERE " . Util::backquote('page_nr') . " = " . intval($pg);
$page_name = $GLOBALS['dbi']->fetchResult(
$query,
null,
null,
DatabaseInterface::CONNECT_CONTROL,
DatabaseInterface::QUERY_STORE
);
return ( is_array($page_name) && isset($page_name[0]) ) ? $page_name[0] : null;
}
/**
* Deletes a given pdf page and its corresponding coordinates
*
* @param int $pg page id
*
* @return boolean success/failure
*/
public function deletePage($pg)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return false;
}
$query = "DELETE FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['table_coords'])
. " WHERE " . Util::backquote('pdf_page_number') . " = " . intval($pg);
$success = $this->relation->queryAsControlUser(
$query, true, DatabaseInterface::QUERY_STORE
);
if ($success) {
$query = "DELETE FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['pdf_pages'])
. " WHERE " . Util::backquote('page_nr') . " = " . intval($pg);
$success = $this->relation->queryAsControlUser(
$query, true, DatabaseInterface::QUERY_STORE
);
}
return (boolean) $success;
}
/**
* Returns the id of the default pdf page of the database.
* Default page is the one which has the same name as the database.
*
* @param string $db database
*
* @return int id of the default pdf page for the database
*/
public function getDefaultPage($db)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return -1;
}
$query = "SELECT `page_nr`"
. " FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['pdf_pages'])
. " WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($db) . "'"
. " AND `page_descr` = '" . $GLOBALS['dbi']->escapeString($db) . "'";
$default_page_no = $GLOBALS['dbi']->fetchResult(
$query,
null,
null,
DatabaseInterface::CONNECT_CONTROL,
DatabaseInterface::QUERY_STORE
);
if (is_array($default_page_no) && isset($default_page_no[0])) {
return intval($default_page_no[0]);
}
return -1;
}
/**
* Get the id of the page to load. If a default page exists it will be returned.
* If no such exists, returns the id of the first page of the database.
*
* @param string $db database
*
* @return int id of the page to load
*/
public function getLoadingPage($db)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return -1;
}
$page_no = -1;
$default_page_no = $this->getDefaultPage($db);
if ($default_page_no != -1) {
$page_no = $default_page_no;
} else {
$query = "SELECT MIN(`page_nr`)"
. " FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['pdf_pages'])
. " WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($db) . "'";
$min_page_no = $GLOBALS['dbi']->fetchResult(
$query,
null,
null,
DatabaseInterface::CONNECT_CONTROL,
DatabaseInterface::QUERY_STORE
);
if (is_array($min_page_no) && isset($min_page_no[0])) {
$page_no = $min_page_no[0];
}
}
return intval($page_no);
}
/**
* Creates a new page and returns its auto-incrementing id
*
* @param string $pageName name of the page
* @param string $db name of the database
*
* @return int|null
*/
public function createNewPage($pageName, $db)
{
$cfgRelation = $this->relation->getRelationsParam();
if ($cfgRelation['pdfwork']) {
$pageNumber = $this->relation->createPage(
$pageName,
$cfgRelation,
$db
);
return $pageNumber;
}
return null;
}
/**
* Saves positions of table(s) of a given pdf page
*
* @param int $pg pdf page id
*
* @return boolean success/failure
*/
public function saveTablePositions($pg)
{
$pageId = $GLOBALS['dbi']->escapeString($pg);
$db = $GLOBALS['dbi']->escapeString($_POST['db']);
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return false;
}
$query = "DELETE FROM "
. Util::backquote($cfgRelation['db'])
. "." . Util::backquote(
$cfgRelation['table_coords']
)
. " WHERE `pdf_page_number` = '" . $pageId . "'";
$res = $this->relation->queryAsControlUser(
$query,
true,
DatabaseInterface::QUERY_STORE
);
if (!$res) {
return (boolean)$res;
}
foreach ($_POST['t_h'] as $key => $value) {
$DB = $_POST['t_db'][$key];
$TAB = $_POST['t_tbl'][$key];
if (!$value) {
continue;
}
$query = "INSERT INTO "
. Util::backquote($cfgRelation['db']) . "."
. Util::backquote($cfgRelation['table_coords'])
. " (`db_name`, `table_name`, `pdf_page_number`, `x`, `y`)"
. " VALUES ("
. "'" . $GLOBALS['dbi']->escapeString($DB) . "', "
. "'" . $GLOBALS['dbi']->escapeString($TAB) . "', "
. "'" . $pageId . "', "
. "'" . $GLOBALS['dbi']->escapeString($_POST['t_x'][$key]) . "', "
. "'" . $GLOBALS['dbi']->escapeString($_POST['t_y'][$key]) . "')";
$res = $this->relation->queryAsControlUser(
$query, true, DatabaseInterface::QUERY_STORE
);
}
return (boolean) $res;
}
/**
* Saves the display field for a table.
*
* @param string $db database name
* @param string $table table name
* @param string $field display field name
*
* @return array<bool,string>
*/
public function saveDisplayField($db, $table, $field)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['displaywork']) {
return [
false,
_pgettext(
'phpMyAdmin configuration storage is not configured for "Display Features" on designer when user tries to set a display field.',
'phpMyAdmin configuration storage is not configured for "Display Features".'
),
];
}
$upd_query = new Table($table, $db, $GLOBALS['dbi']);
$upd_query->updateDisplayField($field, $cfgRelation);
return [
true,
null,
];
}
/**
* Adds a new foreign relation
*
* @param string $db database name
* @param string $T1 foreign table
* @param string $F1 foreign field
* @param string $T2 master table
* @param string $F2 master field
* @param string $on_delete on delete action
* @param string $on_update on update action
* @param string $DB1 database
* @param string $DB2 database
*
* @return array array of success/failure and message
*/
public function addNewRelation($db, $T1, $F1, $T2, $F2, $on_delete, $on_update, $DB1, $DB2)
{
$tables = $GLOBALS['dbi']->getTablesFull($DB1, $T1);
$type_T1 = mb_strtoupper($tables[$T1]['ENGINE']);
$tables = $GLOBALS['dbi']->getTablesFull($DB2, $T2);
$type_T2 = mb_strtoupper($tables[$T2]['ENGINE']);
// native foreign key
if (Util::isForeignKeySupported($type_T1)
&& Util::isForeignKeySupported($type_T2)
&& $type_T1 == $type_T2
) {
// relation exists?
$existrel_foreign = $this->relation->getForeigners($DB2, $T2, '', 'foreign');
$foreigner = $this->relation->searchColumnInForeigners($existrel_foreign, $F2);
if ($foreigner
&& isset($foreigner['constraint'])
) {
return array(false, __('Error: relationship already exists.'));
}
// note: in InnoDB, the index does not requires to be on a PRIMARY
// or UNIQUE key
// improve: check all other requirements for InnoDB relations
$result = $GLOBALS['dbi']->query(
'SHOW INDEX FROM ' . Util::backquote($DB1)
. '.' . Util::backquote($T1) . ';'
);
// will be use to emphasis prim. keys in the table view
$index_array1 = array();
while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
$index_array1[$row['Column_name']] = 1;
}
$GLOBALS['dbi']->freeResult($result);
$result = $GLOBALS['dbi']->query(
'SHOW INDEX FROM ' . Util::backquote($DB2)
. '.' . Util::backquote($T2) . ';'
);
// will be used to emphasis prim. keys in the table view
$index_array2 = array();
while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
$index_array2[$row['Column_name']] = 1;
}
$GLOBALS['dbi']->freeResult($result);
if (! empty($index_array1[$F1]) && ! empty($index_array2[$F2])) {
$upd_query = 'ALTER TABLE ' . Util::backquote($DB2)
. '.' . Util::backquote($T2)
. ' ADD FOREIGN KEY ('
. Util::backquote($F2) . ')'
. ' REFERENCES '
. Util::backquote($DB1) . '.'
. Util::backquote($T1) . '('
. Util::backquote($F1) . ')';
if ($on_delete != 'nix') {
$upd_query .= ' ON DELETE ' . $on_delete;
}
if ($on_update != 'nix') {
$upd_query .= ' ON UPDATE ' . $on_update;
}
$upd_query .= ';';
if ($GLOBALS['dbi']->tryQuery($upd_query)) {
return array(true, __('FOREIGN KEY relationship has been added.'));
}
$error = $GLOBALS['dbi']->getError();
return array(
false,
__('Error: FOREIGN KEY relationship could not be added!')
. "<br/>" . $error
);
}
return array(false, __('Error: Missing index on column(s).'));
}
// internal (pmadb) relation
if ($GLOBALS['cfgRelation']['relwork'] == false) {
return array(false, __('Error: Relational features are disabled!'));
}
// no need to recheck if the keys are primary or unique at this point,
// this was checked on the interface part
$q = "INSERT INTO "
. Util::backquote($GLOBALS['cfgRelation']['db'])
. "."
. Util::backquote($GLOBALS['cfgRelation']['relation'])
. "(master_db, master_table, master_field, "
. "foreign_db, foreign_table, foreign_field)"
. " values("
. "'" . $GLOBALS['dbi']->escapeString($DB2) . "', "
. "'" . $GLOBALS['dbi']->escapeString($T2) . "', "
. "'" . $GLOBALS['dbi']->escapeString($F2) . "', "
. "'" . $GLOBALS['dbi']->escapeString($DB1) . "', "
. "'" . $GLOBALS['dbi']->escapeString($T1) . "', "
. "'" . $GLOBALS['dbi']->escapeString($F1) . "')";
if ($this->relation->queryAsControlUser($q, false, DatabaseInterface::QUERY_STORE)
) {
return array(true, __('Internal relationship has been added.'));
}
$error = $GLOBALS['dbi']->getError(DatabaseInterface::CONNECT_CONTROL);
return array(
false,
__('Error: Internal relationship could not be added!')
. "<br/>" . $error
);
}
/**
* Removes a foreign relation
*
* @param string $T1 foreign db.table
* @param string $F1 foreign field
* @param string $T2 master db.table
* @param string $F2 master field
*
* @return array array of success/failure and message
*/
public function removeRelation($T1, $F1, $T2, $F2)
{
list($DB1, $T1) = explode(".", $T1);
list($DB2, $T2) = explode(".", $T2);
$tables = $GLOBALS['dbi']->getTablesFull($DB1, $T1);
$type_T1 = mb_strtoupper($tables[$T1]['ENGINE']);
$tables = $GLOBALS['dbi']->getTablesFull($DB2, $T2);
$type_T2 = mb_strtoupper($tables[$T2]['ENGINE']);
if (Util::isForeignKeySupported($type_T1)
&& Util::isForeignKeySupported($type_T2)
&& $type_T1 == $type_T2
) {
// InnoDB
$existrel_foreign = $this->relation->getForeigners($DB2, $T2, '', 'foreign');
$foreigner = $this->relation->searchColumnInForeigners($existrel_foreign, $F2);
if (isset($foreigner['constraint'])) {
$upd_query = 'ALTER TABLE ' . Util::backquote($DB2)
. '.' . Util::backquote($T2) . ' DROP FOREIGN KEY '
. Util::backquote($foreigner['constraint']) . ';';
if ($GLOBALS['dbi']->query($upd_query)) {
return array(true, __('FOREIGN KEY relationship has been removed.'));
}
$error = $GLOBALS['dbi']->getError();
return array(
false,
__('Error: FOREIGN KEY relationship could not be removed!')
. "<br/>" . $error
);
}
}
// internal relations
$delete_query = "DELETE FROM "
. Util::backquote($GLOBALS['cfgRelation']['db']) . "."
. $GLOBALS['cfgRelation']['relation'] . " WHERE "
. "master_db = '" . $GLOBALS['dbi']->escapeString($DB2) . "'"
. " AND master_table = '" . $GLOBALS['dbi']->escapeString($T2) . "'"
. " AND master_field = '" . $GLOBALS['dbi']->escapeString($F2) . "'"
. " AND foreign_db = '" . $GLOBALS['dbi']->escapeString($DB1) . "'"
. " AND foreign_table = '" . $GLOBALS['dbi']->escapeString($T1) . "'"
. " AND foreign_field = '" . $GLOBALS['dbi']->escapeString($F1) . "'";
$result = $this->relation->queryAsControlUser(
$delete_query,
false,
DatabaseInterface::QUERY_STORE
);
if (!$result) {
$error = $GLOBALS['dbi']->getError(DatabaseInterface::CONNECT_CONTROL);
return array(
false,
__('Error: Internal relationship could not be removed!') . "<br/>" . $error
);
}
return array(true, __('Internal relationship has been removed.'));
}
/**
* Save value for a designer setting
*
* @param string $index setting
* @param string $value value
*
* @return bool whether the operation succeeded
*/
public function saveSetting($index, $value)
{
$cfgRelation = $this->relation->getRelationsParam();
$success = true;
if ($cfgRelation['designersettingswork']) {
$cfgDesigner = array(
'user' => $GLOBALS['cfg']['Server']['user'],
'db' => $cfgRelation['db'],
'table' => $cfgRelation['designer_settings']
);
$orig_data_query = "SELECT settings_data"
. " FROM " . Util::backquote($cfgDesigner['db'])
. "." . Util::backquote($cfgDesigner['table'])
. " WHERE username = '"
. $GLOBALS['dbi']->escapeString($cfgDesigner['user']) . "';";
$orig_data = $GLOBALS['dbi']->fetchSingleRow(
$orig_data_query, 'ASSOC', DatabaseInterface::CONNECT_CONTROL
);
if (! empty($orig_data)) {
$orig_data = json_decode($orig_data['settings_data'], true);
$orig_data[$index] = $value;
$orig_data = json_encode($orig_data);
$save_query = "UPDATE "
. Util::backquote($cfgDesigner['db'])
. "." . Util::backquote($cfgDesigner['table'])
. " SET settings_data = '" . $orig_data . "'"
. " WHERE username = '"
. $GLOBALS['dbi']->escapeString($cfgDesigner['user']) . "';";
$success = $this->relation->queryAsControlUser($save_query);
} else {
$save_data = array($index => $value);
$query = "INSERT INTO "
. Util::backquote($cfgDesigner['db'])
. "." . Util::backquote($cfgDesigner['table'])
. " (username, settings_data)"
. " VALUES('" . $GLOBALS['dbi']->escapeString($cfgDesigner['user'])
. "', '" . json_encode($save_data) . "');";
$success = $this->relation->queryAsControlUser($query);
}
}
return (bool) $success;
}
}

View File

@@ -0,0 +1,98 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Database\Designer\DesignerTable class
*
* @package PhpMyAdmin-Designer
*/
namespace PhpMyAdmin\Database\Designer;
use PhpMyAdmin\Util;
/**
* Common functions for Designer
*
* @package PhpMyAdmin-Designer
*/
class DesignerTable
{
private $tableName;
private $databaseName;
private $tableEngine;
private $displayField;
/**
* Create a new DesignerTable
*
* @param string $databaseName The database name
* @param string $tableName The table name
* @param string $tableEngine The table engine
* @param string|null $displayField The display field if available
*/
public function __construct(
$databaseName,
$tableName,
$tableEngine,
$displayField
) {
$this->databaseName = $databaseName;
$this->tableName = $tableName;
$this->tableEngine = $tableEngine;
$this->displayField = $displayField;
}
/**
* The table engine supports or not foreign keys
*
* @return bool
*/
public function supportsForeignkeys() {
return Util::isForeignKeySupported($this->tableEngine);
}
/**
* Get the database name
*
* @return string
*/
public function getDatabaseName() {
return $this->databaseName;
}
/**
* Get the table name
*
* @return string
*/
public function getTableName() {
return $this->tableName;
}
/**
* Get the table engine
*
* @return string
*/
public function getTableEngine() {
return $this->tableEngine;
}
/**
* Get the displayed field
*
* @return string
*/
public function getDisplayField()
{
return $this->displayField;
}
/**
* Get the db and table separated with a dot
*
* @return string
*/
public function getDbTableString() {
return $this->databaseName . '.' . $this->tableName;
}
}

View File

@@ -0,0 +1,135 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Handles DB Multi-table query
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Database;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ParseAnalyze;
use PhpMyAdmin\Sql;
use PhpMyAdmin\Template;
/**
* Class to handle database Multi-table querying
*
* @package PhpMyAdmin
*/
class MultiTableQuery
{
/**
* DatabaseInterface instance
*
* @access private
* @var DatabaseInterface
*/
private $dbi;
/**
* Database name
*
* @access private
* @var string
*/
private $db;
/**
* Default number of columns
*
* @access private
* @var integer
*/
private $defaultNoOfColumns;
/**
* Table names
*
* @access private
* @var array
*/
private $tables;
/**
* Constructor
*
* @param DatabaseInterface $dbi DatabaseInterface instance
* @param string $dbName Database name
* @param integer $defaultNoOfColumns Default number of columns
*/
public function __construct(
DatabaseInterface $dbi,
$dbName,
$defaultNoOfColumns = 3
) {
$this->dbi = $dbi;
$this->db = $dbName;
$this->defaultNoOfColumns = $defaultNoOfColumns;
$this->tables = $this->dbi->getTables($this->db);
}
/**
* Get Multi-Table query page HTML
*
* @return string Multi-Table query page HTML
*/
public function getFormHtml()
{
$tables = [];
foreach($this->tables as $table) {
$tables[$table]['hash'] = md5($table);
$tables[$table]['columns'] = array_keys(
$this->dbi->getColumns($this->db, $table)
);
}
return Template::get('database/multi_table_query/form')->render([
'db' => $this->db,
'tables' => $tables,
'default_no_of_columns' => $this->defaultNoOfColumns,
]);
}
/**
* Displays multi-table query results
*
* @param string $sqlQuery The query to parse
* @param string $db The current database
* @param string $pmaThemeImage Uri of the PMA theme image
*
* @return void
*/
public static function displayResults($sqlQuery, $db, $pmaThemeImage)
{
list(
$analyzedSqlResults,
$db,
$tableFromSql
) = ParseAnalyze::sqlQuery($sqlQuery, $db);
extract($analyzedSqlResults);
$goto = 'db_multi_table_query.php';
$sql = new Sql();
$sql->executeQueryAndSendQueryResponse(
null, // analyzed_sql_results
false, // is_gotofile
$db, // db
null, // table
null, // find_real_end
null, // sql_query_for_bookmark - see below
null, // extra_data
null, // message_to_show
null, // message
null, // sql_data
$goto, // goto
$pmaThemeImage, // pmaThemeImage
null, // disp_query
null, // disp_message
null, // query_type
$sqlQuery, // sql_query
null, // selectedTables
null // complete_query
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,339 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Handles Database Search
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Database;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
/**
* Class to handle database search
*
* @package PhpMyAdmin
*/
class Search
{
/**
* Database name
*
* @access private
* @var string
*/
private $db;
/**
* Table Names
*
* @access private
* @var array
*/
private $tablesNamesOnly;
/**
* Type of search
*
* @access private
* @var array
*/
private $searchTypes;
/**
* Already set search type
*
* @access private
* @var integer
*/
private $criteriaSearchType;
/**
* Already set search type's description
*
* @access private
* @var string
*/
private $searchTypeDescription;
/**
* Search string/regexp
*
* @access private
* @var string
*/
private $criteriaSearchString;
/**
* Criteria Tables to search in
*
* @access private
* @var array
*/
private $criteriaTables;
/**
* Restrict the search to this column
*
* @access private
* @var string
*/
private $criteriaColumnName;
/**
* Public Constructor
*
* @param string $db Database name
*/
public function __construct($db)
{
$this->db = $db;
$this->searchTypes = array(
'1' => __('at least one of the words'),
'2' => __('all of the words'),
'3' => __('the exact phrase as substring'),
'4' => __('the exact phrase as whole field'),
'5' => __('as regular expression'),
);
// Sets criteria parameters
$this->setSearchParams();
}
/**
* Sets search parameters
*
* @return void
*/
private function setSearchParams()
{
$this->tablesNamesOnly = $GLOBALS['dbi']->getTables($this->db);
if (empty($_POST['criteriaSearchType'])
|| ! is_string($_POST['criteriaSearchType'])
|| ! array_key_exists(
$_POST['criteriaSearchType'],
$this->searchTypes
)
) {
$this->criteriaSearchType = 1;
unset($_POST['submit_search']);
} else {
$this->criteriaSearchType = (int) $_POST['criteriaSearchType'];
$this->searchTypeDescription
= $this->searchTypes[$_POST['criteriaSearchType']];
}
if (empty($_POST['criteriaSearchString'])
|| ! is_string($_POST['criteriaSearchString'])
) {
$this->criteriaSearchString = '';
unset($_POST['submit_search']);
} else {
$this->criteriaSearchString = $_POST['criteriaSearchString'];
}
$this->criteriaTables = array();
if (empty($_POST['criteriaTables'])
|| ! is_array($_POST['criteriaTables'])
) {
unset($_POST['submit_search']);
} else {
$this->criteriaTables = array_intersect(
$_POST['criteriaTables'], $this->tablesNamesOnly
);
}
if (empty($_POST['criteriaColumnName'])
|| ! is_string($_POST['criteriaColumnName'])
) {
unset($this->criteriaColumnName);
} else {
$this->criteriaColumnName = $GLOBALS['dbi']->escapeString(
$_POST['criteriaColumnName']
);
}
}
/**
* Builds the SQL search query
*
* @param string $table The table name
*
* @return array 3 SQL queries (for count, display and delete results)
*
* @todo can we make use of fulltextsearch IN BOOLEAN MODE for this?
* PMA_backquote
* DatabaseInterface::freeResult
* DatabaseInterface::fetchAssoc
* $GLOBALS['db']
* explode
* count
* strlen
*/
private function getSearchSqls($table)
{
// Statement types
$sqlstr_select = 'SELECT';
$sqlstr_delete = 'DELETE';
// Table to use
$sqlstr_from = ' FROM '
. Util::backquote($GLOBALS['db']) . '.'
. Util::backquote($table);
// Gets where clause for the query
$where_clause = $this->getWhereClause($table);
// Builds complete queries
$sql = array();
$sql['select_columns'] = $sqlstr_select . ' * ' . $sqlstr_from
. $where_clause;
// here, I think we need to still use the COUNT clause, even for
// VIEWs, anyway we have a WHERE clause that should limit results
$sql['select_count'] = $sqlstr_select . ' COUNT(*) AS `count`'
. $sqlstr_from . $where_clause;
$sql['delete'] = $sqlstr_delete . $sqlstr_from . $where_clause;
return $sql;
}
/**
* Provides where clause for building SQL query
*
* @param string $table The table name
*
* @return string The generated where clause
*/
private function getWhereClause($table)
{
// Columns to select
$allColumns = $GLOBALS['dbi']->getColumns($GLOBALS['db'], $table);
$likeClauses = array();
// Based on search type, decide like/regex & '%'/''
$like_or_regex = (($this->criteriaSearchType == 5) ? 'REGEXP' : 'LIKE');
$automatic_wildcard = (($this->criteriaSearchType < 4) ? '%' : '');
// For "as regular expression" (search option 5), LIKE won't be used
// Usage example: If user is searching for a literal $ in a regexp search,
// he should enter \$ as the value.
$criteriaSearchStringEscaped = $GLOBALS['dbi']->escapeString(
$this->criteriaSearchString
);
// Extract search words or pattern
$search_words = (($this->criteriaSearchType > 2)
? array($criteriaSearchStringEscaped)
: explode(' ', $criteriaSearchStringEscaped));
foreach ($search_words as $search_word) {
// Eliminates empty values
if (strlen($search_word) === 0) {
continue;
}
$likeClausesPerColumn = array();
// for each column in the table
foreach ($allColumns as $column) {
if (! isset($this->criteriaColumnName)
|| strlen($this->criteriaColumnName) === 0
|| $column['Field'] == $this->criteriaColumnName
) {
$column = 'CONVERT(' . Util::backquote($column['Field'])
. ' USING utf8)';
$likeClausesPerColumn[] = $column . ' ' . $like_or_regex . ' '
. "'"
. $automatic_wildcard . $search_word . $automatic_wildcard
. "'";
}
} // end for
if (count($likeClausesPerColumn) > 0) {
$likeClauses[] = implode(' OR ', $likeClausesPerColumn);
}
} // end for
// Use 'OR' if 'at least one word' is to be searched, else use 'AND'
$implode_str = ($this->criteriaSearchType == 1 ? ' OR ' : ' AND ');
if (empty($likeClauses)) {
// this could happen when the "inside column" does not exist
// in any selected tables
$where_clause = ' WHERE FALSE';
} else {
$where_clause = ' WHERE ('
. implode(') ' . $implode_str . ' (', $likeClauses)
. ')';
}
return $where_clause;
}
/**
* Displays database search results
*
* @return string HTML for search results
*/
public function getSearchResults()
{
$resultTotal = 0;
$rows = [];
// For each table selected as search criteria
foreach ($this->criteriaTables as $eachTable) {
// Gets the SQL statements
$newSearchSqls = $this->getSearchSqls($eachTable);
// Executes the "COUNT" statement
$resultCount = intval($GLOBALS['dbi']->fetchValue(
$newSearchSqls['select_count']
));
$resultTotal += $resultCount;
// Gets the result row's HTML for a table
$rows[] = [
'table' => htmlspecialchars($eachTable),
'new_search_sqls' => $newSearchSqls,
'result_count' => $resultCount,
];
}
return Template::get('database/search/results')->render([
'db' => $this->db,
'rows' => $rows,
'result_total' => $resultTotal,
'criteria_tables' => $this->criteriaTables,
'criteria_search_string' => htmlspecialchars($this->criteriaSearchString),
'search_type_description' => $this->searchTypeDescription,
]);
}
/**
* Provides the main search form's html
*
* @return string HTML for selection form
*/
public function getSelectionForm()
{
$choices = array(
'1' => $this->searchTypes[1] . ' '
. Util::showHint(
__('Words are separated by a space character (" ").')
),
'2' => $this->searchTypes[2] . ' '
. Util::showHint(
__('Words are separated by a space character (" ").')
),
'3' => $this->searchTypes[3],
'4' => $this->searchTypes[4],
'5' => $this->searchTypes[5] . ' ' . Util::showMySQLDocu('Regexp')
);
return Template::get('database/search/selection_form')->render([
'db' => $this->db,
'choices' => $choices,
'criteria_search_string' => $this->criteriaSearchString,
'criteria_search_type' => $this->criteriaSearchType,
'criteria_tables' => $this->criteriaTables,
'tables_names_only' => $this->tablesNamesOnly,
'criteria_column_name' => isset($this->criteriaColumnName)
? $this->criteriaColumnName : null,
]);
}
/**
* Provides div tags for browsing search results and sql query form.
*
* @return string div tags
*/
public function getResultDivs()
{
return Template::get('database/search/result_divs')->render();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,454 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Fake database driver for testing purposes
*
* It has hardcoded results for given queries what makes easy to use it
* in testsuite. Feel free to include other queries which your test will
* need.
*
* @package PhpMyAdmin-DBI
* @subpackage Dummy
*/
namespace PhpMyAdmin\Dbi;
require_once 'libraries/dbi/dbi_dummy.inc.php';
/**
* Fake database driver for testing purposes
*
* It has hardcoded results for given queries what makes easy to use it
* in testsuite. Feel free to include other queries which your test will
* need.
*
* @package PhpMyAdmin-DBI
* @subpackage Dummy
*/
class DbiDummy implements DbiExtension
{
private $_queries = array();
const OFFSET_GLOBAL = 1000;
/**
* connects to the database server
*
* @param string $user mysql user name
* @param string $password mysql user password
* @param array $server host/port/socket/persistent
*
* @return mixed false on error or a mysqli object on success
*/
public function connect(
$user,
$password,
array $server = []
) {
return true;
}
/**
* selects given database
*
* @param string $dbname name of db to select
* @param resource $link mysql link resource
*
* @return bool
*/
public function selectDb($dbname, $link)
{
$GLOBALS['dummy_db'] = $dbname;
return true;
}
/**
* runs a query and returns the result
*
* @param string $query query to run
* @param resource $link mysql link resource
* @param int $options query options
*
* @return mixed
*/
public function realQuery($query, $link = null, $options = 0)
{
$query = trim(preg_replace('/ */', ' ', str_replace("\n", ' ', $query)));
for ($i = 0, $nb = count($this->_queries); $i < $nb; $i++) {
if ($this->_queries[$i]['query'] != $query) {
continue;
}
$this->_queries[$i]['pos'] = 0;
if (!is_array($this->_queries[$i]['result'])) {
return false;
}
return $i;
}
for ($i = 0, $nb = count($GLOBALS['dummy_queries']); $i < $nb; $i++) {
if ($GLOBALS['dummy_queries'][$i]['query'] != $query) {
continue;
}
$GLOBALS['dummy_queries'][$i]['pos'] = 0;
if (!is_array($GLOBALS['dummy_queries'][$i]['result'])) {
return false;
}
return $i + self::OFFSET_GLOBAL;
}
echo "Not supported query: $query\n";
return false;
}
/**
* Run the multi query and output the results
*
* @param resource $link connection object
* @param string $query multi query statement to execute
*
* @return array|bool
*/
public function realMultiQuery($link, $query)
{
return false;
}
/**
* returns result data from $result
*
* @param object $result MySQL result
*
* @return array
*/
public function fetchAny($result)
{
$query_data = &$this->getQueryData($result);
if ($query_data['pos'] >= count($query_data['result'])) {
return false;
}
$ret = $query_data['result'][$query_data['pos']];
$query_data['pos'] += 1;
return $ret;
}
/**
* returns array of rows with associative and numeric keys from $result
*
* @param object $result result MySQL result
*
* @return array
*/
public function fetchArray($result)
{
$query_data = &$this->getQueryData($result);
$data = $this->fetchAny($result);
if (!is_array($data)
|| !isset($query_data['columns'])
) {
return $data;
}
foreach ($data as $key => $val) {
$data[$query_data['columns'][$key]] = $val;
}
return $data;
}
/**
* returns array of rows with associative keys from $result
*
* @param object $result MySQL result
*
* @return array
*/
public function fetchAssoc($result)
{
$data = $this->fetchAny($result);
$query_data = &$this->getQueryData($result);
if (!is_array($data) || !isset($query_data['columns'])) {
return $data;
}
$ret = array();
foreach ($data as $key => $val) {
$ret[$query_data['columns'][$key]] = $val;
}
return $ret;
}
/**
* returns array of rows with numeric keys from $result
*
* @param object $result MySQL result
*
* @return array
*/
public function fetchRow($result)
{
$data = $this->fetchAny($result);
return $data;
}
/**
* Adjusts the result pointer to an arbitrary row in the result
*
* @param object $result database result
* @param integer $offset offset to seek
*
* @return bool true on success, false on failure
*/
public function dataSeek($result, $offset)
{
$query_data = &$this->getQueryData($result);
if ($offset > count($query_data['result'])) {
return false;
}
$query_data['pos'] = $offset;
return true;
}
/**
* Frees memory associated with the result
*
* @param object $result database result
*
* @return void
*/
public function freeResult($result)
{
return;
}
/**
* Check if there are any more query results from a multi query
*
* @param resource $link the connection object
*
* @return bool false
*/
public function moreResults($link)
{
return false;
}
/**
* Prepare next result from multi_query
*
* @param resource $link the connection object
*
* @return boolean false
*/
public function nextResult($link)
{
return false;
}
/**
* Store the result returned from multi query
*
* @param resource $link the connection object
*
* @return mixed false when empty results / result set when not empty
*/
public function storeResult($link)
{
return false;
}
/**
* Returns a string representing the type of connection used
*
* @param resource $link mysql link
*
* @return string type of connection used
*/
public function getHostInfo($link)
{
return '';
}
/**
* Returns the version of the MySQL protocol used
*
* @param resource $link mysql link
*
* @return integer version of the MySQL protocol used
*/
public function getProtoInfo($link)
{
return -1;
}
/**
* returns a string that represents the client library version
*
* @return string MySQL client library version
*/
public function getClientInfo()
{
return '';
}
/**
* returns last error message or false if no errors occurred
*
* @param resource $link connection link
*
* @return string|bool $error or false
*/
public function getError($link)
{
return false;
}
/**
* returns the number of rows returned by last query
*
* @param object $result MySQL result
*
* @return string|int
*/
public function numRows($result)
{
if (is_bool($result)) {
return 0;
}
$query_data = &$this->getQueryData($result);
return count($query_data['result']);
}
/**
* returns the number of rows affected by last query
*
* @param resource $link the mysql object
* @param bool $get_from_cache whether to retrieve from cache
*
* @return string|int
*/
public function affectedRows($link = null, $get_from_cache = true)
{
return 0;
}
/**
* returns metainfo for fields in $result
*
* @param object $result result set identifier
*
* @return array meta info for fields in $result
*/
public function getFieldsMeta($result)
{
return array();
}
/**
* return number of fields in given $result
*
* @param object $result MySQL result
*
* @return int field count
*/
public function numFields($result)
{
$query_data = &$this->getQueryData($result);
if (!isset($query_data['columns'])) {
return 0;
}
return count($query_data['columns']);
}
/**
* returns the length of the given field $i in $result
*
* @param object $result result set identifier
* @param int $i field
*
* @return int length of field
*/
public function fieldLen($result, $i)
{
return -1;
}
/**
* returns name of $i. field in $result
*
* @param object $result result set identifier
* @param int $i field
*
* @return string name of $i. field in $result
*/
public function fieldName($result, $i)
{
return '';
}
/**
* returns concatenated string of human readable field flags
*
* @param object $result result set identifier
* @param int $i field
*
* @return string field flags
*/
public function fieldFlags($result, $i)
{
return '';
}
/**
* returns properly escaped string for use in MySQL queries
*
* @param mixed $link database link
* @param string $str string to be escaped
*
* @return string a MySQL escaped string
*/
public function escapeString($link, $str)
{
return $str;
}
/**
* Adds query result for testing
*
* @param string $query SQL
* @param array $result Expected result
*
* @return void
*/
public function setResult($query, $result)
{
$this->_queries[] = array(
'query' => $query,
'result' => $result,
);
}
/**
* Return query data for ID
*
* @param object $result result set identifier
*
* @return array
*/
private function &getQueryData($result)
{
if ($result >= self::OFFSET_GLOBAL) {
return $GLOBALS['dummy_queries'][$result - self::OFFSET_GLOBAL];
} else {
return $this->_queries[$result];
}
}
}

View File

@@ -0,0 +1,242 @@
<?php
/**
* Contract for every database extension supported by phpMyAdmin
*
* @package PhpMyAdmin-DBI
*/
namespace PhpMyAdmin\Dbi;
/**
* Contract for every database extension supported by phpMyAdmin
*
* @package PhpMyAdmin-DBI
*/
interface DbiExtension
{
/**
* connects to the database server
*
* @param string $user user name
* @param string $password user password
* @param array $server host/port/socket/persistent
*
* @return mixed false on error or a connection object on success
*/
public function connect(
$user, $password, array $server
);
/**
* selects given database
*
* @param string $dbname database name to select
* @param resource $link connection object
*
* @return boolean
*/
public function selectDb($dbname, $link);
/**
* runs a query and returns the result
*
* @param string $query query to execute
* @param resource $link connection object
* @param int $options query options
*
* @return mixed result
*/
public function realQuery($query, $link, $options);
/**
* Run the multi query and output the results
*
* @param resource $link connection object
* @param string $query multi query statement to execute
*
* @return array|bool
*/
public function realMultiQuery($link, $query);
/**
* returns array of rows with associative and numeric keys from $result
*
* @param object $result result set identifier
*
* @return array
*/
public function fetchArray($result);
/**
* returns array of rows with associative keys from $result
*
* @param object $result result set identifier
*
* @return array
*/
public function fetchAssoc($result);
/**
* returns array of rows with numeric keys from $result
*
* @param object $result result set identifier
*
* @return array
*/
public function fetchRow($result);
/**
* Adjusts the result pointer to an arbitrary row in the result
*
* @param object $result database result
* @param integer $offset offset to seek
*
* @return bool true on success, false on failure
*/
public function dataSeek($result, $offset);
/**
* Frees memory associated with the result
*
* @param object $result database result
*
* @return void
*/
public function freeResult($result);
/**
* Check if there are any more query results from a multi query
*
* @param resource $link the connection object
*
* @return bool true or false
*/
public function moreResults($link);
/**
* Prepare next result from multi_query
*
* @param resource $link the connection object
*
* @return bool true or false
*/
public function nextResult($link);
/**
* Store the result returned from multi query
*
* @param resource $link mysql link
*
* @return mixed false when empty results / result set when not empty
*/
public function storeResult($link);
/**
* Returns a string representing the type of connection used
*
* @param resource $link mysql link
*
* @return string type of connection used
*/
public function getHostInfo($link);
/**
* Returns the version of the MySQL protocol used
*
* @param resource $link mysql link
*
* @return integer version of the MySQL protocol used
*/
public function getProtoInfo($link);
/**
* returns a string that represents the client library version
*
* @return string MySQL client library version
*/
public function getClientInfo();
/**
* returns last error message or false if no errors occurred
*
* @param resource $link connection link
*
* @return string|bool $error or false
*/
public function getError($link);
/**
* returns the number of rows returned by last query
*
* @param object $result result set identifier
*
* @return string|int
*/
public function numRows($result);
/**
* returns the number of rows affected by last query
*
* @param resource $link the connection object
*
* @return int
*/
public function affectedRows($link);
/**
* returns metainfo for fields in $result
*
* @param object $result result set identifier
*
* @return array meta info for fields in $result
*/
public function getFieldsMeta($result);
/**
* return number of fields in given $result
*
* @param object $result result set identifier
*
* @return int field count
*/
public function numFields($result);
/**
* returns the length of the given field $i in $result
*
* @param object $result result set identifier
* @param int $i field
*
* @return int length of field
*/
public function fieldLen($result, $i);
/**
* returns name of $i. field in $result
*
* @param object $result result set identifier
* @param int $i field
*
* @return string name of $i. field in $result
*/
public function fieldName($result, $i);
/**
* returns concatenated string of human readable field flags
*
* @param object $result result set identifier
* @param int $i field
*
* @return string field flags
*/
public function fieldFlags($result, $i);
/**
* returns properly escaped string for use in MySQL queries
*
* @param mixed $link database link
* @param string $str string to be escaped
*
* @return string a MySQL escaped string
*/
public function escapeString($link, $str);
}

View File

@@ -0,0 +1,461 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Interface to the classic MySQL extension
*
* @package PhpMyAdmin-DBI
* @subpackage MySQL
*/
namespace PhpMyAdmin\Dbi;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
if (! defined('PHPMYADMIN')) {
exit;
}
if (! extension_loaded('mysql')) {
// The old MySQL extension is deprecated as of PHP 5.5.0, and will be
// removed in the future. Instead, the `MySQLi` or `PDO_MySQL` extension
// should be used.
return;
}
/**
* Interface to the classic MySQL extension
*
* @package PhpMyAdmin-DBI
* @subpackage MySQL
*/
class DbiMysql implements DbiExtension
{
/**
* Helper function for connecting to the database server
*
* @param string $server host/port/socket
* @param string $user mysql user name
* @param string $password mysql user password
* @param int $client_flags client flags of connection
* @param bool $persistent whether to use persistent connection
*
* @return mixed false on error or a mysql connection resource on success
*/
private function _realConnect($server, $user, $password, $client_flags,
$persistent = false
) {
global $cfg;
if (ini_get('mysql.allow_local_infile')) {
Core::fatalError(__('Please disable mysql.allow_local_infile in your PHP configuration or install the mysqli extension.'));
}
if (empty($client_flags)) {
if ($cfg['PersistentConnections'] || $persistent) {
$link = @mysql_pconnect($server, $user, $password);
} else {
$link = @mysql_connect($server, $user, $password);
}
} else {
if ($cfg['PersistentConnections'] || $persistent) {
$link = @mysql_pconnect($server, $user, $password, $client_flags);
} else {
$link = @mysql_connect(
$server, $user, $password, false, $client_flags
);
}
}
return $link;
}
/**
* Run the multi query and output the results
*
* @param mysqli $link mysqli object
* @param string $query multi query statement to execute
*
* @return boolean false always false since mysql extension not support
* for multi query executions
*/
public function realMultiQuery($link, $query)
{
// N.B.: PHP's 'mysql' extension does not support
// multi_queries so this function will always
// return false. Use the 'mysqli' extension, if
// you need support for multi_queries.
return false;
}
/**
* connects to the database server
*
* @param string $user mysql user name
* @param string $password mysql user password
* @param array $server host/port/socket/persistent
*
* @return mixed false on error or a mysqli object on success
*/
public function connect(
$user, $password, array $server
) {
if ($server['port'] === 0) {
$server_port = '';
} else {
$server_port = ':' . $server['port'];
}
if (is_null($server['socket'])) {
$server_socket = '';
} else {
$server_socket = ':' . $server['socket'];
}
$client_flags = 0;
if (defined('PMA_ENABLE_LDI')) {
// use CLIENT_LOCAL_FILES as defined in mysql_com.h
// for the case where the client library was not compiled
// with --enable-local-infile
$client_flags |= 128;
}
/* Optionally compress connection */
if (defined('MYSQL_CLIENT_COMPRESS') && $server['compress']) {
$client_flags |= MYSQL_CLIENT_COMPRESS;
}
/* Optionally enable SSL */
if (defined('MYSQL_CLIENT_SSL') && $server['ssl']) {
$client_flags |= MYSQL_CLIENT_SSL;
}
if (!isset($server['host'])) {
$link = $this->_realConnect($server_socket, $user, $password, null);
} else {
$link = $this->_realConnect(
$server['host'] . $server_port . $server_socket,
$user, $password, null
);
}
return $link;
}
/**
* selects given database
*
* @param string $dbname name of db to select
* @param resource|null $link mysql link resource
*
* @return bool
*/
public function selectDb($dbname, $link)
{
return mysql_select_db($dbname, $link);
}
/**
* runs a query and returns the result
*
* @param string $query query to run
* @param resource|null $link mysql link resource
* @param int $options query options
*
* @return mixed
*/
public function realQuery($query, $link, $options)
{
if ($options == ($options | DatabaseInterface::QUERY_STORE)) {
return mysql_query($query, $link);
} elseif ($options == ($options | DatabaseInterface::QUERY_UNBUFFERED)) {
return mysql_unbuffered_query($query, $link);
}
return mysql_query($query, $link);
}
/**
* returns array of rows with associative and numeric keys from $result
*
* @param resource $result result MySQL result
*
* @return array
*/
public function fetchArray($result)
{
return mysql_fetch_array($result, MYSQL_BOTH);
}
/**
* returns array of rows with associative keys from $result
*
* @param resource $result MySQL result
*
* @return array
*/
public function fetchAssoc($result)
{
return mysql_fetch_array($result, MYSQL_ASSOC);
}
/**
* returns array of rows with numeric keys from $result
*
* @param resource $result MySQL result
*
* @return array
*/
public function fetchRow($result)
{
return mysql_fetch_array($result, MYSQL_NUM);
}
/**
* Adjusts the result pointer to an arbitrary row in the result
*
* @param resource $result database result
* @param integer $offset offset to seek
*
* @return bool true on success, false on failure
*/
public function dataSeek($result, $offset)
{
return mysql_data_seek($result, $offset);
}
/**
* Frees memory associated with the result
*
* @param resource $result database result
*
* @return void
*/
public function freeResult($result)
{
if (is_resource($result) && get_resource_type($result) === 'mysql result') {
mysql_free_result($result);
}
}
/**
* Check if there are any more query results from a multi query
*
* @param resource $link the connection object
*
* @return bool false
*/
public function moreResults($link)
{
// N.B.: PHP's 'mysql' extension does not support
// multi_queries so this function will always
// return false. Use the 'mysqli' extension, if
// you need support for multi_queries.
return false;
}
/**
* Prepare next result from multi_query
*
* @param resource $link the connection object
*
* @return boolean false
*/
public function nextResult($link)
{
// N.B.: PHP's 'mysql' extension does not support
// multi_queries so this function will always
// return false. Use the 'mysqli' extension, if
// you need support for multi_queries.
return false;
}
/**
* Returns a string representing the type of connection used
*
* @param resource|null $link mysql link
*
* @return string type of connection used
*/
public function getHostInfo($link)
{
return mysql_get_host_info($link);
}
/**
* Returns the version of the MySQL protocol used
*
* @param resource|null $link mysql link
*
* @return int version of the MySQL protocol used
*/
public function getProtoInfo($link)
{
return mysql_get_proto_info($link);
}
/**
* returns a string that represents the client library version
*
* @return string MySQL client library version
*/
public function getClientInfo()
{
return mysql_get_client_info();
}
/**
* returns last error message or false if no errors occurred
*
* @param resource|null $link mysql link
*
* @return string|bool $error or false
*/
public function getError($link)
{
$GLOBALS['errno'] = 0;
if (null !== $link && false !== $link) {
$error_number = mysql_errno($link);
$error_message = mysql_error($link);
} else {
$error_number = mysql_errno();
$error_message = mysql_error();
}
if (0 == $error_number) {
return false;
}
// keep the error number for further check after
// the call to getError()
$GLOBALS['errno'] = $error_number;
return $GLOBALS['dbi']->formatError($error_number, $error_message);
}
/**
* returns the number of rows returned by last query
*
* @param resource $result MySQL result
*
* @return string|int
*/
public function numRows($result)
{
if (is_bool($result)) {
return 0;
}
return mysql_num_rows($result);
}
/**
* returns the number of rows affected by last query
*
* @param resource|null $link the mysql object
*
* @return int
*/
public function affectedRows($link)
{
return mysql_affected_rows($link);
}
/**
* returns metainfo for fields in $result
*
* @param resource $result MySQL result
*
* @return array meta info for fields in $result
*
* @todo add missing keys like in mysqli_query (decimals)
*/
public function getFieldsMeta($result)
{
$fields = array();
$num_fields = mysql_num_fields($result);
for ($i = 0; $i < $num_fields; $i++) {
$field = mysql_fetch_field($result, $i);
$field->flags = mysql_field_flags($result, $i);
$field->orgtable = mysql_field_table($result, $i);
$field->orgname = mysql_field_name($result, $i);
$fields[] = $field;
}
return $fields;
}
/**
* return number of fields in given $result
*
* @param resource $result MySQL result
*
* @return int field count
*/
public function numFields($result)
{
return mysql_num_fields($result);
}
/**
* returns the length of the given field $i in $result
*
* @param resource $result MySQL result
* @param int $i field
*
* @return int length of field
*/
public function fieldLen($result, $i)
{
return mysql_field_len($result, $i);
}
/**
* returns name of $i. field in $result
*
* @param resource $result MySQL result
* @param int $i field
*
* @return string name of $i. field in $result
*/
public function fieldName($result, $i)
{
return mysql_field_name($result, $i);
}
/**
* returns concatenated string of human readable field flags
*
* @param resource $result MySQL result
* @param int $i field
*
* @return string field flags
*/
public function fieldFlags($result, $i)
{
return mysql_field_flags($result, $i);
}
/**
* Store the result returned from multi query
*
* @param resource $result MySQL result
*
* @return false
*/
public function storeResult($result)
{
return false;
}
/**
* returns properly escaped string for use in MySQL queries
*
* @param mixed $link database link
* @param string $str string to be escaped
*
* @return string a MySQL escaped string
*/
public function escapeString($link, $str)
{
return mysql_real_escape_string($str, $link);
}
}

View File

@@ -0,0 +1,637 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Interface to the improved MySQL extension (MySQLi)
*
* @package PhpMyAdmin-DBI
* @subpackage MySQLi
*/
namespace PhpMyAdmin\Dbi;
use PhpMyAdmin\DatabaseInterface;
if (! defined('PHPMYADMIN')) {
exit;
}
/**
* some PHP versions are reporting extra messages like "No index used in query"
*/
mysqli_report(MYSQLI_REPORT_OFF);
/**
* some older mysql client libs are missing these constants ...
*/
if (! defined('MYSQLI_BINARY_FLAG')) {
define('MYSQLI_BINARY_FLAG', 128);
}
/**
* @see https://bugs.php.net/36007
*/
if (! defined('MYSQLI_TYPE_NEWDECIMAL')) {
define('MYSQLI_TYPE_NEWDECIMAL', 246);
}
if (! defined('MYSQLI_TYPE_BIT')) {
define('MYSQLI_TYPE_BIT', 16);
}
if (! defined('MYSQLI_TYPE_JSON')) {
define('MYSQLI_TYPE_JSON', 245);
}
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Interface to the improved MySQL extension (MySQLi)
*
* @package PhpMyAdmin-DBI
* @subpackage MySQLi
*/
class DbiMysqli implements DbiExtension
{
static private $pma_mysqli_flag_names = array(
MYSQLI_NUM_FLAG => 'num',
MYSQLI_PART_KEY_FLAG => 'part_key',
MYSQLI_SET_FLAG => 'set',
MYSQLI_TIMESTAMP_FLAG => 'timestamp',
MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment',
MYSQLI_ENUM_FLAG => 'enum',
MYSQLI_ZEROFILL_FLAG => 'zerofill',
MYSQLI_UNSIGNED_FLAG => 'unsigned',
MYSQLI_BLOB_FLAG => 'blob',
MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key',
MYSQLI_UNIQUE_KEY_FLAG => 'unique_key',
MYSQLI_PRI_KEY_FLAG => 'primary_key',
MYSQLI_NOT_NULL_FLAG => 'not_null',
);
/**
* connects to the database server
*
* @param string $user mysql user name
* @param string $password mysql user password
* @param array $server host/port/socket/persistent
*
* @return mixed false on error or a mysqli object on success
*/
public function connect(
$user, $password, array $server
) {
if ($server) {
$server['host'] = (empty($server['host']))
? 'localhost'
: $server['host'];
}
// NULL enables connection to the default socket
$link = mysqli_init();
$client_flags = 0;
/* Optionally compress connection */
if ($server['compress'] && defined('MYSQLI_CLIENT_COMPRESS')) {
$client_flags |= MYSQLI_CLIENT_COMPRESS;
}
/* Optionally enable SSL */
if ($server['ssl']) {
$client_flags |= MYSQLI_CLIENT_SSL;
if (! empty($server['ssl_key']) ||
! empty($server['ssl_cert']) ||
! empty($server['ssl_ca']) ||
! empty($server['ssl_ca_path']) ||
! empty($server['ssl_ciphers'])
) {
if (! isset($server['ssl_key']) || is_null($server['ssl_key'])) {
$server['ssl_key'] = '';
}
if (! isset($server['ssl_cert']) || is_null($server['ssl_cert'])) {
$server['ssl_cert'] = '';
}
if (! isset($server['ssl_ca']) || is_null($server['ssl_ca'])) {
$server['ssl_ca'] = '';
}
if (! isset($server['ssl_ca_path']) || is_null($server['ssl_ca_path'])) {
$server['ssl_ca_path'] = '';
}
if (! isset($server['ssl_ciphers']) || is_null($server['ssl_ciphers'])) {
$server['ssl_ciphers'] = '';
}
mysqli_ssl_set(
$link,
$server['ssl_key'],
$server['ssl_cert'],
$server['ssl_ca'],
$server['ssl_ca_path'],
$server['ssl_ciphers']
);
}
/*
* disables SSL certificate validation on mysqlnd for MySQL 5.6 or later
* @link https://bugs.php.net/bug.php?id=68344
* @link https://github.com/phpmyadmin/phpmyadmin/pull/11838
*/
if (! $server['ssl_verify']) {
mysqli_options(
$link,
MYSQLI_OPT_SSL_VERIFY_SERVER_CERT,
$server['ssl_verify']
);
$client_flags |= MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
}
}
if ($GLOBALS['cfg']['PersistentConnections']) {
$host = 'p:' . $server['host'];
} else {
$host = $server['host'];
}
if ($server['hide_connection_errors']) {
$return_value = @mysqli_real_connect(
$link,
$host,
$user,
$password,
'',
$server['port'],
$server['socket'],
$client_flags
);
} else {
$return_value = mysqli_real_connect(
$link,
$host,
$user,
$password,
'',
$server['port'],
$server['socket'],
$client_flags
);
}
if ($return_value === false || is_null($return_value)) {
/*
* Switch to SSL if server asked us to do so, unfortunately
* there are more ways MySQL server can tell this:
*
* - MySQL 8.0 and newer should return error 3159
* - #2001 - SSL Connection is required. Please specify SSL options and retry.
* - #9002 - SSL connection is required. Please specify SSL options and retry.
*/
$error_number = mysqli_connect_errno();
$error_message = mysqli_connect_error();
if (! $server['ssl'] && ($error_number == 3159 ||
(($error_number == 2001 || $error_number == 9002) && stripos($error_message, 'SSL Connection is required') !== false))
) {
trigger_error(
__('SSL connection enforced by server, automatically enabling it.'),
E_USER_WARNING
);
$server['ssl'] = true;
return self::connect($user, $password, $server);
} elseif ($error_number === 1045 && $server['hide_connection_errors']) {
trigger_error(
sprintf(
__(
'Error 1045: Access denied for user. Additional error information'
. ' may be available, but is being hidden by the %s configuration directive.'
),
'[code][doc@cfg_Servers_hide_connection_errors]'
. '$cfg[\'Servers\'][$i][\'hide_connection_errors\'][/doc][/code]'
),
E_USER_ERROR
);
}
return false;
}
if (defined('PMA_ENABLE_LDI')) {
mysqli_options($link, MYSQLI_OPT_LOCAL_INFILE, true);
} else {
mysqli_options($link, MYSQLI_OPT_LOCAL_INFILE, false);
}
return $link;
}
/**
* selects given database
*
* @param string $dbname database name to select
* @param mysqli $link the mysqli object
*
* @return boolean
*/
public function selectDb($dbname, $link)
{
return mysqli_select_db($link, $dbname);
}
/**
* runs a query and returns the result
*
* @param string $query query to execute
* @param mysqli $link mysqli object
* @param int $options query options
*
* @return mysqli_result|bool
*/
public function realQuery($query, $link, $options)
{
if ($options == ($options | DatabaseInterface::QUERY_STORE)) {
$method = MYSQLI_STORE_RESULT;
} elseif ($options == ($options | DatabaseInterface::QUERY_UNBUFFERED)) {
$method = MYSQLI_USE_RESULT;
} else {
$method = 0;
}
return mysqli_query($link, $query, $method);
}
/**
* Run the multi query and output the results
*
* @param mysqli $link mysqli object
* @param string $query multi query statement to execute
*
* @return mysqli_result collection | boolean(false)
*/
public function realMultiQuery($link, $query)
{
return mysqli_multi_query($link, $query);
}
/**
* returns array of rows with associative and numeric keys from $result
*
* @param mysqli_result $result result set identifier
*
* @return array
*/
public function fetchArray($result)
{
return mysqli_fetch_array($result, MYSQLI_BOTH);
}
/**
* returns array of rows with associative keys from $result
*
* @param mysqli_result $result result set identifier
*
* @return array
*/
public function fetchAssoc($result)
{
return mysqli_fetch_array($result, MYSQLI_ASSOC);
}
/**
* returns array of rows with numeric keys from $result
*
* @param mysqli_result $result result set identifier
*
* @return array
*/
public function fetchRow($result)
{
return mysqli_fetch_array($result, MYSQLI_NUM);
}
/**
* Adjusts the result pointer to an arbitrary row in the result
*
* @param mysqli_result $result database result
* @param integer $offset offset to seek
*
* @return bool true on success, false on failure
*/
public function dataSeek($result, $offset)
{
return mysqli_data_seek($result, $offset);
}
/**
* Frees memory associated with the result
*
* @param mysqli_result $result database result
*
* @return void
*/
public function freeResult($result)
{
if ($result instanceof mysqli_result) {
mysqli_free_result($result);
}
}
/**
* Check if there are any more query results from a multi query
*
* @param mysqli $link the mysqli object
*
* @return bool true or false
*/
public function moreResults($link)
{
return mysqli_more_results($link);
}
/**
* Prepare next result from multi_query
*
* @param mysqli $link the mysqli object
*
* @return bool true or false
*/
public function nextResult($link)
{
return mysqli_next_result($link);
}
/**
* Store the result returned from multi query
*
* @param mysqli $link the mysqli object
*
* @return mixed false when empty results / result set when not empty
*/
public function storeResult($link)
{
return mysqli_store_result($link);
}
/**
* Returns a string representing the type of connection used
*
* @param resource $link mysql link
*
* @return string type of connection used
*/
public function getHostInfo($link)
{
return mysqli_get_host_info($link);
}
/**
* Returns the version of the MySQL protocol used
*
* @param resource $link mysql link
*
* @return integer version of the MySQL protocol used
*/
public function getProtoInfo($link)
{
return mysqli_get_proto_info($link);
}
/**
* returns a string that represents the client library version
*
* @return string MySQL client library version
*/
public function getClientInfo()
{
return mysqli_get_client_info();
}
/**
* returns last error message or false if no errors occurred
*
* @param resource $link mysql link
*
* @return string|bool $error or false
*/
public function getError($link)
{
$GLOBALS['errno'] = 0;
if (null !== $link && false !== $link) {
$error_number = mysqli_errno($link);
$error_message = mysqli_error($link);
} else {
$error_number = mysqli_connect_errno();
$error_message = mysqli_connect_error();
}
if (0 == $error_number) {
return false;
}
// keep the error number for further check after
// the call to getError()
$GLOBALS['errno'] = $error_number;
return $GLOBALS['dbi']->formatError($error_number, $error_message);
}
/**
* returns the number of rows returned by last query
*
* @param mysqli_result $result result set identifier
*
* @return string|int
*/
public function numRows($result)
{
// see the note for tryQuery();
if (is_bool($result)) {
return 0;
}
return @mysqli_num_rows($result);
}
/**
* returns the number of rows affected by last query
*
* @param mysqli $link the mysqli object
*
* @return int
*/
public function affectedRows($link)
{
return mysqli_affected_rows($link);
}
/**
* returns metainfo for fields in $result
*
* @param mysqli_result $result result set identifier
*
* @return array meta info for fields in $result
*/
public function getFieldsMeta($result)
{
// Build an associative array for a type look up
$typeAr = array();
$typeAr[MYSQLI_TYPE_DECIMAL] = 'real';
$typeAr[MYSQLI_TYPE_NEWDECIMAL] = 'real';
$typeAr[MYSQLI_TYPE_BIT] = 'int';
$typeAr[MYSQLI_TYPE_TINY] = 'int';
$typeAr[MYSQLI_TYPE_SHORT] = 'int';
$typeAr[MYSQLI_TYPE_LONG] = 'int';
$typeAr[MYSQLI_TYPE_FLOAT] = 'real';
$typeAr[MYSQLI_TYPE_DOUBLE] = 'real';
$typeAr[MYSQLI_TYPE_NULL] = 'null';
$typeAr[MYSQLI_TYPE_TIMESTAMP] = 'timestamp';
$typeAr[MYSQLI_TYPE_LONGLONG] = 'int';
$typeAr[MYSQLI_TYPE_INT24] = 'int';
$typeAr[MYSQLI_TYPE_DATE] = 'date';
$typeAr[MYSQLI_TYPE_TIME] = 'time';
$typeAr[MYSQLI_TYPE_DATETIME] = 'datetime';
$typeAr[MYSQLI_TYPE_YEAR] = 'year';
$typeAr[MYSQLI_TYPE_NEWDATE] = 'date';
$typeAr[MYSQLI_TYPE_ENUM] = 'unknown';
$typeAr[MYSQLI_TYPE_SET] = 'unknown';
$typeAr[MYSQLI_TYPE_TINY_BLOB] = 'blob';
$typeAr[MYSQLI_TYPE_MEDIUM_BLOB] = 'blob';
$typeAr[MYSQLI_TYPE_LONG_BLOB] = 'blob';
$typeAr[MYSQLI_TYPE_BLOB] = 'blob';
$typeAr[MYSQLI_TYPE_VAR_STRING] = 'string';
$typeAr[MYSQLI_TYPE_STRING] = 'string';
// MySQL returns MYSQLI_TYPE_STRING for CHAR
// and MYSQLI_TYPE_CHAR === MYSQLI_TYPE_TINY
// so this would override TINYINT and mark all TINYINT as string
// see https://github.com/phpmyadmin/phpmyadmin/issues/8569
//$typeAr[MYSQLI_TYPE_CHAR] = 'string';
$typeAr[MYSQLI_TYPE_GEOMETRY] = 'geometry';
$typeAr[MYSQLI_TYPE_BIT] = 'bit';
$typeAr[MYSQLI_TYPE_JSON] = 'json';
$fields = mysqli_fetch_fields($result);
// this happens sometimes (seen under MySQL 4.0.25)
if (!is_array($fields)) {
return false;
}
foreach ($fields as $k => $field) {
$fields[$k]->_type = $field->type;
$fields[$k]->type = $typeAr[$field->type];
$fields[$k]->_flags = $field->flags;
$fields[$k]->flags = $this->fieldFlags($result, $k);
// Enhance the field objects for mysql-extension compatibility
//$flags = explode(' ', $fields[$k]->flags);
//array_unshift($flags, 'dummy');
$fields[$k]->multiple_key
= (int) (bool) ($fields[$k]->_flags & MYSQLI_MULTIPLE_KEY_FLAG);
$fields[$k]->primary_key
= (int) (bool) ($fields[$k]->_flags & MYSQLI_PRI_KEY_FLAG);
$fields[$k]->unique_key
= (int) (bool) ($fields[$k]->_flags & MYSQLI_UNIQUE_KEY_FLAG);
$fields[$k]->not_null
= (int) (bool) ($fields[$k]->_flags & MYSQLI_NOT_NULL_FLAG);
$fields[$k]->unsigned
= (int) (bool) ($fields[$k]->_flags & MYSQLI_UNSIGNED_FLAG);
$fields[$k]->zerofill
= (int) (bool) ($fields[$k]->_flags & MYSQLI_ZEROFILL_FLAG);
$fields[$k]->numeric
= (int) (bool) ($fields[$k]->_flags & MYSQLI_NUM_FLAG);
$fields[$k]->blob
= (int) (bool) ($fields[$k]->_flags & MYSQLI_BLOB_FLAG);
}
return $fields;
}
/**
* return number of fields in given $result
*
* @param mysqli_result $result result set identifier
*
* @return int field count
*/
public function numFields($result)
{
return mysqli_num_fields($result);
}
/**
* returns the length of the given field $i in $result
*
* @param mysqli_result $result result set identifier
* @param int $i field
*
* @return int length of field
*/
public function fieldLen($result, $i)
{
if ($i >= $this->numFields($result)) {
return false;
}
return mysqli_fetch_field_direct($result, $i)->length;
}
/**
* returns name of $i. field in $result
*
* @param mysqli_result $result result set identifier
* @param int $i field
*
* @return string name of $i. field in $result
*/
public function fieldName($result, $i)
{
if ($i >= $this->numFields($result)) {
return false;
}
return mysqli_fetch_field_direct($result, $i)->name;
}
/**
* returns concatenated string of human readable field flags
*
* @param mysqli_result $result result set identifier
* @param int $i field
*
* @return string field flags
*/
public function fieldFlags($result, $i)
{
if ($i >= $this->numFields($result)) {
return false;
}
$f = mysqli_fetch_field_direct($result, $i);
$type = $f->type;
$charsetnr = $f->charsetnr;
$f = $f->flags;
$flags = array();
foreach (self::$pma_mysqli_flag_names as $flag => $name) {
if ($f & $flag) {
$flags[] = $name;
}
}
// See https://dev.mysql.com/doc/refman/6.0/en/c-api-datatypes.html:
// to determine if a string is binary, we should not use MYSQLI_BINARY_FLAG
// but instead the charsetnr member of the MYSQL_FIELD
// structure. Watch out: some types like DATE returns 63 in charsetnr
// so we have to check also the type.
// Unfortunately there is no equivalent in the mysql extension.
if (($type == MYSQLI_TYPE_TINY_BLOB || $type == MYSQLI_TYPE_BLOB
|| $type == MYSQLI_TYPE_MEDIUM_BLOB || $type == MYSQLI_TYPE_LONG_BLOB
|| $type == MYSQLI_TYPE_VAR_STRING || $type == MYSQLI_TYPE_STRING)
&& 63 == $charsetnr
) {
$flags[] = 'binary';
}
return implode(' ', $flags);
}
/**
* returns properly escaped string for use in MySQL queries
*
* @param mixed $link database link
* @param string $str string to be escaped
*
* @return string a MySQL escaped string
*/
public function escapeString($link, $str)
{
return mysqli_real_escape_string($link, $str);
}
}

View File

@@ -0,0 +1,46 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Di\AliasItem class
*
* @package PhpMyAdmin\Di
*/
namespace PhpMyAdmin\Di;
/**
* Class AliasItem
*
* @package PhpMyAdmin\Di
*/
class AliasItem implements Item
{
/** @var Container */
protected $container;
/** @var string */
protected $target;
/**
* Constructor
*
* @param Container $container Container
* @param string $target Target
*/
public function __construct(Container $container, $target)
{
$this->container = $container;
$this->target = $target;
}
/**
* Get the target item
*
* @param array $params Parameters
* @return mixed
*/
public function get(array $params = array())
{
return $this->container->get($this->target, $params);
}
}

View File

@@ -0,0 +1,189 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Di\Container class
*
* @package PhpMyAdmin\Di
*/
namespace PhpMyAdmin\Di;
use Psr\Container\ContainerInterface;
/**
* Class Container
*
* @package PhpMyAdmin\Di
*/
class Container implements ContainerInterface
{
/**
* @var Item[] $content
*/
protected $content = array();
/**
* @var Container
*/
protected static $defaultContainer;
/**
* Create a dependency injection container
*
* @param Container $base Container
*/
public function __construct(Container $base = null)
{
if (isset($base)) {
$this->content = $base->content;
} else {
$this->alias('container', 'Container');
}
$this->set('Container', $this);
}
/**
* Get an object with given name and parameters
*
* @param string $name Name
* @param array $params Parameters
*
* @throws NotFoundException No entry was found for **this** identifier.
* @throws ContainerException Error while retrieving the entry.
*
* @return mixed
*/
public function get($name, array $params = array())
{
if (!$this->has($name)) {
throw new NotFoundException("No entry was found for $name identifier.");
}
if (isset($this->content[$name])) {
return $this->content[$name]->get($params);
} elseif (isset($GLOBALS[$name])) {
return $GLOBALS[$name];
} else {
throw new ContainerException("Error while retrieving the entry.");
}
}
/**
* Returns true if the container can return an entry for the given identifier.
* Returns false otherwise.
*
* `has($name)` returning true does not mean that `get($name)` will not throw an exception.
* It does however mean that `get($name)` will not throw a `NotFoundException`.
*
* @param string $name Identifier of the entry to look for.
*
* @return bool
*/
public function has($name)
{
return isset($this->content[$name]) || isset($GLOBALS[$name]);
}
/**
* Remove an object from container
*
* @param string $name Name
*
* @return void
*/
public function remove($name)
{
unset($this->content[$name]);
}
/**
* Rename an object in container
*
* @param string $name Name
* @param string $newName New name
*
* @return void
*/
public function rename($name, $newName)
{
$this->content[$newName] = $this->content[$name];
$this->remove($name);
}
/**
* Set values in the container
*
* @param string|array $name Name
* @param mixed $value Value
*
* @return void
*/
public function set($name, $value = null)
{
if (is_array($name)) {
foreach ($name as $key => $val) {
$this->set($key, $val);
}
return;
}
$this->content[$name] = new ValueItem($value);
}
/**
* Register a service in the container
*
* @param string $name Name
* @param mixed $service Service
*
* @return void
*/
public function service($name, $service = null)
{
if (!isset($service)) {
$service = $name;
}
$this->content[$name] = new ServiceItem($this, $service);
}
/**
* Register a factory in the container
*
* @param string $name Name
* @param mixed $factory Factory
*
* @return void
*/
public function factory($name, $factory = null)
{
if (!isset($factory)) {
$factory = $name;
}
$this->content[$name] = new FactoryItem($this, $factory);
}
/**
* Register an alias in the container
*
* @param string $name Name
* @param string $target Target
*
* @return void
*/
public function alias($name, $target)
{
// The target may be not defined yet
$this->content[$name] = new AliasItem($this, $target);
}
/**
* Get the global default container
*
* @return Container
*/
public static function getDefaultContainer()
{
if (!isset(static::$defaultContainer)) {
static::$defaultContainer = new Container();
}
return static::$defaultContainer;
}
}

View File

@@ -0,0 +1,21 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Di\ContainerException class
*
* @package PhpMyAdmin\Di
*/
namespace PhpMyAdmin\Di;
use Exception;
use Psr\Container\ContainerExceptionInterface;
/**
* Class ContainerException
*
* @package PhpMyAdmin\Di
*/
class ContainerException extends Exception implements ContainerExceptionInterface
{
}

View File

@@ -0,0 +1,29 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Di\FactoryItem class
*
* @package PhpMyAdmin\Di
*/
namespace PhpMyAdmin\Di;
/**
* Factory manager
*
* @package PhpMyAdmin\Di
*/
class FactoryItem extends ReflectorItem
{
/**
* Construct an instance
*
* @param array $params Parameters
*
* @return mixed
*/
public function get(array $params = array())
{
return $this->invoke($params);
}
}

View File

@@ -0,0 +1,25 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Di\Item class
*
* @package PhpMyAdmin\Di
*/
namespace PhpMyAdmin\Di;
/**
* Interface Item
*
* @package PhpMyAdmin\Di
*/
interface Item
{
/**
* Get a value from the item
*
* @param array $params Parameters
* @return mixed
*/
public function get(array $params = array());
}

View File

@@ -0,0 +1,20 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Di\NotFoundException class
*
* @package PhpMyAdmin\Di
*/
namespace PhpMyAdmin\Di;
use Psr\Container\NotFoundExceptionInterface;
/**
* Class NotFoundException
*
* @package PhpMyAdmin\Di
*/
class NotFoundException extends ContainerException implements NotFoundExceptionInterface
{
}

View File

@@ -0,0 +1,128 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Di\ReflectorItem class
*
* @package PhpMyAdmin\Di
*/
namespace PhpMyAdmin\Di;
/**
* Reflector manager
*
* @package PhpMyAdmin\Di
*/
abstract class ReflectorItem implements Item
{
/** @var Container */
private $_container;
/** @var \Reflector */
private $_reflector;
/**
* Constructor
*
* @param Container $container Container
* @param mixed $definition Definition
*/
public function __construct(Container $container, $definition)
{
$this->_container = $container;
$this->_reflector = self::_resolveReflector($definition);
}
/**
* Invoke the reflector with given parameters
*
* @param array $params Parameters
* @return mixed
*/
protected function invoke(array $params = array())
{
$args = array();
$reflector = $this->_reflector;
if ($reflector instanceof \ReflectionClass) {
$constructor = $reflector->getConstructor();
if (isset($constructor)) {
$args = $this->_resolveArgs(
$constructor->getParameters(),
$params
);
}
return $reflector->newInstanceArgs($args);
}
/** @var \ReflectionFunctionAbstract $reflector */
$args = $this->_resolveArgs(
$reflector->getParameters(),
$params
);
if ($reflector instanceof \ReflectionMethod) {
/** @var \ReflectionMethod $reflector */
return $reflector->invokeArgs(null, $args);
}
/** @var \ReflectionFunction $reflector */
return $reflector->invokeArgs($args);
}
/**
* Getting required arguments with given parameters
*
* @param \ReflectionParameter[] $required Arguments
* @param array $params Parameters
*
*@return array
*/
private function _resolveArgs($required, array $params = array())
{
$args = array();
foreach ($required as $param) {
$name = $param->getName();
$type = $param->getClass();
if (isset($type)) {
$type = $type->getName();
}
if (isset($params[$name])) {
$args[] = $params[$name];
} elseif (is_string($type) && isset($params[$type])) {
$args[] = $params[$type];
} else {
try {
$content = $this->_container->get($name);
if (isset($content)) {
$args[] = $content;
} elseif (is_string($type)) {
$args[] = $this->_container->get($type);
} else {
$args[] = null;
}
} catch (NotFoundException $e) {
$args[] = null;
}
}
}
return $args;
}
/**
* Resolve the reflection
*
* @param mixed $definition Definition
*
* @return \Reflector
*/
private static function _resolveReflector($definition)
{
if (function_exists($definition)) {
return new \ReflectionFunction($definition);
}
if (is_string($definition)) {
$definition = explode('::', $definition);
}
if (!isset($definition[1])) {
return new \ReflectionClass($definition[0]);
}
return new \ReflectionMethod($definition[0], $definition[1]);
}
}

View File

@@ -0,0 +1,34 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Di\ServiceItem class
*
* @package PhpMyAdmin\Di
*/
namespace PhpMyAdmin\Di;
/**
* Service manager
*
* @package PhpMyAdmin\Di
*/
class ServiceItem extends ReflectorItem
{
/** @var mixed */
protected $instance;
/**
* Get the instance of the service
*
* @param array $params Parameters
* @return mixed
*/
public function get(array $params = array())
{
if (!isset($this->instance)) {
$this->instance = $this->invoke();
}
return $this->instance;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Di\ValueItem class
*
* @package PhpMyAdmin\Di
*/
namespace PhpMyAdmin\Di;
/**
* Value manager
*
* @package PhpMyAdmin\Di
*/
class ValueItem implements Item
{
/** @var mixed */
protected $value;
/**
* Constructor
*
* @param mixed $value Value
*/
public function __construct($value)
{
$this->value = $value;
}
/**
* Get the value
*
* @param array $params Parameters
* @return mixed
*/
public function get(array $params = array())
{
return $this->value;
}
}

View File

@@ -0,0 +1,165 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Displays form for password change
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Display;
use PhpMyAdmin\Message;
use PhpMyAdmin\Server\Privileges;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
/**
* Displays form for password change
*
* @package PhpMyAdmin
*/
class ChangePassword
{
/**
* Get HTML for the Change password dialog
*
* @param string $mode where is the function being called?
* values : 'change_pw' or 'edit_other'
* @param string $username username
* @param string $hostname hostname
*
* @return string html snippet
*/
public static function getHtml($mode, $username, $hostname)
{
/**
* autocomplete feature of IE kills the "onchange" event handler and it
* must be replaced by the "onpropertychange" one in this case
*/
$chg_evt_handler = 'onchange';
$is_privileges = basename($_SERVER['SCRIPT_NAME']) === 'server_privileges.php';
$html = '<form method="post" id="change_password_form" '
. 'action="' . basename($GLOBALS['PMA_PHP_SELF']) . '" '
. 'name="chgPassword" '
. 'class="' . ($is_privileges ? 'submenu-item' : '') . '">';
$html .= Url::getHiddenInputs();
if (strpos($GLOBALS['PMA_PHP_SELF'], 'server_privileges') !== false) {
$html .= '<input type="hidden" name="username" '
. 'value="' . htmlspecialchars($username) . '" />'
. '<input type="hidden" name="hostname" '
. 'value="' . htmlspecialchars($hostname) . '" />';
}
$html .= '<fieldset id="fieldset_change_password">'
. '<legend'
. ($is_privileges
? ' data-submenu-label="' . __('Change password') . '"'
: ''
)
. '>' . __('Change password') . '</legend>'
. '<table class="data noclick">'
. '<tr>'
. '<td colspan="2">'
. '<input type="radio" name="nopass" value="1" id="nopass_1" '
. 'onclick="pma_pw.value = \'\'; pma_pw2.value = \'\'; '
. 'this.checked = true" />'
. '<label for="nopass_1">' . __('No Password') . '</label>'
. '</td>'
. '</tr>'
. '<tr class="vmiddle">'
. '<td>'
. '<input type="radio" name="nopass" value="0" id="nopass_0" '
. 'onclick="document.getElementById(\'text_pma_change_pw\').focus();" '
. 'checked="checked" />'
. '<label for="nopass_0">' . __('Password:') . '&nbsp;</label>'
. '</td>'
. '<td>'
. __('Enter:') . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp'
. '<input type="password" name="pma_pw" id="text_pma_change_pw" size="10" '
. 'class="textfield"'
. 'onkeyup="checkPasswordStrength($(this).val(), $(\'#change_password_strength_meter\'), meter_obj_label = $(\'#change_password_strength\'), PMA_commonParams.get(\'user\'));" '
. $chg_evt_handler . '="nopass[1].checked = true" />'
. '<span>Strength:</span> '
. '<meter max="4" id="change_password_strength_meter" name="pw_meter"></meter> '
. '<span id="change_password_strength" name="pw_strength">Good</span>'
. '<br>' . __('Re-type:') . '&nbsp;'
. '<input type="password" name="pma_pw2" id="text_pma_change_pw2" size="10" '
. 'class="textfield"'
. $chg_evt_handler . '="nopass[1].checked = true" />'
. '</td>'
. '</tr>';
$serverType = Util::getServerType();
$serverVersion = $GLOBALS['dbi']->getVersion();
$orig_auth_plugin = Privileges::getCurrentAuthenticationPlugin(
'change',
$username,
$hostname
);
if (($serverType == 'MySQL'
&& $serverVersion >= 50507)
|| ($serverType == 'MariaDB'
&& $serverVersion >= 50200)
) {
// Provide this option only for 5.7.6+
// OR for privileged users in 5.5.7+
if (($serverType == 'MySQL'
&& $serverVersion >= 50706)
|| ($GLOBALS['dbi']->isSuperuser() && $mode == 'edit_other')
) {
$auth_plugin_dropdown = Privileges::getHtmlForAuthPluginsDropdown(
$orig_auth_plugin, 'change_pw', 'new'
);
$html .= '<tr class="vmiddle">'
. '<td>' . __('Password Hashing:') . '</td><td>';
$html .= $auth_plugin_dropdown;
$html .= '</td></tr>'
. '<tr id="tr_element_before_generate_password"></tr>'
. '</table>';
$html .= '<div'
. ($orig_auth_plugin != 'sha256_password'
? ' class="hide"'
: '')
. ' id="ssl_reqd_warning_cp">'
. Message::notice(
__(
'This method requires using an \'<i>SSL connection</i>\' '
. 'or an \'<i>unencrypted connection that encrypts the '
. 'password using RSA</i>\'; while connecting to the server.'
)
. Util::showMySQLDocu(
'sha256-authentication-plugin'
)
)
->getDisplay()
. '</div>';
} else {
$html .= '<tr id="tr_element_before_generate_password"></tr>'
. '</table>';
}
} else {
$auth_plugin_dropdown = Privileges::getHtmlForAuthPluginsDropdown(
$orig_auth_plugin, 'change_pw', 'old'
);
$html .= '<tr class="vmiddle">'
. '<td>' . __('Password Hashing:') . '</td><td>';
$html .= $auth_plugin_dropdown . '</td></tr>'
. '<tr id="tr_element_before_generate_password"></tr>'
. '</table>';
}
$html .= '</fieldset>'
. '<fieldset id="fieldset_change_password_footer" class="tblFooters">'
. '<input type="hidden" name="change_pw" value="1" />'
. '<input type="submit" value="' . __('Go') . '" />'
. '</fieldset>'
. '</form>';
return $html;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Displays form for creating a table (if user has privileges for that)
*
* for MySQL >= 4.1.0, we should be able to detect if user has a CREATE
* privilege by looking at SHOW GRANTS output;
* for < 4.1.0, it could be more difficult because the logic tries to
* detect the current host and it might be expressed in many ways; also
* on a shared server, the user might be unable to define a controluser
* that has the proper rights to the "mysql" db;
* so we give up and assume that user has the right to create a table
*
* Note: in this case we could even skip the following "foreach" logic
*
* Addendum, 2006-01-19: ok, I give up. We got some reports about servers
* where the hostname field in mysql.user is not the same as the one
* in mysql.db for a user. In this case, SHOW GRANTS does not return
* the db-specific privileges. And probably, those users are on a shared
* server, so can't set up a control user with rights to the "mysql" db.
* We cannot reliably detect the db-specific privileges, so no more
* warnings about the lack of privileges for CREATE TABLE. Tested
* on MySQL 5.0.18.
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Display;
use PhpMyAdmin\Template;
require_once './libraries/check_user_privileges.inc.php';
/**
* PhpMyAdmin\Display\CreateTable class
*
* @package PhpMyAdmin
*/
class CreateTable
{
/**
* Returns the html for create table.
*
* @param string $db database name
*
* @return string
*/
public static function getHtml($db)
{
return Template::get('database/create_table')->render(
array('db' => $db)
);
}
}

View File

@@ -0,0 +1,806 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* functions for displaying server, database and table export
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Display;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Encoding;
use PhpMyAdmin\Message;
use PhpMyAdmin\Plugins;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Relation;
use PhpMyAdmin\Response;
use PhpMyAdmin\Table;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
/**
* PhpMyAdmin\Display\Export class
*
* @package PhpMyAdmin
*/
class Export
{
/**
* @var Relation $relation
*/
private $relation;
/**
* Constructor
*/
public function __construct()
{
$this->relation = new Relation();
}
/**
* Outputs appropriate checked statement for checkbox.
*
* @param string $str option name
*
* @return boolean
*/
private function checkboxCheck($str)
{
return isset($GLOBALS['cfg']['Export'][$str])
&& $GLOBALS['cfg']['Export'][$str];
}
/**
* Prints Html For Export Selection Options
*
* @param string $tmpSelect Tmp selected method of export
*
* @return string
*/
public function getHtmlForSelectOptions($tmpSelect = '')
{
// Check if the selected databases are defined in $_POST
// (from clicking Back button on export.php)
if (isset($_POST['db_select'])) {
$_POST['db_select'] = urldecode($_POST['db_select']);
$_POST['db_select'] = explode(",", $_POST['db_select']);
}
$databases = [];
foreach ($GLOBALS['dblist']->databases as $currentDb) {
if ($GLOBALS['dbi']->isSystemSchema($currentDb, true)) {
continue;
}
$isSelected = false;
if (isset($_POST['db_select'])) {
if (in_array($currentDb, $_POST['db_select'])) {
$isSelected = true;
}
} elseif (!empty($tmpSelect)) {
if (mb_strpos(
' ' . $tmpSelect,
'|' . $currentDb . '|'
)) {
$isSelected = true;
}
} else {
$isSelected = true;
}
$databases[] = [
'name' => $currentDb,
'is_selected' => $isSelected,
];
}
return Template::get('display/export/select_options')->render([
'databases' => $databases,
]);
}
/**
* Prints Html For Export Hidden Input
*
* @param string $exportType Selected Export Type
* @param string $db Selected DB
* @param string $table Selected Table
* @param string $singleTable Single Table
* @param string $sqlQuery SQL Query
*
* @return string
*/
public function getHtmlForHiddenInputs(
$exportType,
$db,
$table,
$singleTable,
$sqlQuery
) {
global $cfg;
// If the export method was not set, the default is quick
if (isset($_POST['export_method'])) {
$cfg['Export']['method'] = $_POST['export_method'];
} elseif (! isset($cfg['Export']['method'])) {
$cfg['Export']['method'] = 'quick';
}
if (empty($sqlQuery) && isset($_POST['sql_query'])) {
$sqlQuery = $_POST['sql_query'];
}
return Template::get('display/export/hidden_inputs')->render([
'db' => $db,
'table' => $table,
'export_type' => $exportType,
'export_method' => $cfg['Export']['method'],
'single_table' => $singleTable,
'sql_query' => $sqlQuery,
'template_id' => isset($_POST['template_id']) ? $_POST['template_id'] : '',
]);
}
/**
* Returns HTML for the options in template dropdown
*
* @param string $exportType export type - server, database, or table
*
* @return string HTML for the options in teplate dropdown
*/
private function getOptionsForTemplates($exportType)
{
// Get the relation settings
$cfgRelation = $this->relation->getRelationsParam();
$query = "SELECT `id`, `template_name` FROM "
. Util::backquote($cfgRelation['db']) . '.'
. Util::backquote($cfgRelation['export_templates'])
. " WHERE `username` = "
. "'" . $GLOBALS['dbi']->escapeString($GLOBALS['cfg']['Server']['user'])
. "' AND `export_type` = '" . $GLOBALS['dbi']->escapeString($exportType) . "'"
. " ORDER BY `template_name`;";
$result = $this->relation->queryAsControlUser($query);
$templates = [];
if ($result !== false) {
while ($row = $GLOBALS['dbi']->fetchAssoc($result, DatabaseInterface::CONNECT_CONTROL)) {
$templates[] = [
'name' => $row['template_name'],
'id' => $row['id'],
];
}
}
return Template::get('display/export/template_options')->render([
'templates' => $templates,
'selected_template' => !empty($_POST['template_id']) ? $_POST['template_id'] : null,
]);
}
/**
* Prints Html For Export Options Method
*
* @return string
*/
private function getHtmlForOptionsMethod()
{
global $cfg;
if (isset($_POST['quick_or_custom'])) {
$exportMethod = $_POST['quick_or_custom'];
} else {
$exportMethod = $cfg['Export']['method'];
}
return Template::get('display/export/method')->render([
'export_method' => $exportMethod,
]);
}
/**
* Prints Html For Export Options Selection
*
* @param string $exportType Selected Export Type
* @param string $multiValues Export Options
*
* @return string
*/
private function getHtmlForOptionsSelection($exportType, $multiValues)
{
return Template::get('display/export/selection')->render([
'export_type' => $exportType,
'multi_values' => $multiValues,
]);
}
/**
* Prints Html For Export Options Format dropdown
*
* @param ExportPlugin[] $exportList Export List
*
* @return string
*/
private function getHtmlForOptionsFormatDropdown($exportList)
{
$dropdown = Plugins::getChoice('Export', 'what', $exportList, 'format');
return Template::get('display/export/format_dropdown')->render([
'dropdown' => $dropdown,
]);
}
/**
* Prints Html For Export Options Format-specific options
*
* @param ExportPlugin[] $exportList Export List
*
* @return string
*/
private function getHtmlForOptionsFormat($exportList)
{
global $cfg;
$options = Plugins::getOptions('Export', $exportList);
return Template::get('display/export/options_format')->render([
'options' => $options,
'can_convert_kanji' => Encoding::canConvertKanji(),
'exec_time_limit' => $cfg['ExecTimeLimit'],
]);
}
/**
* Prints Html For Export Options Rows
*
* @param string $db Selected DB
* @param string $table Selected Table
* @param string $unlimNumRows Num of Rows
*
* @return string
*/
private function getHtmlForOptionsRows($db, $table, $unlimNumRows)
{
$tableObject = new Table($table, $db);
$numberOfRows = $tableObject->countRecords();
return Template::get('display/export/options_rows')->render([
'allrows' => isset($_POST['allrows']) ? $_POST['allrows'] : null,
'limit_to' => isset($_POST['limit_to']) ? $_POST['limit_to'] : null,
'limit_from' => isset($_POST['limit_from']) ? $_POST['limit_from'] : null,
'unlim_num_rows' => $unlimNumRows,
'number_of_rows' => $numberOfRows,
]);
}
/**
* Prints Html For Export Options Quick Export
*
* @return string
*/
private function getHtmlForOptionsQuickExport()
{
global $cfg;
$saveDir = Util::userDir($cfg['SaveDir']);
$exportIsChecked = $this->checkboxCheck(
'quick_export_onserver'
);
$exportOverwriteIsChecked = $this->checkboxCheck(
'quick_export_onserver_overwrite'
);
return Template::get('display/export/options_quick_export')->render([
'save_dir' => $saveDir,
'export_is_checked' => $exportIsChecked,
'export_overwrite_is_checked' => $exportOverwriteIsChecked,
]);
}
/**
* Prints Html For Export Options Save Dir
*
* @return string
*/
private function getHtmlForOptionsOutputSaveDir()
{
global $cfg;
$saveDir = Util::userDir($cfg['SaveDir']);
$exportIsChecked = $this->checkboxCheck(
'onserver'
);
$exportOverwriteIsChecked = $this->checkboxCheck(
'onserver_overwrite'
);
return Template::get('display/export/options_output_save_dir')->render([
'save_dir' => $saveDir,
'export_is_checked' => $exportIsChecked,
'export_overwrite_is_checked' => $exportOverwriteIsChecked,
]);
}
/**
* Prints Html For Export Options
*
* @param string $exportType Selected Export Type
*
* @return string
*/
private function getHtmlForOptionsOutputFormat($exportType)
{
$trans = new Message;
$trans->addText(__('@SERVER@ will become the server name'));
if ($exportType == 'database' || $exportType == 'table') {
$trans->addText(__(', @DATABASE@ will become the database name'));
if ($exportType == 'table') {
$trans->addText(__(', @TABLE@ will become the table name'));
}
}
$msg = new Message(
__(
'This value is interpreted using %1$sstrftime%2$s, '
. 'so you can use time formatting strings. '
. 'Additionally the following transformations will happen: %3$s. '
. 'Other text will be kept as is. See the %4$sFAQ%5$s for details.'
)
);
$msg->addParamHtml(
'<a href="' . Core::linkURL(Core::getPHPDocLink('function.strftime.php'))
. '" target="documentation" title="' . __('Documentation') . '">'
);
$msg->addParamHtml('</a>');
$msg->addParam($trans);
$docUrl = Util::getDocuLink('faq', 'faq6-27');
$msg->addParamHtml(
'<a href="' . $docUrl . '" target="documentation">'
);
$msg->addParamHtml('</a>');
if (isset($_POST['filename_template'])) {
$filenameTemplate = $_POST['filename_template'];
} else {
if ($exportType == 'database') {
$filenameTemplate = $GLOBALS['PMA_Config']->getUserValue(
'pma_db_filename_template',
$GLOBALS['cfg']['Export']['file_template_database']
);
} elseif ($exportType == 'table') {
$filenameTemplate = $GLOBALS['PMA_Config']->getUserValue(
'pma_table_filename_template',
$GLOBALS['cfg']['Export']['file_template_table']
);
} else {
$filenameTemplate = $GLOBALS['PMA_Config']->getUserValue(
'pma_server_filename_template',
$GLOBALS['cfg']['Export']['file_template_server']
);
}
}
return Template::get('display/export/options_output_format')->render([
'message' => $msg->getMessage(),
'filename_template' => $filenameTemplate,
'is_checked' => $this->checkboxCheck('remember_file_template'),
]);
}
/**
* Prints Html For Export Options Charset
*
* @return string
*/
private function getHtmlForOptionsOutputCharset()
{
global $cfg;
return Template::get('display/export/options_output_charset')->render([
'encodings' => Encoding::listEncodings(),
'export_charset' => $cfg['Export']['charset'],
]);
}
/**
* Prints Html For Export Options Compression
*
* @return string
*/
private function getHtmlForOptionsOutputCompression()
{
global $cfg;
if (isset($_POST['compression'])) {
$selectedCompression = $_POST['compression'];
} elseif (isset($cfg['Export']['compression'])) {
$selectedCompression = $cfg['Export']['compression'];
} else {
$selectedCompression = 'none';
}
// Since separate files export works with ZIP only
if (isset($cfg['Export']['as_separate_files'])
&& $cfg['Export']['as_separate_files']
) {
$selectedCompression = 'zip';
}
// zip and gzip encode features
$isZip = ($cfg['ZipDump'] && function_exists('gzcompress'));
$isGzip = ($cfg['GZipDump'] && function_exists('gzencode'));
return Template::get('display/export/options_output_compression')->render([
'is_zip' => $isZip,
'is_gzip' => $isGzip,
'selected_compression' => $selectedCompression,
]);
}
/**
* Prints Html For Export Options Radio
*
* @return string
*/
private function getHtmlForOptionsOutputRadio()
{
return Template::get('display/export/options_output_radio')->render([
'has_repopulate' => isset($_POST['repopulate']),
'export_asfile' => $GLOBALS['cfg']['Export']['asfile'],
]);
}
/**
* Prints Html For Export Options Checkbox - Separate files
*
* @param string $exportType Selected Export Type
*
* @return string
*/
private function getHtmlForOptionsOutputSeparateFiles($exportType)
{
$isChecked = $this->checkboxCheck('as_separate_files');
return Template::get('display/export/options_output_separate_files')->render([
'is_checked' => $isChecked,
'export_type' => $exportType,
]);
}
/**
* Prints Html For Export Options
*
* @param string $exportType Selected Export Type
*
* @return string
*/
private function getHtmlForOptionsOutput($exportType)
{
global $cfg;
$hasAliases = isset($_SESSION['tmpval']['aliases'])
&& !Core::emptyRecursive($_SESSION['tmpval']['aliases']);
unset($_SESSION['tmpval']['aliases']);
$isCheckedLockTables = $this->checkboxCheck('lock_tables');
$isCheckedAsfile = $this->checkboxCheck('asfile');
$optionsOutputSaveDir = '';
if (isset($cfg['SaveDir']) && !empty($cfg['SaveDir'])) {
$optionsOutputSaveDir = $this->getHtmlForOptionsOutputSaveDir();
}
$optionsOutputFormat = $this->getHtmlForOptionsOutputFormat($exportType);
$optionsOutputCharset = '';
if (Encoding::isSupported()) {
$optionsOutputCharset = $this->getHtmlForOptionsOutputCharset();
}
$optionsOutputCompression = $this->getHtmlForOptionsOutputCompression();
$optionsOutputSeparateFiles = '';
if ($exportType == 'server' || $exportType == 'database') {
$optionsOutputSeparateFiles = $this->getHtmlForOptionsOutputSeparateFiles(
$exportType
);
}
$optionsOutputRadio = $this->getHtmlForOptionsOutputRadio();
return Template::get('display/export/options_output')->render([
'has_aliases' => $hasAliases,
'export_type' => $exportType,
'is_checked_lock_tables' => $isCheckedLockTables,
'is_checked_asfile' => $isCheckedAsfile,
'repopulate' => isset($_POST['repopulate']),
'lock_tables' => isset($_POST['lock_tables']),
'save_dir' => isset($cfg['SaveDir']) ? $cfg['SaveDir'] : null,
'is_encoding_supported' => Encoding::isSupported(),
'options_output_save_dir' => $optionsOutputSaveDir,
'options_output_format' => $optionsOutputFormat,
'options_output_charset' => $optionsOutputCharset,
'options_output_compression' => $optionsOutputCompression,
'options_output_separate_files' => $optionsOutputSeparateFiles,
'options_output_radio' => $optionsOutputRadio,
]);
}
/**
* Prints Html For Export Options
*
* @param string $exportType Selected Export Type
* @param string $db Selected DB
* @param string $table Selected Table
* @param string $multiValues Export selection
* @param string $numTables number of tables
* @param ExportPlugin[] $exportList Export List
* @param string $unlimNumRows Number of Rows
*
* @return string
*/
public function getHtmlForOptions(
$exportType,
$db,
$table,
$multiValues,
$numTables,
$exportList,
$unlimNumRows
) {
global $cfg;
$html = $this->getHtmlForOptionsMethod();
$html .= $this->getHtmlForOptionsFormatDropdown($exportList);
$html .= $this->getHtmlForOptionsSelection($exportType, $multiValues);
$tableObject = new Table($table, $db);
if (strlen($table) > 0 && empty($numTables) && ! $tableObject->isMerge()) {
$html .= $this->getHtmlForOptionsRows($db, $table, $unlimNumRows);
}
if (isset($cfg['SaveDir']) && !empty($cfg['SaveDir'])) {
$html .= $this->getHtmlForOptionsQuickExport();
}
$html .= $this->getHtmlForAliasModalDialog();
$html .= $this->getHtmlForOptionsOutput($exportType);
$html .= $this->getHtmlForOptionsFormat($exportList);
return $html;
}
/**
* Generate Html For currently defined aliases
*
* @return string
*/
private function getHtmlForCurrentAlias()
{
$result = '<table id="alias_data"><thead><tr><th colspan="4">'
. __('Defined aliases')
. '</th></tr></thead><tbody>';
$template = Template::get('export/alias_item');
if (isset($_SESSION['tmpval']['aliases'])) {
foreach ($_SESSION['tmpval']['aliases'] as $db => $dbData) {
if (isset($dbData['alias'])) {
$result .= $template->render(array(
'type' => _pgettext('Alias', 'Database'),
'name' => $db,
'field' => 'aliases[' . $db . '][alias]',
'value' => $dbData['alias'],
));
}
if (! isset($dbData['tables'])) {
continue;
}
foreach ($dbData['tables'] as $table => $tableData) {
if (isset($tableData['alias'])) {
$result .= $template->render(array(
'type' => _pgettext('Alias', 'Table'),
'name' => $db . '.' . $table,
'field' => 'aliases[' . $db . '][tables][' . $table . '][alias]',
'value' => $tableData['alias'],
));
}
if (! isset($tableData['columns'])) {
continue;
}
foreach ($tableData['columns'] as $column => $columnName) {
$result .= $template->render(array(
'type' => _pgettext('Alias', 'Column'),
'name' => $db . '.' . $table . '.'. $column,
'field' => 'aliases[' . $db . '][tables][' . $table . '][colums][' . $column . ']',
'value' => $columnName,
));
}
}
}
}
// Empty row for javascript manipulations
$result .= '</tbody><tfoot class="hide">' . $template->render(array(
'type' => '', 'name' => '', 'field' => 'aliases_new', 'value' => ''
)) . '</tfoot>';
return $result . '</table>';
}
/**
* Generate Html For Alias Modal Dialog
*
* @return string
*/
public function getHtmlForAliasModalDialog()
{
$title = __('Rename exported databases/tables/columns');
$html = '<div id="alias_modal" class="hide" title="' . $title . '">';
$html .= $this->getHtmlForCurrentAlias();
$html .= Template::get('export/alias_add')->render();
$html .= '</div>';
return $html;
}
/**
* Gets HTML to display export dialogs
*
* @param string $exportType export type: server|database|table
* @param string $db selected DB
* @param string $table selected table
* @param string $sqlQuery SQL query
* @param int $numTables number of tables
* @param int $unlimNumRows unlimited number of rows
* @param string $multiValues selector options
*
* @return string $html
*/
public function getDisplay(
$exportType,
$db,
$table,
$sqlQuery,
$numTables,
$unlimNumRows,
$multiValues
) {
$cfgRelation = $this->relation->getRelationsParam();
if (isset($_POST['single_table'])) {
$GLOBALS['single_table'] = $_POST['single_table'];
}
// Export a single table
if (isset($_GET['single_table'])) {
$GLOBALS['single_table'] = $_GET['single_table'];
}
/* Scan for plugins */
/* @var $exportList ExportPlugin[] */
$exportList = Plugins::getPlugins(
"export",
'libraries/classes/Plugins/Export/',
array(
'export_type' => $exportType,
'single_table' => isset($GLOBALS['single_table'])
)
);
/* Fail if we didn't find any plugin */
if (empty($exportList)) {
Message::error(
__('Could not load export plugins, please check your installation!')
)->display();
exit;
}
$html = Template::get('display/export/option_header')->render([
'export_type' => $exportType,
'db' => $db,
'table' => $table,
]);
if ($cfgRelation['exporttemplateswork']) {
$html .= Template::get('display/export/template_loading')->render([
'options' => $this->getOptionsForTemplates($exportType),
]);
}
$html .= '<form method="post" action="export.php" '
. ' name="dump" class="disableAjax">';
//output Hidden Inputs
$singleTableStr = isset($GLOBALS['single_table']) ? $GLOBALS['single_table']
: '';
$html .= $this->getHtmlForHiddenInputs(
$exportType,
$db,
$table,
$singleTableStr,
$sqlQuery
);
//output Export Options
$html .= $this->getHtmlForOptions(
$exportType,
$db,
$table,
$multiValues,
$numTables,
$exportList,
$unlimNumRows
);
$html .= '</form>';
return $html;
}
/**
* Handles export template actions
*
* @param array $cfgRelation Relation configuration
*
* @return void
*/
public function handleTemplateActions(array $cfgRelation)
{
if (isset($_POST['templateId'])) {
$id = $GLOBALS['dbi']->escapeString($_POST['templateId']);
} else {
$id = '';
}
$templateTable = Util::backquote($cfgRelation['db']) . '.'
. Util::backquote($cfgRelation['export_templates']);
$user = $GLOBALS['dbi']->escapeString($GLOBALS['cfg']['Server']['user']);
switch ($_POST['templateAction']) {
case 'create':
$query = "INSERT INTO " . $templateTable . "("
. " `username`, `export_type`,"
. " `template_name`, `template_data`"
. ") VALUES ("
. "'" . $user . "', "
. "'" . $GLOBALS['dbi']->escapeString($_POST['exportType'])
. "', '" . $GLOBALS['dbi']->escapeString($_POST['templateName'])
. "', '" . $GLOBALS['dbi']->escapeString($_POST['templateData'])
. "');";
break;
case 'load':
$query = "SELECT `template_data` FROM " . $templateTable
. " WHERE `id` = " . $id . " AND `username` = '" . $user . "'";
break;
case 'update':
$query = "UPDATE " . $templateTable . " SET `template_data` = "
. "'" . $GLOBALS['dbi']->escapeString($_POST['templateData']) . "'"
. " WHERE `id` = " . $id . " AND `username` = '" . $user . "'";
break;
case 'delete':
$query = "DELETE FROM " . $templateTable
. " WHERE `id` = " . $id . " AND `username` = '" . $user . "'";
break;
default:
$query = '';
break;
}
$result = $this->relation->queryAsControlUser($query, false);
$response = Response::getInstance();
if (! $result) {
$error = $GLOBALS['dbi']->getError(DatabaseInterface::CONNECT_CONTROL);
$response->setRequestStatus(false);
$response->addJSON('message', $error);
exit;
}
$response->setRequestStatus(true);
if ('create' == $_POST['templateAction']) {
$response->addJSON(
'data',
$this->getOptionsForTemplates($_POST['exportType'])
);
} elseif ('load' == $_POST['templateAction']) {
$data = null;
while ($row = $GLOBALS['dbi']->fetchAssoc(
$result, DatabaseInterface::CONNECT_CONTROL
)) {
$data = $row['template_data'];
}
$response->addJSON('data', $data);
}
$GLOBALS['dbi']->freeResult($result);
}
}

View File

@@ -0,0 +1,103 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Displays git revision
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Display;
use PhpMyAdmin\Core;
use PhpMyAdmin\Response;
use PhpMyAdmin\Util;
/**
* PhpMyAdmin\Display\GitRevision class
*
* @package PhpMyAdmin
*/
class GitRevision
{
/**
* Prints details about the current Git commit revision
*
* @return void
*/
public static function display()
{
// load revision data from repo
$GLOBALS['PMA_Config']->checkGitRevision();
if (! $GLOBALS['PMA_Config']->get('PMA_VERSION_GIT')) {
$response = Response::getInstance();
$response->setRequestStatus(false);
return;
}
// if using a remote commit fast-forwarded, link to GitHub
$commit_hash = substr(
$GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_COMMITHASH'),
0,
7
);
$commit_hash = '<strong title="'
. htmlspecialchars($GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_MESSAGE'))
. '">' . htmlspecialchars($commit_hash) . '</strong>';
if ($GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_ISREMOTECOMMIT')) {
$commit_hash = '<a href="'
. Core::linkURL(
'https://github.com/phpmyadmin/phpmyadmin/commit/'
. htmlspecialchars($GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_COMMITHASH'))
)
. '" rel="noopener noreferrer" target="_blank">' . $commit_hash . '</a>';
}
$branch = $GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_BRANCH');
$isRemoteBranch = $GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_ISREMOTEBRANCH');
if ($isRemoteBranch) {
$branch = '<a href="'
. Core::linkURL(
'https://github.com/phpmyadmin/phpmyadmin/tree/'
. $GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_BRANCH')
)
. '" rel="noopener noreferrer" target="_blank">' . htmlspecialchars($branch) . '</a>';
}
if ($branch !== false) {
$branch = sprintf(
__('%1$s from %2$s branch'),
$commit_hash,
$isRemoteBranch ? $branch : htmlspecialchars($branch)
);
} else {
$branch = $commit_hash . ' (' . __('no branch') . ')';
}
$committer = $GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_COMMITTER');
$author = $GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_AUTHOR');
Core::printListItem(
__('Git revision:') . ' '
. $branch . ',<br /> '
. sprintf(
__('committed on %1$s by %2$s'),
Util::localisedDate(strtotime($committer['date'])),
'<a href="' . Core::linkURL(
'mailto:' . htmlspecialchars($committer['email'])
) . '">'
. htmlspecialchars($committer['name']) . '</a>'
)
. ($author != $committer
? ', <br />'
. sprintf(
__('authored on %1$s by %2$s'),
Util::localisedDate(strtotime($author['date'])),
'<a href="' . Core::linkURL(
'mailto:' . htmlspecialchars($author['email'])
) . '">'
. htmlspecialchars($author['name']) . '</a>'
)
: ''),
'li_pma_version_git', null, null, null
);
}
}

View File

@@ -0,0 +1,111 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* functions for displaying import for: server, database and table
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Display;
use PhpMyAdmin\Core;
use PhpMyAdmin\Display\ImportAjax;
use PhpMyAdmin\Encoding;
use PhpMyAdmin\Message;
use PhpMyAdmin\Plugins;
use PhpMyAdmin\Template;
/**
* PhpMyAdmin\Display\Import class
*
* @package PhpMyAdmin
*/
class Import
{
/**
* Gets HTML to display import dialogs
*
* @param string $importType Import type: server|database|table
* @param string $db Selected DB
* @param string $table Selected Table
* @param int $maxUploadSize Max upload size
*
* @return string HTML
*/
public static function get($importType, $db, $table, $maxUploadSize)
{
global $cfg;
global $SESSION_KEY;
list(
$SESSION_KEY,
$uploadId,
) = ImportAjax::uploadProgressSetup();
/* Scan for plugins */
/* @var $importList \PhpMyAdmin\Plugins\ImportPlugin[] */
$importList = Plugins::getPlugins(
"import",
'libraries/classes/Plugins/Import/',
$importType
);
/* Fail if we didn't find any plugin */
if (empty($importList)) {
Message::error(
__(
'Could not load import plugins, please check your installation!'
)
)->display();
exit;
}
if (Core::isValid($_REQUEST['offset'], 'numeric')) {
$offset = intval($_REQUEST['offset']);
}
if (isset($_REQUEST['timeout_passed'])) {
$timeoutPassed = $_REQUEST['timeout_passed'];
}
$localImportFile = '';
if (isset($_REQUEST['local_import_file'])) {
$localImportFile = $_REQUEST['local_import_file'];
}
// zip, gzip and bzip2 encode features
$compressions = array();
if ($cfg['GZipDump'] && function_exists('gzopen')) {
$compressions[] = 'gzip';
}
if ($cfg['BZipDump'] && function_exists('bzopen')) {
$compressions[] = 'bzip2';
}
if ($cfg['ZipDump'] && function_exists('zip_open')) {
$compressions[] = 'zip';
}
return Template::get('display/import/import')->render([
'upload_id' => $uploadId,
'handler' => $_SESSION[$SESSION_KEY]["handler"],
'id_key' => $_SESSION[$SESSION_KEY]['handler']::getIdKey(),
'pma_theme_image' => $GLOBALS['pmaThemeImage'],
'import_type' => $importType,
'db' => $db,
'table' => $table,
'max_upload_size' => $maxUploadSize,
'import_list' => $importList,
'local_import_file' => $localImportFile,
'is_upload' => $GLOBALS['is_upload'],
'upload_dir' => isset($cfg['UploadDir']) ? $cfg['UploadDir'] : null,
'timeout_passed_global' => isset($GLOBALS['timeout_passed']) ? $GLOBALS['timeout_passed'] : null,
'compressions' => $compressions,
'is_encoding_supported' => Encoding::isSupported(),
'encodings' => Encoding::listEncodings(),
'import_charset' => isset($cfg['Import']['charset']) ? $cfg['Import']['charset'] : null,
'dbi' => $GLOBALS['dbi'],
'disable_is' => $cfg['Server']['DisableIS'],
'timeout_passed' => isset($timeoutPassed) ? $timeoutPassed : null,
'offset' => isset($offset) ? $offset : null,
'can_convert_kanji' => Encoding::canConvertKanji(),
]);
}
}

View File

@@ -0,0 +1,134 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Handles plugins that show the upload progress
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Display;
use PhpMyAdmin\Core;
/**
* PhpMyAdmin\Display\ImportAjax class
*
* @package PhpMyAdmin
*/
class ImportAjax
{
/**
* Sets up some variables for upload progress
*
* @return array
*/
public static function uploadProgressSetup()
{
/**
* constant for differentiating array in $_SESSION variable
*/
$SESSION_KEY = '__upload_status';
/**
* sets default plugin for handling the import process
*/
$_SESSION[$SESSION_KEY]["handler"] = "";
/**
* unique ID for each upload
*/
$upload_id = uniqid("");
/**
* list of available plugins
*/
$plugins = array(
// PHP 5.4 session-based upload progress is problematic, see bug 3964
//"session",
"progress",
"apc",
"noplugin"
);
// select available plugin
foreach ($plugins as $plugin) {
$check = $plugin . "Check";
if (self::$check()) {
$upload_class = 'PhpMyAdmin\Plugins\Import\Upload\Upload' . ucwords(
$plugin
);
$_SESSION[$SESSION_KEY]["handler"] = $upload_class;
break;
}
}
return array($SESSION_KEY, $upload_id, $plugins);
}
/**
* Checks if APC bar extension is available and configured correctly.
*
* @return boolean true if APC extension is available and if rfc1867 is enabled,
* false if it is not
*/
public static function apcCheck()
{
if (! extension_loaded('apc')
|| ! function_exists('apc_fetch')
|| ! function_exists('getallheaders')
) {
return false;
}
return (ini_get('apc.enabled') && ini_get('apc.rfc1867'));
}
/**
* Checks if PhpMyAdmin\Plugins\Import\Upload\UploadProgress bar extension is
* available.
*
* @return boolean true if PhpMyAdmin\Plugins\Import\Upload\UploadProgress
* extension is available, false if it is not
*/
public static function progressCheck()
{
return function_exists("uploadprogress_get_info")
&& function_exists('getallheaders');
}
/**
* Checks if PHP 5.4 session upload-progress feature is available.
*
* @return boolean true if PHP 5.4 session upload-progress is available,
* false if it is not
*/
public static function sessionCheck()
{
return ini_get('session.upload_progress.enabled');
}
/**
* Default plugin for handling import.
* If no other plugin is available, noplugin is used.
*
* @return boolean true
*/
public static function nopluginCheck()
{
return true;
}
/**
* The function outputs json encoded status of uploaded.
* It uses PMA_getUploadStatus, which is defined in plugin's file.
*
* @param string $id ID of transfer, usually $upload_id
*
* @return void
*/
public static function status($id)
{
Core::headerJSON();
echo json_encode(
$_SESSION[$GLOBALS['SESSION_KEY']]['handler']::getUploadStatus($id)
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,334 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Hold the PhpMyAdmin\Encoding class
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Core;
use PhpMyAdmin\Template;
/**
* Encoding conversion helper class
*
* @package PhpMyAdmin
*/
class Encoding
{
/**
* None encoding conversion engine
*
* @var int
*/
const ENGINE_NONE = 0;
/**
* iconv encoding conversion engine
*
* @var int
*/
const ENGINE_ICONV = 1;
/**
* recode encoding conversion engine
*
* @var int
*/
const ENGINE_RECODE = 2;
/**
* mbstring encoding conversion engine
*
* @var int
*/
const ENGINE_MB = 3;
/**
* Chosen encoding engine
*
* @var int
*/
private static $_engine = null;
/**
* Map of conversion engine configurations
*
* Each entry contains:
*
* - function to detect
* - engine contant
* - extension name to warn when missing
*
* @var array
*/
private static $_enginemap = array(
'iconv' => array('iconv', self::ENGINE_ICONV, 'iconv'),
'recode' => array('recode_string', self::ENGINE_RECODE, 'recode'),
'mb' => array('mb_convert_encoding', self::ENGINE_MB, 'mbstring'),
'none' => array('isset', self::ENGINE_NONE, ''),
);
/**
* Order of automatic detection of engines
*
* @var array
*/
private static $_engineorder = array(
'iconv', 'mb', 'recode',
);
/**
* Kanji encodings list
*
* @var string
*/
private static $_kanji_encodings = 'ASCII,SJIS,EUC-JP,JIS';
/**
* Initializes encoding engine detecting available backends.
*
* @return void
*/
public static function initEngine()
{
$engine = 'auto';
if (isset($GLOBALS['cfg']['RecodingEngine'])) {
$engine = $GLOBALS['cfg']['RecodingEngine'];
}
/* Use user configuration */
if (isset(self::$_enginemap[$engine])) {
if (function_exists(self::$_enginemap[$engine][0])) {
self::$_engine = self::$_enginemap[$engine][1];
return;
} else {
Core::warnMissingExtension(self::$_enginemap[$engine][2]);
}
}
/* Autodetection */
foreach (self::$_engineorder as $engine) {
if (function_exists(self::$_enginemap[$engine][0])) {
self::$_engine = self::$_enginemap[$engine][1];
return;
}
}
/* Fallback to none conversion */
self::$_engine = self::ENGINE_NONE;
}
/**
* Setter for engine. Use with caution, mostly useful for testing.
*
* @param int $engine Engine enconding
*
* @return void
*/
public static function setEngine($engine)
{
self::$_engine = $engine;
}
/**
* Checks whether there is any charset conversion supported
*
* @return bool
*/
public static function isSupported()
{
if (is_null(self::$_engine)) {
self::initEngine();
}
return self::$_engine != self::ENGINE_NONE;
}
/**
* Converts encoding of text according to parameters with detected
* conversion function.
*
* @param string $src_charset source charset
* @param string $dest_charset target charset
* @param string $what what to convert
*
* @return string converted text
*
* @access public
*/
public static function convertString($src_charset, $dest_charset, $what)
{
if ($src_charset == $dest_charset) {
return $what;
}
if (is_null(self::$_engine)) {
self::initEngine();
}
switch (self::$_engine) {
case self::ENGINE_RECODE:
return recode_string(
$src_charset . '..' . $dest_charset,
$what
);
case self::ENGINE_ICONV:
return iconv(
$src_charset,
$dest_charset .
(isset($GLOBALS['cfg']['IconvExtraParams']) ? $GLOBALS['cfg']['IconvExtraParams'] : ''),
$what
);
case self::ENGINE_MB:
return mb_convert_encoding(
$what,
$dest_charset,
$src_charset
);
default:
return $what;
}
}
/**
* Detects whether Kanji encoding is available
*
* @return bool
*/
public static function canConvertKanji()
{
return $GLOBALS['lang'] == 'ja';
}
/**
* Setter for Kanji encodings. Use with caution, mostly useful for testing.
*
* @return string
*/
public static function getKanjiEncodings()
{
return self::$_kanji_encodings;
}
/**
* Setter for Kanji encodings. Use with caution, mostly useful for testing.
*
* @param string $value Kanji encodings list
*
* @return void
*/
public static function setKanjiEncodings($value)
{
self::$_kanji_encodings = $value;
}
/**
* Reverses SJIS & EUC-JP position in the encoding codes list
*
* @return void
*/
public static function kanjiChangeOrder()
{
$parts = explode(',', self::$_kanji_encodings);
if ($parts[1] == 'EUC-JP') {
self::$_kanji_encodings = 'ASCII,SJIS,EUC-JP,JIS';
} else {
self::$_kanji_encodings = 'ASCII,EUC-JP,SJIS,JIS';
}
}
/**
* Kanji string encoding convert
*
* @param string $str the string to convert
* @param string $enc the destination encoding code
* @param string $kana set 'kana' convert to JIS-X208-kana
*
* @return string the converted string
*/
public static function kanjiStrConv($str, $enc, $kana)
{
if ($enc == '' && $kana == '') {
return $str;
}
$string_encoding = mb_detect_encoding($str, self::$_kanji_encodings);
if ($string_encoding === false) {
$string_encoding = 'utf-8';
}
if ($kana == 'kana') {
$dist = mb_convert_kana($str, 'KV', $string_encoding);
$str = $dist;
}
if ($string_encoding != $enc && $enc != '') {
$dist = mb_convert_encoding($str, $enc, $string_encoding);
} else {
$dist = $str;
}
return $dist;
}
/**
* Kanji file encoding convert
*
* @param string $file the name of the file to convert
* @param string $enc the destination encoding code
* @param string $kana set 'kana' convert to JIS-X208-kana
*
* @return string the name of the converted file
*/
public static function kanjiFileConv($file, $enc, $kana)
{
if ($enc == '' && $kana == '') {
return $file;
}
$tmpfname = tempnam($GLOBALS['PMA_Config']->getUploadTempDir(), $enc);
$fpd = fopen($tmpfname, 'wb');
$fps = fopen($file, 'r');
self::kanjiChangeOrder();
while (!feof($fps)) {
$line = fgets($fps, 4096);
$dist = self::kanjiStrConv($line, $enc, $kana);
fputs($fpd, $dist);
} // end while
self::kanjiChangeOrder();
fclose($fps);
fclose($fpd);
unlink($file);
return $tmpfname;
}
/**
* Defines radio form fields to switch between encoding modes
*
* @return string xhtml code for the radio controls
*/
public static function kanjiEncodingForm()
{
return Template::get('encoding/kanji_encoding_form')->render();
}
/**
* Lists available encodings.
*
* @return array
*/
public static function listEncodings()
{
if (is_null(self::$_engine)) {
self::initEngine();
}
/* Most engines do not support listing */
if (self::$_engine != self::ENGINE_MB) {
return $GLOBALS['cfg']['AvailableCharsets'];
}
return array_intersect(
array_map('strtolower', mb_list_encodings()),
$GLOBALS['cfg']['AvailableCharsets']
);
}
}

Some files were not shown because too many files have changed in this diff Show More