common: Add '$i:identd', '*' for empty, tooltips
authorShane Synan <digitalcircuit36939@gmail.com>
Wed, 30 May 2018 00:27:37 +0000 (19:27 -0500)
committerManuel Nickschas <sputnick@quassel-irc.org>
Wed, 6 Jun 2018 18:08:26 +0000 (20:08 +0200)
Add "$i:identd" paramater variable to aliases, representing the ident
if verified, or "*" if unknown or unverified (prefixed with '~').

This allows for a general banning rule to target all idents for a
nickname that doesn't provide verified idents (e.g. general clients),
while limiting bans to specific idents for a nickname that provides a
verified ident (e.g. shared bouncer/core).

For example...
/mode channel +b *!$1:identd:$1:hostname

...would target "*!*@bad.example.com" for someone connecting without
an identd server, and "*!userid@shared.example.com" for someone
connecting with an identd server.

For all nickname-based parameters, substitute in "*" when empty
instead of leaving it blank.  This means using the variables for
nicknames that haven't been WHO'd will still provide results.

This may be dangerous if using the above example /ban alias on a
nickname that doesn't have hostname information available, e.g. if
you joined a channel after they did.

Update alias tooltips with 'identd', and totally revamp them using
the table layout for better readability and (hopefully?) easier
translation.

Update the comments in AliasManager, too, so it's a bit more
readable.

Thanks Exterminador for the suggestion/reporting, and @justJanne for
further clarifications!

src/common/aliasmanager.cpp
src/qtui/settingspages/aliasesmodel.cpp

index 9fcc567..359db28 100644 (file)
@@ -187,10 +187,43 @@ void AliasManager::expand(const QString &alias, const BufferInfo &bufferInfo, co
         }
 
         for (int j = params.count(); j > 0; j--) {
         }
 
         for (int j = params.count(); j > 0; j--) {
+            // Find the referenced IRC user...
             IrcUser *ircUser = net->ircUser(params[j - 1]);
             IrcUser *ircUser = net->ircUser(params[j - 1]);
-            command = command.replace(QString("$%1:hostname").arg(j), ircUser ? ircUser->host() : QString("*"));
-            command = command.replace(QString("$%1:ident").arg(j), ircUser ? ircUser->user() : QString("*"));
-            command = command.replace(QString("$%1:account").arg(j), ircUser ? ircUser->account() : QString("*"));
+            // ...and replace components, using short-circuit evaluation as ircUser might be null
+
+            // Account, or "*" if blank/nonexistent/logged out
+            command = command.replace(
+                        QString("$%1:account").arg(j),
+                        (ircUser && !ircUser->account().isEmpty()) ? ircUser->account()
+                                                                   : QString("*"));
+
+            // Hostname, or "*" if blank/nonexistent
+            command = command.replace(
+                        QString("$%1:hostname").arg(j),
+                        (ircUser && !ircUser->host().isEmpty()) ? ircUser->host() : QString("*"));
+
+            // Identd
+            // Ident if verified, or "*" if blank/unknown/unverified (prefixed with "~")
+            //
+            // Most IRC daemons have the option to prefix an ident with "~" if it could not be
+            // verified via an identity daemon such as oidentd.  In these cases, it can be handy to
+            // have a way to ban via ident if verified, or all idents if not verified.  If the
+            // server does not verify idents, it usually won't add "~".
+            //
+            // Identd must be replaced before ident to avoid being treated as "$i:ident" + "d"
+            command = command.replace(
+                        QString("$%1:identd").arg(j),
+                        (ircUser && !ircUser->user().isEmpty()
+                         && !ircUser->user().startsWith("~"))
+                        ? ircUser->user() : QString("*"));
+
+            // Ident, or "*" if blank/nonexistent
+            command = command.replace(
+                        QString("$%1:ident").arg(j),
+                        (ircUser && !ircUser->user().isEmpty()) ? ircUser->user() : QString("*"));
+
+            // Nickname
+            // Must be replaced last to avoid interferring with more specific aliases
             command = command.replace(QString("$%1").arg(j), params[j - 1]);
         }
         command = command.replace("$0", msg);
             command = command.replace(QString("$%1").arg(j), params[j - 1]);
         }
         command = command.replace("$0", msg);
index fa885b9..569f56c 100644 (file)
@@ -58,19 +58,78 @@ QVariant AliasesModel::data(const QModelIndex &index, int role) const
                       "It can be used as a regular slash command.<br /><br />"
                       "<b>Example:</b> \"foo\" can be used per /foo");
         case 1:
                       "It can be used as a regular slash command.<br /><br />"
                       "<b>Example:</b> \"foo\" can be used per /foo");
         case 1:
-            return tr("<b>The string the shortcut will be expanded to</b><br />"
-                      "<b>special variables:</b><br />"
-                      " - <b>$i</b> represents the i'th parameter.<br />"
-                      " - <b>$i..j</b> represents the i'th to j'th parameter separated by spaces.<br />"
-                      " - <b>$i..</b> represents all parameters from i on separated by spaces.<br />"
-                      " - <b>$i:hostname</b> represents the hostname of the user identified by the i'th parameter or a * if unknown.<br />"
-                      " - <b>$i:ident</b> represents the ident of the user identified by the i'th parameter or a * if unknown.<br />"
-                      " - <b>$i:account</b> represents the account of the user identified by the i'th parameter or a * if logged out or unknown.<br />"
-                      " - <b>$0</b> the whole string.<br />"
-                      " - <b>$nick</b> your current nickname<br />"
-                      " - <b>$channel</b> the name of the selected channel<br /><br />"
-                      "Multiple commands can be separated with semicolons<br /><br />"
-                      "<b>Example:</b> \"Test $1; Test $2; Test All $0\" will be expanded to three separate messages \"Test 1\", \"Test 2\" and \"Test All 1 2 3\" when called like /test 1 2 3");
+        {
+            // To avoid overwhelming the user, organize things into a table
+            QString strTooltip;
+            QTextStream tooltip( &strTooltip, QIODevice::WriteOnly );
+            tooltip << "<qt><style>.bold { font-weight: bold; } .italic { font-style: italic; }</style>";
+
+            // Function to add a row to the tooltip table
+            auto addRow = [&](
+                    const QString& key, const QString& value = QString(), bool condition = true) {
+                if (condition) {
+                    if (value.isEmpty()) {
+                        tooltip << "<tr><td class='italic' align='left' colspan='2'>"
+                                      << key << "</td></tr>";
+                    } else {
+                        tooltip << "<tr><td class='bold' align='left'>"
+                                      << key << "</td><td>" << value << "</td></tr>";
+                    }
+                }
+            };
+
+            tooltip << "<p class='bold'>"
+                    << tr("The string the shortcut will be expanded to") << "</p>";
+
+            tooltip << "<p class='bold' align='center'>"
+                    << tr("Special variables") << "</p>";
+
+            // Variable option table
+            tooltip << "<table cellspacing='5' cellpadding='0'>";
+
+            // Parameter variables
+            addRow(tr("Parameter variables"));
+            addRow("$i", tr("i'th parameter"));
+            addRow("$i..j", tr("i'th to j'th parameter separated by spaces"));
+            addRow("$i..", tr("all parameters from i on separated by spaces"));
+
+            // IrcUser handling
+            addRow(tr("Nickname parameter variables"));
+            addRow("$i:account",
+                   tr("account of user identified by i'th parameter, or a '*' if logged out or "
+                      "unknown"));
+            addRow("$i:hostname",
+                   tr("hostname of user identified by i'th parameter, or a '*' if unknown"));
+            addRow("$i:ident",
+                   tr("ident of user identified by i'th parameter, or a '*' if unknown"));
+            addRow("$i:identd",
+                   tr("ident of user identified by i'th parameter if verified, or a '*' if unknown "
+                      "or unverified (prefixed with '~')"));
+
+            // General variables
+            addRow(tr("General variables"));
+            addRow("$0", tr("the whole string"));
+            addRow("$nick", tr("your current nickname"));
+            addRow("$channel", tr("the name of the selected channel"));
+
+            // End table
+            tooltip << "</table>";
+
+            // Example header
+            tooltip << "<p>"
+                    << tr("Multiple commands can be separated with semicolons") << "</p>";
+            // Example
+            tooltip << "<p>";
+            tooltip << QString("<p><span class='bold'>%1</span> %2<br />").arg(
+                           tr("Example:"), tr("\"Test $1; Test $2; Test All $0\""));
+            tooltip << tr("...will be expanded to three separate messages \"Test 1\", \"Test 2\" "
+                          "and \"Test All 1 2 3\" when called like <i>/test 1 2 3</i>")
+                    << "</p>";
+
+            // End tooltip
+            tooltip << "</qt>";
+            return strTooltip;
+        }
         default:
             return QVariant();
         }
         default:
             return QVariant();
         }