From feda23587c7e24dec0cfc8be9f2f444e009f967f Mon Sep 17 00:00:00 2001 From: zotlabs Date: Tue, 13 Aug 2019 16:53:04 -0700 Subject: [PATCH 001/136] illegal offset warning (prevents encrypted signatures from being used for encrypted messages). Not fatal but can leak metadata. --- include/zot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/zot.php b/include/zot.php index 53c3d4d86..60bada1d6 100644 --- a/include/zot.php +++ b/include/zot.php @@ -304,7 +304,7 @@ function zot_zot($url, $data, $channel = null,$crypto = null) { if($channel) { $headers['X-Zot-Token'] = random_string(); $headers['X-Zot-Digest'] = \Zotlabs\Web\HTTPSig::generate_digest_header($data); - $h = \Zotlabs\Web\HTTPSig::create_sig($headers,$channel['channel_prvkey'],'acct:' . channel_reddress($channel),false,'sha512',(($crypto) ? $crypto['hubloc_sitekey'] : ''), (($crypto) ? zot_best_algorithm($crypto['site_crypto']) : '')); + $h = \Zotlabs\Web\HTTPSig::create_sig($headers,$channel['channel_prvkey'],'acct:' . channel_reddress($channel),false,'sha512',(($crypto) ? [ 'key' => $crypto['hubloc_sitekey'], 'algorithm' => $crypto['site_crypto'] ] : false)); } $redirects = 0; From 9a2fbdde200eae4e9ff8163cfa0c47e1111966b6 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Wed, 14 Aug 2019 17:55:56 -0700 Subject: [PATCH 002/136] zot6 compatibility: when posting from a non-primary clone the actor->id is that of the primary, resulting in an author/owner identity mismatch. Solution is to always post with the actor->id set to the sender if it is a local channel. --- Zotlabs/Lib/Activity.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index f86dc1604..5f5f74ca9 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -668,8 +668,24 @@ class Activity { } $ret = []; + $c = ((array_key_exists('channel_id',$p)) ? $p : channelx_by_hash($p['xchan_hash'])); + $ret['type'] = 'Person'; - $ret['id'] = $p['xchan_url']; + + if ($c) { + $role = get_pconfig($c['channel_id'],'system','permissions_role'); + if (strpos($role,'forum') !== false) { + $ret['type'] = 'Group'; + } + } + + if ($c) { + $ret['id'] = channel_url($c); + } + else { + $ret['id'] = ((strpos($p['xchan_hash'],'http') === 0) ? $p['xchan_hash'] : $p['xchan_url']); + } + if($p['xchan_addr'] && strpos($p['xchan_addr'],'@')) $ret['preferredUsername'] = substr($p['xchan_addr'],0,strpos($p['xchan_addr'],'@')); $ret['name'] = $p['xchan_name']; From beeafc6bc5eb1d564a60986bbc2de3f7b06bfdc1 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Thu, 15 Aug 2019 16:28:06 -0700 Subject: [PATCH 003/136] fix bitrot in util/zotsh --- Zotlabs/Module/Admin/Addons.php | 31 +- Zotlabs/Module/Cloud.php | 7 - Zotlabs/Module/Dav.php | 2 + Zotlabs/Storage/Directory.php | 9 +- util/zotsh/README.txt | 7 + util/zotsh/easywebdav/__init__.pyc | Bin 457 -> 483 bytes util/zotsh/easywebdav/__version__.pyc | Bin 174 -> 187 bytes util/zotsh/easywebdav/client.py | 404 ++++++++-------- util/zotsh/easywebdav/client.pyc | Bin 8871 -> 9211 bytes util/zotsh/zotsh.py | 637 +++++++++++++------------- 10 files changed, 546 insertions(+), 551 deletions(-) diff --git a/Zotlabs/Module/Admin/Addons.php b/Zotlabs/Module/Admin/Addons.php index b8e3e3a2e..243eb242f 100644 --- a/Zotlabs/Module/Admin/Addons.php +++ b/Zotlabs/Module/Admin/Addons.php @@ -2,6 +2,7 @@ namespace Zotlabs\Module\Admin; +use App; use \Zotlabs\Storage\GitRepo; use \Michelf\MarkdownExtra; @@ -253,14 +254,14 @@ class Addons { * Single plugin */ - if (\App::$argc == 3){ - $plugin = \App::$argv[2]; + if (App::$argc == 3){ + $plugin = App::$argv[2]; if (!is_file("addon/$plugin/$plugin.php")){ notice( t("Item not found.") ); return ''; } - $enabled = in_array($plugin,\App::$plugins); + $enabled = in_array($plugin,App::$plugins); $info = get_plugin_info($plugin); $x = check_plugin_versions($info); @@ -268,11 +269,11 @@ class Addons { if($enabled && ! $x) { $enabled = false; - $idz = array_search($plugin, \App::$plugins); + $idz = array_search($plugin, App::$plugins); if ($idz !== false) { - unset(\App::$plugins[$idz]); + unset(App::$plugins[$idz]); uninstall_plugin($plugin); - set_config("system","addon", implode(", ",\App::$plugins)); + set_config("system","addon", implode(", ",App::$plugins)); } } $info['disabled'] = 1-intval($x); @@ -281,19 +282,19 @@ class Addons { check_form_security_token_redirectOnErr('/admin/addons', 'admin_addons', 't'); $pinstalled = false; // Toggle plugin status - $idx = array_search($plugin, \App::$plugins); + $idx = array_search($plugin, App::$plugins); if ($idx !== false){ - unset(\App::$plugins[$idx]); + unset(App::$plugins[$idx]); uninstall_plugin($plugin); $pinstalled = false; info( sprintf( t("Plugin %s disabled."), $plugin ) ); } else { - \App::$plugins[] = $plugin; + App::$plugins[] = $plugin; install_plugin($plugin); $pinstalled = true; info( sprintf( t("Plugin %s enabled."), $plugin ) ); } - set_config("system","addon", implode(", ",\App::$plugins)); + set_config("system","addon", implode(", ",App::$plugins)); if($pinstalled) { @require_once("addon/$plugin/$plugin.php"); @@ -305,7 +306,7 @@ class Addons { // display plugin details - if (in_array($plugin, \App::$plugins)){ + if (in_array($plugin, App::$plugins)){ $status = 'on'; $action = t('Disable'); } else { @@ -380,18 +381,18 @@ class Addons { list($tmp, $id) = array_map('trim', explode('/', $file)); $info = get_plugin_info($id); - $enabled = in_array($id,\App::$plugins); + $enabled = in_array($id,App::$plugins); $x = check_plugin_versions($info); // disable plugins which are installed but incompatible versions if($enabled && ! $x) { $enabled = false; - $idz = array_search($id, \App::$plugins); + $idz = array_search($id, App::$plugins); if ($idz !== false) { - unset(\App::$plugins[$idz]); + unset(App::$plugins[$idz]); uninstall_plugin($id); - set_config("system","addon", implode(", ",\App::$plugins)); + set_config("system","addon", implode(", ",App::$plugins)); } } $info['disabled'] = 1-intval($x); diff --git a/Zotlabs/Module/Cloud.php b/Zotlabs/Module/Cloud.php index 1b330ecba..f595e0fac 100644 --- a/Zotlabs/Module/Cloud.php +++ b/Zotlabs/Module/Cloud.php @@ -35,13 +35,6 @@ class Cloud extends \Zotlabs\Web\Controller { if (argc() > 1) $which = argv(1); - - if (argc() < 2 && intval(get_config('system','cloud_disable_siteroot'))) { - notice( t('Permission denied.') . EOL); - construct_page(); - killme(); - } - $profile = 0; if ($which) diff --git a/Zotlabs/Module/Dav.php b/Zotlabs/Module/Dav.php index 866520461..e8ce6a703 100644 --- a/Zotlabs/Module/Dav.php +++ b/Zotlabs/Module/Dav.php @@ -95,6 +95,8 @@ class Dav extends \Zotlabs\Web\Controller { $auth = new \Zotlabs\Storage\BasicAuth(); + $auth->observer = get_observer_hash(); + $auth->setRealm(ucfirst(\Zotlabs\Lib\System::get_platform_name()) . ' ' . 'WebDAV'); $rootDirectory = new \Zotlabs\Storage\Directory('/', $auth); diff --git a/Zotlabs/Storage/Directory.php b/Zotlabs/Storage/Directory.php index b30aecf92..ae36fc1c0 100644 --- a/Zotlabs/Storage/Directory.php +++ b/Zotlabs/Storage/Directory.php @@ -720,7 +720,11 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo * @return array Directory[] */ function ChannelList(&$auth) { - $ret = array(); + $ret = []; + + if (intval(get_config('system','cloud_disable_siteroot'))) { + return $ret; + } $r = q("SELECT channel_id, channel_address, profile.publish FROM channel left join profile on profile.uid = channel.channel_id WHERE channel_removed = 0 AND channel_system = 0 AND (channel_pageflags & %d) = 0", intval(PAGE_HIDDEN) @@ -730,8 +734,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo foreach ($r as $rr) { if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage') && $rr['publish']) { logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA); - // @todo can't we drop '/cloud'? It gets stripped off anyway in RedDirectory - $ret[] = new Directory('/cloud/' . $rr['channel_address'], $auth); + $ret[] = new Directory($rr['channel_address'], $auth); } } } diff --git a/util/zotsh/README.txt b/util/zotsh/README.txt index facddc850..58e8d4bd0 100644 --- a/util/zotsh/README.txt +++ b/util/zotsh/README.txt @@ -16,6 +16,13 @@ Extract somewere and launch zotsh.py Description ----------- +Update: 2019-08-14 + +Have just looked at this after several years of bitrot and made some updates. +it functions for cli DAV access on your assigned hub, but magic-auth to dav repos on other hubs +(e.g. the host command) needs to be updated to work with openwebauth. + +---- ZotSH is a command line WebDAV client for Hubzilla. It knows how to magic-auth to remote hubs using Zot. diff --git a/util/zotsh/easywebdav/__init__.pyc b/util/zotsh/easywebdav/__init__.pyc index 4f30c1871f17d184e480c6fd40eb6cb0738066c5..b69303d1b870203cd641ec090fbfc91d88cd18de 100644 GIT binary patch delta 100 zcmX@f{Fs@8`7m1XAXSLwmoddc~@llvK! MC)+aG5UJJ*0NUIlyZ`_I delta 74 zcmaFNe3F@i`74GAd8@Vzj|6 GZ3O^t+865p diff --git a/util/zotsh/easywebdav/__version__.pyc b/util/zotsh/easywebdav/__version__.pyc index b19bf50b046e332e539cb36f14570542c14b236c..bdc53441feff1538b1ccbad1f20c6b4fade0f37f 100644 GIT binary patch delta 55 zcmZ3-xSNrK`7y7`7y$a^%!cH8M2rdQWzMrIT(tJCcCkeVAr|%0*f&tcB#o1 zSQIx~u`R*rUKWMPX~KM)l{k;#G(l2v5aB3*1t5O!=1Mx3%KoQt{S9Cs-dPYUn$w$QBp(vUB zO`;M>0O;1ulAlopUP-M-6}T(?A5}m>_9%+LW=*+u$o2wFl~y>6q(pHooU{3&;%a2I KKyMsSwgvz~tcha) diff --git a/util/zotsh/zotsh.py b/util/zotsh/zotsh.py index 36506b39d..89865fcbe 100755 --- a/util/zotsh/zotsh.py +++ b/util/zotsh/zotsh.py @@ -1,324 +1,313 @@ -#!/usr/bin/env python2 -import sys, os -import ConfigParser -import requests -from requests.auth import HTTPBasicAuth -import easywebdav -import easywebdav.__version__ as easywebdavversion - -__version__= "0.0.2" - -SERVER = None -USER = None -PASSWD = None -VERIFY_SSL=True - -##################################################### - -class CommandNotFound(Exception): - pass - -class ZotSH(object): - commands = ['cd','ls','exists','mkdir','mkdirs','rmdir','delete','upload','download', - 'host', 'pwd','cat', - 'lcd','lpwd', 'lls', - 'quit', 'help'] - def __init__(self, host, session=None, davclient=None): - self.sessions = {} - self.host = host - self.session = session - self.davclient = davclient - - - @property - def host(self): - return self._host - - @host.setter - def host(self, host): - self._host = host - self._hostname = host.replace("https:","").replace("/","") - - @property - def hostname(self): - return self._hostname - - @hostname.setter - def hostname(self, hostname): - self._host = "https://%s/" % (hostname) - self._hostname = hostname - - @property - def session(self): - return self._session - - @session.setter - def session(self, session): - self._session = session - self.davclient = easywebdav.connect( self.hostname, protocol='https', session=session, path="cloud", verify_ssl=VERIFY_SSL) - - @property - def PS1(self): - if self.davclient is None: - return "[!]> " - return "%s:%s> " % (self.hostname, self.davclient.cwd) - - def get_host_session(self, host=None): - #~ if host is None: - #~ host = self.host - #~ if not host.startswith("https"): - #~ host = "https://%s/" % (host) - #~ if host in self.sessions: - #~ session = self.sessions[host] - #~ else: - #~ session = requests.Session() - #~ self.sessions[host] = session - #~ if not host == SERVER - #~ session.params.update({'davguest':1}) - #~ return session - - if self.session is None: - session = requests.Session() - #session.params.update({'davguest':1}) - else: - session = self.session - session.params.update({'davguest': (not host == SERVER) }) - return session - - def do(self, command, *args): - if not command in self.commands: - raise CommandNotFound("Unknow command '%s'" % command) - - cmd = getattr(self, "cmd_%s"%command, None) - if cmd is None: - cmd = getattr(self.davclient, command) - - return cmd(*args) - - def cmd_exists(self, *args): - if (len(args)==0): - return - return self.davclient.exists(args[0]) - - def cmd_mkdir(self, *args): - if (len(args)==0): - return - return self.davclient.mkdir(args[0]) - - def cmd_mkdirs(self, *args): - if (len(args)==0): - return - return self.davclient.mkdirs(args[0]) - - def cmd_rmdir(self, *args): - if (len(args)==0): - return - return self.davclient.rmdir(args[0]) - - def cmd_delete(self, *args): - if (len(args)==0): - return - return self.davclient.delete(args[0]) - - def cmd_upload(self, *args): - if (len(args)==0): - return - args = list(args) - if (len(args)==1): - args.append(args[0]) - - return self.davclient.upload(args[0], args[1]) - - def cmd_download(self, *args): - if (len(args)==0): - return - args = list(args) - if (len(args)==1): - args.append(args[0]) - - return self.davclient.download(args[0], args[1]) - - def cmd_host(self, *args): - if (len(args)==0): - return - newhostname = args[0] - newhost = "https://%s/" % newhostname - if newhostname == "~" or newhost == SERVER: - # bach to home server - self.host = SERVER - self.session = self.get_host_session(SERVER) - return - - session_remote = self.get_host_session(newhost) - session_home = self.get_host_session(SERVER) - - # call /magic on SERVER - r = session_home.get( - SERVER + "magic", - params={'dest': newhost}, - allow_redirects=False, - verify=VERIFY_SSL ) - - if not 'location' in r.headers: - raise Exception("Cannot start magic auth to '%s'" % newhostname) - auth_url = r.headers['location'] - - - # call auth_url with "test" param - - r = session_remote.get( - auth_url, - params={'test': 1 }, - verify=VERIFY_SSL ) - - if r.json()['success']: - self.hostname = newhostname - self.session = session_remote - else: - raise Exception("Cannot magic auth to '%s'" % newhostname) - - - def cmd_pwd(self, *args): - return "%s%s" % ( self.davclient.baseurl, self.davclient.cwd ) - - def cmd_ls(self, *args): - extra_args = ["-a", "-l", "-d"] - - show_hidden = "-a" in args - show_list = "-l" in args - show_only_dir = "-d" in args - args = [ a for a in args if not a in extra_args ] - - - r = self.davclient.ls(*args) - l = max([ len(str(f.size)) for f in r ] + [7,]) - - def _fmt(type, size, name): - if show_list: - return "%s %*d %s" % (type, l, f.size , name) - else: - return name - - if show_hidden : - print _fmt('d', 0, "./") - if self.davclient.cwd!="/": - print _fmt('d', 0, "../") - - for f in r: - name = f.name.replace("/cloud"+self.davclient.cwd,"") - type = "-" - if name.endswith("/"): - type = "d" - if name!="": - if show_hidden or not name.startswith("."): - if not show_only_dir or type=="d": - print _fmt(type, f.size , name) - - def cmd_lpwd(self, *args): - return os.getcwd() - - def cmd_lcd(self, *args): - if (len(args)==0): - return - os.chdir(args[0]) - - def cmd_lls(self, *args): - for f in os.listdir(os.getcwd()): - if os.path.isdir(f): - f=f+"/" - print f - - def cmd_help(self, *args): - print "ZotSH",__version__ - print - print "Commands:" - for c in self.commands: - print "\t",c - print - print "easywebdav", easywebdavversion.__version__, "(mod)" - print "requests", requests.__version__ - - def cmd_cat(self,*args): - if (len(args)==0): - return - rfile = args[0] - resp = self.davclient._send('GET', rfile, (200,)) - print resp.text - -def load_conf(): - global SERVER,USER,PASSWD,VERIFY_SSL - homedir = os.getenv("HOME") - if homedir is None: - homedir = os.path.join(os.getenv("HOMEDRIVE"), os.getenv("HOMEPATH")) - - optsfile = ".zotshrc" - if not os.path.isfile(optsfile): - optsfile = os.path.join(homedir, ".zotshrc") - - if not os.path.isfile(optsfile): - print "Please create a configuration file called '.zotshrc':" - print "[zotsh]" - print "host = https://yourhost.com/" - print "username = your_username" - print "password = your_password" - sys.exit(-1) - - config = ConfigParser.ConfigParser() - config.read(optsfile) - SERVER = config.get('zotsh', 'host') - USER = config.get('zotsh', 'username') - PASSWD = config.get('zotsh', 'password') - if config.has_option('zotsh', 'verify_ssl'): - VERIFY_SSL = config.getboolean('zotsh', 'verify_ssl') - - -def zotsh(): - - zotsh = ZotSH( SERVER) - - session_home = zotsh.get_host_session() - - #~ #login on home server - print "loggin in..." - r = session_home.get( - SERVER + "api/account/verify_credentials", - auth=HTTPBasicAuth(USER, PASSWD), - verify=VERIFY_SSL ) - - print "Hi", r.json()['name'] - - zotsh.session = session_home - - # command loop - input = raw_input(zotsh.PS1) - while (input != "quit"): - input = input.strip() - if len(input)>0: - toks = [ x.strip() for x in input.split(" ") ] - - command = toks[0] - args = toks[1:] - try: - ret = zotsh.do(command, *args) - except easywebdav.client.OperationFailed, e: - print e - except CommandNotFound, e: - print e - else: - if ret is not None: - print ret - - - input = raw_input(zotsh.PS1) - - - - -if __name__=="__main__": - load_conf() - zotsh() - sys.exit() - - - - +#!/usr/bin/env python + +import sys, os +import ConfigParser +import requests +from requests.auth import HTTPBasicAuth +import easywebdav +import easywebdav.__version__ as easywebdavversion +import base64 + +__version__= "0.0.2" + +SERVER = None +USER = None +PASSWD = None +VERIFY_SSL=True + +##################################################### + +class CommandNotFound(Exception): + pass + +class ZotSH(object): + commands = ['cd','ls','exists','mkdir','mkdirs','rmdir','delete','upload','download', + 'host', 'pwd','cat', + 'lcd','lpwd', 'lls', + 'quit', 'help'] + def __init__(self, host, session=None, davclient=None): + self.sessions = {} + self.host = host + self.session = session + self.davclient = davclient + + + @property + def host(self): + return self._host + + @host.setter + def host(self, host): + self._host = host + self._hostname = host.replace("https:","").replace("/","") + + @property + def hostname(self): + return self._hostname + + @hostname.setter + def hostname(self, hostname): + self._host = "https://%s/" % (hostname) + self._hostname = hostname + + @property + def session(self): + return self._session + + @session.setter + def session(self, session): + self._session = session + self.davclient = easywebdav.connect( self.hostname, protocol='https', session=session, path="dav", verify_ssl=VERIFY_SSL) + + @property + def PS1(self): + if self.davclient is None: + return "[!]> " + return "%s:%s> " % (self.hostname, self.davclient.cwd) + + def get_host_session(self, host=None): + #~ if host is None: + #~ host = self.host + #~ if not host.startswith("https"): + #~ host = "https://%s/" % (host) + #~ if host in self.sessions: + #~ session = self.sessions[host] + #~ else: + #~ session = requests.Session() + #~ self.sessions[host] = session + #~ if not host == SERVER + #~ session.params.update({'davguest':1}) + #~ return session + + if self.session is None: + session = requests.Session() + #session.params.update({'davguest':1}) + else: + session = self.session + #session.params.update({'davguest': (not host == SERVER) }) + return session + + def do(self, command, *args): + if not command in self.commands: + raise CommandNotFound("Unknown command '%s'" % command) + + cmd = getattr(self, "cmd_%s"%command, None) + if cmd is None: + cmd = getattr(self.davclient, command) + + return cmd(*args) + + def cmd_exists(self, *args): + if (len(args)==0): + return + return self.davclient.exists(args[0]) + + def cmd_mkdir(self, *args): + if (len(args)==0): + return + return self.davclient.mkdir(args[0]) + + def cmd_mkdirs(self, *args): + if (len(args)==0): + return + return self.davclient.mkdirs(args[0]) + + def cmd_rmdir(self, *args): + if (len(args)==0): + return + return self.davclient.rmdir(args[0]) + + def cmd_delete(self, *args): + if (len(args)==0): + return + return self.davclient.delete(args[0]) + + def cmd_upload(self, *args): + if (len(args)==0): + return + args = list(args) + if (len(args)==1): + args.append(args[0]) + + return self.davclient.upload(args[0], args[1]) + + def cmd_download(self, *args): + if (len(args)==0): + return + args = list(args) + if (len(args)==1): + args.append(args[0]) + + return self.davclient.download(args[0], args[1]) + + def cmd_host(self, *args): + if (len(args)==0): + return + newhostname = args[0] + newhost = "https://%s/" % newhostname + if newhostname == "~" or newhost == SERVER: + # bach to home server + self.host = SERVER + self.session = self.get_host_session(SERVER) + return + + session_remote = self.get_host_session(newhost) + session_home = self.get_host_session(SERVER) + + bnewhost = newhost + 'dav' + bnewhost = bnewhost.encode('hex') + + r = session_home.get( + SERVER + "magic", + params={'bdest': bnewhost, 'owa': 1}, + allow_redirects=True, + verify=VERIFY_SSL ) + + self.hostname = newhostname + self.session = session_remote + + + def cmd_pwd(self, *args): + return "%s%s" % ( self.davclient.baseurl, self.davclient.cwd ) + + def cmd_ls(self, *args): + extra_args = ["-a", "-l", "-d"] + + show_hidden = "-a" in args + show_list = "-l" in args + show_only_dir = "-d" in args + args = [ a for a in args if not a in extra_args ] + + + r = self.davclient.ls(*args) + l = max([ len(str(f.size)) for f in r ] + [7,]) + + def _fmt(type, size, name): + if show_list: + return "%s %*d %s" % (type, l, f.size , name) + else: + return name + + if show_hidden : + print _fmt('d', 0, "./") + if self.davclient.cwd!="/": + print _fmt('d', 0, "../") + + for f in r: + name = f.name.replace("/dav"+self.davclient.cwd,"") + type = "-" + if name.endswith("/"): + type = "d" + if name!="": + if show_hidden or not name.startswith("."): + if not show_only_dir or type=="d": + print _fmt(type, f.size , name) + + def cmd_lpwd(self, *args): + return os.getcwd() + + def cmd_lcd(self, *args): + if (len(args)==0): + return + os.chdir(args[0]) + + def cmd_lls(self, *args): + for f in os.listdir(os.getcwd()): + if os.path.isdir(f): + f=f+"/" + print f + + def cmd_help(self, *args): + print "ZotSH",__version__ + print + print "Commands:" + for c in self.commands: + print "\t",c + print + print "easywebdav", easywebdavversion.__version__, "(mod)" + print "requests", requests.__version__ + + def cmd_cat(self,*args): + if (len(args)==0): + return + rfile = args[0] + resp = self.davclient._send('GET', rfile, (200,)) + print resp.text + +def load_conf(): + global SERVER,USER,PASSWD,VERIFY_SSL + homedir = os.getenv("HOME") + if homedir is None: + homedir = os.path.join(os.getenv("HOMEDRIVE"), os.getenv("HOMEPATH")) + + optsfile = ".zotshrc" + if not os.path.isfile(optsfile): + optsfile = os.path.join(homedir, ".zotshrc") + + if not os.path.isfile(optsfile): + print "Please create a configuration file called '.zotshrc':" + print "[zotsh]" + print "host = https://yourhost.com/" + print "username = your_username" + print "password = your_password" + sys.exit(-1) + + config = ConfigParser.ConfigParser() + config.read(optsfile) + SERVER = config.get('zotsh', 'host') + USER = config.get('zotsh', 'username') + PASSWD = config.get('zotsh', 'password') + if config.has_option('zotsh', 'verify_ssl'): + VERIFY_SSL = config.getboolean('zotsh', 'verify_ssl') + + +def zotsh(): + + zotsh = ZotSH( SERVER) + + session_home = zotsh.get_host_session() + + #~ #login on home server + print "loggin in..." + r = session_home.get( + SERVER + "api/account/verify_credentials", + auth=HTTPBasicAuth(USER, PASSWD), + verify=VERIFY_SSL ) + + print "Hi", r.json()['name'] + + zotsh.session = session_home + + # command loop + input = raw_input(zotsh.PS1) + while (input != "quit"): + input = input.strip() + if len(input)>0: + toks = [ x.strip() for x in input.split(" ") ] + + command = toks[0] + args = toks[1:] + try: + ret = zotsh.do(command, *args) + except easywebdav.client.OperationFailed, e: + print e + except CommandNotFound, e: + print e + else: + if ret is not None: + print ret + + + input = raw_input(zotsh.PS1) + + + + +if __name__=="__main__": + load_conf() + zotsh() + sys.exit() + + + + From 808baf203d8c7aa57d89a3d5412742a4c84934b6 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Thu, 15 Aug 2019 19:18:46 -0700 Subject: [PATCH 004/136] show correct profile photo when previewing and editing profiles --- include/channel.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/channel.php b/include/channel.php index 0280cd1cd..7c0397e11 100644 --- a/include/channel.php +++ b/include/channel.php @@ -1262,7 +1262,7 @@ function channel_export_items_page($channel_id, $start, $finish, $page = 0, $lim */ function profile_load($nickname, $profile = '') { -// logger('profile_load: ' . $nickname . (($profile) ? ' profile: ' . $profile : '')); + //logger('profile_load: ' . $nickname . (($profile) ? ' profile: ' . $profile : '')); $user = q("select channel_id from channel where channel_address = '%s' and channel_removed = 0 limit 1", dbesc($nickname) @@ -1303,6 +1303,14 @@ function profile_load($nickname, $profile = '') { dbesc($nickname), dbesc($profile) ); + if (! $p) { + $p = q("SELECT profile.uid AS profile_uid, profile.*, channel.* FROM profile + LEFT JOIN channel ON profile.uid = channel.channel_id + WHERE channel.channel_address = '%s' AND profile.id = %d LIMIT 1", + dbesc($nickname), + intval($profile) + ); + } } if(! $p) { From ac05a2ede7023495618b316635b93274416b69d8 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Thu, 15 Aug 2019 21:30:47 -0700 Subject: [PATCH 005/136] support "bearcaps" in Activity library --- Zotlabs/Lib/Activity.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 5f5f74ca9..0757eec37 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -63,12 +63,31 @@ class Activity { } else { $m = parse_url($url); + + // handle bearcaps + if ($m['scheme'] === 'bear') { + $params = explode('&',$m['query']); + if ($params) { + foreach ($params as $p) { + if (substr($p,0,2) === 'u=') { + $url = substr($p,2); + } + if (substr($p,0,2) === 't=') { + $token = substr($p,2); + } + } + } + } + $headers = [ 'Accept' => 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"', 'Host' => $m['host'], '(request-target)' => 'get ' . get_request_string($url), 'Date' => datetime_convert('UTC','UTC','now','D, d M Y H:i:s') . ' UTC' ]; + if (isset($token)) { + $headers['Authorization'] = 'Bearer ' . $token; + } $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false); $x = z_fetch_url($url, true, $redirects, [ 'headers' => $h ] ); } From e5539c0d01c1be0b9235aee4aec38f7860cc6959 Mon Sep 17 00:00:00 2001 From: Mario Vavti Date: Fri, 16 Aug 2019 20:19:49 +0200 Subject: [PATCH 006/136] update changelog --- CHANGELOG | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 3813bc1a9..d97314674 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +Hubzilla 4.4.1 (2019-08-16) + - Fix wrong profile photo displayed when previewing and editing profiles + - Fix regression from 4.4 which prevented encrypted signatures from being used for encrypted messages + - Fix typo in queueworker addon which broke filtering of duplicate work + + Hubzilla 4.4 (2019-08-13) - Change primary directory from zotadel.net to hub.netzgemeinde.eu (requested by zotadel admin) - Add Russian context help files From e800e2db2b6149cb1a3d32bb111fc23eb8bcc2c4 Mon Sep 17 00:00:00 2001 From: "DM42.Net Zap Dev" Date: Mon, 19 Aug 2019 23:13:19 -0400 Subject: [PATCH 007/136] Return in the case of further processing --- Zotlabs/Daemon/Cron.php | 2 +- Zotlabs/Daemon/CurlAuth.php | 6 +++--- Zotlabs/Daemon/Master.php | 2 +- Zotlabs/Daemon/Poller.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index fe356bcbf..a08d2b7d2 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -215,7 +215,7 @@ class Cron { $restart = true; $generation = intval($argv[2]); if(! $generation) - killme(); + return; } reload_plugins(); diff --git a/Zotlabs/Daemon/CurlAuth.php b/Zotlabs/Daemon/CurlAuth.php index be12bc779..de41382e3 100644 --- a/Zotlabs/Daemon/CurlAuth.php +++ b/Zotlabs/Daemon/CurlAuth.php @@ -13,7 +13,7 @@ class CurlAuth { static public function run($argc,$argv) { if($argc != 2) - killme(); + return; \App::$session->start(); @@ -50,6 +50,6 @@ class CurlAuth { file_put_contents($c,$x); - killme(); + return; } -} \ No newline at end of file +} diff --git a/Zotlabs/Daemon/Master.php b/Zotlabs/Daemon/Master.php index 67a3acc0a..8c3a7e570 100644 --- a/Zotlabs/Daemon/Master.php +++ b/Zotlabs/Daemon/Master.php @@ -9,7 +9,7 @@ if(array_search( __file__ , get_included_files()) === 0) { if($argc) Master::Release($argc,$argv); - killme(); + return; } diff --git a/Zotlabs/Daemon/Poller.php b/Zotlabs/Daemon/Poller.php index 84bf7e923..ebc0584ba 100644 --- a/Zotlabs/Daemon/Poller.php +++ b/Zotlabs/Daemon/Poller.php @@ -47,7 +47,7 @@ class Poller { $restart = true; $generation = intval($argv[2]); if(! $generation) - killme(); + return; } if(($argc > 1) && intval($argv[1])) { From d2565519070255af7f127c90b4dadef9c24328f8 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Wed, 21 Aug 2019 18:49:11 -0700 Subject: [PATCH 008/136] possible for DB to return hublocs with no sitekey --- Zotlabs/Lib/Queue.php | 2 +- include/queue_fn.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Zotlabs/Lib/Queue.php b/Zotlabs/Lib/Queue.php index baa1da70d..49891a55b 100644 --- a/Zotlabs/Lib/Queue.php +++ b/Zotlabs/Lib/Queue.php @@ -250,7 +250,7 @@ class Queue { $host_crypto = null; if($channel && $base) { - $h = q("select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' order by hubloc_id desc limit 1", + $h = q("select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' and hubloc_sitekey != '' order by hubloc_id desc limit 1", dbesc($base) ); if($h) { diff --git a/include/queue_fn.php b/include/queue_fn.php index 85f98aaf9..865228041 100644 --- a/include/queue_fn.php +++ b/include/queue_fn.php @@ -286,7 +286,7 @@ function queue_deliver($outq, $immediate = false) { $host_crypto = null; if($channel && $base) { - $h = q("select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' order by hubloc_id desc limit 1", + $h = q("select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' and hubloc_sitekey != '' order by hubloc_id desc limit 1", dbesc($base) ); if($h) { From cf9ef615c96d0b74ad4d676f48cb67e41a56f5d5 Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Thu, 22 Aug 2019 20:17:22 +0200 Subject: [PATCH 009/136] Fix spoiler displaying while convert BBcode to markdown --- include/markdown.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/markdown.php b/include/markdown.php index 7d1f16958..0a8076799 100644 --- a/include/markdown.php +++ b/include/markdown.php @@ -264,6 +264,9 @@ function bb_to_markdown($Text, $options = []) { // Remove empty zrl links $Text = preg_replace("/\[zrl\=\].*?\[\/zrl\]/is", "", $Text); + + // Remove unprocessed spoiler HTML tags + $Text = strip_tags(preg_replace("/(<\/div>)(>.+)$/im", "$1\n$2", $Text)); $Text = trim($Text); From 58d7c7f6aed50d8b40334c011490b1b26eee4ede Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Thu, 22 Aug 2019 20:20:54 +0200 Subject: [PATCH 010/136] Fix attach permissions sync for clonned channel --- include/import.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/import.php b/include/import.php index 1d3b7c035..d3e8f7091 100644 --- a/include/import.php +++ b/include/import.php @@ -1190,9 +1190,9 @@ function sync_files($channel, $files) { logger('sync_files duplicate check: attach_by_hash() returned ' . print_r($x,true), LOGGER_DEBUG); if($x['success']) { - $orig_attach = $x[0]; + $orig_attach = $x['data']; $attach_exists = true; - $attach_id = $x[0]['id']; + $attach_id = $orig_attach['id']; } $newfname = 'store/' . $channel['channel_address'] . '/' . get_attach_binname($att['content']); From 8cc40038375c33886f7c6f7d429c33a47eab8fb9 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Thu, 22 Aug 2019 19:39:11 -0700 Subject: [PATCH 011/136] issues with image import to zot6 - wrong mid. Also label source project of zotfeed since it is not completely compatible across projects. --- Zotlabs/Lib/Activity.php | 2 +- Zotlabs/Module/Zotfeed.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 0757eec37..12b6cbdfd 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -596,7 +596,7 @@ class Activity { $i['obj'] = json_decode($i['obj'],true); } if($i['obj']['type'] === ACTIVITY_OBJ_PHOTO) { - $i['obj']['id'] = $i['id']; + $i['obj']['id'] = $i['mid']; } $obj = self::encode_object($i['obj']); diff --git a/Zotlabs/Module/Zotfeed.php b/Zotlabs/Module/Zotfeed.php index 381e3acb2..8c13682b4 100644 --- a/Zotlabs/Module/Zotfeed.php +++ b/Zotlabs/Module/Zotfeed.php @@ -42,7 +42,7 @@ class Zotfeed extends \Zotlabs\Web\Controller { } logger('zotfeed request: ' . $r[0]['channel_name'], LOGGER_DEBUG); - + $result['project'] = 'Hubzilla'; $result['messages'] = zot_feed($r[0]['channel_id'],$observer['xchan_hash'],array('mindate' => $mindate)); $result['success'] = true; json_return_and_die($result); From 60919488f17ded7bcd1dd48429baaca530475161 Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Fri, 23 Aug 2019 23:58:07 +0200 Subject: [PATCH 012/136] Better spoiler BBcode tag conversion to Markdown --- include/markdown.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/markdown.php b/include/markdown.php index 0a8076799..213986867 100644 --- a/include/markdown.php +++ b/include/markdown.php @@ -266,7 +266,7 @@ function bb_to_markdown($Text, $options = []) { $Text = preg_replace("/\[zrl\=\].*?\[\/zrl\]/is", "", $Text); // Remove unprocessed spoiler HTML tags - $Text = strip_tags(preg_replace("/(<\/div>)(>.+)$/im", "$1\n$2", $Text)); + $Text = preg_replace("/([^<]+)<.+>(>.+)$/im", "$1\n$2", $Text); $Text = trim($Text); From 51954c7f3de4940bb9bdc545f8a2a26e6ea7b101 Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Fri, 30 Aug 2019 00:51:46 +0200 Subject: [PATCH 013/136] Add CalDAV / CardDAV autodiscovery --- Zotlabs/Module/Well_known.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Zotlabs/Module/Well_known.php b/Zotlabs/Module/Well_known.php index 09e743788..d322c016c 100644 --- a/Zotlabs/Module/Well_known.php +++ b/Zotlabs/Module/Well_known.php @@ -63,6 +63,18 @@ class Well_known extends \Zotlabs\Web\Controller { case 'dnt-policy.txt': echo file_get_contents('doc/dnt-policy.txt'); killme(); + + case 'caldav': + if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') { + http_status('301', 'moved permanently'); + goaway(z_root() . '/cdav'); + }; + + case 'caldav': + if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') { + http_status('301', 'moved permanently'); + goaway(z_root() . '/cdav'); + }; default: if(file_exists(\App::$cmd)) { From d267dd251579c28346882a3c69c5749fa4b6962d Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Fri, 30 Aug 2019 00:55:36 +0200 Subject: [PATCH 014/136] Update Well_known.php --- Zotlabs/Module/Well_known.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zotlabs/Module/Well_known.php b/Zotlabs/Module/Well_known.php index d322c016c..140ab260d 100644 --- a/Zotlabs/Module/Well_known.php +++ b/Zotlabs/Module/Well_known.php @@ -70,7 +70,7 @@ class Well_known extends \Zotlabs\Web\Controller { goaway(z_root() . '/cdav'); }; - case 'caldav': + case 'carddav': if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') { http_status('301', 'moved permanently'); goaway(z_root() . '/cdav'); From 1f8d29a22171138c90377007496c4af2033b1095 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Sat, 31 Aug 2019 14:04:10 -0700 Subject: [PATCH 015/136] improved conversion of emoji reactions from zot to zot6 --- Zotlabs/Lib/Activity.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 12b6cbdfd..721ed10fd 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -516,6 +516,25 @@ class Activity { xchan_query($p,true); $p = fetch_post_tags($p,true); $i['obj'] = self::encode_item($p[0]); + + // convert to zot6 emoji reaction encoding which uses the target object to indicate the + // specific emoji instead of overloading the verb or type. + + $im = explode('#',$i['verb']); + if($im && count($im) > 1) + $emoji = $im[1]; + if(preg_match("/\[img(.*?)\](.*?)\[\/img\]/ism", $i['body'], $match)) { + $ln = $match[2]; + } + + $i['tgt_type'] = 'Image'; + + $i['target'] = [ + 'type' => 'Image', + 'name' => $emoji, + 'url' => (($ln) ? $ln : z_root() . '/images/emoji/' . $emoji . '.png') + ]; + } } From bbc98db6b4071296ea02cea1a8c89fd33b251b1c Mon Sep 17 00:00:00 2001 From: zotlabs Date: Sat, 31 Aug 2019 14:43:08 -0700 Subject: [PATCH 016/136] Clarify private mail deletion policy. Related to issue #1391. --- include/connections.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/connections.php b/include/connections.php index e942503f0..51df18b70 100644 --- a/include/connections.php +++ b/include/connections.php @@ -299,6 +299,11 @@ function remove_all_xchan_resources($xchan, $channel_id = 0) { $r = q("delete from pgrp_member where xchan = '%s'", dbesc($xchan) ); + + // Cannot delete just one side of the conversation since we do not allow + // you to block private mail replies. This would leave open a gateway for abuse. + // Both participants are owners of the conversation and both can remove it. + $r = q("delete from mail where ( from_xchan = '%s' or to_xchan = '%s' )", dbesc($xchan), dbesc($xchan) From 93ec01e0a1fcf7a5574d8bf6ac044331b5b94724 Mon Sep 17 00:00:00 2001 From: Terrox Date: Tue, 3 Sep 2019 14:32:52 +0200 Subject: [PATCH 017/136] More nofollow tags to discourage backlink farmers --- Zotlabs/Module/Directory.php | 2 +- include/channel.php | 4 ++-- view/tpl/conv_item.tpl | 2 +- view/tpl/conv_list.tpl | 2 +- view/tpl/usermenu.tpl | 2 +- view/tpl/xchan_vcard.tpl | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Zotlabs/Module/Directory.php b/Zotlabs/Module/Directory.php index 8f5db6635..dee22721d 100644 --- a/Zotlabs/Module/Directory.php +++ b/Zotlabs/Module/Directory.php @@ -345,7 +345,7 @@ class Directory extends \Zotlabs\Web\Controller { 'pdesc_label' => t('Description:'), 'marital' => $marital, 'homepage' => $homepage, - 'homepageurl' => linkify($homepageurl), + 'homepageurl' => linkify($homepageurl, true), 'hometown' => $hometown, 'hometown_label' => t('Hometown:'), 'about' => $about, diff --git a/include/channel.php b/include/channel.php index 7c0397e11..5d583e4f1 100644 --- a/include/channel.php +++ b/include/channel.php @@ -1718,9 +1718,9 @@ function advanced_profile() { if(App::$profile['sexual']) $profile['sexual'] = array( t('Sexual Preference:'), App::$profile['sexual'] ); - if(App::$profile['homepage']) $profile['homepage'] = array( t('Homepage:'), linkify(App::$profile['homepage']) ); + if(App::$profile['homepage']) $profile['homepage'] = array( t('Homepage:'), linkify(App::$profile['homepage'], true) ); - if(App::$profile['hometown']) $profile['hometown'] = array( t('Hometown:'), linkify(App::$profile['hometown']) ); + if(App::$profile['hometown']) $profile['hometown'] = array( t('Hometown:'), linkify(App::$profile['hometown'], true) ); if(App::$profile['politic']) $profile['politic'] = array( t('Political Views:'), App::$profile['politic']); diff --git a/view/tpl/conv_item.tpl b/view/tpl/conv_item.tpl index 09a2e05e0..186551e2d 100755 --- a/view/tpl/conv_item.tpl +++ b/view/tpl/conv_item.tpl @@ -20,7 +20,7 @@ {{/if}} {{if $item.title && !$item.event}}
- {{if $item.title_tosource}}{{if $item.plink}}{{/if}}{{/if}}{{$item.title}}{{if $item.title_tosource}}{{if $item.plink}}{{/if}}{{/if}} + {{if $item.title_tosource}}{{if $item.plink}}{{/if}}{{/if}}{{$item.title}}{{if $item.title_tosource}}{{if $item.plink}}{{/if}}{{/if}}
{{if ! $item.is_new}}
diff --git a/view/tpl/conv_list.tpl b/view/tpl/conv_list.tpl index a0c2cf827..8c5b47bf3 100755 --- a/view/tpl/conv_list.tpl +++ b/view/tpl/conv_list.tpl @@ -20,7 +20,7 @@ {{/if}} {{if $item.title && !$item.event}}
- {{if $item.title_tosource}}{{if $item.plink}}{{/if}}{{/if}}{{$item.title}}{{if $item.title_tosource}}{{if $item.plink}}{{/if}}{{/if}} + {{if $item.title_tosource}}{{if $item.plink}}{{/if}}{{/if}}{{$item.title}}{{if $item.title_tosource}}{{if $item.plink}}{{/if}}{{/if}}
{{if ! $item.is_new}}
diff --git a/view/tpl/usermenu.tpl b/view/tpl/usermenu.tpl index 8bbfedd07..535d5b5a8 100644 --- a/view/tpl/usermenu.tpl +++ b/view/tpl/usermenu.tpl @@ -8,7 +8,7 @@ '; } echo "\n"; }; ob_start(); - echo "