~ubuntu-branches/debian/experimental/nzbget/experimental

« back to all changes in this revision

Viewing changes to Scanner.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Moog
  • Date: 2011-05-06 19:01:38 UTC
  • Revision ID: james.westby@ubuntu.com-20110506190138-6l4nro6jmhr3837i
Tags: upstream-0.7.0
ImportĀ upstreamĀ versionĀ 0.7.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  This file is part of nzbget
 
3
 *
 
4
 *  Copyright (C) 2007-2010 Andrei Prygounkov <hugbug@users.sourceforge.net>
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 *
 
20
 * $Revision: 378 $
 
21
 * $Date: 2010-01-29 10:34:44 +0100 (Fri, 29 Jan 2010) $
 
22
 *
 
23
 */
 
24
 
 
25
 
 
26
#ifdef HAVE_CONFIG_H
 
27
#include <config.h>
 
28
#endif
 
29
 
 
30
#ifdef WIN32
 
31
#include "win32.h"
 
32
#endif
 
33
 
 
34
#include <stdlib.h>
 
35
#include <string.h>
 
36
#include <fstream>
 
37
#ifdef WIN32
 
38
#include <direct.h>
 
39
#endif
 
40
#include <sys/stat.h>
 
41
#include <errno.h>
 
42
 
 
43
#include "nzbget.h"
 
44
#include "Scanner.h"
 
45
#include "Options.h"
 
46
#include "Log.h"
 
47
#include "QueueCoordinator.h"
 
48
#include "ScriptController.h"
 
49
#include "DiskState.h"
 
50
#include "Util.h"
 
51
 
 
52
extern QueueCoordinator* g_pQueueCoordinator;
 
53
extern Options* g_pOptions;
 
54
extern DiskState* g_pDiskState;
 
55
 
 
56
Scanner::FileData::FileData(const char* szFilename)
 
57
{
 
58
        m_szFilename = strdup(szFilename);
 
59
        m_iSize = 0;
 
60
        m_tLastChange = 0;
 
61
}
 
62
 
 
63
Scanner::FileData::~FileData()
 
64
{
 
65
        free(m_szFilename);
 
66
}
 
67
 
 
68
Scanner::Scanner()
 
69
{
 
70
        debug("Creating Scanner");
 
71
 
 
72
        m_bRequestedNZBDirScan = false;
 
73
        m_iNZBDirInterval = g_pOptions->GetNzbDirInterval() * 1000;
 
74
        m_iPass = 0;
 
75
        m_iStepMSec = 0;
 
76
 
 
77
        const char* szNZBScript = g_pOptions->GetNZBProcess();
 
78
        m_bNZBScript = szNZBScript && strlen(szNZBScript) > 0;
 
79
}
 
80
 
 
81
Scanner::~Scanner()
 
82
{
 
83
        debug("Destroying Scanner");
 
84
 
 
85
        for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); it++)
 
86
    {
 
87
        delete *it;
 
88
        }
 
89
        m_FileList.clear();
 
90
}
 
91
 
 
92
void Scanner::Check()
 
93
{
 
94
        if (g_pOptions->GetNzbDir() && (m_bRequestedNZBDirScan || 
 
95
                (!g_pOptions->GetPauseScan() && g_pOptions->GetNzbDirInterval() > 0 && 
 
96
                 m_iNZBDirInterval >= g_pOptions->GetNzbDirInterval() * 1000)))
 
97
        {
 
98
                // check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
 
99
                bool bCheckStat = !m_bRequestedNZBDirScan;
 
100
                m_bRequestedNZBDirScan = false;
 
101
                CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
 
102
                m_iNZBDirInterval = 0;
 
103
 
 
104
                // if NzbDirFileAge is less than NzbDirInterval (that can happen if NzbDirInterval
 
105
                // is set for rare scans like once per hour) we make 4 scans:
 
106
                //   - one additional scan is neccessary to check sizes of detected files;
 
107
                //   - another scan is required to check files which were extracted by NzbProcess-script;
 
108
                //   - third scan is needed to check sizes of extracted files.
 
109
                if (g_pOptions->GetNzbDirFileAge() < g_pOptions->GetNzbDirInterval())
 
110
                {
 
111
                        int iMaxPass = m_bNZBScript ? 3 : 1;
 
112
                        if (m_iPass < iMaxPass)
 
113
                        {
 
114
                                // scheduling another scan of incoming directory in NzbDirFileAge seconds.
 
115
                                m_iNZBDirInterval = (g_pOptions->GetNzbDirInterval() - g_pOptions->GetNzbDirFileAge()) * 1000;
 
116
                                m_iPass++;
 
117
                        }
 
118
                        else
 
119
                        {
 
120
                                m_iPass = 0;
 
121
                        }
 
122
                }
 
123
 
 
124
                DropOldFiles();
 
125
        }
 
126
        m_iNZBDirInterval += m_iStepMSec;
 
127
}
 
128
 
 
129
/**
 
130
* Check if there are files in directory for incoming nzb-files
 
131
* and add them to download queue
 
132
*/
 
133
void Scanner::CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat)
 
134
{
 
135
        DirBrowser dir(szDirectory);
 
136
        while (const char* filename = dir.Next())
 
137
        {
 
138
                struct stat buffer;
 
139
                char fullfilename[1023 + 1]; // one char reserved for the trailing slash (if needed)
 
140
                snprintf(fullfilename, 1023, "%s%s", szDirectory, filename);
 
141
                fullfilename[1023 - 1] = '\0';
 
142
                if (!stat(fullfilename, &buffer))
 
143
                {
 
144
                        // check subfolders
 
145
                        if ((buffer.st_mode & S_IFDIR) != 0 && strcmp(filename, ".") && strcmp(filename, ".."))
 
146
                        {
 
147
                                fullfilename[strlen(fullfilename) + 1] = '\0';
 
148
                                fullfilename[strlen(fullfilename)] = PATH_SEPARATOR;
 
149
                                const char* szUseCategory = filename;
 
150
                                char szSubCategory[1024];
 
151
                                if (strlen(szCategory) > 0)
 
152
                                {
 
153
                                        snprintf(szSubCategory, 1023, "%s%c%s", szCategory, PATH_SEPARATOR, filename);
 
154
                                        szSubCategory[1024 - 1] = '\0';
 
155
                                        szUseCategory = szSubCategory;
 
156
                                }
 
157
                                CheckIncomingNZBs(fullfilename, szUseCategory, bCheckStat);
 
158
                        }
 
159
                        else if ((buffer.st_mode & S_IFDIR) == 0 && CanProcessFile(fullfilename, bCheckStat))
 
160
                        {
 
161
                                ProcessIncomingFile(szDirectory, filename, fullfilename, szCategory);
 
162
                        }
 
163
                }
 
164
        }
 
165
}
 
166
 
 
167
/**
 
168
 * Only files which were not changed during last g_pOptions->GetNzbDirFileAge() seconds
 
169
 * can be processed. That prevents the processing of files, which are currently being
 
170
 * copied into nzb-directory (eg. being downloaded in web-browser).
 
171
 */
 
172
bool Scanner::CanProcessFile(const char* szFullFilename, bool bCheckStat)
 
173
{
 
174
        const char* szExtension = strrchr(szFullFilename, '.');
 
175
        if (!szExtension ||
 
176
                !strcasecmp(szExtension, ".queued") ||
 
177
                !strcasecmp(szExtension, ".error") ||
 
178
                !strcasecmp(szExtension, ".processed"))
 
179
        {
 
180
                return false;
 
181
        }
 
182
 
 
183
        if (!bCheckStat)
 
184
        {
 
185
                return true;
 
186
        }
 
187
 
 
188
        long long lSize = Util::FileSize(szFullFilename);
 
189
        time_t tCurrent = time(NULL);
 
190
        bool bCanProcess = false;
 
191
        bool bInList = false;
 
192
 
 
193
        for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); it++)
 
194
    {
 
195
        FileData* pFileData = *it;
 
196
                if (!strcmp(pFileData->GetFilename(), szFullFilename))
 
197
                {
 
198
                        bInList = true;
 
199
                        if (pFileData->GetSize() == lSize &&
 
200
                                tCurrent - pFileData->GetLastChange() >= g_pOptions->GetNzbDirFileAge())
 
201
                        {
 
202
                                bCanProcess = true;
 
203
                                delete pFileData;
 
204
                                m_FileList.erase(it);
 
205
                        }
 
206
                        else
 
207
                        {
 
208
                                pFileData->SetSize(lSize);
 
209
                                if (pFileData->GetSize() != lSize)
 
210
                                {
 
211
                                        pFileData->SetLastChange(tCurrent);
 
212
                                }
 
213
                        }
 
214
                        break;
 
215
                }
 
216
        }
 
217
 
 
218
        if (!bInList)
 
219
        {
 
220
                FileData* pFileData = new FileData(szFullFilename);
 
221
                pFileData->SetSize(lSize);
 
222
                pFileData->SetLastChange(tCurrent);
 
223
                m_FileList.push_back(pFileData);
 
224
        }
 
225
 
 
226
        return bCanProcess;
 
227
}
 
228
 
 
229
/**
 
230
 * Remove old files from the list of monitored files.
 
231
 * Normally these files are deleted from the list when they are processed.
 
232
 * However if a file was detected by function "CanProcessFile" once but wasn't 
 
233
 * processed later (for example if the user deleted it), it will stay in the list,
 
234
 * until we remove it here.
 
235
 */
 
236
void Scanner::DropOldFiles()
 
237
{
 
238
        time_t tCurrent = time(NULL);
 
239
 
 
240
        int i = 0;
 
241
        for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); )
 
242
    {
 
243
        FileData* pFileData = *it;
 
244
                if ((tCurrent - pFileData->GetLastChange() >= 
 
245
                        (g_pOptions->GetNzbDirInterval() + g_pOptions->GetNzbDirFileAge()) * 2) ||
 
246
                        // can occur if the system clock was adjusted
 
247
                        tCurrent < pFileData->GetLastChange())
 
248
                {
 
249
                        debug("Removing file %s from scan file list", pFileData->GetFilename());
 
250
 
 
251
                        delete pFileData;
 
252
                        m_FileList.erase(it);
 
253
                        it = m_FileList.begin() + i;
 
254
                }
 
255
                else
 
256
                {
 
257
                        it++;
 
258
                        i++;
 
259
                }
 
260
        }
 
261
}
 
262
 
 
263
void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory)
 
264
{
 
265
        const char* szExtension = strrchr(szBaseFilename, '.');
 
266
        if (!szExtension)
 
267
        {
 
268
                return;
 
269
        }
 
270
 
 
271
        char* szNZBCategory = strdup(szCategory);
 
272
        NZBParameterList* pParameterList = new NZBParameterList;
 
273
 
 
274
        bool bExists = true;
 
275
 
 
276
        if (m_bNZBScript && strcasecmp(szExtension, ".nzb_processed"))
 
277
        {
 
278
                NZBScriptController::ExecuteScript(g_pOptions->GetNZBProcess(), szFullFilename, szDirectory, &szNZBCategory, pParameterList); 
 
279
                bExists = Util::FileExists(szFullFilename);
 
280
                if (bExists && strcasecmp(szExtension, ".nzb"))
 
281
                {
 
282
                        char bakname2[1024];
 
283
                        bool bRenameOK = Util::RenameBak(szFullFilename, "processed", false, bakname2, 1024);
 
284
                        if (!bRenameOK)
 
285
                        {
 
286
                                error("Could not rename file %s to %s! Errcode: %i", szFullFilename, bakname2, errno);
 
287
                        }
 
288
                }
 
289
        }
 
290
 
 
291
        if (!strcasecmp(szExtension, ".nzb_processed"))
 
292
        {
 
293
                char szRenamedName[1024];
 
294
                bool bRenameOK = Util::RenameBak(szFullFilename, "nzb", true, szRenamedName, 1024);
 
295
                if (bRenameOK)
 
296
                {
 
297
                        AddFileToQueue(szRenamedName, szNZBCategory, pParameterList);
 
298
                }
 
299
                else
 
300
                {
 
301
                        error("Could not rename file %s to %s! Errcode: %i", szFullFilename, szRenamedName, errno);
 
302
                }
 
303
        }
 
304
        else if (bExists && !strcasecmp(szExtension, ".nzb"))
 
305
        {
 
306
                AddFileToQueue(szFullFilename, szNZBCategory, pParameterList);
 
307
        }
 
308
 
 
309
        for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
 
310
        {
 
311
                delete *it;
 
312
        }
 
313
        pParameterList->clear();
 
314
        delete pParameterList;
 
315
 
 
316
        free(szNZBCategory);
 
317
}
 
318
 
 
319
void Scanner::AddFileToQueue(const char* szFilename, const char* szCategory, NZBParameterList* pParameterList)
 
320
{
 
321
        const char* szBasename = Util::BaseFileName(szFilename);
 
322
 
 
323
        info("Collection %s found", szBasename);
 
324
 
 
325
        NZBFile* pNZBFile = NZBFile::CreateFromFile(szFilename, szCategory);
 
326
        if (!pNZBFile)
 
327
        {
 
328
                error("Could not add collection %s to queue", szBasename);
 
329
        }
 
330
 
 
331
        char bakname2[1024];
 
332
        bool bRenameOK = Util::RenameBak(szFilename, pNZBFile ? "queued" : "error", false, bakname2, 1024);
 
333
        if (!bRenameOK)
 
334
        {
 
335
                error("Could not rename file %s to %s! Errcode: %i", szFilename, bakname2, errno);
 
336
        }
 
337
 
 
338
        if (pNZBFile && bRenameOK)
 
339
        {
 
340
                pNZBFile->GetNZBInfo()->SetQueuedFilename(bakname2);
 
341
 
 
342
                for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
 
343
                {
 
344
                        NZBParameter* pParameter = *it;
 
345
                        pNZBFile->GetNZBInfo()->SetParameter(pParameter->GetName(), pParameter->GetValue());
 
346
                }
 
347
 
 
348
                g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, false);
 
349
                info("Collection %s added to queue", szBasename);
 
350
        }
 
351
 
 
352
        if (pNZBFile)
 
353
        {
 
354
                delete pNZBFile;
 
355
        }
 
356
}
 
357
 
 
358
void Scanner::ScanNZBDir()
 
359
{
 
360
        // ideally we should use mutex to access "m_bRequestedNZBDirScan",
 
361
        // but it's not critical here.
 
362
        m_bRequestedNZBDirScan = true;
 
363
}