After some weeks hacking, multisync is now usable for me. I am using T68, Evolution and Palm on Debian Sarge with chinese GBK environment, and I hope multisync will be my final solution for sync PIM data with different devices.
Here are my patches: 02-palm_category.patch -- see http://sourceforge.net/mailarchive/forum.php?thread_id=5437490&forum_id=12899 03-utf8.patch -- see http://sourceforge.net/mailarchive/forum.php?thread_id=5092577&forum_id=31160 04-evo_utf8_char_edge.patch -- line of records got from evoluion will be wrapped if too long, but evolution does care nothing about the edge of characters which has multi-bytes encoding, such as UTF8, if wrap occured in the middle of one such character, multisync will crash because invalid UTF8 character, so I add this patch to join all wrapped lines before do any processing. 05-evo_lost_records.patch -- see http://sourceforge.net/mailarchive/forum.php?thread_id=5319816&forum_id=31160 06-evo_work_fax_entry.patch -- sync T68 to Evolution, phone with label 'Work' on T68 will save to 'Work' entry on Evolution, that's ok, but this entry will never sync to Palm, instead, multisync sync 'Business' entry on Evolution to Palm as 'Work', this patch convert all 'Work' entry from T68 to 'Business' and 'Fax' to 'Business Fax'. 07-irmc_cant_remove_rec.patch -- a deleted record on T68 will not be deleted after synchronize and just become an empty record on Evolution 08-multi_filter.patch -- filter now support multiple items with comma separated, such as category list 09-evo_preserve_categories.patch -- if some records was modified on T68, after sync with Evolution, all categories info on Evolution will be lost -- same problem with 'Note' entry on Evolution and Palm, because there is no such entry on T68, so, if a record was modified on T68 and then sync back, this entry will be lost. I will try to fix this if I have time. 10-evo_default_category.patch -- add default category 'Unfiled' to any changed records if no category was specified, because I have no idea to find out the records which has no category on Evolution's addressbook
diff -ur --exclude=CVS multisync.cvs.org/plugins/palm_sync/src/palm_sync.c multisync.cvs/plugins/palm_sync/src/palm_sync.c --- multisync.cvs.org/plugins/palm_sync/src/palm_sync.c 2004-04-15 23:46:35.000000000 +0800 +++ multisync.cvs/plugins/palm_sync/src/palm_sync.c 2004-08-15 23:18:06.000000000 +0800 @@ -701,18 +701,6 @@ strcpy(database, detectDB(conn, comp)); palm_debug(conn, 2, "Detected vcard to belong to %s", database); - //Convert the vcard to our palm entry - if (!strcmp(database, "AddressDB")) { - vcard2address(conn, &entry, comp); - l = pack_Address(&entry.address, buffer, sizeof(buffer)); - } else if (!strcmp(database, "DatebookDB")) { - vevent2calendar(conn, &entry, comp); - l = pack_Appointment(&entry.appointment, buffer, sizeof(buffer)); - } else { - vcal2todo(conn, &entry, comp); - l = pack_ToDo(&(entry.todo), buffer, sizeof(buffer)); - } - //open the DB ret = openDB(conn, database); if (ret == -2) { @@ -737,6 +725,18 @@ palm_debug(conn, 2, "Created Calendar."); } + //Convert the vcard to our palm entry + if (!strcmp(database, "AddressDB")) { + vcard2address(conn, &entry, comp); + l = pack_Address(&entry.address, buffer, sizeof(buffer)); + } else if (!strcmp(database, "DatebookDB")) { + vevent2calendar(conn, &entry, comp); + l = pack_Appointment(&entry.appointment, buffer, sizeof(buffer)); + } else { + vcal2todo(conn, &entry, comp); + l = pack_ToDo(&(entry.todo), buffer, sizeof(buffer)); + } + if (uid) { int ret = 0; //Modify a entry
diff -urN multisync-0.82.org/src/sync_vtype.c multisync-0.82/src/sync_vtype.c --- multisync-0.82.org/src/sync_vtype.c 2004-04-13 05:03:29.000000000 +0800 +++ multisync-0.82/src/sync_vtype.c 2004-07-10 12:54:38.000000000 +0800 @@ -61,6 +61,46 @@ VTYPE_VCARD = 0xc } vtype_type; +// convert charset of value from which specified in properties +static void +convert_charset (char* line, unsigned char** value, const char* charset) +{ + while (line = strchr (line, ';')) { + const char* key = "CHARSET="; + char org_charset[64]; + int len; + iconv_t ic; + + line++; // skip the ';' character + + if ((strlen (line) <= strlen (key)) + || g_strncasecmp (line, key, sizeof (key))) + // key not found + continue; + + org_charset[0] = 0; + len = strcspn (line + strlen (key), ";: \r\n"); + if (len >= sizeof (org_charset)) + len = sizeof (org_charset) - 1; + strncat (org_charset, line + strlen (key), len); + + ic = iconv_open (charset, org_charset); + if (ic >= 0) { + char *utfvalue = g_malloc0(65536); + size_t inbytes = strlen(*value); + size_t outbytes = 65536; + char *inbuf = *value, *outbuf = utfvalue; + + iconv(ic, &inbuf, &inbytes, &outbuf, &outbytes); + g_free(*value); + *value = utfvalue; + iconv_close(ic); + } + + break; + } // while every ';' character +} + // Parse and correct a number of errors in the VCARD/VTODO data // The "opts" parameter decides which errors/features that should be corrected char* sync_vtype_convert(char *card, sync_voption opts, char* charset) { @@ -136,6 +176,7 @@ value = tmp; } qp = FALSE; // Do a proper QP detection + convert_charset (line, &value, "UTF-8"); if (endent) endent[0] = 0; strncpy(name, line, 255); @@ -312,7 +353,10 @@ !g_strcasecmp(propdata, "END")) triggerrelatedend = TRUE; if (!g_strcasecmp(propname, "CHARSET")) + { strncpy(linecharset, propdata, 255); + outputprop = FALSE; // value has been converted to UTF-8 + } if (adr && (!g_strcasecmp(propname, "HOME") || !g_strcasecmp(propname, "WORK"))) { @@ -362,7 +406,7 @@ value = sync_vtype_decode_qp(value); g_free(tmp); } - if ((opts & VOPTION_FIXCHARSET) && value && charset) { + if ((opts & VOPTION_FIXCHARSET) && value && charset && !linecharset[0]) { iconv_t ic; int t; gboolean highchar = FALSE; @@ -1132,6 +1176,7 @@ res = tmp; start = newstart; } while(start < card+strlen(card) && start[0] == ' '); + convert_charset (pos, (unsigned char**)&res, "UTF-8"); return(res); } }
diff -ur org/plugins/evolution_sync/src/evolution_sync.c org.new/plugins/evolution_sync/src/evolution_sync.c --- org/plugins/evolution_sync/src/evolution_sync.c 2004-09-02 16:33:29.000000000 +0800 +++ org.new/plugins/evolution_sync/src/evolution_sync.c 2004-09-02 16:34:04.000000000 +0800 @@ -46,6 +46,17 @@ extern gboolean multisync_debug; +static char* +join_wrapped_lines (char* str) { + char* p; + p = str; + while (p && (p = strstr (p, "\n "))) { + strcpy (p, p + 2); + } + + return str; +} + void evo_load_state(evolution_connection *conn) { char *filename; FILE *f; @@ -157,6 +168,7 @@ change->comp = g_strdup_printf("BEGIN:VCALENDAR\r\nVERSION:2.0\r\n%s" "END:VCALENDAR\r\n", cal_component_get_as_string(comp)); + join_wrapped_lines (change->comp); if (clientchange->type & CAL_CLIENT_CHANGE_DELETED) change->change_type = SYNC_OBJ_HARDDELETED; else if (clientchange->type & CAL_CLIENT_CHANGE_MODIFIED) @@ -206,6 +218,7 @@ change->comp = g_strdup_printf("BEGIN:VCALENDAR\r\nVERSION:2.0\r\n%s" "END:VCALENDAR\r\n", cal_component_get_as_string(comp)); + join_wrapped_lines (change->comp); if (clientchange->type & CAL_CLIENT_CHANGE_DELETED) change->change_type = SYNC_OBJ_HARDDELETED; else if (clientchange->type & CAL_CLIENT_CHANGE_MODIFIED) @@ -339,6 +352,7 @@ change->comp = g_strdup_printf("BEGIN:VCALENDAR\r\nVERSION:2.0\r\n%s" "END:VCALENDAR\r\n", cal_component_get_as_string(comp)); + join_wrapped_lines (change->comp); change->change_type = SYNC_OBJ_MODIFIED; change->object_type = object_type_from_component(comp); dtend = icalcomponent_get_first_property(icalcomp, @@ -383,6 +397,7 @@ change->comp = g_strdup_printf("BEGIN:VCALENDAR\r\nVERSION:2.0\r\n%s" "END:VCALENDAR\r\n", cal_component_get_as_string(comp)); + join_wrapped_lines (change->comp); change->change_type = SYNC_OBJ_MODIFIED; change->object_type = object_type_from_component(comp); changes = evo_append_change(changes, change);
diff -ur org/plugins/evolution_sync/src/addr_sync.c org.new/plugins/evolution_sync/src/addr_sync.c --- org/plugins/evolution_sync/src/addr_sync.c 2004-09-02 16:35:30.000000000 +0800 +++ org.new/plugins/evolution_sync/src/addr_sync.c 2004-09-02 16:37:07.000000000 +0800 @@ -35,6 +35,11 @@ extern gboolean multisync_debug; +typedef struct { + syncobj_modify_result *result; + evolution_connection *conn; +} modify_result_t; + // Returns the UID of a vcard as a g_malloced string. char* evo_addr_get_uid(char *vcard) { char *pos = vcard; @@ -281,10 +286,10 @@ void evo_addr_add_cb (EBook *book, EBookStatus status, const char *id, gpointer data) { - evolution_connection *conn = data; + modify_result_t* modify_result = data; + if (status == E_BOOK_STATUS_SUCCESS) { - syncobj_modify_result *result = g_list_nth_data(conn->modify_results, - conn->modify_no); + syncobj_modify_result *result = modify_result->result; if (result) { if (result->returnuid) g_free(result->returnuid); @@ -292,8 +297,9 @@ result->result = SYNC_MSG_REQDONE; } } else { - evo_addr_modify_next(conn, FALSE); + evo_addr_modify_next(modify_result->conn, FALSE); } + g_free (modify_result); } @@ -400,7 +406,11 @@ g_free(tmp); conn->addr_mode = EVO_ADDR_MODE_MODIFIED; if (!obj->uid || tryadd) { // Add new card - e_book_add_vcard (conn->ebook, newcard, evo_addr_add_cb, conn); + modify_result_t* modify_result = g_malloc (sizeof (modify_result_t)); + modify_result->result = g_list_nth_data(conn->modify_results, + conn->modify_no); + modify_result->conn = conn; + e_book_add_vcard (conn->ebook, newcard, evo_addr_add_cb, modify_result); } else { // Modify a card e_book_commit_vcard (conn->ebook, newcard, evo_addr_modify_cb, conn); }
diff -ur org/plugins/evolution_sync/src/addr_sync.c org.new/plugins/evolution_sync/src/addr_sync.c --- org/plugins/evolution_sync/src/addr_sync.c 2004-09-02 16:57:30.000000000 +0800 +++ org.new/plugins/evolution_sync/src/addr_sync.c 2004-09-02 16:57:59.000000000 +0800 @@ -404,6 +404,21 @@ char *tmp = sync_vtype_convert(obj->comp, VOPTION_REMOVEPHOTO, NULL); char *newcard = evo_addr_set_uid(tmp, obj->uid); g_free(tmp); + char *p; + if (p = strstr (newcard, "\nTEL;WORK:")) { + // replace TEL;WORK with TEL;WORK;VOICE + *p = 0; + p = g_strconcat (newcard, "\nTEL;WORK;VOICE:", p + 10, NULL); + g_free (newcard); + newcard = p; + } + if (p = strstr (newcard, "\nTEL;FAX:")) { + // replace TEL;FAX with TEL;WORK;FAX + *p = 0; + p = g_strconcat (newcard, "\nTEL;WORK;FAX:", p + 9, NULL); + g_free (newcard); + newcard = p; + } conn->addr_mode = EVO_ADDR_MODE_MODIFIED; if (!obj->uid || tryadd) { // Add new card modify_result_t* modify_result = g_malloc (sizeof (modify_result_t));
diff --exclude=CVS -ur multisync.cvs.local/plugins/irmc_sync/src/irmc_sync.c multisync.cvs.debug1/plugins/irmc_sync/src/irmc_sync.c --- multisync.cvs.local/plugins/irmc_sync/src/irmc_sync.c 2004-08-25 16:24:16.000000000 +0800 +++ multisync.cvs.debug1/plugins/irmc_sync/src/irmc_sync.c 2004-08-25 16:30:24.000000000 +0800 @@ -662,7 +662,7 @@ change->change_type = SYNC_OBJ_SOFTDELETED; if (type == 'H') change->change_type = SYNC_OBJ_HARDDELETED; - if (type == 'M' || objlen == 0) + if (type == 'M' || (objlen == 0 && ret < 0)) change->change_type = SYNC_OBJ_MODIFIED; change->object_type = objtype; *sync_changes = g_list_append(*sync_changes, change);
diff -ur debug.org/src/syncengine.c debug/src/syncengine.c --- debug.org/src/syncengine.c 2004-09-03 15:07:34.000000000 +0800 +++ debug/src/syncengine.c 2004-09-03 15:09:01.000000000 +0800 @@ -1258,16 +1258,28 @@ gboolean match = FALSE; if (fielddata && filter->data) { char *pos = fielddata; + char* filter_data = g_strdup (filter->data); + dd (printf ("filter=[%s]\n", filter_data)); + dd (printf ("field=[%s]\n", fielddata)); while (pos) { char *nextword = strstr(pos, ","); if (nextword) { nextword[0] = 0; nextword++; } - if (!g_strcasecmp(pos, filter->data)) - match = TRUE; + strcpy (filter_data, filter->data); + char *f = strtok (filter_data, ","); + while (f) { + dd (printf ("compare field.[%s] with filter.[%s]\n", pos, f)); + if (!g_strcasecmp(pos, f)) { + dd (printf ("match!\n")); + match = TRUE; + } + f = strtok (NULL, ","); + } pos = nextword; } + g_free(filter_data); g_free(fielddata); } if (filter->rule == SYNC_RULE_NONE)
diff -ur org.new/plugins/evolution_sync/src/evolution_sync.c cur/plugins/evolution_sync/src/evolution_sync.c --- org.new/plugins/evolution_sync/src/evolution_sync.c 2004-09-02 16:56:42.000000000 +0800 +++ cur/plugins/evolution_sync/src/evolution_sync.c 2004-09-02 16:50:35.000000000 +0800 @@ -41,11 +41,17 @@ #include <ical.h> #include "addr_sync.h" #include "gui.h" +#include <e-util/e-categories-master-list-wombat.h> gboolean versionok = FALSE; extern gboolean multisync_debug; +typedef struct { + GList* node; + evolution_connection *conn; +} modify_node_t; + static char* join_wrapped_lines (char* str) { char* p; @@ -57,6 +63,244 @@ return str; } +static gboolean +category_exist_in_master_list (const char* comp) { + gboolean ret = FALSE; + ECategoriesMasterList *master_categories = NULL; + char* categories = NULL; + char* category; + + if (!comp) + return FALSE; + + if (categories = strstr (comp, "\nCATEGORIES:")) { + categories += 12; + categories = g_strndup (categories, strcspn (categories, "\r\n")); + while (category = strstr (categories, "\\,")) + strcpy (category, category + 1); + } + else + goto out; + + master_categories = e_categories_master_list_wombat_new (); + if (!master_categories) + goto out; + + category = strtok (categories, ","); + while (category) { + int c; + + dd (printf ("category_exist_in_master_list: category=%s\n", category)); + for (c = e_categories_master_list_count (master_categories); c > 0; c--) { + const char* master_category = e_categories_master_list_nth ( + master_categories, c - 1); + + dd (printf ("category_exist_in_master_list: master_category=%s\n", + master_category)); + if (strcmp (master_category, category) == 0) { + // found in master list + ret = TRUE; + dd (printf ("category_exist_in_master_list: match!\n")); + goto out; + } + } // for each category in master category list + + category = strtok (NULL, ","); + } // while each cateory in categories + +out: + if (master_categories) + g_object_unref (master_categories); + if (categories) + g_free (categories); + + return ret; +} + +static void +remove_non_master_categories (char* categories) { + ECategoriesMasterList *master_categories = NULL; + char* category; + char* delimiter = "\\,"; + + if (!categories) + return; + + master_categories = e_categories_master_list_wombat_new (); + if (!master_categories) + return; + + // some client use escaped ',' as delimiter, and some use literal ',' + if (strstr (categories, "\\,")) + delimiter = "\\,"; + else if (strstr (categories, ",")) + delimiter = ","; + + category = categories; + while (category && *category) { + int c; + char* category_end; + + category_end = strstr (category, delimiter); + if (!category_end) { + // set to the end of line if no delimit string found + category_end = category + strlen (category); + } + + dd (printf ("remove_non_master_categories: category=%s\n", category)); + for (c = e_categories_master_list_count (master_categories); c > 0; c--) { + const char* master_category = e_categories_master_list_nth ( + master_categories, c - 1); + + dd (printf ("remove_non_master_categories: master_category=%s\n", + master_category)); + if (category_end - category == strlen (master_category) && + strncmp (master_category, category, category_end - category) == 0) { + // found in master list + break; + } + } // for each category in master category list + + if (c == 0) { + // not found in master category list, remove it + if (*category_end) + strcpy (category, category_end + strlen (delimiter)); + else { + *category = 0; + // current is the last element, remove the delimit string of previous + if (category > categories) + // not the first + *(category - strlen (delimiter)) = 0; + break; // while + } + continue; + } + + if (!*category_end) + // reach the end of categories + break; + + category = category_end + strlen (delimiter); + } // while each cateory in categories + + g_object_unref (master_categories); +} + +// mergory categories from original object to new modified +static void +merge_categories (char** modified_comp, const char* orig_comp) { + char *orig_categories = NULL; + char *categories = NULL; + char *modified_comp_tail; + char *p; + char* delimiter = "\\,"; + + if (!(modified_comp && *modified_comp)) + return; + + if (!orig_comp) + return; + + // extrace the original categories if exist + if (p = strstr (orig_comp, "\nCATEGORIES:")) { + p += 12; + orig_categories = g_strndup (p, strcspn (p, "\r\n")); + remove_non_master_categories (orig_categories); + if (!*orig_categories) { + g_free (orig_categories); + orig_categories = NULL; + } + } + if (!orig_categories) + return; + + if (p = strstr (*modified_comp, "\nCATEGORIES:")) { + // extrace the new categories + *p = 0; + p += 12; + categories = g_strndup (p, strcspn (p, "\r\n")); + modified_comp_tail = p + strlen (categories); + modified_comp_tail += strspn (modified_comp_tail, "\r\n"); + } + else { + p = strstr (*modified_comp, "\nEND:"); + if (!p) { + // invalid card + return; + } + + modified_comp_tail = p + 1; + *p = 0; + } + + // some client use escaped ',' as delimiter, and some use literal ',' + if ((categories && strstr (categories, "\\,")) || + strstr (orig_categories, "\\,")) + delimiter = "\\,"; + else if ((categories && strstr (categories, ",")) || + strstr (orig_categories, ",")) + delimiter = ","; + + if (categories) { + // merge them all + p = g_strconcat (orig_categories, delimiter, categories, NULL); + g_free (categories); + g_free (orig_categories); + categories = p; + } + else { + categories = orig_categories; + } + + // remove duplicates + p = categories; + while (p) { + char *p_end = strstr (p, delimiter); + char *s; + + if (!p_end) + break; + + s = p_end + strlen (delimiter); + while (s) { + char *s_end = strstr (s, delimiter); + if (!s_end) { + // set to the end of line if no delimit string found + s_end = s + strlen (s); + } + if ((p_end - p == s_end - s) && strncmp (p, s, s_end - s) == 0) { + // same category, remove the current + if (*s_end) + strcpy (s, s_end + strlen (delimiter)); + else + { + *s = 0; + + // current is the last element, remove the delimit string of previous + *(s - strlen (delimiter)) = 0; + break; + } + continue; + } + + if (*s_end) + s = s_end + strlen (delimiter); + else + break; + } // while (s) + + // move to next category + p = p_end + strlen (delimiter); + } // remove duplicates + + // generate new card + p = g_strconcat (*modified_comp, + "\nCATEGORIES:", categories, "\n", modified_comp_tail, NULL); + g_free (categories); + g_free (*modified_comp); + *modified_comp = p; +} + void evo_load_state(evolution_connection *conn) { char *filename; FILE *f; @@ -900,6 +1144,28 @@ syncobj_modify_result *result = results->data; if (object->object_type == SYNC_OBJECT_TYPE_CALENDAR || object->object_type == SYNC_OBJECT_TYPE_TODO) { + if (object->comp && + object->uid && + object->change_type == SYNC_OBJ_MODIFIED && + !category_exist_in_master_list (object->comp)) { + // merge categories from original object + CalComponent *org_comp; + CalClientGetStatus r; + if (object->object_type == SYNC_OBJECT_TYPE_CALENDAR) + r = cal_client_get_object (conn->cal_client, object->uid, &org_comp); + if (object->object_type == SYNC_OBJECT_TYPE_TODO) + r = cal_client_get_object (conn->todo_client, object->uid, &org_comp); + if (r == CAL_CLIENT_GET_SUCCESS) { + char *org_comp_as_string; + org_comp_as_string = cal_component_get_as_string (org_comp); + g_object_unref (org_comp); + dd (printf ("org=[%s]\n", org_comp_as_string)); + dd (printf ("mod=[%s]\n", object->comp)); + merge_categories (&object->comp, org_comp_as_string); + dd (printf ("merged=[%s]\n", object->comp)); + g_free (org_comp_as_string); + } + } if (evo_cal_modify_one(conn, object, &(result->returnuid))) result->result = SYNC_MSG_REQDONE; } @@ -917,8 +1183,69 @@ g_idle_add(evo_get_changes, conn); // Remove known changes } +static void +evo_get_card_cb (EBook *book, EBookStatus status, ECard *card, gpointer closure) +{ + modify_node_t *modify_node = closure; + changed_object *obj = modify_node->node->data; + + if (status == E_BOOK_STATUS_SUCCESS) { + // merge categories + char *org_card; + // original card + org_card = e_card_get_vcard_assume_utf8 (card); + dd (printf ("org=[%s]\n", org_card)); + dd (printf ("mod=[%s]\n", obj->comp)); + // merge from new modified card + merge_categories (&obj->comp, org_card); + dd (printf ("merged=[%s]\n", obj->comp)); + g_free (org_card); + } + + // move to next node of modified phonebook + while (1) { + modify_node->node = modify_node->node->next; + + if (!modify_node->node) { + // no more node, finish call chain + g_idle_add (evo_addr_modify, modify_node->conn); + g_free (modify_node); + return; + } + + obj = modify_node->node->data; + if (obj->comp && + obj->uid && + obj->object_type == SYNC_OBJECT_TYPE_PHONEBOOK && + obj->change_type == SYNC_OBJ_MODIFIED && + !category_exist_in_master_list (obj->comp)) { + // continue next node + e_book_get_card (modify_node->conn->ebook, obj->uid, + evo_get_card_cb, modify_node); + break; + } + } // while 1 +} + void evo_cal_modify_done_cb(gpointer data, evolution_connection *conn) { + GList *node = conn->modify_objects; conn->callback = evo_addr_modify_done_cb; // Continue and modify addr + while (node) { + // search for the first modified node of phonebook + changed_object *obj = node->data; + if (obj->comp && + obj->uid && + obj->object_type == SYNC_OBJECT_TYPE_PHONEBOOK && + obj->change_type == SYNC_OBJ_MODIFIED && + !category_exist_in_master_list (obj->comp)) { + modify_node_t *modify_node = g_malloc (sizeof (modify_node_t)); + modify_node->node = node; + modify_node->conn = conn; + e_book_get_card (conn->ebook, obj->uid, evo_get_card_cb, modify_node); + return; + } + node = node->next; + } g_idle_add(evo_addr_modify, conn); }
diff -ur --exclude=CVS cur/plugins/evolution_sync/src/addr_sync.c debug/plugins/evolution_sync/src/addr_sync.c --- cur/plugins/evolution_sync/src/addr_sync.c 2004-09-02 16:18:55.000000000 +0800 +++ debug/plugins/evolution_sync/src/addr_sync.c 2004-09-02 21:10:46.000000000 +0800 @@ -419,6 +419,15 @@ g_free (newcard); newcard = p; } + if (!strstr (newcard, "\nCATEGORIES:")) { + // add default category + if (p = strstr (newcard, "\nEND:")) { + *p = 0; + p = g_strconcat (newcard, "\nCATEGORIES:Unfiled\n", p + 1, NULL); + g_free (newcard); + newcard = p; + } + } conn->addr_mode = EVO_ADDR_MODE_MODIFIED; if (!obj->uid || tryadd) { // Add new card modify_result_t* modify_result = g_malloc (sizeof (modify_result_t)); diff -ur --exclude=CVS cur/plugins/evolution_sync/src/evolution_sync.c debug/plugins/evolution_sync/src/evolution_sync.c --- cur/plugins/evolution_sync/src/evolution_sync.c 2004-09-02 17:04:03.000000000 +0800 +++ debug/plugins/evolution_sync/src/evolution_sync.c 2004-09-02 21:03:53.000000000 +0800 @@ -1102,6 +1102,9 @@ if (uidret) *uidret = g_strdup(uid); } + cal_component_get_categories (calcomp, (const char**)&tmp); + if (!tmp || !*tmp) + cal_component_set_categories (calcomp, "Unfiled"); if (obj->object_type == SYNC_OBJECT_TYPE_CALENDAR) res = cal_client_update_object (conn->cal_client, calcomp); if (obj->object_type == SYNC_OBJECT_TYPE_TODO)