1 /* liblivejournal - a client library for LiveJournal.
2 * Copyright (C) 2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
9 #include "liblj/getevents.h"
10 #include "liblj/syncitems.h"
11 #include "liblj/sync.h"
13 typedef struct _Sync
{
15 const char *usejournal
;
21 gboolean sync_complete
;
22 GHashTable
*syncitems
;
26 LJPutLastSyncCallback put_lastsync
;
27 LJRunVerbCallback run_verb
;
28 LJPutEntriesCallback put_entries
;
29 LJSyncProgressCallback sync_progress
;
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;
49 sync_report_progress(Sync
*sync
, LJSyncProgress progress
) {
50 sync
->sync_progress(sync
->user_data
, progress
,
51 sync
->synced
, sync
->total
, sync
->lastsync
);
55 run_one_getevents(Sync
*sync
, GSList
**warnings
) {
56 LJGetEventsSync
*getevents
;
60 getevents
= lj_getevents_sync_new(sync
->user
, sync
->usejournal
,
62 if (!sync
->run_verb(sync
->user_data
, (LJVerb
*)getevents
, sync
->err
)) {
63 lj_getevents_sync_free(getevents
, TRUE
);
66 if (getevents
->entry_count
== 0) {
67 g_warning("getevents returned no entries!?\n");
68 lj_getevents_sync_free(getevents
, TRUE
);
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
)));
86 /* update latest time. */
87 if (itemtime
> lastsync
)
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
);
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
;
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
);
137 run_syncitems(Sync
*sync
) {
139 if (!run_one_syncitems(sync
))
141 sync_report_progress(sync
, LJ_SYNC_PROGRESS_ITEMS
);
142 } while (sync
->synced
< sync
->total
);
148 run_getevents(Sync
*sync
, GSList
**warnings
) {
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
);
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
))
170 sync_report_progress(sync
, LJ_SYNC_PROGRESS_ENTRIES
);
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
;
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
;
195 if (run_syncitems(sync
)) {
196 g_free(sync
->lastsync
);
197 sync
->lastsync
= lastsync
? g_strdup(lastsync
) : NULL
;
198 if (run_getevents(sync
, warnings
))
202 g_free(sync
->lastsync
);
204 g_hash_table_destroy(sync
->syncitems
);