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