Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@ If you want to get a hold of the latest version, just go to the Releases page an

Alternatively, most distributions such as Ubuntu, Fedora or Arch already package cool-retro-term in their official repositories.

## Command line

cool-retro-term can run a command on startup with `-e`:

```sh
cool-retro-term -e top
```

It can also open a telnet connection on startup:

```sh
cool-retro-term --telnet-host example.com --telnet-port 23
```

For telnet servers that prompt for credentials, cool-retro-term can send a username and password when it detects login and password prompts:

```sh
cool-retro-term --telnet-host example.com --telnet-user myuser --telnet-password mypass
```

The telnet options use the system `telnet` command, so `telnet` must be installed and available in `PATH`. Passing passwords on the command line can expose them through shell history or process listings.

## Building

Check out the wiki and follow the instructions on how to build it on [Linux](https://github.com/Swordfish90/cool-retro-term/wiki/Build-Instructions-(Linux)) and [macOS](https://github.com/Swordfish90/cool-retro-term/wiki/Build-Instructions-(macOS)).
41 changes: 34 additions & 7 deletions app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <QQmlContext>
#include <QStringList>
#include <QVariantMap>

#include <QDir>

Expand Down Expand Up @@ -31,7 +32,7 @@
QString getNamedArgument(QStringList args, QString name, QString defaultName)
{
int index = args.indexOf(name);
return (index != -1) ? args[index + 1] : QString(defaultName);
return (index != -1 && index + 1 < args.size()) ? args[index + 1] : QString(defaultName);
}

QString getNamedArgument(QStringList args, QString name)
Expand Down Expand Up @@ -64,12 +65,16 @@ int main(int argc, char *argv[])

if (argc>1 && (!strcmp(argv[1],"-h") || !strcmp(argv[1],"--help"))) {
QTextStream cout(stdout, QIODevice::WriteOnly);
cout << "Usage: " << argv[0] << " [--default-settings] [--workdir <dir>] [--program <prog>] [-p|--profile <prof>] [--fullscreen] [-h|--help]" << Qt::endl;
cout << "Usage: " << argv[0] << " [--default-settings] [--workdir <dir>] [--program <prog>] [-p|--profile <prof>] [--fullscreen] [--telnet-host <host>] [--telnet-port <port>] [--telnet-user <user>] [--telnet-password <password>] [-h|--help]" << Qt::endl;
cout << " --default-settings Run cool-retro-term with the default settings" << Qt::endl;
cout << " --workdir <dir> Change working directory to 'dir'" << Qt::endl;
cout << " -e <cmd> Command to execute. This option will catch all following arguments, so use it as the last option." << Qt::endl;
cout << " --fullscreen Run cool-retro-term in fullscreen." << Qt::endl;
cout << " -p|--profile <prof> Run cool-retro-term with the given profile." << Qt::endl;
cout << " --telnet-host <host> Connect to a host with telnet on startup." << Qt::endl;
cout << " --telnet-port <port> Port for --telnet-host. Defaults to telnet's default port." << Qt::endl;
cout << " --telnet-user <user> Send this username when a login prompt is detected." << Qt::endl;
cout << " --telnet-password <pass> Send this password when a password prompt is detected." << Qt::endl;
cout << " -h|--help Print this help." << Qt::endl;
cout << " --verbose Print additional information such as profiles and settings." << Qt::endl;
return 0;
Expand All @@ -90,12 +95,19 @@ int main(int argc, char *argv[])
app.setOrganizationDomain(QStringLiteral("cool-retro-term"));
app.setApplicationVersion(appVersion);

QStringList args = app.arguments();
const bool hasStartupCommand = args.contains(QStringLiteral("-e")) || args.contains(QStringLiteral("--telnet-host"));

KDSingleApplication singleApp(QStringLiteral("cool-retro-term"));

if (!singleApp.isPrimaryInstance()) {
if (singleApp.sendMessage("new-window"))
return 0;
qWarning() << "KDSingleApplication: primary not reachable, continuing as independent instance.";
if (hasStartupCommand) {
qInfo() << "KDSingleApplication: startup command requested, continuing as independent instance.";
} else {
if (singleApp.sendMessage("new-window"))
return 0;
qWarning() << "KDSingleApplication: primary not reachable, continuing as independent instance.";
}
}

QQmlApplicationEngine engine;
Expand All @@ -114,18 +126,33 @@ int main(int argc, char *argv[])
#endif

// Manage command line arguments from the cpp side
QStringList args = app.arguments();

// Manage default command
QStringList cmdList;
const QString telnetHost = getNamedArgument(args, "--telnet-host");
const QString telnetPort = getNamedArgument(args, "--telnet-port");
const QString telnetUser = getNamedArgument(args, "--telnet-user");
const QString telnetPassword = getNamedArgument(args, "--telnet-password");

if (args.contains("-e")) {
cmdList << args.mid(args.indexOf("-e") + 1);
} else if (!telnetHost.isEmpty()) {
cmdList << QStringLiteral("telnet") << telnetHost;
if (!telnetPort.isEmpty()) {
cmdList << telnetPort;
}
}

QVariantMap telnetLogin;
telnetLogin.insert(QStringLiteral("enabled"), !telnetHost.isEmpty() && (!telnetUser.isEmpty() || !telnetPassword.isEmpty()));
telnetLogin.insert(QStringLiteral("username"), telnetUser);
telnetLogin.insert(QStringLiteral("password"), telnetPassword);

QVariant command(cmdList.empty() ? QVariant() : cmdList[0]);
QVariant commandArgs(cmdList.size() <= 1 ? QVariant() : QVariant(cmdList.mid(1)));
engine.rootContext()->setContextProperty("appVersion", appVersion);
engine.rootContext()->setContextProperty("defaultCmd", command);
engine.rootContext()->setContextProperty("defaultCmdArgs", commandArgs);
engine.rootContext()->setContextProperty("telnetLogin", telnetLogin);

engine.rootContext()->setContextProperty("workdir", getNamedArgument(args, "--workdir", QDir::currentPath()));
engine.rootContext()->setContextProperty("fileIO", &fileIO);
Expand Down
30 changes: 30 additions & 0 deletions app/qml/PreprocessedTerminal.qml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Item{
property alias title: ksession.title
property alias kterminal: kterminal
property bool isActive: false
property int telnetLoginState: 0
property string telnetPromptBuffer: ""

property size terminalSize: kterminal.terminalSize
property size fontMetrics: kterminal.fontMetrics
Expand Down Expand Up @@ -125,6 +127,10 @@ Item{
onFinished: {
terminalContainer.sessionFinished()
}

onReceivedData: function(text) {
kterminal.handleTelnetLogin(text)
}
}

QMLTermScrollbar {
Expand Down Expand Up @@ -186,6 +192,30 @@ Item{
ksession.startShellProgram();
forceActiveFocus();
}

function handleTelnetLogin(text) {
if (!telnetLogin.enabled || telnetLoginState >= 2) {
return;
}

telnetPromptBuffer = (telnetPromptBuffer + text).slice(-512).toLowerCase();

if (telnetLoginState === 0
&& telnetLogin.username
&& /(login|username|user)[^a-z0-9]*:\s*$/.test(telnetPromptBuffer)) {
ksession.sendText(telnetLogin.username + "\r");
telnetLoginState = 1;
telnetPromptBuffer = "";
return;
}

if (telnetLogin.password
&& /password[^a-z0-9]*:\s*$/.test(telnetPromptBuffer)) {
ksession.sendText(telnetLogin.password + "\r");
telnetLoginState = 2;
telnetPromptBuffer = "";
}
}
Component.onCompleted: {
appSettings.fontManager.terminalFontChanged.connect(handleFontChanged);
appSettings.fontManager.refresh()
Expand Down
2 changes: 1 addition & 1 deletion qmltermwidget