function thing_init(&$a) { if (!local_channel()) { return; } $channel = App::get_channel(); $term_hash = $_REQUEST['term_hash'] ? $_REQUEST['term_hash'] : ''; $name = escape_tags($_REQUEST['term']); $verb = escape_tags($_REQUEST['verb']); $activity = intval($_REQUEST['activity']); $profile_guid = escape_tags($_REQUEST['profile_assign']); $url = $_REQUEST['link']; $photo = $_REQUEST['img']; $hash = random_string(); $verbs = obj_verbs(); /** * verbs: [0] = first person singular, e.g. "I want", [1] = 3rd person singular, e.g. "Bill wants" * We use the first person form when creating an activity, but the third person for use in activities * @FIXME There is no accounting for verb gender for languages where this is significant. We may eventually * require obj_verbs() to provide full conjugations and specify which form to use in the $_REQUEST params to this module. */ $translated_verb = $verbs[$verb][1]; /* * The site administrator can do things that normals cannot. * This is restricted because it will likely cause * an activitystreams protocol violation and the activity might * choke in some other network and result in unnecessary * support requests. It isn't because we're trying to be heavy-handed * about what you can and can't do. */ if (!$translated_verb) { if (is_site_admin()) { $translated_verb = $verb; } } /* * Things, objects: We do not provide definite (a, an) or indefinite (the) articles or singular/plural designators * That needs to be specified in your thing. e.g. Mike has "a carrot", Greg wants "balls", Bob likes "the Boston Red Sox". */ /* * Future work on this module might produce more complex activities with targets, e.g. Phillip likes Karen's moustache * and to describe other non-thing objects like channels, such as Karl wants Susan - where Susan represents a channel profile. */ if (!$name || !$translated_verb) { return; } $acl = new Zotlabs\Access\AccessList($channel); if (array_key_exists('contact_allow', $_REQUEST) || array_key_exists('group_allow', $_REQUEST) || array_key_exists('contact_deny', $_REQUEST) || array_key_exists('group_deny', $_REQUEST)) { $acl->set_from_array($_REQUEST); } $x = $acl->get(); if ($term_hash) { $t = q("select * from obj where obj_obj = '%s' and obj_channel = %d limit 1", dbesc($term_hash), intval(local_channel())); if (!$t) { notice(t('Item not found.') . EOL); return; } $orig_record = $t[0]; if ($photo != $orig_record['obj_imgurl']) { $arr = import_xchan_photo($photo, get_observer_hash(), true); $local_photo = $arr[0]; $local_photo_type = $arr[3]; } else { $local_photo = $orig_record['obj_imgurl']; } $r = q("update obj set obj_term = '%s', obj_url = '%s', obj_imgurl = '%s', obj_edited = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where obj_obj = '%s' and obj_channel = %d ", dbesc($name), dbesc($url ? $url : z_root() . '/thing/' . $term_hash), dbesc($local_photo), dbesc(datetime_convert()), dbesc($x['allow_cid']), dbesc($x['allow_gid']), dbesc($x['deny_cid']), dbesc($x['deny_gid']), dbesc($term_hash), intval(local_channel())); info(t('Thing updated') . EOL); $r = q("select * from obj where obj_channel = %d and obj_obj = '%s' limit 1", intval(local_channel()), dbesc($term_hash)); if ($r) { build_sync_packet(0, array('obj' => $r)); } return; } $sql = $profile_guid ? " and profile_guid = '" . dbesc($profile_guid) . "' " : " and is_default = 1 "; $p = q("select profile_guid, is_default from profile where uid = %d {$sql} limit 1", intval(local_channel())); if ($p) { $profile = $p[0]; } else { return; } $local_photo = null; if ($photo) { $arr = import_xchan_photo($photo, get_observer_hash(), true); $local_photo = $arr[0]; $local_photo_type = $arr[3]; } $created = datetime_convert(); $url = $url ? $url : z_root() . '/thing/' . $hash; $r = q("insert into obj ( obj_page, obj_verb, obj_type, obj_channel, obj_obj, obj_term, obj_url, obj_imgurl, obj_created, obj_edited, allow_cid, allow_gid, deny_cid, deny_gid ) values ('%s','%s', %d, %d, '%s','%s','%s','%s','%s','%s','%s','%s','%s','%s') ", dbesc($profile['profile_guid']), dbesc($verb), intval(TERM_OBJ_THING), intval(local_channel()), dbesc($hash), dbesc($name), dbesc($url), dbesc($photo ? $local_photo : ''), dbesc($created), dbesc($created), dbesc($x['allow_cid']), dbesc($x['allow_gid']), dbesc($x['deny_cid']), dbesc($x['deny_gid'])); if (!$r) { notice(t('Object store: failed')); return; } info(t('Thing added')); $r = q("select * from obj where obj_channel = %d and obj_obj = '%s' limit 1", intval(local_channel()), dbesc($hash)); if ($r) { build_sync_packet(0, array('obj' => $r)); } if ($activity) { $arr = array(); $links = array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => $url)); if ($local_photo) { $links[] = array('rel' => 'photo', 'type' => $local_photo_type, 'href' => $local_photo); } $objtype = ACTIVITY_OBJ_THING; $obj = json_encode(array('type' => $objtype, 'id' => $url, 'link' => $links, 'title' => $name, 'content' => $name)); $bodyverb = str_replace('OBJ: ', '', t('OBJ: %1$s %2$s %3$s')); $arr['owner_xchan'] = $channel['channel_hash']; $arr['author_xchan'] = $channel['channel_hash']; $arr['item_origin'] = 1; $arr['item_wall'] = 1; $arr['item_thread_top'] = 1; $ulink = '[zrl=' . $channel['xchan_url'] . ']' . $channel['channel_name'] . '[/zrl]'; $plink = '[zrl=' . $url . ']' . $name . '[/zrl]'; $arr['body'] = sprintf($bodyverb, $ulink, $translated_verb, $plink); if ($local_photo) { $arr['body'] .= "\n\n[zmg]" . $local_photo . "[/zmg]"; } $arr['verb'] = $verb; $arr['obj_type'] = $objtype; $arr['object'] = $obj; if (!$profile['is_default']) { $arr['item_private'] = true; $str = ''; $r = q("select abook_xchan from abook where abook_channel = %d and abook_profile = '%s'", intval(local_channel()), dbesc($profile_guid)); if ($r) { $arr['allow_cid'] = ''; foreach ($r as $rr) { $arr['allow_cid'] .= '<' . $rr['abook_xchan'] . '>'; } } else { $arr['allow_cid'] = '<' . get_observer_hash() . '>'; } } $ret = post_activity_item($arr); } }
/** * @brief Takes an associative array of a fetched discovery packet and updates * all internal data structures which need to be updated as a result. * * @param array $arr => json_decoded discovery packet * @param int $ud_flags * Determines whether to create a directory update record if any changes occur, default is UPDATE_FLAGS_UPDATED * $ud_flags = UPDATE_FLAGS_FORCED indicates a forced refresh where we unconditionally create a directory update record * this typically occurs once a month for each channel as part of a scheduled ping to notify the directory * that the channel still exists * @param array $ud_arr * If set [typically by update_directory_entry()] indicates a specific update table row and more particularly * contains a particular address (ud_addr) which needs to be updated in that table. * * @return associative array * * \e boolean \b success boolean true or false * * \e string \b message (optional) error string only if success is false */ function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { call_hooks('import_xchan', $arr); $ret = array('success' => false); $dirmode = intval(get_config('system', 'directory_mode')); $changed = false; $what = ''; if (!(is_array($arr) && array_key_exists('success', $arr) && $arr['success'])) { logger('import_xchan: invalid data packet: ' . print_r($arr, true)); $ret['message'] = t('Invalid data packet'); return $ret; } if (!($arr['guid'] && $arr['guid_sig'])) { logger('import_xchan: no identity information provided. ' . print_r($arr, true)); return $ret; } $xchan_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']); $arr['hash'] = $xchan_hash; $import_photos = false; if (!rsa_verify($arr['guid'], base64url_decode($arr['guid_sig']), $arr['key'])) { logger('import_xchan: Unable to verify channel signature for ' . $arr['address']); $ret['message'] = t('Unable to verify channel signature'); return $ret; } logger('import_xchan: ' . $xchan_hash, LOGGER_DEBUG); $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($xchan_hash)); if (!array_key_exists('connect_url', $arr)) { $arr['connect_url'] = ''; } if (strpos($arr['address'], '/') !== false) { $arr['address'] = substr($arr['address'], 0, strpos($arr['address'], '/')); } if ($r) { if ($r[0]['xchan_photo_date'] != $arr['photo_updated']) { $import_photos = true; } // if we import an entry from a site that's not ours and either or both of us is off the grid - hide the entry. /** @TODO: check if we're the same directory realm, which would mean we are allowed to see it */ $dirmode = get_config('system', 'directory_mode'); if (($arr['site']['directory_mode'] === 'standalone' || $dirmode & DIRECTORY_MODE_STANDALONE) && $arr['site']['url'] != z_root()) { $arr['searchable'] = false; } $hidden = 1 - intval($arr['searchable']); $hidden_changed = $adult_changed = $deleted_changed = $pubforum_changed = 0; if (intval($r[0]['xchan_hidden']) != 1 - intval($arr['searchable'])) { $hidden_changed = 1; } if (intval($r[0]['xchan_selfcensored']) != intval($arr['adult_content'])) { $adult_changed = 1; } if (intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) { $deleted_changed = 1; } if (intval($r[0]['xchan_pubforum']) != intval($arr['public_forum'])) { $pubforum_changed = 1; } if ($r[0]['xchan_name_date'] != $arr['name_updated'] || $r[0]['xchan_connurl'] != $arr['connections_url'] || $r[0]['xchan_addr'] != $arr['address'] || $r[0]['xchan_follow'] != $arr['follow_url'] || $r[0]['xchan_connpage'] != $arr['connect_url'] || $r[0]['xchan_url'] != $arr['url'] || $hidden_changed || $adult_changed || $deleted_changed || $pubforum_changed) { $rup = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_follow = '%s', \n\t\t\t\txchan_connpage = '%s', xchan_hidden = %d, xchan_selfcensored = %d, xchan_deleted = %d, xchan_pubforum = %d, \n\t\t\t\txchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s'", dbesc($arr['name'] ? $arr['name'] : '-'), dbesc($arr['name_updated']), dbesc($arr['connections_url']), dbesc($arr['follow_url']), dbesc($arr['connect_url']), intval(1 - intval($arr['searchable'])), intval($arr['adult_content']), intval($arr['deleted']), intval($arr['public_forum']), dbesc($arr['address']), dbesc($arr['url']), dbesc($xchan_hash)); logger('import_xchan: update: existing: ' . print_r($r[0], true), LOGGER_DATA); logger('import_xchan: update: new: ' . print_r($arr, true), LOGGER_DATA); $what .= 'xchan '; $changed = true; } } else { $import_photos = true; if (($arr['site']['directory_mode'] === 'standalone' || $dirmode & DIRECTORY_MODE_STANDALONE) && $arr['site']['url'] != z_root()) { $arr['searchable'] = false; } $x = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_mimetype,\n\t\t\t\txchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_follow, xchan_connpage, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_hidden, xchan_selfcensored, xchan_deleted, xchan_pubforum )\n\t\t\t\tvalues ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d) ", dbesc($xchan_hash), dbesc($arr['guid']), dbesc($arr['guid_sig']), dbesc($arr['key']), dbesc($arr['photo_mimetype']), dbesc($arr['photo']), dbesc($arr['address']), dbesc($arr['url']), dbesc($arr['connections_url']), dbesc($arr['follow_url']), dbesc($arr['connect_url']), dbesc($arr['name'] ? $arr['name'] : '-'), dbesc('zot'), dbescdate($arr['photo_updated']), dbescdate($arr['name_updated']), intval(1 - intval($arr['searchable'])), intval($arr['adult_content']), intval($arr['deleted']), intval($arr['public_forum'])); $what .= 'new_xchan'; $changed = true; } if ($import_photos) { require_once 'include/photo/photo_driver.php'; // see if this is a channel clone that's hosted locally - which we treat different from other xchans/connections $local = q("select channel_account_id, channel_id from channel where channel_hash = '%s' limit 1", dbesc($xchan_hash)); if ($local) { $ph = z_fetch_url($arr['photo'], true); if ($ph['success']) { $hash = import_channel_photo($ph['body'], $arr['photo_mimetype'], $local[0]['channel_account_id'], $local[0]['channel_id']); if ($hash) { // unless proven otherwise $is_default_profile = 1; $profile = q("select is_default from profile where aid = %d and uid = %d limit 1", intval($local[0]['channel_account_id']), intval($local[0]['channel_id'])); if ($profile) { if (!intval($profile[0]['is_default'])) { $is_default_profile = 0; } } // If setting for the default profile, unset the profile photo flag from any other photos I own if ($is_default_profile) { q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND aid = %d AND uid = %d", intval(PHOTO_NORMAL), intval(PHOTO_PROFILE), dbesc($hash), intval($local[0]['channel_account_id']), intval($local[0]['channel_id'])); } } // reset the names in case they got messed up when we had a bug in this function $photos = array(z_root() . '/photo/profile/l/' . $local[0]['channel_id'], z_root() . '/photo/profile/m/' . $local[0]['channel_id'], z_root() . '/photo/profile/s/' . $local[0]['channel_id'], $arr['photo_mimetype'], false); } } else { $photos = import_xchan_photo($arr['photo'], $xchan_hash); } if ($photos) { if ($photos[4]) { // importing the photo failed somehow. Leave the photo_date alone so we can try again at a later date. // This often happens when somebody joins the matrix with a bad cert. $r = 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($xchan_hash)); } else { $r = q("update xchan set xchan_photo_date = '%s', 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'", dbescdate(datetime_convert('UTC', 'UTC', $arr['photo_updated'])), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($xchan_hash)); } $what .= 'photo '; $changed = true; } } // what we are missing for true hub independence is for any changes in the primary hub to // get reflected not only in the hublocs, but also to update the URLs and addr in the appropriate xchan $s = sync_locations($arr, $arr); if ($s) { if ($s['change_message']) { $what .= $s['change_message']; } if ($s['changed']) { $changed = $s['changed']; } if ($s['message']) { $ret['message'] .= $s['message']; } } // Which entries in the update table are we interested in updating? $address = $ud_arr && $ud_arr['ud_addr'] ? $ud_arr['ud_addr'] : $arr['address']; // Are we a directory server of some kind? $other_realm = false; $realm = get_directory_realm(); if (array_key_exists('site', $arr) && array_key_exists('realm', $arr['site']) && strpos($arr['site']['realm'], $realm) === false) { $other_realm = true; } if ($dirmode != DIRECTORY_MODE_NORMAL) { // We're some kind of directory server. However we can only add directory information // if the entry is in the same realm (or is a sub-realm). Sub-realms are denoted by // including the parent realm in the name. e.g. 'RED_GLOBAL:foo' would allow an entry to // be in directories for the local realm (foo) and also the RED_GLOBAL realm. if (array_key_exists('profile', $arr) && is_array($arr['profile']) && !$other_realm) { $profile_changed = import_directory_profile($xchan_hash, $arr['profile'], $address, $ud_flags, 1); if ($profile_changed) { $what .= 'profile '; $changed = true; } } else { logger('import_xchan: profile not available - hiding'); // they may have made it private $r = q("delete from xprof where xprof_hash = '%s'", dbesc($xchan_hash)); $r = q("delete from xtag where xtag_hash = '%s' and xtag_flags = 0", dbesc($xchan_hash)); } } if (array_key_exists('site', $arr) && is_array($arr['site'])) { $profile_changed = import_site($arr['site'], $arr['key']); if ($profile_changed) { $what .= 'site '; $changed = true; } } if ($changed || $ud_flags == UPDATE_FLAGS_FORCED) { $guid = random_string() . '@' . get_app()->get_hostname(); update_modtime($xchan_hash, $guid, $address, $ud_flags); logger('import_xchan: changed: ' . $what, LOGGER_DEBUG); } elseif (!$ud_flags) { // nothing changed but we still need to update the updates record q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ", intval(UPDATE_FLAGS_UPDATED), dbesc($address), intval(UPDATE_FLAGS_UPDATED)); } if (!x($ret, 'message')) { $ret['success'] = true; $ret['hash'] = $xchan_hash; } logger('import_xchan: result: ' . print_r($ret, true), LOGGER_DATA); return $ret; }
function diaspora_discover(&$a, &$b) { require_once 'include/network.php'; $result = array(); $network = null; $diaspora = false; $diaspora_base = ''; $diaspora_guid = ''; $diaspora_key = ''; $dfrn = false; $x = old_webfinger($webbie); if ($x) { logger('old_webfinger: ' . print_r($x, true)); foreach ($x as $link) { if ($link['@attributes']['rel'] === NAMESPACE_DFRN) { $dfrn = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'salmon') { $notify = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === NAMESPACE_FEED) { $poll = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') { $hcard = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { $profile = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0') { $poco = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') { $diaspora_base = unamp($link['@attributes']['href']); $diaspora = true; } if ($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') { $diaspora_guid = unamp($link['@attributes']['href']); $diaspora = true; } if ($link['@attributes']['rel'] === 'diaspora-public-key') { $diaspora_key = base64_decode(unamp($link['@attributes']['href'])); if (strstr($diaspora_key, 'RSA ')) { $pubkey = rsatopem($diaspora_key); } else { $pubkey = $diaspora_key; } $diaspora = true; } } if ($diaspora && $diaspora_base && $diaspora_guid) { $guid = $diaspora_guid; $diaspora_base = trim($diaspora_base, '/'); $notify = $diaspora_base . '/receive'; if (strpos($webbie, '@')) { $addr = str_replace('acct:', '', $webbie); $hostname = substr($webbie, strpos($webbie, '@') + 1); } $network = 'diaspora'; // until we get a dfrn layer, we'll use diaspora protocols for Friendica, // but give it a different network so we can go back and fix these when we get proper support. // It really should be just 'friendica' but we also want to distinguish // between Friendica sites that we can use D* protocols with and those we can't. // Some Friendica sites will have Diaspora disabled. if ($dfrn) { $network = 'friendica-over-diaspora'; } if ($hcard) { $vcard = scrape_vcard($hcard); $vcard['nick'] = substr($webbie, 0, strpos($webbie, '@')); if (!$vcard['fn']) { $vcard['fn'] = $webbie; } } $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($addr)); /** * * Diaspora communications are notoriously unreliable and receiving profile update messages (indeed any messages) * are pretty much random luck. We'll check the timestamp of the xchan_name_date at a higher level and refresh * this record once a month; because if you miss a profile update message and they update their profile photo or name * you're otherwise stuck with stale info until they change their profile again - which could be years from now. * */ if ($r) { $r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s' limit 1", dbesc($vcard['fn']), dbesc($network), dbesc(datetime_convert()), dbesc($addr)); } else { $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", dbesc($addr), dbesc($guid), dbesc($pubkey), dbesc($addr), dbesc($profile), dbesc($vcard['fn']), dbesc($network), dbescdate(datetime_convert())); } $r = q("select * from hubloc where hubloc_hash = '%s' limit 1", dbesc($webbie)); if (!$r) { $r = q("insert into hubloc ( hubloc_guid, hubloc_hash, hubloc_addr, hubloc_network, hubloc_url, hubloc_host, hubloc_callback, hubloc_updated, hubloc_primary ) values ('%s','%s','%s','%s','%s','%s','%s','%s', 1)", dbesc($guid), dbesc($addr), dbesc($addr), dbesc($network), dbesc(trim($diaspora_base, '/')), dbesc($hostname), dbesc($notify), dbescdate(datetime_convert())); } $photos = import_xchan_photo($vcard['photo'], $addr); $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", dbescdate(datetime_convert('UTC', 'UTC', $arr['photo_updated'])), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($addr)); return true; } return false; /* $vcard['fn'] = notags($vcard['fn']); $vcard['nick'] = str_replace(' ','',notags($vcard['nick'])); $result['name'] = $vcard['fn']; $result['nick'] = $vcard['nick']; $result['guid'] = $guid; $result['url'] = $profile; $result['hostname'] = $hostname; $result['addr'] = $addr; $result['batch'] = $batch; $result['notify'] = $notify; $result['poll'] = $poll; $result['request'] = $request; $result['confirm'] = $confirm; $result['poco'] = $poco; $result['photo'] = $vcard['photo']; $result['priority'] = $priority; $result['network'] = $network; $result['alias'] = $alias; $result['pubkey'] = $pubkey; logger('probe_url: ' . print_r($result,true), LOGGER_DEBUG); return $result; */ /* Sample Diaspora result. Array ( [name] => Mike Macgirvin [nick] => macgirvin [guid] => a9174a618f8d269a [url] => https://joindiaspora.com/u/macgirvin [hostname] => joindiaspora.com [addr] => macgirvin@joindiaspora.com [batch] => [notify] => https://joindiaspora.com/receive [poll] => https://joindiaspora.com/public/macgirvin.atom [request] => [confirm] => [poco] => [photo] => https://joindiaspora.s3.amazonaws.com/uploads/images/thumb_large_fec4e6eef13ae5e56207.jpg [priority] => [network] => diaspora [alias] => [pubkey] => -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtihtyIuRDWkDpCA+I1UaQ jI4S7k625+A7EEJm+pL2ZVSJxeCKiFeEgHBQENjLMNNm8l8F6blxgQqE6ZJ9Spa7f tlaXYTRCrfxKzh02L3hR7sNA+JS/nXJaUAIo+IwpIEspmcIRbD9GB7Wv/rr+M28uH 31EeYyDz8QL6InU/bJmnCdFvmEMBQxJOw1ih9tQp7UNJAbUMCje0WYFzBz7sfcaHL OyYcCOqOCBLdGucUoJzTQ9iDBVzB8j1r1JkIHoEb2moUoKUp+tkCylNfd/3IVELF9 7w1Qjmit3m50OrJk2DQOXvCW9KQxaQNdpRPSwhvemIt98zXSeyZ1q/YjjOwG0DWDq AF8aLj3/oQaZndTPy/6tMiZogKaijoxj8xFLuPYDTw5VpKquriVC0z8oxyRbv4t9v 8JZZ9BXqzmayvY3xZGGp8NulrfjW+me2bKh0/df1aHaBwpZdDTXQ6kqAiS2FfsuPN vg57fhfHbL1yJ4oDbNNNeI0kJTGchXqerr8C20khU/cQ2Xt31VyEZtnTB665Ceugv kp3t2qd8UpAVKl430S5Quqx2ymfUIdxdW08CEjnoRNEL3aOWOXfbf4gSVaXmPCR4i LSIeXnd14lQYK/uxW/8cTFjcmddsKxeXysoQxbSa9VdDK+KkpZdgYXYrTTofXs6v+ 4afAEhRaaY+MCAwEAAQ== -----END PUBLIC KEY----- ) */ } }
function import_account($account_id) { if (!$account_id) { logger("import_account: No account ID supplied"); return; } $max_identities = account_service_class_fetch($account_id, 'total_identities'); $max_friends = account_service_class_fetch($account_id, 'total_channels'); $max_feeds = account_service_class_fetch($account_id, 'total_feeds'); if ($max_identities !== false) { $r = q("select channel_id from channel where channel_account_id = %d", intval($account_id)); if ($r && count($r) > $max_identities) { notice(sprintf(t('Your service plan only allows %d channels.'), $max_identities) . EOL); return; } } $data = null; $seize = x($_REQUEST, 'make_primary') ? intval($_REQUEST['make_primary']) : 0; $import_posts = x($_REQUEST, 'import_posts') ? intval($_REQUEST['import_posts']) : 0; $src = $_FILES['filename']['tmp_name']; $filename = basename($_FILES['filename']['name']); $filesize = intval($_FILES['filename']['size']); $filetype = $_FILES['filename']['type']; $completed = array_key_exists('import_step', $_SESSION) ? intval($_SESSION['import_step']) : 0; if ($completed) { logger('saved import step: ' . $_SESSION['import_step']); } if ($src) { // This is OS specific and could also fail if your tmpdir isn't very large // mostly used for Diaspora which exports gzipped files. if (strpos($filename, '.gz')) { @rename($src, $src . '.gz'); @system('gunzip ' . escapeshellarg($src . '.gz')); } if ($filesize) { $data = @file_get_contents($src); } unlink($src); } if (!$src) { $old_address = x($_REQUEST, 'old_address') ? $_REQUEST['old_address'] : ''; if (!$old_address) { logger('mod_import: nothing to import.'); notice(t('Nothing to import.') . EOL); return; } $email = x($_REQUEST, 'email') ? $_REQUEST['email'] : ''; $password = x($_REQUEST, 'password') ? $_REQUEST['password'] : ''; $channelname = substr($old_address, 0, strpos($old_address, '@')); $servername = substr($old_address, strpos($old_address, '@') + 1); $scheme = 'https://'; $api_path = '/api/red/channel/export/basic?f=&channel=' . $channelname; if ($import_posts) { $api_path .= '&posts=1'; } $binary = false; $redirects = 0; $opts = array('http_auth' => $email . ':' . $password); $url = $scheme . $servername . $api_path; $ret = z_fetch_url($url, $binary, $redirects, $opts); if (!$ret['success']) { $ret = z_fetch_url('http://' . $servername . $api_path, $binary, $redirects, $opts); } if ($ret['success']) { $data = $ret['body']; } else { notice(t('Unable to download data from old server') . EOL); } } if (!$data) { logger('mod_import: empty file.'); notice(t('Imported file is empty.') . EOL); return; } $data = json_decode($data, true); // logger('import: data: ' . print_r($data,true)); // print_r($data); if (array_key_exists('user', $data) && array_key_exists('version', $data)) { require_once 'include/Import/import_diaspora.php'; import_diaspora($data); return; } $moving = false; if (array_key_exists('compatibility', $data) && array_key_exists('database', $data['compatibility'])) { $v1 = substr($data['compatibility']['database'], -4); $v2 = substr(DB_UPDATE_VERSION, -4); if ($v2 > $v1) { $t = sprintf(t('Warning: Database versions differ by %1$d updates.'), $v2 - $v1); notice($t); } if (array_key_exists('server_role', $data['compatibility']) && $data['compatibility']['server_role'] == 'basic') { $moving = true; } } if ($moving) { $seize = 1; } // import channel $relocate = array_key_exists('relocate', $data) ? $data['relocate'] : null; if (array_key_exists('channel', $data)) { if ($completed < 1) { $channel = import_channel($data['channel'], $account_id, $seize); } else { $r = q("select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", intval($account_id), dbesc($channel['channel_guid'])); if ($r) { $channel = $r[0]; } } if (!$channel) { logger('mod_import: channel not found. ', print_r($channel, true)); notice(t('Cloned channel not found. Import failed.') . EOL); return; } } if (!$channel) { $channel = \App::get_channel(); } if (!$channel) { logger('mod_import: channel not found. ', print_r($channel, true)); notice(t('No channel. Import failed.') . EOL); return; } if ($completed < 2) { if (is_array($data['config'])) { import_config($channel, $data['config']); } logger('import step 2'); $_SESSION['import_step'] = 2; } if ($completed < 3) { if ($data['photo']) { require_once 'include/photo/photo_driver.php'; import_channel_photo(base64url_decode($data['photo']['data']), $data['photo']['type'], $account_id, $channel['channel_id']); } if (is_array($data['profile'])) { import_profiles($channel, $data['profile']); } logger('import step 3'); $_SESSION['import_step'] = 3; } if ($completed < 4) { if (is_array($data['hubloc']) && !$moving) { import_hublocs($channel, $data['hubloc'], $seize); } logger('import step 4'); $_SESSION['import_step'] = 4; } if ($completed < 5) { // create new hubloc for the new channel at this site $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_network, hubloc_primary, \n\t\t\t\thubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey )\n\t\t\t\tvalues ( '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s' )", dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_hash']), dbesc(channel_reddress($channel)), dbesc('zot'), intval($seize ? 1 : 0), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $channel['channel_prvkey']))), dbesc(\App::get_hostname()), dbesc(z_root() . '/post'), dbesc(get_config('system', 'pubkey'))); // reset the original primary hubloc if it is being seized if ($seize) { $r = q("update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' and hubloc_url != '%s' ", dbesc($channel['channel_hash']), dbesc(z_root())); } logger('import step 5'); $_SESSION['import_step'] = 5; } if ($completed < 6) { // import xchans and contact photos if ($seize) { // replace any existing xchan we may have on this site if we're seizing control $r = q("delete from xchan where xchan_hash = '%s'", dbesc($channel['channel_hash'])); $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_l, xchan_photo_m, xchan_photo_s, xchan_addr, xchan_url, xchan_follow, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_hidden, xchan_orphan, xchan_censored, xchan_selfcensored, xchan_system, xchan_pubforum, xchan_deleted ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, %d, %d, %d )", dbesc($channel['channel_hash']), dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_pubkey']), dbesc(z_root() . "/photo/profile/l/" . $channel['channel_id']), dbesc(z_root() . "/photo/profile/m/" . $channel['channel_id']), dbesc(z_root() . "/photo/profile/s/" . $channel['channel_id']), dbesc(channel_reddress($channel)), dbesc(z_root() . '/channel/' . $channel['channel_address']), dbesc(z_root() . '/follow?f=&url=%s'), dbesc(z_root() . '/poco/' . $channel['channel_address']), dbesc($channel['channel_name']), dbesc('zot'), dbesc(datetime_convert()), dbesc(datetime_convert()), 0, 0, 0, 0, 0, 0, 0); } logger('import step 6'); $_SESSION['import_step'] = 6; } if ($completed < 7) { $xchans = $data['xchan']; if ($xchans) { foreach ($xchans as $xchan) { $hash = make_xchan_hash($xchan['xchan_guid'], $xchan['xchan_guid_sig']); if ($xchan['xchan_network'] === 'zot' && $hash !== $xchan['xchan_hash']) { logger('forged xchan: ' . print_r($xchan, true)); continue; } if (!array_key_exists('xchan_hidden', $xchan)) { $xchan['xchan_hidden'] = $xchan['xchan_flags'] & 0x1 ? 1 : 0; $xchan['xchan_orphan'] = $xchan['xchan_flags'] & 0x2 ? 1 : 0; $xchan['xchan_censored'] = $xchan['xchan_flags'] & 0x4 ? 1 : 0; $xchan['xchan_selfcensored'] = $xchan['xchan_flags'] & 0x8 ? 1 : 0; $xchan['xchan_system'] = $xchan['xchan_flags'] & 0x10 ? 1 : 0; $xchan['xchan_pubforum'] = $xchan['xchan_flags'] & 0x20 ? 1 : 0; $xchan['xchan_deleted'] = $xchan['xchan_flags'] & 0x1000 ? 1 : 0; } $r = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1", dbesc($xchan['xchan_hash'])); if ($r) { continue; } dbesc_array($xchan); $r = dbq("INSERT INTO xchan (`" . implode("`, `", array_keys($xchan)) . "`) VALUES ('" . implode("', '", array_values($xchan)) . "')"); require_once 'include/photo/photo_driver.php'; $photos = import_xchan_photo($xchan['xchan_photo_l'], $xchan['xchan_hash']); if ($photos[4]) { $photodate = NULL_DATE; } else { $photodate = $xchan['xchan_photo_date']; } $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s'\n\t\t\t\t\t\twhere xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($photodate), dbesc($xchan['xchan_hash'])); } } logger('import step 7'); $_SESSION['import_step'] = 7; } // FIXME - ensure we have an xchan if somebody is trying to pull a fast one if ($completed < 8) { $friends = 0; $feeds = 0; // import contacts $abooks = $data['abook']; if ($abooks) { foreach ($abooks as $abook) { $abook_copy = $abook; $abconfig = null; if (array_key_exists('abconfig', $abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) { $abconfig = $abook['abconfig']; } unset($abook['abook_id']); unset($abook['abook_rating']); unset($abook['abook_rating_text']); unset($abook['abconfig']); unset($abook['abook_their_perms']); unset($abook['abook_my_perms']); $abook['abook_account'] = $account_id; $abook['abook_channel'] = $channel['channel_id']; if (!array_key_exists('abook_blocked', $abook)) { $abook['abook_blocked'] = $abook['abook_flags'] & 0x1 ? 1 : 0; $abook['abook_ignored'] = $abook['abook_flags'] & 0x2 ? 1 : 0; $abook['abook_hidden'] = $abook['abook_flags'] & 0x4 ? 1 : 0; $abook['abook_archived'] = $abook['abook_flags'] & 0x8 ? 1 : 0; $abook['abook_pending'] = $abook['abook_flags'] & 0x10 ? 1 : 0; $abook['abook_unconnected'] = $abook['abook_flags'] & 0x20 ? 1 : 0; $abook['abook_self'] = $abook['abook_flags'] & 0x80 ? 1 : 0; $abook['abook_feed'] = $abook['abook_flags'] & 0x100 ? 1 : 0; } if ($abook['abook_self']) { $role = get_pconfig($channel['channel_id'], 'system', 'permissions_role'); if ($role === 'forum' || $abook['abook_my_perms'] & PERMS_W_TAGWALL) { q("update xchan set xchan_pubforum = 1 where xchan_hash = '%s' ", dbesc($abook['abook_xchan'])); } } else { if ($max_friends !== false && $friends > $max_friends) { continue; } if ($max_feeds !== false && intval($abook['abook_feed']) && $feeds > $max_feeds) { continue; } } dbesc_array($abook); $r = dbq("INSERT INTO abook (`" . implode("`, `", array_keys($abook)) . "`) VALUES ('" . implode("', '", array_values($abook)) . "')"); $friends++; if (intval($abook['abook_feed'])) { $feeds++; } translate_abook_perms_inbound($channel, $abook_copy); if ($abconfig) { // @fixme does not handle sync of del_abconfig foreach ($abconfig as $abc) { set_abconfig($channel['channel_id'], $abc['xchan'], $abc['cat'], $abc['k'], $abc['v']); } } } } logger('import step 8'); $_SESSION['import_step'] = 8; } if ($completed < 9) { $groups = $data['group']; if ($groups) { $saved = array(); foreach ($groups as $group) { $saved[$group['hash']] = array('old' => $group['id']); if (array_key_exists('name', $group)) { $group['gname'] = $group['name']; unset($group['name']); } unset($group['id']); $group['uid'] = $channel['channel_id']; dbesc_array($group); $r = dbq("INSERT INTO groups (`" . implode("`, `", array_keys($group)) . "`) VALUES ('" . implode("', '", array_values($group)) . "')"); } $r = q("select * from `groups` where uid = %d", intval($channel['channel_id'])); if ($r) { foreach ($r as $rr) { $saved[$rr['hash']]['new'] = $rr['id']; } } } $group_members = $data['group_member']; if ($group_members) { foreach ($group_members as $group_member) { unset($group_member['id']); $group_member['uid'] = $channel['channel_id']; foreach ($saved as $x) { if ($x['old'] == $group_member['gid']) { $group_member['gid'] = $x['new']; } } dbesc_array($group_member); $r = dbq("INSERT INTO group_member (`" . implode("`, `", array_keys($group_member)) . "`) VALUES ('" . implode("', '", array_values($group_member)) . "')"); } } logger('import step 9'); $_SESSION['import_step'] = 9; } if (is_array($data['obj'])) { import_objs($channel, $data['obj']); } if (is_array($data['likes'])) { import_likes($channel, $data['likes']); } if (is_array($data['app'])) { import_apps($channel, $data['app']); } if (is_array($data['chatroom'])) { import_chatrooms($channel, $data['chatroom']); } if (is_array($data['conv'])) { import_conv($channel, $data['conv']); } if (is_array($data['mail'])) { import_mail($channel, $data['mail']); } if (is_array($data['event'])) { import_events($channel, $data['event']); } if (is_array($data['event_item'])) { import_items($channel, $data['event_item'], false, $relocate); } if (is_array($data['menu'])) { import_menus($channel, $data['menu']); } $addon = array('channel' => $channel, 'data' => $data); call_hooks('import_channel', $addon); $saved_notification_flags = notifications_off($channel['channel_id']); if ($import_posts && array_key_exists('item', $data) && $data['item']) { import_items($channel, $data['item'], false, $relocate); } notifications_on($channel['channel_id'], $saved_notification_flags); if (array_key_exists('item_id', $data) && $data['item_id']) { import_item_ids($channel, $data['item_id']); } // FIXME - ensure we have a self entry if somebody is trying to pull a fast one // send out refresh requests // notify old server that it may no longer be primary. \Zotlabs\Daemon\Master::Summon(array('Notifier', 'location', $channel['channel_id'])); // This will indirectly perform a refresh_all *and* update the directory \Zotlabs\Daemon\Master::Summon(array('Directory', $channel['channel_id'])); notice(t('Import completed.') . EOL); change_channel($channel['channel_id']); unset($_SESSION['import_step']); goaway(z_root() . '/network'); }
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 get() { $noid = get_config('system', 'disable_openid'); if ($noid) { goaway(z_root()); } logger('mod_openid ' . print_r($_REQUEST, true), LOGGER_DATA); if (x($_REQUEST, 'openid_mode')) { $openid = new LightOpenID(z_root()); if ($openid->validate()) { logger('openid: validate'); $authid = normalise_openid($_REQUEST['openid_identity']); if (!strlen($authid)) { logger(t('OpenID protocol error. No ID returned.') . EOL); goaway(z_root()); } $x = match_openid($authid); if ($x) { $r = q("select * from channel where channel_id = %d limit 1", intval($x)); if ($r) { $y = q("select * from account where account_id = %d limit 1", intval($r[0]['channel_account_id'])); if ($y) { foreach ($y as $record) { if ($record['account_flags'] == ACCOUNT_OK || $record['account_flags'] == ACCOUNT_UNVERIFIED) { logger('mod_openid: openid success for ' . $x[0]['channel_name']); $_SESSION['uid'] = $r[0]['channel_id']; $_SESSION['account_id'] = $r[0]['channel_account_id']; $_SESSION['authenticated'] = true; authenticate_success($record, $r[0], true, true, true, true); goaway(z_root()); } } } } } // Successful OpenID login - but we can't match it to an existing account. // See if they've got an xchan $r = q("select * from xconfig left join xchan on xchan_hash = xconfig.xchan where cat = 'system' and k = 'openid' and v = '%s' limit 1", dbesc($authid)); if ($r) { $_SESSION['authenticated'] = 1; $_SESSION['visitor_id'] = $r[0]['xchan_hash']; $_SESSION['my_url'] = $r[0]['xchan_url']; $_SESSION['my_address'] = $r[0]['xchan_addr']; $arr = array('xchan' => $r[0], 'session' => $_SESSION); call_hooks('magic_auth_openid_success', $arr); \App::set_observer($r[0]); require_once 'include/security.php'; \App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); info(sprintf(t('Welcome %s. Remote authentication successful.'), $r[0]['xchan_name'])); logger('mod_openid: remote auth success from ' . $r[0]['xchan_addr']); if ($_SESSION['return_url']) { goaway($_SESSION['return_url']); } goaway(z_root()); } // no xchan... // create one. // We should probably probe the openid url and figure out if they have any kind of // social presence we might be able to scrape some identifying info from. $name = $authid; $url = trim($_REQUEST['openid_identity'], '/'); if (strpos($url, 'http') === false) { $url = 'https://' . $url; } $pphoto = z_root() . '/' . get_default_profile_photo(); $parsed = @parse_url($url); if ($parsed) { $host = $parsed['host']; } $attr = $openid->getAttributes(); if (is_array($attr) && count($attr)) { foreach ($attr as $k => $v) { if ($k === 'namePerson/friendly') { $nick = notags(trim($v)); } if ($k === 'namePerson/first') { $first = notags(trim($v)); } if ($k === 'namePerson') { $name = notags(trim($v)); } if ($k === 'contact/email') { $addr = notags(trim($v)); } if ($k === 'media/image/aspect11') { $photosq = trim($v); } if ($k === 'media/image/default') { $photo_other = trim($v); } } } if (!$nick) { if ($first) { $nick = $first; } else { $nick = $name; } } require_once 'library/urlify/URLify.php'; $x = strtolower(\URLify::transliterate($nick)); if ($nick & $host) { $addr = $nick . '@' . $host; } $network = 'unknown'; if ($photosq) { $pphoto = $photosq; } elseif ($photo_other) { $pphoto = $photo_other; } $mimetype = guess_image_type($pphoto); $x = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_mimetype,\n\t xchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_follow, xchan_connpage, xchan_name, xchan_network, xchan_photo_date, \n\t\t\t\t\txchan_name_date, xchan_hidden)\n\t values ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', 1) ", dbesc($url), dbesc(''), dbesc(''), dbesc(''), dbesc($mimetype), dbesc($pphoto), dbesc($addr), dbesc($url), dbesc(''), dbesc(''), dbesc(''), dbesc($name), dbesc($network), dbesc(datetime_convert()), dbesc(datetime_convert())); if ($x) { $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($url)); if ($r) { $photos = import_xchan_photo($pphoto, $url); if ($photos) { $z = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', \n\t\t\t\t\t\t\t\txchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", dbesc(datetime_convert()), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($url)); } set_xconfig($url, 'system', 'openid', $authid); $_SESSION['authenticated'] = 1; $_SESSION['visitor_id'] = $r[0]['xchan_hash']; $_SESSION['my_url'] = $r[0]['xchan_url']; $_SESSION['my_address'] = $r[0]['xchan_addr']; $arr = array('xchan' => $r[0], 'session' => $_SESSION); call_hooks('magic_auth_openid_success', $arr); \App::set_observer($r[0]); info(sprintf(t('Welcome %s. Remote authentication successful.'), $r[0]['xchan_name'])); logger('mod_openid: remote auth success from ' . $r[0]['xchan_addr']); if ($_SESSION['return_url']) { goaway($_SESSION['return_url']); } goaway(z_root()); } } } } notice(t('Login failed.') . EOL); goaway(z_root()); // NOTREACHED }
function sync_apps($channel, $apps) { if ($channel && $apps) { foreach ($apps as $app) { $exists = false; $term = array_key_exists('term', $app) ? $app['term'] : null; $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['app_id']), intval($channel['channel_id'])); if ($x) { $exists = $x[0]; } if (array_key_exists('app_deleted', $app) && $app['app_deleted'] && $app['app_id']) { q("delete from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['app_id']), intval($channel['channel_id'])); if ($exists) { q("delete from term where otype = %d and oid = %d", intval(TERM_OBJ_APP), intval($exists['id'])); } continue; } unset($app['id']); unset($app['app_channel']); unset($app['term']); if ($exists) { q("delete from term where otype = %d and oid = %d", intval(TERM_OBJ_APP), intval($exists['id'])); } if (!$app['app_created'] || $app['app_created'] === NULL_DATE) { $app['app_created'] = datetime_convert(); } if (!$app['app_edited'] || $app['app_edited'] === NULL_DATE) { $app['app_edited'] = datetime_convert(); } $app['app_channel'] = $channel['channel_id']; if ($app['app_photo']) { $x = import_xchan_photo($app['app_photo'], $channel['channel_hash'], true); $app['app_photo'] = $x[0]; } if ($exists && $term) { foreach ($term as $t) { if (array_key_exists('type', $t)) { $t['ttype'] = $t['type']; } store_item_tag($channel['channel_id'], $exists['id'], TERM_OBJ_APP, $t['ttype'], escape_tags($t['term']), escape_tags($t['url'])); } } if ($exists) { if ($exists['app_edited'] >= $app['app_edited']) { continue; } } $hash = $app['app_id']; if ($exists) { unset($app['app_id']); foreach ($app as $k => $v) { $r = q("UPDATE app SET `%s` = '%s' WHERE app_id = '%s' AND app_channel = %d", dbesc($k), dbesc($v), dbesc($hash), intval($channel['channel_id'])); } } else { dbesc_array($app); $r = dbq("INSERT INTO app (`" . implode("`, `", array_keys($app)) . "`) VALUES ('" . implode("', '", array_values($app)) . "')"); if ($term) { $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", dbesc($hash), intval($channel['channel_id'])); if ($x) { foreach ($term as $t) { if (array_key_exists('type', $t)) { $t['ttype'] = $t['type']; } store_item_tag($channel['channel_id'], $x[0]['id'], TERM_OBJ_APP, $t['ttype'], escape_tags($t['term']), escape_tags($t['url'])); } } } } } } }
function import_author_unknown($x) { if (!$x['url']) { return false; } $r = q("select xchan_hash from xchan where xchan_network = 'unknown' and xchan_url = '%s' limit 1", dbesc($x['url'])); if ($r) { logger('import_author_unknown: in cache', LOGGER_DEBUG); return $r[0]['xchan_hash']; } $name = trim($x['name']); $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_url, xchan_name, xchan_network )\n\t\tvalues ( '%s', '%s', '%s', '%s', '%s' )", dbesc($x['url']), dbesc($x['url']), dbesc($x['url']), dbesc($name ? $name : t('(Unknown)')), dbesc('unknown')); if ($r && $x['photo']) { $photos = import_xchan_photo($x['photo']['src'], $x['url']); if ($photos) { /** @bug $arr is undefined in this SQL query */ $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_url = '%s' and xchan_network = 'unknown'", dbesc(datetime_convert('UTC', 'UTC', $arr['photo_updated'])), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($x['url'])); if ($r) { return $x['url']; } } } return false; }
function sync_apps($channel, $apps) { if ($channel && $apps) { foreach ($apps as $app) { if (array_key_exists('app_deleted', $app) && $app['app_deleted'] && $app['app_id']) { q("delete from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['app_id']), intval($channel['channel_id'])); continue; } unset($app['id']); unset($app['app_channel']); if (!$app['app_created'] || $app['app_created'] === NULL_DATE) { $app['app_created'] = datetime_convert(); } if (!$app['app_edited'] || $app['app_edited'] === NULL_DATE) { $app['app_edited'] = datetime_convert(); } $app['app_channel'] = $channel['channel_id']; if ($app['app_photo']) { $x = import_xchan_photo($app['app_photo'], $channel['channel_hash'], true); $app['app_photo'] = $x[0]; } $exists = false; $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['app_id']), intval($channel['channel_id'])); if ($x) { if ($x[0]['app_edited'] >= $app['app_edited']) { continue; } $exists = true; } $hash = $app['app_id']; if ($exists) { unset($app['app_id']); foreach ($app as $k => $v) { $r = q("UPDATE app SET `%s` = '%s' WHERE app_id = '%s' AND app_channel = %d", dbesc($k), dbesc($v), dbesc($hash), intval($channel['channel_id'])); } } else { dbesc_array($app); $r = dbq("INSERT INTO app (`" . implode("`, `", array_keys($app)) . "`) VALUES ('" . implode("', '", array_values($app)) . "')"); } } } }
function discover_by_webbie($webbie) { require_once 'library/HTML5/Parser.php'; $result = array(); $network = null; $diaspora = false; $gnusoc = false; $dfrn = false; $has_salmon = false; $salmon_key = false; $atom_feed = false; $diaspora_base = ''; $diaspora_guid = ''; $diaspora_key = ''; $webbie = strtolower($webbie); $x = webfinger_rfc7033($webbie, true); if ($x && array_key_exists('links', $x) && $x['links']) { foreach ($x['links'] as $link) { if (array_key_exists('rel', $link)) { // If we discover zot - don't search further; grab the info and get out of // here. if ($link['rel'] === PROTOCOL_ZOT) { logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG); if (array_key_exists('zot', $x) && $x['zot']['success']) { $i = import_xchan($x['zot']); } else { $z = z_fetch_url($link['href']); if ($z['success']) { $j = json_decode($z['body'], true); $i = import_xchan($j); return true; } } } if ($link['rel'] == NAMESPACE_DFRN) { $dfrn = $link['href']; } if ($link['rel'] == 'magic-public-key') { if (substr($link['href'], 0, 5) === 'data:') { $salmon_key = convert_salmon_key($link['href']); } } if ($link['rel'] == 'salmon') { $has_salmon = true; $salmon = $link['href']; } if ($link['rel'] == 'http://schemas.google.com/g/2010#updates-from') { $atom_feed = $link['href']; } } } } logger('webfinger: ' . print_r($x, true), LOGGER_DATA, LOG_INFO); $arr = array('address' => $webbie, 'success' => false, 'webfinger' => $x); call_hooks('discover_channel_webfinger', $arr); if ($arr['success']) { return true; } $aliases = array(); // Now let's make some decisions on what we may need // to obtain further info $probe_atom = false; $probe_old = false; $probe_hcard = false; $address = ''; $location = ''; $nickname = ''; $fullname = ''; $avatar = ''; $pubkey = ''; if (is_array($x)) { if (array_key_exists('address', $x)) { $address = $x['address']; } if (array_key_exists('location', $x)) { $location = $x['location']; } if (array_key_exists('nickname', $x)) { $nickname = $x['nickname']; } } if (!$x) { $probe_old = true; } if (!$dfrn && !$has_salmon) { $probe_old = true; } if ($probe_old) { $y = old_webfinger($webbie); if ($y) { logger('old_webfinger: ' . print_r($x, true)); foreach ($y as $link) { if ($link['@attributes']['rel'] === NAMESPACE_DFRN) { $dfrn = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'salmon') { $notify = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === NAMESPACE_FEED) { $poll = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') { $hcard = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { $profile = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0') { $poco = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') { $diaspora_base = unamp($link['@attributes']['href']); $diaspora = true; } if ($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') { $diaspora_guid = unamp($link['@attributes']['href']); $diaspora = true; } if ($link['@attributes']['rel'] === 'diaspora-public-key') { $diaspora_key = base64_decode(unamp($link['@attributes']['href'])); if (strstr($diaspora_key, 'RSA ')) { $pubkey = rsatopem($diaspora_key); } else { $pubkey = $diaspora_key; } $diaspora = true; } if ($link['@attributes']['rel'] == 'magic-public-key') { if (substr($link['@attributes']['href'], 0, 5) === 'data:') { $salmon_key = convert_salmon_key($link['@attributes']['href']); } } if ($link['@attributes']['rel'] == 'salmon') { $has_salmon = true; $salmon = $link['@attributes']['href']; } if ($link['@attributes']['rel'] == 'http://schemas.google.com/g/2010#updates-from') { $atom_feed = $link['@attributes']['href']; } if ($link['@attributes']['rel'] === 'alias') { $aliases[] = $link['@attributes']['href']; } if ($link['@attributes']['rel'] === 'subject') { $subject = $link['@attributes']['href']; } } } } if ($subject || $aliases) { if (strpos($webbie, '@')) { $rhs = substr($webbie, strpos($webbie, '@') + 1); } else { $m = parse_url($webbie); if ($m) { $rhs = $m['host'] . ($m['port'] ? ':' . $m['port'] : ''); } } $v = array('subject' => $subject, 'aliases' => $aliases); $address = find_webfinger_address($v, $rhs); $location = find_webfinger_location($v, $rhs); if ($address) { $nickname = substr($address, 0, strpos($address, '@')); } } if ($salmon_key && $has_salmon && $atom_feed && !$dfrn && !$diaspora) { $gnusoc = true; $probe_atom = true; } if (!$pubkey) { $pubkey = $salmon_key; } if (($dfrn || $diaspora) && $hcard) { $probe_hcard = true; } if (!$fullname) { $fullname = $nickname; } if ($probe_atom) { $k = z_fetch_url($atom_feed); if ($k['success']) { $feed_meta = feed_meta($k['body']); } if ($feed_meta) { // stash any discovered pubsubhubbub hubs in case we need to follow them // this will save an expensive lookup later if ($feed_meta['hubs'] && $address) { set_xconfig($address, 'system', 'push_hubs', $feed_meta['hubs']); set_xconfig($address, 'system', 'feed_url', $atom_feed); } if ($feed_meta['author']['author_name']) { $fullname = $feed_meta['author']['author_name']; } if (!$avatar) { if ($feed_meta['author']['author_photo']) { $avatar = $feed_meta['author']['author_photo']; } } // for GNU-social over-ride any url aliases we may have picked up in webfinger // The author.uri element in the feed is likely to be more accurate if ($gnusoc && $feed_meta['author']['author_uri']) { $location = $feed_meta['author']['author_uri']; } } } else { if ($probe_hcard) { $vcard = scrape_vcard($hcard); if ($vcard) { logger('vcard: ' . print_r($vcard, true), LOGGER_DATA); if ($vcard['fn']) { $fullname = $vcard['fn']; } if ($vcard['photo'] && strpos($vcard['photo'], 'http') !== 0) { $vcard['photo'] = $diaspora_base . '/' . $vcard['photo']; } if (!$avatar) { $avatar = $vcard['photo']; } } } } if ($profile && !$location) { $location = $profile; } if ($location) { $m = parse_url($location); $base = $m['scheme'] . '://' . $m['host']; $host = $m['host']; } if ($diaspora && $diaspora_base && $diaspora_guid) { if ($dfrn) { $network = 'friendica-over-diaspora'; } else { $network = 'diaspora'; } $base = trim($diaspora_base, '/'); $notify = $base . '/receive'; } else { if ($gnusoc) { $network = 'gnusoc'; $notify = $salmon; } } logger('network: ' . $network); logger('address: ' . $address); logger('fullname: ' . $fullname); logger('pubkey: ' . $pubkey); logger('location: ' . $location); // if we have everything we need, let's create the records if ($network && $address && $fullname && $pubkey && $location) { $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($address)); if ($r) { $r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s' limit 1", dbesc($fullname), dbesc($network), dbesc(datetime_convert()), dbesc($address)); } else { $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", dbesc($address), dbesc($diaspora_guid ? $diaspora_guid : $location), dbesc($pubkey), dbesc($address), dbesc($location), dbesc($fullname), dbesc($network), dbescdate(datetime_convert())); } $r = q("select * from hubloc where hubloc_hash = '%s' limit 1", dbesc($address)); if (!$r) { $r = q("insert into hubloc ( hubloc_guid, hubloc_hash, hubloc_addr, hubloc_network, hubloc_url, hubloc_host, hubloc_callback, hubloc_updated, hubloc_primary ) values ('%s','%s','%s','%s','%s','%s','%s','%s', 1)", dbesc($diaspora_guid ? $diaspora_guid : $location), dbesc($address), dbesc($address), dbesc($network), dbesc($base), dbesc($host), dbesc($notify), dbescdate(datetime_convert())); } $photos = import_xchan_photo($avatar, $address); $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", dbescdate(datetime_convert()), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($address)); return true; } return false; }
function diaspora_profile($importer, $xml, $msg) { $a = get_app(); $diaspora_handle = notags(diaspora_get_author($xml)); logger('xml: ' . print_r($xml, true), LOGGER_DEBUG); if ($diaspora_handle != $msg['author']) { logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.'); return 202; } $contact = diaspora_get_contact_by_handle($importer['channel_id'], $diaspora_handle); if (!$contact) { return; } if ($contact['blocked']) { logger('diaspora_profile: Ignoring this author.'); return 202; } $name = unxmlify($xml['first_name'] . ($xml['last_name'] ? ' ' . $xml['last_name'] : '')); $image_url = unxmlify($xml['image_url']); $birthday = unxmlify($xml['birthday']); $handle_parts = explode("@", $diaspora_handle); if ($name === '') { $name = $handle_parts[0]; } if (preg_match("|^https?://|", $image_url) === 0) { $image_url = "http://" . $handle_parts[1] . $image_url; } require_once 'include/photo/photo_driver.php'; $images = import_xchan_photo($image_url, $contact['xchan_hash']); // Generic birthday. We don't know the timezone. The year is irrelevant. $birthday = str_replace('1000', '1901', $birthday); $birthday = datetime_convert('UTC', 'UTC', $birthday, 'Y-m-d'); // this is to prevent multiple birthday notifications in a single year // if we already have a stored birthday and the 'm-d' part hasn't changed, preserve the entry, which will preserve the notify year if (substr($birthday, 5) === substr($contact['bd'], 5)) { $birthday = $contact['bd']; } $r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s' ", dbesc($name), dbesc(datetime_convert()), dbesc($images[0]), dbesc($images[1]), dbesc($images[2]), dbesc($images[3]), dbesc(datetime_convert()), intval($contact['xchan_hash'])); return; }
public static function app_update($arr) { $darray = array(); $ret = array('success' => false); $darray['app_url'] = x($arr, 'url') ? $arr['url'] : ''; $darray['app_channel'] = x($arr, 'uid') ? $arr['uid'] : 0; $darray['app_id'] = x($arr, 'guid') ? $arr['guid'] : 0; if (!$darray['app_url'] || !$darray['app_channel'] || !$darray['app_id']) { return $ret; } if ($arr['photo'] && !strstr($arr['photo'], z_root())) { $x = import_xchan_photo($arr['photo'], get_observer_hash(), true); $arr['photo'] = $x[1]; } $darray['app_sig'] = x($arr, 'sig') ? $arr['sig'] : ''; $darray['app_author'] = x($arr, 'author') ? $arr['author'] : get_observer_hash(); $darray['app_name'] = x($arr, 'name') ? escape_tags($arr['name']) : t('Unknown'); $darray['app_desc'] = x($arr, 'desc') ? escape_tags($arr['desc']) : ''; $darray['app_photo'] = x($arr, 'photo') ? $arr['photo'] : z_root() . '/' . get_default_profile_photo(80); $darray['app_version'] = x($arr, 'version') ? escape_tags($arr['version']) : ''; $darray['app_addr'] = x($arr, 'addr') ? escape_tags($arr['addr']) : ''; $darray['app_price'] = x($arr, 'price') ? escape_tags($arr['price']) : ''; $darray['app_page'] = x($arr, 'page') ? escape_tags($arr['page']) : ''; $darray['app_requires'] = x($arr, 'requires') ? escape_tags($arr['requires']) : ''; $darray['app_system'] = x($arr, 'system') ? intval($arr['system']) : 0; $darray['app_deleted'] = x($arr, 'deleted') ? intval($arr['deleted']) : 0; $edited = datetime_convert(); $r = q("update app set app_sig = '%s', app_author = '%s', app_name = '%s', app_desc = '%s', app_url = '%s', app_photo = '%s', app_version = '%s', app_addr = '%s', app_price = '%s', app_page = '%s', app_requires = '%s', app_edited = '%s', app_system = %d, app_deleted = %d where app_id = '%s' and app_channel = %d", dbesc($darray['app_sig']), dbesc($darray['app_author']), dbesc($darray['app_name']), dbesc($darray['app_desc']), dbesc($darray['app_url']), dbesc($darray['app_photo']), dbesc($darray['app_version']), dbesc($darray['app_addr']), dbesc($darray['app_price']), dbesc($darray['app_page']), dbesc($darray['app_requires']), dbesc($edited), intval($darray['app_system']), intval($darray['app_deleted']), dbesc($darray['app_id']), intval($darray['app_channel'])); if ($r) { $ret['success'] = true; $ret['app_id'] = $darray['app_id']; } $x = q("select id from app where app_id = '%s' and app_channel = %d limit 1", dbesc($darray['app_id']), intval($darray['app_channel'])); if ($x) { q("delete from term where otype = %d and oid = %d", intval(TERM_OBJ_APP), intval($x[0]['id'])); if ($arr['categories']) { $y = explode(',', $arr['categories']); if ($y) { foreach ($y as $t) { $t = trim($t); if ($t) { store_item_tag($darray['app_channel'], $x[0]['id'], TERM_OBJ_APP, TERM_CATEGORY, escape_tags($t), escape_tags(z_root() . '/apps/?f=&cat=' . escape_tags($t))); } } } } } return $ret; }
function xchan_store($arr) { logger('xchan_store: ' . print_r($arr, true)); if (!$arr['hash']) { $arr['hash'] = $arr['guid']; } if (!$arr['hash']) { return false; } $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($arr['hash'])); if ($r) { return true; } if (!$arr['network']) { $arr['network'] = 'unknown'; } if (!$arr['name']) { $arr['name'] = 'unknown'; } if (!$arr['url']) { $arr['url'] = z_root(); } if (!$arr['photo']) { $arr['photo'] = z_root() . '/' . get_default_profile_photo(); } $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_addr, xchan_url, xchan_connurl, xchan_follow, xchan_connpage, xchan_name, xchan_network, xchan_instance_url, xchan_hidden, xchan_orphan, xchan_censored, xchan_selfcensored, xchan_system, xchan_pubforum, xchan_deleted, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s','%s',%d, %d, %d, %d, %d, %d, %d, '%s') ", dbesc($arr['hash']), dbesc($arr['guid']), dbesc($arr['guid_sig']), dbesc($arr['pubkey']), dbesc($arr['address']), dbesc($arr['url']), dbesc($arr['connurl']), dbesc($arr['follow']), dbesc($arr['connpage']), dbesc($arr['name']), dbesc($arr['network']), dbesc($arr['instance_url']), intval($arr['hidden']), intval($arr['orphan']), intval($arr['censored']), intval($arr['selfcensored']), intval($arr['system']), intval($arr['pubforum']), intval($arr['deleted']), dbesc(datetime_convert())); if (!$r) { return $r; } $photos = import_xchan_photo($arr['photo'], $arr['hash']); $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", dbesc(datetime_convert()), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($arr['hash'])); return $r; }
function app_update($arr) { $darray = array(); $ret = array('success' => false); $darray['app_url'] = x($arr, 'url') ? $arr['url'] : ''; $darray['app_channel'] = x($arr, 'uid') ? $arr['uid'] : 0; $darray['app_id'] = x($arr, 'guid') ? $arr['guid'] : 0; if (!$darray['app_url'] || !$darray['app_channel'] || !$darray['app_id']) { return $ret; } if ($arr['photo'] && !strstr($arr['photo'], z_root())) { $x = import_xchan_photo($arr['photo'], get_observer_hash(), true); $arr['photo'] = $x[1]; } $darray['app_sig'] = x($arr, 'sig') ? $arr['sig'] : ''; $darray['app_author'] = x($arr, 'author') ? $arr['author'] : get_observer_hash(); $darray['app_name'] = x($arr, 'name') ? escape_tags($arr['name']) : t('Unknown'); $darray['app_desc'] = x($arr, 'desc') ? escape_tags($arr['desc']) : ''; $darray['app_photo'] = x($arr, 'photo') ? $arr['photo'] : z_root() . '/' . get_default_profile_photo(80); $darray['app_version'] = x($arr, 'version') ? escape_tags($arr['version']) : ''; $darray['app_addr'] = x($arr, 'addr') ? escape_tags($arr['addr']) : ''; $darray['app_price'] = x($arr, 'price') ? escape_tags($arr['price']) : ''; $darray['app_page'] = x($arr, 'page') ? escape_tags($arr['page']) : ''; $darray['app_requires'] = x($arr, 'requires') ? escape_tags($arr['requires']) : ''; $edited = datetime_convert(); $r = q("update app set app_sig = '%s', app_author = '%s', app_name = '%s', app_desc = '%s', app_url = '%s', app_photo = '%s', app_version = '%s', app_addr = '%s', app_price = '%s', app_page = '%s', app_requires = '%s', app_edited = '%s' where app_id = '%s' and app_channel = %d", dbesc($darray['app_sig']), dbesc($darray['app_author']), dbesc($darray['app_name']), dbesc($darray['app_desc']), dbesc($darray['app_url']), dbesc($darray['app_photo']), dbesc($darray['app_version']), dbesc($darray['app_addr']), dbesc($darray['app_price']), dbesc($darray['app_page']), dbesc($darray['app_requires']), dbesc($edited), dbesc($darray['app_id']), intval($darray['app_channel'])); if ($r) { $ret['success'] = true; $ret['app_id'] = $darray['app_id']; } return $ret; }
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; }