NOTE: This concerns the Win32 client (in source or binary form).
To get the most out of this, you should have a passing familiarity with
C++, with the Standard Template library (STL), and the Windows Base API.
Some of the classes are modelled after the Java class library, particularly
java.io
. No knowledge of MFC, ATL, .NET, or anything else is needed.
All Windows-based programs must have a main-event loop. This is a single loop that pulls the next event off of the Windows event queue, and dispatches accordingly. A normal program would have code like:
while (GetMessage(&msg, 0, 0, NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); }
If you're familiar with Windows C programming, you've seen code like this a gadjillion times. The BookmarkSync client is a bit more complex, because it wants to deal with GUI events (above) with File HANDLE Events (something completely different). File handles are much like file descriptors in Unix. Asynchronous events, like thread exit, or network IO completion, or file change notifications are tracked by file handle events.
So, if you look in SyncIT/Synchronizer.cxx
, you'll find a
function Synchronizer::driver()
. This function is the main
event loop for BookmarkSync. It's state-based: in certain states it does
the main event loop like a normal windows application, in other states it
calls MsgWaitForMultipleObjects
, the Windows function that
merges File handle events with GUI events.
The entire "trick" of BookmarkSync is the FileChangeNotification
function in Windows. An application can register a directory to "watch" using
FindFirstChangeNotification
, then wait for a change using the file
handle notification discussed above, calling FindNextChangeNotification
each time a notification occurs.
Each browser type requires slightly different calls to
FindFirstChangeNotification
. This is why there's an abstract
base class Browser (found in SyncIT/Browser.h
) and different
implementations (found in SyncIT/MicrosoftBrowser.cxx
,
NetscapeBrowser.cxx
, and OperaBrowser.cxx
). The
actual code to read and write bookmark files is kept in the BookmarkLib
directory, in files like NetscapeInput.cxx
, NetscapeOutput.cxx
,
WinFavoritesInput.cxx
, etc.
The code in NetscapeBrowser.cxx
is a lot more complex than
the other browser's code, because if you edit Netscape's bookmark file while
Netscape is still running, Netscape will just overwrite its own changes.
Yuck. So we maintain a DDE connection to Netscape so we can tell when
Netscape starts up or shuts down. This code is found in
SyncIT/NetscapeBrowser.cxx
in the Callback
function. This Callback
function is called by DDE, this is set
up in the NetscapeBrowser::NetscapeBrowser()
function.
So we know when to read bookmark files (when the directories that the browsers store their bookmark files in change) and we know when to write bookmark files (anytime, for most browsers except for Netscape) and we know the bookmark file formats.
To do this requires a hierarchical "diff" algorithm. This is found in
BookmarkLib/BookmarkModel.cxx
in the function diffBookmarks
.
The idea here is to give this function two separate bookmark sets, and a
"listener" that will be told of differences, ie add a folder, delete a
bookmark, etc. One listener edits a target bookmark set, one listener
handles almost all edits except deletes (that's the code that does the
"merge") and one listener builds the set of instructions to send to the
server (in SyncProtocol.cxx
).
The file SyncIT/Synchronizer.cxx
is a giant state machine.
Events like "File change" or "Timeout expired" or "Start" trigger certain
events. The state machine is documented above Synchronizer::onStartErr()
in SyncIT/Synchronizer.cxx
. When it is time to actually make a
connection to the BookmarkSync server, a thread is created, and the code in
SyncIT/SyncProtocol.cxx
is run. The Input/Output code looks a
*LOT* like the Java IO class library did 5 years ago.
Our internal file format for storing what the bookmarks look like during sync is XBEL. Since we wrote this in the early days of XML, we wrote our own XML parser and API. SAX was still buggy when we wrote this.
We have an abstract Socket
class that can be overridden by
SocksSocket
if there's a SOCKS proxy. Ditto with an
HttpClient
connection, that can be overridden with the
WININET DLL
. Look in SyncLib/HttpRequest.{h, cxx}
and SyncLib/WinInetHttpRequest.{h, cxx}
.