FtpWebRequest: More Mainframe Adventures with DotNetCore

Problem:

Our iSeries (mainframe/as400) team has figured out a way to upgrade all our forms and can easily deposit these forms into a folder on the as400.  They are looking for the fastest way to push these forms to our customers.

They ask: Is there a way I can (.NET Core Dev) retrieve their PDF statements from the mainframe (iSeries/AS400) for viewing online? The files are not exposed in Zend (the as400 webserver) but are in a specific “folder” (if you could call it that, the structure of as400 file system is different).

Thought:

Though I actively employ their Zend system to utilize my own API for querying, etc, there was not much interest in this in their team other than knowing how I was calling their programs.  They wanted this SPECIFIC folder to be accessed to retrieve statements.

I tried to propose a HTTP web service via Zend, other HTTP/PHP solutions… but in the end, I explained what I am currently doing:  utilizing my own API to accept an authentication key with parameters (in json using PHP) and convert those parameters to the parameters they need and then calling a program.  The program I call does the logic and I get a response. (In this case I will get a stream).

Solution:

In the end, we decided on a combo of passing parameters via my API (Zend/PHP) and then receiving a file name which I will retrieve via FTP.

I know that I can access files via FTP if given access.  I know I can get to the file(s) they want using FTP and C#.  I do not like it, I insist on many, many security measures but I assure them I can access via FTP and prove it using a FTP client.

It’s decided:  I will use Zend (and PHP) to pass parameters (using post/json) and the response I get will be a file name.  That file, I have to retrieve off of that specific path on the as400.

Problems Particular to Mainframe:

First, I followed the ANSWER to this post:  https://stackoverflow.com/questions/57236179/how-to-download-xlsx-file-using-ftpwebrequest-through-c-sharp-asp-net

I didn’t want to download, as the second half of his post suggested.  I wanted to immediate display (stream) the pdf.  This is how I did it:

[HttpGet]
        public async Task<Stream> GetStatementAsync()
        {
           
            string url = "ftp://1.1.1.1" + "/%2F" + "thisismyfilesharefolder" + "/%2F";
            //TODO:  REPLACE FILE NAME / MAKE DYNAMIC
            string ftpFileName = "MYTESTFILE.PDF";
            var requestUri = new Uri(url + ftpFileName);
            var fileBytes = FtpDownloadBinary(requestUri);

            return new MemoryStream(fileBytes);
        }

        //TODO: MOVE TO FTPHELPER SERVICE
        public byte[] FtpDownloadBinary(Uri fromUri)
        {
            //TODO: REPLACE WITH FTPUSER
            string username = "ASKTHESPAMMER";
            string password = "WHOWANTS10K";

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(fromUri);
            request.Method = WebRequestMethods.Ftp.DownloadFile;
            request.Credentials = new NetworkCredential(username, password);
            request.UseBinary = true;
            request.KeepAlive = true;
            request.UsePassive = true;
            //TODO: ENABLE FTPSSL ON AS400 TO MAKE THIS POSSIBLE
            //request.EnableSsl = true;

            FtpWebResponse response = (FtpWebResponse)request.GetResponse();

            using (var responseStream = response.GetResponseStream())
            using (var memoryStream = new MemoryStream())
            {
                responseStream.CopyTo(memoryStream);
                return memoryStream.ToArray();
            }
        }

Very raw and in house, our team is making that FTPUSER profile, etc. We just wanted to know if it was POSSIBLE.

Permissions

First error was an obvious authentication error.  The standard profile user I used did not have the same permission I do on FTP.  Simple way to clarify: I did logged on with both users via FTP client (sorry, I still love FileZilla) and with user one I could get in, the other I could not.

Once we established a user with correct permissions, I received another error.

Escapes

“(501) Syntax error in parameters or arguments”

I got past authentication but I kept getting an error somewhere on response.  In truth, the error is simply saying there’s a syntax error in the REQUEST.  The syntax error was a bit hard to find – it ended up being the forward slash in the url.  It looked normal, but the mainframe was denying it.  If you research this, the posts are so old that many of the links are dead.

Quick tutorial for those still dealing with Mainframe / AS400 / iSeries:

If you feed it a string (url) with a slash, it does NOT understand the slash.  You must ESCAPE the slash.

The escape string is this: “/%2F”

in example:  google.com/bugs = google.com/%2Fbugs

for devs: string url = “ftp://1.1.1.1” + “/%2F” + “thisismyfilesharefolder” + “/%2F” + filename

It took forever to find the right escape sequence as so much of the documentation is lost, outdated, etc.

After ftp:// EVERY forward slash must look like this:  /%2F

Notes:

In truth, this is not an “escape” sequence, it’s changing directories forward.  You can also move backward:

https://stackoverflow.com/questions/330155/how-do-you-change-directories-using-ftpwebrequest-net

Unfortunately, so many of the links with information regarding this are dead.

I’m going to tag everything I can on this on hopes that some of you still working with mainframe find it.  Just lending my support as I’m sure I might need yours in the future!

 

Continue Reading

Site 24×7 and JSONPath Example

We had an issue a couple of days ago where two points of failure caused some downtime and so I spent most of today revisiting all my monitors to give us as much warning as possible if one of our services fails.

One particular API I set up lives on the as400 (iSeries) and operates off of it’s ZendCore server in PHP.  I quickly scripted a page to verify:  connection between ZendCore PHP and db2, that the connection to that endpoint was in fact SSL, and on what port.

The PHP code is very simple and returns a JSON string that looks like this:

{
    "AS400CONNECTION": true,
    "SSL": true,
    "PORT": "633"
}

The slight problem today was figuring out how to get Site 24×7 to not just verify the link, but check the JSON values and verify it is what I need.  If not, then send the alert.  In this case, I want to verify AS400CONNECTION is true and SSL is true.

Site 24×7 suggests using a REST API monitor and then asserting the value in JSONPath.  I was completely new to this and finding a good clean example was a bit tough, hopefully this saves someone some time.  Here’s how I setup the JSONPath expression:

$[?(@.AS400CONNECTION==true)]
$[?(@.SSL==true)]

For a full screenshot of the setup I did to make this work click here.

 

 

 

Continue Reading