void NntpProcessor::Run() { m_connection->SetSuppressErrors(false); #ifndef DISABLE_TLS if (m_secureCert && !m_connection->StartTls(false, m_secureCert, m_secureKey)) { error("Could not establish secure connection to nntp-client: Start TLS failed"); return; } #endif info("[%i] Incoming connection from: %s", m_id, m_connection->GetHost() ); m_connection->WriteLine("200 Welcome (NServ)\r\n"); CharBuffer buf(1024); int bytesRead = 0; while (CString line = m_connection->ReadLine(buf, 1024, &bytesRead)) { line.TrimRight(); detail("[%i] Received: %s", m_id, *line); if (!strncasecmp(line, "ARTICLE ", 8)) { m_messageid = line + 8; m_sendHeaders = true; ServArticle(); } else if (!strncasecmp(line, "BODY ", 5)) { m_messageid = line + 5; m_sendHeaders = false; ServArticle(); } else if (!strncasecmp(line, "GROUP ", 6)) { m_connection->WriteLine(CString::FormatStr("211 0 0 0 %s\r\n", line + 7)); } else if (!strncasecmp(line, "AUTHINFO ", 9)) { m_connection->WriteLine("281 Authentication accepted\r\n"); } else if (!strcasecmp(line, "QUIT")) { detail("[%i] Closing connection", m_id); m_connection->WriteLine("205 Connection closing\r\n"); break; } else { warn("[%i] Unknown command: %s", m_id, *line); m_connection->WriteLine("500 Unknown command\r\n"); } } m_connection->SetGracefull(true); m_connection->Disconnect(); }
/* Message-id format: <file-path-relative-to-dataDir?xxx=yyy:zzz!1,2,3> where: xxx - part number (integer) xxx - offset from which to read the files (integer) yyy - size of file block to return (integer) 1,2,3 - list of server ids, which have the article (optional), if the list is given and current server is not in the list the "article not found"-error is returned. Examples: <parchecker/testfile.dat?1=0:50000> - return first 50000 bytes starting from beginning <parchecker/testfile.dat?2=50000:50000> - return 50000 bytes starting from offset 50000 <parchecker/testfile.dat?2=50000:50000!2> - article is missing on server 1 */ void NntpProcessor::ServArticle() { detail("[%i] Serving: %s", m_id, m_messageid); if (m_latency) { usleep(1000 * m_latency); } bool ok = false; const char* from = strchr(m_messageid, '?'); const char* off = strchr(m_messageid, '='); const char* to = strchr(m_messageid, ':'); const char* end = strchr(m_messageid, '>'); const char* serv = strchr(m_messageid, '!'); if (from && off && to && end) { m_filename.Set(m_messageid + 1, (int)(from - m_messageid - 1)); m_part = atoi(from + 1); m_offset = atoll(off + 1); m_size = atoi(to + 1); ok = !serv || ServerInList(serv + 1); if (ok) { SendSegment(); return; } if (!ok) { m_connection->WriteLine("430 No Such Article Found\r\n"); } } else { m_connection->WriteLine("430 No Such Article Found (invalid message id format)\r\n"); } }
void NntpProcessor::SendSegment() { detail("[%i] Sending segment %s (%i=%lli:%i)", m_id, *m_filename, m_part, (long long)m_offset, m_size); if (m_speed > 0) { m_start = Util::GetCurrentTicks(); } BString<1024> fullFilename("%s/%s", m_dataDir, *m_filename); BString<1024> cacheFileDir("%s/%s", m_cacheDir, *m_filename); BString<1024> cacheFileName("%i=%lli-%i", m_part, (long long)m_offset, m_size); BString<1024> cacheFullFilename("%s/%s", *cacheFileDir, *cacheFileName); BString<1024> cacheKey("%s/%s", *m_filename, *cacheFileName); const char* cachedData = nullptr; int cachedSize; if (m_cache) { m_cache->Find(cacheKey, cachedData, cachedSize); } DiskFile cacheFile; bool readCache = !cachedData && m_cacheDir && cacheFile.Open(cacheFullFilename, DiskFile::omRead); bool writeCache = !cachedData && m_cacheDir && !readCache; StringBuilder cacheMem; if (m_cache && !cachedData) { cacheMem.Reserve((int)(m_size * 1.1)); } CString errmsg; if (writeCache && !FileSystem::ForceDirectories(cacheFileDir, errmsg)) { error("Could not create directory %s: %s", *cacheFileDir, *errmsg); } if (writeCache && !cacheFile.Open(cacheFullFilename, DiskFile::omWrite)) { error("Could not create file %s: %s", *cacheFullFilename, *FileSystem::GetLastErrorMessage()); } if (!cachedData && !readCache && !FileSystem::FileExists(fullFilename)) { m_connection->WriteLine(CString::FormatStr("430 Article not found\r\n")); return; } YEncoder encoder(fullFilename, m_part, m_offset, m_size, [proc = this, writeCache, &cacheFile, &cacheMem](const char* buf, int size) { if (proc->m_cache) { cacheMem.Append(buf); } if (writeCache) { cacheFile.Write(buf, size); } proc->SendData(buf, size); }); if (!cachedData && !readCache && !encoder.OpenFile(errmsg)) { m_connection->WriteLine(CString::FormatStr("403 %s\r\n", *errmsg)); return; } m_connection->WriteLine(CString::FormatStr("%i, 0 %s\r\n", m_sendHeaders ? 222 : 220, m_messageid)); if (m_sendHeaders) { m_connection->WriteLine(CString::FormatStr("Message-ID: %s\r\n", m_messageid)); m_connection->WriteLine(CString::FormatStr("Subject: \"%s\"\r\n", FileSystem::BaseFileName(m_filename))); m_connection->WriteLine("\r\n"); } if (cachedData) { SendData(cachedData, cachedSize); } else if (readCache) { cacheFile.Seek(0, DiskFile::soEnd); int size = (int)cacheFile.Position(); CharBuffer buf(size); cacheFile.Seek(0); if (cacheFile.Read((char*)buf, size) != size) { error("Could not read file %s: %s", *cacheFullFilename, *FileSystem::GetLastErrorMessage()); } if (m_cache) { cacheMem.Append(buf, size); } SendData(buf, size); } else { encoder.WriteSegment(); } if (!cachedData && cacheMem.Length() > 0) { m_cache->Append(cacheKey, cacheMem, cacheMem.Length()); } m_connection->WriteLine(".\r\n"); }