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

Using Async, Await, Plus a Slight Delay

Today’s problem dealt with how we view our invoices online.  We use an app on the iSeries that creates a PDF and delivers it to a set destination.  That destination, in our case is a regular windows server, the files landing in a small site:   pdf.mycompany.com.

My initial approach was simple, use the PHP API I have sitting on the iSeries to make a call to the program – passing it the parameters for that specific invoice, await response (which gave me the new created filename) and then redirect to that URL.  The method looks something like this:

[HttpGet]
public async Task<ActionResult> GetInvoiceAsync(int invoice)
{
    GetInvoice getInvoice = new GetInvoice();
    var client = new HttpClient();

    string fileName = await getInvoice.LoadPDF(invoice);

    string url = "http://pdf.mycompany.com/";
    url += fileName + ".pdf";

    return Redirect(url);
}

 

This worked great… 90% of the time, but the other 10% of the time, I clicked too quickly on an invoice and got forwarded to a 404.

Continue Reading

Securing AS400 / iSeries (Comodo Free SSL)

Enabling SSL certificates with Comodo’s Free 90 Day trial SSL Certificate.  This process enables you to secure both web services, login, and SSL across all other servers/services on the AS400 / iSeries.

Note:  If you are replacing/renewing your cert and something goes wrong, do not be afraid to delete your certificate store (if this is your only cert) and start fresh with this tutorial.  I’ve done it twice, now, as the key in store had trouble matching the new cert, etc…  I deleted certificate store, followed my own tutorial and was up and going in minutes.

Continue Reading