(no commit message)
[atutor.git] / mods / _standard / patcher / classes / Patch.class.php
1 <?php
2 /************************************************************************/
3 /* ATutor                                                               */
4 /************************************************************************/
5 /* Copyright (c) 2002-2008 by Greg Gay, Joel Kronenberg & Heidi Hazelton*/
6 /* Adaptive Technology Resource Centre / University of Toronto          */
7 /* http://atutor.ca                                                     */
8 /*                                                                      */
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 /************************************************************************/
13 // $Id: Patch.class.php 7208 2008-02-08 16:07:24Z cindy $
14
15 /**
16 * Patch
17 * Class for patch installation
18 * @access       public
19 * @author       Cindy Qi Li
20 * @package      Patch
21 */
22
23 define('AT_INCLUDE_PATH', '../../../include/');
24
25 require_once(AT_INCLUDE_PATH. "../mods/_standard/patcher/include/common.inc.php");
26
27 class Patch {
28
29         // all private
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
34         
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
39
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
47
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
52
53         /**
54         * Constructor: Initialize object members
55         * @access  public
56         * @param   $patch_array The name of the file to find charset definition
57         *          $patch_summary_array
58         *          $skipFilesModified
59         * @author  Cindy Qi Li
60         */
61         function Patch($patch_array, $patch_summary_array, $skipFilesModified, $patch_folder) 
62         {
63                 // add relative path to move to ATutor root folder
64                 for ($i = 0; $i < count($patch_array[files]); $i++)
65                 {
66                         $patch_array[files][$i]['location'] = $this->relative_to_atutor_root . $patch_array[files][$i]['location'];
67                 }
68                 
69                 $this->patch_array = $patch_array; 
70                 $this->patch_summary_array = $patch_summary_array;
71
72                 $this->baseURL = $patch_folder;
73                 $this ->backup_suffix = $patch_array['atutor_patch_id'] . ".old";
74                 $this ->patch_suffix = $patch_array['atutor_patch_id'];
75                 $this->skipFilesModified = $skipFilesModified;
76                 
77                 $this->module_content_dir = AT_CONTENT_DIR . "patcher";
78
79                 session_start();
80                 
81                 if (!is_array($_SESSION['remove_permission'])) $_SESSION['remove_permission']=array();
82                 
83         }
84
85         /**
86         * Main process to apply patch.
87         * @access  public
88         * @return  true  if patch is successfully applied
89         *          false if failed
90         * @author  Cindy Qi Li
91         */
92         function applyPatch() 
93         {
94                 global $msg;
95                 
96                 // Checks on 
97                 // 1. if svn server is up. If not, consider all files manipulated by patch as modified
98                 // 2. if the local file is customized by user
99                 // 3. if script has write priviledge on local file/folder
100                 // 4. if dependent patches have been installed
101                 if (!$this->pingDomain($this->svn_tag_folder)) 
102                 {
103                         $msg->addInfo('CANNOT_CONNECT_SVN_SERVER');
104                         $msg->printInfos();
105                         $this->svn_server_connected = false;
106                 }
107                 else
108                         $this->svn_server_connected = true;
109                 
110                 if (!$this->checkDependentPatches()) return false;
111
112                 if (!$this->checkAppliedVersion()) return false;
113
114                 if (!$this->skipFilesModified && $this->hasFilesModified()) return false;
115                 
116                 if (!$this->checkPriviledge()) return false;
117                 // End of check
118
119                 if (strlen(trim($this->patch_array['sql'])) > 0) $this->runSQL();
120
121                 // Start applying patch
122                 $this->createPatchesRecord($this->patch_summary_array);
123
124                 // if no file action defined, update database and return true
125                 if (!is_array($this->patch_array[files])) 
126                 {
127                         $updateInfo = array("status"=>"Installed");
128                         updatePatchesRecord($this->patch_id, $updateInfo);
129         
130                         return true;
131                 }
132                 
133                 foreach ($this->patch_array[files] as $row_num => $patch_file)
134                 {
135                         $this->createPatchesFilesRecord($this->patch_array['files'][$row_num]);
136
137                         if ($patch_file['action'] == 'alter')
138                         {
139                                 $this->alterFile($row_num);
140                         }
141                         else if ($patch_file['action'] == 'add')
142                         {
143                                 $this->addFile($row_num);
144                         }
145                         else if ($patch_file['action'] == 'delete')
146                         {
147                                 $this->deleteFile($row_num);
148                         }
149                         else if ($patch_file['action'] == 'overwrite')
150                         {
151                                 $this->overwriteFile($row_num);
152                         }
153                 }
154                 
155                 // if only has backup files info, patch is considered successfully installed
156                 // if has permission to remove, considered partly installed
157                 $updateInfo = array();
158
159                 if (count($this->backup_files) > 0)
160                 {
161                         foreach($this->backup_files as $backup_file)
162                                 $backup_files .= $backup_file. '|';
163                 
164                         $updateInfo = array("backup_files"=>mysql_real_escape_string($backup_files));
165                 }
166         
167                 if (count($this->patch_files) > 0)
168                 {
169                         foreach($this->patch_files as $patch_file)
170                                 $patch_files .= $patch_file. '|';
171                 
172                         $updateInfo = array_merge($updateInfo, array("patch_files"=>mysql_real_escape_string($patch_files)));
173                 }
174         
175                 if (is_array($_SESSION['remove_permission']) && count($_SESSION['remove_permission']))
176                 {
177                         foreach($_SESSION['remove_permission'] as $remove_permission_file)
178                                 $remove_permission_files .= $remove_permission_file. '|';
179
180                         $updateInfo = array_merge($updateInfo, array("remove_permission_files"=>mysql_real_escape_string($remove_permission_files), "status"=>"Partly Installed"));
181                 }
182                 else
183                 {
184                         $updateInfo = array_merge($updateInfo, array("status"=>"Installed"));
185                 } 
186
187                 updatePatchesRecord($this->patch_id, $updateInfo);
188                 
189                 unset($_SESSION['remove_permission']);
190
191                 return true;
192         }
193
194         /**
195         * return patch array
196         * @access  public
197         * @return  patch array
198         * @author  Cindy Qi Li
199         */
200         function getPatchArray() 
201         {
202                 return $this->patch_array;
203         }
204         
205         /**
206         * return patch id processed by this object
207         * @access  public
208         * @return  patch id
209         * @author  Cindy Qi Li
210         */
211         function getPatchID() 
212         {
213                 return $this->patch_id;
214         }
215         
216         /**
217         * Check if script has write permission to the files and folders that need to be written
218         * if no permission, warn user to give permission
219         * @access  private
220         * @return  true  if there are files or folders that script has no permission
221         *          false if permissions are in place
222         * @author  Cindy Qi Li
223         */
224         function checkPriviledge()
225         {
226                 global $id, $who;
227                 
228                 // no file action is defined, return true;
229                 if (!is_array($this->patch_array[files])) return true;
230                 
231                 foreach ($this->patch_array[files] as $row_num => $patch_file)
232                 {
233                         $real_location = realpath($patch_file['location']);
234                         if (!is_writable($patch_file['location']) && !in_array($real_location, $this->need_access_to_folders))
235                         {
236                                 $this->need_access_to_folders[] = $real_location;
237
238                                 if (!in_array($real_location, $_SESSION['remove_permission']))
239                                         $_SESSION['remove_permission'][] = $real_location;
240                         }
241
242                         if ($patch_file['action'] == 'alter' || $patch_file['action'] == 'delete' || $patch_file['action'] == 'overwrite')
243                         {
244                                 $file = $patch_file['location'] . "/" . $patch_file['name'];
245
246                                 $real_file = realpath($file);
247                                 if (file_exists($file) && !is_writable($file) && !in_array($real_file, $this->need_access_to_files))
248                                 {
249                                         $this->need_access_to_files[] = $real_file;
250
251                                         if (!in_array($real_file, $_SESSION['remove_permission']) && $patch_file['action'] <> 'delete')
252                                                 $_SESSION['remove_permission'][] = $real_file;
253                                 }
254                         }
255                 }
256                 
257                 if (count($this->need_access_to_folders) > 0 || count($this->need_access_to_files) > 0)
258                 {
259                         $this->errors[] = _AT('grant_write_permission');
260                         
261                         foreach($this->need_access_to_folders as $folder)
262                         {
263                                 $this->errors[0] .= '<strong>'. $folder . "</strong><br />";
264                         }
265
266                         foreach($this->need_access_to_files as $file)
267                         {
268                                 $this->errors[0] .= '<strong>'. $file . "</strong><br />";
269                         }
270
271                         $notes = '<form action="'. $_SERVER['PHP_SELF'].'?id='.$id.'&who='. $who .'" method="post" name="skip_files_modified">
272                   <div class="row buttons">
273                                 <input type="submit" name="yes" value="'._AT('continue').'" accesskey="y" />
274                                 <input type="submit" name="no" value="'. _AT('cancel'). '" />
275                                 <input type="hidden" name="install" value="' . $_POST['install'] . '" />
276                                 <input type="hidden" name="install_upload" value="' . $_POST['install_upload'] . '" />
277                                 <input type="hidden" name="ignore_version" value="' . $_POST['ignore_version'] . '" />
278                         </div>
279                         </form>';
280                         
281                         print_errors($this->errors, $notes);
282                 
283                         unset($this->errors);
284                         return false;
285                 }
286                 
287                 return true;
288         }
289         
290         /**
291         * Check if ATutor version is same as "applied version" defined in the patch.
292         * @access  private
293         * @return  true  if versions match
294         *          false if versions don't match
295         * @author  Cindy Qi Li
296         */
297         function checkAppliedVersion()
298         {
299                 global $msg;
300                 
301                 if ($this->patch_summary_array["applied_version"] <> VERSION)
302                 {
303                                 $this->errors[] = _AT("version_not_match", $this->patch_summary_array["applied_version"]);
304                                 
305                                 $notes = '
306                           <form action="'. $_SERVER['PHP_SELF'].'?id='.$_POST['id'].'&who='. $_POST['who'] .'" method="post" name="skip_files_modified">
307                           <div class="row buttons">
308                                         <input type="submit" name="ignore_version" value="'._AT('yes').'" accesskey="y" />
309                                         <input type="submit" name="not_ignore_version" value="'. _AT('no'). '" />
310                                         <input type="hidden" name="install" value="' . $_POST['install'] . '" />
311                                         <input type="hidden" name="install_upload" value="' . $_POST['install_upload'] . '" />
312                                 </div>
313                                 </form>';
314
315                         print_errors($this->errors, $notes);
316                 
317                         unset($this->errors);
318                         
319                         return false;
320                 }
321
322                 return true;
323         }
324
325         /**
326         * Check if all the dependent patches have been installed.
327         * @access  private
328         * @return  true  if all the dependent patches have been installed
329         *          false if any dependent patch has not been installed.
330         * @author  Cindy Qi Li
331         */
332         function checkDependentPatches()
333         {
334                 global $msg;
335                 
336                 $dependent_patches_installed = true;
337                 
338                 // if no dependent patch defined, return true
339                 if (!is_array($this->patch_summary_array["dependent_patches"])) return true;
340                 
341                 foreach($this->patch_summary_array["dependent_patches"] as $num => $dependent_patch)
342                 {
343                         if (!is_patch_installed($dependent_patch))
344                         {
345                                 $dependent_patches_installed = false;
346                                 $dependent_patches .= $dependent_patch. ", ";
347                         }
348                 }
349                 
350                 if (!$dependent_patches_installed)
351                 {
352                         $errors = array('PATCH_DEPENDENCY', substr($dependent_patches, 0, -2));
353                         $msg->addError($errors);
354                         return false;
355                 }
356                 
357                 return true;
358         }
359         
360         /**
361         * Loop thru all the patch files that will be overwitten or altered, 
362         * to find out if they are modified by user. If it's modified, warn user.
363         * @access  private
364         * @return  true  if there are files being modified
365         *          false if no file is modified
366         * @author  Cindy Qi Li
367         */
368         function hasFilesModified()
369         {
370                 $overwrite_modified_files = false;
371                 $alter_modified_files = false;
372                 $has_not_exist_files = false;
373                 
374                 // no file action is defined, return nothing is modified (false)
375                 if (!is_array($this->patch_array[files])) return false;
376                 
377                 foreach ($this->patch_array[files] as $row_num => $patch_file)
378                 {
379                         if ($patch_file["action"]=='alter' || $patch_file["action"]=='overwrite')
380                         {
381                                 if (!file_exists($patch_file['location'] . $patch_file['name']))
382                                 {
383                                         $not_exist_files .= $patch_file['location'] . $patch_file['name'] . '<br />';
384                                         $has_not_exist_files = true;
385                                 }
386                                 else if ($this->isFileModified($patch_file['location'], $patch_file['name']))
387                                 {
388                                         if ($patch_file['action']=='overwrite')
389                                         {
390                                                 $overwrite_files .= realpath($patch_file['location'] . $patch_file['name']) . '<br />';
391                                                 $overwrite_modified_files = true;
392                                         }
393                                         if ($patch_file['action']=='alter')
394                                         {
395                                                 $alter_files .= realpath($patch_file['location'] . $patch_file['name']) . '<br />';
396                                                 $alter_modified_files = true;
397                                         }
398                                 }
399                         }
400                 }
401
402                 if ($has_not_exist_files) $this->errors[] = _AT('patch_local_file_not_exist'). $not_exist_files;
403                 if ($overwrite_modified_files)    $this->errors[] = _AT('patcher_overwrite_modified_files') . $overwrite_files;
404                 if ($alter_modified_files)    $this->errors[] = _AT('patcher_alter_modified_files') . $alter_files;
405                 if (count($this->errors) > 0)
406                 {
407                         if ($has_not_exist_files)
408                                 $notes = '';
409                         else
410                                 $notes = '
411                           <form action="'. $_SERVER['PHP_SELF'].'?id='.$_POST['id'].'&who='. $_POST['who'] .'" method="post" name="skip_files_modified">
412                           <div class="row buttons">
413                                         <input type="submit" name="yes" value="'._AT('yes').'" accesskey="y" />
414                                         <input type="submit" name="no" value="'. _AT('no'). '" />
415                                         <input type="hidden" name="install" value="' . $_POST['install'] . '" />
416                                         <input type="hidden" name="install_upload" value="' . $_POST['install_upload'] . '" />
417                                         <input type="hidden" name="ignore_version" value="' . $_POST['ignore_version'] . '" />
418                                 </div>
419                                 </form>';
420
421                         print_errors($this->errors, $notes);
422                 
423                         unset($this->errors);
424                         return true;
425                 }
426                 
427                 return false;
428         }
429
430         /**
431         * Compare user's local file with SVN backup for user's ATutor version,
432         * if different, check table at_patches_files to see if user's local file
433         * was altered by previous patch installation. If it is, return false 
434         * (not modified), otherwise, return true (modified).
435         * @access  private
436         * @param   $folder  folder of the file to be compared
437         *          $file    name of the file to be compared
438         * @return  true     if the file is modified
439         *          false    if the file is not modified
440         * @author  Cindy Qi Li
441         */
442         function isFileModified($folder, $file)
443         {
444                 global $db;
445
446                 if (!$this->svn_server_connected) return true;
447                 
448                 $svn_file = $this->svn_tag_folder . 'atutor_' . str_replace('.', '_', VERSION) .
449                             str_replace(substr($this->relative_to_atutor_root, 0, -1), '' , $folder) .$file;
450                 $local_file = $folder.$file;
451
452                 // if svn script does not exist, consider the script is modified
453                 if (!file_exists($svn_file)) return true;
454                 
455                 // check if the local file has been modified by user. if it is, don't overwrite
456                 if ($this->compareFiles($svn_file, $local_file) <> 0)
457                 {
458                         // check if the file was changed by previous installed patches
459                         $sql = "SELECT count(*) num_of_updates FROM " . TABLE_PREFIX. "patches patches, " . TABLE_PREFIX."patches_files patches_files " .
460                                "WHERE patches.applied_version = '" . VERSION . "' ".
461                                "  AND patches.status = 'Installed' " .
462                                "  AND patches.patches_id = patches_files.patches_id " .
463                                "  AND patches_files.name = '" . $file . "'";
464                         
465                         $result = mysql_query($sql, $db) or die(mysql_error());
466                         $row = mysql_fetch_assoc($result);
467                         
468                         if ($row["num_of_updates"] == 0) return true;
469                 }
470                 return false;
471         }
472
473         /**
474         * Run SQL defined in patch.xml
475         * @access  private
476         * @author  Cindy Qi Li
477         */
478         function runSQL()
479         {
480                 // run sql
481                 // As sqlutility.class.php reads sql from a file, write sql to module content folder
482                 $patch_sql_file = $this->module_content_dir . '/' . $this->sql_file;
483
484                 $fp = fopen($patch_sql_file, 'w');
485                 fwrite($fp, trim($this->patch_array['sql']));
486                 fclose($fp);
487
488                 require(AT_INCLUDE_PATH . 'classes/sqlutility.class.php');
489                 $sqlUtility =& new SqlUtility();
490         
491                 $sqlUtility->queryFromFile($patch_sql_file, TABLE_PREFIX);
492                 
493                 @unlink($patch_sql_file);
494                 
495                 return true;
496         }
497                 
498         /**
499         * Copy file from update.atutor.ca to user's computer
500         * @access  private
501         * @param   $row_num     row number of patch record to be processed
502         * @author  Cindy Qi Li
503         */
504         function addFile($row_num)
505         {
506                 $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']);
507                 
508                 return true;
509         }
510         
511         /**
512         * Delete file, backup before deletion
513         * @access  private
514         * @param   $row_num     row number of patch record to be processed
515         * @author  Cindy Qi Li
516         */
517         function deleteFile($row_num)
518         {
519                 $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name'];
520                 $backup_file = $local_file . "." . $this->backup_suffix;
521                 
522                 if (file_exists($local_file))
523                 {
524                         // move file to backup
525                         $this->copyFile($local_file, $backup_file);
526                         $this->backup_files[] = realpath($backup_file);
527                         @unlink($local_file);
528                 }
529                 
530                 return true;
531                 
532         }
533         
534         /**
535         * Alter file based on <action_detail>
536         * If user's local file is modified and user agrees to proceed with applying patch,
537         * alter user's local file.
538         * @access  private
539         * @param   $row_num     row number of patch record to be processed
540         * @author  Cindy Qi Li
541         */
542         function alterFile($row_num)
543         {
544                 $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name'];
545                 
546                 // backup user's file
547                 $backup_file = $local_file . "." . $this->backup_suffix;
548                 
549                 // Checking existence of $backup_file is to fix the bug when there are multiple alter/delete actions 
550                 // on the same file, the following backups overwrite the first backup which results in the loss of the
551                 // original code.
552                 if (!file_exists($backup_file))
553                 {
554                         $this->copyFile($local_file, $backup_file);
555                         $this->backup_files[] = realpath($backup_file);
556                 }
557                 
558                 $local_file_content = file_get_contents($local_file);
559
560                 // Modify user's file
561                 foreach ($this->patch_array['files'][$row_num]['action_detail'] as $garbage => $alter_file_action)
562                 {
563                         if ($alter_file_action['type'] == 'delete')
564                                 $modified_local_file_content = $this->strReplace($alter_file_action['code_from'], '', $local_file_content);
565
566                         if ($alter_file_action['type'] == 'replace')
567                                 $modified_local_file_content = $this->strReplace($alter_file_action['code_from'], $alter_file_action['code_to'], $local_file_content);
568                                 
569                         // when code_from is not found, add in warning
570                         if ($modified_local_file_content == $local_file_content)  
571                         {
572                                 for ($i = 0; $i < count($this->backup_files); $i++)
573                                         if ($this->backup_files[$i] == realpath($backup_file))
574                                                 $this->backup_files[$i] .= ' '._AT("chunks_not_found");
575                         }
576                         else
577                                 $local_file_content = $modified_local_file_content;
578
579                         $this->createPatchesFilesActionsRecord($alter_file_action);
580                 }
581
582                 $fp = fopen($local_file, 'w');
583                 fwrite($fp, $local_file_content);
584                 fclose($fp);
585
586                 return true;
587         }
588         
589         /**
590         * Fetch file from update.atutor.ca and overwrite user's local file if the local file is not modified
591         * If user's local file is modified and user agrees to proceed with applying patch,
592         * copy the new file to user's local for them to merge manually.
593         * @access  private
594         * @param   $row_num     row number of patch record to be processed
595         * @author  Cindy Qi Li
596         */
597         function overwriteFile($row_num)
598         {
599                 $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name'];
600                 $patch_file = $this->baseURL . preg_replace('/.php$/', '.new', $this->patch_array['files'][$row_num]['name']);
601                 
602                 // if local file is modified and user agrees to proceed with applying patch,
603                 // copy the new file to user's local for them to merge manually
604                 if ($this->skipFilesModified && $this->isFileModified($this->patch_array['files'][$row_num]['location'], $this->patch_array['files'][$row_num]['name']))
605                 {
606                         $local_patch_file = $local_file . "." . $this->patch_suffix;
607
608                         $this->copyFile($patch_file, $local_patch_file);
609                         
610                         $this->patch_files[] = realpath($local_patch_file);
611                 }
612                 else
613                 {
614                         $backup_file = $local_file . "." . $this->backup_suffix;
615                         
616                         // backup user's file
617                         $this->copyFile($local_file, $backup_file);
618                         $this->backup_files[] = realpath($backup_file);
619                         
620                         // overwrite user's file
621                         $this->copyFile($patch_file, $local_file);
622                 }
623                 
624                 return true;
625         }
626         
627         /**
628         * Copy file $src to $dest. $src can be a local file or a remote file
629         * @access  private
630         * @param   $src location of the source file
631         *          $dest        location of the destination file
632         * @author  Cindy Qi Li
633         */
634         function copyFile($src, $dest)
635         {
636                 $content = file_get_contents($src);
637                 $fp = fopen($dest, 'w');
638                 fwrite($fp, $content);
639                 fclose($fp);
640                 
641                 return true;
642         }
643         
644         /**
645         * Compare files $src against $dest
646         * @access  private
647         * @param   $src location of the source file
648         *          $dest        location of the destination file
649         * @return  Returns < 0 if $src is less than $dest ; > 0 if $src is greater than $dest, and 0 if they are equal.
650         * @author  Cindy Qi Li
651         */
652         function compareFiles($src, $dest)
653         {
654                 // use preg_replace to delete 
655                 // 1. the line starting with // $Id:
656                 // 2. the line starting with $lm = '$LastChangedDate, ending with ;
657                 // These lines are created by SVN. It could be different in different copies of the same file.
658                 $pattern = '/\/\/ \$Id.*\$|\$lm = \'\$LastChangedDate.*;/';
659                 
660                 $src_content = preg_replace($pattern, '', file_get_contents($src));
661                 $dest_content = preg_replace($pattern, '', file_get_contents($dest));
662                 
663                 return strcasecmp($src_content, $dest_content);
664         }
665         
666         /**
667         * Replace single/multiple lines of string. 
668         * This function handles different new line character at windows/unix platform
669         * @access  private
670         * @param   $search      String to replace from
671         *          $replace     String to replace to
672         *          $subject Subject to be handled  
673         * @return  return replaced string, if nothing is replaced, return original subject
674         * @author  Cindy Qi Li
675         */
676         function strReplace($search, $replace, $subject)
677         {
678                 $new_line_array = array("\n", "\r", "\n\r", "\r\n");
679                 
680                 foreach ($new_line_array as $new_line)
681                 {
682                         if (preg_match('/'.preg_quote($new_line).'/', $search) > 0)   $search_new_line = $new_line;
683                         if (preg_match('/'.preg_quote($new_line).'/', $replace) > 0)   $replace_new_line = $new_line;
684                         if (preg_match('/'.preg_quote($new_line).'/', $subject) > 0)   $subject_new_line = $new_line;
685                 }
686
687                 // replace new line chars in $search & $replace to new line in $subject
688                 if ($search_new_line <> "" && $search_new_line <> $subject_new_line) 
689                         $search = preg_replace('/'.preg_quote($search_new_line).'/', $subject_new_line, $search);
690                 
691                 if ($replace_new_line <> "" && $replace_new_line <> $subject_new_line) 
692                         $replace = preg_replace('/'.preg_quote($replace_new_line).'/', $subject_new_line, $replace);
693                 
694                 return preg_replace('/'. preg_quote($search, '/') .'/', $replace, $subject);
695         }
696         
697         /**
698         * Check if the server is down
699         * @access  private
700         * @param   $domain      Server Domain
701         * @return  return false if server is down, otherwise, return true
702         * @author  Cindy Qi Li
703         */
704         function pingDomain($domain)
705         {
706     $file = @fopen ($domain, 'r');
707
708     if (!$file) 
709         return false;
710
711     return true;
712         }
713
714         /**
715         * Insert record into table patches
716         * @access  private
717         * @param   $patch_summary_array Patch summary information
718         * @author  Cindy Qi Li
719         */
720         function createPatchesRecord($patch_summary_array)
721         {
722                 global $db;
723                 
724                 $sql = "INSERT INTO " . TABLE_PREFIX. "patches " .
725                                          "(atutor_patch_id, 
726                                            applied_version,
727                                            patch_folder,
728                                            description,
729                                            available_to,
730                                            sql_statement,
731                                            status,
732                                            remove_permission_files,
733                                            backup_files,
734                                            patch_files,
735                                            author,
736                                            installed_date)
737                                           VALUES
738                                           ('".$patch_summary_array["atutor_patch_id"]."',
739                                            '".$patch_summary_array["applied_version"]."',
740                                            '".mysql_real_escape_string($patch_summary_array["patch_folder"])."',
741                                            '".mysql_real_escape_string($patch_summary_array["description"])."',
742                                            '".$patch_summary_array["available_to"]."',
743                                            '".mysql_real_escape_string($patch_summary_array["sql"])."',
744                                            '".$patch_summary_array["status"]."',
745                                            '',
746                                            '',
747                                            '',
748                                            '".mysql_real_escape_string($patch_summary_array["author"])."',
749                                            now()
750                                            )";
751
752                 $result = mysql_query($sql, $db) or die(mysql_error());
753                 
754                 $this->patch_id = mysql_insert_id();
755                 
756                 return true;
757         }
758
759         /**
760         * Insert record into table patches_files
761         * @access  private
762         * @param   $patch_files_array   Patch information
763         * @author  Cindy Qi Li
764         */
765         function createPatchesFilesRecord($patch_files_array)
766         {
767                 global $db;
768                 
769                 $sql = "INSERT INTO " . TABLE_PREFIX. "patches_files " .
770                                          "(patches_id, 
771                                            action,
772                                            name,
773                                            location)
774                                           VALUES
775                                           (".$this->patch_id.",
776                                            '".$patch_files_array['action']."',
777                                            '".mysql_real_escape_string($patch_files_array['name'])."',
778                                            '".mysql_real_escape_string($patch_files_array['location'])."'
779                                            )";
780
781                 $result = mysql_query($sql, $db) or die(mysql_error());
782                 
783                 $this->patch_file_id = mysql_insert_id();
784
785                 return true;
786         }
787
788         /**
789         * Insert record into table patches_files_actions
790         * @access  private
791         * @param   $patch_files_actions_array   alter file actions and contents
792         * @author  Cindy Qi Li
793         */
794         function createPatchesFilesActionsRecord($patch_files_actions_array)
795         {
796                 global $db;
797                 
798                 $sql = "INSERT INTO " . TABLE_PREFIX. "patches_files_actions " .
799                                          "(patches_files_id, 
800                                            action,
801                                            code_from,
802                                            code_to)
803                                           VALUES
804                                           (".$this->patch_file_id.",
805                                            '".$patch_files_actions_array['type']."',
806                                            '".mysql_real_escape_string($patch_files_actions_array['code_from'])."',
807                                            '".mysql_real_escape_string($patch_files_actions_array['code_to'])."'
808                                            )";
809
810                 $result = mysql_query($sql, $db) or die(mysql_error());
811                 
812                 return true;
813         }
814 }
815
816 ?>