1 /*
2  * Copyright (C) 2004-2020 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/znc.h>
18 #include <znc/User.h>
19 #include <znc/FileUtils.h>
20 
21 using std::set;
22 
23 class CDCCMod;
24 
25 class CDCCSock : public CSocket {
26   public:
27     CDCCSock(CDCCMod* pMod, const CString& sRemoteNick,
28              const CString& sLocalFile, unsigned long uFileSize = 0,
29              CFile* pFile = nullptr);
30     CDCCSock(CDCCMod* pMod, const CString& sRemoteNick,
31              const CString& sRemoteIP, unsigned short uRemotePort,
32              const CString& sLocalFile, unsigned long uFileSize);
33     ~CDCCSock() override;
34 
35     void ReadData(const char* data, size_t len) override;
36     void ConnectionRefused() override;
37     void SockError(int iErrno, const CString& sDescription) override;
38     void Timeout() override;
39     void Connected() override;
40     void Disconnected() override;
41     void SendPacket();
42     Csock* GetSockObj(const CString& sHost, unsigned short uPort) override;
43     CFile* OpenFile(bool bWrite = true);
44     bool Seek(unsigned long int uPos);
45 
46     // Setters
SetRemoteIP(const CString & s)47     void SetRemoteIP(const CString& s) { m_sRemoteIP = s; }
SetRemoteNick(const CString & s)48     void SetRemoteNick(const CString& s) { m_sRemoteNick = s; }
SetFileName(const CString & s)49     void SetFileName(const CString& s) { m_sFileName = s; }
SetFileOffset(unsigned long u)50     void SetFileOffset(unsigned long u) { m_uBytesSoFar = u; }
51     // !Setters
52 
53     // Getters
GetUserPort() const54     unsigned short GetUserPort() const { return m_uRemotePort; }
GetRemoteNick() const55     const CString& GetRemoteNick() const { return m_sRemoteNick; }
GetFileName() const56     const CString& GetFileName() const { return m_sFileName; }
GetLocalFile() const57     const CString& GetLocalFile() const { return m_sLocalFile; }
GetFile()58     CFile* GetFile() { return m_pFile; }
GetProgress() const59     double GetProgress() const {
60         return ((m_uFileSize) && (m_uBytesSoFar))
61                    ? (double)(((double)m_uBytesSoFar / (double)m_uFileSize) *
62                               100.0)
63                    : 0;
64     }
IsSend() const65     bool IsSend() const { return m_bSend; }
66     // const CString& GetRemoteIP() const { return m_sRemoteIP; }
67     // !Getters
68   private:
69   protected:
70     CString m_sRemoteNick;
71     CString m_sRemoteIP;
72     CString m_sFileName;
73     CString m_sLocalFile;
74     CString m_sSendBuf;
75     unsigned short m_uRemotePort;
76     unsigned long long m_uFileSize;
77     unsigned long long m_uBytesSoFar;
78     bool m_bSend;
79     bool m_bNoDelFile;
80     CFile* m_pFile;
81     CDCCMod* m_pModule;
82 };
83 
84 class CDCCMod : public CModule {
85   public:
MODCONSTRUCTOR(CDCCMod)86     MODCONSTRUCTOR(CDCCMod) {
87         AddHelpCommand();
88         AddCommand("Send", t_d("<nick> <file>"),
89                    t_d("Send a file from ZNC to someone"),
90                    [=](const CString& sLine) { SendCommand(sLine); });
91         AddCommand("Get", t_d("<file>"),
92                    t_d("Send a file from ZNC to your client"),
93                    [=](const CString& sLine) { GetCommand(sLine); });
94         AddCommand("ListTransfers", "", t_d("List current transfers"),
95                    [=](const CString& sLine) { ListTransfersCommand(sLine); });
96     }
97 
~CDCCMod()98     ~CDCCMod() override {}
99 
100 #ifndef MOD_DCC_ALLOW_EVERYONE
OnLoad(const CString & sArgs,CString & sMessage)101     bool OnLoad(const CString& sArgs, CString& sMessage) override {
102         if (!GetUser()->IsAdmin()) {
103             sMessage = t_s("You must be admin to use the DCC module");
104             return false;
105         }
106 
107         return true;
108     }
109 #endif
110 
SendFile(const CString & sRemoteNick,const CString & sFileName)111     bool SendFile(const CString& sRemoteNick, const CString& sFileName) {
112         CString sFullPath = CDir::ChangeDir(GetSavePath(), sFileName,
113                                             CZNC::Get().GetHomePath());
114         CDCCSock* pSock = new CDCCSock(this, sRemoteNick, sFullPath);
115 
116         CFile* pFile = pSock->OpenFile(false);
117 
118         if (!pFile) {
119             delete pSock;
120             return false;
121         }
122 
123         CString sLocalDCCIP = GetUser()->GetLocalDCCIP();
124         unsigned short uPort = CZNC::Get().GetManager().ListenRand(
125             "DCC::LISTEN::" + sRemoteNick, sLocalDCCIP, false, SOMAXCONN, pSock,
126             120);
127 
128         if (GetUser()->GetNick().Equals(sRemoteNick)) {
129             PutUser(":*dcc!znc@znc.in PRIVMSG " + sRemoteNick +
130                     " :\001DCC SEND " + pFile->GetShortName() + " " +
131                     CString(CUtils::GetLongIP(sLocalDCCIP)) + " " +
132                     CString(uPort) + " " + CString(pFile->GetSize()) + "\001");
133         } else {
134             PutIRC("PRIVMSG " + sRemoteNick + " :\001DCC SEND " +
135                    pFile->GetShortName() + " " +
136                    CString(CUtils::GetLongIP(sLocalDCCIP)) + " " +
137                    CString(uPort) + " " + CString(pFile->GetSize()) + "\001");
138         }
139 
140         PutModule(t_f("Attempting to send [{1}] to [{2}].")(
141             pFile->GetShortName(), sRemoteNick));
142         return true;
143     }
144 
GetFile(const CString & sRemoteNick,const CString & sRemoteIP,unsigned short uRemotePort,const CString & sFileName,unsigned long uFileSize)145     bool GetFile(const CString& sRemoteNick, const CString& sRemoteIP,
146                  unsigned short uRemotePort, const CString& sFileName,
147                  unsigned long uFileSize) {
148         if (CFile::Exists(sFileName)) {
149             PutModule(t_f("Receiving [{1}] from [{2}]: File already exists.")(
150                 sFileName, sRemoteNick));
151             return false;
152         }
153 
154         CDCCSock* pSock = new CDCCSock(this, sRemoteNick, sRemoteIP,
155                                        uRemotePort, sFileName, uFileSize);
156 
157         if (!pSock->OpenFile()) {
158             delete pSock;
159             return false;
160         }
161 
162         CZNC::Get().GetManager().Connect(sRemoteIP, uRemotePort,
163                                          "DCC::GET::" + sRemoteNick, 60, false,
164                                          GetUser()->GetLocalDCCIP(), pSock);
165 
166         PutModule(
167             t_f("Attempting to connect to [{1} {2}] in order to download [{3}] "
168                 "from [{4}].")(sRemoteIP, uRemotePort, sFileName, sRemoteNick));
169         return true;
170     }
171 
SendCommand(const CString & sLine)172     void SendCommand(const CString& sLine) {
173         CString sToNick = sLine.Token(1);
174         CString sFile = sLine.Token(2);
175         CString sAllowedPath = GetSavePath();
176         CString sAbsolutePath;
177 
178         if ((sToNick.empty()) || (sFile.empty())) {
179             PutModule(t_s("Usage: Send <nick> <file>"));
180             return;
181         }
182 
183         sAbsolutePath = CDir::CheckPathPrefix(sAllowedPath, sFile);
184 
185         if (sAbsolutePath.empty()) {
186             PutStatus(t_s("Illegal path."));
187             return;
188         }
189 
190         SendFile(sToNick, sFile);
191     }
192 
GetCommand(const CString & sLine)193     void GetCommand(const CString& sLine) {
194         CString sFile = sLine.Token(1);
195         CString sAllowedPath = GetSavePath();
196         CString sAbsolutePath;
197 
198         if (sFile.empty()) {
199             PutModule(t_s("Usage: Get <file>"));
200             return;
201         }
202 
203         sAbsolutePath = CDir::CheckPathPrefix(sAllowedPath, sFile);
204 
205         if (sAbsolutePath.empty()) {
206             PutModule(t_s("Illegal path."));
207             return;
208         }
209 
210         SendFile(GetUser()->GetNick(), sFile);
211     }
212 
ListTransfersCommand(const CString & sLine)213     void ListTransfersCommand(const CString& sLine) {
214         CTable Table;
215         Table.AddColumn(t_s("Type", "list"));
216         Table.AddColumn(t_s("State", "list"));
217         Table.AddColumn(t_s("Speed", "list"));
218         Table.AddColumn(t_s("Nick", "list"));
219         Table.AddColumn(t_s("IP", "list"));
220         Table.AddColumn(t_s("File", "list"));
221 
222         set<CSocket*>::const_iterator it;
223         for (it = BeginSockets(); it != EndSockets(); ++it) {
224             CDCCSock* pSock = (CDCCSock*)*it;
225 
226             Table.AddRow();
227             Table.SetCell(t_s("Nick", "list"), pSock->GetRemoteNick());
228             Table.SetCell(t_s("IP", "list"), pSock->GetRemoteIP());
229             Table.SetCell(t_s("File", "list"), pSock->GetFileName());
230 
231             if (pSock->IsSend()) {
232                 Table.SetCell(t_s("Type", "list"), t_s("Sending", "list-type"));
233             } else {
234                 Table.SetCell(t_s("Type", "list"), t_s("Getting", "list-type"));
235             }
236 
237             if (pSock->GetType() == Csock::LISTENER) {
238                 Table.SetCell(t_s("State", "list"),
239                               t_s("Waiting", "list-state"));
240             } else {
241                 Table.SetCell(t_s("State", "list"),
242                               CString::ToPercent(pSock->GetProgress()));
243                 Table.SetCell(t_s("Speed", "list"),
244                               t_f("{1} KiB/s")(static_cast<int>(
245                                   pSock->GetAvgRead() / 1024.0)));
246             }
247         }
248 
249         if (PutModule(Table) == 0) {
250             PutModule(t_s("You have no active DCC transfers."));
251         }
252     }
253 
OnModCTCP(const CString & sMessage)254     void OnModCTCP(const CString& sMessage) override {
255         if (sMessage.StartsWith("DCC RESUME ")) {
256             CString sFile = sMessage.Token(2);
257             unsigned short uResumePort = sMessage.Token(3).ToUShort();
258             unsigned long uResumeSize = sMessage.Token(4).ToULong();
259 
260             set<CSocket*>::const_iterator it;
261             for (it = BeginSockets(); it != EndSockets(); ++it) {
262                 CDCCSock* pSock = (CDCCSock*)*it;
263 
264                 if (pSock->GetLocalPort() == uResumePort) {
265                     if (pSock->Seek(uResumeSize)) {
266                         PutModule(
267                             t_f("Attempting to resume send from position {1} "
268                                 "of file [{2}] for [{3}]")(
269                                 uResumeSize, pSock->GetFileName(),
270                                 pSock->GetRemoteNick()));
271                         PutUser(":*dcc!znc@znc.in PRIVMSG " +
272                                 GetUser()->GetNick() + " :\001DCC ACCEPT " +
273                                 sFile + " " + CString(uResumePort) + " " +
274                                 CString(uResumeSize) + "\001");
275                     } else {
276                         PutModule(t_f(
277                             "Couldn't resume file [{1}] for [{2}]: not sending "
278                             "anything.")(sFile, GetUser()->GetNick()));
279                     }
280                 }
281             }
282         } else if (sMessage.StartsWith("DCC SEND ")) {
283             CString sLocalFile =
284                 CDir::CheckPathPrefix(GetSavePath(), sMessage.Token(2));
285             if (sLocalFile.empty()) {
286                 PutModule(t_f("Bad DCC file: {1}")(sMessage.Token(2)));
287             }
288             unsigned long uLongIP = sMessage.Token(3).ToULong();
289             unsigned short uPort = sMessage.Token(4).ToUShort();
290             unsigned long uFileSize = sMessage.Token(5).ToULong();
291             GetFile(GetClient()->GetNick(), CUtils::GetIP(uLongIP), uPort,
292                     sLocalFile, uFileSize);
293         }
294     }
295 };
296 
CDCCSock(CDCCMod * pMod,const CString & sRemoteNick,const CString & sLocalFile,unsigned long uFileSize,CFile * pFile)297 CDCCSock::CDCCSock(CDCCMod* pMod, const CString& sRemoteNick,
298                    const CString& sLocalFile, unsigned long uFileSize,
299                    CFile* pFile)
300     : CSocket(pMod) {
301     m_sRemoteNick = sRemoteNick;
302     m_uFileSize = uFileSize;
303     m_uRemotePort = 0;
304     m_uBytesSoFar = 0;
305     m_pModule = pMod;
306     m_pFile = pFile;
307     m_sLocalFile = sLocalFile;
308     m_bSend = true;
309     m_bNoDelFile = false;
310     SetMaxBufferThreshold(0);
311 }
312 
CDCCSock(CDCCMod * pMod,const CString & sRemoteNick,const CString & sRemoteIP,unsigned short uRemotePort,const CString & sLocalFile,unsigned long uFileSize)313 CDCCSock::CDCCSock(CDCCMod* pMod, const CString& sRemoteNick,
314                    const CString& sRemoteIP, unsigned short uRemotePort,
315                    const CString& sLocalFile, unsigned long uFileSize)
316     : CSocket(pMod) {
317     m_sRemoteNick = sRemoteNick;
318     m_sRemoteIP = sRemoteIP;
319     m_uRemotePort = uRemotePort;
320     m_uFileSize = uFileSize;
321     m_uBytesSoFar = 0;
322     m_pModule = pMod;
323     m_pFile = nullptr;
324     m_sLocalFile = sLocalFile;
325     m_bSend = false;
326     m_bNoDelFile = false;
327     SetMaxBufferThreshold(0);
328 }
329 
~CDCCSock()330 CDCCSock::~CDCCSock() {
331     if ((m_pFile) && (!m_bNoDelFile)) {
332         m_pFile->Close();
333         delete m_pFile;
334     }
335 }
336 
ReadData(const char * data,size_t len)337 void CDCCSock::ReadData(const char* data, size_t len) {
338     if (!m_pFile) {
339         DEBUG("File not open! closing get.");
340         if (m_bSend) {
341             m_pModule->PutModule(t_f("Sending [{1}] to [{2}]: File not open!")(
342                 m_sFileName, m_sRemoteNick));
343         } else {
344             m_pModule->PutModule(
345                 t_f("Receiving [{1}] from [{2}]: File not open!")(
346                     m_sFileName, m_sRemoteNick));
347         }
348         Close();
349         return;
350     }
351 
352     // DCC specs says the receiving end sends the number of bytes it
353     // received so far as a 4 byte integer in network byte order, so we need
354     // uint32_t to do the job portably. This also means that the maximum
355     // file that we can transfer is 4 GiB big (see OpenFile()).
356     if (m_bSend) {
357         m_sSendBuf.append(data, len);
358 
359         while (m_sSendBuf.size() >= 4) {
360             uint32_t iRemoteSoFar;
361             memcpy(&iRemoteSoFar, m_sSendBuf.data(), sizeof(iRemoteSoFar));
362             iRemoteSoFar = ntohl(iRemoteSoFar);
363 
364             if ((iRemoteSoFar + 65536) >= m_uBytesSoFar) {
365                 SendPacket();
366             }
367 
368             m_sSendBuf.erase(0, 4);
369         }
370     } else {
371         m_pFile->Write(data, len);
372         m_uBytesSoFar += len;
373         uint32_t uSoFar = htonl((uint32_t)m_uBytesSoFar);
374         Write((char*)&uSoFar, sizeof(uSoFar));
375 
376         if (m_uBytesSoFar >= m_uFileSize) {
377             Close();
378         }
379     }
380 }
381 
ConnectionRefused()382 void CDCCSock::ConnectionRefused() {
383     DEBUG(GetSockName() << " == ConnectionRefused()");
384     if (m_bSend) {
385         m_pModule->PutModule(t_f("Sending [{1}] to [{2}]: Connection refused.")(
386             m_sFileName, m_sRemoteNick));
387     } else {
388         m_pModule->PutModule(
389             t_f("Receiving [{1}] from [{2}]: Connection refused.")(
390                 m_sFileName, m_sRemoteNick));
391     }
392 }
393 
Timeout()394 void CDCCSock::Timeout() {
395     DEBUG(GetSockName() << " == Timeout()");
396     if (m_bSend) {
397         m_pModule->PutModule(t_f("Sending [{1}] to [{2}]: Timeout.")(
398             m_sFileName, m_sRemoteNick));
399     } else {
400         m_pModule->PutModule(
401             t_f("Receiving [{1}] from [{2}]: Timeout.")(
402                 m_sFileName, m_sRemoteNick));
403     }
404 }
405 
SockError(int iErrno,const CString & sDescription)406 void CDCCSock::SockError(int iErrno, const CString& sDescription) {
407     DEBUG(GetSockName() << " == SockError(" << iErrno << ", " << sDescription
408                         << ")");
409     if (m_bSend) {
410         m_pModule->PutModule(
411             t_f("Sending [{1}] to [{2}]: Socket error {3}: {4}")(
412                 m_sFileName, m_sRemoteNick, iErrno, sDescription));
413     } else {
414         m_pModule->PutModule(
415             t_f("Receiving [{1}] from [{2}]: Socket error {3}: {4}")(
416                 m_sFileName, m_sRemoteNick, iErrno, sDescription));
417     }
418 }
419 
Connected()420 void CDCCSock::Connected() {
421     DEBUG(GetSockName() << " == Connected(" << GetRemoteIP() << ")");
422     if (m_bSend) {
423         m_pModule->PutModule(t_f("Sending [{1}] to [{2}]: Transfer started.")(
424             m_sFileName, m_sRemoteNick));
425     } else {
426         m_pModule->PutModule(
427             t_f("Receiving [{1}] from [{2}]: Transfer started.")(
428                 m_sFileName, m_sRemoteNick));
429     }
430 
431     if (m_bSend) {
432         SendPacket();
433     }
434 
435     SetTimeout(120);
436 }
437 
Disconnected()438 void CDCCSock::Disconnected() {
439     const CString sStart = ((m_bSend) ? "DCC -> [" : "DCC <- [") +
440                            m_sRemoteNick + "][" + m_sFileName + "] - ";
441 
442     DEBUG(GetSockName() << " == Disconnected()");
443 
444     if (m_uBytesSoFar > m_uFileSize) {
445         if (m_bSend) {
446             m_pModule->PutModule(t_f("Sending [{1}] to [{2}]: Too much data!")(
447                 m_sFileName, m_sRemoteNick));
448         } else {
449             m_pModule->PutModule(
450                 t_f("Receiving [{1}] from [{2}]: Too much data!")(
451                     m_sFileName, m_sRemoteNick));
452         }
453     } else if (m_uBytesSoFar == m_uFileSize) {
454         if (m_bSend) {
455             m_pModule->PutModule(
456                 t_f("Sending [{1}] to [{2}] completed at {3} KiB/s")(
457                     m_sFileName, m_sRemoteNick,
458                     static_cast<int>(GetAvgWrite() / 1024.0)));
459         } else {
460             m_pModule->PutModule(
461                 t_f("Receiving [{1}] from [{2}] completed at {3} KiB/s")(
462                     m_sFileName, m_sRemoteNick,
463                     static_cast<int>(GetAvgRead() / 1024.0)));
464         }
465     } else {
466         m_pModule->PutModule(sStart + "Incomplete!");
467     }
468 }
469 
SendPacket()470 void CDCCSock::SendPacket() {
471     if (!m_pFile) {
472         if (m_bSend) {
473             m_pModule->PutModule(
474                 t_f("Sending [{1}] to [{2}]: File closed prematurely.")(
475                     m_sFileName, m_sRemoteNick));
476         } else {
477             m_pModule->PutModule(
478                 t_f("Receiving [{1}] from [{2}]: File closed prematurely.")(
479                     m_sFileName, m_sRemoteNick));
480         }
481 
482         Close();
483         return;
484     }
485 
486     if (GetInternalWriteBuffer().size() > 1024 * 1024) {
487         // There is still enough data to be written, don't add more
488         // stuff to that buffer.
489         DEBUG("SendPacket(): Skipping send, buffer still full enough ["
490               << GetInternalWriteBuffer().size() << "][" << m_sRemoteNick
491               << "][" << m_sFileName << "]");
492         return;
493     }
494 
495     char szBuf[4096];
496     ssize_t iLen = m_pFile->Read(szBuf, 4096);
497 
498     if (iLen < 0) {
499         if (m_bSend) {
500             m_pModule->PutModule(
501                 t_f("Sending [{1}] to [{2}]: Error reading from file.")(
502                     m_sFileName, m_sRemoteNick));
503         } else {
504             m_pModule->PutModule(
505                 t_f("Receiving [{1}] from [{2}]: Error reading from file.")(
506                     m_sFileName, m_sRemoteNick));
507         }
508 
509         Close();
510         return;
511     }
512 
513     if (iLen > 0) {
514         Write(szBuf, iLen);
515         m_uBytesSoFar += iLen;
516     }
517 }
518 
GetSockObj(const CString & sHost,unsigned short uPort)519 Csock* CDCCSock::GetSockObj(const CString& sHost, unsigned short uPort) {
520     Close();
521 
522     CDCCSock* pSock = new CDCCSock(m_pModule, m_sRemoteNick, m_sLocalFile,
523                                    m_uFileSize, m_pFile);
524     pSock->SetSockName("DCC::SEND::" + m_sRemoteNick);
525     pSock->SetTimeout(120);
526     pSock->SetFileName(m_sFileName);
527     pSock->SetFileOffset(m_uBytesSoFar);
528     m_bNoDelFile = true;
529 
530     return pSock;
531 }
532 
OpenFile(bool bWrite)533 CFile* CDCCSock::OpenFile(bool bWrite) {
534     if ((m_pFile) || (m_sLocalFile.empty())) {
535         if (m_bSend) {
536             m_pModule->PutModule(
537                 t_f("Sending [{1}] to [{2}]: Unable to open file.")(
538                     m_sFileName, m_sRemoteNick));
539         } else {
540             m_pModule->PutModule(
541                 t_f("Receiving [{1}] from [{2}]: Unable to open file.")(
542                     m_sFileName, m_sRemoteNick));
543         }
544         return nullptr;
545     }
546 
547     m_pFile = new CFile(m_sLocalFile);
548 
549     if (bWrite) {
550         if (m_pFile->Exists()) {
551             delete m_pFile;
552             m_pFile = nullptr;
553             m_pModule->PutModule(
554                 t_f("Receiving [{1}] from [{2}]: File already exists.")(
555                     m_sFileName, m_sRemoteNick));
556             return nullptr;
557         }
558 
559         if (!m_pFile->Open(O_WRONLY | O_TRUNC | O_CREAT)) {
560             delete m_pFile;
561             m_pFile = nullptr;
562             m_pModule->PutModule(
563                 t_f("Receiving [{1}] from [{2}]: Could not open file.")(
564                     m_sFileName, m_sRemoteNick));
565             return nullptr;
566         }
567     } else {
568         if (!m_pFile->IsReg()) {
569             delete m_pFile;
570             m_pFile = nullptr;
571             m_pModule->PutModule(
572                 t_f("Sending [{1}] to [{2}]: Not a file.")(
573                     m_sFileName, m_sRemoteNick));
574             return nullptr;
575         }
576 
577         if (!m_pFile->Open()) {
578             delete m_pFile;
579             m_pFile = nullptr;
580             m_pModule->PutModule(
581                 t_f("Sending [{1}] to [{2}]: Could not open file.")(
582                     m_sFileName, m_sRemoteNick));
583             return nullptr;
584         }
585 
586         // The DCC specs only allow file transfers with files smaller
587         // than 4GiB (see ReadData()).
588         unsigned long long uFileSize = m_pFile->GetSize();
589         if (uFileSize > (unsigned long long)0xffffffffULL) {
590             delete m_pFile;
591             m_pFile = nullptr;
592             m_pModule->PutModule(
593                 t_f("Sending [{1}] to [{2}]: File too large (>4 GiB).")(
594                     m_sFileName, m_sRemoteNick));
595             return nullptr;
596         }
597 
598         m_uFileSize = uFileSize;
599     }
600 
601     m_sFileName = m_pFile->GetShortName();
602 
603     return m_pFile;
604 }
605 
Seek(unsigned long int uPos)606 bool CDCCSock::Seek(unsigned long int uPos) {
607     if (m_pFile) {
608         if (m_pFile->Seek(uPos)) {
609             m_uBytesSoFar = uPos;
610             return true;
611         }
612     }
613 
614     return false;
615 }
616 
617 template <>
TModInfo(CModInfo & Info)618 void TModInfo<CDCCMod>(CModInfo& Info) {
619     Info.SetWikiPage("dcc");
620 }
621 
622 USERMODULEDEFS(CDCCMod,
623                t_s("This module allows you to transfer files to and from ZNC"))
624