discuz_patch.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. <?php
  2. /**
  3. * [Discuz!] (C)2001-2099 Comsenz Inc.
  4. * This is NOT a freeware, use is subject to license terms
  5. *
  6. * $Id: discuz_patch.php 33628 2013-07-22 03:48:48Z jeffjzhang $
  7. */
  8. if(!defined('IN_DISCUZ')) {
  9. exit('Access Denied');
  10. }
  11. class discuz_patch {
  12. public function save_patch_setting($settingnew) {
  13. if($settingnew['patch']['autoopened'] && !$this->test_writable(DISCUZ_ROOT)) {
  14. return false;
  15. }
  16. C::t('common_setting')->update_batch($settingnew);
  17. include_once libfile('function/cache');
  18. updatecache('setting');
  19. return true;
  20. }
  21. public function fetch_patch_notice() {
  22. global $_G;
  23. $serials = $fixed_serials = $unfixed_serials = array();
  24. $showpatchnotice = 1;
  25. $serials = C::t('common_patch')->fetch_all();
  26. if($serials) {
  27. foreach($serials as $serial) {
  28. if($serial['status'] <= 0) {
  29. $showpatchnotice = 2;
  30. $unfixed_serials[] = $serial;
  31. } else {
  32. $fixed_serials[] = $serial;
  33. }
  34. }
  35. }
  36. if($showpatchnotice == 2) {
  37. $serials = $unfixed_serials;
  38. } else {
  39. C::t('common_setting')->delete('showpatchnotice');
  40. include_once libfile('function/cache');
  41. updatecache('setting');
  42. }
  43. return array('fixed' => (!empty($serials) && $showpatchnotice == 1) ? 1 : 0, 'data' => $serials);
  44. }
  45. public function check_patch($ignore = 0) {
  46. global $_G;
  47. if(!$ignore && $_G['cookie']['checkpatch']) {
  48. return false;
  49. }
  50. require_once DISCUZ_ROOT.'source/discuz_version.php';
  51. require_once libfile('class/xml');
  52. $versionpath = '';
  53. foreach(explode(' ', substr(DISCUZ_VERSION, 1)) as $unit) {
  54. $versionpath = $unit;
  55. break;
  56. }
  57. $patchdir = 'http://upgrade.discuz.com/DiscuzX/'.$versionpath.'/';
  58. $checkurl = $patchdir.'md5sums';
  59. $patchlist = dfsockopen($checkurl);
  60. if(defined('DISCUZ_FIXBUG')) {
  61. C::t('common_patch')->update_status_by_serial(1, DISCUZ_FIXBUG, '<=');
  62. }
  63. if($patchlist) {
  64. $serial_md5s = explode("\r\n", $patchlist);
  65. $bound = intval(substr($serial_md5s[count($serial_md5s)-2], 0, 8));
  66. $maxpatch = intval(C::t('common_patch')->fetch_max_serial());
  67. if(defined('DISCUZ_FIXBUG')) {
  68. $maxpatch = $maxpatch < DISCUZ_FIXBUG ? DISCUZ_FIXBUG : $maxpatch;
  69. }
  70. if($bound > $maxpatch) {
  71. $insertarrlist = array();
  72. foreach($serial_md5s as $serial_md5) {
  73. $downloadpatch = $patch = '';
  74. list($serial, $md5, $release) = explode(' ', $serial_md5);
  75. if($serial > $maxpatch && (!$release || in_array(DISCUZ_RELEASE, explode(',', $release)))) {
  76. $downloadpatch = $patchdir.$serial.'.xml';
  77. $patch = dfsockopen($downloadpatch);
  78. if(md5($patch) != $md5) {
  79. continue;
  80. }
  81. $patch = xml2array($patch);
  82. if(is_array($patch) && !empty($patch)) {
  83. $insertarr = array(
  84. 'serial' => intval($patch['serial']),
  85. 'rule' => serialize($patch['rule']),
  86. 'note' => $patch['note'],
  87. 'status' => 0,
  88. 'dateline' => $patch['dateline'],
  89. );
  90. C::t('common_patch')->insert($insertarr);
  91. $insertarrlist[$insertarr['serial']] = $insertarr;
  92. }
  93. }
  94. }
  95. if($insertarrlist && $_G['setting']['patch']['autoopened']) {
  96. foreach($insertarrlist as $key => $patch) {
  97. $this->fix_patch($patch);
  98. }
  99. }
  100. if($insertarrlist) {
  101. C::t('common_setting')->update('showpatchnotice', 1);
  102. include_once libfile('function/cache');
  103. updatecache('setting');
  104. }
  105. }
  106. }
  107. dsetcookie('checkpatch', 1, 60);
  108. return true;
  109. }
  110. public function fix_patch($patch, $type = 'file') {
  111. global $_G;
  112. $serial = $patch['serial'];
  113. if(!$serial) {
  114. return -1;
  115. }
  116. $returnflag = 1;
  117. $trymax = 1000;
  118. $rules = dunserialize($patch['rule']);
  119. $tmpfiles = $bakfiles = array();
  120. if($type == 'ftp') {
  121. $siteftp = $_GET['siteftp'];
  122. }
  123. foreach($rules as $rule) {
  124. $filename = DISCUZ_ROOT.$rule['filename'];
  125. $search = base64_decode($rule['search']);
  126. $replace = base64_decode($rule['replace']);
  127. $count = $rule['count'];
  128. $nums = $rule['nums'];
  129. if(!$siteftp && !is_writable($filename)) {
  130. $returnflag = -2;
  131. break;
  132. }
  133. $str = file_get_contents($filename);
  134. $findcount = substr_count($str, $search);
  135. if($findcount != $count) {
  136. $returnflag = 2;
  137. break;
  138. }
  139. $bakfile = basename($rule['filename']);
  140. $bakfile = '_'.$serial.'_'.substr($bakfile, 0, strrpos($bakfile, '.')).'_'.substr(md5($_G['config']['security']['authkey']), -6).'.bak.'.substr($bakfile, strrpos($bakfile, '.') +1);
  141. $bakfile = $siteftp ? dirname($rule['filename']).'/'.$bakfile : dirname($filename).'/'.$bakfile;
  142. $tmpfile = tempnam(DISCUZ_ROOT.'./data', 'patch');
  143. $strarr = explode($search, $str);
  144. $replacestr = '';
  145. foreach($strarr as $key => $value) {
  146. if($key == $findcount) {
  147. $replacestr .= $value;
  148. } else {
  149. if(in_array(($key + 1), $nums)) {
  150. $replacestr .= $value.$replace;
  151. } else {
  152. $replacestr .= $value.$search;
  153. }
  154. }
  155. }
  156. if(!file_put_contents($tmpfile, $replacestr)) {
  157. $returnflag = -3;
  158. break;
  159. }
  160. if($siteftp) {
  161. if(!file_exists(DISCUZ_ROOT.$bakfile) && !$this->copy_file($filename, $bakfile, 'ftp')) {
  162. $returnflag = -4;
  163. break;
  164. }
  165. $i = 0;
  166. while(!$this->copy_file($tmpfile, $rule['filename'], 'ftp')) {
  167. if($i >= $trymax) {
  168. $returnflag = -4;
  169. break;
  170. }
  171. $i++;
  172. }
  173. } else {
  174. if(!file_exists($bakfile) && !$this->copy_file($filename, $bakfile, 'file')) {
  175. $returnflag = -5;
  176. break;
  177. }
  178. $i = 0;
  179. while(!$this->copy_file($tmpfile, $filename, 'file')) {
  180. if($i >= $trymax) {
  181. $returnflag = -5;
  182. break;
  183. }
  184. $i++;
  185. }
  186. }
  187. $tmpfiles[] = $tmpfile;
  188. $bakfiles[] = $bakfile;
  189. }
  190. if($returnflag < 0) {
  191. if(!empty($bakfiles)) {
  192. foreach($bakfiles as $backfile) {
  193. if($siteftp) {
  194. $i = 0;
  195. while(!$this->copy_file($backfile, substr($backfile, -12), 'ftp')) {
  196. if($i >= $trymax) {
  197. $returnflag = -6;
  198. break;
  199. }
  200. $i++;
  201. }
  202. } else {
  203. $i = 0;
  204. while(!$this->copy_file($backfile, substr($backfile, -12), 'file')) {
  205. if($i >= $trymax) {
  206. $returnflag = -6;
  207. break;
  208. }
  209. $i++;
  210. }
  211. }
  212. }
  213. }
  214. }
  215. if(!empty($tmpfiles)) {
  216. foreach($tmpfiles as $tmpfile) {
  217. @unlink($tmpfile);
  218. }
  219. }
  220. C::t('common_patch')->update($serial, array('status' => $returnflag));
  221. return $returnflag;
  222. }
  223. public function test_writable($sdir) {
  224. $dir = opendir($sdir);
  225. while($entry = readdir($dir)) {
  226. $file = $sdir.$entry;
  227. if($entry != '.' && $entry != '..') {
  228. if(is_dir($file) && !strrpos($file.'/', '.svn')) {
  229. if(!self::test_writable($file.'/')) {
  230. return false;
  231. }
  232. }
  233. }
  234. }
  235. if($fp = @fopen("$sdir/test.txt", 'w')) {
  236. @fclose($fp);
  237. @unlink("$sdir/test.txt");
  238. $writeable = true;
  239. } else {
  240. $writeable = false;
  241. }
  242. return $writeable;
  243. }
  244. public function test_patch_writable($patch) {
  245. $rules = dunserialize($patch['rule']);
  246. if($rules) {
  247. foreach($rules as $rule) {
  248. if(!is_writable(DISCUZ_ROOT.$rule['filename'])) {
  249. return false;
  250. }
  251. }
  252. return true;
  253. }
  254. return false;
  255. }
  256. public function copy_file($srcfile, $desfile, $type) {
  257. global $_G;
  258. if(!is_file($srcfile)) {
  259. return false;
  260. }
  261. if($type == 'file') {
  262. $this->mkdirs(dirname($desfile));
  263. copy($srcfile, $desfile);
  264. } elseif($type == 'ftp') {
  265. $siteftp = $_GET['siteftp'];
  266. $siteftp['on'] = 1;
  267. $siteftp['password'] = authcode($siteftp['password'], 'ENCODE', md5($_G['config']['security']['authkey']));
  268. $ftp = & discuz_ftp::instance($siteftp);
  269. $ftp->connect();
  270. $ftp->upload($srcfile, $desfile);
  271. if($ftp->error()) {
  272. return false;
  273. }
  274. }
  275. return true;
  276. }
  277. public function mkdirs($dir) {
  278. if(!is_dir($dir)) {
  279. if(!self::mkdirs(dirname($dir))) {
  280. return false;
  281. }
  282. if(!mkdir($dir)) {
  283. return false;
  284. }
  285. }
  286. return true;
  287. }
  288. public function test_patch($patch) {
  289. $serial = $patch['serial'];
  290. $rules = dunserialize($patch['rule']);
  291. foreach($rules as $rule) {
  292. $filename = DISCUZ_ROOT.$rule['filename'];
  293. $search = base64_decode($rule['search']);
  294. $replace = base64_decode($rule['replace']);
  295. $count = $rule['count'];
  296. $nums = $rule['nums'];
  297. $str = file_get_contents($filename);
  298. $findcount = substr_count($str, $search);
  299. if($findcount != $count) {
  300. return true;
  301. }
  302. $replacefindcount = substr_count($str, $replace);
  303. if($replacefindcount == $count) {
  304. return true;
  305. }
  306. }
  307. return false;
  308. }
  309. public function recheck_patch() {
  310. $updatestatus = array();
  311. $patchlist = C::t('common_patch')->fetch_patch_by_status(array(1,2));
  312. foreach($patchlist as $patch) {
  313. if(!$this->test_patch($patch)) {
  314. $updatestatus[] = $patch['serial'];
  315. }
  316. }
  317. if($updatestatus) {
  318. C::t('common_patch')->update_status_by_serial(0, $updatestatus);
  319. }
  320. return true;
  321. }
  322. }
  323. ?>