Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(163)

Side by Side Diff: chrome/browser/sync/engine/syncapi.cc

Issue 194065: Initial commit of sync engine code to browser/sync.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Fixes to gtest include path, reverted syncapi. Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/sync/engine/syncapi.h"
6
7 #if defined(OS_WINDOWS)
8 #include <windows.h>
9 #include <iphlpapi.h>
10 #endif
11
12 #include <iomanip>
13 #include <list>
14 #include <string>
15 #include <vector>
16
17 #include "base/at_exit.h"
18 #include "base/basictypes.h"
19 #include "base/scoped_ptr.h"
20 #include "base/string_util.h"
21 #include "chrome/browser/sync/engine/all_status.h"
22 #include "chrome/browser/sync/engine/auth_watcher.h"
23 #include "chrome/browser/sync/engine/change_reorder_buffer.h"
24 #include "chrome/browser/sync/engine/client_command_channel.h"
25 #include "chrome/browser/sync/engine/model_safe_worker.h"
26 #include "chrome/browser/sync/engine/net/gaia_authenticator.h"
27 #include "chrome/browser/sync/engine/net/server_connection_manager.h"
28 #include "chrome/browser/sync/engine/net/syncapi_server_connection_manager.h"
29 #include "chrome/browser/sync/engine/syncer.h"
30 #include "chrome/browser/sync/engine/syncer_thread.h"
31 #include "chrome/browser/sync/notifier/listener/talk_mediator.h"
32 #include "chrome/browser/sync/notifier/listener/talk_mediator_impl.h"
33 #include "chrome/browser/sync/protocol/service_constants.h"
34 #include "chrome/browser/sync/syncable/directory_manager.h"
35 #include "chrome/browser/sync/syncable/syncable.h"
36 #include "chrome/browser/sync/util/character_set_converters.h"
37 #include "chrome/browser/sync/util/closure.h"
38 #include "chrome/browser/sync/util/crypto_helpers.h"
39 #include "chrome/browser/sync/util/event_sys.h"
40 #include "chrome/browser/sync/util/path_helpers.h"
41 #include "chrome/browser/sync/util/pthread_helpers.h"
42 #include "chrome/browser/sync/util/user_settings.h"
43 #include "googleurl/src/gurl.h"
44
45 using browser_sync::AllStatus;
46 using browser_sync::AllStatusEvent;
47 using browser_sync::AuthWatcher;
48 using browser_sync::AuthWatcherEvent;
49 using browser_sync::ClientCommandChannel;
50 using browser_sync::Syncer;
51 using browser_sync::SyncerEvent;
52 using browser_sync::SyncerStatus;
53 using browser_sync::SyncerThread;
54 using browser_sync::UserSettings;
55 using browser_sync::TalkMediator;
56 using browser_sync::TalkMediatorImpl;
57 using std::list;
58 using std::hex;
59 using std::string;
60 using std::vector;
61 using syncable::Directory;
62 using syncable::DirectoryManager;
63
64 static const int kServerReachablePollingIntervalMsec = 60000 * 60;
65 static const int kThreadExitTimeoutMsec = 60000;
66 static const int kSSLPort = 443;
67
68 // We shouldn't call InitLogFiles more than once since that will cause a crash.
69 // So we use a global state variable to avoid that. This doesn't work in case
70 // of multiple threads, and if some other part also tries to call InitLogFiles
71 // apart from this file. But this is okay for now since this is the only
72 // place we call InitLogFiles.
73 namespace {
74 static bool g_log_files_initialized = false;
75 static base::AtExitManager g_at_exit_manager; // Necessary for NewCallback
76 } // empty namespace
77
78 struct ThreadParams {
79 browser_sync::ServerConnectionManager* conn_mgr;
80 #if defined(OS_WINDOWS)
81 HANDLE exit_flag;
82 #endif
83 };
84
85 // This thread calls CheckServerReachable() whenever a change occurs
86 // in the table that maps IP addresses to interfaces, for example when
87 // the user unplugs his network cable.
88 void* AddressWatchThread(void* arg) {
89 NameCurrentThreadForDebugging("SyncEngine_AddressWatcher");
90 LOG(INFO) << "starting the address watch thread";
91 const ThreadParams* const params = reinterpret_cast<const ThreadParams*>(arg);
92 #if defined(OS_WINDOWS)
93 OVERLAPPED overlapped = {0};
94 overlapped.hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
95 HANDLE file;
96 DWORD rc = WAIT_OBJECT_0;
97 while (true) {
98 // Only call NotifyAddrChange() after the IP address has changed or if this
99 // is the first time through the loop.
100 if (WAIT_OBJECT_0 == rc) {
101 ResetEvent(overlapped.hEvent);
102 DWORD notify_result = NotifyAddrChange(&file, &overlapped);
103 if (ERROR_IO_PENDING != notify_result) {
104 LOG(ERROR) << "NotifyAddrChange() returned unexpected result "
105 << hex << notify_result;
106 break;
107 }
108 }
109 HANDLE events[] = { overlapped.hEvent, params->exit_flag };
110 rc = WaitForMultipleObjects(ARRAYSIZE(events), events, FALSE,
111 kServerReachablePollingIntervalMsec);
112
113 // If the exit flag was signaled, the thread will exit.
114 if (WAIT_OBJECT_0 + 1 == rc)
115 break;
116
117 params->conn_mgr->CheckServerReachable();
118 }
119 CloseHandle(overlapped.hEvent);
120 #else
121 // TODO(zork): Add this functionality to Linux.
122 #endif
123 LOG(INFO) << "The address watch thread has stopped";
124 return 0;
125 }
126
127 namespace sync_api {
128 class ModelSafeWorkerBridge;
129
130 static const PSTR_CHAR kBookmarkSyncUserSettingsDatabase[] =
131 PSTR("BookmarkSyncSettings.sqlite3");
132 static const PSTR_CHAR kDefaultNameForNewNodes[] = PSTR(" ");
133
134 // The list of names which are reserved for use by the server.
135 static const char16* kForbiddenServerNames[] =
136 { STRING16(""), STRING16("."), STRING16("..") };
137
138 //////////////////////////////////////////////////////////////////////////
139 // Static helper functions.
140
141 // Helper function to look up the int64 metahandle of an object given the ID
142 // string.
143 static int64 IdToMetahandle(syncable::BaseTransaction* trans,
144 const syncable::Id& id) {
145 syncable::Entry entry(trans, syncable::GET_BY_ID, id);
146 if (!entry.good())
147 return kInvalidId;
148 return entry.Get(syncable::META_HANDLE);
149 }
150
151 // Checks whether |name| is a server-illegal name followed by zero or more space
152 // characters. The three server-illegal names are the empty string, dot, and
153 // dot-dot. Very long names (>255 bytes in UTF-8 Normalization Form C) are
154 // also illegal, but are not considered here.
155 static bool IsNameServerIllegalAfterTrimming(const string16& name) {
156 size_t untrimmed_count = name.find_last_not_of(' ') + 1;
157 for (int i = 0; i < arraysize(kForbiddenServerNames); ++i) {
158 if (name.compare(0, untrimmed_count, kForbiddenServerNames[i]) == 0)
159 return true;
160 }
161 return false;
162 }
163
164 static bool EndsWithSpace(const string16& string) {
165 return !string.empty() && *string.rbegin() == ' ';
166 }
167
168 static inline void String16ToPathString(const sync_char16 *in,
169 PathString *out) {
170 string16 in_str(in);
171 #if defined(OS_WINDOWS)
172 out->assign(in_str);
173 #else
174 UTF16ToUTF8(in_str.c_str(), in_str.length(), out);
175 #endif
176 }
177
178 static inline void PathStringToString16(const PathString& in, string16* out) {
179 #if defined(OS_WINDOWS)
180 out->assign(in);
181 #else
182 UTF8ToUTF16(in.c_str(), in.length(), out);
183 #endif
184 }
185
186 // When taking a name from the syncapi, append a space if it matches the
187 // pattern of a server-illegal name followed by zero or more spaces.
188 static void SyncAPINameToServerName(const sync_char16 *sync_api_name,
189 PathString* out) {
190 String16ToPathString(sync_api_name, out);
191 string16 sync_api_name_str(sync_api_name);
192 if (IsNameServerIllegalAfterTrimming(sync_api_name_str))
193 out->append(PSTR(" "));
194 }
195
196 // In the reverse direction, if a server name matches the pattern of a
197 // server-illegal name followed by one or more spaces, remove the trailing
198 // space.
199 static void ServerNameToSyncAPIName(const PathString& server_name,
200 string16*out) {
201 string16 server_name_str;
202 PathStringToString16(server_name, &server_name_str);
203 if (IsNameServerIllegalAfterTrimming(server_name_str) &&
204 EndsWithSpace(server_name_str))
205 out->assign(server_name_str, 0, server_name_str.size() - 1);
206 else
207 out->assign(server_name_str);
208 }
209
210 // A UserShare encapsulates the syncable pieces that represent an authenticated
211 // user and their data (share).
212 // This encompasses all pieces required to build transaction objects on the
213 // syncable share.
214 struct UserShare {
215 // The DirectoryManager itself, which is the parent of Transactions and can
216 // be shared across multiple threads (unlike Directory).
217 scoped_ptr<DirectoryManager> dir_manager;
218
219 // The username of the sync user. This is empty until we have performed at
220 // least one successful GAIA authentication with this username, which means
221 // on first-run it is empty until an AUTH_SUCCEEDED event and on future runs
222 // it is set as soon as the client instructs us to authenticate for the last
223 // known valid user (AuthenticateForLastKnownUser()).
224 // Stored as a PathString to avoid string conversions each time a transaction
225 // is created.
226 PathString authenticated_name;
227 };
228
229 ////////////////////////////////////
230 // BaseNode member definitions.
231
232 // BaseNode::BaseNodeInternal provides storage for member Get() functions that
233 // need to return pointers (e.g. strings).
234 struct BaseNode::BaseNodeInternal {
235 string16 url;
236 string16 title;
237 Directory::ChildHandles child_handles;
238 syncable::Blob favicon;
239 };
240
241 BaseNode::BaseNode() : data_(new BaseNode::BaseNodeInternal) {}
242
243 BaseNode::~BaseNode() {
244 delete data_;
245 }
246
247 int64 BaseNode::GetParentId() const {
248 return IdToMetahandle(GetTransaction()->GetWrappedTrans(),
249 GetEntry()->Get(syncable::PARENT_ID));
250 }
251
252 int64 BaseNode::GetId() const {
253 return GetEntry()->Get(syncable::META_HANDLE);
254 }
255
256 bool BaseNode::GetIsFolder() const {
257 return GetEntry()->Get(syncable::IS_DIR);
258 }
259
260 const sync_char16* BaseNode::GetTitle() const {
261 // Store the string in data_ so that the returned pointer is valid.
262 ServerNameToSyncAPIName(GetEntry()->GetName().non_unique_value(),
263 &data_->title);
264 return data_->title.c_str();
265 }
266
267 const sync_char16* BaseNode::GetURL() const {
268 // Store the string in data_ so that the returned pointer is valid.
269 PathStringToString16(GetEntry()->Get(syncable::BOOKMARK_URL), &data_->url);
270 return data_->url.c_str();
271 }
272
273 const int64* BaseNode::GetChildIds(size_t* child_count) const {
274 DCHECK(child_count);
275 Directory* dir = GetTransaction()->GetLookup();
276 dir->GetChildHandles(GetTransaction()->GetWrappedTrans(),
277 GetEntry()->Get(syncable::ID), &data_->child_handles);
278
279 *child_count = data_->child_handles.size();
280 return (data_->child_handles.empty()) ? NULL : &data_->child_handles[0];
281 }
282
283 int64 BaseNode::GetPredecessorId() const {
284 syncable::Id id_string = GetEntry()->Get(syncable::PREV_ID);
285 if (id_string.IsRoot())
286 return kInvalidId;
287 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
288 }
289
290 int64 BaseNode::GetSuccessorId() const {
291 syncable::Id id_string = GetEntry()->Get(syncable::NEXT_ID);
292 if (id_string.IsRoot())
293 return kInvalidId;
294 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
295 }
296
297 int64 BaseNode::GetFirstChildId() const {
298 syncable::Directory* dir = GetTransaction()->GetLookup();
299 syncable::BaseTransaction* trans = GetTransaction()->GetWrappedTrans();
300 syncable::Id id_string =
301 dir->GetFirstChildId(trans, GetEntry()->Get(syncable::ID));
302 if (id_string.IsRoot())
303 return kInvalidId;
304 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
305 }
306
307 const unsigned char* BaseNode::GetFaviconBytes(size_t* size_in_bytes) {
308 data_->favicon = GetEntry()->Get(syncable::BOOKMARK_FAVICON);
309 *size_in_bytes = data_->favicon.size();
310 if (*size_in_bytes)
311 return &(data_->favicon[0]);
312 else
313 return NULL;
314 }
315
316 int64 BaseNode::GetExternalId() const {
317 return GetEntry()->Get(syncable::LOCAL_EXTERNAL_ID);
318 }
319
320 ////////////////////////////////////
321 // WriteNode member definitions
322 void WriteNode::SetIsFolder(bool folder) {
323 if (entry_->Get(syncable::IS_DIR) == folder)
324 return; // Skip redundant changes.
325
326 entry_->Put(syncable::IS_DIR, folder);
327 MarkForSyncing();
328 }
329
330 void WriteNode::SetTitle(const sync_char16* title) {
331 PathString server_legal_name;
332 SyncAPINameToServerName(title, &server_legal_name);
333 syncable::SyncName sync_name(server_legal_name);
334 syncable::DBName db_name(sync_name.value());
335 db_name.MakeOSLegal();
336 db_name.MakeNoncollidingForEntry(transaction_->GetWrappedTrans(),
337 entry_->Get(syncable::PARENT_ID), entry_);
338
339 syncable::Name new_name = syncable::Name::FromDBNameAndSyncName(db_name,
340 sync_name);
341 if (new_name == entry_->GetName())
342 return; // Skip redundant changes.
343
344 entry_->PutName(new_name);
345 MarkForSyncing();
346 }
347
348 void WriteNode::SetURL(const sync_char16* url) {
349 PathString url_string;
350 String16ToPathString(url, &url_string);
351 if (url_string == entry_->Get(syncable::BOOKMARK_URL))
352 return; // Skip redundant changes.
353
354 entry_->Put(syncable::BOOKMARK_URL, url_string);
355 MarkForSyncing();
356 }
357
358 void WriteNode::SetExternalId(int64 id) {
359 if (GetExternalId() != id)
360 entry_->Put(syncable::LOCAL_EXTERNAL_ID, id);
361 }
362
363 WriteNode::WriteNode(WriteTransaction* transaction)
364 : entry_(NULL), transaction_(transaction) {
365 DCHECK(transaction);
366 }
367
368 WriteNode::~WriteNode() {
369 delete entry_;
370 }
371
372 // Find an existing node matching the ID |id|, and bind this WriteNode
373 // to it. Return true on success.
374 bool WriteNode::InitByIdLookup(int64 id) {
375 DCHECK(!entry_) << "Init called twice";
376 DCHECK_NE(id, kInvalidId);
377 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
378 syncable::GET_BY_HANDLE, id);
379 return (entry_->good() && !entry_->Get(syncable::IS_DEL));
380 }
381
382 // Create a new node with default properties, and bind this WriteNode to it.
383 // Return true on success.
384 bool WriteNode::InitByCreation(const BaseNode& parent,
385 const BaseNode* predecessor) {
386 DCHECK(!entry_) << "Init called twice";
387 // |predecessor| must be a child of |parent| or NULL.
388 if (predecessor && predecessor->GetParentId() != parent.GetId()) {
389 DCHECK(false);
390 return false;
391 }
392
393 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID);
394
395 // Start out with a dummy name, but make it unique. We expect
396 // the caller to set a meaningful name after creation.
397 syncable::DBName dummy(kDefaultNameForNewNodes);
398 dummy.MakeOSLegal();
399 dummy.MakeNoncollidingForEntry(transaction_->GetWrappedTrans(), parent_id,
400 NULL);
401
402 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
403 syncable::CREATE, parent_id, dummy);
404
405 if (!entry_->good())
406 return false;
407
408 // Entries are untitled folders by default.
409 entry_->Put(syncable::IS_DIR, true);
410 // TODO(ncarter): Naming this bit IS_BOOKMARK_OBJECT is a bit unfortunate,
411 // since the rest of SyncAPI is essentially bookmark-agnostic.
412 entry_->Put(syncable::IS_BOOKMARK_OBJECT, true);
413
414 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
415 PutPredecessor(predecessor);
416
417 return true;
418 }
419
420 bool WriteNode::SetPosition(const BaseNode& new_parent,
421 const BaseNode* predecessor) {
422 // |predecessor| must be a child of |new_parent| or NULL.
423 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
424 DCHECK(false);
425 return false;
426 }
427
428 syncable::Id new_parent_id = new_parent.GetEntry()->Get(syncable::ID);
429
430 // Filter out redundant changes if both the parent and the predecessor match.
431 if (new_parent_id == entry_->Get(syncable::PARENT_ID)) {
432 const syncable::Id& old = entry_->Get(syncable::PREV_ID);
433 if ((!predecessor && old.IsRoot()) ||
434 (predecessor && (old == predecessor->GetEntry()->Get(syncable::ID)))) {
435 return true;
436 }
437 }
438
439 // Discard the old database name, derive a new database name from the sync
440 // name, and make it legal and unique.
441 syncable::Name name = syncable::Name::FromSyncName(GetEntry()->GetName());
442 name.db_value().MakeOSLegal();
443 name.db_value().MakeNoncollidingForEntry(GetTransaction()->GetWrappedTrans(),
444 new_parent_id, entry_);
445
446 // Atomically change the parent and name. This will fail if it would
447 // introduce a cycle in the hierarchy.
448 if (!entry_->PutParentIdAndName(new_parent_id, name))
449 return false;
450
451 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
452 PutPredecessor(predecessor);
453
454 return true;
455 }
456
457 const syncable::Entry* WriteNode::GetEntry() const {
458 return entry_;
459 }
460
461 const BaseTransaction* WriteNode::GetTransaction() const {
462 return transaction_;
463 }
464
465 void WriteNode::Remove() {
466 entry_->Put(syncable::IS_DEL, true);
467 MarkForSyncing();
468 }
469
470 void WriteNode::PutPredecessor(const BaseNode* predecessor) {
471 syncable::Id predecessor_id = predecessor ?
472 predecessor->GetEntry()->Get(syncable::ID) : syncable::Id();
473 entry_->PutPredecessor(predecessor_id);
474 // Mark this entry as unsynced, to wake up the syncer.
475 MarkForSyncing();
476 }
477
478 void WriteNode::SetFaviconBytes(const unsigned char* bytes,
479 size_t size_in_bytes) {
480 syncable::Blob new_favicon(bytes, bytes + size_in_bytes);
481 if (new_favicon == entry_->Get(syncable::BOOKMARK_FAVICON))
482 return; // Skip redundant changes.
483
484 entry_->Put(syncable::BOOKMARK_FAVICON, new_favicon);
485 MarkForSyncing();
486 }
487
488 void WriteNode::MarkForSyncing() {
489 syncable::MarkForSyncing(entry_);
490 }
491
492 //////////////////////////////////////////////////////////////////////////
493 // ReadNode member definitions
494 ReadNode::ReadNode(const BaseTransaction* transaction)
495 : entry_(NULL), transaction_(transaction) {
496 DCHECK(transaction);
497 }
498
499 ReadNode::~ReadNode() {
500 delete entry_;
501 }
502
503 void ReadNode::InitByRootLookup() {
504 DCHECK(!entry_) << "Init called twice";
505 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans();
506 entry_ = new syncable::Entry(trans, syncable::GET_BY_ID, trans->root_id());
507 if (!entry_->good())
508 DCHECK(false) << "Could not lookup root node for reading.";
509 }
510
511 bool ReadNode::InitByIdLookup(int64 id) {
512 DCHECK(!entry_) << "Init called twice";
513 DCHECK_NE(id, kInvalidId);
514 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans();
515 entry_ = new syncable::Entry(trans, syncable::GET_BY_HANDLE, id);
516 if (!entry_->good())
517 return false;
518 if (entry_->Get(syncable::IS_DEL))
519 return false;
520 LOG_IF(WARNING, !entry_->Get(syncable::IS_BOOKMARK_OBJECT))
521 << "SyncAPI InitByIdLookup referencing non-bookmark object.";
522 return true;
523 }
524
525 const syncable::Entry* ReadNode::GetEntry() const {
526 return entry_;
527 }
528
529 const BaseTransaction* ReadNode::GetTransaction() const {
530 return transaction_;
531 }
532
533 bool ReadNode::InitByTagLookup(const sync_char16* tag) {
534 DCHECK(!entry_) << "Init called twice";
535 PathString tag_string;
536 String16ToPathString(tag, &tag_string);
537 if (tag_string.empty())
538 return false;
539 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans();
540 entry_ = new syncable::Entry(trans, syncable::GET_BY_TAG, tag_string);
541 if (!entry_->good())
542 return false;
543 if (entry_->Get(syncable::IS_DEL))
544 return false;
545 LOG_IF(WARNING, !entry_->Get(syncable::IS_BOOKMARK_OBJECT))
546 << "SyncAPI InitByTagLookup referencing non-bookmark object.";
547 return true;
548 }
549
550
551 //////////////////////////////////////////////////////////////////////////
552 // ReadTransaction member definitions
553 ReadTransaction::ReadTransaction(UserShare* share)
554 : BaseTransaction(share),
555 transaction_(NULL) {
556 transaction_ = new syncable::ReadTransaction(GetLookup(), __FILE__, __LINE__);
557 }
558
559 ReadTransaction::~ReadTransaction() {
560 delete transaction_;
561 }
562
563 syncable::BaseTransaction* ReadTransaction::GetWrappedTrans() const {
564 return transaction_;
565 }
566
567 //////////////////////////////////////////////////////////////////////////
568 // WriteTransaction member definitions
569 WriteTransaction::WriteTransaction(UserShare* share)
570 : BaseTransaction(share),
571 transaction_(NULL) {
572 transaction_ = new syncable::WriteTransaction(GetLookup(), syncable::SYNCAPI,
573 __FILE__, __LINE__);
574 }
575
576 WriteTransaction::~WriteTransaction() {
577 delete transaction_;
578 }
579
580 syncable::BaseTransaction* WriteTransaction::GetWrappedTrans() const {
581 return transaction_;
582 }
583
584 // An implementation of Visitor that we use to "visit" the
585 // ModelSafeWorkerInterface provided by a client of this API. The object we
586 // visit is responsible for calling DoWork, which will invoke Run() on it's
587 // cached work closure.
588 class ModelSafeWorkerVisitor : public ModelSafeWorkerInterface::Visitor {
589 public:
590 explicit ModelSafeWorkerVisitor(Closure* work) : work_(work) { }
591 virtual ~ModelSafeWorkerVisitor() { }
592
593 // ModelSafeWorkerInterface::Visitor implementation.
594 virtual void DoWork() {
595 work_->Run();
596 }
597
598 private:
599 // The work to be done. We run this on DoWork and it cleans itself up
600 // after it is run.
601 Closure* work_;
602
603 DISALLOW_COPY_AND_ASSIGN(ModelSafeWorkerVisitor);
604 };
605
606 // This class is declared in the cc file to allow inheritance from sync types.
607 // The ModelSafeWorkerBridge is a liason between a syncapi-client defined
608 // ModelSafeWorkerInterface and the actual ModelSafeWorker used by the Syncer
609 // for the current SyncManager.
610 class ModelSafeWorkerBridge : public browser_sync::ModelSafeWorker {
611 public:
612 // Takes ownership of |worker|.
613 explicit ModelSafeWorkerBridge(ModelSafeWorkerInterface* worker)
614 : worker_(worker) {
615 }
616 virtual ~ModelSafeWorkerBridge() { }
617
618 // Overriding ModelSafeWorker.
619 virtual void DoWorkAndWaitUntilDone(Closure* work) {
620 // When the syncer has work to be done, we forward it to our worker who
621 // will invoke DoWork on |visitor| when appropriate (from model safe
622 // thread).
623 ModelSafeWorkerVisitor visitor(work);
624 worker_->CallDoWorkFromModelSafeThreadAndWait(&visitor);
625 }
626
627 private:
628 // The worker that we can forward work requests to, to ensure the work
629 // is performed on an appropriate model safe thread.
630 scoped_ptr<ModelSafeWorkerInterface> worker_;
631
632 DISALLOW_COPY_AND_ASSIGN(ModelSafeWorkerBridge);
633 };
634
635 // A GaiaAuthenticator that uses HttpPostProviders instead of CURL.
636 class BridgedGaiaAuthenticator : public browser_sync::GaiaAuthenticator {
637 public:
638 BridgedGaiaAuthenticator(const string& user_agent, const string& service_id,
639 const string& gaia_url,
640 HttpPostProviderFactory* factory)
641 : GaiaAuthenticator(user_agent, service_id, gaia_url),
642 gaia_source_(user_agent), post_factory_(factory) {
643 }
644
645 virtual ~BridgedGaiaAuthenticator() {
646 }
647
648 virtual bool Post(const GURL& url, const string& post_body,
649 unsigned long* response_code, string* response_body) {
650 string connection_url = "https://";
651 connection_url += url.host();
652 connection_url += url.path();
653 HttpPostProviderInterface* http = post_factory_->Create();
654 http->SetUserAgent(gaia_source_.c_str());
655 // SSL is on 443 for Gaia Posts always.
656 http->SetURL(connection_url.c_str(), kSSLPort);
657 http->SetPostPayload("application/x-www-form-urlencoded",
658 post_body.length(), post_body.c_str());
659
660 int os_error_code = 0;
661 int int_response_code = 0;
662 if (!http->MakeSynchronousPost(&os_error_code, &int_response_code)) {
663 LOG(INFO) << "Http POST failed, error returns: " << os_error_code;
664 return false;
665 }
666 *response_code = static_cast<int>(int_response_code);
667 response_body->assign(http->GetResponseContent(),
668 http->GetResponseContentLength());
669 post_factory_->Destroy(http);
670 return true;
671 }
672 private:
673 const std::string gaia_source_;
674 scoped_ptr<HttpPostProviderFactory> post_factory_;
675 DISALLOW_COPY_AND_ASSIGN(BridgedGaiaAuthenticator);
676 };
677
678 //////////////////////////////////////////////////////////////////////////
679 // SyncManager's implementation: SyncManager::SyncInternal
680 class SyncManager::SyncInternal {
681 public:
682 typedef PThreadScopedLock<PThreadMutex> MutexLock;
683 explicit SyncInternal(SyncManager* sync_manager)
684 : observer_(NULL),
685 command_channel_(0),
686 auth_problem_(AUTH_PROBLEM_NONE),
687 sync_manager_(sync_manager),
688 notification_pending_(false),
689 initialized_(false) {
690 }
691
692 ~SyncInternal() { }
693
694 bool Init(const PathString& database_location,
695 const std::string& sync_server_and_path,
696 int port,
697 const char* gaia_service_id,
698 const char* gaia_source,
699 bool use_ssl,
700 HttpPostProviderFactory* post_factory,
701 HttpPostProviderFactory* auth_post_factory,
702 ModelSafeWorkerInterface* model_safe_worker,
703 bool attempt_last_user_authentication,
704 const char* user_agent);
705
706 // Tell sync engine to submit credentials to GAIA for verification and start
707 // the syncing process on success. Successful GAIA authentication will kick
708 // off the following chain of events:
709 // 1. Cause sync engine to open the syncer database.
710 // 2. Trigger the AuthWatcher to create a Syncer for the directory and call
711 // SyncerThread::SyncDirectory; the SyncerThread will block until (4).
712 // 3. Tell the ServerConnectionManager to pass the newly received GAIA auth
713 // token to a sync server to obtain a sync token.
714 // 4. On receipt of this token, the ServerConnectionManager broadcasts
715 // a server-reachable event, which will unblock the SyncerThread,
716 // and the rest is the future.
717 //
718 // If authentication fails, an event will be broadcast all the way up to
719 // the SyncManager::Observer. It may, in turn, decide to try again with new
720 // credentials. Calling this method again is the appropriate course of action
721 // to "retry".
722 void Authenticate(const std::string& username, const std::string& password);
723
724 // Call periodically from a database-safe thread to persist recent changes
725 // to the syncapi model.
726 void SaveChanges();
727
728 // This listener is called upon completion of a syncable transaction, and
729 // builds the list of sync-engine initiated changes that will be forwarded to
730 // the SyncManager's Observers.
731 void HandleChangeEvent(const syncable::DirectoryChangeEvent& event);
732 void HandleTransactionCompleteChangeEvent(
733 const syncable::DirectoryChangeEvent& event);
734 void HandleCalculateChangesChangeEventFromSyncApi(
735 const syncable::DirectoryChangeEvent& event);
736 void HandleCalculateChangesChangeEventFromSyncer(
737 const syncable::DirectoryChangeEvent& event);
738
739 // This listener is called by the syncer channel for all syncer events.
740 void HandleSyncerEvent(const SyncerEvent& event);
741
742 // We have a direct hookup to the authwatcher to be notified for auth failures
743 // on startup, to serve our UI needs.
744 void HandleAuthWatcherEvent(const AuthWatcherEvent& event);
745
746 // Accessors for the private members.
747 DirectoryManager* dir_manager() { return share_.dir_manager.get(); }
748 SyncAPIServerConnectionManager* connection_manager() {
749 return connection_manager_.get();
750 }
751 SyncerThread* syncer_thread() { return syncer_thread_.get(); }
752 TalkMediator* talk_mediator() { return talk_mediator_.get(); }
753 AuthWatcher* auth_watcher() { return auth_watcher_.get(); }
754 AllStatus* allstatus() { return &allstatus_; }
755 void set_observer(Observer* observer) { observer_ = observer; }
756 UserShare* GetUserShare() { return &share_; }
757
758 // Return the currently active (validated) username as a PathString for
759 // use with syncable types.
760 const PathString& username_for_share() const {
761 return share_.authenticated_name;
762 }
763
764 // Returns the authenticated username from our AuthWatcher in UTF8.
765 // See SyncManager::GetAuthenticatedUsername for details.
766 const char* GetAuthenticatedUsername();
767
768 // Note about SyncManager::Status implementation: Status is a trimmed
769 // down AllStatus::Status, augmented with authentication failure information
770 // gathered from the internal AuthWatcher. The sync UI itself hooks up to
771 // various sources like the AuthWatcher individually, but with syncapi we try
772 // to keep everything status-related in one place. This means we have to
773 // privately manage state about authentication failures, and whenever the
774 // status or status summary is requested we aggregate this state with
775 // AllStatus::Status information.
776 Status ComputeAggregatedStatus();
777 Status::Summary ComputeAggregatedStatusSummary();
778
779 // See SyncManager::SetupForTestMode for information.
780 void SetupForTestMode(const sync_char16* test_username);
781
782 // See SyncManager::Shutdown for information.
783 void Shutdown();
784
785 // Whether we're initialized to the point of being able to accept changes
786 // (and hence allow transaction creation). See initialized_ for details.
787 bool initialized() const {
788 MutexLock lock(&initialized_mutex_);
789 return initialized_;
790 }
791 private:
792 // Try to authenticate using persisted credentials from a previous successful
793 // authentication. If no such credentials exist, calls OnAuthError on
794 // the client to collect credentials. Otherwise, there exist local
795 // credentials that were once used for a successful auth, so we'll try to
796 // re-use these.
797 // Failure of that attempt will be communicated as normal using
798 // OnAuthError. Since this entry point will bypass normal GAIA
799 // authentication and try to authenticate directly with the sync service
800 // using a cached token, authentication failure will generally occur due to
801 // expired credentials, or possibly because of a password change.
802 void AuthenticateForLastKnownUser();
803
804 // Helper to call OnAuthError when no authentication credentials
805 // are available.
806 void RaiseAuthNeededEvent();
807
808 // Helper to set initialized_ to true and raise an event to clients to
809 // notify that initialization is complete and it is safe to send us changes.
810 // If already initialized, this is a no-op.
811 void MarkAndNotifyInitializationComplete();
812
813 // Determine if the parents or predecessors differ between the old and new
814 // versions of an entry stored in |a| and |b|. Note that a node's index
815 // may change without its NEXT_ID changing if the node at NEXT_ID also
816 // moved (but the relative order is unchanged). To handle such cases,
817 // we rely on the caller to treat a position update on any sibling as
818 // updating the positions of all siblings.
819 static bool BookmarkPositionsDiffer(const syncable::EntryKernel& a,
820 const syncable::Entry& b) {
821 if (a.ref(syncable::NEXT_ID) != b.Get(syncable::NEXT_ID))
822 return true;
823 if (a.ref(syncable::PARENT_ID) != b.Get(syncable::PARENT_ID))
824 return true;
825 return false;
826 }
827
828 // Determine if any of the fields made visible to clients of the Sync API
829 // differ between the versions of an entry stored in |a| and |b|.
830 // A return value of false means that it should be OK to ignore this change.
831 static bool BookmarkPropertiesDiffer(const syncable::EntryKernel& a,
832 const syncable::Entry& b) {
833 if (a.ref(syncable::NAME) != b.Get(syncable::NAME))
834 return true;
835 if (a.ref(syncable::UNSANITIZED_NAME) != b.Get(syncable::UNSANITIZED_NAME))
836 return true;
837 if (a.ref(syncable::IS_DIR) != b.Get(syncable::IS_DIR))
838 return true;
839 if (a.ref(syncable::BOOKMARK_URL) != b.Get(syncable::BOOKMARK_URL))
840 return true;
841 if (a.ref(syncable::BOOKMARK_FAVICON) != b.Get(syncable::BOOKMARK_FAVICON))
842 return true;
843 if (BookmarkPositionsDiffer(a, b))
844 return true;
845 return false;
846 }
847
848 // We couple the DirectoryManager and username together in a UserShare member
849 // so we can return a handle to share_ to clients of the API for use when
850 // constructing any transaction type.
851 UserShare share_;
852
853 // A cached string for callers of GetAuthenticatedUsername. We just store the
854 // last result of auth_watcher_->email() here and change it on future calls,
855 // because callers of GetAuthenticatedUsername are supposed to copy the value
856 // if they need it for longer than the scope of the call.
857 std::string cached_auth_watcher_email_;
858
859 // A wrapper around a sqlite store used for caching authentication data,
860 // last user information, current sync-related URLs, and more.
861 scoped_ptr<UserSettings> user_settings_;
862
863 // Observer registered via SetObserver/RemoveObserver.
864 // WARNING: This can be NULL!
865 Observer* observer_;
866
867 // A sink for client commands from the syncer needed to create a SyncerThread.
868 ClientCommandChannel command_channel_;
869
870 // The ServerConnectionManager used to abstract communication between
871 // the client (the Syncer) and the sync server.
872 scoped_ptr<SyncAPIServerConnectionManager> connection_manager_;
873
874 // The thread that runs the Syncer. Needs to be explicitly Start()ed.
875 scoped_ptr<SyncerThread> syncer_thread_;
876
877 // Notification (xmpp) handler.
878 scoped_ptr<TalkMediator> talk_mediator_;
879
880 // A multi-purpose status watch object that aggregates stats from various
881 // sync components.
882 AllStatus allstatus_;
883
884 // AuthWatcher kicks off the authentication process and follows it through
885 // phase 1 (GAIA) to phase 2 (sync engine). As part of this work it determines
886 // the initial connectivity and causes the server connection event to be
887 // broadcast, which signals the syncer thread to start syncing.
888 // It has a heavy duty constructor requiring boilerplate so we heap allocate.
889 scoped_ptr<AuthWatcher> auth_watcher_;
890
891 // A store of change records produced by HandleChangeEvent during the
892 // CALCULATE_CHANGES step, and to be processed, and forwarded to the
893 // observer, by HandleChangeEvent during the TRANSACTION_COMPLETE step.
894 ChangeReorderBuffer change_buffer_;
895
896 // The event listener hookup that is registered for HandleChangeEvent.
897 scoped_ptr<EventListenerHookup> dir_change_hookup_;
898
899 // The event listener hookup registered for HandleSyncerEvent.
900 scoped_ptr<EventListenerHookup> syncer_event_;
901
902 // The event listener hookup registered for HandleAuthWatcherEvent.
903 scoped_ptr<EventListenerHookup> authwatcher_hookup_;
904
905 // Our cache of a recent authentication problem. If no authentication problem
906 // occurred, or if the last problem encountered has been cleared (by a
907 // subsequent AuthWatcherEvent), this is set to AUTH_PROBLEM_NONE.
908 AuthProblem auth_problem_;
909
910 // The sync dir_manager to which we belong.
911 SyncManager* const sync_manager_;
912
913 // Parameters for our thread listening to network status changes.
914 ThreadParams address_watch_params_;
915 thread_handle address_watch_thread_;
916
917 // True if the next SyncCycle should notify peers of an update.
918 bool notification_pending_;
919
920 // Set to true once Init has been called, and we know of an authenticated
921 // valid) username either from a fresh authentication attempt (as in
922 // first-use case) or from a previous attempt stored in our UserSettings
923 // (as in the steady-state), and the syncable::Directory has been opened,
924 // meaning we are ready to accept changes. Protected by initialized_mutex_
925 // as it can get read/set by both the SyncerThread and the AuthWatcherThread.
926 bool initialized_;
927 mutable PThreadMutex initialized_mutex_;
928 };
929
930 SyncManager::SyncManager() {
931 data_ = new SyncInternal(this);
932 }
933
934 bool SyncManager::Init(const sync_char16* database_location,
935 const char* sync_server_and_path,
936 int sync_server_port,
937 const char* gaia_service_id,
938 const char* gaia_source,
939 bool use_ssl,
940 HttpPostProviderFactory* post_factory,
941 HttpPostProviderFactory* auth_post_factory,
942 ModelSafeWorkerInterface* model_safe_worker,
943 bool attempt_last_user_authentication,
944 const char* user_agent) {
945 DCHECK(database_location);
946 DCHECK(post_factory);
947
948 PathString db_path;
949 String16ToPathString(database_location, &db_path);
950 string server_string(sync_server_and_path);
951 return data_->Init(db_path,
952 server_string,
953 sync_server_port,
954 gaia_service_id,
955 gaia_source,
956 use_ssl,
957 post_factory,
958 auth_post_factory,
959 model_safe_worker,
960 attempt_last_user_authentication,
961 user_agent);
962 }
963
964 void SyncManager::Authenticate(const char* username, const char* password) {
965 data_->Authenticate(std::string(username), std::string(password));
966 }
967
968 const char* SyncManager::GetAuthenticatedUsername() {
969 if (!data_)
970 return NULL;
971 return data_->GetAuthenticatedUsername();
972 }
973
974 const char* SyncManager::SyncInternal::GetAuthenticatedUsername() {
975 cached_auth_watcher_email_ = browser_sync::ToUTF8(
976 username_for_share()).get_string();
977 return cached_auth_watcher_email_.c_str();
978 }
979
980 bool SyncManager::SyncInternal::Init(
981 const PathString& database_location,
982 const std::string& sync_server_and_path,
983 int port,
984 const char* gaia_service_id,
985 const char* gaia_source,
986 bool use_ssl, HttpPostProviderFactory* post_factory,
987 HttpPostProviderFactory* auth_post_factory,
988 ModelSafeWorkerInterface* model_safe_worker,
989 bool attempt_last_user_authentication,
990 const char* user_agent) {
991
992 if (!g_log_files_initialized) {
993 // TODO(timsteele): Call InitLogFiles() or equivalent.
994 g_log_files_initialized = true;
995 }
996
997 // Set up UserSettings, creating the db if necessary. We need this to
998 // instantiate a URLFactory to give to the Syncer.
999 PathString settings_db_file = AppendSlash(database_location) +
1000 kBookmarkSyncUserSettingsDatabase;
1001 user_settings_.reset(new UserSettings());
1002 if (!user_settings_->Init(settings_db_file))
1003 return false;
1004
1005 share_.dir_manager.reset(new DirectoryManager(database_location));
1006
1007 string client_id = user_settings_->GetClientId();
1008 connection_manager_.reset(new SyncAPIServerConnectionManager(
1009 sync_server_and_path, port, use_ssl, user_agent, client_id));
1010
1011 // TODO(timsteele): This is temporary windows crap needed to listen for
1012 // network status changes. We should either pump this up to the embedder to
1013 // do (and call us in CheckServerReachable, for ex), or at least make this
1014 // platform independent in here.
1015 // TODO(ncarter): When this gets cleaned up, the implementation of
1016 // CreatePThread can also be removed.
1017 #if defined(OS_WINDOWS)
1018 HANDLE exit_flag = CreateEvent(NULL, TRUE /*manual reset*/, FALSE, NULL);
1019 address_watch_params_.exit_flag = exit_flag;
1020 #endif
1021 address_watch_params_.conn_mgr = connection_manager();
1022 address_watch_thread_ = CreatePThread(AddressWatchThread,
1023 &address_watch_params_);
1024 DCHECK(NULL != address_watch_thread_);
1025
1026 // Hand over the bridged POST factory to be owned by the connection
1027 // dir_manager.
1028 connection_manager()->SetHttpPostProviderFactory(post_factory);
1029
1030 // Watch various objects for aggregated status.
1031 allstatus()->WatchConnectionManager(connection_manager());
1032
1033 std::string gaia_url = browser_sync::kGaiaUrl;
1034 const char* service_id = gaia_service_id ?
1035 gaia_service_id : SYNC_SERVICE_NAME;
1036
1037 talk_mediator_.reset(new TalkMediatorImpl());
1038 allstatus()->WatchTalkMediator(talk_mediator());
1039
1040 BridgedGaiaAuthenticator* gaia_auth = new BridgedGaiaAuthenticator(
1041 gaia_source, service_id, gaia_url, auth_post_factory);
1042
1043 auth_watcher_.reset(new AuthWatcher(dir_manager(),
1044 connection_manager(),
1045 &allstatus_,
1046 gaia_source,
1047 service_id,
1048 gaia_url,
1049 user_settings_.get(),
1050 gaia_auth,
1051 talk_mediator()));
1052
1053 talk_mediator()->WatchAuthWatcher(auth_watcher());
1054 allstatus()->WatchAuthWatcher(auth_watcher());
1055 authwatcher_hookup_.reset(NewEventListenerHookup(auth_watcher_->channel(),
1056 this, &SyncInternal::HandleAuthWatcherEvent));
1057
1058 // Tell the SyncerThread to use the ModelSafeWorker for bookmark model work.
1059 // We set up both sides of the "bridge" here, with the ModelSafeWorkerBridge
1060 // on the Syncer side, and |model_safe_worker| on the API client side.
1061 ModelSafeWorkerBridge* worker = new ModelSafeWorkerBridge(model_safe_worker);
1062
1063 syncer_thread_.reset(new SyncerThread(&command_channel_,
1064 dir_manager(),
1065 connection_manager(),
1066 &allstatus_,
1067 worker));
1068 syncer_thread()->WatchTalkMediator(talk_mediator());
1069 allstatus()->WatchSyncerThread(syncer_thread());
1070
1071 syncer_thread()->Start(); // Start the syncer thread. This won't actually
1072 // result in any syncing until at least the
1073 // DirectoryManager broadcasts the OPENED event,
1074 // and a valid server connection is detected.
1075
1076 if (attempt_last_user_authentication)
1077 AuthenticateForLastKnownUser();
1078 return true;
1079 }
1080
1081 void SyncManager::SyncInternal::MarkAndNotifyInitializationComplete() {
1082 // There is only one real time we need this mutex. If we get an auth
1083 // success, and before the initial sync ends we get an auth failure. In this
1084 // case we'll be listening to both the AuthWatcher and Syncer, and it's a race
1085 // between their respective threads to call MarkAndNotify. We need to make
1086 // sure the observer is notified once and only once.
1087 {
1088 MutexLock lock(&initialized_mutex_);
1089 if (initialized_)
1090 return;
1091 initialized_ = true;
1092 }
1093
1094 // Notify that initialization is complete.
1095 if (observer_)
1096 observer_->OnInitializationComplete();
1097 }
1098
1099 void SyncManager::SyncInternal::Authenticate(const std::string& username,
1100 const std::string& password) {
1101 DCHECK(username_for_share().empty() ||
1102 (username == browser_sync::ToUTF8(username_for_share()).get_string()))
1103 << "Username change from valid username detected";
1104 if (allstatus()->status().authenticated)
1105 return;
1106 if (password.empty()) {
1107 // TODO(timsteele): Seems like this shouldn't be needed, but auth_watcher
1108 // currently drops blank password attempts on the floor and doesn't update
1109 // state; it only LOGs an error in this case. We want to make sure we set
1110 // our AuthProblem state to denote an error.
1111 RaiseAuthNeededEvent();
1112 }
1113 auth_watcher()->Authenticate(username, password, true);
1114 }
1115
1116 void SyncManager::SyncInternal::AuthenticateForLastKnownUser() {
1117 std::string username;
1118 std::string auth_token;
1119 if (!(auth_watcher()->settings()->GetLastUserAndServiceToken(
1120 SYNC_SERVICE_NAME, &username, &auth_token))) {
1121 RaiseAuthNeededEvent();
1122 return;
1123 }
1124
1125 browser_sync::ToPathString s(username);
1126 if (s.good()) {
1127 share_.authenticated_name = s.get_string16();
1128 } else {
1129 RaiseAuthNeededEvent();
1130 return;
1131 }
1132
1133 // We optimize by opening the directory before the "fresh" authentication
1134 // attempt completes so that we can immediately begin processing changes.
1135 if (!dir_manager()->Open(username_for_share())) {
1136 DCHECK(false) << "Had last known user but could not open directory";
1137 return;
1138 }
1139
1140 // Set the sync data type so that the server only sends us bookmarks
1141 // changes.
1142 {
1143 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1144 if (!lookup.good()) {
1145 DCHECK(false) << "ScopedDirLookup failed on successfully opened dir";
1146 return;
1147 }
1148 if (lookup->initial_sync_ended())
1149 MarkAndNotifyInitializationComplete();
1150 }
1151
1152 auth_watcher()->AuthenticateWithToken(username, auth_token);
1153 }
1154
1155 void SyncManager::SyncInternal::RaiseAuthNeededEvent() {
1156 auth_problem_ = AUTH_PROBLEM_INVALID_GAIA_CREDENTIALS;
1157 if (observer_)
1158 observer_->OnAuthProblem(auth_problem_);
1159 }
1160
1161 SyncManager::~SyncManager() {
1162 delete data_;
1163 }
1164
1165 void SyncManager::SetObserver(Observer* observer) {
1166 data_->set_observer(observer);
1167 }
1168
1169 void SyncManager::RemoveObserver() {
1170 data_->set_observer(NULL);
1171 }
1172
1173 void SyncManager::Shutdown() {
1174 data_->Shutdown();
1175 }
1176
1177 void SyncManager::SyncInternal::Shutdown() {
1178 // First reset the AuthWatcher in case an auth attempt is in progress so that
1179 // it terminates gracefully before we shutdown and close other components.
1180 // Otherwise the attempt can complete after we've closed the directory, for
1181 // example, and cause initialization to continue, which is bad.
1182 auth_watcher_.reset();
1183
1184 if (syncer_thread()) {
1185 if (!syncer_thread()->Stop(kThreadExitTimeoutMsec))
1186 DCHECK(false) << "Unable to stop the syncer, it won't be happy...";
1187 }
1188
1189 // Shutdown the xmpp buzz connection.
1190 LOG(INFO) << "P2P: Mediator logout started.";
1191 if (talk_mediator()) {
1192 talk_mediator()->Logout();
1193 }
1194 LOG(INFO) << "P2P: Mediator logout completed.";
1195
1196 if (dir_manager()) {
1197 dir_manager()->FinalSaveChangesForAll();
1198 dir_manager()->Close(username_for_share());
1199 }
1200
1201 // Reset the DirectoryManager and UserSettings so they relinquish sqlite
1202 // handles to backing files.
1203 share_.dir_manager.reset();
1204 user_settings_.reset();
1205
1206 // We don't want to process any more events.
1207 dir_change_hookup_.reset();
1208 syncer_event_.reset();
1209 authwatcher_hookup_.reset();
1210
1211 #if defined(OS_WINDOWS)
1212 // Stop the address watch thread by signaling the exit flag.
1213 // TODO(timsteele): Same as todo in Init().
1214 SetEvent(address_watch_params_.exit_flag);
1215 const DWORD wait_result = WaitForSingleObject(address_watch_thread_,
1216 kThreadExitTimeoutMsec);
1217 LOG_IF(ERROR, WAIT_FAILED == wait_result) << "Waiting for addr change thread "
1218 "to exit failed. GetLastError(): " << hex << GetLastError();
1219 LOG_IF(ERROR, WAIT_TIMEOUT == wait_result) << "Thread exit timeout expired";
1220 CloseHandle(address_watch_params_.exit_flag);
1221 #endif
1222 }
1223
1224 // Listen to model changes, filter out ones initiated by the sync API, and
1225 // saves the rest (hopefully just backend Syncer changes resulting from
1226 // ApplyUpdates) to data_->changelist.
1227 void SyncManager::SyncInternal::HandleChangeEvent(
1228 const syncable::DirectoryChangeEvent& event) {
1229 if (event.todo == syncable::DirectoryChangeEvent::TRANSACTION_COMPLETE) {
1230 HandleTransactionCompleteChangeEvent(event);
1231 return;
1232 } else if (event.todo == syncable::DirectoryChangeEvent::CALCULATE_CHANGES) {
1233 if (event.writer == syncable::SYNCAPI) {
1234 HandleCalculateChangesChangeEventFromSyncApi(event);
1235 return;
1236 }
1237 HandleCalculateChangesChangeEventFromSyncer(event);
1238 return;
1239 } else if (event.todo == syncable::DirectoryChangeEvent::SHUTDOWN) {
1240 dir_change_hookup_.reset();
1241 }
1242 }
1243
1244 void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent(
1245 const syncable::DirectoryChangeEvent& event) {
1246 DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::TRANSACTION_COMPLETE);
1247 // This notification happens immediately after a syncable WriteTransaction
1248 // falls out of scope.
1249 if (change_buffer_.IsEmpty() || !observer_)
1250 return;
1251
1252 ReadTransaction trans(GetUserShare());
1253 vector<ChangeRecord> ordered_changes;
1254 change_buffer_.GetAllChangesInTreeOrder(&trans, &ordered_changes);
1255 if (!ordered_changes.empty()) {
1256 observer_->OnChangesApplied(&trans, &ordered_changes[0],
1257 ordered_changes.size());
1258 }
1259 change_buffer_.Clear();
1260 }
1261
1262 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi(
1263 const syncable::DirectoryChangeEvent& event) {
1264 // We have been notified about a user action changing the bookmark model.
1265 DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::CALCULATE_CHANGES);
1266 DCHECK_EQ(event.writer, syncable::SYNCAPI);
1267 LOG_IF(WARNING, !change_buffer_.IsEmpty()) <<
1268 "CALCULATE_CHANGES called with unapplied old changes.";
1269
1270 bool exists_unsynced_items = false;
1271 for (syncable::OriginalEntries::const_iterator i = event.originals->begin();
1272 i != event.originals->end() && !exists_unsynced_items;
1273 ++i) {
1274 int64 id = i->ref(syncable::META_HANDLE);
1275 syncable::Entry e(event.trans, syncable::GET_BY_HANDLE, id);
1276 DCHECK(e.good());
1277
1278 if (e.IsRoot()) {
1279 // Ignore root object, should it ever change.
1280 continue;
1281 } else if (!e.Get(syncable::IS_BOOKMARK_OBJECT)) {
1282 // Ignore non-bookmark objects.
1283 continue;
1284 } else if (e.Get(syncable::IS_UNSYNCED)) {
1285 // Unsynced items will cause us to nudge the the syncer.
1286 exists_unsynced_items = true;
1287 }
1288 }
1289 if (exists_unsynced_items && syncer_thread()) {
1290 syncer_thread()->NudgeSyncer(200, SyncerThread::kLocal); // 1/5 a second.
1291 }
1292 }
1293
1294 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer(
1295 const syncable::DirectoryChangeEvent& event) {
1296 // We only expect one notification per sync step, so change_buffer_ should
1297 // contain no pending entries.
1298 DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::CALCULATE_CHANGES);
1299 DCHECK_EQ(event.writer, syncable::SYNCER);
1300 LOG_IF(WARNING, !change_buffer_.IsEmpty()) <<
1301 "CALCULATE_CHANGES called with unapplied old changes.";
1302
1303 for (syncable::OriginalEntries::const_iterator i = event.originals->begin();
1304 i != event.originals->end(); ++i) {
1305 int64 id = i->ref(syncable::META_HANDLE);
1306 syncable::Entry e(event.trans, syncable::GET_BY_HANDLE, id);
1307 bool existed_before = !i->ref(syncable::IS_DEL);
1308 bool exists_now = e.good() && !e.Get(syncable::IS_DEL);
1309 DCHECK(e.good());
1310
1311 // Ignore root object, should it ever change.
1312 if (e.IsRoot())
1313 continue;
1314 // Ignore non-bookmark objects.
1315 if (!e.Get(syncable::IS_BOOKMARK_OBJECT))
1316 continue;
1317
1318 if (exists_now && !existed_before)
1319 change_buffer_.PushAddedItem(id);
1320 else if (!exists_now && existed_before)
1321 change_buffer_.PushDeletedItem(id);
1322 else if (exists_now && existed_before && BookmarkPropertiesDiffer(*i, e))
1323 change_buffer_.PushUpdatedItem(id, BookmarkPositionsDiffer(*i, e));
1324 }
1325 }
1326
1327 SyncManager::Status::Summary
1328 SyncManager::SyncInternal::ComputeAggregatedStatusSummary() {
1329 switch (allstatus()->status().icon) {
1330 case AllStatus::OFFLINE:
1331 return Status::OFFLINE;
1332 case AllStatus::OFFLINE_UNSYNCED:
1333 return Status::OFFLINE_UNSYNCED;
1334 case AllStatus::SYNCING:
1335 return Status::SYNCING;
1336 case AllStatus::READY:
1337 return Status::READY;
1338 case AllStatus::CONFLICT:
1339 return Status::CONFLICT;
1340 case AllStatus::OFFLINE_UNUSABLE:
1341 return Status::OFFLINE_UNUSABLE;
1342 default:
1343 return Status::INVALID;
1344 }
1345 }
1346
1347 SyncManager::Status SyncManager::SyncInternal::ComputeAggregatedStatus() {
1348 Status return_status =
1349 { ComputeAggregatedStatusSummary(),
1350 allstatus()->status().authenticated,
1351 allstatus()->status().server_up,
1352 allstatus()->status().server_reachable,
1353 allstatus()->status().server_broken,
1354 allstatus()->status().notifications_enabled,
1355 allstatus()->status().notifications_received,
1356 allstatus()->status().notifications_sent,
1357 allstatus()->status().unsynced_count,
1358 allstatus()->status().conflicting_count,
1359 allstatus()->status().syncing,
1360 allstatus()->status().initial_sync_ended,
1361 allstatus()->status().syncer_stuck,
1362 allstatus()->status().updates_available,
1363 allstatus()->status().updates_received,
1364 allstatus()->status().disk_full,
1365 allstatus()->status().max_consecutive_errors};
1366 return return_status;
1367 }
1368
1369 void SyncManager::SyncInternal::HandleSyncerEvent(const SyncerEvent& event) {
1370 if (!initialized()) {
1371 // We get here if A) We have successfully authenticated at least once (
1372 // because we attach HandleSyncerEvent only once we receive notification of
1373 // successful authentication [locally or otherwise]), but B) the initial
1374 // sync had not completed at that time.
1375 if (SyncerStatus(event.last_session).IsShareUsable())
1376 MarkAndNotifyInitializationComplete();
1377 return;
1378 }
1379
1380 if (!observer_)
1381 return;
1382
1383 // Only send an event if this is due to a cycle ending and this cycle
1384 // concludes a canonical "sync" process; that is, based on what is known
1385 // locally we are "all happy" and up-to-date. There may be new changes on
1386 // the server, but we'll get them on a subsequent sync.
1387 //
1388 // Notifications are sent at the end of every sync cycle, regardless of
1389 // whether we should sync again.
1390 if (event.what_happened == SyncerEvent::SYNC_CYCLE_ENDED) {
1391 if (!event.last_session->ShouldSyncAgain()) {
1392 observer_->OnSyncCycleCompleted();
1393 }
1394
1395 // TODO(chron): Consider changing this back to track ShouldSyncAgain
1396 // Only notify peers if a commit has occurred and change the bookmark model.
1397 if (event.last_session && event.last_session->items_committed()) {
1398 notification_pending_ = true;
1399 }
1400
1401 // SyncCycles are started by the following events: creation of the syncer,
1402 // (re)connection to buzz, local changes, peer notifications of updates.
1403 // Peers will be notified of changes made while there is no buzz connection
1404 // immediately after a connection has been re-established.
1405 // the next sync cycle.
1406 // TODO(brg): Move this to TalkMediatorImpl as a SyncerThread event hook.
1407 if (notification_pending_ && talk_mediator()) {
1408 LOG(INFO) << "Sending XMPP notification...";
1409 bool success = talk_mediator()->SendNotification();
1410 if (success) {
1411 notification_pending_ = false;
1412 }
1413 } else {
1414 LOG(INFO) << "Didn't send XMPP notification!"
1415 << " event.last_session: " << event.last_session
1416 << " event.last_session->items_committed(): "
1417 << event.last_session->items_committed()
1418 << " talk_mediator(): " << talk_mediator();
1419 }
1420 }
1421 }
1422
1423 void SyncManager::SyncInternal::HandleAuthWatcherEvent(
1424 const AuthWatcherEvent& event) {
1425 // We don't care about an authentication attempt starting event, and we
1426 // don't want to reset our state to AUTH_PROBLEM_NONE because the fact that
1427 // an _attempt_ is starting doesn't change the fact that we have an auth
1428 // problem.
1429 if (event.what_happened == AuthWatcherEvent::AUTHENTICATION_ATTEMPT_START)
1430 return;
1431 // We clear our last auth problem cache on new auth watcher events, and only
1432 // set it to indicate a problem state for certain AuthWatcherEvent types.
1433 auth_problem_ = AUTH_PROBLEM_NONE;
1434 switch (event.what_happened) {
1435 case AuthWatcherEvent::AUTH_SUCCEEDED:
1436 // We now know the supplied username and password were valid. If this
1437 // wasn't the first sync, authenticated_name should already be assigned.
1438 if (username_for_share().empty()) {
1439 browser_sync::ToPathString s(event.user_email);
1440 if (s.good())
1441 share_.authenticated_name = s.get_string16();
1442 }
1443
1444 DCHECK(LowerCaseEqualsASCII(browser_sync::ToUTF8(
1445 username_for_share()).get_string(),
1446 StringToLowerASCII(event.user_email).c_str()))
1447 << "username_for_share= "
1448 << browser_sync::ToUTF8(username_for_share())
1449 << ", event.user_email= " << event.user_email;
1450
1451 if (observer_)
1452 observer_->OnAuthProblem(AUTH_PROBLEM_NONE);
1453
1454 // Hook up the DirectoryChangeEvent listener, HandleChangeEvent.
1455 {
1456 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1457 if (!lookup.good()) {
1458 DCHECK(false) << "ScopedDirLookup creation failed; unable to hook "
1459 << "up directory change event listener!";
1460 return;
1461 }
1462 dir_change_hookup_.reset(NewEventListenerHookup(
1463 lookup->changes_channel(), this,
1464 &SyncInternal::HandleChangeEvent));
1465
1466 if (lookup->initial_sync_ended())
1467 MarkAndNotifyInitializationComplete();
1468 }
1469 {
1470 // Start watching the syncer channel directly here.
1471 DCHECK(syncer_thread() != NULL);
1472 syncer_event_.reset(NewEventListenerHookup(syncer_thread()->channel(),
1473 this, &SyncInternal::HandleSyncerEvent));
1474 }
1475 return;
1476 // Authentication failures translate to Status::AuthProblem events.
1477 case AuthWatcherEvent::GAIA_AUTH_FAILED: // Invalid GAIA credentials.
1478 case AuthWatcherEvent::SERVICE_AUTH_FAILED: // Expired GAIA credentials.
1479 auth_problem_ = AUTH_PROBLEM_INVALID_GAIA_CREDENTIALS;
1480 break;
1481 case AuthWatcherEvent::SERVICE_USER_NOT_SIGNED_UP:
1482 auth_problem_ = AUTH_PROBLEM_USER_NOT_SIGNED_UP;
1483 break;
1484 case AuthWatcherEvent::SERVICE_CONNECTION_FAILED:
1485 auth_problem_ = AUTH_PROBLEM_CONNECTION_FAILED;
1486 break;
1487 default: // We don't care about the many other AuthWatcherEvent types.
1488 return;
1489 }
1490
1491 // Fire notification that the status changed due to an authentication error.
1492 if (observer_)
1493 observer_->OnAuthProblem(auth_problem_);
1494 }
1495
1496 SyncManager::Status::Summary SyncManager::GetStatusSummary() const {
1497 return data_->ComputeAggregatedStatusSummary();
1498 }
1499
1500 SyncManager::Status SyncManager::GetDetailedStatus() const {
1501 return data_->ComputeAggregatedStatus();
1502 }
1503
1504 SyncManager::SyncInternal* SyncManager::GetImpl() const { return data_; }
1505
1506 void SyncManager::SaveChanges() {
1507 data_->SaveChanges();
1508 }
1509
1510 void SyncManager::SyncInternal::SaveChanges() {
1511 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1512 if (!lookup.good()) {
1513 DCHECK(false) << "ScopedDirLookup creation failed; Unable to SaveChanges";
1514 return;
1515 }
1516 lookup->SaveChanges();
1517 }
1518
1519 void SyncManager::SetupForTestMode(const sync_char16* test_username) {
1520 DCHECK(data_) << "SetupForTestMode requires initialization";
1521 data_->SetupForTestMode(test_username);
1522 }
1523
1524 void SyncManager::SyncInternal::SetupForTestMode(
1525 const sync_char16* test_username) {
1526 String16ToPathString(test_username, &share_.authenticated_name);
1527
1528 if (!dir_manager()->Open(username_for_share()))
1529 DCHECK(false) << "Could not open directory when running in test mode";
1530
1531 // Hook up the DirectoryChangeEvent listener, HandleChangeEvent.
1532 {
1533 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1534 if (!lookup.good()) {
1535 DCHECK(false) << "ScopedDirLookup creation failed; unable to hook "
1536 << "up directory change event listener!";
1537 return;
1538 }
1539 dir_change_hookup_.reset(NewEventListenerHookup(
1540 lookup->changes_channel(), this,
1541 &SyncInternal::HandleChangeEvent));
1542 }
1543 MarkAndNotifyInitializationComplete();
1544 }
1545
1546 //////////////////////////////////////////////////////////////////////////
1547 // BaseTransaction member definitions
1548 BaseTransaction::BaseTransaction(UserShare* share)
1549 : lookup_(NULL) {
1550 DCHECK(share && share->dir_manager.get());
1551 lookup_ = new syncable::ScopedDirLookup(share->dir_manager.get(),
1552 share->authenticated_name);
1553 if (!(lookup_->good()))
1554 DCHECK(false) << "ScopedDirLookup failed on valid DirManager.";
1555 }
1556 BaseTransaction::~BaseTransaction() {
1557 delete lookup_;
1558 }
1559
1560 UserShare* SyncManager::GetUserShare() const {
1561 DCHECK(data_->initialized()) << "GetUserShare requires initialization!";
1562 return data_->GetUserShare();
1563 }
1564
1565 } // namespace sync_api
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698