ScheduledTasks.php 50 KB

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