lib: set ProbeResult's constructor to private
[barry.git] / opensync-plugin / src / environment.cc
blob69bd4d16a2bb5d1ef1d47d454764e2a2dca15966
1 //
2 // \file environment.cc
3 // Container / environment class for the sync module.
4 //
6 /*
7 Copyright (C) 2006-2010, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "environment.h"
23 #include "trace.h"
24 #include <glib.h>
25 #include <string>
26 #include <fstream>
27 #include <sstream>
28 #include <iomanip>
29 #include <string.h>
30 #include <unistd.h>
33 //////////////////////////////////////////////////////////////////////////////
34 // DatabaseSyncState
36 DatabaseSyncState::DatabaseSyncState(OSyncMember *pm, const char *description)
37 : m_dbId(0),
38 m_Sync(false),
39 m_Desc(description)
41 m_CacheFilename = m_MapFilename =
42 osync_member_get_configdir(pm);
43 m_CacheFilename += "/barry_" + m_Desc + "_cache.txt";
44 m_MapFilename += "/barry_" + m_Desc + "_idmap.txt";
47 DatabaseSyncState::~DatabaseSyncState()
51 bool DatabaseSyncState::LoadCache()
53 Trace trace("LoadCache", m_Desc.c_str());
55 m_Cache.clear();
56 std::ifstream ifs(m_CacheFilename.c_str());
57 if( !ifs )
58 return false;
60 while( ifs ) {
61 uint32_t recordId = 0;
62 ifs >> recordId;
63 if( recordId ) {
64 m_Cache[recordId] = false;
68 if( !ifs.eof() ) {
69 m_Cache.clear(); // assume full sync
70 trace.log("Load failed!");
71 return false;
73 return true;
76 bool DatabaseSyncState::SaveCache()
78 Trace trace("SaveCache", m_Desc.c_str());
80 std::ofstream ofs(m_CacheFilename.c_str());
81 if( !ofs )
82 return false;
84 cache_type::const_iterator i = m_Cache.begin();
85 for( ; i != m_Cache.end(); ++i ) {
86 ofs << i->first << std::endl;
88 return !ofs.bad() && !ofs.fail();
91 bool DatabaseSyncState::LoadMap()
93 return m_IdMap.Load(m_MapFilename.c_str());
96 bool DatabaseSyncState::SaveMap()
98 return m_IdMap.Save(m_MapFilename.c_str());
101 // cycle through the map and search the state table for each rid,
102 // and remove any that do not exist
103 void DatabaseSyncState::CleanupMap()
105 idmap::iterator i = m_IdMap.begin();
106 for( ; i != m_IdMap.end(); ++i ) {
107 if( !m_Table.GetIndex(i->second) ) {
108 // Record Id does not exist in state table, so it is
109 // not needed anymore in the ID map
110 m_IdMap.Unmap(i);
116 // Map2Uid
118 /// Searches for the given record ID, and returns the mapped UID. If not
119 /// found, it creates a new UID and returns it without mapping it.
121 std::string DatabaseSyncState::Map2Uid(uint32_t recordId) const
123 // search the idmap for the UID
124 std::string uid;
125 idmap::const_iterator mapped_id;
126 if( m_IdMap.RidExists(recordId, &mapped_id) ) {
127 uid = mapped_id->first;
129 else {
130 // not mapped, map it ourselves
131 char *puid = g_strdup_printf("%s-%u", m_Desc.c_str(), recordId);
132 uid = puid;
133 g_free(puid);
135 return uid;
138 unsigned long DatabaseSyncState::GetMappedRecordId(const std::string &uid)
140 Trace trace("DatabaseSyncState::GetMappedRecordId()", m_Desc.c_str());
142 // if already in map, use the matching rid
143 idmap::const_iterator it;
144 if( m_IdMap.UidExists(uid, &it) ) {
145 trace.logf("found existing uid in map: %lu", it->second);
146 return it->second;
149 // nothing in the map, so try to convert the string to a number
150 unsigned long RecordId;
151 if( sscanf(uid.c_str(), "%lu", &RecordId) != 0 ) {
152 trace.logf("parsed uid as: %lu", RecordId);
153 if( m_IdMap.Map(uid, RecordId) != m_IdMap.end() )
154 return RecordId;
156 trace.logf("parsed uid already exists in map, skipping");
159 // create one of our own, if we get here...
160 // do this in a loop to keep going until we find an ID that's unique
161 do {
162 RecordId = m_Table.MakeNewRecordId();
163 } while( m_IdMap.Map(uid, RecordId) == m_IdMap.end() );
165 trace.logf("made new record id: %lu", RecordId);
166 return RecordId;
171 //////////////////////////////////////////////////////////////////////////////
172 // BarryEnvironment Public API
174 BarryEnvironment::BarryEnvironment(OSyncMember *pm)
175 : member(pm),
176 m_pin(-1),
177 m_DebugMode(false),
178 m_password(""),
179 m_IConverter("UTF-8"),
180 m_pCon(0),
181 m_pDesktop(0),
182 m_CalendarSync(pm, "calendar"),
183 m_ContactsSync(pm, "contacts")
187 BarryEnvironment::~BarryEnvironment()
189 delete m_pDesktop;
190 delete m_pCon;
193 void BarryEnvironment::DoConnect()
195 // Create controller
197 // Override the timeout due to a firmware issue... sometimes
198 // the firmware will hang during a Reconnect, and fail to
199 // respond to a Desktop::Open(). To work around this, we
200 // set the default timeout to 15 seconds so that we find this
201 // failure early enough to fix it within opensync's 30 second timeout.
202 // Then if we get such a timeout, we do the Reconnect again and
203 // hope for the best... this often fixes it.
205 if( !m_ProbeResult.get() )
206 throw std::logic_error("Tried to use empty ProbeResult");
207 m_pCon = new Barry::Controller(*m_ProbeResult, 15000);
208 m_pDesktop = new Barry::Mode::Desktop(*m_pCon, m_IConverter);
209 m_pDesktop->Open(m_password.c_str());
211 // Save the DBIDs and DBNames of the databases we will work with
212 if( m_CalendarSync.m_Sync ) {
213 m_CalendarSync.m_dbName = Barry::Calendar::GetDBName();
214 m_CalendarSync.m_dbId = m_pDesktop->GetDBID(Barry::Calendar::GetDBName());
217 if( m_ContactsSync.m_Sync ) {
218 m_ContactsSync.m_dbId = m_pDesktop->GetDBID(Barry::Contact::GetDBName());
219 m_ContactsSync.m_dbName = Barry::Contact::GetDBName();
223 void BarryEnvironment::Connect(const Barry::ProbeResult &result)
225 Disconnect();
227 // save result in case we need to reconnect later
228 m_ProbeResult.reset( new Barry::ProbeResult(result) );
230 DoConnect();
233 void BarryEnvironment::Reconnect()
235 int tries = 0;
237 while(1) try {
239 tries++;
241 Disconnect();
243 // let the device settle... this seems to help prevent the
244 // firmware hang, and therefore ultimately speeds up the sync
245 sleep(1);
247 // FIXME - temporary fix for odd reconnect message... without this
248 // probe, the reconnect will often fail on newer Blackberries
249 // due to an unexpected close socket message. It is unclear
250 // if this is really a message from the device, but until then,
251 // we add this probe.
253 Barry::Probe probe;
254 int i = probe.FindActive(m_ProbeResult->m_pin);
255 if( i != -1 )
256 m_ProbeResult.reset( new Barry::ProbeResult(probe.Get(i)) );
259 DoConnect();
261 return;
263 catch( Usb::Timeout & ) {
264 if( tries > 1 ) {
265 throw;
267 else {
268 std::cout << "Timeout in Reconnect()... trying again" << std::endl;
273 void BarryEnvironment::Disconnect()
275 delete m_pDesktop;
276 m_pDesktop = 0;
278 delete m_pCon;
279 m_pCon = 0;
282 void BarryEnvironment::ClearDirtyFlags(Barry::RecordStateTable &table,
283 const std::string &dbname)
285 Trace trace("ClearDirtyFlags");
287 unsigned int dbId = m_pDesktop->GetDBID(dbname);
289 Barry::RecordStateTable::StateMapType::const_iterator i = table.StateMap.begin();
290 for( ; i != table.StateMap.end(); ++i ) {
291 if( i->second.Dirty ) {
292 trace.logf("Clearing dirty flag for db %u, index %u",
293 dbId, i->first);
294 m_pDesktop->ClearDirty(dbId, i->first);
299 void BarryEnvironment::ClearCalendarDirtyFlags()
301 Trace trace("ClearCalendarDirtyFlags");
302 ClearDirtyFlags(m_CalendarSync.m_Table, Barry::Calendar::GetDBName());
305 void BarryEnvironment::ClearContactsDirtyFlags()
307 Trace trace("ClearContactsDirtyFlags");
308 ClearDirtyFlags(m_ContactsSync.m_Table, Barry::Contact::GetDBName());
311 DatabaseSyncState* BarryEnvironment::GetSyncObject(OSyncChange *change)
313 Trace trace("BarryEnvironment::GetSyncObject()");
315 OSyncObjType *type = osync_change_get_objtype(change);
316 const char *name = osync_objtype_get_name(type);
317 if( strcmp(name, "event") == 0 ) {
318 return &m_CalendarSync;
320 else if( strcmp(name, "contact") == 0 ) {
321 return &m_ContactsSync;
323 else {
324 return 0;
328 void BarryEnvironment::ParseConfig(const char *data, int size)
330 Trace trace("ParseConfig");
332 m_ConfigData.assign(data, size);
334 // The config data should contain:
335 // - Keyword: DebugMode
336 // - if the single word "DebugMode" is found, enable Debug
338 // - Keyword: Device <pin> ...
339 // - PIN of device to sync with
340 // - or a flag that says "autoconfig with first device found"
341 // which will autodetect, and update the config
342 // automatically with the found PIN... all future syncs
343 // will then have a PIN
344 // - checkboxes for (both can be on):
345 // - sync calendar items
346 // - sync contacts
348 std::istringstream iss(m_ConfigData);
349 std::string line;
350 while( std::getline(iss, line) ) {
352 if( line[0] == '#' )
353 continue;
355 std::istringstream ils(line);
356 int cal = 0, con = 0;
358 std::string key;
359 ils >> key;
361 if( key == "DebugMode" ) {
362 m_DebugMode = true;
364 else if( key == "Device" ) {
365 ils >> std::hex >> m_pin >> cal >> con;
367 std::ostringstream oss;
368 oss << std::hex << m_pin;
369 trace.log(oss.str().c_str());
371 if( cal ) {
372 m_CalendarSync.m_Sync = true;
373 trace.log("calendar syncing enabled");
376 if( con ) {
377 m_ContactsSync.m_Sync = true;
378 trace.log("contacts syncing enabled");
381 else if ( key == "Password" ) {
382 ils >> m_password;
383 trace.log("using password from config file");
388 //void BarryEnvironment::BuildConfig()
390 // FIXME - build back into one long string