Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

481 lines
14KB

  1. <?php
  2. if (!file_exists("boot.php") AND (sizeof($_SERVER["argv"]) != 0)) {
  3. $directory = dirname($_SERVER["argv"][0]);
  4. if (substr($directory, 0, 1) != "/")
  5. $directory = $_SERVER["PWD"]."/".$directory;
  6. $directory = realpath($directory."/..");
  7. chdir($directory);
  8. }
  9. require_once("boot.php");
  10. require_once("include/photos.php");
  11. function cron_run(&$argv, &$argc){
  12. global $a, $db;
  13. if(is_null($a)) {
  14. $a = new App;
  15. }
  16. if(is_null($db)) {
  17. @include(".htconfig.php");
  18. require_once("include/dba.php");
  19. $db = new dba($db_host, $db_user, $db_pass, $db_data);
  20. unset($db_host, $db_user, $db_pass, $db_data);
  21. };
  22. require_once('include/session.php');
  23. require_once('include/datetime.php');
  24. require_once('include/items.php');
  25. require_once('include/Contact.php');
  26. require_once('include/email.php');
  27. require_once('include/socgraph.php');
  28. require_once('mod/nodeinfo.php');
  29. require_once('include/post_update.php');
  30. load_config('config');
  31. load_config('system');
  32. // Don't check this stuff if the function is called by the poller
  33. if (App::callstack() != "poller_run") {
  34. if ($a->maxload_reached())
  35. return;
  36. if (App::is_already_running('cron', 'include/cron.php', 540))
  37. return;
  38. }
  39. $last = get_config('system','last_cron');
  40. $poll_interval = intval(get_config('system','cron_interval'));
  41. if(! $poll_interval)
  42. $poll_interval = 10;
  43. if($last) {
  44. $next = $last + ($poll_interval * 60);
  45. if($next > time()) {
  46. logger('cron intervall not reached');
  47. return;
  48. }
  49. }
  50. $a->set_baseurl(get_config('system','url'));
  51. load_hooks();
  52. logger('cron: start');
  53. // run queue delivery process in the background
  54. proc_run(PRIORITY_NEGLIGIBLE, "include/queue.php");
  55. // run the process to discover global contacts in the background
  56. proc_run(PRIORITY_LOW, "include/discover_poco.php");
  57. // run the process to update locally stored global contacts in the background
  58. proc_run(PRIORITY_LOW, "include/discover_poco.php", "checkcontact");
  59. // Expire and remove user entries
  60. cron_expire_and_remove_users();
  61. // If the worker is active, split the jobs in several sub processes
  62. if (get_config("system", "worker")) {
  63. // Check OStatus conversations
  64. proc_run(PRIORITY_MEDIUM, "include/cronjobs.php", "ostatus_mentions");
  65. // Check every conversation
  66. proc_run(PRIORITY_MEDIUM, "include/cronjobs.php", "ostatus_conversations");
  67. // Call possible post update functions
  68. proc_run(PRIORITY_LOW, "include/cronjobs.php", "post_update");
  69. // update nodeinfo data
  70. proc_run(PRIORITY_LOW, "include/cronjobs.php", "nodeinfo");
  71. } else {
  72. // Check OStatus conversations
  73. // Check only conversations with mentions (for a longer time)
  74. ostatus::check_conversations(true);
  75. // Check every conversation
  76. ostatus::check_conversations(false);
  77. // Call possible post update functions
  78. // see include/post_update.php for more details
  79. post_update();
  80. // update nodeinfo data
  81. nodeinfo_cron();
  82. }
  83. // once daily run birthday_updates and then expire in background
  84. $d1 = get_config('system','last_expire_day');
  85. $d2 = intval(datetime_convert('UTC','UTC','now','d'));
  86. if($d2 != intval($d1)) {
  87. update_contact_birthdays();
  88. proc_run(PRIORITY_LOW, "include/discover_poco.php", "suggestions");
  89. set_config('system','last_expire_day',$d2);
  90. proc_run(PRIORITY_LOW, 'include/expire.php');
  91. proc_run(PRIORITY_LOW, 'include/dbclean.php');
  92. cron_update_photo_albums();
  93. }
  94. // Clear cache entries
  95. cron_clear_cache($a);
  96. // Repair missing Diaspora values in contacts
  97. cron_repair_diaspora($a);
  98. // Repair entries in the database
  99. cron_repair_database();
  100. // Poll contacts
  101. cron_poll_contacts($argc, $argv);
  102. logger('cron: end');
  103. set_config('system','last_cron', time());
  104. return;
  105. }
  106. /**
  107. * @brief Update the cached values for the number of photo albums per user
  108. */
  109. function cron_update_photo_albums() {
  110. $r = q("SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed`");
  111. if (!dbm::is_result($r)) {
  112. return;
  113. }
  114. foreach ($r AS $user) {
  115. photo_albums($user['uid'], true);
  116. }
  117. }
  118. /**
  119. * @brief Expire and remove user entries
  120. */
  121. function cron_expire_and_remove_users() {
  122. // expire any expired accounts
  123. q("UPDATE user SET `account_expired` = 1 where `account_expired` = 0
  124. AND `account_expires_on` != '0000-00-00 00:00:00'
  125. AND `account_expires_on` < UTC_TIMESTAMP() ");
  126. // delete user and contact records for recently removed accounts
  127. $r = q("SELECT * FROM `user` WHERE `account_removed` AND `account_expires_on` < UTC_TIMESTAMP() - INTERVAL 3 DAY");
  128. if ($r) {
  129. foreach($r as $user) {
  130. q("DELETE FROM `contact` WHERE `uid` = %d", intval($user['uid']));
  131. q("DELETE FROM `user` WHERE `uid` = %d", intval($user['uid']));
  132. }
  133. }
  134. }
  135. /**
  136. * @brief Poll contacts for unreceived messages
  137. *
  138. * @param Integer $argc Number of command line arguments
  139. * @param Array $argv Array of command line arguments
  140. */
  141. function cron_poll_contacts($argc, $argv) {
  142. $manual_id = 0;
  143. $generation = 0;
  144. $force = false;
  145. $restart = false;
  146. if (($argc > 1) && ($argv[1] == 'force'))
  147. $force = true;
  148. if (($argc > 1) && ($argv[1] == 'restart')) {
  149. $restart = true;
  150. $generation = intval($argv[2]);
  151. if (!$generation)
  152. killme();
  153. }
  154. if (($argc > 1) && intval($argv[1])) {
  155. $manual_id = intval($argv[1]);
  156. $force = true;
  157. }
  158. $interval = intval(get_config('system','poll_interval'));
  159. if (!$interval)
  160. $interval = ((get_config('system','delivery_interval') === false) ? 3 : intval(get_config('system','delivery_interval')));
  161. // If we are using the worker we don't need a delivery interval
  162. if (get_config("system", "worker"))
  163. $interval = false;
  164. $sql_extra = (($manual_id) ? " AND `id` = $manual_id " : "");
  165. reload_plugins();
  166. $d = datetime_convert();
  167. // Only poll from those with suitable relationships,
  168. // and which have a polling address and ignore Diaspora since
  169. // we are unable to match those posts with a Diaspora GUID and prevent duplicates.
  170. $abandon_days = intval(get_config('system','account_abandon_days'));
  171. if($abandon_days < 1)
  172. $abandon_days = 0;
  173. $abandon_sql = (($abandon_days)
  174. ? sprintf(" AND `user`.`login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY ", intval($abandon_days))
  175. : ''
  176. );
  177. $contacts = q("SELECT `contact`.`id` FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
  178. WHERE `rel` IN (%d, %d) AND `poll` != '' AND `network` IN ('%s', '%s', '%s', '%s', '%s', '%s')
  179. $sql_extra
  180. AND NOT `self` AND NOT `contact`.`blocked` AND NOT `contact`.`readonly` AND NOT `contact`.`archive`
  181. AND NOT `user`.`account_expired` AND NOT `user`.`account_removed` $abandon_sql ORDER BY RAND()",
  182. intval(CONTACT_IS_SHARING),
  183. intval(CONTACT_IS_FRIEND),
  184. dbesc(NETWORK_DFRN),
  185. dbesc(NETWORK_ZOT),
  186. dbesc(NETWORK_OSTATUS),
  187. dbesc(NETWORK_FEED),
  188. dbesc(NETWORK_MAIL),
  189. dbesc(NETWORK_MAIL2)
  190. );
  191. if (!count($contacts)) {
  192. return;
  193. }
  194. foreach ($contacts as $c) {
  195. $res = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
  196. intval($c['id'])
  197. );
  198. if((! $res) || (! count($res)))
  199. continue;
  200. foreach($res as $contact) {
  201. $xml = false;
  202. if($manual_id)
  203. $contact['last-update'] = '0000-00-00 00:00:00';
  204. if(in_array($contact['network'], array(NETWORK_DFRN, NETWORK_ZOT, NETWORK_OSTATUS)))
  205. $contact['priority'] = 2;
  206. if($contact['subhub'] AND in_array($contact['network'], array(NETWORK_DFRN, NETWORK_ZOT, NETWORK_OSTATUS))) {
  207. // We should be getting everything via a hub. But just to be sure, let's check once a day.
  208. // (You can make this more or less frequent if desired by setting 'pushpoll_frequency' appropriately)
  209. // This also lets us update our subscription to the hub, and add or replace hubs in case it
  210. // changed. We will only update hubs once a day, regardless of 'pushpoll_frequency'.
  211. $poll_interval = get_config('system','pushpoll_frequency');
  212. $contact['priority'] = (($poll_interval !== false) ? intval($poll_interval) : 3);
  213. }
  214. if($contact['priority'] AND !$force) {
  215. $update = false;
  216. $t = $contact['last-update'];
  217. /**
  218. * Based on $contact['priority'], should we poll this site now? Or later?
  219. */
  220. switch ($contact['priority']) {
  221. case 5:
  222. if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 month"))
  223. $update = true;
  224. break;
  225. case 4:
  226. if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 week"))
  227. $update = true;
  228. break;
  229. case 3:
  230. if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day"))
  231. $update = true;
  232. break;
  233. case 2:
  234. if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 12 hour"))
  235. $update = true;
  236. break;
  237. case 1:
  238. default:
  239. if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 hour"))
  240. $update = true;
  241. break;
  242. }
  243. if (!$update)
  244. continue;
  245. }
  246. logger("Polling ".$contact["network"]." ".$contact["id"]." ".$contact["nick"]." ".$contact["name"]);
  247. if (($contact['network'] == NETWORK_FEED) AND ($contact['priority'] <= 3)) {
  248. proc_run(PRIORITY_MEDIUM, 'include/onepoll.php', $contact['id']);
  249. } else {
  250. proc_run(PRIORITY_LOW, 'include/onepoll.php', $contact['id']);
  251. }
  252. if($interval)
  253. @time_sleep_until(microtime(true) + (float) $interval);
  254. }
  255. }
  256. }
  257. /**
  258. * @brief Clear cache entries
  259. *
  260. * @param App $a
  261. */
  262. function cron_clear_cache(&$a) {
  263. $last = get_config('system','cache_last_cleared');
  264. if($last) {
  265. $next = $last + (3600); // Once per hour
  266. $clear_cache = ($next <= time());
  267. } else
  268. $clear_cache = true;
  269. if (!$clear_cache)
  270. return;
  271. // clear old cache
  272. Cache::clear();
  273. // clear old item cache files
  274. clear_cache();
  275. // clear cache for photos
  276. clear_cache($a->get_basepath(), $a->get_basepath()."/photo");
  277. // clear smarty cache
  278. clear_cache($a->get_basepath()."/view/smarty3/compiled", $a->get_basepath()."/view/smarty3/compiled");
  279. // clear cache for image proxy
  280. if (!get_config("system", "proxy_disabled")) {
  281. clear_cache($a->get_basepath(), $a->get_basepath()."/proxy");
  282. $cachetime = get_config('system','proxy_cache_time');
  283. if (!$cachetime) $cachetime = PROXY_DEFAULT_TIME;
  284. q('DELETE FROM `photo` WHERE `uid` = 0 AND `resource-id` LIKE "pic:%%" AND `created` < NOW() - INTERVAL %d SECOND', $cachetime);
  285. }
  286. // Delete the cached OEmbed entries that are older than one year
  287. q("DELETE FROM `oembed` WHERE `created` < NOW() - INTERVAL 3 MONTH");
  288. // Delete the cached "parse_url" entries that are older than one year
  289. q("DELETE FROM `parsed_url` WHERE `created` < NOW() - INTERVAL 3 MONTH");
  290. // Maximum table size in megabyte
  291. $max_tablesize = intval(get_config('system','optimize_max_tablesize')) * 1000000;
  292. if ($max_tablesize == 0)
  293. $max_tablesize = 100 * 1000000; // Default are 100 MB
  294. if ($max_tablesize > 0) {
  295. // Minimum fragmentation level in percent
  296. $fragmentation_level = intval(get_config('system','optimize_fragmentation')) / 100;
  297. if ($fragmentation_level == 0)
  298. $fragmentation_level = 0.3; // Default value is 30%
  299. // Optimize some tables that need to be optimized
  300. $r = q("SHOW TABLE STATUS");
  301. foreach($r as $table) {
  302. // Don't optimize tables that are too large
  303. if ($table["Data_length"] > $max_tablesize)
  304. continue;
  305. // Don't optimize empty tables
  306. if ($table["Data_length"] == 0)
  307. continue;
  308. // Calculate fragmentation
  309. $fragmentation = $table["Data_free"] / ($table["Data_length"] + $table["Index_length"]);
  310. logger("Table ".$table["Name"]." - Fragmentation level: ".round($fragmentation * 100, 2), LOGGER_DEBUG);
  311. // Don't optimize tables that needn't to be optimized
  312. if ($fragmentation < $fragmentation_level)
  313. continue;
  314. // So optimize it
  315. logger("Optimize Table ".$table["Name"], LOGGER_DEBUG);
  316. q("OPTIMIZE TABLE `%s`", dbesc($table["Name"]));
  317. }
  318. }
  319. set_config('system','cache_last_cleared', time());
  320. }
  321. /**
  322. * @brief Repair missing values in Diaspora contacts
  323. *
  324. * @param App $a
  325. */
  326. function cron_repair_diaspora(&$a) {
  327. $r = q("SELECT `id`, `url` FROM `contact`
  328. WHERE `network` = '%s' AND (`batch` = '' OR `notify` = '' OR `poll` = '' OR pubkey = '')
  329. ORDER BY RAND() LIMIT 50", dbesc(NETWORK_DIASPORA));
  330. if ($r) {
  331. foreach ($r AS $contact) {
  332. if (poco_reachable($contact["url"])) {
  333. $data = probe_url($contact["url"]);
  334. if ($data["network"] == NETWORK_DIASPORA) {
  335. logger("Repair contact ".$contact["id"]." ".$contact["url"], LOGGER_DEBUG);
  336. q("UPDATE `contact` SET `batch` = '%s', `notify` = '%s', `poll` = '%s', pubkey = '%s' WHERE `id` = %d",
  337. dbesc($data["batch"]), dbesc($data["notify"]), dbesc($data["poll"]), dbesc($data["pubkey"]),
  338. intval($contact["id"]));
  339. }
  340. }
  341. }
  342. }
  343. }
  344. /**
  345. * @brief Do some repairs in database entries
  346. *
  347. */
  348. function cron_repair_database() {
  349. // Set the parent if it wasn't set. (Shouldn't happen - but does sometimes)
  350. // This call is very "cheap" so we can do it at any time without a problem
  351. q("UPDATE `item` INNER JOIN `item` AS `parent` ON `parent`.`uri` = `item`.`parent-uri` AND `parent`.`uid` = `item`.`uid` SET `item`.`parent` = `parent`.`id` WHERE `item`.`parent` = 0");
  352. // There was an issue where the nick vanishes from the contact table
  353. q("UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`=''");
  354. // Update the global contacts for local users
  355. $r = q("SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`");
  356. if ($r)
  357. foreach ($r AS $user)
  358. update_gcontact_for_user($user["uid"]);
  359. /// @todo
  360. /// - remove thread entries without item
  361. /// - remove sign entries without item
  362. /// - remove children when parent got lost
  363. /// - set contact-id in item when not present
  364. }
  365. if (array_search(__file__,get_included_files())===0){
  366. cron_run($_SERVER["argv"],$_SERVER["argc"]);
  367. killme();
  368. }