diff options
-rw-r--r-- | CHANGELOG | 11 | ||||
-rw-r--r-- | Zotlabs/Daemon/Xchan_photo.php | 9 | ||||
-rw-r--r-- | Zotlabs/Lib/Activity.php | 19 | ||||
-rw-r--r-- | Zotlabs/Lib/Config.php | 2 | ||||
-rw-r--r-- | Zotlabs/Lib/Libsync.php | 2 | ||||
-rw-r--r-- | Zotlabs/Lib/ThreadItem.php | 4 | ||||
-rw-r--r-- | Zotlabs/Module/Help.php | 3 | ||||
-rw-r--r-- | Zotlabs/Module/Hq.php | 20 | ||||
-rw-r--r-- | Zotlabs/Module/Item.php | 2 | ||||
-rw-r--r-- | Zotlabs/Module/Rbmark.php | 7 | ||||
-rw-r--r-- | Zotlabs/Module/Setup.php | 8 | ||||
-rw-r--r-- | Zotlabs/Render/SmartyInterface.php | 2 | ||||
-rw-r--r-- | Zotlabs/Render/Theme.php | 6 | ||||
-rw-r--r-- | boot.php | 14 | ||||
-rw-r--r-- | include/attach.php | 9 | ||||
-rw-r--r-- | include/auth.php | 37 | ||||
-rw-r--r-- | include/config.php | 4 | ||||
-rw-r--r-- | include/event.php | 6 | ||||
-rw-r--r-- | include/html2plain.php | 4 | ||||
-rw-r--r-- | include/language.php | 4 | ||||
-rw-r--r-- | include/menu.php | 7 | ||||
-rw-r--r-- | include/network.php | 12 | ||||
-rw-r--r-- | tests/unit/UnitTestCase.php | 1 | ||||
-rw-r--r-- | tests/unit/includes/AuthTest.php | 81 | ||||
-rw-r--r-- | tests/unit/includes/dba/_files/account.yml | 2 | ||||
-rw-r--r-- | view/tpl/cal_event.tpl | 2 | ||||
-rw-r--r-- | view/tpl/event.tpl | 6 | ||||
-rw-r--r-- | view/tpl/install.tpl | 4 |
28 files changed, 207 insertions, 81 deletions
@@ -1,3 +1,14 @@ +Hubzilla 9.0.2 (2024-06-07) + - Fix buttons in event viewer + - Fix some PHP warnings and errors + - Fix issue when inReplyTo field is an array + - Fix possible queueworker crash + - Fix missing pdl file for mod home + - Reduced default directory result set + - Fix fatal error in likebanner addon + - Fix fatal error in hilite addon + + Hubzilla 9.0.1 (2024-03-26) - Fix an issue where after an update initiated from a modal the modal backdrop would remain - Fix bootstrap namespace in conv list templates diff --git a/Zotlabs/Daemon/Xchan_photo.php b/Zotlabs/Daemon/Xchan_photo.php index f90d1d726..662fc967c 100644 --- a/Zotlabs/Daemon/Xchan_photo.php +++ b/Zotlabs/Daemon/Xchan_photo.php @@ -8,14 +8,15 @@ class Xchan_photo { static public function run($argc, $argv) { - if ($argc != 3) { + if ($argc < 3) { return; } $url = hex2bin($argv[1]); $xchan = hex2bin($argv[2]); + $force = $argv[3]; - $photos = import_xchan_photo($url, $xchan); + $photos = import_xchan_photo($url, $xchan, false, $force); if ($photos) { $result = 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()), @@ -27,8 +28,10 @@ class Xchan_photo { ); if (! $result) { - logger("xchan update failed for $url"); + logger("xchan photo update failed for $url"); } } + + return; } } diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index b819ef0f7..9178dac39 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -70,7 +70,7 @@ class Activity { } if ($items) { - return self::encode_item(array_shift($items), true); + return self::encode_item(array_shift($items)); } return null; @@ -762,7 +762,12 @@ class Activity { $ptr = [$ptr]; } foreach ($ptr as $att) { + if (!is_array($att)) { + continue; + } + $entry = []; + if (array_key_exists('href', $att) && $att['href']) { $entry['href'] = $att['href']; } elseif (array_key_exists('url', $att) && $att['url']) { @@ -1085,12 +1090,16 @@ class Activity { static function encode_person($p, $extended = true) { + $ret = (($extended) ? [] : ''); + + if (!is_array($p)) { + return $ret; + } + $c = ((array_key_exists('channel_id', $p)) ? $p : channelx_by_hash($p['xchan_hash'])); $id = (($c) ? channel_url($c) : ((filter_var($p['xchan_hash'], FILTER_VALIDATE_URL)) ? $p['xchan_hash'] : $p['xchan_url'])); - $ret = (($extended) ? [] : ''); - if (!$id) { return $ret; } @@ -1866,7 +1875,7 @@ class Activity { } if ($icon) { - Master::Summon(['Xchan_photo', bin2hex($icon), bin2hex($url)]); + Master::Summon(['Xchan_photo', bin2hex($icon), bin2hex($url), $force]); } } @@ -2893,7 +2902,7 @@ class Activity { if (intval($parent[0]['item_private'])) { if (!intval($item['item_private'])) { - $item['item_private'] = intval($parent_item['item_private']); + $item['item_private'] = intval($parent[0]['item_private']); $item['allow_cid'] = '<' . $channel['channel_hash'] . '>'; $item['allow_gid'] = $item['deny_cid'] = $item['deny_gid'] = ''; } diff --git a/Zotlabs/Lib/Config.php b/Zotlabs/Lib/Config.php index 95df8ed6f..cd8b08991 100644 --- a/Zotlabs/Lib/Config.php +++ b/Zotlabs/Lib/Config.php @@ -115,7 +115,7 @@ class Config { * The category of the configuration value * @param string $key * The configuration key to query - * @param string $default (optional) default false + * @param mixed $default (optional) default false * @return mixed Return value or false on error or if not set */ public static function Get($family, $key, $default = false) { diff --git a/Zotlabs/Lib/Libsync.php b/Zotlabs/Lib/Libsync.php index e68fd53f1..a7e33ba6b 100644 --- a/Zotlabs/Lib/Libsync.php +++ b/Zotlabs/Lib/Libsync.php @@ -267,7 +267,7 @@ class Libsync { } if ($cat !== 'hz_delpconfig') { - set_pconfig($channel['channel_id'],$cat,$k,$v,$pconfig_updated[$k]); + set_pconfig($channel['channel_id'], $cat, $k, $v, $pconfig_updated[$k]); } } } diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 290a7b0c2..eb1020cd2 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -44,8 +44,6 @@ class ThreadItem { $this->toplevel = ($this->get_id() == $this->get_data_value('parent')); $this->threaded = Config::Get('system','thread_allow'); - $observer = \App::get_observer(); - // Prepare the children if(isset($data['children'])) { @@ -875,7 +873,7 @@ class ThreadItem { '$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false), '$encrypt' => t('Encrypt text'), '$cipher' => $conv->get_cipher(), - '$sourceapp' => \App::$sourcename, + '$sourceapp' => App::$sourcename, '$observer' => get_observer_hash(), '$anoncomments' => ((in_array($conv->get_mode(), ['channel', 'display', 'cards', 'articles']) && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false), '$anonname' => [ 'anonname', t('Your full name (required)') ], diff --git a/Zotlabs/Module/Help.php b/Zotlabs/Module/Help.php index 5d52440a5..fd5cacee6 100644 --- a/Zotlabs/Module/Help.php +++ b/Zotlabs/Module/Help.php @@ -210,7 +210,8 @@ class Help extends \Zotlabs\Web\Controller { $content = preg_replace_callback( "/#include (.*?)\;/ism", function ($matches) { - $sub_file_type = array_pop(explode('.', $matches[1])); + $parts = explode('.', $matches[1]); + $sub_file_type = array_pop($parts); $included_content = $this->render_help_file($matches[1], $sub_file_type); return str_replace($matches[0], $included_content, $matches[0]); }, diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php index ae6a016fc..c8e6efe38 100644 --- a/Zotlabs/Module/Hq.php +++ b/Zotlabs/Module/Hq.php @@ -4,14 +4,6 @@ namespace Zotlabs\Module; use App; use Zotlabs\Widget\Messages; - -require_once("include/bbcode.php"); -require_once('include/security.php'); -require_once('include/conversation.php'); -require_once('include/acl_selectors.php'); -require_once('include/items.php'); - - class Hq extends \Zotlabs\Web\Controller { function init() { @@ -98,7 +90,6 @@ class Hq extends \Zotlabs\Web\Controller { 'permissions' => $channel_acl, 'bang' => '', 'visitor' => true, - 'profile_uid' => local_channel(), 'return_path' => 'hq', 'expanded' => true, 'editor_autocomplete' => true, @@ -117,17 +108,6 @@ class Hq extends \Zotlabs\Web\Controller { nav_set_selected('HQ'); - if($target_item) { - // if the target item is not a post (eg a like) we want to address its thread parent - //$mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); - - // if we got a decoded hash we must encode it again before handing to javascript - // $mid = gen_link_id($target_item['mid']); - } - else { - $mid = ''; - } - $o .= '<div id="live-hq"></div>' . "\r\n"; $o .= "<script> var profile_uid = " . local_channel() . "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . ";</script>\r\n"; diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 0324bf662..0b46f2baf 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -1654,7 +1654,7 @@ class Item extends Controller { $listener = Libzot::zot_record_preferred($listener); $c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", - intval($profile_uid), + intval($item['uid']), dbesc($listener['hubloc_hash']) ); diff --git a/Zotlabs/Module/Rbmark.php b/Zotlabs/Module/Rbmark.php index b1aea2590..df32a97c2 100644 --- a/Zotlabs/Module/Rbmark.php +++ b/Zotlabs/Module/Rbmark.php @@ -73,16 +73,17 @@ class Rbmark extends \Zotlabs\Web\Controller { false, '', $this->get_bookmark_folders(intval($channel_id)), + null, ]; return replace_macros(get_markup_template('rbmark.tpl'), array( '$header' => t('Save Bookmark'), - '$url' => array('url',t('URL of bookmark'),$_REQUEST['url']), - '$title' => array('title',t('Description'),$_REQUEST['title']), + '$url' => array('url',t('URL of bookmark'),$_REQUEST['url'], null, null, null), + '$title' => array('title',t('Description'),$_REQUEST['title'], null, null, null), '$ischat' => ((x($_REQUEST,'ischat')) ? intval($_REQUEST['ischat']) : 0), '$private' => ((x($_REQUEST,'private')) ? intval($_REQUEST['private']) : 0), '$submit' => t('Save'), - '$menu_name' => array('menu_name',t('Or enter new bookmark folder name'),'',''), + '$menu_name' => array('menu_name',t('Or enter new bookmark folder name'),'','', null, null), '$menus' => $menu_select )); } diff --git a/Zotlabs/Module/Setup.php b/Zotlabs/Module/Setup.php index 647415385..7ca3f827c 100644 --- a/Zotlabs/Module/Setup.php +++ b/Zotlabs/Module/Setup.php @@ -46,9 +46,9 @@ class Setup extends \Zotlabs\Web\Controller { } if(x($_POST, 'pass')) { - $this->install_wizard_pass = intval($_POST['pass']); + self::$install_wizard_pass = intval($_POST['pass']); } else { - $this->install_wizard_pass = 1; + self::$install_wizard_pass = 1; } } @@ -215,9 +215,11 @@ class Setup extends \Zotlabs\Web\Controller { $tpl = get_markup_template('install.tpl'); return replace_macros($tpl, array( '$title' => $install_title, + '$icon' => null, '$pass' => '', '$status' => t('Permission denied.'), '$text' => '', + '$what_next' => null, )); } } @@ -237,7 +239,7 @@ class Setup extends \Zotlabs\Web\Controller { )); } - switch ($this->install_wizard_pass){ + switch (self::$install_wizard_pass){ case 1: { // System check $checks = array(); diff --git a/Zotlabs/Render/SmartyInterface.php b/Zotlabs/Render/SmartyInterface.php index d80ea3f3a..64c6aa377 100644 --- a/Zotlabs/Render/SmartyInterface.php +++ b/Zotlabs/Render/SmartyInterface.php @@ -20,7 +20,7 @@ class SmartyInterface extends Smarty { $template_dirs = array('theme' => "view/theme/$thname/tpl/"); if ( x(App::$theme_info,"extends") ) { - $template_dirs = $template_dirs + array('extends' => "view/theme/" . \App::$theme_info["extends"] . "/tpl/"); + $template_dirs = $template_dirs + array('extends' => "view/theme/" . App::$theme_info["extends"] . "/tpl/"); } $template_dirs = $template_dirs + array('base' => 'view/tpl/'); $this->setTemplateDir($template_dirs); diff --git a/Zotlabs/Render/Theme.php b/Zotlabs/Render/Theme.php index 543bf7a3f..a42b65e03 100644 --- a/Zotlabs/Render/Theme.php +++ b/Zotlabs/Render/Theme.php @@ -24,7 +24,7 @@ class Theme { * * @return array */ - static public function current(){ + static public function current() { self::$system_theme = ((isset(App::$config['system']['theme'])) ? App::$config['system']['theme'] : ''); @@ -37,7 +37,7 @@ class Theme { if(App::$profile_uid) { $r = q("select channel_theme from channel where channel_id = %d limit 1", - intval(\App::$profile_uid) + intval(App::$profile_uid) ); if($r) { $page_theme = $r[0]['channel_theme']; @@ -46,7 +46,7 @@ class Theme { // Themes from Comanche layouts over-ride the channel theme - if(array_key_exists('theme', \App::$layout) && \App::$layout['theme']) { + if(array_key_exists('theme', App::$layout) && App::$layout['theme']) { $page_theme = App::$layout['theme']; } @@ -62,9 +62,11 @@ require_once('include/conversation.php'); require_once('include/acl_selectors.php'); require_once('include/selectors.php'); require_once('include/activities.php'); +require_once('include/security.php'); + define('PLATFORM_NAME', 'hubzilla'); -define('STD_VERSION', '9.1.3'); +define('STD_VERSION', '9.1.4'); define('ZOT_REVISION', '6.0'); define('DB_UPDATE_VERSION', 1263); @@ -1646,7 +1648,7 @@ function fix_system_urls($oldurl, $newurl) { } } - Zotlabs\Daemon\Master::Summon(['Notifier', 'refresh_all', $c[0]['channel_id']]); + Master::Summon(['Notifier', 'refresh_all', $c[0]['channel_id']]); } } @@ -1758,9 +1760,9 @@ function login($register = false, $form_id = 'main_login', $hiddens = false, $lo '$login' => t('Login'), '$remote_login' => t('Remote Authentication'), '$form_id' => $form_id, - '$lname' => [$form_id . '_username', $lname_label], - '$lpassword' => [$form_id . '_password', t('Password')], - '$remember_me' => [$form_id . '_remember', t('Remember me'), '', '', [t('No'), t('Yes')]], + '$lname' => [$form_id . '_username', $lname_label, null, null, null, null], + '$lpassword' => [$form_id . '_password', t('Password'), null, null, null, null], + '$remember_me' => [$form_id . '_remember', t('Remember me'), '', '', [t('No'), t('Yes')], null, null], '$hiddens' => $hiddens, '$register' => $reg, '$lostpass' => t('Forgot your password?'), @@ -2723,7 +2725,7 @@ function check_cron_broken() { $d = Config::Get('system', 'lastcron'); if ((!$d) || ($d < datetime_convert('UTC', 'UTC', 'now - 4 hours'))) { - Zotlabs\Daemon\Master::Summon(['Cron']); + Master::Summon(['Cron']); Config::Set('system', 'lastcron', datetime_convert()); } diff --git a/include/attach.php b/include/attach.php index 2283da1c6..449721793 100644 --- a/include/attach.php +++ b/include/attach.php @@ -113,7 +113,6 @@ function z_mime_content_type($filename) { 'odf' => 'application/vnd.oasis.opendocument.formula', 'odi' => 'application/vnd.oasis.opendocument.image', 'odm' => 'application/vnd.oasis.opendocument.text-master', - 'odb' => 'application/vnd.oasis.opendocument.base', 'odb' => 'application/vnd.oasis.opendocument.database', 'ott' => 'application/vnd.oasis.opendocument.text-template', 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', @@ -2467,8 +2466,8 @@ function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpat * @param int $channel_id * @param int $resource_id * @param string $new_folder_hash - * @param (optional) string $newname - * @param (optional) boolean $recurse + * @param string (optional) $newname + * @param boolean (optional) $recurse * @return array Associative array with: * * \e boolean \b success * * \e string \b resource_id @@ -2668,8 +2667,8 @@ function attach_move($channel_id, $resource_id, $new_folder_hash, $newname = '', * @param int $channel_id * @param int $resource_id * @param string $new_folder_hash - * @param (optional) string $newname - * @param (optional) boolean $recurse + * @param string (optional) $newname + * @param boolean (optional) $recurse * @return array Associative array with: * * \e boolean \b success * * \e string \b resource_id of the new resource diff --git a/include/auth.php b/include/auth.php index 0cd48bce3..1fc2cc556 100644 --- a/include/auth.php +++ b/include/auth.php @@ -176,6 +176,40 @@ function log_failed_login($errormsg) { @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $errormsg . PHP_EOL, FILE_APPEND); } + +/** + * Determines if checking for multifactor authentication needs to be checked. + * + * Checks that multi factor authentication is enabled for the given account_id, + * and whether it's already authenticated or not. + * + * Some modules needs to be excluded from the mfa checks for various reasons: + * + * - `totp_check` is used by the mfa module itself. + * - `dav` provides WebDAV access, and has no way of providing a mfa code. + * - `cdav` is accessed both via CardDAV which has the same limitations as + * the `dav` module, but may also be accessed via a web browser over http. + * We only exclude it if it's not being accessed via a web browser. + * + * @param int $account_id The id of the account we're verifying. + * @param string $module The requested module. + * @param string $arg The first arg passed to the module (or empty if none.) + * + * @return bool `true` if mfa status needs to be checked, `false` otherwise. + */ +function requires_mfa_check(int $account_id, string $module, string $arg): bool { + if (in_array($module, ['totp_check', 'dav'], true)) { + return false; + } + + if ($module === 'cdav' && !in_array($arg, ['addressbook', 'calendar'], true)) { + return false; + } + + $multiFactor = AConfig::Get($account_id, 'system', 'mfa_enabled'); + return $multiFactor && empty($_SESSION['2FA_VERIFIED']); +} + /** * Inline - not a function * look for auth parameters or re-validate an existing session @@ -267,8 +301,7 @@ if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) && $login_refresh = true; } - $multiFactor = AConfig::Get(App::$account['account_id'], 'system', 'mfa_enabled'); - if ($multiFactor && empty($_SESSION['2FA_VERIFIED']) && App::$module !== 'totp_check') { + if (requires_mfa_check(App::$account['account_id'], App::$module, argv(1))) { $o = new Totp_check; echo $o->get(); killme(); diff --git a/include/config.php b/include/config.php index 674d5afe4..4dd40eccf 100644 --- a/include/config.php +++ b/include/config.php @@ -120,8 +120,8 @@ function get_pconfig($uid, $family, $key, $default = false) { return Zlib\PConfig::Get($uid,$family,$key,$default); } -function set_pconfig($uid, $family, $key, $value) { - return Zlib\PConfig::Set($uid,$family,$key,$value); +function set_pconfig($uid, $family, $key, $value, $updated = NULL) { + return Zlib\PConfig::Set($uid, $family, $key, $value, $updated); } function del_pconfig($uid, $family, $key, $updated = NULL) { diff --git a/include/event.php b/include/event.php index ef73fc9b5..f8511cbe2 100644 --- a/include/event.php +++ b/include/event.php @@ -272,9 +272,9 @@ function format_event_ical($ev) { if($ev['adjust']) { if($ev['dtstart']) - $o .= "\r\nDTSTART$tzid:" . datetime_convert($tz,'UTC', $ev['dtstart'],'Ymd\\THis\\Z'); + $o .= "\r\nDTSTART$tzid:" . datetime_convert('UTC', $tz, $ev['dtstart'],'Ymd\\THis'); if($ev['dtend'] && ! $ev['nofinish']) - $o .= "\r\nDTEND$tzid:" . datetime_convert($tz,'UTC', $ev['dtend'],'Ymd\\THis\\Z'); + $o .= "\r\nDTEND$tzid:" . datetime_convert('UTC', $tz, $ev['dtend'],'Ymd\\THis'); } else { if($ev['dtstart']) @@ -797,7 +797,7 @@ function parse_event_object($event_object_json) { $tz = $object['timezone'] ?? 'UTC'; $ev['summary'] = $object['summary'] ?? $object['name'] ?? ''; - $ev['description'] = html2bbcode($content['content']) ?? ''; + $ev['description'] = html2bbcode($object['content']) ?? ''; $ev['dtstart'] = $object['startTime'] ? datetime_convert('UTC', 'UTC', $object['startTime']) : ''; $ev['dtend'] = $object['endTime'] ? datetime_convert('UTC', 'UTC', $object['endTime']) : $ev['dtstart']; $ev['location'] = $object['location']['name'] ?? ''; diff --git a/include/html2plain.php b/include/html2plain.php index 88dce577d..69fb5193a 100644 --- a/include/html2plain.php +++ b/include/html2plain.php @@ -196,7 +196,7 @@ function html2plain($html, $wraplength = 75, $compact = false) // Problem: there is no reliable way to detect if it is a link to a tag or profile //node2bbcode($doc, 'a', array('href'=>'/(.+)/'), ' $1 ', '', true); - node2bbcode($doc, 'a', array('href'=>'/(.+)/', 'rel'=>'oembed'), ' $1 ', '', true); + node2bbcode($doc, 'a', array('href'=>'/(.+)/', 'rel'=>'oembed'), ' $1 ', ''); //node2bbcode($doc, 'img', array('alt'=>'/(.+)/'), '$1', ''); //node2bbcode($doc, 'img', array('title'=>'/(.+)/'), '$1', ''); //node2bbcode($doc, 'img', array(), '', ''); @@ -205,7 +205,7 @@ function html2plain($html, $wraplength = 75, $compact = false) else node2bbcode($doc, 'img', array('src'=>'/(.+)/'), '', ''); - node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), ' $1 ', '', true); + node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), ' $1 ', ''); $message = $doc->saveHTML(); diff --git a/include/language.php b/include/language.php index 22f350aac..538f67d90 100644 --- a/include/language.php +++ b/include/language.php @@ -198,10 +198,10 @@ function load_translation_table($lang, $install = false) { * * @param string $s string that should get translated * @param string $ctx (optional) context to appear in po file - * @return translated string if exists, otherwise return $s + * @return string translated string if exists, otherwise return $s * */ -function t($s, $ctx = '') { +function t($s, $ctx = ''): string { $cs = $ctx ? '__ctx:' . $ctx . '__ ' . $s : $s; if (x(App::$strings, $cs)) { diff --git a/include/menu.php b/include/menu.php index 1f65f987d..2fcf88bca 100644 --- a/include/menu.php +++ b/include/menu.php @@ -166,7 +166,8 @@ function menu_create($arr) { if($r) return false; - $t = datetime_convert(); + $time_created = datetime_convert('UTC', 'UTC', empty($arr['menu_created']) ? 'now' : $arr['menu_created']); + $time_edited = empty($arr['menu_edited']) ? $time_created : datetime_convert('UTC', 'UTC', $arr['menu_edited']); $r = q("insert into menu ( menu_name, menu_desc, menu_flags, menu_channel_id, menu_created, menu_edited ) values( '%s', '%s', %d, %d, '%s', '%s' )", @@ -174,8 +175,8 @@ function menu_create($arr) { dbesc($menu_desc), intval($menu_flags), intval($menu_channel_id), - dbesc(datetime_convert('UTC','UTC',(($arr['menu_created']) ? $arr['menu_created'] : $t))), - dbesc(datetime_convert('UTC','UTC',(($arr['menu_edited']) ? $arr['menu_edited'] : $t))) + dbesc($time_created), + dbesc($time_edited) ); if(! $r) return false; diff --git a/include/network.php b/include/network.php index a7a11ff6e..bb5bc1ce7 100644 --- a/include/network.php +++ b/include/network.php @@ -116,7 +116,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { } else { $curl_time = intval(@Config::Get('system','curl_timeout')); - @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); + @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== 0) ? $curl_time : 60)); } if(x($opts,'connecttimeout') && intval($opts['connecttimeout'])) { @@ -124,7 +124,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { } else { $curl_contime = intval(@Config::Get('system','curl_connecttimeout')); - @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (($curl_contime !== false) ? $curl_contime : 30)); + @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (($curl_contime !== 0) ? $curl_contime : 30)); } if(x($opts,'http_auth')) { @@ -298,7 +298,7 @@ function z_post_url($url, $params, $redirects = 0, $opts = array()) { } else { $curl_time = intval(@Config::Get('system','curl_timeout')); - @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); + @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== 0) ? $curl_time : 60)); } if(x($opts,'http_auth')) { @@ -2146,9 +2146,9 @@ function get_request_string($url) { * * @param array $parsed_url An associative array as produced by `parse_url`. * - * @return The reassembled URL as a string. + * @return string The reassembled URL as a string. */ -function unparse_url($parsed_url) { +function unparse_url(array $parsed_url): string { $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; $host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; @@ -2158,5 +2158,5 @@ function unparse_url($parsed_url) { $path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; - return "$scheme$user$pass$host$port$path$query$fragment"; + return $scheme . $user . $pass . $host . $port . $path . $query . $fragment; } diff --git a/tests/unit/UnitTestCase.php b/tests/unit/UnitTestCase.php index 844746a51..afc309205 100644 --- a/tests/unit/UnitTestCase.php +++ b/tests/unit/UnitTestCase.php @@ -31,6 +31,7 @@ use PHPUnit\Framework\TestCase; */ require_once __DIR__ . '/../../boot.php'; require_once 'include/dba/dba_driver.php' ; +require_once 'include/dba/dba_transaction.php'; /** * Base class for our Unit Tests. diff --git a/tests/unit/includes/AuthTest.php b/tests/unit/includes/AuthTest.php new file mode 100644 index 000000000..fa9726fe8 --- /dev/null +++ b/tests/unit/includes/AuthTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Tests for the authentication code used in Hubzilla. + * + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * SPDX-FileContributor: Harald Eilertsen + * + * SPDX-License-Identifier: MIT + */ + +namespace Zotlabs\Tests\Unit\Includes; + +use App; +use Zotlabs\Lib\AConfig; +use Zotlabs\Tests\Unit\UnitTestCase; +use PHPUnit\Framework\Attributes\{DataProvider, RunTestsInSeparateProcesses}; + +/** + * Test class containing the test for the Hubzilla authentication code. + * + * Since the main authentication code is executed in the global scope on + * inclusion of the `includes/auth.php` file, we need to run each test in a + * separate process to make sure we can excersize the code as we need. + */ +#[RunTestsInSeparateProcesses] +class AuthTest extends UnitTestCase { + + /** + * Check that mfa status is not checked for certain modules. + * + * This causes issues with things like WebDAV and CardDAV, as there's + * currently no way for these modules to signal that a TOTP code is needed + * back to the connecting client. + */ + #[DataProvider('modules_excluded_from_mfa')] + public function test_mfa_is_not_checked_for_excluded_modules(string $module, array $args): void { + $account_id = $this->fixtures['account']['0']['account_id']; + + $_SESSION = [ + 'authenticated' => true, + 'account_id' => $account_id, + + // Trick the code to not warn that $_SESSION['uid'] is not set, + // but also not trigger the code that tries to change to the + // given channel. *Remove when code is fixed!* + 'uid' => 0, + ]; + + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + + App::$session = $this->create_session_stub(); + App::$module = $module; + App::$argv = $args; + App::$argc = count($args); + + // Enable multi factor authentication for this account + AConfig::Set($account_id, 'system', 'mfa_enabled', true); + + require 'include/auth.php'; + + $this->assertEquals(1, $_SESSION['authenticated']); + } + + /** + * Data provider for testing modules excluded from mfa + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) + */ + public static function modules_excluded_from_mfa(): array { + return [ + ['totp_check', []], + ['cdav', []], + ['cdav', ['calendar']], + ['cdav', ['addressbook']], + ['dav', []], + ]; + } + + private function create_session_stub(): \Zotlabs\Web\Session { + return $this->createStub('Zotlabs\Web\Session'); + } +} diff --git a/tests/unit/includes/dba/_files/account.yml b/tests/unit/includes/dba/_files/account.yml index 344bdb799..88e59056e 100644 --- a/tests/unit/includes/dba/_files/account.yml +++ b/tests/unit/includes/dba/_files/account.yml @@ -3,7 +3,9 @@ account: account_id: 42 account_email: "hubzilla@example.com" account_language: "no" + account_flags: 0 - account_id: 43 account_email: "hubzilla@example.org" account_language: "de" + account_flags: 1 diff --git a/view/tpl/cal_event.tpl b/view/tpl/cal_event.tpl index d7662786b..0719d5262 100644 --- a/view/tpl/cal_event.tpl +++ b/view/tpl/cal_event.tpl @@ -6,7 +6,7 @@ </div> {{$event.html}} <div class="event-buttons"> - {{if $event.item.plink}}<a href="{{$event.plink.0}}" title="{{$event.plink.1}}" class="plink-event-link"><i class="fa fa-external-link btn btn-outline-secondary" ></i></a>{{/if}} + {{if $event.item.plink}}<a href="{{$event.plink.0}}" title="{{$event.plink.1}}" class="btn btn-sm btn-outline-secondary border-0 plink-event-link"><i class="fa fa-external-link"></i></a>{{/if}} </div> <div class="clear"></div> </div> diff --git a/view/tpl/event.tpl b/view/tpl/event.tpl index cc0bfc1c7..91dc53421 100644 --- a/view/tpl/event.tpl +++ b/view/tpl/event.tpl @@ -6,9 +6,9 @@ </div> {{$event.html}} <div class="event-buttons"> - {{if $event.item.plink}}<a href="{{$event.plink.0}}" title="{{$event.plink.1}}" class="plink-event-link"><i class="fa fa-external-link btn btn-outline-secondary" ></i></a>{{/if}} - {{if $event.edit}}<a href="{{$event.edit.0}}" title="{{$event.edit.1}}" class="edit-event-link"><i class="fa fa-pencil btn btn-outline-secondary"></i></a>{{/if}} - {{if $event.drop}}<a href="{{$event.drop.0}}" title="{{$event.drop.1}}" class="drop-event-link"><i class="fa fa-trash-o btn btn-outline-secondary"></i></a>{{/if}} + {{if $event.item.plink}}<a href="{{$event.plink.0}}" title="{{$event.plink.1}}" class="btn btn-sm btn-outline-secondary border-0 plink-event-link"><i class="fa fa-external-link" ></i></a>{{/if}} + {{if $event.edit}}<a href="{{$event.edit.0}}" title="{{$event.edit.1}}" class="btn btn-sm btn-outline-secondary border-0 edit-event-link"><i class="fa fa-pencil"></i></a>{{/if}} + {{if $event.drop}}<a href="{{$event.drop.0}}" title="{{$event.drop.1}}" class="btn btn-sm btn-outline-secondary border-0 drop-event-link"><i class="fa fa-trash-o"></i></a>{{/if}} </div> <div class="clear"></div> </div> diff --git a/view/tpl/install.tpl b/view/tpl/install.tpl index 5ab722b12..56faa0527 100644 --- a/view/tpl/install.tpl +++ b/view/tpl/install.tpl @@ -2,7 +2,9 @@ <div class="jumbotron"> <h1>{{$title}}</h1> <hr class="my-4"> + {{if $icon}} <h2><i class="fa fa-{{$icon}}"></i> {{$pass}}</h2> + {{/if}} </div> {{if $status}} @@ -11,5 +13,5 @@ <div class="alert alert-info">{{$text}}</div> <br> - {{$what_next}} + {{if $what_next}}{{$what_next}}{{/if}} </div> |