Coverage Report

Created: 2025-11-15 06:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/znc/src/ClientCommand.cpp
Line
Count
Source
1
/*
2
 * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include <znc/Chan.h>
18
#include <znc/Client.h>
19
#include <znc/FileUtils.h>
20
#include <znc/IRCNetwork.h>
21
#include <znc/IRCSock.h>
22
#include <znc/Query.h>
23
#include <znc/Server.h>
24
#include <znc/User.h>
25
26
using std::map;
27
using std::set;
28
using std::vector;
29
30
0
void CClient::UserCommand(CString& sLine) {
31
0
    if (!m_pUser) {
32
0
        return;
33
0
    }
34
35
0
    if (sLine.empty()) {
36
0
        return;
37
0
    }
38
39
0
    bool bReturn = false;
40
0
    NETWORKMODULECALL(OnStatusCommand(sLine), m_pUser, m_pNetwork, this,
41
0
                      &bReturn);
42
0
    if (bReturn) return;
43
44
0
    const CString sCommand = sLine.Token(0);
45
46
0
    if (sCommand.Equals("HELP")) {
47
0
        HelpUser(sLine.Token(1));
48
0
    } else if (sCommand.Equals("LISTNICKS")) {
49
0
        if (!m_pNetwork) {
50
0
            PutStatus(t_s(
51
0
                "You must be connected with a network to use this command"));
52
0
            return;
53
0
        }
54
55
0
        CString sChan = sLine.Token(1);
56
57
0
        if (sChan.empty()) {
58
0
            PutStatus(t_s("Usage: ListNicks <#chan>"));
59
0
            return;
60
0
        }
61
62
0
        CChan* pChan = m_pNetwork->FindChan(sChan);
63
64
0
        if (!pChan) {
65
0
            PutStatus(t_f("You are not on [{1}]")(sChan));
66
0
            return;
67
0
        }
68
69
0
        if (!pChan->IsOn()) {
70
0
            PutStatus(t_f("You are not on [{1}] (trying)")(sChan));
71
0
            return;
72
0
        }
73
74
0
        const map<CString, CNick>& msNicks = pChan->GetNicks();
75
0
        CIRCSock* pIRCSock = m_pNetwork->GetIRCSock();
76
0
        const CString& sPerms = (pIRCSock) ? pIRCSock->GetPerms() : "";
77
78
0
        if (msNicks.empty()) {
79
0
            PutStatus(t_f("No nicks on [{1}]")(sChan));
80
0
            return;
81
0
        }
82
83
0
        CTable Table;
84
85
0
        for (unsigned int p = 0; p < sPerms.size(); p++) {
86
0
            CString sPerm;
87
0
            sPerm += sPerms[p];
88
0
            Table.AddColumn(sPerm);
89
0
        }
90
91
0
        Table.AddColumn(t_s("Nick"));
92
0
        Table.AddColumn(t_s("Ident"));
93
0
        Table.AddColumn(t_s("Host"));
94
95
0
        for (const auto& it : msNicks) {
96
0
            Table.AddRow();
97
98
0
            for (unsigned int b = 0; b < sPerms.size(); b++) {
99
0
                if (it.second.HasPerm(sPerms[b])) {
100
0
                    CString sPerm;
101
0
                    sPerm += sPerms[b];
102
0
                    Table.SetCell(sPerm, sPerm);
103
0
                }
104
0
            }
105
106
0
            Table.SetCell(t_s("Nick"), it.second.GetNick());
107
0
            Table.SetCell(t_s("Ident"), it.second.GetIdent());
108
0
            Table.SetCell(t_s("Host"), it.second.GetHost());
109
0
        }
110
111
0
        PutStatus(Table);
112
0
    } else if (sCommand.Equals("ATTACH")) {
113
0
        if (!m_pNetwork) {
114
0
            PutStatus(t_s(
115
0
                "You must be connected with a network to use this command"));
116
0
            return;
117
0
        }
118
119
0
        CString sPatterns = sLine.Token(1, true);
120
121
0
        if (sPatterns.empty()) {
122
0
            PutStatus(t_s("Usage: Attach <#chans>"));
123
0
            return;
124
0
        }
125
126
0
        set<CChan*> sChans = MatchChans(sPatterns);
127
0
        unsigned int uAttachedChans = AttachChans(sChans);
128
129
0
        PutStatus(t_p("There was {1} channel matching [{2}]",
130
0
                      "There were {1} channels matching [{2}]",
131
0
                      sChans.size())(sChans.size(), sPatterns));
132
0
        PutStatus(t_p("Attached {1} channel", "Attached {1} channels",
133
0
                      uAttachedChans)(uAttachedChans));
134
0
    } else if (sCommand.Equals("DETACH")) {
135
0
        if (!m_pNetwork) {
136
0
            PutStatus(t_s(
137
0
                "You must be connected with a network to use this command"));
138
0
            return;
139
0
        }
140
141
0
        CString sPatterns = sLine.Token(1, true);
142
143
0
        if (sPatterns.empty()) {
144
0
            PutStatus(t_s("Usage: Detach <#chans>"));
145
0
            return;
146
0
        }
147
148
0
        set<CChan*> sChans = MatchChans(sPatterns);
149
0
        unsigned int uDetached = DetachChans(sChans);
150
151
0
        PutStatus(t_p("There was {1} channel matching [{2}]",
152
0
                      "There were {1} channels matching [{2}]",
153
0
                      sChans.size())(sChans.size(), sPatterns));
154
0
        PutStatus(t_p("Detached {1} channel", "Detached {1} channels",
155
0
                      uDetached)(uDetached));
156
0
    } else if (sCommand.Equals("VERSION")) {
157
0
        PutStatus(CZNC::GetTag());
158
0
        PutStatus(CZNC::GetCompileOptionsString());
159
0
    } else if (sCommand.Equals("MOTD") || sCommand.Equals("ShowMOTD")) {
160
0
        if (!SendMotd()) {
161
0
            PutStatus(t_s("There is no MOTD set."));
162
0
        }
163
0
    } else if (m_pUser->IsAdmin() && sCommand.Equals("Rehash")) {
164
0
        CString sRet;
165
166
0
        if (CZNC::Get().RehashConfig(sRet)) {
167
0
            PutStatus(t_s("Rehashing succeeded!"));
168
0
        } else {
169
0
            PutStatus(t_f("Rehashing failed: {1}")(sRet));
170
0
        }
171
0
    } else if (m_pUser->IsAdmin() && sCommand.Equals("SaveConfig")) {
172
0
        if (CZNC::Get().WriteConfig()) {
173
0
            PutStatus(t_f("Wrote config to {1}")(CZNC::Get().GetConfigFile()));
174
0
        } else {
175
0
            PutStatus(t_s("Error while trying to write config."));
176
0
        }
177
0
    } else if (sCommand.Equals("LISTCLIENTS")) {
178
0
        CUser* pUser = m_pUser;
179
0
        CString sNick = sLine.Token(1);
180
181
0
        if (!sNick.empty()) {
182
0
            if (!m_pUser->IsAdmin()) {
183
0
                PutStatus(t_s("Usage: ListClients"));
184
0
                return;
185
0
            }
186
187
0
            pUser = CZNC::Get().FindUser(sNick);
188
189
0
            if (!pUser) {
190
0
                PutStatus(t_f("No such user: {1}")(sNick));
191
0
                return;
192
0
            }
193
0
        }
194
195
0
        vector<CClient*> vClients = pUser->GetAllClients();
196
197
0
        if (vClients.empty()) {
198
0
            PutStatus(t_s("No clients are connected"));
199
0
            return;
200
0
        }
201
202
0
        CTable Table;
203
0
        Table.AddColumn(t_s("Host", "listclientscmd"));
204
0
        Table.AddColumn(t_s("Network", "listclientscmd"));
205
0
        Table.AddColumn(t_s("Identifier", "listclientscmd"));
206
207
0
        for (const CClient* pClient : vClients) {
208
0
            Table.AddRow();
209
0
            Table.SetCell(t_s("Host", "listclientscmd"),
210
0
                          pClient->GetRemoteIP());
211
0
            if (pClient->GetNetwork()) {
212
0
                Table.SetCell(t_s("Network", "listclientscmd"),
213
0
                              pClient->GetNetwork()->GetName());
214
0
            }
215
0
            Table.SetCell(t_s("Identifier", "listclientscmd"),
216
0
                          pClient->GetIdentifier());
217
0
        }
218
219
0
        PutStatus(Table);
220
0
    } else if (m_pUser->IsAdmin() && sCommand.Equals("LISTUSERS")) {
221
0
        const map<CString, CUser*>& msUsers = CZNC::Get().GetUserMap();
222
0
        CTable Table;
223
0
        Table.AddColumn(t_s("Username", "listuserscmd"));
224
0
        Table.AddColumn(t_s("Networks", "listuserscmd"));
225
0
        Table.AddColumn(t_s("Clients", "listuserscmd"));
226
227
0
        for (const auto& it : msUsers) {
228
0
            Table.AddRow();
229
0
            Table.SetCell(t_s("Username", "listuserscmd"), it.first);
230
0
            Table.SetCell(t_s("Networks", "listuserscmd"),
231
0
                          CString(it.second->GetNetworks().size()));
232
0
            Table.SetCell(t_s("Clients", "listuserscmd"),
233
0
                          CString(it.second->GetAllClients().size()));
234
0
        }
235
236
0
        PutStatus(Table);
237
0
    } else if (m_pUser->IsAdmin() && sCommand.Equals("LISTALLUSERNETWORKS")) {
238
0
        const map<CString, CUser*>& msUsers = CZNC::Get().GetUserMap();
239
0
        CTable Table;
240
0
        Table.AddColumn(t_s("Username", "listallusernetworkscmd"));
241
0
        Table.AddColumn(t_s("Network", "listallusernetworkscmd"));
242
0
        Table.AddColumn(t_s("Clients", "listallusernetworkscmd"));
243
0
        Table.AddColumn(t_s("On IRC", "listallusernetworkscmd"));
244
0
        Table.AddColumn(t_s("IRC Server", "listallusernetworkscmd"));
245
0
        Table.AddColumn(t_s("IRC User", "listallusernetworkscmd"));
246
0
        Table.AddColumn(t_s("Channels", "listallusernetworkscmd"));
247
248
0
        for (const auto& it : msUsers) {
249
0
            Table.AddRow();
250
0
            Table.SetCell(t_s("Username", "listallusernetworkscmd"), it.first);
251
0
            Table.SetCell(t_s("Network", "listallusernetworkscmd"), t_s("N/A"));
252
0
            Table.SetCell(t_s("Clients", "listallusernetworkscmd"),
253
0
                          CString(it.second->GetUserClients().size()));
254
255
0
            const vector<CIRCNetwork*>& vNetworks = it.second->GetNetworks();
256
257
0
            for (const CIRCNetwork* pNetwork : vNetworks) {
258
0
                Table.AddRow();
259
0
                if (pNetwork == vNetworks.back()) {
260
0
                    Table.SetCell(t_s("Username", "listallusernetworkscmd"),
261
0
                                  "`-");
262
0
                } else {
263
0
                    Table.SetCell(t_s("Username", "listallusernetworkscmd"),
264
0
                                  "|-");
265
0
                }
266
0
                Table.SetCell(t_s("Network", "listallusernetworkscmd"),
267
0
                              pNetwork->GetName());
268
0
                Table.SetCell(t_s("Clients", "listallusernetworkscmd"),
269
0
                              CString(pNetwork->GetClients().size()));
270
0
                if (pNetwork->IsIRCConnected()) {
271
0
                    Table.SetCell(t_s("On IRC", "listallusernetworkscmd"),
272
0
                                  t_s("Yes", "listallusernetworkscmd"));
273
0
                    Table.SetCell(t_s("IRC Server", "listallusernetworkscmd"),
274
0
                                  pNetwork->GetIRCServer());
275
0
                    Table.SetCell(t_s("IRC User", "listallusernetworkscmd"),
276
0
                                  pNetwork->GetIRCNick().GetNickMask());
277
0
                    Table.SetCell(t_s("Channels", "listallusernetworkscmd"),
278
0
                                  CString(pNetwork->GetChans().size()));
279
0
                } else {
280
0
                    Table.SetCell(t_s("On IRC", "listallusernetworkscmd"),
281
0
                                  t_s("No", "listallusernetworkscmd"));
282
0
                }
283
0
            }
284
0
        }
285
286
0
        PutStatus(Table);
287
0
    } else if (m_pUser->IsAdmin() && sCommand.Equals("SetMOTD")) {
288
0
        CString sMessage = sLine.Token(1, true);
289
290
0
        if (sMessage.empty()) {
291
0
            PutStatus(t_s("Usage: SetMOTD <message>"));
292
0
        } else {
293
0
            CZNC::Get().SetMotd(sMessage);
294
0
            PutStatus(t_f("MOTD set to: {1}")(sMessage));
295
0
        }
296
0
    } else if (m_pUser->IsAdmin() && sCommand.Equals("AddMOTD")) {
297
0
        CString sMessage = sLine.Token(1, true);
298
299
0
        if (sMessage.empty()) {
300
0
            PutStatus(t_s("Usage: AddMOTD <message>"));
301
0
        } else {
302
0
            CZNC::Get().AddMotd(sMessage);
303
0
            PutStatus(t_f("Added [{1}] to MOTD")(sMessage));
304
0
        }
305
0
    } else if (m_pUser->IsAdmin() && sCommand.Equals("ClearMOTD")) {
306
0
        CZNC::Get().ClearMotd();
307
0
        PutStatus(t_s("Cleared MOTD"));
308
0
    } else if (m_pUser->IsAdmin() && sCommand.Equals("BROADCAST")) {
309
0
        CZNC::Get().Broadcast(sLine.Token(1, true));
310
0
    } else if (m_pUser->IsAdmin() &&
311
0
               (sCommand.Equals("SHUTDOWN") || sCommand.Equals("RESTART"))) {
312
0
        bool bRestart = sCommand.Equals("RESTART");
313
0
        CString sMessage = sLine.Token(1, true);
314
0
        bool bForce = false;
315
316
0
        if (sMessage.Token(0).Equals("FORCE")) {
317
0
            bForce = true;
318
0
            sMessage = sMessage.Token(1, true);
319
0
        }
320
321
0
        if (sMessage.empty()) {
322
            // No t_s here because language of user can be different
323
0
            sMessage = (bRestart ? "ZNC is being restarted NOW!"
324
0
                                 : "ZNC is being shut down NOW!");
325
0
        }
326
327
0
        if (!CZNC::Get().WriteConfig() && !bForce) {
328
0
            PutStatus(
329
0
                t_f("ERROR: Writing config file to disk failed! Aborting. Use "
330
0
                    "{1} FORCE to ignore.")(sCommand.AsUpper()));
331
0
        } else {
332
0
            CZNC::Get().Broadcast(sMessage);
333
0
            throw CException(bRestart ? CException::EX_Restart
334
0
                                      : CException::EX_Shutdown);
335
0
        }
336
0
    } else if (sCommand.Equals("JUMP") || sCommand.Equals("CONNECT")) {
337
0
        if (!m_pNetwork) {
338
0
            PutStatus(t_s(
339
0
                "You must be connected with a network to use this command"));
340
0
            return;
341
0
        }
342
343
0
        if (!m_pNetwork->HasServers()) {
344
0
            PutStatus(t_s("You don't have any servers added."));
345
0
            return;
346
0
        }
347
348
0
        CString sArgs = sLine.Token(1, true);
349
0
        sArgs.Trim();
350
0
        CServer* pServer = nullptr;
351
352
0
        if (!sArgs.empty()) {
353
0
            pServer = m_pNetwork->FindServer(sArgs);
354
0
            if (!pServer) {
355
0
                PutStatus(t_f("Server [{1}] not found")(sArgs));
356
0
                return;
357
0
            }
358
0
            m_pNetwork->SetNextServer(pServer);
359
360
            // If we are already connecting to some server,
361
            // we have to abort that attempt
362
0
            Csock* pIRCSock = GetIRCSock();
363
0
            if (pIRCSock && !pIRCSock->IsConnected()) {
364
0
                pIRCSock->Close();
365
0
            }
366
0
        }
367
368
0
        if (!pServer) {
369
0
            pServer = m_pNetwork->GetNextServer(false);
370
0
        }
371
372
0
        if (GetIRCSock()) {
373
0
            GetIRCSock()->Quit();
374
0
            if (pServer)
375
0
                PutStatus(t_f("Connecting to {1}...")(pServer->GetName()));
376
0
            else
377
0
                PutStatus(t_s("Jumping to the next server in the list..."));
378
0
        } else {
379
0
            if (pServer)
380
0
                PutStatus(t_f("Connecting to {1}...")(pServer->GetName()));
381
0
            else
382
0
                PutStatus(t_s("Connecting..."));
383
0
        }
384
385
0
        m_pNetwork->SetIRCConnectEnabled(true);
386
0
        return;
387
0
    } else if (sCommand.Equals("DISCONNECT")) {
388
0
        if (!m_pNetwork) {
389
0
            PutStatus(t_s(
390
0
                "You must be connected with a network to use this command"));
391
0
            return;
392
0
        }
393
394
0
        if (GetIRCSock()) {
395
0
            CString sQuitMsg = sLine.Token(1, true);
396
0
            GetIRCSock()->Quit(sQuitMsg);
397
0
        }
398
399
0
        m_pNetwork->SetIRCConnectEnabled(false);
400
0
        PutStatus(t_s("Disconnected from IRC. Use 'connect' to reconnect."));
401
0
        return;
402
0
    } else if (sCommand.Equals("ENABLECHAN")) {
403
0
        if (!m_pNetwork) {
404
0
            PutStatus(t_s(
405
0
                "You must be connected with a network to use this command"));
406
0
            return;
407
0
        }
408
409
0
        CString sPatterns = sLine.Token(1, true);
410
411
0
        if (sPatterns.empty()) {
412
0
            PutStatus(t_s("Usage: EnableChan <#chans>"));
413
0
        } else {
414
0
            set<CChan*> sChans = MatchChans(sPatterns);
415
416
0
            unsigned int uEnabled = 0;
417
0
            for (CChan* pChan : sChans) {
418
0
                if (!pChan->IsDisabled()) continue;
419
0
                uEnabled++;
420
0
                pChan->Enable();
421
0
            }
422
423
0
            PutStatus(t_p("There was {1} channel matching [{2}]",
424
0
                          "There were {1} channels matching [{2}]",
425
0
                          sChans.size())(sChans.size(), sPatterns));
426
0
            PutStatus(t_p("Enabled {1} channel", "Enabled {1} channels",
427
0
                          uEnabled)(uEnabled));
428
0
        }
429
0
    } else if (sCommand.Equals("DISABLECHAN")) {
430
0
        if (!m_pNetwork) {
431
0
            PutStatus(t_s(
432
0
                "You must be connected with a network to use this command"));
433
0
            return;
434
0
        }
435
436
0
        CString sPatterns = sLine.Token(1, true);
437
438
0
        if (sPatterns.empty()) {
439
0
            PutStatus(t_s("Usage: DisableChan <#chans>"));
440
0
        } else {
441
0
            set<CChan*> sChans = MatchChans(sPatterns);
442
443
0
            unsigned int uDisabled = 0;
444
0
            for (CChan* pChan : sChans) {
445
0
                if (pChan->IsDisabled()) continue;
446
0
                uDisabled++;
447
0
                pChan->Disable();
448
0
            }
449
450
0
            PutStatus(t_p("There was {1} channel matching [{2}]",
451
0
                          "There were {1} channels matching [{2}]",
452
0
                          sChans.size())(sChans.size(), sPatterns));
453
0
            PutStatus(t_p("Disabled {1} channel", "Disabled {1} channels",
454
0
                          uDisabled)(uDisabled));
455
0
        }
456
0
    } else if (sCommand.Equals("MOVECHAN")) {
457
0
        if (!m_pNetwork) {
458
0
            PutStatus(t_s(
459
0
                "You must be connected with a network to use this command"));
460
0
            return;
461
0
        }
462
463
0
        const auto sChan = sLine.Token(1);
464
0
        const auto sTarget = sLine.Token(2);
465
0
        if (sChan.empty() || sTarget.empty()) {
466
0
            PutStatus(t_s("Usage: MoveChan <#chan> <index>"));
467
0
            return;
468
0
        }
469
470
0
        unsigned int uIndex = sTarget.ToUInt();
471
472
0
        CString sError;
473
0
        if (m_pNetwork->MoveChan(sChan, uIndex - 1, sError))
474
0
            PutStatus(t_f("Moved channel {1} to index {2}")(sChan, uIndex));
475
0
        else
476
0
            PutStatus(sError);
477
0
    } else if (sCommand.Equals("SWAPCHANS")) {
478
0
        if (!m_pNetwork) {
479
0
            PutStatus(t_s(
480
0
                "You must be connected with a network to use this command"));
481
0
            return;
482
0
        }
483
484
0
        const auto sChan1 = sLine.Token(1);
485
0
        const auto sChan2 = sLine.Token(2);
486
0
        if (sChan1.empty() || sChan2.empty()) {
487
0
            PutStatus(t_s("Usage: SwapChans <#chan1> <#chan2>"));
488
0
            return;
489
0
        }
490
491
0
        CString sError;
492
0
        if (m_pNetwork->SwapChans(sChan1, sChan2, sError))
493
0
            PutStatus(t_f("Swapped channels {1} and {2}")(sChan1, sChan2));
494
0
        else
495
0
            PutStatus(sError);
496
0
    } else if (sCommand.Equals("LISTCHANS")) {
497
0
        if (!m_pNetwork) {
498
0
            PutStatus(t_s(
499
0
                "You must be connected with a network to use this command"));
500
0
            return;
501
0
        }
502
503
0
        CIRCNetwork* pNetwork = m_pNetwork;
504
505
0
        const CString sUser = sLine.Token(1);
506
0
        const CString sNetwork = sLine.Token(2);
507
508
0
        if (!sUser.empty()) {
509
0
            if (!m_pUser->IsAdmin()) {
510
0
                PutStatus(t_s("Usage: ListChans"));
511
0
                return;
512
0
            }
513
514
0
            CUser* pUser = CZNC::Get().FindUser(sUser);
515
516
0
            if (!pUser) {
517
0
                PutStatus(t_f("No such user [{1}]")(sUser));
518
0
                return;
519
0
            }
520
521
0
            pNetwork = pUser->FindNetwork(sNetwork);
522
0
            if (!pNetwork) {
523
0
                PutStatus(t_f("User [{1}] doesn't have network [{2}]")(
524
0
                    sUser, sNetwork));
525
0
                return;
526
0
            }
527
0
        }
528
529
0
        const vector<CChan*>& vChans = pNetwork->GetChans();
530
0
        CIRCSock* pIRCSock = pNetwork->GetIRCSock();
531
0
        CString sPerms = pIRCSock ? pIRCSock->GetPerms() : "";
532
533
0
        if (vChans.empty()) {
534
0
            PutStatus(t_s("There are no channels defined."));
535
0
            return;
536
0
        }
537
538
0
        CTable Table;
539
0
        Table.AddColumn(t_s("Index", "listchans"));
540
0
        Table.AddColumn(t_s("Name", "listchans"));
541
0
        Table.AddColumn(t_s("Status", "listchans"));
542
0
        Table.AddColumn(t_s("In config", "listchans"));
543
0
        Table.AddColumn(t_s("Buffer", "listchans"));
544
0
        Table.AddColumn(t_s("Clear", "listchans"));
545
0
        Table.AddColumn(t_s("Modes", "listchans"));
546
0
        Table.AddColumn(t_s("Users", "listchans"));
547
548
0
        for (char cPerm : sPerms) {
549
0
            Table.AddColumn(CString(cPerm));
550
0
        }
551
552
0
        unsigned int uNumDetached = 0, uNumDisabled = 0, uNumJoined = 0,
553
0
                     uChanIndex = 1;
554
555
0
        for (const CChan* pChan : vChans) {
556
0
            Table.AddRow();
557
0
            Table.SetCell(t_s("Index", "listchans"), CString(uChanIndex));
558
0
            Table.SetCell(t_s("Name", "listchans"),
559
0
                          pChan->GetPermStr() + pChan->GetName());
560
0
            Table.SetCell(
561
0
                t_s("Status", "listchans"),
562
0
                pChan->IsOn()
563
0
                    ? (pChan->IsDetached() ? t_s("Detached", "listchans")
564
0
                                           : t_s("Joined", "listchans"))
565
0
                    : (pChan->IsDisabled() ? t_s("Disabled", "listchans")
566
0
                                           : t_s("Trying", "listchans")));
567
0
            Table.SetCell(
568
0
                t_s("In config", "listchans"),
569
0
                CString(pChan->InConfig() ? t_s("yes", "listchans") : ""));
570
0
            Table.SetCell(t_s("Buffer", "listchans"),
571
0
                          CString(pChan->HasBufferCountSet() ? "*" : "") +
572
0
                              CString(pChan->GetBufferCount()));
573
0
            Table.SetCell(
574
0
                t_s("Clear", "listchans"),
575
0
                CString(pChan->HasAutoClearChanBufferSet() ? "*" : "") +
576
0
                    CString(pChan->AutoClearChanBuffer()
577
0
                                ? t_s("yes", "listchans")
578
0
                                : ""));
579
0
            Table.SetCell(t_s("Modes", "listchans"), pChan->GetModeString());
580
0
            Table.SetCell(t_s("Users", "listchans"),
581
0
                          CString(pChan->GetNickCount()));
582
583
0
            std::map<char, unsigned int> mPerms = pChan->GetPermCounts();
584
0
            for (char cPerm : sPerms) {
585
0
                Table.SetCell(CString(cPerm), CString(mPerms[cPerm]));
586
0
            }
587
588
0
            if (pChan->IsDetached()) uNumDetached++;
589
0
            if (pChan->IsOn()) uNumJoined++;
590
0
            if (pChan->IsDisabled()) uNumDisabled++;
591
592
0
            uChanIndex++;
593
0
        }
594
595
0
        PutStatus(Table);
596
0
        PutStatus(t_f("Total: {1}, Joined: {2}, Detached: {3}, Disabled: {4}")(
597
0
            vChans.size(), uNumJoined, uNumDetached, uNumDisabled));
598
0
    } else if (sCommand.Equals("ADDNETWORK")) {
599
0
        if (!m_pUser->IsAdmin() && m_pUser->DenySetNetwork()) {
600
0
            PutStatus(t_s("Access denied!"));
601
0
            return;
602
0
        }
603
604
0
        if (!m_pUser->IsAdmin() && !m_pUser->HasSpaceForNewNetwork()) {
605
0
            PutStatus(t_s(
606
0
                "Network number limit reached. Ask an admin to increase the "
607
0
                "limit for you, or delete unneeded networks using /znc "
608
0
                "DelNetwork <name>"));
609
0
            return;
610
0
        }
611
612
0
        CString sNetwork = sLine.Token(1);
613
614
0
        if (sNetwork.empty()) {
615
0
            PutStatus(t_s("Usage: AddNetwork <name>"));
616
0
            return;
617
0
        }
618
0
        if (!CIRCNetwork::IsValidNetwork(sNetwork)) {
619
0
            PutStatus(t_s("Network name should be alphanumeric"));
620
0
            return;
621
0
        }
622
623
0
        CString sNetworkAddError;
624
0
        if (m_pUser->AddNetwork(sNetwork, sNetworkAddError)) {
625
0
            PutStatus(
626
0
                t_f("Network added. Use /znc JumpNetwork {1}, or connect to "
627
0
                    "ZNC with username {2} (instead of just {3}) to connect to "
628
0
                    "it.")(sNetwork, m_pUser->GetUsername() + "/" + sNetwork,
629
0
                           m_pUser->GetUsername()));
630
0
        } else {
631
0
            PutStatus(t_s("Unable to add that network"));
632
0
            PutStatus(sNetworkAddError);
633
0
        }
634
0
    } else if (sCommand.Equals("DELNETWORK")) {
635
0
        if (!m_pUser->IsAdmin() && m_pUser->DenySetNetwork()) {
636
0
            PutStatus(t_s("Access denied!"));
637
0
            return;
638
0
        }
639
640
0
        CString sNetwork = sLine.Token(1);
641
642
0
        if (sNetwork.empty()) {
643
0
            PutStatus(t_s("Usage: DelNetwork <name>"));
644
0
            return;
645
0
        }
646
647
0
        if (m_pNetwork && m_pNetwork->GetName().Equals(sNetwork)) {
648
0
            SetNetwork(nullptr);
649
0
        }
650
651
0
        if (m_pUser->DeleteNetwork(sNetwork)) {
652
0
            PutStatus(t_s("Network deleted"));
653
0
        } else {
654
0
            PutStatus(
655
0
                t_s("Failed to delete network, perhaps this network doesn't "
656
0
                    "exist"));
657
0
        }
658
0
    } else if (sCommand.Equals("LISTNETWORKS")) {
659
0
        CUser* pUser = m_pUser;
660
661
0
        if (m_pUser->IsAdmin() && !sLine.Token(1).empty()) {
662
0
            pUser = CZNC::Get().FindUser(sLine.Token(1));
663
664
0
            if (!pUser) {
665
0
                PutStatus(t_f("User {1} not found")(sLine.Token(1)));
666
0
                return;
667
0
            }
668
0
        }
669
670
0
        const vector<CIRCNetwork*>& vNetworks = pUser->GetNetworks();
671
672
0
        CTable Table;
673
0
        Table.AddColumn(t_s("Network", "listnetworks"));
674
0
        Table.AddColumn(t_s("On IRC", "listnetworks"));
675
0
        Table.AddColumn(t_s("IRC Server", "listnetworks"));
676
0
        Table.AddColumn(t_s("IRC User", "listnetworks"));
677
0
        Table.AddColumn(t_s("Channels", "listnetworks"));
678
679
0
        for (const CIRCNetwork* pNetwork : vNetworks) {
680
0
            Table.AddRow();
681
0
            Table.SetCell(t_s("Network", "listnetworks"), pNetwork->GetName());
682
0
            if (pNetwork->IsIRCConnected()) {
683
0
                Table.SetCell(t_s("On IRC", "listnetworks"),
684
0
                              t_s("Yes", "listnetworks"));
685
0
                Table.SetCell(t_s("IRC Server", "listnetworks"),
686
0
                              pNetwork->GetIRCServer());
687
0
                Table.SetCell(t_s("IRC User", "listnetworks"),
688
0
                              pNetwork->GetIRCNick().GetNickMask());
689
0
                Table.SetCell(t_s("Channels", "listnetworks"),
690
0
                              CString(pNetwork->GetChans().size()));
691
0
            } else {
692
0
                Table.SetCell(t_s("On IRC", "listnetworks"),
693
0
                              t_s("No", "listnetworks"));
694
0
            }
695
0
        }
696
697
0
        if (PutStatus(Table) == 0) {
698
0
            PutStatus(t_s("No networks", "listnetworks"));
699
0
        }
700
0
    } else if (sCommand.Equals("MOVENETWORK")) {
701
0
        if (!m_pUser->IsAdmin()) {
702
0
            PutStatus(t_s("Access denied."));
703
0
            return;
704
0
        }
705
706
0
        CString sOldUser = sLine.Token(1);
707
0
        CString sOldNetwork = sLine.Token(2);
708
0
        CString sNewUser = sLine.Token(3);
709
0
        CString sNewNetwork = sLine.Token(4);
710
711
0
        if (sOldUser.empty() || sOldNetwork.empty() || sNewUser.empty()) {
712
0
            PutStatus(t_s(
713
0
                "Usage: MoveNetwork <old user> <old network> <new user> [new "
714
0
                "network]"));
715
0
            return;
716
0
        }
717
0
        if (sNewNetwork.empty()) {
718
0
            sNewNetwork = sOldNetwork;
719
0
        }
720
721
0
        CUser* pOldUser = CZNC::Get().FindUser(sOldUser);
722
0
        if (!pOldUser) {
723
0
            PutStatus(t_f("Old user {1} not found.")(sOldUser));
724
0
            return;
725
0
        }
726
727
0
        CIRCNetwork* pOldNetwork = pOldUser->FindNetwork(sOldNetwork);
728
0
        if (!pOldNetwork) {
729
0
            PutStatus(t_f("Old network {1} not found.")(sOldNetwork));
730
0
            return;
731
0
        }
732
733
0
        CUser* pNewUser = CZNC::Get().FindUser(sNewUser);
734
0
        if (!pNewUser) {
735
0
            PutStatus(t_f("New user {1} not found.")(sNewUser));
736
0
            return;
737
0
        }
738
739
0
        if (pNewUser->FindNetwork(sNewNetwork)) {
740
0
            PutStatus(t_f("User {1} already has network {2}.")(sNewUser,
741
0
                                                               sNewNetwork));
742
0
            return;
743
0
        }
744
745
0
        if (!CIRCNetwork::IsValidNetwork(sNewNetwork)) {
746
0
            PutStatus(t_f("Invalid network name [{1}]")(sNewNetwork));
747
0
            return;
748
0
        }
749
750
0
        const CModules& vMods = pOldNetwork->GetModules();
751
0
        for (CModule* pMod : vMods) {
752
0
            CString sOldModPath = pOldNetwork->GetNetworkPath() + "/moddata/" +
753
0
                                  pMod->GetModName();
754
0
            CString sNewModPath = pNewUser->GetUserPath() + "/networks/" +
755
0
                                  sNewNetwork + "/moddata/" +
756
0
                                  pMod->GetModName();
757
758
0
            CDir oldDir(sOldModPath);
759
0
            for (CFile* pFile : oldDir) {
760
0
                if (pFile->GetShortName() != ".registry") {
761
0
                    PutStatus(
762
0
                        t_f("Some files seem to be in {1}. You might want to "
763
0
                            "move them to {2}")(sOldModPath, sNewModPath));
764
0
                    break;
765
0
                }
766
0
            }
767
768
0
            pMod->MoveRegistry(sNewModPath);
769
0
        }
770
771
0
        CString sNetworkAddError;
772
0
        CIRCNetwork* pNewNetwork =
773
0
            pNewUser->AddNetwork(sNewNetwork, sNetworkAddError);
774
775
0
        if (!pNewNetwork) {
776
0
            PutStatus(t_f("Error adding network: {1}")(sNetworkAddError));
777
0
            return;
778
0
        }
779
780
0
        pNewNetwork->Clone(*pOldNetwork, false);
781
782
0
        if (m_pNetwork && m_pNetwork->GetName().Equals(sOldNetwork) &&
783
0
            m_pUser == pOldUser) {
784
0
            SetNetwork(nullptr);
785
0
        }
786
787
0
        if (pOldUser->DeleteNetwork(sOldNetwork)) {
788
0
            PutStatus(t_s("Success."));
789
0
        } else {
790
0
            PutStatus(
791
0
                t_s("Copied the network to new user, but failed to delete old "
792
0
                    "network"));
793
0
        }
794
0
    } else if (sCommand.Equals("JUMPNETWORK")) {
795
0
        CString sNetwork = sLine.Token(1);
796
797
0
        if (sNetwork.empty()) {
798
0
            PutStatus(t_s("No network supplied."));
799
0
            return;
800
0
        }
801
802
0
        if (m_pNetwork && (m_pNetwork->GetName() == sNetwork)) {
803
0
            PutStatus(t_s("You are already connected with this network."));
804
0
            return;
805
0
        }
806
807
0
        CIRCNetwork* pNetwork = m_pUser->FindNetwork(sNetwork);
808
0
        if (pNetwork) {
809
0
            PutStatus(t_f("Switched to {1}")(sNetwork));
810
0
            SetNetwork(pNetwork);
811
0
        } else {
812
0
            PutStatus(t_f("You don't have a network named {1}")(sNetwork));
813
0
        }
814
0
    } else if (sCommand.Equals("ADDSERVER")) {
815
0
        if (!m_pUser->IsAdmin() && m_pUser->DenySetNetwork()) {
816
0
            PutStatus(t_s("Access denied!"));
817
0
            return;
818
0
        }
819
820
0
        CString sServer = sLine.Token(1, true);
821
822
0
        if (!m_pNetwork) {
823
0
            PutStatus(t_s(
824
0
                "You must be connected with a network to use this command"));
825
0
            return;
826
0
        }
827
828
0
        if (sServer.empty()) {
829
0
            PutStatus(t_s("Usage: AddServer <host> [[+]port] [pass]"));
830
0
            if (m_pUser->IsAdmin()) {
831
0
                PutStatus(t_s("Or: AddServer unix:[ssl:]/path/to/socket"));
832
0
            }
833
0
            PutStatus(t_s("+ means SSL"));
834
0
            return;
835
0
        }
836
837
0
        CServer Server = CServer::Parse(sServer);
838
0
        if (Server.IsUnixSocket() && !m_pUser->IsAdmin()) {
839
0
            PutStatus(t_s("Access denied!"));
840
0
            return;
841
0
        }
842
843
0
        if (m_pNetwork->AddServer(std::move(Server))) {
844
0
            PutStatus(t_s("Server added"));
845
0
        } else {
846
0
            PutStatus(
847
0
                t_s("Unable to add that server. Perhaps the server is already "
848
0
                    "added or openssl is disabled?"));
849
0
        }
850
0
    } else if (sCommand.Equals("REMSERVER") || sCommand.Equals("DELSERVER")) {
851
0
        if (!m_pUser->IsAdmin() && m_pUser->DenySetNetwork()) {
852
0
            PutStatus(t_s("Access denied!"));
853
0
            return;
854
0
        }
855
856
0
        if (!m_pNetwork) {
857
0
            PutStatus(t_s(
858
0
                "You must be connected with a network to use this command"));
859
0
            return;
860
0
        }
861
862
0
        CServer Server = CServer::Parse(sLine.Token(1, true));
863
864
0
        if (Server.GetName().empty()) {
865
0
            PutStatus(t_s("Usage: DelServer <host> [port] [pass]"));
866
0
            return;
867
0
        }
868
869
0
        if (!m_pNetwork->HasServers()) {
870
0
            PutStatus(t_s("You don't have any servers added."));
871
0
            return;
872
0
        }
873
874
        // Unix sockets can be removed with "unix:" prefix and without, both
875
        // work.
876
0
        if (m_pNetwork->DelServer(Server)) {
877
0
            PutStatus(t_s("Server removed"));
878
0
        } else {
879
0
            PutStatus(t_s("No such server"));
880
0
        }
881
0
    } else if (sCommand.Equals("LISTSERVERS")) {
882
0
        if (!m_pNetwork) {
883
0
            PutStatus(t_s(
884
0
                "You must be connected with a network to use this command"));
885
0
            return;
886
0
        }
887
888
0
        if (m_pNetwork->HasServers()) {
889
0
            const vector<CServer*>& vServers = m_pNetwork->GetServers();
890
0
            CServer* pCurServ = m_pNetwork->GetCurrentServer();
891
0
            CTable Table;
892
0
            Table.AddColumn(t_s("Host", "listservers"));
893
0
            Table.AddColumn(t_s("Port", "listservers"));
894
0
            Table.AddColumn(t_s("SSL", "listservers"));
895
0
            Table.AddColumn(t_s("Password", "listservers"));
896
897
0
            for (const CServer* pServer : vServers) {
898
0
                Table.AddRow();
899
0
                Table.SetCell(
900
0
                    t_s("Host", "listservers"),
901
0
                    (pServer->IsUnixSocket() ? pServer->GetString(false)
902
0
                                             : pServer->GetName()) +
903
0
                        (pServer == pCurServ ? "*" : ""));
904
0
                if (!pServer->IsUnixSocket())
905
0
                    Table.SetCell(t_s("Port", "listservers"),
906
0
                                  CString(pServer->GetPort()));
907
0
                Table.SetCell(
908
0
                    t_s("SSL", "listservers"),
909
0
                    (pServer->IsSSL()) ? t_s("SSL", "listservers|cell") : "");
910
0
                Table.SetCell(t_s("Password", "listservers"),
911
0
                              pServer->GetPass().empty() ? "" : "******");
912
0
            }
913
914
0
            PutStatus(Table);
915
0
        } else {
916
0
            PutStatus(t_s("You don't have any servers added."));
917
0
        }
918
0
    } else if (sCommand.Equals("AddTrustedServerFingerprint")) {
919
0
        if (!m_pNetwork) {
920
0
            PutStatus(t_s(
921
0
                "You must be connected with a network to use this command"));
922
0
            return;
923
0
        }
924
0
        CString sFP = sLine.Token(1);
925
0
        if (sFP.empty()) {
926
0
            PutStatus(t_s("Usage: AddTrustedServerFingerprint <fi:ng:er>"));
927
0
            return;
928
0
        }
929
0
        m_pNetwork->AddTrustedFingerprint(sFP);
930
0
        PutStatus(t_s("Done."));
931
0
    } else if (sCommand.Equals("DelTrustedServerFingerprint")) {
932
0
        if (!m_pNetwork) {
933
0
            PutStatus(t_s(
934
0
                "You must be connected with a network to use this command"));
935
0
            return;
936
0
        }
937
0
        CString sFP = sLine.Token(1);
938
0
        if (sFP.empty()) {
939
0
            PutStatus(t_s("Usage: DelTrustedServerFingerprint <fi:ng:er>"));
940
0
            return;
941
0
        }
942
0
        m_pNetwork->DelTrustedFingerprint(sFP);
943
0
        PutStatus(t_s("Done."));
944
0
    } else if (sCommand.Equals("ListTrustedServerFingerprints")) {
945
0
        if (!m_pNetwork) {
946
0
            PutStatus(t_s(
947
0
                "You must be connected with a network to use this command"));
948
0
            return;
949
0
        }
950
0
        const SCString& ssFPs = m_pNetwork->GetTrustedFingerprints();
951
0
        if (ssFPs.empty()) {
952
0
            PutStatus(t_s("No fingerprints added."));
953
0
        } else {
954
0
            int k = 0;
955
0
            for (const CString& sFP : ssFPs) {
956
0
                PutStatus(CString(++k) + ". " + sFP);
957
0
            }
958
0
        }
959
0
    } else if (sCommand.Equals("TOPICS")) {
960
0
        if (!m_pNetwork) {
961
0
            PutStatus(t_s(
962
0
                "You must be connected with a network to use this command"));
963
0
            return;
964
0
        }
965
966
0
        const vector<CChan*>& vChans = m_pNetwork->GetChans();
967
0
        CTable Table;
968
0
        Table.AddColumn(t_s("Channel", "topicscmd"));
969
0
        Table.AddColumn(t_s("Set By", "topicscmd"));
970
0
        Table.AddColumn(t_s("Topic", "topicscmd"));
971
972
0
        for (const CChan* pChan : vChans) {
973
0
            Table.AddRow();
974
0
            Table.SetCell(t_s("Channel", "topicscmd"), pChan->GetName());
975
0
            Table.SetCell(t_s("Set By", "topicscmd"), pChan->GetTopicOwner());
976
0
            Table.SetCell(t_s("Topic", "topicscmd"), pChan->GetTopic());
977
0
        }
978
979
0
        PutStatus(Table);
980
0
    } else if (sCommand.Equals("LISTMODS") || sCommand.Equals("LISTMODULES")) {
981
0
        const auto PrintModules = [this](const CModules& Modules) {
982
0
            CTable Table;
983
0
            Table.AddColumn(t_s("Name", "listmods"));
984
0
            Table.AddColumn(t_s("Arguments", "listmods"));
985
0
            Table.SetStyle(CTable::ListStyle);
986
0
            for (const CModule* pMod : Modules) {
987
0
                Table.AddRow();
988
0
                Table.SetCell(t_s("Name", "listmods"), pMod->GetModName());
989
0
                Table.SetCell(t_s("Arguments", "listmods"), pMod->GetArgs());
990
0
            }
991
0
            PutStatus(Table);
992
0
        };
993
0
        if (m_pUser->IsAdmin()) {
994
0
            const CModules& GModules = CZNC::Get().GetModules();
995
996
0
            PutStatus("");
997
0
            if (!GModules.size()) {
998
0
                PutStatus(t_s("No global modules loaded."));
999
0
            } else {
1000
0
                PutStatus(t_s("Global modules:"));
1001
0
                PrintModules(GModules);
1002
0
            }
1003
0
        }
1004
1005
0
        const CModules& UModules = m_pUser->GetModules();
1006
1007
0
        PutStatus("");
1008
0
        if (!UModules.size()) {
1009
0
            PutStatus(t_s("Your user has no modules loaded."));
1010
0
        } else {
1011
0
            PutStatus(t_s("User modules:"));
1012
0
            PrintModules(UModules);
1013
0
        }
1014
1015
0
        if (m_pNetwork) {
1016
0
            const CModules& NetworkModules = m_pNetwork->GetModules();
1017
0
            PutStatus("");
1018
0
            if (NetworkModules.empty()) {
1019
0
                PutStatus(t_s("This network has no modules loaded."));
1020
0
            } else {
1021
0
                PutStatus(t_s("Network modules:"));
1022
0
                PrintModules(NetworkModules);
1023
0
            }
1024
0
        }
1025
1026
0
        return;
1027
0
    } else if (sCommand.Equals("LISTAVAILMODS") ||
1028
0
               sCommand.Equals("LISTAVAILABLEMODULES")) {
1029
0
        if (m_pUser->DenyLoadMod()) {
1030
0
            PutStatus(t_s("Access denied."));
1031
0
            return;
1032
0
        }
1033
1034
0
        const auto PrintModules = [this](const set<CModInfo>& ssModules) {
1035
0
            CTable Table;
1036
0
            Table.AddColumn(t_s("Name", "listavailmods"));
1037
0
            Table.AddColumn(t_s("Description", "listavailmods"));
1038
0
            Table.SetStyle(CTable::ListStyle);
1039
1040
0
            for (const CModInfo& Info : ssModules) {
1041
0
                Table.AddRow();
1042
0
                Table.SetCell(
1043
0
                    t_s("Name", "listavailmods"),
1044
0
                    (CZNC::Get().GetModules().FindModule(Info.GetName())
1045
0
                         ? "*"
1046
0
                         : " ") +
1047
0
                        Info.GetName());
1048
0
                Table.SetCell(t_s("Description", "listavailmods"),
1049
0
                              Info.GetDescription().Ellipsize(128));
1050
0
            }
1051
1052
0
            PutStatus(Table);
1053
0
        };
1054
1055
0
        if (m_pUser->IsAdmin()) {
1056
0
            set<CModInfo> ssGlobalMods;
1057
0
            CZNC::Get().GetModules().GetAvailableMods(ssGlobalMods,
1058
0
                                                      CModInfo::GlobalModule);
1059
1060
0
            PutStatus("");
1061
0
            if (ssGlobalMods.empty()) {
1062
0
                PutStatus(t_s("No global modules available."));
1063
0
            } else {
1064
0
                PutStatus(t_s("Global modules:"));
1065
0
                PrintModules(ssGlobalMods);
1066
0
            }
1067
0
        }
1068
1069
0
        set<CModInfo> ssUserMods;
1070
0
        CZNC::Get().GetModules().GetAvailableMods(ssUserMods);
1071
1072
0
        PutStatus("");
1073
0
        if (ssUserMods.empty()) {
1074
0
            PutStatus(t_s("No user modules available."));
1075
0
        } else {
1076
0
            PutStatus(t_s("User modules:"));
1077
0
            PrintModules(ssUserMods);
1078
0
        }
1079
1080
0
        set<CModInfo> ssNetworkMods;
1081
0
        CZNC::Get().GetModules().GetAvailableMods(ssNetworkMods,
1082
0
                                                  CModInfo::NetworkModule);
1083
1084
0
        PutStatus("");
1085
0
        if (ssNetworkMods.empty()) {
1086
0
            PutStatus(t_s("No network modules available."));
1087
0
        } else {
1088
0
            PutStatus(t_s("Network modules:"));
1089
0
            PrintModules(ssNetworkMods);
1090
0
        }
1091
0
        return;
1092
0
    } else if (sCommand.Equals("LOADMOD") || sCommand.Equals("LOADMODULE")) {
1093
0
        CModInfo::EModuleType eType;
1094
0
        CString sType = sLine.Token(1);
1095
0
        CString sMod = sLine.Token(2);
1096
0
        CString sArgs = sLine.Token(3, true);
1097
1098
        // TODO use proper library for parsing arguments
1099
0
        if (sType.Equals("--type=global")) {
1100
0
            eType = CModInfo::GlobalModule;
1101
0
        } else if (sType.Equals("--type=user")) {
1102
0
            eType = CModInfo::UserModule;
1103
0
        } else if (sType.Equals("--type=network")) {
1104
0
            eType = CModInfo::NetworkModule;
1105
0
        } else {
1106
0
            sMod = sType;
1107
0
            sArgs = sLine.Token(2, true);
1108
0
            sType = "default";
1109
            // Will be set correctly later
1110
0
            eType = CModInfo::UserModule;
1111
0
        }
1112
1113
0
        if (m_pUser->DenyLoadMod()) {
1114
0
            PutStatus(t_f("Unable to load {1}: Access denied.")(sMod));
1115
0
            return;
1116
0
        }
1117
1118
0
        if (sMod.empty()) {
1119
0
            PutStatus(t_s(
1120
0
                "Usage: LoadMod [--type=global|user|network] <module> [args]"));
1121
0
            return;
1122
0
        }
1123
1124
0
        CModInfo ModInfo;
1125
0
        CString sRetMsg;
1126
0
        if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sMod, sRetMsg)) {
1127
0
            PutStatus(t_f("Unable to load {1}: {2}")(sMod, sRetMsg));
1128
0
            return;
1129
0
        }
1130
1131
0
        if (sType.Equals("default")) {
1132
0
            eType = ModInfo.GetDefaultType();
1133
0
        }
1134
1135
0
        if (eType == CModInfo::GlobalModule && !m_pUser->IsAdmin()) {
1136
0
            PutStatus(
1137
0
                t_f("Unable to load global module {1}: Access denied.")(sMod));
1138
0
            return;
1139
0
        }
1140
1141
0
        if (eType == CModInfo::NetworkModule && !m_pNetwork) {
1142
0
            PutStatus(
1143
0
                t_f("Unable to load network module {1}: Not connected with a "
1144
0
                    "network.")(sMod));
1145
0
            return;
1146
0
        }
1147
1148
0
        CString sModRet;
1149
0
        bool bLoaded = false;
1150
1151
0
        switch (eType) {
1152
0
            case CModInfo::GlobalModule:
1153
0
                bLoaded = CZNC::Get().GetModules().LoadModule(
1154
0
                    sMod, sArgs, eType, nullptr, nullptr, sModRet);
1155
0
                break;
1156
0
            case CModInfo::UserModule:
1157
0
                bLoaded = m_pUser->GetModules().LoadModule(
1158
0
                    sMod, sArgs, eType, m_pUser, nullptr, sModRet);
1159
0
                break;
1160
0
            case CModInfo::NetworkModule:
1161
0
                bLoaded = m_pNetwork->GetModules().LoadModule(
1162
0
                    sMod, sArgs, eType, m_pUser, m_pNetwork, sModRet);
1163
0
                break;
1164
0
            default:
1165
0
                sModRet = t_s("Unknown module type");
1166
0
        }
1167
1168
0
        if (bLoaded) {
1169
0
            if (sModRet.empty()) {
1170
0
                PutStatus(t_f("Loaded module {1}")(sMod));
1171
0
            } else {
1172
0
                PutStatus(t_f("Loaded module {1}: {2}")(sMod, sModRet));
1173
0
            }
1174
0
        } else {
1175
0
            PutStatus(t_f("Unable to load module {1}: {2}")(sMod, sModRet));
1176
0
        }
1177
1178
0
        return;
1179
0
    } else if (sCommand.Equals("UNLOADMOD") ||
1180
0
               sCommand.Equals("UNLOADMODULE")) {
1181
0
        CModInfo::EModuleType eType = CModInfo::UserModule;
1182
0
        CString sType = sLine.Token(1);
1183
0
        CString sMod = sLine.Token(2);
1184
1185
        // TODO use proper library for parsing arguments
1186
0
        if (sType.Equals("--type=global")) {
1187
0
            eType = CModInfo::GlobalModule;
1188
0
        } else if (sType.Equals("--type=user")) {
1189
0
            eType = CModInfo::UserModule;
1190
0
        } else if (sType.Equals("--type=network")) {
1191
0
            eType = CModInfo::NetworkModule;
1192
0
        } else {
1193
0
            sMod = sType;
1194
0
            sType = "default";
1195
0
        }
1196
1197
0
        if (m_pUser->DenyLoadMod()) {
1198
0
            PutStatus(t_f("Unable to unload {1}: Access denied.")(sMod));
1199
0
            return;
1200
0
        }
1201
1202
0
        if (sMod.empty()) {
1203
0
            PutStatus(
1204
0
                t_s("Usage: UnloadMod [--type=global|user|network] <module>"));
1205
0
            return;
1206
0
        }
1207
1208
0
        if (sType.Equals("default")) {
1209
0
            CModInfo ModInfo;
1210
0
            CString sRetMsg;
1211
0
            if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sMod, sRetMsg)) {
1212
0
                PutStatus(
1213
0
                    t_f("Unable to determine type of {1}: {2}")(sMod, sRetMsg));
1214
0
                return;
1215
0
            }
1216
1217
0
            eType = ModInfo.GetDefaultType();
1218
0
        }
1219
1220
0
        if (eType == CModInfo::GlobalModule && !m_pUser->IsAdmin()) {
1221
0
            PutStatus(t_f("Unable to unload global module {1}: Access denied.")(
1222
0
                sMod));
1223
0
            return;
1224
0
        }
1225
1226
0
        if (eType == CModInfo::NetworkModule && !m_pNetwork) {
1227
0
            PutStatus(
1228
0
                t_f("Unable to unload network module {1}: Not connected with a "
1229
0
                    "network.")(sMod));
1230
0
            return;
1231
0
        }
1232
1233
0
        CString sModRet;
1234
1235
0
        switch (eType) {
1236
0
            case CModInfo::GlobalModule:
1237
0
                CZNC::Get().GetModules().UnloadModule(sMod, sModRet);
1238
0
                break;
1239
0
            case CModInfo::UserModule:
1240
0
                m_pUser->GetModules().UnloadModule(sMod, sModRet);
1241
0
                break;
1242
0
            case CModInfo::NetworkModule:
1243
0
                m_pNetwork->GetModules().UnloadModule(sMod, sModRet);
1244
0
                break;
1245
0
            default:
1246
0
                sModRet = t_f(
1247
0
                    "Unable to unload module {1}: Unknown module type")(sMod);
1248
0
        }
1249
1250
0
        PutStatus(sModRet);
1251
0
        return;
1252
0
    } else if (sCommand.Equals("RELOADMOD") ||
1253
0
               sCommand.Equals("RELOADMODULE")) {
1254
0
        CModInfo::EModuleType eType;
1255
0
        CString sType = sLine.Token(1);
1256
0
        CString sMod = sLine.Token(2);
1257
0
        CString sArgs = sLine.Token(3, true);
1258
1259
0
        if (m_pUser->DenyLoadMod()) {
1260
0
            PutStatus(t_s("Unable to reload modules. Access denied."));
1261
0
            return;
1262
0
        }
1263
1264
        // TODO use proper library for parsing arguments
1265
0
        if (sType.Equals("--type=global")) {
1266
0
            eType = CModInfo::GlobalModule;
1267
0
        } else if (sType.Equals("--type=user")) {
1268
0
            eType = CModInfo::UserModule;
1269
0
        } else if (sType.Equals("--type=network")) {
1270
0
            eType = CModInfo::NetworkModule;
1271
0
        } else {
1272
0
            sMod = sType;
1273
0
            sArgs = sLine.Token(2, true);
1274
0
            sType = "default";
1275
            // Will be set correctly later
1276
0
            eType = CModInfo::UserModule;
1277
0
        }
1278
1279
0
        if (sMod.empty()) {
1280
0
            PutStatus(
1281
0
                t_s("Usage: ReloadMod [--type=global|user|network] <module> "
1282
0
                    "[args]"));
1283
0
            return;
1284
0
        }
1285
1286
0
        if (sType.Equals("default")) {
1287
0
            CModInfo ModInfo;
1288
0
            CString sRetMsg;
1289
0
            if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sMod, sRetMsg)) {
1290
0
                PutStatus(t_f("Unable to reload {1}: {2}")(sMod, sRetMsg));
1291
0
                return;
1292
0
            }
1293
1294
0
            eType = ModInfo.GetDefaultType();
1295
0
        }
1296
1297
0
        if (eType == CModInfo::GlobalModule && !m_pUser->IsAdmin()) {
1298
0
            PutStatus(t_f("Unable to reload global module {1}: Access denied.")(
1299
0
                sMod));
1300
0
            return;
1301
0
        }
1302
1303
0
        if (eType == CModInfo::NetworkModule && !m_pNetwork) {
1304
0
            PutStatus(
1305
0
                t_f("Unable to reload network module {1}: Not connected with a "
1306
0
                    "network.")(sMod));
1307
0
            return;
1308
0
        }
1309
1310
0
        CString sModRet;
1311
1312
0
        switch (eType) {
1313
0
            case CModInfo::GlobalModule:
1314
0
                CZNC::Get().GetModules().ReloadModule(sMod, sArgs, nullptr,
1315
0
                                                      nullptr, sModRet);
1316
0
                break;
1317
0
            case CModInfo::UserModule:
1318
0
                m_pUser->GetModules().ReloadModule(sMod, sArgs, m_pUser,
1319
0
                                                   nullptr, sModRet);
1320
0
                break;
1321
0
            case CModInfo::NetworkModule:
1322
0
                m_pNetwork->GetModules().ReloadModule(sMod, sArgs, m_pUser,
1323
0
                                                      m_pNetwork, sModRet);
1324
0
                break;
1325
0
            default:
1326
0
                sModRet = t_f(
1327
0
                    "Unable to reload module {1}: Unknown module type")(sMod);
1328
0
        }
1329
1330
0
        PutStatus(sModRet);
1331
0
        return;
1332
0
    } else if ((sCommand.Equals("UPDATEMOD") ||
1333
0
                sCommand.Equals("UPDATEMODULE")) &&
1334
0
               m_pUser->IsAdmin()) {
1335
0
        CString sMod = sLine.Token(1);
1336
1337
0
        if (sMod.empty()) {
1338
0
            PutStatus(t_s("Usage: UpdateMod <module>"));
1339
0
            return;
1340
0
        }
1341
1342
0
        PutStatus(t_f("Reloading {1} everywhere")(sMod));
1343
0
        if (CZNC::Get().UpdateModule(sMod)) {
1344
0
            PutStatus(t_s("Done"));
1345
0
        } else {
1346
0
            PutStatus(
1347
0
                t_f("Done, but there were errors, module {1} could not be "
1348
0
                    "reloaded everywhere.")(sMod));
1349
0
        }
1350
0
    } else if ((sCommand.Equals("SETBINDHOST") ||
1351
0
                sCommand.Equals("SETVHOST")) &&
1352
0
               (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
1353
0
        if (!m_pNetwork) {
1354
0
            PutStatus(t_s(
1355
0
                "You must be connected with a network to use this command. Try "
1356
0
                "SetUserBindHost instead"));
1357
0
            return;
1358
0
        }
1359
0
        CString sArg = sLine.Token(1);
1360
1361
0
        if (sArg.empty()) {
1362
0
            PutStatus(t_s("Usage: SetBindHost <host>"));
1363
0
            return;
1364
0
        }
1365
1366
0
        if (sArg.Equals(m_pNetwork->GetBindHost())) {
1367
0
            PutStatus(t_s("You already have this bind host!"));
1368
0
            return;
1369
0
        }
1370
1371
0
        m_pNetwork->SetBindHost(sArg);
1372
0
        PutStatus(t_f("Set bind host for network {1} to {2}")(
1373
0
            m_pNetwork->GetName(), m_pNetwork->GetBindHost()));
1374
0
    } else if (sCommand.Equals("SETUSERBINDHOST") &&
1375
0
               (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
1376
0
        CString sArg = sLine.Token(1);
1377
1378
0
        if (sArg.empty()) {
1379
0
            PutStatus(t_s("Usage: SetUserBindHost <host>"));
1380
0
            return;
1381
0
        }
1382
1383
0
        if (sArg.Equals(m_pUser->GetBindHost())) {
1384
0
            PutStatus(t_s("You already have this bind host!"));
1385
0
            return;
1386
0
        }
1387
1388
0
        m_pUser->SetBindHost(sArg);
1389
0
        PutStatus(t_f("Set default bind host to {1}")(m_pUser->GetBindHost()));
1390
0
    } else if ((sCommand.Equals("CLEARBINDHOST") ||
1391
0
                sCommand.Equals("CLEARVHOST")) &&
1392
0
               (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
1393
0
        if (!m_pNetwork) {
1394
0
            PutStatus(t_s(
1395
0
                "You must be connected with a network to use this command. Try "
1396
0
                "ClearUserBindHost instead"));
1397
0
            return;
1398
0
        }
1399
0
        m_pNetwork->SetBindHost("");
1400
0
        PutStatus(t_s("Bind host cleared for this network."));
1401
0
    } else if (sCommand.Equals("CLEARUSERBINDHOST") &&
1402
0
               (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
1403
0
        m_pUser->SetBindHost("");
1404
0
        PutStatus(t_s("Default bind host cleared for your user."));
1405
0
    } else if (sCommand.Equals("SHOWBINDHOST")) {
1406
0
        if (m_pUser->GetBindHost().empty()) {
1407
0
            PutStatus(t_s("This user's default bind host not set"));
1408
0
        } else {
1409
0
            PutStatus(t_f("This user's default bind host is {1}")(
1410
0
                m_pUser->GetBindHost()));
1411
0
        }
1412
0
        if (m_pNetwork) {
1413
0
            if (m_pNetwork->GetBindHost().empty()) {
1414
0
                PutStatus(t_s("This network's bind host not set"));
1415
0
            } else {
1416
0
                PutStatus(t_f("This network's default bind host is {1}")(
1417
0
                    m_pNetwork->GetBindHost()));
1418
0
            }
1419
0
        }
1420
0
    } else if (sCommand.Equals("PLAYBUFFER")) {
1421
0
        if (!m_pNetwork) {
1422
0
            PutStatus(t_s(
1423
0
                "You must be connected with a network to use this command"));
1424
0
            return;
1425
0
        }
1426
1427
0
        CString sBuffer = sLine.Token(1);
1428
1429
0
        if (sBuffer.empty()) {
1430
0
            PutStatus(t_s("Usage: PlayBuffer <#chan|query>"));
1431
0
            return;
1432
0
        }
1433
1434
0
        if (m_pNetwork->IsChan(sBuffer)) {
1435
0
            CChan* pChan = m_pNetwork->FindChan(sBuffer);
1436
1437
0
            if (!pChan) {
1438
0
                PutStatus(t_f("You are not on {1}")(sBuffer));
1439
0
                return;
1440
0
            }
1441
1442
0
            if (!pChan->IsOn()) {
1443
0
                PutStatus(t_f("You are not on {1} (trying to join)")(sBuffer));
1444
0
                return;
1445
0
            }
1446
1447
0
            if (pChan->GetBuffer().IsEmpty()) {
1448
0
                PutStatus(t_f("The buffer for channel {1} is empty")(sBuffer));
1449
0
                return;
1450
0
            }
1451
1452
0
            pChan->SendBuffer(this);
1453
0
        } else {
1454
0
            CQuery* pQuery = m_pNetwork->FindQuery(sBuffer);
1455
1456
0
            if (!pQuery) {
1457
0
                PutStatus(t_f("No active query with {1}")(sBuffer));
1458
0
                return;
1459
0
            }
1460
1461
0
            if (pQuery->GetBuffer().IsEmpty()) {
1462
0
                PutStatus(t_f("The buffer for {1} is empty")(sBuffer));
1463
0
                return;
1464
0
            }
1465
1466
0
            pQuery->SendBuffer(this);
1467
0
        }
1468
0
    } else if (sCommand.Equals("CLEARBUFFER")) {
1469
0
        if (!m_pNetwork) {
1470
0
            PutStatus(t_s(
1471
0
                "You must be connected with a network to use this command"));
1472
0
            return;
1473
0
        }
1474
1475
0
        CString sBuffer = sLine.Token(1);
1476
1477
0
        if (sBuffer.empty()) {
1478
0
            PutStatus(t_s("Usage: ClearBuffer <#chan|query>"));
1479
0
            return;
1480
0
        }
1481
1482
0
        unsigned int uMatches = 0;
1483
0
        vector<CChan*> vChans = m_pNetwork->FindChans(sBuffer);
1484
0
        for (CChan* pChan : vChans) {
1485
0
            uMatches++;
1486
1487
0
            pChan->ClearBuffer();
1488
0
        }
1489
1490
0
        vector<CQuery*> vQueries = m_pNetwork->FindQueries(sBuffer);
1491
0
        for (CQuery* pQuery : vQueries) {
1492
0
            uMatches++;
1493
1494
0
            m_pNetwork->DelQuery(pQuery->GetName());
1495
0
        }
1496
1497
0
        PutStatus(t_p("{1} buffer matching {2} has been cleared",
1498
0
                      "{1} buffers matching {2} have been cleared",
1499
0
                      uMatches)(uMatches, sBuffer));
1500
0
    } else if (sCommand.Equals("CLEARALLCHANNELBUFFERS")) {
1501
0
        if (!m_pNetwork) {
1502
0
            PutStatus(t_s(
1503
0
                "You must be connected with a network to use this command"));
1504
0
            return;
1505
0
        }
1506
1507
0
        for (CChan* pChan : m_pNetwork->GetChans()) {
1508
0
            pChan->ClearBuffer();
1509
0
        }
1510
0
        PutStatus(t_s("All channel buffers have been cleared"));
1511
0
    } else if (sCommand.Equals("CLEARALLQUERYBUFFERS")) {
1512
0
        if (!m_pNetwork) {
1513
0
            PutStatus(t_s(
1514
0
                "You must be connected with a network to use this command"));
1515
0
            return;
1516
0
        }
1517
1518
0
        m_pNetwork->ClearQueryBuffer();
1519
0
        PutStatus(t_s("All query buffers have been cleared"));
1520
0
    } else if (sCommand.Equals("CLEARALLBUFFERS")) {
1521
0
        if (!m_pNetwork) {
1522
0
            PutStatus(t_s(
1523
0
                "You must be connected with a network to use this command"));
1524
0
            return;
1525
0
        }
1526
1527
0
        for (CChan* pChan : m_pNetwork->GetChans()) {
1528
0
            pChan->ClearBuffer();
1529
0
        }
1530
0
        m_pNetwork->ClearQueryBuffer();
1531
0
        PutStatus(t_s("All buffers have been cleared"));
1532
0
    } else if (sCommand.Equals("SETBUFFER")) {
1533
0
        if (!m_pNetwork) {
1534
0
            PutStatus(t_s(
1535
0
                "You must be connected with a network to use this command"));
1536
0
            return;
1537
0
        }
1538
1539
0
        CString sBuffer = sLine.Token(1);
1540
1541
0
        if (sBuffer.empty()) {
1542
0
            PutStatus(t_s("Usage: SetBuffer <#chan|query> [linecount]"));
1543
0
            return;
1544
0
        }
1545
1546
0
        unsigned int uLineCount = sLine.Token(2).ToUInt();
1547
0
        unsigned int uMatches = 0, uFail = 0;
1548
0
        vector<CChan*> vChans = m_pNetwork->FindChans(sBuffer);
1549
0
        for (CChan* pChan : vChans) {
1550
0
            uMatches++;
1551
1552
0
            if (!pChan->SetBufferCount(uLineCount)) uFail++;
1553
0
        }
1554
1555
0
        vector<CQuery*> vQueries = m_pNetwork->FindQueries(sBuffer);
1556
0
        for (CQuery* pQuery : vQueries) {
1557
0
            uMatches++;
1558
1559
0
            if (!pQuery->SetBufferCount(uLineCount)) uFail++;
1560
0
        }
1561
1562
0
        if (uFail > 0) {
1563
0
            PutStatus(t_p("Setting buffer size failed for {1} buffer",
1564
0
                          "Setting buffer size failed for {1} buffers",
1565
0
                          uFail)(uFail));
1566
0
            PutStatus(t_p("Maximum buffer size is {1} line",
1567
0
                          "Maximum buffer size is {1} lines",
1568
0
                          CZNC::Get().GetMaxBufferSize())(
1569
0
                CZNC::Get().GetMaxBufferSize()));
1570
0
        } else {
1571
0
            PutStatus(t_p("Size of every buffer was set to {1} line",
1572
0
                          "Size of every buffer was set to {1} lines",
1573
0
                          uLineCount)(uLineCount));
1574
0
        }
1575
0
    } else if (m_pUser->IsAdmin() && sCommand.Equals("TRAFFIC")) {
1576
0
        CZNC::TrafficStatsPair Users, ZNC, Total;
1577
0
        CZNC::TrafficStatsMap traffic =
1578
0
            CZNC::Get().GetTrafficStats(Users, ZNC, Total);
1579
1580
0
        CTable Table;
1581
0
        Table.AddColumn(t_s("Username", "trafficcmd"));
1582
0
        Table.AddColumn(t_s("In", "trafficcmd"));
1583
0
        Table.AddColumn(t_s("Out", "trafficcmd"));
1584
0
        Table.AddColumn(t_s("Total", "trafficcmd"));
1585
1586
0
        for (const auto& it : traffic) {
1587
0
            Table.AddRow();
1588
0
            Table.SetCell(t_s("Username", "trafficcmd"), it.first);
1589
0
            Table.SetCell(t_s("In", "trafficcmd"),
1590
0
                          CString::ToByteStr(it.second.first));
1591
0
            Table.SetCell(t_s("Out", "trafficcmd"),
1592
0
                          CString::ToByteStr(it.second.second));
1593
0
            Table.SetCell(
1594
0
                t_s("Total", "trafficcmd"),
1595
0
                CString::ToByteStr(it.second.first + it.second.second));
1596
0
        }
1597
1598
0
        Table.AddRow();
1599
0
        Table.SetCell(t_s("Username", "trafficcmd"),
1600
0
                      t_s("<Users>", "trafficcmd"));
1601
0
        Table.SetCell(t_s("In", "trafficcmd"), CString::ToByteStr(Users.first));
1602
0
        Table.SetCell(t_s("Out", "trafficcmd"),
1603
0
                      CString::ToByteStr(Users.second));
1604
0
        Table.SetCell(t_s("Total", "trafficcmd"),
1605
0
                      CString::ToByteStr(Users.first + Users.second));
1606
1607
0
        Table.AddRow();
1608
0
        Table.SetCell(t_s("Username", "trafficcmd"),
1609
0
                      t_s("<ZNC>", "trafficcmd"));
1610
0
        Table.SetCell(t_s("In", "trafficcmd"), CString::ToByteStr(ZNC.first));
1611
0
        Table.SetCell(t_s("Out", "trafficcmd"), CString::ToByteStr(ZNC.second));
1612
0
        Table.SetCell(t_s("Total", "trafficcmd"),
1613
0
                      CString::ToByteStr(ZNC.first + ZNC.second));
1614
1615
0
        Table.AddRow();
1616
0
        Table.SetCell(t_s("Username", "trafficcmd"),
1617
0
                      t_s("<Total>", "trafficcmd"));
1618
0
        Table.SetCell(t_s("In", "trafficcmd"), CString::ToByteStr(Total.first));
1619
0
        Table.SetCell(t_s("Out", "trafficcmd"),
1620
0
                      CString::ToByteStr(Total.second));
1621
0
        Table.SetCell(t_s("Total", "trafficcmd"),
1622
0
                      CString::ToByteStr(Total.first + Total.second));
1623
1624
0
        PutStatus(Table);
1625
0
    } else if (sCommand.Equals("UPTIME")) {
1626
0
        PutStatus(t_f("Running for {1}")(CZNC::Get().GetUptime()));
1627
0
    } else if (m_pUser->IsAdmin() &&
1628
0
               (sCommand.Equals("LISTPORTS") || sCommand.Equals("ADDPORT") ||
1629
0
                sCommand.Equals("DELPORT"))) {
1630
0
        UserPortCommand(sLine);
1631
0
    } else {
1632
0
        PutStatus(t_s("Unknown command, try 'Help'"));
1633
0
    }
1634
0
}
1635
1636
namespace {
1637
struct PortCommandUsage {};
1638
}
1639
1640
0
void CClient::UserPortCommand(CString& sLine) {
1641
0
    const CString sCommand = sLine.Token(0);
1642
1643
0
    if (sCommand.Equals("LISTPORTS")) {
1644
0
        CTable TableT;
1645
0
        TableT.AddColumn(t_s("Port", "listports"));
1646
0
        TableT.AddColumn(t_s("BindHost", "listports"));
1647
0
        TableT.AddColumn(t_s("Protocol", "listports"));
1648
0
        TableT.AddColumn(t_s("SSL", "listports"));
1649
0
        TableT.AddColumn(t_s("IRC", "listports"));
1650
0
        TableT.AddColumn(t_s("Web", "listports"));
1651
1652
0
        CTable TableU;
1653
0
        TableU.AddColumn(t_s("Path", "listports"));
1654
0
        TableU.AddColumn(t_s("Mode", "listports"));
1655
0
        TableU.AddColumn(t_s("Group", "listports"));
1656
0
        TableU.AddColumn(t_s("SSL", "listports"));
1657
0
        TableU.AddColumn(t_s("IRC", "listports"));
1658
0
        TableU.AddColumn(t_s("Web", "listports"));
1659
1660
0
        const vector<CListener*>& vpListeners = CZNC::Get().GetListeners();
1661
1662
0
        for (const CListener* pListener : vpListeners) {
1663
0
            CTable* pTable;
1664
0
            if (const CTCPListener* pTCPListener =
1665
0
                    dynamic_cast<const CTCPListener*>(pListener)) {
1666
0
                TableT.AddRow();
1667
0
                pTable = &TableT;
1668
0
                TableT.SetCell(t_s("Port", "listports"),
1669
0
                               CString(pTCPListener->GetPort()));
1670
0
                TableT.SetCell(t_s("BindHost", "listports"),
1671
0
                               (pTCPListener->GetBindHost().empty()
1672
0
                                    ? CString("*")
1673
0
                                    : pTCPListener->GetBindHost()));
1674
1675
0
                EAddrType eAddr = pTCPListener->GetAddrType();
1676
0
                TableT.SetCell(
1677
0
                    t_s("Protocol", "listports"),
1678
0
                    eAddr == ADDR_ALL
1679
0
                        ? t_s("IPv4 and IPv6", "listports")
1680
0
                        : (eAddr == ADDR_IPV4ONLY ? t_s("IPv4", "listports")
1681
0
                                                  : t_s("IPv6", "listports")));
1682
0
            } else if (const CUnixListener* pUnixListener =
1683
0
                           dynamic_cast<const CUnixListener*>(pListener)) {
1684
0
                TableU.AddRow();
1685
0
                pTable = &TableU;
1686
0
                TableU.SetCell(t_s("Path", "listports"),
1687
0
                               pUnixListener->GetPath());
1688
0
                TableU.SetCell(t_s("Mode", "listports"),
1689
0
                               pUnixListener->GetMode());
1690
0
                TableU.SetCell(t_s("Group", "listports"),
1691
0
                               pUnixListener->GetGroup());
1692
0
            } else {
1693
0
                continue;
1694
0
            }
1695
0
            pTable->SetCell(t_s("SSL", "listports"),
1696
0
                            pListener->IsSSL() ? t_s("yes", "listports|ssl")
1697
0
                                               : t_s("no", "listports|ssl"));
1698
1699
0
            CListener::EAcceptType eAccept = pListener->GetAcceptType();
1700
0
            pTable->SetCell(t_s("IRC", "listports"),
1701
0
                            eAccept == CListener::ACCEPT_ALL ||
1702
0
                                    eAccept == CListener::ACCEPT_IRC
1703
0
                                ? t_s("yes", "listports|irc")
1704
0
                                : t_s("no", "listports|irc"));
1705
0
            pTable->SetCell(t_s("Web", "listports"),
1706
0
                            eAccept == CListener::ACCEPT_ALL ||
1707
0
                                    eAccept == CListener::ACCEPT_HTTP
1708
0
                                ? t_f("yes, on {1}", "listports|irc")(
1709
0
                                      pListener->GetURIPrefix() + "/")
1710
0
                                : t_s("no", "listports|web"));
1711
0
        }
1712
1713
0
        PutStatus(TableT);
1714
0
        PutStatus(TableU);
1715
1716
0
        return;
1717
0
    }
1718
1719
0
    auto ParseEAddr = [](const CString& sAddr) {
1720
0
        if (sAddr.Equals("IPV4")) {
1721
0
            return ADDR_IPV4ONLY;
1722
0
        } else if (sAddr.Equals("IPV6")) {
1723
0
            return ADDR_IPV6ONLY;
1724
0
        } else if (sAddr.Equals("ALL")) {
1725
0
            return ADDR_ALL;
1726
0
        } else {
1727
0
            throw PortCommandUsage{};
1728
0
        }
1729
0
    };
1730
1731
0
    auto ParseEAccept = [](const CString& sAccept) {
1732
0
        if (sAccept.Equals("WEB")) {
1733
0
            return CListener::ACCEPT_HTTP;
1734
0
        } else if (sAccept.Equals("IRC")) {
1735
0
            return CListener::ACCEPT_IRC;
1736
0
        } else if (sAccept.Equals("ALL")) {
1737
0
            return CListener::ACCEPT_ALL;
1738
0
        } else {
1739
0
            throw PortCommandUsage{};
1740
0
        }
1741
0
    };
1742
1743
0
    CString sPort = sLine.Token(1);
1744
0
    unsigned short uPort = sPort.ToUShort();
1745
1746
0
    if (sCommand.Equals("ADDPORT")) {
1747
0
        try {
1748
0
            if (sPort.empty()) {
1749
0
                throw PortCommandUsage{};
1750
0
            }
1751
1752
0
            std::unique_ptr<CListener> pListener;
1753
0
            if (sPort.TrimPrefix("unix:")) {
1754
0
                bool bSSL = false;
1755
0
                CString sMode;
1756
0
                CString sGroup;
1757
0
                if (auto colon = sPort.find_first_of(':'); colon != std::string::npos) {
1758
0
                    VCString vsOpts;
1759
0
                    CString(sPort.substr(0, colon)).Split(",", vsOpts, false);
1760
0
                    for (CString& sOpt : vsOpts) {
1761
0
                        if (sOpt == "ssl") {
1762
0
                            bSSL = true;
1763
0
                        } else if (sOpt.TrimPrefix("mode=")) {
1764
0
                            sMode = sOpt;
1765
0
                        } else if (sOpt.TrimPrefix("group=")) {
1766
0
                            sGroup = sOpt;
1767
0
                        } else {
1768
0
                            throw PortCommandUsage{};
1769
0
                        }
1770
0
                    }
1771
0
                    sPort = sPort.substr(colon + 1);
1772
0
                }
1773
0
                const CString& sPath = sPort;
1774
0
                CListener::EAcceptType eAccept = ParseEAccept(sLine.Token(2));
1775
0
                CString sURIPrefix = sLine.Token(3);
1776
1777
0
                pListener.reset(new CUnixListener(sPath, sURIPrefix, bSSL, eAccept, sGroup, sMode));
1778
0
            } else {
1779
0
                bool bSSL = sPort.StartsWith("+");
1780
0
                EAddrType eAddr = ParseEAddr(sLine.Token(2));
1781
0
                CListener::EAcceptType eAccept = ParseEAccept(sLine.Token(3));
1782
0
                const CString sBindHost = sLine.Token(4);
1783
0
                const CString sURIPrefix = sLine.Token(5);
1784
1785
0
                pListener.reset(new CTCPListener(uPort, sBindHost, sURIPrefix,
1786
0
                                                 bSSL, eAddr, eAccept));
1787
0
            }
1788
1789
0
            if (!pListener->Listen()) {
1790
0
                auto e = errno;
1791
0
                PutStatus(t_f("Unable to bind: {1}")(CString(strerror(e))));
1792
0
            } else {
1793
0
                if (CZNC::Get().AddListener(pListener.release())) {
1794
0
                    PutStatus(t_s("Port added"));
1795
0
                } else {
1796
0
                    PutStatus(t_s("Couldn't add port"));
1797
0
                }
1798
0
            }
1799
0
        } catch (PortCommandUsage) {
1800
0
            PutStatus(
1801
0
                t_s("Usage: AddPort <[+]port> <ipv4|ipv6|all> <web|irc|all> "
1802
0
                    "[bindhost [uriprefix]]"));
1803
0
            PutStatus(t_s("+ means SSL"));
1804
0
            PutStatus(
1805
0
                t_s("Or: AddPort unix:[ssl,mode=NNN,group=foo]:/path/to/socket <web|irc|all> "
1806
0
                    "[uriprefix]"));
1807
0
        }
1808
0
    } else if (sCommand.Equals("DELPORT")) {
1809
0
        try {
1810
0
            if (sPort.empty()) {
1811
0
                throw PortCommandUsage{};
1812
0
            }
1813
0
            CListener* pListener;
1814
0
            if (sPort.TrimPrefix("unix:")) {
1815
0
                sPort.TrimPrefix("ssl:");
1816
0
                pListener = CZNC::Get().FindUnixListener(sPort);
1817
0
            } else {
1818
0
                CString sAddr = sLine.Token(2);
1819
0
                CString sBindHost = sLine.Token(3);
1820
0
                pListener = CZNC::Get().FindListener(
1821
0
                    uPort, sBindHost, ParseEAddr(sAddr));
1822
0
            }
1823
1824
0
            if (pListener) {
1825
0
                CZNC::Get().DelListener(pListener);
1826
0
                PutStatus(t_s("Deleted Port"));
1827
0
            } else {
1828
0
                PutStatus(t_s("Unable to find a matching port"));
1829
0
            }
1830
0
        } catch (PortCommandUsage) {
1831
0
            PutStatus(t_s("Usage: DelPort <port> <ipv4|ipv6|all> [bindhost]"));
1832
0
            PutStatus(t_s("Or: DelPort unix:/path/to/socket"));
1833
0
        }
1834
0
    }
1835
0
}
1836
1837
0
void CClient::HelpUser(const CString& sFilter) {
1838
0
    CTable Table;
1839
0
    Table.AddColumn(t_s("Command", "helpcmd"));
1840
0
    Table.AddColumn(t_s("Description", "helpcmd"));
1841
0
    Table.SetStyle(CTable::ListStyle);
1842
1843
0
    if (sFilter.empty()) {
1844
0
        PutStatus(
1845
0
            t_s("In the following list all occurrences of <#chan> support "
1846
0
                "wildcards (* and ?) except ListNicks"));
1847
0
    }
1848
1849
0
    const auto AddCommandHelp = [&](const CString& sCmd, const CString& sArgs,
1850
0
                                    const CString& sDesc) {
1851
0
        if (sFilter.empty() || sCmd.StartsWith(sFilter) ||
1852
0
            sCmd.AsLower().WildCmp(sFilter.AsLower())) {
1853
0
            Table.AddRow();
1854
0
            Table.SetCell(t_s("Command", "helpcmd"),
1855
0
                          sCmd + (sArgs.empty() ? "" : " ") + sArgs);
1856
0
            Table.SetCell(t_s("Description", "helpcmd"), sDesc);
1857
0
        }
1858
0
    };
1859
1860
0
    AddCommandHelp(
1861
0
        "Version", "",
1862
0
        t_s("Print which version of ZNC this is", "helpcmd|Version|desc"));
1863
1864
0
    AddCommandHelp("ListMods", "",
1865
0
                   t_s("List all loaded modules", "helpcmd|ListMods|desc"));
1866
0
    AddCommandHelp(
1867
0
        "ListAvailMods", "",
1868
0
        t_s("List all available modules", "helpcmd|ListAvailMods|desc"));
1869
0
    if (!m_pUser->IsAdmin()) {
1870
        // If they are an admin we will add this command below with an argument
1871
0
        AddCommandHelp("ListChans", "",
1872
0
                       t_s("List all channels", "helpcmd|ListChans|desc"));
1873
0
    }
1874
0
    AddCommandHelp(
1875
0
        "ListNicks", t_s("<#chan>", "helpcmd|ListNicks|args"),
1876
0
        t_s("List all nicks on a channel", "helpcmd|ListNicks|desc"));
1877
0
    if (!m_pUser->IsAdmin()) {
1878
0
        AddCommandHelp("ListClients", "",
1879
0
                       t_s("List all clients connected to your ZNC user",
1880
0
                           "helpcmd|ListClients|desc"));
1881
0
    }
1882
0
    AddCommandHelp("ListServers", "",
1883
0
                   t_s("List all servers of current IRC network",
1884
0
                       "helpcmd|ListServers|desc"));
1885
1886
0
    AddCommandHelp(
1887
0
        "AddNetwork", t_s("<name>", "helpcmd|AddNetwork|args"),
1888
0
        t_s("Add a network to your user", "helpcmd|AddNetwork|desc"));
1889
0
    AddCommandHelp(
1890
0
        "DelNetwork", t_s("<name>", "helpcmd|DelNetwork|args"),
1891
0
        t_s("Delete a network from your user", "helpcmd|DelNetwork|desc"));
1892
0
    AddCommandHelp("ListNetworks", "",
1893
0
                   t_s("List all networks", "helpcmd|ListNetworks|desc"));
1894
0
    if (m_pUser->IsAdmin()) {
1895
0
        AddCommandHelp("MoveNetwork",
1896
0
                       t_s("<old user> <old network> <new user> [new network]",
1897
0
                           "helpcmd|MoveNetwork|args"),
1898
0
                       t_s("Move an IRC network from one user to another",
1899
0
                           "helpcmd|MoveNetwork|desc"));
1900
0
    }
1901
0
    AddCommandHelp(
1902
0
        "JumpNetwork", t_s("<network>", "helpcmd|JumpNetwork|args"),
1903
0
        t_s("Jump to another network (Alternatively, you can connect to ZNC "
1904
0
            "several times, using `user/network` as username)",
1905
0
            "helpcmd|JumpNetwork|desc"));
1906
1907
0
    AddCommandHelp("AddServer",
1908
0
                   t_s("<host> [[+]port] [pass]", "helpcmd|AddServer|args"),
1909
0
                   t_s("Add a server to the list of alternate/backup servers "
1910
0
                       "of current IRC network.",
1911
0
                       "helpcmd|AddServer|desc"));
1912
0
    AddCommandHelp("DelServer",
1913
0
                   t_s("<host> [port] [pass]", "helpcmd|DelServer|args"),
1914
0
                   t_s("Remove a server from the list of alternate/backup "
1915
0
                       "servers of current IRC network",
1916
0
                       "helpcmd|DelServer|desc"));
1917
1918
0
    AddCommandHelp(
1919
0
        "AddTrustedServerFingerprint",
1920
0
        t_s("<fi:ng:er>", "helpcmd|AddTrustedServerFingerprint|args"),
1921
0
        t_s("Add a trusted server SSL certificate fingerprint (SHA-256) to "
1922
0
            "current IRC network.",
1923
0
            "helpcmd|AddTrustedServerFingerprint|desc"));
1924
0
    AddCommandHelp(
1925
0
        "DelTrustedServerFingerprint",
1926
0
        t_s("<fi:ng:er>", "helpcmd|DelTrustedServerFingerprint|args"),
1927
0
        t_s("Delete a trusted server SSL certificate from current IRC network.",
1928
0
            "helpcmd|DelTrustedServerFingerprint|desc"));
1929
0
    AddCommandHelp(
1930
0
        "ListTrustedServerFingerprints", "",
1931
0
        t_s("List all trusted server SSL certificates of current IRC network.",
1932
0
            "helpcmd|ListTrustedServerFingerprints|desc"));
1933
1934
0
    AddCommandHelp("EnableChan", t_s("<#chans>", "helpcmd|EnableChan|args"),
1935
0
                   t_s("Enable channels", "helpcmd|EnableChan|desc"));
1936
0
    AddCommandHelp("DisableChan", t_s("<#chans>", "helpcmd|DisableChan|args"),
1937
0
                   t_s("Disable channels", "helpcmd|DisableChan|desc"));
1938
0
    AddCommandHelp("MoveChan", t_s("<#chan> <index>", "helpcmd|MoveChan|args"),
1939
0
                   t_s("Move channel in sort order", "helpcmd|MoveChan|desc"));
1940
0
    AddCommandHelp(
1941
0
        "SwapChans", t_s("<#chan1> <#chan2>", "helpcmd|SwapChans|args"),
1942
0
        t_s("Swap channels in sort order", "helpcmd|SwapChans|desc"));
1943
0
    AddCommandHelp("Attach", t_s("<#chans>", "helpcmd|Attach|args"),
1944
0
                   t_s("Attach to channels", "helpcmd|Attach|desc"));
1945
0
    AddCommandHelp("Detach", t_s("<#chans>", "helpcmd|Detach|args"),
1946
0
                   t_s("Detach from channels", "helpcmd|Detach|desc"));
1947
0
    AddCommandHelp(
1948
0
        "Topics", "",
1949
0
        t_s("Show topics in all your channels", "helpcmd|Topics|desc"));
1950
1951
0
    AddCommandHelp(
1952
0
        "PlayBuffer", t_s("<#chan|query>", "helpcmd|PlayBuffer|args"),
1953
0
        t_s("Play back the specified buffer", "helpcmd|PlayBuffer|desc"));
1954
0
    AddCommandHelp(
1955
0
        "ClearBuffer", t_s("<#chan|query>", "helpcmd|ClearBuffer|args"),
1956
0
        t_s("Clear the specified buffer", "helpcmd|ClearBuffer|desc"));
1957
0
    AddCommandHelp("ClearAllBuffers", "",
1958
0
                   t_s("Clear all channel and query buffers",
1959
0
                       "helpcmd|ClearAllBuffers|desc"));
1960
0
    AddCommandHelp("ClearAllChannelBuffers", "",
1961
0
                   t_s("Clear the channel buffers",
1962
0
                       "helpcmd|ClearAllChannelBuffers|desc"));
1963
0
    AddCommandHelp(
1964
0
        "ClearAllQueryBuffers", "",
1965
0
        t_s("Clear the query buffers", "helpcmd|ClearAllQueryBuffers|desc"));
1966
0
    AddCommandHelp("SetBuffer",
1967
0
                   t_s("<#chan|query> [linecount]", "helpcmd|SetBuffer|args"),
1968
0
                   t_s("Set the buffer count", "helpcmd|SetBuffer|desc"));
1969
1970
0
    if (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost()) {
1971
0
        AddCommandHelp("SetBindHost",
1972
0
                       t_s("<host (IP preferred)>", "helpcmd|SetBindHost|args"),
1973
0
                       t_s("Set the bind host for this network",
1974
0
                           "helpcmd|SetBindHost|desc"));
1975
0
        AddCommandHelp(
1976
0
            "SetUserBindHost",
1977
0
            t_s("<host (IP preferred)>", "helpcmd|SetUserBindHost|args"),
1978
0
            t_s("Set the default bind host for this user",
1979
0
                "helpcmd|SetUserBindHost|desc"));
1980
0
        AddCommandHelp("ClearBindHost", "",
1981
0
                       t_s("Clear the bind host for this network",
1982
0
                           "helpcmd|ClearBindHost|desc"));
1983
0
        AddCommandHelp("ClearUserBindHost", "",
1984
0
                       t_s("Clear the default bind host for this user",
1985
0
                           "helpcmd|ClearUserBindHost|desc"));
1986
0
    }
1987
1988
0
    AddCommandHelp(
1989
0
        "ShowBindHost", "",
1990
0
        t_s("Show currently selected bind host", "helpcmd|ShowBindHost|desc"));
1991
0
    AddCommandHelp(
1992
0
        "Jump", t_s("[server]", "helpcmd|Jump|args"),
1993
0
        t_s("Jump to the next or the specified server", "helpcmd|Jump|desc"));
1994
0
    AddCommandHelp("Disconnect", t_s("[message]", "helpcmd|Disconnect|args"),
1995
0
                   t_s("Disconnect from IRC", "helpcmd|Disconnect|desc"));
1996
0
    AddCommandHelp("Connect", "",
1997
0
                   t_s("Reconnect to IRC", "helpcmd|Connect|desc"));
1998
0
    AddCommandHelp(
1999
0
        "Uptime", "",
2000
0
        t_s("Show for how long ZNC has been running", "helpcmd|Uptime|desc"));
2001
2002
0
    if (!m_pUser->DenyLoadMod()) {
2003
0
        AddCommandHelp("LoadMod",
2004
0
                       t_s("[--type=global|user|network] <module> [args]",
2005
0
                           "helpcmd|LoadMod|args"),
2006
0
                       t_s("Load a module", "helpcmd|LoadMod|desc"));
2007
0
        AddCommandHelp("UnloadMod",
2008
0
                       t_s("[--type=global|user|network] <module>",
2009
0
                           "helpcmd|UnloadMod|args"),
2010
0
                       t_s("Unload a module", "helpcmd|UnloadMod|desc"));
2011
0
        AddCommandHelp("ReloadMod",
2012
0
                       t_s("[--type=global|user|network] <module> [args]",
2013
0
                           "helpcmd|ReloadMod|args"),
2014
0
                       t_s("Reload a module", "helpcmd|ReloadMod|desc"));
2015
0
        if (m_pUser->IsAdmin()) {
2016
0
            AddCommandHelp(
2017
0
                "UpdateMod", t_s("<module>", "helpcmd|UpdateMod|args"),
2018
0
                t_s("Reload a module everywhere", "helpcmd|UpdateMod|desc"));
2019
0
        }
2020
0
    }
2021
2022
0
    AddCommandHelp(
2023
0
        "ShowMOTD", "",
2024
0
        t_s("Show ZNC's message of the day", "helpcmd|ShowMOTD|desc"));
2025
2026
0
    if (m_pUser->IsAdmin()) {
2027
0
        AddCommandHelp(
2028
0
            "SetMOTD", t_s("<message>", "helpcmd|SetMOTD|args"),
2029
0
            t_s("Set ZNC's message of the day", "helpcmd|SetMOTD|desc"));
2030
0
        AddCommandHelp(
2031
0
            "AddMOTD", t_s("<message>", "helpcmd|AddMOTD|args"),
2032
0
            t_s("Append <message> to ZNC's MOTD", "helpcmd|AddMOTD|desc"));
2033
0
        AddCommandHelp("ClearMOTD", "",
2034
0
                       t_s("Clear ZNC's MOTD", "helpcmd|ClearMOTD|desc"));
2035
0
        AddCommandHelp(
2036
0
            "ListPorts", "",
2037
0
            t_s("Show all active listeners", "helpcmd|ListPorts|desc"));
2038
0
        AddCommandHelp("AddPort",
2039
0
                       t_s("<[+]port> <ipv4|ipv6|all> <web|irc|all> [bindhost "
2040
0
                           "[uriprefix]]",
2041
0
                           "helpcmd|AddPort|args"),
2042
0
                       t_s("Add another port for ZNC to listen on",
2043
0
                           "helpcmd|AddPort|desc"));
2044
0
        AddCommandHelp(
2045
0
            "DelPort",
2046
0
            t_s("<port> <ipv4|ipv6|all> [bindhost]", "helpcmd|DelPort|args"),
2047
0
            t_s("Remove a port from ZNC", "helpcmd|DelPort|desc"));
2048
0
        AddCommandHelp(
2049
0
            "Rehash", "",
2050
0
            t_s("Reload global settings, modules, and listeners from znc.conf",
2051
0
                "helpcmd|Rehash|desc"));
2052
0
        AddCommandHelp("SaveConfig", "",
2053
0
                       t_s("Save the current settings to disk",
2054
0
                           "helpcmd|SaveConfig|desc"));
2055
0
        AddCommandHelp("ListUsers", "",
2056
0
                       t_s("List all ZNC users and their connection status",
2057
0
                           "helpcmd|ListUsers|desc"));
2058
0
        AddCommandHelp("ListAllUserNetworks", "",
2059
0
                       t_s("List all ZNC users and their networks",
2060
0
                           "helpcmd|ListAllUserNetworks|desc"));
2061
0
        AddCommandHelp("ListChans",
2062
0
                       t_s("[user <network>]", "helpcmd|ListChans|args"),
2063
0
                       t_s("List all channels", "helpcmd|ListChans|desc"));
2064
0
        AddCommandHelp(
2065
0
            "ListClients", t_s("[user]", "helpcmd|ListClients|args"),
2066
0
            t_s("List all connected clients", "helpcmd|ListClients|desc"));
2067
0
        AddCommandHelp("Traffic", "",
2068
0
                       t_s("Show basic traffic stats for all ZNC users",
2069
0
                           "helpcmd|Traffic|desc"));
2070
0
        AddCommandHelp("Broadcast", t_s("[message]", "helpcmd|Broadcast|args"),
2071
0
                       t_s("Broadcast a message to all ZNC users",
2072
0
                           "helpcmd|Broadcast|desc"));
2073
0
        AddCommandHelp(
2074
0
            "Shutdown", t_s("[message]", "helpcmd|Shutdown|args"),
2075
0
            t_s("Shut down ZNC completely", "helpcmd|Shutdown|desc"));
2076
0
        AddCommandHelp("Restart", t_s("[message]", "helpcmd|Restart|args"),
2077
0
                       t_s("Restart ZNC", "helpcmd|Restart|desc"));
2078
0
    }
2079
2080
0
    if (Table.empty()) {
2081
0
        PutStatus(t_f("No matches for '{1}'")(sFilter));
2082
0
    } else {
2083
0
        PutStatus(Table);
2084
0
    }
2085
0
}