ScheduledTasks.php 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794
  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 2013 Simple Machines and individual contributors
  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('No direct access...');
  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' || $db_type == 'mysqli') && 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 = {literal:mail_next_send}
  776. AND value = {string:last_send}',
  777. array(
  778. 'next_mail_send' => time() + $delay,
  779. 'last_send' => $modSettings['mail_next_send'],
  780. )
  781. );
  782. if ($smcFunc['db_affected_rows']() == 0)
  783. return false;
  784. $modSettings['mail_next_send'] = time() + $delay;
  785. }
  786. // If we're not overriding how many are we allow to send?
  787. if (!$override_limit && !empty($modSettings['mail_limit']))
  788. {
  789. list ($mt, $mn) = @explode('|', $modSettings['mail_recent']);
  790. // Nothing worth noting...
  791. if (empty($mn) || $mt < time() - 60)
  792. {
  793. $mt = time();
  794. $mn = $number;
  795. }
  796. // Otherwise we have a few more we can spend?
  797. elseif ($mn < $modSettings['mail_limit'])
  798. {
  799. $mn += $number;
  800. }
  801. // No more I'm afraid, return!
  802. else
  803. return false;
  804. // Reflect that we're about to send some, do it now to be safe.
  805. updateSettings(array('mail_recent' => $mt . '|' . $mn));
  806. }
  807. // Now we know how many we're sending, let's send them.
  808. $request = $smcFunc['db_query']('', '
  809. SELECT /*!40001 SQL_NO_CACHE */ id_mail, recipient, body, subject, headers, send_html, time_sent, private
  810. FROM {db_prefix}mail_queue
  811. ORDER BY priority ASC, id_mail ASC
  812. LIMIT ' . $number,
  813. array(
  814. )
  815. );
  816. $ids = array();
  817. $emails = array();
  818. while ($row = $smcFunc['db_fetch_assoc']($request))
  819. {
  820. // We want to delete these from the database ASAP, so just get the data and go.
  821. $ids[] = $row['id_mail'];
  822. $emails[] = array(
  823. 'to' => $row['recipient'],
  824. 'body' => $row['body'],
  825. 'subject' => $row['subject'],
  826. 'headers' => $row['headers'],
  827. 'send_html' => $row['send_html'],
  828. 'time_sent' => $row['time_sent'],
  829. 'private' => $row['private'],
  830. );
  831. }
  832. $smcFunc['db_free_result']($request);
  833. // Delete, delete, delete!!!
  834. if (!empty($ids))
  835. $smcFunc['db_query']('', '
  836. DELETE FROM {db_prefix}mail_queue
  837. WHERE id_mail IN ({array_int:mail_list})',
  838. array(
  839. 'mail_list' => $ids,
  840. )
  841. );
  842. // Don't believe we have any left?
  843. if (count($ids) < $number)
  844. {
  845. // Only update the setting if no-one else has beaten us to it.
  846. $smcFunc['db_query']('', '
  847. UPDATE {db_prefix}settings
  848. SET value = {string:no_send}
  849. WHERE variable = {literal:mail_next_send}
  850. AND value = {string:last_mail_send}',
  851. array(
  852. 'no_send' => '0',
  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'], $email['time_sent'], $email['private']);
  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:next_mail_send}
  901. WHERE variable = {literal:mail_next_send}
  902. AND value = {string:last_send}',
  903. array(
  904. 'next_mail_send' => time() + 60,
  905. 'last_send' => $modSettings['mail_next_send'],
  906. ));
  907. // Add our email back to the queue, manually.
  908. $smcFunc['db_insert']('insert',
  909. '{db_prefix}mail_queue',
  910. array('recipient' => 'string', 'body' => 'string', 'subject' => 'string', 'headers' => 'string', 'send_html' => 'string', 'time_sent' => 'string', 'private' => 'int'),
  911. $failed_emails,
  912. array('id_mail')
  913. );
  914. return false;
  915. }
  916. // We where unable to send the email, clear our failed attempts.
  917. elseif (!empty($modSettings['mail_failed_attempts']))
  918. $smcFunc['db_query']('', '
  919. UPDATE {db_prefix}settings
  920. SET value = {string:zero}
  921. WHERE variable = {string:mail_failed_attempts}',
  922. array(
  923. 'zero' => '0',
  924. 'mail_failed_attempts' => 'mail_failed_attempts',
  925. ));
  926. // Had something to send...
  927. return true;
  928. }
  929. /**
  930. * Calculate the next time the passed tasks should be triggered.
  931. *
  932. * @param type $tasks
  933. * @param type $forceUpdate
  934. */
  935. function CalculateNextTrigger($tasks = array(), $forceUpdate = false)
  936. {
  937. global $modSettings, $smcFunc;
  938. $task_query = '';
  939. if (!is_array($tasks))
  940. $tasks = array($tasks);
  941. // Actually have something passed?
  942. if (!empty($tasks))
  943. {
  944. if (!isset($tasks[0]) || is_numeric($tasks[0]))
  945. $task_query = ' AND id_task IN ({array_int:tasks})';
  946. else
  947. $task_query = ' AND task IN ({array_string:tasks})';
  948. }
  949. $nextTaskTime = empty($tasks) ? time() + 86400 : $modSettings['next_task_time'];
  950. // Get the critical info for the tasks.
  951. $request = $smcFunc['db_query']('', '
  952. SELECT id_task, next_time, time_offset, time_regularity, time_unit
  953. FROM {db_prefix}scheduled_tasks
  954. WHERE disabled = {int:no_disabled}
  955. ' . $task_query,
  956. array(
  957. 'no_disabled' => 0,
  958. 'tasks' => $tasks,
  959. )
  960. );
  961. $tasks = array();
  962. while ($row = $smcFunc['db_fetch_assoc']($request))
  963. {
  964. $next_time = next_time($row['time_regularity'], $row['time_unit'], $row['time_offset']);
  965. // Only bother moving the task if it's out of place or we're forcing it!
  966. if ($forceUpdate || $next_time < $row['next_time'] || $row['next_time'] < time())
  967. $tasks[$row['id_task']] = $next_time;
  968. else
  969. $next_time = $row['next_time'];
  970. // If this is sooner than the current next task, make this the next task.
  971. if ($next_time < $nextTaskTime)
  972. $nextTaskTime = $next_time;
  973. }
  974. $smcFunc['db_free_result']($request);
  975. // Now make the changes!
  976. foreach ($tasks as $id => $time)
  977. $smcFunc['db_query']('', '
  978. UPDATE {db_prefix}scheduled_tasks
  979. SET next_time = {int:next_time}
  980. WHERE id_task = {int:id_task}',
  981. array(
  982. 'next_time' => $time,
  983. 'id_task' => $id,
  984. )
  985. );
  986. // If the next task is now different update.
  987. if ($modSettings['next_task_time'] != $nextTaskTime)
  988. updateSettings(array('next_task_time' => $nextTaskTime));
  989. }
  990. /**
  991. * Simply returns a time stamp of the next instance of these time parameters.
  992. *
  993. * @param int $regularity
  994. * @param type $unit
  995. * @param type $offset
  996. * @return int
  997. */
  998. function next_time($regularity, $unit, $offset)
  999. {
  1000. // Just in case!
  1001. if ($regularity == 0)
  1002. $regularity = 2;
  1003. $curHour = date('H', time());
  1004. $curMin = date('i', time());
  1005. $next_time = 9999999999;
  1006. // If the unit is minutes only check regularity in minutes.
  1007. if ($unit == 'm')
  1008. {
  1009. $off = date('i', $offset);
  1010. // If it's now just pretend it ain't,
  1011. if ($off == $curMin)
  1012. $next_time = time() + $regularity;
  1013. else
  1014. {
  1015. // Make sure that the offset is always in the past.
  1016. $off = $off > $curMin ? $off - 60 : $off;
  1017. while ($off <= $curMin)
  1018. $off += $regularity;
  1019. // Now we know when the time should be!
  1020. $next_time = time() + 60 * ($off - $curMin);
  1021. }
  1022. }
  1023. // Otherwise, work out what the offset would be with todays date.
  1024. else
  1025. {
  1026. $next_time = mktime(date('H', $offset), date('i', $offset), 0, date('m'), date('d'), date('Y'));
  1027. // Make the time offset in the past!
  1028. if ($next_time > time())
  1029. {
  1030. $next_time -= 86400;
  1031. }
  1032. // Default we'll jump in hours.
  1033. $applyOffset = 3600;
  1034. // 24 hours = 1 day.
  1035. if ($unit == 'd')
  1036. $applyOffset = 86400;
  1037. // Otherwise a week.
  1038. if ($unit == 'w')
  1039. $applyOffset = 604800;
  1040. $applyOffset *= $regularity;
  1041. // Just add on the offset.
  1042. while ($next_time <= time())
  1043. {
  1044. $next_time += $applyOffset;
  1045. }
  1046. }
  1047. return $next_time;
  1048. }
  1049. /**
  1050. * This loads the bare minimum data to allow us to load language files!
  1051. */
  1052. function loadEssentialThemeData()
  1053. {
  1054. global $settings, $modSettings, $smcFunc, $mbname, $context, $sourcedir, $txt;
  1055. // Get all the default theme variables.
  1056. $result = $smcFunc['db_query']('', '
  1057. SELECT id_theme, variable, value
  1058. FROM {db_prefix}themes
  1059. WHERE id_member = {int:no_member}
  1060. AND id_theme IN (1, {int:theme_guests})',
  1061. array(
  1062. 'no_member' => 0,
  1063. 'theme_guests' => $modSettings['theme_guests'],
  1064. )
  1065. );
  1066. while ($row = $smcFunc['db_fetch_assoc']($result))
  1067. {
  1068. $settings[$row['variable']] = $row['value'];
  1069. // Is this the default theme?
  1070. if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1')
  1071. $settings['default_' . $row['variable']] = $row['value'];
  1072. }
  1073. $smcFunc['db_free_result']($result);
  1074. // Check we have some directories setup.
  1075. if (empty($settings['template_dirs']))
  1076. {
  1077. $settings['template_dirs'] = array($settings['theme_dir']);
  1078. // Based on theme (if there is one).
  1079. if (!empty($settings['base_theme_dir']))
  1080. $settings['template_dirs'][] = $settings['base_theme_dir'];
  1081. // Lastly the default theme.
  1082. if ($settings['theme_dir'] != $settings['default_theme_dir'])
  1083. $settings['template_dirs'][] = $settings['default_theme_dir'];
  1084. }
  1085. // Assume we want this.
  1086. $context['forum_name'] = $mbname;
  1087. // Check loadLanguage actually exists!
  1088. if (!function_exists('loadLanguage'))
  1089. {
  1090. require_once($sourcedir . '/Load.php');
  1091. require_once($sourcedir . '/Subs.php');
  1092. }
  1093. loadLanguage('index+Modifications');
  1094. // Just in case it wasn't already set elsewhere.
  1095. $context['character_set'] = empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set'];
  1096. $context['utf8'] = $context['character_set'] === 'UTF-8';
  1097. $context['right_to_left'] = !empty($txt['lang_rtl']);
  1098. }
  1099. /**
  1100. * This retieves data (e.g. last version of SMF) from sm.org
  1101. */
  1102. function scheduled_fetchSMfiles()
  1103. {
  1104. global $sourcedir, $txt, $language, $forum_version, $modSettings, $smcFunc, $context;
  1105. // What files do we want to get
  1106. $request = $smcFunc['db_query']('', '
  1107. SELECT id_file, filename, path, parameters
  1108. FROM {db_prefix}admin_info_files',
  1109. array(
  1110. )
  1111. );
  1112. $js_files = array();
  1113. while ($row = $smcFunc['db_fetch_assoc']($request))
  1114. {
  1115. $js_files[$row['id_file']] = array(
  1116. 'filename' => $row['filename'],
  1117. 'path' => $row['path'],
  1118. 'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
  1119. );
  1120. }
  1121. $smcFunc['db_free_result']($request);
  1122. // We're gonna need fetch_web_data() to pull this off.
  1123. require_once($sourcedir . '/Subs-Package.php');
  1124. // Just in case we run into a problem.
  1125. loadEssentialThemeData();
  1126. loadLanguage('Errors', $language, false);
  1127. foreach ($js_files as $ID_FILE => $file)
  1128. {
  1129. // Create the url
  1130. $server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.simplemachines.org' : '';
  1131. $url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
  1132. // Get the file
  1133. $file_data = fetch_web_data($url);
  1134. // If we got an error - give up - the site might be down. And if we should happen to be coming from elsewhere, let's also make a note of it.
  1135. if ($file_data === false)
  1136. {
  1137. $context['scheduled_errors']['fetchSMfiles'][] = sprintf($txt['st_cannot_retrieve_file'], $url);
  1138. log_error(sprintf($txt['st_cannot_retrieve_file'], $url));
  1139. return false;
  1140. }
  1141. // Save the file to the database.
  1142. $smcFunc['db_query']('substring', '
  1143. UPDATE {db_prefix}admin_info_files
  1144. SET data = SUBSTRING({string:file_data}, 1, 65534)
  1145. WHERE id_file = {int:id_file}',
  1146. array(
  1147. 'id_file' => $ID_FILE,
  1148. 'file_data' => $file_data,
  1149. )
  1150. );
  1151. }
  1152. return true;
  1153. }
  1154. /**
  1155. * Happy birthday!!
  1156. */
  1157. function scheduled_birthdayemails()
  1158. {
  1159. global $modSettings, $sourcedir, $mbname, $txt, $smcFunc, $birthdayEmails;
  1160. // Need this in order to load the language files.
  1161. loadEssentialThemeData();
  1162. // Going to need this to send the emails.
  1163. require_once($sourcedir . '/Subs-Post.php');
  1164. $greeting = isset($modSettings['birthday_email']) ? $modSettings['birthday_email'] : 'happy_birthday';
  1165. // Get the month and day of today.
  1166. $month = date('n'); // Month without leading zeros.
  1167. $day = date('j'); // Day without leading zeros.
  1168. // So who are the lucky ones? Don't include those who are banned and those who don't want them.
  1169. $result = $smcFunc['db_query']('', '
  1170. SELECT id_member, real_name, lngfile, email_address
  1171. FROM {db_prefix}members
  1172. WHERE is_activated < 10
  1173. AND MONTH(birthdate) = {int:month}
  1174. AND DAYOFMONTH(birthdate) = {int:day}
  1175. AND notify_announcements = {int:notify_announcements}
  1176. AND YEAR(birthdate) > {int:year}',
  1177. array(
  1178. 'notify_announcements' => 1,
  1179. 'year' => 1,
  1180. 'month' => $month,
  1181. 'day' => $day,
  1182. )
  1183. );
  1184. // Group them by languages.
  1185. $birthdays = array();
  1186. while ($row = $smcFunc['db_fetch_assoc']($result))
  1187. {
  1188. if (!isset($birthdays[$row['lngfile']]))
  1189. $birthdays[$row['lngfile']] = array();
  1190. $birthdays[$row['lngfile']][$row['id_member']] = array(
  1191. 'name' => $row['real_name'],
  1192. 'email' => $row['email_address']
  1193. );
  1194. }
  1195. $smcFunc['db_free_result']($result);
  1196. // Send out the greetings!
  1197. foreach ($birthdays as $lang => $recps)
  1198. {
  1199. // We need to do some shuffling to make this work properly.
  1200. loadLanguage('EmailTemplates', $lang);
  1201. $txt['emails']['happy_birthday']['subject'] = $txtBirthdayEmails[$greeting . '_subject'];
  1202. $txt['emails']['happy_birthday']['body'] = $txtBirthdayEmails[$greeting . '_body'];
  1203. foreach ($recps as $recp)
  1204. {
  1205. $replacements = array(
  1206. 'REALNAME' => $recp['name'],
  1207. );
  1208. $emaildata = loadEmailTemplate('happy_birthday', $replacements, $lang, false);
  1209. sendmail($recp['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 4);
  1210. // Try to stop a timeout, this would be bad...
  1211. @set_time_limit(300);
  1212. if (function_exists('apache_reset_timeout'))
  1213. @apache_reset_timeout();
  1214. }
  1215. }
  1216. // Flush the mail queue, just in case.
  1217. AddMailQueue(true);
  1218. return true;
  1219. }
  1220. /**
  1221. * Weekly maintenance
  1222. */
  1223. function scheduled_weekly_maintenance()
  1224. {
  1225. global $modSettings, $smcFunc;
  1226. // Delete some settings that needn't be set if they are otherwise empty.
  1227. $emptySettings = array(
  1228. 'warning_mute', 'warning_moderate', 'warning_watch', 'warning_show', 'disableCustomPerPage', 'spider_mode', 'spider_group',
  1229. 'paid_currency_code', 'paid_currency_symbol', 'paid_email_to', 'paid_email', 'paid_enabled', 'paypal_email',
  1230. 'search_enable_captcha', 'search_floodcontrol_time', 'show_spider_online',
  1231. );
  1232. $smcFunc['db_query']('', '
  1233. DELETE FROM {db_prefix}settings
  1234. WHERE variable IN ({array_string:setting_list})
  1235. AND (value = {string:zero_value} OR value = {string:blank_value})',
  1236. array(
  1237. 'zero_value' => '0',
  1238. 'blank_value' => '',
  1239. 'setting_list' => $emptySettings,
  1240. )
  1241. );
  1242. // Some settings we never want to keep - they are just there for temporary purposes.
  1243. $deleteAnywaySettings = array(
  1244. 'attachment_full_notified',
  1245. );
  1246. $smcFunc['db_query']('', '
  1247. DELETE FROM {db_prefix}settings
  1248. WHERE variable IN ({array_string:setting_list})',
  1249. array(
  1250. 'setting_list' => $deleteAnywaySettings,
  1251. )
  1252. );
  1253. // Ok should we prune the logs?
  1254. if (!empty($modSettings['pruningOptions']))
  1255. {
  1256. if (!empty($modSettings['pruningOptions']) && strpos($modSettings['pruningOptions'], ',') !== false)
  1257. list ($modSettings['pruneErrorLog'], $modSettings['pruneModLog'], $modSettings['pruneBanLog'], $modSettings['pruneReportLog'], $modSettings['pruneScheduledTaskLog'], $modSettings['pruneSpiderHitLog']) = explode(',', $modSettings['pruningOptions']);
  1258. if (!empty($modSettings['pruneErrorLog']))
  1259. {
  1260. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1261. $t = time() - $modSettings['pruneErrorLog'] * 86400;
  1262. $smcFunc['db_query']('', '
  1263. DELETE FROM {db_prefix}log_errors
  1264. WHERE log_time < {int:log_time}',
  1265. array(
  1266. 'log_time' => $t,
  1267. )
  1268. );
  1269. }
  1270. if (!empty($modSettings['pruneModLog']))
  1271. {
  1272. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1273. $t = time() - $modSettings['pruneModLog'] * 86400;
  1274. $smcFunc['db_query']('', '
  1275. DELETE FROM {db_prefix}log_actions
  1276. WHERE log_time < {int:log_time}
  1277. AND id_log = {int:moderation_log}',
  1278. array(
  1279. 'log_time' => $t,
  1280. 'moderation_log' => 1,
  1281. )
  1282. );
  1283. }
  1284. if (!empty($modSettings['pruneBanLog']))
  1285. {
  1286. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1287. $t = time() - $modSettings['pruneBanLog'] * 86400;
  1288. $smcFunc['db_query']('', '
  1289. DELETE FROM {db_prefix}log_banned
  1290. WHERE log_time < {int:log_time}',
  1291. array(
  1292. 'log_time' => $t,
  1293. )
  1294. );
  1295. }
  1296. if (!empty($modSettings['pruneReportLog']))
  1297. {
  1298. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1299. $t = time() - $modSettings['pruneReportLog'] * 86400;
  1300. // This one is more complex then the other logs. First we need to figure out which reports are too old.
  1301. $reports = array();
  1302. $result = $smcFunc['db_query']('', '
  1303. SELECT id_report
  1304. FROM {db_prefix}log_reported
  1305. WHERE time_started < {int:time_started}',
  1306. array(
  1307. 'time_started' => $t,
  1308. )
  1309. );
  1310. while ($row = $smcFunc['db_fetch_row']($result))
  1311. $reports[] = $row[0];
  1312. $smcFunc['db_free_result']($result);
  1313. if (!empty($reports))
  1314. {
  1315. // Now delete the reports...
  1316. $smcFunc['db_query']('', '
  1317. DELETE FROM {db_prefix}log_reported
  1318. WHERE id_report IN ({array_int:report_list})',
  1319. array(
  1320. 'report_list' => $reports,
  1321. )
  1322. );
  1323. // And delete the comments for those reports...
  1324. $smcFunc['db_query']('', '
  1325. DELETE FROM {db_prefix}log_reported_comments
  1326. WHERE id_report IN ({array_int:report_list})',
  1327. array(
  1328. 'report_list' => $reports,
  1329. )
  1330. );
  1331. }
  1332. }
  1333. if (!empty($modSettings['pruneScheduledTaskLog']))
  1334. {
  1335. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1336. $t = time() - $modSettings['pruneScheduledTaskLog'] * 86400;
  1337. $smcFunc['db_query']('', '
  1338. DELETE FROM {db_prefix}log_scheduled_tasks
  1339. WHERE time_run < {int:time_run}',
  1340. array(
  1341. 'time_run' => $t,
  1342. )
  1343. );
  1344. }
  1345. if (!empty($modSettings['pruneSpiderHitLog']))
  1346. {
  1347. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1348. $t = time() - $modSettings['pruneSpiderHitLog'] * 86400;
  1349. $smcFunc['db_query']('', '
  1350. DELETE FROM {db_prefix}log_spider_hits
  1351. WHERE log_time < {int:log_time}',
  1352. array(
  1353. 'log_time' => $t,
  1354. )
  1355. );
  1356. }
  1357. }
  1358. // Get rid of any paid subscriptions that were never actioned.
  1359. $smcFunc['db_query']('', '
  1360. DELETE FROM {db_prefix}log_subscribed
  1361. WHERE end_time = {int:no_end_time}
  1362. AND status = {int:not_active}
  1363. AND start_time < {int:start_time}
  1364. AND payments_pending < {int:payments_pending}',
  1365. array(
  1366. 'no_end_time' => 0,
  1367. 'not_active' => 0,
  1368. 'start_time' => time() - 60,
  1369. 'payments_pending' => 1,
  1370. )
  1371. );
  1372. // Some OS's don't seem to clean out their sessions.
  1373. $smcFunc['db_query']('', '
  1374. DELETE FROM {db_prefix}sessions
  1375. WHERE last_update < {int:last_update}',
  1376. array(
  1377. 'last_update' => time() - 86400,
  1378. )
  1379. );
  1380. return true;
  1381. }
  1382. /**
  1383. * Perform the standard checks on expiring/near expiring subscriptions.
  1384. */
  1385. function scheduled_paid_subscriptions()
  1386. {
  1387. global $txt, $sourcedir, $scripturl, $smcFunc, $modSettings, $language;
  1388. // Start off by checking for removed subscriptions.
  1389. $request = $smcFunc['db_query']('', '
  1390. SELECT id_subscribe, id_member
  1391. FROM {db_prefix}log_subscribed
  1392. WHERE status = {int:is_active}
  1393. AND end_time < {int:time_now}',
  1394. array(
  1395. 'is_active' => 1,
  1396. 'time_now' => time(),
  1397. )
  1398. );
  1399. while ($row = $smcFunc['db_fetch_assoc']($request))
  1400. {
  1401. require_once($sourcedir . '/ManagePaid.php');
  1402. removeSubscription($row['id_subscribe'], $row['id_member']);
  1403. }
  1404. $smcFunc['db_free_result']($request);
  1405. // Get all those about to expire that have not had a reminder sent.
  1406. $request = $smcFunc['db_query']('', '
  1407. SELECT ls.id_sublog, m.id_member, m.member_name, m.email_address, m.lngfile, s.name, ls.end_time
  1408. FROM {db_prefix}log_subscribed AS ls
  1409. INNER JOIN {db_prefix}subscriptions AS s ON (s.id_subscribe = ls.id_subscribe)
  1410. INNER JOIN {db_prefix}members AS m ON (m.id_member = ls.id_member)
  1411. WHERE ls.status = {int:is_active}
  1412. AND ls.reminder_sent = {int:reminder_sent}
  1413. AND s.reminder > {int:reminder_wanted}
  1414. AND ls.end_time < ({int:time_now} + s.reminder * 86400)',
  1415. array(
  1416. 'is_active' => 1,
  1417. 'reminder_sent' => 0,
  1418. 'reminder_wanted' => 0,
  1419. 'time_now' => time(),
  1420. )
  1421. );
  1422. $subs_reminded = array();
  1423. while ($row = $smcFunc['db_fetch_assoc']($request))
  1424. {
  1425. // If this is the first one load the important bits.
  1426. if (empty($subs_reminded))
  1427. {
  1428. require_once($sourcedir . '/Subs-Post.php');
  1429. // Need the below for loadLanguage to work!
  1430. loadEssentialThemeData();
  1431. }
  1432. $subs_reminded[] = $row['id_sublog'];
  1433. $replacements = array(
  1434. 'PROFILE_LINK' => $scripturl . '?action=profile;area=subscriptions;u=' . $row['id_member'],
  1435. 'REALNAME' => $row['member_name'],
  1436. 'SUBSCRIPTION' => $row['name'],
  1437. 'END_DATE' => strip_tags(timeformat($row['end_time'])),
  1438. );
  1439. $emaildata = loadEmailTemplate('paid_subscription_reminder', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
  1440. // Send the actual email.
  1441. sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 2);
  1442. }
  1443. $smcFunc['db_free_result']($request);
  1444. // Mark the reminder as sent.
  1445. if (!empty($subs_reminded))
  1446. $smcFunc['db_query']('', '
  1447. UPDATE {db_prefix}log_subscribed
  1448. SET reminder_sent = {int:reminder_sent}
  1449. WHERE id_sublog IN ({array_int:subscription_list})',
  1450. array(
  1451. 'subscription_list' => $subs_reminded,
  1452. 'reminder_sent' => 1,
  1453. )
  1454. );
  1455. return true;
  1456. }
  1457. /**
  1458. * Check for un-posted attachments is something we can do once in a while :P
  1459. * This function uses opendir cycling through all the attachments
  1460. */
  1461. function scheduled_remove_temp_attachments()
  1462. {
  1463. global $modSettings, $context, $txt;
  1464. // We need to know where this thing is going.
  1465. if (!empty($modSettings['currentAttachmentUploadDir']))
  1466. {
  1467. if (!is_array($modSettings['attachmentUploadDir']))
  1468. $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
  1469. // Just use the current path for temp files.
  1470. $attach_dirs = $modSettings['attachmentUploadDir'];
  1471. }
  1472. else
  1473. {
  1474. $attach_dirs = array($modSettings['attachmentUploadDir']);
  1475. }
  1476. foreach ($attach_dirs as $attach_dir)
  1477. {
  1478. $dir = @opendir($attach_dir);
  1479. if (!$dir)
  1480. {
  1481. loadEssentialThemeData();
  1482. loadLanguage('Post');
  1483. $context['scheduled_errors']['remove_temp_attachments'][] = $txt['cant_access_upload_path'] . ' (' . $attach_dir . ')';
  1484. log_error($txt['cant_access_upload_path'] . ' (' . $attach_dir . ')', 'critical');
  1485. return false;
  1486. }
  1487. while ($file = readdir($dir))
  1488. {
  1489. if ($file == '.' || $file == '..')
  1490. continue;
  1491. if (strpos($file, 'post_tmp_') !== false)
  1492. {
  1493. // Temp file is more than 5 hours old!
  1494. if (filemtime($attach_dir . '/' . $file) < time() - 18000)
  1495. @unlink($attach_dir . '/' . $file);
  1496. }
  1497. }
  1498. closedir($dir);
  1499. }
  1500. return true;
  1501. }
  1502. /**
  1503. * Check for move topic notices that have past their best by date
  1504. */
  1505. function scheduled_remove_topic_redirect()
  1506. {
  1507. global $smcFunc, $sourcedir;
  1508. // init
  1509. $topics = array();
  1510. // We will need this for lanaguage files
  1511. loadEssentialThemeData();
  1512. // Find all of the old MOVE topic notices that were set to expire
  1513. $request = $smcFunc['db_query']('', '
  1514. SELECT id_topic
  1515. FROM {db_prefix}topics
  1516. WHERE redirect_expires <= {int:redirect_expires}
  1517. AND redirect_expires <> 0',
  1518. array(
  1519. 'redirect_expires' => time(),
  1520. )
  1521. );
  1522. while ($row = $smcFunc['db_fetch_row']($request))
  1523. $topics[] = $row[0];
  1524. $smcFunc['db_free_result']($request);
  1525. // Zap, your gone
  1526. if (count($topics) > 0)
  1527. {
  1528. require_once($sourcedir . '/RemoveTopic.php');
  1529. removeTopics($topics, false, true);
  1530. }
  1531. return true;
  1532. }
  1533. /**
  1534. * Check for old drafts and remove them
  1535. */
  1536. function scheduled_remove_old_drafts()
  1537. {
  1538. global $smcFunc, $sourcedir, $modSettings;
  1539. if (empty($modSettings['drafts_keep_days']))
  1540. return true;
  1541. // init
  1542. $drafts = array();
  1543. // We need this for lanaguage items
  1544. loadEssentialThemeData();
  1545. // Find all of the old drafts
  1546. $request = $smcFunc['db_query']('', '
  1547. SELECT id_draft
  1548. FROM {db_prefix}user_drafts
  1549. WHERE poster_time <= {int:poster_time_old}',
  1550. array(
  1551. 'poster_time_old' => time() - (86400 * $modSettings['drafts_keep_days']),
  1552. )
  1553. );
  1554. while ($row = $smcFunc['db_fetch_row']($request))
  1555. $drafts[] = (int) $row[0];
  1556. $smcFunc['db_free_result']($request);
  1557. // If we have old one, remove them
  1558. if (count($drafts) > 0)
  1559. {
  1560. require_once($sourcedir . '/Drafts.php');
  1561. DeleteDraft($drafts, false);
  1562. }
  1563. return true;
  1564. }
  1565. ?>