source: box/trunk/bin/bbackupquery/BackupQueries.cpp @ 3103

Revision 3103, 56.3 KB checked in by chris, 4 weeks ago (diff)

Change BackupQueries? List() to use C++ streams for output.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    BackupQueries.cpp
5//              Purpose: Perform various queries on the backup store server.
6//              Created: 2003/10/10
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#ifdef HAVE_UNISTD_H
13        #include <unistd.h>
14#endif
15
16#include <stdio.h>
17#include <errno.h>
18#include <stdlib.h>
19#include <limits.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22
23#ifdef HAVE_DIRENT_H
24        #include <dirent.h>
25#endif
26
27#include <cstring>
28#include <limits>
29#include <iostream>
30#include <ostream>
31#include <set>
32
33#include "BackupClientFileAttributes.h"
34#include "BackupClientMakeExcludeList.h"
35#include "BackupClientRestore.h"
36#include "BackupQueries.h"
37#include "BackupStoreDirectory.h"
38#include "BackupStoreException.h"
39#include "BackupStoreFile.h"
40#include "BackupStoreFilenameClear.h"
41#include "BoxTimeToText.h"
42#include "CommonException.h"
43#include "Configuration.h"
44#include "ExcludeList.h"
45#include "FileModificationTime.h"
46#include "FileStream.h"
47#include "IOStream.h"
48#include "Logging.h"
49#include "PathUtils.h"
50#include "SelfFlushingStream.h"
51#include "Utils.h"
52#include "autogen_BackupProtocol.h"
53#include "autogen_CipherException.h"
54
55#include "MemLeakFindOn.h"
56
57// min() and max() macros from stdlib.h break numeric_limits<>::min(), etc.
58#undef min
59#undef max
60
61#define COMPARE_RETURN_SAME             1
62#define COMPARE_RETURN_DIFFERENT        2
63#define COMPARE_RETURN_ERROR            3
64#define COMMAND_RETURN_ERROR            4
65
66// --------------------------------------------------------------------------
67//
68// Function
69//              Name:    BackupQueries::BackupQueries()
70//              Purpose: Constructor
71//              Created: 2003/10/10
72//
73// --------------------------------------------------------------------------
74BackupQueries::BackupQueries(BackupProtocolClient &rConnection,
75        const Configuration &rConfiguration, bool readWrite)
76        : mReadWrite(readWrite),
77          mrConnection(rConnection),
78          mrConfiguration(rConfiguration),
79          mQuitNow(false),
80          mRunningAsRoot(false),
81          mWarnedAboutOwnerAttributes(false),
82          mReturnCode(0)                // default return code
83{
84        #ifdef WIN32
85        mRunningAsRoot = TRUE;
86        #else
87        mRunningAsRoot = (::geteuid() == 0);
88        #endif
89}
90
91// --------------------------------------------------------------------------
92//
93// Function
94//              Name:    BackupQueries::~BackupQueries()
95//              Purpose: Destructor
96//              Created: 2003/10/10
97//
98// --------------------------------------------------------------------------
99BackupQueries::~BackupQueries()
100{
101}
102
103// --------------------------------------------------------------------------
104//
105// Function
106//              Name:    BackupQueries::DoCommand(const char *, bool)
107//              Purpose: Perform a command
108//              Created: 2003/10/10
109//
110// --------------------------------------------------------------------------
111void BackupQueries::DoCommand(ParsedCommand& rCommand)
112{
113        // Check...
114
115        if(rCommand.mFailed)
116        {
117                BOX_ERROR("Parse failed");
118                return;
119        }
120
121        if(rCommand.mCmdElements.size() < 1)
122        {
123                // blank command
124                return;
125        }
126
127        if(rCommand.pSpec->type == Command_sh &&
128                rCommand.mCmdElements.size() == 2)
129        {
130                // Yes, run shell command
131                int result = ::system(rCommand.mCmdElements[1].c_str());
132                if(result != 0)
133                {
134                        BOX_WARNING("System command returned error code " <<
135                                result);
136                        SetReturnCode(ReturnCode::Command_Error);
137                }
138                return;
139        }
140               
141        if(rCommand.pSpec->type == Command_Unknown)
142        {
143                // No such command
144                BOX_ERROR("Unrecognised command: " << rCommand.mCmdElements[0]);
145                return;
146        }
147
148        // Arguments
149        std::vector<std::string> args(rCommand.mCmdElements.begin() + 1,
150                rCommand.mCmdElements.end());
151
152        // Set up options
153        bool opts[256];
154        for(int o = 0; o < 256; ++o) opts[o] = false;
155        // BLOCK
156        {
157                // options
158                const char *c = rCommand.mOptions.c_str();
159                while(*c != 0)
160                {
161                        // Valid option?
162                        if(::strchr(rCommand.pSpec->opts, *c) == NULL)
163                        {
164                                BOX_ERROR("Invalid option '" << *c << "' for "
165                                        "command " << rCommand.pSpec->name);
166                                return;
167                        }
168                        opts[(int)*c] = true;
169                        ++c;
170                }
171        }
172
173        if(rCommand.pSpec->type != Command_Quit)
174        {
175                // If not a quit command, set the return code to zero
176                SetReturnCode(ReturnCode::Command_OK);
177        }
178
179        // Handle command
180        switch(rCommand.pSpec->type)
181        {
182        case Command_Quit:
183                mQuitNow = true;
184                break;
185               
186        case Command_List:
187                CommandList(args, opts);
188                break;
189               
190        case Command_pwd:
191                {
192                        // Simple implementation, so do it here
193                        BOX_NOTICE(GetCurrentDirectoryName() << " (" <<
194                                BOX_FORMAT_OBJECTID(GetCurrentDirectoryID()) <<
195                                ")");
196                }
197                break;
198
199        case Command_cd:
200                CommandChangeDir(args, opts);
201                break;
202               
203        case Command_lcd:
204                CommandChangeLocalDir(args);
205                break;
206               
207        case Command_sh:
208                BOX_ERROR("The command to run must be specified as an argument.");
209                break;
210               
211        case Command_GetObject:
212                CommandGetObject(args, opts);
213                break;
214               
215        case Command_Get:
216                CommandGet(args, opts);
217                break;
218               
219        case Command_Compare:
220                CommandCompare(args, opts);
221                break;
222               
223        case Command_Restore:
224                CommandRestore(args, opts);
225                break;
226               
227        case Command_Usage:
228                CommandUsage(opts);
229                break;
230               
231        case Command_Help:
232                CommandHelp(args);
233                break;
234
235        case Command_Undelete:
236                CommandUndelete(args, opts);
237                break;
238               
239        case Command_Delete:
240                CommandDelete(args, opts);
241                break;
242               
243        default:
244                BOX_ERROR("Unknown command: " << rCommand.mCmdElements[0]);
245                break;
246        }
247}
248
249
250// --------------------------------------------------------------------------
251//
252// Function
253//              Name:    BackupQueries::CommandList(const std::vector<std::string> &, const bool *)
254//              Purpose: List directories (optionally recursive)
255//              Created: 2003/10/10
256//
257// --------------------------------------------------------------------------
258void BackupQueries::CommandList(const std::vector<std::string> &args, const bool *opts)
259{
260        #define LIST_OPTION_RECURSIVE           'r'
261        #define LIST_OPTION_NOOBJECTID          'I'
262        #define LIST_OPTION_NOFLAGS             'F'
263        #define LIST_OPTION_TIMES_LOCAL         't'
264        #define LIST_OPTION_TIMES_UTC           'T'
265        #define LIST_OPTION_TIMES_ATTRIBS       'a'
266        #define LIST_OPTION_SIZEINBLOCKS        's'
267        #define LIST_OPTION_DISPLAY_HASH        'h'
268
269        // default to using the current directory
270        int64_t rootDir = GetCurrentDirectoryID();
271
272        // name of base directory
273        std::string listRoot;   // blank
274
275        // Got a directory in the arguments?
276        if(args.size() > 0)
277        {
278#ifdef WIN32
279                std::string storeDirEncoded;
280                if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded))
281                        return;
282#else
283                const std::string& storeDirEncoded(args[0]);
284#endif
285       
286                // Attempt to find the directory
287                rootDir = FindDirectoryObjectID(storeDirEncoded, 
288                        opts[LIST_OPTION_ALLOWOLD], 
289                        opts[LIST_OPTION_ALLOWDELETED]);
290
291                if(rootDir == 0)
292                {
293                        BOX_ERROR("Directory '" << args[0] << "' not found "
294                                "on store.");
295                        SetReturnCode(ReturnCode::Command_Error);
296                        return;
297                }
298        }
299       
300        // List it
301        List(rootDir, listRoot, opts, true /* first level to list */);
302}
303
304static std::string GetTimeString(BackupStoreDirectory::Entry& en,
305        bool useLocalTime, bool showAttrModificationTimes)
306{
307        std::ostringstream out;
308        box_time_t originalTime, newAttributesTime;
309
310        // there is no attribute modification time in the directory
311        // entry, unfortunately, so we can't display it.
312        originalTime = en.GetModificationTime();
313        out << BoxTimeToISO8601String(originalTime, useLocalTime);
314
315        if(en.HasAttributes())
316        {
317                const StreamableMemBlock &storeAttr(en.GetAttributes());
318                BackupClientFileAttributes attr(storeAttr);
319               
320                box_time_t NewModificationTime, NewAttrModificationTime;
321                attr.GetModificationTimes(&NewModificationTime,
322                        &NewAttrModificationTime);
323               
324                if (showAttrModificationTimes)
325                {
326                        newAttributesTime = NewAttrModificationTime;
327                }
328                else
329                {
330                        newAttributesTime = NewModificationTime;
331                }
332               
333                if (newAttributesTime == originalTime)
334                {
335                        out << "*";
336                }
337                else
338                {
339                        out << "~" << BoxTimeToISO8601String(newAttributesTime,
340                                useLocalTime);
341                }
342        }
343        else
344        {
345                out << " ";
346        }
347       
348        return out.str();
349}
350
351// --------------------------------------------------------------------------
352//
353// Function
354//              Name:    BackupQueries::List(int64_t, const std::string &, const bool *, bool)
355//              Purpose: Do the actual listing of directories and files
356//              Created: 2003/10/10
357//
358// --------------------------------------------------------------------------
359void BackupQueries::List(int64_t DirID, const std::string &rListRoot,
360        const bool *opts, bool FirstLevel, std::ostream &out)
361{
362        // Generate exclude flags
363        int16_t excludeFlags = BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING;
364        if(!opts[LIST_OPTION_ALLOWOLD]) excludeFlags |= BackupProtocolListDirectory::Flags_OldVersion;
365        if(!opts[LIST_OPTION_ALLOWDELETED]) excludeFlags |= BackupProtocolListDirectory::Flags_Deleted;
366
367        // Do communication
368        try
369        {
370                mrConnection.QueryListDirectory(
371                        DirID,
372                        BackupProtocolListDirectory::Flags_INCLUDE_EVERYTHING,
373                        // both files and directories
374                        excludeFlags,
375                        true /* want attributes */);
376        }
377        catch (std::exception &e)
378        {
379                BOX_ERROR("Failed to list directory: " << e.what());
380                SetReturnCode(ReturnCode::Command_Error);
381                return;
382        }
383        catch (...)
384        {
385                BOX_ERROR("Failed to list directory: unknown error");
386                SetReturnCode(ReturnCode::Command_Error);
387                return;
388        }
389
390        // Retrieve the directory from the stream following
391        BackupStoreDirectory dir;
392        std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
393        dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
394
395        // Then... display everything
396        BackupStoreDirectory::Iterator i(dir);
397        BackupStoreDirectory::Entry *en = 0;
398        while((en = i.Next()) != 0)
399        {
400                // Display this entry
401                BackupStoreFilenameClear clear(en->GetName());
402               
403                // Object ID?
404                if(!opts[LIST_OPTION_NOOBJECTID])
405                {
406                        // add object ID to line
407                        out << std::hex << std::internal << std::setw(8) <<
408                                std::setfill('0') << en->GetObjectID() <<
409                                std::dec << " ";
410                }
411               
412                // Flags?
413                if(!opts[LIST_OPTION_NOFLAGS])
414                {
415                        static const char *flags = BACKUPSTOREDIRECTORY_ENTRY_FLAGS_DISPLAY_NAMES;
416                        char displayflags[16];
417                        // make sure f is big enough
418                        ASSERT(sizeof(displayflags) >= sizeof(BACKUPSTOREDIRECTORY_ENTRY_FLAGS_DISPLAY_NAMES) + 3);
419                        // Insert flags
420                        char *f = displayflags;
421                        const char *t = flags;
422                        int16_t en_flags = en->GetFlags();
423                        while(*t != 0)
424                        {
425                                *f = ((en_flags&1) == 0)?'-':*t;
426                                en_flags >>= 1;
427                                f++;
428                                t++;
429                        }
430                        // attributes flags
431                        *(f++) = (en->HasAttributes())?'a':'-';
432
433                        // terminate
434                        *(f++) = ' ';
435                        *(f++) = '\0';
436                        out << displayflags;
437                       
438                        if(en_flags != 0)
439                        {
440                                out << "[ERROR: Entry has additional flags set] ";
441                        }
442                }
443               
444                if(opts[LIST_OPTION_TIMES_UTC])
445                {
446                        // Show UTC times...
447                        out << GetTimeString(*en, false,
448                                opts[LIST_OPTION_TIMES_ATTRIBS]) << " ";
449                }
450
451                if(opts[LIST_OPTION_TIMES_LOCAL])
452                {
453                        // Show local times...
454                        out << GetTimeString(*en, true,
455                                opts[LIST_OPTION_TIMES_ATTRIBS]) << " ";
456                }
457               
458                if(opts[LIST_OPTION_DISPLAY_HASH])
459                {
460                        out << std::hex << std::internal << std::setw(16) <<
461                                std::setfill('0') << en->GetAttributesHash() <<
462                                std::dec;
463                }
464               
465                if(opts[LIST_OPTION_SIZEINBLOCKS])
466                {
467                        out << std::internal << std::setw(5) <<
468                                std::setfill('0') << en->GetSizeInBlocks() <<
469                                " ";
470                }
471               
472                // add name
473                if(!FirstLevel)
474                {
475#ifdef WIN32
476                        std::string listRootDecoded;
477                        if(!ConvertUtf8ToConsole(rListRoot.c_str(), 
478                                listRootDecoded)) return;
479                        out << listRootDecoded << "/";
480#else
481                        out << rListRoot << "/";
482#endif
483                }
484               
485                std::string fileName;
486                try
487                {
488                        fileName = clear.GetClearFilename();
489                }
490                catch(CipherException &e)
491                {
492                        fileName = "<decrypt failed>";
493                }
494
495#ifdef WIN32
496                std::string fileNameUtf8 = fileName;
497                if(!ConvertUtf8ToConsole(fileNameUtf8, fileName))
498                {
499                        fileName = fileNameUtf8 + " [convert encoding failed]";
500                }
501#endif
502
503                out << fileName;
504               
505                if(!en->GetName().IsEncrypted())
506                {
507                        out << " [FILENAME NOT ENCRYPTED]";
508                }
509
510                out << std::endl;
511               
512                // Directory?
513                if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) != 0)
514                {
515                        // Recurse?
516                        if(opts[LIST_OPTION_RECURSIVE])
517                        {
518                                std::string subroot(rListRoot);
519                                if(!FirstLevel) subroot += '/';
520                                subroot += clear.GetClearFilename();
521                                List(en->GetObjectID(), subroot, opts,
522                                        false /* not the first level to list */,
523                                        out);
524                        }
525                }
526        }
527}
528
529
530// --------------------------------------------------------------------------
531//
532// Function
533//              Name:    BackupQueries::FindDirectoryObjectID(const
534//                       std::string &)
535//              Purpose: Find the object ID of a directory on the store,
536//                       or return 0 for not found. If pStack != 0, the
537//                       object is set to the stack of directories.
538//                       Will start from the current directory stack.
539//              Created: 2003/10/10
540//
541// --------------------------------------------------------------------------
542int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName,
543        bool AllowOldVersion, bool AllowDeletedDirs,
544        std::vector<std::pair<std::string, int64_t> > *pStack)
545{
546        // Split up string into elements
547        std::vector<std::string> dirElements;
548        SplitString(rDirName, '/', dirElements);
549
550        // Start from current stack, or root, whichever is required
551        std::vector<std::pair<std::string, int64_t> > stack;
552        int64_t dirID = BackupProtocolListDirectory::RootDirectory;
553        if(rDirName.size() > 0 && rDirName[0] == '/')
554        {
555                // Root, do nothing
556        }
557        else
558        {
559                // Copy existing stack
560                stack = mDirStack;
561                if(stack.size() > 0)
562                {
563                        dirID = stack[stack.size() - 1].second;
564                }
565        }
566
567        // Generate exclude flags
568        int16_t excludeFlags = BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING;
569        if(!AllowOldVersion) excludeFlags |= BackupProtocolListDirectory::Flags_OldVersion;
570        if(!AllowDeletedDirs) excludeFlags |= BackupProtocolListDirectory::Flags_Deleted;
571
572        // Read directories
573        for(unsigned int e = 0; e < dirElements.size(); ++e)
574        {
575                if(dirElements[e].size() > 0)
576                {
577                        if(dirElements[e] == ".")
578                        {
579                                // Ignore.
580                        }
581                        else if(dirElements[e] == "..")
582                        {
583                                // Up one!
584                                if(stack.size() > 0)
585                                {
586                                        // Remove top element
587                                        stack.pop_back();
588                                       
589                                        // New dir ID
590                                        dirID = (stack.size() > 0)?(stack[stack.size() - 1].second):BackupProtocolListDirectory::RootDirectory;
591                                }
592                                else
593                                {       
594                                        // At root anyway
595                                        dirID = BackupProtocolListDirectory::RootDirectory;
596                                }
597                        }
598                        else
599                        {
600                                // Not blank element. Read current directory.
601                                std::auto_ptr<BackupProtocolSuccess> dirreply(mrConnection.QueryListDirectory(
602                                                dirID,
603                                                BackupProtocolListDirectory::Flags_Dir, // just directories
604                                                excludeFlags,
605                                                true /* want attributes */));
606
607                                // Retrieve the directory from the stream following
608                                BackupStoreDirectory dir;
609                                std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
610                                dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
611
612                                // Then... find the directory within it
613                                BackupStoreDirectory::Iterator i(dir);
614                                BackupStoreFilenameClear dirname(dirElements[e]);
615                                BackupStoreDirectory::Entry *en = i.FindMatchingClearName(dirname);
616                                if(en == 0)
617                                {
618                                        // Not found
619                                        return 0;
620                                }
621                               
622                                // Object ID for next round of searching
623                                dirID = en->GetObjectID();
624
625                                // Push onto stack
626                                stack.push_back(std::pair<std::string, int64_t>(dirElements[e], dirID));
627                        }
628                }
629        }
630       
631        // If required, copy the new stack to the caller
632        if(pStack)
633        {
634                *pStack = stack;
635        }
636
637        return dirID;
638}
639
640
641// --------------------------------------------------------------------------
642//
643// Function
644//              Name:    BackupQueries::GetCurrentDirectoryID()
645//              Purpose: Returns the ID of the current directory
646//              Created: 2003/10/10
647//
648// --------------------------------------------------------------------------
649int64_t BackupQueries::GetCurrentDirectoryID()
650{
651        // Special case for root
652        if(mDirStack.size() == 0)
653        {
654                return BackupProtocolListDirectory::RootDirectory;
655        }
656       
657        // Otherwise, get from the last entry on the stack
658        return mDirStack[mDirStack.size() - 1].second;
659}
660
661// --------------------------------------------------------------------------
662//
663// Function
664//              Name:    BackupQueries::GetCurrentDirectoryName()
665//              Purpose: Gets the name of the current directory
666//              Created: 2003/10/10
667//
668// --------------------------------------------------------------------------
669std::string BackupQueries::GetCurrentDirectoryName()
670{
671        // Special case for root
672        if(mDirStack.size() == 0)
673        {
674                return std::string("/");
675        }
676
677        // Build path
678        std::string r;
679        for(unsigned int l = 0; l < mDirStack.size(); ++l)
680        {
681                r += "/";
682#ifdef WIN32
683                std::string dirName;
684                if(!ConvertUtf8ToConsole(mDirStack[l].first.c_str(), dirName))
685                        return "error";
686                r += dirName;
687#else
688                r += mDirStack[l].first;
689#endif
690        }
691       
692        return r;
693}
694
695
696// --------------------------------------------------------------------------
697//
698// Function
699//              Name:    BackupQueries::CommandChangeDir(const std::vector<std::string> &)
700//              Purpose: Change directory command
701//              Created: 2003/10/10
702//
703// --------------------------------------------------------------------------
704void BackupQueries::CommandChangeDir(const std::vector<std::string> &args, const bool *opts)
705{
706        if(args.size() != 1 || args[0].size() == 0)
707        {
708                BOX_ERROR("Incorrect usage. cd [-o] [-d] <directory>");
709                SetReturnCode(ReturnCode::Command_Error);
710                return;
711        }
712
713#ifdef WIN32
714        std::string dirName;
715        if(!ConvertConsoleToUtf8(args[0].c_str(), dirName)) return;
716#else
717        const std::string& dirName(args[0]);
718#endif
719       
720        std::vector<std::pair<std::string, int64_t> > newStack;
721        int64_t id = FindDirectoryObjectID(dirName, opts['o'], opts['d'], 
722                &newStack);
723       
724        if(id == 0)
725        {
726                BOX_ERROR("Directory '" << args[0] << "' not found.");
727                SetReturnCode(ReturnCode::Command_Error);
728                return;
729        }
730       
731        // Store new stack
732        mDirStack = newStack;
733}
734
735
736// --------------------------------------------------------------------------
737//
738// Function
739//              Name:    BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &)
740//              Purpose: Change local directory command
741//              Created: 2003/10/11
742//
743// --------------------------------------------------------------------------
744void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
745{
746        if(args.size() != 1 || args[0].size() == 0)
747        {
748                BOX_ERROR("Incorrect usage. lcd <local-directory>");
749                SetReturnCode(ReturnCode::Command_Error);
750                return;
751        }
752       
753        // Try changing directory
754#ifdef WIN32
755        std::string dirName;
756        if(!ConvertConsoleToUtf8(args[0].c_str(), dirName))
757        {
758                BOX_ERROR("Failed to convert path from console encoding.");
759                SetReturnCode(ReturnCode::Command_Error);
760                return;
761        }
762        int result = ::chdir(dirName.c_str());
763#else
764        int result = ::chdir(args[0].c_str());
765#endif
766        if(result != 0)
767        {
768                if(errno == ENOENT || errno == ENOTDIR)
769                {
770                        BOX_ERROR("Directory '" << args[0] << "' does not exist.");
771                }
772                else
773                {
774                        BOX_LOG_SYS_ERROR("Failed to change to directory "
775                                "'" << args[0] << "'");
776                }
777
778                SetReturnCode(ReturnCode::Command_Error);
779                return;
780        }
781       
782        // Report current dir
783        char wd[PATH_MAX];
784        if(::getcwd(wd, PATH_MAX) == 0)
785        {
786                BOX_LOG_SYS_ERROR("Error getting current directory");
787                SetReturnCode(ReturnCode::Command_Error);
788                return;
789        }
790
791#ifdef WIN32
792        if(!ConvertUtf8ToConsole(wd, dirName))
793        {
794                BOX_ERROR("Failed to convert new path from console encoding.");
795                SetReturnCode(ReturnCode::Command_Error);
796                return;
797        }
798        BOX_INFO("Local current directory is now '" << dirName << "'.");
799#else
800        BOX_INFO("Local current directory is now '" << wd << "'.");
801#endif
802}
803
804
805// --------------------------------------------------------------------------
806//
807// Function
808//              Name:    BackupQueries::CommandGetObject(const std::vector<std::string> &, const bool *)
809//              Purpose: Gets an object without any translation.
810//              Created: 2003/10/11
811//
812// --------------------------------------------------------------------------
813void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const bool *opts)
814{
815        // Check args
816        if(args.size() != 2)
817        {
818                BOX_ERROR("Incorrect usage. getobject <object-id> "
819                        "<local-filename>");
820                return;
821        }
822       
823        int64_t id = ::strtoll(args[0].c_str(), 0, 16);
824        if(id == std::numeric_limits<long long>::min() || id == std::numeric_limits<long long>::max() || id == 0)
825        {
826                BOX_ERROR("Not a valid object ID (specified in hex).");
827                return;
828        }
829       
830        // Does file exist?
831        EMU_STRUCT_STAT st;
832        if(EMU_STAT(args[1].c_str(), &st) == 0 || errno != ENOENT)
833        {
834                BOX_ERROR("The local file '" << args[1] << " already exists.");
835                return;
836        }
837       
838        // Open file
839        FileStream out(args[1].c_str(), O_WRONLY | O_CREAT | O_EXCL);
840       
841        // Request that object
842        try
843        {
844                // Request object
845                std::auto_ptr<BackupProtocolSuccess> getobj(mrConnection.QueryGetObject(id));
846                if(getobj->GetObjectID() != BackupProtocolGetObject::NoObject)
847                {
848                        // Stream that object out to the file
849                        std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream());
850                        objectStream->CopyStreamTo(out);
851                       
852                        BOX_INFO("Object ID " << BOX_FORMAT_OBJECTID(id) <<
853                                " fetched successfully.");
854                }
855                else
856                {
857                        BOX_ERROR("Object ID " << BOX_FORMAT_OBJECTID(id) <<
858                                " does not exist on store.");
859                        ::unlink(args[1].c_str());
860                }
861        }
862        catch(...)
863        {
864                ::unlink(args[1].c_str());
865                BOX_ERROR("Error occured fetching object.");
866        }
867}
868
869
870// --------------------------------------------------------------------------
871//
872// Function
873//              Name:    BackupQueries::FindFileID(const std::string&
874//                       rNameOrIdString, const bool *options,
875//                       int64_t *pDirIdOut, std::string* pFileNameOut)
876//              Purpose: Locate a file on the store (either by name or by
877//                       object ID, depending on opts['i'], where name can
878//                       include a path) and return the file ID, placing the
879//                       directory ID in *pDirIdOut and the filename part
880//                       of the path in *pFileNameOut (if not NULL).
881//              Created: 2008-09-12
882//
883// --------------------------------------------------------------------------
884int64_t BackupQueries::FindFileID(const std::string& rNameOrIdString,
885        const bool *opts, int64_t *pDirIdOut, std::string* pFileNameOut,
886        int16_t flagsInclude, int16_t flagsExclude, int16_t* pFlagsOut)
887{
888        // Find object ID somehow
889        int64_t fileId;
890        int64_t dirId = GetCurrentDirectoryID();
891        std::string fileName = rNameOrIdString;
892
893        if(!opts['i'])
894        {
895                // does this remote filename include a path?
896                std::string::size_type index = fileName.rfind('/');
897                if(index != std::string::npos)
898                {
899                        std::string dirName(fileName.substr(0, index));
900                        fileName = fileName.substr(index + 1);
901
902                        dirId = FindDirectoryObjectID(dirName);
903                        if(dirId == 0)
904                        {
905                                BOX_ERROR("Directory '" << dirName <<
906                                        "' not found.");
907                                return 0;
908                        }
909                }
910        }
911
912        BackupStoreFilenameClear fn(fileName);
913
914        // Need to look it up in the current directory
915        mrConnection.QueryListDirectory(
916                dirId, flagsInclude, flagsExclude,
917                true /* do want attributes */);
918
919        // Retrieve the directory from the stream following
920        BackupStoreDirectory dir;
921        std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
922        dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
923        BackupStoreDirectory::Entry *en;
924
925        if(opts['i'])
926        {
927                // Specified as ID.
928                fileId = ::strtoll(rNameOrIdString.c_str(), 0, 16);
929                if(fileId == std::numeric_limits<long long>::min() || 
930                        fileId == std::numeric_limits<long long>::max() || 
931                        fileId == 0)
932                {
933                        BOX_ERROR("Not a valid object ID (specified in hex).");
934                        return 0;
935                }
936               
937                // Check that the item is actually in the directory
938                en = dir.FindEntryByID(fileId);
939                if(en == 0)
940                {
941                        BOX_ERROR("File ID " << 
942                                BOX_FORMAT_OBJECTID(fileId) <<
943                                " not found in current directory on store.\n"
944                                "(You can only access files by ID from the "
945                                "current directory.)");
946                        return 0;
947                }
948        }
949        else
950        {                               
951                // Specified by name, find the object in the directory to get the ID
952                BackupStoreDirectory::Iterator i(dir);
953                en = i.FindMatchingClearName(fn);
954                if(en == 0)
955                {
956                        BOX_ERROR("Filename '" << rNameOrIdString << "' "
957                                "not found in current directory on store.\n"
958                                "(Subdirectories in path not searched.)");
959                        return 0;
960                }
961               
962                fileId = en->GetObjectID();
963        }
964
965        *pDirIdOut = dirId;
966
967        if(pFlagsOut)
968        {
969                *pFlagsOut = en->GetFlags();
970        }
971
972        if(pFileNameOut)
973        {
974                BackupStoreFilenameClear entryName(en->GetName());
975                *pFileNameOut = entryName.GetClearFilename();
976        }
977
978        return fileId;
979}
980
981
982// --------------------------------------------------------------------------
983//
984// Function
985//              Name:    BackupQueries::CommandGet(const std::vector<std::string> &, const bool *)
986//              Purpose: Command to get a file from the store
987//              Created: 2003/10/12
988//
989// --------------------------------------------------------------------------
990void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts)
991{
992        // At least one argument?
993        // Check args
994        if(args.size() < 1 || (opts['i'] && args.size() != 2) || args.size() > 2)
995        {
996                BOX_ERROR("Incorrect usage.\n"
997                        "get <remote-filename> [<local-filename>] or\n"
998                        "get -i <object-id> <local-filename>");
999                return;
1000        }
1001
1002        // Find object ID somehow
1003        int64_t fileId, dirId;
1004        std::string localName;
1005
1006#ifdef WIN32
1007        for (std::vector<std::string>::iterator
1008                i = args.begin(); i != args.end(); i++)
1009        {
1010                std::string out;
1011                if(!ConvertConsoleToUtf8(i->c_str(), out))
1012                {
1013                        BOX_ERROR("Failed to convert encoding.");
1014                        return;
1015                }
1016                *i = out;
1017        }
1018#endif
1019
1020        int16_t flagsExclude;
1021
1022        if(opts['i'])
1023        {
1024                // can retrieve anything by ID
1025                flagsExclude = BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING;
1026        }
1027        else
1028        {
1029                // only current versions by name
1030                flagsExclude =
1031                        BackupProtocolListDirectory::Flags_OldVersion |
1032                        BackupProtocolListDirectory::Flags_Deleted;
1033        }
1034
1035
1036        fileId = FindFileID(args[0], opts, &dirId, &localName,
1037                BackupProtocolListDirectory::Flags_File, // just files
1038                flagsExclude, NULL /* don't care about flags found */);
1039
1040        if (fileId == 0)
1041        {
1042                // error already reported
1043                return;
1044        }
1045
1046        if(opts['i'])
1047        {
1048                // Specified as ID.  Must have a local name in the arguments
1049                // (check at beginning of function ensures this)
1050                localName = args[1];
1051        }
1052        else
1053        {                               
1054                // Specified by name. Local name already set by FindFileID,
1055                // but may be overridden by user supplying a second argument.
1056                if(args.size() == 2)
1057                {
1058                        localName = args[1];
1059                }
1060        }
1061       
1062        // Does local file already exist? (don't want to overwrite)
1063        EMU_STRUCT_STAT st;
1064        if(EMU_STAT(localName.c_str(), &st) == 0 || errno != ENOENT)
1065        {
1066                BOX_ERROR("The local file " << localName << " already exists, "
1067                        "will not overwrite it.");
1068                SetReturnCode(ReturnCode::Command_Error);
1069                return;
1070        }
1071       
1072        // Request it from the store
1073        try
1074        {
1075                // Request object
1076                mrConnection.QueryGetFile(dirId, fileId);
1077
1078                // Stream containing encoded file
1079                std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream());
1080               
1081                // Decode it
1082                BackupStoreFile::DecodeFile(*objectStream, localName.c_str(), mrConnection.GetTimeout());
1083
1084                // Done.
1085                BOX_INFO("Object ID " << BOX_FORMAT_OBJECTID(fileId) <<
1086                        " fetched successfully.");
1087        }
1088        catch (BoxException &e)
1089        {
1090                BOX_ERROR("Failed to fetch file: " << 
1091                        e.what());
1092                ::unlink(localName.c_str());
1093        }
1094        catch(std::exception &e)
1095        {
1096                BOX_ERROR("Failed to fetch file: " <<
1097                        e.what());
1098                ::unlink(localName.c_str());
1099        }
1100        catch(...)
1101        {
1102                BOX_ERROR("Failed to fetch file: unknown error");
1103                ::unlink(localName.c_str());
1104        }
1105}
1106
1107// --------------------------------------------------------------------------
1108//
1109// Function
1110//              Name:    BackupQueries::CompareParams::CompareParams()
1111//              Purpose: Constructor
1112//              Created: 29/1/04
1113//
1114// --------------------------------------------------------------------------
1115BackupQueries::CompareParams::CompareParams(bool QuickCompare,
1116        bool IgnoreExcludes, bool IgnoreAttributes,
1117        box_time_t LatestFileUploadTime)
1118: BoxBackupCompareParams(QuickCompare, IgnoreExcludes, IgnoreAttributes,
1119        LatestFileUploadTime),
1120  mDifferences(0),
1121  mDifferencesExplainedByModTime(0),
1122  mUncheckedFiles(0),
1123  mExcludedDirs(0),
1124  mExcludedFiles(0)
1125{ }
1126
1127// --------------------------------------------------------------------------
1128//
1129// Function
1130//              Name:    BackupQueries::CommandCompare(const std::vector<std::string> &, const bool *)
1131//              Purpose: Command to compare data on the store with local data
1132//              Created: 2003/10/12
1133//
1134// --------------------------------------------------------------------------
1135void BackupQueries::CommandCompare(const std::vector<std::string> &args, const bool *opts)
1136{
1137        box_time_t LatestFileUploadTime = GetCurrentBoxTime();
1138       
1139        // Try and work out the time before which all files should be on the server
1140        {
1141                std::string syncTimeFilename(mrConfiguration.GetKeyValue("DataDirectory") + DIRECTORY_SEPARATOR_ASCHAR);
1142                syncTimeFilename += "last_sync_start";
1143                // Stat it to get file time
1144                EMU_STRUCT_STAT st;
1145                if(EMU_STAT(syncTimeFilename.c_str(), &st) == 0)
1146                {
1147                        // Files modified after this time shouldn't be on the server, so report errors slightly differently
1148                        LatestFileUploadTime = FileModificationTime(st) -
1149                                SecondsToBoxTime(mrConfiguration.GetKeyValueInt("MinimumFileAge"));
1150                }
1151                else
1152                {
1153                        BOX_WARNING("Failed to determine the time of the last "
1154                                "synchronisation -- checks not performed.");
1155                }
1156        }
1157
1158        // Parameters, including count of differences
1159        BackupQueries::CompareParams params(opts['q'], // quick compare?
1160                opts['E'], // ignore excludes
1161                opts['A'], // ignore attributes
1162                LatestFileUploadTime);
1163       
1164        params.mQuietCompare = opts['Q'];
1165       
1166        // Quick compare?
1167        if(params.QuickCompare())
1168        {
1169                BOX_WARNING("Quick compare used -- file attributes are not "
1170                        "checked.");
1171        }
1172       
1173        if(!opts['l'] && opts['a'] && args.size() == 0)
1174        {
1175                // Compare all locations
1176                const Configuration &rLocations(
1177                        mrConfiguration.GetSubConfiguration("BackupLocations"));
1178                std::vector<std::string> locNames =
1179                        rLocations.GetSubConfigurationNames();
1180                for(std::vector<std::string>::iterator
1181                        pLocName  = locNames.begin();
1182                        pLocName != locNames.end();
1183                        pLocName++)
1184                {
1185                        CompareLocation(*pLocName, params);
1186                }
1187        }
1188        else if(opts['l'] && !opts['a'] && args.size() == 1)
1189        {
1190                // Compare one location
1191                CompareLocation(args[0], params);
1192        }
1193        else if(!opts['l'] && !opts['a'] && args.size() == 2)
1194        {
1195                // Compare directory to directory
1196               
1197                // Can't be bothered to do all the hard work to work out which location it's on, and hence which exclude list
1198                if(!params.IgnoreExcludes())
1199                {
1200                        BOX_ERROR("Cannot use excludes on directory to directory comparison -- use -E flag to specify ignored excludes.");
1201                        return;
1202                }
1203                else
1204                {
1205                        // Do compare
1206                        Compare(args[0], args[1], params);
1207                }
1208        }
1209        else
1210        {
1211                BOX_ERROR("Incorrect usage.\ncompare -a\n or compare -l <location-name>\n or compare <store-dir-name> <local-dir-name>");
1212                return;
1213        }
1214
1215        if (!params.mQuietCompare)
1216        {       
1217                BOX_INFO("[ " <<
1218                        params.mDifferencesExplainedByModTime << " (of " <<
1219                        params.mDifferences << ") differences probably "
1220                        "due to file modifications after the last upload ]");
1221        }
1222
1223        BOX_INFO("Differences: " << params.mDifferences << " (" <<
1224                params.mExcludedDirs   << " dirs excluded, " <<
1225                params.mExcludedFiles  << " files excluded, " <<
1226                params.mUncheckedFiles << " files not checked)");
1227       
1228        // Set return code?
1229        if(opts['c'])
1230        {
1231                if (params.mUncheckedFiles != 0)
1232                {
1233                        SetReturnCode(ReturnCode::Compare_Error);
1234                } 
1235                else if (params.mDifferences != 0)
1236                {
1237                        SetReturnCode(ReturnCode::Compare_Different);
1238                }
1239                else
1240                {
1241                        SetReturnCode(ReturnCode::Compare_Same);
1242                }
1243        }
1244}
1245
1246
1247// --------------------------------------------------------------------------
1248//
1249// Function
1250//              Name:    BackupQueries::CompareLocation(const std::string &, BackupQueries::CompareParams &)
1251//              Purpose: Compare a location
1252//              Created: 2003/10/13
1253//
1254// --------------------------------------------------------------------------
1255void BackupQueries::CompareLocation(const std::string &rLocation,
1256        BoxBackupCompareParams &rParams)
1257{
1258        // Find the location's sub configuration
1259        const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations"));
1260        if(!locations.SubConfigurationExists(rLocation.c_str()))
1261        {
1262                BOX_ERROR("Location " << rLocation << " does not exist.");
1263                return;
1264        }
1265        const Configuration &loc(locations.GetSubConfiguration(rLocation.c_str()));
1266
1267        #ifdef WIN32
1268        {
1269                std::string path = loc.GetKeyValue("Path");
1270                if (path.size() > 0 && path[path.size()-1] == 
1271                        DIRECTORY_SEPARATOR_ASCHAR)
1272                {
1273                        BOX_WARNING("Location '" << rLocation << "' path ends "
1274                                "with '" DIRECTORY_SEPARATOR "', "
1275                                "compare may fail!");
1276                }
1277        }
1278        #endif
1279       
1280        // Generate the exclude lists
1281        if(!rParams.IgnoreExcludes())
1282        {
1283                rParams.LoadExcludeLists(loc);
1284        }
1285                       
1286        // Then get it compared
1287        Compare(std::string("/") + rLocation, loc.GetKeyValue("Path"), rParams);
1288}
1289
1290
1291// --------------------------------------------------------------------------
1292//
1293// Function
1294//              Name:    BackupQueries::Compare(const std::string &,
1295//                       const std::string &, BackupQueries::CompareParams &)
1296//              Purpose: Compare a store directory against a local directory
1297//              Created: 2003/10/13
1298//
1299// --------------------------------------------------------------------------
1300void BackupQueries::Compare(const std::string &rStoreDir,
1301        const std::string &rLocalDir, BoxBackupCompareParams &rParams)
1302{
1303#ifdef WIN32
1304        std::string localDirEncoded;
1305        std::string storeDirEncoded;
1306        if(!ConvertConsoleToUtf8(rLocalDir.c_str(), localDirEncoded)) return;
1307        if(!ConvertConsoleToUtf8(rStoreDir.c_str(), storeDirEncoded)) return;
1308#else
1309        const std::string& localDirEncoded(rLocalDir);
1310        const std::string& storeDirEncoded(rStoreDir);
1311#endif
1312       
1313        // Get the directory ID of the directory -- only use current data
1314        int64_t dirID = FindDirectoryObjectID(storeDirEncoded);
1315       
1316        // Found?
1317        if(dirID == 0)
1318        {
1319                bool modifiedAfterLastSync = false;
1320               
1321                EMU_STRUCT_STAT st;
1322                if(EMU_STAT(rLocalDir.c_str(), &st) == 0)
1323                {
1324                        if(FileAttrModificationTime(st) >
1325                                rParams.LatestFileUploadTime())
1326                        {
1327                                modifiedAfterLastSync = true;
1328                        }
1329                }
1330               
1331                rParams.NotifyRemoteFileMissing(localDirEncoded,
1332                        storeDirEncoded, modifiedAfterLastSync);
1333                return;
1334        }
1335       
1336        // Go!
1337        Compare(dirID, storeDirEncoded, localDirEncoded, rParams);
1338}
1339
1340void BackupQueries::CompareOneFile(int64_t DirID,
1341        BackupStoreDirectory::Entry *pEntry,
1342        const std::string& rLocalPath,
1343        const std::string& rStorePath,
1344        BoxBackupCompareParams &rParams)
1345{
1346        int64_t fileId = pEntry->GetObjectID();
1347        int64_t fileSize = 0;
1348
1349        EMU_STRUCT_STAT st;
1350        if(EMU_STAT(rLocalPath.c_str(), &st) == 0)
1351        {
1352                fileSize = st.st_size;
1353        }
1354
1355        try
1356        {
1357                // Files the same flag?
1358                bool equal = true;
1359               
1360                // File modified after last sync flag
1361                bool modifiedAfterLastSync = false;
1362               
1363                bool hasDifferentAttribs = false;
1364
1365                bool alreadyReported = false;
1366                       
1367                if(rParams.QuickCompare())
1368                {
1369                        // Compare file -- fetch it
1370                        mrConnection.QueryGetBlockIndexByID(fileId);
1371
1372                        // Stream containing block index
1373                        std::auto_ptr<IOStream> blockIndexStream(mrConnection.ReceiveStream());
1374                       
1375                        // Compare
1376                        equal = BackupStoreFile::CompareFileContentsAgainstBlockIndex(
1377                                rLocalPath.c_str(), *blockIndexStream,
1378                                mrConnection.GetTimeout());
1379                }
1380                else
1381                {
1382                        // Compare file -- fetch it
1383                        mrConnection.QueryGetFile(DirID, pEntry->GetObjectID());
1384
1385                        // Stream containing encoded file
1386                        std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream());
1387
1388                        // Decode it
1389                        std::auto_ptr<BackupStoreFile::DecodedStream> fileOnServerStream;
1390
1391                        // Got additional attributes?
1392                        if(pEntry->HasAttributes())
1393                        {
1394                                // Use these attributes
1395                                const StreamableMemBlock &storeAttr(pEntry->GetAttributes());
1396                                BackupClientFileAttributes attr(storeAttr);
1397                                fileOnServerStream.reset(
1398                                        BackupStoreFile::DecodeFileStream(
1399                                                *objectStream,
1400                                                mrConnection.GetTimeout(),
1401                                                &attr).release());
1402                        }
1403                        else
1404                        {
1405                                // Use attributes stored in file
1406                                fileOnServerStream.reset(BackupStoreFile::DecodeFileStream(*objectStream, mrConnection.GetTimeout()).release());
1407                        }
1408                       
1409                        // Should always be something in the auto_ptr, it's how the interface is defined. But be paranoid.
1410                        if(!fileOnServerStream.get())
1411                        {
1412                                THROW_EXCEPTION(BackupStoreException, Internal)
1413                        }
1414                       
1415                        // Compare attributes
1416                        BackupClientFileAttributes localAttr;
1417                        box_time_t fileModTime = 0;
1418                        localAttr.ReadAttributes(rLocalPath.c_str(), false /* don't zero mod times */, &fileModTime);                                   
1419                        modifiedAfterLastSync = (fileModTime > rParams.LatestFileUploadTime());
1420                        bool ignoreAttrModTime = true;
1421
1422                        #ifdef WIN32
1423                        // attr mod time is really
1424                        // creation time, so check it
1425                        ignoreAttrModTime = false;
1426                        #endif
1427
1428                        if(!rParams.IgnoreAttributes() &&
1429                        #ifdef PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE
1430                           !fileOnServerStream->IsSymLink() &&
1431                        #endif
1432                           !localAttr.Compare(fileOnServerStream->GetAttributes(),
1433                                        ignoreAttrModTime,
1434                                        fileOnServerStream->IsSymLink() /* ignore modification time if it's a symlink */))
1435                        {
1436                                hasDifferentAttribs = true;
1437                        }
1438
1439                        // Compare contents, if it's a regular file not a link
1440                        // Remember, we MUST read the entire stream from the server.
1441                        SelfFlushingStream flushObject(*objectStream);
1442
1443                        if(!fileOnServerStream->IsSymLink())
1444                        {
1445                                SelfFlushingStream flushFile(*fileOnServerStream);
1446                                // Open the local file
1447                                std::auto_ptr<FileStream> apLocalFile;
1448
1449                                try
1450                                {
1451                                        apLocalFile.reset(new FileStream(rLocalPath.c_str()));
1452                                }
1453                                catch(std::exception &e)
1454                                {
1455                                        rParams.NotifyLocalFileReadFailed(rLocalPath,
1456                                                rStorePath, fileSize, e);
1457                                        alreadyReported = true;
1458                                }
1459                                catch(...)
1460                                {       
1461                                        rParams.NotifyLocalFileReadFailed(rLocalPath,
1462                                                rStorePath, fileSize);
1463                                        alreadyReported = true;
1464                                }
1465
1466                                if(apLocalFile.get())
1467                                {
1468                                        equal = apLocalFile->CompareWith(*fileOnServerStream,
1469                                                mrConnection.GetTimeout());
1470                                }
1471                        }
1472                }
1473
1474                rParams.NotifyFileCompared(rLocalPath, rStorePath, fileSize,
1475                        hasDifferentAttribs, !equal, modifiedAfterLastSync,
1476                        pEntry->HasAttributes());
1477        }
1478        catch(BoxException &e)
1479        {
1480                rParams.NotifyDownloadFailed(rLocalPath, rStorePath, fileSize,
1481                        e);
1482        }
1483        catch(std::exception &e)
1484        {
1485                rParams.NotifyDownloadFailed(rLocalPath, rStorePath, fileSize,
1486                        e);
1487        }
1488        catch(...)
1489        {       
1490                rParams.NotifyDownloadFailed(rLocalPath, rStorePath, fileSize);
1491        }
1492}
1493
1494// --------------------------------------------------------------------------
1495//
1496// Function
1497//              Name:    BackupQueries::Compare(int64_t, const std::string &,
1498//                       const std::string &, BackupQueries::CompareParams &)
1499//              Purpose: Compare a store directory against a local directory
1500//              Created: 2003/10/13
1501//
1502// --------------------------------------------------------------------------
1503void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir,
1504        const std::string &rLocalDir, BoxBackupCompareParams &rParams)
1505{
1506        rParams.NotifyDirComparing(rLocalDir, rStoreDir);
1507
1508        // Get info on the local directory
1509        EMU_STRUCT_STAT st;
1510        if(EMU_LSTAT(rLocalDir.c_str(), &st) != 0)
1511        {
1512                // What kind of error?
1513                if(errno == ENOTDIR || errno == ENOENT)
1514                {
1515                        rParams.NotifyLocalDirMissing(rLocalDir, rStoreDir);
1516                }
1517                else
1518                {
1519                        rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir);
1520                }
1521                return;
1522        }
1523
1524        // Get the directory listing from the store
1525        mrConnection.QueryListDirectory(
1526                DirID,
1527                BackupProtocolListDirectory::Flags_INCLUDE_EVERYTHING,
1528                // get everything
1529                BackupProtocolListDirectory::Flags_OldVersion |
1530                BackupProtocolListDirectory::Flags_Deleted,
1531                // except for old versions and deleted files
1532                true /* want attributes */);
1533
1534        // Retrieve the directory from the stream following
1535        BackupStoreDirectory dir;
1536        std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
1537        dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
1538
1539        // Test out the attributes
1540        if(!dir.HasAttributes())
1541        {
1542                rParams.NotifyStoreDirMissingAttributes(rLocalDir, rStoreDir);
1543        }
1544        else
1545        {
1546                // Fetch the attributes
1547                const StreamableMemBlock &storeAttr(dir.GetAttributes());
1548                BackupClientFileAttributes attr(storeAttr);
1549
1550                // Get attributes of local directory
1551                BackupClientFileAttributes localAttr;
1552                localAttr.ReadAttributes(rLocalDir.c_str(), 
1553                        true /* directories have zero mod times */);
1554
1555                if(attr.Compare(localAttr, true, true /* ignore modification times */))
1556                {
1557                        rParams.NotifyDirCompared(rLocalDir, rStoreDir,
1558                                false, false /* actually we didn't check :) */);
1559                }
1560                else
1561                {
1562                        bool modifiedAfterLastSync = false;
1563                       
1564                        EMU_STRUCT_STAT st;
1565                        if(EMU_STAT(rLocalDir.c_str(), &st) == 0)
1566                        {
1567                                if(FileAttrModificationTime(st) >
1568                                        rParams.LatestFileUploadTime())
1569                                {
1570                                        modifiedAfterLastSync = true;
1571                                }
1572                        }
1573                       
1574                        rParams.NotifyDirCompared(rLocalDir, rStoreDir,
1575                                true, modifiedAfterLastSync);
1576                }
1577        }
1578
1579        // Open the local directory
1580        DIR *dirhandle = ::opendir(rLocalDir.c_str());
1581        if(dirhandle == 0)
1582        {
1583                rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir);
1584                return;
1585        }
1586       
1587        try
1588        {
1589                // Read the files and directories into sets
1590                std::set<std::string> localFiles;
1591                std::set<std::string> localDirs;
1592                struct dirent *localDirEn = 0;
1593                while((localDirEn = readdir(dirhandle)) != 0)
1594                {
1595                        // Not . and ..!
1596                        if(localDirEn->d_name[0] == '.' && 
1597                                (localDirEn->d_name[1] == '\0' || (localDirEn->d_name[1] == '.' && localDirEn->d_name[2] == '\0')))
1598                        {
1599                                // ignore, it's . or ..
1600                               
1601#ifdef HAVE_VALID_DIRENT_D_TYPE
1602                                if (localDirEn->d_type != DT_DIR)
1603                                {
1604                                        BOX_ERROR("d_type does not really "
1605                                                "work on your platform. "
1606                                                "Reconfigure Box!");
1607                                        return;
1608                                }
1609#endif
1610                               
1611                                continue;
1612                        }
1613
1614                        std::string localDirPath(MakeFullPath(rLocalDir,
1615                                localDirEn->d_name));
1616                        std::string storeDirPath(rStoreDir + "/" +
1617                                localDirEn->d_name);
1618
1619#ifndef HAVE_VALID_DIRENT_D_TYPE
1620                        EMU_STRUCT_STAT st;
1621                        if(EMU_LSTAT(localDirPath.c_str(), &st) != 0)
1622                        {
1623                                // Check whether dir is excluded before trying
1624                                // to stat it, to fix problems with .gvfs
1625                                // directories that are not readable by root
1626                                // causing compare to crash:
1627                                // http://lists.boxbackup.org/pipermail/boxbackup/2010-January/000013.html
1628                                if(rParams.IsExcludedDir(localDirPath))
1629                                {
1630                                        rParams.NotifyExcludedDir(localDirPath,
1631                                                storeDirPath);
1632                                        continue;
1633                                }
1634                                else
1635                                {
1636                                        THROW_EXCEPTION_MESSAGE(CommonException,
1637                                                OSFileError, localDirPath);
1638                                }
1639                        }
1640                       
1641                        // Entry -- file or dir?
1642                        if(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
1643                        {       
1644                            // File or symbolic link
1645                            localFiles.insert(std::string(localDirEn->d_name));
1646                        }
1647                        else if(S_ISDIR(st.st_mode))
1648                        {
1649                            // Directory
1650                            localDirs.insert(std::string(localDirEn->d_name));
1651                        }
1652#else
1653                        // Entry -- file or dir?
1654                        if(localDirEn->d_type == DT_REG || localDirEn->d_type == DT_LNK)
1655                        {
1656                                // File or symbolic link
1657                                localFiles.insert(std::string(localDirEn->d_name));
1658                        }
1659                        else if(localDirEn->d_type == DT_DIR)
1660                        {
1661                                // Directory
1662                                localDirs.insert(std::string(localDirEn->d_name));
1663                        }
1664#endif
1665                }
1666                // Close directory
1667                if(::closedir(dirhandle) != 0)
1668                {
1669                        BOX_LOG_SYS_ERROR("Failed to close local directory "
1670                                "'" << rLocalDir << "'");
1671                }
1672                dirhandle = 0;
1673       
1674                // Do the same for the store directories
1675                std::set<std::pair<std::string, BackupStoreDirectory::Entry *> > storeFiles;
1676                std::set<std::pair<std::string, BackupStoreDirectory::Entry *> > storeDirs;
1677               
1678                BackupStoreDirectory::Iterator i(dir);
1679                BackupStoreDirectory::Entry *storeDirEn = 0;
1680                while((storeDirEn = i.Next()) != 0)
1681                {
1682                        // Decrypt filename
1683                        BackupStoreFilenameClear name(storeDirEn->GetName());
1684               
1685                        // What is it?
1686                        if((storeDirEn->GetFlags() & BackupStoreDirectory::Entry::Flags_File) == BackupStoreDirectory::Entry::Flags_File)
1687                        {
1688                                // File
1689                                storeFiles.insert(std::pair<std::string, BackupStoreDirectory::Entry *>(name.GetClearFilename(), storeDirEn));
1690                        }
1691                        else
1692                        {
1693                                // Dir
1694                                storeDirs.insert(std::pair<std::string, BackupStoreDirectory::Entry *>(name.GetClearFilename(), storeDirEn));
1695                        }
1696                }
1697
1698#ifdef _MSC_VER
1699                typedef std::set<std::string>::iterator string_set_iter_t;
1700#else
1701                typedef std::set<std::string>::const_iterator string_set_iter_t;
1702#endif
1703               
1704                // Now compare files.
1705                for(std::set<std::pair<std::string, BackupStoreDirectory::Entry *> >::const_iterator i = storeFiles.begin(); i != storeFiles.end(); ++i)
1706                {
1707                        const std::string& fileName(i->first);
1708
1709                        std::string localPath(MakeFullPath(rLocalDir, fileName));
1710                        std::string storePath(rStoreDir + "/" + fileName);
1711
1712                        rParams.NotifyFileComparing(localPath, storePath);
1713                       
1714                        // Does the file exist locally?
1715                        string_set_iter_t local(localFiles.find(fileName));
1716                        if(local == localFiles.end())
1717                        {
1718                                // Not found -- report
1719                                rParams.NotifyLocalFileMissing(localPath,
1720                                        storePath);
1721                        }
1722                        else
1723                        {                               
1724                                CompareOneFile(DirID, i->second, localPath,
1725                                        storePath, rParams);
1726
1727                                // Remove from set so that we know it's been compared
1728                                localFiles.erase(local);
1729                        }
1730                }
1731               
1732                // Report any files which exist locally, but not on the store
1733                for(string_set_iter_t i = localFiles.begin(); i != localFiles.end(); ++i)
1734                {
1735                        std::string localPath(MakeFullPath(rLocalDir, *i));
1736                        std::string storePath(rStoreDir + "/" + *i);
1737
1738                        // Should this be ignored (ie is excluded)?
1739                        if(!rParams.IsExcludedFile(localPath))
1740                        {
1741                                bool modifiedAfterLastSync = false;
1742                               
1743                                EMU_STRUCT_STAT st;
1744                                if(EMU_STAT(localPath.c_str(), &st) == 0)
1745                                {
1746                                        if(FileModificationTime(st) >
1747                                                rParams.LatestFileUploadTime())
1748                                        {
1749                                                modifiedAfterLastSync = true;
1750                                        }
1751                                }
1752                               
1753                                rParams.NotifyRemoteFileMissing(localPath,
1754                                        storePath, modifiedAfterLastSync);
1755                        }
1756                        else
1757                        {
1758                                rParams.NotifyExcludedFile(localPath,
1759                                        storePath);
1760                        }
1761                }               
1762               
1763                // Finished with the files, clear the sets to reduce memory usage slightly
1764                localFiles.clear();
1765                storeFiles.clear();
1766               
1767                // Now do the directories, recursively to check subdirectories
1768                for(std::set<std::pair<std::string, BackupStoreDirectory::Entry *> >::const_iterator i = storeDirs.begin(); i != storeDirs.end(); ++i)
1769                {
1770                        std::string localPath(MakeFullPath(rLocalDir, i->first));
1771                        std::string storePath(rStoreDir + "/" + i->first);
1772
1773                        // Does the directory exist locally?
1774                        string_set_iter_t local(localDirs.find(i->first));
1775                        if(local == localDirs.end() &&
1776                                rParams.IsExcludedDir(localPath))
1777                        {
1778                                rParams.NotifyExcludedFileNotDeleted(localPath,
1779                                        storePath);
1780                        }
1781                        else if(local == localDirs.end())
1782                        {
1783                                // Not found -- report
1784                                rParams.NotifyLocalFileMissing(localPath,
1785                                        storePath);
1786                        }
1787                        else if(rParams.IsExcludedDir(localPath))
1788                        {
1789                                // don't recurse into excluded directories
1790                        }
1791                        else
1792                        {
1793                                // Compare directory
1794                                Compare(i->second->GetObjectID(),
1795                                        storePath, localPath, rParams);
1796                               
1797                                // Remove from set so that we know it's been compared
1798                                localDirs.erase(local);
1799                        }
1800                }
1801               
1802                // Report any directories which exist locally, but not on the store
1803                for(std::set<std::string>::const_iterator
1804                        i  = localDirs.begin();
1805                        i != localDirs.end(); ++i)
1806                {
1807                        std::string localPath(MakeFullPath(rLocalDir, *i));
1808                        std::string storePath(rStoreDir + "/" + *i);
1809
1810                        // Should this be ignored (ie is excluded)?
1811                        if(!rParams.IsExcludedDir(localPath))
1812                        {
1813                                bool modifiedAfterLastSync = false;
1814                               
1815                                // Check the dir modification time
1816                                EMU_STRUCT_STAT st;
1817                                if(EMU_STAT(localPath.c_str(), &st) == 0 &&
1818                                        FileModificationTime(st) >
1819                                        rParams.LatestFileUploadTime())
1820                                {
1821                                        modifiedAfterLastSync = true;
1822                                }
1823
1824                                rParams.NotifyRemoteFileMissing(localPath,
1825                                        storePath, modifiedAfterLastSync);
1826                        }
1827                        else
1828                        {
1829                                rParams.NotifyExcludedDir(localPath, storePath);
1830                        }
1831                }               
1832        }
1833        catch(...)
1834        {
1835                if(dirhandle != 0)
1836                {
1837                        ::closedir(dirhandle);
1838                }
1839                throw;
1840        }
1841}
1842
1843
1844// --------------------------------------------------------------------------
1845//
1846// Function
1847//              Name:    BackupQueries::CommandRestore(const std::vector<std::string> &, const bool *)
1848//              Purpose: Restore a directory
1849//              Created: 23/11/03
1850//
1851// --------------------------------------------------------------------------
1852void BackupQueries::CommandRestore(const std::vector<std::string> &args, const bool *opts)
1853{
1854        // Check arguments
1855        if(args.size() < 1 || args.size() > 2)
1856        {
1857                BOX_ERROR("Incorrect usage. restore [-drif] <remote-name> "
1858                        "[<local-name>]");
1859                return;
1860        }
1861
1862        // Restoring deleted things?
1863        bool restoreDeleted = opts['d'];
1864
1865        std::string storeDirEncoded;
1866
1867        // Get directory ID
1868        int64_t dirID = 0;
1869        if(opts['i'])
1870        {
1871                // Specified as ID.
1872                dirID = ::strtoll(args[0].c_str(), 0, 16);
1873                if(dirID == std::numeric_limits<long long>::min() || dirID == std::numeric_limits<long long>::max() || dirID == 0)
1874                {
1875                        BOX_ERROR("Not a valid object ID (specified in hex)");
1876                        return;
1877                }
1878                std::ostringstream oss;
1879                oss << BOX_FORMAT_OBJECTID(args[0]);
1880                storeDirEncoded = oss.str();
1881        }
1882        else
1883        {
1884#ifdef WIN32
1885                if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded))
1886                        return;
1887#else
1888                storeDirEncoded = args[0];
1889#endif
1890       
1891                // Look up directory ID
1892                dirID = FindDirectoryObjectID(storeDirEncoded, 
1893                        false /* no old versions */, 
1894                        restoreDeleted /* find deleted dirs */);
1895        }
1896       
1897        // Allowable?
1898        if(dirID == 0)
1899        {
1900                BOX_ERROR("Directory '" << args[0] << "' not found on server");
1901                return;
1902        }
1903
1904        if(dirID == BackupProtocolListDirectory::RootDirectory)
1905        {
1906                BOX_ERROR("Cannot restore the root directory -- restore locations individually.");
1907                return;
1908        }
1909
1910        std::string localName;
1911
1912        if(args.size() == 2)
1913        {
1914                #ifdef WIN32
1915                        if(!ConvertConsoleToUtf8(args[1].c_str(), localName))
1916                        {
1917                                return;
1918                        }
1919                #else
1920                        localName = args[1];
1921                #endif
1922        }
1923        else
1924        {
1925                localName = args[0];
1926        }
1927
1928        // Go and restore...
1929        int result;
1930
1931        try
1932        {
1933                // At TRACE level, we print a line for each file and
1934                // directory, so we don't need dots.
1935
1936                bool printDots = ! Logging::IsEnabled(Log::TRACE);
1937
1938                result = BackupClientRestore(mrConnection, dirID, 
1939                        storeDirEncoded.c_str(), localName.c_str(), 
1940                        printDots /* print progress dots */, restoreDeleted, 
1941                        false /* don't undelete after restore! */, 
1942                        opts['r'] /* resume? */,
1943                        opts['f'] /* force continue after errors */);
1944        }
1945        catch(std::exception &e)
1946        {
1947                BOX_ERROR("Failed to restore: " << e.what());
1948                SetReturnCode(ReturnCode::Command_Error);
1949                return;
1950        }
1951        catch(...)
1952        {
1953                BOX_ERROR("Failed to restore: unknown exception");
1954                SetReturnCode(ReturnCode::Command_Error);
1955                return;
1956        }
1957
1958        switch(result)
1959        {
1960        case Restore_Complete:
1961                BOX_INFO("Restore complete.");
1962                break;
1963       
1964        case Restore_CompleteWithErrors:
1965                BOX_WARNING("Restore complete, but some files could not be "
1966                        "restored.");
1967                break;
1968       
1969        case Restore_ResumePossible:
1970                BOX_ERROR("Resume possible -- repeat command with -r flag "
1971                        "to resume.");
1972                SetReturnCode(ReturnCode::Command_Error);
1973                break;
1974       
1975        case Restore_TargetExists:
1976                BOX_ERROR("The target directory exists. You cannot restore "
1977                        "over an existing directory.");
1978                SetReturnCode(ReturnCode::Command_Error);
1979                break;
1980               
1981        case Restore_TargetPathNotFound:
1982                BOX_ERROR("The target directory path does not exist.\n"
1983                        "To restore to a directory whose parent "
1984                        "does not exist, create the parent first.");
1985                SetReturnCode(ReturnCode::Command_Error);
1986                break;
1987
1988        case Restore_UnknownError:
1989                BOX_ERROR("Unknown error during restore.");
1990                SetReturnCode(ReturnCode::Command_Error);
1991                break;
1992
1993        default:
1994                BOX_ERROR("Unknown restore result " << result << ".");
1995                SetReturnCode(ReturnCode::Command_Error);
1996                break;
1997        }
1998}
1999
2000
2001
2002// These are autogenerated by a script.
2003extern const char *help_commands[];
2004extern const char *help_text[];
2005
2006
2007// --------------------------------------------------------------------------
2008//
2009// Function
2010//              Name:    BackupQueries::CommandHelp(const std::vector<std::string> &args)
2011//              Purpose: Display help on commands
2012//              Created: 15/2/04
2013//
2014// --------------------------------------------------------------------------
2015void BackupQueries::CommandHelp(const std::vector<std::string> &args)
2016{
2017        if(args.size() == 0)
2018        {
2019                // Display a list of all commands
2020                printf("Available commands are:\n");
2021                for(int c = 0; help_commands[c] != 0; ++c)
2022                {
2023                        printf("    %s\n", help_commands[c]);
2024                }
2025                printf("Type \"help <command>\" for more information on a command.\n\n");
2026        }
2027        else
2028        {
2029                // Display help on a particular command
2030                int c;
2031                for(c = 0; help_commands[c] != 0; ++c)
2032                {
2033                        if(::strcmp(help_commands[c], args[0].c_str()) == 0)
2034                        {
2035                                // Found the command, print help
2036                                printf("\n%s\n", help_text[c]);
2037                                break;
2038                        }
2039                }
2040                if(help_commands[c] == 0)
2041                {
2042                        printf("No help found for command '%s'\n", args[0].c_str());
2043                }
2044        }
2045}
2046
2047
2048// --------------------------------------------------------------------------
2049//
2050// Function
2051//              Name:    BackupQueries::CommandUsage()
2052//              Purpose: Display storage space used on server
2053//              Created: 19/4/04
2054//
2055// --------------------------------------------------------------------------
2056void BackupQueries::CommandUsage(const bool *opts)
2057{
2058        bool MachineReadable = opts['m'];
2059
2060        // Request full details from the server
2061        std::auto_ptr<BackupProtocolAccountUsage> usage(mrConnection.QueryGetAccountUsage());
2062
2063        // Display each entry in turn
2064        int64_t hardLimit = usage->GetBlocksHardLimit();
2065        int32_t blockSize = usage->GetBlockSize();
2066        CommandUsageDisplayEntry("Used", usage->GetBlocksUsed(), hardLimit,
2067                blockSize, MachineReadable);
2068        CommandUsageDisplayEntry("Old files", usage->GetBlocksInOldFiles(),
2069                hardLimit, blockSize, MachineReadable);
2070        CommandUsageDisplayEntry("Deleted files", usage->GetBlocksInDeletedFiles(),
2071                hardLimit, blockSize, MachineReadable);
2072        CommandUsageDisplayEntry("Directories", usage->GetBlocksInDirectories(),
2073                hardLimit, blockSize, MachineReadable);
2074        CommandUsageDisplayEntry("Soft limit", usage->GetBlocksSoftLimit(),
2075                hardLimit, blockSize, MachineReadable);
2076        CommandUsageDisplayEntry("Hard limit", hardLimit, hardLimit, blockSize,
2077                MachineReadable);
2078}
2079
2080
2081// --------------------------------------------------------------------------
2082//
2083// Function
2084//              Name:    BackupQueries::CommandUsageDisplayEntry(const char *,
2085//                       int64_t, int64_t, int32_t, bool)
2086//              Purpose: Display an entry in the usage table
2087//              Created: 19/4/04
2088//
2089// --------------------------------------------------------------------------
2090void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size,
2091int64_t HardLimit, int32_t BlockSize, bool MachineReadable)
2092{
2093        std::cout << FormatUsageLineStart(Name, MachineReadable) <<
2094                FormatUsageBar(Size, Size * BlockSize, HardLimit * BlockSize,
2095                        MachineReadable) << std::endl;
2096}
2097
2098
2099// --------------------------------------------------------------------------
2100//
2101// Function
2102//              Name:    BackupQueries::CommandUndelete(const std::vector<std::string> &, const bool *)
2103//              Purpose: Undelete a directory
2104//              Created: 23/11/03
2105//
2106// --------------------------------------------------------------------------
2107void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const bool *opts)
2108{
2109        if (!mReadWrite)
2110        {
2111                BOX_ERROR("This command requires a read-write connection. "
2112                        "Please reconnect with the -w option.");
2113                return;
2114        }
2115
2116        // Check arguments
2117        if(args.size() != 1)
2118        {
2119                BOX_ERROR("Incorrect usage. undelete <name> or undelete -i <object-id>");
2120                return;
2121        }
2122
2123#ifdef WIN32
2124        std::string storeDirEncoded;
2125        if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded)) return;
2126#else
2127        const std::string& storeDirEncoded(args[0]);
2128#endif
2129
2130        // Find object ID somehow
2131        int64_t fileId, parentId;
2132        std::string fileName;
2133        int16_t flagsOut;
2134
2135        fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName,
2136                /* include files and directories */
2137                BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING,
2138                /* include old and deleted files */
2139                BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING,
2140                &flagsOut);
2141
2142        if (fileId == 0)
2143        {
2144                // error already reported
2145                return;
2146        }
2147
2148        // Undelete it on the store
2149        try
2150        {
2151                // Undelete object
2152                if(flagsOut & BackupProtocolListDirectory::Flags_File)
2153                {
2154                        mrConnection.QueryUndeleteFile(parentId, fileId);
2155                }
2156                else
2157                {
2158                        mrConnection.QueryUndeleteDirectory(fileId);
2159                }
2160        }
2161        catch (BoxException &e)
2162        {
2163                BOX_ERROR("Failed to undelete object: " << 
2164                        e.what());
2165        }
2166        catch(std::exception &e)
2167        {
2168                BOX_ERROR("Failed to undelete object: " <<
2169                        e.what());
2170        }
2171        catch(...)
2172        {
2173                BOX_ERROR("Failed to undelete object: unknown error");
2174        }
2175}
2176
2177// --------------------------------------------------------------------------
2178//
2179// Function
2180//              Name:    BackupQueries::CommandDelete(const
2181//                       std::vector<std::string> &, const bool *)
2182//              Purpose: Deletes a file
2183//              Created: 23/11/03
2184//
2185// --------------------------------------------------------------------------
2186void BackupQueries::CommandDelete(const std::vector<std::string> &args,
2187        const bool *opts)
2188{
2189        if (!mReadWrite)
2190        {
2191                BOX_ERROR("This command requires a read-write connection. "
2192                        "Please reconnect with the -w option.");
2193                return;
2194        }
2195
2196        // Check arguments
2197        if(args.size() != 1)
2198        {
2199                BOX_ERROR("Incorrect usage. delete <name>");
2200                return;
2201        }
2202
2203#ifdef WIN32
2204        std::string storeDirEncoded;
2205        if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded)) return;
2206#else
2207        const std::string& storeDirEncoded(args[0]);
2208#endif
2209
2210        // Find object ID somehow
2211        int64_t fileId, parentId;
2212        std::string fileName;
2213        int16_t flagsOut;
2214
2215        fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName,
2216                /* include files and directories */
2217                BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING,
2218                /* exclude old and deleted files */
2219                BackupProtocolListDirectory::Flags_OldVersion |
2220                BackupProtocolListDirectory::Flags_Deleted,
2221                &flagsOut);
2222
2223        if (fileId == 0)
2224        {
2225                // error already reported
2226                return;
2227        }
2228
2229        BackupStoreFilenameClear fn(fileName);
2230
2231        // Delete it on the store
2232        try
2233        {
2234                // Delete object
2235                if(flagsOut & BackupProtocolListDirectory::Flags_File)
2236                {
2237                        mrConnection.QueryDeleteFile(parentId, fn);
2238                }
2239                else
2240                {
2241                        mrConnection.QueryDeleteDirectory(fileId);
2242                }
2243        }
2244        catch (BoxException &e)
2245        {
2246                BOX_ERROR("Failed to delete object: " << 
2247                        e.what());
2248        }
2249        catch(std::exception &e)
2250        {
2251                BOX_ERROR("Failed to delete object: " <<
2252                        e.what());
2253        }
2254        catch(...)
2255        {
2256                BOX_ERROR("Failed to delete object: unknown error");
2257        }
2258}
Note: See TracBrowser for help on using the repository browser.