2026/07/26

From WoozleCodes
< 2026 | 07
Jump to navigation Jump to search
Sunday, July 26, 2026 (#207)
Saturday Sunday Monday

References

Main Work

Ferreteria FileSys stuff in order to support sftp in FTM

I think I see two possible ways out of this:

1. My original solution of using PHP native functions for file I/O and #phpseclib (PSL) for everything else.
2. The problem might just be between fopen() and PSL's Stream wrapper. There is another way to read/write files via PSL, which I avoided using because its default behavior is to do an entire file at once. It can also be used to retrieve chunks of a file, but it opens and closes the file for each operation. However, there is also an option for a callback to report progress. I've only avoided using this because it complicates things.

I think I should at least do some structural work towards #1, if only because separation-of-concerns is good practice.

(option 1) mapping out library-choice

Core library clade-families:

  • IO\Aspect\Connx\Server\SSH2
    • IO\Aspect\Connx\Server\SSH2\SFTP
  • Sys\FileSys\Aspect\Connx\Stream

Places where a choice is made:

class used alias object access used in
Sys\FileSys\Aspect\Connx\Stream\File\SecLib StreamClass OStream(NodeIface $oNode) IO\Aspect\Connx\Server\SSH2\SFTP
IO\Aspect\Connx\Server\SSH2\SFTP\SecLib ServerClass $OCoreLib Sys\FileSys\Node\venue\VenConn\SFTP

This would suggest that the necessary separation is already in place, and the only additional work that might be helpful is to have both of those choices enshrined in the same place (as class constants, perhaps).

Unfortunately, what I see first is that IO\Aspect\Connx and Sys\FileSys\Aspect\Connx have become interdependent, which tells me that the first step would be to straighten all that out -- which would also likely involve tying IO\Aspect\Connx\Stream\Exec\SSH2 in with IO\Aspect\Connx\Server\SSH2... which may actually need to be done anyway...

Let me see if swapping in the Native Stream clade works, at least, before starting in to that mess.

...except I think I did that table wrong. Let me start over. ...and I think I need another column, too.

Places where a choice is made:

used in class used access used by
IO\Aspect\Connx\Server\SSH2\SFTP\SecLib Sys\FileSys\Aspect\Connx\Stream\File\SecLib alias: StreamClass

fx/var: function OStream(NodeIface $oNode)

Sys\FileSys\Node\venue\VenNode\SFTP->OConnLib->OStream($this->ONode)
  • Where OConnLib is a shortcut to $this->ONode->OVenConn->OCoreLib
Sys\FileSys\Node\venue\VenConn\SFTP IO\Aspect\Connx\Server\SSH2\SFTP\SecLib alias: ServerClass

fx/var: $OCoreLib

  • Sys\FileSys\Node\venue\VenConn
  • Sys\FileSys\Node\venue\VenConn\SFTP
  • Sys\FileSys\Node\venue\VenNode\SFTP (from VenConn)

Part of my confusion, I think, has been Kate showing me search-results from files that I've deleted or renamed (but not showing that they're deleted or renamed).

...but in any case, I think this is at a point where the question to ask is "how should this work", not "how does it work now" (because how it works now is a bit of a confusing, tangled mess).

(option 1) how it should work

  • All the library-choices should be in Node, methinks. ...and I'm not sure where else that leads...
  • I want to move all the IO\Aspect\Connx\Stream stuff -- or do I mean the IO\Aspect\Connx stuff or maybe even the the IO\Aspect stuff? -- into \Sys, but that really seems like asking for another 1-2 weeks (optimistically) of refactoring.

I think all of this points to the idea that maybe I should look at Option 2 instead.

(option 2) implement file-I/O progress as a callback

The question here is: how much will I have to mangle Ferreteria's existing file-I/O API to make this work...

(option 3) squirrel!

So what am I doing instead? Well, I noticed that the error message said the problem was in phpseclib3\Net\SFTP\Stream::stream_open, so I looked for that code to see what was going on.

It turns out that the open-attempt is being rejected because the URL does not contain a password -- even though I explicitly am authorizing via SSH2 agent, not a password.

Also, there doesn't seem to be any attempt to capture (much less report) why the call failed; the function just exits when various blocker-conditions are encountered.[1]

Here's the relevant code:

#
            if (isset($context[$scheme]['password'])) {
                $pass = $context[$scheme]['password'];
            }
            if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof PrivateKey) {
                $pass = $context[$scheme]['privkey'];
            }

            if (!isset($user) || !isset($pass)) {
                return false;
            }

It looks like $context can be set from either stream_context_get_params($this->context) or stream_context_get_options($this->context).[2] So then the question becomes, did my code's call to set the auth agent get bypassed somehow? The answer:

First, this code is definitely executing, because I get "remote user 'root' authenticated." in the readout:

#
        $ok = $this->AuthAgent($sUser);
        if (!$ok) {
            $sMsg = "Could not authenticate '$ftConn' with SSH agent.";
            $oOp->IsOkay = FALSE;
            $oOp->AddMsgString($sMsg);
            $this->ShowAction($sMsg);
            return $oOp;
        }

        // TODO: use UI-output object
        $this->ShowAction("remote user '$sUser' authenticated.");

So, what happens in AuthAgent()? That function is pretty minimal: it

  • creates $oAgent as an instance of phpseclib3\System\SSH\Agent
  • calls phpseclib3\Net\SFTP->login('root',$oAgent) (implemented in phpseclib3\Net\SSH2), which then
  • calls $this->sublogin() with the same arguments, which
  • checks a bunch of stuff, and ultimately (for my setup specifically) does $this->login_credentials_finalized = true;.

...so I asked Gemini what was going on, and it explained how to pass the auth agent to it via a context-resource. This seems to have worked, and now I'm just trying to resolve object-structure-issues in my code.

Footnotes

    1. This is sloppy.
    2. This also feels sloppy. (It seems clear that a lot of PSL was written before functions could have return-types, as they generally are not specified. This too is sloppy, at least in the context of PHP 7 and later.)