Some more improvements:
[mailer.git] / inc / db / lib-mysql3.php
1 <?php
2 /************************************************************************
3  * Mailer v0.2.1-FINAL                                Start: 08/29/2004 *
4  * ===================                          Last change: 08/29/2004 *
5  *                                                                      *
6  * -------------------------------------------------------------------- *
7  * File              : lib-mysql3.php                                   *
8  * -------------------------------------------------------------------- *
9  * Short description : Database layer for MySQL 3/4/5 server            *
10  * -------------------------------------------------------------------- *
11  * Kurzbeschreibung  : Datenbankschicht fuer MySQL 3/4/5 Server         *
12  * -------------------------------------------------------------------- *
13  * $Revision::                                                        $ *
14  * $Date::                                                            $ *
15  * $Tag:: 0.2.1-FINAL                                                 $ *
16  * $Author::                                                          $ *
17  * -------------------------------------------------------------------- *
18  * Copyright (c) 2003 - 2009 by Roland Haeder                           *
19  * Copyright (c) 2009 - 2013 by Mailer Developer Team                   *
20  * For more information visit: http://mxchange.org                      *
21  *                                                                      *
22  * This program is free software; you can redistribute it and/or modify *
23  * it under the terms of the GNU General Public License as published by *
24  * the Free Software Foundation; either version 2 of the License, or    *
25  * (at your option) any later version.                                  *
26  *                                                                      *
27  * This program is distributed in the hope that it will be useful,      *
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
30  * GNU General Public License for more details.                         *
31  *                                                                      *
32  * You should have received a copy of the GNU General Public License    *
33  * along with this program; if not, write to the Free Software          *
34  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,               *
35  * MA  02110-1301  USA                                                  *
36  ************************************************************************/
37
38 // Some security stuff...
39 if (!defined('__SECURITY')) {
40         die();
41 } // END - if
42
43 // SQL queries
44 function sqlQuery ($sqlString, $file, $line, $enableCodes = TRUE) {
45         // Is there cache?
46         if (!isset($GLOBALS[__FUNCTION__][$sqlString])) {
47                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'Called: ' . $sqlString);
48
49                 // Trim SQL string
50                 $sqlStringModified = trim($sqlString);
51
52                 // Empty query string or link is not up?
53                 if (empty($sqlStringModified)) {
54                         // Empty SQL string!
55                         reportBug(__FUNCTION__, __LINE__, sprintf('SQL string is empty, please fix this: file=%s, line=%s',
56                                 basename($file),
57                                 $line
58                         ));
59                 } elseif (!isSqlLinkUp()) {
60                         // We should not quietly ignore this
61                         reportBug(__FUNCTION__, __LINE__, sprintf('Cannot query database: sqlString=%s,file=%s,line=%s',
62                                 $sqlStringModified,
63                                 basename($file),
64                                 $line
65                         ));
66                 }
67
68                 // Remove \t, \n and \r from queries they may confuse some MySQL versions
69                 $sqlStringModified = str_replace(array(chr(9), PHP_EOL, chr(13)), array(' ', ' ', ' '), $sqlStringModified);
70
71                 // Compile config entries out
72                 $sqlStringModified = sqlPrepareQueryString($sqlStringModified, $enableCodes);
73
74                 // Cache it and remember as last SQL query
75                 $GLOBALS[__FUNCTION__][$sqlString] = $sqlStringModified;
76                 $GLOBALS['last_sql']               = $sqlStringModified;
77                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'Stored cache: ' . $sqlStringModified);
78         } elseif (!isSqlLinkUp()) {
79                 // Link went down while using cached SQL
80                 reportBug(__FUNCTION__, __LINE__, 'Link went down while using cached SQL: sqlString=' . $sqlString . ',file=' . basename($file) . ',line=' . $line . ',enableCodes=' . intval($enableCodes));
81         } else {
82                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'Cache used: ' . $sqlString);
83
84                 // Use cache (to save a lot function calls
85                 $GLOBALS['last_sql'] = $GLOBALS[__FUNCTION__][$sqlString];
86
87                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'Cache is: ' . $sqlString);
88         }
89
90         // Starting time
91         $querytimeBefore = microtime(TRUE);
92
93         // Run SQL command
94         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'file=' . basename($file) . ',line=' . $line . ',sql=' . $GLOBALS['last_sql']);
95         $result = mysql_query($GLOBALS['last_sql'], getSqlLink())
96                 or sqlError($file, $line, 'file='. basename($file) . ',line=' . $line . ':mysql_error()=' . mysql_error() . ',last_query=' . $GLOBALS['last_sql']);
97         //* DEBUG: */ logDebugMessage($file, $line, 'sql=' . $GLOBALS['last_sql'] . ',affected=' . sqlAffectedRows() . ',numRows='.(isValidSqlLink($result) ? sqlNumRows($result) : gettype($result)));
98
99         // Calculate query time
100         $queryTime = microtime(TRUE) - $querytimeBefore;
101
102         // Add this query to array including timing
103         addSqlToDebug($result, $GLOBALS['last_sql'], $queryTime, $file, $line);
104
105         // Save last successfull query
106         setConfigEntry('db_last_query', $GLOBALS['last_sql']);
107
108         // Count all query times
109         incrementConfigEntry('sql_time', $queryTime);
110
111         // Count this query
112         incrementConfigEntry('sql_count');
113
114         // Debug output
115         if (isSqlDebugEnabled()) {
116                 // Is this the first call?
117                 if (!isset($GLOBALS['sql_first_entry'])) {
118                         // Write first entry
119                         appendLineToFile(getCachePath() . 'mysql.log', 'Module=' . getModule());
120                         $GLOBALS['sql_first_entry'] = TRUE;
121                 } // END - if
122
123                 // Append debug line
124                 appendLineToFile(getCachePath() . 'mysql.log', basename($file) . '|LINE=' . $line . '|NUM=' . (isValidSqlLink($result) ? sqlNumRows($result) : 'false') . '|AFFECTED=' . sqlAffectedRows() . '|QUERYTIME:' . ($queryTime * 1000) . 'ms): ' . str_replace(array(chr(13), PHP_EOL), array('', ' '), $GLOBALS['last_sql']));
125         } // END - if
126
127         // Increment stats entry
128         incrementStatsEntry('db_hits');
129
130         // Return the result
131         return $result;
132 }
133
134 // SQL num rows
135 function sqlNumRows ($resource) {
136         // Valid link resource?
137         if (!isSqlLinkUp()) return FALSE;
138
139         // Link is not up, no rows by default
140         $lines = FALSE;
141
142         // Is the result a valid resource?
143         if (isset($GLOBALS['sql_numrows'][intval($resource)])) {
144                 // Use cache
145                 $lines = $GLOBALS['sql_numrows'][intval($resource)];
146         } elseif (isValidSqlLink($resource)) {
147                 // Get the count of rows from database
148                 $lines = mysql_num_rows($resource);
149
150                 // Remember it in cache
151                 $GLOBALS['sql_numrows'][intval($resource)] = $lines;
152         } else {
153                 // No resource given, please fix this
154                 reportBug(__FUNCTION__, __LINE__, 'No resource given! result[]=' . gettype($resource) . ',last_sql=' .  $GLOBALS['last_sql']);
155         }
156
157         // Return lines
158         return $lines;
159 }
160
161 // SQL affected rows
162 function sqlAffectedRows () {
163         // Valid link resource?
164         if (!isSqlLinkUp()) return FALSE;
165
166         // Get affected rows
167         $lines = mysql_affected_rows(getSqlLink());
168
169         // Return it
170         return $lines;
171 }
172
173 // SQL fetch row
174 function sqlFetchRow ($resource) {
175         // Is $resource valid?
176         if ((!isValidSqlLink($resource)) || (!isSqlLinkUp())) return FALSE;
177
178         // Fetch the data and return it
179         return mysql_fetch_row($resource);
180 }
181
182 // SQL fetch array
183 function sqlFetchArray ($resource) {
184         // Is $resource valid?
185         if ((!isValidSqlLink($resource)) || (!isSqlLinkUp())) return FALSE;
186
187         // Load row as array from database
188         $row = mysql_fetch_assoc($resource);
189
190         // Return only arrays here
191         if (is_array($row)) {
192                 // Return row
193                 return $row;
194         } else {
195                 // Return a false, else some loops would go endless...
196                 return FALSE;
197         }
198 }
199
200 // SQL result
201 function sqlResult ($resource, $row, $field = '0') {
202         // Is $resource valid?
203         if ((!isValidSqlLink($resource)) || (!isSqlLinkUp())) return FALSE;
204
205         // Run the result command
206         $result = mysql_result($resource, $row, $field);
207
208         // ... and return the result
209         return $result;
210 }
211
212 // SQL connect
213 function sqlConnectToDatabase ($host, $login, $password, $file, $line) {
214         // Try to connect
215         $linkResource = mysql_connect($host, $login, $password) or sqlError($file, $line,  mysql_error());
216
217         // Set the link resource
218         if (isValidSqlLink($linkResource)) {
219                 /*
220                  * A non-resource (boolean) may happen on installation phase which
221                  * shall not be set here. Only valid link resources shall be set so
222                  * isSqlLinkUp() will only return 'true' if there is really a
223                  * working database link.
224                  */
225                 setSqlLink($file . ':' . __FUNCTION__, $line . ':' . __LINE__, $linkResource);
226
227                 // Init charsets (UTF-8 is default now)
228                 sqlQuery("SET
229         `character_set_results`='utf8',
230         `character_set_client`='utf8',
231         `character_set_connection`='utf8',
232         `character_set_database`='utf8',
233         `character_set_server`='utf8'", $file . ':' . __FUNCTION__, $line . ':' . __LINE__);
234
235                 // Disallow subtraction for unsigned columns
236                 sqlQuery("SET `sql_mode`='NO_UNSIGNED_SUBTRACTION'", $file . ':' . __FUNCTION__, $line . ':' . __LINE__);
237         } // END - if
238
239         // Return the resource
240         //* DEBUG: */ logDebugMessage($file . ':' . __FUNCTION__, $line . ':' . __LINE__, 'linkResource[]=' . gettype($linkResource));
241         return $linkResource;
242 }
243
244 // SQL select database
245 function sqlSelectDatabase ($dbName, $file, $line) {
246         // Is there still a valid link? If not, skip it.
247         if (!isSqlLinkUp()) return FALSE;
248
249         // Return the result
250         //* DEBUG: */ logDebugMessage($file . ':' . __FUNCTION__, $line . ':' . __LINE__, 'Selecting database ' . $dbName);
251         return mysql_select_db($dbName, getSqlLink()) or sqlError($file, $line,  mysql_error());
252 }
253
254 // SQL close link
255 function sqlCloseLink ($file, $line) {
256         // Is the link up?
257         if (!isSqlLinkUp()) {
258                 // Skip double close
259                 //* DEBUG: */ logDebugMessage($file . ':' . __FUNCTION__, $line . ':' . __LINE__, 'Called but no link is open.');
260                 return FALSE;
261         } // END - if
262
263         // Close database link and forget the link
264         $close = mysql_close(getSqlLink()) or sqlError($file . ':' . __FUNCTION__, $line . ':' . __LINE__, mysql_error());
265
266         // Close link in this layer
267         unsetSqlLinkUp(__FUNCTION__, __LINE__);
268
269         // Return the result
270         //* DEBUG: */ logDebugMessage($file . ':' . __FUNCTION__, $line . ':' . __LINE__, 'close[' . gettype($close) . ']=' . intval($close));
271         return $close;
272 }
273
274 // SQL free result
275 function sqlFreeResult ($resource) {
276         if ((!isValidSqlLink($resource)) || (!isSqlLinkUp())) {
277                 // Abort here
278                 return FALSE;
279         } // END - if
280
281         // Free result
282         $res = mysql_free_result($resource);
283
284         // And return that result of freeing it...
285         return $res;
286 }
287
288 // Get id from last INSERT command and secure id
289 function getSqlInsertId () {
290         if (!isSqlLinkUp()) return FALSE;
291         return bigintval(mysql_insert_id());
292 }
293
294 // Escape a string for the database
295 function sqlEscapeString ($str, $secureString = TRUE, $strip = TRUE) {
296         // Is there cache?
297         if (!isset($GLOBALS['sql_escapes']['' . $str . ''])) {
298                 // Debug message
299                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'str=' . $str . ' - BEFORE!');
300
301                 // Prepare the string here
302                 $str = sqlPrepareQueryString($str);
303
304                 // Debug message
305                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'str=' . $str . ' - AFTER!');
306
307                 // Secure string first? (which is the default behaviour!)
308                 if ($secureString === TRUE) {
309                         // Debug message
310                         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'str=' . $str . ',strip=' . intval($strip) . ' - BEFORE!');
311
312                         // Then do it here
313                         $str = secureString($str, $strip);
314
315                         // Debug message
316                         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'str=' . $str . ',strip=' . intval($strip) . ' - AFTER!');
317                 } // END - if
318
319                 // Init (invalid) value
320                 $ret = '!INVALID!';
321
322                 if (!isSqlLinkUp()) {
323                         // Fall-back to escapeQuotes() when there is no link
324                         $ret = escapeQuotes($str);
325                 } elseif (function_exists('mysql_real_escape_string')) {
326                         // Debug message
327                         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'str=' . $str);
328
329                         // The new and improved version
330                         $ret = mysql_real_escape_string($str, getSqlLink());
331
332                         // Debug message
333                         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'str=' . $str . ',ret=' . $ret);
334                 } elseif (function_exists('mysql_escape_string')) {
335                         // The obsolete function
336                         $ret = mysql_escape_string($str, getSqlLink());
337                 } else {
338                         // If nothing else works, fall back to escapeQuotes() again
339                         $ret = escapeQuotes($str);
340                 }
341
342                 // Log message
343                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'str=' . $str . ',ret=' . $ret);
344
345                 // Cache result
346                 $GLOBALS['sql_escapes']['' . $str . ''] = $ret;
347         } // END - if
348
349         // Log message
350         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'str=' . $str . ',sql_escapes=' . $GLOBALS['sql_escapes']['' . $str . '']);
351
352         // Return it
353         return $GLOBALS['sql_escapes']['' . $str . ''];
354 }
355
356 // Log SQL errors to debug.log in installation phase or call reportBug()
357 function sqlError ($file, $line, $message) {
358         // Remember plain error in last_sql_error
359         $GLOBALS['last_sql_error'] = mysql_error();
360
361         // Is login set?
362         if (!empty($GLOBALS['mysql']['login'])) {
363                 // Secure login name in message
364                 $message = str_replace($GLOBALS['mysql']['login'], '***', $message);
365         } // END - if
366
367         // Is database password set?
368         if (!empty($GLOBALS['mysql']['password'])) {
369                 // Secure password in message
370                 $message = str_replace($GLOBALS['mysql']['password'], '***', $message);
371         } // END - if
372
373         // Is database name set?
374         if (!empty($GLOBALS['mysql']['dbase'])) {
375                 // Secure database name in message
376                 $message = str_replace($GLOBALS['mysql']['dbase'], '***', $message);
377         } // END - if
378
379         // Is there installation phase?
380         if (isInstaller()) {
381                 /*
382                  * In installation phase, we don't want SQL errors abort e.g. connection
383                  * tests, so just log it away.
384                  */
385                 logDebugMessage($file, $line, $message);
386         } else {
387                 // Regular mode, then call reportBug()
388                 reportBug($file, $line, $message);
389         }
390 }
391
392 // Checks whether given link is a valid SQL link
393 function isValidSqlLink ($linkResource) {
394         // Is it a resource?
395         return is_resource($linkResource);
396 }
397
398 // [EOF]
399 ?>