March 29, 2024, 08:24:54 AM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Telnet Client

Started by mlbaker4, February 24, 2008, 09:08:49 PM

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

Mike Stefanik

Just to point out, PuTTY and PsFTP aren't libraries. They're applications, and while there is source, it's written in C and is definitely not something that you could just compile and link into an application. For one, it uses globals all over the place and doesn't even have an internal API that is thread-safe. So calling it a library is incorrect, unless there's some version of PuTTY I'm not aware of that is implemented as a DLL with an exposed API. The best that you could probably do is shell out to PLink (the console version of PuTTY) and use pipes.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

mlbaker4

My whole point is to be able to provide a customized telnet client to do specific functions, for technicians in the filed that are using slow speed wireles air cards, who are not unix guru's so they can push abutton, and get done what they need, then to build off of this to do other functions. And yes I could buy a number of Telnet Clients and write scripts in Visual basic to ge the job done, but then to be able to legally distribute this I would need a copy for each technician. By compliling my own code I can buy they compilier, sockets for development, and distrubute it freely as long as the complied code is not disassembled or the programs and sockets given to others. I hope this is correct.

  I have used Putty, and been a ProComm users for many years I have most of my scripts written for ProComm which include custom windows and buttons, I have looked at every client on the Market and none cone close to ProComm.

  SO I decided to venture forward with a compiler buy the needed sockets and give it a shot on my own,  learning to write code has been something I have wanted to do a tinkered with for years.

My first attempt at this was with Liberty Basic, got a great window, all the menues I needed, all the common commands like save, copy and paste working, but couldn't figure out the Telnet at all, and I bought that program, I then Downladed Microsoft visual studios, and with little experience it was cumbersome to use, I then downloaded a Borland C++ it has some new name, and it was very cumbersome to do little things in.

  I then stumbled onto Aurora and with in a day I had coded the window, all the buttons, menuse and got the common items to work, but still the lack ofa complete manual means a lot of trial and error, and then I couldn't seem to find anyway to make the CTelnet functions work, Once I got the Socket wrench products I was able to get the demo working with a little trial and error, but as soone as I try to copy and play with the code on my own I could get connected, get an error message, and never get a return back from the host.

  Even with the sample program I haven't been able to get the local echo to work and I am not sure how to handle the problem where I have to hit a control enter to move to the next line, I was going to remap the key when I got more of the project working,

SO so day I am going to try to just get the bare code working to telnet. I am not sure what each of the libraries or files I need but I am going to dig around in the samples and try to come up with it.

Look on Socket wrenches web site, and learn as I hack away at the code. For an old man of 52 I enjoy this and if I get it to work I will feel a sense of acomplishement, will I make any money the answer is no, but it will make my day to day life easier, and it will give me a new skill set something of value you as the corporate world only cares about you while you can do what they need and they can't get it done cheaper.

  Who knows maybe I can write a diet program, and get on the diet bandwagon, and have the program offer encouragement, and with a few sensors make the decision if the portions are to much, then I could make a little money.  And make insulting remarks as the person walks buy the computer ot if they play an online game telling them to go aoutside and run around the block before it lets you play.

Rock Ridge Farm (Larry)

Do not know if this would work but you can check it out - http://www.dart.com/cpp_trial.aspx

Mike Stefanik

Quote from: Rock Ridge Farm (Larry) on February 27, 2008, 11:57:46 AM
Do not know if this would work but you can check it out - http://www.dart.com/cpp_trial.aspx

They wouldn't because they're C++ classes, and you can't link C++ and Auroa objects together. He'd need a standard Windows DLL that exports unmangled functions. I don't recall if Aurora can directly link to COFF or OMF object files generated by Visual C++ or C++ Builder, but I do remember that Paul commented that because of how C++ libraries are mangled (and each compiler does it differently), you couldn't just link them in.



Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

mlbaker4

I think socket wrench is going to work but it  is probably my lack of knowledge, I may  try a different laguage socket wrench supports if I can find a demo to try before I buy , I really enjoy Aurora.

Mike Stefanik

February 27, 2008, 07:35:39 PM #30 Last Edit: February 27, 2008, 11:51:57 PM by Mike Stefanik
Actually, the SocketTools Telnet DLL would be easier for you; SocketWrench is lower-level, and you'd basically have to implement the Telnet protocol yourself in code (something which our Telnet API will do for you). It would be helpful to understand more about what you actually need done. Do you actually want a terminal emulation interface, for example?

I downloaded the latest build of Aurora and did some poking around. The problem that you saw in csrtkey5.inc can be easily fixed by changing:


string CSTOOLS5_LICENSE_KEY = "";


to:


#define CSTOOLS5_LICENSE_KEY ""


Apparently at some point, Paul modified Aurora to allow you to define string values and the initialization of strings was removed or modified in some fashion. In any case, that small change takes care of that.

In the Terminal example, if you wanted to implement local echo, there's a few small changes to the example in MainWnd.src. First, modify the OnChar handler so that it looks like this:


CMainWnd::OnChar(unsigned int nChar, int nRepCnt), int
{
if (!g_pClient->IsConnected())
{
MessageBeep(-1);
return 0;
}

g_pClient->WriteChar(nChar, nRepCnt);
return CTerminal!!OnChar(nChar, nRepCnt);
}


When I had told you to use SetLocalEcho, the problem was that OnChar wasn't calling the base class implementation in CTerminal (which is what handles the local echoing of characters). Then, add a call to SetLocalEcho in main():


global sub main()
{
g_pMainWnd = new(CMainWnd, 1);
g_pClient = new(CTelnet, 1);

g_pMainWnd->Create(100, 100, 500, 300,
   AWS_OVERLAPPEDWINDOW | AWS_HSCROLL | AWS_VSCROLL | AWS_VISIBLE,
   0,
   "Terminal",
   null);

// Resize the window to the appropriate width and height for
// the current (default) font; this will make sure that all
// rows and columns can be displayed without requiring scrollbars
//
// Note that if you want to force the display to always be
// this size, call the SetAutoSize method
g_pMainWnd->Resize();
g_pMainWnd->SetLocalEcho(true);

do
{
Wait();
}
until (g_pMainWnd->m_hwnd = 0);

delete g_pMainWnd;
delete g_pClient;
return 0;
}


I just added it right after the call to Resize(), which adjusts the window size to match the virtual display used by the emulator. Whatever is entered is echoed locally. I can send you the modified sample, if you'd prefer.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Mike Stefanik

I put together the simplest Telnet program I could for you. It's console based, and with no emulation it effectively acts just like a dumb terminal. As you'll see, the IP address and so on are hard-coded, but I didn't want to add a lot extraneous code that dealt with things like parsing command line arguments.


// SocketTools 5.0 Telnet example for Aurora

#autodefine "off"

// Include the SocketTools header file
#include "cstools5.inc"

// Default buffer size used for reading data sent by the
// server; this could be any reasonably sized value
#define BUFFERSIZE    4096

// The libraries that should be linked into the program
#use "cstntav5.lib"
#use "cstacls5.lib"

global sub main()
{
    CTelnetClient *pClient;
    int bLocalEcho = false;
   
    // Make sure that a console window is open
    OpenConsole();

    // Create an instance of the CTelnetClient class
    pClient = new(CTelnetClient, 1);

    //
    // Establish a connection with the server. The first argument
    // is the IP address or hostname of the server. The second
    // argument is the port number (23 is the default port number
    // used for Telnet). The third argument is the timeout period
    // in seconds.
    //
    if (!pClient->Connect("192.168.0.2", 23, 10))
    {
        ShowError(pClient);
        return;
    }
   
    //
    // Set the terminal type to "dumb" which is what most UNIX
    // based Telnet servers will recognize as a terminal with
    // no character positioning or graphics capability
    //
    pClient->SetTerminalType("dumb");
   
    //
    // The main loop where were check for data to be read and
    // displayed, as well as keypresses
    //
    while (pClient->IsConnected())
    {
        //
        // If the IsReadable method returns true, this means
        // that there is data available to read from the server
        //
        while (pClient->IsReadable())
        {
            dstring strBuffer[BUFFERSIZE+1];
            int nBytesRead;
            //
            // The Read method reads the data from the server and
            // copies it into the buffer that we provide. If it is
            // successful, it will return the number of bytes read;
            // otherwise if it fails, it will return TELNET_ERROR
            //
            nBytesRead = pClient->Read(strBuffer, BUFFERSIZE);
            if (nBytesRead > 0)
            {
                // Write the data to the console
                writeln(StrLeft(strBuffer, nBytesRead));
            }
            else if (nBytesRead == TELNET_ERROR)
            {
                // An error has occurred, display an error message
                // to the user and disconnect from the server
                ShowError(pClient);
                pClient->Disconnect();
                break;
            }
        }

        //
        // If we are no longer connected to the server, then an
        // error must have occurred, so break out of the loop
        //
        if (!pClient->IsConnected())
            break;

        //
        // Check if the user has pressed a key; if they have,
        // then send that keypress to the server using the Write
        // method
        //
        do
        {
            string strKeyPress = GetKey();

            if (len(strKeyPress) > 0)
            {
                // Send the keypress to the server as input
                pClient->Write(strKeyPress, 1);

                // If local echo is enabled, then also write
                // the character to the console
                if (bLocalEcho)
                    writeln(strKeyPress);
            }
        }
        until len(strKeyPress) == 0;
    }
   
    // Destroy this instance of the CTelnetClient object
    delete pClient;
}

//
// If an error has occurred, then write it out to the console.
// Note that if the error indicates that the connection has
// been closed, we simply return since this is normal when the
// user logs out from the server.
//
global sub ShowError(CTelnetClient *pClient)
{
    unsigned int nError;
    string strError;
   
    nError = pClient->GetLastError();

    if (nError == ST_ERROR_CONNECTION_CLOSED)
        return;

    if (nError > 0)
    {
        pClient->GetErrorString(nError, strError);
        print("\nError 0x" + NumToHex(nError) + ": " + strError);
    }
}


With the comments, it should be fairly self-explanatory in terms of how things work. Internally, the library handles all of the protocol related stuff like option negotiation, so all your program "sees" is the standard output and error channels from the server.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

mlbaker4

Well I am doing pretty good but I am having a few problems that aren't about Telnet, If I use the options section and paste my telnet programming it works, but when I add on to the existing menu for options it works well.

here is part of the Menu text

m.MenuItem( "&Options",0,IDM_TOOLS_OPTIONS);
   m.MenuTitle( "Help");
   m.MenuItem( "About...",0,IDM_HELP_ABOUT);
   m.MenuTitle( "&Telnet BTS");
   m.MenuItem( "COLU Telnet",0,IDM_COLU_TELNET);

case IDM_COLU_TELNET:
   OnMenuColu();

I added IDM_COLU_TELNET

I Then added the below line to resources.inc
#define IDM_COLU_TELNET   15

I then added declare OnMenuColu(), int; to MainWnd.inc

But the menu option doesn't work.

What am I missing.

The only other thing is I can't get is the default window color to change, I think this might have to do with the menu options for color are defaulting it to white.

   any idea?

Mike Stefanik

Honestly, I don't know what your menu related issues would be. I just tried adding it to the example and didn't have a problem. As far as the foreground and background colors are concerned, you just need to make sure that you call the Redraw() method after you're done setting them. I've attached the updated Terminal example with the changes made. Hopefully that gets you pointed in the right direction.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

mlbaker4

Mike

  Got the menu problem resolved I found an extra } at the end of the first line way out from the text itself off the screen resolution I was using.  After finding that it worked great, I took the Tools section and added a popup set of of buttons to turn local echo on and off and I am well on my way. I haven't fixed the color but that is minor and I will work on it now. I want to thank you for all the help, and am I using the Library or the Standard Socket wrench. I plan to keep churning out customized terminal program so I want to purchase what will work better. Sofar the few changes you have made have it working very well with Aurora.

  I know you showed me how to send a character in text, What if it is a full long line of text, can I send a full line, or several lines, then a return at the end?

  Any way thank you very much.

  Also I have one stsem I log into the handles the normal return fine, and another that requires me to use control return, Is there an iption in the tools to send what I think amounts to a linefeed return, or what ever the control return is. I guess I could remap the key.

  But now it is becoming fun, I found I had three compiliers installed all different brands and they fought over certain files, once I removed all But Aurora and put all the files needed for the Terminal program in one folder, I had a lot better success than having files all over.

 

mlbaker4

I tried putting the color statement in various places and the rebuild and never could get the screen color to change, I put it down in the resize section and got a blue and white striped scrolling color, interesteng but not very useful.


  I am starting to think the default color is either coming from p_gclient, or one of the CTerminal statements. I messed around with it in the these areas and got an undefined statement error each time. I am digging through the C5TOOLS and each of the source and include code pages but havent figure it out yet. I may remark out a few lines and see what happens.

Mike Stefanik

There's already code (in the menu handler) to set the foreground and background color of the emulation window, so I'm not really clear on what you're asking for here. If you wanted to set it to a specific color instead of using ChooseColor, then use SetForeColor and/or SetBackColor as appropriate, then call the Redraw method. The CTerminal class is what manages the colors; if you're attempting to use lower level Windows API code to change the colors, then that's not going to work. You need to use the methods in that class, following the code that's already in the example.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

mlbaker4

Mike

  You have my thoughts correct I wan the terminal to start up in DarkBlue background and white type.

  I was using g_pMainWnd->SetWindowColor (RGB (64,0,130)) butas you described it didn't work. I am sorry I am using so much of your time, since the help files don't show any examples, is there book I can get that would get me close so I don't have to ask so much since I am  beginner. I look at this code and I am lost I have tried to play with different paramaters and nothing works. declare SetBackColor(unsigned int clrref), int;
I would assume I would add the statements to telnet.src? If you have a moment can you look at my post before the color question, and head me the right direction?

Mike Stefanik

I'm afraid there isn't a book about Windows programming in Aurora (at least, not that one that I'm aware of). The documentation that we provide for the SocketTools Library Edition is geared primarily towards C/C++ programmers. The information generally applies, but the class implementation is different. In any case, if you wanted to tell the emulator to use a purple background with white text, you'd add:


g_pMainWnd->SetForeColor(RGB(255,255,255));
g_pMainWnd->SetBackColor(RGB(64,0,130));


Put that right before the call to the Resize method, in main(). It'll change the colors, and then when Resize is called, it'll adjust the display and redraw it, which will cause the background and foreground to be drawn using those colors.

As far as sending lines of text and so on is concerned, you'd just use something like g_pClient->Write(strBuffer, len(strBuffer)), where strBuffer is a string that contains what you want to send. The second argument is the number of bytes that you want to send. For example:


if (g_pClient->IsConnected())
{
    string strBuffer = "/bin/ls -l /usr/bin\n";
    g_pClient->Write(strBuffer, len(strBuffer));
}
else
{
    MessageBox(this, "You are not connected to a server", "Error");
}


The "\n" in the string is how you embed a newline in a string literal. Depending on the server/device that you're connected to, you may need to send something like "\x0d\x0a" (which is a carriage-return/linefeed pair) instead. It depends on what end-of-line conventions it uses.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

mlbaker4

Mike

    Thanks for the help I had just figured out the color and was coming back here to tell you I got it, I am grateful for the line text information with that I will start the hard stuff, the text sending and receiving even in a simple script language like program was intense, I have one script to all technicians to turn on and off a piece of equipment that took 2000 lines, do to the different unix prompts that our various computer uses.

   

mlbaker4

Mike

  I am able to send the text with your help with no problem, but how do I make a simple waitfor name: send "name" waitfor password: send "Password"

then after waitng for other strings of text a waitfor ">" some other text.

I see the login function in C5TOOLS but havent; figured out the string, and search refrences for C++ for most of these did not pan out. Once connected I can send text all day but with the line you gave me even if I rem out the id connected statement I can't get it to send a name and password.

Hope you ahve a little more time in you for help.
Mike

Mike Stefanik

You would use the Search method to look for specific text in the data stream returned by the server; everything read up to (and including) that text is then returned to you in a buffer, which you can either examine or discard. Here's an example with a modified version of the OnMenuConnect implementation that looks for login and password prompts. The thing that makes it somewhat more complicated is that you're mixing blocking functions (Search, in this case) with an asynchronous session. The way that I did it here is that I only enable asynchronous events after the "autologin" code has been executed (look down where the "bAutoLogin" variable is declared). Keep in mind that, of course, the username and password will need to changed, as probably will the prompt string and so forth. Your specific requirements would dictate what you would specifically need to do.


// Create an instance of the CConnectDlg dialog class which will
// allow the user to specify the host name, port, etc. for the
// connection
CMainWnd::OnMenuConnect(), int
{
    CConnectDlg dlg;

    dlg.Create(0,0,300,140,0x80C00080,0,"Connect",this);
    dlg.AddControl(CTSTATIC,"Server:",14,17,41,18,0x5000010B,0x0,IDC_LABEL_HOSTNAME);
    dlg.AddControl(CTEDIT,"",71,14,121,20,0x50810000,0x200,IDC_EDIT_HOSTNAME);
    dlg.AddControl(CTSTATIC,"Port:",201,17,31,18,0x5000010B,0x0,IDC_LABEL_HOSTPORT);
    dlg.AddControl(CTEDIT,"",247,14,34,20,0x50810000,0x200,IDC_EDIT_HOSTPORT);
    dlg.AddControl(CTSTATIC,"Emulation:",14,46,54,18,0x5000010B,0x0,IDC_LABEL_EMULATION);
    dlg.AddControl(CTCOMBOBOX,"ComboBox1",71,43,121,41,0x50A10603,0x0,IDC_COMBO_EMULATION);
    dlg.AddControl(CTSTATIC,"Timeout:",201,46,47,18,0x5000010B,0x0,IDC_LABEL_TIMEOUT);
    dlg.AddControl(CTEDIT,"",247,43,34,20,0x50810000,0x200,IDC_EDIT_TIMEOUT);
    dlg.AddControl(CTDEFBUTTON,"Connect",70,97,70,28,0x50010001,0x0,IDC_BUTTON_CONNECT);
    dlg.AddControl(CTBUTTON,"Cancel",161,97,70,28,0x50010000,0x0,IDC_BUTTON_CANCEL);

    dlg.SetConnectInfo(&m_conInfo);
    dlg.DoModal();

    // Check if the user pressed the Cancel button
    if (dlg.IsCanceled())
        return 0;

    // Get the connection info specified by the user
    dlg.GetConnectInfo(&m_conInfo);

    // Set the emulation based on what the user selected
    if (SetEmulation(m_conInfo.m_nEmulation))
    {
        string strTermType;
        if (GetTerminalType(strTermType))
        {
            g_pClient->SetTerminalType(strTermType);
        }
    }
    else
    {
        MessageBox(this, LoadString(IDS_BADEMULATION), "Error");
        return 0;
    }

    // Automatic login information for this session
    int bAutoLogin = true;
    string strUserName = "user";
    string strPassword = "secret";
   
    // Put the Telnet client into asynchronous (non-blocking)
    // mode where events will be generated; this should only
    // be done if we're not automatically logging in
    if (!bAutoLogin)
    {
        g_pClient->EnableEvents(m_hWnd, WM_CLIENT_EVENT);
    }
   
    // Establish a connection with the remote host
    if (g_pClient->Connect(m_conInfo.m_strHostName,
                           m_conInfo.m_nHostPort,
                           m_conInfo.m_nTimeout) = false)
    {
        g_pClient->ShowError();
        return 0;
    }

    if (bAutoLogin)
    {
        dstring strBuffer[1024];
        unsigned int dwLength = 1024;
        int bLoggedIn = false;
       
        // Set the timeout to a low value so that we don't wait a long
        // time if the wrong prompts are returned
        g_pClient->SetTimeout(5);
       
        // Search for the login prompt in the data returned by the server
        if (g_pClient->Search("ogin: ", strBuffer, dwLength))
        {
            // Send the username, terminated with a carriage-return/linefeed pair
            g_pClient->Write(strUserName + "\x0d\x0a", len(strUserName) + 2);

            // Search for the password prompt
            dwLength = 1024;
            if (g_pClient->Search("word: ", strBuffer, dwLength))
            {
                // Send the password, terminated with a carriage-return/linefeed pair
                g_pClient->Write(strPassword + "\x0d\x0a", len(strPassword) + 2);
                bLoggedIn = true;
            }
        }
       
        if (bLoggedIn)
        {
            // If we are logged in, then search for a command prompt (in this case,
            // it's the "$" sign on a UNIX system) and then send a command
            dwLength = 1024;
            if (g_pClient->Search("$ ", strBuffer, dwLength))
            {
                strBuffer = "/bin/date\x0d\x0a";
                g_pClient->Write(strBuffer, len(strBuffer));
            }
            else
            {
                MessageBox(this, "Unable to send command, prompt not found", "Error");
            }
        }
        else
        {
            MessageBox(this, "Login failed, no username or password prompt found", "Error");
        }

        // Restore the timeout period specified by the user
        g_pClient->SetTimeout(m_conInfo.m_nTimeout);

        // Enable event handling for the remainder of the session, now that
        // we have logged in successfully
        g_pClient->EnableEvents(m_hWnd, WM_CLIENT_EVENT);
    }
    else
    {
        // If we are not automatically logged in, start a timer; if the the
        // timeout period elapses, then the connection will be terminated
        // and the user will be warned
        m_pTimer = new(CConnectTimer, 1);
        m_pTimer->StartTimer((m_conInfo.m_nTimeout * 1000), this);
    }

    return 0;
}

Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

mlbaker4

Mike Or any one else who can guide me,

  I want to create the following and use them global in the program:

string strUserName;
string Password;

strUserName = "Mike"
strPassword = "password"

If I put these in global sub main ()

It compiles past this but lower in the programs where I actually use the strings I get an undefined variable error when I try to write the string.

My goal is to add text boxes that will allow the user to change these strings as passwords have to change on a regular basis.

If I use the same string in every connect section I get duplicate string errors.  I just can't figure out the syntax to be able to call the strings globally

Mike Stefanik

If you want to create a global variable, make sure you define it outside of any functions or methods. Look at how the g_pMainWnd and g_pClient variables are declared (the coding style that I use, I prefix globals with g_ just to make it clear in the source). With Aurora, it would look like:


global g_strUserName;
string g_strUserName;


If you want to use that global in another source module, you'd declare it as:


extern g_strUserName as string;


Personally I wish Aurora would combine the global syntax so you could write something like "global string g_strUserName", but right now I believe that you have to use two different statements (if I'm wrong on this, and something's changed, I'm sure Paul will correct me there).
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation