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