function webfinger_content(&$a) { $o .= '<h3>Webfinger Diagnostic</h3>'; $o .= '<form action="webfinger" method="get">'; $o .= 'Lookup address: <input type="text" style="width: 250px;" name="addr" value="' . $_GET['addr'] . '" />'; $o .= '<input type="submit" name="submit" value="Submit" /></form>'; $o .= '<br /><br />'; if (x($_GET, 'addr')) { $addr = trim($_GET['addr']); if (strpos($addr, '@') !== false) { $res = webfinger_rfc7033($addr, true); if (!$res) { $res = old_webfinger($addr); } } else { if (function_exists('lrdd')) { $res = lrdd($addr); } } $o .= '<pre>'; $o .= str_replace("\n", '<br />', print_r($res, true)); $o .= '</pre>'; } return $o; }
function get_salmon_key($uri, $keyhash) { $ret = array(); logger('Fetching salmon key for ' . $uri); $arr = lrdd($uri); if (is_array($arr)) { foreach ($arr as $a) { if ($a['@attributes']['rel'] === 'magic-public-key') { $ret[] = $a['@attributes']['href']; } } } else { return ''; } // We have found at least one key URL // If it's inline, parse it - otherwise get the key if (count($ret)) { for ($x = 0; $x < count($ret); $x++) { if (substr($ret[$x], 0, 5) === 'data:') { if (strstr($ret[$x], ',')) { $ret[$x] = substr($ret[$x], strpos($ret[$x], ',') + 1); } else { $ret[$x] = substr($ret[$x], 5); } } else { $ret[$x] = fetch_url($ret[$x]); } } } logger('Key located: ' . print_r($ret, true)); if (count($ret) == 1) { // We only found one one key so we don't care if the hash matches. // If it's the wrong key we'll find out soon enough because // message verification will fail. This also covers some older // software which don't supply a keyhash. As long as they only // have one key we'll be right. return $ret[0]; } else { foreach ($ret as $a) { $hash = base64url_encode(hash('sha256', $a)); if ($hash == $keyhash) { return $a; } } } return ''; }
function probe_url($url, $mode = PROBE_NORMAL) { require_once 'include/email.php'; $result = array(); if (!$url) { return $result; } $network = null; $diaspora = false; $diaspora_base = ''; $diaspora_guid = ''; $diaspora_key = ''; $has_lrdd = false; $email_conversant = false; $twitter = strpos($url, 'twitter.com') !== false ? true : false; $at_addr = strpos($url, '@') !== false ? true : false; if (!$twitter) { if (strpos($url, 'mailto:') !== false && $at_addr) { $url = str_replace('mailto:', '', $url); $links = array(); } else { $links = lrdd($url); } if (count($links)) { $has_lrdd = true; logger('probe_url: found lrdd links: ' . print_r($links, true), LOGGER_DATA); foreach ($links as $link) { if ($link['@attributes']['rel'] === NAMESPACE_ZOT) { $zot = unamp($link['@attributes']['href']); } 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'])); $pubkey = rsatopem($diaspora_key); $diaspora = true; } } // Status.Net can have more than one profile URL. We need to match the profile URL // to a contact on incoming messages to prevent spam, and we won't know which one // to match. So in case of two, one of them is stored as an alias. Only store URL's // and not webfinger user@host aliases. If they've got more than two non-email style // aliases, let's hope we're lucky and get one that matches the feed author-uri because // otherwise we're screwed. foreach ($links as $link) { if ($link['@attributes']['rel'] === 'alias') { if (strpos($link['@attributes']['href'], '@') === false) { if (isset($profile)) { if ($link['@attributes']['href'] !== $profile) { $alias = unamp($link['@attributes']['href']); } } else { $profile = unamp($link['@attributes']['href']); } } } } } elseif ($mode == PROBE_NORMAL) { // Check email $orig_url = $url; if (strpos($orig_url, '@') && validate_email($orig_url)) { $x = q("SELECT `prvkey` FROM `user` WHERE `uid` = %d LIMIT 1", intval(local_user())); $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d AND `server` != '' LIMIT 1", intval(local_user())); if (count($x) && count($r)) { $mailbox = construct_mailbox_name($r[0]); $password = ''; openssl_private_decrypt(hex2bin($r[0]['pass']), $password, $x[0]['prvkey']); $mbox = email_connect($mailbox, $r[0]['user'], $password); if (!$mbox) { logger('probe_url: email_connect failed.'); } unset($password); } if ($mbox) { $msgs = email_poll($mbox, $orig_url); logger('probe_url: searching ' . $orig_url . ', ' . count($msgs) . ' messages found.', LOGGER_DEBUG); if (count($msgs)) { $addr = $orig_url; $network = NETWORK_MAIL; $name = substr($url, 0, strpos($url, '@')); $phost = substr($url, strpos($url, '@') + 1); $profile = 'http://' . $phost; // fix nick character range $vcard = array('fn' => $name, 'nick' => $name, 'photo' => avatar_img($url)); $notify = 'smtp ' . random_string(); $poll = 'email ' . random_string(); $priority = 0; $x = email_msg_meta($mbox, $msgs[0]); if (stristr($x->from, $orig_url)) { $adr = imap_rfc822_parse_adrlist($x->from, ''); } elseif (stristr($x->to, $orig_url)) { $adr = imap_rfc822_parse_adrlist($x->to, ''); } if (isset($adr)) { foreach ($adr as $feadr) { if (strcasecmp($feadr->mailbox, $name) == 0 && strcasecmp($feadr->host, $phost) == 0 && strlen($feadr->personal)) { $personal = imap_mime_header_decode($feadr->personal); $vcard['fn'] = ""; foreach ($personal as $perspart) { if ($perspart->charset != "default") { $vcard['fn'] .= iconv($perspart->charset, 'UTF-8//IGNORE', $perspart->text); } else { $vcard['fn'] .= $perspart->text; } } $vcard['fn'] = notags($vcard['fn']); } } } } imap_close($mbox); } } } } if ($mode == PROBE_NORMAL) { if (strlen($zot)) { $s = fetch_url($zot); if ($s) { $j = json_decode($s); if ($j) { $network = NETWORK_ZOT; $vcard = array('fn' => $j->fullname, 'nick' => $j->nickname, 'photo' => $j->photo); $profile = $j->url; $notify = $j->post; $pubkey = $j->pubkey; $poll = 'N/A'; } } } if (strlen($dfrn)) { $ret = scrape_dfrn($hcard ? $hcard : $dfrn); if (is_array($ret) && x($ret, 'dfrn-request')) { $network = NETWORK_DFRN; $request = $ret['dfrn-request']; $confirm = $ret['dfrn-confirm']; $notify = $ret['dfrn-notify']; $poll = $ret['dfrn-poll']; $vcard = array(); $vcard['fn'] = $ret['fn']; $vcard['nick'] = $ret['nick']; $vcard['photo'] = $ret['photo']; } } } if ($diaspora && $diaspora_base && $diaspora_guid) { if ($mode == PROBE_DIASPORA || !$notify) { $notify = $diaspora_base . 'receive/users/' . $diaspora_guid; $batch = $diaspora_base . 'receive/public'; } if (strpos($url, '@')) { $addr = str_replace('acct:', '', $url); } } if ($network !== NETWORK_ZOT && $network !== NETWORK_DFRN && $network !== NETWORK_MAIL) { if ($diaspora) { $network = NETWORK_DIASPORA; } elseif ($has_lrdd) { $network = NETWORK_OSTATUS; } $priority = 0; if ($hcard && !$vcard) { $vcard = scrape_vcard($hcard); // Google doesn't use absolute url in profile photos if (x($vcard, 'photo') && substr($vcard['photo'], 0, 1) == '/') { $h = @parse_url($hcard); if ($h) { $vcard['photo'] = $h['scheme'] . '://' . $h['host'] . $vcard['photo']; } } logger('probe_url: scrape_vcard: ' . print_r($vcard, true), LOGGER_DATA); } if ($twitter) { logger('twitter: setup'); $tid = basename($url); $tapi = 'https://api.twitter.com/1/statuses/user_timeline.rss'; if (intval($tid)) { $poll = $tapi . '?user_id=' . $tid; } else { $poll = $tapi . '?screen_name=' . $tid; } $profile = 'http://twitter.com/#!/' . $tid; $vcard['photo'] = 'https://api.twitter.com/1/users/profile_image/' . $tid; $vcard['nick'] = $tid; $vcard['fn'] = $tid . '@twitter'; } if (!x($vcard, 'fn')) { if (x($vcard, 'nick')) { $vcard['fn'] = $vcard['nick']; } } $check_feed = false; if ($twitter || !$poll) { $check_feed = true; } if (!isset($vcard) || !x($vcard, 'fn') || !$profile) { $check_feed = true; } if ($at_addr && !count($links)) { $check_feed = false; } if ($check_feed) { $feedret = scrape_feed($poll ? $poll : $url); logger('probe_url: scrape_feed ' . ($poll ? $poll : $url) . ' returns: ' . print_r($feedret, true), LOGGER_DATA); if (count($feedret) && ($feedret['feed_atom'] || $feedret['feed_rss'])) { $poll = x($feedret, 'feed_atom') ? unamp($feedret['feed_atom']) : unamp($feedret['feed_rss']); if (!x($vcard)) { $vcard = array(); } } if (x($feedret, 'photo') && !x($vcard, 'photo')) { $vcard['photo'] = $feedret['photo']; } require_once 'library/simplepie/simplepie.inc'; $feed = new SimplePie(); $xml = fetch_url($poll); logger('probe_url: fetch feed: ' . $poll . ' returns: ' . $xml, LOGGER_DATA); $a = get_app(); logger('probe_url: scrape_feed: headers: ' . $a->get_curl_headers(), LOGGER_DATA); $feed->set_raw_data($xml); $feed->init(); if ($feed->error()) { logger('probe_url: scrape_feed: Error parsing XML: ' . $feed->error()); } if (!x($vcard, 'photo')) { $vcard['photo'] = $feed->get_image_url(); } $author = $feed->get_author(); if ($author) { $vcard['fn'] = unxmlify(trim($author->get_name())); if (!$vcard['fn']) { $vcard['fn'] = trim(unxmlify($author->get_email())); } if (strpos($vcard['fn'], '@') !== false) { $vcard['fn'] = substr($vcard['fn'], 0, strpos($vcard['fn'], '@')); } $email = unxmlify($author->get_email()); if (!$profile && $author->get_link()) { $profile = trim(unxmlify($author->get_link())); } if (!$vcard['photo']) { $rawtags = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); if ($rawtags) { $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; if (x($elems, 'link') && $elems['link'][0]['attribs']['']['rel'] === 'photo') { $vcard['photo'] = $elems['link'][0]['attribs']['']['href']; } } } } else { $item = $feed->get_item(0); if ($item) { $author = $item->get_author(); if ($author) { $vcard['fn'] = trim(unxmlify($author->get_name())); if (!$vcard['fn']) { $vcard['fn'] = trim(unxmlify($author->get_email())); } if (strpos($vcard['fn'], '@') !== false) { $vcard['fn'] = substr($vcard['fn'], 0, strpos($vcard['fn'], '@')); } $email = unxmlify($author->get_email()); if (!$profile && $author->get_link()) { $profile = trim(unxmlify($author->get_link())); } } if (!$vcard['photo']) { $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/', 'thumbnail'); if ($rawmedia && $rawmedia[0]['attribs']['']['url']) { $vcard['photo'] = unxmlify($rawmedia[0]['attribs']['']['url']); } } if (!$vcard['photo']) { $rawtags = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); if ($rawtags) { $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; if (x($elems, 'link') && $elems['link'][0]['attribs']['']['rel'] === 'photo') { $vcard['photo'] = $elems['link'][0]['attribs']['']['href']; } } } } } if (!$vcard['photo'] && strlen($email)) { $vcard['photo'] = avatar_img($email); } if ($poll === $profile) { $lnk = $feed->get_permalink(); } if (isset($lnk) && strlen($lnk)) { $profile = $lnk; } if (!x($vcard, 'fn')) { $vcard['fn'] = notags($feed->get_title()); } if (!x($vcard, 'fn')) { $vcard['fn'] = notags($feed->get_description()); } if (strpos($vcard['fn'], 'Twitter / ') !== false) { $vcard['fn'] = substr($vcard['fn'], strpos($vcard['fn'], '/') + 1); $vcard['fn'] = trim($vcard['fn']); } if (!x($vcard, 'nick')) { $vcard['nick'] = strtolower(notags(unxmlify($vcard['fn']))); if (strpos($vcard['nick'], ' ')) { $vcard['nick'] = trim(substr($vcard['nick'], 0, strpos($vcard['nick'], ' '))); } } if (!$network) { $network = NETWORK_FEED; } if (!$priority) { $priority = 2; } } } if (!x($vcard, 'photo')) { $a = get_app(); $vcard['photo'] = $a->get_baseurl() . '/images/person-175.jpg'; } if (!$profile) { $profile = $url; } // No human could be associated with this link, use the URL as the contact name if ($network === NETWORK_FEED && $poll && !x($vcard, 'fn')) { $vcard['fn'] = $url; } $vcard['fn'] = notags($vcard['fn']); $vcard['nick'] = str_replace(' ', '', notags($vcard['nick'])); $result['name'] = $vcard['fn']; $result['nick'] = $vcard['nick']; $result['url'] = $profile; $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; }
function photos_post(&$a) { logger('mod-photos: photos_post: begin', LOGGER_DEBUG); logger('mod_photos: REQUEST ' . print_r($_REQUEST, true), LOGGER_DATA); logger('mod_photos: FILES ' . print_r($_FILES, true), LOGGER_DATA); $can_post = false; $visitor = 0; $page_owner_uid = $a->data['user']['uid']; $community_page = $a->data['user']['page-flags'] == PAGE_COMMUNITY ? true : false; if (local_user() && local_user() == $page_owner_uid) { $can_post = true; } else { if ($community_page && remote_user()) { $r = q("SELECT `uid` FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 AND `id` = %d AND `uid` = %d LIMIT 1", intval(remote_user()), intval($page_owner_uid)); if (count($r)) { $can_post = true; $visitor = remote_user(); } } } if (!$can_post) { notice(t('Permission denied.') . EOL); killme(); } $r = q("SELECT `contact`.*, `user`.`nickname` FROM `contact` LEFT JOIN `user` ON `user`.`uid` = `contact`.`uid` \n\t\tWHERE `user`.`uid` = %d AND `self` = 1 LIMIT 1", intval($page_owner_uid)); if (!count($r)) { notice(t('Contact information unavailable') . EOL); logger('photos_post: unable to locate contact record for page owner. uid=' . $page_owner_uid); killme(); } $owner_record = $r[0]; if ($a->argc > 3 && $a->argv[2] === 'album') { $album = hex2bin($a->argv[3]); if ($album === t('Profile Photos') || $album === 'Contact Photos' || $album === t('Contact Photos')) { goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); return; // NOTREACHED } $r = q("SELECT count(*) FROM `photo` WHERE `album` = '%s' AND `uid` = %d", dbesc($album), intval($page_owner_uid)); if (!count($r)) { notice(t('Album not found.') . EOL); goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); return; // NOTREACHED } $newalbum = notags(trim($_POST['albumname'])); if ($newalbum != $album) { q("UPDATE `photo` SET `album` = '%s' WHERE `album` = '%s' AND `uid` = %d", dbesc($newalbum), dbesc($album), intval($page_owner_uid)); $newurl = str_replace(bin2hex($album), bin2hex($newalbum), $_SESSION['photo_return']); goaway($a->get_baseurl() . '/' . $newurl); return; // NOTREACHED } if ($_POST['dropalbum'] == t('Delete Album')) { $res = array(); // get the list of photos we are about to delete if ($visitor) { $r = q("SELECT distinct(`resource-id`) as `rid` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d AND `album` = '%s'", intval($visitor), intval($page_owner_uid), dbesc($album)); } else { $r = q("SELECT distinct(`resource-id`) as `rid` FROM `photo` WHERE `uid` = %d AND `album` = '%s'", intval(local_user()), dbesc($album)); } if (count($r)) { foreach ($r as $rr) { $res[] = "'" . dbesc($rr['rid']) . "'"; } } else { goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); return; // NOTREACHED } $str_res = implode(',', $res); // remove the associated photos q("DELETE FROM `photo` WHERE `resource-id` IN ( {$str_res} ) AND `uid` = %d", intval($page_owner_uid)); // find and delete the corresponding item with all the comments and likes/dislikes $r = q("SELECT `parent-uri` FROM `item` WHERE `resource-id` IN ( {$str_res} ) AND `uid` = %d", intval($page_owner_uid)); if (count($r)) { foreach ($r as $rr) { q("UPDATE `item` SET `deleted` = 1, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", dbesc(datetime_convert()), dbesc($rr['parent-uri']), intval($page_owner_uid)); $drop_id = intval($rr['id']); // send the notification upstream/downstream as the case may be if ($rr['visible']) { proc_run('php', "include/notifier.php", "drop", "{$drop_id}"); } } } } goaway($a->get_baseurl() . '/photos/' . $a->data['user']['nickname']); return; // NOTREACHED } if ($a->argc > 2 && x($_POST, 'delete') && $_POST['delete'] == t('Delete Photo')) { // same as above but remove single photo if ($visitor) { $r = q("SELECT `id`, `resource-id` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d AND `resource-id` = '%s' LIMIT 1", intval($visitor), intval($page_owner_uid), dbesc($a->argv[2])); } else { $r = q("SELECT `id`, `resource-id` FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' LIMIT 1", intval(local_user()), dbesc($a->argv[2])); } if (count($r)) { q("DELETE FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s'", intval($page_owner_uid), dbesc($r[0]['resource-id'])); $i = q("SELECT * FROM `item` WHERE `resource-id` = '%s' AND `uid` = %d LIMIT 1", dbesc($r[0]['resource-id']), intval($page_owner_uid)); if (count($i)) { q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($i[0]['uri']), intval($page_owner_uid)); $url = $a->get_baseurl(); $drop_id = intval($i[0]['id']); if ($i[0]['visible']) { proc_run('php', "include/notifier.php", "drop", "{$drop_id}"); } } } goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); return; // NOTREACHED } if ($a->argc > 2 && (x($_POST, 'desc') !== false || x($_POST, 'newtag') !== false) || x($_POST, 'albname') !== false) { $desc = x($_POST, 'desc') ? notags(trim($_POST['desc'])) : ''; $rawtags = x($_POST, 'newtag') ? notags(trim($_POST['newtag'])) : ''; $item_id = x($_POST, 'item_id') ? intval($_POST['item_id']) : 0; $albname = x($_POST, 'albname') ? notags(trim($_POST['albname'])) : ''; $str_group_allow = perms2str($_POST['group_allow']); $str_contact_allow = perms2str($_POST['contact_allow']); $str_group_deny = perms2str($_POST['group_deny']); $str_contact_deny = perms2str($_POST['contact_deny']); $resource_id = $a->argv[2]; if (!strlen($albname)) { $albname = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y'); } if (x($_POST, 'rotate') !== false && intval($_POST['rotate']) == 1) { logger('rotate'); $r = q("select * from photo where `resource-id` = '%s' and uid = %d and scale = 0 limit 1", dbesc($resource_id), intval($page_owner_uid)); if (count($r)) { $ph = new Photo($r[0]['data']); if ($ph->is_valid()) { $ph->rotate(270); $width = $ph->getWidth(); $height = $ph->getHeight(); $x = q("update photo set data = '%s', height = %d, width = %d where `resource-id` = '%s' and uid = %d and scale = 0 limit 1", dbesc($ph->imageString()), intval($height), intval($width), dbesc($resource_id), intval($page_owner_uid)); if ($width > 640 || $height > 640) { $ph->scaleImage(640); $width = $ph->getWidth(); $height = $ph->getHeight(); $x = q("update photo set data = '%s', height = %d, width = %d where `resource-id` = '%s' and uid = %d and scale = 1 limit 1", dbesc($ph->imageString()), intval($height), intval($width), dbesc($resource_id), intval($page_owner_uid)); } if ($width > 320 || $height > 320) { $ph->scaleImage(320); $width = $ph->getWidth(); $height = $ph->getHeight(); $x = q("update photo set data = '%s', height = %d, width = %d where `resource-id` = '%s' and uid = %d and scale = 2 limit 1", dbesc($ph->imageString()), intval($height), intval($width), dbesc($resource_id), intval($page_owner_uid)); } } } } $p = q("SELECT * FROM `photo` WHERE `resource-id` = '%s' AND `uid` = %d ORDER BY `scale` DESC", dbesc($resource_id), intval($page_owner_uid)); if (count($p)) { $r = q("UPDATE `photo` SET `desc` = '%s', `album` = '%s', `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s' WHERE `resource-id` = '%s' AND `uid` = %d", dbesc($desc), dbesc($albname), dbesc($str_contact_allow), dbesc($str_group_allow), dbesc($str_contact_deny), dbesc($str_group_deny), dbesc($resource_id), intval($page_owner_uid)); } /* Don't make the item visible if the only change was the album name */ $visibility = 0; if ($p[0]['desc'] !== $desc || strlen($rawtags)) { $visibility = 1; } if (!$item_id) { // Create item container $title = ''; $uri = item_new_uri($a->get_hostname(), $page_owner_uid); $arr = array(); $arr['uid'] = $page_owner_uid; $arr['uri'] = $uri; $arr['parent-uri'] = $uri; $arr['type'] = 'photo'; $arr['wall'] = 1; $arr['resource-id'] = $p[0]['resource-id']; $arr['contact-id'] = $owner_record['id']; $arr['owner-name'] = $owner_record['name']; $arr['owner-link'] = $owner_record['url']; $arr['owner-avatar'] = $owner_record['thumb']; $arr['author-name'] = $owner_record['name']; $arr['author-link'] = $owner_record['url']; $arr['author-avatar'] = $owner_record['thumb']; $arr['title'] = $title; $arr['allow_cid'] = $p[0]['allow_cid']; $arr['allow_gid'] = $p[0]['allow_gid']; $arr['deny_cid'] = $p[0]['deny_cid']; $arr['deny_gid'] = $p[0]['deny_gid']; $arr['last-child'] = 1; $arr['visible'] = $visibility; $arr['origin'] = 1; $arr['body'] = '[url=' . $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $p[0]['resource-id'] . ']' . '[img]' . $a->get_baseurl() . '/photo/' . $p[0]['resource-id'] . '-' . $p[0]['scale'] . '.jpg' . '[/img]' . '[/url]'; $item_id = item_store($arr); } if ($item_id) { $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($item_id), intval($page_owner_uid)); } if (count($r)) { $old_tag = $r[0]['tag']; $old_inform = $r[0]['inform']; } if (strlen($rawtags)) { $str_tags = ''; $inform = ''; // if the new tag doesn't have a namespace specifier (@foo or #foo) give it a hashtag $x = substr($rawtags, 0, 1); if ($x !== '@' && $x !== '#') { $rawtags = '#' . $rawtags; } $taginfo = array(); $tags = get_tags($rawtags); if (count($tags)) { foreach ($tags as $tag) { if (isset($profile)) { unset($profile); } if (strpos($tag, '@') === 0) { $name = substr($tag, 1); if (strpos($name, '@') || strpos($name, 'http://')) { $newname = $name; $links = @lrdd($name); if (count($links)) { foreach ($links as $link) { if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { $profile = $link['@attributes']['href']; } if ($link['@attributes']['rel'] === 'salmon') { $salmon = '$url:' . str_replace(',', '%sc', $link['@attributes']['href']); if (strlen($inform)) { $inform .= ','; } $inform .= $salmon; } } } $taginfo[] = array($newname, $profile, $salmon); } else { $newname = $name; $alias = ''; $tagcid = 0; if (strrpos($newname, '+')) { $tagcid = intval(substr($newname, strrpos($newname, '+') + 1)); } if ($tagcid) { $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($tagcid), intval($profile_uid)); } elseif (strstr($name, '_') || strstr($name, ' ')) { $newname = str_replace('_', ' ', $name); $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1", dbesc($newname), intval($page_owner_uid)); } else { $r = q("SELECT * FROM `contact` WHERE `attag` = '%s' OR `nick` = '%s' AND `uid` = %d ORDER BY `attag` DESC LIMIT 1", dbesc($name), dbesc($name), intval($page_owner_uid)); } if (count($r)) { $newname = $r[0]['name']; $profile = $r[0]['url']; $notify = 'cid:' . $r[0]['id']; if (strlen($inform)) { $inform .= ','; } $inform .= $notify; } } if ($profile) { if (substr($notify, 0, 4) === 'cid:') { $taginfo[] = array($newname, $profile, $notify, $r[0], '@[url=' . str_replace(',', '%2c', $profile) . ']' . $newname . '[/url]'); } else { $taginfo[] = array($newname, $profile, $notify, null, $str_tags .= '@[url=' . $profile . ']' . $newname . '[/url]'); } if (strlen($str_tags)) { $str_tags .= ','; } $profile = str_replace(',', '%2c', $profile); $str_tags .= '@[url=' . $profile . ']' . $newname . '[/url]'; } } } } $newtag = $old_tag; if (strlen($newtag) && strlen($str_tags)) { $newtag .= ','; } $newtag .= $str_tags; $newinform = $old_inform; if (strlen($newinform) && strlen($inform)) { $newinform .= ','; } $newinform .= $inform; $r = q("UPDATE `item` SET `tag` = '%s', `inform` = '%s', `edited` = '%s', `changed` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1", dbesc($newtag), dbesc($newinform), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($item_id), intval($page_owner_uid)); $best = 0; foreach ($p as $scales) { if (intval($scales['scale']) == 2) { $best = 2; break; } if (intval($scales['scale']) == 4) { $best = 4; break; } } if (count($taginfo)) { foreach ($taginfo as $tagged) { $uri = item_new_uri($a->get_hostname(), $page_owner_uid); $arr = array(); $arr['uid'] = $page_owner_uid; $arr['uri'] = $uri; $arr['parent-uri'] = $uri; $arr['type'] = 'activity'; $arr['wall'] = 1; $arr['contact-id'] = $owner_record['id']; $arr['owner-name'] = $owner_record['name']; $arr['owner-link'] = $owner_record['url']; $arr['owner-avatar'] = $owner_record['thumb']; $arr['author-name'] = $owner_record['name']; $arr['author-link'] = $owner_record['url']; $arr['author-avatar'] = $owner_record['thumb']; $arr['title'] = ''; $arr['allow_cid'] = $p[0]['allow_cid']; $arr['allow_gid'] = $p[0]['allow_gid']; $arr['deny_cid'] = $p[0]['deny_cid']; $arr['deny_gid'] = $p[0]['deny_gid']; $arr['last-child'] = 1; $arr['visible'] = 1; $arr['verb'] = ACTIVITY_TAG; $arr['object-type'] = ACTIVITY_OBJ_PERSON; $arr['target-type'] = ACTIVITY_OBJ_PHOTO; $arr['tag'] = $tagged[4]; $arr['inform'] = $tagged[2]; $arr['origin'] = 1; $arr['body'] = '[url=' . $tagged[1] . ']' . $tagged[0] . '[/url]' . ' ' . t('was tagged in a') . ' ' . '[url=' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $p[0]['resource-id'] . ']' . t('photo') . '[/url]' . ' ' . t('by') . ' ' . '[url=' . $owner_record['url'] . ']' . $owner_record['name'] . '[/url]'; $arr['body'] .= "\n\n" . '[url=' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $p[0]['resource-id'] . ']' . '[img]' . $a->get_baseurl() . "/photo/" . $p[0]['resource-id'] . '-' . $best . '.jpg' . '[/img][/url]' . "\n"; $arr['object'] = '<object><type>' . ACTIVITY_OBJ_PERSON . '</type><title>' . $tagged[0] . '</title><id>' . $tagged[1] . '/' . $tagged[0] . '</id>'; $arr['object'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $tagged[1] . '" />' . "\n"); if ($tagged[3]) { $arr['object'] .= xmlify('<link rel="photo" type="image/jpeg" href="' . $tagged[3]['photo'] . '" />' . "\n"); } $arr['object'] .= '</link></object>' . "\n"; $arr['target'] = '<target><type>' . ACTIVITY_OBJ_PHOTO . '</type><title>' . $p[0]['desc'] . '</title><id>' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $p[0]['resource-id'] . '</id>'; $arr['target'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $p[0]['resource-id'] . '" />' . "\n" . '<link rel="preview" type="image/jpeg" href="' . $a->get_baseurl() . "/photo/" . $p[0]['resource-id'] . '-' . $best . '.jpg' . '" />') . '</link></target>'; $item_id = item_store($arr); if ($item_id) { q("UPDATE `item` SET `plink` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", dbesc($a->get_baseurl() . '/display/' . $owner_record['nickname'] . '/' . $item_id), intval($page_owner_uid), intval($item_id)); proc_run('php', "include/notifier.php", "tag", "{$item_id}"); } } } } goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); return; // NOTREACHED } /** * default post action - upload a photo */ call_hooks('photo_post_init', $_POST); /** * Determine the album to use */ $album = notags(trim($_REQUEST['album'])); $newalbum = notags(trim($_REQUEST['newalbum'])); logger('mod/photos.php: photos_post(): album= ' . $album . ' newalbum= ' . $newalbum, LOGGER_DEBUG); if (!strlen($album)) { if (strlen($newalbum)) { $album = $newalbum; } else { $album = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y'); } } /** * * We create a wall item for every photo, but we don't want to * overwhelm the data stream with a hundred newly uploaded photos. * So we will make the first photo uploaded to this album in the last several hours * visible by default, the rest will become visible over time when and if * they acquire comments, likes, dislikes, and/or tags * */ $r = q("SELECT * FROM `photo` WHERE `album` = '%s' AND `uid` = %d AND `created` > UTC_TIMESTAMP() - INTERVAL 3 HOUR ", dbesc($album), intval($page_owner_uid)); if (!count($r) || $album == t('Profile Photos')) { $visible = 1; } else { $visible = 0; } if (intval($_REQUEST['not_visible']) || $_REQUEST['not_visible'] === 'true') { $visible = 0; } $str_group_allow = perms2str(is_array($_REQUEST['group_allow']) ? $_REQUEST['group_allow'] : explode(',', $_REQUEST['group_allow'])); $str_contact_allow = perms2str(is_array($_REQUEST['contact_allow']) ? $_REQUEST['contact_allow'] : explode(',', $_REQUEST['contact_allow'])); $str_group_deny = perms2str(is_array($_REQUEST['group_deny']) ? $_REQUEST['group_deny'] : explode(',', $_REQUEST['group_deny'])); $str_contact_deny = perms2str(is_array($_REQUEST['contact_deny']) ? $_REQUEST['contact_deny'] : explode(',', $_REQUEST['contact_deny'])); $ret = array('src' => '', 'filename' => '', 'filesize' => 0); call_hooks('photo_post_file', $ret); if (x($ret, 'src') && x($ret, 'filesize')) { $src = $ret['src']; $filename = $ret['filename']; $filesize = $ret['filesize']; } else { $src = $_FILES['userfile']['tmp_name']; $filename = basename($_FILES['userfile']['name']); $filesize = intval($_FILES['userfile']['size']); } logger('photos: upload: received file: ' . $filename . ' as ' . $src . ' ' . $filesize . ' bytes', LOGGER_DEBUG); $maximagesize = get_config('system', 'maximagesize'); if ($maximagesize && $filesize > $maximagesize) { notice(t('Image exceeds size limit of ') . $maximagesize . EOL); @unlink($src); $foo = 0; call_hooks('photo_post_end', $foo); return; } if (!$filesize) { notice(t('Image file is empty.') . EOL); @unlink($src); $foo = 0; call_hooks('photo_post_end', $foo); return; } logger('mod/photos.php: photos_post(): loading the contents of ' . $src, LOGGER_DEBUG); $imagedata = @file_get_contents($src); $ph = new Photo($imagedata); if (!$ph->is_valid()) { logger('mod/photos.php: photos_post(): unable to process image', LOGGER_DEBUG); notice(t('Unable to process image.') . EOL); @unlink($src); $foo = 0; call_hooks('photo_post_end', $foo); killme(); } @unlink($src); $width = $ph->getWidth(); $height = $ph->getHeight(); $smallest = 0; $photo_hash = photo_new_resource(); $r = $ph->store($page_owner_uid, $visitor, $photo_hash, $filename, $album, 0, 0, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny); if (!$r) { logger('mod/photos.php: photos_post(): image store failed', LOGGER_DEBUG); notice(t('Image upload failed.') . EOL); killme(); } if ($width > 640 || $height > 640) { $ph->scaleImage(640); $ph->store($page_owner_uid, $visitor, $photo_hash, $filename, $album, 1, 0, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny); $smallest = 1; } if ($width > 320 || $height > 320) { $ph->scaleImage(320); $ph->store($page_owner_uid, $visitor, $photo_hash, $filename, $album, 2, 0, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny); $smallest = 2; } $basename = basename($filename); $uri = item_new_uri($a->get_hostname(), $page_owner_uid); // Create item container $arr = array(); $arr['uid'] = $page_owner_uid; $arr['uri'] = $uri; $arr['parent-uri'] = $uri; $arr['type'] = 'photo'; $arr['wall'] = 1; $arr['resource-id'] = $photo_hash; $arr['contact-id'] = $owner_record['id']; $arr['owner-name'] = $owner_record['name']; $arr['owner-link'] = $owner_record['url']; $arr['owner-avatar'] = $owner_record['thumb']; $arr['author-name'] = $owner_record['name']; $arr['author-link'] = $owner_record['url']; $arr['author-avatar'] = $owner_record['thumb']; $arr['title'] = ''; $arr['allow_cid'] = $str_contact_allow; $arr['allow_gid'] = $str_group_allow; $arr['deny_cid'] = $str_contact_deny; $arr['deny_gid'] = $str_group_deny; $arr['last-child'] = 1; $arr['visible'] = $visible; $arr['origin'] = 1; $arr['body'] = '[url=' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $photo_hash . ']' . '[img]' . $a->get_baseurl() . "/photo/{$photo_hash}-{$smallest}.jpg" . '[/img]' . '[/url]'; $item_id = item_store($arr); if ($item_id) { q("UPDATE `item` SET `plink` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", dbesc($a->get_baseurl() . '/display/' . $owner_record['nickname'] . '/' . $item_id), intval($page_owner_uid), intval($item_id)); } if ($visible) { proc_run('php', "include/notifier.php", 'wall-new', $item_id); } call_hooks('photo_post_end', intval($item_id)); // addon uploaders should call "killme()" [e.g. exit] within the photo_post_end hook // if they do not wish to be redirected goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); // NOTREACHED }
/** * This function removes the tag $tag from the text $body and replaces it with * the appropiate link. * * @param unknown_type $body the text to replace the tag in * @param unknown_type $inform a comma-seperated string containing everybody to inform * @param unknown_type $str_tags string to add the tag to * @param unknown_type $profile_uid * @param unknown_type $tag the tag to replace * * @return boolean true if replaced, false if not replaced */ function handle_tag($a, &$body, &$inform, &$str_tags, $profile_uid, $tag, $network = "") { $replaced = false; $r = null; //is it a person tag? if (strpos($tag, '@') === 0) { //is it already replaced? if (strpos($tag, '[url=')) { return $replaced; } $stat = false; //get the person's name $name = substr($tag, 1); //is it a link or a full dfrn address? if (strpos($name, '@') || strpos($name, 'http://')) { $newname = $name; //get the profile links $links = @lrdd($name); if (count($links)) { //for all links, collect how is to inform and how's profile is to link foreach ($links as $link) { if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { $profile = $link['@attributes']['href']; } if ($link['@attributes']['rel'] === 'salmon') { if (strlen($inform)) { $inform .= ','; } $inform .= 'url:' . str_replace(',', '%2c', $link['@attributes']['href']); } } } } elseif ($network != NETWORK_OSTATUS and $network != NETWORK_TWITTER and $network != NETWORK_STATUSNET and $network != NETWORK_APPNET) { //if it is a name rather than an address $newname = $name; $alias = ''; $tagcid = 0; //is it some generated name? if (strrpos($newname, '+')) { //get the id $tagcid = intval(substr($newname, strrpos($newname, '+') + 1)); //remove the next word from tag's name if (strpos($name, ' ')) { $name = substr($name, 0, strpos($name, ' ')); } } if ($tagcid) { //if there was an id //select contact with that id from the logged in user's contact list $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($tagcid), intval($profile_uid)); } else { $newname = str_replace('_', ' ', $name); // At first try to fetch a contact according to the given network if ($network != "") { //select someone from this user's contacts by name $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `network` = '%s' AND `uid` = %d LIMIT 1", dbesc($newname), dbesc($network), intval($profile_uid)); if (!$r) { //select someone by attag or nick and the name passed in $r = q("SELECT * FROM `contact` WHERE `attag` = '%s' OR `nick` = '%s' AND `network` = '%s' AND `uid` = %d ORDER BY `attag` DESC LIMIT 1", dbesc($name), dbesc($name), dbesc($network), intval($profile_uid)); } } else { $r = false; } if (!$r) { //select someone from this user's contacts by name $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1", dbesc($newname), intval($profile_uid)); } if (!$r) { //select someone by attag or nick and the name passed in $r = q("SELECT * FROM `contact` WHERE `attag` = '%s' OR `nick` = '%s' AND `uid` = %d ORDER BY `attag` DESC LIMIT 1", dbesc($name), dbesc($name), intval($profile_uid)); } } /* } elseif(strstr($name,'_') || strstr($name,' ')) { //no id //get the real name $newname = str_replace('_',' ',$name); //select someone from this user's contacts by name $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1", dbesc($newname), intval($profile_uid) ); } else { //select someone by attag or nick and the name passed in $r = q("SELECT * FROM `contact` WHERE `attag` = '%s' OR `nick` = '%s' AND `uid` = %d ORDER BY `attag` DESC LIMIT 1", dbesc($name), dbesc($name), intval($profile_uid) ); }*/ //$r is set, if someone could be selected if (count($r)) { $profile = $r[0]['url']; //set newname to nick, find alias if ($r[0]['network'] === NETWORK_OSTATUS or $r[0]['network'] === NETWORK_TWITTER or $r[0]['network'] === NETWORK_STATUSNET or $r[0]['network'] === NETWORK_APPNET) { $newname = $r[0]['nick']; $stat = true; if ($r[0]['alias']) { $alias = $r[0]['alias']; } } else { $newname = $r[0]['name']; } //add person's id to $inform if (strlen($inform)) { $inform .= ','; } $inform .= 'cid:' . $r[0]['id']; } } //if there is an url for this persons profile if (isset($profile)) { $replaced = true; //create profile link $profile = str_replace(',', '%2c', $profile); $newtag = '@[url=' . $profile . ']' . $newname . '[/url]'; $body = str_replace('@' . $name, $newtag, $body); //append tag to str_tags if (!stristr($str_tags, $newtag)) { if (strlen($str_tags)) { $str_tags .= ','; } $str_tags .= $newtag; } // Status.Net seems to require the numeric ID URL in a mention if the person isn't // subscribed to you. But the nickname URL is OK if they are. Grrr. We'll tag both. if (strlen($alias)) { $newtag = '@[url=' . $alias . ']' . $newname . '[/url]'; if (!stristr($str_tags, $newtag)) { if (strlen($str_tags)) { $str_tags .= ','; } $str_tags .= $newtag; } } } } return array('replaced' => $replaced, 'contact' => $r[0]); }
function profiles_post(&$a) { if (!local_user()) { notice(t('Permission denied.') . EOL); return; } $namechanged = false; call_hooks('profile_post', $_POST); if ($a->argc > 1 && $a->argv[1] !== "new" && intval($a->argv[1])) { $orig = q("SELECT * FROM `profile` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($a->argv[1]), intval(local_user())); if (!count($orig)) { notice(t('Profile not found.') . EOL); return; } $is_default = $orig[0]['is-default'] ? 1 : 0; $profile_name = notags(trim($_POST['profile_name'])); if (!strlen($profile_name)) { notify(t('Profile Name is required.') . EOL); return; } $year = intval($_POST['year']); if ($year < 1900 || $year > 2100 || $year < 0) { $year = 0; } $month = intval($_POST['month']); if ($month > 12 || $month < 0) { $month = 0; } $mtab = array(0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); $day = intval($_POST['day']); if ($day > $mtab[$month] || $day < 0) { $day = 0; } $dob = '0000-00-00'; $dob = sprintf('%04d-%02d-%02d', $year, $month, $day); $name = notags(trim($_POST['name'])); if ($orig[0]['name'] != $name) { $namechanged = true; } $pdesc = notags(trim($_POST['pdesc'])); $gender = notags(trim($_POST['gender'])); $address = notags(trim($_POST['address'])); $locality = notags(trim($_POST['locality'])); $region = notags(trim($_POST['region'])); $postal_code = notags(trim($_POST['postal_code'])); $country_name = notags(trim($_POST['country_name'])); $pub_keywords = notags(trim($_POST['pub_keywords'])); $prv_keywords = notags(trim($_POST['prv_keywords'])); $marital = notags(trim($_POST['marital'])); if ($marital != $orig[0]['marital']) { $maritalchanged = true; } $with = x($_POST, 'with') ? notags(trim($_POST['with'])) : ''; // linkify the relationship target if applicable if (strlen($with)) { if ($with != strip_tags($orig[0]['with'])) { $prf = ''; $lookup = $with; if (strpos($lookup, '@') === 0) { $lookup = substr($lookup, 1); } $lookup = str_replace('_', ' ', $lookup); if (strpos($lookup, '@') || strpos($lookup, 'http://')) { $newname = $lookup; $links = @lrdd($lookup); if (count($links)) { foreach ($links as $link) { if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { $prf = $link['@attributes']['href']; } } } } else { $newname = $lookup; if (strstr($lookup, ' ')) { $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1", dbesc($newname), intval(local_user())); } else { $r = q("SELECT * FROM `contact` WHERE `nick` = '%s' AND `uid` = %d LIMIT 1", dbesc($lookup), intval(local_user())); } if (count($r)) { $prf = $r[0]['url']; $newname = $r[0]['name']; } } if ($prf) { $with = str_replace($lookup, '<a href="' . $prf . '">' . $newname . '</a>', $with); if (strpos($with, '@') === 0) { $with = substr($with, 1); } } } else { $with = $orig[0]['with']; } } $sexual = notags(trim($_POST['sexual'])); $homepage = notags(trim($_POST['homepage'])); $politic = notags(trim($_POST['politic'])); $religion = notags(trim($_POST['religion'])); $about = escape_tags(trim($_POST['about'])); $interest = escape_tags(trim($_POST['interest'])); $contact = escape_tags(trim($_POST['contact'])); $music = escape_tags(trim($_POST['music'])); $book = escape_tags(trim($_POST['book'])); $tv = escape_tags(trim($_POST['tv'])); $film = escape_tags(trim($_POST['film'])); $romance = escape_tags(trim($_POST['romance'])); $work = escape_tags(trim($_POST['work'])); $education = escape_tags(trim($_POST['education'])); $hide_friends = $_POST['hide-friends'] == 1 ? 1 : 0; $r = q("UPDATE `profile` \n\t\t\tSET `profile-name` = '%s',\n\t\t\t`name` = '%s',\n\t\t\t`pdesc` = '%s',\n\t\t\t`gender` = '%s',\n\t\t\t`dob` = '%s',\n\t\t\t`address` = '%s',\n\t\t\t`locality` = '%s',\n\t\t\t`region` = '%s',\n\t\t\t`postal-code` = '%s',\n\t\t\t`country-name` = '%s',\n\t\t\t`marital` = '%s',\n\t\t\t`with` = '%s',\n\t\t\t`sexual` = '%s',\n\t\t\t`homepage` = '%s',\n\t\t\t`politic` = '%s',\n\t\t\t`religion` = '%s',\n\t\t\t`pub_keywords` = '%s',\n\t\t\t`prv_keywords` = '%s',\n\t\t\t`about` = '%s',\n\t\t\t`interest` = '%s',\n\t\t\t`contact` = '%s',\n\t\t\t`music` = '%s',\n\t\t\t`book` = '%s',\n\t\t\t`tv` = '%s',\n\t\t\t`film` = '%s',\n\t\t\t`romance` = '%s',\n\t\t\t`work` = '%s',\n\t\t\t`education` = '%s',\n\t\t\t`hide-friends` = %d\n\t\t\tWHERE `id` = %d AND `uid` = %d LIMIT 1", dbesc($profile_name), dbesc($name), dbesc($pdesc), dbesc($gender), dbesc($dob), dbesc($address), dbesc($locality), dbesc($region), dbesc($postal_code), dbesc($country_name), dbesc($marital), dbesc($with), dbesc($sexual), dbesc($homepage), dbesc($politic), dbesc($religion), dbesc($pub_keywords), dbesc($prv_keywords), dbesc($about), dbesc($interest), dbesc($contact), dbesc($music), dbesc($book), dbesc($tv), dbesc($film), dbesc($romance), dbesc($work), dbesc($education), intval($hide_friends), intval($a->argv[1]), intval($_SESSION['uid'])); if ($r) { info(t('Profile updated.') . EOL); } if ($namechanged && $is_default) { $r = q("UPDATE `contact` SET `name-date` = '%s' WHERE `self` = 1 AND `uid` = %d LIMIT 1", dbesc(datetime_convert()), intval(local_user())); } if ($is_default) { // Update global directory in background $url = $_SESSION['my_url']; if ($url && strlen(get_config('system', 'directory_submit_url'))) { proc_run('php', "include/directory.php", "{$url}"); } require_once 'include/profile_update.php'; profile_change(); } } }
function profiles_post(&$a) { if (!local_user()) { notice(t('Permission denied.') . EOL); return; } $namechanged = false; call_hooks('profile_post', $_POST); if ($a->argc > 1 && $a->argv[1] !== "new" && intval($a->argv[1])) { $orig = q("SELECT * FROM `profile` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($a->argv[1]), intval(local_user())); if (!count($orig)) { notice(t('Profile not found.') . EOL); return; } check_form_security_token_redirectOnErr('/profiles', 'profile_edit'); $is_default = $orig[0]['is-default'] ? 1 : 0; $profile_name = notags(trim($_POST['profile_name'])); if (!strlen($profile_name)) { notice(t('Profile Name is required.') . EOL); return; } $dob = $_POST['dob'] ? escape_tags(trim($_POST['dob'])) : '0000-00-00'; // FIXME: Needs to be validated? $y = substr($dob, 0, 4); if (!ctype_digit($y) || $y < 1900) { $ignore_year = true; } else { $ignore_year = false; } if ($dob != '0000-00-00') { if (strpos($dob, '0000-') === 0) { $ignore_year = true; $dob = substr($dob, 5); } $dob = datetime_convert('UTC', 'UTC', $ignore_year ? '1900-' . $dob : $dob, $ignore_year ? 'm-d' : 'Y-m-d'); if ($ignore_year) { $dob = '0000-' . $dob; } } $name = notags(trim($_POST['name'])); if (!strlen($name)) { $name = '[No Name]'; } if ($orig[0]['name'] != $name) { $namechanged = true; } $pdesc = notags(trim($_POST['pdesc'])); $gender = notags(trim($_POST['gender'])); $address = notags(trim($_POST['address'])); $locality = notags(trim($_POST['locality'])); $region = notags(trim($_POST['region'])); $postal_code = notags(trim($_POST['postal_code'])); $country_name = notags(trim($_POST['country_name'])); $pub_keywords = profile_clean_keywords(notags(trim($_POST['pub_keywords']))); $prv_keywords = profile_clean_keywords(notags(trim($_POST['prv_keywords']))); $marital = notags(trim($_POST['marital'])); $howlong = notags(trim($_POST['howlong'])); $with = x($_POST, 'with') ? notags(trim($_POST['with'])) : ''; if (!strlen($howlong)) { $howlong = '0000-00-00 00:00:00'; } else { $howlong = datetime_convert(date_default_timezone_get(), 'UTC', $howlong); } // linkify the relationship target if applicable $withchanged = false; if (strlen($with)) { if ($with != strip_tags($orig[0]['with'])) { $withchanged = true; $prf = ''; $lookup = $with; if (strpos($lookup, '@') === 0) { $lookup = substr($lookup, 1); } $lookup = str_replace('_', ' ', $lookup); if (strpos($lookup, '@') || strpos($lookup, 'http://')) { $newname = $lookup; $links = @lrdd($lookup); if (count($links)) { foreach ($links as $link) { if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { $prf = $link['@attributes']['href']; } } } } else { $newname = $lookup; /* if(strstr($lookup,' ')) { $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1", dbesc($newname), intval(local_user()) ); } else { $r = q("SELECT * FROM `contact` WHERE `nick` = '%s' AND `uid` = %d LIMIT 1", dbesc($lookup), intval(local_user()) ); }*/ $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1", dbesc($newname), intval(local_user())); if (!$r) { $r = q("SELECT * FROM `contact` WHERE `nick` = '%s' AND `uid` = %d LIMIT 1", dbesc($lookup), intval(local_user())); } if (count($r)) { $prf = $r[0]['url']; $newname = $r[0]['name']; } } if ($prf) { $with = str_replace($lookup, '<a href="' . $prf . '">' . $newname . '</a>', $with); if (strpos($with, '@') === 0) { $with = substr($with, 1); } } } else { $with = $orig[0]['with']; } } $sexual = notags(trim($_POST['sexual'])); $homepage = notags(trim($_POST['homepage'])); if (strpos($homepage, 'http') !== 0 && strlen($homepage)) { // neither http nor https in URL, add them $homepage = 'http://' . $homepage; } $hometown = notags(trim($_POST['hometown'])); $politic = notags(trim($_POST['politic'])); $religion = notags(trim($_POST['religion'])); $likes = fix_mce_lf(escape_tags(trim($_POST['likes']))); $dislikes = fix_mce_lf(escape_tags(trim($_POST['dislikes']))); $about = fix_mce_lf(escape_tags(trim($_POST['about']))); $interest = fix_mce_lf(escape_tags(trim($_POST['interest']))); $contact = fix_mce_lf(escape_tags(trim($_POST['contact']))); $music = fix_mce_lf(escape_tags(trim($_POST['music']))); $book = fix_mce_lf(escape_tags(trim($_POST['book']))); $tv = fix_mce_lf(escape_tags(trim($_POST['tv']))); $film = fix_mce_lf(escape_tags(trim($_POST['film']))); $romance = fix_mce_lf(escape_tags(trim($_POST['romance']))); $work = fix_mce_lf(escape_tags(trim($_POST['work']))); $education = fix_mce_lf(escape_tags(trim($_POST['education']))); $hide_friends = $_POST['hide-friends'] == 1 ? 1 : 0; $changes = array(); $value = ''; if ($is_default) { if ($marital != $orig[0]['marital']) { $changes[] = '[color=#ff0000]♥[/color] ' . t('Marital Status'); $value = $marital; } if ($withchanged) { $changes[] = '[color=#ff0000]♥[/color] ' . t('Romantic Partner'); $value = strip_tags($with); } if ($likes != $orig[0]['likes']) { $changes[] = t('Likes'); $value = $likes; } if ($dislikes != $orig[0]['dislikes']) { $changes[] = t('Dislikes'); $value = $dislikes; } if ($work != $orig[0]['work']) { $changes[] = t('Work/Employment'); } if ($religion != $orig[0]['religion']) { $changes[] = t('Religion'); $value = $religion; } if ($politic != $orig[0]['politic']) { $changes[] = t('Political Views'); $value = $politic; } if ($gender != $orig[0]['gender']) { $changes[] = t('Gender'); $value = $gender; } if ($sexual != $orig[0]['sexual']) { $changes[] = t('Sexual Preference'); $value = $sexual; } if ($homepage != $orig[0]['homepage']) { $changes[] = t('Homepage'); $value = $homepage; } if ($interest != $orig[0]['interest']) { $changes[] = t('Interests'); $value = $interest; } if ($address != $orig[0]['address']) { $changes[] = t('Address'); // New address not sent in notifications, potential privacy issues // in case this leaks to unintended recipients. Yes, it's in the public // profile but that doesn't mean we have to broadcast it to everybody. } if ($locality != $orig[0]['locality'] || $region != $orig[0]['region'] || $country_name != $orig[0]['country-name']) { $changes[] = t('Location'); $comma1 = $locality && ($region || $country_name) ? ', ' : ' '; $comma2 = $region && $country_name ? ', ' : ''; $value = $locality . $comma1 . $region . $comma2 . $country_name; } profile_activity($changes, $value); } $r = q("UPDATE `profile`\n\t\t\tSET `profile-name` = '%s',\n\t\t\t`name` = '%s',\n\t\t\t`pdesc` = '%s',\n\t\t\t`gender` = '%s',\n\t\t\t`dob` = '%s',\n\t\t\t`address` = '%s',\n\t\t\t`locality` = '%s',\n\t\t\t`region` = '%s',\n\t\t\t`postal-code` = '%s',\n\t\t\t`country-name` = '%s',\n\t\t\t`marital` = '%s',\n\t\t\t`with` = '%s',\n\t\t\t`howlong` = '%s',\n\t\t\t`sexual` = '%s',\n\t\t\t`homepage` = '%s',\n\t\t\t`hometown` = '%s',\n\t\t\t`politic` = '%s',\n\t\t\t`religion` = '%s',\n\t\t\t`pub_keywords` = '%s',\n\t\t\t`prv_keywords` = '%s',\n\t\t\t`likes` = '%s',\n\t\t\t`dislikes` = '%s',\n\t\t\t`about` = '%s',\n\t\t\t`interest` = '%s',\n\t\t\t`contact` = '%s',\n\t\t\t`music` = '%s',\n\t\t\t`book` = '%s',\n\t\t\t`tv` = '%s',\n\t\t\t`film` = '%s',\n\t\t\t`romance` = '%s',\n\t\t\t`work` = '%s',\n\t\t\t`education` = '%s',\n\t\t\t`hide-friends` = %d\n\t\t\tWHERE `id` = %d AND `uid` = %d", dbesc($profile_name), dbesc($name), dbesc($pdesc), dbesc($gender), dbesc($dob), dbesc($address), dbesc($locality), dbesc($region), dbesc($postal_code), dbesc($country_name), dbesc($marital), dbesc($with), dbesc($howlong), dbesc($sexual), dbesc($homepage), dbesc($hometown), dbesc($politic), dbesc($religion), dbesc($pub_keywords), dbesc($prv_keywords), dbesc($likes), dbesc($dislikes), dbesc($about), dbesc($interest), dbesc($contact), dbesc($music), dbesc($book), dbesc($tv), dbesc($film), dbesc($romance), dbesc($work), dbesc($education), intval($hide_friends), intval($a->argv[1]), intval(local_user())); if ($r) { info(t('Profile updated.') . EOL); } if ($namechanged && $is_default) { $r = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `self` = 1 AND `uid` = %d", dbesc($name), dbesc(datetime_convert()), intval(local_user())); $r = q("UPDATE `user` set `username` = '%s' where `uid` = %d", dbesc($name), intval(local_user())); } if ($is_default) { $location = $locality; if ($region != "") { if ($location != "") { $location .= ", "; } $location .= $region; } if ($country_name != "") { if ($location != "") { $location .= ", "; } $location .= $country_name; } $r = q("UPDATE `contact` SET `about` = '%s', `location` = '%s', `keywords` = '%s', `gender` = '%s' WHERE `self` = 1 AND `uid` = %d", dbesc($about), dbesc($location), dbesc($pub_keywords), dbesc($gender), intval(local_user())); // Update global directory in background $url = $_SESSION['my_url']; if ($url && strlen(get_config('system', 'directory'))) { proc_run('php', "include/directory.php", "{$url}"); } require_once 'include/profile_update.php'; profile_change(); } } }
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 item_post(&$a) { if (!local_user() && !remote_user()) { return; } require_once 'include/security.php'; $uid = local_user(); if (x($_POST, 'dropitems')) { require_once 'include/items.php'; $arr_drop = explode(',', $_POST['dropitems']); drop_items($arr_drop); $json = array('success' => 1); echo json_encode($json); killme(); } call_hooks('post_local_start', $_POST); $api_source = x($_POST, 'api_source') && $_POST['api_source'] ? true : false; $return_path = x($_POST, 'return') ? $_POST['return'] : ''; /** * Is this a reply to something? */ $parent = x($_POST, 'parent') ? intval($_POST['parent']) : 0; $parent_uri = x($_POST, 'parent_uri') ? trim($_POST['parent_uri']) : ''; $parent_item = null; $parent_contact = null; $thr_parent = ''; $parid = 0; $r = false; $preview = x($_POST, 'preview') ? intval($_POST['preview']) : 0; if ($parent || $parent_uri) { if (!x($_POST, 'type')) { $_POST['type'] = 'net-comment'; } if ($parent) { $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1", intval($parent)); } elseif ($parent_uri && local_user()) { // This is coming from an API source, and we are logged in $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($parent_uri), intval(local_user())); } // if this isn't the real parent of the conversation, find it if ($r !== false && count($r)) { $parid = $r[0]['parent']; if ($r[0]['id'] != $r[0]['parent']) { $r = q("SELECT * FROM `item` WHERE `id` = `parent` AND `parent` = %d LIMIT 1", intval($parid)); } } if ($r === false || !count($r)) { notice(t('Unable to locate original post.') . EOL); if (x($_POST, 'return')) { goaway($a->get_baseurl() . "/" . $return_path); } killme(); } $parent_item = $r[0]; $parent = $r[0]['id']; // multi-level threading - preserve the info but re-parent to our single level threading if ($parid && $parid != $parent) { $thr_parent = $parent_uri; } if ($parent_item['contact-id'] && $uid) { $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($parent_item['contact-id']), intval($uid)); if (count($r)) { $parent_contact = $r[0]; } } } if ($parent) { logger('mod_post: parent=' . $parent); } $profile_uid = x($_POST, 'profile_uid') ? intval($_POST['profile_uid']) : 0; $post_id = x($_POST['post_id']) ? intval($_POST['post_id']) : 0; $app = x($_POST['source']) ? strip_tags($_POST['source']) : ''; if (!can_write_wall($a, $profile_uid)) { notice(t('Permission denied.') . EOL); if (x($_POST, 'return')) { goaway($a->get_baseurl() . "/" . $return_path); } killme(); } // is this an edited post? $orig_post = null; if ($post_id) { $i = q("SELECT * FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1", intval($profile_uid), intval($post_id)); if (!count($i)) { killme(); } $orig_post = $i[0]; } $user = null; $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($profile_uid)); if (count($r)) { $user = $r[0]; } if ($orig_post) { $str_group_allow = $orig_post['allow_gid']; $str_contact_allow = $orig_post['allow_cid']; $str_group_deny = $orig_post['deny_gid']; $str_contact_deny = $orig_post['deny_cid']; $title = $orig_post['title']; $location = $orig_post['location']; $coord = $orig_post['coord']; $verb = $orig_post['verb']; $emailcc = $orig_post['emailcc']; $app = $orig_post['app']; $body = escape_tags(trim($_POST['body'])); $private = $orig_post['private']; $pubmail_enable = $orig_post['pubmail']; } else { $str_group_allow = perms2str($_POST['group_allow']); $str_contact_allow = perms2str($_POST['contact_allow']); $str_group_deny = perms2str($_POST['group_deny']); $str_contact_deny = perms2str($_POST['contact_deny']); $title = notags(trim($_POST['title'])); $location = notags(trim($_POST['location'])); $coord = notags(trim($_POST['coord'])); $verb = notags(trim($_POST['verb'])); $emailcc = notags(trim($_POST['emailcc'])); $body = escape_tags(trim($_POST['body'])); $private = strlen($str_group_allow) || strlen($str_contact_allow) || strlen($str_group_deny) || strlen($str_contact_deny) ? 1 : 0; if ($parent_item && ($parent_item['private'] || strlen($parent_item['allow_cid']) || strlen($parent_item['allow_gid']) || strlen($parent_item['deny_cid']) || strlen($parent_item['deny_gid']))) { $private = 1; } $pubmail_enable = x($_POST, 'pubmail_enable') && intval($_POST['pubmail_enable']) && !$private ? 1 : 0; // if using the API, we won't see pubmail_enable - figure out if it should be set if ($api_source && $profile_uid && $profile_uid == local_user() && !$private) { $mail_disabled = function_exists('imap_open') && !get_config('system', 'imap_disabled') ? 0 : 1; if (!$mail_disabled) { $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d AND `server` != '' LIMIT 1", intval(local_user())); if (count($r) && intval($r[0]['pubmail'])) { $pubmail_enabled = true; } } } if (!strlen($body)) { info(t('Empty post discarded.') . EOL); if (x($_POST, 'return')) { goaway($a->get_baseurl() . "/" . $return_path); } killme(); } } if ($api_source && !array_key_exists('allow_cid', $_REQUEST) && !array_key_exists('allow_gid', $_REQUEST) && !array_key_exists('deny_cid', $_REQUEST) && !array_key_exists('deny_gid', $_REQUEST)) { $str_group_allow = $user['allow_gid']; $str_contact_allow = $user['allow_cid']; $str_group_deny = $user['deny_gid']; $str_contact_deny = $user['deny_cid']; } // get contact info for poster $author = null; $self = false; if ($_SESSION['uid'] && $_SESSION['uid'] == $profile_uid) { $self = true; $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 1 LIMIT 1", intval($_SESSION['uid'])); } else { if (x($_SESSION, 'visitor_id') && intval($_SESSION['visitor_id'])) { $r = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1", intval($_SESSION['visitor_id'])); } } if (count($r)) { $author = $r[0]; $contact_id = $author['id']; } // get contact info for owner if ($profile_uid == $_SESSION['uid']) { $contact_record = $author; } else { $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 1 LIMIT 1", intval($profile_uid)); if (count($r)) { $contact_record = $r[0]; } } $post_type = notags(trim($_POST['type'])); if ($post_type === 'net-comment') { if ($parent_item !== null) { if ($parent_item['wall'] == 1) { $post_type = 'wall-comment'; } else { $post_type = 'remote-comment'; } } } /** * * When a photo was uploaded into the message using the (profile wall) ajax * uploader, The permissions are initially set to disallow anybody but the * owner from seeing it. This is because the permissions may not yet have been * set for the post. If it's private, the photo permissions should be set * appropriately. But we didn't know the final permissions on the post until * now. So now we'll look for links of uploaded messages that are in the * post and set them to the same permissions as the post itself. * */ $match = null; if (!$preview && preg_match_all("/\\[img\\](.*?)\\[\\/img\\]/", $body, $match)) { $images = $match[1]; if (count($images)) { foreach ($images as $image) { if (!stristr($image, $a->get_baseurl() . '/photo/')) { continue; } $image_uri = substr($image, strrpos($image, '/') + 1); $image_uri = substr($image_uri, 0, strpos($image_uri, '-')); if (!strlen($image_uri)) { continue; } $srch = '<' . intval($profile_uid) . '>'; $r = q("SELECT `id` FROM `photo` WHERE `allow_cid` = '%s' AND `allow_gid` = '' AND `deny_cid` = '' AND `deny_gid` = ''\n\t\t\t\t\tAND `resource-id` = '%s' AND `uid` = %d LIMIT 1", dbesc($srch), dbesc($image_uri), intval($profile_uid)); if (!count($r)) { continue; } $r = q("UPDATE `photo` SET `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s'\n\t\t\t\t\tWHERE `resource-id` = '%s' AND `uid` = %d AND `album` = '%s' ", dbesc($str_contact_allow), dbesc($str_group_allow), dbesc($str_contact_deny), dbesc($str_group_deny), dbesc($image_uri), intval($profile_uid), dbesc(t('Wall Photos'))); } } } /** * Next link in any attachment references we find in the post. */ $match = false; if (!$preview && preg_match_all("/\\[attachment\\](.*?)\\[\\/attachment\\]/", $body, $match)) { $attaches = $match[1]; if (count($attaches)) { foreach ($attaches as $attach) { $r = q("SELECT * FROM `attach` WHERE `uid` = %d AND `id` = %d LIMIT 1", intval($profile_uid), intval($attach)); if (count($r)) { $r = q("UPDATE `attach` SET `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s'\n\t\t\t\t\t\tWHERE `uid` = %d AND `id` = %d LIMIT 1", dbesc($str_contact_allow), dbesc($str_group_allow), dbesc($str_contact_deny), dbesc($str_group_deny), intval($profile_uid), intval($attach)); } } } } // embedded bookmark in post? set bookmark flag $bookmark = 0; if (preg_match_all("/\\[bookmark\\=([^\\]]*)\\](.*?)\\[\\/bookmark\\]/ism", $body, $match, PREG_SET_ORDER)) { $bookmark = 1; } $body = bb_translate_video($body); /** * Fold multi-line [code] sequences */ $body = preg_replace('/\\[\\/code\\]\\s*\\[code\\]/ism', "\n", $body); /** * Look for any tags and linkify them */ $str_tags = ''; $inform = ''; $tags = get_tags($body); /** * add a statusnet style reply tag if the original post was from there * and we are replying, and there isn't one already */ if ($parent_contact && $parent_contact['network'] === NETWORK_OSTATUS && $parent_contact['nick'] && !in_array('@' . $parent_contact['nick'], $tags)) { $body = '@' . $parent_contact['nick'] . ' ' . $body; $tags[] = '@' . $parent_contact['nick']; } if (count($tags)) { foreach ($tags as $tag) { if (isset($profile)) { unset($profile); } if (strpos($tag, '#') === 0) { if (strpos($tag, '[url=')) { continue; } $basetag = str_replace('_', ' ', substr($tag, 1)); $body = str_replace($tag, '#[url=' . $a->get_baseurl() . '/search?search=' . rawurlencode($basetag) . ']' . $basetag . '[/url]', $body); $newtag = '#[url=' . $a->get_baseurl() . '/search?search=' . rawurlencode($basetag) . ']' . $basetag . '[/url]'; if (!stristr($str_tags, $newtag)) { if (strlen($str_tags)) { $str_tags .= ','; } $str_tags .= $newtag; } continue; } if (strpos($tag, '@') === 0) { if (strpos($tag, '[url=')) { continue; } $stat = false; $name = substr($tag, 1); if (strpos($name, '@') || strpos($name, 'http://')) { $newname = $name; $links = @lrdd($name); if (count($links)) { foreach ($links as $link) { if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { $profile = $link['@attributes']['href']; } if ($link['@attributes']['rel'] === 'salmon') { if (strlen($inform)) { $inform .= ','; } $inform .= 'url:' . str_replace(',', '%2c', $link['@attributes']['href']); } } } } else { $newname = $name; $alias = ''; $tagcid = 0; if (strrpos($newname, '+')) { $tagcid = intval(substr($newname, strrpos($newname, '+') + 1)); if (strpos($name, ' ')) { $name = substr($name, 0, strpos($name, ' ')); } } if ($tagcid) { $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($tagcid), intval($profile_uid)); } elseif (strstr($name, '_') || strstr($name, ' ')) { $newname = str_replace('_', ' ', $name); $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1", dbesc($newname), intval($profile_uid)); } else { $r = q("SELECT * FROM `contact` WHERE `attag` = '%s' OR `nick` = '%s' AND `uid` = %d ORDER BY `attag` DESC LIMIT 1", dbesc($name), dbesc($name), intval($profile_uid)); } if (count($r)) { $profile = $r[0]['url']; if ($r[0]['network'] === 'stat') { $newname = $r[0]['nick']; $stat = true; if ($r[0]['alias']) { $alias = $r[0]['alias']; } } else { $newname = $r[0]['name']; } if (strlen($inform)) { $inform .= ','; } $inform .= 'cid:' . $r[0]['id']; } } if ($profile) { $body = str_replace('@' . $name, '@' . '[url=' . $profile . ']' . $newname . '[/url]', $body); $profile = str_replace(',', '%2c', $profile); $newtag = '@[url=' . $profile . ']' . $newname . '[/url]'; if (!stristr($str_tags, $newtag)) { if (strlen($str_tags)) { $str_tags .= ','; } $str_tags .= $newtag; } // Status.Net seems to require the numeric ID URL in a mention if the person isn't // subscribed to you. But the nickname URL is OK if they are. Grrr. We'll tag both. if (strlen($alias)) { $newtag = '@[url=' . $alias . ']' . $newname . '[/url]'; if (!stristr($str_tags, $newtag)) { if (strlen($str_tags)) { $str_tags .= ','; } $str_tags .= $newtag; } } } } } } $attachments = ''; $match = false; if (preg_match_all('/(\\[attachment\\]([0-9]+)\\[\\/attachment\\])/', $body, $match)) { foreach ($match[2] as $mtch) { $r = q("SELECT `id`,`filename`,`filesize`,`filetype` FROM `attach` WHERE `uid` = %d AND `id` = %d LIMIT 1", intval($profile_uid), intval($mtch)); if (count($r)) { if (strlen($attachments)) { $attachments .= ','; } $attachments .= '[attach]href="' . $a->get_baseurl() . '/attach/' . $r[0]['id'] . '" length="' . $r[0]['filesize'] . '" type="' . $r[0]['filetype'] . '" title="' . ($r[0]['filename'] ? $r[0]['filename'] : '') . '"[/attach]'; } $body = str_replace($match[1], '', $body); } } $wall = 0; if ($post_type === 'wall' || $post_type === 'wall-comment') { $wall = 1; } if (!strlen($verb)) { $verb = ACTIVITY_POST; } $gravity = $parent ? 6 : 0; // even if the post arrived via API we are considering that it // originated on this site by default for determining relayability. $origin = x($_REQUEST, 'origin') ? intval($_REQUEST['origin']) : 1; $notify_type = $parent ? 'comment-new' : 'wall-new'; $uri = item_new_uri($a->get_hostname(), $profile_uid); $datarray = array(); $datarray['uid'] = $profile_uid; $datarray['type'] = $post_type; $datarray['wall'] = $wall; $datarray['gravity'] = $gravity; $datarray['contact-id'] = $contact_id; $datarray['owner-name'] = $contact_record['name']; $datarray['owner-link'] = $contact_record['url']; $datarray['owner-avatar'] = $contact_record['thumb']; $datarray['author-name'] = $author['name']; $datarray['author-link'] = $author['url']; $datarray['author-avatar'] = $author['thumb']; $datarray['created'] = datetime_convert(); $datarray['edited'] = datetime_convert(); $datarray['commented'] = datetime_convert(); $datarray['received'] = datetime_convert(); $datarray['changed'] = datetime_convert(); $datarray['uri'] = $uri; $datarray['title'] = $title; $datarray['body'] = $body; $datarray['app'] = $app; $datarray['location'] = $location; $datarray['coord'] = $coord; $datarray['tag'] = $str_tags; $datarray['inform'] = $inform; $datarray['verb'] = $verb; $datarray['allow_cid'] = $str_contact_allow; $datarray['allow_gid'] = $str_group_allow; $datarray['deny_cid'] = $str_contact_deny; $datarray['deny_gid'] = $str_group_deny; $datarray['private'] = $private; $datarray['pubmail'] = $pubmail_enable; $datarray['attach'] = $attachments; $datarray['bookmark'] = intval($bookmark); $datarray['thr-parent'] = $thr_parent; $datarray['postopts'] = ''; $datarray['origin'] = $origin; /** * These fields are for the convenience of plugins... * 'self' if true indicates the owner is posting on their own wall * If parent is 0 it is a top-level post. */ $datarray['parent'] = $parent; $datarray['self'] = $self; // $datarray['prvnets'] = $user['prvnets']; if ($orig_post) { $datarray['edit'] = true; } else { $datarray['guid'] = get_guid(); } // preview mode - prepare the body for display and send it via json if ($preview) { require_once 'include/conversation.php'; $o = conversation(&$a, array(array_merge($contact_record, $datarray)), 'search', false, true); echo json_encode(array('preview' => $o)); killme(); } call_hooks('post_local', $datarray); if ($orig_post) { $r = q("UPDATE `item` SET `body` = '%s', `edited` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1", dbesc($body), dbesc(datetime_convert()), intval($post_id), intval($profile_uid)); proc_run('php', "include/notifier.php", 'edit_post', "{$post_id}"); if (x($_POST, 'return') && strlen($return_path)) { logger('return: ' . $return_path); goaway($a->get_baseurl() . "/" . $return_path); } killme(); } else { $post_id = 0; } $r = q("INSERT INTO `item` (`guid`, `uid`,`type`,`wall`,`gravity`,`contact-id`,`owner-name`,`owner-link`,`owner-avatar`, \n\t\t`author-name`, `author-link`, `author-avatar`, `created`, `edited`, `commented`, `received`, `changed`, `uri`, `thr-parent`, `title`, `body`, `app`, `location`, `coord`, \n\t\t`tag`, `inform`, `verb`, `postopts`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid`, `private`, `pubmail`, `attach`, `bookmark`,`origin` )\n\t\tVALUES( '%s', %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d )", dbesc($datarray['guid']), intval($datarray['uid']), dbesc($datarray['type']), intval($datarray['wall']), intval($datarray['gravity']), intval($datarray['contact-id']), dbesc($datarray['owner-name']), dbesc($datarray['owner-link']), dbesc($datarray['owner-avatar']), dbesc($datarray['author-name']), dbesc($datarray['author-link']), dbesc($datarray['author-avatar']), dbesc($datarray['created']), dbesc($datarray['edited']), dbesc($datarray['commented']), dbesc($datarray['received']), dbesc($datarray['changed']), dbesc($datarray['uri']), dbesc($datarray['thr-parent']), dbesc($datarray['title']), dbesc($datarray['body']), dbesc($datarray['app']), dbesc($datarray['location']), dbesc($datarray['coord']), dbesc($datarray['tag']), dbesc($datarray['inform']), dbesc($datarray['verb']), dbesc($datarray['postopts']), dbesc($datarray['allow_cid']), dbesc($datarray['allow_gid']), dbesc($datarray['deny_cid']), dbesc($datarray['deny_gid']), intval($datarray['private']), intval($datarray['pubmail']), dbesc($datarray['attach']), intval($datarray['bookmark']), intval($datarray['origin'])); $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1", dbesc($datarray['uri'])); if (count($r)) { $post_id = $r[0]['id']; logger('mod_item: saved item ' . $post_id); if ($parent) { // This item is the last leaf and gets the comment box, clear any ancestors $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent` = %d ", dbesc(datetime_convert()), intval($parent)); // Inherit ACL's from the parent item. $r = q("UPDATE `item` SET `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s', `private` = %d\n\t\t\t\tWHERE `id` = %d LIMIT 1", dbesc($parent_item['allow_cid']), dbesc($parent_item['allow_gid']), dbesc($parent_item['deny_cid']), dbesc($parent_item['deny_gid']), intval($parent_item['private']), intval($post_id)); if ($contact_record != $author) { notification(array('type' => NOTIFY_COMMENT, 'notify_flags' => $user['notify-flags'], 'language' => $user['language'], 'to_name' => $user['username'], 'to_email' => $user['email'], 'item' => $datarray, 'link' => $a->get_baseurl() . '/display/' . $user['nickname'] . '/' . $post_id, 'source_name' => $datarray['author-name'], 'source_link' => $datarray['author-link'], 'source_photo' => $datarray['author-avatar'], 'verb' => ACTIVITY_POST, 'otype' => 'item')); } // We won't be able to sign Diaspora comments for authenticated visitors - we don't have their private key if ($self) { require_once 'include/bb2diaspora.php'; $signed_body = html_entity_decode(bb2diaspora($datarray['body'])); $myaddr = $a->user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(), '://') + 3); if ($datarray['verb'] === ACTIVITY_LIKE) { $signed_text = $datarray['guid'] . ';' . 'Post' . ';' . $parent_item['guid'] . ';' . 'true' . ';' . $myaddr; } else { $signed_text = $datarray['guid'] . ';' . $parent_item['guid'] . ';' . $signed_body . ';' . $myaddr; } $authorsig = base64_encode(rsa_sign($signed_text, $a->user['prvkey'], 'sha256')); q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", intval($post_id), dbesc($signed_text), dbesc(base64_encode($authorsig)), dbesc($myaddr)); } } else { $parent = $post_id; if ($contact_record != $author) { notification(array('type' => NOTIFY_WALL, 'notify_flags' => $user['notify-flags'], 'language' => $user['language'], 'to_name' => $user['username'], 'to_email' => $user['email'], 'item' => $datarray, 'link' => $a->get_baseurl() . '/display/' . $user['nickname'] . '/' . $post_id, 'source_name' => $datarray['author-name'], 'source_link' => $datarray['author-link'], 'source_photo' => $datarray['author-avatar'], 'verb' => ACTIVITY_POST, 'otype' => 'item')); } } // fallback so that parent always gets set to non-zero. if (!$parent) { $parent = $post_id; } $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s', `plink` = '%s', `changed` = '%s', `last-child` = 1, `visible` = 1\n\t\t\tWHERE `id` = %d LIMIT 1", intval($parent), dbesc($parent == $post_id ? $uri : $parent_item['uri']), dbesc($a->get_baseurl() . '/display/' . $user['nickname'] . '/' . $post_id), dbesc(datetime_convert()), intval($post_id)); // photo comments turn the corresponding item visible to the profile wall // This way we don't see every picture in your new photo album posted to your wall at once. // They will show up as people comment on them. if (!$parent_item['visible']) { $r = q("UPDATE `item` SET `visible` = 1 WHERE `id` = %d LIMIT 1", intval($parent_item['id'])); } } else { logger('mod_item: unable to retrieve post that was just stored.'); notify(t('System error. Post not saved.')); goaway($a->get_baseurl() . "/" . $return_path); // NOTREACHED } // update the commented timestamp on the parent q("UPDATE `item` set `commented` = '%s', `changed` = '%s' WHERE `id` = %d LIMIT 1", dbesc(datetime_convert()), dbesc(datetime_convert()), intval($parent)); $datarray['id'] = $post_id; $datarray['plink'] = $a->get_baseurl() . '/display/' . $user['nickname'] . '/' . $post_id; call_hooks('post_local_end', $datarray); if (strlen($emailcc) && $profile_uid == local_user()) { $erecips = explode(',', $emailcc); if (count($erecips)) { foreach ($erecips as $recip) { $addr = trim($recip); if (!strlen($addr)) { continue; } $disclaimer = '<hr />' . sprintf(t('This message was sent to you by %s, a member of the Friendica social network.'), $a->user['username']) . '<br />'; $disclaimer .= sprintf(t('You may visit them online at %s'), $a->get_baseurl() . '/profile/' . $a->user['nickname']) . EOL; $disclaimer .= t('Please contact the sender by replying to this post if you do not wish to receive these messages.') . EOL; $subject = '[Friendica]' . ' ' . sprintf(t('%s posted an update.'), $a->user['username']); $headers = 'From: ' . $a->user['username'] . ' <' . $a->user['email'] . '>' . "\n"; $headers .= 'MIME-Version: 1.0' . "\n"; $headers .= 'Content-Type: text/html; charset=UTF-8' . "\n"; $headers .= 'Content-Transfer-Encoding: 8bit' . "\n\n"; $link = '<a href="' . $a->get_baseurl() . '/profile/' . $a->user['nickname'] . '"><img src="' . $author['thumb'] . '" alt="' . $a->user['username'] . '" /></a><br /><br />'; $html = prepare_body($datarray); $message = '<html><body>' . $link . $html . $disclaimer . '</body></html>'; @mail($addr, $subject, $message, $headers); } } } // This is a real juggling act on shared hosting services which kill your processes // e.g. dreamhost. We used to start delivery to our native delivery agents in the background // and then run our plugin delivery from the foreground. We're now doing plugin delivery first, // because as soon as you start loading up a bunch of remote delivey processes, *this* page is // likely to get killed off. If you end up looking at an /item URL and a blank page, // it's very likely the delivery got killed before all your friends could be notified. // Currently the only realistic fixes are to use a reliable server - which precludes shared hosting, // or cut back on plugins which do remote deliveries. proc_run('php', "include/notifier.php", $notify_type, "{$post_id}"); logger('post_complete'); // figure out how to return, depending on from whence we came if ($api_source) { return; } if ($return_path) { goaway($a->get_baseurl() . "/" . $return_path); } $json = array('success' => 1); if (x($_POST, 'jsreload') && strlen($_POST['jsreload'])) { $json['reload'] = $a->get_baseurl() . '/' . $_POST['jsreload']; } logger('post_json: ' . print_r($json, true), LOGGER_DEBUG); echo json_encode($json); killme(); // NOTREACHED }
function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { require_once 'include/email.php'; $result = array(); if (!$url) { return $result; } $result = Cache::get("probe_url:" . $mode . ":" . $url); if (!is_null($result)) { $result = unserialize($result); return $result; } $network = null; $diaspora = false; $diaspora_base = ''; $diaspora_guid = ''; $diaspora_key = ''; $has_lrdd = false; $email_conversant = false; $connectornetworks = false; $appnet = false; if (strpos($url, 'twitter.com')) { $connectornetworks = true; $network = NETWORK_TWITTER; } // Twitter is deactivated since twitter closed its old API //$twitter = ((strpos($url,'twitter.com') !== false) ? true : false); $lastfm = strpos($url, 'last.fm/user') !== false ? true : false; $at_addr = strpos($url, '@') !== false ? true : false; if (!$appnet && !$lastfm && !$connectornetworks) { if (strpos($url, 'mailto:') !== false && $at_addr) { $url = str_replace('mailto:', '', $url); $links = array(); } else { $links = lrdd($url); } if (count($links)) { $has_lrdd = true; logger('probe_url: found lrdd links: ' . print_r($links, true), LOGGER_DATA); foreach ($links as $link) { if ($link['@attributes']['rel'] === NAMESPACE_ZOT) { $zot = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === NAMESPACE_DFRN) { $dfrn = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'salmon') { $notify = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === NAMESPACE_FEED) { $poll = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') { $hcard = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { $profile = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0') { $poco = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') { $diaspora_base = unamp($link['@attributes']['href']); $diaspora = true; } if ($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') { $diaspora_guid = unamp($link['@attributes']['href']); $diaspora = true; } if ($link['@attributes']['rel'] === 'diaspora-public-key') { $diaspora_key = base64_decode(unamp($link['@attributes']['href'])); if (strstr($diaspora_key, 'RSA ')) { $pubkey = rsatopem($diaspora_key); } else { $pubkey = $diaspora_key; } $diaspora = true; } if ($link['@attributes']['rel'] === 'http://ostatus.org/schema/1.0/subscribe' and $mode == PROBE_NORMAL) { $diaspora = false; } } // Status.Net can have more than one profile URL. We need to match the profile URL // to a contact on incoming messages to prevent spam, and we won't know which one // to match. So in case of two, one of them is stored as an alias. Only store URL's // and not webfinger user@host aliases. If they've got more than two non-email style // aliases, let's hope we're lucky and get one that matches the feed author-uri because // otherwise we're screwed. foreach ($links as $link) { if ($link['@attributes']['rel'] === 'alias') { if (strpos($link['@attributes']['href'], '@') === false) { if (isset($profile)) { if ($link['@attributes']['href'] !== $profile) { $alias = unamp($link['@attributes']['href']); } } else { $profile = unamp($link['@attributes']['href']); } } } } // If the profile is different from the url then the url is abviously an alias if ($alias == "" and $profile != "" and !$at_addr and normalise_link($profile) != normalise_link($url)) { $alias = $url; } } elseif ($mode == PROBE_NORMAL) { // Check email $orig_url = $url; if (strpos($orig_url, '@') && validate_email($orig_url)) { $x = q("SELECT `prvkey` FROM `user` WHERE `uid` = %d LIMIT 1", intval(local_user())); $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d AND `server` != '' LIMIT 1", intval(local_user())); if (count($x) && count($r)) { $mailbox = construct_mailbox_name($r[0]); $password = ''; openssl_private_decrypt(hex2bin($r[0]['pass']), $password, $x[0]['prvkey']); $mbox = email_connect($mailbox, $r[0]['user'], $password); if (!$mbox) { logger('probe_url: email_connect failed.'); } unset($password); } if ($mbox) { $msgs = email_poll($mbox, $orig_url); logger('probe_url: searching ' . $orig_url . ', ' . count($msgs) . ' messages found.', LOGGER_DEBUG); if (count($msgs)) { $addr = $orig_url; $network = NETWORK_MAIL; $name = substr($url, 0, strpos($url, '@')); $phost = substr($url, strpos($url, '@') + 1); $profile = 'http://' . $phost; // fix nick character range $vcard = array('fn' => $name, 'nick' => $name, 'photo' => avatar_img($url)); $notify = 'smtp ' . random_string(); $poll = 'email ' . random_string(); $priority = 0; $x = email_msg_meta($mbox, $msgs[0]); if (stristr($x[0]->from, $orig_url)) { $adr = imap_rfc822_parse_adrlist($x[0]->from, ''); } elseif (stristr($x[0]->to, $orig_url)) { $adr = imap_rfc822_parse_adrlist($x[0]->to, ''); } if (isset($adr)) { foreach ($adr as $feadr) { if (strcasecmp($feadr->mailbox, $name) == 0 && strcasecmp($feadr->host, $phost) == 0 && strlen($feadr->personal)) { $personal = imap_mime_header_decode($feadr->personal); $vcard['fn'] = ""; foreach ($personal as $perspart) { if ($perspart->charset != "default") { $vcard['fn'] .= iconv($perspart->charset, 'UTF-8//IGNORE', $perspart->text); } else { $vcard['fn'] .= $perspart->text; } } $vcard['fn'] = notags($vcard['fn']); } } } } imap_close($mbox); } } } } if ($mode == PROBE_NORMAL) { if (strlen($zot)) { $s = fetch_url($zot); if ($s) { $j = json_decode($s); if ($j) { $network = NETWORK_ZOT; $vcard = array('fn' => $j->fullname, 'nick' => $j->nickname, 'photo' => $j->photo); $profile = $j->url; $notify = $j->post; $pubkey = $j->pubkey; $poll = 'N/A'; } } } if (strlen($dfrn)) { $ret = scrape_dfrn($hcard ? $hcard : $dfrn, true); if (is_array($ret) && x($ret, 'dfrn-request')) { $network = NETWORK_DFRN; $request = $ret['dfrn-request']; $confirm = $ret['dfrn-confirm']; $notify = $ret['dfrn-notify']; $poll = $ret['dfrn-poll']; $vcard = array(); $vcard['fn'] = $ret['fn']; $vcard['nick'] = $ret['nick']; $vcard['photo'] = $ret['photo']; } } } if ($diaspora && $diaspora_base && $diaspora_guid) { if ($mode == PROBE_DIASPORA || !$notify) { $notify = $diaspora_base . 'receive/users/' . $diaspora_guid; $batch = $diaspora_base . 'receive/public'; } if (strpos($url, '@')) { $addr = str_replace('acct:', '', $url); } } if ($network !== NETWORK_ZOT && $network !== NETWORK_DFRN && $network !== NETWORK_MAIL) { if ($diaspora) { $network = NETWORK_DIASPORA; } elseif ($has_lrdd and $notify) { $network = NETWORK_OSTATUS; } if (strpos($url, '@')) { $addr = str_replace('acct:', '', $url); } $priority = 0; if ($hcard && !$vcard) { $vcard = scrape_vcard($hcard); // Google doesn't use absolute url in profile photos if (x($vcard, 'photo') && substr($vcard['photo'], 0, 1) == '/') { $h = @parse_url($hcard); if ($h) { $vcard['photo'] = $h['scheme'] . '://' . $h['host'] . $vcard['photo']; } } logger('probe_url: scrape_vcard: ' . print_r($vcard, true), LOGGER_DATA); } if ($diaspora && $addr) { // Diaspora returns the name as the nick. As the nick will never be updated, // let's use the Diaspora nickname (the first part of the handle) as the nick instead $addr_parts = explode('@', $addr); $vcard['nick'] = $addr_parts[0]; } /* if($twitter) { logger('twitter: setup'); $tid = basename($url); $tapi = 'https://api.twitter.com/1/statuses/user_timeline.rss'; if(intval($tid)) $poll = $tapi . '?user_id=' . $tid; else $poll = $tapi . '?screen_name=' . $tid; $profile = 'http://twitter.com/#!/' . $tid; //$vcard['photo'] = 'https://api.twitter.com/1/users/profile_image/' . $tid; $vcard['photo'] = 'https://api.twitter.com/1/users/profile_image?screen_name=' . $tid . '&size=bigger'; $vcard['nick'] = $tid; $vcard['fn'] = $tid; } */ if ($lastfm) { $profile = $url; $poll = str_replace(array('www.', 'last.fm/'), array('', 'ws.audioscrobbler.com/1.0/'), $url) . '/recenttracks.rss'; $vcard['nick'] = basename($url); $vcard['fn'] = $vcard['nick'] . t(' on Last.fm'); $network = NETWORK_FEED; } if (!x($vcard, 'fn')) { if (x($vcard, 'nick')) { $vcard['fn'] = $vcard['nick']; } } $check_feed = false; if (stristr($url, 'tumblr.com') && !stristr($url, '/rss')) { $poll = $url . '/rss'; $check_feed = true; // Will leave it to others to figure out how to grab the avatar, which is on the $url page in the open graph meta links } if ($appnet || !$poll) { $check_feed = true; } if (!isset($vcard) || !x($vcard, 'fn') || !$profile) { $check_feed = true; } if ($at_addr && !count($links)) { $check_feed = false; } if ($connectornetworks) { $check_feed = false; } if ($check_feed) { $feedret = scrape_feed($poll ? $poll : $url); logger('probe_url: scrape_feed ' . ($poll ? $poll : $url) . ' returns: ' . print_r($feedret, true), LOGGER_DATA); if (count($feedret) && ($feedret['feed_atom'] || $feedret['feed_rss'])) { $poll = x($feedret, 'feed_atom') ? unamp($feedret['feed_atom']) : unamp($feedret['feed_rss']); if (!x($vcard)) { $vcard = array(); } } if (x($feedret, 'photo') && !x($vcard, 'photo')) { $vcard['photo'] = $feedret['photo']; } require_once 'library/simplepie/simplepie.inc'; $feed = new SimplePie(); $xml = fetch_url($poll); logger('probe_url: fetch feed: ' . $poll . ' returns: ' . $xml, LOGGER_DATA); $a = get_app(); logger('probe_url: scrape_feed: headers: ' . $a->get_curl_headers(), LOGGER_DATA); // Don't try and parse an empty string $feed->set_raw_data($xml ? $xml : '<?xml version="1.0" encoding="utf-8" ?><xml></xml>'); $feed->init(); if ($feed->error()) { logger('probe_url: scrape_feed: Error parsing XML: ' . $feed->error()); $network = NETWORK_PHANTOM; } if (!x($vcard, 'photo')) { $vcard['photo'] = $feed->get_image_url(); } $author = $feed->get_author(); if ($author) { $vcard['fn'] = unxmlify(trim($author->get_name())); if (!$vcard['fn']) { $vcard['fn'] = trim(unxmlify($author->get_email())); } if (strpos($vcard['fn'], '@') !== false) { $vcard['fn'] = substr($vcard['fn'], 0, strpos($vcard['fn'], '@')); } $email = unxmlify($author->get_email()); if (!$profile && $author->get_link()) { $profile = trim(unxmlify($author->get_link())); } if (!$vcard['photo']) { $rawtags = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); if ($rawtags) { $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; if (x($elems, 'link') && $elems['link'][0]['attribs']['']['rel'] === 'photo') { $vcard['photo'] = $elems['link'][0]['attribs']['']['href']; } } } // Fetch fullname via poco:displayName $pocotags = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); if ($pocotags) { $elems = $pocotags[0]['child']['http://portablecontacts.net/spec/1.0']; if (isset($elems["displayName"])) { $vcard['fn'] = $elems["displayName"][0]["data"]; } if (isset($elems["preferredUsername"])) { $vcard['nick'] = $elems["preferredUsername"][0]["data"]; } } } else { $item = $feed->get_item(0); if ($item) { $author = $item->get_author(); if ($author) { $vcard['fn'] = trim(unxmlify($author->get_name())); if (!$vcard['fn']) { $vcard['fn'] = trim(unxmlify($author->get_email())); } if (strpos($vcard['fn'], '@') !== false) { $vcard['fn'] = substr($vcard['fn'], 0, strpos($vcard['fn'], '@')); } $email = unxmlify($author->get_email()); if (!$profile && $author->get_link()) { $profile = trim(unxmlify($author->get_link())); } } if (!$vcard['photo']) { $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/', 'thumbnail'); if ($rawmedia && $rawmedia[0]['attribs']['']['url']) { $vcard['photo'] = unxmlify($rawmedia[0]['attribs']['']['url']); } } if (!$vcard['photo']) { $rawtags = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); if ($rawtags) { $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; if (x($elems, 'link') && $elems['link'][0]['attribs']['']['rel'] === 'photo') { $vcard['photo'] = $elems['link'][0]['attribs']['']['href']; } } } } } // Workaround for misconfigured Friendica servers if ($network == "" and strstr($url, "/profile/")) { $noscrape = str_replace("/profile/", "/noscrape/", $url); $noscrapejson = fetch_url($noscrape); if ($noscrapejson) { $network = NETWORK_DFRN; $poco = str_replace("/profile/", "/poco/", $url); $noscrapedata = json_decode($noscrapejson, true); if (isset($noscrapedata["addr"])) { $addr = $noscrapedata["addr"]; } if (isset($noscrapedata["fn"])) { $vcard["fn"] = $noscrapedata["fn"]; } if (isset($noscrapedata["key"])) { $pubkey = $noscrapedata["key"]; } if (isset($noscrapedata["photo"])) { $vcard["photo"] = $noscrapedata["photo"]; } if (isset($noscrapedata["dfrn-request"])) { $request = $noscrapedata["dfrn-request"]; } if (isset($noscrapedata["dfrn-confirm"])) { $confirm = $noscrapedata["dfrn-confirm"]; } if (isset($noscrapedata["dfrn-notify"])) { $notify = $noscrapedata["dfrn-notify"]; } if (isset($noscrapedata["dfrn-poll"])) { $poll = $noscrapedata["dfrn-poll"]; } } } if (!$vcard['photo'] && strlen($email)) { $vcard['photo'] = avatar_img($email); } if ($poll === $profile) { $lnk = $feed->get_permalink(); } if (isset($lnk) && strlen($lnk)) { $profile = $lnk; } if (!$network) { $network = NETWORK_FEED; // If it is a feed, don't take the author name as feed name unset($vcard['fn']); } if (!x($vcard, 'fn')) { $vcard['fn'] = notags($feed->get_title()); } if (!x($vcard, 'fn')) { $vcard['fn'] = notags($feed->get_description()); } if (strpos($vcard['fn'], 'Twitter / ') !== false) { $vcard['fn'] = substr($vcard['fn'], strpos($vcard['fn'], '/') + 1); $vcard['fn'] = trim($vcard['fn']); } if (!x($vcard, 'nick')) { $vcard['nick'] = strtolower(notags(unxmlify($vcard['fn']))); if (strpos($vcard['nick'], ' ')) { $vcard['nick'] = trim(substr($vcard['nick'], 0, strpos($vcard['nick'], ' '))); } } if (!$priority) { $priority = 2; } } } if (!x($vcard, 'photo')) { $a = get_app(); $vcard['photo'] = $a->get_baseurl() . '/images/person-175.jpg'; } if (!$profile) { $profile = $url; } // No human could be associated with this link, use the URL as the contact name if ($network === NETWORK_FEED && $poll && !x($vcard, 'fn')) { $vcard['fn'] = $url; } if ($notify != "" and $poll != "") { $baseurl = matching(normalise_link($notify), normalise_link($poll)); $baseurl2 = matching($baseurl, normalise_link($profile)); if ($baseurl2 != "") { $baseurl = $baseurl2; } } if ($baseurl == "" and $notify != "") { $baseurl = matching(normalise_link($profile), normalise_link($notify)); } if ($baseurl == "" and $poll != "") { $baseurl = matching(normalise_link($profile), normalise_link($poll)); } $baseurl = rtrim($baseurl, "/"); if (strpos($url, '@') and $addr == "" and $network == NETWORK_DFRN) { $addr = str_replace('acct:', '', $url); } $vcard['fn'] = notags($vcard['fn']); $vcard['nick'] = str_replace(' ', '', notags($vcard['nick'])); $result['name'] = $vcard['fn']; $result['nick'] = $vcard['nick']; $result['url'] = $profile; $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; $result['baseurl'] = $baseurl; logger('probe_url: ' . print_r($result, true), LOGGER_DEBUG); if ($level == 1) { // Trying if it maybe a diaspora account if ($result['network'] == NETWORK_FEED or $result['addr'] == "") { require_once 'include/bbcode.php'; $address = GetProfileUsername($url, "", true); $result2 = probe_url($address, $mode, ++$level); if ($result2['network'] != "") { $result = $result2; } } // Maybe it's some non standard GNU Social installation (Single user, subfolder or no uri rewrite) if ($result['network'] == NETWORK_FEED and $result['baseurl'] != "" and $result['nick'] != "") { $addr = $result['nick'] . '@' . str_replace("http://", "", $result['baseurl']); $result2 = probe_url($addr, $mode, ++$level); if ($result2['network'] != "" and $result2['network'] != NETWORK_FEED) { $result = $result2; } } } // Only store into the cache if the value seems to be valid if ($result['network'] != NETWORK_PHANTOM) { Cache::set("probe_url:" . $mode . ":" . $url, serialize($result), CACHE_DAY); } return $result; }