2 /************************************************************************/
4 /************************************************************************/
5 /* Copyright (c) 2002-2010 */
6 /* Inclusive Design Institute */
9 /* This program is free software. You can redistribute it and/or */
10 /* modify it under the terms of the GNU General Public License */
11 /* as published by the Free Software Foundation. */
12 /************************************************************************/
17 * Class for patch installation
23 define('AT_INCLUDE_PATH', '../../../include/');
25 require_once(AT_INCLUDE_PATH. "../mods/_standard/patcher/include/common.inc.php");
30 var $patch_array = array(); // the patch data
31 var $patch_summary_array = array(); // patch summary information
32 var $patch_id; // current patches.patches_id
33 var $patch_file_id; // current patches_files.patches_files_id
35 var $need_access_to_folders = array();// folders that need to have write permission
36 var $need_access_to_files = array(); // files that need to have write permission
37 var $backup_files = array(); // backup files
38 var $patch_files = array(); // patch files
40 var $errors = array(); // error messages
41 var $baseURL; // patch folder at update.atutor.ca
42 var $backup_suffix; // suffix appended for backup files
43 var $patch_suffix; // suffix appended for patch files copied from update.atutor.ca
44 var $skipFilesModified = false; // if set to true, report error for files that have been modified by user
45 var $module_content_dir; // content folder used to create patch.sql
46 var $svn_server_connected; // flag indicating if can connect to svn server, if not, consider all files manipulated by patch as modified
48 // constant, URL of user's ATutor release version in SVN
49 var $svn_tag_folder = 'http://atutorsvn.atrc.utoronto.ca/repos/atutor/tags/';
50 var $sql_file = 'patch.sql';
51 var $relative_to_atutor_root = '../../../'; // relative path from mods/_standard/patcher to root
53 var $error_title_printed = false; // a flag only used in constructor when error happens
54 // it's to indicate if the title of the error has been printed into $this->errors
57 * Constructor: Initialize object members
59 * @param $patch_array The name of the file to find charset definition
60 * $patch_summary_array
64 function Patch($patch_array, $patch_summary_array, $skipFilesModified, $patch_folder)
66 // add relative path to move to ATutor root folder
67 for ($i = 0; $i < count($patch_array[files]); $i++)
69 // prevent the access to the areas that are outside of ATutor
70 $first_char = substr($patch_array[files][$i]['location'], 0, 1);
71 if ($first_char == '.' || $first_char == '/'){
72 if (!$this->error_title_printed) {
73 $this->errors[] = _AT('path_not_allowed');
74 $this->error_title_printed = true;
76 $this->errors[0] .= '<strong>'. $patch_array[files][$i]['location'] . "</strong><br />";
78 $patch_array[files][$i]['location'] = $this->relative_to_atutor_root . $patch_array[files][$i]['location'];
81 $this->patch_array = $patch_array;
82 $this->patch_summary_array = $patch_summary_array;
84 $this->baseURL = $patch_folder;
85 $this ->backup_suffix = $patch_array['atutor_patch_id'] . ".old";
86 $this ->patch_suffix = $patch_array['atutor_patch_id'];
87 $this->skipFilesModified = $skipFilesModified;
89 $this->module_content_dir = AT_CONTENT_DIR . "patcher";
93 if (!is_array($_SESSION['remove_permission'])) $_SESSION['remove_permission']=array();
98 * Main process to apply patch.
100 * @return true if patch is successfully applied
102 * @author Cindy Qi Li
104 function applyPatch()
109 // 1. if there's error from class constructor
110 // 2. if svn server is up. If not, consider all files manipulated by patch as modified
111 // 3. if the local file is customized by user
112 // 4. if script has write priviledge on local file/folder
113 // 5. if dependent patches have been installed
115 if (count($this->errors) > 0) {
116 print_errors($this->errors, $notes);
118 unset($this->errors);
122 if (!$this->pingDomain($this->svn_tag_folder))
124 $msg->addInfo('CANNOT_CONNECT_SVN_SERVER');
126 $this->svn_server_connected = false;
129 $this->svn_server_connected = true;
131 if (!$this->checkDependentPatches()) return false;
133 if (!$this->checkAppliedVersion()) return false;
135 if (!$this->skipFilesModified && $this->hasFilesModified()) return false;
137 if (!$this->checkPriviledge()) return false;
140 if (strlen(trim($this->patch_array['sql'])) > 0) $this->runSQL();
142 // Start applying patch
143 $this->createPatchesRecord($this->patch_summary_array);
145 // if no file action defined, update database and return true
146 if (!is_array($this->patch_array[files]))
148 $updateInfo = array("status"=>"Installed");
149 updatePatchesRecord($this->patch_id, $updateInfo);
154 foreach ($this->patch_array[files] as $row_num => $patch_file)
156 $this->createPatchesFilesRecord($this->patch_array['files'][$row_num]);
158 if ($patch_file['action'] == 'alter')
160 $this->alterFile($row_num);
162 else if ($patch_file['action'] == 'add')
164 $this->addFile($row_num);
166 else if ($patch_file['action'] == 'delete')
168 $this->deleteFile($row_num);
170 else if ($patch_file['action'] == 'overwrite')
172 $this->overwriteFile($row_num);
176 // if only has backup files info, patch is considered successfully installed
177 // if has permission to remove, considered partly installed
178 $updateInfo = array();
180 if (count($this->backup_files) > 0)
182 foreach($this->backup_files as $backup_file)
183 $backup_files .= $backup_file. '|';
185 $updateInfo = array("backup_files"=>mysql_real_escape_string($backup_files));
188 if (count($this->patch_files) > 0)
190 foreach($this->patch_files as $patch_file)
191 $patch_files .= $patch_file. '|';
193 $updateInfo = array_merge($updateInfo, array("patch_files"=>mysql_real_escape_string($patch_files)));
196 if (is_array($_SESSION['remove_permission']) && count($_SESSION['remove_permission']))
198 foreach($_SESSION['remove_permission'] as $remove_permission_file)
199 $remove_permission_files .= $remove_permission_file. '|';
201 $updateInfo = array_merge($updateInfo, array("remove_permission_files"=>mysql_real_escape_string($remove_permission_files), "status"=>"Partly Installed"));
205 $updateInfo = array_merge($updateInfo, array("status"=>"Installed"));
208 updatePatchesRecord($this->patch_id, $updateInfo);
210 unset($_SESSION['remove_permission']);
218 * @return patch array
219 * @author Cindy Qi Li
221 function getPatchArray()
223 return $this->patch_array;
227 * return patch id processed by this object
230 * @author Cindy Qi Li
232 function getPatchID()
234 return $this->patch_id;
238 * Check if script has write permission to the files and folders that need to be written
239 * if no permission, warn user to give permission
241 * @return true if there are files or folders that script has no permission
242 * false if permissions are in place
243 * @author Cindy Qi Li
245 function checkPriviledge()
249 // no file action is defined, return true;
250 if (!is_array($this->patch_array[files])) return true;
252 foreach ($this->patch_array[files] as $row_num => $patch_file)
254 $real_location = realpath($patch_file['location']);
256 if ($real_location <> '' && !is_writable($patch_file['location']) && !in_array($real_location, $this->need_access_to_folders))
257 { // folder exists. check if has write permission
258 $this->need_access_to_folders[] = $real_location;
260 if (!in_array($real_location, $_SESSION['remove_permission']))
261 $_SESSION['remove_permission'][] = $real_location;
262 } else if ($real_location == '' && $patch_file['action'] != 'delete') {
263 // The folder does not exist. Create it before proceed
265 // find the real path for the folder to be created
266 $full_folder_path = realpath($this->relative_to_atutor_root).
267 substr($patch_file['location'], strlen($this->relative_to_atutor_root)-1);
269 mkdir($full_folder_path, 0755, true);
272 if ($patch_file['action'] == 'alter' || $patch_file['action'] == 'delete' || $patch_file['action'] == 'overwrite')
274 $file = $patch_file['location'] . "/" . $patch_file['name'];
276 $real_file = realpath($file);
277 if (file_exists($file) && !is_writable($file) && !in_array($real_file, $this->need_access_to_files))
279 $this->need_access_to_files[] = $real_file;
281 if (!in_array($real_file, $_SESSION['remove_permission']) && $patch_file['action'] <> 'delete')
282 $_SESSION['remove_permission'][] = $real_file;
287 if (count($this->need_access_to_folders) > 0 || count($this->need_access_to_files) > 0)
289 $this->errors[] = _AT('grant_write_permission');
291 foreach($this->need_access_to_folders as $folder)
293 $this->errors[0] .= '<strong>'. $folder . "</strong><br />";
296 foreach($this->need_access_to_files as $file)
298 $this->errors[0] .= '<strong>'. $file . "</strong><br />";
301 $notes = '<form action="'. $_SERVER['PHP_SELF'].'?id='.$id.'&who='. $who .'" method="post" name="skip_files_modified">
302 <div class="row buttons">
303 <input type="submit" name="yes" value="'._AT('continue').'" accesskey="y" />
304 <input type="submit" name="no" value="'. _AT('cancel'). '" />
305 <input type="hidden" name="install" value="' . $_POST['install'] . '" />
306 <input type="hidden" name="install_upload" value="' . $_POST['install_upload'] . '" />
307 <input type="hidden" name="ignore_version" value="' . $_POST['ignore_version'] . '" />
311 print_errors($this->errors, $notes);
313 unset($this->errors);
321 * Check if ATutor version is same as "applied version" defined in the patch.
323 * @return true if versions match
324 * false if versions don't match
325 * @author Cindy Qi Li
327 function checkAppliedVersion()
331 if ($this->patch_summary_array["applied_version"] <> VERSION)
333 $this->errors[] = _AT("version_not_match", $this->patch_summary_array["applied_version"]);
336 <form action="'. $_SERVER['PHP_SELF'].'?id='.$_POST['id'].'&who='. $_POST['who'] .'" method="post" name="skip_files_modified">
337 <div class="row buttons">
338 <input type="submit" name="ignore_version" value="'._AT('yes').'" accesskey="y" />
339 <input type="submit" name="not_ignore_version" value="'. _AT('no'). '" />
340 <input type="hidden" name="install" value="' . $_POST['install'] . '" />
341 <input type="hidden" name="install_upload" value="' . $_POST['install_upload'] . '" />
345 print_errors($this->errors, $notes);
347 unset($this->errors);
356 * Check if all the dependent patches have been installed.
358 * @return true if all the dependent patches have been installed
359 * false if any dependent patch has not been installed.
360 * @author Cindy Qi Li
362 function checkDependentPatches()
366 $dependent_patches_installed = true;
368 // if no dependent patch defined, return true
369 if (!is_array($this->patch_summary_array["dependent_patches"])) return true;
371 foreach($this->patch_summary_array["dependent_patches"] as $num => $dependent_patch)
373 if (!is_patch_installed($dependent_patch))
375 $dependent_patches_installed = false;
376 $dependent_patches .= $dependent_patch. ", ";
380 if (!$dependent_patches_installed)
382 $errors = array('PATCH_DEPENDENCY', substr($dependent_patches, 0, -2));
383 $msg->addError($errors);
391 * Loop thru all the patch files that will be overwitten or altered,
392 * to find out if they are modified by user. If it's modified, warn user.
394 * @return true if there are files being modified
395 * false if no file is modified
396 * @author Cindy Qi Li
398 function hasFilesModified()
400 $overwrite_modified_files = false;
401 $alter_modified_files = false;
402 $has_not_exist_files = false;
404 // no file action is defined, return nothing is modified (false)
405 if (!is_array($this->patch_array[files])) return false;
407 foreach ($this->patch_array[files] as $row_num => $patch_file)
409 if ($patch_file["action"]=='alter' || $patch_file["action"]=='overwrite')
411 if (!file_exists($patch_file['location'] . $patch_file['name']))
413 $not_exist_files .= $patch_file['location'] . $patch_file['name'] . '<br />';
414 $has_not_exist_files = true;
416 else if ($this->isFileModified($patch_file['location'], $patch_file['name']))
418 if ($patch_file['action']=='overwrite')
420 $overwrite_files .= realpath($patch_file['location'] . $patch_file['name']) . '<br />';
421 $overwrite_modified_files = true;
423 if ($patch_file['action']=='alter')
425 $alter_files .= realpath($patch_file['location'] . $patch_file['name']) . '<br />';
426 $alter_modified_files = true;
432 if ($has_not_exist_files) $this->errors[] = _AT('patch_local_file_not_exist'). $not_exist_files;
433 if ($overwrite_modified_files) $this->errors[] = _AT('patcher_overwrite_modified_files') . $overwrite_files;
434 if ($alter_modified_files) $this->errors[] = _AT('patcher_alter_modified_files') . $alter_files;
435 if (count($this->errors) > 0)
437 if ($has_not_exist_files)
441 <form action="'. $_SERVER['PHP_SELF'].'?id='.$_POST['id'].'&who='. $_POST['who'] .'" method="post" name="skip_files_modified">
442 <div class="row buttons">
443 <input type="submit" name="yes" value="'._AT('yes').'" accesskey="y" />
444 <input type="submit" name="no" value="'. _AT('no'). '" />
445 <input type="hidden" name="install" value="' . $_POST['install'] . '" />
446 <input type="hidden" name="install_upload" value="' . $_POST['install_upload'] . '" />
447 <input type="hidden" name="ignore_version" value="' . $_POST['ignore_version'] . '" />
451 print_errors($this->errors, $notes);
453 unset($this->errors);
461 * Compare user's local file with SVN backup for user's ATutor version,
462 * if different, check table at_patches_files to see if user's local file
463 * was altered by previous patch installation. If it is, return false
464 * (not modified), otherwise, return true (modified).
466 * @param $folder folder of the file to be compared
467 * $file name of the file to be compared
468 * @return true if the file is modified
469 * false if the file is not modified
470 * @author Cindy Qi Li
472 function isFileModified($folder, $file)
476 if (!$this->svn_server_connected) return true;
478 $svn_file = $this->svn_tag_folder . 'atutor_' . str_replace('.', '_', VERSION) .
479 str_replace(substr($this->relative_to_atutor_root, 0, -1), '' , $folder) .$file;
480 $local_file = $folder.$file;
482 // if svn script does not exist, consider the script is modified
483 if (!file_get_contents($svn_file)) return true;
485 // check if the local file has been modified by user. if it is, don't overwrite
486 if ($this->compareFiles($svn_file, $local_file) <> 0)
488 // check if the file was changed by previous installed patches
489 $sql = "SELECT count(*) num_of_updates FROM " . TABLE_PREFIX. "patches patches, " . TABLE_PREFIX."patches_files patches_files " .
490 "WHERE patches.applied_version = '" . VERSION . "' ".
491 " AND patches.status = 'Installed' " .
492 " AND patches.patches_id = patches_files.patches_id " .
493 " AND patches_files.name = '" . $file . "'";
495 $result = mysql_query($sql, $db) or die(mysql_error());
496 $row = mysql_fetch_assoc($result);
498 if ($row["num_of_updates"] == 0) return true;
504 * Run SQL defined in patch.xml
506 * @author Cindy Qi Li
511 // As sqlutility.class.php reads sql from a file, write sql to module content folder
512 $patch_sql_file = $this->module_content_dir . '/' . $this->sql_file;
514 $fp = fopen($patch_sql_file, 'w');
515 fwrite($fp, trim($this->patch_array['sql']));
518 require(AT_INCLUDE_PATH . 'classes/sqlutility.class.php');
519 $sqlUtility = new SqlUtility();
521 $sqlUtility->queryFromFile($patch_sql_file, TABLE_PREFIX);
523 @unlink($patch_sql_file);
529 * Copy file from update.atutor.ca to user's computer
531 * @param $row_num row number of patch record to be processed
532 * @author Cindy Qi Li
534 function addFile($row_num)
536 $this->copyFile($this->baseURL . preg_replace('/.php$/', '.new', $this->patch_array['files'][$row_num]['name']), $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name']);
542 * Delete file, backup before deletion
544 * @param $row_num row number of patch record to be processed
545 * @author Cindy Qi Li
547 function deleteFile($row_num)
549 $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name'];
550 $backup_file = $local_file . "." . $this->backup_suffix;
552 if (file_exists($local_file))
554 // move file to backup
555 $this->copyFile($local_file, $backup_file);
556 $this->backup_files[] = realpath($backup_file);
557 @unlink($local_file);
565 * Alter file based on <action_detail>
566 * If user's local file is modified and user agrees to proceed with applying patch,
567 * alter user's local file.
569 * @param $row_num row number of patch record to be processed
570 * @author Cindy Qi Li
572 function alterFile($row_num)
574 $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name'];
576 // backup user's file
577 $backup_file = $local_file . "." . $this->backup_suffix;
579 // Checking existence of $backup_file is to fix the bug when there are multiple alter/delete actions
580 // on the same file, the following backups overwrite the first backup which results in the loss of the
582 if (!file_exists($backup_file))
584 $this->copyFile($local_file, $backup_file);
585 $this->backup_files[] = realpath($backup_file);
588 $local_file_content = file_get_contents($local_file);
590 // Modify user's file
591 foreach ($this->patch_array['files'][$row_num]['action_detail'] as $garbage => $alter_file_action)
593 if ($alter_file_action['type'] == 'delete')
594 $modified_local_file_content = $this->strReplace($alter_file_action['code_from'], '', $local_file_content);
596 if ($alter_file_action['type'] == 'replace')
597 $modified_local_file_content = $this->strReplace($alter_file_action['code_from'], $alter_file_action['code_to'], $local_file_content);
599 // when code_from is not found, add in warning
600 if ($modified_local_file_content == $local_file_content)
602 for ($i = 0; $i < count($this->backup_files); $i++)
603 if ($this->backup_files[$i] == realpath($backup_file))
604 $this->backup_files[$i] .= ' '._AT("chunks_not_found");
607 $local_file_content = $modified_local_file_content;
609 $this->createPatchesFilesActionsRecord($alter_file_action);
612 $fp = fopen($local_file, 'w');
613 fwrite($fp, $local_file_content);
620 * Fetch file from update.atutor.ca and overwrite user's local file if the local file is not modified
621 * If user's local file is modified and user agrees to proceed with applying patch,
622 * copy the new file to user's local for them to merge manually.
624 * @param $row_num row number of patch record to be processed
625 * @author Cindy Qi Li
627 function overwriteFile($row_num)
629 $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name'];
630 $patch_file = $this->baseURL . preg_replace('/.php$/', '.new', $this->patch_array['files'][$row_num]['name']);
632 // if local file is modified and user agrees to proceed with applying patch,
633 // copy the new file to user's local for them to merge manually
634 if ($this->skipFilesModified && $this->isFileModified($this->patch_array['files'][$row_num]['location'], $this->patch_array['files'][$row_num]['name']))
636 $local_patch_file = $local_file . "." . $this->patch_suffix;
638 $this->copyFile($patch_file, $local_patch_file);
640 $this->patch_files[] = realpath($local_patch_file);
644 $backup_file = $local_file . "." . $this->backup_suffix;
646 // backup user's file
647 $this->copyFile($local_file, $backup_file);
648 $this->backup_files[] = realpath($backup_file);
650 // overwrite user's file
651 $this->copyFile($patch_file, $local_file);
658 * Copy file $src to $dest. $src can be a local file or a remote file
660 * @param $src location of the source file
661 * $dest location of the destination file
662 * @author Cindy Qi Li
664 function copyFile($src, $dest)
666 $content = file_get_contents($src);
667 $fp = fopen($dest, 'w');
668 fwrite($fp, $content);
675 * Compare files $src against $dest
677 * @param $src location of the source file
678 * $dest location of the destination file
679 * @return Returns < 0 if $src is less than $dest ; > 0 if $src is greater than $dest, and 0 if they are equal.
680 * @author Cindy Qi Li
682 function compareFiles($src, $dest)
684 // use preg_replace to delete
685 // 1. the line starting with // $Id:
686 // 2. the line starting with $lm = '$LastChangedDate, ending with ;
687 // These lines are created by SVN. It could be different in different copies of the same file.
688 $pattern = '/\/\/ \$Id.*\$|\$lm = \'\$LastChangedDate.*;/';
690 $src_content = preg_replace($pattern, '', file_get_contents($src));
691 $dest_content = preg_replace($pattern, '', file_get_contents($dest));
693 return strcasecmp($src_content, $dest_content);
697 * Replace single/multiple lines of string.
698 * This function handles different new line character at windows/unix platform
700 * @param $search String to replace from
701 * $replace String to replace to
702 * $subject Subject to be handled
703 * @return return replaced string, if nothing is replaced, return original subject
704 * @author Cindy Qi Li
706 function strReplace($search, $replace, $subject)
708 // Note: DO NOT change the order of the array elements.
709 // "\n\r", "\r\n" must come before "\n", "\r" in the array,
710 // otherwise, the new line replace underneath would wrongly replace "\n\r" to "\r\r" or "\n\n"
711 $new_line_array = array("\n\r", "\r\n", "\r", "\n");
713 foreach ($new_line_array as $new_line)
715 if (preg_match('/'.preg_quote($new_line).'/', $search) > 0) $search_new_lines[] = $new_line;
716 if (preg_match('/'.preg_quote($new_line).'/', $replace) > 0) $replace_new_lines[] = $new_line;
717 if (preg_match('/'.preg_quote($new_line).'/', $subject) > 0) $subject_new_lines[] = $new_line;
720 // replace new line chars in $search, $replace, $subject to the last new line in $subject
721 if (is_array($subject_new_lines)) $new_line_replace_to = array_pop($subject_new_lines);
723 if ($new_line_replace_to <> '')
725 if (count($search_new_lines) > 0)
726 foreach ($search_new_lines as $new_line)
727 if ($new_line <> $new_line_replace_to)
728 $search = preg_replace('/'.preg_quote($new_line).'/', $new_line_replace_to, $search);
730 if (count($replace_new_lines) > 0)
731 foreach ($replace_new_lines as $new_line)
732 if ($new_line <> $new_line_replace_to)
733 $replace = preg_replace('/'.preg_quote($new_line).'/', $new_line_replace_to, $replace);
735 if (count($subject_new_lines) > 0)
736 foreach ($subject_new_lines as $new_line)
737 $subject = preg_replace('/'.preg_quote($new_line).'/', $new_line_replace_to, $subject);
740 return preg_replace('/'. preg_quote($search, '/') .'/', $replace, $subject);
744 * Check if the server is down
746 * @param $domain Server Domain
747 * @return return false if server is down, otherwise, return true
748 * @author Cindy Qi Li
750 function pingDomain($domain)
752 $file = @fopen ($domain, 'r');
761 * Insert record into table patches
763 * @param $patch_summary_array Patch summary information
764 * @author Cindy Qi Li
766 function createPatchesRecord($patch_summary_array)
770 $sql = "INSERT INTO " . TABLE_PREFIX. "patches " .
778 remove_permission_files,
784 ('".$patch_summary_array["atutor_patch_id"]."',
785 '".$patch_summary_array["applied_version"]."',
786 '".mysql_real_escape_string($patch_summary_array["patch_folder"])."',
787 '".mysql_real_escape_string($patch_summary_array["description"])."',
788 '".$patch_summary_array["available_to"]."',
789 '".mysql_real_escape_string($patch_summary_array["sql"])."',
790 '".$patch_summary_array["status"]."',
794 '".mysql_real_escape_string($patch_summary_array["author"])."',
798 $result = mysql_query($sql, $db) or die(mysql_error());
800 $this->patch_id = mysql_insert_id();
806 * Insert record into table patches_files
808 * @param $patch_files_array Patch information
809 * @author Cindy Qi Li
811 function createPatchesFilesRecord($patch_files_array)
815 $sql = "INSERT INTO " . TABLE_PREFIX. "patches_files " .
821 (".$this->patch_id.",
822 '".$patch_files_array['action']."',
823 '".mysql_real_escape_string($patch_files_array['name'])."',
824 '".mysql_real_escape_string($patch_files_array['location'])."'
827 $result = mysql_query($sql, $db) or die(mysql_error());
829 $this->patch_file_id = mysql_insert_id();
835 * Insert record into table patches_files_actions
837 * @param $patch_files_actions_array alter file actions and contents
838 * @author Cindy Qi Li
840 function createPatchesFilesActionsRecord($patch_files_actions_array)
844 $sql = "INSERT INTO " . TABLE_PREFIX. "patches_files_actions " .
850 (".$this->patch_file_id.",
851 '".$patch_files_actions_array['type']."',
852 '".mysql_real_escape_string($patch_files_actions_array['code_from'])."',
853 '".mysql_real_escape_string($patch_files_actions_array['code_to'])."'
856 $result = mysql_query($sql, $db) or die(mysql_error());