File indexing completed on 2024-04-21 05:51:27
0001 /* 0002 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 // Own 0008 #include "SSHProcessInfo.h" 0009 0010 // Unix 0011 #ifndef Q_OS_WIN 0012 #include <arpa/inet.h> 0013 #include <cerrno> 0014 #include <netinet/in.h> 0015 #include <pwd.h> 0016 #include <sys/param.h> 0017 #include <sys/socket.h> 0018 #include <unistd.h> 0019 #endif // Q_OS_WIN 0020 0021 // Qt 0022 #include <QDebug> 0023 0024 using namespace Konsole; 0025 0026 SSHProcessInfo::SSHProcessInfo(const ProcessInfo &process) 0027 : _process(process) 0028 , _user(QString()) 0029 , _host(QString()) 0030 , _port(QString()) 0031 , _command(QString()) 0032 { 0033 bool ok = false; 0034 0035 // check that this is a SSH process 0036 const QString &name = _process.name(&ok); 0037 0038 if (!ok || name != QLatin1String("ssh")) { 0039 if (!ok) { 0040 qWarning() << "Could not read process info"; 0041 } else { 0042 qWarning() << "Process is not a SSH process"; 0043 } 0044 0045 return; 0046 } 0047 0048 // read arguments 0049 const QVector<QString> &args = _process.arguments(&ok); 0050 0051 // SSH options 0052 // these are taken from the SSH manual ( accessed via 'man ssh' ) 0053 0054 // options which take no arguments 0055 static const QString noArgumentOptions(QStringLiteral("1246AaCfgKkMNnqsTtVvXxYy")); 0056 // options which take one argument 0057 static const QString singleArgumentOptions(QStringLiteral("bcDeFIiJLlmOopRSWw")); 0058 0059 if (ok) { 0060 // find the username, host and command arguments 0061 // 0062 // the username/host is assumed to be the first argument 0063 // which is not an option 0064 // ( ie. does not start with a dash '-' character ) 0065 // or an argument to a previous option. 0066 // 0067 // the command, if specified, is assumed to be the argument following 0068 // the username and host 0069 // 0070 // note that we skip the argument at index 0 because that is the 0071 // program name ( expected to be 'ssh' in this case ) 0072 for (int i = 1; i < args.count(); i++) { 0073 // If this one is an option ... 0074 // Most options together with its argument will be skipped. 0075 if (args[i].startsWith(QLatin1Char('-'))) { 0076 const QChar optionChar = (args[i].length() > 1) ? args[i][1] : QLatin1Char('\0'); 0077 // for example: -p2222 or -p 2222 ? 0078 const bool optionArgumentCombined = args[i].length() > 2; 0079 0080 if (noArgumentOptions.contains(optionChar)) { 0081 continue; 0082 } else if (singleArgumentOptions.contains(optionChar)) { 0083 QString argument; 0084 if (optionArgumentCombined) { 0085 argument = args[i].mid(2); 0086 } else { 0087 // Verify correct # arguments are given 0088 if ((i + 1) < args.count()) { 0089 argument = args[i + 1]; 0090 } 0091 i++; 0092 } 0093 0094 // support using `-l user` to specify username. 0095 if (optionChar == QLatin1Char('l')) { 0096 _user = argument; 0097 } 0098 // support using `-p port` to specify port. 0099 else if (optionChar == QLatin1Char('p')) { 0100 _port = argument; 0101 } 0102 0103 continue; 0104 } 0105 } 0106 0107 // check whether the host has been found yet 0108 // if not, this must be the username/host argument 0109 if (_host.isEmpty()) { 0110 // check to see if only a hostname is specified, or whether 0111 // both a username and host are specified ( in which case they 0112 // are separated by an '@' character: username@host ) 0113 0114 const int separatorPosition = args[i].indexOf(QLatin1Char('@')); 0115 if (separatorPosition != -1) { 0116 // username and host specified 0117 _user = args[i].left(separatorPosition); 0118 _host = args[i].mid(separatorPosition + 1); 0119 } else { 0120 // just the host specified 0121 _host = args[i]; 0122 } 0123 } else { 0124 // host has already been found, this must be part of the 0125 // command arguments. 0126 // Note this is not 100% correct. If any of the above 0127 // noArgumentOptions or singleArgumentOptions are found, this 0128 // will not be correct (example ssh server top -i 50) 0129 // Suggest putting ssh command in quotes 0130 if (_command.isEmpty()) { 0131 _command = args[i]; 0132 } else { 0133 _command = _command + QLatin1Char(' ') + args[i]; 0134 } 0135 } 0136 } 0137 } else { 0138 qWarning() << "Could not read arguments"; 0139 0140 return; 0141 } 0142 } 0143 0144 QString SSHProcessInfo::userName() const 0145 { 0146 return _user; 0147 } 0148 0149 QString SSHProcessInfo::host() const 0150 { 0151 return _host; 0152 } 0153 0154 QString SSHProcessInfo::port() const 0155 { 0156 return _port; 0157 } 0158 0159 QString SSHProcessInfo::command() const 0160 { 0161 return _command; 0162 } 0163 0164 QString SSHProcessInfo::format(const QString &input) const 0165 { 0166 QString output(input); 0167 0168 // search for and replace known markers 0169 output.replace(QLatin1String("%u"), _user); 0170 0171 // provide 'user@' if user is defined -- this makes nicer 0172 // remote tabs possible: "%U%h %c" => User@Host Command 0173 // => Host Command 0174 // Depending on whether -l was passed to ssh (which is mostly not the 0175 // case due to ~/.ssh/config). 0176 if (_user.isEmpty()) { 0177 output.remove(QLatin1String("%U")); 0178 } else { 0179 output.replace(QLatin1String("%U"), _user + QLatin1Char('@')); 0180 } 0181 0182 #ifdef Q_OS_WIN 0183 // TODO 0184 #else 0185 // test whether host is an ip address 0186 // in which case 'short host' and 'full host' 0187 // markers in the input string are replaced with 0188 // the full address 0189 struct in_addr address; 0190 const bool isIpAddress = inet_aton(_host.toLocal8Bit().constData(), &address) != 0; 0191 if (isIpAddress) { 0192 output.replace(QLatin1String("%h"), _host); 0193 } else { 0194 output.replace(QLatin1String("%h"), _host.left(_host.indexOf(QLatin1Char('.')))); 0195 } 0196 0197 QString fullHost = _host; 0198 if (!_port.isEmpty() && _port != QLatin1String("22")) { 0199 fullHost.append(QLatin1Char(':')); 0200 fullHost.append(_port); 0201 } 0202 output.replace(QLatin1String("%H"), fullHost); 0203 output.replace(QLatin1String("%c"), _command); 0204 #endif 0205 return output; 0206 }