OpenSync 0.22
|
00001 /* 00002 * libosengine - A synchronization engine for the opensync framework 00003 * Copyright (C) 2004-2005 Armin Bauer <armin.bauer@opensync.org> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Lesser General Public 00007 * License as published by the Free Software Foundation; either 00008 * version 2.1 of the License, or (at your option) any later version. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Lesser General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public 00016 * License along with this library; if not, write to the Free Software 00017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00018 * 00019 */ 00020 00021 #include "engine.h" 00022 #include "engine_internals.h" 00023 00028 00029 static OSyncMappingEntry *_osync_find_next_diff(OSyncMapping *mapping, OSyncMappingEntry *orig_entry) 00030 { 00031 GList *e; 00032 for (e = mapping->entries; e; e = e->next) { 00033 OSyncMappingEntry *entry = e->data; 00034 if (osync_change_get_changetype(entry->change) == CHANGE_UNKNOWN) 00035 continue; 00036 if ((entry->change != orig_entry->change) && osync_change_compare(orig_entry->change, entry->change) != CONV_DATA_SAME) 00037 return entry; 00038 } 00039 osync_debug("MAP", 3, "Could not find next diff"); 00040 return NULL; 00041 } 00042 00043 static OSyncMappingEntry *_osync_find_next_same(OSyncMapping *mapping, OSyncMappingEntry *orig_entry) 00044 { 00045 GList *e; 00046 for (e = mapping->entries; e; e = e->next) { 00047 OSyncMappingEntry *entry = e->data; 00048 if (osync_change_get_changetype(entry->change) == CHANGE_UNKNOWN) 00049 continue; 00050 if ((entry->change != orig_entry->change) && osync_change_compare(orig_entry->change, entry->change) == CONV_DATA_SAME) 00051 return entry; 00052 } 00053 osync_debug("MAP", 3, "Could not find next diff"); 00054 return NULL; 00055 } 00056 00057 static OSyncMappingEntry *_osync_change_clone(OSyncEngine *engine, OSyncMapping *new_mapping, OSyncMappingEntry *comp_entry) 00058 { 00059 OSyncMappingEntry *newentry = osengine_mappingentry_new(NULL); 00060 newentry->change = osync_change_new(); 00061 newentry->client = comp_entry->client; 00062 osengine_mapping_add_entry(new_mapping, newentry); 00063 osengine_mappingview_add_entry(comp_entry->view, newentry); 00064 osengine_mappingentry_update(newentry, comp_entry->change); 00065 osync_change_set_uid(newentry->change, osync_change_get_uid(comp_entry->change)); 00066 osync_flag_set(newentry->fl_has_data); 00067 osync_flag_set(newentry->fl_mapped); 00068 osync_flag_set(newentry->fl_has_info); 00069 osync_flag_set(newentry->fl_dirty); 00070 osync_flag_unset(newentry->fl_synced); 00071 osync_change_save(newentry->change, TRUE, NULL); 00072 return newentry; 00073 } 00074 00075 osync_bool osync_change_elevate(OSyncEngine *engine, OSyncChange *change, int level) 00076 { 00077 osync_debug("MAP", 3, "elevating change %s (%p) to level %i", osync_change_get_uid(change), change, level); 00078 int i = 0; 00079 for (i = 0; i < level; i++) { 00080 if (!osync_change_duplicate(change)) 00081 return FALSE; 00082 } 00083 osync_debug("MAP", 3, "change after being elevated %s (%p)", osync_change_get_uid(change), change); 00084 osync_change_save(change, TRUE, NULL); 00085 return TRUE; 00086 } 00087 00088 osync_bool osync_change_check_level(OSyncEngine *engine, OSyncMappingEntry *entry) 00089 { 00090 GList *c; 00091 osync_debug("MAP", 3, "checking level for change %s (%p)", osync_change_get_uid(entry->change), entry); 00092 for (c = engine->clients; c; c = c->next) { 00093 OSyncClient *client = c->data; 00094 OSyncMappingView *view = osengine_mappingtable_find_view(engine->maptable, client->member); 00095 if (!osengine_mappingview_uid_is_unique(view, entry, TRUE)) 00096 return FALSE; 00097 } 00098 return TRUE; 00099 } 00100 00101 static int prod(int n) 00102 { 00103 int ret; 00104 for (ret = 0; n > 0; n--) 00105 ret += n; 00106 return ret; 00107 } 00108 00109 void osengine_mapping_multiply_master(OSyncEngine *engine, OSyncMapping *mapping) 00110 { 00111 osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, engine, mapping); 00112 g_assert(engine); 00113 00114 OSyncMappingTable *table = engine->maptable; 00115 OSyncMappingEntry *entry = NULL; 00116 OSyncMappingEntry *master = NULL; 00117 00118 master = mapping->master; 00119 g_assert(master); 00120 if (osync_flag_is_not_set(master->fl_dirty)) 00121 osync_flag_set(master->fl_synced); 00122 else 00123 osync_flag_attach(master->fl_committed, table->engine->cmb_committed_all); 00124 00125 //Send the change to every source that is different to the master source and set state to writing in the changes 00126 GList *v; 00127 for (v = table->views; v; v = v->next) { 00128 OSyncMappingView *view = v->data; 00129 //Check if this client is already listed in the mapping 00130 entry = osengine_mapping_find_entry(mapping, NULL, view); 00131 if (entry == master) 00132 continue; 00133 if (entry && (osync_change_compare(entry->change, master->change) == CONV_DATA_SAME)) { 00134 if (osync_flag_is_not_set(entry->fl_dirty)) 00135 osync_flag_set(entry->fl_synced); 00136 continue; 00137 } 00138 if (!entry) { 00139 entry = osengine_mappingentry_new(NULL); 00140 entry->change = osync_change_new(); 00141 entry->client = view->client; 00142 osengine_mappingview_add_entry(view, entry); 00143 osengine_mappingentry_update(entry, master->change); 00144 osync_change_set_uid(entry->change, osync_change_get_uid(master->change)); 00145 osync_change_set_member(entry->change, view->client->member); 00146 osengine_mapping_add_entry(mapping, entry); 00147 } else { 00148 osync_bool had_data = osync_change_has_data(entry->change); 00149 osengine_mappingentry_update(entry, master->change); 00150 if (osync_change_get_changetype(entry->change) == CHANGE_ADDED || osync_change_get_changetype(entry->change) == CHANGE_UNKNOWN) { 00151 osync_change_set_changetype(entry->change, CHANGE_MODIFIED); 00152 } 00153 00154 if (osync_member_get_slow_sync(view->client->member, osync_objtype_get_name(osync_change_get_objtype(entry->change))) && !had_data) { 00155 osync_change_set_changetype(entry->change, CHANGE_ADDED); 00156 } 00157 } 00158 if (osync_flag_is_set(view->client->fl_sent_changes)) { 00159 //osync_change_flags_attach(change, mapping); 00160 osync_flag_set(entry->fl_dirty); 00161 osync_flag_set(entry->fl_has_data); 00162 osync_flag_set(entry->fl_mapped); 00163 osync_flag_set(entry->fl_has_info); 00164 osync_flag_unset(entry->fl_synced); 00165 OSyncError *error = NULL; 00166 osync_change_save(entry->change, TRUE, &error); 00167 osync_flag_attach(entry->fl_committed, table->engine->cmb_committed_all); 00168 } 00169 } 00170 00171 OSyncError *error = NULL; 00172 osync_change_save(master->change, TRUE, &error); 00173 00174 osync_flag_set(mapping->fl_multiplied); 00175 osync_trace(TRACE_EXIT, "%s", __func__); 00176 } 00177 00178 00179 00180 void osengine_mapping_check_conflict(OSyncEngine *engine, OSyncMapping *mapping) 00181 { 00182 osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, engine, mapping); 00183 GList *e; 00184 GList *n; 00185 osync_bool is_conflict = FALSE; 00186 int is_same = 0; 00187 OSyncMappingEntry *leftentry = NULL; 00188 OSyncMappingEntry *rightentry = NULL; 00189 00190 g_assert(engine != NULL); 00191 g_assert(mapping != NULL); 00192 g_assert(!mapping->master); 00193 00194 for (e = mapping->entries; e; e = e->next) { 00195 leftentry = e->data; 00196 if (osync_change_get_changetype(leftentry->change) == CHANGE_UNKNOWN) 00197 continue; 00198 mapping->master = leftentry; 00199 for (n = e->next; n; n = n->next) { 00200 rightentry = n->data; 00201 if (osync_change_get_changetype(rightentry->change) == CHANGE_UNKNOWN) 00202 continue; 00203 00204 if (osync_change_compare(leftentry->change, rightentry->change) != CONV_DATA_SAME) { 00205 is_conflict = TRUE; 00206 goto conflict; 00207 } else { 00208 is_same++; 00209 } 00210 } 00211 } 00212 00213 conflict: 00214 if (is_conflict) { 00215 //conflict, solve conflict 00216 osync_debug("MAP", 2, "Got conflict for mapping %p", mapping); 00217 osync_status_conflict(engine, mapping); 00218 osync_flag_set(mapping->fl_chkconflict); 00219 osync_trace(TRACE_EXIT, "%s: Got conflict", __func__); 00220 return; 00221 } 00222 g_assert(mapping->master); 00223 osync_flag_set(mapping->fl_chkconflict); 00224 00225 //Our mapping is already solved since there is no conflict 00226 osync_flag_set(mapping->fl_solved); 00227 00228 if (is_same == prod(g_list_length(engine->maptable->views) - 1)) { 00229 osync_trace(TRACE_INTERNAL, "No need to sync. All entries are the same"); 00230 osync_flag_set(mapping->cmb_synced); 00231 osync_flag_set(mapping->fl_multiplied); 00232 } 00233 00234 send_mapping_changed(engine, mapping); 00235 osync_trace(TRACE_EXIT, "%s: No conflict", __func__); 00236 } 00237 00238 static OSyncMapping *_osengine_mapping_find(OSyncMappingTable *table, OSyncMappingEntry *orig_entry) 00239 { 00240 GList *i; 00241 GList *n; 00242 osync_bool mapping_found = FALSE; 00243 00244 for (i = table->mappings; i; i = i->next) { 00245 OSyncMapping *mapping = i->data; 00246 //We only need mapping where our member isnt listed yet. 00247 if (!osengine_mapping_find_entry(mapping, NULL, orig_entry->view)) { 00248 mapping_found = TRUE; 00249 for (n = mapping->entries; n; n = n->next) { 00250 OSyncMappingEntry *entry = n->data; 00251 if (osync_change_compare_data(entry->change, orig_entry->change) == CONV_DATA_MISMATCH) { 00252 mapping_found = FALSE; 00253 continue; 00254 } 00255 } 00256 if (mapping_found) 00257 return mapping; 00258 } 00259 } 00260 return NULL; 00261 } 00262 00263 void osengine_change_map(OSyncEngine *engine, OSyncMappingEntry *entry) 00264 { 00265 osync_trace(TRACE_ENTRY, "osengine_change_map(%p, %p)", engine, entry); 00266 OSyncMapping *mapping = NULL; 00267 if (!(mapping = _osengine_mapping_find(engine->maptable, entry))) { 00268 mapping = osengine_mapping_new(engine->maptable); 00269 osync_flag_unset(mapping->fl_chkconflict); 00270 osync_flag_unset(mapping->fl_multiplied); 00271 mapping->id = osengine_mappingtable_get_next_id(engine->maptable); 00272 osync_trace(TRACE_INTERNAL, "No previous mapping found. Creating new one: %p", mapping); 00273 } 00274 osengine_mapping_add_entry(mapping, entry); 00275 osync_flag_set(entry->fl_mapped); 00276 osync_change_save(entry->change, FALSE, NULL); 00277 osync_trace(TRACE_EXIT, "osengine_change_map"); 00278 } 00279 00289 00296 void osengine_mapping_duplicate(OSyncEngine *engine, OSyncMapping *dupe_mapping) 00297 { 00298 osync_trace(TRACE_ENTRY, "osengine_mapping_duplicate(%p, %p)", engine, dupe_mapping); 00299 g_assert(dupe_mapping); 00300 int elevation = 0; 00301 OSyncMappingEntry *orig_entry = NULL; 00302 OSyncMappingEntry *first_diff_entry = NULL; 00303 OSyncMappingEntry *next_entry = NULL; 00304 OSyncMapping *new_mapping = NULL; 00305 00306 //Remove all deleted items first. 00307 GList *entries, *e; 00308 entries = g_list_copy(dupe_mapping->entries); 00309 for (e = entries; e; e = e->next) { 00310 OSyncMappingEntry *entry = e->data; 00311 if (osync_change_get_changetype(entry->change) == CHANGE_DELETED) { 00312 osync_change_delete(entry->change, NULL); 00313 osengine_mappingentry_free(entry); 00314 } 00315 } 00316 g_list_free(entries); 00317 00318 //Choose the first modified change as the master of the mapping to duplicate 00319 GList *i = dupe_mapping->entries; 00320 do { 00321 orig_entry = i->data; 00322 i = i->next; 00323 } while (osync_change_get_changetype(orig_entry->change) != CHANGE_MODIFIED && osync_change_get_changetype(orig_entry->change) != CHANGE_ADDED); 00324 dupe_mapping->master = orig_entry; 00325 osync_change_set_changetype(orig_entry->change, CHANGE_MODIFIED); 00326 00327 /* Now we go through the list of changes in the mapping to 00328 * duplicate and search for the next entry that is different 00329 * to our choosen master. This entry then has to be moved to a 00330 * new mapping along with all changes to are the same as this next 00331 * different change 00332 */ 00333 while ((first_diff_entry = _osync_find_next_diff(dupe_mapping, orig_entry))) { 00334 //We found a different change 00335 elevation = 0; 00336 new_mapping = osengine_mapping_new(engine->maptable); 00337 new_mapping->id = osengine_mappingtable_get_next_id(engine->maptable); 00338 osync_flag_unset(new_mapping->cmb_synced); 00339 osync_flag_set(new_mapping->fl_chkconflict); 00340 osync_flag_unset(new_mapping->fl_multiplied); 00341 osync_flag_set(new_mapping->fl_solved); 00342 send_mapping_changed(engine, new_mapping); 00343 osync_debug("MAP", 3, "Created new mapping for duplication %p with mappingid %lli", new_mapping, new_mapping->id); 00344 00345 /* Now we copy the change that differs, and set it as the master of the new 00346 * mapping.*/ 00347 OSyncMappingEntry *newentry = osengine_mappingentry_copy(first_diff_entry); 00348 new_mapping->master = newentry; 00349 osengine_mapping_add_entry(new_mapping, newentry); 00350 osync_change_set_changetype(newentry->change, CHANGE_ADDED); 00351 osync_flag_set(newentry->fl_has_data); 00352 osync_flag_set(newentry->fl_mapped); 00353 osync_flag_set(newentry->fl_has_info); 00354 osync_flag_set(newentry->fl_dirty); 00355 osync_flag_unset(newentry->fl_synced); 00356 00357 /* Now we elevate the change (which might be done by adding a -dupe 00358 * or a (2) to the change uid. We then check if there is already 00359 * another change on this level and if there is, we elevate again */ 00360 do { 00361 if (!osync_change_elevate(engine, newentry->change, 1)) 00362 break; 00363 elevation += 1; 00364 } while (!osync_change_check_level(engine, newentry)); 00365 OSyncError *error = NULL; 00366 osync_change_save(newentry->change, TRUE, &error); 00367 00368 /* Now we search for all changes to belong to the new mapping, so 00369 * we are searching for changes to do not differ from the change we found 00370 * to be different from the master of the mapping to duplicate */ 00371 while ((next_entry = _osync_find_next_same(dupe_mapping, first_diff_entry))) { 00372 newentry = _osync_change_clone(engine, new_mapping, first_diff_entry); 00373 osync_change_elevate(engine, newentry->change, elevation); 00374 osengine_mappingentry_update(orig_entry, next_entry->change); 00375 osync_change_save(next_entry->change, TRUE, NULL); 00376 } 00377 00378 /* Now we can reset the different change and prepare it for 00379 * being overwriten during mulitply_master */ 00380 osync_change_set_changetype(first_diff_entry->change, CHANGE_UNKNOWN); 00381 00382 //We can now add the new mapping into the queue so it get processed 00383 send_mapping_changed(engine, new_mapping); 00384 } 00385 00386 //Multiply our original mapping 00387 osync_flag_set(dupe_mapping->fl_solved); 00388 send_mapping_changed(engine, dupe_mapping); 00389 osync_trace(TRACE_EXIT, "osengine_mapping_duplicate"); 00390 } 00391 00401 void osengine_mapping_solve(OSyncEngine *engine, OSyncMapping *mapping, OSyncChange *change) 00402 { 00403 osync_trace(TRACE_ENTRY, "osengine_mapping_solve(%p, %p, %p)", engine, mapping, change); 00404 OSyncMappingEntry *entry = osengine_mapping_find_entry(mapping, change, NULL); 00405 mapping->master = entry; 00406 osync_flag_set(mapping->fl_solved); 00407 send_mapping_changed(engine, mapping); 00408 osync_trace(TRACE_EXIT, "osengine_mapping_solve"); 00409 } 00410 00420 osync_bool osengine_mapping_ignore_conflict(OSyncEngine *engine, OSyncMapping *mapping, OSyncError **error) 00421 { 00422 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, engine, mapping, error); 00423 00424 if (!osengine_mapping_ignore_supported(engine, mapping)) { 00425 osync_error_set(error, OSYNC_ERROR_GENERIC, "Ignore is not supported for this mapping"); 00426 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 00427 return FALSE; 00428 } 00429 00430 GList *e = NULL; 00431 for (e = mapping->entries; e; e = e->next) { 00432 OSyncMappingEntry *entry = e->data; 00433 osync_trace(TRACE_INTERNAL, "Adding %p to logchanges", entry); 00434 OSyncError *error = NULL; 00435 if (osync_change_get_changetype(entry->change) != CHANGE_UNKNOWN) 00436 osync_group_save_changelog(engine->group, entry->change, &error); 00437 } 00438 00439 //And make sure we dont synchronize it this time 00440 //osengine_mapping_reset(mapping); 00441 osync_flag_set(mapping->fl_multiplied); 00442 osync_flag_set(mapping->cmb_synced); 00443 osync_flag_set(mapping->cmb_has_info); 00444 osync_trace(TRACE_EXIT, "%s", __func__); 00445 return TRUE; 00446 } 00447 00461 osync_bool osengine_mapping_ignore_supported(OSyncEngine *engine, OSyncMapping *mapping) 00462 { 00463 osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, engine, mapping); 00464 00465 int i, count = 0; 00466 OSyncChange *change = NULL; 00467 OSyncMember *member = NULL; 00468 OSyncObjType *objtype = NULL; 00469 00470 count = osengine_mapping_num_changes(mapping); 00471 for (i = 0; i < count; ++i) { 00472 change = osengine_mapping_nth_change(mapping, i); 00473 objtype = osync_change_get_objtype(change); 00474 00475 member = osync_change_get_member(change); 00476 00477 if (!osync_member_has_read_function(member, objtype)) { 00478 osync_trace(TRACE_EXIT, "%s: Ignore NOT supported", __func__); 00479 return FALSE; 00480 } 00481 } 00482 00483 osync_trace(TRACE_EXIT, "%s: Ignore supported", __func__); 00484 return TRUE; 00485 } 00486 00499 osync_bool osengine_mapping_solve_latest(OSyncEngine *engine, OSyncMapping *mapping, OSyncError **error) 00500 { 00501 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, engine, mapping, error); 00502 00503 time_t time = 0; 00504 time_t latesttime = 0; 00505 osync_bool preveq = FALSE; 00506 00507 GList *e = NULL; 00508 for (e = mapping->entries; e; e = e->next) { 00509 OSyncMappingEntry *entry = e->data; 00510 00511 if (osync_change_get_changetype(entry->change) != CHANGE_UNKNOWN) { 00512 time = osync_change_get_revision(entry->change, error); 00513 if (time == -1) { 00514 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 00515 mapping->master = NULL; 00516 return FALSE; 00517 } 00518 00519 if (time > latesttime) { 00520 latesttime = time; 00521 mapping->master = entry; 00522 preveq = FALSE; 00523 } else if (time == latesttime) 00524 preveq = TRUE; 00525 } 00526 } 00527 00528 if (preveq == TRUE) { 00529 osync_error_set(error, OSYNC_ERROR_GENERIC, "Could not decide for one entry. Timestamps where equal"); 00530 mapping->master = NULL; 00531 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 00532 return FALSE; 00533 } 00534 00535 osync_flag_set(mapping->fl_solved); 00536 send_mapping_changed(engine, mapping); 00537 00538 osync_trace(TRACE_EXIT, "%s: %p", __func__, mapping->master); 00539 return TRUE; 00540 } 00541 00554 osync_bool osengine_mapping_check_timestamps(OSyncEngine *engine, OSyncMapping *mapping, OSyncError **error) 00555 { 00556 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, engine, mapping, error); 00557 00558 time_t time = 0; 00559 time_t latesttime = 0; 00560 osync_bool preveq = FALSE; 00561 00562 GList *e = NULL; 00563 for (e = mapping->entries; e; e = e->next) { 00564 OSyncMappingEntry *entry = e->data; 00565 00566 if (osync_change_get_changetype(entry->change) != CHANGE_UNKNOWN) { 00567 time = osync_change_get_revision(entry->change, error); 00568 if (time == -1) { 00569 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 00570 return FALSE; 00571 } 00572 00573 if (time > latesttime) { 00574 latesttime = time; 00575 preveq = FALSE; 00576 } else if (time == latesttime) 00577 preveq = TRUE; 00578 } 00579 } 00580 00581 if (preveq == TRUE) { 00582 osync_error_set(error, OSYNC_ERROR_GENERIC, "Could not decide for one entry. Timestamps where equal"); 00583 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 00584 return FALSE; 00585 } 00586 00587 osync_trace(TRACE_EXIT, "%s", __func__); 00588 return TRUE; 00589 } 00590 00601 void osengine_mapping_solve_updated(OSyncEngine *engine, OSyncMapping *mapping, OSyncChange *change) 00602 { 00603 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, engine, mapping, change); 00604 OSyncMappingEntry *entry = osengine_mapping_find_entry(mapping, change, NULL); 00605 mapping->master = entry; 00606 00607 osync_flag_set(entry->fl_dirty); 00608 osync_flag_unset(entry->fl_synced); 00609 send_mappingentry_changed(engine, entry); 00610 00611 osync_flag_set(mapping->fl_solved); 00612 send_mapping_changed(engine, mapping); 00613 osync_trace(TRACE_EXIT, "%s", __func__); 00614 } 00615