1<?php
2
3// security - hide paths
4if (!defined('ADODB_DIR')) die();
5
6global $ADODB_INCLUDED_CSV;
7$ADODB_INCLUDED_CSV = 1;
8
9/*
10
11  V4.93 10 Oct 2006  (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved.
12  Released under both BSD license and Lesser GPL library license.
13  Whenever there is any discrepancy between the two licenses,
14  the BSD license will take precedence. See License.txt.
15  Set tabs to 4 for best viewing.
16
17  Latest version is available at http://adodb.sourceforge.net
18
19  Library for CSV serialization. This is used by the csv/proxy driver and is the
20  CacheExecute() serialization format.
21
22  ==== NOTE ====
23  Format documented at http://php.weblogs.com/ADODB_CSV
24  ==============
25*/
26
27	/**
28 	 * convert a recordset into special format
29	 *
30	 * @param rs	the recordset
31	 *
32	 * @return	the CSV formated data
33	 */
34	function _rs2serialize(&$rs,$conn=false,$sql='')
35	{
36		$max = ($rs) ? $rs->FieldCount() : 0;
37
38		if ($sql) $sql = urlencode($sql);
39		// metadata setup
40
41		if ($max <= 0 || $rs->dataProvider == 'empty') { // is insert/update/delete
42			if (is_object($conn)) {
43				$sql .= ','.$conn->Affected_Rows();
44				$sql .= ','.$conn->Insert_ID();
45			} else
46				$sql .= ',,';
47
48			$text = "====-1,0,$sql\n";
49			return $text;
50		}
51		$tt = ($rs->timeCreated) ? $rs->timeCreated : time();
52
53		## changed format from ====0 to ====1
54		$line = "====1,$tt,$sql\n";
55
56		if ($rs->databaseType == 'array') {
57			$rows =& $rs->_array;
58		} else {
59			$rows = array();
60			while (!$rs->EOF) {
61				$rows[] = $rs->fields;
62				$rs->MoveNext();
63			}
64		}
65
66		for($i=0; $i < $max; $i++) {
67			$o =& $rs->FetchField($i);
68			$flds[] = $o;
69		}
70
71		$savefetch = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
72		$class = $rs->connection->arrayClass;
73		$rs2 = new $class();
74		$rs2->sql = $rs->sql;
75		$rs2->oldProvider = $rs->dataProvider;
76		$rs2->InitArrayFields($rows,$flds);
77		$rs2->fetchMode = $savefetch;
78		return $line.serialize($rs2);
79	}
80
81
82/**
83* Open CSV file and convert it into Data.
84*
85* @param url  		file/ftp/http url
86* @param err		returns the error message
87* @param timeout	dispose if recordset has been alive for $timeout secs
88*
89* @return		recordset, or false if error occured. If no
90*			error occurred in sql INSERT/UPDATE/DELETE,
91*			empty recordset is returned
92*/
93	function &csv2rs($url,&$err,$timeout=0, $rsclass='ADORecordSet_array')
94	{
95		$false = false;
96		$err = false;
97		$fp = @fopen($url,'rb');
98		if (!$fp) {
99			$err = $url.' file/URL not found';
100			return $false;
101		}
102		@flock($fp, LOCK_SH);
103		$arr = array();
104		$ttl = 0;
105
106		if ($meta = fgetcsv($fp, 32000, ",")) {
107			// check if error message
108			if (strncmp($meta[0],'****',4) === 0) {
109				$err = trim(substr($meta[0],4,1024));
110				fclose($fp);
111				return $false;
112			}
113			// check for meta data
114			// $meta[0] is -1 means return an empty recordset
115			// $meta[1] contains a time
116
117			if (strncmp($meta[0], '====',4) === 0) {
118
119				if ($meta[0] == "====-1") {
120					if (sizeof($meta) < 5) {
121						$err = "Corrupt first line for format -1";
122						fclose($fp);
123						return $false;
124					}
125					fclose($fp);
126
127					if ($timeout > 0) {
128						$err = " Illegal Timeout $timeout ";
129						return $false;
130					}
131
132					$rs = new $rsclass($val=true);
133					$rs->fields = array();
134					$rs->timeCreated = $meta[1];
135					$rs->EOF = true;
136					$rs->_numOfFields = 0;
137					$rs->sql = urldecode($meta[2]);
138					$rs->affectedrows = (integer)$meta[3];
139					$rs->insertid = $meta[4];
140					return $rs;
141				}
142			# Under high volume loads, we want only 1 thread/process to _write_file
143			# so that we don't have 50 processes queueing to write the same data.
144			# We use probabilistic timeout, ahead of time.
145			#
146			# -4 sec before timeout, give processes 1/32 chance of timing out
147			# -2 sec before timeout, give processes 1/16 chance of timing out
148			# -1 sec after timeout give processes 1/4 chance of timing out
149			# +0 sec after timeout, give processes 100% chance of timing out
150				if (sizeof($meta) > 1) {
151					if($timeout >0){
152						$tdiff = (integer)( $meta[1]+$timeout - time());
153						if ($tdiff <= 2) {
154							switch($tdiff) {
155							case 4:
156							case 3:
157								if ((rand() & 31) == 0) {
158									fclose($fp);
159									$err = "Timeout 3";
160									return $false;
161								}
162								break;
163							case 2:
164								if ((rand() & 15) == 0) {
165									fclose($fp);
166									$err = "Timeout 2";
167									return $false;
168								}
169								break;
170							case 1:
171								if ((rand() & 3) == 0) {
172									fclose($fp);
173									$err = "Timeout 1";
174									return $false;
175								}
176								break;
177							default:
178								fclose($fp);
179								$err = "Timeout 0";
180								return $false;
181							} // switch
182
183						} // if check flush cache
184					}// (timeout>0)
185					$ttl = $meta[1];
186				}
187				//================================================
188				// new cache format - use serialize extensively...
189				if ($meta[0] === '====1') {
190					// slurp in the data
191					$MAXSIZE = 128000;
192
193					$text = fread($fp,$MAXSIZE);
194					if (strlen($text)) {
195						while ($txt = fread($fp,$MAXSIZE)) {
196							$text .= $txt;
197						}
198					}
199					fclose($fp);
200					$rs = unserialize($text);
201					if (is_object($rs)) $rs->timeCreated = $ttl;
202					else {
203						$err = "Unable to unserialize recordset";
204						//echo htmlspecialchars($text),' !--END--!<p>';
205					}
206					return $rs;
207				}
208
209				$meta = false;
210				$meta = fgetcsv($fp, 32000, ",");
211				if (!$meta) {
212					fclose($fp);
213					$err = "Unexpected EOF 1";
214					return $false;
215				}
216			}
217
218			// Get Column definitions
219			$flds = array();
220			foreach($meta as $o) {
221				$o2 = explode(':',$o);
222				if (sizeof($o2)!=3) {
223					$arr[] = $meta;
224					$flds = false;
225					break;
226				}
227				$fld = new ADOFieldObject();
228				$fld->name = urldecode($o2[0]);
229				$fld->type = $o2[1];
230				$fld->max_length = $o2[2];
231				$flds[] = $fld;
232			}
233		} else {
234			fclose($fp);
235			$err = "Recordset had unexpected EOF 2";
236			return $false;
237		}
238
239		// slurp in the data
240		$MAXSIZE = 128000;
241
242		$text = '';
243		while ($txt = fread($fp,$MAXSIZE)) {
244			$text .= $txt;
245		}
246
247		fclose($fp);
248		@$arr = unserialize($text);
249		//var_dump($arr);
250		if (!is_array($arr)) {
251			$err = "Recordset had unexpected EOF (in serialized recordset)";
252			if (get_magic_quotes_runtime()) $err .= ". Magic Quotes Runtime should be disabled!";
253			return $false;
254		}
255		$rs = new $rsclass();
256		$rs->timeCreated = $ttl;
257		$rs->InitArrayFields($arr,$flds);
258		return $rs;
259	}
260
261
262	/**
263	* Save a file $filename and its $contents (normally for caching) with file locking
264	*/
265	function adodb_write_file($filename, $contents,$debug=false)
266	{
267	# http://www.php.net/bugs.php?id=9203 Bug that flock fails on Windows
268	# So to simulate locking, we assume that rename is an atomic operation.
269	# First we delete $filename, then we create a $tempfile write to it and
270	# rename to the desired $filename. If the rename works, then we successfully
271	# modified the file exclusively.
272	# What a stupid need - having to simulate locking.
273	# Risks:
274	# 1. $tempfile name is not unique -- very very low
275	# 2. unlink($filename) fails -- ok, rename will fail
276	# 3. adodb reads stale file because unlink fails -- ok, $rs timeout occurs
277	# 4. another process creates $filename between unlink() and rename() -- ok, rename() fails and  cache updated
278		if (strncmp(PHP_OS,'WIN',3) === 0) {
279			// skip the decimal place
280			$mtime = substr(str_replace(' ','_',microtime()),2);
281			// getmypid() actually returns 0 on Win98 - never mind!
282			$tmpname = $filename.uniqid($mtime).getmypid();
283			if (!($fd = @fopen($tmpname,'a'))) return false;
284			$ok = ftruncate($fd,0);
285			if (!fwrite($fd,$contents)) $ok = false;
286			fclose($fd);
287			chmod($tmpname,0644);
288			// the tricky moment
289			@unlink($filename);
290			if (!@rename($tmpname,$filename)) {
291				unlink($tmpname);
292				$ok = false;
293			}
294			if (!$ok) {
295				if ($debug) ADOConnection::outp( " Rename $tmpname ".($ok? 'ok' : 'failed'));
296			}
297			return $ok;
298		}
299		if (!($fd = @fopen($filename, 'a'))) return false;
300		if (flock($fd, LOCK_EX) && ftruncate($fd, 0)) {
301			$ok = fwrite( $fd, $contents );
302			fclose($fd);
303			chmod($filename,0644);
304		}else {
305			fclose($fd);
306			if ($debug)ADOConnection::outp( " Failed acquiring lock for $filename<br>\n");
307			$ok = false;
308		}
309
310		return $ok;
311	}
312?>