PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Sources/ScheduledTasks.php

https://github.com/smf-portal/SMF2.1
PHP | 1778 lines | 1299 code | 226 blank | 253 comment | 201 complexity | 869149a99c9f9b265b724093eaca476a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * This file is automatically called and handles all manner of scheduled things.
  4. *
  5. * Simple Machines Forum (SMF)
  6. *
  7. * @package SMF
  8. * @author Simple Machines http://www.simplemachines.org
  9. * @copyright 2012 Simple Machines
  10. * @license http://www.simplemachines.org/about/smf/license.php BSD
  11. *
  12. * @version 2.1 Alpha 1
  13. */
  14. if (!defined('SMF'))
  15. die('Hacking attempt...');
  16. /**
  17. * This function works out what to do!
  18. */
  19. function AutoTask()
  20. {
  21. global $time_start, $modSettings, $smcFunc;
  22. // Special case for doing the mail queue.
  23. if (isset($_GET['scheduled']) && $_GET['scheduled'] == 'mailq')
  24. ReduceMailQueue();
  25. else
  26. {
  27. call_integration_hook('integrate_autotask_include');
  28. // Select the next task to do.
  29. $request = $smcFunc['db_query']('', '
  30. SELECT id_task, task, next_time, time_offset, time_regularity, time_unit
  31. FROM {db_prefix}scheduled_tasks
  32. WHERE disabled = {int:not_disabled}
  33. AND next_time <= {int:current_time}
  34. ORDER BY next_time ASC
  35. LIMIT 1',
  36. array(
  37. 'not_disabled' => 0,
  38. 'current_time' => time(),
  39. )
  40. );
  41. if ($smcFunc['db_num_rows']($request) != 0)
  42. {
  43. // The two important things really...
  44. $row = $smcFunc['db_fetch_assoc']($request);
  45. // When should this next be run?
  46. $next_time = next_time($row['time_regularity'], $row['time_unit'], $row['time_offset']);
  47. // How long in seconds it the gap?
  48. $duration = $row['time_regularity'];
  49. if ($row['time_unit'] == 'm')
  50. $duration *= 60;
  51. elseif ($row['time_unit'] == 'h')
  52. $duration *= 3600;
  53. elseif ($row['time_unit'] == 'd')
  54. $duration *= 86400;
  55. elseif ($row['time_unit'] == 'w')
  56. $duration *= 604800;
  57. // If we were really late running this task actually skip the next one.
  58. if (time() + ($duration / 2) > $next_time)
  59. $next_time += $duration;
  60. // Update it now, so no others run this!
  61. $smcFunc['db_query']('', '
  62. UPDATE {db_prefix}scheduled_tasks
  63. SET next_time = {int:next_time}
  64. WHERE id_task = {int:id_task}
  65. AND next_time = {int:current_next_time}',
  66. array(
  67. 'next_time' => $next_time,
  68. 'id_task' => $row['id_task'],
  69. 'current_next_time' => $row['next_time'],
  70. )
  71. );
  72. $affected_rows = $smcFunc['db_affected_rows']();
  73. // The function must exist or we are wasting our time, plus do some timestamp checking, and database check!
  74. if (function_exists('scheduled_' . $row['task']) && (!isset($_GET['ts']) || $_GET['ts'] == $row['next_time']) && $affected_rows)
  75. {
  76. ignore_user_abort(true);
  77. // Do the task...
  78. $completed = call_user_func('scheduled_' . $row['task']);
  79. // Log that we did it ;)
  80. if ($completed)
  81. {
  82. $total_time = round(array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)), 3);
  83. $smcFunc['db_insert']('',
  84. '{db_prefix}log_scheduled_tasks',
  85. array(
  86. 'id_task' => 'int', 'time_run' => 'int', 'time_taken' => 'float',
  87. ),
  88. array(
  89. $row['id_task'], time(), (int) $total_time,
  90. ),
  91. array()
  92. );
  93. }
  94. }
  95. }
  96. $smcFunc['db_free_result']($request);
  97. // Get the next timestamp right.
  98. $request = $smcFunc['db_query']('', '
  99. SELECT next_time
  100. FROM {db_prefix}scheduled_tasks
  101. WHERE disabled = {int:not_disabled}
  102. ORDER BY next_time ASC
  103. LIMIT 1',
  104. array(
  105. 'not_disabled' => 0,
  106. )
  107. );
  108. // No new task scheduled yet?
  109. if ($smcFunc['db_num_rows']($request) === 0)
  110. $nextEvent = time() + 86400;
  111. else
  112. list ($nextEvent) = $smcFunc['db_fetch_row']($request);
  113. $smcFunc['db_free_result']($request);
  114. updateSettings(array('next_task_time' => $nextEvent));
  115. }
  116. // Shall we return?
  117. if (!isset($_GET['scheduled']))
  118. return true;
  119. // Finally, send some stuff...
  120. header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
  121. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  122. header('Content-Type: image/gif');
  123. die("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B");
  124. }
  125. /**
  126. * Function to sending out approval notices to moderators etc.
  127. */
  128. function scheduled_approval_notification()
  129. {
  130. global $scripturl, $modSettings, $mbname, $txt, $sourcedir, $smcFunc;
  131. // Grab all the items awaiting approval and sort type then board - clear up any things that are no longer relevant.
  132. $request = $smcFunc['db_query']('', '
  133. SELECT aq.id_msg, aq.id_attach, aq.id_event, m.id_topic, m.id_board, m.subject, t.id_first_msg,
  134. b.id_profile
  135. FROM {db_prefix}approval_queue AS aq
  136. INNER JOIN {db_prefix}messages AS m ON (m.id_msg = aq.id_msg)
  137. INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
  138. INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)',
  139. array(
  140. )
  141. );
  142. $notices = array();
  143. $profiles = array();
  144. while ($row = $smcFunc['db_fetch_assoc']($request))
  145. {
  146. // If this is no longer around we'll ignore it.
  147. if (empty($row['id_topic']))
  148. continue;
  149. // What type is it?
  150. if ($row['id_first_msg'] && $row['id_first_msg'] == $row['id_msg'])
  151. $type = 'topic';
  152. elseif ($row['id_attach'])
  153. $type = 'attach';
  154. else
  155. $type = 'msg';
  156. // Add it to the array otherwise.
  157. $notices[$row['id_board']][$type][] = array(
  158. 'subject' => $row['subject'],
  159. 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
  160. );
  161. // Store the profile for a bit later.
  162. $profiles[$row['id_board']] = $row['id_profile'];
  163. }
  164. $smcFunc['db_free_result']($request);
  165. // Delete it all!
  166. $smcFunc['db_query']('', '
  167. DELETE FROM {db_prefix}approval_queue',
  168. array(
  169. )
  170. );
  171. // If nothing quit now.
  172. if (empty($notices))
  173. return true;
  174. // Now we need to think about finding out *who* can approve - this is hard!
  175. // First off, get all the groups with this permission and sort by board.
  176. $request = $smcFunc['db_query']('', '
  177. SELECT id_group, id_profile, add_deny
  178. FROM {db_prefix}board_permissions
  179. WHERE permission = {string:approve_posts}
  180. AND id_profile IN ({array_int:profile_list})',
  181. array(
  182. 'profile_list' => $profiles,
  183. 'approve_posts' => 'approve_posts',
  184. )
  185. );
  186. $perms = array();
  187. $addGroups = array(1);
  188. while ($row = $smcFunc['db_fetch_assoc']($request))
  189. {
  190. // Sorry guys, but we have to ignore guests AND members - it would be too many otherwise.
  191. if ($row['id_group'] < 2)
  192. continue;
  193. $perms[$row['id_profile']][$row['add_deny'] ? 'add' : 'deny'][] = $row['id_group'];
  194. // Anyone who can access has to be considered.
  195. if ($row['add_deny'])
  196. $addGroups[] = $row['id_group'];
  197. }
  198. $smcFunc['db_free_result']($request);
  199. // Grab the moderators if they have permission!
  200. $mods = array();
  201. $members = array();
  202. if (in_array(2, $addGroups))
  203. {
  204. $request = $smcFunc['db_query']('', '
  205. SELECT id_member, id_board
  206. FROM {db_prefix}moderators',
  207. array(
  208. )
  209. );
  210. while ($row = $smcFunc['db_fetch_assoc']($request))
  211. {
  212. $mods[$row['id_member']][$row['id_board']] = true;
  213. // Make sure they get included in the big loop.
  214. $members[] = $row['id_member'];
  215. }
  216. $smcFunc['db_free_result']($request);
  217. }
  218. // Come along one and all... until we reject you ;)
  219. $request = $smcFunc['db_query']('', '
  220. SELECT id_member, real_name, email_address, lngfile, id_group, additional_groups, mod_prefs
  221. FROM {db_prefix}members
  222. WHERE id_group IN ({array_int:additional_group_list})
  223. OR FIND_IN_SET({raw:additional_group_list_implode}, additional_groups) != 0' . (empty($members) ? '' : '
  224. OR id_member IN ({array_int:member_list})') . '
  225. ORDER BY lngfile',
  226. array(
  227. 'additional_group_list' => $addGroups,
  228. 'member_list' => $members,
  229. 'additional_group_list_implode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $addGroups),
  230. )
  231. );
  232. $members = array();
  233. while ($row = $smcFunc['db_fetch_assoc']($request))
  234. {
  235. // Check whether they are interested.
  236. if (!empty($row['mod_prefs']))
  237. {
  238. list(,, $pref_binary) = explode('|', $row['mod_prefs']);
  239. if (!($pref_binary & 4))
  240. continue;
  241. }
  242. $members[$row['id_member']] = array(
  243. 'id' => $row['id_member'],
  244. 'groups' => array_merge(explode(',', $row['additional_groups']), array($row['id_group'])),
  245. 'language' => $row['lngfile'],
  246. 'email' => $row['email_address'],
  247. 'name' => $row['real_name'],
  248. );
  249. }
  250. $smcFunc['db_free_result']($request);
  251. // Get the mailing stuff.
  252. require_once($sourcedir . '/Subs-Post.php');
  253. // Need the below for loadLanguage to work!
  254. loadEssentialThemeData();
  255. $current_language = '';
  256. // Finally, loop through each member, work out what they can do, and send it.
  257. foreach ($members as $id => $member)
  258. {
  259. $emailbody = '';
  260. // Load the language file as required.
  261. if (empty($current_language) || $current_language != $member['language'])
  262. $current_language = loadLanguage('EmailTemplates', $member['language'], false);
  263. // Loop through each notice...
  264. foreach ($notices as $board => $notice)
  265. {
  266. $access = false;
  267. // Can they mod in this board?
  268. if (isset($mods[$id][$board]))
  269. $access = true;
  270. // Do the group check...
  271. if (!$access && isset($perms[$profiles[$board]]['add']))
  272. {
  273. // They can access?!
  274. if (array_intersect($perms[$profiles[$board]]['add'], $member['groups']))
  275. $access = true;
  276. // If they have deny rights don't consider them!
  277. if (isset($perms[$profiles[$board]]['deny']))
  278. if (array_intersect($perms[$profiles[$board]]['deny'], $member['groups']))
  279. $access = false;
  280. }
  281. // Finally, fix it for admins!
  282. if (in_array(1, $member['groups']))
  283. $access = true;
  284. // If they can't access it then give it a break!
  285. if (!$access)
  286. continue;
  287. foreach ($notice as $type => $items)
  288. {
  289. // Build up the top of this section.
  290. $emailbody .= $txt['scheduled_approval_email_' . $type] . "\n" .
  291. '------------------------------------------------------' . "\n";
  292. foreach ($items as $item)
  293. $emailbody .= $item['subject'] . ' - ' . $item['href'] . "\n";
  294. $emailbody .= "\n";
  295. }
  296. }
  297. if ($emailbody == '')
  298. continue;
  299. $replacements = array(
  300. 'REALNAME' => $member['name'],
  301. 'BODY' => $emailbody,
  302. );
  303. $emaildata = loadEmailTemplate('scheduled_approval', $replacements, $current_language);
  304. // Send the actual email.
  305. sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 2);
  306. }
  307. // All went well!
  308. return true;
  309. }
  310. /**
  311. * Do some daily cleaning up.
  312. */
  313. function scheduled_daily_maintenance()
  314. {
  315. global $smcFunc, $modSettings, $sourcedir, $db_type;
  316. // First clean out the cache.
  317. clean_cache();
  318. // If warning decrement is enabled and we have people who have not had a new warning in 24 hours, lower their warning level.
  319. list (, , $modSettings['warning_decrement']) = explode(',', $modSettings['warning_settings']);
  320. if ($modSettings['warning_decrement'])
  321. {
  322. // Find every member who has a warning level...
  323. $request = $smcFunc['db_query']('', '
  324. SELECT id_member, warning
  325. FROM {db_prefix}members
  326. WHERE warning > {int:no_warning}',
  327. array(
  328. 'no_warning' => 0,
  329. )
  330. );
  331. $members = array();
  332. while ($row = $smcFunc['db_fetch_assoc']($request))
  333. $members[$row['id_member']] = $row['warning'];
  334. $smcFunc['db_free_result']($request);
  335. // Have some members to check?
  336. if (!empty($members))
  337. {
  338. // Find out when they were last warned.
  339. $request = $smcFunc['db_query']('', '
  340. SELECT id_recipient, MAX(log_time) AS last_warning
  341. FROM {db_prefix}log_comments
  342. WHERE id_recipient IN ({array_int:member_list})
  343. AND comment_type = {string:warning}
  344. GROUP BY id_recipient',
  345. array(
  346. 'member_list' => array_keys($members),
  347. 'warning' => 'warning',
  348. )
  349. );
  350. $member_changes = array();
  351. while ($row = $smcFunc['db_fetch_assoc']($request))
  352. {
  353. // More than 24 hours ago?
  354. if ($row['last_warning'] <= time() - 86400)
  355. $member_changes[] = array(
  356. 'id' => $row['id_recipient'],
  357. 'warning' => $members[$row['id_recipient']] >= $modSettings['warning_decrement'] ? $members[$row['id_recipient']] - $modSettings['warning_decrement'] : 0,
  358. );
  359. }
  360. $smcFunc['db_free_result']($request);
  361. // Have some members to change?
  362. if (!empty($member_changes))
  363. foreach ($member_changes as $change)
  364. $smcFunc['db_query']('', '
  365. UPDATE {db_prefix}members
  366. SET warning = {int:warning}
  367. WHERE id_member = {int:id_member}',
  368. array(
  369. 'warning' => $change['warning'],
  370. 'id_member' => $change['id'],
  371. )
  372. );
  373. }
  374. }
  375. // Do any spider stuff.
  376. if (!empty($modSettings['spider_mode']) && $modSettings['spider_mode'] > 1)
  377. {
  378. require_once($sourcedir . '/ManageSearchEngines.php');
  379. consolidateSpiderStats();
  380. }
  381. // Check the database version - for some buggy MySQL version.
  382. $server_version = $smcFunc['db_server_info']();
  383. if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
  384. updateSettings(array('db_mysql_group_by_fix' => '1'));
  385. elseif (!empty($modSettings['db_mysql_group_by_fix']))
  386. $smcFunc['db_query']('', '
  387. DELETE FROM {db_prefix}settings
  388. WHERE variable = {string:mysql_fix}',
  389. array(
  390. 'mysql_fix' => 'db_mysql_group_by_fix',
  391. )
  392. );
  393. // Regenerate the Diffie-Hellman keys if OpenID is enabled.
  394. if (!empty($modSettings['enableOpenID']))
  395. {
  396. require_once($sourcedir . '/Subs-OpenID.php');
  397. smf_openID_setup_DH(true);
  398. }
  399. elseif (!empty($modSettings['dh_keys']))
  400. $smcFunc['db_query']('', '
  401. DELETE FROM {db_prefix}settings
  402. WHERE variable = {string:dh_keys}',
  403. array(
  404. 'dh_keys' => 'dh_keys',
  405. )
  406. );
  407. // Clean up some old login history information.
  408. $smcFunc['db_query']('', '
  409. DELETE FROM {db_prefix}member_logins
  410. WHERE time > {int:oldLogins}',
  411. array(
  412. 'oldLogins' => !empty($modSettings['loginHistoryDays']) ? 60 * 60 * $modSettings['loginHistoryDays'] : 108000,
  413. ));
  414. // Log we've done it...
  415. return true;
  416. }
  417. /**
  418. * Auto optimize the database?
  419. */
  420. function scheduled_auto_optimize()
  421. {
  422. global $modSettings, $smcFunc, $db_prefix, $db_type;
  423. // By default do it now!
  424. $delay = false;
  425. // As a kind of hack, if the server load is too great delay, but only by a bit!
  426. if (!empty($modSettings['load_average']) && !empty($modSettings['loadavg_auto_opt']) && $modSettings['load_average'] >= $modSettings['loadavg_auto_opt'])
  427. $delay = true;
  428. // Otherwise are we restricting the number of people online for this?
  429. if (!empty($modSettings['autoOptMaxOnline']))
  430. {
  431. $request = $smcFunc['db_query']('', '
  432. SELECT COUNT(*)
  433. FROM {db_prefix}log_online',
  434. array(
  435. )
  436. );
  437. list ($dont_do_it) = $smcFunc['db_fetch_row']($request);
  438. $smcFunc['db_free_result']($request);
  439. if ($dont_do_it > $modSettings['autoOptMaxOnline'])
  440. $delay = true;
  441. }
  442. // If we are gonna delay, do so now!
  443. if ($delay)
  444. return false;
  445. db_extend();
  446. // Get all the tables.
  447. $tables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
  448. // Actually do the optimisation.
  449. if ($db_type == 'sqlite')
  450. $smcFunc['db_optimize_table']($tables[0]);
  451. else
  452. foreach ($tables as $table)
  453. $smcFunc['db_optimize_table']($table);
  454. // Return for the log...
  455. return true;
  456. }
  457. /**
  458. * Send out a daily email of all subscribed topics.
  459. */
  460. function scheduled_daily_digest()
  461. {
  462. global $is_weekly, $txt, $mbname, $scripturl, $sourcedir, $smcFunc, $context, $modSettings;
  463. // We'll want this...
  464. require_once($sourcedir . '/Subs-Post.php');
  465. loadEssentialThemeData();
  466. $is_weekly = !empty($is_weekly) ? 1 : 0;
  467. // Right - get all the notification data FIRST.
  468. $request = $smcFunc['db_query']('', '
  469. SELECT ln.id_topic, COALESCE(t.id_board, ln.id_board) AS id_board, mem.email_address, mem.member_name, mem.notify_types,
  470. mem.lngfile, mem.id_member
  471. FROM {db_prefix}log_notify AS ln
  472. INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
  473. LEFT JOIN {db_prefix}topics AS t ON (ln.id_topic != {int:empty_topic} AND t.id_topic = ln.id_topic)
  474. WHERE mem.notify_regularity = {int:notify_regularity}
  475. AND mem.is_activated = {int:is_activated}',
  476. array(
  477. 'empty_topic' => 0,
  478. 'notify_regularity' => $is_weekly ? '3' : '2',
  479. 'is_activated' => 1,
  480. )
  481. );
  482. $members = array();
  483. $langs = array();
  484. $notify = array();
  485. while ($row = $smcFunc['db_fetch_assoc']($request))
  486. {
  487. if (!isset($members[$row['id_member']]))
  488. {
  489. $members[$row['id_member']] = array(
  490. 'email' => $row['email_address'],
  491. 'name' => $row['member_name'],
  492. 'id' => $row['id_member'],
  493. 'notifyMod' => $row['notify_types'] < 3 ? true : false,
  494. 'lang' => $row['lngfile'],
  495. );
  496. $langs[$row['lngfile']] = $row['lngfile'];
  497. }
  498. // Store this useful data!
  499. $boards[$row['id_board']] = $row['id_board'];
  500. if ($row['id_topic'])
  501. $notify['topics'][$row['id_topic']][] = $row['id_member'];
  502. else
  503. $notify['boards'][$row['id_board']][] = $row['id_member'];
  504. }
  505. $smcFunc['db_free_result']($request);
  506. if (empty($boards))
  507. return true;
  508. // Just get the board names.
  509. $request = $smcFunc['db_query']('', '
  510. SELECT id_board, name
  511. FROM {db_prefix}boards
  512. WHERE id_board IN ({array_int:board_list})',
  513. array(
  514. 'board_list' => $boards,
  515. )
  516. );
  517. $boards = array();
  518. while ($row = $smcFunc['db_fetch_assoc']($request))
  519. $boards[$row['id_board']] = $row['name'];
  520. $smcFunc['db_free_result']($request);
  521. if (empty($boards))
  522. return true;
  523. // Get the actual topics...
  524. $request = $smcFunc['db_query']('', '
  525. SELECT ld.note_type, t.id_topic, t.id_board, t.id_member_started, m.id_msg, m.subject,
  526. b.name AS board_name
  527. FROM {db_prefix}log_digest AS ld
  528. INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ld.id_topic
  529. AND t.id_board IN ({array_int:board_list}))
  530. INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
  531. INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
  532. WHERE ' . ($is_weekly ? 'ld.daily != {int:daily_value}' : 'ld.daily IN (0, 2)'),
  533. array(
  534. 'board_list' => array_keys($boards),
  535. 'daily_value' => 2,
  536. )
  537. );
  538. $types = array();
  539. while ($row = $smcFunc['db_fetch_assoc']($request))
  540. {
  541. if (!isset($types[$row['note_type']][$row['id_board']]))
  542. $types[$row['note_type']][$row['id_board']] = array(
  543. 'lines' => array(),
  544. 'name' => $row['board_name'],
  545. 'id' => $row['id_board'],
  546. );
  547. if ($row['note_type'] == 'reply')
  548. {
  549. if (isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
  550. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['count']++;
  551. else
  552. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
  553. 'id' => $row['id_topic'],
  554. 'subject' => un_htmlspecialchars($row['subject']),
  555. 'count' => 1,
  556. );
  557. }
  558. elseif ($row['note_type'] == 'topic')
  559. {
  560. if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
  561. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
  562. 'id' => $row['id_topic'],
  563. 'subject' => un_htmlspecialchars($row['subject']),
  564. );
  565. }
  566. else
  567. {
  568. if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
  569. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
  570. 'id' => $row['id_topic'],
  571. 'subject' => un_htmlspecialchars($row['subject']),
  572. 'starter' => $row['id_member_started'],
  573. );
  574. }
  575. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array();
  576. if (!empty($notify['topics'][$row['id_topic']]))
  577. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array_merge($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'], $notify['topics'][$row['id_topic']]);
  578. if (!empty($notify['boards'][$row['id_board']]))
  579. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array_merge($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'], $notify['boards'][$row['id_board']]);
  580. }
  581. $smcFunc['db_free_result']($request);
  582. if (empty($types))
  583. return true;
  584. // Let's load all the languages into a cache thingy.
  585. $langtxt = array();
  586. foreach ($langs as $lang)
  587. {
  588. loadLanguage('Post', $lang);
  589. loadLanguage('index', $lang);
  590. loadLanguage('EmailTemplates', $lang);
  591. $langtxt[$lang] = array(
  592. 'subject' => $txt['digest_subject_' . ($is_weekly ? 'weekly' : 'daily')],
  593. 'char_set' => $txt['lang_character_set'],
  594. 'intro' => sprintf($txt['digest_intro_' . ($is_weekly ? 'weekly' : 'daily')], $mbname),
  595. 'new_topics' => $txt['digest_new_topics'],
  596. 'topic_lines' => $txt['digest_new_topics_line'],
  597. 'new_replies' => $txt['digest_new_replies'],
  598. 'mod_actions' => $txt['digest_mod_actions'],
  599. 'replies_one' => $txt['digest_new_replies_one'],
  600. 'replies_many' => $txt['digest_new_replies_many'],
  601. 'sticky' => $txt['digest_mod_act_sticky'],
  602. 'lock' => $txt['digest_mod_act_lock'],
  603. 'unlock' => $txt['digest_mod_act_unlock'],
  604. 'remove' => $txt['digest_mod_act_remove'],
  605. 'move' => $txt['digest_mod_act_move'],
  606. 'merge' => $txt['digest_mod_act_merge'],
  607. 'split' => $txt['digest_mod_act_split'],
  608. 'bye' => $txt['regards_team'],
  609. );
  610. }
  611. // Right - send out the silly things - this will take quite some space!
  612. $emails = array();
  613. foreach ($members as $mid => $member)
  614. {
  615. // Right character set!
  616. $context['character_set'] = empty($modSettings['global_character_set']) ? $langtxt[$lang]['char_set'] : $modSettings['global_character_set'];
  617. // Do the start stuff!
  618. $email = array(
  619. 'subject' => $mbname . ' - ' . $langtxt[$lang]['subject'],
  620. 'body' => $member['name'] . ',' . "\n\n" . $langtxt[$lang]['intro'] . "\n" . $scripturl . '?action=profile;area=notification;u=' . $member['id'] . "\n",
  621. 'email' => $member['email'],
  622. );
  623. // All new topics?
  624. if (isset($types['topic']))
  625. {
  626. $titled = false;
  627. foreach ($types['topic'] as $id => $board)
  628. foreach ($board['lines'] as $topic)
  629. if (in_array($mid, $topic['members']))
  630. {
  631. if (!$titled)
  632. {
  633. $email['body'] .= "\n" . $langtxt[$lang]['new_topics'] . ':' . "\n" . '-----------------------------------------------';
  634. $titled = true;
  635. }
  636. $email['body'] .= "\n" . sprintf($langtxt[$lang]['topic_lines'], $topic['subject'], $board['name']);
  637. }
  638. if ($titled)
  639. $email['body'] .= "\n";
  640. }
  641. // What about replies?
  642. if (isset($types['reply']))
  643. {
  644. $titled = false;
  645. foreach ($types['reply'] as $id => $board)
  646. foreach ($board['lines'] as $topic)
  647. if (in_array($mid, $topic['members']))
  648. {
  649. if (!$titled)
  650. {
  651. $email['body'] .= "\n" . $langtxt[$lang]['new_replies'] . ':' . "\n" . '-----------------------------------------------';
  652. $titled = true;
  653. }
  654. $email['body'] .= "\n" . ($topic['count'] == 1 ? sprintf($langtxt[$lang]['replies_one'], $topic['subject']) : sprintf($langtxt[$lang]['replies_many'], $topic['count'], $topic['subject']));
  655. }
  656. if ($titled)
  657. $email['body'] .= "\n";
  658. }
  659. // Finally, moderation actions!
  660. $titled = false;
  661. foreach ($types as $note_type => $type)
  662. {
  663. if ($note_type == 'topic' || $note_type == 'reply')
  664. continue;
  665. foreach ($type as $id => $board)
  666. foreach ($board['lines'] as $topic)
  667. if (in_array($mid, $topic['members']))
  668. {
  669. if (!$titled)
  670. {
  671. $email['body'] .= "\n" . $langtxt[$lang]['mod_actions'] . ':' . "\n" . '-----------------------------------------------';
  672. $titled = true;
  673. }
  674. $email['body'] .= "\n" . sprintf($langtxt[$lang][$note_type], $topic['subject']);
  675. }
  676. }
  677. if ($titled)
  678. $email['body'] .= "\n";
  679. // Then just say our goodbyes!
  680. $email['body'] .= "\n\n" . $txt['regards_team'];
  681. // Send it - low priority!
  682. sendmail($email['email'], $email['subject'], $email['body'], null, null, false, 4);
  683. }
  684. // Clean up...
  685. if ($is_weekly)
  686. {
  687. $smcFunc['db_query']('', '
  688. DELETE FROM {db_prefix}log_digest
  689. WHERE daily != {int:not_daily}',
  690. array(
  691. 'not_daily' => 0,
  692. )
  693. );
  694. $smcFunc['db_query']('', '
  695. UPDATE {db_prefix}log_digest
  696. SET daily = {int:daily_value}
  697. WHERE daily = {int:not_daily}',
  698. array(
  699. 'daily_value' => 2,
  700. 'not_daily' => 0,
  701. )
  702. );
  703. }
  704. else
  705. {
  706. // Clear any only weekly ones, and stop us from sending daily again.
  707. $smcFunc['db_query']('', '
  708. DELETE FROM {db_prefix}log_digest
  709. WHERE daily = {int:daily_value}',
  710. array(
  711. 'daily_value' => 2,
  712. )
  713. );
  714. $smcFunc['db_query']('', '
  715. UPDATE {db_prefix}log_digest
  716. SET daily = {int:both_value}
  717. WHERE daily = {int:no_value}',
  718. array(
  719. 'both_value' => 1,
  720. 'no_value' => 0,
  721. )
  722. );
  723. }
  724. // Just in case the member changes their settings mark this as sent.
  725. $members = array_keys($members);
  726. $smcFunc['db_query']('', '
  727. UPDATE {db_prefix}log_notify
  728. SET sent = {int:is_sent}
  729. WHERE id_member IN ({array_int:member_list})',
  730. array(
  731. 'member_list' => $members,
  732. 'is_sent' => 1,
  733. )
  734. );
  735. // Log we've done it...
  736. return true;
  737. }
  738. /**
  739. * Like the daily stuff - just seven times less regular ;)
  740. */
  741. function scheduled_weekly_digest()
  742. {
  743. global $is_weekly;
  744. // We just pass through to the daily function - avoid duplication!
  745. $is_weekly = true;
  746. return scheduled_daily_digest();
  747. }
  748. /**
  749. * Send a group of emails from the mail queue.
  750. *
  751. * @param type $number the number to send each loop through
  752. * @param type $override_limit bypassing our limit flaf
  753. * @param type $force_send
  754. * @return boolean
  755. */
  756. function ReduceMailQueue($number = false, $override_limit = false, $force_send = false)
  757. {
  758. global $modSettings, $smcFunc, $sourcedir;
  759. // Are we intending another script to be sending out the queue?
  760. if (!empty($modSettings['mail_queue_use_cron']) && empty($force_send))
  761. return false;
  762. // By default send 5 at once.
  763. if (!$number)
  764. $number = empty($modSettings['mail_quantity']) ? 5 : $modSettings['mail_quantity'];
  765. // If we came with a timestamp, and that doesn't match the next event, then someone else has beaten us.
  766. if (isset($_GET['ts']) && $_GET['ts'] != $modSettings['mail_next_send'] && empty($force_send))
  767. return false;
  768. // By default move the next sending on by 10 seconds, and require an affected row.
  769. if (!$override_limit)
  770. {
  771. $delay = !empty($modSettings['mail_queue_delay']) ? $modSettings['mail_queue_delay'] : (!empty($modSettings['mail_limit']) && $modSettings['mail_limit'] < 5 ? 10 : 5);
  772. $smcFunc['db_query']('', '
  773. UPDATE {db_prefix}settings
  774. SET value = {string:next_mail_send}
  775. WHERE variable = {string:mail_next_send}
  776. AND value = {string:last_send}',
  777. array(
  778. 'next_mail_send' => time() + $delay,
  779. 'mail_next_send' => 'mail_next_send',
  780. 'last_send' => $modSettings['mail_next_send'],
  781. )
  782. );
  783. if ($smcFunc['db_affected_rows']() == 0)
  784. return false;
  785. $modSettings['mail_next_send'] = time() + $delay;
  786. }
  787. // If we're not overriding how many are we allow to send?
  788. if (!$override_limit && !empty($modSettings['mail_limit']))
  789. {
  790. list ($mt, $mn) = @explode('|', $modSettings['mail_recent']);
  791. // Nothing worth noting...
  792. if (empty($mn) || $mt < time() - 60)
  793. {
  794. $mt = time();
  795. $mn = $number;
  796. }
  797. // Otherwise we have a few more we can spend?
  798. elseif ($mn < $modSettings['mail_limit'])
  799. {
  800. $mn += $number;
  801. }
  802. // No more I'm afraid, return!
  803. else
  804. return false;
  805. // Reflect that we're about to send some, do it now to be safe.
  806. updateSettings(array('mail_recent' => $mt . '|' . $mn));
  807. }
  808. // Now we know how many we're sending, let's send them.
  809. $request = $smcFunc['db_query']('', '
  810. SELECT /*!40001 SQL_NO_CACHE */ id_mail, recipient, body, subject, headers, send_html
  811. FROM {db_prefix}mail_queue
  812. ORDER BY priority ASC, id_mail ASC
  813. LIMIT ' . $number,
  814. array(
  815. )
  816. );
  817. $ids = array();
  818. $emails = array();
  819. while ($row = $smcFunc['db_fetch_assoc']($request))
  820. {
  821. // We want to delete these from the database ASAP, so just get the data and go.
  822. $ids[] = $row['id_mail'];
  823. $emails[] = array(
  824. 'to' => $row['recipient'],
  825. 'body' => $row['body'],
  826. 'subject' => $row['subject'],
  827. 'headers' => $row['headers'],
  828. 'send_html' => $row['send_html'],
  829. );
  830. }
  831. $smcFunc['db_free_result']($request);
  832. // Delete, delete, delete!!!
  833. if (!empty($ids))
  834. $smcFunc['db_query']('', '
  835. DELETE FROM {db_prefix}mail_queue
  836. WHERE id_mail IN ({array_int:mail_list})',
  837. array(
  838. 'mail_list' => $ids,
  839. )
  840. );
  841. // Don't believe we have any left?
  842. if (count($ids) < $number)
  843. {
  844. // Only update the setting if no-one else has beaten us to it.
  845. $smcFunc['db_query']('', '
  846. UPDATE {db_prefix}settings
  847. SET value = {string:no_send}
  848. WHERE variable = {string:mail_next_send}
  849. AND value = {string:last_mail_send}',
  850. array(
  851. 'no_send' => '0',
  852. 'mail_next_send' => 'mail_next_send',
  853. 'last_mail_send' => $modSettings['mail_next_send'],
  854. )
  855. );
  856. }
  857. if (empty($ids))
  858. return false;
  859. if (!empty($modSettings['mail_type']) && $modSettings['smtp_host'] != '')
  860. require_once($sourcedir . '/Subs-Post.php');
  861. // Send each email, yea!
  862. $failed_emails = array();
  863. foreach ($emails as $key => $email)
  864. {
  865. if (empty($modSettings['mail_type']) || $modSettings['smtp_host'] == '')
  866. {
  867. $email['subject'] = strtr($email['subject'], array("\r" => '', "\n" => ''));
  868. if (!empty($modSettings['mail_strip_carriage']))
  869. {
  870. $email['body'] = strtr($email['body'], array("\r" => ''));
  871. $email['headers'] = strtr($email['headers'], array("\r" => ''));
  872. }
  873. // No point logging a specific error here, as we have no language. PHP error is helpful anyway...
  874. $result = mail(strtr($email['to'], array("\r" => '', "\n" => '')), $email['subject'], $email['body'], $email['headers']);
  875. // Try to stop a timeout, this would be bad...
  876. @set_time_limit(300);
  877. if (function_exists('apache_reset_timeout'))
  878. @apache_reset_timeout();
  879. }
  880. else
  881. $result = smtp_mail(array($email['to']), $email['subject'], $email['body'], $email['send_html'] ? $email['headers'] : 'Mime-Version: 1.0' . "\r\n" . $email['headers']);
  882. // Hopefully it sent?
  883. if (!$result)
  884. $failed_emails[] = array($email['to'], $email['body'], $email['subject'], $email['headers'], $email['send_html']);
  885. }
  886. // Any emails that didn't send?
  887. if (!empty($failed_emails))
  888. {
  889. // Update the failed attempts check.
  890. $smcFunc['db_insert']('replace',
  891. '{db_prefix}settings',
  892. array('variable' => 'string', 'value' => 'string'),
  893. array('mail_failed_attempts', empty($modSettings['mail_failed_attempts']) ? 1 : ++$modSettings['mail_failed_attempts']),
  894. array('variable')
  895. );
  896. // If we have failed to many times, tell mail to wait a bit and try again.
  897. if ($modSettings['mail_failed_attempts'] > 5)
  898. $smcFunc['db_query']('', '
  899. UPDATE {db_prefix}settings
  900. SET value = {string:mail_next_send}
  901. WHERE variable = {string:next_mail_send}
  902. AND value = {string:last_send}',
  903. array(
  904. 'next_mail_send' => time() + 60,
  905. 'mail_next_send' => 'mail_next_send',
  906. 'last_send' => $modSettings['mail_next_send'],
  907. ));
  908. // Add our email back to the queue, manually.
  909. $smcFunc['db_insert']('insert',
  910. '{db_prefix}mail_queue',
  911. array('recipient' => 'string', 'body' => 'string', 'subject' => 'string', 'headers' => 'string', 'send_html' => 'string'),
  912. $failed_emails,
  913. array('id_mail')
  914. );
  915. return false;
  916. }
  917. // We where unable to send the email, clear our failed attempts.
  918. elseif (!empty($modSettings['mail_failed_attempts']))
  919. $smcFunc['db_query']('', '
  920. UPDATE {db_prefix}settings
  921. SET value = {string:zero}
  922. WHERE variable = {string:mail_failed_attempts}',
  923. array(
  924. 'zero' => '0',
  925. 'mail_failed_attempts' => 'mail_failed_attempts',
  926. ));
  927. // Had something to send...
  928. return true;
  929. }
  930. /**
  931. * Calculate the next time the passed tasks should be triggered.
  932. *
  933. * @param type $tasks
  934. * @param type $forceUpdate
  935. */
  936. function CalculateNextTrigger($tasks = array(), $forceUpdate = false)
  937. {
  938. global $modSettings, $smcFunc;
  939. $task_query = '';
  940. if (!is_array($tasks))
  941. $tasks = array($tasks);
  942. // Actually have something passed?
  943. if (!empty($tasks))
  944. {
  945. if (!isset($tasks[0]) || is_numeric($tasks[0]))
  946. $task_query = ' AND id_task IN ({array_int:tasks})';
  947. else
  948. $task_query = ' AND task IN ({array_string:tasks})';
  949. }
  950. $nextTaskTime = empty($tasks) ? time() + 86400 : $modSettings['next_task_time'];
  951. // Get the critical info for the tasks.
  952. $request = $smcFunc['db_query']('', '
  953. SELECT id_task, next_time, time_offset, time_regularity, time_unit
  954. FROM {db_prefix}scheduled_tasks
  955. WHERE disabled = {int:no_disabled}
  956. ' . $task_query,
  957. array(
  958. 'no_disabled' => 0,
  959. 'tasks' => $tasks,
  960. )
  961. );
  962. $tasks = array();
  963. while ($row = $smcFunc['db_fetch_assoc']($request))
  964. {
  965. $next_time = next_time($row['time_regularity'], $row['time_unit'], $row['time_offset']);
  966. // Only bother moving the task if it's out of place or we're forcing it!
  967. if ($forceUpdate || $next_time < $row['next_time'] || $row['next_time'] < time())
  968. $tasks[$row['id_task']] = $next_time;
  969. else
  970. $next_time = $row['next_time'];
  971. // If this is sooner than the current next task, make this the next task.
  972. if ($next_time < $nextTaskTime)
  973. $nextTaskTime = $next_time;
  974. }
  975. $smcFunc['db_free_result']($request);
  976. // Now make the changes!
  977. foreach ($tasks as $id => $time)
  978. $smcFunc['db_query']('', '
  979. UPDATE {db_prefix}scheduled_tasks
  980. SET next_time = {int:next_time}
  981. WHERE id_task = {int:id_task}',
  982. array(
  983. 'next_time' => $time,
  984. 'id_task' => $id,
  985. )
  986. );
  987. // If the next task is now different update.
  988. if ($modSettings['next_task_time'] != $nextTaskTime)
  989. updateSettings(array('next_task_time' => $nextTaskTime));
  990. }
  991. /**
  992. * Simply returns a time stamp of the next instance of these time parameters.
  993. *
  994. * @param int $regularity
  995. * @param type $unit
  996. * @param type $offset
  997. * @return int
  998. */
  999. function next_time($regularity, $unit, $offset)
  1000. {
  1001. // Just in case!
  1002. if ($regularity == 0)
  1003. $regularity = 2;
  1004. $curHour = date('H', time());
  1005. $curMin = date('i', time());
  1006. $next_time = 9999999999;
  1007. // If the unit is minutes only check regularity in minutes.
  1008. if ($unit == 'm')
  1009. {
  1010. $off = date('i', $offset);
  1011. // If it's now just pretend it ain't,
  1012. if ($off == $curMin)
  1013. $next_time = time() + $regularity;
  1014. else
  1015. {
  1016. // Make sure that the offset is always in the past.
  1017. $off = $off > $curMin ? $off - 60 : $off;
  1018. while ($off <= $curMin)
  1019. $off += $regularity;
  1020. // Now we know when the time should be!
  1021. $next_time = time() + 60 * ($off - $curMin);
  1022. }
  1023. }
  1024. // Otherwise, work out what the offset would be with todays date.
  1025. else
  1026. {
  1027. $next_time = mktime(date('H', $offset), date('i', $offset), 0, date('m'), date('d'), date('Y'));
  1028. // Make the time offset in the past!
  1029. if ($next_time > time())
  1030. {
  1031. $next_time -= 86400;
  1032. }
  1033. // Default we'll jump in hours.
  1034. $applyOffset = 3600;
  1035. // 24 hours = 1 day.
  1036. if ($unit == 'd')
  1037. $applyOffset = 86400;
  1038. // Otherwise a week.
  1039. if ($unit == 'w')
  1040. $applyOffset = 604800;
  1041. $applyOffset *= $regularity;
  1042. // Just add on the offset.
  1043. while ($next_time <= time())
  1044. {
  1045. $next_time += $applyOffset;
  1046. }
  1047. }
  1048. return $next_time;
  1049. }
  1050. /**
  1051. * This loads the bare minimum data to allow us to load language files!
  1052. */
  1053. function loadEssentialThemeData()
  1054. {
  1055. global $settings, $modSettings, $smcFunc, $mbname, $context, $sourcedir;
  1056. // Get all the default theme variables.
  1057. $result = $smcFunc['db_query']('', '
  1058. SELECT id_theme, variable, value
  1059. FROM {db_prefix}themes
  1060. WHERE id_member = {int:no_member}
  1061. AND id_theme IN (1, {int:theme_guests})',
  1062. array(
  1063. 'no_member' => 0,
  1064. 'theme_guests' => $modSettings['theme_guests'],
  1065. )
  1066. );
  1067. while ($row = $smcFunc['db_fetch_assoc']($result))
  1068. {
  1069. $settings[$row['variable']] = $row['value'];
  1070. // Is this the default theme?
  1071. if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1')
  1072. $settings['default_' . $row['variable']] = $row['value'];
  1073. }
  1074. $smcFunc['db_free_result']($result);
  1075. // Check we have some directories setup.
  1076. if (empty($settings['template_dirs']))
  1077. {
  1078. $settings['template_dirs'] = array($settings['theme_dir']);
  1079. // Based on theme (if there is one).
  1080. if (!empty($settings['base_theme_dir']))
  1081. $settings['template_dirs'][] = $settings['base_theme_dir'];
  1082. // Lastly the default theme.
  1083. if ($settings['theme_dir'] != $settings['default_theme_dir'])
  1084. $settings['template_dirs'][] = $settings['default_theme_dir'];
  1085. }
  1086. // Assume we want this.
  1087. $context['forum_name'] = $mbname;
  1088. // Check loadLanguage actually exists!
  1089. if (!function_exists('loadLanguage'))
  1090. {
  1091. require_once($sourcedir . '/Load.php');
  1092. require_once($sourcedir . '/Subs.php');
  1093. }
  1094. loadLanguage('index+Modifications');
  1095. }
  1096. /**
  1097. * This retieves data (e.g. last version of SMF) from sm.org
  1098. */
  1099. function scheduled_fetchSMfiles()
  1100. {
  1101. global $sourcedir, $txt, $language, $settings, $forum_version, $modSettings, $smcFunc;
  1102. // What files do we want to get
  1103. $request = $smcFunc['db_query']('', '
  1104. SELECT id_file, filename, path, parameters
  1105. FROM {db_prefix}admin_info_files',
  1106. array(
  1107. )
  1108. );
  1109. $js_files = array();
  1110. while ($row = $smcFunc['db_fetch_assoc']($request))
  1111. {
  1112. $js_files[$row['id_file']] = array(
  1113. 'filename' => $row['filename'],
  1114. 'path' => $row['path'],
  1115. 'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
  1116. );
  1117. }
  1118. $smcFunc['db_free_result']($request);
  1119. // We're gonna need fetch_web_data() to pull this off.
  1120. require_once($sourcedir . '/Subs-Package.php');
  1121. // Just in case we run into a problem.
  1122. loadEssentialThemeData();
  1123. loadLanguage('Errors', $language, false);
  1124. foreach ($js_files as $ID_FILE => $file)
  1125. {
  1126. // Create the url
  1127. $server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.simplemachines.org' : '';
  1128. $url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
  1129. // Get the file
  1130. $file_data = fetch_web_data($url);
  1131. // If we got an error - give up - the site might be down.
  1132. if ($file_data === false)
  1133. {
  1134. log_error(sprintf($txt['st_cannot_retrieve_file'], $url));
  1135. return false;
  1136. }
  1137. // Save the file to the database.
  1138. $smcFunc['db_query']('substring', '
  1139. UPDATE {db_prefix}admin_info_files
  1140. SET data = SUBSTRING({string:file_data}, 1, 65534)
  1141. WHERE id_file = {int:id_file}',
  1142. array(
  1143. 'id_file' => $ID_FILE,
  1144. 'file_data' => $file_data,
  1145. )
  1146. );
  1147. }
  1148. return true;
  1149. }
  1150. /**
  1151. * Happy birthday!!
  1152. */
  1153. function scheduled_birthdayemails()
  1154. {
  1155. global $modSettings, $sourcedir, $mbname, $txt, $smcFunc, $birthdayEmails;
  1156. // Need this in order to load the language files.
  1157. loadEssentialThemeData();
  1158. // Going to need this to send the emails.
  1159. require_once($sourcedir . '/Subs-Post.php');
  1160. $greeting = isset($modSettings['birthday_email']) ? $modSettings['birthday_email'] : 'happy_birthday';
  1161. // Get the month and day of today.
  1162. $month = date('n'); // Month without leading zeros.
  1163. $day = date('j'); // Day without leading zeros.
  1164. // So who are the lucky ones? Don't include those who are banned and those who don't want them.
  1165. $result = $smcFunc['db_query']('', '
  1166. SELECT id_member, real_name, lngfile, email_address
  1167. FROM {db_prefix}members
  1168. WHERE is_activated < 10
  1169. AND MONTH(birthdate) = {int:month}
  1170. AND DAYOFMONTH(birthdate) = {int:day}
  1171. AND notify_announcements = {int:notify_announcements}
  1172. AND YEAR(birthdate) > {int:year}',
  1173. array(
  1174. 'notify_announcements' => 1,
  1175. 'year' => 1,
  1176. 'month' => $month,
  1177. 'day' => $day,
  1178. )
  1179. );
  1180. // Group them by languages.
  1181. $birthdays = array();
  1182. while ($row = $smcFunc['db_fetch_assoc']($result))
  1183. {
  1184. if (!isset($birthdays[$row['lngfile']]))
  1185. $birthdays[$row['lngfile']] = array();
  1186. $birthdays[$row['lngfile']][$row['id_member']] = array(
  1187. 'name' => $row['real_name'],
  1188. 'email' => $row['email_address']
  1189. );
  1190. }
  1191. $smcFunc['db_free_result']($result);
  1192. // Send out the greetings!
  1193. foreach ($birthdays as $lang => $recps)
  1194. {
  1195. // We need to do some shuffling to make this work properly.
  1196. loadLanguage('EmailTemplates', $lang);
  1197. $txt['emails']['happy_birthday']['subject'] = $txtBirthdayEmails[$greeting . '_subject'];
  1198. $txt['emails']['happy_birthday']['body'] = $txtBirthdayEmails[$greeting . '_body'];
  1199. foreach ($recps as $recp)
  1200. {
  1201. $replacements = array(
  1202. 'REALNAME' => $recp['name'],
  1203. );
  1204. $emaildata = loadEmailTemplate('happy_birthday', $replacements, $lang, false);
  1205. sendmail($recp['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 4);
  1206. // Try to stop a timeout, this would be bad...
  1207. @set_time_limit(300);
  1208. if (function_exists('apache_reset_timeout'))
  1209. @apache_reset_timeout();
  1210. }
  1211. }
  1212. // Flush the mail queue, just in case.
  1213. AddMailQueue(true);
  1214. return true;
  1215. }
  1216. /**
  1217. * Weekly maintenance
  1218. */
  1219. function scheduled_weekly_maintenance()
  1220. {
  1221. global $modSettings, $smcFunc;
  1222. // Delete some settings that needn't be set if they are otherwise empty.
  1223. $emptySettings = array(
  1224. 'warning_mute', 'warning_moderate', 'warning_watch', 'warning_show', 'disableCustomPerPage', 'spider_mode', 'spider_group',
  1225. 'paid_currency_code', 'paid_currency_symbol', 'paid_email_to', 'paid_email', 'paid_enabled', 'paypal_email',
  1226. 'search_enable_captcha', 'search_floodcontrol_time', 'show_spider_online',
  1227. );
  1228. $smcFunc['db_query']('', '
  1229. DELETE FROM {db_prefix}settings
  1230. WHERE variable IN ({array_string:setting_list})
  1231. AND (value = {string:zero_value} OR value = {string:blank_value})',
  1232. array(
  1233. 'zero_value' => '0',
  1234. 'blank_value' => '',
  1235. 'setting_list' => $emptySettings,
  1236. )
  1237. );
  1238. // Some settings we never want to keep - they are just there for temporary purposes.
  1239. $deleteAnywaySettings = array(
  1240. 'attachment_full_notified',
  1241. );
  1242. $smcFunc['db_query']('', '
  1243. DELETE FROM {db_prefix}settings
  1244. WHERE variable IN ({array_string:setting_list})',
  1245. array(
  1246. 'setting_list' => $deleteAnywaySettings,
  1247. )
  1248. );
  1249. // Ok should we prune the logs?
  1250. if (!empty($modSettings['pruningOptions']))
  1251. {
  1252. if (!empty($modSettings['pruningOptions']) && strpos($modSettings['pruningOptions'], ',') !== false)
  1253. list ($modSettings['pruneErrorLog'], $modSettings['pruneModLog'], $modSettings['pruneBanLog'], $modSettings['pruneReportLog'], $modSettings['pruneScheduledTaskLog'], $modSettings['pruneSpiderHitLog']) = explode(',', $modSettings['pruningOptions']);
  1254. if (!empty($modSettings['pruneErrorLog']))
  1255. {
  1256. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1257. $t = time() - $modSettings['pruneErrorLog'] * 86400;
  1258. $smcFunc['db_query']('', '
  1259. DELETE FROM {db_prefix}log_errors
  1260. WHERE log_time < {int:log_time}',
  1261. array(
  1262. 'log_time' => $t,
  1263. )
  1264. );
  1265. }
  1266. if (!empty($modSettings['pruneModLog']))
  1267. {
  1268. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1269. $t = time() - $modSettings['pruneModLog'] * 86400;
  1270. $smcFunc['db_query']('', '
  1271. DELETE FROM {db_prefix}log_actions
  1272. WHERE log_time < {int:log_time}
  1273. AND id_log = {int:moderation_log}',
  1274. array(
  1275. 'log_time' => $t,
  1276. 'moderation_log' => 1,
  1277. )
  1278. );
  1279. }
  1280. if (!empty($modSettings['pruneBanLog']))
  1281. {
  1282. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1283. $t = time() - $modSettings['pruneBanLog'] * 86400;
  1284. $smcFunc['db_query']('', '
  1285. DELETE FROM {db_prefix}log_banned
  1286. WHERE log_time < {int:log_time}',
  1287. array(
  1288. 'log_time' => $t,
  1289. )
  1290. );
  1291. }
  1292. if (!empty($modSettings['pruneReportLog']))
  1293. {
  1294. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1295. $t = time() - $modSettings['pruneReportLog'] * 86400;
  1296. // This one is more complex then the other logs. First we need to figure out which reports are too old.
  1297. $reports = array();
  1298. $result = $smcFunc['db_query']('', '
  1299. SELECT id_report
  1300. FROM {db_prefix}log_reported
  1301. WHERE time_started < {int:time_started}',
  1302. array(
  1303. 'time_started' => $t,
  1304. )
  1305. );
  1306. while ($row = $smcFunc['db_fetch_row']($result))
  1307. $reports[] = $row[0];
  1308. $smcFunc['db_free_result']($result);
  1309. if (!empty($reports))
  1310. {
  1311. // Now delete the reports...
  1312. $smcFunc['db_query']('', '
  1313. DELETE FROM {db_prefix}log_reported
  1314. WHERE id_report IN ({array_int:report_list})',
  1315. array(
  1316. 'report_list' => $reports,
  1317. )
  1318. );
  1319. // And delete the comments for those reports...
  1320. $smcFunc['db_query']('', '
  1321. DELETE FROM {db_prefix}log_reported_comments
  1322. WHERE id_report IN ({array_int:report_list})',
  1323. array(
  1324. 'report_list' => $reports,
  1325. )
  1326. );
  1327. }
  1328. }
  1329. if (!empty($modSettings['pruneScheduledTaskLog']))
  1330. {
  1331. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1332. $t = time() - $modSettings['pruneScheduledTaskLog'] * 86400;
  1333. $smcFunc['db_query']('', '
  1334. DELETE FROM {db_prefix}log_scheduled_tasks
  1335. WHERE time_run < {int:time_run}',
  1336. array(
  1337. 'time_run' => $t,
  1338. )
  1339. );
  1340. }
  1341. if (!empty($modSettings['pruneSpiderHitLog']))
  1342. {
  1343. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1344. $t = time() - $modSettings['pruneSpiderHitLog'] * 86400;
  1345. $smcFunc['db_query']('', '
  1346. DELETE FROM {db_prefix}log_spider_hits
  1347. WHERE log_time < {int:log_time}',
  1348. array(
  1349. 'log_time' => $t,
  1350. )
  1351. );
  1352. }
  1353. }
  1354. // Get rid of any paid subscriptions that were never actioned.
  1355. $smcFunc['db_query']('', '
  1356. DELETE FROM {db_prefix}log_subscribed
  1357. WHERE end_time = {int:no_end_time}
  1358. AND status = {int:not_active}
  1359. AND start_time < {int:start_time}
  1360. AND payments_pending < {int:payments_pending}',
  1361. array(
  1362. 'no_end_time' => 0,
  1363. 'not_active' => 0,
  1364. 'start_time' => time() - 60,
  1365. 'payments_pending' => 1,
  1366. )
  1367. );
  1368. // Some OS's don't seem to clean out their sessions.
  1369. $smcFunc['db_query']('', '
  1370. DELETE FROM {db_prefix}sessions
  1371. WHERE last_update < {int:last_update}',
  1372. array(
  1373. 'last_update' => time() - 86400,
  1374. )
  1375. );
  1376. return true;
  1377. }
  1378. /**
  1379. * Perform the standard checks on expiring/near expiring subscriptions.
  1380. */
  1381. function scheduled_paid_subscriptions()
  1382. {
  1383. global $txt, $sourcedir, $scripturl, $smcFunc, $modSettings, $language;
  1384. // Start off by checking for removed subscriptions.
  1385. $request = $smcFunc['db_query']('', '
  1386. SELECT id_subscribe, id_member
  1387. FROM {db_prefix}log_subscribed
  1388. WHERE status = {int:is_active}
  1389. AND end_time < {int:time_now}',
  1390. array(
  1391. 'is_active' => 1,
  1392. 'time_now' => time(),
  1393. )
  1394. );
  1395. while ($row = $smcFunc['db_fetch_assoc']($request))
  1396. {
  1397. require_once($sourcedir . '/ManagePaid.php');
  1398. removeSubscription($row['id_subscribe'], $row['id_member']);
  1399. }
  1400. $smcFunc['db_free_result']($request);
  1401. // Get all those about to expire that have not had a reminder sent.
  1402. $request = $smcFunc['db_query']('', '
  1403. SELECT ls.id_sublog, m.id_member, m.member_name, m.email_address, m.lngfile, s.name, ls.end_time
  1404. FROM {db_prefix}log_subscribed AS ls
  1405. INNER JOIN {db_prefix}subscriptions AS s ON (s.id_subscribe = ls.id_subscribe)
  1406. INNER JOIN {db_prefix}members AS m ON (m.id_member = ls.id_member)
  1407. WHERE ls.status = {int:is_active}
  1408. AND ls.reminder_sent = {int:reminder_sent}
  1409. AND s.reminder > {int:reminder_wanted}
  1410. AND ls.end_time < ({int:time_now} + s.reminder * 86400)',
  1411. array(
  1412. 'is_active' => 1,
  1413. 'reminder_sent' => 0,
  1414. 'reminder_wanted' => 0,
  1415. 'time_now' => time(),
  1416. )
  1417. );
  1418. $subs_reminded = array();
  1419. while ($row = $smcFunc['db_fetch_assoc']($request))
  1420. {
  1421. // If this is the first one load the important bits.
  1422. if (empty($subs_reminded))
  1423. {
  1424. require_once($sourcedir . '/Subs-Post.php');
  1425. // Need the below for loadLanguage to work!
  1426. loadEssentialThemeData();
  1427. }
  1428. $subs_reminded[] = $row['id_sublog'];
  1429. $replacements = array(
  1430. 'PROFILE_LINK' => $scripturl . '?action=profile;area=subscriptions;u=' . $row['id_member'],
  1431. 'REALNAME' => $row['member_name'],
  1432. 'SUBSCRIPTION' => $row['name'],
  1433. 'END_DATE' => strip_tags(timeformat($row['end_time'])),
  1434. );
  1435. $emaildata = loadEmailTemplate('paid_subscription_reminder', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
  1436. // Send the actual email.
  1437. sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 2);
  1438. }
  1439. $smcFunc['db_free_result']($request);
  1440. // Mark the reminder as sent.
  1441. if (!empty($subs_reminded))
  1442. $smcFunc['db_query']('', '
  1443. UPDATE {db_prefix}log_subscribed
  1444. SET reminder_sent = {int:reminder_sent}
  1445. WHERE id_sublog IN ({array_int:subscription_list})',
  1446. array(
  1447. 'subscription_list' => $subs_reminded,
  1448. 'reminder_sent' => 1,
  1449. )
  1450. );
  1451. return true;
  1452. }
  1453. /**
  1454. * Check for un-posted attachments is something we can do once in a while :P
  1455. * This function uses opendir cycling through all the attachments
  1456. */
  1457. function scheduled_remove_temp_attachments()
  1458. {
  1459. global $modSettings;
  1460. // We need to know where this thing is going.
  1461. if (!empty($modSettings['currentAttachmentUploadDir']))
  1462. {
  1463. if (!is_array($modSettings['attachmentUploadDir']))
  1464. $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
  1465. // Just use the current path for temp files.
  1466. $attach_dirs = $modSettings['attachmentUploadDir'];
  1467. }
  1468. else
  1469. {
  1470. $attach_dirs = array($modSettings['attachmentUploadDir']);
  1471. }
  1472. foreach ($attach_dirs as $attach_dir)
  1473. {
  1474. $dir = @opendir($attach_dir) or fatal_lang_error('cant_access_upload_path', 'critical');
  1475. while ($file = readdir($dir))
  1476. {
  1477. if ($file == '.' || $file == '..')
  1478. continue;
  1479. if (strpos($file, 'post_tmp_') !== false)
  1480. {
  1481. // Temp file is more than 5 hours old!
  1482. if (filemtime($attach_dir . '/' . $file) < time() - 18000)
  1483. @unlink($attach_dir . '/' . $file);
  1484. }
  1485. }
  1486. closedir($dir);
  1487. }
  1488. }
  1489. /**
  1490. * Check for move topic notices that have past their best by date
  1491. */
  1492. function scheduled_remove_topic_redirect()
  1493. {
  1494. global $smcFunc, $sourcedir;
  1495. // init
  1496. $topics = array();
  1497. // We will need this for lanaguage files
  1498. loadEssentialThemeData();
  1499. // Find all of the old MOVE topic notices that were set to expire
  1500. $request = $smcFunc['db_query']('', '
  1501. SELECT id_topic
  1502. FROM {db_prefix}topics
  1503. WHERE redirect_expires <= {int:redirect_expires}
  1504. AND redirect_expires <> 0',
  1505. array(
  1506. 'redirect_ex…

Large files files are truncated, but you can click here to view the full file