1 // distribution boxbackup-0.11_trunk_2979 (svn version: 2979)
2 // Box Backup, http://www.boxbackup.org/
3 //
4 // Copyright (c) 2003-2010, Ben Summers and contributors.
5 // All rights reserved.
6 //
7 // Note that this project uses mixed licensing. Any file with this license
8 // attached, or where the code LICENSE-GPL appears on the first line, falls
9 // under the "Box Backup GPL" license. See the file COPYING.txt for more
10 // information about this license.
11 //
12 // ---------------------------------------------------------------------
13 // This program is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU General Public License
15 // as published by the Free Software Foundation; either version 2
16 // of the License, or (at your option) any later version.
17 //
18 // This program is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 //
27 // [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4]
28 //
29 // As a special exception to the GPLv2, the Box Backup Project gives
30 // permission to link any code falling under this license (the Box Backup
31 // GPL) with any software that can be downloaded from
32 // the OpenSSL website [http://www.openssl.org] under either the
33 // "OpenSSL License" or the "Original SSLeay License", and to distribute
34 // the linked executables under the terms of the "Box Backup GPL" license.
35 //
36 // As a special exception to the GPLv2, the Box Backup Project gives
37 // permission to link any code falling under this license (the Box Backup
38 // GPL) with any version of Microsoft's Volume Shadow Copy Service 7.2 SDK
39 // or Microsoft Windows Software Development Kit (SDK), including
40 // vssapi.lib, that can be downloaded from the Microsoft website
41 // [*.microsoft.com], and to distribute the linked executables under the
42 // terms of the "Box Backup GPL" license.
43 // --------------------------------------------------------------------------
44 //
45 // File
46 // Name: BackupClientContext.cpp
47 // Purpose: Keep track of context
48 // Created: 2003/10/08
49 //
50 // --------------------------------------------------------------------------
51
52 #include "Box.h"
53
54 #ifdef HAVE_SIGNAL_H
55 #include <signal.h>
56 #endif
57
58 #ifdef HAVE_SYS_TIME_H
59 #include <sys/time.h>
60 #endif
61
62 #include "BoxPortsAndFiles.h"
63 #include "BoxTime.h"
64 #include "BackupClientContext.h"
65 #include "SocketStreamTLS.h"
66 #include "Socket.h"
67 #include "BackupStoreConstants.h"
68 #include "BackupStoreException.h"
69 #include "BackupDaemon.h"
70 #include "autogen_BackupProtocolClient.h"
71 #include "BackupStoreFile.h"
72 #include "Logging.h"
73
74 #include "MemLeakFindOn.h"
75
76 // --------------------------------------------------------------------------
77 //
78 // Function
79 // Name: BackupClientContext::BackupClientContext(BackupDaemon &, TLSContext &, const std::string &, int32_t, bool, bool, std::string)
80 // Purpose: Constructor
81 // Created: 2003/10/08
82 //
83 // --------------------------------------------------------------------------
BackupClientContext(LocationResolver & rResolver,TLSContext & rTLSContext,const std::string & rHostname,int Port,uint32_t AccountNumber,bool ExtendedLogging,bool ExtendedLogToFile,std::string ExtendedLogFile,ProgressNotifier & rProgressNotifier)84 BackupClientContext::BackupClientContext
85 (
86 LocationResolver &rResolver,
87 TLSContext &rTLSContext,
88 const std::string &rHostname,
89 int Port,
90 uint32_t AccountNumber,
91 bool ExtendedLogging,
92 bool ExtendedLogToFile,
93 std::string ExtendedLogFile,
94 ProgressNotifier& rProgressNotifier
95 )
96 : mrResolver(rResolver),
97 mrTLSContext(rTLSContext),
98 mHostname(rHostname),
99 mPort(Port),
100 mAccountNumber(AccountNumber),
101 mpSocket(0),
102 mpConnection(0),
103 mExtendedLogging(ExtendedLogging),
104 mExtendedLogToFile(ExtendedLogToFile),
105 mExtendedLogFile(ExtendedLogFile),
106 mpExtendedLogFileHandle(NULL),
107 mClientStoreMarker(ClientStoreMarker_NotKnown),
108 mpDeleteList(0),
109 mpCurrentIDMap(0),
110 mpNewIDMap(0),
111 mStorageLimitExceeded(false),
112 mpExcludeFiles(0),
113 mpExcludeDirs(0),
114 mKeepAliveTimer(0, "KeepAliveTime"),
115 mbIsManaged(false),
116 mrProgressNotifier(rProgressNotifier)
117 {
118 }
119
120 // --------------------------------------------------------------------------
121 //
122 // Function
123 // Name: BackupClientContext::~BackupClientContext()
124 // Purpose: Destructor
125 // Created: 2003/10/08
126 //
127 // --------------------------------------------------------------------------
~BackupClientContext()128 BackupClientContext::~BackupClientContext()
129 {
130 CloseAnyOpenConnection();
131
132 // Delete delete list
133 if(mpDeleteList != 0)
134 {
135 delete mpDeleteList;
136 mpDeleteList = 0;
137 }
138 }
139
140 // --------------------------------------------------------------------------
141 //
142 // Function
143 // Name: BackupClientContext::GetConnection()
144 // Purpose: Returns the connection, making the connection and logging into
145 // the backup store if necessary.
146 // Created: 2003/10/08
147 //
148 // --------------------------------------------------------------------------
GetConnection()149 BackupProtocolClient &BackupClientContext::GetConnection()
150 {
151 // Already got it? Just return it.
152 if(mpConnection != 0)
153 {
154 return *mpConnection;
155 }
156
157 // Get a socket connection
158 if(mpSocket == 0)
159 {
160 mpSocket = new SocketStreamTLS;
161 ASSERT(mpSocket != 0); // will have exceptioned if this was a problem
162 }
163
164 try
165 {
166 // Defensive.
167 if(mpConnection != 0)
168 {
169 delete mpConnection;
170 mpConnection = 0;
171 }
172
173 // Log intention
174 BOX_INFO("Opening connection to server '" <<
175 mHostname << "'...");
176
177 // Connect!
178 mpSocket->Open(mrTLSContext, Socket::TypeINET,
179 mHostname.c_str(), mPort);
180
181 // And create a procotol object
182 mpConnection = new BackupProtocolClient(*mpSocket);
183
184 // Set logging option
185 mpConnection->SetLogToSysLog(mExtendedLogging);
186
187 if (mExtendedLogToFile)
188 {
189 ASSERT(mpExtendedLogFileHandle == NULL);
190
191 mpExtendedLogFileHandle = fopen(
192 mExtendedLogFile.c_str(), "a+");
193
194 if (!mpExtendedLogFileHandle)
195 {
196 BOX_LOG_SYS_ERROR("Failed to open extended "
197 "log file: " << mExtendedLogFile);
198 }
199 else
200 {
201 mpConnection->SetLogToFile(mpExtendedLogFileHandle);
202 }
203 }
204
205 // Handshake
206 mpConnection->Handshake();
207
208 // Check the version of the server
209 {
210 std::auto_ptr<BackupProtocolClientVersion> serverVersion(mpConnection->QueryVersion(BACKUP_STORE_SERVER_VERSION));
211 if(serverVersion->GetVersion() != BACKUP_STORE_SERVER_VERSION)
212 {
213 THROW_EXCEPTION(BackupStoreException, WrongServerVersion)
214 }
215 }
216
217 // Login -- if this fails, the Protocol will exception
218 std::auto_ptr<BackupProtocolClientLoginConfirmed> loginConf(mpConnection->QueryLogin(mAccountNumber, 0 /* read/write */));
219
220 // Check that the client store marker is the one we expect
221 if(mClientStoreMarker != ClientStoreMarker_NotKnown)
222 {
223 if(loginConf->GetClientStoreMarker() != mClientStoreMarker)
224 {
225 // Not good... finish the connection, abort, etc, ignoring errors
226 try
227 {
228 mpConnection->QueryFinished();
229 mpSocket->Shutdown();
230 mpSocket->Close();
231 }
232 catch(...)
233 {
234 // IGNORE
235 }
236
237 // Then throw an exception about this
238 THROW_EXCEPTION(BackupStoreException, ClientMarkerNotAsExpected)
239 }
240 }
241
242 // Log success
243 BOX_INFO("Connection made, login successful");
244
245 // Check to see if there is any space available on the server
246 if(loginConf->GetBlocksUsed() >= loginConf->GetBlocksHardLimit())
247 {
248 // no -- flag so only things like deletions happen
249 mStorageLimitExceeded = true;
250 // Log
251 BOX_WARNING("Exceeded storage hard-limit on server, "
252 "not uploading changes to files");
253 }
254 }
255 catch(...)
256 {
257 // Clean up.
258 delete mpConnection;
259 mpConnection = 0;
260 delete mpSocket;
261 mpSocket = 0;
262 throw;
263 }
264
265 return *mpConnection;
266 }
267
268 // --------------------------------------------------------------------------
269 //
270 // Function
271 // Name: BackupClientContext::CloseAnyOpenConnection()
272 // Purpose: Closes a connection, if it's open
273 // Created: 2003/10/08
274 //
275 // --------------------------------------------------------------------------
CloseAnyOpenConnection()276 void BackupClientContext::CloseAnyOpenConnection()
277 {
278 if(mpConnection)
279 {
280 try
281 {
282 // Need to set a client store marker?
283 if(mClientStoreMarker == ClientStoreMarker_NotKnown)
284 {
285 // Yes, choose one, the current time will do
286 box_time_t marker = GetCurrentBoxTime();
287
288 // Set it on the store
289 mpConnection->QuerySetClientStoreMarker(marker);
290
291 // Record it so that it can be picked up later.
292 mClientStoreMarker = marker;
293 }
294
295 // Quit nicely
296 mpConnection->QueryFinished();
297 }
298 catch(...)
299 {
300 // Ignore errors here
301 }
302
303 // Delete it anyway.
304 delete mpConnection;
305 mpConnection = 0;
306 }
307
308 if(mpSocket)
309 {
310 try
311 {
312 // Be nice about closing the socket
313 mpSocket->Shutdown();
314 mpSocket->Close();
315 }
316 catch(...)
317 {
318 // Ignore errors
319 }
320
321 // Delete object
322 delete mpSocket;
323 mpSocket = 0;
324 }
325
326 // Delete any pending list
327 if(mpDeleteList != 0)
328 {
329 delete mpDeleteList;
330 mpDeleteList = 0;
331 }
332
333 if (mpExtendedLogFileHandle != NULL)
334 {
335 fclose(mpExtendedLogFileHandle);
336 mpExtendedLogFileHandle = NULL;
337 }
338 }
339
340
341
342 // --------------------------------------------------------------------------
343 //
344 // Function
345 // Name: BackupClientContext::GetTimeout()
346 // Purpose: Gets the current timeout time.
347 // Created: 2003/10/08
348 //
349 // --------------------------------------------------------------------------
GetTimeout() const350 int BackupClientContext::GetTimeout() const
351 {
352 if(mpConnection)
353 {
354 return mpConnection->GetTimeout();
355 }
356
357 return (15*60*1000);
358 }
359
360
361 // --------------------------------------------------------------------------
362 //
363 // Function
364 // Name: BackupClientContext::GetDeleteList()
365 // Purpose: Returns the delete list, creating one if necessary
366 // Created: 10/11/03
367 //
368 // --------------------------------------------------------------------------
GetDeleteList()369 BackupClientDeleteList &BackupClientContext::GetDeleteList()
370 {
371 // Already created?
372 if(mpDeleteList == 0)
373 {
374 mpDeleteList = new BackupClientDeleteList;
375 }
376
377 // Return reference to object
378 return *mpDeleteList;
379 }
380
381
382 // --------------------------------------------------------------------------
383 //
384 // Function
385 // Name: BackupClientContext::PerformDeletions()
386 // Purpose: Perform any pending file deletions.
387 // Created: 10/11/03
388 //
389 // --------------------------------------------------------------------------
PerformDeletions()390 void BackupClientContext::PerformDeletions()
391 {
392 // Got a list?
393 if(mpDeleteList == 0)
394 {
395 // Nothing to do
396 return;
397 }
398
399 // Delegate to the delete list object
400 mpDeleteList->PerformDeletions(*this);
401
402 // Delete the object
403 delete mpDeleteList;
404 mpDeleteList = 0;
405 }
406
407
408
409 // --------------------------------------------------------------------------
410 //
411 // Function
412 // Name: BackupClientContext::GetCurrentIDMap() const
413 // Purpose: Return a (const) reference to the current ID map
414 // Created: 11/11/03
415 //
416 // --------------------------------------------------------------------------
GetCurrentIDMap() const417 const BackupClientInodeToIDMap &BackupClientContext::GetCurrentIDMap() const
418 {
419 ASSERT(mpCurrentIDMap != 0);
420 if(mpCurrentIDMap == 0)
421 {
422 THROW_EXCEPTION(CommonException, Internal)
423 }
424 return *mpCurrentIDMap;
425 }
426
427 // --------------------------------------------------------------------------
428 //
429 // Function
430 // Name: BackupClientContext::GetNewIDMap() const
431 // Purpose: Return a reference to the new ID map
432 // Created: 11/11/03
433 //
434 // --------------------------------------------------------------------------
GetNewIDMap() const435 BackupClientInodeToIDMap &BackupClientContext::GetNewIDMap() const
436 {
437 ASSERT(mpNewIDMap != 0);
438 if(mpNewIDMap == 0)
439 {
440 THROW_EXCEPTION(CommonException, Internal)
441 }
442 return *mpNewIDMap;
443 }
444
445
446 // --------------------------------------------------------------------------
447 //
448 // Function
449 // Name: BackupClientContext::FindFilename(int64_t, int64_t, std::string &, bool &) const
450 // Purpose: Attempts to find the pathname of an object with a given ID on the server.
451 // Returns true if it can be found, in which case rPathOut is the local filename,
452 // and rIsDirectoryOut == true if the local object is a directory.
453 // Created: 12/11/03
454 //
455 // --------------------------------------------------------------------------
FindFilename(int64_t ObjectID,int64_t ContainingDirectory,std::string & rPathOut,bool & rIsDirectoryOut,bool & rIsCurrentVersionOut,box_time_t * pModTimeOnServer,box_time_t * pAttributesHashOnServer,BackupStoreFilenameClear * pLeafname)456 bool BackupClientContext::FindFilename(int64_t ObjectID, int64_t ContainingDirectory, std::string &rPathOut, bool &rIsDirectoryOut,
457 bool &rIsCurrentVersionOut, box_time_t *pModTimeOnServer, box_time_t *pAttributesHashOnServer, BackupStoreFilenameClear *pLeafname)
458 {
459 // Make a connection to the server
460 BackupProtocolClient &connection(GetConnection());
461
462 // Request filenames from the server, in a "safe" manner to ignore errors properly
463 {
464 BackupProtocolClientGetObjectName send(ObjectID, ContainingDirectory);
465 connection.Send(send);
466 }
467 std::auto_ptr<BackupProtocolObjectCl> preply(connection.Receive());
468
469 // Is it of the right type?
470 if(preply->GetType() != BackupProtocolClientObjectName::TypeID)
471 {
472 // Was an error or something
473 return false;
474 }
475
476 // Cast to expected type.
477 BackupProtocolClientObjectName *names = (BackupProtocolClientObjectName *)(preply.get());
478
479 // Anything found?
480 int32_t numElements = names->GetNumNameElements();
481 if(numElements <= 0)
482 {
483 // No.
484 return false;
485 }
486
487 // Get the stream containing all the names
488 std::auto_ptr<IOStream> nameStream(connection.ReceiveStream());
489
490 // Path
491 std::string path;
492
493 // Remember this is in reverse order!
494 for(int l = 0; l < numElements; ++l)
495 {
496 BackupStoreFilenameClear elementName;
497 elementName.ReadFromStream(*nameStream, GetTimeout());
498
499 // Store leafname for caller?
500 if(l == 0 && pLeafname)
501 {
502 *pLeafname = elementName;
503 }
504
505 // Is it part of the filename in the location?
506 if(l < (numElements - 1))
507 {
508 // Part of filename within
509 path = (path.empty())?(elementName.GetClearFilename()):(elementName.GetClearFilename() + DIRECTORY_SEPARATOR_ASCHAR + path);
510 }
511 else
512 {
513 // Location name -- look up in daemon's records
514 std::string locPath;
515 if(!mrResolver.FindLocationPathName(elementName.GetClearFilename(), locPath))
516 {
517 // Didn't find the location... so can't give the local filename
518 return false;
519 }
520
521 // Add in location path
522 path = (path.empty())?(locPath):(locPath + DIRECTORY_SEPARATOR_ASCHAR + path);
523 }
524 }
525
526 // Is it a directory?
527 rIsDirectoryOut = ((names->GetFlags() & BackupProtocolClientListDirectory::Flags_Dir) == BackupProtocolClientListDirectory::Flags_Dir);
528
529 // Is it the current version?
530 rIsCurrentVersionOut = ((names->GetFlags() & (BackupProtocolClientListDirectory::Flags_OldVersion | BackupProtocolClientListDirectory::Flags_Deleted)) == 0);
531
532 // And other information which may be required
533 if(pModTimeOnServer) *pModTimeOnServer = names->GetModificationTime();
534 if(pAttributesHashOnServer) *pAttributesHashOnServer = names->GetAttributesHash();
535
536 // Tell caller about the pathname
537 rPathOut = path;
538
539 // Found
540 return true;
541 }
542
SetMaximumDiffingTime(int iSeconds)543 void BackupClientContext::SetMaximumDiffingTime(int iSeconds)
544 {
545 mMaximumDiffingTime = iSeconds < 0 ? 0 : iSeconds;
546 BOX_TRACE("Set maximum diffing time to " << mMaximumDiffingTime <<
547 " seconds");
548 }
549
SetKeepAliveTime(int iSeconds)550 void BackupClientContext::SetKeepAliveTime(int iSeconds)
551 {
552 mKeepAliveTime = iSeconds < 0 ? 0 : iSeconds;
553 BOX_TRACE("Set keep-alive time to " << mKeepAliveTime << " seconds");
554 mKeepAliveTimer = Timer(mKeepAliveTime, "KeepAliveTime");
555 }
556
557 // --------------------------------------------------------------------------
558 //
559 // Function
560 // Name: BackupClientContext::ManageDiffProcess()
561 // Purpose: Initiates a file diff control timer
562 // Created: 04/19/2005
563 //
564 // --------------------------------------------------------------------------
ManageDiffProcess()565 void BackupClientContext::ManageDiffProcess()
566 {
567 ASSERT(!mbIsManaged);
568 mbIsManaged = true;
569 }
570
571 // --------------------------------------------------------------------------
572 //
573 // Function
574 // Name: BackupClientContext::UnManageDiffProcess()
575 // Purpose: suspends file diff control timer
576 // Created: 04/19/2005
577 //
578 // --------------------------------------------------------------------------
UnManageDiffProcess()579 void BackupClientContext::UnManageDiffProcess()
580 {
581 // ASSERT(mbIsManaged);
582 mbIsManaged = false;
583 }
584
585 // --------------------------------------------------------------------------
586 //
587 // Function
588 // Name: BackupClientContext::DoKeepAlive()
589 // Purpose: Check whether it's time to send a KeepAlive
590 // message over the SSL link, and if so, send it.
591 // Created: 04/19/2005
592 //
593 // --------------------------------------------------------------------------
DoKeepAlive()594 void BackupClientContext::DoKeepAlive()
595 {
596 if (!mpConnection)
597 {
598 return;
599 }
600
601 if (mKeepAliveTime == 0)
602 {
603 return;
604 }
605
606 if (!mKeepAliveTimer.HasExpired())
607 {
608 return;
609 }
610
611 BOX_TRACE("KeepAliveTime reached, sending keep-alive message");
612 mpConnection->QueryGetIsAlive();
613
614 mKeepAliveTimer = Timer(mKeepAliveTime, "KeepAliveTime");
615 }
616
GetMaximumDiffingTime()617 int BackupClientContext::GetMaximumDiffingTime()
618 {
619 return mMaximumDiffingTime;
620 }
621