function crepair_post(&$a) { if (!local_user()) { return; } $cid = $a->argc > 1 ? intval($a->argv[1]) : 0; if ($cid) { $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($cid), intval(local_user())); } if (!count($r)) { return; } $contact = $r[0]; $nick = x($_POST, 'nick') ? $_POST['nick'] : ''; $url = x($_POST, 'url') ? $_POST['url'] : ''; $request = x($_POST, 'request') ? $_POST['request'] : ''; $confirm = x($_POST, 'confirm') ? $_POST['confirm'] : ''; $notify = x($_POST, 'notify') ? $_POST['notify'] : ''; $poll = x($_POST, 'poll') ? $_POST['poll'] : ''; $attag = x($_POST, 'attag') ? $_POST['attag'] : ''; $photo = x($_POST, 'photo') ? $_POST['photo'] : ''; $r = q("UPDATE `contact` SET `nick` = '%s', `url` = '%s', `request` = '%s', `confirm` = '%s', `notify` = '%s', `poll` = '%s', `attag` = '%s' \n\t\tWHERE `id` = %d AND `uid` = %d LIMIT 1", dbesc($nick), dbesc($url), dbesc($request), dbesc($confirm), dbesc($notify), dbesc($poll), dbesc($attag), intval($contact['id']), local_user()); if ($photo) { logger('mod-crepair: updating photo from ' . $photo); require_once "Photo.php"; $photos = import_profile_photo($photo, local_user(), $contact['id']); $x = q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t`thumb` = '%s',\n\t\t\t`micro` = '%s',\n\t\t\t`name-date` = '%s',\n\t\t\t`uri-date` = '%s',\n\t\t\t`avatar-date` = '%s'\n\t\t\tWHERE `id` = %d LIMIT 1\n\t\t\t", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact['id'])); } if ($r) { info(t('Contact settings applied.') . EOL); } else { notice(t('Contact update failed.') . EOL); } return; }
/** * @function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED) * 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. * * @returns array => 'success' (boolean true or false) * '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']); $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']); // Be careful - XCHAN_FLAGS_HIDDEN should evaluate to 1 if (($r[0]['xchan_flags'] & XCHAN_FLAGS_HIDDEN) != $hidden) { $new_flags = $r[0]['xchan_flags'] ^ XCHAN_FLAGS_HIDDEN; } else { $new_flags = $r[0]['xchan_flags']; } $adult = $r[0]['xchan_flags'] & XCHAN_FLAGS_SELFCENSORED ? true : false; $adult_changed = intval($adult) != intval($arr['adult_content']) ? true : false; if ($adult_changed) { $new_flags = $new_flags ^ XCHAN_FLAGS_SELFCENSORED; } $deleted = $r[0]['xchan_flags'] & XCHAN_FLAGS_DELETED ? true : false; $deleted_changed = intval($deleted) != intval($arr['deleted']) ? true : false; if ($deleted_changed) { $new_flags = $new_flags ^ XCHAN_FLAGS_DELETED; } if ($r[0]['xchan_name_date'] != $arr['name_updated'] || $r[0]['xchan_connurl'] != $arr['connections_url'] || $r[0]['xchan_flags'] != $new_flags || $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']) { $r = 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_flags = %d,\n\t\t\t\txchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s' limit 1", dbesc($arr['name']), dbesc($arr['name_updated']), dbesc($arr['connections_url']), dbesc($arr['follow_url']), dbesc($arr['connect_url']), intval($new_flags), dbesc($arr['address']), dbesc($arr['url']), dbesc($xchan_hash)); logger('import_xchan: existing: ' . print_r($r[0], true), LOGGER_DATA); logger('import_xchan: 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; } $hidden = 1 - intval($arr['searchable']); if ($hidden) { $new_flags = XCHAN_FLAGS_HIDDEN; } else { $new_flags = 0; } if ($arr['adult_content']) { $new_flags |= XCHAN_FLAGS_SELFCENSORED; } if (array_key_exists('deleted', $arr) && $arr['deleted']) { $new_flags |= XCHAN_FLAGS_DELETED; } $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_flags)\n\t\t\t\tvalues ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %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']), dbesc('zot'), dbesc($arr['photo_updated']), dbesc($arr['name_updated']), intval($new_flags)); $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']) { import_channel_photo($ph['body'], $arr['photo_mimetype'], $local[0]['channel_account_id'], $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_profile_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' limit 1", 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' limit 1", dbesc(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 if ($arr['locations']) { $xisting = q("select hubloc_id, hubloc_url, hubloc_sitekey from hubloc where hubloc_hash = '%s'", dbesc($xchan_hash)); // See if a primary is specified $has_primary = false; foreach ($arr['locations'] as $location) { if ($location['primary']) { $has_primary = true; break; } } foreach ($arr['locations'] as $location) { if (!rsa_verify($location['url'], base64url_decode($location['url_sig']), $arr['key'])) { logger('import_xchan: Unable to verify site signature for ' . $location['url']); $ret['message'] .= sprintf(t('Unable to verify site signature for %s'), $location['url']) . EOL; continue; } // Ensure that they have one primary hub if (!$has_primary) { $location['primary'] = true; } for ($x = 0; $x < count($xisting); $x++) { if ($xisting[$x]['hubloc_url'] === $location['url'] && $xisting[$x]['hubloc_sitekey'] === $location['sitekey']) { $xisting[$x]['updated'] = true; } } if (!$location['sitekey']) { logger('import_xchan: empty hubloc sitekey. ' . print_r($location, true)); continue; } // Catch some malformed entries from the past which still exist if (strpos($location['address'], '/') !== false) { $location['address'] = substr($location['address'], 0, strpos($location['address'], '/')); } // match as many fields as possible in case anything at all changed. $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ", dbesc($xchan_hash), dbesc($arr['guid']), dbesc($arr['guid_sig']), dbesc($location['url']), dbesc($location['url_sig']), dbesc($location['host']), dbesc($location['address']), dbesc($location['callback']), dbesc($location['sitekey'])); if ($r) { logger('import_xchan: hub exists: ' . $location['url']); // update connection timestamp if this is the site we're talking to if ($location['url'] == $arr['site']['url']) { q("update hubloc set hubloc_connected = '%s', hubloc_updated = '%s' where hubloc_id = %d limit 1", dbesc(datetime_convert()), dbesc(datetime_convert()), intval($r[0]['hubloc_id'])); } if ($r[0]['hubloc_status'] & HUBLOC_OFFLINE) { q("update hubloc set hubloc_status = (hubloc_status ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_OFFLINE), intval($r[0]['hubloc_id'])); if ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_ORPHANCHECK) { q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_ORPHANCHECK), intval($r[0]['hubloc_id'])); } q("update xchan set xchan_flags = (xchan_flags ^ %d) where (xchan_flags & %d) and xchan_hash = '%s' limit 1", intval(XCHAN_FLAGS_ORPHAN), intval(XCHAN_FLAGS_ORPHAN), dbesc($xchan_hash)); } // Remove pure duplicates if (count($r) > 1) { for ($h = 1; $h < count($r); $h++) { q("delete from hubloc where hubloc_id = %d limit 1", intval($r[$h]['hubloc_id'])); } } if ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY && !$location['primary'] || !($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) && $location['primary']) { $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_PRIMARY), dbesc(datetime_convert()), intval($r[0]['hubloc_id'])); $what = 'primary_hub '; $changed = true; } if ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED && !$location['deleted'] || !($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED) && $location['deleted']) { $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_DELETED), dbesc(datetime_convert()), intval($r[0]['hubloc_id'])); $what = 'delete_hub '; $changed = true; } continue; } // new hub claiming to be primary. Make it so. if (intval($location['primary'])) { $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_hash = '%s' and (hubloc_flags & %d )", intval(HUBLOC_FLAGS_PRIMARY), dbesc(datetime_convert()), dbesc($xchan_hash), intval(HUBLOC_FLAGS_PRIMARY)); } logger('import_xchan: new hub: ' . $location['url']); $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_network, hubloc_flags, hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_updated, hubloc_connected)\n\t\t\t\t\tvalues ( '%s','%s','%s','%s', '%s', %d ,'%s','%s','%s','%s','%s','%s','%s')", dbesc($arr['guid']), dbesc($arr['guid_sig']), dbesc($xchan_hash), dbesc($location['address']), dbesc('zot'), intval(intval($location['primary']) ? HUBLOC_FLAGS_PRIMARY : 0), dbesc($location['url']), dbesc($location['url_sig']), dbesc($location['host']), dbesc($location['callback']), dbesc($location['sitekey']), dbesc(datetime_convert()), dbesc(datetime_convert())); $what .= 'newhub '; $changed = true; } // get rid of any hubs we have for this channel which weren't reported. // This was needed at one time to resolve complicated cross-site inconsistencies, but can cause sync conflict. // currently disabled. // if($xisting) { // foreach($xisting as $x) { // if(! array_key_exists('updated',$x)) { // logger('import_xchan: removing unreferenced hub location ' . $x['hubloc_url']); // $r = q("delete from hubloc where hubloc_id = %d limit 1", // intval($x['hubloc_id']) // ); // $what .= 'removed_hub'; // $changed = true; // } // } // } } // 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' limit 1", dbesc($xchan_hash)); $r = q("delete from xtag where xtag_hash = '%s' limit 1", 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) ", 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 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_profile_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 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_profile_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']) : ''; $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' where app_id = '%s' and app_channel = %d limit 1", 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($darray['app_id']), intval($darray['app_channel'])); if ($r) { $ret['success'] = true; $ret['app_id'] = $darray['app_id']; } return $ret; }
function new_follower($importer, $contact, $datarray, $item, $sharing = false) { $url = notags(trim($datarray['author-link'])); $name = notags(trim($datarray['author-name'])); $photo = notags(trim($datarray['author-avatar'])); if (is_object($item)) { $rawtag = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor'); if ($rawtag && $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data']) { $nick = $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data']; } } else { $nick = $item; } if (is_array($contact)) { if ($contact['network'] == NETWORK_OSTATUS && $contact['rel'] == CONTACT_IS_SHARING || $sharing && $contact['rel'] == CONTACT_IS_FOLLOWER) { $r = q("UPDATE `contact` SET `rel` = %d, `writable` = 1 WHERE `id` = %d AND `uid` = %d", intval(CONTACT_IS_FRIEND), intval($contact['id']), intval($importer['uid'])); } // send email notification to owner? } else { // create contact record $r = q("INSERT INTO `contact` (`uid`, `created`, `url`, `nurl`, `name`, `nick`, `photo`, `network`, `rel`,\n\t\t\t`blocked`, `readonly`, `pending`, `writable`)\n\t\t\tVALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 0, 1, 1)", intval($importer['uid']), dbesc(datetime_convert()), dbesc($url), dbesc(normalise_link($url)), dbesc($name), dbesc($nick), dbesc($photo), dbesc($sharing ? NETWORK_ZOT : NETWORK_OSTATUS), intval($sharing ? CONTACT_IS_SHARING : CONTACT_IS_FOLLOWER)); $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `pending` = 1 LIMIT 1", intval($importer['uid']), dbesc($url)); if (count($r)) { $contact_record = $r[0]; $photos = import_profile_photo($photo, $importer["uid"], $contact_record["id"]); q("UPDATE `contact` SET `photo` = '%s', `thumb` = '%s', `micro` = '%s' WHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), intval($contact_record["id"])); } $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($importer['uid'])); $a = get_app(); if (count($r) and !in_array($r[0]['page-flags'], array(PAGE_SOAPBOX, PAGE_FREELOVE))) { // create notification $hash = random_string(); if (is_array($contact_record)) { $ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `hash`, `datetime`)\n\t\t\t\t\tVALUES ( %d, %d, 0, 0, '%s', '%s' )", intval($importer['uid']), intval($contact_record['id']), dbesc($hash), dbesc(datetime_convert())); } if (intval($r[0]['def_gid'])) { require_once 'include/group.php'; group_add_member($r[0]['uid'], '', $contact_record['id'], $r[0]['def_gid']); } if ($r[0]['notify-flags'] & NOTIFY_INTRO && in_array($r[0]['page-flags'], array(PAGE_NORMAL))) { notification(array('type' => NOTIFY_INTRO, 'notify_flags' => $r[0]['notify-flags'], 'language' => $r[0]['language'], 'to_name' => $r[0]['username'], 'to_email' => $r[0]['email'], 'uid' => $r[0]['uid'], 'link' => $a->get_baseurl() . '/notifications/intro', 'source_name' => strlen(stripslashes($contact_record['name'])) ? stripslashes($contact_record['name']) : t('[Name Withheld]'), 'source_link' => $contact_record['url'], 'source_photo' => $contact_record['photo'], 'verb' => $sharing ? ACTIVITY_FRIEND : ACTIVITY_FOLLOW, 'otype' => 'intro')); } } elseif (count($r) and in_array($r[0]['page-flags'], array(PAGE_SOAPBOX, PAGE_FREELOVE))) { $r = q("UPDATE `contact` SET `pending` = 0 WHERE `uid` = %d AND `url` = '%s' AND `pending` LIMIT 1", intval($importer['uid']), dbesc($url)); } } }
/** * @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']); // Be careful - XCHAN_FLAGS_HIDDEN should evaluate to 1 if (($r[0]['xchan_flags'] & XCHAN_FLAGS_HIDDEN) != $hidden) { $new_flags = $r[0]['xchan_flags'] ^ XCHAN_FLAGS_HIDDEN; } else { $new_flags = $r[0]['xchan_flags']; } $adult = $r[0]['xchan_flags'] & XCHAN_FLAGS_SELFCENSORED ? true : false; $adult_changed = intval($adult) != intval($arr['adult_content']) ? true : false; if ($adult_changed) { $new_flags = $new_flags ^ XCHAN_FLAGS_SELFCENSORED; } $deleted = $r[0]['xchan_flags'] & XCHAN_FLAGS_DELETED ? true : false; $deleted_changed = intval($deleted) != intval($arr['deleted']) ? true : false; if ($deleted_changed) { $new_flags = $new_flags ^ XCHAN_FLAGS_DELETED; } $public_forum = $r[0]['xchan_flags'] & XCHAN_FLAGS_PUBFORUM ? true : false; $pubforum_changed = intval($public_forum) != intval($arr['public_forum']) ? true : false; if ($pubforum_changed) { $new_flags = $r[0]['xchan_flags'] ^ XCHAN_FLAGS_PUBFORUM; } if ($r[0]['xchan_name_date'] != $arr['name_updated'] || $r[0]['xchan_connurl'] != $arr['connections_url'] || $r[0]['xchan_flags'] != $new_flags || $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']) { $r = 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_flags = %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($new_flags), dbesc($arr['address']), dbesc($arr['url']), dbesc($xchan_hash)); logger('import_xchan: existing: ' . print_r($r[0], true), LOGGER_DATA); logger('import_xchan: 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; } $hidden = 1 - intval($arr['searchable']); if ($hidden) { $new_flags = XCHAN_FLAGS_HIDDEN; } else { $new_flags = 0; } if ($arr['adult_content']) { $new_flags |= XCHAN_FLAGS_SELFCENSORED; } if (array_key_exists('deleted', $arr) && $arr['deleted']) { $new_flags |= XCHAN_FLAGS_DELETED; } $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_flags)\n\t\t\t\tvalues ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %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($new_flags)); $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']) { import_channel_photo($ph['body'], $arr['photo_mimetype'], $local[0]['channel_account_id'], $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_profile_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 pumpio_get_contact($uid, $contact) { $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1", dbesc(normalise_link($contact->url))); if (count($r) == 0) { q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')", dbesc(normalise_link($contact->url)), dbesc($contact->displayName), dbesc($contact->preferredUsername), dbesc($contact->image->url)); } else { q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'", dbesc($contact->displayName), dbesc($contact->preferredUsername), dbesc($contact->image->url), dbesc(normalise_link($contact->url))); } if (DB_UPDATE_VERSION >= "1177") { q("UPDATE `unique_contacts` SET `location` = '%s', `about` = '%s' WHERE url = '%s'", dbesc($contact->location->displayName), dbesc($contact->summary), dbesc(normalise_link($contact->url))); } $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", intval($uid), dbesc($contact->url)); if (!count($r)) { // create contact record q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,\n\t\t\t\t\t`name`, `nick`, `photo`, `network`, `rel`, `priority`,\n\t\t\t\t\t`writable`, `blocked`, `readonly`, `pending` )\n\t\t\t\tVALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ", intval($uid), dbesc(datetime_convert()), dbesc($contact->url), dbesc(normalise_link($contact->url)), dbesc(str_replace("acct:", "", $contact->id)), dbesc(''), dbesc($contact->id), dbesc('pump.io ' . $contact->id), dbesc($contact->displayName), dbesc($contact->preferredUsername), dbesc($contact->image->url), dbesc(NETWORK_PUMPIO), intval(CONTACT_IS_FRIEND), intval(1), intval(1)); $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", dbesc($contact->url), intval($uid)); if (!count($r)) { return false; } $contact_id = $r[0]['id']; $g = q("select def_gid from user where uid = %d limit 1", intval($uid)); if ($g && intval($g[0]['def_gid'])) { require_once 'include/group.php'; group_add_member($uid, '', $contact_id, $g[0]['def_gid']); } require_once "Photo.php"; $photos = import_profile_photo($contact->image->url, $uid, $contact_id); q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t\t\t`thumb` = '%s',\n\t\t\t\t\t`micro` = '%s',\n\t\t\t\t\t`name-date` = '%s',\n\t\t\t\t\t`uri-date` = '%s',\n\t\t\t\t\t`avatar-date` = '%s'\n\t\t\t\tWHERE `id` = %d\n\t\t\t", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact_id)); if (DB_UPDATE_VERSION >= "1177") { q("UPDATE `contact` SET `location` = '%s',\n\t\t\t\t\t\t`about` = '%s'\n\t\t\t\t\tWHERE `id` = %d", dbesc($contact->location->displayName), dbesc($contact->summary), intval($contact_id)); } } else { // update profile photos once every two weeks as we have no notification of when they change. //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -14 days')) ? true : false); $update_photo = $r[0]['avatar-date'] < datetime_convert('', '', 'now -12 hours'); // check that we have all the photos, this has been known to fail on occasion if (!$r[0]['photo'] || !$r[0]['thumb'] || !$r[0]['micro'] || $update_photo) { require_once "Photo.php"; $photos = import_profile_photo($contact->image->url, $uid, $r[0]['id']); q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t\t\t`thumb` = '%s',\n\t\t\t\t\t`micro` = '%s',\n\t\t\t\t\t`name-date` = '%s',\n\t\t\t\t\t`uri-date` = '%s',\n\t\t\t\t\t`avatar-date` = '%s',\n\t\t\t\t\t`name` = '%s',\n\t\t\t\t\t`nick` = '%s'\n\t\t\t\t\tWHERE `id` = %d\n\t\t\t\t", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($contact->displayName), dbesc($contact->preferredUsername), intval($r[0]['id'])); if (DB_UPDATE_VERSION >= "1177") { q("UPDATE `contact` SET `location` = '%s',\n\t\t\t\t\t\t\t`about` = '%s'\n\t\t\t\t\t\tWHERE `id` = %d", dbesc($contact->location->displayName), dbesc($contact->summary), intval($r[0]['id'])); } } } return $r[0]["id"]; }
function ostatus_fetchauthor($xpath, $context, $importer, &$contact) { $author = array(); $author["author-link"] = $xpath->evaluate('atom:author/atom:uri/text()', $context)->item(0)->nodeValue; $author["author-name"] = $xpath->evaluate('atom:author/atom:name/text()', $context)->item(0)->nodeValue; // Preserve the value $authorlink = $author["author-link"]; $alternate = $xpath->query("atom:author/atom:link[@rel='alternate']", $context)->item(0)->attributes; if (is_object($alternate)) { foreach ($alternate as $attributes) { if ($attributes->name == "href") { $author["author-link"] = $attributes->textContent; } } } $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` IN ('%s', '%s') AND `network` != '%s'", intval($importer["uid"]), dbesc(normalise_link($author["author-link"])), dbesc(normalise_link($authorlink)), dbesc(NETWORK_STATUSNET)); if ($r) { $contact = $r[0]; $author["contact-id"] = $r[0]["id"]; } else { $author["contact-id"] = $contact["id"]; } $avatarlist = array(); $avatars = $xpath->query("atom:author/atom:link[@rel='avatar']", $context); foreach ($avatars as $avatar) { $href = ""; $width = 0; foreach ($avatar->attributes as $attributes) { if ($attributes->name == "href") { $href = $attributes->textContent; } if ($attributes->name == "width") { $width = $attributes->textContent; } } if ($width > 0 and $href != "") { $avatarlist[$width] = $href; } } if (count($avatarlist) > 0) { krsort($avatarlist); $author["author-avatar"] = current($avatarlist); } $displayname = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue; if ($displayname != "") { $author["author-name"] = $displayname; } $author["owner-name"] = $author["author-name"]; $author["owner-link"] = $author["author-link"]; $author["owner-avatar"] = $author["author-avatar"]; if ($r) { // Update contact data $update_contact = $r[0]['name-date'] < datetime_convert('', '', 'now -12 hours'); if ($update_contact) { logger("Update contact data for contact " . $contact["id"], LOGGER_DEBUG); $value = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue; if ($value != "") { $contact["name"] = $value; } $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue; if ($value != "") { $contact["nick"] = $value; } $value = $xpath->evaluate('atom:author/poco:note/text()', $context)->item(0)->nodeValue; if ($value != "") { $contact["about"] = $value; } $value = $xpath->evaluate('atom:author/poco:address/poco:formatted/text()', $context)->item(0)->nodeValue; if ($value != "") { $contact["location"] = $value; } q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `about` = '%s', `location` = '%s', `name-date` = '%s' WHERE `id` = %d", dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["about"]), dbesc($contact["location"]), dbesc(datetime_convert()), intval($contact["id"])); poco_check($contact["url"], $contact["name"], $contact["network"], $author["author-avatar"], $contact["about"], $contact["location"], "", "", "", datetime_convert(), 2, $contact["id"], $contact["uid"]); } $update_photo = $r[0]['avatar-date'] < datetime_convert('', '', 'now -12 hours'); if ($update_photo and isset($author["author-avatar"])) { logger("Update profile picture for contact " . $contact["id"], LOGGER_DEBUG); $photos = import_profile_photo($author["author-avatar"], $importer["uid"], $contact["id"]); q("UPDATE `contact` SET `photo` = '%s', `thumb` = '%s', `micro` = '%s', `avatar-date` = '%s' WHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), intval($contact["id"])); } } return $author; }
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_restrict & %d ) = 0 ", dbesc(NULL_DATE), db_utcnow(), 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 < %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_restrict & %d ) > 0 and created <= %s and created > '%s' ", intval(ITEM_DELAYED_PUBLISH), db_utcnow(), dbesc(datetime_convert('UTC', 'UTC', 'now - 2 days'))); if ($r) { foreach ($r as $rr) { $x = q("update item set item_restrict = ( item_restrict & ~%d ) where id = %d", 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()); 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)); } /** * 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 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_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'", 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 abook_id, abook_flags, abook_updated, abook_connected, abook_closeness, abook_xchan, abook_channel, xchan_network\n\t\tFROM abook LEFT JOIN xchan on abook_xchan = xchan_hash LEFT JOIN account on abook_account = account_id\n\t\t{$sql_extra} \n\t\tAND (( abook_flags & %d ) > 0 OR ( abook_flags = %d )) \n\t\tAND (( account_flags = %d ) OR ( account_flags = %d )) {$abandon_sql} ORDER BY {$randfunc}", 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) { if ($contact['abook_flags'] & ABOOK_FLAG_SELF) { continue; } $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 ($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_flags = (abook_flags | %d) where abook_id = %d", 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 ($contact['abook_flags'] & (ABOOK_FLAG_PENDING | ABOOK_FLAG_ARCHIVED | ABOOK_FLAG_IGNORED)) { 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; }
/** * @param int $uid * @param object $contact */ function fb_get_friends_sync_parsecontact($uid, $contact) { $contact->link = 'http://facebook.com/profile.php?id=' . $contact->id; // If its a page then set the first name from the username if (!$contact->first_name and $contact->username) { $contact->first_name = $contact->username; } // check if we already have a contact $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", intval($uid), dbesc($contact->link)); if (count($r)) { // check that we have all the photos, this has been known to fail on occasion if (!$r[0]['photo'] || !$r[0]['thumb'] || !$r[0]['micro']) { require_once "Photo.php"; $photos = import_profile_photo('https://graph.facebook.com/' . $contact->id . '/picture', $uid, $r[0]['id']); q("UPDATE `contact` SET `photo` = '%s',\n `thumb` = '%s',\n `micro` = '%s',\n `name-date` = '%s',\n `uri-date` = '%s',\n `avatar-date` = '%s'\n WHERE `id` = %d LIMIT 1\n ", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($r[0]['id'])); } return; } else { // create contact record q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,\n `name`, `nick`, `photo`, `network`, `rel`, `priority`,\n `writable`, `blocked`, `readonly`, `pending` )\n VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ", intval($uid), dbesc(datetime_convert()), dbesc($contact->link), dbesc(normalise_link($contact->link)), dbesc(''), dbesc(''), dbesc($contact->id), dbesc('facebook ' . $contact->id), dbesc($contact->name), dbesc($contact->nickname ? $contact->nickname : strtolower($contact->first_name)), dbesc('https://graph.facebook.com/' . $contact->id . '/picture'), dbesc(NETWORK_FACEBOOK), intval(CONTACT_IS_FRIEND), intval(1), intval(1)); } $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", dbesc($contact->link), intval($uid)); if (!count($r)) { return; } $contact_id = $r[0]['id']; $g = q("select def_gid from user where uid = %d limit 1", intval($uid)); if ($g && intval($g[0]['def_gid'])) { require_once 'include/group.php'; group_add_member($uid, '', $contact_id, $g[0]['def_gid']); } require_once "Photo.php"; $photos = import_profile_photo($r[0]['photo'], $uid, $contact_id); q("UPDATE `contact` SET `photo` = '%s',\n `thumb` = '%s',\n `micro` = '%s',\n `name-date` = '%s',\n `uri-date` = '%s',\n `avatar-date` = '%s'\n WHERE `id` = %d LIMIT 1\n ", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact_id)); }
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_flags, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s','%s',%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['flags']), dbesc(datetime_convert())); if (!$r) { return $r; } $photos = import_profile_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 follow_init(&$a) { if (!local_user()) { notice(t('Permission denied.') . EOL); goaway($_SESSION['return_url']); // NOTREACHED } $url = $orig_url = notags(trim($_REQUEST['url'])); // remove ajax junk, e.g. Twitter $url = str_replace('/#!/', '/', $url); if (!allowed_url($url)) { notice(t('Disallowed profile URL.') . EOL); goaway($_SESSION['return_url']); // NOTREACHED } if (!$url) { notice(t('Connect URL missing.') . EOL); goaway($_SESSION['return_url']); // NOTREACHED } $ret = probe_url($url); if ($ret['network'] === NETWORK_DFRN) { if (strlen($a->path)) { $myaddr = bin2hex($a->get_baseurl() . '/profile/' . $a->user['nickname']); } else { $myaddr = bin2hex($a->user['nickname'] . '@' . $a->get_hostname()); } goaway($ret['request'] . "&addr={$myaddr}"); // NOTREACHED } else { if (get_config('system', 'dfrn_only')) { notice(t('This site is not configured to allow communications with other networks.') . EOL); notice(t('No compatible communication protocols or feeds were discovered.') . EOL); goaway($_SESSION['return_url']); } } // do we have enough information? if (!(x($ret, 'name') && x($ret, 'poll') && (x($ret, 'url') || x($ret, 'addr')))) { notice(t('The profile address specified does not provide adequate information.') . EOL); if (!x($ret, 'poll')) { notice(t('No compatible communication protocols or feeds were discovered.') . EOL); } if (!x($ret, 'name')) { notice(t('An author or name was not found.') . EOL); } if (!x($ret, 'url')) { notice(t('No browser URL could be matched to this address.') . EOL); } if (strpos($url, '@') !== false) { notice('Unable to match @-style Identity Address with a known protocol or email contact'); } goaway($_SESSION['return_url']); } if ($ret['network'] === NETWORK_OSTATUS && get_config('system', 'ostatus_disabled')) { notice(t('The profile address specified belongs to a network which has been disabled on this site.') . EOL); $ret['notify'] = ''; } if (!$ret['notify']) { notice(t('Limited profile. This person will be unable to receive direct/personal notifications from you.') . EOL); } $writeable = $ret['network'] === NETWORK_OSTATUS && $ret['notify'] ? 1 : 0; $hidden = $ret['network'] === NETWORK_MAIL ? 1 : 0; if ($ret['network'] === NETWORK_MAIL) { $writeable = 1; } if ($ret['network'] === NETWORK_DIASPORA) { $writeable = 1; } // check if we already have a contact // the poll url is more reliable than the profile url, as we may have // indirect links or webfinger links $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `poll` = '%s' LIMIT 1", intval(local_user()), dbesc($ret['poll'])); if (count($r)) { // update contact if ($r[0]['rel'] == CONTACT_IS_FOLLOWER || $network === NETWORK_DIASPORA && $r[0]['rel'] == CONTACT_IS_SHARING) { q("UPDATE `contact` SET `rel` = %d , `readonly` = 0 WHERE `id` = %d AND `uid` = %d LIMIT 1", intval(CONTACT_IS_FRIEND), intval($r[0]['id']), intval(local_user())); } } else { $new_relation = $ret['network'] === NETWORK_MAIL ? CONTACT_IS_FRIEND : CONTACT_IS_SHARING; if ($ret['network'] === NETWORK_DIASPORA) { $new_relation = CONTACT_IS_FOLLOWER; } // create contact record $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `batch`, `notify`, `poll`, `poco`, `name`, `nick`, `photo`, `network`, `pubkey`, `rel`, `priority`,\n\t\t\t`writable`, `hidden`, `blocked`, `readonly`, `pending` )\n\t\t\tVALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, 0, 0, 0 ) ", intval(local_user()), dbesc(datetime_convert()), dbesc($ret['url']), dbesc(normalise_link($ret['url'])), dbesc($ret['addr']), dbesc($ret['alias']), dbesc($ret['batch']), dbesc($ret['notify']), dbesc($ret['poll']), dbesc($ret['poco']), dbesc($ret['name']), dbesc($ret['nick']), dbesc($ret['photo']), dbesc($ret['network']), dbesc($ret['pubkey']), intval($new_relation), intval($ret['priority']), intval($writeable), intval($hidden)); } $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", dbesc($ret['url']), intval(local_user())); if (!count($r)) { notice(t('Unable to retrieve contact information.') . EOL); goaway($_SESSION['return_url']); // NOTREACHED } $contact = $r[0]; $contact_id = $r[0]['id']; require_once "Photo.php"; $photos = import_profile_photo($ret['photo'], local_user(), $contact_id); $r = q("UPDATE `contact` SET `photo` = '%s', \n\t\t\t`thumb` = '%s',\n\t\t\t`micro` = '%s', \n\t\t\t`name-date` = '%s', \n\t\t\t`uri-date` = '%s', \n\t\t\t`avatar-date` = '%s'\n\t\t\tWHERE `id` = %d LIMIT 1\n\t\t", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact_id)); // pull feed and consume it, which should subscribe to the hub. proc_run('php', "include/poller.php", "{$contact_id}"); // create a follow slap $tpl = get_markup_template('follow_slap.tpl'); $slap = replace_macros($tpl, array('$name' => $a->user['username'], '$profile_page' => $a->get_baseurl() . '/profile/' . $a->user['nickname'], '$photo' => $a->contact['photo'], '$thumb' => $a->contact['thumb'], '$published' => datetime_convert('UTC', 'UTC', 'now', ATOM_TIME), '$item_id' => 'urn:X-dfrn:' . $a->get_hostname() . ':follow:' . random_string(), '$title' => '', '$type' => 'text', '$content' => t('following'), '$nick' => $a->user['nickname'], '$verb' => ACTIVITY_FOLLOW, '$ostat_follow' => '')); $r = q("SELECT `contact`.*, `user`.* FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid` \n\t\t\tWHERE `user`.`uid` = %d AND `contact`.`self` = 1 LIMIT 1", intval(local_user())); if (count($r)) { if ($contact['network'] == NETWORK_OSTATUS && strlen($contact['notify'])) { require_once 'include/salmon.php'; slapper($r[0], $contact['notify'], $slap); } if ($contact['network'] == NETWORK_DIASPORA) { require_once 'include/diaspora.php'; $ret = diaspora_share($a->user, $contact); logger('mod_follow: diaspora_share returns: ' . $ret); } } if (strstr($_SESSION['return_url'], 'contacts')) { goaway($a->get_baseurl() . '/contacts/' . $contact_id); } goaway($_SESSION['return_url']); // NOTREACHED }
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; }
function _contact_update_profile($contact_id) { $r = q("SELECT `url`, `network` FROM `contact` WHERE `id` = %d", intval($contact_id)); if (!$r) { return; } $data = probe_url($r[0]["url"]); // "Feed" is mostly a sign of communication problems if ($data["network"] == NETWORK_FEED and $data["network"] != $r[0]["network"]) { return; } $updatefields = array("name", "nick", "url", "addr", "batch", "notify", "poll", "request", "confirm", "poco", "network", "alias", "pubkey"); $update = array(); foreach ($updatefields as $field) { if (isset($data[$field]) and $data[$field] != "") { $update[$field] = $data[$field]; } } $update["nurl"] = normalise_link($data["url"]); $query = ""; if (isset($data["priority"]) and $data["priority"] != 0) { $query = "`priority` = " . intval($data["priority"]); } foreach ($update as $key => $value) { if ($query != "") { $query .= ", "; } $query .= "`" . $key . "` = '" . dbesc($value) . "'"; } if ($query == "") { return; } $r = q("UPDATE `contact` SET {$query} WHERE `id` = %d AND `uid` = %d", intval($contact_id), intval(local_user())); $photos = import_profile_photo($data['photo'], local_user(), $contact_id); $r = q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t`thumb` = '%s',\n\t\t\t`micro` = '%s',\n\t\t\t`name-date` = '%s',\n\t\t\t`uri-date` = '%s',\n\t\t\t`avatar-date` = '%s'\n\t\t\tWHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact_id)); }
function thing_init(&$a) { if (!local_channel()) { return; } $account_id = $a->get_account(); $channel = $a->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; } if ($term_hash) { $t = q("select * from obj left join term on obj_obj = term_hash where term_hash != '' and obj_type = %d and term_hash = '%s' limit 1", intval(TERM_OBJ_THING), dbesc($term_hash)); if (!$t) { notice(t('Item not found.') . EOL); return; } $orig_record = $t[0]; if ($photo != $orig_record['imgurl']) { $arr = import_profile_photo($photo, get_observer_hash(), true); $local_photo = $arr[0]; $local_photo_type = $arr[3]; } else { $local_photo = $orig_record['imgurl']; } $r = q("update term set term = '%s', url = '%s', imgurl = '%s' where term_hash = '%s' and uid = %d", dbesc($name), dbesc($url ? $url : z_root() . '/thing/' . $term_hash), dbesc($local_photo), dbesc($term_hash), intval(local_channel())); info(t('Thing updated') . EOL); 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_profile_photo($photo, get_observer_hash(), true); $local_photo = $arr[0]; $local_photo_type = $arr[3]; } $r = q("select * from term where uid = %d and otype = %d and type = %d and term = '%s' limit 1", intval(local_channel()), intval(TERM_OBJ_THING), intval(TERM_THING), dbesc($name)); if (!$r) { $r = q("insert into term ( aid, uid, oid, otype, type, term, url, imgurl, term_hash )\n\t\t\tvalues( %d, %d, %d, %d, %d, '%s', '%s', '%s', '%s' ) ", intval($account_id), intval(local_channel()), 0, intval(TERM_OBJ_THING), intval(TERM_THING), dbesc($name), dbesc($url ? $url : z_root() . '/thing/' . $hash), dbesc($photo ? $local_photo : ''), dbesc($hash)); $r = q("select * from term where uid = %d and otype = %d and type = %d and term = '%s' limit 1", intval(local_channel()), intval(TERM_OBJ_THING), intval(TERM_THING), dbesc($name)); } $term = $r[0]; $r = q("insert into obj ( obj_page, obj_verb, obj_type, obj_channel, obj_obj) values ('%s','%s', %d, %d, '%s') ", dbesc($profile['profile_guid']), dbesc($verb), intval(TERM_OBJ_THING), intval(local_channel()), dbesc($term['term_hash'])); if (!$r) { notice(t('Object store: failed')); return; } info(t('Thing added')); if ($activity) { $arr = array(); $links = array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => $term['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' => $term['url'], 'link' => $links, 'title' => $term['term'], 'content' => $term['term'])); $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_flags'] = ITEM_ORIGIN | ITEM_WALL | ITEM_THREAD_TOP; $ulink = '[zrl=' . $channel['xchan_url'] . ']' . $channel['channel_name'] . '[/zrl]'; $plink = '[zrl=' . $term['url'] . ']' . $term['term'] . '[/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); } }
function diaspora_profile($importer, $xml, $msg) { $a = get_app(); $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); 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_post: Ignoring this author.'); return 202; } $name = unxmlify($xml->first_name) . (strlen($xml->last_name) ? ' ' . unxmlify($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_profile_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; }
function twitter_fetch_contact($uid, $contact, $create_user) { require_once "include/Photo.php"; if ($contact->id_str == "") { return -1; } $avatar = str_replace("_normal.", ".", $contact->profile_image_url_https); $info = get_photo_info($avatar); if (!$info) { $avatar = $contact->profile_image_url_https; } // Check if the unique contact is existing // To-Do: only update once a while $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1", dbesc(normalise_link("https://twitter.com/" . $contact->screen_name))); if (count($r) == 0) { q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')", dbesc(normalise_link("https://twitter.com/" . $contact->screen_name)), dbesc($contact->name), dbesc($contact->screen_name), dbesc($avatar)); } else { q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'", dbesc($contact->name), dbesc($contact->screen_name), dbesc($avatar), dbesc(normalise_link("https://twitter.com/" . $contact->screen_name))); } if (DB_UPDATE_VERSION >= "1177") { q("UPDATE `unique_contacts` SET `location` = '%s', `about` = '%s' WHERE url = '%s'", dbesc($contact->location), dbesc($contact->description), dbesc(normalise_link("https://twitter.com/" . $contact->screen_name))); } $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1", intval($uid), dbesc("twitter::" . $contact->id_str)); if (!count($r) and !$create_user) { return 0; } if (count($r) and ($r[0]["readonly"] or $r[0]["blocked"])) { logger("twitter_fetch_contact: Contact '" . $r[0]["nick"] . "' is blocked or readonly.", LOGGER_DEBUG); return -1; } if (!count($r)) { // create contact record q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,\n\t\t\t\t\t`name`, `nick`, `photo`, `network`, `rel`, `priority`,\n\t\t\t\t\t`writable`, `blocked`, `readonly`, `pending` )\n\t\t\t\t\tVALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0) ", intval($uid), dbesc(datetime_convert()), dbesc("https://twitter.com/" . $contact->screen_name), dbesc(normalise_link("https://twitter.com/" . $contact->screen_name)), dbesc($contact->screen_name . "@twitter.com"), dbesc("twitter::" . $contact->id_str), dbesc(''), dbesc("twitter::" . $contact->id_str), dbesc($contact->name), dbesc($contact->screen_name), dbesc($avatar), dbesc(NETWORK_TWITTER), intval(CONTACT_IS_FRIEND), intval(1), intval(1)); $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1", dbesc("twitter::" . $contact->id_str), intval($uid)); if (!count($r)) { return false; } $contact_id = $r[0]['id']; $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1", intval($uid)); if ($g && intval($g[0]['def_gid'])) { require_once 'include/group.php'; group_add_member($uid, '', $contact_id, $g[0]['def_gid']); } require_once "Photo.php"; $photos = import_profile_photo($avatar, $uid, $contact_id); q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t\t\t`thumb` = '%s',\n\t\t\t\t\t`micro` = '%s',\n\t\t\t\t\t`name-date` = '%s',\n\t\t\t\t\t`uri-date` = '%s',\n\t\t\t\t\t`avatar-date` = '%s'\n\t\t\t\tWHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact_id)); if (DB_UPDATE_VERSION >= "1177") { q("UPDATE `contact` SET `location` = '%s',\n\t\t\t\t\t\t`about` = '%s'\n\t\t\t\t\tWHERE `id` = %d", dbesc($contact->location), dbesc($contact->description), intval($contact_id)); } } else { // update profile photos once every two weeks as we have no notification of when they change. //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false); $update_photo = $r[0]['avatar-date'] < datetime_convert('', '', 'now -12 hours'); // check that we have all the photos, this has been known to fail on occasion if (!$r[0]['photo'] || !$r[0]['thumb'] || !$r[0]['micro'] || $update_photo) { logger("twitter_fetch_contact: Updating contact " . $contact->screen_name, LOGGER_DEBUG); require_once "Photo.php"; $photos = import_profile_photo($avatar, $uid, $r[0]['id']); q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t\t\t\t`thumb` = '%s',\n\t\t\t\t\t\t`micro` = '%s',\n\t\t\t\t\t\t`name-date` = '%s',\n\t\t\t\t\t\t`uri-date` = '%s',\n\t\t\t\t\t\t`avatar-date` = '%s',\n\t\t\t\t\t\t`url` = '%s',\n\t\t\t\t\t\t`nurl` = '%s',\n\t\t\t\t\t\t`addr` = '%s',\n\t\t\t\t\t\t`name` = '%s',\n\t\t\t\t\t\t`nick` = '%s'\n\t\t\t\t\tWHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc("https://twitter.com/" . $contact->screen_name), dbesc(normalise_link("https://twitter.com/" . $contact->screen_name)), dbesc($contact->screen_name . "@twitter.com"), dbesc($contact->name), dbesc($contact->screen_name), intval($r[0]['id'])); if (DB_UPDATE_VERSION >= "1177") { q("UPDATE `contact` SET `location` = '%s',\n\t\t\t\t\t\t\t`about` = '%s'\n\t\t\t\t\t\tWHERE `id` = %d", dbesc($contact->location), dbesc($contact->description), intval($r[0]['id'])); } } } return $r[0]["id"]; }
function diaspora_profile($importer, $xml) { $a = get_app(); $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); $contact = diaspora_get_contact_by_handle($importer['uid'], $diaspora_handle); if (!$contact) { return; } if ($contact['blocked']) { logger('diaspora_post: Ignoring this author.'); return 202; } $name = unxmlify($xml->first_name) . (strlen($xml->last_name) ? ' ' . unxmlify($xml->last_name) : ''); $image_url = unxmlify($xml->image_url); $birthday = unxmlify($xml->birthday); $r = q("SELECT DISTINCT ( `resource-id` ) FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' ", intval($importer['uid']), intval($contact['id'])); $oldphotos = count($r) ? $r : null; require_once 'include/Photo.php'; $images = import_profile_photo($image_url, $importer['uid'], $contact['id']); // 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 `contact` SET `name` = '%s', `name-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s', `avatar-date` = '%s' , `bd` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1", dbesc($name), dbesc(datetime_convert()), dbesc($images[0]), dbesc($images[1]), dbesc($images[2]), dbesc(datetime_convert()), dbesc($birthday), intval($contact['id']), intval($importer['uid'])); if ($r) { if ($oldphotos) { foreach ($oldphotos as $ph) { q("DELETE FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' AND `resource-id` = '%s' ", intval($importer['uid']), intval($contact['id']), dbesc($ph['resource-id'])); } } } return; }
function openid_content(&$a) { $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, 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); $a->set_observer($r[0]); require_once 'include/security.php'; $a->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 xchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_follow, xchan_connpage, xchan_name, xchan_network, xchan_photo_date, \n\t\t\t\txchan_name_date, xchan_flags)\n values ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d) ", 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()), intval(XCHAN_FLAGS_HIDDEN)); if ($x) { $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($url)); if ($r) { $photos = import_profile_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\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); $a->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 get_contact($url, $uid = 0) { require_once "include/Scrape.php"; $data = array(); $contactid = 0; // is it an address in the format user@server.tld? if (!strstr($url, "http") or strstr($url, "@")) { $data = probe_url($url); $url = $data["url"]; if ($url == "") { return 0; } } $contact = q("SELECT `id`, `avatar-date` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d", dbesc(normalise_link($url)), intval($uid)); if (!$contact) { $contact = q("SELECT `id`, `avatar-date` FROM `contact` WHERE `alias` IN ('%s', '%s') AND `uid` = %d", dbesc($url), dbesc(normalise_link($url)), intval($uid)); } if ($contact) { $contactid = $contact[0]["id"]; // Update the contact every 7 days $update_photo = $contact[0]['avatar-date'] < datetime_convert('', '', 'now -7 days'); //$update_photo = ($contact[0]['avatar-date'] < datetime_convert('','','now -12 hours')); if (!$update_photo) { return $contactid; } } if (!count($data)) { $data = probe_url($url); } // Does this address belongs to a valid network? if (!in_array($data["network"], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA))) { return 0; } // tempory programming. Can be deleted after 2015-02-07 if ($data["alias"] == "" and normalise_link($data["url"]) != normalise_link($url)) { $data["alias"] = normalise_link($url); } if ($contactid == 0) { q("INSERT INTO `contact` (`uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,\n\t\t\t\t\t`name`, `nick`, `photo`, `network`, `pubkey`, `rel`, `priority`,\n\t\t\t\t\t`batch`, `request`, `confirm`, `poco`,\n\t\t\t\t\t`writable`, `blocked`, `readonly`, `pending`)\n\t\t\t\t\tVALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', 1, 0, 0, 0)", intval($uid), dbesc(datetime_convert()), dbesc($data["url"]), dbesc(normalise_link($data["url"])), dbesc($data["addr"]), dbesc($data["alias"]), dbesc($data["notify"]), dbesc($data["poll"]), dbesc($data["name"]), dbesc($data["nick"]), dbesc($data["photo"]), dbesc($data["network"]), dbesc($data["pubkey"]), intval(CONTACT_IS_SHARING), intval($data["priority"]), dbesc($data["batch"]), dbesc($data["request"]), dbesc($data["confirm"]), dbesc($data["poco"])); $contact = q("SELECT `id` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d", dbesc(normalise_link($data["url"])), intval($uid)); if (!$contact) { return 0; } $contactid = $contact[0]["id"]; } require_once "Photo.php"; $photos = import_profile_photo($data["photo"], $uid, $contactid); q("UPDATE `contact` SET `photo` = '%s', `thumb` = '%s', `micro` = '%s',\n\t\t`addr` = '%s', `alias` = '%s', `name` = '%s', `nick` = '%s',\n\t\t`name-date` = '%s', `uri-date` = '%s', `avatar-date` = '%s' WHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($data["addr"]), dbesc($data["alias"]), dbesc($data["name"]), dbesc($data["nick"]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contactid)); return $contactid; }
function import_post(&$a) { $account_id = get_account_id(); if (!$account_id) { 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']; 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; } 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 ($data['compatibility']['project'] !== PLATFORM_NAME) { notice(t('The data provided is not compatible with this project.')); return; } } if ($v2 > $v1) { $t = sprintf(t('Warning: Database versions differ by %1$d updates.'), $v2 - $v1); notice($t); } // import channel $channel = $data['channel']; $r = q("select * from channel where (channel_guid = '%s' or channel_hash = '%s' or channel_address = '%s' ) limit 1", dbesc($channel['channel_guid']), dbesc($channel['channel_hash']), dbesc($channel['channel_address'])); // We should probably also verify the hash if ($r) { if ($r[0]['channel_guid'] === $channel['channel_guid'] || $r[0]['channel_hash'] === $channel['channel_hash']) { logger('mod_import: duplicate channel. ', print_r($channel, true)); notice(t('Cannot create a duplicate channel identifier on this system. Import failed.') . EOL); return; } else { // try at most ten times to generate a unique address. $x = 0; $found_unique = false; do { $tmp = $channel['channel_address'] . mt_rand(1000, 9999); $r = q("select * from channel where channel_address = '%s' limit 1", dbesc($tmp)); if (!$r) { $channel['channel_address'] = $tmp; $found_unique = true; break; } $x++; } while ($x < 10); if (!$found_unique) { logger('mod_import: duplicate channel. randomisation failed.', print_r($channel, true)); notice(t('Unable to create a unique channel address. Import failed.') . EOL); return; } } } unset($channel['channel_id']); $channel['channel_account_id'] = get_account_id(); $channel['channel_primary'] = $seize ? 1 : 0; dbesc_array($channel); $r = dbq("INSERT INTO channel (`" . implode("`, `", array_keys($channel)) . "`) VALUES ('" . implode("', '", array_values($channel)) . "')"); if (!$r) { logger('mod_import: channel clone failed. ', print_r($channel, true)); notice(t('Channel clone failed. Import failed.') . EOL); return; } $r = q("select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", intval(get_account_id()), $channel['channel_guid']); if (!$r) { logger('mod_import: channel not found. ', print_r($channel, true)); notice(t('Cloned channel not found. Import failed.') . EOL); return; } // reset $channel = $r[0]; set_default_login_identity(get_account_id(), $channel['channel_id'], false); if ($data['photo']) { require_once 'include/photo/photo_driver.php'; import_channel_photo(base64url_decode($data['photo']['data']), $data['photo']['type'], get_account_id(), $channel['channel_id']); } $profiles = $data['profile']; if ($profiles) { foreach ($profiles as $profile) { unset($profile['id']); $profile['aid'] = get_account_id(); $profile['uid'] = $channel['channel_id']; // we are going to reset all profile photos to the original // somebody will have to fix this later and put all the applicable photos into the export $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; dbesc_array($profile); $r = dbq("INSERT INTO profile (`" . implode("`, `", array_keys($profile)) . "`) VALUES ('" . implode("', '", array_values($profile)) . "')"); } } $hublocs = $data['hubloc']; if ($hublocs) { foreach ($hublocs as $hubloc) { $arr = array('guid' => $hubloc['hubloc_guid'], 'guid_sig' => $hubloc['guid_sig'], 'url' => $hubloc['hubloc_url'], 'url_sig' => $hubloc['hubloc_url_sig']); if ($hubloc['hubloc_hash'] === $channel['channel_hash'] && $hubloc['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY && $seize) { $hubloc['hubloc_flags'] = $hubloc['hubloc_flags'] ^ HUBLOC_FLAGS_PRIMARY; } if (!zot_gethub($arr)) { unset($hubloc['hubloc_id']); dbesc_array($hubloc); $r = dbq("INSERT INTO hubloc (`" . implode("`, `", array_keys($hubloc)) . "`) VALUES ('" . implode("', '", array_values($hubloc)) . "')"); } } } // 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_flags, \n\t\thubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey )\n\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['channel_address'] . '@' . get_app()->get_hostname()), dbesc('zot'), intval($seize ? HUBLOC_FLAGS_PRIMARY : 0), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $channel['channel_prvkey']))), dbesc(get_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_flags = (hubloc_flags & ~%d) where (hubloc_flags & %d)>0 and hubloc_hash = '%s' and hubloc_url != '%s' ", intval(HUBLOC_FLAGS_PRIMARY), intval(HUBLOC_FLAGS_PRIMARY), dbesc($channel['channel_hash']), dbesc(z_root())); } // 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 ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", dbesc($channel['channel_hash']), dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_pubkey']), dbesc($a->get_baseurl() . "/photo/profile/l/" . $channel['channel_id']), dbesc($a->get_baseurl() . "/photo/profile/m/" . $channel['channel_id']), dbesc($a->get_baseurl() . "/photo/profile/s/" . $channel['channel_id']), dbesc($channel['channel_address'] . '@' . get_app()->get_hostname()), 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())); } $xchans = $data['xchan']; if ($xchans) { foreach ($xchans as $xchan) { $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_profile_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\twhere xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($photodate), dbesc($xchan['xchan_hash'])); } } // FIXME - ensure we have an xchan if somebody is trying to pull a fast one $friends = 0; $feeds = 0; // import contacts $abooks = $data['abook']; if ($abooks) { foreach ($abooks as $abook) { if ($max_friends !== false && $friends > $max_friends) { continue; } if ($max_feeds !== false && $abook['abook_flags'] & ABOOK_FLAG_FEED && $feeds > $max_feeds) { continue; } unset($abook['abook_id']); $abook['abook_account'] = get_account_id(); $abook['abook_channel'] = $channel['channel_id']; dbesc_array($abook); $r = dbq("INSERT INTO abook (`" . implode("`, `", array_keys($abook)) . "`) VALUES ('" . implode("', '", array_values($abook)) . "')"); $friends++; if ($abook['abook_flags'] & ABOOK_FLAG_FEED) { $feeds++; } } } $configs = $data['config']; if ($configs) { foreach ($configs as $config) { unset($config['id']); $config['uid'] = $channel['channel_id']; dbesc_array($config); $r = dbq("INSERT INTO pconfig (`" . implode("`, `", array_keys($config)) . "`) VALUES ('" . implode("', '", array_values($config)) . "')"); } } $groups = $data['group']; if ($groups) { $saved = array(); foreach ($groups as $group) { $saved[$group['hash']] = array('old' => $group['id']); 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)) . "')"); } } $saved_notification_flags = notifications_off($channel['channel_id']); if ($import_posts && array_key_exists('item', $data) && $data['item']) { foreach ($data['item'] as $i) { $item = get_item_elements($i); $r = q("select id, edited from item where mid = '%s' and uid = %d limit 1", dbesc($item['mid']), intval($channel['channel_id'])); if ($r) { if ($item['edited'] > $r[0]['edited']) { $item['id'] = $r[0]['id']; $item['uid'] = $channel['channel_id']; item_store_update($item); continue; } } else { $item['aid'] = $channel['channel_account_id']; $item['uid'] = $channel['channel_id']; $item_result = item_store($item); } } } notifications_on($channel['channel_id'], $saved_notification_flags); if (array_key_exists('item_id', $data) && $data['item_id']) { foreach ($data['item_id'] as $i) { $r = q("select id from item where mid = '%s' and uid = %d limit 1", dbesc($i['mid']), intval($channel['channel_id'])); if (!$r) { continue; } $z = q("select * from item_id where service = '%s' and sid = '%s' and iid = %d and uid = %d limit 1", dbesc($i['service']), dbesc($i['sid']), intval($r[0]['id']), intval($channel['channel_id'])); if (!$z) { q("insert into item_id (iid,uid,sid,service) values(%d,%d,'%s','%s')", intval($r[0]['id']), intval($channel['channel_id']), dbesc($i['sid']), dbesc($i['service'])); } } } // 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. proc_run('php', 'include/notifier.php', 'location', $channel['channel_id']); // This will indirectly perform a refresh_all *and* update the directory proc_run('php', 'include/directory.php', $channel['channel_id']); notice(t('Import completed.') . EOL); change_channel($channel['channel_id']); goaway(z_root() . '/network'); }
function discover_by_webbie($webbie) { require_once 'library/HTML5/Parser.php'; $webbie = strtolower($webbie); $x = webfinger_rfc7033($webbie); if ($x && array_key_exists('links', $x) && $x['links']) { foreach ($x['links'] as $link) { if (array_key_exists('rel', $link) && $link['rel'] == 'http://purl.org/zot/protocol') { logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG); $z = z_fetch_url($link['href']); if ($z['success']) { $j = json_decode($z['body'], true); $i = import_xchan($j); return true; } } } } $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, '@')); } $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($webbie)); if (!$r) { $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_instance_url, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", dbesc($addr), dbesc($guid), dbesc($pubkey), dbesc($addr), dbesc($profile), dbesc($vcard['fn']), dbesc($network), dbesc(z_root()), 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_flags ) values ('%s','%s','%s','%s','%s','%s','%s','%s', %d)", dbesc($guid), dbesc($addr), dbesc($addr), dbesc($network), dbesc(trim($diaspora_base, '/')), dbesc($hostname), dbesc($notify), dbescdate(datetime_convert()), intval(HUBLOC_FLAGS_PRIMARY)); } $photos = import_profile_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 dfrn_confirm_post(&$a, $handsfree = null) { if (is_array($handsfree)) { /** * We were called directly from dfrn_request due to automatic friend acceptance. * Any $_POST parameters we may require are supplied in the $handsfree array. * */ $node = $handsfree['node']; $a->interactive = false; // notice() becomes a no-op since nobody is there to see it } else { if ($a->argc > 1) { $node = $a->argv[1]; } } /** * * Main entry point. Scenario 1. Our user received a friend request notification (perhaps * from another site) and clicked 'Approve'. * $POST['source_url'] is not set. If it is, it indicates Scenario 2. * * We may also have been called directly from dfrn_request ($handsfree != null) due to * this being a page type which supports automatic friend acceptance. That is also Scenario 1 * since we are operating on behalf of our registered user to approve a friendship. * */ if (!x($_POST, 'source_url')) { $uid = is_array($handsfree) ? $handsfree['uid'] : local_user(); if (!$uid) { notice(t('Permission denied.') . EOL); return; } $user = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($uid)); if (!$user) { notice(t('Profile not found.') . EOL); return; } // These data elements may come from either the friend request notification form or $handsfree array. if (is_array($handsfree)) { logger('Confirm in handsfree mode'); $dfrn_id = $handsfree['dfrn_id']; $intro_id = $handsfree['intro_id']; $duplex = $handsfree['duplex']; $hidden = array_key_exists('hidden', $handsfree) ? intval($handsfree['hidden']) : 0; $activity = array_key_exists('activity', $handsfree) ? intval($handsfree['activity']) : 0; } else { $dfrn_id = x($_POST, 'dfrn_id') ? notags(trim($_POST['dfrn_id'])) : ""; $intro_id = x($_POST, 'intro_id') ? intval($_POST['intro_id']) : 0; $duplex = x($_POST, 'duplex') ? intval($_POST['duplex']) : 0; $cid = x($_POST, 'contact_id') ? intval($_POST['contact_id']) : 0; $hidden = x($_POST, 'hidden') ? intval($_POST['hidden']) : 0; $activity = x($_POST, 'activity') ? intval($_POST['activity']) : 0; } /** * * Ensure that dfrn_id has precedence when we go to find the contact record. * We only want to search based on contact id if there is no dfrn_id, * e.g. for OStatus network followers. * */ if (strlen($dfrn_id)) { $cid = 0; } logger('Confirming request for dfrn_id (issued) ' . $dfrn_id); if ($cid) { logger('Confirming follower with contact_id: ' . $cid); } /** * * The other person will have been issued an ID when they first requested friendship. * Locate their record. At this time, their record will have both pending and blocked set to 1. * There won't be any dfrn_id if this is a network follower, so use the contact_id instead. * */ $r = q("SELECT * FROM `contact` WHERE ( ( `issued-id` != '' AND `issued-id` = '%s' ) OR ( `id` = %d AND `id` != 0 ) ) AND `uid` = %d AND `duplex` = 0 LIMIT 1", dbesc($dfrn_id), intval($cid), intval($uid)); if (!count($r)) { logger('Contact not found in DB.'); notice(t('Contact not found.') . EOL); notice(t('This may occasionally happen if contact was requested by both persons and it has already been approved.') . EOL); return; } $contact = $r[0]; $contact_id = $contact['id']; $relation = $contact['rel']; $site_pubkey = $contact['site-pubkey']; $dfrn_confirm = $contact['confirm']; $aes_allow = $contact['aes_allow']; $network = strlen($contact['issued-id']) ? NETWORK_DFRN : NETWORK_OSTATUS; if ($contact['network']) { $network = $contact['network']; } if ($network === NETWORK_DFRN) { /** * * Generate a key pair for all further communications with this person. * We have a keypair for every contact, and a site key for unknown people. * This provides a means to carry on relationships with other people if * any single key is compromised. It is a robust key. We're much more * worried about key leakage than anybody cracking it. * */ require_once 'include/crypto.php'; $res = new_keypair(4096); $private_key = $res['prvkey']; $public_key = $res['pubkey']; // Save the private key. Send them the public key. $r = q("UPDATE `contact` SET `prvkey` = '%s' WHERE `id` = %d AND `uid` = %d", dbesc($private_key), intval($contact_id), intval($uid)); $params = array(); /** * * Per the DFRN protocol, we will verify both ends by encrypting the dfrn_id with our * site private key (person on the other end can decrypt it with our site public key). * Then encrypt our profile URL with the other person's site public key. They can decrypt * it with their site private key. If the decryption on the other end fails for either * item, it indicates tampering or key failure on at least one site and we will not be * able to provide a secure communication pathway. * * If other site is willing to accept full encryption, (aes_allow is 1 AND we have php5.3 * or later) then we encrypt the personal public key we send them using AES-256-CBC and a * random key which is encrypted with their site public key. * */ $src_aes_key = random_string(); $result = ''; openssl_private_encrypt($dfrn_id, $result, $user[0]['prvkey']); $params['dfrn_id'] = bin2hex($result); $params['public_key'] = $public_key; $my_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname']; openssl_public_encrypt($my_url, $params['source_url'], $site_pubkey); $params['source_url'] = bin2hex($params['source_url']); if ($aes_allow && function_exists('openssl_encrypt')) { openssl_public_encrypt($src_aes_key, $params['aes_key'], $site_pubkey); $params['aes_key'] = bin2hex($params['aes_key']); $params['public_key'] = bin2hex(openssl_encrypt($public_key, 'AES-256-CBC', $src_aes_key)); } $params['dfrn_version'] = DFRN_PROTOCOL_VERSION; if ($duplex == 1) { $params['duplex'] = 1; } if ($user[0]['page-flags'] == PAGE_COMMUNITY) { $params['page'] = 1; } if ($user[0]['page-flags'] == PAGE_PRVGROUP) { $params['page'] = 2; } logger('Confirm: posting data to ' . $dfrn_confirm . ': ' . print_r($params, true), LOGGER_DATA); /** * * POST all this stuff to the other site. * Temporarily raise the network timeout to 120 seconds because the default 60 * doesn't always give the other side quite enough time to decrypt everything. * */ $a->config['system']['curl_timeout'] = 120; $res = post_url($dfrn_confirm, $params); logger(' Confirm: received data: ' . $res, LOGGER_DATA); // Now figure out what they responded. Try to be robust if the remote site is // having difficulty and throwing up errors of some kind. $leading_junk = substr($res, 0, strpos($res, '<?xml')); $res = substr($res, strpos($res, '<?xml')); if (!strlen($res)) { // No XML at all, this exchange is messed up really bad. // We shouldn't proceed, because the xml parser might choke, // and $status is going to be zero, which indicates success. // We can hardly call this a success. notice(t('Response from remote site was not understood.') . EOL); return; } if (strlen($leading_junk) && get_config('system', 'debugging')) { // This might be more common. Mixed error text and some XML. // If we're configured for debugging, show the text. Proceed in either case. notice(t('Unexpected response from remote site: ') . EOL . $leading_junk . EOL); } if (stristr($res, "<status") === false) { // wrong xml! stop here! notice(t('Unexpected response from remote site: ') . EOL . htmlspecialchars($res) . EOL); return; } $xml = parse_xml_string($res); $status = (int) $xml->status; $message = unxmlify($xml->message); // human readable text of what may have gone wrong. switch ($status) { case 0: info(t("Confirmation completed successfully.") . EOL); if (strlen($message)) { notice(t('Remote site reported: ') . $message . EOL); } break; case 1: // birthday paradox - generate new dfrn-id and fall through. $new_dfrn_id = random_string(); $r = q("UPDATE contact SET `issued-id` = '%s' WHERE `id` = %d AND `uid` = %d", dbesc($new_dfrn_id), intval($contact_id), intval($uid)); case 2: notice(t("Temporary failure. Please wait and try again.") . EOL); if (strlen($message)) { notice(t('Remote site reported: ') . $message . EOL); } break; case 3: notice(t("Introduction failed or was revoked.") . EOL); if (strlen($message)) { notice(t('Remote site reported: ') . $message . EOL); } break; } if ($status == 0 && $intro_id) { // Success. Delete the notification. $r = q("DELETE FROM `intro` WHERE `id` = %d AND `uid` = %d", intval($intro_id), intval($uid)); } if ($status != 0) { return; } } /* * * We have now established a relationship with the other site. * Let's make our own personal copy of their profile photo so we don't have * to always load it from their site. * * We will also update the contact record with the nature and scope of the relationship. * */ require_once 'include/Photo.php'; $photos = import_profile_photo($contact['photo'], $uid, $contact_id); logger('dfrn_confirm: confirm - imported photos'); if ($network === NETWORK_DFRN) { $new_relation = CONTACT_IS_FOLLOWER; if ($relation == CONTACT_IS_SHARING || $duplex) { $new_relation = CONTACT_IS_FRIEND; } if ($relation == CONTACT_IS_SHARING && $duplex) { $duplex = 0; } $r = q("UPDATE `contact` SET\n\t\t\t\t`photo` = '%s',\n\t\t\t\t`thumb` = '%s',\n\t\t\t\t`micro` = '%s',\n\t\t\t\t`rel` = %d,\n\t\t\t\t`name-date` = '%s',\n\t\t\t\t`uri-date` = '%s',\n\t\t\t\t`avatar-date` = '%s',\n\t\t\t\t`blocked` = 0,\n\t\t\t\t`pending` = 0,\n\t\t\t\t`duplex` = %d,\n\t\t\t\t`hidden` = %d,\n\t\t\t\t`network` = '%s' WHERE `id` = %d\n\t\t\t", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), intval($new_relation), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($duplex), intval($hidden), dbesc(NETWORK_DFRN), intval($contact_id)); } else { // $network !== NETWORK_DFRN $network = $contact['network'] ? $contact['network'] : NETWORK_OSTATUS; $notify = $contact['notify'] ? $contact['notify'] : ''; $poll = $contact['poll'] ? $contact['poll'] : ''; if (!$contact['notify'] || !$contact['poll']) { $arr = lrdd($contact['url']); if (count($arr)) { foreach ($arr as $link) { if ($link['@attributes']['rel'] === 'salmon') { $notify = $link['@attributes']['href']; } if ($link['@attributes']['rel'] === NAMESPACE_FEED) { $poll = $link['@attributes']['href']; } } } } $new_relation = $contact['rel']; $writable = $contact['writable']; if ($network === NETWORK_DIASPORA) { if ($duplex) { $new_relation = CONTACT_IS_FRIEND; } else { $new_relation = CONTACT_IS_FOLLOWER; } if ($new_relation != CONTACT_IS_FOLLOWER) { $writable = 1; } } $r = q("DELETE FROM `intro` WHERE `id` = %d AND `uid` = %d", intval($intro_id), intval($uid)); $r = q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t\t`thumb` = '%s',\n\t\t\t\t`micro` = '%s',\n\t\t\t\t`name-date` = '%s',\n\t\t\t\t`uri-date` = '%s',\n\t\t\t\t`avatar-date` = '%s',\n\t\t\t\t`notify` = '%s',\n\t\t\t\t`poll` = '%s',\n\t\t\t\t`blocked` = 0,\n\t\t\t\t`pending` = 0,\n\t\t\t\t`network` = '%s',\n\t\t\t\t`writable` = %d,\n\t\t\t\t`hidden` = %d,\n\t\t\t\t`rel` = %d\n\t\t\t\tWHERE `id` = %d\n\t\t\t", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($notify), dbesc($poll), dbesc($network), intval($writable), intval($hidden), intval($new_relation), intval($contact_id)); } if ($r === false) { notice(t('Unable to set contact photo.') . EOL); } // reload contact info $r = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1", intval($contact_id)); if (count($r)) { $contact = $r[0]; } else { $contact = null; } if (isset($new_relation) && $new_relation == CONTACT_IS_FRIEND) { if ($contact && $contact['network'] === NETWORK_DIASPORA) { require_once 'include/diaspora.php'; $ret = diaspora_share($user[0], $r[0]); logger('mod_follow: diaspora_share returns: ' . $ret); } // Send a new friend post if we are allowed to... $r = q("SELECT `hide-friends` FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1", intval($uid)); if (count($r) && $r[0]['hide-friends'] == 0 && $activity && !$hidden) { require_once 'include/items.php'; $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1", intval($uid)); if (count($self)) { $arr = array(); $arr['uri'] = $arr['parent-uri'] = item_new_uri($a->get_hostname(), $uid); $arr['uid'] = $uid; $arr['contact-id'] = $self[0]['id']; $arr['wall'] = 1; $arr['type'] = 'wall'; $arr['gravity'] = 0; $arr['origin'] = 1; $arr['author-name'] = $arr['owner-name'] = $self[0]['name']; $arr['author-link'] = $arr['owner-link'] = $self[0]['url']; $arr['author-avatar'] = $arr['owner-avatar'] = $self[0]['thumb']; $A = '[url=' . $self[0]['url'] . ']' . $self[0]['name'] . '[/url]'; $APhoto = '[url=' . $self[0]['url'] . ']' . '[img]' . $self[0]['thumb'] . '[/img][/url]'; $B = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]'; $BPhoto = '[url=' . $contact['url'] . ']' . '[img]' . $contact['thumb'] . '[/img][/url]'; $arr['verb'] = ACTIVITY_FRIEND; $arr['object-type'] = ACTIVITY_OBJ_PERSON; $arr['body'] = sprintf(t('%1$s is now friends with %2$s'), $A, $B) . "\n\n\n" . $BPhoto; $arr['object'] = '<object><type>' . ACTIVITY_OBJ_PERSON . '</type><title>' . $contact['name'] . '</title>' . '<id>' . $contact['url'] . '/' . $contact['name'] . '</id>'; $arr['object'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $contact['url'] . '" />' . "\n"); $arr['object'] .= xmlify('<link rel="photo" type="image/jpeg" href="' . $contact['thumb'] . '" />' . "\n"); $arr['object'] .= '</link></object>' . "\n"; $arr['last-child'] = 1; $arr['allow_cid'] = $user[0]['allow_cid']; $arr['allow_gid'] = $user[0]['allow_gid']; $arr['deny_cid'] = $user[0]['deny_cid']; $arr['deny_gid'] = $user[0]['deny_gid']; $i = item_store($arr); if ($i) { proc_run('php', "include/notifier.php", "activity", "{$i}"); } } } } $g = q("select def_gid from user where uid = %d limit 1", intval($uid)); if ($contact && $g && intval($g[0]['def_gid'])) { require_once 'include/group.php'; group_add_member($uid, '', $contact['id'], $g[0]['def_gid']); } // Let's send our user to the contact editor in case they want to // do anything special with this new friend. if ($handsfree === null) { goaway($a->get_baseurl() . '/contacts/' . intval($contact_id)); } else { return; } //NOTREACHED } /** * * * End of Scenario 1. [Local confirmation of remote friend request]. * * Begin Scenario 2. This is the remote response to the above scenario. * This will take place on the site that originally initiated the friend request. * In the section above where the confirming party makes a POST and * retrieves xml status information, they are communicating with the following code. * */ if (x($_POST, 'source_url')) { // We are processing an external confirmation to an introduction created by our user. $public_key = x($_POST, 'public_key') ? $_POST['public_key'] : ''; $dfrn_id = x($_POST, 'dfrn_id') ? hex2bin($_POST['dfrn_id']) : ''; $source_url = x($_POST, 'source_url') ? hex2bin($_POST['source_url']) : ''; $aes_key = x($_POST, 'aes_key') ? $_POST['aes_key'] : ''; $duplex = x($_POST, 'duplex') ? intval($_POST['duplex']) : 0; $page = x($_POST, 'page') ? intval($_POST['page']) : 0; $version_id = x($_POST, 'dfrn_version') ? (double) $_POST['dfrn_version'] : 2.0; $forum = $page == 1 ? 1 : 0; $prv = $page == 2 ? 1 : 0; logger('dfrn_confirm: requestee contacted: ' . $node); logger('dfrn_confirm: request: POST=' . print_r($_POST, true), LOGGER_DATA); // If $aes_key is set, both of these items require unpacking from the hex transport encoding. if (x($aes_key)) { $aes_key = hex2bin($aes_key); $public_key = hex2bin($public_key); } // Find our user's account $r = q("SELECT * FROM `user` WHERE `nickname` = '%s' LIMIT 1", dbesc($node)); if (!count($r)) { $message = sprintf(t('No user record found for \'%s\' '), $node); xml_status(3, $message); // failure // NOTREACHED } $my_prvkey = $r[0]['prvkey']; $local_uid = $r[0]['uid']; if (!strstr($my_prvkey, 'PRIVATE KEY')) { $message = t('Our site encryption key is apparently messed up.'); xml_status(3, $message); } // verify everything $decrypted_source_url = ""; openssl_private_decrypt($source_url, $decrypted_source_url, $my_prvkey); if (!strlen($decrypted_source_url)) { $message = t('Empty site URL was provided or URL could not be decrypted by us.'); xml_status(3, $message); // NOTREACHED } $ret = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", dbesc($decrypted_source_url), intval($local_uid)); if (!count($ret)) { if (strstr($decrypted_source_url, 'http:')) { $newurl = str_replace('http:', 'https:', $decrypted_source_url); } else { $newurl = str_replace('https:', 'http:', $decrypted_source_url); } $ret = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", dbesc($newurl), intval($local_uid)); if (!count($ret)) { // this is either a bogus confirmation (?) or we deleted the original introduction. $message = t('Contact record was not found for you on our site.'); xml_status(3, $message); return; // NOTREACHED } } $relation = $ret[0]['rel']; // Decrypt all this stuff we just received $foreign_pubkey = $ret[0]['site-pubkey']; $dfrn_record = $ret[0]['id']; if (!$foreign_pubkey) { $message = sprintf(t('Site public key not available in contact record for URL %s.'), $newurl); xml_status(3, $message); } $decrypted_dfrn_id = ""; openssl_public_decrypt($dfrn_id, $decrypted_dfrn_id, $foreign_pubkey); if (strlen($aes_key)) { $decrypted_aes_key = ""; openssl_private_decrypt($aes_key, $decrypted_aes_key, $my_prvkey); $dfrn_pubkey = openssl_decrypt($public_key, 'AES-256-CBC', $decrypted_aes_key); } else { $dfrn_pubkey = $public_key; } $r = q("SELECT * FROM `contact` WHERE `dfrn-id` = '%s' LIMIT 1", dbesc($decrypted_dfrn_id)); if (count($r)) { $message = t('The ID provided by your system is a duplicate on our system. It should work if you try again.'); xml_status(1, $message); // Birthday paradox - duplicate dfrn-id // NOTREACHED } $r = q("UPDATE `contact` SET `dfrn-id` = '%s', `pubkey` = '%s' WHERE `id` = %d", dbesc($decrypted_dfrn_id), dbesc($dfrn_pubkey), intval($dfrn_record)); if (!count($r)) { $message = t('Unable to set your contact credentials on our system.'); xml_status(3, $message); } // It's possible that the other person also requested friendship. // If it is a duplex relationship, ditch the issued-id if one exists. if ($duplex) { $r = q("UPDATE `contact` SET `issued-id` = '' WHERE `id` = %d", intval($dfrn_record)); } // We're good but now we have to scrape the profile photo and send notifications. $r = q("SELECT `photo` FROM `contact` WHERE `id` = %d LIMIT 1", intval($dfrn_record)); if (count($r)) { $photo = $r[0]['photo']; } else { $photo = $a->get_baseurl() . '/images/person-175.jpg'; } require_once "include/Photo.php"; $photos = import_profile_photo($photo, $local_uid, $dfrn_record); logger('dfrn_confirm: request - photos imported'); $new_relation = CONTACT_IS_SHARING; if ($relation == CONTACT_IS_FOLLOWER || $duplex) { $new_relation = CONTACT_IS_FRIEND; } if ($relation == CONTACT_IS_FOLLOWER && $duplex) { $duplex = 0; } $r = q("UPDATE `contact` SET\n\t\t\t`photo` = '%s',\n\t\t\t`thumb` = '%s',\n\t\t\t`micro` = '%s',\n\t\t\t`rel` = %d,\n\t\t\t`name-date` = '%s',\n\t\t\t`uri-date` = '%s',\n\t\t\t`avatar-date` = '%s',\n\t\t\t`blocked` = 0,\n\t\t\t`pending` = 0,\n\t\t\t`duplex` = %d,\n\t\t\t`forum` = %d,\n\t\t\t`prv` = %d,\n\t\t\t`network` = '%s' WHERE `id` = %d\n\t\t", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), intval($new_relation), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($duplex), intval($forum), intval($prv), dbesc(NETWORK_DFRN), intval($dfrn_record)); if ($r === false) { // indicates schema is messed up or total db failure $message = t('Unable to update your contact profile details on our system'); xml_status(3, $message); } // Otherwise everything seems to have worked and we are almost done. Yay! // Send an email notification logger('dfrn_confirm: request: info updated'); $r = q("SELECT `contact`.*, `user`.* FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`\n\t\t\tWHERE `contact`.`id` = %d LIMIT 1", intval($dfrn_record)); if (count($r)) { $combined = $r[0]; } if (count($r) && $r[0]['notify-flags'] & NOTIFY_CONFIRM) { $mutual = $new_relation == CONTACT_IS_FRIEND; notification(array('type' => NOTIFY_CONFIRM, 'notify_flags' => $r[0]['notify-flags'], 'language' => $r[0]['language'], 'to_name' => $r[0]['username'], 'to_email' => $r[0]['email'], 'uid' => $r[0]['uid'], 'link' => $a->get_baseurl() . '/contacts/' . $dfrn_record, 'source_name' => strlen(stripslashes($r[0]['name'])) ? stripslashes($r[0]['name']) : t('[Name Withheld]'), 'source_link' => $r[0]['url'], 'source_photo' => $r[0]['photo'], 'verb' => $mutual ? ACTIVITY_FRIEND : ACTIVITY_FOLLOW, 'otype' => 'intro')); } // Send a new friend post if we are allowed to... if ($page && intval(get_pconfig($local_uid, 'system', 'post_joingroup'))) { $r = q("SELECT `hide-friends` FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1", intval($local_uid)); if (count($r) && $r[0]['hide-friends'] == 0) { require_once 'include/items.php'; $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1", intval($local_uid)); if (count($self)) { $arr = array(); $arr['uri'] = $arr['parent-uri'] = item_new_uri($a->get_hostname(), $local_uid); $arr['uid'] = $local_uid; $arr['contact-id'] = $self[0]['id']; $arr['wall'] = 1; $arr['type'] = 'wall'; $arr['gravity'] = 0; $arr['origin'] = 1; $arr['author-name'] = $arr['owner-name'] = $self[0]['name']; $arr['author-link'] = $arr['owner-link'] = $self[0]['url']; $arr['author-avatar'] = $arr['owner-avatar'] = $self[0]['thumb']; $A = '[url=' . $self[0]['url'] . ']' . $self[0]['name'] . '[/url]'; $APhoto = '[url=' . $self[0]['url'] . ']' . '[img]' . $self[0]['thumb'] . '[/img][/url]'; $B = '[url=' . $combined['url'] . ']' . $combined['name'] . '[/url]'; $BPhoto = '[url=' . $combined['url'] . ']' . '[img]' . $combined['thumb'] . '[/img][/url]'; $arr['verb'] = ACTIVITY_JOIN; $arr['object-type'] = ACTIVITY_OBJ_GROUP; $arr['body'] = sprintf(t('%1$s has joined %2$s'), $A, $B) . "\n\n\n" . $BPhoto; $arr['object'] = '<object><type>' . ACTIVITY_OBJ_GROUP . '</type><title>' . $combined['name'] . '</title>' . '<id>' . $combined['url'] . '/' . $combined['name'] . '</id>'; $arr['object'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $combined['url'] . '" />' . "\n"); $arr['object'] .= xmlify('<link rel="photo" type="image/jpeg" href="' . $combined['thumb'] . '" />' . "\n"); $arr['object'] .= '</link></object>' . "\n"; $arr['last-child'] = 1; $arr['allow_cid'] = $user[0]['allow_cid']; $arr['allow_gid'] = $user[0]['allow_gid']; $arr['deny_cid'] = $user[0]['deny_cid']; $arr['deny_gid'] = $user[0]['deny_gid']; $i = item_store($arr); if ($i) { proc_run('php', "include/notifier.php", "activity", "{$i}"); } } } } xml_status(0); // Success return; // NOTREACHED ////////////////////// End of this scenario /////////////////////////////////////////////// } // somebody arrived here by mistake or they are fishing. Send them to the homepage. goaway(z_root()); // NOTREACHED }
function new_contact($uid, $url, $interactive = false) { $result = array('cid' => -1, 'success' => false, 'message' => ''); $a = get_app(); // remove ajax junk, e.g. Twitter $url = str_replace('/#!/', '/', $url); if (!allowed_url($url)) { $result['message'] = t('Disallowed profile URL.'); return $result; } if (!$url) { $result['message'] = t('Connect URL missing.'); return $result; } $arr = array('url' => $url, 'contact' => array()); call_hooks('follow', $arr); if (x($arr['contact'], 'name')) { $ret = $arr['contact']; } else { $ret = probe_url($url); } if ($ret['network'] === NETWORK_DFRN) { if ($interactive) { if (strlen($a->path)) { $myaddr = bin2hex($a->get_baseurl() . '/profile/' . $a->user['nickname']); } else { $myaddr = bin2hex($a->user['nickname'] . '@' . $a->get_hostname()); } goaway($ret['request'] . "&addr={$myaddr}"); // NOTREACHED } } else { if (get_config('system', 'dfrn_only')) { $result['message'] = t('This site is not configured to allow communications with other networks.') . EOL; $result['message'] != t('No compatible communication protocols or feeds were discovered.') . EOL; return $result; } } // This extra param just confuses things, remove it if ($ret['network'] === NETWORK_DIASPORA) { $ret['url'] = str_replace('?absolute=true', '', $ret['url']); } // do we have enough information? if (!(x($ret, 'name') && x($ret, 'poll') && (x($ret, 'url') || x($ret, 'addr')))) { $result['message'] .= t('The profile address specified does not provide adequate information.') . EOL; if (!x($ret, 'poll')) { $result['message'] .= t('No compatible communication protocols or feeds were discovered.') . EOL; } if (!x($ret, 'name')) { $result['message'] .= t('An author or name was not found.') . EOL; } if (!x($ret, 'url')) { $result['message'] .= t('No browser URL could be matched to this address.') . EOL; } if (strpos($url, '@') !== false) { $result['message'] .= t('Unable to match @-style Identity Address with a known protocol or email contact.') . EOL; $result['message'] .= t('Use mailto: in front of address to force email check.') . EOL; } return $result; } if ($ret['network'] === NETWORK_OSTATUS && get_config('system', 'ostatus_disabled')) { $result['message'] .= t('The profile address specified belongs to a network which has been disabled on this site.') . EOL; $ret['notify'] = ''; } if (!$ret['notify']) { $result['message'] .= t('Limited profile. This person will be unable to receive direct/personal notifications from you.') . EOL; } $writeable = $ret['network'] === NETWORK_OSTATUS && $ret['notify'] ? 1 : 0; $subhub = $ret['network'] === NETWORK_OSTATUS ? true : false; $hidden = $ret['network'] === NETWORK_MAIL ? 1 : 0; if ($ret['network'] === NETWORK_MAIL) { $writeable = 1; } if ($ret['network'] === NETWORK_DIASPORA) { $writeable = 1; } // check if we already have a contact // the poll url is more reliable than the profile url, as we may have // indirect links or webfinger links $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `poll` IN ('%s', '%s') AND `network` = '%s' LIMIT 1", intval($uid), dbesc($ret['poll']), dbesc(normalise_link($ret['poll'])), dbesc($ret['network'])); if (!count($r)) { $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` = '%s' LIMIT 1", intval($uid), dbesc(normalise_link($url)), dbesc($ret['network'])); } if (count($r)) { // update contact if ($r[0]['rel'] == CONTACT_IS_FOLLOWER || $network === NETWORK_DIASPORA && $r[0]['rel'] == CONTACT_IS_SHARING) { q("UPDATE `contact` SET `rel` = %d , `subhub` = %d, `readonly` = 0 WHERE `id` = %d AND `uid` = %d", intval(CONTACT_IS_FRIEND), intval($subhub), intval($r[0]['id']), intval($uid)); } } else { // check service class limits $r = q("select count(*) as total from contact where uid = %d and pending = 0 and self = 0", intval($uid)); if (count($r)) { $total_contacts = $r[0]['total']; } if (!service_class_allows($uid, 'total_contacts', $total_contacts)) { $result['message'] .= upgrade_message(); return $result; } $r = q("select count(network) as total from contact where uid = %d and network = '%s' and pending = 0 and self = 0", intval($uid), dbesc($network)); if (count($r)) { $total_network = $r[0]['total']; } if (!service_class_allows($uid, 'total_contacts_' . $network, $total_network)) { $result['message'] .= upgrade_message(); return $result; } $new_relation = $ret['network'] === NETWORK_MAIL ? CONTACT_IS_FRIEND : CONTACT_IS_SHARING; if ($ret['network'] === NETWORK_DIASPORA) { $new_relation = CONTACT_IS_FOLLOWER; } // create contact record $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `batch`, `notify`, `poll`, `poco`, `name`, `nick`, `network`, `pubkey`, `rel`, `priority`,\n\t\t\t`writable`, `hidden`, `blocked`, `readonly`, `pending`, `subhub` )\n\t\t\tVALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, 0, 0, 0, %d ) ", intval($uid), dbesc(datetime_convert()), dbesc($ret['url']), dbesc(normalise_link($ret['url'])), dbesc($ret['addr']), dbesc($ret['alias']), dbesc($ret['batch']), dbesc($ret['notify']), dbesc($ret['poll']), dbesc($ret['poco']), dbesc($ret['name']), dbesc($ret['nick']), dbesc($ret['network']), dbesc($ret['pubkey']), intval($new_relation), intval($ret['priority']), intval($writeable), intval($hidden), intval($subhub)); } $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `network` = '%s' AND `uid` = %d LIMIT 1", dbesc($ret['url']), dbesc($ret['network']), intval($uid)); if (!count($r)) { $result['message'] .= t('Unable to retrieve contact information.') . EOL; return $result; } $contact = $r[0]; $contact_id = $r[0]['id']; $result['cid'] = $contact_id; $g = q("select def_gid from user where uid = %d limit 1", intval($uid)); if ($g && intval($g[0]['def_gid'])) { require_once 'include/group.php'; group_add_member($uid, '', $contact_id, $g[0]['def_gid']); } require_once "include/Photo.php"; $photos = import_profile_photo($ret['photo'], $uid, $contact_id); $r = q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t`thumb` = '%s',\n\t\t\t`micro` = '%s',\n\t\t\t`name-date` = '%s',\n\t\t\t`uri-date` = '%s',\n\t\t\t`avatar-date` = '%s'\n\t\t\tWHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact_id)); // pull feed and consume it, which should subscribe to the hub. proc_run('php', "include/onepoll.php", "{$contact_id}", "force"); // create a follow slap $tpl = get_markup_template('follow_slap.tpl'); $slap = replace_macros($tpl, array('$name' => $a->user['username'], '$profile_page' => $a->get_baseurl() . '/profile/' . $a->user['nickname'], '$photo' => $a->contact['photo'], '$thumb' => $a->contact['thumb'], '$published' => datetime_convert('UTC', 'UTC', 'now', ATOM_TIME), '$item_id' => 'urn:X-dfrn:' . $a->get_hostname() . ':follow:' . get_guid(32), '$title' => '', '$type' => 'text', '$content' => t('following'), '$nick' => $a->user['nickname'], '$verb' => ACTIVITY_FOLLOW, '$ostat_follow' => '')); $r = q("SELECT `contact`.*, `user`.* FROM `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid`\n\t\t\tWHERE `user`.`uid` = %d AND `contact`.`self` = 1 LIMIT 1", intval($uid)); if (count($r)) { if ($contact['network'] == NETWORK_OSTATUS && strlen($contact['notify'])) { require_once 'include/salmon.php'; slapper($r[0], $contact['notify'], $slap); } if ($contact['network'] == NETWORK_DIASPORA) { require_once 'include/diaspora.php'; $ret = diaspora_share($a->user, $contact); logger('mod_follow: diaspora_share returns: ' . $ret); } } $result['success'] = true; return $result; }
function import_post(&$a) { if (!get_account_id()) { return; } $data = null; $seize = x($_REQUEST, 'make_primary') ? intval($_REQUEST['make_primary']) : 0; $src = $_FILES['filename']['tmp_name']; $filename = basename($_FILES['filename']['name']); $filesize = intval($_FILES['filename']['size']); $filetype = $_FILES['filename']['type']; if ($src) { 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; $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); // import channel $channel = $data['channel']; $r = q("select * from channel where (channel_guid = '%s' or channel_hash = '%s' or channel_address = '%s' ) limit 1", dbesc($channel['channel_guid']), dbesc($channel['channel_hash']), dbesc($channel['channel_address'])); // We should probably also verify the hash if ($r) { logger('mod_import: duplicate channel. ', print_r($channel, true)); notice(t('Cannot create a duplicate channel identifier on this system. Import failed.') . EOL); return; } unset($channel['channel_id']); $channel['channel_account_id'] = get_account_id(); $channel['channel_primary'] = $seize ? 1 : 0; dbesc_array($channel); $r = dbq("INSERT INTO channel (`" . implode("`, `", array_keys($channel)) . "`) VALUES ('" . implode("', '", array_values($channel)) . "')"); if (!$r) { logger('mod_import: channel clone failed. ', print_r($channel, true)); notice(t('Channel clone failed. Import failed.') . EOL); return; } $r = q("select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", intval(get_account_id()), $channel['channel_guid']); if (!$r) { logger('mod_import: channel not found. ', print_r($channel, true)); notice(t('Cloned channel not found. Import failed.') . EOL); return; } // reset $channel = $r[0]; set_default_login_identity(get_account_id(), $channel['channel_id'], false); if ($data['photo']) { require_once 'include/photo/photo_driver.php'; import_channel_photo(base64url_decode($data['photo']['data']), $data['photo']['type'], get_account_id(), $channel['channel_id']); } $profiles = $data['profile']; if ($profiles) { foreach ($profiles as $profile) { unset($profile['id']); $profile['aid'] = get_account_id(); $profile['uid'] = $channel['channel_id']; // we are going to reset all profile photos to the original // somebody will have to fix this later and put all the applicable photos into the export $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; dbesc_array($profile); $r = dbq("INSERT INTO profile (`" . implode("`, `", array_keys($profile)) . "`) VALUES ('" . implode("', '", array_values($profile)) . "')"); } } $hublocs = $data['hubloc']; if ($hublocs) { foreach ($hublocs as $hubloc) { $arr = array('guid' => $hubloc['hubloc_guid'], 'guid_sig' => $hubloc['guid_sig'], 'url' => $hubloc['hubloc_url'], 'url_sig' => $hubloc['hubloc_url_sig']); if ($hubloc['hubloc_hash'] === $channel['channel_hash'] && $hubloc['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY && $seize) { $hubloc['hubloc_flags'] = $hubloc['hubloc_flags'] ^ HUBLOC_FLAGS_PRIMARY; } if (!zot_gethub($arr)) { unset($hubloc['hubloc_id']); dbesc_array($hubloc); $r = dbq("INSERT INTO hubloc (`" . implode("`, `", array_keys($hubloc)) . "`) VALUES ('" . implode("', '", array_values($hubloc)) . "')"); } } } // 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_flags, \n\t\thubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey )\n\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['channel_address'] . '@' . get_app()->get_hostname()), dbesc('zot'), intval($seize ? HUBLOC_FLAGS_PRIMARY : 0), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $channel['channel_prvkey']))), dbesc(get_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_flags = (hubloc_flags ^ %d) where (hubloc_flags & %d) and hubloc_hash = '%s' and hubloc_url != '%s' ", intval(HUBLOC_FLAGS_PRIMARY), intval(HUBLOC_FLAGS_PRIMARY), dbesc($channel['channel_hash']), dbesc(z_root())); } // import xchans and contact photos if ($seize) { // replace our existing xchan if we're seizing control $r = q("delete from xchan where xchan_hash = '%s' limit 1", 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 ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", dbesc($channel['channel_hash']), dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_pubkey']), dbesc($a->get_baseurl() . "/photo/profile/l/" . $channel['channel_id']), dbesc($a->get_baseurl() . "/photo/profile/m/" . $channel['channel_id']), dbesc($a->get_baseurl() . "/photo/profile/s/" . $channel['channel_id']), dbesc($channel['channel_address'] . '@' . get_app()->get_hostname()), 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())); } $xchans = $data['xchan']; if ($xchans) { foreach ($xchans as $xchan) { $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_profile_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\twhere xchan_hash = '%s' limit 1", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($photodate), dbesc($xchan_hash)); } } // FIXME - ensure we have an xchan if somebody is trying to pull a fast one // import contacts $abooks = $data['abook']; if ($abooks) { foreach ($abooks as $abook) { unset($abook['abook_id']); $abook['abook_account'] = get_account_id(); $abook['abook_channel'] = $channel['channel_id']; dbesc_array($abook); $r = dbq("INSERT INTO abook (`" . implode("`, `", array_keys($abook)) . "`) VALUES ('" . implode("', '", array_values($abook)) . "')"); } } $configs = $data['config']; if ($configs) { foreach ($configs as $config) { unset($config['id']); $config['uid'] = $channel['channel_id']; dbesc_array($config); $r = dbq("INSERT INTO pconfig (`" . implode("`, `", array_keys($config)) . "`) VALUES ('" . implode("', '", array_values($config)) . "')"); } } $groups = $data['group']; if ($groups) { $saved = array(); foreach ($groups as $group) { $saved[$group['hash']] = array('old' => $group['id']); unset($group['id']); $group['uid'] = $channel['channel_id']; dbesc_array($group); $r = dbq("INSERT INTO group (`" . 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 ($groups_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)) . "')"); } } // FIXME - ensure we have a self entry if somebody is trying to pull a fast one if ($seize) { // notify old server that it is no longer primary. } // This will indirectly perform a refresh_all *and* update the directory proc_run('php', 'include/directory.php', $channel['channel_id']); // send out refresh requests notice(t('Import completed.') . EOL); change_channel($channel['channel_id']); goaway(z_root() . '/network'); }
function diaspora_profile($importer, $xml, $msg) { $a = get_app(); $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); 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_post: Ignoring this author.'); return 202; } $name = unxmlify($xml->first_name) . (strlen($xml->last_name) ? ' ' . unxmlify($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; } /* $r = q("SELECT DISTINCT ( `resource-id` ) FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' ", intval($importer['channel_id']), intval($contact['id']) ); $oldphotos = ((count($r)) ? $r : null);*/ require_once 'include/Photo.php'; $images = import_profile_photo($image_url, $importer['channel_id'], $contact['id']); // 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']; } // TODO: update name on item['author-name'] if the name changed. See consume_feed() // Not doing this currently because D* protocol is scheduled for revision soon. $r = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s', `avatar-date` = '%s' , `bd` = '%s' WHERE `id` = %d AND `uid` = %d", dbesc($name), dbesc(datetime_convert()), dbesc($images[0]), dbesc($images[1]), dbesc($images[2]), dbesc(datetime_convert()), dbesc($birthday), intval($contact['id']), intval($importer['channel_id'])); /* if($r) { if($oldphotos) { foreach($oldphotos as $ph) { q("DELETE FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' AND `resource-id` = '%s' ", intval($importer['channel_id']), intval($contact['id']), dbesc($ph['resource-id']) ); } } } */ return; }
function appnet_fetchcontact($a, $uid, $contact, $me, $create_user) { if (function_exists("update_gcontact")) { update_gcontact(array("url" => $contact["canonical_url"], "generation" => 2, "network" => NETWORK_APPNET, "photo" => $contact["avatar_image"]["url"], "name" => $contact["name"], "nick" => $contact["username"], "about" => $contact["description"]["text"], "hide" => true, "addr" => $contact["username"] . "@app.net")); } else { // Old Code $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1", dbesc(normalise_link($contact["canonical_url"]))); if (count($r) == 0) { q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')", dbesc(normalise_link($contact["canonical_url"])), dbesc($contact["name"]), dbesc($contact["username"]), dbesc($contact["avatar_image"]["url"])); } else { q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'", dbesc($contact["name"]), dbesc($contact["username"]), dbesc($contact["avatar_image"]["url"]), dbesc(normalise_link($contact["canonical_url"]))); } if (DB_UPDATE_VERSION >= "1177") { q("UPDATE `unique_contacts` SET `location` = '%s', `about` = '%s' WHERE url = '%s'", dbesc(""), dbesc($contact["description"]["text"]), dbesc(normalise_link($contact["canonical_url"]))); } } $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1", intval($uid), dbesc("adn::" . $contact["id"])); if (!count($r) and !$create_user) { return $me["id"]; } if ($contact["canonical_url"] == "") { return $me["id"]; } if (count($r) and ($r[0]["readonly"] or $r[0]["blocked"])) { logger("appnet_fetchcontact: Contact '" . $r[0]["nick"] . "' is blocked or readonly.", LOGGER_DEBUG); return -1; } if (!count($r)) { if ($contact["name"] == "") { $contact["name"] = $contact["username"]; } if ($contact["username"] == "") { $contact["username"] = $contact["name"]; } // create contact record q("INSERT INTO `contact` (`uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,\n\t\t\t\t\t`name`, `nick`, `photo`, `network`, `rel`, `priority`,\n\t\t\t\t\t`writable`, `blocked`, `readonly`, `pending` )\n\t\t\t\t\tVALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ", intval($uid), dbesc(datetime_convert()), dbesc($contact["canonical_url"]), dbesc(normalise_link($contact["canonical_url"])), dbesc($contact["username"] . "@app.net"), dbesc("adn::" . $contact["id"]), dbesc(''), dbesc("adn::" . $contact["id"]), dbesc($contact["name"]), dbesc($contact["username"]), dbesc($contact["avatar_image"]["url"]), dbesc(NETWORK_APPNET), intval(CONTACT_IS_FRIEND), intval(1), intval(1)); $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1", dbesc("adn::" . $contact["id"]), intval($uid)); if (!count($r)) { return false; } $contact_id = $r[0]['id']; $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1", intval($uid)); if ($g && intval($g[0]['def_gid'])) { require_once 'include/group.php'; group_add_member($uid, '', $contact_id, $g[0]['def_gid']); } require_once "Photo.php"; $photos = import_profile_photo($contact["avatar_image"]["url"], $uid, $contact_id); q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t\t\t`thumb` = '%s',\n\t\t\t\t\t`micro` = '%s',\n\t\t\t\t\t`name-date` = '%s',\n\t\t\t\t\t`uri-date` = '%s',\n\t\t\t\t\t`avatar-date` = '%s'\n\t\t\t\tWHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact_id)); if (DB_UPDATE_VERSION >= "1177") { q("UPDATE `contact` SET `location` = '%s',\n\t\t\t\t\t\t`about` = '%s'\n\t\t\t\t\tWHERE `id` = %d", dbesc(""), dbesc($contact["description"]["text"]), intval($contact_id)); } } else { // update profile photos once every two weeks as we have no notification of when they change. //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false); $update_photo = $r[0]['avatar-date'] < datetime_convert('', '', 'now -12 hours'); // check that we have all the photos, this has been known to fail on occasion if (!$r[0]['photo'] || !$r[0]['thumb'] || !$r[0]['micro'] || $update_photo) { logger("appnet_fetchcontact: Updating contact " . $contact["username"], LOGGER_DEBUG); require_once "Photo.php"; $photos = import_profile_photo($contact["avatar_image"]["url"], $uid, $r[0]['id']); q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t\t\t\t`thumb` = '%s',\n\t\t\t\t\t\t`micro` = '%s',\n\t\t\t\t\t\t`name-date` = '%s',\n\t\t\t\t\t\t`uri-date` = '%s',\n\t\t\t\t\t\t`avatar-date` = '%s',\n\t\t\t\t\t\t`url` = '%s',\n\t\t\t\t\t\t`nurl` = '%s',\n\t\t\t\t\t\t`addr` = '%s',\n\t\t\t\t\t\t`name` = '%s',\n\t\t\t\t\t\t`nick` = '%s'\n\t\t\t\t\tWHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($contact["canonical_url"]), dbesc(normalise_link($contact["canonical_url"])), dbesc($contact["username"] . "@app.net"), dbesc($contact["name"]), dbesc($contact["username"]), intval($r[0]['id'])); if (DB_UPDATE_VERSION >= "1177") { q("UPDATE `contact` SET `location` = '%s',\n\t\t\t\t\t\t\t`about` = '%s'\n\t\t\t\t\t\tWHERE `id` = %d", dbesc(""), dbesc($contact["description"]["text"]), intval($r[0]['id'])); } } } return $r[0]["id"]; }
function diaspora_profile($importer, $xml, $msg) { $a = get_app(); $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); 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['uid'], $diaspora_handle); if (!$contact) { return; } if ($contact['blocked']) { logger('diaspora_post: Ignoring this author.'); return 202; } $name = unxmlify($xml->first_name) . (strlen($xml->last_name) ? ' ' . unxmlify($xml->last_name) : ''); $image_url = unxmlify($xml->image_url); $birthday = unxmlify($xml->birthday); $location = diaspora2bb(unxmlify($xml->location)); $about = diaspora2bb(unxmlify($xml->bio)); $gender = unxmlify($xml->gender); $tags = unxmlify($xml->tag_string); $tags = explode("#", $tags); $keywords = array(); foreach ($tags as $tag) { $tag = trim(strtolower($tag)); if ($tag != "") { $keywords[] = $tag; } } $keywords = implode(", ", $keywords); $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; } /* $r = q("SELECT DISTINCT ( `resource-id` ) FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' ", intval($importer['uid']), intval($contact['id']) ); $oldphotos = ((count($r)) ? $r : null);*/ require_once 'include/Photo.php'; $images = import_profile_photo($image_url, $importer['uid'], $contact['id']); // 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']; } // TODO: update name on item['author-name'] if the name changed. See consume_feed() // Not doing this currently because D* protocol is scheduled for revision soon. $r = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s', `avatar-date` = '%s' , `bd` = '%s', `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s' WHERE `id` = %d AND `uid` = %d", dbesc($name), dbesc(datetime_convert()), dbesc($images[0]), dbesc($images[1]), dbesc($images[2]), dbesc(datetime_convert()), dbesc($birthday), dbesc($location), dbesc($about), dbesc($keywords), dbesc($gender), intval($contact['id']), intval($importer['uid'])); if (unxmlify($xml->searchable) == "true") { require_once 'include/socgraph.php'; poco_check($contact['url'], $name, NETWORK_DIASPORA, $images[0], $about, $location, $gender, $keywords, "", datetime_convert(), 2, $contact['id'], $importer['uid']); } $profileurl = ""; $author = q("SELECT * FROM `unique_contacts` WHERE `url`='%s' LIMIT 1", dbesc(normalise_link($contact['url']))); if (count($author) == 0) { q("INSERT INTO `unique_contacts` (`url`, `name`, `avatar`, `location`, `about`) VALUES ('%s', '%s', '%s', '%s', '%s')", dbesc(normalise_link($contact['url'])), dbesc($name), dbesc($location), dbesc($about), dbesc($images[0])); $author = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1", dbesc(normalise_link($contact['url']))); } else { if (normalise_link($contact['url']) . $name . $location . $about != normalise_link($author[0]["url"]) . $author[0]["name"] . $author[0]["location"] . $author[0]["about"]) { q("UPDATE unique_contacts SET name = '%s', avatar = '%s', `location` = '%s', `about` = '%s' WHERE url = '%s'", dbesc($name), dbesc($images[0]), dbesc($location), dbesc($about), dbesc(normalise_link($contact['url']))); } } /* if($r) { if($oldphotos) { foreach($oldphotos as $ph) { q("DELETE FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' AND `resource-id` = '%s' ", intval($importer['uid']), intval($contact['id']), dbesc($ph['resource-id']) ); } } } */ return; }
function fbsync_fetch_contact($uid, $contact, $create_user) { if ($contact->url == "") { return 0; } // Check if the unique contact is existing // To-Do: only update once a while $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1", dbesc(normalise_link($contact->url))); if (count($r) == 0) { q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')", dbesc(normalise_link($contact->url)), dbesc($contact->name), dbesc($contact->username), dbesc($contact->pic_square)); } else { q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'", dbesc($contact->name), dbesc($contact->username), dbesc($contact->pic_square), dbesc(normalise_link($contact->url))); } $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1", intval($uid), dbesc("facebook::" . $contact->id)); if (!count($r) and !$create_user) { return 0; } if (count($r) and ($r[0]["readonly"] or $r[0]["blocked"])) { logger("fbsync_fetch_contact: Contact '" . $r[0]["nick"] . "' is blocked or readonly.", LOGGER_DEBUG); return -1; } $avatarpicture = $contact->pic_square; if (!count($r)) { // create contact record q("INSERT INTO `contact` (`uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,\n\t\t\t\t\t`name`, `nick`, `photo`, `network`, `rel`, `priority`,\n\t\t\t\t\t`writable`, `blocked`, `readonly`, `pending`)\n\t\t\t\t\tVALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0)", intval($uid), dbesc(datetime_convert()), dbesc($contact->url), dbesc(normalise_link($contact->url)), dbesc($contact->username . "@facebook.com"), dbesc("facebook::" . $contact->id), dbesc($contact->id), dbesc("facebook::" . $contact->id), dbesc($contact->name), dbesc($contact->username), dbesc($avatarpicture), dbesc(NETWORK_FACEBOOK), intval(CONTACT_IS_FRIEND), intval(1), intval(1)); $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1", dbesc("facebook::" . $contact->id), intval($uid)); if (!count($r)) { return false; } $contact_id = $r[0]['id']; $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1", intval($uid)); if ($g && intval($g[0]['def_gid'])) { require_once 'include/group.php'; group_add_member($uid, '', $contact_id, $g[0]['def_gid']); } require_once "Photo.php"; $photos = import_profile_photo($avatarpicture, $uid, $contact_id); q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t\t\t`thumb` = '%s',\n\t\t\t\t\t`micro` = '%s',\n\t\t\t\t\t`name-date` = '%s',\n\t\t\t\t\t`uri-date` = '%s',\n\t\t\t\t\t`avatar-date` = '%s'\n\t\t\t\tWHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact_id)); } else { // update profile photos once every 12 hours as we have no notification of when they change. $update_photo = $r[0]['avatar-date'] < datetime_convert('', '', 'now -12 hours'); // check that we have all the photos, this has been known to fail on occasion if (!$r[0]['photo'] || !$r[0]['thumb'] || !$r[0]['micro'] || $update_photo) { logger("fbsync_fetch_contact: Updating contact " . $contact->username, LOGGER_DEBUG); require_once "Photo.php"; $photos = import_profile_photo($avatarpicture, $uid, $r[0]['id']); q("UPDATE `contact` SET `photo` = '%s',\n\t\t\t\t\t\t`thumb` = '%s',\n\t\t\t\t\t\t`micro` = '%s',\n\t\t\t\t\t\t`name-date` = '%s',\n\t\t\t\t\t\t`uri-date` = '%s',\n\t\t\t\t\t\t`avatar-date` = '%s',\n\t\t\t\t\t\t`url` = '%s',\n\t\t\t\t\t\t`nurl` = '%s',\n\t\t\t\t\t\t`addr` = '%s',\n\t\t\t\t\t\t`name` = '%s',\n\t\t\t\t\t\t`nick` = '%s',\n\t\t\t\t\t\t`notify` = '%s'\n\t\t\t\t\tWHERE `id` = %d", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($contact->url), dbesc(normalise_link($contact->url)), dbesc($contact->username . "@facebook.com"), dbesc($contact->name), dbesc($contact->username), dbesc($contact->id), intval($r[0]['id'])); } } return $r[0]["id"]; }