diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/account.php | 38 | ||||
-rw-r--r-- | include/acl_selectors.php | 2 | ||||
-rw-r--r-- | include/api_zot.php | 22 | ||||
-rw-r--r-- | include/attach.php | 43 | ||||
-rw-r--r-- | include/bbcode.php | 57 | ||||
-rw-r--r-- | include/channel.php | 116 | ||||
-rw-r--r-- | include/connections.php | 33 | ||||
-rwxr-xr-x | include/dba/dba_driver.php | 25 | ||||
-rwxr-xr-x | include/dba/dba_pdo.php | 20 | ||||
-rw-r--r-- | include/event.php | 15 | ||||
-rw-r--r-- | include/features.php | 18 | ||||
-rw-r--r-- | include/follow.php | 16 | ||||
-rw-r--r-- | include/html2bbcode.php | 3 | ||||
-rw-r--r-- | include/hubloc.php | 38 | ||||
-rw-r--r-- | include/import.php | 142 | ||||
-rwxr-xr-x | include/items.php | 38 | ||||
-rw-r--r-- | include/js_strings.php | 1 | ||||
-rw-r--r-- | include/message.php | 4 | ||||
-rw-r--r-- | include/nav.php | 12 | ||||
-rw-r--r-- | include/network.php | 20 | ||||
-rwxr-xr-x | include/oembed.php | 31 | ||||
-rw-r--r-- | include/permissions.php | 22 | ||||
-rw-r--r-- | include/photos.php | 9 | ||||
-rw-r--r-- | include/security.php | 278 | ||||
-rw-r--r-- | include/taxonomy.php | 2 | ||||
-rw-r--r-- | include/text.php | 60 | ||||
-rw-r--r-- | include/xchan.php | 1 | ||||
-rw-r--r-- | include/zid.php | 90 | ||||
-rw-r--r-- | include/zot.php | 112 |
29 files changed, 950 insertions, 318 deletions
diff --git a/include/account.php b/include/account.php index 2ab99ce19..5f0c8737f 100644 --- a/include/account.php +++ b/include/account.php @@ -142,12 +142,10 @@ function create_account($arr) { $invite_code = ((x($arr,'invite_code')) ? notags(trim($arr['invite_code'])) : ''); $email = ((x($arr,'email')) ? notags(punify(trim($arr['email']))) : ''); $password = ((x($arr,'password')) ? trim($arr['password']) : ''); - $password2 = ((x($arr,'password2')) ? trim($arr['password2']) : ''); $parent = ((x($arr,'parent')) ? intval($arr['parent']) : 0 ); $flags = ((x($arr,'account_flags')) ? intval($arr['account_flags']) : ACCOUNT_OK); $roles = ((x($arr,'account_roles')) ? intval($arr['account_roles']) : 0 ); $expires = ((x($arr,'expires')) ? intval($arr['expires']) : NULL_DATE); - $techlevel = ((array_key_exists('techlevel',$arr)) ? intval($arr['techlevel']) : intval(get_config('system','techlevel'))); $default_service_class = get_config('system','default_service_class'); @@ -264,9 +262,8 @@ function create_account($arr) { function verify_email_address($arr) { if(array_key_exists('resend',$arr)) { - $email = $arr['email']; $a = q("select * from account where account_email = '%s' limit 1", - dbesc($arr['email']) + dbesc($arr['email']) ); if(! ($a && ($a[0]['account_flags'] & ACCOUNT_UNVERIFIED))) { return false; @@ -285,7 +282,7 @@ function verify_email_address($arr) { else { $hash = random_string(24); - $r = q("INSERT INTO register ( hash, created, uid, password, lang ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", + q("INSERT INTO register ( hash, created, uid, password, lang ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", dbesc($hash), dbesc(datetime_convert()), intval($arr['account']['account_id']), @@ -304,7 +301,7 @@ function verify_email_address($arr) { '$email' => $arr['email'], '$uid' => $account['account_id'], '$hash' => $hash, - '$details' => $details + '$details' => '' ] ); @@ -318,9 +315,7 @@ function verify_email_address($arr) { pop_lang(); - if($res) - $delivered ++; - else + if(! $res) logger('send_reg_approval_email: failed to account_id: ' . $arr['account']['account_id']); return $res; @@ -442,16 +437,17 @@ function account_allow($hash) { if(! $account) return $ret; - $r = q("DELETE FROM register WHERE hash = '%s'", + q("DELETE FROM register WHERE hash = '%s'", dbesc($register[0]['hash']) ); - $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", + q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", intval(ACCOUNT_BLOCKED), intval(ACCOUNT_BLOCKED), intval($register[0]['uid']) ); - $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", + + q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", intval(ACCOUNT_PENDING), intval(ACCOUNT_PENDING), intval($register[0]['uid']) @@ -516,11 +512,11 @@ function account_deny($hash) { if(! $account) return false; - $r = q("DELETE FROM account WHERE account_id = %d", + q("DELETE FROM account WHERE account_id = %d", intval($register[0]['uid']) ); - $r = q("DELETE FROM register WHERE id = %d", + q("DELETE FROM register WHERE id = %d", dbesc($register[0]['id']) ); notice( sprintf(t('Registration revoked for %s'), $account[0]['account_email']) . EOL); @@ -551,21 +547,23 @@ function account_approve($hash) { if(! $account) return $ret; - $r = q("DELETE FROM register WHERE hash = '%s' and password = 'verify'", + q("DELETE FROM register WHERE hash = '%s' and password = 'verify'", dbesc($register[0]['hash']) ); - $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", + q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", intval(ACCOUNT_BLOCKED), intval(ACCOUNT_BLOCKED), intval($register[0]['uid']) ); - $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", + + q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", intval(ACCOUNT_PENDING), intval(ACCOUNT_PENDING), intval($register[0]['uid']) ); - $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", + + q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", intval(ACCOUNT_UNVERIFIED), intval(ACCOUNT_UNVERIFIED), intval($register[0]['uid']) @@ -620,7 +618,7 @@ function downgrade_accounts() { foreach($r as $rr) { if(($basic) && ($rr['account_service_class']) && ($rr['account_service_class'] != $basic)) { - $x = q("UPDATE account set account_service_class = '%s', account_expires = '%s' + q("UPDATE account set account_service_class = '%s', account_expires = '%s' where account_id = %d", dbesc($basic), dbesc(NULL_DATE), @@ -631,7 +629,7 @@ function downgrade_accounts() { logger('downgrade_accounts: Account id ' . $rr['account_id'] . ' downgraded.'); } else { - $x = q("UPDATE account SET account_flags = (account_flags | %d) where account_id = %d", + q("UPDATE account SET account_flags = (account_flags | %d) where account_id = %d", intval(ACCOUNT_EXPIRED), intval($rr['account_id']) ); diff --git a/include/acl_selectors.php b/include/acl_selectors.php index c7a87afee..35e385058 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -84,7 +84,7 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti ); if($r) { foreach($r as $rv) { - $selected = (($single_group && 'vp.' . $rr['hash'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); + $selected = (($single_group && 'vp.' . $rv['profile_guid'] === $allow_gid[0]) ? ' selected = "selected" ' : ''); $groups .= '<option id="' . 'vp' . $rv['id'] . '" value="' . 'vp.' . $rv['profile_guid'] . '"' . $selected . '>' . t('Profile','acl') . ' ' . $rv['profile_name'] . '</option>' . "\r\n"; } } diff --git a/include/api_zot.php b/include/api_zot.php index 6a5b9a268..b332aea71 100644 --- a/include/api_zot.php +++ b/include/api_zot.php @@ -6,6 +6,8 @@ api_register_func('api/export/basic','api_export_basic', true); api_register_func('api/red/channel/export/basic','api_export_basic', true); api_register_func('api/z/1.0/channel/export/basic','api_export_basic', true); + api_register_func('api/red/item/export/page','api_item_export_page', true); + api_register_func('api/z/1.0/item/export/page','api_item_export_page', true); api_register_func('api/red/channel/list','api_channel_list', true); api_register_func('api/z/1.0/channel/list','api_channel_list', true); api_register_func('api/red/channel/stream','api_channel_stream', true); @@ -80,6 +82,26 @@ json_return_and_die(identity_basic_export(api_user(),$sections)); } + function api_item_export_page($type) { + if(api_user() === false) { + logger('api_item_export_page: no user'); + return false; + } + $page = intval($_REQUEST['page']); + $records = intval($_REQUEST['records']); + if(! $records) { + $records = 50; + } + if(! $_REQUEST['since']) + $start = NULL_DATE; + else { + $start = datetime_convert(date_default_timezone_get(),'UTC', $_REQUEST['since']); + } + $finish = datetime_convert(date_default_timezone_get(),'UTC', (($_REQUEST['until']) ? $_REQUEST['until'] : 'now')); + + json_return_and_die(channel_export_items_page(api_user(),$start,$finish,$page,$records)); + } + function api_network_stream($type) { if(api_user() === false) { diff --git a/include/attach.php b/include/attach.php index 17a47d9ac..f169e0669 100644 --- a/include/attach.php +++ b/include/attach.php @@ -321,7 +321,6 @@ function attach_can_view_folder($uid,$ob_hash,$folder_hash) { $sql_extra = permissions_sql($uid,$ob_hash); $hash = $folder_hash; - $result = false; if(! $folder_hash) { return perm_is_allowed($uid,$ob_hash,'view_storage'); @@ -352,7 +351,7 @@ function attach_can_view_folder($uid,$ob_hash,$folder_hash) { * @param string $hash * @param string $observer_hash * @param int $rev (optional) revision default 0 - * @return associative array with everything except data + * @return array (associative) with everything except data * * \e boolean \b success boolean true or false * * \e string \b message (optional) only when success is false * * \e array \b data array of attach DB entry without data component @@ -1224,7 +1223,7 @@ function attach_mkdir($channel, $observer_hash, $arr = null) { $ret['success'] = true; // update the parent folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", + q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", dbesc($created), dbesc($arr['folder']), intval($channel_id) @@ -1270,8 +1269,6 @@ function attach_mkdirp($channel, $observer_hash, $arr = null) { $ret = array('success' => false); $channel_id = $channel['channel_id']; - $sql_options = ''; - $basepath = 'store/' . $channel['channel_address']; logger('basepath: ' . $basepath); @@ -1374,7 +1371,7 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi } } - $x = q("update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where hash = '%s' and uid = %d", + q("update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where hash = '%s' and uid = %d", dbesc($allow_cid), dbesc($allow_gid), dbesc($deny_cid), @@ -1383,7 +1380,7 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi intval($channel_id) ); if($r[0]['is_photo']) { - $x = q("update photo set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where resource_id = '%s' and uid = %d", + q("update photo set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where resource_id = '%s' and uid = %d", dbesc($allow_cid), dbesc($allow_gid), dbesc($deny_cid), @@ -1482,7 +1479,7 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { } // delete from database - $z = q("DELETE FROM attach WHERE hash = '%s' AND uid = %d", + q("DELETE FROM attach WHERE hash = '%s' AND uid = %d", dbesc($resource), intval($channel_id) ); @@ -1493,7 +1490,7 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { // update the parent folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", + q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", dbesc(datetime_convert()), dbesc($r[0]['folder']), intval($channel_id) @@ -1524,6 +1521,17 @@ function attach_drop_photo($channel_id,$resource) { if($x) { drop_item($x[0]['id'],false,(($x[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1),true); } + + $r = q("SELECT content FROM photo WHERE resource_id = '%s' AND uid = %d AND os_storage = 1", + dbesc($resource), + intval($channel_id) + ); + if($r) { + foreach($r as $i) { + @unlink(dbunescbin($i['content'])); + } + } + q("DELETE FROM photo WHERE uid = %d AND resource_id = '%s'", intval($channel_id), dbesc($resource) @@ -1815,7 +1823,7 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $u_jsonobject = json_encode($object); //we have got the relevant info - delete the old item before we create the new one - $z = q("DELETE FROM item WHERE obj_type = '%s' AND verb = '%s' AND mid = '%s'", + q("DELETE FROM item WHERE obj_type = '%s' AND verb = '%s' AND mid = '%s'", dbesc(ACTIVITY_OBJ_FILE), dbesc(ACTIVITY_POST), dbesc($y[0]['mid']) @@ -1946,7 +1954,6 @@ function attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $ $ret = array(); $parent_arr = array(); $count_values = array(); - $poster = App::get_observer(); //lookup all channels in sharee group and add them to sharee $arr_allow_cid if($arr_allow_gid) { @@ -2351,7 +2358,6 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) { if(! $n) return false; - $newdirname = $n[0]['filename']; $newalbumname = $n[0]['display_path']; $newstorepath = dbunescbin($n[0]['content']) . '/' . $resource_id; } @@ -2359,7 +2365,6 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) { // root directory - $newdirname = EMPTY_STR; $newalbumname = EMPTY_STR; $newstorepath = 'store/' . $c['channel_address'] . '/' . $resource_id; } @@ -2428,7 +2433,7 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) { } } - $t = q("update attach set content = '%s', folder = '%s', filename = '%s' where id = %d", + q("update attach set content = '%s', folder = '%s', filename = '%s' where id = %d", dbescbin($newstorepath), dbesc($new_folder_hash), dbesc($filename), @@ -2438,7 +2443,7 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) { $x = attach_syspaths($channel_id,$resource_id); - $t1 = q("update attach set os_path = '%s', display_path = '%s' where id = %d", + q("update attach set os_path = '%s', display_path = '%s' where id = %d", dbesc($x['os_path']), dbesc($x['path']), intval($r[0]['id']) @@ -2446,7 +2451,7 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) { if($r[0]['is_photo']) { - $t = q("update photo set album = '%s', filename = '%s', os_path = '%s', display_path = '%s' + q("update photo set album = '%s', filename = '%s', os_path = '%s', display_path = '%s' where resource_id = '%s' and uid = %d", dbesc($newalbumname), dbesc($filename), @@ -2456,7 +2461,7 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) { intval($channel_id) ); - $t = q("update photo set content = '%s' where resource_id = '%s' and uid = %d and imgscale = 0", + q("update photo set content = '%s' where resource_id = '%s' and uid = %d and imgscale = 0", dbescbin($newstorepath), dbesc($resource_id), intval($channel_id) @@ -2587,12 +2592,12 @@ function attach_upgrade() { foreach($r as $rv) { $x = attach_syspaths($rv['uid'],$rv['hash']); if($x) { - $w = q("update attach set os_path = '%s', display_path = '%s' where id = %d", + q("update attach set os_path = '%s', display_path = '%s' where id = %d", dbesc($x['os_path']), dbesc($x['path']), intval($rv['id']) ); - $y = q("update photo set os_path = '%s', display_path = '%s' where uid = %d and resource_id = '%s'", + q("update photo set os_path = '%s', display_path = '%s' where uid = %d and resource_id = '%s'", dbesc($x['os_path']), dbesc($x['path']), intval($rv['uid']), diff --git a/include/bbcode.php b/include/bbcode.php index 7531bd774..485a1f5b2 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -87,12 +87,11 @@ function nakedoembed($match) { $strip_url = strip_escaped_zids($url); - $o = oembed_fetch_url($strip_url); - - if ($o['type'] == 'error') - return str_replace($url,$strip_url,$match[0]); - - return '[embed]' . $strip_url . '[/embed]'; + // this function no longer performs oembed on naked links + // because they author may have created naked links intentionally. + // Now it just strips zids on naked links. + + return str_replace($url,$strip_url,$match[0]); } function tryzrlaudio($match) { @@ -117,6 +116,26 @@ function tryzrlvideo($match) { return '<video ' . $poster . ' controls="controls" preload="none" src="' . str_replace(' ','%20',$link) . '" style="width:100%; max-width:' . App::$videowidth . 'px"><a href="' . str_replace(' ','%20',$link) . '">' . $link . '</a></video>'; } +function videowithopts($match) { + $link = $match[2]; + $zrl = is_matrix_url($link); + if($zrl) + $link = zid($link); + + $attributes = $match[1]; + + $poster = ""; + + preg_match("/poster='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $poster = 'poster="' . (($zrl) ? zid($matches[1]) : $matches[1]) . '"'; + + return '<video ' . $poster . ' controls="controls" preload="none" src="' . str_replace(' ','%20',$link) . '" style="width:100%; max-width:' . App::$videowidth . 'px"><a href="' . str_replace(' ','%20',$link) . '">' . $link . '</a></video>'; +} + + + + // [noparse][i]italic[/i][/noparse] turns into // [noparse][ i ]italic[ /i ][/noparse], // to hide them from parser. @@ -966,17 +985,22 @@ function bbcode($Text, $options = []) { // leave open the posibility of [map=something] // this is replaced in prepare_body() which has knowledge of the item location - - if (strpos($Text,'[/map]') !== false) { - $Text = preg_replace_callback("/\[map\](.*?)\[\/map\]/ism", 'bb_map_location', $Text); - } - if (strpos($Text,'[map=') !== false) { - $Text = preg_replace_callback("/\[map=(.*?)\]/ism", 'bb_map_coords', $Text); + if ($cache) { + $Text = str_replace([ '[map]','[/map]' ], [ '','' ], $Text); + $Text = preg_replace('/\[map=(.*?)\]/ism','$1',$Text); } - if (strpos($Text,'[map]') !== false) { - $Text = preg_replace("/\[map\]/", '<div class="map"></div>', $Text); + else { + if (strpos($Text,'[/map]') !== false) { + $Text = preg_replace_callback("/\[map\](.*?)\[\/map\]/ism", 'bb_map_location', $Text); + } + if (strpos($Text,'[map=') !== false) { + $Text = preg_replace_callback("/\[map=(.*?)\]/ism", 'bb_map_coords', $Text); + } + if (strpos($Text,'[map]') !== false) { + $Text = preg_replace("/\[map\]/", '<div class="map"></div>', $Text); + } } - + // Check for bold text if (strpos($Text,'[b]') !== false) { $Text = preg_replace("(\[b\](.*?)\[\/b\])ism", '<strong>$1</strong>', $Text); @@ -1251,12 +1275,14 @@ function bbcode($Text, $options = []) { // html5 video and audio if (strpos($Text,'[/video]') !== false) { + $Text = preg_replace_callback("/\[video (.*?)\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mpeg|mpg))\[\/video\]/ism", 'videowithopts', $Text); $Text = preg_replace_callback("/\[video\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mpeg|mpg))\[\/video\]/ism", 'tryzrlvideo', $Text); } if (strpos($Text,'[/audio]') !== false) { $Text = preg_replace_callback("/\[audio\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mp3|opus|m4a))\[\/audio\]/ism", 'tryzrlaudio', $Text); } if (strpos($Text,'[/zvideo]') !== false) { + $Text = preg_replace_callback("/\[zvideo (.*?)\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mpeg|mpg))\[\/zvideo\]/ism", 'videowithopts', $Text); $Text = preg_replace_callback("/\[zvideo\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mpeg|mpg))\[\/zvideo\]/ism", 'tryzrlvideo', $Text); } if (strpos($Text,'[/zaudio]') !== false) { @@ -1360,4 +1386,3 @@ function bbcode($Text, $options = []) { return $Text; } - diff --git a/include/channel.php b/include/channel.php index 95a3f96cf..e4b6df47b 100644 --- a/include/channel.php +++ b/include/channel.php @@ -873,6 +873,13 @@ function identity_basic_export($channel_id, $sections = null) { $ret['abook'][$x]['abconfig'] = $abconfig; translate_abook_perms_outbound($ret['abook'][$x]); } + + // pick up the zot6 xchan and hublocs also + + if($ret['channel']['channel_portable_id']) { + $xchans[] = $ret['channel']['channel_portable_id']; + } + stringify_array_elms($xchans); } @@ -948,6 +955,18 @@ function identity_basic_export($channel_id, $sections = null) { } $ret['app'] = $r; } + $r = q("select * from app where app_channel = %d and app_system = 1", + intval($channel_id) + ); + if($r) { + for($x = 0; $x < count($r); $x ++) { + $r[$x]['term'] = q("select * from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($r[$x]['id']) + ); + } + $ret['sysapp'] = $r; + } } if(in_array('chatrooms',$sections)) { @@ -1080,6 +1099,7 @@ function identity_basic_export($channel_id, $sections = null) { return $ret; } + /** * @brief Export items for a year, or a month of a year. * @@ -1102,23 +1122,50 @@ function identity_export_year($channel_id, $year, $month = 0) { else $target_month = '01'; + $mindate = datetime_convert('UTC', 'UTC', $year . '-' . $target_month . '-01 00:00:00'); + if($month && $month < 12) + $maxdate = datetime_convert('UTC', 'UTC', $year . '-' . $target_month_plus . '-01 00:00:00'); + else + $maxdate = datetime_convert('UTC', 'UTC', $year+1 . '-01-01 00:00:00'); + + return channel_export_items_date($channel_id,$mindate,$maxdate); + +} + +/** + * @brief Export items within an arbitrary date range. + * + * Date/time is in UTC. + * + * @param int $channel_id The channel ID + * @param string $start + * @param string $finish + * @return array + */ + +function channel_export_items_date($channel_id, $start, $finish) { + + if(! $start) + return array(); + else + $start = datetime_convert('UTC', 'UTC', $start); + + $finish = datetime_convert('UTC', 'UTC', (($finish) ? $finish : 'now')); + if($finish < $start) + return array(); + $ret = array(); $ch = channelx_by_n($channel_id); if($ch) { $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; } - $mindate = datetime_convert('UTC', 'UTC', $year . '-' . $target_month . '-01 00:00:00'); - if($month && $month < 12) - $maxdate = datetime_convert('UTC', 'UTC', $year . '-' . $target_month_plus . '-01 00:00:00'); - else - $maxdate = datetime_convert('UTC', 'UTC', $year+1 . '-01-01 00:00:00'); - $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created < '%s' and resource_type = '' order by created", + $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created <= '%s' and resource_type = '' order by created", intval(ITEM_TYPE_POST), intval($channel_id), - dbesc($mindate), - dbesc($maxdate) + dbesc($start), + dbesc($finish) ); if($r) { @@ -1132,39 +1179,57 @@ function identity_export_year($channel_id, $year, $month = 0) { return $ret; } + + /** - * @brief Export items within an arbitrary date range. + * @brief Export items with pagination * - * Date/time is in UTC. * * @param int $channel_id The channel ID - * @param string $start - * @param string $finish + * @param int $page + * @param int $limit (default 50) * @return array */ -function channel_export_items($channel_id, $start, $finish) { + +function channel_export_items_page($channel_id, $start, $finish, $page = 0, $limit = 50) { + + if(intval($page) < 1) { + $page = 0; + } + + if(intval($limit) < 1) { + $limit = 1; + } + + if(intval($limit) > 5000) { + $limit = 5000; + } if(! $start) - return array(); + $start = NULL_DATE; else $start = datetime_convert('UTC', 'UTC', $start); $finish = datetime_convert('UTC', 'UTC', (($finish) ? $finish : 'now')); if($finish < $start) - return array(); + return []; - $ret = array(); + $offset = intval($limit) * intval($page); + + $ret = []; $ch = channelx_by_n($channel_id); if($ch) { $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; } - $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created <= '%s' and resource_type = '' order by created", + $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and resource_type = '' and created >= '%s' and created <= '%s' order by created limit %d offset %d", intval(ITEM_TYPE_POST), intval($channel_id), dbesc($start), - dbesc($finish) + dbesc($finish), + intval($limit), + intval($offset) ); if($r) { @@ -1179,6 +1244,7 @@ function channel_export_items($channel_id, $start, $finish) { } + /** * @brief Loads a profile into the App structure. * @@ -1392,7 +1458,7 @@ function profile_edit_menu($uid) { * @param boolean $show_connect (optional) default true * @param mixed $zcard (optional) default false * - * @return HTML string suitable for sidebar inclusion + * @return string (HTML) suitable for sidebar inclusion * Exceptions: Returns empty string if passed $profile is wrong type or not populated */ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = false) { @@ -1753,13 +1819,16 @@ function zid_init() { call_hooks('zid_init', $arr); if(! local_channel()) { - $r = q("select * from hubloc where hubloc_addr = '%s' order by hubloc_connected desc limit 1", + $r = q("select * from hubloc where hubloc_addr = '%s' order by hubloc_connected desc", dbesc($tmp_str) ); if(! $r) { Master::Summon(array('Gprobe',bin2hex($tmp_str))); } - if($r && remote_channel() && remote_channel() === $r[0]['hubloc_hash']) + if($r) { + $r = zot_record_preferred($r); + } + if($r && remote_channel() && remote_channel() === $r['hubloc_hash']) return; logger('Not authenticated. Invoking reverse magic-auth for ' . $tmp_str); @@ -1767,8 +1836,8 @@ function zid_init() { $query = App::$query_string; $query = str_replace(array('?zid=','&zid='),array('?rzid=','&rzid='),$query); $dest = '/' . $query; - if($r && ($r[0]['hubloc_url'] != z_root()) && (! strstr($dest,'/magic')) && (! strstr($dest,'/rmagic'))) { - goaway($r[0]['hubloc_url'] . '/magic' . '?f=&rev=1&owa=1&bdest=' . bin2hex(z_root() . $dest)); + if($r && ($r['hubloc_url'] != z_root()) && (! strstr($dest,'/magic')) && (! strstr($dest,'/rmagic'))) { + goaway($r['hubloc_url'] . '/magic' . '?f=&rev=1&owa=1&bdest=' . bin2hex(z_root() . $dest)); } else logger('No hubloc found.'); @@ -1789,6 +1858,7 @@ function zat_init() { ); if($r) { $xchan = atoken_xchan($r[0]); + atoken_create_xchan($xchan); atoken_login($xchan); } } diff --git a/include/connections.php b/include/connections.php index d97ea3887..e942503f0 100644 --- a/include/connections.php +++ b/include/connections.php @@ -373,19 +373,46 @@ function contact_remove($channel_id, $abook_id) { if(intval($abook['abook_self'])) return false; - $r = q("select id from item where (owner_xchan = '%s' or author_xchan = '%s') and uid = %d and item_retained = 0 and item_starred = 0", + $r = q("select id, parent from item where (owner_xchan = '%s' or author_xchan = '%s') and uid = %d and item_retained = 0 and item_starred = 0", dbesc($abook['abook_xchan']), dbesc($abook['abook_xchan']), intval($channel_id) ); if($r) { + $already_saved = []; foreach($r as $rr) { - $x = q("select uid from term where otype = %d and oid = %d and ttype = %d limit 1", + $w = $x = $y = null; + + // optimise so we only process newly seen parent items + if (in_array($rr['parent'],$already_saved)) { + continue; + } + // if this isn't the parent, fetch the parent's item_retained and item_starred to see if the conversation + // should be retained + if($rr['id'] != $rr['parent']) { + $w = q("select id, item_retained, item_starred from item where id = %d", + intval($rr['parent']) + ); + if($w) { + // see if the conversation was filed + $x = q("select uid from term where otype = %d and oid = %d and ttype = %d limit 1", + intval(TERM_OBJ_POST), + intval($w[0]['id']), + intval(TERM_FILE) + ); + if (intval($w[0]['item_retained']) || intval($w[0]['item_starred']) || $x) { + $already_saved[] = $rr['parent']; + continue; + } + } + } + // see if this item was filed + $y = q("select uid from term where otype = %d and oid = %d and ttype = %d limit 1", intval(TERM_OBJ_POST), intval($rr['id']), intval(TERM_FILE) ); - if($x) { + if ($y) { continue; } drop_item($rr['id'],false); diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php index ee0e06a91..cfb208e2d 100755 --- a/include/dba/dba_driver.php +++ b/include/dba/dba_driver.php @@ -485,3 +485,28 @@ function db_columns($table) { return []; } + + +function db_indexes($table) { + + if($table) { + if(ACTIVE_DBTYPE === DBTYPE_POSTGRES) { + $r = q("SELECT indexname from pg_indexes where tablename = '%s'", + dbesc($table) + ); + if($r) { + return ids_to_array($r,'indexname'); + } + } + else { + $r = q("show index from %s", + dbesc($table) + ); + if($r) { + return ids_to_array($r,'Key_name'); + } + } + } + + return []; +} diff --git a/include/dba/dba_pdo.php b/include/dba/dba_pdo.php index 5002f53e7..0279342ec 100755 --- a/include/dba/dba_pdo.php +++ b/include/dba/dba_pdo.php @@ -19,7 +19,7 @@ class dba_pdo extends dba_driver { $this->driver_dbtype = $scheme; if(strpbrk($server,':;')) { - $dsn = $server; + $dsn = $this->driver_dbtype . ':unix_socket=' . trim($server, ':;'); } else { $dsn = $this->driver_dbtype . ':host=' . $server . (intval($port) ? ';port=' . $port : ''); @@ -161,23 +161,17 @@ class dba_pdo extends dba_driver { } function unescapebin($str) { - if($this->driver_dbtype === 'pgsql' && (! is_null($str))) { - $x = ''; - while(! feof($str)) { - $x .= fread($str,8192); + if($this->driver_dbtype === 'pgsql') { + if(gettype($str) === 'resource') { + $str = stream_get_contents($str); } - if(substr($x,0,2) === '\\x') { - $x = hex2bin(substr($x,2)); + if(substr($str,0,2) === '\\x') { + $str = hex2bin(substr($str,2)); } - return $x; - - } - else { - return $str; } + return $str; } - function getdriver() { return 'pdo'; } diff --git a/include/event.php b/include/event.php index fdb9e1415..471fb7afa 100644 --- a/include/event.php +++ b/include/event.php @@ -4,8 +4,11 @@ * @brief Event related functions. */ + use Sabre\VObject; +use Zotlabs\Lib\Activity; + use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Exception\UnsatisfiedDependencyException; @@ -65,7 +68,7 @@ function format_event_html($ev) { } function format_event_obj($jobject) { - $event = array(); + $event = []; $object = json_decode($jobject,true); @@ -1046,6 +1049,7 @@ function event_store_item($arr, $event) { 'location' => $arr['location'], 'adjust' => $arr['adjust'], 'content' => format_event_bbcode($arr), + 'attachment' => Activity::encode_attachment($r[0]), 'author' => array( 'name' => $r[0]['xchan_name'], 'address' => $r[0]['xchan_addr'], @@ -1159,7 +1163,7 @@ function event_store_item($arr, $event) { $item_arr['item_thread_top'] = $item_thread_top; $attach = array(array( - 'href' => z_root() . '/events/ical/' . urlencode($event['event_hash']), + 'href' => z_root() . '/channel_calendar/ical/' . urlencode($event['event_hash']), 'length' => 0, 'type' => 'text/calendar', 'title' => t('event') . '-' . $event['event_hash'], @@ -1181,7 +1185,7 @@ function event_store_item($arr, $event) { // otherwise we'll fallback to /display/$message_id if($wall) - $item_arr['plink'] = z_root() . '/channel/' . $z[0]['channel_address'] . '/?f=&mid=' . urlencode($item_arr['mid']); + $item_arr['plink'] = z_root() . '/channel/' . $z[0]['channel_address'] . '/?f=&mid=' . gen_link_id($item_arr['mid']); else $item_arr['plink'] = z_root() . '/display/' . gen_link_id($item_arr['mid']); @@ -1200,6 +1204,7 @@ function event_store_item($arr, $event) { 'location' => $arr['location'], 'adjust' => $arr['adjust'], 'content' => format_event_bbcode($arr), + 'attachment' => Activity::encode_attachment($item_arr), 'author' => array( 'name' => $x[0]['xchan_name'], 'address' => $x[0]['xchan_addr'], @@ -1279,6 +1284,10 @@ function cdav_principal($uri) { } function cdav_perms($needle, $haystack, $check_rw = false) { + + if($needle == 'channel_calendar') + return true; + foreach ($haystack as $item) { if($check_rw) { if(is_array($item['id'])) { diff --git a/include/features.php b/include/features.php index 35a4c0dd4..d8607f447 100644 --- a/include/features.php +++ b/include/features.php @@ -79,7 +79,7 @@ function get_features($filtered = true, $level = (-1)) { 'calendar' => [ - t('CalDAV'), + t('Calendar'), [ 'cal_first_day', @@ -167,6 +167,14 @@ function get_features($filtered = true, $level = (-1)) { t('Ability to mark special posts with a star indicator'), false, get_config('feature_lock','star_posts'), + ], + + [ + 'reply_to', + t('Reply on comment'), + t('Ability to reply on selected comment'), + false, + get_config('feature_lock','reply_to'), ] ], @@ -361,14 +369,6 @@ function get_features($filtered = true, $level = (-1)) { ], [ - 'suggest', - t('Suggest Channels'), - t('Show friend and connection suggestions'), - false, - get_config('feature_lock','suggest') - ], - - [ 'network_list_mode', t('Use blog/list mode'), t('Comments will be displayed separately'), diff --git a/include/follow.php b/include/follow.php index db77a0160..50b952881 100644 --- a/include/follow.php +++ b/include/follow.php @@ -119,6 +119,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) if( array_key_exists('permissions',$j) && array_key_exists('data',$j['permissions'])) { $permissions = crypto_unencapsulate(array( 'data' => $j['permissions']['data'], + 'alg' => $j['permissions']['alg'], 'key' => $j['permissions']['key'], 'iv' => $j['permissions']['iv']), $channel['channel_prvkey']); @@ -141,7 +142,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $sql_options = (($protocol) ? " and xchan_network = '" . dbesc($protocol) . "' " : ''); - $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' $sql_options limit 1", + $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' $sql_options ", dbesc($url), dbesc($url) ); @@ -168,18 +169,19 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) } if($wf || $d) { - $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", + $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s'", dbesc(($wf) ? $wf : $url), dbesc($url) ); } } + $xchan = zot_record_preferred($r,'xchan_network'); + // if discovery was a success we should have an xchan record in $r - if($r) { - $xchan = $r[0]; - $xchan_hash = $r[0]['xchan_hash']; + if($xchan) { + $xchan_hash = $xchan['xchan_hash']; $their_perms = 0; } } @@ -190,9 +192,9 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) return $result; } - $allowed = (($is_zot || in_array($r[0]['xchan_network'],['rss','zot6'])) ? 1 : 0); + $allowed = (($is_zot || in_array($xchan['xchan_network'],['rss','zot6'])) ? 1 : 0); - $x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => $allowed, 'singleton' => 0); + $x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $xchan, 'allowed' => $allowed, 'singleton' => 0); call_hooks('follow_allow',$x); diff --git a/include/html2bbcode.php b/include/html2bbcode.php index 1a03fbdaf..c916421b8 100644 --- a/include/html2bbcode.php +++ b/include/html2bbcode.php @@ -219,8 +219,7 @@ function html2bbcode($message) $message = $doc->saveHTML(); - // I'm removing something really disturbing - // Don't know exactly what it is + // I'm removing the UTF-8 encoding of a NO-BREAK SPACE codepoint $message = str_replace(chr(194).chr(160), ' ', $message); $message = str_replace(" ", " ", $message); diff --git a/include/hubloc.php b/include/hubloc.php index b2903b0ee..4a1f77733 100644 --- a/include/hubloc.php +++ b/include/hubloc.php @@ -305,3 +305,41 @@ function ping_site($url) { return $ret; } + + +function z6_discover() { + + // find unregistered zot6 clone hublocs + + $c = q("select channel_hash, channel_portable_id from channel where channel_deleted = '%s'", + dbesc(NULL_DATE) + ); + if ($c) { + foreach ($c as $entry) { + $q1 = q("select * from hubloc left join site on hubloc_url = site_url where hubloc_deleted = 0 and site_dead = 0 and hubloc_hash = '%s' and hubloc_url != '%s'", + dbesc($entry['channel_hash']), + dbesc(z_root()) + ); + if (! $q1) { + // channel has no zot clones + continue; + } + // does this particular server have a zot6 clone registered on our site for this channel? + foreach ($q1 as $q) { + $q2 = q("select * from hubloc left join site on hubloc_url = site_url where hubloc_deleted = 0 and site_dead = 0 and hubloc_hash = '%s' and hubloc_url = '%s'", + dbesc($entry['channel_portable_id']), + dbesc($q['hubloc_url']) + ); + if ($q2) { + continue; + } + // zot6 hubloc not found. + if(strpos($q['site_project'],'hubzilla') !== false && version_compare($q['site_version'],'4.0') >= 0) { + // probe and store results - only for zot6 (over-ride the zot default) + discover_by_webbie($q['hubloc_addr'],'zot6'); + } + } + } + } + +} diff --git a/include/import.php b/include/import.php index f391400bd..92ba014d8 100644 --- a/include/import.php +++ b/include/import.php @@ -94,7 +94,8 @@ function import_channel($channel, $account_id, $seize, $newname = '') { 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', 'channel_a_delegate', 'perm_limits', 'channel_password', 'channel_salt', - 'channel_moved', 'channel_removed', 'channel_deleted', 'channel_system' + 'channel_moved', 'channel_removed', 'channel_deleted', 'channel_system', + 'channel_r_photos', 'channel_w_photos' ]; $clean = array(); @@ -147,7 +148,9 @@ function import_config($channel, $configs) { foreach($configs as $config) { unset($config['id']); $config['uid'] = $channel['channel_id']; - + if($config['cat'] === 'system' && $config['k'] === 'import_system_apps') { + continue; + } create_table_from_array('pconfig', $config); } @@ -364,6 +367,9 @@ function import_apps($channel, $apps) { if($channel && $apps) { foreach($apps as $app) { + if(array_key_exists('app_system',$app) && intval($app['app_system'])) + continue; + $term = ((array_key_exists('term',$app) && is_array($app['term'])) ? $app['term'] : null); unset($app['id']); @@ -413,6 +419,9 @@ function sync_apps($channel, $apps) { $exists = false; $term = ((array_key_exists('term',$app)) ? $app['term'] : null); + if(array_key_exists('app_system',$app) && intval($app['app_system'])) + continue; + $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['app_id']), intval($channel['channel_id']) @@ -504,6 +513,84 @@ function sync_apps($channel, $apps) { } } + + +/** + * @brief Import system apps. + * System apps from the original server may not exist on this system + * (e.g. apps associated with addons that are not installed here). + * Check the system apps that were provided in the import file to see if they + * exist here and if so, install them locally. Preserve categories that + * might have been added by this channel on the other server. + * Do not use any paths from the original as they will point to a different server. + * @param array $channel + * @param array $apps + */ +function import_sysapps($channel, $apps) { + + if($channel && $apps) { + + $sysapps = \Zotlabs\Lib\Apps::get_system_apps(false); + + foreach($apps as $app) { + + if(array_key_exists('app_system',$app) && (! intval($app['app_system']))) + continue; + + $term = ((array_key_exists('term',$app) && is_array($app['term'])) ? $app['term'] : null); + + foreach($sysapps as $sysapp) { + if($app['app_id'] === hash('whirlpool',$sysapp['app_name'])) { + // install this app on this server + $newapp = $sysapp; + $newapp['uid'] = $channel['channel_id']; + $newapp['guid'] = hash('whirlpool',$newapp['name']); + + $installed = q("select id from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($newapp['guid']), + intval($channel['channel_id']) + ); + if($installed) { + break; + } + + $newapp['system'] = 1; + if($term) { + $s = EMPTY_STR; + foreach($term as $t) { + if($s) { + $s .= ','; + } + $s .= $t['term']; + } + $newapp['categories'] = $s; + } + \Zotlabs\Lib\Apps::app_install($channel['channel_id'],$newapp); + } + } + } + } +} + +/** + * @brief Sync system apps. + * + * @param array $channel + * @param array $apps + */ +function sync_sysapps($channel, $apps) { + + if($channel && $apps) { + + // we do not currently sync system apps + + } +} + + + + + /** * @brief Import chatrooms. * @@ -1069,6 +1156,9 @@ function sync_files($channel, $files) { require_once('include/attach.php'); if($channel && $files) { + + $limit = service_class_fetch($channel['channel_id'], 'attach_upload_limit'); + foreach($files as $f) { if(! $f) continue; @@ -1189,6 +1279,17 @@ function sync_files($channel, $files) { } else { logger('sync_files attach does not exists: ' . print_r($att,true), LOGGER_DEBUG); + + if($limit !== false) { + $r = q("select sum(filesize) as total from attach where aid = %d ", + intval($channel['channel_account_id']) + ); + if(($r) && (($r[0]['total'] + $att['filesize']) > $limit)) { + logger('service class limit exceeded'); + continue; + } + } + create_table_from_array('attach',$att); } @@ -1245,6 +1346,7 @@ function sync_files($channel, $files) { logger('attachment store failed',LOGGER_NORMAL,LOG_ERR); } if($f['photo']) { + foreach($f['photo'] as $p) { unset($p['id']); $p['aid'] = $channel['channel_account_id']; @@ -1266,6 +1368,7 @@ function sync_files($channel, $files) { dbesc($p['resource_id']), intval($channel['channel_id']) ); + $update_xchan = $p['edited']; } // same for cover photos @@ -1285,19 +1388,20 @@ function sync_files($channel, $files) { else $p['content'] = (($p['content'])? base64_decode($p['content']) : ''); - if(intval($p['imgscale']) && (! $p['content'])) { + if(intval($p['imgscale']) && (! empty($p['content']))) { $time = datetime_convert(); - $parr = array('hash' => $channel['channel_hash'], + $parr = array( + 'hash' => $channel['channel_hash'], 'time' => $time, - 'resource' => $att['hash'], + 'resource' => $p['resource_id'], 'revision' => 0, 'signature' => base64url_encode(rsa_sign($channel['channel_hash'] . '.' . $time, $channel['channel_prvkey'])), - 'resolution' => $p['imgscale'] + 'resolution' => intval($p['imgscale']) ); - $stored_image = $newfname . '-' . intval($p['imgscale']); + $stored_image = $newfname . '-' . $p['imgscale']; $fp = fopen($stored_image,'w'); if(! $fp) { @@ -1306,7 +1410,6 @@ function sync_files($channel, $files) { } $redirects = 0; - $headers = []; $headers['Accept'] = 'application/x-zot+json' ; $headers['Sigtoken'] = random_string(); @@ -1314,8 +1417,17 @@ function sync_files($channel, $files) { $x = z_post_url($fetch_url,$parr,$redirects,[ 'filep' => $fp, 'headers' => $headers]); fclose($fp); - $p['content'] = file_get_contents($stored_image); - unlink($stored_image); + + // Override remote hub thumbnails storage settings + if(! boolval(get_config('system','filesystem_storage_thumbnails', 0))) { + $p['os_storage'] = 0; + $p['content'] = file_get_contents($stored_image); + @unlink($stored_image); + } + else { + $p['os_storage'] = 1; + $p['content'] = $stored_image; + } } if(!isset($p['display_path'])) @@ -1347,6 +1459,16 @@ function sync_files($channel, $files) { create_table_from_array('photo',$p, [ 'content' ] ); } } + + } + + // Set xchan photo date to prevent thumbnails fetch for clones on profile update packet recieve + if(isset($update_xchan)) { + + $x = q("UPDATE xchan SET xchan_photo_date = '%s' WHERE xchan_hash = '%s'", + dbescdate($update_xchan), + dbesc($channel['channel_hash']) + ); } \Zotlabs\Daemon\Master::Summon([ 'Thumbnail' , $att['hash'] ]); diff --git a/include/items.php b/include/items.php index 30e758add..95b696034 100755 --- a/include/items.php +++ b/include/items.php @@ -763,11 +763,11 @@ function get_item_elements($x,$allow_code = false) { // check the supplied signature against the supplied content. // Note that we will purify the content which could change it. - $r = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", + $r = q("select xchan_pubkey, xchan_network from xchan where xchan_hash = '%s' limit 1", dbesc($arr['author_xchan']) ); if($r) { - if($r[0]['xchan_pubkey']) { + if($r[0]['xchan_pubkey'] && $r[0]['xchan_network'] === 'zot') { if(rsa_verify($x['body'],base64url_decode($arr['sig']),$r[0]['xchan_pubkey'])) { $arr['item_verified'] = 1; } @@ -914,6 +914,16 @@ function import_author_xchan($x) { if(array_key_exists('network',$x) && $x['network'] === 'zot') return $y; + // perform zot6 discovery + + if($x['url']) { + $y = discover_by_webbie($x['url'],'zot6'); + + if($y) { + return $y; + } + } + if($x['network'] === 'rss') { $y = import_author_rss($x); } @@ -1920,11 +1930,21 @@ function item_store($arr, $allow_exec = false, $deliver = true) { if($parent_deleted) $arr['item_deleted'] = 1; - $r = q("SELECT id FROM item WHERE mid = '%s' AND uid = %d and revision = %d LIMIT 1", - dbesc($arr['mid']), - intval($arr['uid']), - intval($arr['revision']) - ); + if($arr['uuid']) { + $r = q("SELECT id FROM item WHERE uuid = '%s' AND uid = %d and revision = %d LIMIT 1", + dbesc($arr['uuid']), + intval($arr['uid']), + intval($arr['revision']) + ); + } + else { + $r = q("SELECT id FROM item WHERE mid = '%s' AND uid = %d and revision = %d LIMIT 1", + dbesc($arr['mid']), + intval($arr['uid']), + intval($arr['revision']) + ); + } + if($r) { logger('duplicate item ignored. ' . print_r($arr,true)); $ret['message'] = 'duplicate post.'; @@ -4258,7 +4278,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C if($arr['mid']) $sql_options .= " and parent_mid = '" . dbesc($arr['mid']) . "' "; - $sql_extra = " AND item.parent IN ( SELECT parent FROM item WHERE item_thread_top = 1 $sql_options $item_normal ) "; + $sql_extra = " AND item.parent IN ( SELECT parent FROM item WHERE $item_uids and item_thread_top = 1 $sql_options $item_normal ) "; if($arr['since_id']) $sql_extra .= " and item.id > " . $since_id . " "; @@ -4688,7 +4708,7 @@ function sync_an_item($channel_id,$item_id) { if($r) { xchan_query($r); $sync_item = fetch_post_tags($r); - build_sync_packet($channel_d,array('item' => array(encode_item($sync_item[0],true)))); + build_sync_packet($channel_id, array('item' => array(encode_item($sync_item[0],true)))); } } diff --git a/include/js_strings.php b/include/js_strings.php index c053e5666..f01fa87ae 100644 --- a/include/js_strings.php +++ b/include/js_strings.php @@ -33,6 +33,7 @@ function js_strings() { '$name_empty' => t('A channel name is required.'), '$name_ok1' => t('This is a '), '$name_ok2' => t(' channel name'), + '$to_reply' => t('Back to reply'), // translatable prefix and suffix strings for jquery.timeago - // using the defaults set below if left untranslated, empty strings if diff --git a/include/message.php b/include/message.php index 037c59c60..2486beb83 100644 --- a/include/message.php +++ b/include/message.php @@ -220,8 +220,8 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep foreach($images as $image) { if(! stristr($image,z_root() . '/photo/')) continue; - $image_uri = substr($image,strrpos($image,'/') + 1); - $image_uri = substr($image_uri,0, strpos($image_uri,'-')); + $image_uri = substr($image, strrpos($image, '/') + 1); + $image_uri = substr($image_uri, 0, strpos($image_uri, '.') - 2); $r = q("UPDATE photo SET allow_cid = '%s' WHERE resource_id = '%s' AND uid = %d and allow_cid = '%s'", dbesc('<' . $recipient . '>'), dbesc($image_uri), diff --git a/include/nav.php b/include/nav.php index 58e13dd93..3e1acd306 100644 --- a/include/nav.php +++ b/include/nav.php @@ -42,6 +42,10 @@ function nav($template = 'default') { require_once('include/conversation.php'); + $nav_apps = []; + $navbar_apps = []; + $channel_apps = []; + $channel_apps[] = channel_apps($is_owner, App::$profile['channel_address']); @@ -179,7 +183,6 @@ function nav($template = 'default') { $search_form_action = 'search'; } - $nav['search'] = ['search', t('Search'), "", t('Search site @name, !forum, #tag, ?docs, content'), $search_form_action]; /** @@ -378,16 +381,15 @@ function channel_apps($is_owner = false, $nickname = null) { if(App::$is_sys) return ''; - if(! get_pconfig($uid, 'system', 'channelapps','1')) - return ''; - $channel = App::get_channel(); if($channel && is_null($nickname)) $nickname = $channel['channel_address']; $uid = ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : local_channel()); - $account_id = ((App::$profile['profile_uid']) ? App::$profile['channel_account_id'] : App::$channel['channel_account_id']); + + if(! get_pconfig($uid, 'system', 'channelapps','1')) + return; if($uid == local_channel()) { return; diff --git a/include/network.php b/include/network.php index 8ac71011e..c754625cd 100644 --- a/include/network.php +++ b/include/network.php @@ -114,6 +114,13 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); } + if(x($opts,'connecttimeout') && intval($opts['connecttimeout'])) { + @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($opts['connecttimeout'])); + } + else { + $curl_contime = intval(@get_config('system','curl_connecttimeout')); + @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (($curl_contime !== false) ? $curl_contime : 30)); + } if(x($opts,'http_auth')) { // "username" . ':' . "password" @@ -867,13 +874,16 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = } -function email_header_encode($in_str, $charset = 'UTF-8') { +function email_header_encode($in_str, $charset = 'UTF-8', $header = 'Subject') { + + $out_str = $in_str; $need_to_convert = false; for($x = 0; $x < strlen($in_str); $x ++) { if((ord($in_str[$x]) == 0) || ((ord($in_str[$x]) > 128))) { $need_to_convert = true; + break; } } @@ -885,11 +895,11 @@ function email_header_encode($in_str, $charset = 'UTF-8') { // define start delimimter, end delimiter and spacer $end = "?="; $start = "=?" . $charset . "?B?"; - $spacer = $end . "\r\n " . $start; + $spacer = $end . PHP_EOL . " " . $start; // determine length of encoded text within chunks // and ensure length is even - $length = 75 - strlen($start) - strlen($end); + $length = 75 - strlen($start) - strlen($end) - (strlen($header) + 2); /* [EDIT BY danbrown AT php DOT net: The following @@ -1789,8 +1799,8 @@ function z_mail($params) { $messageHeader = $params['additionalMailHeader'] . - "From: $fromName <{$params['fromEmail']}>\n" . - "Reply-To: $fromName <{$params['replyTo']}>\n" . + "From: $fromName <{$params['fromEmail']}>" . PHP_EOL . + "Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL . "Content-Type: text/plain; charset=UTF-8"; // send the message diff --git a/include/oembed.php b/include/oembed.php index 426197c5f..ee9e57c3f 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -1,6 +1,6 @@ <?php /** @file */ -use Zotlabs\Lib as Zlib; +use Zotlabs\Lib\Cache; function oembed_replacecb($matches){ @@ -133,7 +133,6 @@ function oembed_fetch_url($embedurl){ } } - $txt = null; // we should try to cache this and avoid a lookup on each render @@ -143,10 +142,22 @@ function oembed_fetch_url($embedurl){ $furl = ((local_channel() && $zrl) ? zid($embedurl) : $embedurl); - if($action !== 'block') { - $txt = Zlib\Cache::get('[' . App::$videowidth . '] ' . $furl); + if($action !== 'block' && (! get_config('system','oembed_cache_disable'))) { + $txt = Cache::get('[' . App::$videowidth . '] ' . $furl); } + + if(strpos(strtolower($embedurl),'.pdf') !== false) { + $action = 'allow'; + $j = [ + 'html' => '<object data="' . $embedurl . '" type="application/pdf" style="width: 100%; height: 300px;"></object>', + 'title' => t('View PDF'), + 'type' => 'pdf' + ]; + // set $txt to something so that we don't attempt to fetch what could be a lengthy pdf. + $txt = EMPTY_STR; + } + if(is_null($txt)) { $txt = ""; @@ -215,20 +226,10 @@ function oembed_fetch_url($embedurl){ // save in cache if(! get_config('system','oembed_cache_disable')) - Zlib\Cache::set('[' . App::$videowidth . '] ' . $furl, $txt); + Cache::set('[' . App::$videowidth . '] ' . $furl, $txt); } - if(strpos(strtolower($embedurl),'.pdf') !== false) { - $action = 'allow'; - $j = [ - 'html' => '<object data="' . $embedurl . '" type="application/pdf" style="width: 100%; height: 300px;"></object>', - 'title' => t('View PDF'), - 'type' => 'pdf' - ]; - - } - if(! $j) { $j = json_decode($txt,true); } diff --git a/include/permissions.php b/include/permissions.php index 115d96eca..501b2cc77 100644 --- a/include/permissions.php +++ b/include/permissions.php @@ -157,7 +157,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ // If we're still here, we have an observer, check the network. if($channel_perm & PERMS_NETWORK) { - if($x && $x[0]['xchan_network'] === 'zot') { + if($x && in_array($x[0]['xchan_network'],[ 'zot','zot6'])) { $ret[$perm_name] = true; continue; } @@ -192,7 +192,7 @@ function get_all_perms($uid, $observer_xchan, $check_siteblock = true, $default_ // They are in your address book, but haven't been approved - if($channel_perm & PERMS_PENDING) { + if($channel_perm & PERMS_PENDING && (! intval($x[0]['abook_pseudo']))) { $ret[$perm_name] = true; continue; } @@ -316,10 +316,19 @@ function perm_is_allowed($uid, $observer_xchan, $permission, $check_siteblock = if(! $x) { // not in address book and no guest token, see if they've got an xchan + $y = q("select xchan_network from xchan where xchan_hash = '%s' limit 1", dbesc($observer_xchan) ); if($y) { + + // This requires an explanation and the effects are subtle. + // The following line creates a fake connection, and this allows + // access tokens to have specific permissions even though they are + // not actual connections. + // The existence of this fake entry must be checked when dealing + // with connection related permissions. + $x = array(pseudo_abook($y[0])); } } @@ -327,7 +336,6 @@ function perm_is_allowed($uid, $observer_xchan, $permission, $check_siteblock = } $abperms = load_abconfig($uid,$observer_xchan,'my_perms'); } - // system is blocked to anybody who is not authenticated @@ -349,6 +357,7 @@ function perm_is_allowed($uid, $observer_xchan, $permission, $check_siteblock = return true; // If it's an unauthenticated observer, we only need to see if PERMS_PUBLIC is set + // We just did that. if(! $observer_xchan) { return false; @@ -357,7 +366,7 @@ function perm_is_allowed($uid, $observer_xchan, $permission, $check_siteblock = // If we're still here, we have an observer, check the network. if($channel_perm & PERMS_NETWORK) { - if (($x && $x[0]['xchan_network'] === 'zot') || ($y && $y[0]['xchan_network'] === 'zot')) + if ($x && in_array($x[0]['xchan_network'], ['zot','zot6'])) return true; } @@ -373,8 +382,7 @@ function perm_is_allowed($uid, $observer_xchan, $permission, $check_siteblock = return false; } - // From here on we require that the observer be a connection and - // handle whether we're allowing any, approved or specific ones + // From here on we require that the observer be a connection or pseudo connection if(! $x) { return false; @@ -382,7 +390,7 @@ function perm_is_allowed($uid, $observer_xchan, $permission, $check_siteblock = // They are in your address book, but haven't been approved - if($channel_perm & PERMS_PENDING) { + if($channel_perm & PERMS_PENDING && (! intval($x[0]['abook_pseudo']))) { return true; } diff --git a/include/photos.php b/include/photos.php index 44406e0b0..7ea2729ae 100644 --- a/include/photos.php +++ b/include/photos.php @@ -277,8 +277,7 @@ function photo_upload($channel, $observer, $args) { if(($width > 1024 || $height > 1024) && (! $errors)) $ph->scaleImage(1024); - $p['imgscale'] = 1; - $r1 = $ph->save($p); + $r1 = $ph->storeThumbnail($p, PHOTO_RES_1024); $link[1] = array( 'rel' => 'alternate', 'type' => 'text/html', @@ -292,8 +291,7 @@ function photo_upload($channel, $observer, $args) { if(($width > 640 || $height > 640) && (! $errors)) $ph->scaleImage(640); - $p['imgscale'] = 2; - $r2 = $ph->save($p); + $r2 = $ph->storeThumbnail($p, PHOTO_RES_640); $link[2] = array( 'rel' => 'alternate', 'type' => 'text/html', @@ -307,8 +305,7 @@ function photo_upload($channel, $observer, $args) { if(($width > 320 || $height > 320) && (! $errors)) $ph->scaleImage(320); - $p['imgscale'] = 3; - $r3 = $ph->save($p); + $r3 = $ph->storeThumbnail($p, PHOTO_RES_320); $link[3] = array( 'rel' => 'alternate', 'type' => 'text/html', diff --git a/include/security.php b/include/security.php index 493d34699..38cb72263 100644 --- a/include/security.php +++ b/include/security.php @@ -306,6 +306,7 @@ function change_channel($change_channel) { * * @return string additional SQL where statement */ + function permissions_sql($owner_id, $remote_observer = null, $table = '') { $local_channel = local_channel(); @@ -316,7 +317,7 @@ function permissions_sql($owner_id, $remote_observer = null, $table = '') { * default permissions - anonymous user */ - if($table) + if ($table) $table .= '.'; $sql = " AND {$table}allow_cid = '' @@ -329,38 +330,63 @@ function permissions_sql($owner_id, $remote_observer = null, $table = '') { * Profile owner - everything is visible */ - if(($local_channel) && ($local_channel == $owner_id)) { - $sql = ''; + if (($local_channel) && ($local_channel == $owner_id)) { + return EMPTY_STR; } /** - * Authenticated visitor. Unless pre-verified, - * check that the contact belongs to this $owner_id - * and load the groups the visitor belongs to. - * If pre-verified, the caller is expected to have already - * done this and passed the groups into this function. + * Authenticated visitor. */ else { + $observer = ((! is_null($remote_observer)) ? $remote_observer : get_observer_hash()); - if($observer) { - $groups = init_groups_visitor($observer); - $gs = '<<>>'; // should be impossible to match + if ($observer) { + + $sec = get_security_ids($owner_id,$observer); - if(is_array($groups) && count($groups)) { - foreach($groups as $g) - $gs .= '|<' . $g . '>'; + // always allow the channel owner, even if authenticated as a visitor + + if ($sec['channel_id']) { + foreach ($sec['channel_id'] as $ch) { + if ($observer === $ch) { + return EMPTY_STR; + } + } + } + + if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) { + $ca = []; + foreach ($sec['allow_cid'] as $c) { + $ca[] = '<' . $c . '>'; + } + $cs = implode('|',$ca); + } + else { + $cs = '<<>>'; // should be impossible to match } + + if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) { + $ga = []; + foreach ($sec['allow_gid'] as $g) { + $ga[] = '<' . $g . '>'; + } + $gs = implode('|',$ga); + } + else { + $gs = '<<>>'; // should be impossible to match + } + $regexop = db_getfunc('REGEXP'); $sql = sprintf( - " AND ( NOT ({$table}deny_cid like '%s' OR {$table}deny_gid $regexop '%s') - AND ( {$table}allow_cid like '%s' OR {$table}allow_gid $regexop '%s' OR ( {$table}allow_cid = '' AND {$table}allow_gid = '') ) + " AND ( NOT ({$table}deny_cid $regexop '%s' OR {$table}deny_gid $regexop '%s') + AND ( {$table}allow_cid $regexop '%s' OR {$table}allow_gid $regexop '%s' OR ( {$table}allow_cid = '' AND {$table}allow_gid = '') ) ) ", - dbesc(protect_sprintf( '%<' . $observer . '>%')), + dbesc($cs), dbesc($gs), - dbesc(protect_sprintf( '%<' . $observer . '>%')), + dbesc($cs), dbesc($gs) ); } @@ -377,6 +403,7 @@ function permissions_sql($owner_id, $remote_observer = null, $table = '') { * * @return string additional SQL where statement */ + function item_permissions_sql($owner_id, $remote_observer = null) { $local_channel = local_channel(); @@ -398,37 +425,59 @@ function item_permissions_sql($owner_id, $remote_observer = null) { } /** - * Authenticated visitor. Unless pre-verified, - * check that the contact belongs to this $owner_id - * and load the groups the visitor belongs to. - * If pre-verified, the caller is expected to have already - * done this and passed the groups into this function. + * Authenticated visitor. */ else { - $observer = (($remote_observer) ? $remote_observer : get_observer_hash()); - if($observer) { + $observer = (($remote_observer) ? $remote_observer : get_observer_hash()); - $s = scopes_sql($owner_id,$observer); + if($observer) { - $groups = init_groups_visitor($observer); + $scope = scopes_sql($owner_id,$observer); + $sec = get_security_ids($owner_id,$observer); - $gs = '<<>>'; // should be impossible to match + // always allow the channel owner, even if authenticated as a visitor + + if($sec['channel_id']) { + foreach($sec['channel_id'] as $ch) { + if($observer === $ch) { + return EMPTY_STR; + } + } + } + + if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) { + $ca = []; + foreach ($sec['allow_cid'] as $c) { + $ca[] = '<' . $c . '>'; + } + $cs = implode('|',$ca); + } + else { + $cs = '<<>>'; // should be impossible to match + } - if(is_array($groups) && count($groups)) { - foreach($groups as $g) - $gs .= '|<' . $g . '>'; + if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) { + $ga = []; + foreach ($sec['allow_gid'] as $g) { + $ga[] = '<' . $g . '>'; + } + $gs = implode('|',$ga); + } + else { + $gs = '<<>>'; // should be impossible to match } + $regexop = db_getfunc('REGEXP'); $sql = sprintf( - " AND (( NOT (deny_cid like '%s' OR deny_gid $regexop '%s') - AND ( allow_cid like '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '' AND item_private = 0 )) - ) OR ( item_private = 1 $s )) + " AND (( NOT (deny_cid $regexop '%s' OR deny_gid $regexop '%s') + AND ( allow_cid $regexop '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '' AND item_private = 0 )) + ) OR ( item_private = 1 $scope )) ", - dbesc(protect_sprintf( '%<' . $observer . '>%')), + dbesc($cs), dbesc($gs), - dbesc(protect_sprintf( '%<' . $observer . '>%')), + dbesc($cs), dbesc($gs) ); } @@ -465,40 +514,57 @@ function scopes_sql($uid,$observer) { } - - - - - /** * @param string $observer_hash * * @return string additional SQL where statement */ + function public_permissions_sql($observer_hash) { - $groups = init_groups_visitor($observer_hash); + $owner_id = 0; - $gs = '<<>>'; // should be impossible to match + if ($observer_hash) { + + $sec = get_security_ids($owner_id,$observer_hash); + + if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) { + $ca = []; + foreach ($sec['allow_cid'] as $c) { + $ca[] = '<' . $c . '>'; + } + $cs = implode('|',$ca); + } + else { + $cs = '<<>>'; // should be impossible to match + } + + if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) { + $ga = []; + foreach ($sec['allow_gid'] as $g) { + $ga[] = '<' . $g . '>'; + } + $gs = implode('|',$ga); + } + else { + $gs = '<<>>'; // should be impossible to match + } - if(is_array($groups) && count($groups)) { - foreach($groups as $g) - $gs .= '|<' . $g . '>'; - } - $sql = ''; - if($observer_hash) { $regexop = db_getfunc('REGEXP'); $sql = sprintf( - " OR (( NOT (deny_cid like '%s' OR deny_gid $regexop '%s') - AND ( allow_cid like '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '' AND item_private = 0 ) ) - )) + " AND ( NOT (deny_cid $regexop '%s' OR deny_gid $regexop '%s') + AND ( allow_cid $regexop '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '' AND item_private = 0) ) + ) ", - dbesc(protect_sprintf( '%<' . $observer_hash . '>%')), + dbesc($cs), dbesc($gs), - dbesc(protect_sprintf( '%<' . $observer_hash . '>%')), + dbesc($cs), dbesc($gs) ); } + else { + $sql = EMPTY_STR; + } return $sql; } @@ -510,7 +576,7 @@ function public_permissions_sql($observer_hash) { * In this implementation, a security token is reusable (if the user submits a form, goes back and resubmits the form, maybe with small changes; * or if the security token is used for ajax-calls that happen several times), but only valid for a certain amout of time (3hours). * The "typename" seperates the security tokens of different types of forms. This could be relevant in the following case: - * A security token is used to protekt a link from CSRF (e.g. the "delete this profile"-link). + * A security token is used to protect a link from CSRF (e.g. the "delete this profile"-link). * If the new page contains by any chance external elements, then the used security token is exposed by the referrer. * Actually, important actions should not be triggered by Links / GET-Requests at all, but somethimes they still are, * so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types). @@ -564,24 +630,40 @@ function check_form_security_token_ForbiddenOnErr($typename = '', $formname = 'f function init_groups_visitor($contact_id) { $groups = []; - // private profiles are treated as a virtual group - - $r = q("SELECT abook_profile from abook where abook_xchan = '%s' and abook_profile != '' ", + $x = q("select * from xchan where xchan_hash = '%s'", dbesc($contact_id) ); - if($r) { - foreach($r as $rv) { + + if (! $x) { + return $groups; + } + + // include xchans for all zot-like networks + + $xchans = q("select xchan_hash from xchan where xchan_hash = '%s' OR ( xchan_guid = '%s' AND xchan_pubkey = '%s' ) ", + dbesc($contact_id), + dbesc($x[0]['xchan_guid']), + dbesc($x[0]['xchan_pubkey']) + ); + + if($xchans) { + $hashes = ids_to_querystr($xchans,'xchan_hash',true); + } + + // private profiles are treated as a virtual group + + $r = q("SELECT abook_profile from abook where abook_xchan in ( " . protect_sprintf($hashes) . " ) and abook_profile != '' "); + if ($r) { + foreach ($r as $rv) { $groups[] = 'vp.' . $rv['abook_profile']; } } - // physical groups this channel is a member of + // physical groups this identity is a member of - $r = q("SELECT hash FROM pgrp left join pgrp_member on pgrp.id = pgrp_member.gid WHERE xchan = '%s' ", - dbesc($contact_id) - ); - if($r) { - foreach($r as $rr) + $r = q("SELECT hash FROM pgrp left join pgrp_member on pgrp.id = pgrp_member.gid WHERE xchan in ( " . protect_sprintf($hashes) . " ) "); + if ($r) { + foreach ($r as $rr) $groups[] = $rr['hash']; } return $groups; @@ -589,6 +671,70 @@ function init_groups_visitor($contact_id) { + +function get_security_ids($channel_id, $ob_hash) { + + $ret = [ + 'channel_id' => [], + 'allow_cid' => [], + 'allow_gid' => [] + ]; + + if($channel_id) { + $ch = q("select channel_hash, channel_portable_id from channel where channel_id = %d", + intval($channel_id) + ); + if($ch) { + $ret['channel_id'][] = $ch[0]['channel_hash']; + $ret['channel_id'][] = $ch[0]['channel_portable_id']; + } + } + + $groups = []; + + $x = q("select * from xchan where xchan_hash = '%s'", + dbesc($ob_hash) + ); + + if ($x) { + + // include xchans for all zot-like networks + + $xchans = q("select xchan_hash from xchan where xchan_hash = '%s' OR ( xchan_guid = '%s' AND xchan_pubkey = '%s' ) ", + dbesc($ob_hash), + dbesc($x[0]['xchan_guid']), + dbesc($x[0]['xchan_pubkey']) + ); + + if ($xchans) { + $ret['allow_cid'] = ids_to_array($xchans,'xchan_hash'); + $hashes = ids_to_querystr($xchans,'xchan_hash',true); + + // private profiles are treated as a virtual group + + $r = q("SELECT abook_profile from abook where abook_xchan in ( " . protect_sprintf($hashes) . " ) and abook_profile != '' "); + if($r) { + foreach ($r as $rv) { + $groups[] = 'vp.' . $rv['abook_profile']; + } + } + + // physical groups this identity is a member of + + $r = q("SELECT hash FROM pgrp left join pgrp_member on pgrp.id = pgrp_member.gid WHERE xchan in ( " . protect_sprintf($hashes) . " ) "); + if($r) { + foreach ($r as $rv) { + $groups[] = $rv['hash']; + } + } + $ret['allow_gid'] = $groups; + } + } + + return $ret; +} + + // This is used to determine which uid have posts which are visible to the logged in user (from the API) for the // public_timeline, and we can use this in a community page by making // $perms = (PERMS_NETWORK|PERMS_PUBLIC) unless logged in. diff --git a/include/taxonomy.php b/include/taxonomy.php index 46d95458c..b0304de5b 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -543,8 +543,6 @@ function article_catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0, function dir_tagblock($link,$r) { $o = ''; - $observer = get_observer_hash(); - if(! $r) $r = App::$data['directory_keywords']; diff --git a/include/text.php b/include/text.php index b017b038a..6a2a9e427 100644 --- a/include/text.php +++ b/include/text.php @@ -20,7 +20,7 @@ define('RANDOM_STRING_TEXT', 0x01 ); /** * @brief This is our template processor. * - * @param string|SmartyEngine $s the string requiring macro substitution, + * @param string SmartyEngine $s the string requiring macro substitution, * or an instance of SmartyEngine * @param array $r key value pairs (search => replace) * @@ -409,7 +409,8 @@ function autoname($len) { * @return string Escaped text. */ function xmlify($str) { - $buffer = ''; + + //$buffer = ''; if(is_array($str)) { @@ -418,7 +419,7 @@ function xmlify($str) { btlogger('xmlify called with array: ' . print_r($str,true), LOGGER_NORMAL, LOG_WARNING); } - +/* $len = mb_strlen($str); for($x = 0; $x < $len; $x ++) { $char = mb_substr($str,$x,1); @@ -452,6 +453,11 @@ function xmlify($str) { $buffer = trim($buffer); return($buffer); +*/ + $buffer = htmlspecialchars($str, ENT_QUOTES, "UTF-8"); + $buffer = trim($buffer); + return $buffer; + } /** @@ -464,10 +470,23 @@ function xmlify($str) { * @return string */ function unxmlify($s) { +/* $ret = str_replace('&', '&', $s); $ret = str_replace(array('<', '>', '"', '''), array('<', '>', '"', "'"), $ret); return $ret; +*/ + + if(is_array($s)) { + + // allow to fall through so we ge a PHP error, as the log statement will + // probably get lost in the noise unless we're specifically looking for it. + + btlogger('unxmlify called with array: ' . print_r($s,true), LOGGER_NORMAL, LOG_WARNING); + } + + $ret = htmlspecialchars_decode($s, ENT_QUOTES); + return $ret; } /** @@ -1682,7 +1701,12 @@ function prepare_body(&$item,$attach = false,$opts = false) { $s .= prepare_binary($item); } else { - $s .= prepare_text($item['body'],$item['mimetype'], $opts); + if($item['summary']) { + $s .= prepare_text('[summary]' . $item['summary'] . '[/summary]' . $item['body'],$item['mimetype'],$opts); + } + else { + $s .= prepare_text($item['body'],$item['mimetype'], $opts); + } } $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event_obj($item['obj']) : false); @@ -2545,15 +2569,6 @@ function design_tools() { */ function website_portation_tools() { - $channel = App::get_channel(); - $sys = false; - - if(App::$is_sys && is_site_admin()) { - require_once('include/channel.php'); - $channel = get_sys_channel(); - $sys = true; - } - return replace_macros(get_markup_template('website_portation_tools.tpl'), [ '$title' => t('Import'), '$import_label' => t('Import website...'), @@ -2721,7 +2736,6 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) $name = substr($tag,(($exclusive) ? 2 : 1)); $newname = $name; // make a copy that we can mess with - $tagcid = 0; $r = null; @@ -2780,14 +2794,9 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) } - - - - $fn_results = []; $access_tag = EMPTY_STR; - // $r is set if we found something if($r) { @@ -3081,13 +3090,12 @@ function item_url_replace($channel,&$item,$old,$new,$oldnick = '') { if($oldnick) json_url_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['target']); } + + $item['body'] = preg_replace("/(\[zrl=".preg_quote($old,'/')."\/(photos|gallery)\/".$channel['channel_address'].".+\]\[zmg=\d+x\d+\])".preg_quote($old,'/')."\/(.+\[\/zmg\])/", '${1}'.$new.'/${3}', $item['body']); + $item['body'] = preg_replace("/".preg_quote($old,'/')."\/(search|\w+\/".$channel['channel_address'].")/", $new.'/${1}', $item['body']); - $x = preg_replace("/".preg_quote($old,'/')."\/(search|\w+\/".$channel['channel_address'].")/", $new.'/${1}', $item['body']); - if($x) { - $item['body'] = $x; - $item['sig'] = base64url_encode(rsa_sign($item['body'],$channel['channel_prvkey'])); - $item['item_verified'] = 1; - } + $item['sig'] = base64url_encode(rsa_sign($item['body'],$channel['channel_prvkey'])); + $item['item_verified'] = 1; $item['plink'] = str_replace($old,$new,$item['plink']); if($oldnick) @@ -3241,7 +3249,7 @@ function flatten_array_recursive($arr) { $ret = array_merge($ret, $tmp); } } - elseif($a) { + elseif(isset($a)) { $ret[] = $a; } } diff --git a/include/xchan.php b/include/xchan.php index 4cbfb42c5..4fcdf9fce 100644 --- a/include/xchan.php +++ b/include/xchan.php @@ -1,6 +1,7 @@ <?php use Zotlabs\Zot6\HTTPSig; +use Zotlabs\Lib\Libzot; function xchan_store_lowlevel($arr) { diff --git a/include/zid.php b/include/zid.php index a37ebe1f6..ed79de76a 100644 --- a/include/zid.php +++ b/include/zid.php @@ -1,5 +1,6 @@ <?php +use Zotlabs\Lib\Verify; function is_matrix_url($url) { @@ -270,34 +271,45 @@ function red_zrlify_img_callback($matches) { */ function owt_init($token) { - \Zotlabs\Lib\Verify::purge('owt', '3 MINUTE'); + Verify::purge('owt', '3 MINUTE'); - $ob_hash = \Zotlabs\Lib\Verify::get_meta('owt', 0, $token); + $key = Verify::get_meta('owt', 0, $token); - if($ob_hash === false) { + if($key === false) { + return; + } + + $parts = explode(',',$key,2); + if(count($parts) < 2) { return; } $r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash - where hubloc_addr = '%s' order by hubloc_id desc", - dbesc($ob_hash) + where hubloc_network = '%s' and hubloc_addr = '%s' order by hubloc_id desc", + dbesc($parts[0]), + dbesc($parts[1]) ); if(! $r) { + // finger them if they can't be found. - $j = \Zotlabs\Zot\Finger::run($ob_hash, null); + // @todo check that this is still needed. Discovery should have been performed in the Owa module. + + $j = \Zotlabs\Zot\Finger::run($parts[1], null); if ($j['success']) { import_xchan($j); $r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash - where hubloc_addr = '%s' order by hubloc_id desc", - dbesc($ob_hash) + where hubloc_network = '%s' and hubloc_addr = '%s' order by hubloc_id desc", + dbesc($parts[0]), + dbesc($parts[1]) ); } } if(! $r) { - logger('owt: unable to finger ' . $ob_hash); + logger('owt: unable to finger ' . $key); return; } + $hubloc = $r[0]; $_SESSION['authenticated'] = 1; @@ -324,7 +336,7 @@ function owt_init($token) { if (! $delegate_success) { // normal visitor (remote_channel) login session credentials $_SESSION['visitor_id'] = $hubloc['xchan_hash']; - $_SESSION['my_url'] = $hubloc['xchan_url']; + $_SESSION['my_url'] = $hubloc['xchan_url']; $_SESSION['my_address'] = $hubloc['hubloc_addr']; $_SESSION['remote_hub'] = $hubloc['hubloc_url']; $_SESSION['DNT'] = 1; @@ -332,7 +344,7 @@ function owt_init($token) { $arr = [ 'xchan' => $hubloc, - 'url' => \App::$query_string, + 'url' => App::$query_string, 'session' => $_SESSION ]; /** @@ -344,11 +356,61 @@ function owt_init($token) { */ call_hooks('magic_auth_success', $arr); - \App::set_observer($hubloc); + App::set_observer($hubloc); require_once('include/security.php'); - \App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); + App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); if(! get_config('system', 'hide_owa_greeting')) - info(sprintf( t('OpenWebAuth: %1$s welcomes %2$s'),\App::get_hostname(), $hubloc['xchan_name'])); + info(sprintf( t('OpenWebAuth: %1$s welcomes %2$s'),App::get_hostname(), $hubloc['xchan_name'])); logger('OpenWebAuth: auth success from ' . $hubloc['xchan_addr']); } + + +function observer_auth($ob_hash) { + + if($ob_hash === false) { + return; + } + + $r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash + where hubloc_addr = '%s' or hubloc_id_url = '%s' or hubloc_hash = '%s' order by hubloc_id desc", + dbesc($ob_hash), + dbesc($ob_hash), + dbesc($ob_hash) + ); + + if(! $r) { + // finger them if they can't be found. + $wf = discover_by_webbie($ob_hash); + if($wf) { + $r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash + where hubloc_addr = '%s' or hubloc_id_url = '%s' or hubloc_hash = '%s' order by hubloc_id desc", + dbesc($ob_hash), + dbesc($ob_hash), + dbesc($ob_hash) + ); + } + } + if(! $r) { + logger('unable to finger ' . $ob_hash); + return; + } + + // Note: this has no Libzot namespace so prefers zot over zot6 + + $hubloc = zot_record_preferred($r); + + $_SESSION['authenticated'] = 1; + + // normal visitor (remote_channel) login session credentials + $_SESSION['visitor_id'] = $hubloc['xchan_hash']; + $_SESSION['my_url'] = $hubloc['xchan_url']; + $_SESSION['my_address'] = $hubloc['hubloc_addr']; + $_SESSION['remote_hub'] = $hubloc['hubloc_url']; + $_SESSION['DNT'] = 1; + + App::set_observer($hubloc); + require_once('include/security.php'); + App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); + +} diff --git a/include/zot.php b/include/zot.php index 3b089831b..b99eeb1ec 100644 --- a/include/zot.php +++ b/include/zot.php @@ -925,46 +925,62 @@ function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { $local = q("select channel_account_id, channel_id from channel where channel_hash = '%s' limit 1", dbesc($xchan_hash) ); + if($local) { + // @FIXME This should be removed in future when profile photo update by file sync procedure will be applied + // on most hubs in the network + // <--- $ph = z_fetch_url($arr['photo'], true); + if($ph['success']) { + + // Do not fetch already received thumbnails + $x = q("SELECT resource_id FROM photo WHERE uid = %d AND imgscale = %d AND filesize = %d LIMIT 1", + intval($local[0]['channel_id']), + intval(PHOTO_RES_PROFILE_300), + strlen($ph['body']) + ); + + if($x) + $hash = $x[0]['resource_id']; + else + $hash = import_channel_photo($ph['body'], $arr['photo_mimetype'], $local[0]['channel_account_id'], $local[0]['channel_id']); + } + + if($hash) { + // unless proven otherwise + $is_default_profile = 1; + + $profile = q("select is_default from profile where aid = %d and uid = %d limit 1", + intval($local[0]['channel_account_id']), + intval($local[0]['channel_id']) + ); + if($profile) { + if(! intval($profile[0]['is_default'])) + $is_default_profile = 0; + } - $hash = import_channel_photo($ph['body'], $arr['photo_mimetype'], $local[0]['channel_account_id'], $local[0]['channel_id']); - - if($hash) { - // unless proven otherwise - $is_default_profile = 1; - - $profile = q("select is_default from profile where aid = %d and uid = %d limit 1", + // If setting for the default profile, unset the profile photo flag from any other photos I own + if($is_default_profile) { + q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND aid = %d AND uid = %d", + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE), + dbesc($hash), intval($local[0]['channel_account_id']), intval($local[0]['channel_id']) ); - if($profile) { - if(! intval($profile[0]['is_default'])) - $is_default_profile = 0; - } - - // If setting for the default profile, unset the profile photo flag from any other photos I own - if($is_default_profile) { - q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND aid = %d AND uid = %d", - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE), - dbesc($hash), - intval($local[0]['channel_account_id']), - intval($local[0]['channel_id']) - ); - } } - - // reset the names in case they got messed up when we had a bug in this function - $photos = array( - z_root() . '/photo/profile/l/' . $local[0]['channel_id'], - z_root() . '/photo/profile/m/' . $local[0]['channel_id'], - z_root() . '/photo/profile/s/' . $local[0]['channel_id'], - $arr['photo_mimetype'], - false - ); } + // ---> + + // reset the names in case they got messed up when we had a bug in this function + $photos = array( + z_root() . '/photo/profile/l/' . $local[0]['channel_id'], + z_root() . '/photo/profile/m/' . $local[0]['channel_id'], + z_root() . '/photo/profile/s/' . $local[0]['channel_id'], + $arr['photo_mimetype'], + false + ); } else { $photos = import_xchan_photo($arr['photo'], $xchan_hash); @@ -1712,13 +1728,17 @@ function allowed_public_recips($msg) { $condensed_recips[] = $rr['hash']; $results = array(); - $r = q("select channel_hash as hash from channel left join abook on abook_channel = channel_id where abook_xchan = '%s' and channel_removed = 0 ", + $r = q("select channel_hash as hash, channel_id from channel left join abook on abook_channel = channel_id where abook_xchan = '%s' and channel_removed = 0 ", dbesc($hash) ); if($r) { - foreach($r as $rr) + foreach($r as $rr) { + $cfg = get_abconfig($rr['channel_id'],$rr['hash'],'their_perms','view_stream'); + if((! $cfg) && $scope !== 'any connections') + continue; if(in_array($rr['hash'],$condensed_recips)) $results[] = array('hash' => $rr['hash']); + } } return $results; } @@ -1840,7 +1860,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ intval($channel['channel_id']) ); if ($parent) { - $allowed = can_comment_on_post($d['hash'],$parent[0]); + $allowed = can_comment_on_post($sender['hash'],$parent[0]); } } @@ -3630,7 +3650,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', - 'channel_a_delegate', 'channel_moved' + 'channel_a_delegate', 'channel_moved', 'channel_r_photos', 'channel_w_photos' ]; $clean = array(); @@ -5266,3 +5286,25 @@ function zot_reply_notify($data) { $ret['success'] = true; json_return_and_die($ret); } + + +function zot_record_preferred($arr, $check = 'hubloc_network') { + + if(! $arr) { + return $arr; + } + + foreach($arr as $v) { + if($v[$check] === 'zot') { + return $v; + } + } + foreach($arr as $v) { + if($v[$check] === 'zot6') { + return $v; + } + } + + return $arr[0]; + +} |