function expire_run($argv, $argc) { cli_startup(); $r = q("select id from item where (item_restrict & %d) and not (item_restrict & %d) and changed < UTC_TIMESTAMP() - INTERVAL 10 DAY", intval(ITEM_DELETED), intval(ITEM_PENDING_REMOVE)); if ($r) { foreach ($r as $rr) { drop_item($rr['id'], false, DROPITEM_PHASE2); } } // physically remove anything that has been deleted for more than two months $r = q("delete from item where ( item_restrict & %d ) and changed < UTC_TIMESTAMP() - INTERVAL 36 DAY", intval(ITEM_PENDING_REMOVE)); // make this optional as it could have a performance impact on large sites if (intval(get_config('system', 'optimize_items'))) { q("optimize table item"); } logger('expire: start', LOGGER_DEBUG); $r = q("SELECT channel_id, channel_address, channel_expire_days from channel where channel_expire_days != 0"); if ($r && count($r)) { foreach ($r as $rr) { logger('Expire: ' . $rr['channel_address'] . ' interval: ' . $rr['channel_expire_days'], LOGGER_DEBUG); item_expire($rr['channel_id'], $rr['channel_expire_days']); } } $x = get_sys_channel(); if ($x) { // this should probably just fetch the channel_expire_days from the sys channel, // but there's no convenient way to set it. $expire_days = get_config('externals', 'expire_days'); if ($expire_days === false) { $expire_days = 30; } if ($expire_days) { item_expire($x['channel_id'], $expire_days); } } return; }
function drop_items($items) { $uid = 0; if (!local_channel() && !remote_channel()) { return; } if (count($items)) { foreach ($items as $item) { $owner = drop_item($item, false); if ($owner && !$uid) { $uid = $owner; } } } // multiple threads may have been deleted, send an expire notification if ($uid) { Zotlabs\Daemon\Master::Summon(array('Notifier', 'expire', $uid)); } }
function poller_run($argv, $argc) { cli_startup(); $a = get_app(); $maxsysload = intval(get_config('system', 'maxloadavg')); if ($maxsysload < 1) { $maxsysload = 50; } if (function_exists('sys_getloadavg')) { $load = sys_getloadavg(); if (intval($load[0]) > $maxsysload) { logger('system: load ' . $load . ' too high. Poller deferred to next scheduled run.'); return; } } $interval = intval(get_config('system', 'poll_interval')); if (!$interval) { $interval = get_config('system', 'delivery_interval') === false ? 3 : intval(get_config('system', 'delivery_interval')); } logger('poller: start'); // run queue delivery process in the background proc_run('php', "include/queue.php"); // expire any expired mail q("delete from mail where expires != '%s' and expires < UTC_TIMESTAMP() ", dbesc(NULL_DATE)); // expire any expired items $r = q("select id from item where expires != '%s' and expires < UTC_TIMESTAMP() \n\t\tand not ( item_restrict & %d ) ", dbesc(NULL_DATE), intval(ITEM_DELETED)); if ($r) { require_once 'include/items.php'; foreach ($r as $rr) { drop_item($rr['id'], false); } } // Ensure that every channel pings a directory server once a month. This way we can discover // channels and sites that quietly vanished and prevent the directory from accumulating stale // or dead entries. $r = q("select channel_id from channel where channel_dirdate < UTC_TIMESTAMP() - INTERVAL 30 DAY"); if ($r) { foreach ($r as $rr) { proc_run('php', 'include/directory.php', $rr['channel_id'], 'force'); if ($interval) { @time_sleep_until(microtime(true) + (double) $interval); } } } // publish any applicable items that were set to be published in the future // (time travel posts) $r = q("select id from item where ( item_restrict & %d ) and created <= UTC_TIMESTAMP() ", intval(ITEM_DELAYED_PUBLISH)); if ($r) { foreach ($r as $rr) { $x = q("update item set item_restrict = ( item_restrict ^ %d ) where id = %d limit 1", intval(ITEM_DELAYED_PUBLISH), intval($rr['id'])); if ($x) { proc_run('php', 'include/notifier.php', 'wall-new', $rr['id']); } } } $abandon_days = intval(get_config('system', 'account_abandon_days')); if ($abandon_days < 1) { $abandon_days = 0; } // once daily run birthday_updates and then expire in background // FIXME: add birthday updates, both locally and for xprof for use // by directory servers $d1 = intval(get_config('system', 'last_expire_day')); $d2 = intval(datetime_convert('UTC', 'UTC', 'now', 'd')); // Allow somebody to staggger daily activities if they have more than one site on their server, // or if it happens at an inconvenient (busy) hour. $h1 = intval(get_config('system', 'cron_hour')); $h2 = intval(datetime_convert('UTC', 'UTC', 'now', 'G')); $dirmode = get_config('system', 'directory_mode'); /** * Cron Daily * * Actions in the following block are executed once per day, not on every poller run * */ if ($d2 != $d1 && $h1 == $h2) { require_once 'include/dir_fns.php'; check_upstream_directory(); call_hooks('cron_daily', datetime_convert()); $d3 = intval(datetime_convert('UTC', 'UTC', 'now', 'N')); if ($d3 == 7) { /** * Cron Weekly * * Actions in the following block are executed once per day only on Sunday (once per week). * */ call_hooks('cron_weekly', datetime_convert()); require_once 'include/hubloc.php'; prune_hub_reinstalls(); require_once 'include/Contact.php'; mark_orphan_hubsxchans(); /** * End Cron Weekly */ } update_birthdays(); // expire any read notifications over a month old q("delete from notify where seen = 1 and date < UTC_TIMESTAMP() - INTERVAL 30 DAY"); // expire any expired accounts downgrade_accounts(); // If this is a directory server, request a sync with an upstream // directory at least once a day, up to once every poll interval. // Pull remote changes and push local changes. // potential issue: how do we keep from creating an endless update loop? if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { require_once 'include/dir_fns.php'; sync_directories($dirmode); } set_config('system', 'last_expire_day', $d2); proc_run('php', 'include/expire.php'); proc_run('php', 'include/cli_suggest.php'); /** * End Cron Daily */ } // update any photos which didn't get imported properly // This should be rare $r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = '' \n\t\tand xchan_photo_date < UTC_TIMESTAMP() - INTERVAL 1 DAY"); if ($r) { require_once 'include/photo/photo_driver.php'; foreach ($r as $rr) { $photos = import_profile_photo($rr['xchan_photo_l'], $rr['xchan_hash']); $x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'\n\t\t\t\twhere xchan_hash = '%s' limit 1", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($rr['xchan_hash'])); } } // pull in some public posts if (!get_config('system', 'disable_discover_tab')) { proc_run('php', 'include/externals.php'); } $manual_id = 0; $generation = 0; $force = false; $restart = false; if ($argc > 1 && $argv[1] == 'force') { $force = true; } if ($argc > 1 && $argv[1] == 'restart') { $restart = true; $generation = intval($argv[2]); if (!$generation) { killme(); } } if ($argc > 1 && intval($argv[1])) { $manual_id = intval($argv[1]); $force = true; } $sql_extra = $manual_id ? " AND abook_id = {$manual_id} " : ""; reload_plugins(); $d = datetime_convert(); //TODO check to see if there are any cronhooks before wasting a process if (!$restart) { proc_run('php', 'include/cronhooks.php'); } // Only poll from those with suitable relationships $abandon_sql = $abandon_days ? sprintf(" AND account_lastlog > UTC_TIMESTAMP() - INTERVAL %d DAY ", intval($abandon_days)) : ''; $contacts = q("SELECT abook_id, abook_flags, abook_updated, abook_connected, abook_closeness, abook_channel\n\t\tFROM abook LEFT JOIN account on abook_account = account_id where 1\n\t\t{$sql_extra} \n\t\tAND (( abook_flags & %d ) OR ( abook_flags = %d )) \n\t\tAND (( account_flags = %d ) OR ( account_flags = %d )) {$abandon_sql} ORDER BY RAND()", intval(ABOOK_FLAG_HIDDEN | ABOOK_FLAG_PENDING | ABOOK_FLAG_UNCONNECTED | ABOOK_FLAG_FEED), intval(0), intval(ACCOUNT_OK), intval(ACCOUNT_UNVERIFIED)); if ($contacts) { foreach ($contacts as $contact) { $update = false; $t = $contact['abook_updated']; $c = $contact['abook_connected']; if ($contact['abook_flags'] & ABOOK_FLAG_FEED) { $min = service_class_fetch($contact['abook_channel'], 'minimum_feedcheck_minutes'); if (!$min) { $min = intval(get_config('system', 'minimum_feedcheck_minutes')); } if (!$min) { $min = 60; } $x = datetime_convert('UTC', 'UTC', "now - {$min} minutes"); if ($c < $x) { proc_run('php', 'include/onepoll.php', $contact['abook_id']); if ($interval) { @time_sleep_until(microtime(true) + (double) $interval); } } continue; } if ($c == $t) { if (datetime_convert('UTC', 'UTC', 'now') > datetime_convert('UTC', 'UTC', $t . " + 1 day")) { $update = true; } } else { // if we've never connected with them, start the mark for death countdown from now if ($c == NULL_DATE) { $r = q("update abook set abook_connected = '%s' where abook_id = %d limit 1", dbesc(datetime_convert()), intval($contact['abook_id'])); $c = datetime_convert(); $update = true; } // He's dead, Jim if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 30 day")) > 0) { $r = q("update abook set abook_flags = (abook_flags | %d) where abook_id = %d limit 1", intval(ABOOK_FLAG_ARCHIVED), intval($contact['abook_id'])); $update = false; continue; } if ($contact['abook_flags'] & ABOOK_FLAG_ARCHIVED) { $update = false; continue; } // might be dead, so maybe don't poll quite so often // recently deceased, so keep up the regular schedule for 3 days if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 3 day")) > 0 && strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 1 day")) > 0) { $update = true; } // After that back off and put them on a morphine drip if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 2 day")) > 0) { $update = true; } } if (!$update && !$force) { continue; } proc_run('php', 'include/onepoll.php', $contact['abook_id']); if ($interval) { @time_sleep_until(microtime(true) + (double) $interval); } } } if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { $r = q("select distinct ud_addr, updates.* from updates where not ( ud_flags & %d ) and ud_addr != '' and ( ud_last = '%s' OR ud_last > UTC_TIMESTAMP() - INTERVAL 7 DAY ) group by ud_addr ", intval(UPDATE_FLAGS_UPDATED), dbesc(NULL_DATE)); if ($r) { foreach ($r as $rr) { // If they didn't respond when we attempted before, back off to once a day // After 7 days we won't bother anymore if ($rr['ud_last'] != NULL_DATE) { if ($rr['ud_last'] > datetime_convert('UTC', 'UTC', 'now - 1 day')) { continue; } } proc_run('php', 'include/onedirsync.php', $rr['ud_id']); if ($interval) { @time_sleep_until(microtime(true) + (double) $interval); } } } } return; }
/** * @brief Deletes an imported item. * * @param array $sender * * \e string \b hash a xchan_hash * @param array $item * @param int $uid * @param boolean $relay * @return boolean|int post_id */ function delete_imported_item($sender, $item, $uid, $relay) { logger('delete_imported_item invoked', LOGGER_DEBUG); $ownership_valid = false; $item_found = false; $post_id = 0; $r = q("select id, author_xchan, owner_xchan, source_xchan, item_deleted from item where ( author_xchan = '%s' or owner_xchan = '%s' or source_xchan = '%s' )\n\t\tand mid = '%s' and uid = %d limit 1", dbesc($sender['hash']), dbesc($sender['hash']), dbesc($sender['hash']), dbesc($item['mid']), intval($uid)); if ($r) { if ($r[0]['author_xchan'] === $sender['hash'] || $r[0]['owner_xchan'] === $sender['hash'] || $r[0]['source_xchan'] === $sender['hash']) { $ownership_valid = true; } $post_id = $r[0]['id']; $item_found = true; } else { // perhaps the item is still in transit and the delete notification got here before the actual item did. Store it with the deleted flag set. // item_store() won't try to deliver any notifications or start delivery chains if this flag is set. // This means we won't end up with potentially even more delivery threads trying to push this delete notification. // But this will ensure that if the (undeleted) original post comes in at a later date, we'll reject it because it will have an older timestamp. logger('delete received for non-existent item - storing item data.'); /** @BUG $arr is undefined here, so this is dead code */ if ($arr['author_xchan'] === $sender['hash'] || $arr['owner_xchan'] === $sender['hash'] || $arr['source_xchan'] === $sender['hash']) { $ownership_valid = true; $item_result = item_store($arr); $post_id = $item_result['item_id']; } } if ($ownership_valid === false) { logger('delete_imported_item: failed: ownership issue'); return false; } require_once 'include/items.php'; if ($item_found) { if (intval($r[0]['item_deleted'])) { logger('delete_imported_item: item was already deleted'); if (!$relay) { return false; } // This is a bit hackish, but may have to suffice until the notification/delivery loop is optimised // a bit further. We're going to strip the ITEM_ORIGIN on this item if it's a comment, because // it was already deleted, and we're already relaying, and this ensures that no other process or // code path downstream can relay it again (causing a loop). Since it's already gone it's not coming // back, and we aren't going to (or shouldn't at any rate) delete it again in the future - so losing // this information from the metadata should have no other discernible impact. if ($r[0]['id'] != $r[0]['parent'] && intval($r[0]['item_origin'])) { q("update item set item_origin = 0 where id = %d and uid = %d", intval($r[0]['id']), intval($r[0]['uid'])); } } require_once 'include/items.php'; // Use phased deletion to set the deleted flag, call both tag_deliver and the notifier to notify downstream channels // and then clean up after ourselves with a cron job after several days to do the delete_item_lowlevel() (DROPITEM_PHASE2). drop_item($post_id, false, DROPITEM_PHASE1); tag_deliver($uid, $post_id); } return $post_id; }
/** * */ function api_statuses_destroy(&$a, $type) { if (api_user() === false) { return false; } $user_info = api_get_user($a); // params $id = intval($a->argv[3]); if ($id == 0) { $id = intval($_REQUEST["id"]); } // Hotot workaround if ($id == 0) { $id = intval($a->argv[4]); } logger('API: api_statuses_destroy: ' . $id); $ret = api_statuses_show($a, $type); drop_item($id, false); return $ret; }
function item_content(&$a) { if (!local_channel() && !remote_channel()) { return; } require_once 'include/security.php'; if (argc() == 3 && argv(1) === 'drop' && intval(argv(2))) { require_once 'include/items.php'; $i = q("select id, uid, author_xchan, owner_xchan, source_xchan, item_restrict from item where id = %d limit 1", intval(argv(2))); if ($i) { $can_delete = false; $local_delete = false; if (local_channel() && local_channel() == $i[0]['uid']) { $local_delete = true; } $sys = get_sys_channel(); if (is_site_admin() && $sys['channel_id'] == $i[0]['uid']) { $can_delete = true; } $ob_hash = get_observer_hash(); if ($ob_hash && ($ob_hash === $i[0]['author_xchan'] || $ob_hash === $i[0]['owner_xchan'] || $ob_hash === $i[0]['source_xchan'])) { $can_delete = true; } if (!($can_delete || $local_delete)) { notice(t('Permission denied.') . EOL); return; } // if this is a different page type or it's just a local delete // but not by the item author or owner, do a simple deletion if ($i[0]['item_restrict'] || $local_delete && !$can_delete) { drop_item($i[0]['id']); } else { // complex deletion that needs to propagate and be performed in phases drop_item($i[0]['id'], true, DROPITEM_PHASE1); tag_deliver($i[0]['uid'], $i[0]['id']); } } } }
/** * @brief Delete a file/directory from a channel. * * If the provided resource hash is from a directory it will delete everything * recursively under this directory. * * @param int $channel_id * The id of the channel * @param string $resource * The hash to delete * @return void */ function attach_delete($channel_id, $resource, $is_photo = 0) { $c = q("SELECT channel_address FROM channel WHERE channel_id = %d LIMIT 1", intval($channel_id)); $channel_address = $c ? $c[0]['channel_address'] : 'notfound'; $photo_sql = $is_photo ? " and is_photo = 1 " : ''; $r = q("SELECT hash, flags, is_dir, is_photo, folder FROM attach WHERE hash = '%s' AND uid = %d {$photo_sql} limit 1", dbesc($resource), intval($channel_id)); if (!$r) { return; } $cloudpath = get_parent_cloudpath($channel_id, $channel_address, $resource); $object = get_file_activity_object($channel_id, $resource, $cloudpath); // If resource is a directory delete everything in the directory recursive if (intval($r[0]['is_dir'])) { $x = q("SELECT hash, os_storage, is_dir, flags FROM attach WHERE folder = '%s' AND uid = %d", dbesc($resource), intval($channel_id)); if ($x) { foreach ($x as $xx) { attach_delete($channel_id, $xx['hash']); } } } // delete a file from filesystem if (intval($r[0]['os_storage'])) { $y = q("SELECT data FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", dbesc($resource), intval($channel_id)); if ($y) { $f = 'store/' . $channel_address . '/' . $y[0]['data']; if (is_dir($y[0]['data'])) { @rmdir($y[0]['data']); } elseif (file_exists($f)) { unlink($f); } } } // delete from database $z = q("DELETE FROM attach WHERE hash = '%s' AND uid = %d", dbesc($resource), intval($channel_id)); if ($r[0]['is_photo']) { $x = q("select id, item_hidden from item where resource_id = '%s' and resource_type = 'photo' and uid = %d", dbesc($resource), intval($channel_id)); if ($x) { drop_item($x[0]['id'], false, $x[0]['item_hidden'] ? DROPITEM_NORMAL : DROPITEM_PHASE1, true); q("DELETE FROM photo WHERE uid = %d AND resource_id = '%s'", intval($channel_id), dbesc($resource)); } } // update the parent folder's lastmodified timestamp $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", dbesc(datetime_convert()), dbesc($r[0]['folder']), intval($channel_id)); file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', $notify = 0); }
function like_content(&$a) { $o = ''; $observer = $a->get_observer(); $interactive = $_REQUEST['interactive']; if ($interactive) { $o .= '<h1>' . t('Like/Dislike') . '</h1>'; $o .= EOL . EOL; if (!$observer) { $_SESSION['return_url'] = $a->query_string; $o .= t('This action is restricted to members.') . EOL; $o .= t('Please <a href="rmagic">login with your RedMatrix ID</a> or <a href="register">register as a new RedMatrix member</a> to continue.') . EOL; return $o; } } $verb = notags(trim($_GET['verb'])); if (!$verb) { $verb = 'like'; } switch ($verb) { case 'like': case 'unlike': $activity = ACTIVITY_LIKE; break; case 'dislike': case 'undislike': $activity = ACTIVITY_DISLIKE; break; case 'agree': case 'unagree': $activity = ACTIVITY_AGREE; break; case 'disagree': case 'undisagree': $activity = ACTIVITY_DISAGREE; break; case 'abstain': case 'unabstain': $activity = ACTIVITY_ABSTAIN; break; case 'attendyes': case 'unattendyes': $activity = ACTIVITY_ATTEND; break; case 'attendno': case 'unattendno': $activity = ACTIVITY_ATTENDNO; break; case 'attendmaybe': case 'unattendmaybe': $activity = ACTIVITY_ATTENDMAYBE; break; default: return; break; } $extended_like = false; $object = $target = null; $post_type = ''; $objtype = ''; if (argc() == 3) { if (!$observer) { killme(); } $extended_like = true; $obj_type = argv(1); $obj_id = argv(2); $public = true; if ($obj_type == 'profile') { $r = q("select * from profile where profile_guid = '%s' limit 1", dbesc(argv(2))); if (!$r) { killme(); } $owner_uid = $r[0]['uid']; if ($r[0]['is_default']) { $public = true; } if (!$public) { $d = q("select abook_xchan from abook where abook_profile = '%s' and abook_channel = %d", dbesc($r[0]['profile_guid']), intval($owner_uid)); if (!$d) { // forgery - illegal if ($interactive) { notice(t('Invalid request.') . EOL); return $o; } killme(); } // $d now contains a list of those who can see this profile - only send the status notification // to them. $allow_cid = $allow_gid = $deny_cid = $deny_gid = ''; foreach ($d as $dd) { $allow_gid .= '<' . $dd['abook_xchan'] . '>'; } } $post_type = t('channel'); $objtype = ACTIVITY_OBJ_PROFILE; } elseif ($obj_type == 'thing') { $r = q("select * from obj left join term on obj_obj = term_hash where term_hash != '' \n\t\t\t\tand obj_type = %d and term_hash = '%s' limit 1", intval(TERM_OBJ_THING), dbesc(argv(2))); if (!$r) { if ($interactive) { notice(t('Invalid request.') . EOL); return $o; } killme(); } $owner_uid = $r[0]['obj_channel']; $allow_cid = $r[0]['allow_cid']; $allow_gid = $r[0]['allow_gid']; $deny_cid = $r[0]['deny_cid']; $deny_gid = $r[0]['deny_gid']; if ($allow_cid || $allow_gid || $deny_cid || $deny_gid) { $public = false; } $post_type = t('thing'); $objtype = ACTIVITY_OBJ_PROFILE; $tgttype = ACTIVITY_OBJ_THING; $links = array(); $links[] = array('rel' => 'alternate', 'type' => 'text/html', 'href' => z_root() . '/thing/' . $r[0]['term_hash']); if ($r[0]['imgurl']) { $links[] = array('rel' => 'photo', 'href' => $r[0]['imgurl']); } $target = json_encode(array('type' => $tgttype, 'title' => $r[0]['term'], 'id' => z_root() . '/thing/' . $r[0]['term_hash'], 'link' => $links)); $plink = '[zrl=' . z_root() . '/thing/' . $r[0]['term_hash'] . ']' . $r[0]['term'] . '[/zrl]'; } if (!($owner_uid && $r)) { if ($interactive) { notice(t('Invalid request.') . EOL); return $o; } killme(); } // The resultant activity is going to be a wall-to-wall post, so make sure this is allowed $perms = get_all_perms($owner_uid, $observer['xchan_hash']); if (!($perms['post_like'] && $perms['view_profile'])) { if ($interactive) { notice(t('Permission denied.') . EOL); return $o; } killme(); } $ch = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($owner_uid)); if (!$ch) { if ($interactive) { notice(t('Channel unavailable.') . EOL); return $o; } killme(); } if (!$plink) { $plink = '[zrl=' . z_root() . '/profile/' . $ch[0]['channel_address'] . ']' . $post_type . '[/zrl]'; } $links = array(); $links[] = array('rel' => 'alternate', 'type' => 'text/html', 'href' => z_root() . '/profile/' . $ch[0]['channel_address']); $links[] = array('rel' => 'photo', 'type' => $ch[0]['xchan_photo_mimetype'], 'href' => $ch[0]['xchan_photo_l']); $object = json_encode(array('type' => ACTIVITY_OBJ_PROFILE, 'title' => $ch[0]['channel_name'], 'id' => $ch[0]['xchan_url'] . '/' . $ch[0]['xchan_hash'], 'link' => $links)); // second like of the same thing is "undo" for the first like $z = q("select * from likes where channel_id = %d and liker = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' limit 1", intval($ch[0]['channel_id']), dbesc($observer['xchan_hash']), dbesc($activity), dbesc($tgttype ? $tgttype : $objtype), dbesc($obj_id)); if ($z) { q("delete from likes where id = %d limit 1", intval($z[0]['id'])); drop_item($z[0]['iid'], false); if ($interactive) { notice(t('Previous action reversed.') . EOL); return $o; } killme(); } } else { // this is used to like an item or comment $item_id = argc() == 2 ? notags(trim(argv(1))) : 0; logger('like: verb ' . $verb . ' item ' . $item_id, LOGGER_DEBUG); // get the item. Allow linked photos (which are normally hidden) to be liked $r = q("SELECT * FROM item WHERE id = %d and (item_restrict = 0 or item_restrict = %d) LIMIT 1", intval($item_id), intval(ITEM_HIDDEN)); if (!$item_id || !$r) { logger('like: no item ' . $item_id); killme(); } $item = $r[0]; $owner_uid = $item['uid']; $owner_aid = $item['aid']; $sys = get_sys_channel(); // if this is a "discover" item, (item['uid'] is the sys channel), // fallback to the item comment policy, which should've been // respected when generating the conversation thread. // Even if the activity is rejected by the item owner, it should still get attached // to the local discover conversation on this site. if ($owner_uid != $sys['channel_id'] && !perm_is_allowed($owner_uid, $observer['xchan_hash'], 'post_comments')) { notice(t('Permission denied') . EOL); killme(); } $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($item['owner_xchan'])); if ($r) { $thread_owner = $r[0]; } else { killme(); } $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($item['author_xchan'])); if ($r) { $item_author = $r[0]; } else { killme(); } $verbs = " '" . dbesc($activity) . "' "; $multi_undo = 0; // event participation and consensus items are essentially radio toggles. If you make a subsequent choice, // we need to eradicate your first choice. if ($activity === ACTIVITY_ATTEND || $activity === ACTIVITY_ATTENDNO || $activity === ACTIVITY_ATTENDMAYBE) { $verbs = " '" . dbesc(ACTIVITY_ATTEND) . "','" . dbesc(ACTIVITY_ATTENDNO) . "','" . dbesc(ACTIVITY_ATTENDMAYBE) . "' "; $multi_undo = 1; } if ($activity === ACTIVITY_AGREE || $activity === ACTIVITY_DISAGREE || $activity === ACTIVITY_ABSTAIN) { $verbs = " '" . dbesc(ACTIVITY_AGREE) . "','" . dbesc(ACTIVITY_DISAGREE) . "','" . dbesc(ACTIVITY_ABSTAIN) . "' "; $multi_undo = 1; } $r = q("SELECT id, parent, uid, verb FROM item WHERE verb in ( {$verbs} ) AND item_restrict = 0 \n\t\t\tAND author_xchan = '%s' AND ( parent = %d OR thr_parent = '%s') and uid = %d ", dbesc($observer['xchan_hash']), intval($item_id), dbesc($item['mid']), intval($owner_uid)); if ($r) { // already liked it. Drop that item. require_once 'include/items.php'; foreach ($r as $rr) { drop_item($rr['id'], false, DROPITEM_PHASE1); // set the changed timestamp on the parent so we'll see the update without a page reload $z = q("update item set changed = '%s' where id = %d and uid = %d", dbesc(datetime_convert()), intval($rr['parent']), intval($rr['uid'])); // Prior activity was a duplicate of the one we're submitting, just undo it; // don't fall through and create another if (activity_match($rr['verb'], $activity)) { $multi_undo = false; } } if ($interactive) { return; } if (!$multi_undo) { killme(); } } } $mid = item_message_id(); if ($extended_like) { $item_flags = ITEM_THREAD_TOP | ITEM_ORIGIN | ITEM_WALL; } else { $post_type = $item['resource_type'] === 'photo' ? t('photo') : t('status'); if ($item['obj_type'] === ACTIVITY_OBJ_EVENT) { $post_type = t('event'); } $links = array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item['plink'])); $objtype = $item['resource_type'] === 'photo' ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE; $body = $item['body']; $object = json_encode(array('type' => $objtype, 'id' => $item['mid'], 'parent' => $item['thr_parent'] ? $item['thr_parent'] : $item['parent_mid'], 'link' => $links, 'title' => $item['title'], 'content' => $item['body'], 'created' => $item['created'], 'edited' => $item['edited'], 'author' => array('name' => $item_author['xchan_name'], 'address' => $item_author['xchan_addr'], 'guid' => $item_author['xchan_guid'], 'guid_sig' => $item_author['xchan_guid_sig'], 'link' => array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item_author['xchan_url']), array('rel' => 'photo', 'type' => $item_author['xchan_photo_mimetype'], 'href' => $item_author['xchan_photo_m']))))); if (!($item['item_flags'] & ITEM_THREAD_TOP)) { $post_type = 'comment'; } $item_flags = ITEM_ORIGIN | ITEM_NOTSHOWN; if ($item['item_flags'] & ITEM_WALL) { $item_flags |= ITEM_WALL; } // if this was a linked photo and was hidden, unhide it. if ($item['item_restrict'] & ITEM_HIDDEN) { $r = q("update item set item_restrict = (item_restrict ^ %d) where id = %d", intval(ITEM_HIDDEN), intval($item['id'])); } } if ($verb === 'like') { $bodyverb = t('%1$s likes %2$s\'s %3$s'); } if ($verb === 'dislike') { $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); } if ($verb === 'agree') { $bodyverb = t('%1$s agrees with %2$s\'s %3$s'); } if ($verb === 'disagree') { $bodyverb = t('%1$s doesn\'t agree with %2$s\'s %3$s'); } if ($verb === 'abstain') { $bodyverb = t('%1$s abstains from a decision on %2$s\'s %3$s'); } if ($verb === 'attendyes') { $bodyverb = t('%1$s is attending %2$s\'s %3$s'); } if ($verb === 'attendno') { $bodyverb = t('%1$s is not attending %2$s\'s %3$s'); } if ($verb === 'attendmaybe') { $bodyverb = t('%1$s may attend %2$s\'s %3$s'); } if (!isset($bodyverb)) { killme(); } $arr = array(); if ($extended_like) { $ulink = '[zrl=' . $ch[0]['xchan_url'] . ']' . $ch[0]['xchan_name'] . '[/zrl]'; $alink = '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]'; $private = $public ? 0 : 1; } else { $arr['parent'] = $item['id']; $arr['thr_parent'] = $item['mid']; $ulink = '[zrl=' . $item_author['xchan_url'] . ']' . $item_author['xchan_name'] . '[/zrl]'; $alink = '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]'; $plink = '[zrl=' . $a->get_baseurl() . '/display/' . $item['mid'] . ']' . $post_type . '[/zrl]'; $allow_cid = $item['allow_cid']; $allow_gid = $item['allow_gid']; $deny_cid = $item['deny_cid']; $deny_gid = $item['deny_gid']; $private = $item['private']; } $arr['mid'] = $mid; $arr['aid'] = $extended_like ? $ch[0]['channel_account_id'] : $owner_aid; $arr['uid'] = $owner_uid; $arr['item_flags'] = $item_flags; $arr['parent_mid'] = $extended_like ? $mid : $item['mid']; $arr['owner_xchan'] = $extended_like ? $ch[0]['xchan_hash'] : $thread_owner['xchan_hash']; $arr['author_xchan'] = $observer['xchan_hash']; $arr['body'] = sprintf($bodyverb, $alink, $ulink, $plink); if ($obj_type === 'thing' && $r[0]['imgurl']) { $arr['body'] .= "\n\n[zmg=80x80]" . $r[0]['imgurl'] . '[/zmg]'; } $arr['verb'] = $activity; $arr['obj_type'] = $objtype; $arr['object'] = $object; if ($target) { $arr['tgt_type'] = $tgttype; $arr['target'] = $target; } $arr['allow_cid'] = $allow_cid; $arr['allow_gid'] = $allow_gid; $arr['deny_cid'] = $deny_cid; $arr['deny_gid'] = $deny_gid; $arr['item_private'] = $private; $post = item_store($arr); $post_id = $post['item_id']; $arr['id'] = $post_id; call_hooks('post_local_end', $arr); if ($extended_like) { $r = q("insert into likes (channel_id,liker,likee,iid,verb,target_type,target_id,target) values (%d,'%s','%s',%d,'%s','%s','%s','%s')", intval($ch[0]['channel_id']), dbesc($observer['xchan_hash']), dbesc($ch[0]['channel_hash']), intval($post_id), dbesc($activity), dbesc($tgttype ? $tgttype : $objtype), dbesc($obj_id), dbesc(json_encode($target ? $target : $object))); } proc_run('php', "include/notifier.php", "like", "{$post_id}"); if ($interactive) { notice(t('Action completed.') . EOL); $o .= t('Thank you.'); return $o; } killme(); }
public static function run($argc, $argv) { cli_startup(); // perform final cleanup on previously delete items $r = q("select id from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('10 DAY')); if ($r) { foreach ($r as $rr) { drop_item($rr['id'], false, DROPITEM_PHASE2); } } // physically remove anything that has been deleted for more than two months /** @FIXME - this is a wretchedly inefficient query */ $r = q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('36 DAY')); /** @FIXME make this optional as it could have a performance impact on large sites */ if (intval(get_config('system', 'optimize_items'))) { q("optimize table item"); } logger('expire: start', LOGGER_DEBUG); $site_expire = get_config('system', 'default_expire_days'); logger('site_expire: ' . $site_expire); $r = q("SELECT channel_id, channel_system, channel_address, channel_expire_days from channel where true"); if ($r) { foreach ($r as $rr) { // expire the sys channel separately if (intval($rr['channel_system'])) { continue; } // service class default (if non-zero) over-rides the site default $service_class_expire = service_class_fetch($rr['channel_id'], 'expire_days'); if (intval($service_class_expire)) { $channel_expire = $service_class_expire; } else { $channel_expire = $site_expire; } if (intval($channel_expire) && intval($channel_expire) < intval($rr['channel_expire_days']) || intval($rr['channel_expire_days'] == 0)) { $expire_days = $channel_expire; } else { $expire_days = $rr['channel_expire_days']; } // if the site or service class expiration is non-zero and less than person expiration, use that logger('Expire: ' . $rr['channel_address'] . ' interval: ' . $expire_days, LOGGER_DEBUG); item_expire($rr['channel_id'], $expire_days); } } $x = get_sys_channel(); if ($x) { // this should probably just fetch the channel_expire_days from the sys channel, // but there's no convenient way to set it. $expire_days = get_config('system', 'sys_expire_days'); if ($expire_days === false) { $expire_days = 30; } if (intval($site_expire) && intval($site_expire) < intval($expire_days)) { $expire_days = $site_expire; } logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG); if ($expire_days) { item_expire($x['channel_id'], $expire_days); } logger('Expire: sys: done', LOGGER_DEBUG); } }
function wiki_delete_wiki($resource_id) { $w = wiki_get_wiki($resource_id); $item = $w['wiki']; if (!$item || !$w['path']) { return array('item' => null, 'success' => false); } else { $drop = drop_item($item['id'], false, DROPITEM_NORMAL, true); $pathdel = rrmdir($w['path']); if ($pathdel) { info('Wiki files deleted successfully'); } return array('item' => $item, 'success' => $drop === 1 && $pathdel ? true : false); } }
function photos_post(&$a) { logger('mod-photos: photos_post: begin', LOGGER_DEBUG); logger('mod_photos: REQUEST ' . print_r($_REQUEST, true), LOGGER_DATA); logger('mod_photos: FILES ' . print_r($_FILES, true), LOGGER_DATA); $ph = photo_factory(''); $phototypes = $ph->supportedTypes(); $can_post = false; $page_owner_uid = $a->data['channel']['channel_id']; if (perm_is_allowed($page_owner_uid, get_observer_hash(), 'post_photos')) { $can_post = true; } if (!$can_post) { notice(t('Permission denied.') . EOL); if (is_ajax()) { killme(); } return; } $s = abook_self($page_owner_uid); if (!$s) { notice(t('Page owner information could not be retrieved.') . EOL); logger('mod_photos: post: unable to locate contact record for page owner. uid=' . $page_owner_uid); if (is_ajax()) { killme(); } return; } $owner_record = $s[0]; if (argc() > 3 && argv(2) === 'album') { $album = hex2bin(argv(3)); if ($album === t('Profile Photos')) { // not allowed goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); } if (!photos_album_exists($page_owner_uid, $album)) { notice(t('Album not found.') . EOL); goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); } /* * RENAME photo album */ $newalbum = notags(trim($_REQUEST['albumname'])); if ($newalbum != $album) { $x = photos_album_rename($page_owner_uid, $album, $newalbum); if ($x) { $newurl = str_replace(bin2hex($album), bin2hex($newalbum), $_SESSION['photo_return']); goaway($a->get_baseurl() . '/' . $newurl); } } /* * DELETE photo album and all its photos */ if ($_REQUEST['dropalbum'] == t('Delete Album')) { $res = array(); // get the list of photos we are about to delete if (remote_user() && !local_user()) { $str = photos_album_get_db_idstr($page_owner_uid, $album, remote_user()); } elseif (local_user()) { $str = photos_album_get_db_idstr(local_user(), $album); } else { $str = null; } if (!$str) { goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); } $r = q("select id, item_restrict from item where resource_id in ( {$str} ) and resource_type = 'photo' and uid = %d", intval($page_owner_uid)); if ($r) { foreach ($r as $i) { drop_item($i['id'], false); if (!$item_restrict) { proc_run('php', 'include/notifier.php', 'drop', $i['id']); } } } // remove the associated photos in case they weren't attached to an item q("delete from photo where resource_id in ( {$str} ) and uid = %d", intval($page_owner_uid)); } goaway($a->get_baseurl() . '/photos/' . $a->data['channel']['channel_address']); } if (argc() > 2 && x($_REQUEST, 'delete') && $_REQUEST['delete'] === t('Delete Photo')) { // same as above but remove single photo $ob_hash = get_observer_hash(); if (!$ob_hash) { goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); } $r = q("SELECT `id`, `resource_id` FROM `photo` WHERE ( xchan = '%s' or `uid` = %d ) AND `resource_id` = '%s' LIMIT 1", dbesc($ob_hash), intval(local_user()), dbesc($a->argv[2])); if ($r) { q("DELETE FROM `photo` WHERE `uid` = %d AND `resource_id` = '%s'", intval($page_owner_uid), dbesc($r[0]['resource_id'])); $i = q("SELECT * FROM `item` WHERE `resource_id` = '%s' AND resource_type = 'photo' and `uid` = %d LIMIT 1", dbesc($r[0]['resource_id']), intval($page_owner_uid)); if (count($i)) { q("UPDATE `item` SET item_restrict = (item_restrict | %d), `edited` = '%s', `changed` = '%s' WHERE `parent_mid` = '%s' AND `uid` = %d", intval(ITEM_DELETED), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($i[0]['mid']), intval($page_owner_uid)); $url = $a->get_baseurl(); $drop_id = intval($i[0]['id']); if ($i[0]['visible']) { proc_run('php', "include/notifier.php", "drop", "{$drop_id}"); } } } goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); } if ($a->argc > 2 && (x($_POST, 'desc') !== false || x($_POST, 'newtag') !== false) || x($_POST, 'albname') !== false) { $desc = x($_POST, 'desc') ? notags(trim($_POST['desc'])) : ''; $rawtags = x($_POST, 'newtag') ? notags(trim($_POST['newtag'])) : ''; $item_id = x($_POST, 'item_id') ? intval($_POST['item_id']) : 0; $albname = x($_POST, 'albname') ? notags(trim($_POST['albname'])) : ''; $str_group_allow = perms2str($_POST['group_allow']); $str_contact_allow = perms2str($_POST['contact_allow']); $str_group_deny = perms2str($_POST['group_deny']); $str_contact_deny = perms2str($_POST['contact_deny']); $resource_id = $a->argv[2]; if (!strlen($albname)) { $albname = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y'); } if (x($_POST, 'rotate') !== false && (intval($_POST['rotate']) == 1 || intval($_POST['rotate']) == 2)) { logger('rotate'); $r = q("select * from photo where `resource_id` = '%s' and uid = %d and scale = 0 limit 1", dbesc($resource_id), intval($page_owner_uid)); if (count($r)) { $ph = photo_factory($r[0]['data'], $r[0]['type']); if ($ph->is_valid()) { $rotate_deg = intval($_POST['rotate']) == 1 ? 270 : 90; $ph->rotate($rotate_deg); $width = $ph->getWidth(); $height = $ph->getHeight(); $x = q("update photo set data = '%s', height = %d, width = %d where `resource_id` = '%s' and uid = %d and scale = 0 limit 1", dbesc($ph->imageString()), intval($height), intval($width), dbesc($resource_id), intval($page_owner_uid)); if ($width > 640 || $height > 640) { $ph->scaleImage(640); $width = $ph->getWidth(); $height = $ph->getHeight(); $x = q("update photo set data = '%s', height = %d, width = %d where `resource_id` = '%s' and uid = %d and scale = 1 limit 1", dbesc($ph->imageString()), intval($height), intval($width), dbesc($resource_id), intval($page_owner_uid)); } if ($width > 320 || $height > 320) { $ph->scaleImage(320); $width = $ph->getWidth(); $height = $ph->getHeight(); $x = q("update photo set data = '%s', height = %d, width = %d where `resource_id` = '%s' and uid = %d and scale = 2 limit 1", dbesc($ph->imageString()), intval($height), intval($width), dbesc($resource_id), intval($page_owner_uid)); } } } } $p = q("SELECT * FROM `photo` WHERE `resource_id` = '%s' AND `uid` = %d and ( photo_flags = %d or photo_flags = %d ) ORDER BY `scale` DESC", dbesc($resource_id), intval($page_owner_uid), intval(PHOTO_NORMAL), intval(PHOTO_PROFILE)); if (count($p)) { $ext = $phototypes[$p[0]['type']]; $r = q("UPDATE `photo` SET `description` = '%s', `album` = '%s', `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s' WHERE `resource_id` = '%s' AND `uid` = %d", dbesc($desc), dbesc($albname), dbesc($str_contact_allow), dbesc($str_group_allow), dbesc($str_contact_deny), dbesc($str_group_deny), dbesc($resource_id), intval($page_owner_uid)); } $item_private = $str_contact_allow || $str_group_allow || $str_contact_deny || $str_group_deny ? true : false; /* Don't make the item visible if the only change was the album name */ $visibility = 0; if ($p[0]['description'] !== $desc || strlen($rawtags)) { $visibility = 1; } if (!$item_id) { $item_id = photos_create_item($a->data['channel'], get_observer_hash(), $p[0], $visibility); } if ($item_id) { $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($item_id), intval($page_owner_uid)); } if ($r) { $old_tag = $r[0]['tag']; $old_inform = $r[0]['inform']; } // make sure the linked item has the same permissions as the photo regardless of any other changes $x = q("update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d\n\t\t\twhere id = %d limit 1", dbesc($str_contact_allow), dbesc($str_group_allow), dbesc($str_contact_deny), dbesc($str_group_deny), intval($item_private), intval($item_id)); if (strlen($rawtags)) { $str_tags = ''; $inform = ''; // if the new tag doesn't have a namespace specifier (@foo or #foo) give it a mention $x = substr($rawtags, 0, 1); if ($x !== '@' && $x !== '#') { $rawtags = '@' . $rawtags; } $taginfo = array(); $tags = get_tags($rawtags); if (count($tags)) { foreach ($tags as $tag) { // If we already tagged 'Robert Johnson', don't try and tag 'Robert'. // Robert Johnson should be first in the $tags array $fullnametagged = false; for ($x = 0; $x < count($tagged); $x++) { if (stristr($tagged[$x], $tag . ' ')) { $fullnametagged = true; break; } } if ($fullnametagged) { continue; } require_once 'mod/item.php'; $body = $access_tag = ''; $success = handle_tag($a, $body, $access_tag, $str_tags, local_user() ? local_user() : $a->profile['profile_uid'], $tag); logger('handle_tag: ' . print_r($success, tue), LOGGER_DEBUG); if ($access_tag) { logger('access_tag: ' . $tag . ' ' . print_r($access_tag, true), LOGGER_DEBUG); if (strpos($access_tag, 'cid:') === 0) { $str_contact_allow .= '<' . substr($access_tag, 4) . '>'; $access_tag = ''; } elseif (strpos($access_tag, 'gid:') === 0) { $str_group_allow .= '<' . substr($access_tag, 4) . '>'; $access_tag = ''; } } if ($success['replaced']) { $tagged[] = $tag; $post_tags[] = array('uid' => $a->profile['profile_uid'], 'type' => $success['termtype'], 'otype' => TERM_OBJ_POST, 'term' => $success['term'], 'url' => $success['url']); } } } $r = q("select * from item where id = %d and uid = %d limit 1", intval($item_id), intval($page_owner_uid)); if ($r) { $datarray = $r[0]; $datarray['term'] = $post_tags; item_store_update($datarray, $execflag); } } goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); return; // NOTREACHED } /** * default post action - upload a photo */ $_REQUEST['source'] = 'photos'; $r = photo_upload($a->channel, $a->get_observer(), $_REQUEST); if (!$r['success']) { notice($r['message'] . EOL); } goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); }
function poller_run($argv, $argc) { cli_startup(); $a = get_app(); $maxsysload = intval(get_config('system', 'maxloadavg')); if ($maxsysload < 1) { $maxsysload = 50; } if (function_exists('sys_getloadavg')) { $load = sys_getloadavg(); if (intval($load[0]) > $maxsysload) { logger('system: load ' . $load . ' too high. Poller deferred to next scheduled run.'); return; } } $interval = intval(get_config('system', 'poll_interval')); if (!$interval) { $interval = get_config('system', 'delivery_interval') === false ? 3 : intval(get_config('system', 'delivery_interval')); } // Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it. $lockfile = 'store/[data]/poller'; if (file_exists($lockfile) && filemtime($lockfile) > time() - 3600 && !get_config('system', 'override_poll_lockfile')) { logger("poller: Already running"); return; } // Create a lockfile. Needs two vars, but $x doesn't need to contain anything. file_put_contents($lockfile, $x); logger('poller: start'); // run queue delivery process in the background proc_run('php', "include/queue.php"); // maintenance for mod sharedwithme - check for updated items and remove them require_once 'include/sharedwithme.php'; apply_updates(); // expire any expired mail q("delete from mail where expires != '%s' and expires < %s ", dbesc(NULL_DATE), db_utcnow()); // expire any expired items $r = q("select id from item where expires != '%s' and expires < %s \n\t\tand item_deleted = 0 ", dbesc(NULL_DATE), db_utcnow()); if ($r) { require_once 'include/items.php'; foreach ($r as $rr) { drop_item($rr['id'], false); } } // Ensure that every channel pings a directory server once a month. This way we can discover // channels and sites that quietly vanished and prevent the directory from accumulating stale // or dead entries. $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('30 DAY')); if ($r) { foreach ($r as $rr) { proc_run('php', 'include/directory.php', $rr['channel_id'], 'force'); if ($interval) { @time_sleep_until(microtime(true) + (double) $interval); } } } // publish any applicable items that were set to be published in the future // (time travel posts). Restrict to items that have come of age in the last // couple of days to limit the query to something reasonable. $r = q("select id from item where item_delayed = 1 and created <= %s and created > '%s' ", db_utcnow(), dbesc(datetime_convert('UTC', 'UTC', 'now - 2 days'))); if ($r) { foreach ($r as $rr) { $x = q("update item set item_delayed = 0 where id = %d", intval($rr['id'])); if ($x) { proc_run('php', 'include/notifier.php', 'wall-new', $rr['id']); } } } $abandon_days = intval(get_config('system', 'account_abandon_days')); if ($abandon_days < 1) { $abandon_days = 0; } // once daily run birthday_updates and then expire in background // FIXME: add birthday updates, both locally and for xprof for use // by directory servers $d1 = intval(get_config('system', 'last_expire_day')); $d2 = intval(datetime_convert('UTC', 'UTC', 'now', 'd')); // Allow somebody to staggger daily activities if they have more than one site on their server, // or if it happens at an inconvenient (busy) hour. $h1 = intval(get_config('system', 'cron_hour')); $h2 = intval(datetime_convert('UTC', 'UTC', 'now', 'G')); $dirmode = get_config('system', 'directory_mode'); /** * Cron Daily * * Actions in the following block are executed once per day, not on every poller run * */ if ($d2 != $d1 && $h1 == $h2) { require_once 'include/dir_fns.php'; check_upstream_directory(); call_hooks('cron_daily', datetime_convert()); $d3 = intval(datetime_convert('UTC', 'UTC', 'now', 'N')); if ($d3 == 7) { /** * Cron Weekly * * Actions in the following block are executed once per day only on Sunday (once per week). * */ call_hooks('cron_weekly', datetime_convert()); z_check_cert(); require_once 'include/hubloc.php'; prune_hub_reinstalls(); require_once 'include/Contact.php'; mark_orphan_hubsxchans(); // get rid of really old poco records q("delete from xlink where xlink_updated < %s - INTERVAL %s and xlink_static = 0 ", db_utcnow(), db_quoteinterval('14 DAY')); $dirmode = intval(get_config('system', 'directory_mode')); if ($dirmode === DIRECTORY_MODE_SECONDARY || $dirmode === DIRECTORY_MODE_PRIMARY) { logger('regdir: ' . print_r(z_fetch_url(get_directory_primary() . '/regdir?f=&url=' . urlencode(z_root()) . '&realm=' . urlencode(get_directory_realm())), true)); } // Check for dead sites proc_run('php', 'include/checksites.php'); // update searchable doc indexes proc_run('php', 'include/importdoc.php'); /** * End Cron Weekly */ } update_birthdays(); //update statistics in config require_once 'include/statistics_fns.php'; update_channels_total_stat(); update_channels_active_halfyear_stat(); update_channels_active_monthly_stat(); update_local_posts_stat(); // expire any read notifications over a month old q("delete from notify where seen = 1 and date < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('30 DAY')); // expire old delivery reports $keep_reports = intval(get_config('system', 'expire_delivery_reports')); if ($keep_reports === 0) { $keep_reports = 30; } q("delete from dreport where dreport_time < %s - INTERVAL %s", db_utcnow(), db_quoteinterval($keep_reports . ' DAY')); // expire any expired accounts downgrade_accounts(); // If this is a directory server, request a sync with an upstream // directory at least once a day, up to once every poll interval. // Pull remote changes and push local changes. // potential issue: how do we keep from creating an endless update loop? if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { require_once 'include/dir_fns.php'; sync_directories($dirmode); } set_config('system', 'last_expire_day', $d2); proc_run('php', 'include/expire.php'); proc_run('php', 'include/cli_suggest.php'); require_once 'include/hubloc.php'; remove_obsolete_hublocs(); /** * End Cron Daily */ } // update any photos which didn't get imported properly // This should be rare $r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = '' \n\t\tand xchan_photo_date < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('1 DAY')); if ($r) { require_once 'include/photo/photo_driver.php'; foreach ($r as $rr) { $photos = import_xchan_photo($rr['xchan_photo_l'], $rr['xchan_hash']); $x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'\n\t\t\t\twhere xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($rr['xchan_hash'])); } } // pull in some public posts if (!get_config('system', 'disable_discover_tab')) { proc_run('php', 'include/externals.php'); } $manual_id = 0; $generation = 0; $force = false; $restart = false; if ($argc > 1 && $argv[1] == 'force') { $force = true; } if ($argc > 1 && $argv[1] == 'restart') { $restart = true; $generation = intval($argv[2]); if (!$generation) { killme(); } } if ($argc > 1 && intval($argv[1])) { $manual_id = intval($argv[1]); $force = true; } $sql_extra = $manual_id ? " AND abook_id = " . intval($manual_id) . " " : ""; reload_plugins(); $d = datetime_convert(); // TODO check to see if there are any cronhooks before wasting a process if (!$restart) { proc_run('php', 'include/cronhooks.php'); } // Only poll from those with suitable relationships $abandon_sql = $abandon_days ? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days) . ' DAY')) : ''; $randfunc = db_getfunc('RAND'); $contacts = q("SELECT * FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash \n\t\tLEFT JOIN account on abook_account = account_id\n\t\twhere abook_self = 0\n\t\t{$sql_extra} \n\t\tAND (( account_flags = %d ) OR ( account_flags = %d )) {$abandon_sql} ORDER BY {$randfunc}", intval(ACCOUNT_OK), intval(ACCOUNT_UNVERIFIED)); if ($contacts) { foreach ($contacts as $contact) { $update = false; $t = $contact['abook_updated']; $c = $contact['abook_connected']; if (intval($contact['abook_feed'])) { $min = service_class_fetch($contact['abook_channel'], 'minimum_feedcheck_minutes'); if (!$min) { $min = intval(get_config('system', 'minimum_feedcheck_minutes')); } if (!$min) { $min = 60; } $x = datetime_convert('UTC', 'UTC', "now - {$min} minutes"); if ($c < $x) { proc_run('php', 'include/onepoll.php', $contact['abook_id']); if ($interval) { @time_sleep_until(microtime(true) + (double) $interval); } } continue; } if ($contact['xchan_network'] !== 'zot') { continue; } if ($c == $t) { if (datetime_convert('UTC', 'UTC', 'now') > datetime_convert('UTC', 'UTC', $t . " + 1 day")) { $update = true; } } else { // if we've never connected with them, start the mark for death countdown from now if ($c == NULL_DATE) { $r = q("update abook set abook_connected = '%s' where abook_id = %d", dbesc(datetime_convert()), intval($contact['abook_id'])); $c = datetime_convert(); $update = true; } // He's dead, Jim if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 30 day")) > 0) { $r = q("update abook set abook_archived = 1 where abook_id = %d", intval($contact['abook_id'])); $update = false; continue; } if (intval($contact['abook_archived'])) { $update = false; continue; } // might be dead, so maybe don't poll quite so often // recently deceased, so keep up the regular schedule for 3 days if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 3 day")) > 0 && strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 1 day")) > 0) { $update = true; } // After that back off and put them on a morphine drip if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 2 day")) > 0) { $update = true; } } if (intval($contact['abook_pending']) || intval($contact['abook_archived']) || intval($contact['abook_ignored']) || intval($contact['abook_blocked'])) { continue; } if (!$update && !$force) { continue; } proc_run('php', 'include/onepoll.php', $contact['abook_id']); if ($interval) { @time_sleep_until(microtime(true) + (double) $interval); } } } if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { $r = q("SELECT u.ud_addr, u.ud_id, u.ud_last FROM updates AS u INNER JOIN (SELECT ud_addr, max(ud_id) AS ud_id FROM updates WHERE ( ud_flags & %d ) = 0 AND ud_addr != '' AND ( ud_last = '%s' OR ud_last > %s - INTERVAL %s ) GROUP BY ud_addr) AS s ON s.ud_id = u.ud_id ", intval(UPDATE_FLAGS_UPDATED), dbesc(NULL_DATE), db_utcnow(), db_quoteinterval('7 DAY')); if ($r) { foreach ($r as $rr) { // If they didn't respond when we attempted before, back off to once a day // After 7 days we won't bother anymore if ($rr['ud_last'] != NULL_DATE) { if ($rr['ud_last'] > datetime_convert('UTC', 'UTC', 'now - 1 day')) { continue; } } proc_run('php', 'include/onedirsync.php', $rr['ud_id']); if ($interval) { @time_sleep_until(microtime(true) + (double) $interval); } } } } set_config('system', 'lastpoll', datetime_convert()); //All done - clear the lockfile @unlink($lockfile); return; }
function contact_remove($channel_id, $abook_id) { if (!$channel_id || !$abook_id) { return false; } $archive = get_pconfig($channel_id, 'system', 'archive_removed_contacts'); if ($archive) { q("update abook set abook_flags = ( abook_flags | %d ) where abook_id = %d and abook_channel = %d limit 1", intval(ABOOK_FLAG_ARCHIVED), intval($abook_id), intval($channel_id)); return true; } $r = q("select * from abook where abook_id = %d and abook_channel = %d limit 1", intval($abook_id), intval($channel_id)); if (!$r) { return false; } $abook = $r[0]; if ($abook['abook_flags'] & ABOOK_FLAG_SELF) { return false; } $r = q("select * from item where author_xchan = '%s' and uid = %d", dbesc($abook['abook_xchan']), intval($channel_id)); if ($r) { foreach ($r as $rr) { drop_item($rr['id'], false); } } q("delete from abook where abook_id = %d and abook_channel = %d limit 1", intval($abook['abook_id']), intval($channel_id)); $r = q("delete from event where event_xchan = '%s' and uid = %d", dbesc($abook['abook_xchan']), intval($channel_id)); $r = q("delete from group_member where xchan = '%s' and uid = %d", dbesc($abook['abook_xchan']), intval($channel_id)); $r = q("delete from mail where ( from_xchan = '%s' or to_xchan = '%s' ) and channel_id = %d ", dbesc($abook['abook_xchan']), dbesc($abook['abook_xchan']), intval($channel_id)); return true; }
case 'a': case 'x': $_GET['type'] = 4; break; default: $_GET['type'] = 5; break; } $lim = ''; if (!isset($_GET['all'])) { $lim = 'LIMIT 1'; } $q = do_mysql("SELECT fullname FROM items WHERE belongs = '" . $LOGIN . "' AND is_in = 'inv' AND realname = '" . $rn . "' " . $lim . ";"); while ($i = mysql_fetch_assoc($q)) { $item = $i['fullname']; drop_item($item, $LOGIN); // esli prodolzhaetsja znachit vzjali // nado zanesti v spisok ozhidanija veshej na pojavlenie $rfn = $rn; if (array_key_exists($rfn, $items)) { // $items podkljuchen v faile s_loadmaps.php // znachit nado udalitq avto obnovu $act = do_mysql("SELECT actions FROM maps WHERE map = '" . $pl_map . "';"); $act = mysql_result($act, 0); //echo '$act = '.$act.'<br/>'; if (strpos($act, '~') === false && strpos($act, $rfn) > 0) { $act = ''; } else { $act = explode('~', $act); $cou = count($act); for ($i = 0; $i < $cou; $i++) {
function photos_post(&$a) { logger('mod-photos: photos_post: begin', LOGGER_DEBUG); logger('mod_photos: REQUEST ' . print_r($_REQUEST, true), LOGGER_DATA); logger('mod_photos: FILES ' . print_r($_FILES, true), LOGGER_DATA); $ph = photo_factory(''); $phototypes = $ph->supportedTypes(); $can_post = false; $page_owner_uid = $a->data['channel']['channel_id']; if (perm_is_allowed($page_owner_uid, get_observer_hash(), 'write_storage')) { $can_post = true; } if (!$can_post) { notice(t('Permission denied.') . EOL); if (is_ajax()) { killme(); } return; } $s = abook_self($page_owner_uid); if (!$s) { notice(t('Page owner information could not be retrieved.') . EOL); logger('mod_photos: post: unable to locate contact record for page owner. uid=' . $page_owner_uid); if (is_ajax()) { killme(); } return; } $owner_record = $s[0]; $acl = new AccessList($a->data['channel']); if (argc() > 3 && argv(2) === 'album') { $album = hex2bin(argv(3)); if ($album === t('Profile Photos')) { // not allowed goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); } if (!photos_album_exists($page_owner_uid, $album)) { notice(t('Album not found.') . EOL); goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); } /* * RENAME photo album */ $newalbum = notags(trim($_REQUEST['albumname'])); if ($newalbum != $album) { // @fixme - syncronise with DAV or disallow completely goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); // $x = photos_album_rename($page_owner_uid,$album,$newalbum); // if($x) { // $newurl = str_replace(bin2hex($album),bin2hex($newalbum),$_SESSION['photo_return']); // goaway($a->get_baseurl() . '/' . $newurl); // } } /* * DELETE photo album and all its photos */ if ($_REQUEST['dropalbum'] == t('Delete Album')) { $res = array(); // get the list of photos we are about to delete if (remote_channel() && !local_channel()) { $str = photos_album_get_db_idstr($page_owner_uid, $album, remote_channel()); } elseif (local_channel()) { $str = photos_album_get_db_idstr(local_channel(), $album); } else { $str = null; } if (!$str) { goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); } $r = q("select id from item where resource_id in ( {$str} ) and resource_type = 'photo' and uid = %d " . item_normal(), intval($page_owner_uid)); if ($r) { foreach ($r as $i) { attach_delete($page_owner_uid, $i['resource_id'], 1); drop_item($i['id'], false, DROPITEM_PHASE1, true); proc_run('php', 'include/notifier.php', 'drop', $i['id']); } } // remove the associated photos in case they weren't attached to an item q("delete from photo where resource_id in ( {$str} ) and uid = %d", intval($page_owner_uid)); // @FIXME do the same for the linked attach } goaway($a->get_baseurl() . '/photos/' . $a->data['channel']['channel_address']); } if (argc() > 2 && x($_REQUEST, 'delete') && $_REQUEST['delete'] === t('Delete Photo')) { // same as above but remove single photo $ob_hash = get_observer_hash(); if (!$ob_hash) { goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); } $r = q("SELECT `id`, `resource_id` FROM `photo` WHERE ( xchan = '%s' or `uid` = %d ) AND `resource_id` = '%s' LIMIT 1", dbesc($ob_hash), intval(local_channel()), dbesc($a->argv[2])); if ($r) { q("DELETE FROM `photo` WHERE `uid` = %d AND `resource_id` = '%s'", intval($page_owner_uid), dbesc($r[0]['resource_id'])); attach_delete($page_owner_uid, $r[0]['resource_id'], 1); $i = q("SELECT * FROM `item` WHERE `resource_id` = '%s' AND resource_type = 'photo' and `uid` = %d LIMIT 1", dbesc($r[0]['resource_id']), intval($page_owner_uid)); if (count($i)) { drop_item($i[0]['id'], true, DROPITEM_PHASE1); $url = $a->get_baseurl(); } } goaway($a->get_baseurl() . '/photos/' . $a->data['channel']['channel_address'] . '/album/' . $_SESSION['album_return']); } if ($a->argc > 2 && (x($_POST, 'desc') !== false || x($_POST, 'newtag') !== false) || x($_POST, 'albname') !== false) { $desc = x($_POST, 'desc') ? notags(trim($_POST['desc'])) : ''; $rawtags = x($_POST, 'newtag') ? notags(trim($_POST['newtag'])) : ''; $item_id = x($_POST, 'item_id') ? intval($_POST['item_id']) : 0; $albname = x($_POST, 'albname') ? notags(trim($_POST['albname'])) : ''; $is_nsfw = x($_POST, 'adult') ? intval($_POST['adult']) : 0; $acl->set_from_array($_POST); $perm = $acl->get(); $resource_id = $a->argv[2]; if (!strlen($albname)) { $albname = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y'); } if (x($_POST, 'rotate') !== false && (intval($_POST['rotate']) == 1 || intval($_POST['rotate']) == 2)) { logger('rotate'); $r = q("select * from photo where `resource_id` = '%s' and uid = %d and scale = 0 limit 1", dbesc($resource_id), intval($page_owner_uid)); if (count($r)) { $ph = photo_factory(dbunescbin($r[0]['data']), $r[0]['type']); if ($ph->is_valid()) { $rotate_deg = intval($_POST['rotate']) == 1 ? 270 : 90; $ph->rotate($rotate_deg); $width = $ph->getWidth(); $height = $ph->getHeight(); $x = q("update photo set data = '%s', height = %d, width = %d where `resource_id` = '%s' and uid = %d and scale = 0", dbescbin($ph->imageString()), intval($height), intval($width), dbesc($resource_id), intval($page_owner_uid)); if ($width > 640 || $height > 640) { $ph->scaleImage(640); $width = $ph->getWidth(); $height = $ph->getHeight(); $x = q("update photo set data = '%s', height = %d, width = %d where `resource_id` = '%s' and uid = %d and scale = 1", dbescbin($ph->imageString()), intval($height), intval($width), dbesc($resource_id), intval($page_owner_uid)); } if ($width > 320 || $height > 320) { $ph->scaleImage(320); $width = $ph->getWidth(); $height = $ph->getHeight(); $x = q("update photo set data = '%s', height = %d, width = %d where `resource_id` = '%s' and uid = %d and scale = 2", dbescbin($ph->imageString()), intval($height), intval($width), dbesc($resource_id), intval($page_owner_uid)); } } } } $p = q("SELECT * FROM `photo` WHERE `resource_id` = '%s' AND `uid` = %d ORDER BY `scale` DESC", dbesc($resource_id), intval($page_owner_uid)); if ($p) { $ext = $phototypes[$p[0]['type']]; $r = q("UPDATE `photo` SET `description` = '%s', `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s' WHERE `resource_id` = '%s' AND `uid` = %d", dbesc($desc), dbesc($perm['allow_cid']), dbesc($perm['allow_gid']), dbesc($perm['deny_cid']), dbesc($perm['deny_gid']), dbesc($resource_id), intval($page_owner_uid)); } $item_private = $str_contact_allow || $str_group_allow || $str_contact_deny || $str_group_deny ? true : false; $old_is_nsfw = $p[0]['is_nsfw']; if ($old_is_nsfw != $is_nsfw) { $r = q("update photo set is_nsfw = %d where resource_id = '%s' and uid = %d", intval($is_nsfw), dbesc($resource_id), intval($page_owner_uid)); } /* Don't make the item visible if the only change was the album name */ $visibility = 0; if ($p[0]['description'] !== $desc || strlen($rawtags)) { $visibility = 1; } if (!$item_id) { $item_id = photos_create_item($a->data['channel'], get_observer_hash(), $p[0], $visibility); } if ($item_id) { $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($item_id), intval($page_owner_uid)); if ($r) { $old_tag = $r[0]['tag']; $old_inform = $r[0]['inform']; } } // make sure the linked item has the same permissions as the photo regardless of any other changes $x = q("update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d\n\t\t\twhere id = %d", dbesc($perm['allow_cid']), dbesc($perm['allow_gid']), dbesc($perm['deny_cid']), dbesc($perm['deny_gid']), intval($acl->is_private()), intval($item_id)); // make sure the attach has the same permissions as the photo regardless of any other changes $x = q("update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where hash = '%s' and uid = %d and is_photo = 1", dbesc($perm['allow_cid']), dbesc($perm['allow_gid']), dbesc($perm['deny_cid']), dbesc($perm['deny_gid']), dbesc($resource_id), intval($page_owner_uid)); if (strlen($rawtags)) { $str_tags = ''; $inform = ''; // if the new tag doesn't have a namespace specifier (@foo or #foo) give it a mention $x = substr($rawtags, 0, 1); if ($x !== '@' && $x !== '#') { $rawtags = '@' . $rawtags; } require_once 'include/text.php'; $profile_uid = $a->profile['profile_uid']; $results = linkify_tags($a, $rawtags, local_channel() ? local_channel() : $profile_uid); $success = $results['success']; $post_tags = array(); foreach ($results as $result) { $success = $result['success']; if ($success['replaced']) { $post_tags[] = array('uid' => $profile_uid, 'type' => $success['termtype'], 'otype' => TERM_OBJ_POST, 'term' => $success['term'], 'url' => $success['url']); } } $r = q("select * from item where id = %d and uid = %d limit 1", intval($item_id), intval($page_owner_uid)); if ($r) { $r = fetch_post_tags($r, true); $datarray = $r[0]; if ($post_tags) { if (!array_key_exists('term', $datarray) || !is_array($datarray['term'])) { $datarray['term'] = $post_tags; } else { $datarray['term'] = array_merge($datarray['term'], $post_tags); } } item_store_update($datarray, $execflag); } } goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); return; // NOTREACHED } /** * default post action - upload a photo */ $channel = $a->data['channel']; $observer = $a->data['observer']; $_REQUEST['source'] = 'photos'; require_once 'include/attach.php'; if (!local_channel()) { $_REQUEST['contact_allow'] = expand_acl($channel['channel_allow_cid']); $_REQUEST['group_allow'] = expand_acl($channel['channel_allow_gid']); $_REQUEST['contact_deny'] = expand_acl($channel['channel_deny_cid']); $_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']); } $r = attach_store($a->channel, get_observer_hash(), '', $_REQUEST); if (!$r['success']) { notice($r['message'] . EOL); } if ($_REQUEST['newalbum']) { goaway($a->get_baseurl() . '/photos/' . $a->data['channel']['channel_address'] . '/album/' . bin2hex($_REQUEST['newalbum'])); } else { goaway($a->get_baseurl() . '/photos/' . $a->data['channel']['channel_address'] . '/album/' . bin2hex(datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y'))); } }
function item_content(&$a) { if (!local_user() && !remote_user()) { return; } require_once 'include/security.php'; $o = ''; if ($a->argc == 3 && $a->argv[1] === 'drop' && intval($a->argv[2])) { $o = drop_item($a->argv[2], !is_ajax()); if (is_ajax()) { // ajax return: [<item id>, 0 (no perm) | <owner id>] echo json_encode(array(intval($a->argv[2]), intval($o))); killme(); } } return $o; }
/** * @brief Process atom feed and update anything/everything we might need to update. * * @param array $xml * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. * @param $importer * The contact_record (joined to user_record) of the local user who owns this * relationship. It is this person's stuff that is going to be updated. * @param $contact * The person who is sending us stuff. If not set, we MAY be processing a "follow" activity * from an external network and MAY create an appropriate contact record. Otherwise, we MUST * have a contact record. * @param int $pass by default ($pass = 0) we cannot guarantee that a parent item has been * imported prior to its children being seen in the stream unless we are certain * of how the feed is arranged/ordered. * * With $pass = 1, we only pull parent items out of the stream. * * With $pass = 2, we only pull children (comments/likes). * * So running this twice, first with pass 1 and then with pass 2 will do the right * thing regardless of feed ordering. This won't be adequate in a fully-threaded * model where comments can have sub-threads. That would require some massive sorting * to get all the feed items into a mostly linear ordering, and might still require * recursion. */ function consume_feed($xml, $importer, &$contact, $pass = 0) { require_once 'library/simplepie/simplepie.inc'; if (!strlen($xml)) { logger('consume_feed: empty input'); return; } $sys_expire = intval(get_config('system', 'default_expire_days')); $chn_expire = intval($importer['channel_expire_days']); $expire_days = $sys_expire; if ($chn_expire != 0 && $chn_expire < $sys_expire) { $expire_days = $chn_expire; } // logger('expire_days: ' . $expire_days); $feed = new SimplePie(); $feed->set_raw_data($xml); $feed->init(); if ($feed->error()) { logger('consume_feed: Error parsing XML: ' . $feed->error()); } $permalink = $feed->get_permalink(); // Check at the feed level for updated contact name and/or photo // process any deleted entries $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry'); if (is_array($del_entries) && count($del_entries) && $pass != 2) { foreach ($del_entries as $dentry) { $deleted = false; if (isset($dentry['attribs']['']['ref'])) { $mid = $dentry['attribs']['']['ref']; $deleted = true; if (isset($dentry['attribs']['']['when'])) { $when = $dentry['attribs']['']['when']; $when = datetime_convert('UTC', 'UTC', $when, 'Y-m-d H:i:s'); } else { $when = datetime_convert('UTC', 'UTC', 'now', 'Y-m-d H:i:s'); } } if ($deleted && is_array($contact)) { $r = q("SELECT * from item where mid = '%s' and author_xchan = '%s' and uid = %d limit 1", dbesc(base64url_encode($mid)), dbesc($contact['xchan_hash']), intval($importer['channel_id'])); if ($r) { $item = $r[0]; if (!intval($item['item_deleted'])) { logger('consume_feed: deleting item ' . $item['id'] . ' mid=' . base64url_decode($item['mid']), LOGGER_DEBUG); drop_item($item['id'], false); } } } } } // Now process the feed if ($feed->get_item_quantity()) { logger('consume_feed: feed item count = ' . $feed->get_item_quantity(), LOGGER_DEBUG); $items = $feed->get_items(); foreach ($items as $item) { $is_reply = false; $item_id = base64url_encode($item->get_id()); logger('consume_feed: processing ' . $item_id, LOGGER_DEBUG); $rawthread = $item->get_item_tags(NAMESPACE_THREAD, 'in-reply-to'); if (isset($rawthread[0]['attribs']['']['ref'])) { $is_reply = true; $parent_mid = base64url_encode($rawthread[0]['attribs']['']['ref']); } if ($is_reply) { if ($pass == 1) { continue; } // Have we seen it? If not, import it. $item_id = base64url_encode($item->get_id()); $author = array(); $datarray = get_atom_elements($feed, $item, $author); if ($contact['xchan_network'] === 'rss') { $datarray['public_policy'] = 'specific'; $datarray['comment_policy'] = 'none'; } if (!x($author, 'author_name') || $author['author_is_feed']) { $author['author_name'] = $contact['xchan_name']; } if (!x($author, 'author_link') || $author['author_is_feed']) { $author['author_link'] = $contact['xchan_url']; } if (!x($author, 'author_photo') || $author['author_is_feed']) { $author['author_photo'] = $contact['xchan_photo_m']; } $datarray['author_xchan'] = ''; if ($author['author_link'] != $contact['xchan_url']) { $x = import_author_unknown(array('name' => $author['author_name'], 'url' => $author['author_link'], 'photo' => array('src' => $author['author_photo']))); if ($x) { $datarray['author_xchan'] = $x; } } if (!$datarray['author_xchan']) { $datarray['author_xchan'] = $contact['xchan_hash']; } $datarray['owner_xchan'] = $contact['xchan_hash']; $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", dbesc($item_id), intval($importer['channel_id'])); // Update content if 'updated' changes if ($r) { if (x($datarray, 'edited') !== false && datetime_convert('UTC', 'UTC', $datarray['edited']) !== $r[0]['edited']) { // do not accept (ignore) an earlier edit than one we currently have. if (datetime_convert('UTC', 'UTC', $datarray['edited']) < $r[0]['edited']) { continue; } update_feed_item($importer['channel_id'], $datarray); } continue; } $datarray['parent_mid'] = $parent_mid; $datarray['aid'] = $importer['channel_account_id']; $datarray['uid'] = $importer['channel_id']; logger('consume_feed: ' . print_r($datarray, true), LOGGER_DATA); $xx = item_store($datarray); $r = $xx['item_id']; continue; } else { // Head post of a conversation. Have we seen it? If not, import it. $item_id = base64url_encode($item->get_id()); $author = array(); $datarray = get_atom_elements($feed, $item, $author); if ($contact['xchan_network'] === 'rss') { $datarray['public_policy'] = 'specific'; $datarray['comment_policy'] = 'none'; } if (is_array($contact)) { if (!x($author, 'author_name') || $author['author_is_feed']) { $author['author_name'] = $contact['xchan_name']; } if (!x($author, 'author_link') || $author['author_is_feed']) { $author['author_link'] = $contact['xchan_url']; } if (!x($author, 'author_photo') || $author['author_is_feed']) { $author['author_photo'] = $contact['xchan_photo_m']; } } if (!x($author, 'author_name') || !x($author, 'author_link')) { logger('consume_feed: no author information! ' . print_r($author, true)); continue; } $datarray['author_xchan'] = ''; if (activity_match($datarray['verb'], ACTIVITY_FOLLOW) && $datarray['obj_type'] === ACTIVITY_OBJ_PERSON) { $cb = array('item' => $datarray, 'channel' => $importer, 'xchan' => null, 'author' => $author, 'caught' => false); call_hooks('follow_from_feed', $cb); if ($cb['caught']) { if ($cb['return_code']) { http_status_exit($cb['return_code']); } continue; } } if ($author['author_link'] != $contact['xchan_url']) { $x = import_author_unknown(array('name' => $author['author_name'], 'url' => $author['author_link'], 'photo' => array('src' => $author['author_photo']))); if ($x) { $datarray['author_xchan'] = $x; } } if (!$datarray['author_xchan']) { $datarray['author_xchan'] = $contact['xchan_hash']; } $datarray['owner_xchan'] = $contact['xchan_hash']; if (array_key_exists('created', $datarray) && $datarray['created'] != NULL_DATE && $expire_days) { $t1 = $datarray['created']; $t2 = datetime_convert('UTC', 'UTC', 'now - ' . $expire_days . 'days'); if ($t1 < $t2) { logger('feed content older than expiration. Ignoring.', LOGGER_DEBUG, LOG_INFO); continue; } } $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", dbesc($item_id), intval($importer['channel_id'])); // Update content if 'updated' changes if ($r) { if (x($datarray, 'edited') !== false && datetime_convert('UTC', 'UTC', $datarray['edited']) !== $r[0]['edited']) { // do not accept (ignore) an earlier edit than one we currently have. if (datetime_convert('UTC', 'UTC', $datarray['edited']) < $r[0]['edited']) { continue; } update_feed_item($importer['channel_id'], $datarray); } continue; } $datarray['parent_mid'] = $item_id; $datarray['uid'] = $importer['channel_id']; $datarray['aid'] = $importer['channel_account_id']; if (!link_compare($author['owner_link'], $contact['xchan_url'])) { logger('consume_feed: Correcting item owner.', LOGGER_DEBUG); $author['owner_name'] = $contact['name']; $author['owner_link'] = $contact['url']; $author['owner_avatar'] = $contact['thumb']; } if (!post_is_importable($datarray, $contact)) { continue; } logger('consume_feed: author ' . print_r($author, true), LOGGER_DEBUG); logger('consume_feed: ' . print_r($datarray, true), LOGGER_DATA); $xx = item_store($datarray); $r = $xx['item_id']; continue; } } } }
/** * @brief Delete a chess game using the standard drop_item method for posts in the * item table * * @param $game_id unique ID of the game to be deleted * @param $channel The authenticated local channel requesting the deletion * @return array Success of deletion and item that was deleted */ function chess_delete_game($game_id, $channel) { $items = q("SELECT id FROM item WHERE resource_type = '%s' AND resource_id = '%s' AND uid = %d AND item_deleted = 0 limit 1", dbesc('chess'), dbesc($game_id), intval($channel['channel_id'])); if (!$items) { return array('items' => null, 'status' => false); } else { $drop = drop_item($items[0]['id'], false, DROPITEM_NORMAL, true); return array('items' => $items, 'status' => $drop === 1 ? true : false); } }
function map_revokeAllDynamicShares() { $channel = App::get_channel(); // Get the channel information $r = q("UPDATE `locserv-dynamic-markers` SET resource_id = '' WHERE resource_id != ''"); $items = q("SELECT id FROM item WHERE obj_type = '%s' AND resource_type = '%s' AND resource_id != '' AND object LIKE '%s' AND uid = %d", dbesc(ACTIVITY_POST), dbesc('locserv'), dbesc('%"locationDataType":"dynamicMarker"%'), intval($channel['channel_id'])); logger('map plugin: items for deletion: ' . json_encode($items)); foreach ($items as $item) { drop_item($item['id'], true, DROPITEM_PHASE1); } echo json_encode(array('status' => true)); die; }
function item_content(&$a) { if (!local_user() && !remote_user()) { return; } require_once 'include/security.php'; if ($a->argc == 3 && $a->argv[1] === 'drop' && intval($a->argv[2])) { require_once 'include/items.php'; drop_item($a->argv[2]); } }
public static function run($argc, $argv) { $maxsysload = intval(get_config('system', 'maxloadavg')); if ($maxsysload < 1) { $maxsysload = 50; } if (function_exists('sys_getloadavg')) { $load = sys_getloadavg(); if (intval($load[0]) > $maxsysload) { logger('system: load ' . $load . ' too high. Cron deferred to next scheduled run.'); return; } } // Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it. $lockfile = 'store/[data]/cron'; if (file_exists($lockfile) && filemtime($lockfile) > time() - 3600 && !get_config('system', 'override_cron_lockfile')) { logger("cron: Already running"); return; } // Create a lockfile. Needs two vars, but $x doesn't need to contain anything. file_put_contents($lockfile, $x); logger('cron: start'); // run queue delivery process in the background Master::Summon(array('Queue')); Master::Summon(array('Poller')); // maintenance for mod sharedwithme - check for updated items and remove them require_once 'include/sharedwithme.php'; apply_updates(); // expire any expired mail q("delete from mail where expires > '%s' and expires < %s ", dbesc(NULL_DATE), db_utcnow()); // expire any expired items $r = q("select id from item where expires > '2001-01-01 00:00:00' and expires < %s \n\t\t\tand item_deleted = 0 ", db_utcnow()); if ($r) { require_once 'include/items.php'; foreach ($r as $rr) { drop_item($rr['id'], false); } } // delete expired access tokens $r = q("select atoken_id from atoken where atoken_expires > '%s' and atoken_expires < %s", dbesc(NULL_DATE), db_utcnow()); if ($r) { require_once 'include/security.php'; foreach ($r as $rr) { atoken_delete($rr['atoken_id']); } } // Ensure that every channel pings a directory server once a month. This way we can discover // channels and sites that quietly vanished and prevent the directory from accumulating stale // or dead entries. $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('30 DAY')); if ($r) { foreach ($r as $rr) { Master::Summon(array('Directory', $rr['channel_id'], 'force')); if ($interval) { @time_sleep_until(microtime(true) + (double) $interval); } } } // publish any applicable items that were set to be published in the future // (time travel posts). Restrict to items that have come of age in the last // couple of days to limit the query to something reasonable. $r = q("select id from item where item_delayed = 1 and created <= %s and created > '%s' ", db_utcnow(), dbesc(datetime_convert('UTC', 'UTC', 'now - 2 days'))); if ($r) { foreach ($r as $rr) { $x = q("update item set item_delayed = 0 where id = %d", intval($rr['id'])); if ($x) { $z = q("select * from item where id = %d", intval($message_id)); if ($z) { xchan_query($z); $sync_item = fetch_post_tags($z); build_sync_packet($sync_item[0]['uid'], ['item' => [encode_item($sync_item[0], true)]]); } Master::Summon(array('Notifier', 'wall-new', $rr['id'])); } } } $abandon_days = intval(get_config('system', 'account_abandon_days')); if ($abandon_days < 1) { $abandon_days = 0; } // once daily run birthday_updates and then expire in background // FIXME: add birthday updates, both locally and for xprof for use // by directory servers $d1 = intval(get_config('system', 'last_expire_day')); $d2 = intval(datetime_convert('UTC', 'UTC', 'now', 'd')); // Allow somebody to staggger daily activities if they have more than one site on their server, // or if it happens at an inconvenient (busy) hour. $h1 = intval(get_config('system', 'cron_hour')); $h2 = intval(datetime_convert('UTC', 'UTC', 'now', 'G')); if ($d2 != $d1 && $h1 == $h2) { Master::Summon(array('Cron_daily')); } // update any photos which didn't get imported properly // This should be rare $r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = '' \n\t\t\tand xchan_photo_date < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('1 DAY')); if ($r) { require_once 'include/photo/photo_driver.php'; foreach ($r as $rr) { $photos = import_xchan_photo($rr['xchan_photo_l'], $rr['xchan_hash']); $x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'\n\t\t\t\t\twhere xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($rr['xchan_hash'])); } } // pull in some public posts if (!get_config('system', 'disable_discover_tab')) { Master::Summon(array('Externals')); } $generation = 0; $restart = false; if ($argc > 1 && $argv[1] == 'restart') { $restart = true; $generation = intval($argv[2]); if (!$generation) { killme(); } } reload_plugins(); $d = datetime_convert(); // TODO check to see if there are any cronhooks before wasting a process if (!$restart) { Master::Summon(array('Cronhooks')); } set_config('system', 'lastcron', datetime_convert()); //All done - clear the lockfile @unlink($lockfile); return; }
/** * */ function api_statuses_destroy(&$a, $type) { if (api_user() === false) { return false; } $user_info = api_get_user($a); // params $id = intval($a->argv[3]); logger('API: api_statuses_destroy: ' . $id); require_once 'include/items.php'; drop_item($id, false); if ($type == 'xml') { $ok = "true"; } else { $ok = "ok"; } return api_apply_template('test', $type, array('$ok' => $ok)); }
function contact_remove($channel_id, $abook_id) { if (!$channel_id || !$abook_id) { return false; } logger('removing contact ' . $abook_id . ' for channel ' . $channel_id, LOGGER_DEBUG); $archive = get_pconfig($channel_id, 'system', 'archive_removed_contacts'); if ($archive) { q("update abook set abook_archived = 1 where abook_id = %d and abook_channel = %d", intval($abook_id), intval($channel_id)); return true; } $r = q("select * from abook where abook_id = %d and abook_channel = %d limit 1", intval($abook_id), intval($channel_id)); if (!$r) { return false; } $abook = $r[0]; if (intval($abook['abook_self'])) { return false; } $r = q("select * from item where author_xchan = '%s' and uid = %d", dbesc($abook['abook_xchan']), intval($channel_id)); if ($r) { foreach ($r as $rr) { drop_item($rr['id'], false); } } q("delete from abook where abook_id = %d and abook_channel = %d", intval($abook['abook_id']), intval($channel_id)); $r = q("delete from event where event_xchan = '%s' and uid = %d", dbesc($abook['abook_xchan']), intval($channel_id)); $r = q("delete from group_member where xchan = '%s' and uid = %d", dbesc($abook['abook_xchan']), intval($channel_id)); $r = q("delete from mail where ( from_xchan = '%s' or to_xchan = '%s' ) and channel_id = %d ", dbesc($abook['abook_xchan']), dbesc($abook['abook_xchan']), intval($channel_id)); return true; }
function diaspora_signed_retraction($importer, $xml, $msg) { $guid = notags(unxmlify($xml->target_guid)); $diaspora_handle = notags(unxmlify($xml->sender_handle)); $type = notags(unxmlify($xml->target_type)); $sig = notags(unxmlify($xml->target_author_signature)); $parent_author_signature = $xml->parent_author_signature ? notags(unxmlify($xml->parent_author_signature)) : ''; $contact = diaspora_get_contact_by_handle($importer['channel_id'], $diaspora_handle); if (!$contact) { logger('diaspora_signed_retraction: no contact ' . $diaspora_handle . ' for ' . $importer['channel_id']); return; } $signed_data = $guid . ';' . $type; $key = $msg['key']; /* How Diaspora performs relayable_retraction signature checking: - If an item has been sent by the item author to the top-level post owner to relay on to the rest of the contacts on the top-level post, the top-level post owner checks the author_signature, then creates a parent_author_signature before relaying the item on - If an item has been relayed on by the top-level post owner, the contacts who receive it check only the parent_author_signature. Basically, they trust that the top-level post owner has already verified the authenticity of anything he/she sends out - In either case, the signature that get checked is the signature created by the person who sent the salmon */ if ($parent_author_signature) { $parent_author_signature = base64_decode($parent_author_signature); if (!rsa_verify($signed_data, $parent_author_signature, $key, 'sha256')) { logger('diaspora_signed_retraction: top-level post owner verification failed'); return; } } else { $sig_decode = base64_decode($sig); if (!rsa_verify($signed_data, $sig_decode, $key, 'sha256')) { logger('diaspora_signed_retraction: retraction owner verification failed.' . print_r($msg, true)); return; } } if ($type === 'StatusMessage' || $type === 'Comment' || $type === 'Like') { $r = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($guid), intval($importer['channel_id'])); if ($r) { if ($r[0]['author_xchan'] == $contact['xchan_hash']) { drop_item($r[0]['id'], false, DROPITEM_PHASE1); // Now check if the retraction needs to be relayed by us // // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent. // The only item with `parent` and `id` as the parent id is the parent item. $p = q("select item_flags from item where parent = %d and id = %d limit 1", $r[0]['parent'], $r[0]['parent']); if ($p) { if ($p[0]['item_flags'] & ITEM_ORIGIN && !$parent_author_signature) { // FIXME so we can relay this // q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", // $r[0]['id'], // dbesc($signed_data), // dbesc($sig), // dbesc($diaspora_handle) // ); // the existence of parent_author_signature would have meant the parent_author or owner // is already relaying. logger('diaspora_signed_retraction: relaying relayable_retraction'); proc_run('php', 'include/notifier.php', 'drop', $r[0]['id']); } } } } } else { logger('diaspora_signed_retraction: unknown type: ' . $type); } return 202; // NOTREACHED }
function drop_items($items) { $uid = 0; if (!local_channel() && !remote_channel()) { return; } if (count($items)) { foreach ($items as $item) { $owner = drop_item($item, false); if ($owner && !$uid) { $uid = $owner; } } } // multiple threads may have been deleted, send an expire notification if ($uid) { proc_run('php', "include/notifier.php", "expire", "{$uid}"); } }
function pumpio_dodelete(&$a, $uid, $self, $post, $own_id) { // Two queries for speed issues $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($post->object->id), intval($uid)); if (count($r)) { return drop_item($r[0]["id"], $false); } $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1", dbesc($post->object->id), intval($uid)); if (count($r)) { return drop_item($r[0]["id"], $false); } }
/** * */ function api_statuses_destroy(&$a, $type) { if (api_user() === false) { return false; } $user_info = api_get_user($a); // params $id = intval(argv(3)); if ($id) { // first prove that we own the item $r = q("select * from item where id = %d and uid = %d limit 1", intval($id), intval($user_info['uid'])); if (!$r) { return false; } } else { if ($_REQUEST['namespace'] && $_REQUEST['remote_id']) { $r = q("select * from item_id where service = '%s' and sid = '%s' and uid = %d limit 1", dbesc($_REQUEST['namespace']), dbesc($_REQUEST['remote_id']), intval($user_info['uid'])); if (!$r) { return false; } $id = $r[0]['iid']; } if ($_REQUEST['namespace'] && $_REQUEST['comment_id']) { $r = q("select * from item_id left join item on item.id = item_id.iid where service = '%s' and sid = '%s' and uid = %d and item.id != item.parent limit 1", dbesc($_REQUEST['namespace']), dbesc($_REQUEST['comment_id']), intval($user_info['uid'])); if (!$r) { return false; } $id = $r[0]['iid']; } } if (!$id) { return false; } logger('API: api_statuses_destroy: ' . $id); require_once 'include/items.php'; drop_item($id, false); if ($type == 'xml') { $ok = "true"; } else { $ok = "ok"; } return api_apply_template('test', $type, array('$ok' => $ok)); }
function delete_imported_item($sender, $item, $uid) { logger('delete_imported_item invoked', LOGGER_DEBUG); $r = q("select id, item_restrict from item where ( author_xchan = '%s' or owner_xchan = '%s' or source_xchan = '%s' )\n\t\tand mid = '%s' and uid = %d limit 1", dbesc($sender['hash']), dbesc($sender['hash']), dbesc($sender['hash']), dbesc($item['mid']), intval($uid)); if (!$r) { logger('delete_imported_item: failed: ownership issue'); return false; } if ($r[0]['item_restrict'] & ITEM_DELETED) { logger('delete_imported_item: item was already deleted'); return false; } require_once 'include/items.php'; // Use phased deletion to set the deleted flag, call both tag_deliver and the notifier to notify downstream channels // and then clean up after ourselves with a cron job after several days to do the delete_item_lowlevel() (DROPITEM_PHASE2). drop_item($r[0]['id'], false, DROPITEM_PHASE1); tag_deliver($uid, $r[0]['id']); return $r[0]['id']; }