source: filezilla/trunk/fuentes/src/interface/queue_storage.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 3 years ago

First release to xenial

File size: 31.6 KB
Line 
1#include <filezilla.h>
2#include "queue_storage.h"
3#include "Options.h"
4#include "queue.h"
5
6#include <sqlite3.h>
7#include <wx/wx.h>
8
9#include <unordered_map>
10
11#define INVALID_DATA -1
12
13enum class Column_type
14{
15        text,
16        integer
17};
18
19enum _column_flags
20{
21        not_null = 1,
22        autoincrement
23};
24
25struct _column
26{
27        const wxChar* const name;
28        Column_type type;
29        unsigned int flags;
30};
31
32namespace server_table_column_names
33{
34        enum type
35        {
36                id,
37                host,
38                port,
39                user,
40                password,
41                account,
42                protocol,
43                type,
44                logontype,
45                timezone_offset,
46                transfer_mode,
47                max_connections,
48                encoding,
49                bypass_proxy,
50                post_login_commands,
51                name
52        };
53}
54
55_column server_table_columns[] = {
56        { _T("id"), Column_type::integer, not_null | autoincrement },
57        { _T("host"), Column_type::text, not_null },
58        { _T("port"), Column_type::integer, 0 },
59        { _T("user"), Column_type::text, 0 },
60        { _T("password"), Column_type::text, 0 },
61        { _T("account"), Column_type::text, 0 },
62        { _T("protocol"), Column_type::integer, 0 },
63        { _T("type"), Column_type::integer, 0 },
64        { _T("logontype"), Column_type::integer, 0 },
65        { _T("timezone_offset"), Column_type::integer, 0 },
66        { _T("transfer_mode"), Column_type::text, 0 },
67        { _T("max_connections"), Column_type::integer, 0 },
68        { _T("encoding"), Column_type::text, 0 },
69        { _T("bypass_proxy"), Column_type::integer, 0 },
70        { _T("post_login_commands"), Column_type::text, 0 },
71        { _T("name"), Column_type::text, 0 }
72};
73
74namespace file_table_column_names
75{
76        enum type
77        {
78                id,
79                server,
80                source_file,
81                target_file,
82                local_path,
83                remote_path,
84                download,
85                size,
86                error_count,
87                priority,
88                ascii_file,
89                default_exists_action
90        };
91}
92
93_column file_table_columns[] = {
94        { _T("id"), Column_type::integer, not_null | autoincrement },
95        { _T("server"), Column_type::integer, not_null },
96        { _T("source_file"), Column_type::text, 0 },
97        { _T("target_file"), Column_type::text, 0 },
98        { _T("local_path"), Column_type::integer, 0 },
99        { _T("remote_path"), Column_type::integer, 0 },
100        { _T("download"), Column_type::integer, not_null },
101        { _T("size"), Column_type::integer, 0 },
102        { _T("error_count"), Column_type::integer, 0 },
103        { _T("priority"), Column_type::integer, 0 },
104        { _T("ascii_file"), Column_type::integer, 0 },
105        { _T("default_exists_action"), Column_type::integer, 0 }
106};
107
108namespace path_table_column_names
109{
110        enum type
111        {
112                id,
113                path
114        };
115}
116
117_column path_table_columns[] = {
118        { _T("id"), Column_type::integer, not_null | autoincrement },
119        { _T("path"), Column_type::text, not_null }
120};
121
122struct fast_equal
123{
124        bool operator()(wxString const& lhs, wxString const& rhs) const
125        {
126                return lhs == rhs;
127        }
128};
129
130class CQueueStorage::Impl
131{
132public:
133        Impl()
134                : db_()
135                , insertServerQuery_()
136                , insertFileQuery_()
137                , insertLocalPathQuery_()
138                , insertRemotePathQuery_()
139                , selectServersQuery_()
140                , selectFilesQuery_()
141                , selectLocalPathQuery_()
142                , selectRemotePathQuery_()
143        {
144        }
145
146
147        void CreateTables();
148        wxString CreateColumnDefs(_column* columns, size_t count);
149
150        bool PrepareStatements();
151
152        sqlite3_stmt* PrepareStatement(const wxString& query);
153        sqlite3_stmt* PrepareInsertStatement(const wxString& name, const _column*, unsigned int count);
154
155        bool SaveServer(const CServerItem& item);
156        bool SaveFile(const CFileItem& item);
157        bool SaveDirectory(const CFolderItem& item);
158
159        int64_t SaveLocalPath(const CLocalPath& path);
160        int64_t SaveRemotePath(const CServerPath& path);
161
162        void ReadLocalPaths();
163        void ReadRemotePaths();
164
165        const CLocalPath& GetLocalPath(int64_t id) const;
166        const CServerPath& GetRemotePath(int64_t id) const;
167
168        bool Bind(sqlite3_stmt* statement, int index, int value);
169        bool Bind(sqlite3_stmt* statement, int index, int64_t value);
170        bool Bind(sqlite3_stmt* statement, int index, const wxString& value);
171        bool Bind(sqlite3_stmt* statement, int index, const char* const value);
172        bool BindNull(sqlite3_stmt* statement, int index);
173
174        wxString GetColumnText(sqlite3_stmt* statement, int index, bool shrink = true);
175        int64_t GetColumnInt64(sqlite3_stmt* statement, int index, int64_t def = 0);
176        int GetColumnInt(sqlite3_stmt* statement, int index, int def = 0);
177
178        int64_t ParseServerFromRow(CServer& server);
179        int64_t ParseFileFromRow(CFileItem** pItem);
180
181        bool MigrateSchema();
182
183        sqlite3* db_;
184
185        sqlite3_stmt* insertServerQuery_;
186        sqlite3_stmt* insertFileQuery_;
187        sqlite3_stmt* insertLocalPathQuery_;
188        sqlite3_stmt* insertRemotePathQuery_;
189
190        sqlite3_stmt* selectServersQuery_;
191        sqlite3_stmt* selectFilesQuery_;
192        sqlite3_stmt* selectLocalPathQuery_;
193        sqlite3_stmt* selectRemotePathQuery_;
194
195#ifndef __WXMSW__
196        wxMBConvUTF16 utf16_;
197#endif
198
199        // Caches to speed up saving and loading
200        void ClearCaches();
201
202        std::unordered_map<wxString, int64_t, wxStringHash, fast_equal> localPaths_;
203        std::unordered_map<wxString, int64_t, wxStringHash> remotePaths_; // No need for fast_equal as GetSafePath returns unshared string anyhow
204
205        std::map<int64_t, CLocalPath> reverseLocalPaths_;
206        std::map<int64_t, CServerPath> reverseRemotePaths_;
207};
208
209
210void CQueueStorage::Impl::ReadLocalPaths()
211{
212        if (!selectLocalPathQuery_)
213                return;
214
215        int res;
216        do
217        {
218                res = sqlite3_step(selectLocalPathQuery_);
219                if (res == SQLITE_ROW)
220                {
221                        int64_t id = GetColumnInt64(selectLocalPathQuery_, path_table_column_names::id);
222                        wxString localPathRaw = GetColumnText(selectLocalPathQuery_, path_table_column_names::path);
223                        CLocalPath localPath;
224                        if (id > 0 && !localPathRaw.empty() && localPath.SetPath(localPathRaw))
225                                reverseLocalPaths_[id] = localPath;
226                }
227        }
228        while (res == SQLITE_BUSY || res == SQLITE_ROW);
229
230        sqlite3_reset(selectLocalPathQuery_);
231}
232
233
234void CQueueStorage::Impl::ReadRemotePaths()
235{
236        if (!selectRemotePathQuery_)
237                return;
238
239        int res;
240        do
241        {
242                res = sqlite3_step(selectRemotePathQuery_);
243                if (res == SQLITE_ROW)
244                {
245                        int64_t id = GetColumnInt64(selectRemotePathQuery_, path_table_column_names::id);
246                        wxString remotePathRaw = GetColumnText(selectRemotePathQuery_, path_table_column_names::path);
247                        CServerPath remotePath;
248                        if (id > 0 && !remotePathRaw.empty() && remotePath.SetSafePath(remotePathRaw))
249                                reverseRemotePaths_[id] = remotePath;
250                }
251        }
252        while (res == SQLITE_BUSY || res == SQLITE_ROW);
253
254        sqlite3_reset(selectRemotePathQuery_);
255}
256
257
258const CLocalPath& CQueueStorage::Impl::GetLocalPath(int64_t id) const
259{
260        std::map<int64_t, CLocalPath>::const_iterator it = reverseLocalPaths_.find(id);
261        if (it != reverseLocalPaths_.end())
262                return it->second;
263
264        static CLocalPath const empty{};
265        return empty;
266}
267
268
269const CServerPath& CQueueStorage::Impl::GetRemotePath(int64_t id) const
270{
271        std::map<int64_t, CServerPath>::const_iterator it = reverseRemotePaths_.find(id);
272        if (it != reverseRemotePaths_.end())
273                return it->second;
274
275        static CServerPath const empty{};
276        return empty;
277}
278
279
280static int int_callback(void* p, int n, char** v, char**)
281{
282        int* i = static_cast<int*>(p);
283        if (!i || !n || !v || !*v)
284                return -1;
285
286        *i = atoi(*v);
287        return 0;
288}
289
290
291bool CQueueStorage::Impl::MigrateSchema()
292{
293        int version = 0;
294
295        if (sqlite3_exec(db_, "PRAGMA user_version", int_callback, &version, 0) != SQLITE_OK)
296                return false;
297
298        if (version < 1)
299                return sqlite3_exec(db_, "PRAGMA user_version = 1", 0, 0, 0) == SQLITE_OK;
300
301        return true;
302}
303
304
305void CQueueStorage::Impl::ClearCaches()
306{
307        localPaths_.clear();
308        remotePaths_.clear();
309        reverseLocalPaths_.clear();
310        reverseRemotePaths_.clear();
311}
312
313
314int64_t CQueueStorage::Impl::SaveLocalPath(const CLocalPath& path)
315{
316        std::unordered_map<wxString, int64_t, wxStringHash, fast_equal>::const_iterator it = localPaths_.find(path.GetPath());
317        if (it != localPaths_.end())
318                return it->second;
319
320        Bind(insertLocalPathQuery_, path_table_column_names::path, path.GetPath());
321
322        int res;
323        do {
324                res = sqlite3_step(insertLocalPathQuery_);
325        } while (res == SQLITE_BUSY);
326
327        sqlite3_reset(insertLocalPathQuery_);
328
329        if (res == SQLITE_DONE)
330        {
331                int64_t id = sqlite3_last_insert_rowid(db_);
332                localPaths_[path.GetPath()] = id;
333                return id;
334        }
335
336        return -1;
337}
338
339
340int64_t CQueueStorage::Impl::SaveRemotePath(const CServerPath& path)
341{
342        wxString const& safePath = path.GetSafePath();
343        std::unordered_map<wxString, int64_t, wxStringHash>::const_iterator it = remotePaths_.find(safePath);
344        if (it != remotePaths_.end())
345                return it->second;
346
347        Bind(insertRemotePathQuery_, path_table_column_names::path, safePath);
348
349        int res;
350        do {
351                res = sqlite3_step(insertRemotePathQuery_);
352        } while (res == SQLITE_BUSY);
353
354        sqlite3_reset(insertRemotePathQuery_);
355
356        if (res == SQLITE_DONE) {
357                int64_t id = sqlite3_last_insert_rowid(db_);
358                remotePaths_[safePath] = id;
359                return id;
360        }
361
362        return -1;
363}
364
365
366wxString CQueueStorage::Impl::CreateColumnDefs(_column* columns, size_t count)
367{
368        wxString query = _T("(");
369        for (unsigned int i = 0; i < count; ++i) {
370                if (i)
371                        query += _T(", ");
372                query += columns[i].name;
373                if (columns[i].type == Column_type::integer)
374                        query += _T(" INTEGER");
375                else
376                        query += _T(" TEXT");
377
378                if (columns[i].flags & autoincrement)
379                        query += _T(" PRIMARY KEY AUTOINCREMENT");
380                if (columns[i].flags & not_null)
381                        query += _T(" NOT NULL");
382        }
383        query += _T(")");
384
385        return query;
386}
387
388void CQueueStorage::Impl::CreateTables()
389{
390        if (!db_)
391                return;
392
393        {
394                wxString query( _T("CREATE TABLE IF NOT EXISTS servers ") );
395                query += CreateColumnDefs(server_table_columns, sizeof(server_table_columns) / sizeof(_column));
396
397                if (sqlite3_exec(db_, query.ToUTF8(), 0, 0, 0) != SQLITE_OK)
398                {
399                }
400        }
401        {
402                wxString query( _T("CREATE TABLE IF NOT EXISTS files ") );
403                query += CreateColumnDefs(file_table_columns, sizeof(file_table_columns) / sizeof(_column));
404
405                if (sqlite3_exec(db_, query.ToUTF8(), 0, 0, 0) != SQLITE_OK)
406                {
407                }
408
409                query = _T("CREATE INDEX IF NOT EXISTS server_index ON files (server)");
410                if (sqlite3_exec(db_, query.ToUTF8(), 0, 0, 0) != SQLITE_OK)
411                {
412                }
413        }
414
415        {
416                wxString query( _T("CREATE TABLE IF NOT EXISTS local_paths ") );
417                query += CreateColumnDefs(path_table_columns, sizeof(path_table_columns) / sizeof(_column));
418
419                if (sqlite3_exec(db_, query.ToUTF8(), 0, 0, 0) != SQLITE_OK)
420                {
421                }
422        }
423
424        {
425                wxString query( _T("CREATE TABLE IF NOT EXISTS remote_paths ") );
426                query += CreateColumnDefs(path_table_columns, sizeof(path_table_columns) / sizeof(_column));
427
428                if (sqlite3_exec(db_, query.ToUTF8(), 0, 0, 0) != SQLITE_OK)
429                {
430                }
431        }
432}
433
434sqlite3_stmt* CQueueStorage::Impl::PrepareInsertStatement(const wxString& name, const _column* columns, unsigned int count)
435{
436        if (!db_)
437                return 0;
438
439        wxString query = _T("INSERT INTO ") + name + _T(" (");
440        for (unsigned int i = 1; i < count; ++i)
441        {
442                if (i > 1)
443                        query += _T(", ");
444                query += columns[i].name;
445        }
446        query += _T(") VALUES (");
447        for (unsigned int i = 1; i < count; ++i)
448        {
449                if (i > 1)
450                        query += _T(",");
451                query += wxString(_T(":")) + columns[i].name;
452        }
453
454        query += _T(")");
455
456        return PrepareStatement(query);
457}
458
459
460sqlite3_stmt* CQueueStorage::Impl::PrepareStatement(const wxString& query)
461{
462        sqlite3_stmt* ret = 0;
463
464        int res;
465        do
466        {
467                res = sqlite3_prepare_v2(db_, query.ToUTF8(), -1, &ret, 0);
468        } while (res == SQLITE_BUSY);
469
470        if (res != SQLITE_OK)
471                ret = 0;
472
473        return ret;
474}
475
476
477bool CQueueStorage::Impl::PrepareStatements()
478{
479        if (!db_)
480                return false;
481
482        insertServerQuery_ = PrepareInsertStatement(_T("servers"), server_table_columns, sizeof(server_table_columns) / sizeof(_column));
483        insertFileQuery_ = PrepareInsertStatement(_T("files"), file_table_columns, sizeof(file_table_columns) / sizeof(_column));
484        insertLocalPathQuery_ = PrepareInsertStatement(_T("local_paths"), path_table_columns, sizeof(path_table_columns) / sizeof(_column));
485        insertRemotePathQuery_ = PrepareInsertStatement(_T("remote_paths"), path_table_columns, sizeof(path_table_columns) / sizeof(_column));
486        if (!insertServerQuery_ || !insertFileQuery_ || !insertLocalPathQuery_ || !insertRemotePathQuery_)
487                return false;
488
489        {
490                wxString query = _T("SELECT ");
491                for (unsigned int i = 0; i < (sizeof(server_table_columns) / sizeof(_column)); ++i)
492                {
493                        if (i > 0)
494                                query += _T(", ");
495                        query += server_table_columns[i].name;
496                }
497
498                query += _T(" FROM servers ORDER BY id ASC");
499
500                if (!(selectServersQuery_ = PrepareStatement(query)))
501                        return false;
502        }
503
504        {
505                wxString query = _T("SELECT ");
506                for (unsigned int i = 0; i < (sizeof(file_table_columns) / sizeof(_column)); ++i)
507                {
508                        if (i > 0)
509                                query += _T(", ");
510                        query += file_table_columns[i].name;
511                }
512
513                query += _T(" FROM files WHERE server=:server ORDER BY id ASC");
514
515                if (!(selectFilesQuery_ = PrepareStatement(query)))
516                        return false;
517        }
518
519        {
520                wxString query = _T("SELECT id, path FROM local_paths");
521                if (!(selectLocalPathQuery_ = PrepareStatement(query)))
522                        return false;
523        }
524
525        {
526                wxString query = _T("SELECT id, path FROM remote_paths");
527                if (!(selectRemotePathQuery_ = PrepareStatement(query)))
528                        return false;
529        }
530        return true;
531}
532
533
534bool CQueueStorage::Impl::Bind(sqlite3_stmt* statement, int index, int value)
535{
536        return sqlite3_bind_int(statement, index, value) == SQLITE_OK;
537}
538
539
540bool CQueueStorage::Impl::Bind(sqlite3_stmt* statement, int index, int64_t value)
541{
542        int res = sqlite3_bind_int64(statement, index, value);
543        return res == SQLITE_OK;
544}
545
546
547#ifndef __WXMSW__
548extern "C" {
549static void custom_free(void* v)
550{
551        char* s = static_cast<char*>(v);
552        delete [] s;
553}
554}
555#endif
556
557bool CQueueStorage::Impl::Bind(sqlite3_stmt* statement, int index, const wxString& value)
558{
559#ifdef __WXMSW__
560        return sqlite3_bind_text16(statement, index, value.wc_str(), value.length() * 2, SQLITE_TRANSIENT) == SQLITE_OK;
561#else
562        char* out = new char[value.size() * 2];
563        size_t outlen = utf16_.FromWChar(out, value.size() * 2, value.c_str(), value.size());
564        bool ret = sqlite3_bind_text16(statement, index, out, outlen, custom_free) == SQLITE_OK;
565        return ret;
566#endif
567}
568
569
570bool CQueueStorage::Impl::Bind(sqlite3_stmt* statement, int index, const char* const value)
571{
572        return sqlite3_bind_text(statement, index, value, -1, SQLITE_TRANSIENT) == SQLITE_OK;
573}
574
575
576bool CQueueStorage::Impl::BindNull(sqlite3_stmt* statement, int index)
577{
578        return sqlite3_bind_null(statement, index) == SQLITE_OK;
579}
580
581
582bool CQueueStorage::Impl::SaveServer(const CServerItem& item)
583{
584        bool kiosk_mode = COptions::Get()->GetOptionVal(OPTION_DEFAULT_KIOSKMODE) != 0;
585
586        const CServer& server = item.GetServer();
587
588        Bind(insertServerQuery_, server_table_column_names::host, server.GetHost());
589        Bind(insertServerQuery_, server_table_column_names::port, static_cast<int>(server.GetPort()));
590        Bind(insertServerQuery_, server_table_column_names::protocol, static_cast<int>(server.GetProtocol()));
591        Bind(insertServerQuery_, server_table_column_names::type, static_cast<int>(server.GetType()));
592
593        enum LogonType logonType = server.GetLogonType();
594        if (server.GetLogonType() != ANONYMOUS) {
595                Bind(insertServerQuery_, server_table_column_names::user, server.GetUser());
596
597                if (server.GetLogonType() == NORMAL || server.GetLogonType() == ACCOUNT) {
598                        if (kiosk_mode) {
599                                logonType = ASK;
600                                BindNull(insertServerQuery_, server_table_column_names::password);
601                                BindNull(insertServerQuery_, server_table_column_names::account);
602                        }
603                        else {
604                                Bind(insertServerQuery_, server_table_column_names::password, server.GetPass());
605
606                                if (server.GetLogonType() == ACCOUNT)
607                                        Bind(insertServerQuery_, server_table_column_names::account, server.GetAccount());
608                                else
609                                        BindNull(insertServerQuery_, server_table_column_names::account);
610                        }
611                }
612                else {
613                        BindNull(insertServerQuery_, server_table_column_names::password);
614                        BindNull(insertServerQuery_, server_table_column_names::account);
615                }
616        }
617        else {
618                        BindNull(insertServerQuery_, server_table_column_names::user);
619                        BindNull(insertServerQuery_, server_table_column_names::password);
620                        BindNull(insertServerQuery_, server_table_column_names::account);
621        }
622        Bind(insertServerQuery_, server_table_column_names::logontype, static_cast<int>(logonType));
623
624        Bind(insertServerQuery_, server_table_column_names::timezone_offset, server.GetTimezoneOffset());
625
626        switch (server.GetPasvMode())
627        {
628        case MODE_PASSIVE:
629                Bind(insertServerQuery_, server_table_column_names::transfer_mode, _T("passive"));
630                break;
631        case MODE_ACTIVE:
632                Bind(insertServerQuery_, server_table_column_names::transfer_mode, _T("active"));
633                break;
634        default:
635                Bind(insertServerQuery_, server_table_column_names::transfer_mode, _T("default"));
636                break;
637        }
638        Bind(insertServerQuery_, server_table_column_names::max_connections, server.MaximumMultipleConnections());
639
640        switch (server.GetEncodingType())
641        {
642        default:
643        case ENCODING_AUTO:
644                Bind(insertServerQuery_, server_table_column_names::encoding, _T("Auto"));
645                break;
646        case ENCODING_UTF8:
647                Bind(insertServerQuery_, server_table_column_names::encoding, _T("UTF-8"));
648                break;
649        case ENCODING_CUSTOM:
650                Bind(insertServerQuery_, server_table_column_names::encoding, server.GetCustomEncoding());
651                break;
652        }
653
654        if (CServer::SupportsPostLoginCommands(server.GetProtocol())) {
655                const std::vector<wxString>& postLoginCommands = server.GetPostLoginCommands();
656                if (!postLoginCommands.empty()) {
657                        wxString commands;
658                        for (std::vector<wxString>::const_iterator iter = postLoginCommands.begin(); iter != postLoginCommands.end(); ++iter) {
659                                if (!commands.empty())
660                                        commands += _T("\n");
661                                commands += *iter;
662                        }
663                        Bind(insertServerQuery_, server_table_column_names::post_login_commands, commands);
664                }
665                else
666                        BindNull(insertServerQuery_, server_table_column_names::post_login_commands);
667        }
668        else
669                BindNull(insertServerQuery_, server_table_column_names::post_login_commands);
670
671        Bind(insertServerQuery_, server_table_column_names::bypass_proxy, server.GetBypassProxy() ? 1 : 0);
672        if (!server.GetName().empty())
673                Bind(insertServerQuery_, server_table_column_names::name, server.GetName());
674        else
675                BindNull(insertServerQuery_, server_table_column_names::name);
676
677        int res;
678        do {
679                res = sqlite3_step(insertServerQuery_);
680        } while (res == SQLITE_BUSY);
681
682        sqlite3_reset(insertServerQuery_);
683
684        bool ret = res == SQLITE_DONE;
685        if (ret) {
686                sqlite3_int64 serverId = sqlite3_last_insert_rowid(db_);
687                Bind(insertFileQuery_, file_table_column_names::server, static_cast<int64_t>(serverId));
688
689                const std::vector<CQueueItem*>& children = item.GetChildren();
690                for (std::vector<CQueueItem*>::const_iterator it = children.begin() + item.GetRemovedAtFront(); it != children.end(); ++it) {
691                        CQueueItem & childItem = **it;
692                        if (childItem.GetType() == QueueItemType::File)
693                                ret &= SaveFile(static_cast<CFileItem&>(childItem));
694                        else if (childItem.GetType() == QueueItemType::Folder)
695                                ret &= SaveDirectory(static_cast<CFolderItem&>(childItem));
696                }
697        }
698        return ret;
699}
700
701
702bool CQueueStorage::Impl::SaveFile(const CFileItem& file)
703{
704        if (file.m_edit != CEditHandler::none)
705                return true;
706
707        Bind(insertFileQuery_, file_table_column_names::source_file, file.GetSourceFile());
708        auto const& targetFile = file.GetTargetFile();
709        if (targetFile)
710                Bind(insertFileQuery_, file_table_column_names::target_file, *targetFile);
711        else
712                BindNull(insertFileQuery_, file_table_column_names::target_file);
713
714        int64_t localPathId = SaveLocalPath(file.GetLocalPath());
715        int64_t remotePathId = SaveRemotePath(file.GetRemotePath());
716        if (localPathId == -1 || remotePathId == -1)
717                return false;
718
719        Bind(insertFileQuery_, file_table_column_names::local_path, localPathId);
720        Bind(insertFileQuery_, file_table_column_names::remote_path, remotePathId);
721
722        Bind(insertFileQuery_, file_table_column_names::download, file.Download() ? 1 : 0);
723        if (file.GetSize() != -1)
724                Bind(insertFileQuery_, file_table_column_names::size, file.GetSize());
725        else
726                BindNull(insertFileQuery_, file_table_column_names::size);
727        if (file.m_errorCount)
728                Bind(insertFileQuery_, file_table_column_names::error_count, file.m_errorCount);
729        else
730                BindNull(insertFileQuery_, file_table_column_names::error_count);
731        Bind(insertFileQuery_, file_table_column_names::priority, static_cast<int>(file.GetPriority()));
732        Bind(insertFileQuery_, file_table_column_names::ascii_file, file.Ascii() ? 1 : 0);
733
734        if (file.m_defaultFileExistsAction != CFileExistsNotification::unknown)
735                Bind(insertFileQuery_, file_table_column_names::default_exists_action, file.m_defaultFileExistsAction);
736        else
737                BindNull(insertFileQuery_, file_table_column_names::default_exists_action);
738
739        int res;
740        do {
741                res = sqlite3_step(insertFileQuery_);
742        } while (res == SQLITE_BUSY);
743
744        sqlite3_reset(insertFileQuery_);
745
746        return res == SQLITE_DONE;
747}
748
749
750bool CQueueStorage::Impl::SaveDirectory(const CFolderItem& directory)
751{
752        if (directory.Download())
753                BindNull(insertFileQuery_, file_table_column_names::source_file);
754        else
755                Bind(insertFileQuery_, file_table_column_names::source_file, directory.GetSourceFile());
756        BindNull(insertFileQuery_, file_table_column_names::target_file);
757
758        int64_t localPathId = directory.Download() ? SaveLocalPath(directory.GetLocalPath()) : -1;
759        int64_t remotePathId = directory.Download() ? -1 : SaveRemotePath(directory.GetRemotePath());
760        if (localPathId == -1 && remotePathId == -1)
761                return false;
762
763        Bind(insertFileQuery_, file_table_column_names::local_path, localPathId);
764        Bind(insertFileQuery_, file_table_column_names::remote_path, remotePathId);
765
766        Bind(insertFileQuery_, file_table_column_names::download, directory.Download() ? 1 : 0);
767        BindNull(insertFileQuery_, file_table_column_names::size);
768        if (directory.m_errorCount)
769                Bind(insertFileQuery_, file_table_column_names::error_count, directory.m_errorCount);
770        else
771                BindNull(insertFileQuery_, file_table_column_names::error_count);
772        Bind(insertFileQuery_, file_table_column_names::priority, static_cast<int>(directory.GetPriority()));
773        BindNull(insertFileQuery_, file_table_column_names::ascii_file);
774
775        BindNull(insertFileQuery_, file_table_column_names::default_exists_action);
776
777        int res;
778        do {
779                res = sqlite3_step(insertFileQuery_);
780        } while (res == SQLITE_BUSY);
781
782        sqlite3_reset(insertFileQuery_);
783
784        return res == SQLITE_DONE;
785}
786
787
788wxString CQueueStorage::Impl::GetColumnText(sqlite3_stmt* statement, int index, bool shrink)
789{
790        wxString ret;
791
792#ifdef __WXMSW__
793        (void)shrink;
794        const wxChar* text = static_cast<const wxChar*>(sqlite3_column_text16(statement, index));
795        if (text)
796                ret = text;
797#else
798        const char* text = static_cast<const char*>(sqlite3_column_text16(statement, index));
799        int len = sqlite3_column_bytes16(statement, index);
800        if (text)
801        {
802                wxStringBuffer buffer(ret, len);
803                wxChar* out = buffer;
804
805                int outlen = utf16_.ToWChar( out, len, text, len );
806                buffer[outlen] = 0;
807        }
808        if (shrink)
809                ret.Shrink();
810#endif
811
812        return ret;
813}
814
815int64_t CQueueStorage::Impl::GetColumnInt64(sqlite3_stmt* statement, int index, int64_t def)
816{
817        if (sqlite3_column_type(statement, index) == SQLITE_NULL)
818                return def;
819        else
820                return sqlite3_column_int64(statement, index);
821}
822
823int CQueueStorage::Impl::GetColumnInt(sqlite3_stmt* statement, int index, int def)
824{
825        if (sqlite3_column_type(statement, index) == SQLITE_NULL)
826                return def;
827        else
828                return sqlite3_column_int(statement, index);
829}
830
831int64_t CQueueStorage::Impl::ParseServerFromRow(CServer& server)
832{
833        server = CServer();
834
835        wxString host = GetColumnText(selectServersQuery_, server_table_column_names::host);
836        if (host.empty())
837                return INVALID_DATA;
838
839        int port = GetColumnInt(selectServersQuery_, server_table_column_names::port);
840        if (port < 1 || port > 65535)
841                return INVALID_DATA;
842
843        if (!server.SetHost(host, port))
844                return INVALID_DATA;
845
846        int const protocol = GetColumnInt(selectServersQuery_, server_table_column_names::protocol);
847        if (protocol < 0 || protocol > MAX_VALUE)
848                return INVALID_DATA;
849        server.SetProtocol(static_cast<ServerProtocol>(protocol));
850
851        int type = GetColumnInt(selectServersQuery_, server_table_column_names::type);
852        if (type < 0 || type >= SERVERTYPE_MAX)
853                return INVALID_DATA;
854
855        server.SetType((enum ServerType)type);
856
857        int logonType = GetColumnInt(selectServersQuery_, server_table_column_names::logontype);
858        if (logonType < 0 || logonType >= LOGONTYPE_MAX)
859                return INVALID_DATA;
860
861        server.SetLogonType((enum LogonType)logonType);
862
863        if (server.GetLogonType() != ANONYMOUS)
864        {
865                wxString user = GetColumnText(selectServersQuery_, server_table_column_names::user);
866
867                wxString pass;
868                if ((long)NORMAL == logonType || (long)ACCOUNT == logonType)
869                        pass = GetColumnText(selectServersQuery_, server_table_column_names::password);
870
871                if (!server.SetUser(user, pass))
872                        return INVALID_DATA;
873
874                if ((long)ACCOUNT == logonType)
875                {
876                        wxString account = GetColumnText(selectServersQuery_, server_table_column_names::account);
877                        if (account.empty())
878                                return INVALID_DATA;
879                        if (!server.SetAccount(account))
880                                return INVALID_DATA;
881                }
882        }
883
884        int timezoneOffset = GetColumnInt(selectServersQuery_, server_table_column_names::timezone_offset);
885        if (!server.SetTimezoneOffset(timezoneOffset))
886                return INVALID_DATA;
887
888        wxString pasvMode = GetColumnText(selectServersQuery_, server_table_column_names::transfer_mode);
889        if (pasvMode == _T("passive"))
890                server.SetPasvMode(MODE_PASSIVE);
891        else if (pasvMode == _T("active"))
892                server.SetPasvMode(MODE_ACTIVE);
893        else
894                server.SetPasvMode(MODE_DEFAULT);
895
896        int maximumMultipleConnections = GetColumnInt(selectServersQuery_, server_table_column_names::max_connections);
897        if (maximumMultipleConnections < 0)
898                return INVALID_DATA;
899        server.MaximumMultipleConnections(maximumMultipleConnections);
900
901        wxString encodingType = GetColumnText(selectServersQuery_, server_table_column_names::encoding);
902        if (encodingType.empty() || encodingType == _T("Auto"))
903                server.SetEncodingType(ENCODING_AUTO);
904        else if (encodingType == _T("UTF-8"))
905                server.SetEncodingType(ENCODING_UTF8);
906        else
907        {
908                if (!server.SetEncodingType(ENCODING_CUSTOM, encodingType))
909                        return INVALID_DATA;
910        }
911
912        if (CServer::SupportsPostLoginCommands(server.GetProtocol())) {
913                std::vector<wxString> postLoginCommands;
914
915                wxString commands = GetColumnText(selectServersQuery_, server_table_column_names::post_login_commands);
916                while (!commands.empty())
917                {
918                        int pos = commands.Find('\n');
919                        if (!pos)
920                                commands = commands.Mid(1);
921                        else if (pos == -1)
922                        {
923                                postLoginCommands.push_back(commands);
924                                commands.clear();
925                        }
926                        else
927                        {
928                                postLoginCommands.push_back(commands.Left(pos));
929                                commands = commands.Mid(pos + 1);
930                        }
931                }
932                if (!server.SetPostLoginCommands(postLoginCommands))
933                        return INVALID_DATA;
934        }
935
936
937        server.SetBypassProxy(GetColumnInt(selectServersQuery_, server_table_column_names::bypass_proxy) == 1 );
938        server.SetName( GetColumnText(selectServersQuery_, server_table_column_names::name) );
939
940        return GetColumnInt64(selectServersQuery_, server_table_column_names::id);
941}
942
943
944int64_t CQueueStorage::Impl::ParseFileFromRow(CFileItem** pItem)
945{
946        wxString sourceFile = GetColumnText(selectFilesQuery_, file_table_column_names::source_file);
947        wxString targetFile = GetColumnText(selectFilesQuery_, file_table_column_names::target_file);
948
949        int64_t localPathId = GetColumnInt64(selectFilesQuery_, file_table_column_names::local_path, false);
950        int64_t remotePathId = GetColumnInt64(selectFilesQuery_, file_table_column_names::remote_path, false);
951
952        const CLocalPath& localPath(GetLocalPath(localPathId));
953        const CServerPath& remotePath(GetRemotePath(remotePathId));
954
955        bool download = GetColumnInt(selectFilesQuery_, file_table_column_names::download) != 0;
956
957        if (localPathId == -1 || remotePathId == -1)
958        {
959                // QueueItemType::Folder
960                if ((download && localPath.empty()) ||
961                        (!download && remotePath.empty()))
962                {
963                        return INVALID_DATA;
964                }
965
966                if (download)
967                        *pItem = new CFolderItem(0, true, localPath);
968                else
969                        *pItem = new CFolderItem(0, true, remotePath, sourceFile);
970        }
971        else
972        {
973                int64_t size = GetColumnInt64(selectFilesQuery_, file_table_column_names::size);
974                unsigned char errorCount = static_cast<unsigned char>(GetColumnInt(selectFilesQuery_, file_table_column_names::error_count));
975                int priority = GetColumnInt(selectFilesQuery_, file_table_column_names::priority, static_cast<int>(QueuePriority::normal));
976
977                bool ascii = GetColumnInt(selectFilesQuery_, file_table_column_names::ascii_file) != 0;
978                int overwrite_action = GetColumnInt(selectFilesQuery_, file_table_column_names::default_exists_action, CFileExistsNotification::unknown);
979
980                if (sourceFile.empty() || localPath.empty() ||
981                        remotePath.empty() ||
982                        size < -1 ||
983                        priority < 0 || priority >= static_cast<int>(QueuePriority::count))
984                {
985                        return INVALID_DATA;
986                }
987
988                CFileItem* fileItem = new CFileItem(0, true, download, sourceFile, targetFile, localPath, remotePath, size);
989                *pItem = fileItem;
990                fileItem->SetAscii(ascii);
991                fileItem->SetPriorityRaw(QueuePriority(priority));
992                fileItem->m_errorCount = errorCount;
993
994                if (overwrite_action > 0 && overwrite_action < CFileExistsNotification::ACTION_COUNT)
995                        fileItem->m_defaultFileExistsAction = (CFileExistsNotification::OverwriteAction)overwrite_action;
996        }
997
998        return GetColumnInt64(selectFilesQuery_, file_table_column_names::id);
999}
1000
1001CQueueStorage::CQueueStorage()
1002: d_(new Impl)
1003{
1004        int ret = sqlite3_open(GetDatabaseFilename().ToUTF8(), &d_->db_ );
1005        if (ret != SQLITE_OK)
1006                d_->db_ = 0;
1007
1008        if (sqlite3_exec(d_->db_, "PRAGMA encoding=\"UTF-16le\"", 0, 0, 0) == SQLITE_OK)
1009        {
1010                d_->MigrateSchema();
1011                d_->CreateTables();
1012                d_->PrepareStatements();
1013        }
1014}
1015
1016CQueueStorage::~CQueueStorage()
1017{
1018        sqlite3_finalize(d_->insertServerQuery_);
1019        sqlite3_finalize(d_->insertFileQuery_);
1020        sqlite3_finalize(d_->insertLocalPathQuery_);
1021        sqlite3_finalize(d_->insertRemotePathQuery_);
1022        sqlite3_finalize(d_->selectServersQuery_);
1023        sqlite3_finalize(d_->selectFilesQuery_);
1024        sqlite3_finalize(d_->selectLocalPathQuery_);
1025        sqlite3_finalize(d_->selectRemotePathQuery_);
1026        sqlite3_close(d_->db_);
1027        delete d_;
1028}
1029
1030bool CQueueStorage::SaveQueue(std::vector<CServerItem*> const& queue)
1031{
1032        d_->ClearCaches();
1033
1034        bool ret = true;
1035        if (sqlite3_exec(d_->db_, "BEGIN TRANSACTION", 0, 0, 0) == SQLITE_OK) {
1036                for (std::vector<CServerItem*>::const_iterator it = queue.begin(); it != queue.end(); ++it)
1037                        ret &= d_->SaveServer(**it);
1038
1039                // Even on previous failure, we want to at least try to commit the data we have so far
1040                ret &= sqlite3_exec(d_->db_, "END TRANSACTION", 0, 0, 0) == SQLITE_OK;
1041
1042                d_->ClearCaches();
1043        }
1044        else
1045                ret = false;
1046
1047        return ret;
1048}
1049
1050int64_t CQueueStorage::GetServer(CServer& server, bool fromBeginning)
1051{
1052        int64_t ret = -1;
1053
1054        if (d_->selectServersQuery_)
1055        {
1056                if (fromBeginning)
1057                {
1058                        d_->ReadLocalPaths();
1059                        d_->ReadRemotePaths();
1060                        sqlite3_reset(d_->selectServersQuery_);
1061                }
1062
1063                for (;;)
1064                {
1065                        int res;
1066                        do
1067                        {
1068                                res = sqlite3_step(d_->selectServersQuery_);
1069                        }
1070                        while (res == SQLITE_BUSY);
1071
1072                        if (res == SQLITE_ROW)
1073                        {
1074                                ret = d_->ParseServerFromRow(server);
1075                                if (ret > 0)
1076                                        break;
1077                        }
1078                        else if (res == SQLITE_DONE)
1079                        {
1080                                ret = 0;
1081                                sqlite3_reset(d_->selectServersQuery_);
1082                                break;
1083                        }
1084                        else
1085                        {
1086                                ret = -1;
1087                                sqlite3_reset(d_->selectServersQuery_);
1088                                break;
1089                        }
1090                }
1091        }
1092        else {
1093                ret = -1;
1094        }
1095
1096        return ret;
1097}
1098
1099
1100int64_t CQueueStorage::GetFile(CFileItem** pItem, int64_t server)
1101{
1102        int64_t ret = -1;
1103        *pItem = 0;
1104
1105        if (d_->selectFilesQuery_)
1106        {
1107                if (server > 0)
1108                {
1109                        sqlite3_reset(d_->selectFilesQuery_);
1110                        sqlite3_bind_int64(d_->selectFilesQuery_, 1, server);
1111                }
1112
1113                for (;;)
1114                {
1115                        int res;
1116                        do
1117                        {
1118                                res = sqlite3_step(d_->selectFilesQuery_);
1119                        }
1120                        while (res == SQLITE_BUSY);
1121
1122                        if (res == SQLITE_ROW)
1123                        {
1124                                ret = d_->ParseFileFromRow(pItem);
1125                                if (ret > 0)
1126                                        break;
1127                        }
1128                        else if (res == SQLITE_DONE)
1129                        {
1130                                ret = 0;
1131                                sqlite3_reset(d_->selectFilesQuery_);
1132                                break;
1133                        }
1134                        else
1135                        {
1136                                ret = -1;
1137                                sqlite3_reset(d_->selectFilesQuery_);
1138                                break;
1139                        }
1140                }
1141        }
1142        else {
1143                ret = -1;
1144        }
1145
1146        return ret;
1147}
1148
1149bool CQueueStorage::Clear()
1150{
1151        if (!d_->db_)
1152                return false;
1153
1154        if (sqlite3_exec(d_->db_, "DELETE FROM files", 0, 0, 0) != SQLITE_OK)
1155                return false;
1156
1157        if (sqlite3_exec(d_->db_, "DELETE FROM servers", 0, 0, 0) != SQLITE_OK)
1158                return false;
1159
1160        if (sqlite3_exec(d_->db_, "DELETE FROM local_paths", 0, 0, 0) != SQLITE_OK)
1161                return false;
1162
1163        if (sqlite3_exec(d_->db_, "DELETE FROM remote_paths", 0, 0, 0) != SQLITE_OK)
1164                return false;
1165
1166        d_->ClearCaches();
1167
1168        return true;
1169}
1170
1171wxString CQueueStorage::GetDatabaseFilename()
1172{
1173        wxFileName file(COptions::Get()->GetOption(OPTION_DEFAULT_SETTINGSDIR), _T("queue.sqlite3"));
1174
1175        return file.GetFullPath();
1176}
1177
1178bool CQueueStorage::BeginTransaction()
1179{
1180        return sqlite3_exec(d_->db_, "BEGIN TRANSACTION", 0, 0, 0) == SQLITE_OK;
1181}
1182
1183bool CQueueStorage::EndTransaction()
1184{
1185        return sqlite3_exec(d_->db_, "END TRANSACTION", 0, 0, 0) == SQLITE_OK;
1186}
1187
1188bool CQueueStorage::Vacuum()
1189{
1190        return sqlite3_exec(d_->db_, "VACUUM", 0, 0, 0) == SQLITE_OK;
1191}
Note: See TracBrowser for help on using the repository browser.