/********************************************************************************** * nWordPress is an automated migration of WordPress 2.5.1 performed by Numiton. * * copyright : (C) 2008 Numiton - www.numiton.com * email : numiton@users.sourceforge.net * * $Id: wpdb.java,v 1.4 2008/10/14 13:15:48 numiton Exp $ * **********************************************************************************/ /********************************************************************************** * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * **********************************************************************************/ /*************************************************************************** * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * ***************************************************************************/ package org.numiton.nwp.wp_includes; import static com.numiton.PhpCommonConstants.BOOLEAN_FALSE; import static com.numiton.VarHandling.*; import static com.numiton.generic.PhpWeb.getIncluded; import java.io.Serializable; import java.util.Map; import org.apache.log4j.Logger; import org.numiton.nwp.GlobalConsts; import org.numiton.nwp.GlobalVars; import com.numiton.*; import com.numiton.array.Array; import com.numiton.array.ArrayEntry; import com.numiton.db.MySQL; import com.numiton.error.ErrorHandling; import com.numiton.file.FileSystemOrSocket; import com.numiton.generic.*; import com.numiton.ntile.til.libraries.php.quercus.QRegExPerl; import com.numiton.ntile.til.libraries.php.quercus.QStrings; import com.numiton.string.Strings; public class wpdb implements ContextCarrierInterface, Serializable, Cloneable { protected static final Logger LOG = Logger.getLogger(wpdb.class.getName()); public GlobalConsts gConsts; public GlobalVars gVars; public boolean show_errors = false; public boolean suppress_errors = false; public String last_error = ""; public int num_queries = 0; public String last_query; public Array<StdClass> col_info = new Array<StdClass>(); public Array<Object> queries = new Array<Object>(); public String prefix = ""; public boolean ready = false; // Our tables public String posts; public String users; public String categories; public String post2cat; public String comments; public String links; public String options; public String postmeta; public String usermeta; public String terms; public String term_taxonomy; public String term_relationships; public Array<Object> tables = new Array<Object>(new ArrayEntry<Object>("users"), new ArrayEntry<Object>("usermeta"), new ArrayEntry<Object>("posts"), new ArrayEntry<Object>("categories"), new ArrayEntry<Object>("post2cat"), new ArrayEntry<Object>("comments"), new ArrayEntry<Object>("links"), new ArrayEntry<Object>("link2cat"), new ArrayEntry<Object>("options"), new ArrayEntry<Object>("postmeta"), new ArrayEntry<Object>("terms"), new ArrayEntry<Object>("term_taxonomy"), new ArrayEntry<Object>("term_relationships")); public Object charset; public Object collate; public int dbh; public Array<Object> last_result = new Array<Object>(); public Object func_call; public int result; public int rows_affected; public int insert_id; public int num_rows; public int time_start; public WP_Error error; public Object link2cat; public Array<Object> EZSQL_ERROR = new Array<Object>(); /** * Connects to the database server and selects a database * @param string $dbuser * @param string $dbpassword * @param string $dbname * @param string $dbhost */ // Commented by Numiton // public wpdb(GlobalVars javaGlobalVariables, GlobalConsts // javaGlobalConstants, Object dbuser, Object dbpassword, Object dbname, // Object dbhost) { // setContext(javaGlobalVariables, javaGlobalConstants); // return this.__construct(dbuser, dbpassword, dbname, dbhost); // } public wpdb(GlobalVars javaGlobalVariables, GlobalConsts javaGlobalConstants, String dbuser, String dbpassword, String dbname, String dbhost) { setContext(javaGlobalVariables, javaGlobalConstants); FunctionHandling.register_shutdown_function(gVars.webEnv, new Callback("__destruct", this)); if (gConsts.isWP_DEBUGDefined() && equal(gConsts.getWP_DEBUG(), true)) { this.show_errors(); } if (gConsts.isDB_CHARSETDefined()) { this.charset = gConsts.getDB_CHARSET(); } if (gConsts.isDB_COLLATEDefined()) { this.collate = gConsts.getDB_COLLATE(); } this.dbh = MySQL.mysql_connect(gVars.webEnv, dbhost, dbuser, dbpassword, true); if (!booleanval(this.dbh)) { this.bail( "\n<h1>Error establishing a database connection</h1>\n<p>This either means that the username and password information in your <code>wp-config.php</code> file is incorrect or we can\'t contact the database server at <code>" + dbhost + "</code>. This could mean your host\'s database server is down.</p>\n<ul>\n\t<li>Are you sure you have the correct username and password?</li>\n\t<li>Are you sure that you have typed the correct hostname?</li>\n\t<li>Are you sure that the database server is running?</li>\n</ul>\n<p>If you\'re unsure what these terms mean you should probably contact your host. If you still need help you can always visit the <a href=\'http://wordpress.org/support/\'>WordPress Support Forums</a>.</p>\n"); return; } this.ready = true; // Modified by Numiton this.select(dbname); if (!empty(this.charset) && booleanval(Options.version_compare(MySQL.mysql_get_server_info(gVars.webEnv, this.dbh), "4.1.0", ">="))) { this.query("SET NAMES \'" + this.charset + "\'"); } } public boolean __destruct() { return true; } public Object set_prefix(String prefix) { String old_prefix = null; Object table = null; if (QRegExPerl.preg_match("|[^a-z0-9_]|i", prefix)) { return new WP_Error(gVars, gConsts, "invalid_db_prefix", "Invalid database prefix"); // No gettext here } old_prefix = this.prefix; this.prefix = prefix; for (Map.Entry javaEntry673 : this.tables.entrySet()) { table = javaEntry673.getValue(); // Modified by Numiton String value = this.prefix + table; if (equal(table, "users")) { this.users = value; } else if (equal(table, "usermeta")) { this.usermeta = value; } else if (equal(table, "posts")) { this.posts = value; } else if (equal(table, "categories")) { this.categories = value; } else if (equal(table, "post2cat")) { this.post2cat = value; } else if (equal(table, "comments")) { this.comments = value; } else if (equal(table, "links")) { this.links = value; } else if (equal(table, "link2cat")) { this.link2cat = value; } else if (equal(table, "options")) { this.options = value; } else if (equal(table, "postmeta")) { this.postmeta = value; } else if (equal(table, "terms")) { this.terms = value; } else if (equal(table, "term_taxonomy")) { this.term_taxonomy = value; } else if (equal(table, "term_relationships")) { this.term_relationships = value; } else { LOG.warn("Invalid table name: " + table); } } if (gConsts.isCUSTOM_USER_TABLEDefined()) { this.users = gConsts.getCUSTOM_USER_TABLE(); } if (gConsts.isCUSTOM_USER_META_TABLEDefined()) { this.usermeta = gConsts.getCUSTOM_USER_META_TABLE(); } return old_prefix; } /** * Selects a database using the current class's $this->dbh * @param string $db name */ public void select(String db) { if (!MySQL.mysql_select_db(gVars.webEnv, db, this.dbh)) { this.ready = false; this.bail( "\n<h1>Can’t select database</h1>\n<p>We were able to connect to the database server (which means your username and password is okay) but not able to select the <code>" + db + "</code> database.</p>\n<ul>\n<li>Are you sure it exists?</li>\n<li>Does the user <code>" + gConsts.getDB_USER() + "</code> have permission to use the <code>" + db + "</code> database?</li>\n<li>On some systems the name of your database is prefixed with your username, so it would be like username_wordpress. Could that be the problem?</li>\n</ul>\n<p>If you don\'t know how to setup a database you should <strong>contact your host</strong>. If all else fails you may find help at the <a href=\'http://wordpress.org/support/\'>WordPress Support Forums</a>.</p>"); return; } } /** * Escapes content for insertion into the database, for security * @param string $string * @return string query safe string */ public String escape(String string) { return Strings.addslashes(gVars.webEnv, string); // Disable rest for now, causing problems /* if( !$this->dbh || version_compare( phpversion(), '4.3.0' ) == '-1' ) return mysql_escape_string( $string ); else return mysql_real_escape_string( $string, $this->dbh ); */ } /** * Escapes content by reference for insertion into the database, for * security * @param string $s */ public void escape_by_ref(Ref<Object> s) { s.value = this.escape(strval(s.value)); } /** * Prepares a SQL query for safe use, using sprintf() syntax */ public String prepare(Object... args) { String query; if (strictEqual(null, args)) { return null; } // Modified by Numiton Array<Object> argsArray = new Array<Object>(new Array<Object>()); argsArray.putAllValues(args); query = strval(Array.array_shift(argsArray)); query = Strings.str_replace("\'%s\'", "%s", query); // in case someone mistakenly already singlequoted it query = Strings.str_replace("\"%s\"", "%s", query); // doublequote unquoting query = Strings.str_replace("%s", "\'%s\'", query); // quote the strings Array.array_walk(argsArray, new Callback("escape_by_ref", this)); return QStrings.vsprintf(query, argsArray); } public boolean print_error() { return print_error(""); } // ================================================================== // Print SQL/DB error. public boolean print_error(String str) { String error_str = null; String caller = null; boolean log_error = false; String log_file = null; String query = null; if (!booleanval(str)) { str = MySQL.mysql_error(gVars.webEnv, this.dbh); } EZSQL_ERROR.putValue(new Array<Object>(new ArrayEntry<Object>("query", this.last_query), new ArrayEntry<Object>("error_str", str))); if (this.suppress_errors) { return false; } error_str = "nWordPress database error " + str + " for query " + this.last_query; if (booleanval(caller = this.get_caller())) { error_str = error_str + " made by " + caller; } log_error = true; if (!true)/*Modified by Numiton*/ { log_error = false; } log_file = Options.ini_get(gVars.webEnv, "error_log"); if (!empty(log_file) && !equal("syslog", log_file) && !FileSystemOrSocket.is_writable(gVars.webEnv, log_file)) { log_error = false; } if (log_error) { ErrorHandling.error_log(gVars.webEnv, error_str, 0); } // Is error output turned on or not.. if (!this.show_errors) { return false; } str = Strings.htmlspecialchars(str, Strings.ENT_QUOTES); query = Strings.htmlspecialchars(this.last_query, Strings.ENT_QUOTES); // If there is an error then take note of it print(gVars.webEnv, "<div id=\'error\'>\n\t\t<p class=\'wpdberror\'><strong>nWordPress database error:</strong> [" + str + "]<br />\n\t\t<code>" + query + "</code></p>\n\t\t</div>"); return false; } public boolean show_errors() { return show_errors(true); } // ================================================================== // Turn error handling on or off.. public boolean show_errors(boolean show) { boolean errors = false; errors = this.show_errors; this.show_errors = show; return errors; } public boolean hide_errors() { boolean show = false; show = this.show_errors; this.show_errors = false; return show; } public boolean suppress_errors() { return suppress_errors(true); } public boolean suppress_errors(boolean suppress) { boolean errors = false; errors = this.suppress_errors; this.suppress_errors = suppress; return errors; } // ================================================================== // Kill cached query results public void flush() { this.last_result = new Array<Object>(); this.col_info = new Array<StdClass>(); this.last_query = null; } // ================================================================== // Basic Query - see docs for more detail public int query(String query) { int return_val = 0; int i = 0; int num_rows = 0; StdClass row = null; if (!this.ready) { return BOOLEAN_FALSE; } // filter the query, if filters are available // NOTE: some queries are made before the plugins have been loaded, and thus cannot be filtered with this method if (true)/*Modified by Numiton*/ { query = strval(getIncluded(PluginPage.class, gVars, gConsts).apply_filters("query", query)); } // initialise return return_val = 0; this.flush(); // Log how the function was called this.func_call = "$db->query(\"" + query + "\")"; // Keep track of the last query for debug.. this.last_query = query; // Perform the query via std mysql_query function.. if (gConsts.getSAVEQUERIES()) { this.timer_start(); } this.result = MySQL.mysql_query(gVars.webEnv, query, this.dbh); ++this.num_queries; if (gConsts.getSAVEQUERIES()) { this.queries.putValue(new Array<Object>(new ArrayEntry<Object>(query), new ArrayEntry<Object>(this.timer_stop()), new ArrayEntry<Object>(this.get_caller()))); } // If there is an error then take note of it.. if (booleanval(this.last_error = MySQL.mysql_error(gVars.webEnv, this.dbh))) { this.print_error(); return BOOLEAN_FALSE; } if (QRegExPerl.preg_match("/^\\s*(insert|delete|update|replace) /i", query)) { this.rows_affected = MySQL.mysql_affected_rows(gVars.webEnv, this.dbh); // Take note of the insert_id if (QRegExPerl.preg_match("/^\\s*(insert|replace) /i", query)) { this.insert_id = MySQL.mysql_insert_id(gVars.webEnv, this.dbh); } // Return number of rows affected return_val = this.rows_affected; } else { i = 0; while (i < MySQL.mysql_num_fields(gVars.webEnv, this.result)) { this.col_info.putValue(i, MySQL.mysql_fetch_field(gVars.webEnv, this.result)); i++; } num_rows = 0; while (booleanval(row = MySQL.mysql_fetch_object(gVars.webEnv, this.result))) { this.last_result.putValue(num_rows, row); num_rows++; } MySQL.mysql_free_result(gVars.webEnv, this.result); // Log number of rows the query returned this.num_rows = num_rows; // Return number of rows selected return_val = this.num_rows; } return return_val; } /** * Insert an array of data into a table * @param string $table WARNING: not sanitized! * @param array $data should not already be SQL-escaped * @return mixed results of $this->query() */ public int insert(String table, Array data) { Array fields; data = getIncluded(FunctionsPage.class, gVars, gConsts).add_magic_quotes(data); fields = Array.array_keys(data); return this.query("INSERT INTO " + table + " (`" + Strings.implode("`,`", fields) + "`) VALUES (\'" + Strings.implode("\',\'", data) + "\')"); } /** * Update a row in the table with an array of data * @param string $table WARNING: not sanitized! * @param array $data should not already be SQL-escaped * @param array $where a named array of WHERE column => value relationships. * Multiple member pairs will be joined with ANDs. WARNING: the * column names are not currently sanitized! * @return mixed results of $this->query() */ public int update(Object table, Array<Object> data, Array<Object> where)/* Array or null */ { Array<String> bits = new Array<String>(); Array<String> wheres = new Array<String>(); Object k = null; Object c = null; String v = null; data = getIncluded(FunctionsPage.class, gVars, gConsts).add_magic_quotes(data); // bits = wheres = new Array<Object>(); for (Map.Entry javaEntry674 : Array.array_keys(data).entrySet()) { k = javaEntry674.getValue(); bits.putValue("`" + strval(k) + "` = \'" + strval(data.getValue(k)) + "\'"); } if (is_array(where)) { for (Map.Entry javaEntry675 : where.entrySet()) { c = javaEntry675.getKey(); v = strval(javaEntry675.getValue()); wheres.putValue(strval(c) + " = \'" + this.escape(v) + "\'"); } } else { return intval(false); } return this.query("UPDATE " + table + " SET " + Strings.implode(", ", bits) + " WHERE " + Strings.implode(" AND ", wheres) + " LIMIT 1"); } public Object get_var(String query) { return get_var(query, 0, 0); } /** * Get one variable from the database * @param string $query (can be null as well, for caching, see codex) * @param int $x = 0 row num to return * @param int $y = 0 col num to return * @return mixed results */ public Object get_var(String query, int x, int y) { Array<Object> values = new Array<Object>(); this.func_call = "$db->get_var(\"" + query + "\"," + strval(x) + "," + strval(y) + ")"; if (booleanval(query)) { this.query(query); } // Extract var out of cached results based x,y vals if (!empty(this.last_result.getValue(y))) { values = Array.array_values(ClassHandling.get_object_vars(this.last_result.getValue(y))); } // If there is a value return it else return null return (isset(values.getValue(x)) && !strictEqual(values.getValue(x), "")) ? values.getValue(x) : null; } public Object get_row() { return get_row("", gConsts.getOBJECT(), 0); } public Object get_row(String query) { return get_row(query, gConsts.getOBJECT(), 0); } public Object get_row(String query, String output) { return get_row(query, output, 0); } /** * Get one row from the database * @param string $query * @param string $output ARRAY_A | ARRAY_N | OBJECT * @param int $y row num to return * @return mixed results */ public Object get_row(String query, String output, int y) { this.func_call = "$db->get_row(\"" + query + "\"," + output + "," + strval(y) + ")"; if (booleanval(query)) { this.query(query); } else { return null; } if (!isset(this.last_result.getValue(y))) { return null; } if (equal(output, gConsts.getOBJECT())) { return booleanval(this.last_result.getValue(y)) ? this.last_result.getValue(y) : null; } else if (equal(output, gConsts.getARRAY_A())) { return booleanval(this.last_result.getValue(y)) ? ClassHandling.get_object_vars(this.last_result.getValue(y)) : (Array<Object>) null; } else if (equal(output, gConsts.getARRAY_N())) { return booleanval(this.last_result.getValue(y)) ? Array.array_values(ClassHandling.get_object_vars(this.last_result.getValue(y))) : (Array<Object>) null; } else { this.print_error(" $db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N"); } return null; } public Array get_col(String query) { return get_col(query, 0); } /** * Gets one column from the database * @param string $query (can be null as well, for caching, see codex) * @param int $x col num to return * @return array results */ public Array<Object> get_col(String query, int x) { Array<Object> new_array = new Array<Object>(); int i = 0; if (booleanval(query)) { this.query(query); } new_array = new Array<Object>(); // Extract the column values for (i = 0; i < Array.count(this.last_result); i++) { new_array.putValue(i, this.get_var(null, x, i)); } return new_array; } public Array<Object> get_results() { return get_results("", gConsts.getOBJECT()); } public Array get_results(String query) { return get_results(query, gConsts.getOBJECT()); } /** * Return an entire result set from the database * @param string $query (can also be null to pull from the cache) * @param string $output ARRAY_A | ARRAY_N | OBJECT_K | OBJECT * @return mixed results */ public Array<Object> get_results(String query, String output) { Object key = null; Object row = null; Array<Object> new_array = new Array<Object>(); int i = 0; this.func_call = "$db->get_results(\"" + query + "\", " + output + ")"; if (booleanval(query)) { this.query(query); } else { return null; } if (equal(output, gConsts.getOBJECT())) { // Return an integer-keyed array of row objects return this.last_result; } else if (equal(output, gConsts.getOBJECT_K())) { // Return an array of row objects with keys from column 1 // (Duplicates are discarded) for (Map.Entry javaEntry676 : this.last_result.entrySet()) { row = javaEntry676.getValue(); key = Array.array_shift(ClassHandling.get_object_vars(row)); if (!isset(new_array.getValue(key))) { new_array.putValue(key, row); } } return new_array; } else if (equal(output, gConsts.getARRAY_A()) || equal(output, gConsts.getARRAY_N())) { // Return an integer-keyed array of... if (booleanval(this.last_result)) { i = 0; for (Map.Entry javaEntry677 : this.last_result.entrySet()) { row = javaEntry677.getValue(); if (equal(output, gConsts.getARRAY_N())) { // ...integer-keyed row arrays new_array.putValue(i, Array.array_values(ClassHandling.get_object_vars(row))); } else { // ...column name-keyed row arrays new_array.putValue(i, ClassHandling.get_object_vars(row)); } ++i; } return new_array; } } return new Array<Object>(); } /** * Grabs column metadata from the last query * @param string $info_type one of name, table, def, max_length, not_null, * primary_key, multiple_key, unique_key, numeric, blob, type, * unsigned, zerofill * @param int $col_offset 0: col name. 1: which table the col's in. 2: col's * max length. 3: if the col is numeric. 4: col's type * @return mixed results */ public Object get_col_info(String info_type, int col_offset) { int i = 0; Array<Object> new_array = new Array<Object>(); StdClass col = null; if (booleanval(this.col_info)) { if (equal(col_offset, -1)) { i = 0; for (Map.Entry javaEntry678 : this.col_info.entrySet()) { col = (StdClass) javaEntry678.getValue(); new_array.putValue(i, StdClass.getValue(col, info_type)); i++; } return new_array; } else { return this.col_info.getValue(col_offset).fields.getValue(info_type); } } return null; } /** * Starts the timer, for debugging purposes */ public boolean timer_start() { Array<String> mtime = new Array<String>(); String mtimeStr = strval(DateTime.microtime()); mtime = Strings.explode(" ", mtimeStr); this.time_start = intval(mtime.getValue(1)) + intval(mtime.getValue(0)); return true; } /** * Stops the debugging timer * @return int total time spent on the query, in milliseconds */ public int timer_stop() { Array<String> mtime = new Array<String>(); int time_end = 0; int time_total = 0; String mtimeStr = strval(DateTime.microtime()); mtime = Strings.explode(" ", mtimeStr); time_end = intval(mtime.getValue(1)) + intval(mtime.getValue(0)); time_total = time_end - this.time_start; return time_total; } /** * Wraps fatal errors in a nice header and footer and dies. * @param string $message */ public boolean bail(String message) { // Just wraps errors in a nice header and footer if (!this.show_errors) { /* Modified by Numiton: WP_Error ALWAYS exists */ // if (/* Usage of unsupported runtime function 'class_exists' */Unsupported.class_exists("WP_Error")) { this.error = new WP_Error(gVars, gConsts, "500", message); // } // else // this.error = message; return false; } getIncluded(FunctionsPage.class, gVars, gConsts).wp_die(message, ""); return false; } /** * Checks wether of not the database version is high enough to support the * features WordPress uses * @global $wp_version */ public Object check_database_version() { // Make sure the server has MySQL 4.0 String mysql_version = QRegExPerl.preg_replace("|[^0-9\\.]|", "", MySQL.mysql_get_server_info(gVars.webEnv, this.dbh)); if (booleanval(Options.version_compare(mysql_version, "4.0.0", "<"))) { return new WP_Error(gVars, gConsts, "database_version", QStrings.sprintf(getIncluded(L10nPage.class, gVars, gConsts).__("<strong>ERROR</strong>: nWordPress %s requires MySQL 4.0.0 or higher", "default"), gVars.wp_version)); } return ""; } /** * This function is called when WordPress is generating the table schema * to determine wether or not the current database supports or needs the * collation statements. */ public boolean supports_collation() { return booleanval(Options.version_compare(MySQL.mysql_get_server_info(gVars.webEnv, this.dbh), "4.1.0", ">=")); } /** * Get the name of the function that called wpdb. * @return string the name of the calling function */ public String get_caller() { Array<Object> bt = new Array<Object>(); String caller = null; Array<Object> trace = new Array<Object>(); // requires PHP 4.3+ if (!true)/*Modified by Numiton*/ { return ""; } bt = ErrorHandling.debug_backtrace(); caller = ""; for (Map.Entry javaEntry679 : bt.entrySet()) { trace = (Array<Object>) javaEntry679.getValue(); if (equal(trace.getValue("class"), SourceCodeInfo.getCurrentClass())) { continue; } else if (equal(Strings.strtolower(strval(trace.getValue("function"))), "call_user_func_array")) { continue; } else if (equal(Strings.strtolower(strval(trace.getValue("function"))), "apply_filters")) { continue; } else if (equal(Strings.strtolower(strval(trace.getValue("function"))), "do_action")) { continue; } caller = strval(trace.getValue("function")); break; } return caller; } protected void finalize() throws Throwable { __destruct(); } public void setContext(GlobalVariablesContainer javaGlobalVariables, GlobalConstantsInterface javaGlobalConstants) { gConsts = (GlobalConsts) javaGlobalConstants; gVars = (GlobalVars) javaGlobalVariables; gVars.gConsts = gConsts; } public Object clone() throws CloneNotSupportedException { return super.clone(); } public GlobalVariablesContainer getGlobalVars() { return gVars; } }