music.c: cosmetix
[k8lowj.git] / src / liblj / sync.c
blobf9a658172cc4dfe466f3380218d7d04ce11c22fd
1 /* liblivejournal - a client library for LiveJournal.
2 * Copyright (C) 2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
5 */
7 #include <glib.h>
9 #include "liblj/getevents.h"
10 #include "liblj/syncitems.h"
11 #include "liblj/sync.h"
13 typedef struct _Sync {
14 LJUser *user;
15 const char *usejournal;
17 char *lastsync;
18 int synced;
19 int total;
21 gboolean sync_complete;
22 GHashTable *syncitems;
24 GError **err;
26 LJPutLastSyncCallback put_lastsync;
27 LJRunVerbCallback run_verb;
28 LJPutEntriesCallback put_entries;
29 LJSyncProgressCallback sync_progress;
30 gpointer user_data;
31 } Sync;
33 static int
34 entry_date_compare_func(const void *a, const void *b, void *unused) {
35 /* struct tm* cast needed to un-constify the times passed to mktime. */
36 const LJEntry *entrya = *(LJEntry **)a;
37 const LJEntry *entryb = *(LJEntry **)b;
38 time_t timea = mktime((struct tm*)&entrya->time);
39 time_t timeb = mktime((struct tm*)&entryb->time);
40 /* mktime actually converts the times to local time, which isn't
41 * quite correct, but since we're comparing times directly like this
42 * it should still sort the same way and timegm is potentially slower. */
43 if (timea < timeb) return -1;
44 if (timea > timeb) return 1;
45 return 0;
48 static void
49 sync_report_progress(Sync *sync, LJSyncProgress progress) {
50 sync->sync_progress(sync->user_data, progress,
51 sync->synced, sync->total, sync->lastsync);
54 static time_t
55 run_one_getevents(Sync *sync, GSList **warnings) {
56 LJGetEventsSync *getevents;
57 int i;
58 time_t lastsync = 0;
60 getevents = lj_getevents_sync_new(sync->user, sync->usejournal,
61 sync->lastsync);
62 if (!sync->run_verb(sync->user_data, (LJVerb*)getevents, sync->err)) {
63 lj_getevents_sync_free(getevents, TRUE);
64 return 0;
66 if (getevents->entry_count == 0) {
67 g_warning("getevents returned no entries!?\n");
68 lj_getevents_sync_free(getevents, TRUE);
69 return 0;
72 /* g_print("lastsync %s : got %d entries\n",
73 sync->lastsync ? sync->lastsync : "(none)", getevents->entry_count);
76 /* remove off all the syncitems for the entries we have now received,
77 * also scanning for the latest time we managed to sync. */
78 for (i = 0; i < getevents->entry_count; i++) {
79 int id = getevents->entries[i]->itemid;
80 time_t itemtime = GPOINTER_TO_INT(
81 g_hash_table_lookup(sync->syncitems, GINT_TO_POINTER(id)));
83 if (itemtime) {
84 sync->synced++;
86 /* update latest time. */
87 if (itemtime > lastsync)
88 lastsync = itemtime;
90 /* pop this off the syncitems list. */
91 g_hash_table_remove(sync->syncitems, GINT_TO_POINTER(id));
95 /* a sync returns a list in update order,
96 * and we don't want to depend on that order anyway.
97 * we want them ordered by entry time. */
98 g_qsort_with_data(getevents->entries, getevents->entry_count,
99 sizeof(LJEntry*), entry_date_compare_func, NULL);
100 /* and finally, put these to disk. */
101 if (!sync->put_entries(sync->user_data,
102 getevents->entries, getevents->entry_count, sync->err)) {
103 lastsync = 0; /* signal to caller that we failed. */
106 lj_getevents_sync_free(getevents, TRUE);
108 return lastsync;
111 static gboolean
112 run_one_syncitems(Sync *sync) {
113 LJSyncItems *syncitems;
115 syncitems = lj_syncitems_new(sync->user, sync->usejournal,
116 sync->lastsync, sync->syncitems);
117 if (!sync->run_verb(sync->user_data, (LJVerb*)syncitems, sync->err)) {
118 lj_syncitems_free(syncitems, TRUE);
119 sync->syncitems = NULL;
120 return FALSE;
123 if (!sync->total)
124 sync->total = syncitems->total;
125 sync->synced = sync->total - (syncitems->total - syncitems->count);
127 g_free(sync->lastsync);
128 sync->lastsync = g_strdup(syncitems->lastsync);
130 sync->syncitems = syncitems->items;
131 lj_syncitems_free(syncitems, FALSE);
133 return TRUE;
136 static gboolean
137 run_syncitems(Sync *sync) {
138 do {
139 if (!run_one_syncitems(sync))
140 return FALSE;
141 sync_report_progress(sync, LJ_SYNC_PROGRESS_ITEMS);
142 } while (sync->synced < sync->total);
144 return TRUE;
147 static gboolean
148 run_getevents(Sync *sync, GSList **warnings) {
149 time_t lastsync;
151 sync->synced = 0;
152 sync->total = g_hash_table_size(sync->syncitems);
154 sync_report_progress(sync, LJ_SYNC_PROGRESS_ENTRIES);
156 while (g_hash_table_size(sync->syncitems) > 0) {
157 lastsync = run_one_getevents(sync, warnings);
158 if (lastsync <= 0)
159 return FALSE;
161 /* now that we've successfully synced this batch of entries,
162 * let the caller know; they should write it out to disk,
163 * so if we're cancelled before we complete,
164 * we can resume at this point instead of starting over. */
165 g_free(sync->lastsync);
166 sync->lastsync = lj_tm_to_ljdate(gmtime(&lastsync));
167 if (!sync->put_lastsync(sync->user_data, sync->lastsync, sync->err))
168 return FALSE;
170 sync_report_progress(sync, LJ_SYNC_PROGRESS_ENTRIES);
173 return TRUE;
176 gboolean
177 lj_sync_run(LJUser *user, const char *usejournal,
178 const char *lastsync, LJPutLastSyncCallback put_lastsync_cb,
179 LJRunVerbCallback run_verb_cb, LJPutEntriesCallback put_entries_cb,
180 LJSyncProgressCallback sync_progress_cb,
181 gpointer user_data, GSList **warnings, GError **err) {
182 Sync st = {0}, *sync = &st;
183 gboolean success = FALSE;
185 sync->user = user;
186 sync->usejournal = usejournal;
187 sync->lastsync = lastsync ? g_strdup(lastsync) : NULL;
188 sync->put_lastsync = put_lastsync_cb;
189 sync->run_verb = run_verb_cb;
190 sync->put_entries = put_entries_cb;
191 sync->sync_progress = sync_progress_cb;
192 sync->user_data = user_data;
193 sync->err = err;
195 if (run_syncitems(sync)) {
196 g_free(sync->lastsync);
197 sync->lastsync = lastsync ? g_strdup(lastsync) : NULL;
198 if (run_getevents(sync, warnings))
199 success = TRUE;
202 g_free(sync->lastsync);
203 if (sync->syncitems)
204 g_hash_table_destroy(sync->syncitems);
205 return success;