diff options
Diffstat (limited to 'include')
34 files changed, 2037 insertions, 2553 deletions
diff --git a/include/account.php b/include/account.php index 5e57d53a8..6c6fdece4 100644 --- a/include/account.php +++ b/include/account.php @@ -31,7 +31,7 @@ function check_account_email($email) { if(! strlen($email)) return $result; - if((! valid_email($email)) || (! validate_email($email))) + if(! validate_email($email)) $result['message'] .= t('Not a valid email address') . EOL; elseif(! allowed_email($email)) $result['message'] = t('Your email domain is not among those allowed on this site'); @@ -105,6 +105,33 @@ function account_total() { } +function account_store_lowlevel($arr) { + + $store = [ + 'account_parent' => ((array_key_exists('account_parent',$arr)) ? $arr['account_parent'] : '0'), + 'account_default_channel' => ((array_key_exists('account_default_channel',$arr)) ? $arr['account_default_channel'] : '0'), + 'account_salt' => ((array_key_exists('account_salt',$arr)) ? $arr['account_salt'] : ''), + 'account_password' => ((array_key_exists('account_password',$arr)) ? $arr['account_password'] : ''), + 'account_email' => ((array_key_exists('account_email',$arr)) ? $arr['account_email'] : ''), + 'account_external' => ((array_key_exists('account_external',$arr)) ? $arr['account_external'] : ''), + 'account_language' => ((array_key_exists('account_language',$arr)) ? $arr['account_language'] : 'en'), + 'account_created' => ((array_key_exists('account_created',$arr)) ? $arr['account_created'] : '0001-01-01 00:00:00'), + 'account_lastlog' => ((array_key_exists('account_lastlog',$arr)) ? $arr['account_lastlog'] : '0001-01-01 00:00:00'), + 'account_flags' => ((array_key_exists('account_flags',$arr)) ? $arr['account_flags'] : '0'), + 'account_roles' => ((array_key_exists('account_roles',$arr)) ? $arr['account_roles'] : '0'), + 'account_reset' => ((array_key_exists('account_reset',$arr)) ? $arr['account_reset'] : ''), + 'account_expires' => ((array_key_exists('account_expires',$arr)) ? $arr['account_expires'] : '0001-01-01 00:00:00'), + 'account_expire_notified' => ((array_key_exists('account_expire_notified',$arr)) ? $arr['account_expire_notified'] : '0001-01-01 00:00:00'), + 'account_service_class' => ((array_key_exists('account_service_class',$arr)) ? $arr['account_service_class'] : ''), + 'account_level' => ((array_key_exists('account_level',$arr)) ? $arr['account_level'] : '0'), + 'account_password_changed' => ((array_key_exists('account_password_changed',$arr)) ? $arr['account_password_changed'] : '0001-01-01 00:00:00') + ]; + + return create_table_from_array('account',$store); + +} + + function create_account($arr) { // Required: { email, password } @@ -177,21 +204,20 @@ function create_account($arr) { $salt = random_string(32); $password_encoded = hash('whirlpool', $salt . $password); - $r = q("INSERT INTO account - ( account_parent, account_salt, account_password, account_email, account_language, - account_created, account_flags, account_roles, account_level, account_expires, account_service_class ) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', '%s' )", - intval($parent), - dbesc($salt), - dbesc($password_encoded), - dbesc($email), - dbesc(get_best_language()), - dbesc(datetime_convert()), - intval($flags), - intval($roles), - intval($techlevel), - dbesc($expires), - dbesc($default_service_class) + $r = account_store_lowlevel( + [ + 'account_parent' => intval($parent), + 'account_salt' => $salt, + 'account_password' => $password_encoded, + 'account_email' => $email, + 'account_language' => get_best_language(), + 'account_created' => datetime_convert(), + 'account_flags' => intval($flags), + 'account_roles' => intval($roles), + 'account_level' => intval($techlevel), + 'account_expires' => $expires, + 'account_service_class' => $default_service_class + ] ); if(! $r) { logger('create_account: DB INSERT failed.'); @@ -246,16 +272,18 @@ function verify_email_address($arr) { dbesc($arr['account']['account_language']) ); -//@fixme - get correct language template + push_lang(($arr['account']['account_language']) ? $arr['account']['account_language'] : 'en'); - $email_msg = replace_macros(get_intltext_template('register_verify_member.tpl'), array( - '$sitename' => get_config('system','sitename'), - '$siteurl' => z_root(), - '$email' => $arr['email'], - '$uid' => $arr['account']['account_id'], - '$hash' => $hash, - '$details' => $details - )); + $email_msg = replace_macros(get_intltext_template('register_verify_member.tpl'), + [ + '$sitename' => get_config('system','sitename'), + '$siteurl' => z_root(), + '$email' => $arr['email'], + '$uid' => $arr['account']['account_id'], + '$hash' => $hash, + '$details' => $details + ] + ); $res = z_mail( [ @@ -265,10 +293,12 @@ function verify_email_address($arr) { ] ); + pop_lang(); + if($res) $delivered ++; else - logger('send_reg_approval_email: failed to ' . $admin['email'] . 'account_id: ' . $arr['account']['account_id']); + logger('send_reg_approval_email: failed to account_id: ' . $arr['account']['account_id']); return $res; } @@ -354,9 +384,9 @@ function send_register_success_email($email,$password) { $res = z_mail( [ - 'toEmail' => $email, - 'messageSubject' => sprintf( t('Registration details for %s'), get_config('system','sitename')), - 'textVersion' => $email_msg, + 'toEmail' => $email, + 'messageSubject' => sprintf( t('Registration details for %s'), get_config('system','sitename')), + 'textVersion' => $email_msg, ] ); @@ -424,7 +454,7 @@ function account_allow($hash) { pop_lang(); - if(get_config('system','auto_channel_create') || get_config('system','server_role') === 'basic') + if(get_config('system','auto_channel_create')) auto_channel_create($register[0]['uid']); if ($res) { @@ -525,19 +555,13 @@ function account_approve($hash) { if(! $account) return $ret; - - - - if(get_config('system','auto_channel_create') || get_config('system','server_role') === 'basic') + if(get_config('system','auto_channel_create')) auto_channel_create($register[0]['uid']); else { $_SESSION['login_return_url'] = 'new_channel'; authenticate_success($account[0],null,true,true,false,true); } - - // info( t('Account verified. Please login.') . EOL ); - return true; } @@ -772,12 +796,6 @@ function upgrade_bool_message($bbcode = false) { function get_account_techlevel($account_id = 0) { - $role = \Zotlabs\Lib\System::get_server_role(); - if($role == 'basic') - return 0; - if($role == 'standard') - return 5; - if(! $account_id) { $x = \App::get_account(); } diff --git a/include/api.php b/include/api.php index 693967696..c91590070 100644 --- a/include/api.php +++ b/include/api.php @@ -194,15 +194,25 @@ require_once('include/api_zot.php'); else $redirect = trim($_REQUEST['redirect_uris']); $icon = trim($_REQUEST['logo_uri']); - $r = q("INSERT INTO clients (client_id, pw, clname, redirect_uri, icon, uid) - VALUES ('%s','%s','%s','%s','%s',%d)", - dbesc($key), - dbesc($secret), - dbesc($name), - dbesc($redirect), - dbesc($icon), - intval(0) - ); + if($oauth2) { + $r = q("INSERT INTO oauth_clients (client_id, client_secret, redirect_uri, grant_types, scope, user_id) + VALUES ( '%s', '%s', '%s', null, null, null ) ", + dbesc($key), + dbesc($secret), + dbesc($redirect) + ); + } + else { + $r = q("INSERT INTO clients (client_id, pw, clname, redirect_uri, icon, uid) + VALUES ('%s','%s','%s','%s','%s',%d)", + dbesc($key), + dbesc($secret), + dbesc($name), + dbesc($redirect), + dbesc($icon), + intval(0) + ); + } $ret['client_id'] = $key; $ret['client_secret'] = $secret; diff --git a/include/attach.php b/include/attach.php index ac50b05b1..50de3ac53 100644 --- a/include/attach.php +++ b/include/attach.php @@ -610,7 +610,8 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { // A freshly uploaded file. Check for duplicate and resolve with the channel's overwrite settings. - $r = q("select filename, id, hash, filesize from attach where filename = '%s' and folder = '%s' ", + $r = q("select filename, id, hash, filesize from attach where uid = %d and filename = '%s' and folder = '%s' ", + intval($channel_id), dbesc($filename), dbesc($folder_hash) ); @@ -632,7 +633,8 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $ext = $def_extension; } - $r = q("select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' ", + $r = q("select filename from attach where uid = %d and ( filename = '%s' OR filename like '%s' ) and folder = '%s' ", + intval($channel_id), dbesc($basename . $ext), dbesc($basename . '(%)' . $ext), dbesc($folder_hash) @@ -1596,7 +1598,10 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $folder_hash = $object['folder']; - $r_perms = recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $arr_deny_gid, $folder_hash); + $r_perms = attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $arr_deny_gid, $folder_hash); + + if($r_perms === false) //nobody has recursive perms - nobody must be notified + return; //split up returned perms $arr_allow_cid = $r_perms['allow_cid']; @@ -1675,17 +1680,9 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $arr['obj'] = $u_jsonobject; $arr['body'] = ''; - $post = item_store($arr); - $item_id = $post['item_id']; - if($item_id) { - Zotlabs\Daemon\Master::Summon(array('Notifier','activity',$item_id)); - } - - call_hooks('post_local_end', $arr); + post_activity_item($arr); $update = false; - - //notice( t('File activity updated') . EOL); } //don't create new activity if notify was not enabled @@ -1710,16 +1707,7 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $arr['obj'] = (($update) ? $u_jsonobject : $jsonobject); $arr['body'] = ''; - $post = item_store($arr); - $item_id = $post['item_id']; - - if($item_id) { - Zotlabs\Daemon\Master::Summon(array('Notifier','activity',$item_id)); - } - - call_hooks('post_local_end', $arr); - - //(($verb === 'post') ? notice( t('File activity posted') . EOL) : notice( t('File activity dropped') . EOL)); + post_activity_item($arr); return; } @@ -1776,7 +1764,7 @@ function get_file_activity_object($channel_id, $hash, $cloudpath) { } /** - * @brief Returns array of channels which have recursive permission for a file + * @brief Returns recursive permissions array or false if nobody has recursive permissions * * @param array $arr_allow_cid * @param array $arr_allow_gid @@ -1784,19 +1772,20 @@ function get_file_activity_object($channel_id, $hash, $cloudpath) { * @param array $arr_deny_gid * @param string $folder_hash */ -function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $arr_deny_gid, $folder_hash) { +function attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $arr_deny_gid, $folder_hash) { $ret = array(); $parent_arr = array(); $count_values = array(); $poster = App::get_observer(); - //turn allow_gid into allow_cid's - foreach($arr_allow_gid as $gid) { - $in_group = group_get_members($gid); + //lookup all channels in sharee group and add them to sharee $arr_allow_cid + if($arr_allow_gid) { + $in_group = expand_groups($arr_allow_gid); $arr_allow_cid = array_unique(array_merge($arr_allow_cid, $in_group)); } + //count existing parent folders - we will compare to that count later $count = 0; while($folder_hash) { $x = q("SELECT allow_cid, allow_gid, deny_cid, deny_gid, folder FROM attach WHERE hash = '%s' LIMIT 1", @@ -1805,30 +1794,20 @@ function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny //only process private folders if($x[0]['allow_cid'] || $x[0]['allow_gid'] || $x[0]['deny_cid'] || $x[0]['deny_gid']) { - $parent_arr['allow_cid'][] = expand_acl($x[0]['allow_cid']); $parent_arr['allow_gid'][] = expand_acl($x[0]['allow_gid']); - - /** - * @TODO should find a much better solution for the allow_cid <-> allow_gid problem. - * Do not use allow_gid for now. Instead lookup the members of the group directly and add them to allow_cid. - * */ - if($parent_arr['allow_gid']) { - foreach($parent_arr['allow_gid'][$count] as $gid) { - $in_group = group_get_members($gid); - $parent_arr['allow_cid'][$count] = array_unique(array_merge($parent_arr['allow_cid'][$count], $in_group)); - } - } - $parent_arr['deny_cid'][] = expand_acl($x[0]['deny_cid']); $parent_arr['deny_gid'][] = expand_acl($x[0]['deny_gid']); + //this is the number of all existing parent folders - we will compare to that count later $count++; } $folder_hash = $x[0]['folder']; } + //logger(EOL . 'parent_arr: ' . print_r($parent_arr,true)); + //if none of the parent folders is private just return file perms if(!$parent_arr['allow_cid'] && !$parent_arr['allow_gid'] && !$parent_arr['deny_cid'] && !$parent_arr['deny_gid']) { $ret['allow_gid'] = $arr_allow_gid; @@ -1839,7 +1818,7 @@ function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny return $ret; } - //if there are no perms on the file we get them from the first parent folder + //if there are no perms on the file we will work with the perms from the first parent folder if(!$arr_allow_cid && !$arr_allow_gid && !$arr_deny_cid && !$arr_deny_gid) { $arr_allow_cid = $parent_arr['allow_cid'][0]; $arr_allow_gid = $parent_arr['allow_gid'][0]; @@ -1847,52 +1826,83 @@ function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny $arr_deny_gid = $parent_arr['deny_gid'][0]; } - //allow_cid - $r_arr_allow_cid = false; - foreach ($parent_arr['allow_cid'] as $folder_arr_allow_cid) { - foreach ($folder_arr_allow_cid as $ac_hash) { - $count_values[$ac_hash]++; + + /*** + * + * check if sharee has perms for all parent folders + * + ***/ + + $r_arr_allow_cid = []; + + if($parent_arr['allow_cid']) { + //check sharee arr_allow_cid against allow_cid of all parent folders + foreach($parent_arr['allow_cid'] as $folder_arr_allow_cid) { + foreach($folder_arr_allow_cid as $ac_hash) { + $count_values[$ac_hash]++; + } } - } - foreach ($arr_allow_cid as $fac_hash) { - if($count_values[$fac_hash] == $count) - $r_arr_allow_cid[] = $fac_hash; + foreach($arr_allow_cid as $fac_hash) { + if($count_values[$fac_hash] == $count) + $r_arr_allow_cid[] = $fac_hash; + } + //logger(EOL . 'r_arr_allow_cid: ' . print_r($r_arr_allow_cid,true)); } - //allow_gid - $r_arr_allow_gid = false; - foreach ($parent_arr['allow_gid'] as $folder_arr_allow_gid) { - foreach ($folder_arr_allow_gid as $ag_hash) { - $count_values[$ag_hash]++; + if($parent_arr['allow_gid']) { + //check sharee arr_allow_cid against members of allow_gid of all parent folders + foreach($parent_arr['allow_gid'] as $folder_arr_allow_gid) { + //get the group members + $folder_arr_allow_cid = expand_groups($folder_arr_allow_gid); + foreach($folder_arr_allow_cid as $ac_hash) { + $count_values[$ac_hash]++; + } } - } - foreach ($arr_allow_gid as $fag_hash) { - if($count_values[$fag_hash] == $count) - $r_arr_allow_gid[] = $fag_hash; + foreach($arr_allow_cid as $fac_hash) { + if($count_values[$fac_hash] == $count) + $r_arr_allow_cid[] = $fac_hash; + } + //logger(EOL . 'groups - r_arr_allow_cid: ' . print_r($r_arr_allow_cid,true)); } - //deny_gid - foreach($parent_arr['deny_gid'] as $folder_arr_deny_gid) { - $r_arr_deny_gid = array_merge($arr_deny_gid, $folder_arr_deny_gid); - } - $r_arr_deny_gid = array_unique($r_arr_deny_gid); + + /*** + * + * check if sharee is denied somewhere in parent folders and deny him if so + * + ***/ //deny_cid - foreach($parent_arr['deny_cid'] as $folder_arr_deny_cid) { - $r_arr_deny_cid = array_merge($arr_deny_cid, $folder_arr_deny_cid); + $r_arr_deny_cid = []; + + if($parent_arr['deny_cid']) { + foreach($parent_arr['deny_cid'] as $folder_arr_deny_cid) { + $r_arr_deny_cid = array_merge($arr_deny_cid, $folder_arr_deny_cid); + } + $r_arr_deny_cid = array_unique($r_arr_deny_cid); + //logger(EOL . 'r_arr_deny_cid: ' . print_r($r_arr_deny_cid,true)); } - $r_arr_deny_cid = array_unique($r_arr_deny_cid); - //if none is allowed restrict to self - if(($r_arr_allow_gid === false) && ($r_arr_allow_cid === false)) { - $ret['allow_cid'] = $poster['xchan_hash']; - } else { - $ret['allow_gid'] = $r_arr_allow_gid; - $ret['allow_cid'] = $r_arr_allow_cid; - $ret['deny_gid'] = $r_arr_deny_gid; - $ret['deny_cid'] = $r_arr_deny_cid; + //deny_gid + $r_arr_deny_gid = []; + + if($parent_arr['deny_cid']) { + foreach($parent_arr['deny_gid'] as $folder_arr_deny_gid) { + $r_arr_deny_gid = array_merge($arr_deny_gid, $folder_arr_deny_gid); + } + $r_arr_deny_gid = array_unique($r_arr_deny_gid); + //logger(EOL . 'r_arr_deny_gid: ' . print_r($r_arr_dr_arr_deny_gideny_cid,true)); } + //if no channel is allowed return false + if(! $r_arr_allow_cid) + return false; + + $ret['allow_gid'] = []; // eventual group members are already collected in $r_arr_allow_cid + $ret['allow_cid'] = $r_arr_allow_cid; + $ret['deny_gid'] = $r_arr_deny_gid; + $ret['deny_cid'] = $r_arr_deny_cid; + return $ret; } @@ -2355,3 +2365,104 @@ function attach_upgrade() { } } + +function save_chunk($channel,$start,$end,$len) { + + $result = []; + + $tmp_path = $_FILES['files']['tmp_name']; + $new_base = 'store/[data]/' . $channel['channel_address'] . '/tmp'; + os_mkdir($new_base,STORAGE_DEFAULT_PERMISSIONS,true); + + $new_path = $new_base . '/' . $_FILES['files']['name']; + + if(! file_exists($new_path)) { + rename($tmp_path,$new_path); + } + else { + $istream = fopen($tmp_path,'rb'); + $ostream = fopen($new_path,'ab'); + if($istream && $ostream) { + pipe_streams($istream,$ostream); + fclose($istream); + fclose($ostream); + } + } + if(($len - 1) == $end) { + unlink($tmp_path); + $result['name'] = $_FILES['files']['tmp_name']; + $result['type'] = $_FILES['files']['type']; + $result['tmp_name'] = $new_path; + $result['error'] = 0; + $result['size'] = $len; + $result['complete'] = true; + return $result; + } + $result['partial'] = true; + $result['length'] = intval(filesize($new_path)); + return $result; +} + + +/* + * chunkloader + * Submit handler for chunked uploads + * + */ + +function chunkloader($channel,$arr) { + + logger('request: ' . print_r($arr,true), LOGGER_DEBUG); + logger('files: ' . print_r($_FILES,true), LOGGER_DEBUG); + + + $result = []; + + + $tmp_path = $_FILES['file']['tmp_name']; + $new_base = 'store/[data]/' . $channel['channel_address'] . '/tmp'; + os_mkdir($new_base,STORAGE_DEFAULT_PERMISSIONS,true); + + $new_path = $new_base . '/' . $arr['resumableFilename']; + + rename($tmp_path,$new_path . '.' . intval($arr['resumableChunkNumber'])); + + $missing_parts = false; + for($x = 1; $x <= intval($arr['resumableTotalChunks']); $x ++) { + if(! file_exists($new_path . '.' . $x)) { + $missing_parts = true; + break; + } + } + + if($missing_parts) { + $result['partial'] = true; + return $result; + } + + if(intval($arr['resumableTotalChunks']) === 1) { + rename($new_path . '.' . '1', $new_path); + } + else { + for($x = 1; $x <= intval($arr['resumableTotalChunks']); $x ++) { + $istream = fopen($new_path . '.' . $x,'rb'); + $ostream = fopen($new_path,'ab'); + if($istream && $ostream) { + pipe_streams($istream,$ostream); + fclose($istream); + fclose($ostream); + } + unlink($new_path . '.' . $x); + } + } + + $result['name'] = $arr['resumableFilename']; + $result['type'] = $arr['resumableType']; + $result['tmp_name'] = $new_path; + $result['error'] = 0; + $result['size'] = $arr['resumableTotalSize']; + $result['complete'] = true; + return $result; + +} + diff --git a/include/bbcode.php b/include/bbcode.php index 2f15cefe1..9f9b5c5e1 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -7,6 +7,66 @@ require_once('include/oembed.php'); require_once('include/event.php'); require_once('include/zot.php'); +require_once('include/html2plain.php'); + +function get_bb_tag_pos($s, $name, $occurance = 1) { + + if($occurance < 1) + $occurance = 1; + + $start_open = -1; + for($i = 1; $i <= $occurance; $i++) { + if( $start_open !== false) + $start_open = strpos($s, '[' . $name, $start_open + 1); // allow [name= type tags + } + + if( $start_open === false) + return false; + + $start_equal = strpos($s, '=', $start_open); + $start_close = strpos($s, ']', $start_open); + + if( $start_close === false) + return false; + + $start_close++; + + $end_open = strpos($s, '[/' . $name . ']', $start_close); + + if( $end_open === false) + return false; + + $res = array( 'start' => array('open' => $start_open, 'close' => $start_close), + 'end' => array('open' => $end_open, 'close' => $end_open + strlen('[/' . $name . ']')) ); + if( $start_equal !== false) + $res['start']['equal'] = $start_equal + 1; + + return $res; +} + +function bb_tag_preg_replace($pattern, $replace, $name, $s) { + + $string = $s; + + $occurance = 1; + $pos = get_bb_tag_pos($string, $name, $occurance); + while($pos !== false && $occurance < 1000) { + + $start = substr($string, 0, $pos['start']['open']); + $subject = substr($string, $pos['start']['open'], $pos['end']['close'] - $pos['start']['open']); + $end = substr($string, $pos['end']['close']); + if($end === false) + $end = ''; + + $subject = preg_replace($pattern, $replace, $subject); + $string = $start . $subject . $end; + + $occurance++; + $pos = get_bb_tag_pos($string, $name, $occurance); + } + + return $string; +} function tryoembed($match) { @@ -383,7 +443,7 @@ function bb_definitionList($match) { $eatLeadingSpaces = '(?: |[ \t])*'; // prevent spaces infront of [*= from adding another line to the previous element $listElements = preg_replace('/^(\n|<br \/>)/', '', $match[2]); // ltrim the first newline $listElements = preg_replace( - '/' . $eatLeadingSpaces . '\[\*=([[:print:]]*?)(?<!\\\)\]/ism', + '/' . $eatLeadingSpaces . '\[\*=([[:print:]]*?)(?<!\\\)\]/uism', $closeDescriptionTag . '<dt>$1</dt><dd>', $listElements ); @@ -555,10 +615,7 @@ function bb_code_options($match) { } function bb_highlight($match) { - $lang = ((in_array(strtolower($match[1]),['php','css','mysql','sql','abap','diff','html','perl','ruby', - 'vbscript','avrc','dtd','java','xml','cpp','python','javascript','js','json','sh'])) - ? strtolower($match[1]) : 'php' ); - return text_highlight($match[2],$lang); + return text_highlight($match[2],strtolower($match[1])); } function bb_fixtable_lf($match) { @@ -782,7 +839,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) if($tryoembed) { $Text = preg_replace_callback("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/$urlchars+)/ism", 'tryoembed', $Text); } - $Text = preg_replace("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/$urlchars+)/ism", '$1<a href="$2" target="_blank" >$2</a>', $Text); + $Text = preg_replace("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/$urlchars+)/ism", '$1<a href="$2" target="_blank" rel="nofollow noopener">$2</a>', $Text); } if (strpos($Text,'[/share]') !== false) { @@ -794,16 +851,16 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) } } if (strpos($Text,'[/url]') !== false) { - $Text = preg_replace("/\#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" target="_blank" >$1</a>', $Text); - $Text = preg_replace("/\#\^\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" target="_blank" >$2</a>', $Text); - $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" target="_blank" >$1</a>', $Text); - $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" target="_blank" >$2</a>', $Text); + $Text = preg_replace("/\#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\#\^\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" target="_blank" rel="nofollow noopener" >$2</a>', $Text); + $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" target="_blank" rel="nofollow noopener" >$2</a>', $Text); } if (strpos($Text,'[/zrl]') !== false) { - $Text = preg_replace("/\#\^\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" target="_blank" >$1</a>', $Text); - $Text = preg_replace("/\#\^\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" target="_blank" >$2</a>', $Text); - $Text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<a class="zrl" href="$1" target="_blank" >$1</a>', $Text); - $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" target="_blank" >$2</a>', $Text); + $Text = preg_replace("/\#\^\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\#\^\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" target="_blank" rel="nofollow noopener" >$2</a>', $Text); + $Text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<a class="zrl" href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" target="_blank" rel="nofollow noopener" >$2</a>', $Text); } if (get_account_techlevel() < 2) @@ -811,8 +868,8 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) // Perform MAIL Search if (strpos($Text,'[/mail]') !== false) { - $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1" target="_blank" >$1</a>', $Text); - $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1" target="_blank" >$2</a>', $Text); + $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); + $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1" target="_blank" rel="nofollow noopener" >$2</a>', $Text); } @@ -1132,17 +1189,17 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) // if video couldn't be embedded, link to it instead. if (strpos($Text,'[/video]') !== false) { - $Text = preg_replace("/\[video\](.*?)\[\/video\]/", '<a href="$1" target="_blank" >$1</a>', $Text); + $Text = preg_replace("/\[video\](.*?)\[\/video\]/", '<a href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); } if (strpos($Text,'[/audio]') !== false) { - $Text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '<a href="$1" target="_blank" >$1</a>', $Text); + $Text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '<a href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); } if (strpos($Text,'[/zvideo]') !== false) { - $Text = preg_replace("/\[zvideo\](.*?)\[\/zvideo\]/", '<a class="zid" href="$1" target="_blank" >$1</a>', $Text); + $Text = preg_replace("/\[zvideo\](.*?)\[\/zvideo\]/", '<a class="zid" href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); } if (strpos($Text,'[/zaudio]') !== false) { - $Text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '<a class="zid" href="$1" target="_blank" >$1</a>', $Text); + $Text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '<a class="zid" href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); } // if ($tryoembed){ @@ -1151,7 +1208,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) // } // } else { // if (strpos($Text,'[/iframe]') !== false) { -// $Text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<a href="$1" target="_blank" >$1</a>', $Text); +// $Text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<a href="$1" target="_blank" rel="nofollow noopener" >$1</a>', $Text); // } // } @@ -1174,6 +1231,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("/\[event\-start\](.*?)\[\/event\-start\]/ism",$sub,$Text); + $Text = preg_replace("/\[event\](.*?)\[\/event\]/ism",'',$Text); $Text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism",'',$Text); $Text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism",'',$Text); $Text = preg_replace("/\[event\-finish\](.*?)\[\/event\-finish\]/ism",'',$Text); diff --git a/include/channel.php b/include/channel.php index 31dca1ae5..49da57fd6 100644 --- a/include/channel.php +++ b/include/channel.php @@ -258,6 +258,7 @@ function create_identity($arr) { 'channel_system' => intval($system), 'channel_expire_days' => intval($expire), 'channel_timezone' => App::$timezone + ] ); @@ -736,14 +737,14 @@ function identity_basic_export($channel_id, $sections = null) { $ret['conv'] = $r; } - $r = q("select * from mail where mail.uid = %d", + $r = q("select * from mail where channel_id = %d", intval($channel_id) ); if($r) { $m = array(); foreach($r as $rr) { xchan_mail_query($rr); - $m[] = mail_encode($rr,true); + $m[] = encode_mail($rr,true); } $ret['mail'] = $m; } @@ -1161,31 +1162,16 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa $location = $reddress = $pdesc = $gender = $marital = $homepage = False; } + if($profile['gender']) { + $profile['gender_icon'] = gender_icon($profile['gender']); + } + $firstname = ((strpos($profile['channel_name'],' ')) ? trim(substr($profile['channel_name'],0,strpos($profile['channel_name'],' '))) : $profile['channel_name']); $lastname = (($firstname === $profile['channel_name']) ? '' : trim(substr($profile['channel_name'],strlen($firstname)))); // @fixme move this to the diaspora plugin itself - if(plugin_is_installed('diaspora')) { - $diaspora = array( - 'podloc' => z_root(), - 'guid' => $profile['channel_guid'] . str_replace('.','',App::get_hostname()), - 'pubkey' => pemtorsa($profile['channel_pubkey']), - 'searchable' => (($block) ? 'false' : 'true'), - 'nickname' => $profile['channel_address'], - 'fullname' => $profile['channel_name'], - 'firstname' => $firstname, - 'lastname' => $lastname, - 'photo300' => z_root() . '/photo/profile/300/' . $profile['uid'] . '.jpg', - 'photo100' => z_root() . '/photo/profile/100/' . $profile['uid'] . '.jpg', - 'photo50' => z_root() . '/photo/profile/50/' . $profile['uid'] . '.jpg', - ); - } - else - $diaspora = ''; - - $contact_block = contact_block(); $channel_menu = false; @@ -1218,7 +1204,6 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa '$marital' => $marital, '$homepage' => $homepage, '$chanmenu' => $channel_menu, - '$diaspora' => $diaspora, '$reddress' => $reddress, '$rating' => '', '$contact_block' => $contact_block, @@ -1229,7 +1214,29 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa call_hooks('profile_sidebar', $arr); - return $o; + return $arr['entry']; + +} + +function gender_icon($gender) { + +// logger('gender: ' . $gender); + + // This can easily get throw off if the observer language is different + // than the channel owner language. + + if(strpos(strtolower($gender),strtolower(t('Female'))) !== false) + return 'venus'; + if(strpos(strtolower($gender),strtolower(t('Male'))) !== false) + return 'mars'; + if(strpos(strtolower($gender),strtolower(t('Trans'))) !== false) + return 'transgender'; + if(strpos(strtolower($gender),strtolower(t('Neuter'))) !== false) + return 'neuter'; + if(strpos(strtolower($gender),strtolower(t('Non-specific'))) !== false) + return 'genderless'; + + return ''; } @@ -1998,49 +2005,48 @@ function remote_login() { } - function channel_store_lowlevel($arr) { + $store = [ + 'channel_account_id' => ((array_key_exists('channel_account_id',$arr)) ? $arr['channel_account_id'] : '0'), + 'channel_primary' => ((array_key_exists('channel_primary',$arr)) ? $arr['channel_primary'] : '0'), + 'channel_name' => ((array_key_exists('channel_name',$arr)) ? $arr['channel_name'] : ''), + 'channel_address' => ((array_key_exists('channel_address',$arr)) ? $arr['channel_address'] : ''), + 'channel_guid' => ((array_key_exists('channel_guid',$arr)) ? $arr['channel_guid'] : ''), + 'channel_guid_sig' => ((array_key_exists('channel_guid_sig',$arr)) ? $arr['channel_guid_sig'] : ''), + 'channel_hash' => ((array_key_exists('channel_hash',$arr)) ? $arr['channel_hash'] : ''), + 'channel_timezone' => ((array_key_exists('channel_timezone',$arr)) ? $arr['channel_timezone'] : 'UTC'), + 'channel_location' => ((array_key_exists('channel_location',$arr)) ? $arr['channel_location'] : ''), + 'channel_theme' => ((array_key_exists('channel_theme',$arr)) ? $arr['channel_theme'] : ''), + 'channel_startpage' => ((array_key_exists('channel_startpage',$arr)) ? $arr['channel_startpage'] : ''), + 'channel_pubkey' => ((array_key_exists('channel_pubkey',$arr)) ? $arr['channel_pubkey'] : ''), + 'channel_prvkey' => ((array_key_exists('channel_prvkey',$arr)) ? $arr['channel_prvkey'] : ''), + 'channel_notifyflags' => ((array_key_exists('channel_notifyflags',$arr)) ? $arr['channel_notifyflags'] : '65535'), + 'channel_pageflags' => ((array_key_exists('channel_pageflags',$arr)) ? $arr['channel_pageflags'] : '0'), + 'channel_dirdate' => ((array_key_exists('channel_dirdate',$arr)) ? $arr['channel_dirdate'] : NULL_DATE), + 'channel_lastpost' => ((array_key_exists('channel_lastpost',$arr)) ? $arr['channel_lastpost'] : NULL_DATE), + 'channel_deleted' => ((array_key_exists('channel_deleted',$arr)) ? $arr['channel_deleted'] : NULL_DATE), + 'channel_max_anon_mail' => ((array_key_exists('channel_max_anon_mail',$arr)) ? $arr['channel_max_anon_mail'] : '10'), + 'channel_max_friend_req' => ((array_key_exists('channel_max_friend_req',$arr)) ? $arr['channel_max_friend_req'] : '10'), + 'channel_expire_days' => ((array_key_exists('channel_expire_days',$arr)) ? $arr['channel_expire_days'] : '0'), + 'channel_passwd_reset' => ((array_key_exists('channel_passwd_reset',$arr)) ? $arr['channel_passwd_reset'] : ''), + 'channel_default_group' => ((array_key_exists('channel_default_group',$arr)) ? $arr['channel_default_group'] : ''), + 'channel_allow_cid' => ((array_key_exists('channel_allow_cid',$arr)) ? $arr['channel_allow_cid'] : ''), + 'channel_allow_gid' => ((array_key_exists('channel_allow_gid',$arr)) ? $arr['channel_allow_gid'] : ''), + 'channel_deny_cid' => ((array_key_exists('channel_deny_cid',$arr)) ? $arr['channel_deny_cid'] : ''), + 'channel_deny_gid' => ((array_key_exists('channel_deny_gid',$arr)) ? $arr['channel_deny_gid'] : ''), + 'channel_removed' => ((array_key_exists('channel_removed',$arr)) ? $arr['channel_removed'] : '0'), + 'channel_system' => ((array_key_exists('channel_system',$arr)) ? $arr['channel_system'] : '0'), - $store = [ - 'channel_account_id' => ((array_key_exists('channel_account_id',$arr)) ? $arr['channel_account_id'] : '0'), - 'channel_primary' => ((array_key_exists('channel_primary',$arr)) ? $arr['channel_primary'] : '0'), - 'channel_name' => ((array_key_exists('channel_name',$arr)) ? $arr['channel_name'] : ''), - 'channel_address' => ((array_key_exists('channel_address',$arr)) ? $arr['channel_address'] : ''), - 'channel_guid' => ((array_key_exists('channel_guid',$arr)) ? $arr['channel_guid'] : ''), - 'channel_guid_sig' => ((array_key_exists('channel_guid_sig',$arr)) ? $arr['channel_guid_sig'] : ''), - 'channel_hash' => ((array_key_exists('channel_hash',$arr)) ? $arr['channel_hash'] : ''), - 'channel_timezone' => ((array_key_exists('channel_timezone',$arr)) ? $arr['channel_timezone'] : 'UTC'), - 'channel_location' => ((array_key_exists('channel_location',$arr)) ? $arr['channel_location'] : ''), - 'channel_theme' => ((array_key_exists('channel_theme',$arr)) ? $arr['channel_theme'] : ''), - 'channel_startpage' => ((array_key_exists('channel_startpage',$arr)) ? $arr['channel_startpage'] : ''), - 'channel_pubkey' => ((array_key_exists('channel_pubkey',$arr)) ? $arr['channel_pubkey'] : ''), - 'channel_prvkey' => ((array_key_exists('channel_prvkey',$arr)) ? $arr['channel_prvkey'] : ''), - 'channel_notifyflags' => ((array_key_exists('channel_notifyflags',$arr)) ? $arr['channel_notifyflags'] : '65535'), - 'channel_pageflags' => ((array_key_exists('channel_pageflags',$arr)) ? $arr['channel_pageflags'] : '0'), - 'channel_dirdate' => ((array_key_exists('channel_dirdate',$arr)) ? $arr['channel_dirdate'] : NULL_DATE), - 'channel_lastpost' => ((array_key_exists('channel_lastpost',$arr)) ? $arr['channel_lastpost'] : NULL_DATE), - 'channel_deleted' => ((array_key_exists('channel_deleted',$arr)) ? $arr['channel_deleted'] : NULL_DATE), - 'channel_max_anon_mail' => ((array_key_exists('channel_max_anon_mail',$arr)) ? $arr['channel_max_anon_mail'] : '10'), - 'channel_max_friend_req' => ((array_key_exists('channel_max_friend_req',$arr)) ? $arr['channel_max_friend_req'] : '10'), - 'channel_expire_days' => ((array_key_exists('channel_expire_days',$arr)) ? $arr['channel_expire_days'] : '0'), - 'channel_passwd_reset' => ((array_key_exists('channel_passwd_reset',$arr)) ? $arr['channel_passwd_reset'] : ''), - 'channel_default_group' => ((array_key_exists('channel_default_group',$arr)) ? $arr['channel_default_group'] : ''), - 'channel_allow_cid' => ((array_key_exists('channel_allow_cid',$arr)) ? $arr['channel_allow_cid'] : ''), - 'channel_allow_gid' => ((array_key_exists('channel_allow_gid',$arr)) ? $arr['channel_allow_gid'] : ''), - 'channel_deny_cid' => ((array_key_exists('channel_deny_cid',$arr)) ? $arr['channel_deny_cid'] : ''), - 'channel_deny_gid' => ((array_key_exists('channel_deny_gid',$arr)) ? $arr['channel_deny_gid'] : ''), - 'channel_removed' => ((array_key_exists('channel_removed',$arr)) ? $arr['channel_removed'] : '0'), - 'channel_system' => ((array_key_exists('channel_system',$arr)) ? $arr['channel_system'] : '0'), 'channel_moved' => ((array_key_exists('channel_moved',$arr)) ? $arr['channel_moved'] : ''), 'channel_password' => ((array_key_exists('channel_password',$arr)) ? $arr['channel_password'] : ''), 'channel_salt' => ((array_key_exists('channel_salt',$arr)) ? $arr['channel_salt'] : '') + ]; return create_table_from_array('channel',$store); } - function profile_store_lowlevel($arr) { $store = [ @@ -2322,4 +2328,76 @@ function channel_codeallowed($channel_id) { return true; return false; + +} + +function anon_identity_init($reqvars) { + + $x = [ 'request_vars' => $reqvars, 'xchan' => null, 'success' => 'unset' ]; + call_hooks('anon_identity_init',$x); + if($x['success'] !== 'unset' && intval($x['success']) && $x['xchan']) + return $x['xchan']; + + // allow a captcha handler to over-ride + if($x['success'] !== 'unset' && (intval($x['success']) === 0)) + return false; + + + $anon_name = strip_tags(trim($reqvars['anonname'])); + $anon_email = strip_tags(trim($reqvars['anonmail'])); + $anon_url = strip_tags(trim($reqvars['anonurl'])); + + if(! ($anon_name && $anon_email)) { + logger('anonymous commenter did not complete form'); + return false; + } + + if(! validate_email($anon_email)) { + logger('enonymous email not valid'); + return false; + } + + if(! $anon_url) + $anon_url = z_root(); + + $hash = hash('md5',$anon_email); + + $x = q("select * from xchan where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'unknown' limit 1", + dbesc($anon_email), + dbesc($hash) + ); + + if(! $x) { + xchan_store_lowlevel([ + 'xchan_guid' => $anon_email, + 'xchan_hash' => $hash, + 'xchan_name' => $anon_name, + 'xchan_url' => $anon_url, + 'xchan_network' => 'unknown', + 'xchan_name_date' => datetime_convert() + ]); + + + $x = q("select * from xchan where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'unknown' limit 1", + dbesc($anon_email), + dbesc($hash) + ); + + $photo = z_root() . '/' . get_default_profile_photo(300); + $photos = import_xchan_photo($photo,$hash); + $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'unknown' ", + dbesc(datetime_convert()), + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($anon_email), + dbesc($hash) + ); + + } + + return $x[0]; } + + diff --git a/include/connections.php b/include/connections.php index 8f4f84e1a..8df795190 100644 --- a/include/connections.php +++ b/include/connections.php @@ -341,6 +341,11 @@ function contact_remove($channel_id, $abook_id) { logger('removing contact ' . $abook_id . ' for channel ' . $channel_id,LOGGER_DEBUG); + + $x = [ 'channel_id' => $channel_id, 'abook_id' => $abook_id ]; + call_hooks('connection_remove',$x); + + $archive = get_pconfig($channel_id, 'system','archive_removed_contacts'); if($archive) { q("update abook set abook_archived = 1 where abook_id = %d and abook_channel = %d", @@ -415,9 +420,9 @@ function random_profile() { for($i = 0; $i < $retryrandom; $i++) { - $r = q("select xchan_url from xchan left join hubloc on hubloc_hash = xchan_hash where xchan_addr not like '%s' and xchan_hidden = 0 and hubloc_connected > %s - interval %s order by $randfunc limit 1", - dbesc('sys@%'), - db_utcnow(), db_quoteinterval('30 day') + $r = q("select xchan_url, xchan_hash from xchan left join hubloc on hubloc_hash = xchan_hash where xchan_hidden = 0 and xchan_system = 0 and hubloc_connected > %s - interval %s order by $randfunc limit 1", + db_utcnow(), + db_quoteinterval('30 day') ); if(!$r) return ''; // Couldn't get a random channel @@ -425,12 +430,12 @@ function random_profile() { if($checkrandom) { $x = z_fetch_url($r[0]['xchan_url']); if($x['success']) - return $r[0]['xchan_url']; + return $r[0]['xchan_hash']; else logger('Random channel turned out to be bad.'); } else { - return $r[0]['xchan_url']; + return $r[0]['xchan_hash']; } } @@ -629,7 +634,7 @@ function get_vcard_array($vc,$id) { 'address' => $adr->getParts() ]; $last_entry = end($adrs); - if($adrs[$last_entry]['address']) + if($last_entry && is_array($adrs[$last_entry]['address'])) array_walk($adrs[$last_entry]['address'],'array_escape_tags'); } } @@ -712,4 +717,4 @@ function vcard_query(&$r) { } } } -}
\ No newline at end of file +} diff --git a/include/conversation.php b/include/conversation.php index 0f940b1a3..23220b390 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -353,14 +353,6 @@ function localize_item(&$item){ } */ - // if item body was obscured and we changed it, re-obscure it - // FIXME - we need a better filter than just the string 'data'; try and - // match the fact that it's json encoded - - if(intval($item['item_obscured']) - && strlen($item['body']) && (! strpos($item['body'],'data'))) { - $item['body'] = z_obscure($item['body']); - } } @@ -450,7 +442,6 @@ function is_edit_activity($item) { * figures out how to determine page owner and other contextual items * that are based on unique features of the calling module. * - * @param App &$a * @param array $items * @param string $mode * @param boolean $update @@ -458,7 +449,7 @@ function is_edit_activity($item) { * @param string $prepared_item * @return string */ -function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $prepared_item = '') { +function conversation($items, $mode, $update, $page_mode = 'traditional', $prepared_item = '') { $content_html = ''; $o = ''; @@ -541,8 +532,12 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ $live_update_div = '<div id="live-search"></div>' . "\r\n"; } + elseif ($mode === 'moderate') { + $profile_owner = local_channel(); + } + elseif ($mode === 'photos') { - $profile_onwer = App::$profile['profile_uid']; + $profile_owner = App::$profile['profile_uid']; $page_writeable = ($profile_owner == local_channel()); $live_update_div = '<div id="live-photos"></div>' . "\r\n"; // for photos we've already formatted the top-level item (the photo) @@ -585,7 +580,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ if($items) { - if($mode === 'network-new' || $mode === 'search' || $mode === 'community') { + if(in_array($mode, [ 'network-new', 'search', 'community', 'moderate' ])) { // "New Item View" on network page or search page results // - just loop through the items and format them minimally for display @@ -617,14 +612,14 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) && ($item['id'] != $item['parent'])) continue; - $nickname = $item['nickname']; +// $nickname = $item['nickname']; } - else - $nickname = App::$user['nickname']; +// else +// $nickname = App::$user['nickname']; - $profile_name = ((strlen($item['author-name'])) ? $item['author-name'] : $item['name']); - if($item['author-link'] && (! $item['author-name'])) - $profile_name = $item['author-link']; +// $profile_name = ((strlen($item['author-name'])) ? $item['author-name'] : $item['name']); +// if($item['author-link'] && (! $item['author-name'])) +// $profile_name = $item['author-link']; $sp = false; $profile_link = best_link_url($item,$sp); @@ -633,7 +628,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ else $profile_link = zid($profile_link); - $normalised = normalise_link((strlen($item['author-link'])) ? $item['author-link'] : $item['url']); +// $normalised = normalise_link((strlen($item['author-link'])) ? $item['author-link'] : $item['url']); $profile_name = $item['author']['xchan_name']; $profile_link = $item['author']['xchan_url']; @@ -687,6 +682,8 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ 'template' => $tpl, 'toplevel' => 'toplevel_item', 'mode' => $mode, + 'approve' => t('Approve'), + 'delete' => t('Delete'), 'id' => (($preview) ? 'P0' : $item['item_id']), 'linktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, $profile_url), 'profile_url' => $profile_link, @@ -917,9 +914,9 @@ function thread_action_menu($item,$mode = '') { } -function author_is_pmable($xchan) { +function author_is_pmable($xchan, $abook) { - $x = [ 'xchan' => $xchan, 'result' => 'unset' ]; + $x = [ 'xchan' => $xchan, 'abook' => $abook, 'result' => 'unset' ]; call_hooks('author_is_pmable',$x); if($x['result'] !== 'unset') return $x['result']; @@ -949,7 +946,7 @@ function thread_author_menu($item, $mode = '') { } $profile_link = chanlink_hash($item['author_xchan']); - + $contact = false; if(App::$contacts && array_key_exists($item['author_xchan'],App::$contacts)) $contact = App::$contacts[$item['author_xchan']]; @@ -958,9 +955,9 @@ function thread_author_menu($item, $mode = '') { $follow_url = z_root() . '/follow/?f=&url=' . urlencode($item['author']['xchan_addr']); - if($item['uid'] > 0 && author_is_pmable($item['author'])) + if($item['uid'] > 0 && author_is_pmable($item['author'],$contact)) { $pm_url = z_root() . '/mail/new/?f=&hash=' . urlencode($item['author_xchan']); - + } if($contact) { @@ -1625,11 +1622,9 @@ function network_tabs() { // tabs $tabs = array(); - $d = get_config('system','disable_discover_tab'); - if($d === false) - $d = 1; + $disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false; - if(! $d) { + if(! $disable_discover_tab) { $tabs[] = array( 'label' => t('Discover'), 'url' => z_root() . '/' . $cmd . '?f=&fh=1' , diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php index d362f58b2..7e925a106 100755 --- a/include/dba/dba_driver.php +++ b/include/dba/dba_driver.php @@ -163,8 +163,9 @@ abstract class dba_driver { } function get_install_script() { - if(file_exists('install/' . PLATFORM_NAME . '/' . \DBA::$install_script)) - return 'install/' . PLATFORM_NAME . '/' . \DBA::$install_script; + $platform_name = \Zotlabs\Lib\System::get_platform_name(); + if(file_exists('install/' . $platform_name . '/' . \DBA::$install_script)) + return 'install/' . $platform_name . '/' . \DBA::$install_script; return 'install/' . \DBA::$install_script; } @@ -344,11 +345,8 @@ function q($sql) { if(\DBA::$dba && \DBA::$dba->connected) { $stmt = vsprintf($sql, $args); if($stmt === false) { - if(version_compare(PHP_VERSION, '5.4.0') >= 0) - db_logger('dba: vsprintf error: ' . - print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true),LOGGER_NORMAL,LOG_CRIT); - else - db_logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true),LOGGER_NORMAL,LOG_CRIT); + db_logger('dba: vsprintf error: ' . + print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true),LOGGER_NORMAL,LOG_CRIT); } if(\DBA::$dba->debug) db_logger('Sql: ' . $stmt, LOGGER_DEBUG, LOG_INFO); @@ -445,6 +443,20 @@ function db_getfunc($f) { return $f; } +function db_load_file($f) { + // db errors should get logged to the logfile + $str = @file_get_contents($f); + $arr = explode(';', $str); + if($arr) { + foreach($arr as $a) { + if(strlen(trim($a))) { + $r = dbq(trim($a)); + } + } + } +} + + // The logger function may make DB calls internally to query the system logging parameters. // This can cause a recursion if database debugging is enabled. // So this function preserves the current database debugging state and then turns it off diff --git a/include/dba/dba_pdo.php b/include/dba/dba_pdo.php index f76e6cdd7..f119d8926 100755 --- a/include/dba/dba_pdo.php +++ b/include/dba/dba_pdo.php @@ -74,19 +74,19 @@ class dba_pdo extends dba_driver { return $result; } - if($this->debug) { - db_logger('dba_pdo: DEBUG: ' . printable($sql) . ' returned ' . count($result) . ' results.', LOGGER_NORMAL, LOG_INFO); - } - $r = array(); if($result) { foreach($result as $x) { $r[] = $x; } - if($this->debug) { - db_logger('dba_pdo: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO); - } } + + if($this->debug) { + db_logger('dba_pdo: DEBUG: ' . printable($sql) . ' returned ' . count($r) . ' results.', LOGGER_NORMAL, LOG_INFO); + db_logger('dba_pdo: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO); + } + + return (($this->error) ? false : $r); } diff --git a/include/event.php b/include/event.php index c57e52bc0..b56a5fb3e 100644 --- a/include/event.php +++ b/include/event.php @@ -210,6 +210,10 @@ function format_event_bbcode($ev) { $o = ''; + if($ev['event_vdata']) { + $o .= '[event]' . $ev['event_vdata'] . '[/event]'; + } + if($ev['summary']) $o .= '[event-summary]' . $ev['summary'] . '[/event-summary]'; @@ -1232,3 +1236,71 @@ function tasks_fetch($arr) { return $ret; } + +function cdav_principal($uri) { + $r = q("SELECT uri FROM principals WHERE uri = '%s' LIMIT 1", + dbesc($uri) + ); + + if($r[0]['uri'] === $uri) + return true; + else + return false; +} + +function cdav_perms($needle, $haystack, $check_rw = false) { + foreach ($haystack as $item) { + if($check_rw) { + if(is_array($item['id'])) { + if ($item['id'][0] == $needle && $item['share-access'] != 2) { + return $item['{DAV:}displayname']; + } + } + else { + if ($item['id'] == $needle && $item['share-access'] != 2) { + return $item['{DAV:}displayname']; + } + } + } + else { + if(is_array($item['id'])) { + if ($item['id'][0] == $needle) { + return $item['{DAV:}displayname']; + } + } + else { + if ($item['id'] == $needle) { + return $item['{DAV:}displayname']; + } + } + } + } + return false; +} + + +function translate_type($type) { + + if(!$type) + return; + + $type = strtoupper($type); + + $map = [ + 'CELL' => t('Mobile'), + 'HOME' => t('Home'), + 'HOME,VOICE' => t('Home, Voice'), + 'HOME,FAX' => t('Home, Fax'), + 'WORK' => t('Work'), + 'WORK,VOICE' => t('Work, Voice'), + 'WORK,FAX' => t('Work, Fax'), + 'OTHER' => t('Other') + ]; + + if (array_key_exists($type, $map)) { + return [$type, $map[$type]]; + } + else { + return [$type, t('Other') . ' (' . $type . ')']; + } +} diff --git a/include/features.php b/include/features.php index 0fc6fbc1d..f32dbe82e 100644 --- a/include/features.php +++ b/include/features.php @@ -45,10 +45,6 @@ function feature_level($feature,$def) { function get_features($filtered = true) { - $server_role = \Zotlabs\Lib\System::get_server_role(); - - if($server_role === 'basic' && $filtered) - return array(); $arr = [ @@ -424,16 +420,15 @@ function get_features($filtered = true) { ]; - if($server_role === 'pro') { - $arr['general'][] = [ - 'premium_channel', - t('Premium Channel'), - t('Allows you to set restrictions and terms on those that connect with your channel'), - false, - get_config('feature_lock','premium_channel'), - feature_level('premium_channel',4), - ]; - } + $arr['general'][] = [ + 'premium_channel', + t('Premium Channel'), + t('Allows you to set restrictions and terms on those that connect with your channel'), + false, + get_config('feature_lock','premium_channel'), + feature_level('premium_channel',4), + ]; + $techlevel = get_account_techlevel(); diff --git a/include/feedutils.php b/include/feedutils.php index 6bb7d103e..07cb79340 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -79,9 +79,11 @@ function get_feed_for($channel, $observer_hash, $params) { $feed_author = ''; if(intval($params['compat']) === 1) { - $feed_author = atom_author('author',$channel['channel_address'],$channel['channel_name'],$channel['xchan_url'],300,300,$channel['xchan_photo_mimetype'],$channel['xchan_photo_l']); + $feed_author = atom_render_author('author',$channel); } + $owner = atom_render_author('zot:owner',$channel); + $atom .= replace_macros($feed_template, array( '$version' => xmlify(Zotlabs\Lib\System::get_project_version()), '$red' => xmlify(Zotlabs\Lib\System::get_platform_name()), @@ -89,6 +91,7 @@ function get_feed_for($channel, $observer_hash, $params) { '$feed_title' => xmlify($channel['channel_name']), '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now', ATOM_TIME)), '$author' => $feed_author, + '$owner' => $owner, '$name' => xmlify($channel['channel_name']), '$profile_page' => xmlify($channel['xchan_url']), '$mimephoto' => xmlify($channel['xchan_photo_mimetype']), @@ -112,7 +115,7 @@ function get_feed_for($channel, $observer_hash, $params) { $items = items_fetch( [ - 'wall' => '1', + 'wall' => '1', 'datequery' => $params['end'], 'datequery2' => $params['begin'], 'start' => intval($params['start']), @@ -133,7 +136,7 @@ function get_feed_for($channel, $observer_hash, $params) { continue; /** @BUG $owner is undefined in this call */ - $atom .= atom_entry($item, $type, null, $owner, true); + $atom .= atom_entry($item, $type, null, $owner, true, '', $params['compat']); } } @@ -243,9 +246,17 @@ function get_atom_elements($feed, $item, &$author) { $found_author = $item->get_author(); if($found_author) { + if($rawauthor) { + if($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']) + $author['full_name'] = unxmlify($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']); + } $author['author_name'] = unxmlify($found_author->get_name()); $author['author_link'] = unxmlify($found_author->get_link()); $author['author_is_feed'] = false; + + $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); + logger('rawauthor: ' . print_r($rawauthor, true)); + } else { $author['author_name'] = unxmlify($feed->get_title()); @@ -256,13 +267,15 @@ function get_atom_elements($feed, $item, &$author) { if(substr($author['author_link'],-1,1) == '/') $author['author_link'] = substr($author['author_link'],0,-1); - $res['mid'] = unxmlify($item->get_id()); + $res['mid'] = normalise_id(unxmlify($item->get_id())); $res['title'] = unxmlify($item->get_title()); $res['body'] = unxmlify($item->get_content()); $res['plink'] = unxmlify($item->get_link(0)); $res['item_rss'] = 1; + $summary = unxmlify($item->get_description(true)); + // removing the content of the title if its identically to the body // This helps with auto generated titles e.g. from tumblr @@ -275,6 +288,23 @@ function get_atom_elements($feed, $item, &$author) { $base_url = ''; + $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published'); + if($rawcreated) + $res['created'] = unxmlify($rawcreated[0]['data']); + + $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'); + if($rawedited) + $res['edited'] = unxmlify($rawedited[0]['data']); + + if((x($res,'edited')) && (! (x($res,'created')))) + $res['created'] = $res['edited']; + + if(! $res['created']) + $res['created'] = $item->get_date('c'); + + if(! $res['edited']) + $res['edited'] = $item->get_date('c'); + $rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb'); // select between supported verbs @@ -283,121 +313,125 @@ function get_atom_elements($feed, $item, &$author) { $res['verb'] = unxmlify($rawverb[0]['data']); } - // translate OStatus unfollow to activity streams if it happened to get selected - - if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow')) - $res['verb'] = ACTIVITY_UNFOLLOW; + // look for a photo. We should check media size and find the best one, + // but for now let's just find any author photo - if(array_key_exists('verb',$res) && $res['verb'] === ACTIVITY_SHARE) { - // For Mastodon shares ("boosts"), we need to parse the original author information - // from the activity:object -> author structure - $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); + $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); - if($rawobj) { - $rawauthor = $rawobj->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); - if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name']) { - $author['author_name'] = unxmlify($rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name']); + if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { + $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + foreach($base as $link) { + if(!x($author, 'author_photo') || ! $author['author_photo']) { + if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') + $author['author_photo'] = unxmlify($link['attribs']['']['href']); } + } + } + $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor'); - if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri']) { - $author['author_link'] = unxmlify($rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri']); - } - if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { - $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - foreach($base as $link) { - if(!x($author, 'author_photo') || ! $author['author_photo']) { - if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') - $author['author_photo'] = unxmlify($link['attribs']['']['href']); - } + if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'], ACTIVITY_OBJ_PERSON)) { + $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + if($base && count($base)) { + foreach($base as $link) { + if($link['attribs']['']['rel'] === 'alternate' && (! $res['author_link'])) + $author['author_link'] = unxmlify($link['attribs']['']['href']); + if(!x($author, 'author_photo') || ! $author['author_photo']) { + if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo') + $author['author_photo'] = unxmlify($link['attribs']['']['href']); } } } } - else { - // look for a photo. We should check media size and find the best one, - // but for now let's just find any author photo - $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); + // check for a yahoo media element (github etc.) + if(! $author['author_photo']) { + $rawmedia = $item->get_item_tags(NAMESPACE_YMEDIA,'thumbnail'); + if($rawmedia && $rawmedia[0]['attribs']['']['url']) { + $author['author_photo'] = strip_tags(unxmlify($rawmedia[0]['attribs']['']['url'])); + } + } + + + // No photo/profile-link on the item - look at the feed level + + + if((! (x($author,'author_link'))) || (! (x($author,'author_photo')))) { + $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; foreach($base as $link) { - if(!x($author, 'author_photo') || ! $author['author_photo']) { + if($link['attribs']['']['rel'] === 'alternate' && (! $author['author_link'])) { + $author['author_link'] = unxmlify($link['attribs']['']['href']); + $author['author_is_feed'] = true; + } + if(! $author['author_photo']) { if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') $author['author_photo'] = unxmlify($link['attribs']['']['href']); } } } - $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor'); + $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject'); - if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'], ACTIVITY_OBJ_PERSON)) { + if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'],ACTIVITY_OBJ_PERSON)) { $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + if($base && count($base)) { foreach($base as $link) { if($link['attribs']['']['rel'] === 'alternate' && (! $res['author_link'])) $author['author_link'] = unxmlify($link['attribs']['']['href']); - if(!x($author, 'author_photo') || ! $author['author_photo']) { + if(! (x($author,'author_photo'))) { if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo') $author['author_photo'] = unxmlify($link['attribs']['']['href']); } } } } + } - // check for a yahoo media element (github etc.) - - if(! $author['author_photo']) { - $rawmedia = $item->get_item_tags(NAMESPACE_YMEDIA,'thumbnail'); - if($rawmedia && $rawmedia[0]['attribs']['']['url']) { - $author['author_photo'] = strip_tags(unxmlify($rawmedia[0]['attribs']['']['url'])); - } + $rawcnv = $item->get_item_tags(NAMESPACE_OSTATUS, 'conversation'); + if($rawcnv) { + // new style + $ostatus_conversation = normalise_id(unxmlify($rawcnv[0]['attribs']['']['ref'])); + if(! $ostatus_conversation) { + // old style + $ostatus_conversation = normalise_id(unxmlify($rawcnv[0]['data'])); } + if($ostatus_conversation) { + set_iconfig($res,'ostatus','conversation',$ostatus_conversation,true); + logger('ostatus_conversation: ' . $ostatus_conversation, LOGGER_DATA, LOG_INFO); + } + } + $ostatus_protocol = (($ostatus_conversation) ? true : false); + + $mastodon = (($item->get_item_tags('http://mastodon.social/schema/1.0','scope')) ? true : false); + if($mastodon) { + $ostatus_protocol = true; + if(($mastodon[0]['data']) && ($mastodon[0]['data'] !== 'public')) + $res['item_private'] = 1; + } - // No photo/profile-link on the item - look at the feed level - - if((! (x($author,'author_link'))) || (! (x($author,'author_photo')))) { - $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); - if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { - $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - foreach($base as $link) { - if($link['attribs']['']['rel'] === 'alternate' && (! $author['author_link'])) { - $author['author_link'] = unxmlify($link['attribs']['']['href']); - $author['author_is_feed'] = true; - } - if(! $author['author_photo']) { - if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') - $author['author_photo'] = unxmlify($link['attribs']['']['href']); - } - } - } + $apps = $item->get_item_tags(NAMESPACE_STATUSNET, 'notice_info'); + if($apps && $apps[0]['attribs']['']['source']) { + $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source'])); + } - $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject'); + if($ostatus_protocol) { - if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'], ACTIVITY_OBJ_PERSON)) { - $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + // translate OStatus unfollow to activity streams if it happened to get selected - if($base && count($base)) { - foreach($base as $link) { - if($link['attribs']['']['rel'] === 'alternate' && (! $res['author_link'])) - $author['author_link'] = unxmlify($link['attribs']['']['href']); - if(! (x($author,'author_photo'))) { - if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo') - $author['author_photo'] = unxmlify($link['attribs']['']['href']); - } - } - } - } + if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow')) { + $res['verb'] = ACTIVITY_UNFOLLOW; } - } - $ostatus_protocol = (($item->get_item_tags(NAMESPACE_OSTATUS, 'conversation')) ? true : false); + // And OStatus 'favorite' is pretty much what we call 'like' on other networks - $apps = $item->get_item_tags(NAMESPACE_STATUSNET, 'notice_info'); - if($apps && $apps[0]['attribs']['']['source']) { - $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source'])); + if((x($res,'verb')) && ($res['verb'] === ACTIVITY_FAVORITE)) { + $res['verb'] = ACTIVITY_LIKE; + } } /* @@ -483,6 +517,12 @@ function get_atom_elements($feed, $item, &$author) { ); } + // turn Mastodon content warning into a #nsfw hashtag + if($mastodon && $summary) { + $res['body'] .= "\n\n#ContentWarning\n"; + } + + $private = $item->get_item_tags(NAMESPACE_DFRN, 'private'); if($private && intval($private[0]['data']) > 0) $res['item_private'] = ((intval($private[0]['data'])) ? 1 : 0); @@ -493,23 +533,6 @@ function get_atom_elements($feed, $item, &$author) { if($rawlocation) $res['location'] = unxmlify($rawlocation[0]['data']); - $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published'); - if($rawcreated) - $res['created'] = unxmlify($rawcreated[0]['data']); - - $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'); - if($rawedited) - $res['edited'] = unxmlify($rawedited[0]['data']); - - if((x($res,'edited')) && (! (x($res,'created')))) - $res['created'] = $res['edited']; - - if(! $res['created']) - $res['created'] = $item->get_date('c'); - - if(! $res['edited']) - $res['edited'] = $item->get_date('c'); - // Disallow time travelling posts @@ -591,6 +614,7 @@ function get_atom_elements($feed, $item, &$author) { $attach = $item->get_enclosures(); if($attach) { + $res['attach'] = array(); foreach($attach as $att) { $len = intval($att->get_length()); @@ -607,9 +631,21 @@ function get_atom_elements($feed, $item, &$author) { if(! $type) $type = 'application/octet-stream'; + if($ostatus_protocol) { + if((strpos($type,'image') === 0) && (strpos($res['body'], ']' . $link . '[/img]') === false) && (strpos($link,'http') === 0)) { + $res['body'] .= "\n\n" . '[img]' . $link . '[/img]'; + } + if((strpos($type,'video') === 0) && (strpos($res['body'], ']' . $link . '[/video]') === false) && (strpos($link,'http') === 0)) { + $res['body'] .= "\n\n" . '[video]' . $link . '[/video]'; + } + if((strpos($type,'audio') === 0) && (strpos($res['body'], ']' . $link . '[/audio]') === false) && (strpos($link,'http') === 0)) { + $res['body'] .= "\n\n" . '[audio]' . $link . '[/audio]'; + } + } $res['attach'][] = array('href' => $link, 'length' => $len, 'type' => $type, 'title' => $title ); } } + $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); @@ -687,6 +723,13 @@ function get_atom_elements($feed, $item, &$author) { $res['target'] = $obj; } + + + if(array_key_exists('verb',$res) && $res['verb'] === ACTIVITY_SHARE + && array_key_exists('obj_type',$res) && in_array($res['obj_type'], [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT, ACTIVITY_OBJ_ACTIVITY ] )) { + feed_get_reshare($res,$item); + } + // build array to pass to hook $arr = [ 'feed' => $feed, @@ -703,6 +746,120 @@ function get_atom_elements($feed, $item, &$author) { return $arr['result']; } +function feed_get_reshare(&$res,$item) { + + $share = []; + + // For Mastodon shares ("boosts"), we need to parse the original author information + // from the activity:object -> author structure + $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); + + if($rawobj) { + + $rawauthor = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['author']; + + if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name']) { + $share['author'] = unxmlify($rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']); + } + + if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri']) { + $share['profile'] = unxmlify($rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']); + } + + if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { + $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + foreach($base as $link) { + if(! (array_key_exists('avatar',$share) && $share['avatar'])) { + if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') + $share['avatar'] = unxmlify($link['attribs']['']['href']); + } + } + } + + if(! $share['author']) + $share['author'] = t('unknown'); + if(! $share['avatar']) + $share['avatar'] = z_root() . '/' . get_default_profile_photo(80); + if(! $share['profile']) + $share['profile'] = z_root(); + + $child = $rawobj[0]['child']; + + + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) + $share['links'] = encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']); + + $rawcreated = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['published']; + + if($rawcreated) + $share['created'] = unxmlify($rawcreated[0]['data']); + else + $share['created'] = $res['created']; + + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) + $share['message_id'] = unxmlify($child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']); + + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'content') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) { + $body = unxmlify($child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']); + if(! $body) + $body = unxmlify($child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data']); + + if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { + $body = purify_html($body); + $body = html2bbcode($body); + } + } + + $attach = $share['links']; + if($attach) { + foreach($attach as $att) { + if($att['rel'] === 'alternate') { + $share['alternate'] = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att['href'])))); + continue; + } + if($att['rel'] !== 'enclosure') + continue; + $len = intval($att['length']); + $link = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att['href'])))); + $title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att['title'])))); + $type = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att['type'])))); + if(strpos($type,';')) + $type = substr($type,0,strpos($type,';')); + if((! $link) || (strpos($link,'http') !== 0)) + continue; + + if(! $title) + $title = ' '; + if(! $type) + $type = 'application/octet-stream'; + + if((strpos($type,'image') === 0) && (strpos($body, ']' . $link . '[/img]') === false) && (strpos($link,'http') === 0)) { + $body .= "\n\n" . '[img]' . $link . '[/img]'; + } + if((strpos($type,'video') === 0) && (strpos($body, ']' . $link . '[/video]') === false) && (strpos($link,'http') === 0)) { + $body .= "\n\n" . '[video]' . $link . '[/video]'; + } + if((strpos($type,'audio') === 0) && (strpos($body, ']' . $link . '[/audio]') === false) && (strpos($link,'http') === 0)) { + $body .= "\n\n" . '[audio]' . $link . '[/audio]'; + } + } + } + + $res['body'] = "[share author='" . urlencode($share['author']) . + "' profile='" . $share['profile'] . + "' avatar='" . $share['avatar'] . + "' link='" . $share['alternate'] . + "' posted='" . $share['created'] . + "' message_id='" . $share['message_id'] . "']"; + + $res['body'] .= $body; + $res['body'] .= "[/share]"; + } + +} + + + /** * @brief Encodes SimplePie_Item link arrays. * @@ -718,6 +875,10 @@ function encode_rel_links($links) { $l = array(); if($link['attribs']['']['rel']) $l['rel'] = $link['attribs']['']['rel']; + if($link['attribs']['']['length']) + $l['length'] = $link['attribs']['']['length']; + if($link['attribs']['']['title']) + $l['title'] = $link['attribs']['']['title']; if($link['attribs']['']['type']) $l['type'] = $link['attribs']['']['type']; if($link['attribs']['']['href']) @@ -838,7 +999,8 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { foreach($items as $item) { $is_reply = false; - $item_id = normalise_id($item->get_id()); + $send_downstream = false; + $parent_link = ''; logger('processing ' . $item->get_id(), LOGGER_DEBUG); @@ -847,6 +1009,11 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $is_reply = true; $parent_mid = normalise_id($rawthread[0]['attribs']['']['ref']); } + if(isset($rawthread[0]['attribs']['']['href'])) { + $parent_link = $rawthread[0]['attribs']['']['href']; + } + + logger('in-reply-to: ' . $parent_mid, LOGGER_DEBUG); if($is_reply) { @@ -855,18 +1022,35 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // Have we seen it? If not, import it. - $item_id = normalise_id($item->get_id()); $author = array(); $datarray = get_atom_elements($feed,$item,$author); - if($datarray['mid']) - $datarray['mid'] = normalise_id($item->get_id()); + if(! $datarray['mid']) + continue; + + // This probably isn't an appropriate default but we're about to change it + // if it's wrong. + + $datarray['comment_policy'] = 'authenticated'; + + // A Mastodon privacy tag has been found. We cannot send private comments + // through the OStatus protocol, so block commenting. + + if(array_key_exists('item_private',$datarray) && intval($datarray['item_private'])) { + $datarray['public_policy'] = 'specific'; + $datarray['comment_policy'] = 'none'; + } if($contact['xchan_network'] === 'rss') { $datarray['public_policy'] = 'specific'; $datarray['comment_policy'] = 'none'; } + // if we have everything but a photo, provide the default profile photo + + if($author['author_name'] && $author['author_link'] && (! $author['author_photo'])) + $author['author_photo'] = z_root() . '/' . get_default_profile_photo(80); + if((! x($author,'author_name')) || ($author['author_is_feed'])) $author['author_name'] = $contact['xchan_name']; if((! x($author,'author_link')) || ($author['author_is_feed'])) @@ -877,7 +1061,16 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $datarray['author_xchan'] = ''; if($author['author_link'] != $contact['xchan_url']) { - $x = import_author_unknown(array('name' => $author['author_name'],'url' => $author['author_link'],'photo' => array('src' => $author['author_photo']))); + $name = ''; + if($author['full_name']) { + $name = $author['full_name']; + if($author['author_name']) + $name .= ' (' . $author['author_name'] . ')'; + } + else { + $name = $author['author_name']; + } + $x = import_author_unknown(array('name' => $name,'url' => $author['author_link'],'photo' => array('src' => $author['author_photo']))); if($x) $datarray['author_xchan'] = $x; } @@ -886,8 +1079,8 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $datarray['owner_xchan'] = $contact['xchan_hash']; - $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", - dbesc($item_id), + $r = q("SELECT id, edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", + dbesc($datarray['mid']), intval($importer['channel_id']) ); @@ -902,20 +1095,148 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) continue; + $datarray['uid'] = $importer['channel_id']; + $datarray['aid'] = $importer['channel_account_id']; + $datarray['id'] = $r[0]['id']; + update_feed_item($importer['channel_id'],$datarray); + } continue; } - $x = q("select mid from item where mid = '%s' and uid = %d limit 1", - dbesc($parent_mid), - intval($importer['channel_id']) - ); - if($x) - $parent_mid = $x[0]['mid']; + $pmid = ''; + $conv_id = get_iconfig($datarray,'ostatus','conversation'); + + // match conversations - first try ostatus:conversation + // next try thr:in_reply_to + + if($conv_id) { + logger('find_parent: conversation_id: ' . $conv_id, LOGGER_DEBUG); + $c = q("select parent_mid from item left join iconfig on item.id = iconfig.iid where iconfig.cat = 'ostatus' and iconfig.k = 'conversation' and iconfig.v = '%s' and item.uid = %d order by item.id limit 1", + dbesc($conv_id), + intval($importer['channel_id']) + ); + if($c) { + logger('find_parent: matched conversation: ' . $conv_id, LOGGER_DEBUG); + $pmid = $c[0]['parent_mid']; + $datarray['parent_mid'] = $pmid; + } + } + if(! $pmid) { + $x = q("select parent_mid from item where mid = '%s' and uid = %d limit 1", + dbesc($parent_mid), + intval($importer['channel_id']) + ); + + if($x) { + logger('find_parent: matched in-reply-to: ' . $parent_mid, LOGGER_DEBUG); + $pmid = $x[0]['parent_mid']; + $datarray['parent_mid'] = $pmid; + } + } + if((! $pmid) && $parent_link !== '') { + $f = feed_conversation_fetch($importer,$contact,$parent_link); + if($f) { + // check both potential conversation parents again + if($conv_id) { + $c = q("select parent_mid from item left join iconfig on item.id = iconfig.iid where iconfig.cat = 'ostatus' and iconfig.k = 'conversation' and iconfig.v = '%s' and item.uid = %d order by item.id limit 1", + dbesc($conv_id), + intval($importer['channel_id']) + ); + if($c) { + $pmid = $c[0]['parent_mid']; + $datarray['parent_mid'] = $pmid; + } + } + if(! $pmid) { + $x = q("select parent_mid from item where mid = '%s' and uid = %d limit 1", + dbesc($parent_mid), + intval($importer['channel_id']) + ); + + if($x) { + $pmid = $x[0]['parent_mid']; + $datarray['parent_mid'] = $pmid; + } + } + } + + // the conversation parent might just be the post we are trying to import. + // check existence again in case it was just delivered. - $datarray['parent_mid'] = $parent_mid; + $r = q("SELECT id FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", + dbesc($datarray['mid']), + intval($importer['channel_id']) + ); + if($r) { + continue; + } + } + + if($pmid) { + + // check comment permissions on the parent + + $parent_item = 0; + + $r = q("select * from item where parent_mid = '%s' and parent_mid = mid and uid = %d limit 1", + dbesc($pmid), + intval($importer['channel_id']) + ); + if($r) { + $parent_item = $r[0]; + if(intval($parent_item['item_nocomment']) || $parent_item['comment_policy'] === 'none' + || ($parent_item['comments_closed'] > NULL_DATE && $parent_item['comments_closed'] < datetime_convert())) { + logger('comments disabled for post ' . $parent_item['mid']); + continue; + } + } + + $allowed = false; + + if($parent_item) { + if($parent_item['owner_xchan'] == $importer['channel_hash']) + $allowed = perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'post_comments'); + else + $allowed = true; + + if(! $allowed) { + logger('Ignoring this comment author.'); + $status = 202; + continue; + } + + // The salmon endpoint sets this to indicate that we should send comments from + // interactive feeds (such as OStatus) downstream to our followers + // We do not want to set it for non-interactive feeds or conversations we do not own + + if(array_key_exists('send_downstream',$importer) && intval($importer['send_downstream']) + && ($parent_item['owner_xchan'] == $importer['channel_hash'])) { + $send_downstream = true; + } + } + else { + if((! perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'send_stream')) && (! $importer['system'])) { + // @fixme check for and process ostatus autofriend + // otherwise + + logger('Ignoring this author.'); + continue; + } + } + } + else { + // immediate parent wasn't found. Turn into a top-level post if permissions allow + // but save the thread_parent in case we need to refer to it later. + + if(! post_is_importable($datarray, $contact)) + continue; + $datarray['parent_mid'] = $datarray['mid']; + set_iconfig($datarray,'system','parent_mid',$parent_mid,true); + } + $datarray['aid'] = $importer['channel_account_id']; $datarray['uid'] = $importer['channel_id']; @@ -923,24 +1244,48 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $xx = item_store($datarray); $r = $xx['item_id']; + + if($send_downstream) { + \Zotlabs\Daemon\Master::Summon(array('Notifier', 'comment', $r)); + } + continue; } else { // Head post of a conversation. Have we seen it? If not, import it. - $item_id = normalise_id($item->get_id()); $author = array(); $datarray = get_atom_elements($feed,$item,$author); - if($datarray['mid']) - $datarray['mid'] = normalise_id($item->get_id()); + if(! $datarray['mid']) + continue; + + // This probably isn't an appropriate default but we're about to change it + // if it's wrong. + + $datarray['comment_policy'] = 'authenticated'; + + // A Mastodon privacy tag has been found. We cannot send private comments + // through the OStatus protocol, so block commenting. + + if(array_key_exists('item_private',$datarray) && intval($datarray['item_private'])) { + $datarray['public_policy'] = 'specific'; + $datarray['comment_policy'] = 'none'; + } if($contact['xchan_network'] === 'rss') { $datarray['public_policy'] = 'specific'; $datarray['comment_policy'] = 'none'; } + + + // if we have everything but a photo, provide the default profile photo + + if($author['author_name'] && $author['author_link'] && (! $author['author_photo'])) + $author['author_photo'] = z_root() . '/' . get_default_profile_photo(80); + if(is_array($contact)) { if((! x($author,'author_name')) || ($author['author_is_feed'])) $author['author_name'] = $contact['xchan_name']; @@ -957,19 +1302,17 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $datarray['author_xchan'] = ''; - if(activity_match($datarray['verb'],ACTIVITY_FOLLOW) && $datarray['obj_type'] === ACTIVITY_OBJ_PERSON) { - $cb = array('item' => $datarray,'channel' => $importer, 'xchan' => [ 'placeholder' => '' ], 'author' => $author, 'caught' => false); - call_hooks('follow_from_feed',$cb); - if($cb['caught']) { - if($cb['return_code']) - http_status_exit($cb['return_code']); - - continue; - } - } - if($author['author_link'] != $contact['xchan_url']) { - $x = import_author_unknown(array('name' => $author['author_name'],'url' => $author['author_link'],'photo' => array('src' => $author['author_photo']))); + $name = ''; + if($author['full_name']) { + $name = $author['full_name']; + if($author['author_name']) + $name .= ' (' . $author['author_name'] . ')'; + } + else { + $name = $author['author_name']; + } + $x = import_author_unknown(array('name' => $name,'url' => $author['author_link'],'photo' => array('src' => $author['author_photo']))); if($x) $datarray['author_xchan'] = $x; } @@ -988,8 +1331,8 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { } - $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", - dbesc($item_id), + $r = q("SELECT id, edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", + dbesc($datarray['mid']), intval($importer['channel_id']) ); @@ -1003,13 +1346,17 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) continue; + $datarray['uid'] = $importer['channel_id']; + $datarray['aid'] = $importer['channel_account_id']; + $datarray['id'] = $r[0]['id']; + update_feed_item($importer['channel_id'],$datarray); } continue; } - $datarray['parent_mid'] = $item_id; + $datarray['parent_mid'] = $datarray['mid']; $datarray['uid'] = $importer['channel_id']; $datarray['aid'] = $importer['channel_account_id']; @@ -1035,6 +1382,51 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { } } + +function feed_conversation_fetch($importer,$contact,$parent_link) { + + logger('parent_link: ' . $parent_link, LOGGER_DEBUG, LOG_INFO); + + $link = ''; + + // GNU-Social flavoured feeds + if(strpos($parent_link,'/notice/')) { + $link = str_replace('/notice/','/api/statuses/show/',$parent_link) . '.atom'; + } + + // Mastodon flavoured feeds + if(strpos($parent_link,'/users/') && strpos($parent_link,'/updates/')) { + $link = $parent_link . '.atom'; + } + + if(! $link) + return false; + + logger('fetching: ' . $link, LOGGER_DEBUG, LOG_INFO); + + $fetch = z_fetch_url($link); + + if(! $fetch['success']) + return false; + + $data = $fetch['body']; + + // We will probably receive an atom 'entry' and not an atom 'feed'. Unfortunately + // our parser is a bit strict about compliance so we'll insert just enough of a feed + // tag to trick it into believing it's a compliant feed. + + if(! strstr($data,'<feed')) { + $data = str_replace('<entry ','<feed xmlns="http://www.w3.org/2005/Atom"><entry ',$data); + $data .= '</feed>'; + } + + consume_feed($data,$importer,$contact,1); + consume_feed($data,$importer,$contact,2); + + return true; + +} + /** * @brief Normalise an id. * @@ -1112,7 +1504,7 @@ function process_salmon_feed($xml, $importer) { // reset policies which are restricted by default for RSS connections // This item is likely coming from GNU-social via salmon and allows public interaction $datarray['public_policy'] = ''; - $datarray['comment_policy'] = ''; + $datarray['comment_policy'] = 'authenticated'; $ret['item'] = $datarray; } @@ -1179,6 +1571,10 @@ function feed_meta($xml) { } } + if(! $author['author_photo']) + $author['author_photo'] = $feed->get_image_url(); + + if(substr($author['author_link'],-1,1) == '/') $author['author_link'] = substr($author['author_link'],0,-1); @@ -1194,7 +1590,7 @@ function feed_meta($xml) { * @param array $datarray */ function update_feed_item($uid, $datarray) { - logger('Not implemented! ' . $uid . ' ' . print_r($datarray, true), LOGGER_DATA); + item_store_update($datarray); } /** @@ -1272,6 +1668,59 @@ function atom_author($tag, $nick, $name, $uri, $h, $w, $type, $photo) { return $o; } + +function atom_render_author($tag,$xchan) { + + + $nick = xmlify(substr($xchan['xchan_addr'],0,strpos($xchan['xchan_addr'],'@'))); + $id = xmlify($xchan['xchan_url']); + $name = xmlify($xchan['xchan_name']); + $photo = xmlify($xchan['xchan_photo_l']); + $type = xmlify($xchan['xchan_photo_mimetype']); + $w = $h = 300; + + $o .= "<$tag>\r\n"; + $o .= " <as:object-type>http://activitystrea.ms/schema/1.0/person</as:object-type>\r\n"; + $o .= " <id>$id</id>\r\n"; + $o .= " <name>$nick</name>\r\n"; + $o .= " <uri>$id</uri>\r\n"; + $o .= ' <link rel="alternate" type="text/html" href="' . $id . '" />' . "\r\n"; + $o .= ' <link rel="photo" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n"; + $o .= ' <link rel="avatar" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n"; + $o .= ' <poco:preferredUsername>' . $nick . '</poco:preferredUsername>' . "\r\n"; + $o .= ' <poco:displayName>' . $name . '</poco:displayName>' . "\r\n"; + + call_hooks('atom_render_author', $o); + + $o .= "</$tag>\r\n"; + + return $o; + + +} + +function compat_photos_list($s) { + + $ret = []; + + $found = preg_match_all('/\[[zi]mg(.*?)\](.*?)\[/ism',$s,$matches,PREG_SET_ORDER); + + if($found) { + foreach($matches as $match) { + $ret[] = [ + 'href' => $match[2], + 'length' => 0, + 'type' => guess_image_type($match[2]) + ]; + + } + } + + return $ret; +} + + + /** * @brief Create an item for the Atom feed. * @@ -1285,7 +1734,8 @@ function atom_author($tag, $nick, $name, $uri, $h, $w, $type, $photo) { * @param number $cid default 0 * @return void|string */ -function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0) { +function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0, $compat = false) { + if(! $item['parent']) return; @@ -1300,23 +1750,31 @@ function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0) { else $body = $item['body']; + if($compat) { + $compat_photos = compat_photos_list($body); + } + else { + $compat_photos = null; + } + $o = "\r\n\r\n<entry>\r\n"; if(is_array($author)) { - $reddress = substr($author['xchan_addr'],0,strpos($author['xchan_addr'],'@')); - $o .= atom_author('author',$reddress,$author['xchan_name'],$author['xchan_url'],80,80,$author['xchan_photo_mimetype'],$author['xchan_photo_m']); + $o .= atom_render_author('author',$author); } else { - $reddress = substr($item['author']['xchan_addr'],0,strpos($item['author']['xchan_addr'],'@')); - $o .= atom_author('author',$reddress,$item['author']['xchan_name'],$item['author']['xchan_url'],80,80,$item['author']['xchan_photo_mimetype'], $item['author']['xchan_photo_m']); + $o .= atom_render_author('author',$item['author']); } - $reddress = substr($item['owner']['xchan_addr'],0,strpos($item['owner']['xchan_addr'],'@')); - $o .= atom_author('zot:owner',$reddress,$item['owner']['xchan_name'],$item['owner']['xchan_url'],80,80,$item['owner']['xchan_photo_mimetype'],$item['owner']['xchan_photo_m']); + $o .= atom_render_author('zot:owner',$item['owner']); if(($item['parent'] != $item['id']) || ($item['parent_mid'] !== $item['mid']) || (($item['thr_parent'] !== '') && ($item['thr_parent'] !== $item['mid']))) { $parent_item = (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']); - $o .= '<thr:in-reply-to ref="' . 'X-ZOT:' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; + // ensure it's a legal uri and not just a message-id + if(! strpos($parent_item,':')) + $parent_item = 'X-ZOT:' . $parent_item; + + $o .= '<thr:in-reply-to ref="' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; } if(activity_match($item['obj_type'],ACTIVITY_OBJ_EVENT) && activity_match($item['verb'],ACTIVITY_POST)) { @@ -1372,10 +1830,19 @@ function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0) { . (($enc['href']) ? 'href="' . $enc['href'] . '" ' : '') . (($enc['length']) ? 'length="' . $enc['length'] . '" ' : '') . (($enc['type']) ? 'type="' . $enc['type'] . '" ' : '') - . ' />'; + . ' />' . "\r\n"; } } } + if($compat_photos) { + foreach($compat_photos as $enc) { + $o .= '<link rel="enclosure" ' + . (($enc['href']) ? 'href="' . $enc['href'] . '" ' : '') + . ((array_key_exists('length',$enc)) ? 'length="' . $enc['length'] . '" ' : '') + . (($enc['type']) ? 'type="' . $enc['type'] . '" ' : '') + . ' />' . "\r\n"; + } + } if($item['term']) { foreach($item['term'] as $term) { @@ -1427,98 +1894,3 @@ function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0) { return $x['entry']; } -/** - * @brief - * - * @param array $items - * @return array - */ -function gen_asld($items) { - $ret = array(); - if(! $items) - return $ret; - - foreach($items as $item) { - $ret[] = i2asld($item); - } - - return $ret; -} - -/** - * @brief - * - * @param array $i - * @return array - */ -function i2asld($i) { - - if(! $i) - return array(); - - $ret = array(); - - $ret['@context'] = array( 'http://www.w3.org/ns/activitystreams', 'zot' => 'http://purl.org/zot/protocol'); - - if($i['verb']) { - if(strpos(dirname($i['verb'],'activitystrea.ms/schema/1.0'))) { - $ret['@type'] = ucfirst(basename($i['verb'])); - } - elseif(strpos(dirname($i['verb'],'purl.org/zot'))) { - $ret['@type'] = 'zot:' . ucfirst(basename($i['verb'])); - } - } - $ret['@id'] = $i['plink']; - - $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); - - // we need to pass the parent into this -// if($i['id'] != $i['parent'] && $i['obj_type'] === ACTIVITY_OBJ_NOTE) { -// $ret['inReplyTo'] = asencode_note -// } - - if($i['obj_type'] === ACTIVITY_OBJ_NOTE) - $ret['object'] = asencode_note($i); - - $ret['actor'] = asencode_person($i['author']); - - return $ret; -} - -function asencode_note($i) { - - $ret = array(); - - $ret['@type'] = 'Note'; - $ret['@id'] = $i['plink']; - if($i['title']) - $ret['title'] = bbcode($i['title']); - - $ret['content'] = bbcode($i['body']); - $ret['zot:owner'] = asencode_person($i['owner']); - $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); - if($i['created'] !== $i['edited']) - $ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME); - - return $ret; -} - - -function asencode_person($p) { - $ret = array(); - $ret['@type'] = 'Person'; - $ret['@id'] = 'acct:' . $p['xchan_addr']; - $ret['displayName'] = $p['xchan_name']; - $ret['icon'] = array( - '@type' => 'Link', - 'mediaType' => $p['xchan_photo_mimetype'], - 'href' => $p['xchan_photo_m'] - ); - $ret['url'] = array( - '@type' => 'Link', - 'mediaType' => 'text/html', - 'href' => $p['xchan_url'] - ); - - return $ret; -} diff --git a/include/follow.php b/include/follow.php index 0d7c16aa3..9e2fd6a9c 100644 --- a/include/follow.php +++ b/include/follow.php @@ -182,6 +182,11 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $hash = get_observer_hash(); $default_group = $channel['channel_default_group']; + if($hash == $xchan_hash) { + $result['message'] = t('Cannot connect to yourself.'); + return $result; + } + if($xchan['xchan_network'] === 'rss') { // check service class feed limits @@ -196,28 +201,22 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $result['message'] = upgrade_message(); return $result; } - } - if($hash == $xchan_hash) { - $result['message'] = t('Cannot connect to yourself.'); - return $result; + // Always set these "remote" permissions for feeds since we cannot interact with them + // to negotiate a suitable permission response + + set_abconfig($uid,$xchan_hash,'their_perms','view_stream',1); + set_abconfig($uid,$xchan_hash,'their_perms','republish',1); + } + $r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid) ); - if($is_http) { - - // Always set these "remote" permissions for feeds since we cannot interact with them - // to negotiate a suitable permission response - - set_abconfig($uid,$xchan_hash,'their_perms','view_stream',1); - set_abconfig($uid,$xchan_hash,'their_perms','republish',1); - } - if($r) { $abook_instance = $r[0]['abook_instance']; @@ -226,12 +225,12 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) if($abook_instance) $abook_instance .= ','; $abook_instance .= z_root(); - } - $x = q("update abook set abook_instance = '%s' where abook_id = %d", - dbesc($abook_instance), - intval($r[0]['abook_id']) - ); + $x = q("update abook set abook_instance = '%s', abook_not_here = 0 where abook_id = %d", + dbesc($abook_instance), + intval($r[0]['abook_id']) + ); + } if(intval($r[0]['abook_pending'])) { $x = q("update abook set abook_pending = 0 where abook_id = %d", @@ -250,7 +249,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) 'abook_channel' => intval($uid), 'abook_closeness' => intval($closeness), 'abook_xchan' => $xchan_hash, - 'abook_feed' => intval(($is_http) ? 1 : 0), + 'abook_feed' => intval(($xchan['xchan_network'] === 'rss') ? 1 : 0), 'abook_created' => datetime_convert(), 'abook_updated' => datetime_convert(), 'abook_instance' => (($singleton) ? z_root() : '') diff --git a/include/group.php b/include/group.php index 3b208ef95..e0c20b536 100644 --- a/include/group.php +++ b/include/group.php @@ -278,8 +278,9 @@ function group_side($every="connections",$each="group",$edit = false, $group_id $o = ''; - if(! (local_channel() && feature_enabled(local_channel(),'groups'))) + if(! (local_channel() && feature_enabled(local_channel(),'groups'))) { return ''; + } $groups = array(); diff --git a/include/help.php b/include/help.php index e4725575d..4f9251b1b 100644 --- a/include/help.php +++ b/include/help.php @@ -57,7 +57,7 @@ function get_help_content($tocpath = false) { if(! $text) { $doctype = 'bbcode'; $text = load_doc_file('doc/main.bb'); - goaway('/help/about/about_hubzilla'); + goaway('/help/about/about'); \App::$page['title'] = t('Help'); } @@ -116,9 +116,11 @@ function load_doc_file($s) { $b = basename($s); $d = dirname($s); - $c = find_doc_file("$d/$lang/$b"); - if($c) - return $c; + if($dirname !== '-') { + $c = find_doc_file("$d/$lang/$b"); + if($c) + return $c; + } $c = find_doc_file($s); if($c) return $c; @@ -140,8 +142,8 @@ function find_doc_file($s) { */ function search_doc_files($s) { - $itemspage = get_pconfig(local_channel(),'system','itemspage'); - \App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); + + \App::set_pager_itemspage(60); $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start'])); $regexop = db_getfunc('REGEXP'); @@ -198,6 +200,7 @@ function doc_rank_sort($s1, $s2) { * * @return string */ + function load_context_help() { $path = App::$cmd; diff --git a/include/import.php b/include/import.php index 3f4d84765..5cb354cc3 100644 --- a/include/import.php +++ b/include/import.php @@ -628,16 +628,18 @@ function import_items($channel, $items, $sync = false, $relocate = null) { $item_result = item_store($item,$allow_code,$deliver); } + // preserve conversations you've been involved in from being expired + + $stored = $item_result['item']; + if((is_array($stored)) && ($stored['id'] != $stored['parent']) + && ($stored['author_xchan'] === $channel['channel_hash'])) { + retain_item($stored['item']['parent']); + } + fix_attached_photo_permissions($channel['channel_id'],$item['author_xchan'],$item['body'],$item['allow_cid'],$item['allow_gid'],$item['deny_cid'],$item['deny_gid']); fix_attached_file_permissions($channel,$item['author_xchan'],$item['body'],$item['allow_cid'],$item['allow_gid'],$item['deny_cid'],$item['deny_gid']); - if($sync && $item['item_wall']) { - // deliver singletons if we have any - if($item_result && $item_result['success']) { - Zotlabs\Daemon\Master::Summon( [ 'Notifier','single_activity',$item_result['item_id'] ]); - } - } } } } @@ -1011,9 +1013,6 @@ function import_mail($channel, $mails, $sync = false) { $m['aid'] = $channel['channel_account_id']; $m['uid'] = $channel['channel_id']; $mail_id = mail_store($m); - if($sync && $mail_id) { - Zotlabs\Daemon\Master::Summon(array('Notifier','single_mail',$mail_id)); - } } } } @@ -1094,11 +1093,12 @@ function sync_files($channel, $files) { $ext = ''; } - $r = q("select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' and hash != '%s' ", + $r = q("select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' and hash != '%s' and uid = %d ", dbesc($basename . $ext), dbesc($basename . '(%)' . $ext), dbesc($att['folder']), - dbesc($att['hash']) + dbesc($att['hash']), + intval($channel['channel_id']) ); if($r) { @@ -1264,7 +1264,7 @@ function sync_files($channel, $files) { $r = dbq("update photo set " . $str . " where id = " . intval($exists[0]['id']) ); } else { - create_attach_from_array('photo',$p); + create_table_from_array('photo',$p); } } } diff --git a/include/items.php b/include/items.php index 328079b9d..5a0ca01c6 100755 --- a/include/items.php +++ b/include/items.php @@ -8,6 +8,7 @@ use Zotlabs\Lib as Zlib; require_once('include/bbcode.php'); require_once('include/oembed.php'); require_once('include/crypto.php'); +require_once('include/message.php'); require_once('include/feedutils.php'); require_once('include/photo/photo_driver.php'); require_once('include/permissions.php'); @@ -175,6 +176,13 @@ function item_normal() { and item.item_blocked = 0 "; } +function item_normal_search() { + return " and item.item_hidden = 0 and item.item_type in (0,3) and item.item_deleted = 0 + and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 + and item.item_blocked = 0 "; +} + + /** * @brief * @@ -212,6 +220,11 @@ function can_comment_on_post($observer_xchan, $item) { // logger('can_comment_on_post: comment_policy: ' . $item['comment_policy'], LOGGER_DEBUG); + $x = [ 'observer_hash' => $observer_xchan, 'item' => $item, 'allowed' => 'unset' ]; + call_hooks('can_comment_on_post',$x); + if($x['allowed'] !== 'unset') + return $x['allowed']; + if(! $observer_xchan) return false; @@ -248,8 +261,6 @@ function can_comment_on_post($observer_xchan, $item) { } if(strstr($item['comment_policy'],'network:') && strstr($item['comment_policy'],'red')) return true; - if(strstr($item['comment_policy'],'network:') && strstr($item['comment_policy'],'diaspora')) - return true; if(strstr($item['comment_policy'],'site:') && strstr($item['comment_policy'],App::get_hostname())) return true; @@ -587,11 +598,6 @@ function get_item_elements($x,$allow_code = false) { $arr['sig'] = (($x['signature']) ? htmlspecialchars($x['signature'], ENT_COMPAT,'UTF-8',false) : ''); - if(array_key_exists('diaspora_signature',$x) && is_array($x['diaspora_signature'])) - $x['diaspora_signature'] = json_encode($x['diaspora_signature']); - - $arr['diaspora_meta'] = (($x['diaspora_signature']) ? $x['diaspora_signature'] : ''); - $arr['obj'] = activity_sanitise($x['object']); $arr['target'] = activity_sanitise($x['target']); @@ -797,8 +803,10 @@ function import_author_xchan($x) { if((! array_key_exists('network', $x)) || ($x['network'] === 'zot')) { $y = import_author_zot($x); } - if(! $y) - $y = import_author_diaspora($x); + + // if we were told that it's a zot connection, don't probe/import anything else + if(array_key_exists('network',$x) && $x['network'] === 'zot') + return $y; if($x['network'] === 'rss') { $y = import_author_rss($x); @@ -809,36 +817,7 @@ function import_author_xchan($x) { } return($y); -} - -/** - * @brief Imports an author from Diaspora. - * - * @param array $x an associative array with - * * \e string \b address - * @return boolean|string false on error, otherwise xchan_hash of the new entry - */ -function import_author_diaspora($x) { - if(! $x['address']) - return false; - - $r = q("select * from xchan where xchan_addr = '%s' limit 1", - dbesc($x['address']) - ); - if($r) { - logger('in_cache: ' . $x['address'], LOGGER_DATA); - return $r[0]['xchan_hash']; - } - - if(discover_by_webbie($x['address'])) { - $r = q("select xchan_hash from xchan where xchan_addr = '%s' limit 1", - dbesc($x['address']) - ); - if($r) - return $r[0]['xchan_hash']; - } - return false; } /** @@ -850,6 +829,7 @@ function import_author_diaspora($x) { * * \e string \b guid * @return boolean|string */ + function import_author_rss($x) { if(! $x['url']) return false; @@ -897,6 +877,11 @@ function import_author_rss($x) { function import_author_unknown($x) { + $arr = [ 'author' => $x, 'result' => false ]; + call_hooks('import_author', $arr); + if($arr['result']) + return $arr['result']; + if(! $x['url']) return false; @@ -926,7 +911,7 @@ function import_author_unknown($x) { $photos = import_xchan_photo($x['photo']['src'],$x['url']); if($photos) { - $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_url = '%s' and xchan_network = 'unknown'", + $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s' and xchan_network = 'unknown'", dbesc(datetime_convert()), dbesc($photos[0]), dbesc($photos[1]), @@ -1056,17 +1041,7 @@ function encode_item($item,$mirror = false) { if($item['iconfig']) $x['meta'] = encode_item_meta($item['iconfig'],$mirror); - if($item['diaspora_meta']) { - $z = json_decode($item['diaspora_meta'],true); - if($z) { - if(is_array($z) && array_key_exists('iv',$z)) - $x['diaspora_signature'] = crypto_unencapsulate($z,$key); - else - $x['diaspora_signature'] = $z; - if(! is_array($z)) - logger('encode_item: diaspora meta is not an array: ' . print_r($z,true)); - } - } + logger('encode_item: ' . print_r($x,true), LOGGER_DATA); return $x; @@ -1347,11 +1322,12 @@ function encode_mail($item,$extended = false) { $x['message_parent'] = $item['parent_mid']; $x['created'] = $item['created']; $x['expires'] = $item['expires']; - $x['diaspora_meta'] = $item['diaspora_meta']; $x['title'] = $item['title']; $x['body'] = $item['body']; $x['from'] = encode_item_xchan($item['from']); $x['to'] = encode_item_xchan($item['to']); + $x['raw'] = $item['mail_raw']; + $x['mimetype'] = $item['mail_mimetype']; if($item['attach']) $x['attach'] = json_decode($item['attach'],true); @@ -1385,9 +1361,16 @@ function get_mail_elements($x) { $arr = array(); - $arr['body'] = (($x['body']) ? htmlspecialchars($x['body'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['title'] = (($x['title'])? htmlspecialchars($x['title'],ENT_COMPAT,'UTF-8',false) : ''); + if(intval($x['raw'])) { + $arr['mail_raw'] = intval($x['raw']); + $arr['body'] = $x['body']; + } + else { + $arr['body'] = (($x['body']) ? htmlspecialchars($x['body'], ENT_COMPAT,'UTF-8',false) : ''); + } + $arr['title'] = (($x['title'])? htmlspecialchars($x['title'],ENT_COMPAT,'UTF-8',false) : ''); + $arr['mail_mimetype'] = (($x['mimetype']) ? htmlspecialchars($x['mimetype'],ENT_COMPAT,'UTF-8',false) : 'text/bbcode'); $arr['conv_guid'] = (($x['conv_guid'])? htmlspecialchars($x['conv_guid'],ENT_COMPAT,'UTF-8',false) : ''); $arr['created'] = datetime_convert('UTC','UTC',$x['created']); @@ -1560,7 +1543,6 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $arr['title'] = ((array_key_exists('title',$arr) && strlen($arr['title'])) ? trim($arr['title']) : ''); $arr['body'] = ((array_key_exists('body',$arr) && strlen($arr['body'])) ? trim($arr['body']) : ''); - $arr['diaspora_meta'] = ((x($arr,'diaspora_meta')) ? $arr['diaspora_meta'] : ''); $arr['allow_cid'] = ((x($arr,'allow_cid')) ? trim($arr['allow_cid']) : ''); $arr['allow_gid'] = ((x($arr,'allow_gid')) ? trim($arr['allow_gid']) : ''); $arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : ''); @@ -1755,7 +1737,7 @@ logger('revision: ' . $arr['revision']); if($r[0]['owner_xchan'] !== $arr['owner_xchan']) { $arr['owner_xchan'] = $r[0]['owner_xchan']; -// $uplinked_comment = true; + // $uplinked_comment = true; } // if the parent is private, force privacy for the entire conversation @@ -2060,7 +2042,7 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { $arr['changed'] = $orig[0]['changed']; $arr['route'] = ((array_key_exists('route',$arr)) ? trim($arr['route']) : $orig[0]['route']); - $arr['diaspora_meta'] = ((x($arr,'diaspora_meta')) ? $arr['diaspora_meta'] : $orig[0]['diaspora_meta']); + $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : $orig[0]['location']); $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : $orig[0]['coord']); $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : $orig[0]['verb']); @@ -2219,55 +2201,6 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { -function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, $walltowall = false) { - - // We won't be able to sign Diaspora comments for authenticated visitors - // - we don't have their private key - - // since Diaspora doesn't handle edits we can only do this for the original text and not update it. - - require_once('include/markdown.php'); - $signed_body = bb2diaspora_itembody($datarray,$walltowall); - - if($walltowall) { - logger('wall to wall comment',LOGGER_DEBUG); - // post will come across with the owner's identity. Throw a preamble onto the post to indicate the true author. - $signed_body = "\n\n" - . '![' . $datarray['author']['xchan_name'] . '](' . $datarray['author']['xchan_photo_m'] . ')' - . '[' . $datarray['author']['xchan_name'] . '](' . $datarray['author']['xchan_url'] . ')' . "\n\n" - . $signed_body; - } - - logger('storing diaspora comment signature',LOGGER_DEBUG); - - $diaspora_handle = channel_reddress($channel); - - $signed_text = $datarray['mid'] . ';' . $parent_item['mid'] . ';' . $signed_body . ';' . $diaspora_handle; - - - if( $channel && $channel['channel_prvkey'] ) - $authorsig = base64_encode(rsa_sign($signed_text, $channel['channel_prvkey'], 'sha256')); - else - $authorsig = ''; - - $x = array('signer' => $diaspora_handle, 'body' => $signed_body, 'signed_text' => $signed_text, 'signature' => $authorsig); - - $y = json_encode($x); - - $r = q("update item set diaspora_meta = '%s' where id = %d", - dbesc($y), - intval($post_id) - ); - - - if(! $r) - logger('store_diaspora_comment_sig: DB write failed'); - - return; -} - - - function send_status_notifications($post_id,$item) { // only send notifications for comments @@ -3000,6 +2933,8 @@ function mail_store($arr) { return 0; } + $channel = channelx_by_n($arr['channel_id']); + if(! $arr['mail_obscured']) { if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false)) $arr['body'] = escape_tags($arr['body']); @@ -3025,14 +2960,39 @@ function mail_store($arr) { $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : ''); $arr['sig'] = ((x($arr,'sig')) ? trim($arr['sig']) : ''); $arr['conv_guid'] = ((x($arr,'conv_guid')) ? trim($arr['conv_guid']) : ''); + $arr['mail_mimetype'] = ((x($arr,'mail_mimetype')) ? trim($arr['mail_mimetype']) : 'text/bbcode'); $arr['mail_flags'] = ((x($arr,'mail_flags')) ? intval($arr['mail_flags']) : 0 ); + $arr['mail_raw'] = ((x($arr,'mail_raw')) ? intval($arr['mail_raw']) : 0 ); - if(! $arr['parent_mid']) { + + + if($arr['parent_mid']) { + $parent_item = q("select * from mail where mid = '%s' and channel_id = %d limit 1", + dbesc($arr['parent_mid']), + intval($arr['channel_id']) + ); + if(($parent_item) && (! $arr['conv_guid'])) { + $arr['conv_guid'] = $parent_item[0]['conv_guid']; + } + } + else { logger('mail_store: missing parent'); $arr['parent_mid'] = $arr['mid']; } + if($arr['from_xchan'] === $channel['channel_hash']) + $conversant = $arr['to_xchan']; + else + $conversant = $arr['from_xchan']; + + + if(! $arr['conv_guid']) { + $x = create_conversation($channel,$conversant,(($arr['title']) ? base64url_decode(str_rot47($arr['title'])) : '')); + $arr['conv_guid'] = (($x) ? $x['guid'] : ''); + } + + $r = q("SELECT id FROM mail WHERE mid = '%s' AND channel_id = %d LIMIT 1", dbesc($arr['mid']), intval($arr['channel_id']) @@ -3097,6 +3057,14 @@ function mail_store($arr) { Zlib\Enotify::submit($notif_params); } + if($arr['conv_guid']) { + $c = q("update conv set updated = '%s' where guid = '%s' and uid = %d", + dbesc(datetime_convert()), + dbesc($arr['conv_guid']), + intval($arr['channel_id']) + ); + } + call_hooks('post_mail_end',$arr); return $current_post; } @@ -3961,8 +3929,8 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C } } - if(intval($arr['compat']) === 1) { - $sql_extra = " AND author_xchan = owner_xchan and item_wall = 1 and item_private = 0 "; + if($channel && intval($arr['compat']) === 1) { + $sql_extra = " AND author_xchan = '" . $channel['channel_hash'] . "' and item_private = 0 "; } if ($arr['datequery']) { @@ -3972,11 +3940,6 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert('UTC','UTC',$arr['datequery2'])))); } - if(! array_key_exists('nouveau',$arr)) { - $sql_extra2 = " AND item.parent = item.id "; -// $sql_extra3 = ''; - } - if($arr['search']) { if(strpos($arr['search'],'#') === 0) @@ -4048,7 +4011,8 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C if($arr['item_type'] === '*') $item_restrict = ''; - if ($arr['nouveau'] && ($client_mode & CLIENT_MODE_LOAD) && $channel) { + if ((($arr['compat']) || ($arr['nouveau'] && ($client_mode & CLIENT_MODE_LOAD))) && $channel) { + // "New Item View" - show all items unthreaded in reverse created date order $items = q("SELECT item.*, item.id AS item_id FROM item @@ -4343,10 +4307,7 @@ function sync_an_item($channel_id,$item_id) { if($r) { xchan_query($r); $sync_item = fetch_post_tags($r); - $rid = q("select * from item_id where iid = %d", - intval($item_id) - ); - build_sync_packet($channel_d,array('item' => array(encode_item($sync_item[0],true)),'item_id' => $rid)); + build_sync_packet($channel_d,array('item' => array(encode_item($sync_item[0],true)))); } } diff --git a/include/markdown.php b/include/markdown.php index a9617f72b..0cd9ab237 100644 --- a/include/markdown.php +++ b/include/markdown.php @@ -5,7 +5,7 @@ */ use Michelf\MarkdownExtra; -use Markdownify\Converter; +use League\HTMLToMarkdown\HtmlConverter; require_once("include/oembed.php"); require_once("include/event.php"); @@ -13,138 +13,6 @@ require_once("include/html2bbcode.php"); require_once("include/bbcode.php"); -function get_bb_tag_pos($s, $name, $occurance = 1) { - - if($occurance < 1) - $occurance = 1; - - $start_open = -1; - for($i = 1; $i <= $occurance; $i++) { - if( $start_open !== false) - $start_open = strpos($s, '[' . $name, $start_open + 1); // allow [name= type tags - } - - if( $start_open === false) - return false; - - $start_equal = strpos($s, '=', $start_open); - $start_close = strpos($s, ']', $start_open); - - if( $start_close === false) - return false; - - $start_close++; - - $end_open = strpos($s, '[/' . $name . ']', $start_close); - - if( $end_open === false) - return false; - - $res = array( 'start' => array('open' => $start_open, 'close' => $start_close), - 'end' => array('open' => $end_open, 'close' => $end_open + strlen('[/' . $name . ']')) ); - if( $start_equal !== false) - $res['start']['equal'] = $start_equal + 1; - - return $res; -} - -function bb_tag_preg_replace($pattern, $replace, $name, $s) { - - $string = $s; - - $occurance = 1; - $pos = get_bb_tag_pos($string, $name, $occurance); - while($pos !== false && $occurance < 1000) { - - $start = substr($string, 0, $pos['start']['open']); - $subject = substr($string, $pos['start']['open'], $pos['end']['close'] - $pos['start']['open']); - $end = substr($string, $pos['end']['close']); - if($end === false) - $end = ''; - - $subject = preg_replace($pattern, $replace, $subject); - $string = $start . $subject . $end; - - $occurance++; - $pos = get_bb_tag_pos($string, $name, $occurance); - } - - return $string; -} - -function share_shield($m) { - return str_replace($m[1],'!=+=+=!' . base64url_encode($m[1]) . '=+!=+!=',$m[0]); -} - -function share_unshield($m) { - $x = str_replace(array('!=+=+=!','=+!=+!='),array('',''),$m[1]); - return str_replace($m[1], base64url_decode($x), $m[0]); -} - - -function diaspora_mention_callback($matches) { - - $webbie = $matches[2] . '@' . $matches[3]; - $link = ''; - if($webbie) { - $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1", - dbesc($webbie) - ); - if(! $r) { - $x = discover_by_webbie($webbie); - if($x) { - $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1", - dbesc($webbie) - ); - } - } - if($r) - $link = $r[0]['xchan_url']; - } - if(! $link) - $link = 'https://' . $matches[3] . '/u/' . $matches[2]; - - if($r && $r[0]['hubloc_network'] === 'zot') - return '@[zrl=' . $link . ']' . trim($matches[1]) . ((substr($matches[0],-1,1) === '+') ? '+' : '') . '[/zrl]' ; - else - return '@[url=' . $link . ']' . trim($matches[1]) . ((substr($matches[0],-1,1) === '+') ? '+' : '') . '[/url]' ; - -} - -function diaspora_mention_callback2($matches) { - - $webbie = $matches[1] . '@' . $matches[2]; - $link = ''; - if($webbie) { - $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1", - dbesc($webbie) - ); - if(! $r) { - $x = discover_by_webbie($webbie); - if($x) { - $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1", - dbesc($webbie) - ); - } - } - if($r) - $link = $r[0]['xchan_url']; - } - - $name = (($r) ? $r[0]['xchan_name'] : $matches[1]); - - if(! $link) - $link = 'https://' . $matches[2] . '/u/' . $matches[1]; - - if($r && $r[0]['hubloc_network'] === 'zot') - return '@[zrl=' . $link . ']' . trim($name) . ((substr($matches[0],-1,1) === '+') ? '+' : '') . '[/zrl]' ; - else - return '@[url=' . $link . ']' . trim($name) . ((substr($matches[0],-1,1) === '+') ? '+' : '') . '[/url]' ; - -} - - - /** * @brief * @@ -157,28 +25,29 @@ function diaspora_mention_callback2($matches) { * @param boolean $use_zrl default false * @return string */ -function markdown_to_bb($s, $use_zrl = false) { - $s = str_replace("
","\r",$s); - $s = str_replace("
\n>","",$s); +function markdown_to_bb($s, $use_zrl = false, $options = []) { + if(is_array($s)) { btlogger('markdown_to_bb called with array. ' . print_r($s,true), LOGGER_NORMAL, LOG_WARNING); return ''; } + + $s = str_replace("
","\r",$s); + $s = str_replace("
\n>","",$s); + $s = html_entity_decode($s,ENT_COMPAT,'UTF-8'); // if empty link text replace with the url $s = preg_replace("/\[\]\((.*?)\)/ism",'[$1]($1)',$s); - // first try plustags + $x = [ 'text' => $s , 'zrl' => $use_zrl, 'options' => $options ]; - $s = preg_replace_callback('/\@\{(.+?)\; (.+?)\@(.+?)\}\+/','diaspora_mention_callback',$s); - $s = preg_replace_callback('/\@\{(.+?)\; (.+?)\@(.+?)\}/','diaspora_mention_callback',$s); + call_hooks('markdown_to_bb_init',$x); - $s = preg_replace_callback('/\@\{(.+?)\@(.+?)\}\+/','diaspora_mention_callback2',$s); - $s = preg_replace_callback('/\@\{(.+?)\@(.+?)\}/','diaspora_mention_callback2',$s); + $s = $x['text']; // Escaping the hash tags - doesn't always seem to work // $s = preg_replace('/\#([^\s\#])/','\\#$1',$s); @@ -193,9 +62,6 @@ function markdown_to_bb($s, $use_zrl = false) { $s = html2bbcode($s); - // protect the recycle symbol from turning into a tag, but without unescaping angles and naked ampersands - $s = str_replace('♲',html_entity_decode('♲',ENT_QUOTES,'UTF-8'),$s); - // Convert everything that looks like a link to a link if($use_zrl) { $s = str_replace(array('[img','/img]'),array('[zmg','/zmg]'),$s); @@ -217,287 +83,106 @@ function markdown_to_bb($s, $use_zrl = false) { } -function stripdcode_br_cb($s) { - return '[code]' . str_replace('<br />', "\n\t", $s[1]) . '[/code]'; -} - - -////////////////////// -// The following "diaspora_ul" and "diaspora_ol" are only appropriate for the -// pre-Markdownify conversion. If Markdownify isn't used, use the non-Markdownify -// versions below -////////////////////// -/* -function diaspora_ul($s) { - // Replace "[*]" followed by any number (including zero) of - // spaces by "* " to match Diaspora's list format - if( strpos($s[0], "[list]") === 0 ) - return '<ul class="listbullet" style="list-style-type: circle;">' . preg_replace("/\[\*\]( *)/", "* ", $s[1]) . '</ul>'; - elseif( strpos($s[0], "[ul]") === 0 ) - return '<ul class="listbullet" style="list-style-type: circle;">' . preg_replace("/\[\*\]( *)/", "* ", $s[1]) . '</ul>'; - else - return $s[0]; -} - - -function diaspora_ol($s) { - // A hack: Diaspora will create a properly-numbered ordered list even - // if you use '1.' for each element of the list, like: - // 1. First element - // 1. Second element - // 1. Third element - if( strpos($s[0], "[list=1]") === 0 ) - return '<ul class="listdecimal" style="list-style-type: decimal;">' . preg_replace("/\[\*\]( *)/", "1. ", $s[1]) . '</ul>'; - elseif( strpos($s[0], "[list=i]") === 0 ) - return '<ul class="listlowerroman" style="list-style-type: lower-roman;">' . preg_replace("/\[\*\]( *)/", "1. ", $s[1]) . '</ul>'; - elseif( strpos($s[0], "[list=I]") === 0 ) - return '<ul class="listupperroman" style="list-style-type: upper-roman;">' . preg_replace("/\[\*\]( *)/", "1. ", $s[1]) . '</ul>'; - elseif( strpos($s[0], "[list=a]") === 0 ) - return '<ul class="listloweralpha" style="list-style-type: lower-alpha;">' . preg_replace("/\[\*\]( *)/", "1. ", $s[1]) . '</ul>'; - elseif( strpos($s[0], "[list=A]") === 0 ) - return '<ul class="listupperalpha" style="list-style-type: upper-alpha;">' . preg_replace("/\[\*\]( *)/", "1. ", $s[1]) . '</ul>'; - elseif( strpos($s[0], "[ol]") === 0 ) - return '<ul class="listdecimal" style="list-style-type: decimal;">' . preg_replace("/\[\*\]( *)/", "1. ", $s[1]) . '</ul>'; - else - return $s[0]; -} -*/ - -////////////////////// -// Non-Markdownify versions of "diaspora_ol" and "diaspora_ul" -////////////////////// -/** - * @brief - * - * Replace "[\\*]" followed by any number (including zero) of - * spaces by "* " to match Diaspora's list format. - * - * @param string $s - * @return string - */ -function diaspora_ul($s) { - return preg_replace("/\[\\\\\*\]( *)/", "* ", $s[1]); -} - -/** - * @brief - * - * A hack: Diaspora will create a properly-numbered ordered list even - * if you use '1.' for each element of the list, like: - * \code - * 1. First element - * 1. Second element - * 1. Third element - * \endcode - * @param string $s - * @return string - */ -function diaspora_ol($s) { - return preg_replace("/\[\\\\\*\]( *)/", "1. ", $s[1]); -} - -function bb2dmention_callback($match) { - - $r = q("select xchan_addr from xchan where xchan_url = '%s'", - dbesc($match[2]) - ); - - if($r) - return '@{' . $match[3] . ' ; ' . $r[0]['xchan_addr'] . '}'; - - return '@' . $match[3]; -} - - -function bb2diaspora_itemwallwall(&$item,$uplink = false) { - - // We will provide wallwall (embedded author on the Diaspora side) if - // 1. It is a wall-to-wall post - // 2. A comment arrived which has no Diaspora signature info - - - $wallwall = false; - $author_exists = true; - - if(! array_key_exists('author',$item)) { - $author_exists = false; - logger('bb2diaspora_itemwallwall: no author'); - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($item['author_xchan']) - ); - if($r) - $item['author'] = $r[0]; - } - - $has_meta = false; - if($item['diaspora_meta'] || get_iconfig($item,'diaspora','fields')) - $has_meta = true; - - if($item['author_xchan'] != $item['owner_xchan']) { - if($item['mid'] == $item['parent_mid']) - $wallwall = true; - else { - if(! $has_meta) { - $wallwall = true; - } - } - } - - if($uplink) - $wallwall = true; - - if(($wallwall) && (is_array($item['author'])) && $item['author']['xchan_url'] && $item['author']['xchan_name'] && $item['author']['xchan_photo_s']) { - logger('bb2diaspora_itemwallwall: wall to wall post',LOGGER_DEBUG); - // post will come across with the owner's identity. Throw a preamble onto the post to indicate the true author. - $item['body'] = "\n\n" - . '[quote]' - . '[img]' . $item['author']['xchan_photo_s'] . '[/img]' - . ' ' - . '[url=' . $item['author']['xchan_url'] . '][b]' . $item['author']['xchan_name'] . '[/b][/url]' . "\n\n" - . $item['body'] - . '[/quote]'; - } - - // $item['author'] might cause a surprise further down the line if it wasn't expected to be here. - - if(! $author_exists) - unset($item['author']); -} - -function bb2diaspora_itembody($item, $force_update = false, $have_channel = false, $uplink = false) { - - if(! get_iconfig($item,'diaspora','fields')) { - $force_update = true; - } +function bb_to_markdown_share($match) { $matches = array(); - - if(($item['diaspora_meta']) && (! $force_update)) { - $diaspora_meta = json_decode($item['diaspora_meta'],true); - if($diaspora_meta) { - if(array_key_exists('iv',$diaspora_meta)) { - $key = get_config('system','prvkey'); - $meta = json_decode(crypto_unencapsulate($diaspora_meta,$key),true); - } - else { - $meta = $diaspora_meta; - } - if($meta) { - logger('bb2diaspora_itembody: cached '); - $newitem = $item; - $newitem['body'] = $meta['body']; - return $newitem['body']; - } - } + $attributes = $match[1]; + + $author = ""; + preg_match("/author='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $author = urldecode($matches[1]); + + $link = ""; + preg_match("/link='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $link = $matches[1]; + + $avatar = ""; + preg_match("/avatar='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $avatar = $matches[1]; + + $profile = ""; + preg_match("/profile='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $profile = $matches[1]; + + $posted = ""; + preg_match("/posted='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $posted = $matches[1]; + + // message_id is never used, do we still need it? + $message_id = ""; + preg_match("/message_id='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $message_id = $matches[1]; + + if(! $message_id) { + preg_match("/guid='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $message_id = $matches[1]; } - create_export_photo_body($item); - - $newitem = $item; - - if(array_key_exists('item_obscured',$item) && intval($item['item_obscured'])) { - $key = get_config('system','prvkey'); - $b = json_decode($item['body'],true); - // if called from diaspora_process_outbound, this decoding has already been done. - // Everything else that calls us will not yet be decoded. - if($b && is_array($b) && array_key_exists('iv',$b)) { - $newitem['title'] = (($item['title']) ? crypto_unencapsulate(json_decode($item['title'],true),$key) : ''); - $newitem['body'] = (($item['body']) ? crypto_unencapsulate(json_decode($item['body'],true),$key) : ''); - } - } - if(! $have_channel) - bb2diaspora_itemwallwall($newitem,$uplink); + $reldate = datetime_convert('UTC', date_default_timezone_get(), $posted, 'r'); - $title = $newitem['title']; - $body = preg_replace('/\#\^http/i', 'http', $newitem['body']); + $headline = ''; - // protect tags and mentions from hijacking + if ($avatar != "") + $headline .= '[url=' . zid($profile) . '][img]' . $avatar . '[/img][/url]'; - if(intval(get_pconfig($item['uid'],'system','prevent_tag_hijacking'))) { - $new_tag = html_entity_decode('⋕',ENT_COMPAT,'UTF-8'); - $new_mention = html_entity_decode('@',ENT_COMPAT,'UTF-8'); + // Bob Smith wrote the following post 2 hours ago - // #-tags - $body = preg_replace('/\#\[url/i', $new_tag . '[url', $body); - $body = preg_replace('/\#\[zrl/i', $new_tag . '[zrl', $body); - // @-mentions - $body = preg_replace('/\@\!?\[url/i', $new_mention . '[url', $body); - $body = preg_replace('/\@\!?\[zrl/i', $new_mention . '[zrl', $body); - } + $fmt = sprintf( t('%1$s wrote the following %2$s %3$s'), + '[url=' . zid($profile) . ']' . $author . '[/url]', + '[url=' . zid($link) . ']' . t('post') . '[/url]', + $reldate + ); - // remove multiple newlines - do { - $oldbody = $body; - $body = str_replace("\n\n\n", "\n\n", $body); - } while ($oldbody != $body); - - $body = bb2diaspora($body); - - if(strlen($title)) - $body = "## " . $title . "\n\n" . $body; - - if($item['attach']) { - $cnt = preg_match_all('/href=\"(.*?)\"(.*?)title=\"(.*?)\"/ism', $item['attach'], $matches, PREG_SET_ORDER); - if($cnt) { - $body .= "\n" . t('Attachments:') . "\n"; - foreach($matches as $mtch) { - $body .= '[' . $mtch[3] . '](' . $mtch[1] . ')' . "\n"; - } - } - } + $headline .= $fmt . "\n\n"; -// logger('bb2diaspora_itembody : ' . $body, LOGGER_DATA); + $text = $headline . trim($match[2]); - return html_entity_decode($body); + return $text; } -function bb2diaspora($Text, $preserve_nl = false, $fordiaspora = true) { - // Re-enabling the converter again. - // The bbcode parser now handles youtube-links (and the other stuff) correctly. - // Additionally the html code is now fixed so that lists are now working. + +function bb_to_markdown($Text, $options = []) { /* * Transform #tags, strip off the [url] and replace spaces with underscore */ - $Text = preg_replace_callback('/#\[([zu])rl\=(\w+.*?)\](\w+.*?)\[\/[(zu)]rl\]/i', create_function('$match', - 'return \'#\'. str_replace(\' \', \'_\', $match[3]);' - ), $Text); - - $Text = preg_replace('/#\^\[([zu])rl\=(\w+.*?)\](\w+.*?)\[\/([zu])rl\]/i', '[$1rl=$2]$3[/$4rl]', $Text); - $Text = preg_replace_callback('/\@\!?\[([zu])rl\=(\w+.*?)\](\w+.*?)\[\/([zu])rl\]/i', 'bb2dmention_callback', $Text); + $Text = preg_replace_callback('/#\[([zu])rl\=(.*?)\](.*?)\[\/[(zu)]rl\]/i', + create_function('$match', 'return \'#\'. str_replace(\' \', \'_\', $match[3]);'), $Text); - // strip map tags, as the rendering is performed in bbcode() and the resulting output - // is not compatible with Diaspora (at least in the case of openstreetmap and probably - // due to the inclusion of an html iframe) - $Text = preg_replace("/\[map\=(.*?)\]/ism", '$1', $Text); - $Text = preg_replace("/\[map\](.*?)\[\/map\]/ism", '$1', $Text); + $Text = preg_replace('/#\^\[([zu])rl\=(.*?)\](.*?)\[\/([zu])rl\]/i', '[$1rl=$2]$3[/$4rl]', $Text); // Converting images with size parameters to simple images. Markdown doesn't know it. $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $Text); - // the following was added on 10-January-2012 due to an inability of Diaspora's - // new javascript markdown processor to handle links with images as the link "text" - // It is not optimal and may be removed if this ability is restored in the future - //if ($fordiaspora) - // $Text = preg_replace("/\[url\=([^\[\]]*)\]\s*\[img\](.*?)\[\/img\]\s*\[\/url\]/ism", - // "[url]$1[/url]\n[img]$2[/img]", $Text); + $Text = preg_replace_callback("/\[share(.*?)\](.*?)\[\/share\]/ism", 'bb_to_markdown_share', $Text); + + $x = [ 'bbcode' => $Text, 'options' => $options ]; + + call_hooks('bb_to_markdown_bb',$x); + + $Text = $x['bbcode']; // Convert it to HTML - don't try oembed $Text = bbcode($Text, $preserve_nl, false); // Markdownify does not preserve previously escaped html entities such as <> and &. - $Text = str_replace(array('<','>','&'),array('&_lt_;','&_gt_;','&_amp_;'),$Text); // Now convert HTML to Markdown - $md = new Converter(Converter::LINK_AFTER_CONTENT, false, false); - $Text = $md->parseString($Text); + $Text = html2markdown($Text); // It also adds backslashes to our attempt at getting around the html entity preservation for some weird reason. @@ -512,55 +197,40 @@ function bb2diaspora($Text, $preserve_nl = false, $fordiaspora = true) { // Remove empty zrl links $Text = preg_replace("/\[zrl\=\].*?\[\/zrl\]/is", "", $Text); - // Remove all unconverted tags - $Text = strip_tags($Text); - - // Remove any leading or trailing whitespace, as this will mess up - // the Diaspora signature verification and cause the item to disappear - $Text = trim($Text); - call_hooks('bb2diaspora', $Text); + call_hooks('bb_to_markdown', $Text); return $Text; -} -function unescape_underscores_in_links($m) { - $y = str_replace('\\_','_', $m[2]); - return('[' . $m[1] . '](' . $y . ')'); } -function format_event_diaspora($ev) { - - if(! ((is_array($ev)) && count($ev))) - return ''; - - $bd_format = t('l F d, Y \@ g:i A') ; // Friday January 18, 2011 @ 8 AM - - $o = t('$Projectname event notification:') . "\n"; - - $o .= '**' . (($ev['summary']) ? bb2diaspora($ev['summary']) : bb2diaspora($ev['desc'])) . '**' . "\n"; - - $o .= t('Starts:') . ' ' . '[' - . (($ev['adjust']) ? day_translate(datetime_convert('UTC', 'UTC', - $ev['start'] , $bd_format )) - : day_translate(datetime_convert('UTC', 'UTC', - $ev['start'] , $bd_format))) - . '](' . z_root() . '/localtime/?f=&time=' . urlencode(datetime_convert('UTC','UTC',$ev['start'])) . ")\n"; - - if(! $ev['nofinish']) - $o .= t('Finishes:') . ' ' . '[' - . (($ev['adjust']) ? day_translate(datetime_convert('UTC', 'UTC', - $ev['finish'] , $bd_format )) - : day_translate(datetime_convert('UTC', 'UTC', - $ev['finish'] , $bd_format ))) - . '](' . z_root() . '/localtime/?f=&time=' . urlencode(datetime_convert('UTC','UTC',$ev['finish'])) . ")\n"; - if(strlen($ev['location'])) - $o .= t('Location:') . bb2diaspora($ev['location']) - . "\n"; - $o .= "\n"; +/** + * @brief Convert a HTML text into Markdown. + * + * This function uses the library league/html-to-markdown for this task. + * + * If the HTML text can not get parsed it will return an empty string. + * + * @see HTMLToMarkdown + * + * @param string $html The HTML code to convert + * @return string Markdown representation of the given HTML text, empty on error + */ +function html2markdown($html) { + $markdown = ''; + $converter = new HtmlConverter(); + + try { + $markdown = $converter->convert($html); + } catch (InvalidArgumentException $e) { + logger("Invalid HTML. HTMLToMarkdown library threw an exception."); + } - return $o; + // The old html 2 markdown library "pixel418/markdownify": "^2.2", + //$md = new HtmlConverter(); + //$markdown = $md->convert($Text); + return $markdown; } diff --git a/include/message.php b/include/message.php index 4e8e44adc..3f003e020 100644 --- a/include/message.php +++ b/include/message.php @@ -5,10 +5,20 @@ require_once('include/crypto.php'); require_once('include/attach.php'); + +function mail_prepare_binary($item) { + + return replace_macros(get_markup_template('item_binary.tpl'), [ + '$download' => t('Download binary/encrypted content'), + '$url' => z_root() . '/mail/' . $item['id'] . '/download' + ]); +} + + // send a private message -function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $replyto = '', $expires = NULL_DATE) { +function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $replyto = '', $expires = NULL_DATE, $mimetype = 'text/bbcode', $raw = false) { $ret = array('success' => false); $is_reply = false; @@ -94,38 +104,9 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep // create a new conversation - $conv_guid = random_string(); - - $recip = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($recipient) - ); - if($recip) - $recip_handle = $recip[0]['xchan_addr']; - - $sender_handle = channel_reddress($channel); - - $handles = $recip_handle . ';' . $sender_handle; - - if($subject) - $nsubject = str_rot47(base64url_encode($subject)); - - $r = q("insert into conv (uid,guid,creator,created,updated,subject,recips) values(%d, '%s', '%s', '%s', '%s', '%s', '%s') ", - intval(local_channel()), - dbesc($conv_guid), - dbesc($sender_handle), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc($nsubject), - dbesc($handles) - ); - - $r = q("select * from conv where guid = '%s' and uid = %d limit 1", - dbesc($conv_guid), - intval(local_channel()) - ); - if($r) { - $retconv = $r[0]; - $retconv['subject'] = base64url_decode(str_rot47($retconv['subject'])); + $retconv = create_conversation($channel,$recipient,$subject); + if($retconv) { + $conv_guid = $retconv['guid']; } } @@ -136,7 +117,6 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep ); if($r) { $retconv = $r[0]; - $retconv['subject'] = base64url_decode(str_rot47($retconv['subject'])); } } @@ -145,6 +125,12 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep return $ret; } + $c = q("update conv set updated = '%s' where guid = '%s' and uid = %d", + dbesc(datetime_convert()), + dbesc($conv_guid), + intval(local_channel()) + ); + // generate a unique message_id do { @@ -192,8 +178,8 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep $sig = ''; // placeholder $mimetype = ''; //placeholder - $r = q("INSERT INTO mail ( account_id, conv_guid, mail_obscured, channel_id, from_xchan, to_xchan, mail_mimetype, title, body, sig, attach, mid, parent_mid, created, expires, mail_isreply ) - VALUES ( %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d )", + $r = q("INSERT INTO mail ( account_id, conv_guid, mail_obscured, channel_id, from_xchan, to_xchan, mail_mimetype, title, body, sig, attach, mid, parent_mid, created, expires, mail_isreply, mail_raw ) + VALUES ( %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d )", intval($channel['channel_account_id']), dbesc($conv_guid), intval(1), @@ -209,7 +195,8 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep dbesc($replyto), dbesc(datetime_convert()), dbescdate($expires), - intval($is_reply) + intval($is_reply), + intval($raw) ); // verify the save @@ -273,6 +260,49 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep } +function create_conversation($channel,$recipient,$subject) { + + // create a new conversation + + $conv_guid = random_string(); + + $recip = q("select * from xchan where xchan_hash = '%s' limit 1", + dbesc($recipient) + ); + if($recip) + $recip_handle = $recip[0]['xchan_addr']; + + $sender_handle = channel_reddress($channel); + + $handles = $recip_handle . ';' . $sender_handle; + + if($subject) + $nsubject = str_rot47(base64url_encode($subject)); + + $r = q("insert into conv (uid,guid,creator,created,updated,subject,recips) values(%d, '%s', '%s', '%s', '%s', '%s', '%s') ", + intval($channel['channel_id']), + dbesc($conv_guid), + dbesc($sender_handle), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($nsubject), + dbesc($handles) + ); + + $r = q("select * from conv where guid = '%s' and uid = %d limit 1", + dbesc($conv_guid), + intval($channel['channel_id']) + ); + + return $r[0]; + +} + + + + + + function private_messages_list($uid, $mailbox = '', $start = 0, $numitems = 0) { $where = ''; @@ -304,6 +334,8 @@ function private_messages_list($uid, $mailbox = '', $start = 0, $numitems = 0) { break; case 'combined': + default: + $parents = q("SELECT parent_mid FROM mail WHERE mid = parent_mid AND channel_id = %d ORDER BY created DESC", dbesc($local_channel) ); @@ -315,15 +347,21 @@ function private_messages_list($uid, $mailbox = '', $start = 0, $numitems = 0) { } + $r = null; + if($parents) { foreach($parents as $parent) { - $all[] = q("SELECT * FROM mail WHERE parent_mid = '%s' AND channel_id = %d ORDER BY created DESC", + $all = q("SELECT * FROM mail WHERE parent_mid = '%s' AND channel_id = %d ORDER BY created DESC limit 1", dbesc($parent['parent_mid']), dbesc($local_channel) ); + + if($all) { + foreach($all as $single) { + $r[] = $single; + } + } } - foreach($all as $single) - $r[] = $single[0]; } else { $r = q($sql); @@ -441,10 +479,12 @@ function private_messages_drop($channel_id, $messageitem_id, $drop_conversation intval($channel_id) ); if($z) { - q("delete from conv where guid = '%s' and uid = %d", - dbesc($x[0]['conv_guid']), - intval($channel_id) - ); + if($x[0]['conv_guid']) { + q("delete from conv where guid = '%s' and uid = %d", + dbesc($x[0]['conv_guid']), + intval($channel_id) + ); + } $m['mail'] = array(); foreach($z as $zz) { xchan_mail_query($zz); diff --git a/include/nav.php b/include/nav.php index 08dbbd5e3..2004f6ae3 100644 --- a/include/nav.php +++ b/include/nav.php @@ -34,15 +34,17 @@ EOT; intval($channel['channel_id']) ); - $chans = q("select channel_name, channel_id from channel where channel_account_id = %d and channel_removed = 0 order by channel_name ", - intval(get_account_id()) - ); + if(! $_SESSION['delegate']) { + $chans = q("select channel_name, channel_id from channel where channel_account_id = %d and channel_removed = 0 order by channel_name ", + intval(get_account_id()) + ); + } } elseif(remote_channel()) $observer = App::get_observer(); require_once('include/conversation.php'); - $is_owner = (((local_channel()) && (App::$profile['profile_uid'] == local_channel())) ? true : false); + $is_owner = (((local_channel()) && ((App::$profile_uid == local_channel()) || (App::$profile_uid == 0))) ? true : false); $channel_apps[] = channel_apps($is_owner, App::$profile['channel_address']); $myident = (($channel) ? $channel['xchan_addr'] : ''); @@ -65,8 +67,6 @@ EOT; //we could additionally use this to display important system notifications e.g. for updates )); - $server_role = get_config('system','server_role'); - $basic = (($server_role === 'basic') ? true : false); $techlevel = get_account_techlevel(); // nav links: array of array('href', 'text', 'extra css classes', 'title') @@ -95,20 +95,18 @@ EOT; if(local_channel()) { - - - if($chans && count($chans) > 1 && feature_enabled(local_channel(),'nav_channel_select') && (! $basic)) + if($chans && count($chans) > 1 && feature_enabled(local_channel(),'nav_channel_select')) $nav['channels'] = $chans; $nav['logout'] = ['logout',t('Logout'), "", t('End this session'),'logout_nav_btn']; // user menu - $nav['usermenu'][] = ['profile/' . $channel['channel_address'], t('View Profile'), "", t('Your profile page'),'profile_nav_btn']; + $nav['usermenu'][] = ['profile/' . $channel['channel_address'], t('View Profile'), ((\App::$nav_sel['active'] == 'Profile') ? 'active' : ''), t('Your profile page'),'profile_nav_btn']; - if(feature_enabled(local_channel(),'multi_profiles') && (! $basic)) - $nav['usermenu'][] = ['profiles', t('Edit Profiles'),"", t('Manage/Edit profiles'),'profiles_nav_btn']; + if(feature_enabled(local_channel(),'multi_profiles')) + $nav['usermenu'][] = ['profiles', t('Edit Profiles'), ((\App::$nav_sel['active'] == 'Profiles') ? 'active' : '') , t('Manage/Edit profiles'),'profiles_nav_btn']; else - $nav['usermenu'][] = ['profiles/' . $prof[0]['id'], t('Edit Profile'),"", t('Edit your profile'),'profiles_nav_btn']; + $nav['usermenu'][] = ['profiles/' . $prof[0]['id'], t('Edit Profile'), ((\App::$nav_sel['active'] == 'Profiles') ? 'active' : ''), t('Edit your profile'),'profiles_nav_btn']; } else { @@ -136,12 +134,12 @@ EOT; $homelink = (($observer) ? $observer['xchan_url'] : ''); } - if(! local_channel()) { + if(! $is_owner) { $nav['rusermenu'] = array( $homelink, t('Take me home'), 'logout', - t('Log me out of this site') + ((local_channel()) ? t('Logout') : t('Log me out of this site')) ); } @@ -172,7 +170,6 @@ EOT; if(local_channel()) { - $nav['network'] = array('network', t('Grid'), "", t('Your grid'),'network_nav_btn'); $nav['network']['all'] = [ 'network', t('View your network/grid'), '','' ]; $nav['network']['mark'] = array('', t('Mark all grid notifications seen'), '',''); @@ -201,8 +198,9 @@ EOT; $nav['all_events']['all']=array('events', t('View events'), "", ""); $nav['all_events']['mark'] = array('', t('Mark all events seen'), '',''); - if(! $basic) + if(! $_SESSION['delegate']) { $nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'),'manage_nav_btn'); + } $nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'),'settings_nav_btn'); @@ -224,7 +222,7 @@ EOT; $powered_by = ''; //app bin - if(local_channel()) { + if($is_owner) { if(get_pconfig(local_channel(), 'system','initial_import_system_apps') === false) { Zlib\Apps::import_system_apps(); set_pconfig(local_channel(), 'system','initial_import_system_apps', 1); @@ -245,8 +243,16 @@ EOT; usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare'); + $syslist = Zlib\Apps::app_order(local_channel(),$syslist); + foreach($syslist as $app) { - $nav_apps[] = Zlib\Apps::app_render($app,'nav'); + if(\App::$nav_sel['active'] == $app['name']) + $app['active'] = true; + + if($is_owner) + $nav_apps[] = Zlib\Apps::app_render($app,'nav'); + elseif(! $is_owner && strpos($app['requires'], 'local_channel') === false) + $nav_apps[] = Zlib\Apps::app_render($app,'nav'); } $tpl = get_markup_template('nav.tpl'); @@ -260,6 +266,7 @@ EOT; '$emptynotifications' => t('Loading...'), '$userinfo' => $x['usermenu'], '$localuser' => local_channel(), + '$is_owner' => $is_owner, '$sel' => App::$nav_sel, '$powered_by' => $powered_by, '$help' => t('@name, #tag, ?doc, content'), @@ -267,6 +274,7 @@ EOT; '$nav_apps' => $nav_apps, '$channel_apps' => $channel_apps, '$addapps' => t('Add Apps'), + '$orderapps' => t('Arrange Apps'), '$sysapps_toggle' => t('Toggle System Apps') )); @@ -290,21 +298,7 @@ EOT; * */ function nav_set_selected($item){ - App::$nav_sel = array( - 'community' => null, - 'network' => null, - 'home' => null, - 'profiles' => null, - 'intros' => null, - 'notifications' => null, - 'messages' => null, - 'directory' => null, - 'settings' => null, - 'contacts' => null, - 'manage' => null, - 'register' => null, - ); - App::$nav_sel[$item] = 'active'; + App::$nav_sel['active'] = $item; } diff --git a/include/network.php b/include/network.php index c03ca70f5..5f814ef32 100644 --- a/include/network.php +++ b/include/network.php @@ -343,7 +343,7 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { if (isset($url_parsed)) { curl_close($ch); if($http_code == 303) { - return z_fetch_url($newurl,false,$redirects++,$opts); + return z_fetch_url($newurl,false,++$redirects,$opts); } else { return z_post_url($newurl,$params,++$redirects,$opts); } @@ -397,31 +397,6 @@ function json_return_and_die($x, $content_type = 'application/json') { killme(); } - -/** - * @brief Generic XML return. - * - * Outputs a basic dfrn XML status structure to STDOUT, with a <status> variable - * of $st and an optional text <message> of $message and terminates the current - * process. - * - * @param string $st - * @param string $message - */ -function xml_status($st, $message = '') { - - $xml_message = ((strlen($message)) ? "\t<message>" . xmlify($message) . "</message>\r\n" : ''); - - if($st) - logger('Returning non_zero: ' . $st . " message=" . $message); - - header( "Content-type: text/xml" ); - echo '<?xml version="1.0" encoding="UTF-8"?>'."\r\n"; - echo "<result>\r\n\t<status>$st</status>\r\n$xml_message</result>\r\n"; - killme(); -} - - /** * @brief Send HTTP status header. * @@ -696,11 +671,12 @@ function parse_xml_string($s,$strict = true) { libxml_use_internal_errors(true); $x = @simplexml_load_string($s2); - if(! $x) { + if($x === false) { logger('libxml: parse: error: ' . $s2, LOGGER_DATA); - foreach(libxml_get_errors() as $err) - logger('libxml: parse: ' . $err->code." at ".$err->line.":".$err->column." : ".$err->message, LOGGER_DATA); - + foreach(libxml_get_errors() as $err) { + logger('libxml: parse: ' . $err->code . ' at ' . $err->line + . ':' . $err->column . ' : ' . $err->message, LOGGER_DATA); + } libxml_clear_errors(); } @@ -1007,39 +983,6 @@ function email_header_encode($in_str, $charset = 'UTF-8') { return $out_str; } -function email_send($addr, $subject, $headers, $item) { - //$headers .= 'MIME-Version: 1.0' . "\n"; - //$headers .= 'Content-Type: text/html; charset=UTF-8' . "\n"; - //$headers .= 'Content-Type: text/plain; charset=UTF-8' . "\n"; - //$headers .= 'Content-Transfer-Encoding: 8bit' . "\n\n"; - - $part = uniqid("", true); - - $html = prepare_body($item); - - $headers .= "Mime-Version: 1.0\n"; - $headers .= 'Content-Type: multipart/alternative; boundary="=_'.$part.'"'."\n\n"; - - $body = "\n--=_".$part."\n"; - $body .= "Content-Transfer-Encoding: 8bit\n"; - $body .= "Content-Type: text/plain; charset=utf-8; format=flowed\n\n"; - - $body .= html2plain($html)."\n"; - - $body .= "--=_".$part."\n"; - $body .= "Content-Transfer-Encoding: 8bit\n"; - $body .= "Content-Type: text/html; charset=utf-8\n\n"; - - $body .= '<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; ">'.$html."</body></html>\n"; - - $body .= "--=_".$part."--"; - - //$message = '<html><body>' . $html . '</body></html>'; - //$message = html2plain($html); - logger('notifier: email delivery to ' . $addr); - mail($addr, $subject, $body, $headers); -} - /** * @brief Creates an xchan entry for URL. * @@ -1056,10 +999,10 @@ function discover_by_url($url, $arr = null) { return false; $network = (($arr['network']) ? $arr['network'] : 'unknown'); - $name = (($arr['name']) ? $arr['name'] : 'unknown'); - $photo = (($arr['photo']) ? $arr['photo'] : ''); - $addr = (($arr['addr']) ? $arr['addr'] : ''); - $guid = $url; + $name = (($arr['name']) ? $arr['name'] : 'unknown'); + $photo = (($arr['photo']) ? $arr['photo'] : ''); + $addr = (($arr['addr']) ? $arr['addr'] : ''); + $guid = $url; } $profile = $url; @@ -1095,8 +1038,8 @@ function discover_by_url($url, $arr = null) { if($feed->error()) logger('scrape_feed: Error parsing XML: ' . $feed->error()); - $name = unxmlify(trim($feed->get_title())); - $photo = $feed->get_image_url(); + $name = unxmlify(trim($feed->get_title())); + $photo = $feed->get_image_url(); $author = $feed->get_author(); if($author) { @@ -1195,23 +1138,11 @@ function discover_by_url($url, $arr = null) { } function discover_by_webbie($webbie) { - require_once('library/HTML5/Parser.php'); - $result = array(); + $result = []; $network = null; - $diaspora = false; - $gnusoc = false; - $dfrn = false; - - $has_salmon = false; - $salmon_key = false; - $atom_feed = false; - $diaspora_base = ''; - $diaspora_guid = ''; - $diaspora_key = ''; - $webbie = strtolower($webbie); $x = webfinger_rfc7033($webbie,true); @@ -1237,21 +1168,6 @@ function discover_by_webbie($webbie) { } } } - if($link['rel'] == NAMESPACE_DFRN) { - $dfrn = $link['href']; - } - if($link['rel'] == 'magic-public-key') { - if(substr($link['href'],0,5) === 'data:') { - $salmon_key = convert_salmon_key($link['href']); - } - } - if($link['rel'] == 'salmon') { - $has_salmon = true; - $salmon = $link['href']; - } - if($link['rel'] == 'http://schemas.google.com/g/2010#updates-from') { - $atom_feed = $link['href']; - } } } } @@ -1263,275 +1179,9 @@ function discover_by_webbie($webbie) { if($arr['success']) return true; - $aliases = array(); - - // Now let's make some decisions on what we may need - // to obtain further info - - $probe_atom = false; - $probe_old = false; - $probe_hcard = false; - - $address = ''; - $location = ''; - $nickname = ''; - $fullname = ''; - $avatar = ''; - $pubkey = ''; - - if(is_array($x)) { - if(array_key_exists('address',$x)) - $address = $x['address']; - if(array_key_exists('location',$x)) - $location = $x['location']; - if(array_key_exists('nickname',$x)) - $nickname = $x['nickname']; - } - - if(! $x) - $probe_old = true; - - if((! $dfrn) && (! $has_salmon)) - $probe_old = true; - - if($probe_old) { - $y = old_webfinger($webbie); - if($y) { - logger('old_webfinger: ' . print_r($x,true)); - foreach($y as $link) { - if($link['@attributes']['rel'] === NAMESPACE_DFRN) - $dfrn = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'salmon') - $notify = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === NAMESPACE_FEED) - $poll = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') - $hcard = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') - $profile = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0') - $poco = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') { - $diaspora_base = unamp($link['@attributes']['href']); - $diaspora = true; - } - if($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') { - $diaspora_guid = unamp($link['@attributes']['href']); - $diaspora = true; - } - if($link['@attributes']['rel'] === 'diaspora-public-key') { - $diaspora_key = base64_decode(unamp($link['@attributes']['href'])); - if(strstr($diaspora_key,'RSA ')) - $pubkey = rsatopem($diaspora_key); - else - $pubkey = $diaspora_key; - $diaspora = true; - } - if($link['@attributes']['rel'] == 'magic-public-key') { - if(substr($link['@attributes']['href'],0,5) === 'data:') { - $salmon_key = convert_salmon_key($link['@attributes']['href']); - } - } - if($link['@attributes']['rel'] == 'salmon') { - $has_salmon = true; - $salmon = $link['@attributes']['href']; - } - - if($link['@attributes']['rel'] == 'http://schemas.google.com/g/2010#updates-from') { - $atom_feed = $link['@attributes']['href']; - } - if($link['@attributes']['rel'] === 'alias') { - $aliases[] = $link['@attributes']['href']; - } - if($link['@attributes']['rel'] === 'subject') { - $subject = $link['@attributes']['href']; - } - } - } - } - - if($subject || $aliases) { - if(strpos($webbie,'@')) { - $rhs = substr($webbie,strpos($webbie,'@')+1); - } - else { - $m = parse_url($webbie); - if($m) { - $rhs = $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); - } - } - - $v = array('subject' => $subject,'aliases' => $aliases); - $address = find_webfinger_address($v,$rhs); - $location = find_webfinger_location($v,$rhs); - if($address) - $nickname = substr($address,0,strpos($address,'@')); - } - - if($salmon_key && $has_salmon && $atom_feed && (! $dfrn) && (! $diaspora)) { - $gnusoc = true; - $probe_atom = true; - } - - if(! $pubkey) - $pubkey = $salmon_key; - - if(($dfrn || $diaspora) && $hcard) - $probe_hcard = true; - - if(! $fullname) - $fullname = $nickname; - - if($probe_atom) { - $k = z_fetch_url($atom_feed); - if($k['success']) - $feed_meta = feed_meta($k['body']); - if($feed_meta) { - - // stash any discovered pubsubhubbub hubs in case we need to follow them - // this will save an expensive lookup later - - if($feed_meta['hubs'] && $address) { - set_xconfig($address,'system','push_hubs',$feed_meta['hubs']); - set_xconfig($address,'system','feed_url',$atom_feed); - } - if($feed_meta['author']['author_name']) { - $fullname = $feed_meta['author']['author_name']; - } - if(! $avatar) { - if($feed_meta['author']['author_photo']) - $avatar = $feed_meta['author']['author_photo']; - } - - // for GNU-social over-ride any url aliases we may have picked up in webfinger - // The author.uri element in the feed is likely to be more accurate - - if($gnusoc && $feed_meta['author']['author_uri']) - $location = $feed_meta['author']['author_uri']; - } - } - else { - if($probe_hcard) { - $vcard = scrape_vcard($hcard); - if($vcard) { - logger('vcard: ' . print_r($vcard,true), LOGGER_DATA); - if($vcard['fn']) - $fullname = $vcard['fn']; - if($vcard['photo'] && (strpos($vcard['photo'],'http') !== 0)) - $vcard['photo'] = $diaspora_base . '/' . $vcard['photo']; - if(($vcard['public_key']) && (! $pubkey)) { - $diaspora_key = $vcard['public_key']; - if(strstr($diaspora_key,'RSA ')) - $pubkey = rsatopem($diaspora_key); - else - $pubkey = $diaspora_key; - } - if(! $avatar) - $avatar = $vcard['photo']; - if($diaspora) { - if(($vcard['uid']) && (! $diaspora_guid)) - $diaspora_guid = $vcard['uid']; - if(($vcard['url']) && (! $diaspora_base)) - $diaspora_base = $vcard['url']; - } - } - } - } - - if(($profile) && (! $location)) - $location = $profile; - - if($location) { - $m = parse_url($location); - $base = $m['scheme'] . '://' . $m['host']; - $host = $m['host']; - } - - if($diaspora && $diaspora_base && $diaspora_guid) { - if($dfrn) - $network = 'friendica-over-diaspora'; - else - $network = 'diaspora'; - - $base = trim($diaspora_base, '/'); - $notify = $base . '/receive'; - } - else { - if($gnusoc) { - $network = 'gnusoc'; - $notify = $salmon; - } - } - - logger('network: ' . $network); - logger('address: ' . $address); - logger('fullname: ' . $fullname); - logger('pubkey: ' . $pubkey); - logger('location: ' . $location); - - // if we have everything we need, let's create the records - - if($network && $address && $fullname && $pubkey && $location) { - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($address) - ); - if($r) { - $r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s'", - dbesc($fullname), - dbesc($network), - dbesc(datetime_convert()), - dbesc($address) - ); - } - else { - $r = xchan_store_lowlevel( - [ - 'xchan_hash' => $address, - 'xchan_guid' => (($diaspora_guid) ? $diaspora_guid : $location), - 'xchan_pubkey' => $pubkey, - 'xchan_addr' => $address, - 'xchan_url' => $location, - 'xchan_name' => $fullname, - 'xchan_name_date' => datetime_convert(), - 'xchan_network' => $network - ] - ); - } - - $r = q("select * from hubloc where hubloc_hash = '%s' limit 1", - dbesc($address) - ); - - if(! $r) { - $r = hubloc_store_lowlevel( - [ - 'hubloc_guid' => (($diaspora_guid) ? $diaspora_guid : $location), - 'hubloc_hash' => $address, - 'hubloc_addr' => $address, - 'hubloc_network' => $network, - 'hubloc_url' => $base, - 'hubloc_host' => $host, - 'hubloc_callback' => $notify, - 'hubloc_updated' => datetime_convert(), - 'hubloc_primary' => 1 - ] - ); - } - $photos = import_xchan_photo($avatar,$address); - $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", - dbescdate(datetime_convert()), - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($address) - ); - return true; - } return false; -} - +} function webfinger_rfc7033($webbie,$zot = false) { @@ -1557,105 +1207,22 @@ function webfinger_rfc7033($webbie,$zot = false) { // and results in a 406 (Not Acceptable) response, and will also incorrectly produce an XML // document if you use 'application/jrd+json, */*'. We could set this to application/jrd+json, // but some test webfinger servers may not explicitly set the content type and they would be - // blocked. The best compromise until Mastodon is fixed is to remove the Accept header which is - // accomplished by setting it to nothing. + // blocked. The best compromise until Mastodon is fixed is to remove the Accept header which is + // accomplished by setting it to nothing. $counter = 0; - $s = z_fetch_url('https://' . $rhs . '/.well-known/webfinger?f=&resource=' . $resource . (($zot) ? '&zot=1' : ''), + $s = z_fetch_url('https://' . $rhs . '/.well-known/webfinger?f=&resource=' . $resource . (($zot) ? '&zot=1' : ''), false, $counter, [ 'headers' => [ 'Accept:' ] ]); if($s['success']) { $j = json_decode($s['body'],true); - - // We could have a number of URL aliases and webbies - // make an executive decision about the most likely "best" of each - // by comparing against some examples from known networks we're likely to encounter. - // Otherwise we have to store every alias that we may ever encounter and - // validate every URL we ever find against every possible alias - - /** - * @FIXME pump.io is going to be a real bugger since it doesn't return - * subject or aliases or provide lookup by url - */ - - $j['address'] = find_webfinger_address($j,$rhs); - $j['location'] = find_webfinger_location($j,$rhs); - if($j['address']) - $j['nickname'] = substr($j['address'],0,strpos($j['address'],'@')); - } - else - return false; - - return($j); -} - -function find_webfinger_address($j,$rhs) { - if(is_array($j) && ($j)) { - if(strpos($j['subject'],'acct:') !== false && strpos($j['subject'],'@' . $rhs)) - return str_replace('acct:','',$j['subject']); - if($j['aliases']) { - foreach($j['aliases'] as $alias) { - if(strpos($alias,'acct:') !== false && strpos($alias,'@' . $rhs)) { - return str_replace('acct:','',$alias); - } - } - } + return($j); } - return ''; -} - -function find_webfinger_location($j,$rhs) { - if(is_array($j) && ($j)) { - if(strpos($j['subject'],'http') === 0) { - $x = match_webfinger_location($j['subject'],$rhs); - if($x) - return $x; - } - if($j['aliases']) { - foreach($j['aliases'] as $alias) { - if(strpos($alias,'http') === 0) { - $x = match_webfinger_location($alias,$rhs); - if($x) - return($x); - } - } - } - } - return ''; -} - -/** - * @brief Match the webfinger location for the different networks. - * - * @param string $s The string to search in - * @param string $h The host - * @return string - */ -function match_webfinger_location($s, $h) { - - // GNU-social and the older StatusNet - the $host/user/123 form doesn't work - if(preg_match('|' . $h . '/index.php/user/([0-9]*?)$|', $s)) - return $s; - // Redmatrix / hubzilla - if(preg_match('|' . $h . '/channel/|', $s)) - return $s; - // Friendica - if(preg_match('|' . $h . '/profile/|', $s)) - return $s; - - $arr = array('test' => $s, 'host' => $h, 'success' => false); - call_hooks('match_webfinger_location', $arr); - if($arr['success']) - return $s; + return false; - return ''; } - - - - function old_webfinger($webbie) { $host = ''; @@ -1723,9 +1290,6 @@ function fetch_xrd_links($url) { if ((! $xml) || (! stristr($xml,'<xrd'))) return array(); - // fix diaspora's bad xml - $xml = str_replace(array('href="','"/>'),array('href="','"/>'),$xml); - $h = parse_xml_string($xml); if(! $h) return array(); @@ -1765,89 +1329,13 @@ function fetch_xrd_links($url) { } -function scrape_vcard($url) { - - $ret = array(); - - logger('url=' . $url); - - $x = z_fetch_url($url); - if(! $x['success']) - return $ret; - - $s = $x['body']; - - if(! $s) - return $ret; - - $headers = $x['header']; - $lines = explode("\n",$headers); - if(count($lines)) { - foreach($lines as $line) { - // don't try and run feeds through the html5 parser - if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml')))) - return ret; - } - } - - try { - $dom = HTML5_Parser::parse($s); - } catch (DOMException $e) { - logger('Parse error: ' . $e); - } - - if(! $dom) - return $ret; - - // Pull out hCard profile elements - - $largest_photo = 0; - - $items = $dom->getElementsByTagName('*'); - foreach($items as $item) { - if(attribute_contains($item->getAttribute('class'), 'vcard')) { - $level2 = $item->getElementsByTagName('*'); - foreach($level2 as $x) { - if(attribute_contains($x->getAttribute('id'),'pod_location')) - $ret['pod_location'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'fn')) - $ret['fn'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'uid')) - $ret['uid'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'nickname')) - $ret['nick'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'searchable')) - $ret['searchable'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'key')) - $ret['public_key'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'given_name')) - $ret['given_name'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'family_name')) - $ret['family_name'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'url')) - $ret['url'] = $x->textContent; - - if((attribute_contains($x->getAttribute('class'),'photo')) - || (attribute_contains($x->getAttribute('class'),'avatar'))) { - $size = intval($x->getAttribute('width')); - if(($size > $largest_photo) || (! $largest_photo)) { - $ret['photo'] = $x->getAttribute('src'); - $largest_photo = $size; - } - } - } - } - } - - return $ret; -} - /** * @brief * * @param string $url The URL to scrape * @return array */ + function scrape_feed($url) { require_once('library/HTML5/Parser.php'); @@ -1941,112 +1429,6 @@ function scrape_feed($url) { -function service_plink($contact, $guid) { - - $plink = ''; - - $m = parse_url($contact['xchan_url']); - if($m) { - $url = $m['scheme'] . '://' . $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); - } - else - $url = 'https://' . substr($contact['xchan_addr'],strpos($contact['xchan_addr'],'@')+1); - - $handle = substr($contact['xchan_addr'], 0, strpos($contact['xchan_addr'],'@')); - - if($contact['xchan_network'] === 'diaspora') - $plink = $url . '/posts/' . $guid; - if($contact['xchan_network'] === 'friendica-over-diaspora') - $plink = $url . '/display/' . $handle . '/' . $guid; - if($contact['xchan_network'] === 'zot') - $plink = $url . '/channel/' . $handle . '?f=&mid=' . $guid; - - return $plink; -} - - -function format_and_send_email($sender,$xchan,$item) { - - $title = $item['title']; - $body = $item['body']; - - $textversion = strip_tags(html_entity_decode(bbcode(str_replace(array("\\r", "\\n"), array( "", "\n"), $body)),ENT_QUOTES,'UTF-8')); - - $htmlversion = bbcode(str_replace(array("\\r","\\n"), array("","<br />\n"),$body)); - - $banner = t('$Projectname Notification'); - $product = t('$projectname'); // PLATFORM_NAME; - $siteurl = z_root(); - $thanks = t('Thank You,'); - $sitename = get_config('system', 'sitename'); - $site_admin = sprintf( t('%s Administrator'), $sitename); - - // load the template for private message notifications - $tpl = get_markup_template('email_notify_html.tpl'); - $email_html_body = replace_macros($tpl, array( - '$banner' => $banner, - '$notify_icon' => Zotlabs\Lib\System::get_notify_icon(), - '$product' => $product, - '$preamble' => '', - '$sitename' => $sitename, - '$siteurl' => $siteurl, - '$source_name' => $sender['xchan_name'], - '$source_link' => $sender['xchan_url'], - '$source_photo' => $sender['xchan_photo_m'], - '$username' => $xchan['xchan_name'], - '$hsitelink' => $datarray['hsitelink'], /// @FIXME $datarray is undefined - '$hitemlink' => $datarray['hitemlink'], /// @FIXME $datarray is undefined - '$thanks' => $thanks, - '$site_admin' => $site_admin, - '$title' => $title, - '$htmlversion' => $htmlversion, - )); - - // load the template for private message notifications - $tpl = get_markup_template('email_notify_text.tpl'); - $email_text_body = replace_macros($tpl, array( - '$banner' => $banner, - '$product' => $product, - '$preamble' => '', - '$sitename' => $sitename, - '$siteurl' => $siteurl, - '$source_name' => $sender['xchan_name'], - '$source_link' => $sender['xchan_url'], - '$source_photo' => $sender['xchan_photo_m'], - '$username' => $xchan['xchan_name'], - '$hsitelink' => $datarray['hsitelink'], - '$hitemlink' => $datarray['hitemlink'], - '$thanks' => $thanks, - '$site_admin' => $site_admin, - '$title' => $title, - '$textversion' => $textversion - )); - - $sender_name = t('Administrator'); - - $hostname = App::get_hostname(); - if(strpos($hostname, ':')) - $hostname = substr($hostname,0,strpos($hostname,':')); - - $sender_email = get_config('system', 'reply_address'); - if(! $sender_email) - $sender_email = 'noreply' . '@' . $hostname; - - // use the EmailNotification library to send the message - - Zotlabs\Lib\Enotify::send(array( - 'fromName' => $product, - 'fromEmail' => $sender_email, - 'replyTo' => $sender_email, - 'toEmail' => str_replace('mailto:','',$xchan['xchan_addr']), - 'messageSubject' => (($title) ? $title : t('No Subject')), - 'htmlVersion' => $email_html_body, - 'textVersion' => $email_text_body, - 'additionalMailHeader' => '', - )); -} - - function do_delivery($deliveries) { if(! (is_array($deliveries) && count($deliveries))) @@ -2160,33 +1542,33 @@ function get_site_info() { } - $data = Array( - 'version' => $version, - 'version_tag' => $tag, - 'server_role' => Zotlabs\Lib\System::get_server_role(), - 'commit' => $commit, - 'url' => z_root(), - 'plugins' => $visible_plugins, - 'register_policy' => $register_policy[get_config('system','register_policy')], - 'invitation_only' => intval(get_config('system','invitation_only')), - 'directory_mode' => $directory_mode[get_config('system','directory_mode')], - 'language' => get_config('system','language'), - 'rss_connections' => intval(get_config('system','feed_contacts')), - 'expiration' => $site_expire, + $data = [ + 'url' => z_root(), + 'platform' => Zotlabs\Lib\System::get_platform_name(), + 'site_name' => (($site_name) ? $site_name : ''), + 'version' => $version, + 'version_tag' => $tag, + 'server_role' => Zotlabs\Lib\System::get_server_role(), + 'commit' => $commit, + 'plugins' => $visible_plugins, + 'register_policy' => $register_policy[get_config('system','register_policy')], + 'invitation_only' => intval(get_config('system','invitation_only')), + 'directory_mode' => $directory_mode[get_config('system','directory_mode')], + 'language' => get_config('system','language'), + 'rss_connections' => intval(get_config('system','feed_contacts')), + 'expiration' => $site_expire, 'default_service_restrictions' => $service_class, - 'locked_features' => $locked_features, - 'admin' => $admin, - 'site_name' => (($site_name) ? $site_name : ''), - 'platform' => Zotlabs\Lib\System::get_platform_name(), - 'dbdriver' => DBA::$dba->getdriver(), - 'lastpoll' => get_config('system','lastpoll'), - 'info' => (($site_info) ? $site_info : ''), - 'channels_total' => $channels_total_stat, - 'channels_active_halfyear' => $channels_active_halfyear_stat, - 'channels_active_monthly' => $channels_active_monthly_stat, - 'local_posts' => $local_posts_stat, - 'hide_in_statistics' => $hide_in_statistics - ); + 'locked_features' => $locked_features, + 'admin' => $admin, + 'dbdriver' => DBA::$dba->getdriver(), + 'lastpoll' => get_config('system','lastpoll'), + 'info' => (($site_info) ? $site_info : ''), + 'channels_total' => $channels_total_stat, + 'channels_active_halfyear' => $channels_active_halfyear_stat, + 'channels_active_monthly' => $channels_active_monthly_stat, + 'local_posts' => $local_posts_stat, + 'hide_in_statistics' => $hide_in_statistics + ]; return $data; } @@ -2412,3 +1794,144 @@ function probe_api_path($host) { return ''; } + + +function scrape_vcard($url) { + + require_once('library/HTML5/Parser.php'); + + $ret = array(); + + logger('url=' . $url); + + $x = z_fetch_url($url); + if(! $x['success']) + return $ret; + + $s = $x['body']; + + if(! $s) + return $ret; + + $headers = $x['header']; + $lines = explode("\n",$headers); + if(count($lines)) { + foreach($lines as $line) { + // don't try and run feeds through the html5 parser + if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml')))) + return ret; + } + } + + try { + $dom = HTML5_Parser::parse($s); + } catch (DOMException $e) { + logger('Parse error: ' . $e); + } + + if(! $dom) + return $ret; + + // Pull out hCard profile elements + + $largest_photo = 0; + + $items = $dom->getElementsByTagName('*'); + foreach($items as $item) { + if(attribute_contains($item->getAttribute('class'), 'vcard')) { + $level2 = $item->getElementsByTagName('*'); + foreach($level2 as $x) { + if(attribute_contains($x->getAttribute('id'),'pod_location')) + $ret['pod_location'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'fn')) + $ret['fn'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'uid')) + $ret['uid'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'nickname')) + $ret['nick'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'searchable')) + $ret['searchable'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'key')) + $ret['public_key'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'given_name')) + $ret['given_name'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'family_name')) + $ret['family_name'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'url')) + $ret['url'] = $x->textContent; + + if((attribute_contains($x->getAttribute('class'),'photo')) + || (attribute_contains($x->getAttribute('class'),'avatar'))) { + $size = intval($x->getAttribute('width')); + if(($size > $largest_photo) || (! $largest_photo)) { + $ret['photo'] = $x->getAttribute('src'); + $largest_photo = $size; + } + } + } + } + } + + return $ret; +} + +function service_plink($contact, $guid) { + + $plink = ''; + + $m = parse_url($contact['xchan_url']); + if($m) { + $url = $m['scheme'] . '://' . $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); + } + else { + $url = 'https://' . substr($contact['xchan_addr'],strpos($contact['xchan_addr'],'@')+1); + } + + $handle = substr($contact['xchan_addr'], 0, strpos($contact['xchan_addr'],'@')); + + $plink = $url . '/channel/' . $handle . '?f=&mid=' . $guid; + + $x = [ 'xchan' => $contact, 'guid' => $guid, 'url' => $url, 'plink' => $plink ]; + call_hooks('service_plink', $x); + + return $x['plink']; +} + +function getBestSupportedMimeType($mimeTypes = null, $acceptedTypes = false) { + // Values will be stored in this array + + if($acceptedTypes === false) + $acceptedTypes = $_SERVER['HTTP_ACCEPT']; + + $AcceptTypes = Array (); + + // Accept header is case insensitive, and whitespace isn’t important + $accept = strtolower(str_replace(' ', '', $acceptedTypes)); + // divide it into parts in the place of a "," + $accept = explode(',', $accept); + foreach ($accept as $a) { + // the default quality is 1. + $q = 1; + // check if there is a different quality + if (strpos($a, ';q=')) { + // divide "mime/type;q=X" into two parts: "mime/type" i "X" + list($a, $q) = explode(';q=', $a); + } + // mime-type $a is accepted with the quality $q + // WARNING: $q == 0 means, that mime-type isn’t supported! + $AcceptTypes[$a] = $q; + } + arsort($AcceptTypes); + + // if no parameter was passed, just return parsed data + if (!$mimeTypes) return $AcceptTypes; + + $mimeTypes = array_map('strtolower', (array)$mimeTypes); + + // let’s check our supported types: + foreach ($AcceptTypes as $mime => $q) { + if ($q && in_array($mime, $mimeTypes)) return $mime; + } + // no mime-type found + return null; +}
\ No newline at end of file diff --git a/include/oauth2.php b/include/oauth2.php new file mode 100644 index 000000000..3a71a651d --- /dev/null +++ b/include/oauth2.php @@ -0,0 +1,23 @@ +<?php + +/* + $storage = new OAuth2\Storage\Pdo(\DBA::$dba->db); + $config = [ + 'use_openid_connect' => true, + 'issuer' => \Zotlabs\Lib\System::get_site_name() + ]; + + $oauth2_server = new OAuth2\Server($storage,$config); + + $oauth2_server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage)); + $oauth2_server->addGrantType(new OAuth2\GrantType\AuthorizationCode($storage)); + + $keyStorage = new OAuth2\Storage\Memory( [ + 'keys' => [ + 'public_key' => get_config('system','pubkey'), + 'private_key' => get_config('system','prvkey') + ] + ]); + + $oauth2_server->addStorage($keyStorage,'public_key'); +*/
\ No newline at end of file diff --git a/include/oembed.php b/include/oembed.php index 5865c95d5..460e0244e 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -134,7 +134,9 @@ function oembed_fetch_url($embedurl){ $txt = null; // we should try to cache this and avoid a lookup on each render - $zrl = is_matrix_url($embedurl); + $is_matrix = is_matrix_url($embedurl); + + $zrl = ((get_config('system','oembed_zrl')) ? $is_matrix : false); $furl = ((local_channel() && $zrl) ? zid($embedurl) : $embedurl); @@ -222,7 +224,7 @@ function oembed_fetch_url($embedurl){ if($action === 'filter') { if($j['html']) { $orig = $j['html']; - $allow_position = (($zrl) ? true : false); + $allow_position = (($is_matrix) ? true : false); $j['html'] = purify_html($j['html'],$allow_position); if($j['html'] != $orig) { logger('oembed html was purified. original: ' . $orig . ' purified: ' . $j['html'], LOGGER_DEBUG, LOG_INFO); diff --git a/include/page_widgets.php b/include/page_widgets.php deleted file mode 100644 index 3270de4a3..000000000 --- a/include/page_widgets.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -// A basic toolbar for observers with write_pages permissions - -function writepages_widget ($who,$which) { - return replace_macros(get_markup_template('write_pages.tpl'), array( - '$new' => t('New Page'), - '$newurl' => "webpages/$who", - '$edit' => t('Edit'), - '$editurl' => "editwebpage/$who/$which" - )); -} - - - -// Chan is channel_id, $which is channel_address - we'll need to pass observer later too. - -function pagelist_widget ($owner,$which) { - - $r = q("select * from iconfig left join item on iconfig.iid = item.id where item_id.uid = %d - and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' order by item.created desc", - intval($owner) - ); - - $pages = null; - - if($r) { - $pages = array(); - foreach($r as $rr) { - $pages[$rr['iid']][] = array('url' => $rr['iid'],'pagetitle' => $rr['v'],'title' => $rr['title'],'created' => datetime_convert('UTC',date_default_timezone_get(),$rr['created']),'edited' => datetime_convert('UTC',date_default_timezone_get(),$rr['edited'])); - } - } - - //Build the base URL for edit links - $url = z_root() . "/editwebpage/" . $which; - // This isn't pretty, but it works. Until I figure out what to do with the UI, it's Good Enough(TM). - return $o . replace_macros(get_markup_template("webpagelist.tpl"), array( - '$baseurl' => $url, - '$edit' => t('Edit'), - '$pages' => $pages, - '$channel' => $which, - '$view' => t('View'), - '$preview' => t('Preview'), - '$actions_txt' => t('Actions'), - '$pagelink_txt' => t('Page Link'), - '$title_txt' => t('Title'), - '$created_txt' => t('Created'), - '$edited_txt' => t('Edited') - - )); - -} diff --git a/include/permissions.php b/include/permissions.php index f719394c5..f97142fab 100644 --- a/include/permissions.php +++ b/include/permissions.php @@ -7,61 +7,11 @@ require_once('include/security.php'); * * This file conntains functions to check and work with permissions. * - * Most of this file is obsolete and has been superceded by extensible permissions in v1.12; it is left here - * for reference and because we haven't yet checked that all functions have been replaced and are available - * elsewhere (typically Zotlabs/Access/*). */ /** - * @brief Return an array with all available permissions. - * - * These are channel specific permissions. - * The list of available permissions can get manipulated by the <i>hook</i> - * <b>global_permissions</b>. - * - * @return array associative array containing all permissions - */ -function get_perms() { - -// thinking about making element[2] a bitmask instead of boolean so that we can provide a list of applicable selections -// for any given permission. Currently we use the boolean to disallow write access to "everybody", but we also want to be -// able to handle troublesome settings such as allowing channel_w_stream to anybody in the network. You can allow it, but -// there's no way to implement sending it. - - $global_perms = array( - // Read only permissions - 'view_stream' => array('channel_r_stream', intval(PERMS_R_STREAM), true, t('Can view my normal stream and posts'), ''), - 'view_profile' => array('channel_r_profile', intval(PERMS_R_PROFILE), true, t('Can view my default channel profile'), ''), - 'view_contacts' => array('channel_r_abook', intval(PERMS_R_ABOOK), true, t('Can view my connections'), ''), - 'view_storage' => array('channel_r_storage', intval(PERMS_R_STORAGE), true, t('Can view my file storage and photos'), ''), - 'view_pages' => array('channel_r_pages', intval(PERMS_R_PAGES), true, t('Can view my webpages'), ''), - - // Write permissions - 'send_stream' => array('channel_w_stream', intval(PERMS_W_STREAM), false, t('Can send me their channel stream and posts'), ''), - 'post_wall' => array('channel_w_wall', intval(PERMS_W_WALL), false, t('Can post on my channel page ("wall")'), ''), - 'post_comments' => array('channel_w_comment', intval(PERMS_W_COMMENT), false, t('Can comment on or like my posts'), ''), - 'post_mail' => array('channel_w_mail', intval(PERMS_W_MAIL), false, t('Can send me private mail messages'), ''), - 'post_like' => array('channel_w_like', intval(PERMS_W_LIKE), false, t('Can like/dislike stuff'), t('Profiles and things other than posts/comments')), - - 'tag_deliver' => array('channel_w_tagwall', intval(PERMS_W_TAGWALL), false, t('Can forward to all my channel contacts via post @mentions'), t('Advanced - useful for creating group forum channels')), - 'chat' => array('channel_w_chat', intval(PERMS_W_CHAT), false, t('Can chat with me (when available)'), t('')), - 'write_storage' => array('channel_w_storage', intval(PERMS_W_STORAGE), false, t('Can write to my file storage and photos'), ''), - 'write_pages' => array('channel_w_pages', intval(PERMS_W_PAGES), false, t('Can edit my webpages'), ''), - - 'republish' => array('channel_a_republish', intval(PERMS_A_REPUBLISH), false, t('Can source my public posts in derived channels'), t('Somewhat advanced - very useful in open communities')), - - 'delegate' => array('channel_a_delegate', intval(PERMS_A_DELEGATE), false, t('Can administer my channel resources'), t('Extremely advanced. Leave this alone unless you know what you are doing')), - ); - $ret = array('global_permissions' => $global_perms); - call_hooks('global_permissions', $ret); - - return $ret['global_permissions']; -} - - -/** * get_all_perms($uid,$observer_xchan) * * @param int $uid The channel_id associated with the resource owner @@ -297,7 +247,6 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { * * Checks if the given observer with the hash $observer_xchan has permission * $permission on channel_id $uid. - * $permission is one defined in get_perms(); * * @param int $uid The channel_id associated with the resource owner * @param string $observer_xchan The xchan_hash representing the observer @@ -465,7 +414,7 @@ function perm_is_allowed($uid, $observer_xchan, $permission) { function get_all_api_perms($uid,$api) { - $global_perms = get_perms(); + $global_perms = \Zotlabs\Access\Permissions::Perms(); $ret = array(); @@ -568,6 +517,7 @@ function site_default_perms() { 'view_contacts' => PERMS_PUBLIC, 'view_storage' => PERMS_PUBLIC, 'view_pages' => PERMS_PUBLIC, + 'view_wiki' => PERMS_PUBLIC, 'send_stream' => PERMS_SPECIFIC, 'post_wall' => PERMS_SPECIFIC, 'post_comments' => PERMS_SPECIFIC, @@ -576,16 +526,15 @@ function site_default_perms() { 'chat' => PERMS_SPECIFIC, 'write_storage' => PERMS_SPECIFIC, 'write_pages' => PERMS_SPECIFIC, + 'write_wiki' => PERMS_SPECIFIC, 'delegate' => PERMS_SPECIFIC, 'post_like' => PERMS_NETWORK ); - $global_perms = get_perms(); + $global_perms = \Zotlabs\Access\Permissions::Perms(); foreach($global_perms as $perm => $v) { - $x = get_config('default_perms', $perm); - if($x === false) - $x = $typical[$perm]; + $x = get_config('default_perms', $perm, $typical[$perm]); $ret[$perm] = $x; } @@ -593,362 +542,3 @@ function site_default_perms() { } -/** - * @brief Return an array of all permissions for this role. - * - * Given a string for the channel role ('social','forum', etc) - * return an array of all permission fields pre-filled for this role. - * This includes the channel permission scope indicators (anything beginning with 'channel_') as well as - * * perms_auto: true or false to create auto-permissions for this channel - * * perms_follow: The permissions to apply when initiating a connection request to another channel - * * perms_accept: The permissions to apply when accepting a connection request from another channel (not automatic) - * * default_collection: true or false to make the default ACL include the channel's default collection - * * directory_publish: true or false to publish this channel in the directory - * Any attributes may be extended (new roles defined) and modified (specific permissions altered) by plugins - * - * @param string $role - * @return array - */ -function get_role_perms($role) { - - $ret = array(); - - $ret['role'] = $role; - - switch($role) { - case 'social': - $ret['perms_auto'] = false; - $ret['default_collection'] = false; - $ret['directory_publish'] = true; - $ret['online'] = true; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'send_stream', 'post_wall', 'post_comments', - 'post_mail', 'chat', 'post_like', 'republish' ]; - $ret['limits'] = [ - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_PUBLIC, - 'view_contacts' => PERMS_PUBLIC, - 'view_storage' => PERMS_PUBLIC, - 'view_pages' => PERMS_PUBLIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'post_like' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'republish' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC - ]; - - break; - - case 'social_restricted': - $ret['perms_auto'] = false; - $ret['default_collection'] = true; - $ret['directory_publish'] = true; - $ret['online'] = true; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'send_stream', 'post_wall', 'post_comments', - 'post_mail', 'chat', 'post_like' ]; - $ret['limits'] = [ - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_PUBLIC, - 'view_contacts' => PERMS_PUBLIC, - 'view_storage' => PERMS_PUBLIC, - 'view_pages' => PERMS_PUBLIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'post_like' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'republish' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC - ]; - - - break; - - case 'social_private': - $ret['perms_auto'] = false; - $ret['default_collection'] = true; - $ret['directory_publish'] = false; - $ret['online'] = false; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'send_stream', 'post_wall', 'post_comments', - 'post_mail', 'post_like' ]; - $ret['limits'] = [ - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_PUBLIC, - 'view_contacts' => PERMS_SPECIFIC, - 'view_storage' => PERMS_SPECIFIC, - 'view_pages' => PERMS_PUBLIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'post_like' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'republish' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC - ]; - - break; - - case 'forum': - $ret['perms_auto'] = true; - $ret['default_collection'] = false; - $ret['directory_publish'] = true; - $ret['online'] = false; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'post_wall', 'post_comments', 'tag_deliver', - 'post_mail', 'post_like' , 'republish', 'chat' ]; - $ret['limits'] = [ - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_PUBLIC, - 'view_contacts' => PERMS_PUBLIC, - 'view_storage' => PERMS_PUBLIC, - 'view_pages' => PERMS_PUBLIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'post_like' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'republish' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC - ]; - - break; - - case 'forum_restricted': - $ret['perms_auto'] = false; - $ret['default_collection'] = true; - $ret['directory_publish'] = true; - $ret['online'] = false; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'post_wall', 'post_comments', 'tag_deliver', - 'post_mail', 'post_like' , 'chat' ]; - $ret['limits'] = [ - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_PUBLIC, - 'view_contacts' => PERMS_PUBLIC, - 'view_storage' => PERMS_PUBLIC, - 'view_pages' => PERMS_PUBLIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'post_like' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'republish' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC - ]; - - break; - - case 'forum_private': - $ret['perms_auto'] = false; - $ret['default_collection'] = true; - $ret['directory_publish'] = false; - $ret['online'] = false; - - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'post_wall', 'post_comments', - 'post_mail', 'post_like' , 'chat' ]; - $ret['limits'] = [ - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_SPECIFIC, - 'view_contacts' => PERMS_SPECIFIC, - 'view_storage' => PERMS_SPECIFIC, - 'view_pages' => PERMS_SPECIFIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'post_like' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'republish' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC - ]; - - break; - - case 'feed': - $ret['perms_auto'] = true; - $ret['default_collection'] = false; - $ret['directory_publish'] = true; - $ret['online'] = false; - - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'send_stream', 'post_wall', 'post_comments', - 'post_mail', 'post_like' , 'republish' ]; - $ret['limits'] = [ - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_PUBLIC, - 'view_contacts' => PERMS_PUBLIC, - 'view_storage' => PERMS_PUBLIC, - 'view_pages' => PERMS_PUBLIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'post_like' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'republish' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC - ]; - - break; - - case 'feed_restricted': - $ret['perms_auto'] = false; - $ret['default_collection'] = true; - $ret['directory_publish'] = false; - $ret['online'] = false; - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'send_stream', 'post_wall', 'post_comments', - 'post_mail', 'post_like' , 'republish' ]; - $ret['limits'] = [ - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_PUBLIC, - 'view_contacts' => PERMS_PUBLIC, - 'view_storage' => PERMS_PUBLIC, - 'view_pages' => PERMS_PUBLIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'post_like' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'republish' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC - ]; - - break; - - case 'soapbox': - $ret['perms_auto'] = true; - $ret['default_collection'] = false; - $ret['directory_publish'] = true; - $ret['online'] = false; - - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'post_like' , 'republish' ]; - - $ret['limits'] = [ - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_PUBLIC, - 'view_contacts' => PERMS_PUBLIC, - 'view_storage' => PERMS_PUBLIC, - 'view_pages' => PERMS_PUBLIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'post_like' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'republish' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC - ]; - - break; - - case 'repository': - $ret['perms_auto'] = true; - $ret['default_collection'] = false; - $ret['directory_publish'] = true; - $ret['online'] = false; - - $ret['perms_connect'] = [ - 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'write_storage', 'write_pages', 'post_wall', 'post_comments', 'tag_deliver', - 'post_mail', 'post_like' , 'republish', 'chat' ]; - $ret['limits'] = [ - 'view_stream' => PERMS_PUBLIC, - 'view_profile' => PERMS_PUBLIC, - 'view_contacts' => PERMS_PUBLIC, - 'view_storage' => PERMS_PUBLIC, - 'view_pages' => PERMS_PUBLIC, - 'send_stream' => PERMS_SPECIFIC, - 'post_wall' => PERMS_SPECIFIC, - 'post_comments' => PERMS_SPECIFIC, - 'post_mail' => PERMS_SPECIFIC, - 'post_like' => PERMS_SPECIFIC, - 'tag_deliver' => PERMS_SPECIFIC, - 'chat' => PERMS_SPECIFIC, - 'write_storage' => PERMS_SPECIFIC, - 'write_pages' => PERMS_SPECIFIC, - 'republish' => PERMS_SPECIFIC, - 'delegate' => PERMS_SPECIFIC - ]; - - - break; - - default: - break; - } - - $x = get_config('system','role_perms'); - // let system settings over-ride any or all - if($x && is_array($x) && array_key_exists($role,$x)) - $ret = array_merge($ret,$x[$role]); - - call_hooks('get_role_perms',$ret); - - return $ret; -} - -/** - * @brief Returns a list or roles, grouped by type. - * - * @return string Returns an array of roles, grouped by type - */ -function get_roles() { - $roles = array( - t('Social Networking') => array('social' => t('Social - Mostly Public'), 'social_restricted' => t('Social - Restricted'), 'social_private' => t('Social - Private')), - t('Community Forum') => array('forum' => t('Forum - Mostly Public'), 'forum_restricted' => t('Forum - Restricted'), 'forum_private' => t('Forum - Private')), - t('Feed Republish') => array('feed' => t('Feed - Mostly Public'), 'feed_restricted' => t('Feed - Restricted')), - t('Special Purpose') => array('soapbox' => t('Special - Celebrity/Soapbox'), 'repository' => t('Special - Group Repository')), - t('Other') => array('custom' => t('Custom/Expert Mode')) - ); - - return $roles; -} diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index cc1f3b5d0..f47a9c878 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -252,11 +252,10 @@ abstract class photo_driver { */ if(! $this->is_valid()) - return FALSE; - + return false; if((! function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg')) - return; + return false; $exif = @exif_read_data($filename,null,true); @@ -489,20 +488,32 @@ function guess_image_type($filename, $headers = '') { $ext = pathinfo($filename, PATHINFO_EXTENSION); $ph = photo_factory(''); $types = $ph->supportedTypes(); - foreach ($types as $m=>$e){ - if ($ext==$e) $type = $m; + foreach($types as $m => $e) { + if($ext === $e) { + $type = $m; + } } } - if(is_null($type)) { + if(is_null($type) && (strpos($filename,'http') === false)) { $size = getimagesize($filename); $ph = photo_factory(''); $types = $ph->supportedTypes(); $type = ((array_key_exists($size['mime'], $types)) ? $size['mime'] : 'image/jpeg'); } + if(is_null($type)) { + if(strpos(strtolower($filename),'jpg') !== false) + $type = 'image/jpeg'; + elseif(strpos(strtolower($filename),'jpeg') !== false) + $type = 'image/jpeg'; + elseif(strpos(strtolower($filename),'gif') !== false) + $type = 'image/gif'; + elseif(strpos(strtolower($filename),'png') !== false) + $type = 'image/png'; + } } - logger('Photo: guess_image_type: type='.$type, LOGGER_DEBUG); + logger('Photo: guess_image_type: type = ' . $type, LOGGER_DEBUG); return $type; } diff --git a/include/photos.php b/include/photos.php index 093a43095..f5d5fdb48 100644 --- a/include/photos.php +++ b/include/photos.php @@ -17,6 +17,7 @@ require_once('include/text.php'); * @param array $args * @return array */ + function photo_upload($channel, $observer, $args) { $ret = array('success' => false); @@ -28,9 +29,6 @@ function photo_upload($channel, $observer, $args) { return $ret; } - -//call_hooks('photo_upload_begin', $args); - /* * Determine the album to use */ @@ -90,8 +88,6 @@ function photo_upload($channel, $observer, $args) { } else { $f = array('src' => '', 'filename' => '', 'filesize' => 0, 'type' => ''); -// call_hooks('photo_upload_file',$f); - if (x($f,'src') && x($f,'filesize')) { $src = $f['src']; $filename = $f['filename']; @@ -369,37 +365,37 @@ function photo_upload($channel, $observer, $args) { else { $mid = item_message_id(); - $arr = array(); - - if($lat && $lon) - $arr['coord'] = $lat . ' ' . $lon; + $arr = [ + 'aid' => $account_id, + 'uid' => $channel_id, + 'mid' => $mid, + 'parent_mid' => $mid, + 'item_hidden' => $item_hidden, + 'resource_type' => 'photo', + 'resource_id' => $photo_hash, + 'owner_xchan' => $channel['channel_hash'], + 'author_xchan' => $observer['xchan_hash'], + 'title' => $title, + 'allow_cid' => $ac['allow_cid'], + 'allow_gid' => $ac['allow_gid'], + 'deny_cid' => $ac['deny_cid'], + 'deny_gid' => $ac['deny_gid'], + 'verb' => ACTIVITY_POST, + 'obj_type' => ACTIVITY_OBJ_PHOTO, + 'obj' => json_encode($object), + 'tgt_type' => ACTIVITY_OBJ_ALBUM, + 'target' => json_encode($target), + 'item_wall' => $visible, + 'item_origin' => 1, + 'item_thread_top' => 1, + 'item_private' => intval($acl->is_private()), + 'body' => $summary + ]; - $arr['aid'] = $account_id; - $arr['uid'] = $channel_id; - $arr['mid'] = $mid; - $arr['parent_mid'] = $mid; - $arr['item_hidden'] = $item_hidden; - $arr['resource_type'] = 'photo'; - $arr['resource_id'] = $photo_hash; - $arr['owner_xchan'] = $channel['channel_hash']; - $arr['author_xchan'] = $observer['xchan_hash']; - $arr['title'] = $title; - $arr['allow_cid'] = $ac['allow_cid']; - $arr['allow_gid'] = $ac['allow_gid']; - $arr['deny_cid'] = $ac['deny_cid']; - $arr['deny_gid'] = $ac['deny_gid']; - $arr['verb'] = ACTIVITY_POST; - $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; - $arr['obj'] = json_encode($object); - $arr['tgt_type'] = ACTIVITY_OBJ_ALBUM; - $arr['target'] = json_encode($target); - $arr['item_wall'] = 1; - $arr['item_origin'] = 1; - $arr['item_thread_top'] = 1; - $arr['item_private'] = intval($acl->is_private()); $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); - $arr['body'] = $summary; + if($lat && $lon) + $arr['coord'] = $lat . ' ' . $lon; // this one is tricky because the item and the photo have the same permissions, those of the photo. // Use the channel read_stream permissions to get the correct public_policy for the item and recalculate the @@ -446,6 +442,7 @@ function photo_upload($channel, $observer, $args) { * * \e boolean \b success * * \e array \b albums */ + function photos_albums_list($channel, $observer, $sort_key = 'display_path', $direction = 'asc') { $channel_id = $channel['channel_id']; @@ -495,15 +492,15 @@ function photos_albums_list($channel, $observer, $sort_key = 'display_path', $di $ret['success'] = true; $ret['albums'] = array(); foreach($albums as $k => $album) { - $entry = array( - 'text' => (($album['album']) ? $album['album'] : '/'), + $entry = [ + 'text' => (($album['album']) ? $album['album'] : '/'), 'shorttext' => (($album['album']) ? ellipsify($album['album'],28) : '/'), - 'jstext' => (($album['album']) ? addslashes($album['album']) : '/'), - 'total' => $album['total'], - 'url' => z_root() . '/photos/' . $channel['channel_address'] . '/album/' . $album['folder'], + 'jstext' => (($album['album']) ? addslashes($album['album']) : '/'), + 'total' => $album['total'], + 'url' => z_root() . '/photos/' . $channel['channel_address'] . '/album/' . $album['folder'], 'urlencode' => urlencode($album['album']), - 'bin2hex' => $album['folder'] - ); + 'bin2hex' => $album['folder'] + ]; $ret['albums'][] = $entry; } } @@ -545,6 +542,7 @@ function photos_album_widget($channelx,$observer,$sortkey = 'display_path',$dire * @param string $album default empty * @return boolean|array */ + function photos_list_photos($channel, $observer, $album = '') { $channel_id = $channel['channel_id']; @@ -584,7 +582,10 @@ function photos_list_photos($channel, $observer, $album = '') { * @param string $album name of the album * @return boolean */ + + function photos_album_exists($channel_id, $observer_hash, $album) { + $sql_extra = permissions_sql($channel_id,$observer_hash); $r = q("SELECT folder, hash, is_dir, filename, os_path, display_path FROM attach WHERE hash = '%s' AND is_dir = 1 AND uid = %d $sql_extra limit 1", @@ -614,6 +615,7 @@ function photos_album_exists($channel_id, $observer_hash, $album) { * @param string $newname The new name of the album * @return bool|array */ + function photos_album_rename($channel_id, $oldname, $newname) { return q("UPDATE photo SET album = '%s' WHERE album = '%s' AND uid = %d", dbesc($newname), @@ -632,6 +634,7 @@ function photos_album_rename($channel_id, $oldname, $newname) { * @param string $remote_xchan * @return string|boolean */ + function photos_album_get_db_idstr($channel_id, $album, $remote_xchan = '') { if($remote_xchan) { @@ -668,6 +671,7 @@ function photos_album_get_db_idstr($channel_id, $album, $remote_xchan = '') { * @param boolean $visible default false * @return int item_id */ + function photos_create_item($channel, $creator_hash, $photo, $visible = false) { // Create item container @@ -723,7 +727,7 @@ function getGps($exifCoord, $hemi) { function getGpstimestamp($exifCoord) { - $hours = count($exifCoord) > 0 ? gps2Num($exifCoord[0]) : 0; + $hours = count($exifCoord) > 0 ? gps2Num($exifCoord[0]) : 0; $minutes = count($exifCoord) > 1 ? gps2Num($exifCoord[1]) : 0; $seconds = count($exifCoord) > 2 ? gps2Num($exifCoord[2]) : 0; diff --git a/include/security.php b/include/security.php index ddbb3d806..6e7b3dbb2 100644 --- a/include/security.php +++ b/include/security.php @@ -467,7 +467,6 @@ function scopes_sql($uid,$observer) { */ function public_permissions_sql($observer_hash) { - //$observer = App::get_observer(); $groups = init_groups_visitor($observer_hash); $gs = '<<>>'; // should be impossible to match diff --git a/include/socgraph.php b/include/socgraph.php index 69365f80f..a5b5d1378 100644 --- a/include/socgraph.php +++ b/include/socgraph.php @@ -122,7 +122,7 @@ function poco_load($xchan = '', $url = null) { $profile_url = $url['value']; continue; } - if($url['type'] == 'zot' || $url['type'] == 'diaspora' || $url['type'] == 'friendica') { + if($url['type'] == 'zot') { $network = $url['type']; $address = str_replace('acct:' , '', $url['value']); continue; @@ -163,12 +163,6 @@ function poco_load($xchan = '', $url = null) { continue; } } - else { - $x = import_author_diaspora(array('address' => $address)); - if(! $x) { - continue; - } - } } else { continue; @@ -243,77 +237,6 @@ function common_friends($uid,$xchan,$start = 0,$limit=100000000,$shuffle = false } -function count_common_friends_zcid($uid,$zcid) { - - $r = q("SELECT count(*) as total - FROM glink left join gcontact on glink.gcid = gcontact.id - where glink.zcid = %d - and gcontact.nurl in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 ) ", - intval($zcid), - intval($uid) - ); - - if(count($r)) - return $r[0]['total']; - - return 0; -} - -function common_friends_zcid($uid,$zcid,$start = 0, $limit = 9999,$shuffle = false) { - - if($shuffle) - $sql_extra = " order by rand() "; - else - $sql_extra = " order by gcontact.name asc "; - - $r = q("SELECT gcontact.* - FROM glink left join gcontact on glink.gcid = gcontact.id - where glink.zcid = %d - and gcontact.nurl in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 ) - $sql_extra limit %d offset %d", - intval($zcid), - intval($uid), - intval($limit), - intval($start) - ); - - return $r; -} - - -function count_all_friends($uid,$cid) { - - $r = q("SELECT count(*) as total - FROM glink left join gcontact on glink.gcid = gcontact.id - where glink.cid = %d and glink.uid = %d ", - intval($cid), - intval($uid) - ); - - if(count($r)) - return $r[0]['total']; - - return 0; -} - - -function all_friends($uid,$cid,$start = 0, $limit = 80) { - - $r = q("SELECT gcontact.* - FROM glink left join gcontact on glink.gcid = gcontact.id - where glink.cid = %d and glink.uid = %d - order by gcontact.name asc LIMIT %d OFFSET %d ", - intval($cid), - intval($uid), - intval($limit), - intval($start) - ); - - return $r; -} - - - function suggestion_query($uid, $myxchan, $start = 0, $limit = 80) { if((! $uid) || (! $myxchan)) @@ -564,8 +487,6 @@ function poco($a,$extended = false) { if($fields_ret['urls']) { $entry['urls'] = array(array('value' => $rr['xchan_url'], 'type' => 'profile')); $network = $rr['xchan_network']; - if(strpos($network,'friendica') !== false) - $network = 'friendica'; if($rr['xchan_addr']) $entry['urls'][] = array('value' => 'acct:' . $rr['xchan_addr'], 'type' => $network); } diff --git a/include/statistics_fns.php b/include/statistics_fns.php index cbff3b0b7..d213485bf 100644 --- a/include/statistics_fns.php +++ b/include/statistics_fns.php @@ -7,7 +7,7 @@ function update_channels_total_stat() { $channels_total_stat = intval($r[0]['channels_total']); set_config('system','channels_total_stat',$channels_total_stat); } else { - set_config('system','channels_total_stat',null); + set_config('system','channels_total_stat',0); } } @@ -30,10 +30,10 @@ function update_channels_active_halfyear_stat() { $channels_active_halfyear_stat = count($x); set_config('system','channels_active_halfyear_stat',$channels_active_halfyear_stat); } else { - set_config('system','channels_active_halfyear_stat',null); + set_config('system','channels_active_halfyear_stat',0); } } else { - set_config('system','channels_active_halfyear_stat',null); + set_config('system','channels_active_halfyear_stat',0); } } @@ -56,10 +56,10 @@ function update_channels_active_monthly_stat() { $channels_active_monthly_stat = count($x); set_config('system','channels_active_monthly_stat',$channels_active_monthly_stat); } else { - set_config('system','channels_active_monthly_stat',null); + set_config('system','channels_active_monthly_stat',0); } } else { - set_config('system','channels_active_monthly_stat',null); + set_config('system','channels_active_monthly_stat',0); } } @@ -69,8 +69,16 @@ function update_local_posts_stat() { $local_posts_stat = intval($posts[0]["local_posts"]); set_config('system','local_posts_stat',$local_posts_stat); } else { - set_config('system','local_posts_stat',null); + set_config('system','local_posts_stat',0); } } +function update_local_comments_stat() { + $posts = q("SELECT COUNT(*) AS local_posts FROM item WHERE item_wall = 1 and id != parent"); + if (!is_array($posts)) + $local_posts = 0; + else + $local_posts = $posts[0]["local_posts"]; + set_config('system','local_comments_stat', $local_posts); +}
\ No newline at end of file diff --git a/include/taxonomy.php b/include/taxonomy.php index 0b4b2aa9a..46d661581 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -44,6 +44,45 @@ function term_query($table,$s,$type = TERM_UNKNOWN, $type2 = '') { } +function term_item_parent_query($uid,$table,$s,$type = TERM_UNKNOWN, $type2 = '') { + + // Allow asterisks for wildcard search + // In theory this means '%' will also do a wildcard search, but there appear to be multiple escape + // issues with '%' in term names and trying to fix this with '\\%' here did not help. + // Ideally I think we want '*' to indicate wildcards and allow '%' literally in names, but that is being + // left for another developer on another day. + + $s = str_replace('*','%',$s); + + if($type2) { + $r = q("select parent from item left join term on term.oid = item.id where term.ttype in (%d, %d) and term.term like '%s' and term.uid = %d and term.otype = 1", + intval($type), + intval($type2), + dbesc($s), + intval($uid) + ); + } + else { + $r = q("select parent from item left join term on term.oid = item.id where term.ttype = %d and term.term like '%s' and term.uid = %d and term.otype = 1", + intval($type), + dbesc($s), + intval($uid) + ); + } + + if($r) { + $str = ''; + foreach($r as $rv) { + if($str) + $str .= ','; + $str .= intval($rv['parent']); + } + return " AND " . (($table) ? dbesc($table) . '.' : '') . "id in ( $str ) "; + } + return " AND false "; +} + + function store_item_tag($uid,$iid,$otype,$type,$term,$url = '') { if(! $term) return false; diff --git a/include/text.php b/include/text.php index d352a61bf..5af4f1b67 100644 --- a/include/text.php +++ b/include/text.php @@ -648,12 +648,8 @@ function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { $where = ''; - // We require > 5.4 but leave the version check so that install issues (including version) can be logged - - if(version_compare(PHP_VERSION, '5.4.0') >= 0) { - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; - } + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; $s = datetime_convert() . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL; $pluginfo = array('filename' => $logfile, 'loglevel' => $level, 'message' => $s,'priority' => $priority, 'logged' => false); @@ -687,16 +683,14 @@ function btlogger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { @file_put_contents(BTLOGGER_DEBUG_FILE, $s, FILE_APPEND); } - if(version_compare(PHP_VERSION, '5.4.0') >= 0) { - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - if($stack) { - for($x = 1; $x < count($stack); $x ++) { - $s = 'stack: ' . basename($stack[$x]['file']) . ':' . $stack[$x]['line'] . ':' . $stack[$x]['function'] . '()'; - logger($s,$level, $priority); + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + if($stack) { + for($x = 1; $x < count($stack); $x ++) { + $s = 'stack: ' . basename($stack[$x]['file']) . ':' . $stack[$x]['line'] . ':' . $stack[$x]['function'] . '()'; + logger($s,$level, $priority); - if(file_exists(BTLOGGER_DEBUG_FILE) && is_writable(BTLOGGER_DEBUG_FILE)) { - @file_put_contents(BTLOGGER_DEBUG_FILE, $s . PHP_EOL, FILE_APPEND); - } + if(file_exists(BTLOGGER_DEBUG_FILE) && is_writable(BTLOGGER_DEBUG_FILE)) { + @file_put_contents(BTLOGGER_DEBUG_FILE, $s . PHP_EOL, FILE_APPEND); } } } @@ -751,10 +745,10 @@ function dlogger($msg, $level = 0) { return; $where = ''; - if(version_compare(PHP_VERSION, '5.4.0') >= 0) { - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; - } + + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; + @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND); } @@ -1042,19 +1036,6 @@ function searchbox($s,$id='search-box',$url='/search',$save = false) { )); } -function valid_email_regex($x){ - if(preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x)) - return true; - return false; -} - -function valid_email($x){ - if(get_config('system','disable_email_validation')) - return true; - - return valid_email_regex($x); -} - /** * @brief Replace naked text hyperlink with HTML formatted hyperlink. * @@ -1199,8 +1180,7 @@ function list_smilies($default_only = false) { ':coffee', ':facepalm', ':like', - ':dislike', - ':hubzilla' + ':dislike' ); $icons = array( @@ -1234,8 +1214,7 @@ function list_smilies($default_only = false) { '<img class="smiley" src="' . z_root() . '/images/emoticons/coffee.gif" alt=":coffee" />', '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-facepalm.gif" alt=":facepalm" />', '<img class="smiley" src="' . z_root() . '/images/emoticons/like.gif" alt=":like" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/dislike.gif" alt=":dislike" />', - '<img class="smiley" src="' . z_root() . '/images/hz-16.png" alt=":hubzilla" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/dislike.gif" alt=":dislike" />' ); @@ -1552,17 +1531,22 @@ function prepare_body(&$item,$attach = false) { // if original photo width is <= 640px prepend it to item body if($object['link'][0]['width'] && $object['link'][0]['width'] <= 640) { - $s .= '<div class="inline-photo-item-wrapper"><a href="' . zid(rawurldecode($object['id'])) . '" target="_blank"><img class="inline-photo-item" style="max-width:' . $object['link'][0]['width'] . 'px; width:100%; height:auto;" src="' . zid(rawurldecode($object['link'][0]['href'])) . '"></a></div>' . $s; + $s .= '<div class="inline-photo-item-wrapper"><a href="' . zid(rawurldecode($object['id'])) . '" target="_blank" rel="nofollow noopener" ><img class="inline-photo-item" style="max-width:' . $object['link'][0]['width'] . 'px; width:100%; height:auto;" src="' . zid(rawurldecode($object['link'][0]['href'])) . '"></a></div>' . $s; } // if original photo width is > 640px make it a cover photo if($object['link'][0]['width'] && $object['link'][0]['width'] > 640) { $scale = ((($object['link'][1]['width'] == 1024) || ($object['link'][1]['height'] == 1024)) ? 1 : 0); - $photo = '<a href="' . zid(rawurldecode($object['id'])) . '" target="_blank"><img style="max-width:' . $object['link'][$scale]['width'] . 'px; width:100%; height:auto;" src="' . zid(rawurldecode($object['link'][$scale]['href'])) . '"></a>'; + $photo = '<a href="' . zid(rawurldecode($object['id'])) . '" target="_blank" rel="nofollow noopener"><img style="max-width:' . $object['link'][$scale]['width'] . 'px; width:100%; height:auto;" src="' . zid(rawurldecode($object['link'][$scale]['href'])) . '"></a>'; } } - $s .= prepare_text($item['body'],$item['mimetype'], false); + if($item['item_obscured']) { + $s .= prepare_binary($item); + } + else { + $s .= prepare_text($item['body'],$item['mimetype'], false); + } $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event_obj($item['obj']) : false); @@ -1625,6 +1609,17 @@ function prepare_body(&$item,$attach = false) { return $prep_arr; } + +function prepare_binary($item) { + return replace_macros(get_markup_template('item_binary.tpl'), [ + '$download' => t('Download binary/encrypted content'), + '$url' => z_root() . '/viewsrc/' . $item['id'] . '/download' + ]); +} + + + + /** * @brief Given a text string, convert from bbcode to html and add smilie icons. * @@ -1992,23 +1987,44 @@ function legal_webbie($s) { if(! strlen($s)) return ''; - $x = $s; - do { - $s = $x; - $x = preg_replace('/^([^a-z])(.*?)/',"$2",$s); - } while($x != $s); + // WARNING: This regex will not work in a federated environment. + // You will probably want something like + // preg_replace('/([^a-z0-9\_])/','',strtolower($s)); + + $r = preg_replace('/([^a-z0-9\-\_\.])/','',strtolower($s)); + + $x = [ 'input' => $s, 'output' => $r ]; + call_hooks('legal_webbie',$x); + return $x['output']; + +} + +function legal_webbie_text() { + + // WARNING: This will not work in a federated environment. + + $s = t('a-z, 0-9, -, _, and . only'); + + $x = [ 'text' => $s ]; + call_hooks('legal_webbie_text',$x); + return $x['text']; - return preg_replace('/([^a-z0-9\-\_])/','',$x); } + + + function check_webbie($arr) { + + // These names conflict with the CalDAV server + $taken = [ 'principals', 'addressbooks', 'calendars' ]; + $reservechan = get_config('system','reserved_channels'); - if(strlen($reservechan)) - $taken = explode(',', $reservechan); - else - $taken = array('principals','addressbooks','calendars'); + if(strlen($reservechan)) { + $taken = array_merge($taken,explode(',', $reservechan)); + } $str = ''; if(count($arr)) { @@ -2258,13 +2274,13 @@ function design_tools() { $who = $channel['channel_address']; return replace_macros(get_markup_template('design_tools.tpl'), array( - '$title' => t('Design Tools'), - '$who' => $who, - '$sys' => $sys, + '$title' => t('Design Tools'), + '$who' => $who, + '$sys' => $sys, '$blocks' => t('Blocks'), - '$menus' => t('Menus'), + '$menus' => t('Menus'), '$layout' => t('Layouts'), - '$pages' => t('Pages') + '$pages' => t('Pages') )); } @@ -2285,21 +2301,21 @@ function website_portation_tools() { } return replace_macros(get_markup_template('website_portation_tools.tpl'), array( - '$title' => t('Import'), - '$import_label' => t('Import website...'), - '$import_placeholder' => t('Select folder to import'), - '$file_upload_text' => t('Import from a zipped folder:'), - '$file_import_text' => t('Import from cloud files:'), - '$desc' => t('/cloud/channel/path/to/folder'), - '$hint' => t('Enter path to website files'), - '$select' => t('Select folder'), - '$export_label' => t('Export website...'), - '$file_download_text' => t('Export to a zip file'), - '$filename_desc' => t('website.zip'), - '$filename_hint' => t('Enter a name for the zip file.'), - '$cloud_export_text' => t('Export to cloud files'), - '$cloud_export_desc' => t('/path/to/export/folder'), - '$cloud_export_hint' => t('Enter a path to a cloud files destination.'), + '$title' => t('Import'), + '$import_label' => t('Import website...'), + '$import_placeholder' => t('Select folder to import'), + '$file_upload_text' => t('Import from a zipped folder:'), + '$file_import_text' => t('Import from cloud files:'), + '$desc' => t('/cloud/channel/path/to/folder'), + '$hint' => t('Enter path to website files'), + '$select' => t('Select folder'), + '$export_label' => t('Export website...'), + '$file_download_text' => t('Export to a zip file'), + '$filename_desc' => t('website.zip'), + '$filename_hint' => t('Enter a name for the zip file.'), + '$cloud_export_text' => t('Export to cloud files'), + '$cloud_export_desc' => t('/path/to/export/folder'), + '$cloud_export_hint' => t('Enter a path to a cloud files destination.'), '$cloud_export_select' => t('Specify folder'), )); } @@ -2897,7 +2913,7 @@ function pdl_selector($uid, $current='') { $sql_extra = item_permissions_sql($uid); - $r = q("select iconfig.*, mid from item_id left join item on iconfig.iid = item.id + $r = q("select iconfig.*, mid from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' $sql_extra order by v asc", intval($uid) ); @@ -2969,40 +2985,13 @@ function text_highlight($s, $lang) { $s = jindent($s); } - if(! strpos('Text_Highlighter', get_include_path())) { - set_include_path(get_include_path() . PATH_SEPARATOR . 'library/Text_Highlighter'); - } - require_once('library/Text_Highlighter/Text/Highlighter.php'); - require_once('library/Text_Highlighter/Text/Highlighter/Renderer/Html.php'); - $options = array( - 'numbers' => HL_NUMBERS_LI, - 'tabsize' => 4, - ); - $tag_added = false; - $s = trim(html_entity_decode($s, ENT_COMPAT)); - $s = str_replace(" ", "\t", $s); - - // The highlighter library insists on an opening php tag for php code blocks. If - // it isn't present, nothing is highlighted. So we're going to see if it's present. - // If not, we'll add it, and then quietly remove it after we get the processed output back. - - if($lang === 'php') { - if(strpos('<?php', $s) !== 0) { - $s = '<?php' . "\n" . $s; - $tag_added = true; - } - } - $renderer = new Text_Highlighter_Renderer_HTML($options); - $hl = Text_Highlighter::factory($lang); - $hl->setRenderer($renderer); - $o = $hl->highlight($s); - $o = str_replace([" ", "\n"], [" ", ''], $o); + $arr = [ 'text' => $s, 'language' => $lang, 'success' => false ]; + call_hooks('text_highlight',$arr); - if($tag_added) { - $b = substr($o, 0, strpos($o, '<li>')); - $e = substr($o, strpos($o, '</li>')); - $o = $b . $e; - } + if($arr['success']) + $o = $arr['text']; + else + $o = $s; return('<code>' . $o . '</code>'); } @@ -3033,7 +3022,8 @@ function array2XML($obj, $array) { if(is_array($value)) { $node = $obj->addChild($key); array2XML($node, $value); - } else { + } + else { $obj->addChild($key, htmlspecialchars($value)); } } @@ -3065,6 +3055,14 @@ function create_table_from_array($table, $arr) { return $r; } +function share_shield($m) { + return str_replace($m[1],'!=+=+=!' . base64url_encode($m[1]) . '=+!=+!=',$m[0]); +} + +function share_unshield($m) { + $x = str_replace(array('!=+=+=!','=+!=+!='),array('',''),$m[1]); + return str_replace($m[1], base64url_decode($x), $m[0]); +} function cleanup_bbcode($body) { diff --git a/include/zid.php b/include/zid.php index f5df1c611..ee43fd7c8 100644 --- a/include/zid.php +++ b/include/zid.php @@ -60,7 +60,7 @@ function zid($s,$address = '') { /** * @FIXME checking against our own channel url is no longer reliable. We may have a lot - * of urls attached to out channel. Should probably match against our site, since we + * of urls attached to our channel. Should probably match against our site, since we * will not need to remote authenticate on our own site anyway. */ diff --git a/include/zot.php b/include/zot.php index 31debbe9d..3143fa000 100644 --- a/include/zot.php +++ b/include/zot.php @@ -1326,8 +1326,10 @@ function public_recips($msg) { $include_sys = false; if($msg['message']['type'] === 'activity') { - if(! get_config('system','disable_discover_tab')) + $disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false; + if(! $disable_discover_tab) $include_sys = true; + $perm = 'send_stream'; if(array_key_exists('flags',$msg['message']) && in_array('thread_parent', $msg['message']['flags'])) { @@ -1761,7 +1763,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ $result[] = $DR->get(); } else { - update_imported_item($sender,$arr,$r[0],$channel['channel_id'],$tag_delivery); + $item_result = update_imported_item($sender,$arr,$r[0],$channel['channel_id'],$tag_delivery); $DR->update('updated'); $result[] = $DR->get(); if(! $relay) @@ -1810,6 +1812,14 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ } } + // preserve conversations with which you are involved from expiration + + $stored = (($item_result && $item_result['item']) ? $item_result['item'] : false); + if((is_array($stored)) && ($stored['id'] != $stored['parent']) + && ($stored['author_xchan'] === $channel['channel_hash'])) { + retain_item($stored['item']['parent']); + } + if($relay && $item_id) { logger('process_delivery: invoking relay'); Zotlabs\Daemon\Master::Summon(array('Notifier','relay',intval($item_id))); @@ -1946,6 +1956,8 @@ function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) { logger('update_imported_item: failed: ' . $x['message']); else logger('update_imported_item'); + + return $x; } /** @@ -2237,9 +2249,6 @@ function check_location_move($sender_hash,$locations) { if(! $locations) return; - if(get_config('system','server_role') !== 'basic') - return; - if(count($locations) != 1) return; @@ -2824,7 +2833,7 @@ function import_site($arr, $pubkey) { $access_policy = ACCESS_PRIVATE; if($access_policy != ACCESS_PRIVATE) { - $x = z_fetch_url($arr['url'] . '/siteinfo/json'); + $x = z_fetch_url($arr['url'] . '/siteinfo.json'); if(! $x['success']) $access_policy = ACCESS_PRIVATE; } @@ -2935,8 +2944,6 @@ function import_site($arr, $pubkey) { function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { - if(get_config('system','server_role') === 'basic') - return; logger('build_sync_packet'); @@ -3086,8 +3093,6 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { function process_channel_sync_delivery($sender, $arr, $deliveries) { - if(get_config('system','server_role') === 'basic') - return; require_once('include/import.php'); @@ -3289,6 +3294,11 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { if(! array_key_exists('abook_xchan',$clean)) continue; + if(array_key_exists('abook_instance',$clean) && $clean['abook_instance'] && strpos($clean['abook_instance'],z_root()) === false) { + $clean['abook_not_here'] = 1; + } + + $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($clean['abook_xchan']), intval($channel['channel_id']) @@ -3364,7 +3374,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { dbesc($cl['name']), intval($cl['visible']), intval($cl['deleted']), - dbesc($cl['hash']), + dbesc($cl['collection']), intval($channel['channel_id']) ); } @@ -3586,6 +3596,14 @@ function import_author_zot($x) { $hash = make_xchan_hash($x['guid'],$x['guid_sig']); + // also - this function may get passed a profile url as 'url' and zot_refresh wants a hubloc_url (site baseurl), + // so deconstruct the url (if we have one) and rebuild it with just the baseurl components. + + if(array_key_exists('url',$x)) { + $m = parse_url($x['url']); + $desturl = $m['scheme'] . '://' . $m['host']; + } + $r1 = q("select hubloc_url, hubloc_updated, site_dead from hubloc left join site on hubloc_url = site_url where hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_primary = 1 limit 1", dbesc($x['guid']), @@ -3627,14 +3645,16 @@ function import_author_zot($x) { ); if($r) { logger('found another site that is not dead: ' . $r[0]['hubloc_url'], LOGGER_DEBUG,LOG_INFO); - $x['url'] = $r[0]['hubloc_url']; + $desturl = $r[0]['hubloc_url']; } else { return $hash; } } - $them = array('hubloc_url' => $x['url'], 'xchan_guid' => $x['guid'], 'xchan_guid_sig' => $x['guid_sig']); + + + $them = array('hubloc_url' => $desturl, 'xchan_guid' => $x['guid'], 'xchan_guid_sig' => $x['guid_sig']); if(zot_refresh($them)) return $hash; @@ -3921,6 +3941,7 @@ function zotinfo($arr) { $ret['photo_updated'] = $e['xchan_photo_date']; $ret['url'] = $e['xchan_url']; $ret['connections_url']= (($e['xchan_connurl']) ? $e['xchan_connurl'] : z_root() . '/poco/' . $e['channel_address']); + $ret['follow_url'] = $e['xchan_follow']; $ret['target'] = $ztarget; $ret['target_sig'] = $zsig; $ret['searchable'] = $searchable; @@ -3928,19 +3949,22 @@ function zotinfo($arr) { $ret['public_forum'] = $public_forum; if($deleted) $ret['deleted'] = $deleted; + if(intval($e['channel_removed'])) $ret['deleted_locally'] = true; + + // premium or other channel desiring some contact with potential followers before connecting. // This is a template - %s will be replaced with the follow_url we discover for the return channel. - if($special_channel) - $ret['connect_url'] = z_root() . '/connect/' . $e['channel_address']; - + if($special_channel) { + $ret['connect_url'] = (($e['xchan_connpage']) ? $e['xchan_connpage'] : z_root() . '/connect/' . $e['channel_address']); + } // This is a template for our follow url, %s will be replaced with a webbie - $ret['follow_url'] = z_root() . '/follow?f=&url=%s'; - + if(! $ret['follow_url']) + $ret['follow_url'] = z_root() . '/follow?f=&url=%s'; $permissions = get_all_perms($e['channel_id'],$ztarget_hash,false); |